diff --git a/.github/RELEASE_CHECKLIST.md b/.github/RELEASE_CHECKLIST.md index 72a11d93a0..0f78a82e5c 100644 --- a/.github/RELEASE_CHECKLIST.md +++ b/.github/RELEASE_CHECKLIST.md @@ -33,8 +33,8 @@ Phase 1 requires: - checksums generated after each artifact reaches its final form. The convenience Desktop artifacts must not contain a Git runtime, a bundled-Git manifest, or Git/Dugite -redistribution notices. Managed-workspace execution remains unavailable until a separately reviewed, -ASF-compatible verified runtime is connected before admission/T1. +redistribution notices. The retired Git executable-backed managed-workspace path must not be restored; +future workspace execution requires a separately reviewed Gitoxide production composition before admission/T1. The first product release also requires the exact `maka-agent@` npm package. The product tag and Draft must exist before npm staging, but the Draft must remain unpublished until npm is diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e9fe3aeed2..ca1844a0cf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -462,7 +462,7 @@ jobs: These files are convenience binaries built from the approved ASF source release; they are not ASF release artifacts. - Known limitations: Computer Use and managed-workspace execution are not included in this release. The Desktop convenience artifacts do not distribute a Git runtime." + Known limitations: Computer Use and Gitoxide-backed workspace execution are not included in this release. The Desktop convenience artifacts do not distribute a Git runtime." if gh release view "$TAG" >/dev/null 2>&1; then is_draft="$(gh release view "$TAG" --json isDraft --jq .isDraft)" diff --git a/.github/workflows/windows-baseline.yml b/.github/workflows/windows-baseline.yml index 06f2dafaaa..8a6fd69cd1 100644 --- a/.github/workflows/windows-baseline.yml +++ b/.github/workflows/windows-baseline.yml @@ -167,7 +167,6 @@ jobs: 'packages/storage/dist/__tests__/write-queue.test.js', 'packages/storage/dist/__tests__/marker-file.test.js', 'packages/storage/dist/__tests__/workspace-root.test.js', - 'packages/storage/dist/__tests__/managed-workspace-owner.test.js', 'packages/storage/dist/__tests__/workspace-identity.test.js' ) node.exe --test --test-concurrency=2 --test-force-exit --test-timeout=60000 ` diff --git a/.github/workflows/windows-recovery.yml b/.github/workflows/windows-recovery.yml index dc9184186f..1c978e38d8 100644 --- a/.github/workflows/windows-recovery.yml +++ b/.github/workflows/windows-recovery.yml @@ -192,20 +192,3 @@ jobs: Write-Error 'Runtime Host recovery gate did not run exactly two passing tests' exit 1 } - - - name: Verify managed workspace crash recovery - shell: pwsh - run: | - $env:MAKA_STORAGE_STRESS = '1' - node.exe --test --test-reporter=tap --test-concurrency=1 ` - --test-name-pattern="real process crash|real crash|real-process crash|crash after baseline ref publication" ` - packages/storage/dist/__tests__/managed-workspace-baseline.test.js ` - packages/storage/dist/__tests__/git-workspace-service.test.js ` - 2>&1 | Tee-Object -FilePath "$env:RUNNER_TEMP/managed-workspace-recovery.tap" - $exitCode = $LASTEXITCODE - if ($exitCode -ne 0) { exit $exitCode } - $output = Get-Content "$env:RUNNER_TEMP/managed-workspace-recovery.tap" - if ($output -notcontains '# tests 12' -or $output -notcontains '# pass 12' -or $output -notcontains '# skipped 0') { - Write-Error 'Managed workspace recovery gate did not run exactly twelve passing tests' - exit 1 - } diff --git a/docs/architecture/gitoxide-short-lived-helper-admission-v1.zh-CN.md b/docs/architecture/gitoxide-short-lived-helper-admission-v1.zh-CN.md index 16eb90514d..9f237c1256 100644 --- a/docs/architecture/gitoxide-short-lived-helper-admission-v1.zh-CN.md +++ b/docs/architecture/gitoxide-short-lived-helper-admission-v1.zh-CN.md @@ -31,7 +31,7 @@ > 打开的 object format 稳定 fail closed,且不得调用或回退到系统 Git。 它不证明 source import、clone、fetch、worktree、candidate、projection、ref CAS、Write/Edit 或 -resume。现有 dormant `GitWorkspaceService` 也没有切换到该 helper。 +resume。已删除的 Git executable-backed workspace service 也不由该 helper 复活。 ## 2. 为什么是 helper,不是常驻 broker diff --git a/docs/architecture/managed-dependency-storage-authority-v1.zh-CN.md b/docs/architecture/managed-dependency-storage-authority-v1.zh-CN.md index 4c9974c8c6..168bead1d7 100644 --- a/docs/architecture/managed-dependency-storage-authority-v1.zh-CN.md +++ b/docs/architecture/managed-dependency-storage-authority-v1.zh-CN.md @@ -38,7 +38,7 @@ base: upstream/main@08bcf324b - bundled npm 的实现与网络策略; - `node_modules/.bin` symlink 的 producer 配额扫描; -- ManagedWorkspaceOwner/worker logical binding; +- workspace execution owner/worker logical binding; - Desktop/CLI/Runtime Host 接线; - release packaging、audit 或 SBOM。 diff --git a/docs/architecture/runtime-managed-workspace-baseline-open-v1.zh-CN.md b/docs/architecture/runtime-managed-workspace-baseline-open-v1.zh-CN.md deleted file mode 100644 index 30271ce748..0000000000 --- a/docs/architecture/runtime-managed-workspace-baseline-open-v1.zh-CN.md +++ /dev/null @@ -1,265 +0,0 @@ - - -# Managed Workspace Baseline Open v1:M0 最终接受门 - -- 状态:已合并;M1 execution admission 在其返回值上增加 owner-bound handle,不改变 M0 durable authority -- 更新日期:2026-08-04 -- 主要不变量:只有经 Git artifact owner 持久化并重新验证的 exact baseline receipt,才能由同一 - storage-root lifecycle owner 提交给 SQLite workspace authority;Git artifact 单独成功不构成 - canonical workspace version -- Git artifact owner:`GitWorkspaceService` -- composition/lifecycle owner:`ManagedWorkspaceOwner` -- canonical history owner:SQLite 内 immutable workspace RuntimeEvents - -## 1. 本切片交付什么 - -前三个 M0 切片已经分别证明: - -1. Maka 可以用 pinned Git 创建、验证、repair 和 quarantine 自有 Git repository/worktree artifacts; -2. SQLite 可以把 epoch-opened、baseline-accepted 与三个 projection 原子提交,并从 RuntimeEvents 重建; -3. 一个 authenticated interactive storage root 只发布一个 managed workspace lifecycle owner。 - -它们之间仍缺一个权限与因果门。如果调用者可以把任意 OID 直接交给 SQLite,那么 Git 验证只是注释; -如果 Git 创建成功就被当作 canonical,那么 SQLite 失败后会出现第二套 workspace truth。本切片只关闭这条 -seam,不接 Desktop、CLI、runtime host 或工具执行。 - -公开入口是: - -```ts -owner.openManagedWorkspaceBaseline(runtimeStore, { - repositoryId, - workspaceId, - workspaceEpochId, - workspaceInstanceId, - sourceRoot, -}); -``` - -调用者只提供业务身份与 source root,不能提供 policy hash、commit/tree OID、version ID、event ID、 -tree delta 或“verified”布尔值。M0 policy 是实现实际执行并内部 canonicalize/hash 的固定协议,不是 -调用者提供的标签。 - -## 2. Owner、原子边界、失败状态与回滚 - -| 项目 | 决策 | -|---|---| -| Git artifact owner | `GitWorkspaceService` 证明 Git artifact,并在未导出的 receipt authority capability 中实现持久化与复验 | -| lifecycle owner | `ManagedWorkspaceOwner` 是 receipt capability 的唯一持有者,独占 Git receipt → SQLite authority 的组合调用 | -| canonical truth | SQLite authority stream 的 immutable RuntimeEvents;receipt 是 admission evidence,不是 canonical head | -| Git durability boundary | `baseline-receipt.json` 以同目录临时文件、file fsync、rename、POSIX parent fsync 持久化 | -| SQLite atomic boundary | epoch fact、baseline fact、epoch/version/head projection 位于同一事务 | -| root binding | authority store 必须是 authenticated storage root 下精确的普通非 symlink `runtime.sqlite`,并保持注册时的文件 identity;该文件必须只有一个 hard link,lease 的真实 DB path 是唯一依据;数据库 singleton rootId 必须等于 authenticated owner rootId;SQLite COMMIT 后、返回前再次验证 pathname、identity 与 durable rootId | -| policy identity | policy version/hash 从固定且实际执行的 M0 policy object 内部派生;覆盖 source cleanliness、tracked/untracked/ignored、UTF-8 lossless path、NFC/case-fold collision、symlink/submodule/attributes/special mode 与 materialization;public API 不接受裸 hash | -| exact retry | version/event IDs 由 binding + artifact + policy 确定性派生;durable receipt 冻结可重新证明的 Git evidence 与 delta | -| pre-accept orphan | receipt 已落盘而 SQLite 未接受时,它是可重试 orphan;不是 canonical workspace version | -| post-accept loss | SQLite 已有 head 但 receipt 缺失、漂移或 Git artifact 不可验证时,报 corruption/unavailable,不生成替代 receipt | -| 回滚 | 停止调用本入口即可;既有 RuntimeEvents 仍可读。不能删除已接受 receipt 来“回滚” canonical history | - -本切片没有尝试跨 Git filesystem 与 SQLite 建立一个不真实的分布式事务。它使用可重放协议收敛:Git -receipt 先 durable,SQLite 后原子接受;唯一允许的半完成状态是“未被接受的 Git orphan”。反方向的 -“SQLite 已接受但 Git receipt 不存在”不是可修复半状态,而是 fail-closed corruption。 - -## 3. Durable receipt 合同 - -receipt 位于 managed instance root,和 `binding.json` 同级: - -```text -managed-workspaces/ - w//e//i// - binding.json - baseline-receipt.json - worktree/ -``` - -v1 receipt 严格包含: - -```ts -{ - schemaVersion: 1, - protocol: 'maka_managed_workspace_baseline_receipt_v1', - binding, - workspaceVersionId, - policyVersion: 1, - policyHash, - epochOpenedEventId, - baselineAcceptedEventId, - treeDeltaDigest, - changedFileCount, - deletedFileCount: 0, -} -``` - -所有字段采用 exact-key strict decoder。receipt 必须重新证明: - -- durable binding 与调用 identity 完全相同; -- repository record、epoch artifact、baseline ref、head ref、commit/tree 与 pinned Git capability 一致; -- worktree registration 仍被 Maka ownership lock 锁定; -- HEAD/tree 等于 baseline,worktree clean; -- policy version/hash 等于实现实际执行的 canonical M0 policy; -- workspaceVersionId、epochOpenedEventId 与 baselineAcceptedEventId 可从 binding、baseline artifact 与 - policy 确定性重新派生;合法 shape 的磁盘篡改也必须被拒绝; -- tree delta 摘要与 baseline tree 重新计算结果一致。 - -`GitWorkspaceService` 的 public interface 不暴露 receipt 的 issue/require/verify,package root 也不导出 -`git-workspace-service` factory。artifact-only create/open 只作为 storage 内部前置实现存在;public -`ManagedWorkspaceOwner` 只暴露 `openManagedWorkspaceBaseline(...)` 与 lifecycle close。对应 receipt capability -位于未被 package root 导出的 internal module,并通过实例绑定的 `WeakMap` 只交给该 owner。普通 package -consumer 既不能预占一个 epoch 的 admission identity,也不能在 canonical acceptance 前取得 executable -worktree path。M1 execution admission 只允许消费由该入口成功返回的 owner-bound handle;handle 的 consumer -是同一 `ManagedWorkspaceOwner.withManagedWorkspaceExecution(...)` 准入门,仍未把 cwd 暴露给 Desktop、CLI -或 ToolRuntime。详细合同见 -[Managed Workspace Execution Admission v1](./runtime-managed-workspace-execution-admission-v1.zh-CN.md)。 - -receipt 不保存 wall-clock `committedAt`:artifact owner 无法从 Git evidence 重新证明该时间,允许它进入 receipt -会让 orphan receipt 篡改污染 canonical event 时间。M0 的 baseline authority 使用协议固定逻辑时间 `0`; -canonical 顺序由 SQLite authority spine 的 `event_seq` 决定。真实 wall-clock acceptance time 若未来用于观测, -必须由 store 自己生成并作为 operational metadata 保存,不进入 Git evidence 或恢复身份。 - -## 4. Tree delta 摘要 - -M0 baseline 的逻辑 parent 是 empty tree,因此: - -- `changedFileCount` 等于 baseline tree 的受支持 blob entry 数; -- `deletedFileCount` 固定为 `0`; -- `treeDeltaDigest` 是 domain-separated canonical manifest 的 SHA-256,而不是 caller 自报值。 - -manifest 以 Buffer 读取 Git `ls-tree -r -z`,按 Git canonical 顺序记录每个 entry 的 `mode`、 -`objectType`、`oid` 和 `pathBytesBase64`。v1 为了保证 Git path 与 Node/host 路径语义一致,会严格 -拒绝不能无损解码并 round-trip 的非 UTF-8 path;不会把 replacement character 写进摘要。M0 只接受 -普通 blob mode `100644` / `100755`;symlink、submodule、special mode 与 case collision 继续由 Git -Workspace Service 在 receipt 生成前拒绝。现有 Git CLI adapter 仍拒绝 `.gitattributes`;替代它的 -Gitoxide data plane 只接受 managed tree policy v2 已验证的确定性 attributes 子集,不能把两条路径的 -receipt 或 materialization profile 混用。 - -该摘要证明的是 Git object delta,不是 checkout 文件字节扫描,也不把 ignored/untracked 内容纳入 -canonical baseline。 - -## 5. 正常时序 - -```mermaid -sequenceDiagram - participant C as "Storage composition caller" - participant O as "ManagedWorkspaceOwner" - participant G as "GitWorkspaceService" - participant R as "baseline-receipt.json" - participant S as "SqliteRuntimeStore" - participant E as "Workspace RuntimeEvents" - - C->>O: openManagedWorkspaceBaseline(store, identity) - O->>O: authenticate root lease; assert exact runtime.sqlite identity - O->>S: bind/verify singleton durable storage-root ID - O->>G: create/adopt and verify managed workspace - G->>G: verify binding/repository/epoch/ref/worktree - G->>G: derive canonical policy + IDs + empty-tree → baseline delta - alt receipt already exists - G->>R: strict read + exact revalidation - else no canonical head and receipt absent - G->>R: atomic durable write of frozen receipt - end - G-->>O: verified durable receipt - O->>S: internal commitWorkspaceBaseline(receipt-derived input) - S->>S: BEGIN IMMEDIATE - S->>E: epoch-opened + baseline-accepted - S->>S: epoch/version/head projections - S->>S: COMMIT - S-->>O: created or exact-existing head - O->>O: post-commit revalidate exact runtime.sqlite identity - O->>S: revalidate exact durable storage-root ID - O->>G: post-commit reverify exact receipt and Git artifacts - G-->>O: verified - O->>O: re-authenticate root owner and durable root marker - O-->>C: usable canonical baseline -``` - -只有最后一步成功返回以后,调用者才得到可用的 managed baseline。SQLite commit 后的 reverify 失败 -不会撤销 canonical history;它阻止当前 instance 被交给后续工具,并要求 repair/quarantine 流程处理。 -最终 root-owner 复验与 Git receipt 复验是两个独立闸门:receipt 证明 managed artifact,root-owner -复验证明返回瞬间的 marker、lease、database binding 仍属于同一个 authenticated storage root。 - -## 6. Crash 与并发矩阵 - -| 崩溃/并发点 | durable Git 状态 | canonical SQLite 状态 | 重启结果 | -|---|---|---|---| -| receipt durable 前退出 | 无 receipt 或原子旧值 | absent | 重新验证 Git 后重新生成 receipt | -| receipt durable 后、SQLite 前退出 | exact orphan receipt | absent | exact reuse receipt,再提交 authority | -| epoch/version/projection 事务中退出 | exact receipt | 全回滚 | exact retry;不生成新 IDs | -| SQLite COMMIT 后、post-verify 前退出 | exact receipt | accepted | 重读 head + require existing receipt;exact existing | -| accepted 后 receipt 缺失 | missing | accepted | fail closed;禁止创建 replacement receipt | -| accepted 后 receipt 篡改 | invalid/drifted | accepted | fail closed;canonical history 不变 | -| accepted 后 Git ref/object/worktree 漂移 | unverifiable | accepted | fail closed,后续 quarantine/repair;不返回 cwd | -| 同一 root owner 内两个并发 open | artifact lock/SQLite writer 串行 | 一个 transaction 创建 | 一个 created,一个 exact existing | -| 两个进程竞争同一 root owner | loser 不能越过 root-owner admission | winner 独占 composition | winner 关闭后 loser 重试并得到 exact existing | -| owner 传入另一 storage root 的 DB | 不写 receipt | 不写错误 DB | admission 前拒绝 | -| 两个 storage root 以 hard link 共享 `runtime.sqlite` inode | 不写 receipt | 不接受共享 DB;拒绝 `nlink != 1` | fail closed,避免 WAL/SHM 跨目录分裂 | -| 单独复制或移动已绑定的 `runtime.sqlite` 到另一 root | 不写/不读取错误 root 的 receipt | DB rootId 与 owner rootId 冲突 | fail closed;必须使用 whole-root import/adopt 协议 | -| 正式复制整个 storage root 后 adopt | storage-root marker 与 DB 随 root 保留 | DB rootId 与 marker rootId 保持一致 | `adoptStorageRootOnImport` 只恢复 storage-root identity;不证明旧路径下已 materialize 的 linked worktree 可迁移 | -| imported root 含既有 managed workspace instance | binding 与 Git admin metadata 可能仍引用旧绝对路径 | canonical history 保留 | fail closed;首版必须显式 relocation/adoption 或重新 materialize 新 instance,不能直接返回旧 cwd | -| unbound DB 已含 Session、RuntimeEvent、claim 或 workspace fact 等逻辑数据 | 不生成新 receipt | 禁止静默认领 | 要求独立、显式、可备份和可审计的 legacy root-binding adoption;M0 baseline open 不承担迁移 | -| 初次 DB identity 检查后 canonical pathname 被替换 | receipt 可能成为 orphan | 已打开连接的提交视为 detached,不返回 usable baseline | post-commit DB identity 复验拒绝;新 canonical DB 不获得错误 head | -| post-commit artifact 复验后 root marker 被替换 | exact receipt 与 accepted head 保留 | canonical history 不回滚 | 最终 root-owner identity 复验拒绝;不返回 usable baseline | -| owned quarantine/instance parent 被替换为 symlink | physical layout revalidation 拒绝 | canonical history 不变 | fail closed,不读取外部 control record、不移动到外部目录 | - -真实进程 crash harness 分别覆盖 receipt durable 后、SQLite authority 前,以及 SQLite COMMIT 后、 -post-verify 前的 `SIGKILL`;两者重启后都使用同一 receipt/identity 收敛。SQLite 事务内部的 -process-kill/rollback 证明继续由 Workspace Version Authority 测试拥有;M0 只增加跨 Git、root owner、 -SQLite 与 post-verify 的 composition crash proof。 - -## 7. 平台能力矩阵 - -| 能力 | Linux | macOS | Windows | -|---|---|---|---| -| pinned Git artifact revalidation | 支持 | 支持 | 支持 | -| receipt file fsync + atomic rename | 支持 | 支持 | 进程崩溃支持;不承诺断电级 parent fsync | -| SQLite atomic baseline acceptance | 支持 | 支持 | 支持 | -| raw Git path | 非 UTF-8 fail closed | 非 UTF-8 fail closed | Node/Git UTF-8 path policy | -| real-process crash test | 发布门槛 | 发布门槛 | 有限支持;依赖 Node/Git 的 SIGKILL 等价行为 | -| power-loss durability | 不承诺 | 不承诺 | 不承诺 | - -Windows 上不虚构 POSIX directory fsync 承诺。M0 保证进程崩溃后的可重试状态收敛;机器断电、磁盘缓存 -或文件系统损坏不在 v1 发布证明内。 - -## 8. 明确延期 - -- Desktop、CLI、runtime-host 与设置入口; -- 工具 cwd 切换、ignored dependency/scratch provisioning; -- mutation candidate ref、T1/T2 + workspace version 原子接受、conditional redo; -- continuation 绑定 workspace version 与自动 resume; -- orphan receipt/ref/object GC; -- whole-root import 后既有 managed worktree 的 relocation/adoption 或重新 materialize; -- 非空 legacy DB 的显式备份、授权、root-binding adoption 与完整重扫维护入口; -- replication、跨端同步、publish、undo 与 multi-agent merge。 - -这些能力必须分别由后续 PR 证明自己的 owner、原子边界、失败状态与回滚方式。M0 完成只代表系统拥有 -一个可验证、可重试、canonical 的 managed baseline,不代表 agent 已经在该 worktree 中执行工具。 - -## 9. 验收门槛 - -- 首次 open 与 exact retry 返回同一 receipt/head; -- receipt-after-write crash 与 SQLite rollback 都能 exact retry; -- SQLite COMMIT 后、post-verify 前真实进程 crash 返回 exact existing; -- accepted 后 receipt missing/tampered fail closed; -- post-commit Git 复验后 root marker 变化时,保留 canonical head 但拒绝返回 usable workspace; -- lease 伪 path、同 root 第二 DB、memory DB、DB symlink 与文件 identity 变化均在 admission 前拒绝; -- public Git service 无 receipt issuer,canonical policy/hash 与 deterministic IDs 有 literal/篡改测试; -- raw path digest 使用字节编码,非 UTF-8 source path fail closed; -- 同 owner 并发 open 收敛为一个 created、一个 existing;跨进程首先由 root-owner lock 仲裁; -- Git service、owner、workspace authority 的 focused real-Git/crash suites 全绿; -- `@maka/core` / `@maka/storage` build 与 `git diff --check` 通过; -- 不包含 Desktop/CLI/runtime-host 生产接线。 diff --git a/docs/architecture/runtime-managed-workspace-execution-admission-v1.zh-CN.md b/docs/architecture/runtime-managed-workspace-execution-admission-v1.zh-CN.md deleted file mode 100644 index 9cb4f52268..0000000000 --- a/docs/architecture/runtime-managed-workspace-execution-admission-v1.zh-CN.md +++ /dev/null @@ -1,179 +0,0 @@ - - -# Managed Workspace Execution Admission v1:M1.1 可撤销 scope 门 - -- 状态:M1.1 execution admission 与 M1.2 owner-bound worker / Runtime Host composition 已实现;尚未由 Desktop/CLI 默认启用 -- 更新日期:2026-08-27 -- 主要不变量:同一个 `ManagedWorkspaceOwner` 只能用其亲自签发的进程内 execution handle 创建 active - execution scope;一次 admission 只签发一个 scope,但同一 handle 允许多个只读 admission 并发。每次创建 - scope 前重新证明 exact SQLite workspace head 与 exact Git artifact,公共 API 永不发布 raw cwd,callback - 退出后 scope 立即失效 -- lifecycle / admission owner:`ManagedWorkspaceOwner` -- durable truth:SQLite immutable workspace RuntimeEvents -- artifact evidence:Maka-owned Git binding、baseline receipt、HEAD/tree 与 ownership lock - -## 1. 本切片交付什么 - -M0 已经能创建或 exact-adopt 一个 managed baseline,并把 Git receipt 与 SQLite canonical head 组合接受。 -但 raw `worktreePath` 是不可撤销字符串,调用者可以永久缓存并绕过 owner close、后续 revalidation 和未来 -workspace version 变化。因此 M1.1 使用两级进程内 capability:baseline 返回 owner-bound execution handle; -每次 admission callback 只得到 active execution scope,不得到 path。 - -```ts -const accepted = await owner.openManagedWorkspaceBaseline(store, identity); - -await owner.withManagedWorkspaceExecution(accepted.executionHandle, async (scope) => { - // M1.2 的 storage-internal worker bridge 才能消费 active scope。 -}); -``` - -`openManagedWorkspaceBaseline(...)` 不再公开 raw binding、receipt 或 `worktreePath`。handle 的内部证据保存在 -未从 package root 导出的模块中,并使用 `WeakMap` 与 owner token 绑定;复制相同字段或使用另一个 owner 的 -handle 都不能获得执行权限。scope 同样由 `WeakMap` 保存真实状态,callback 退出后即使被闭包保留也只能得到 -typed `ManagedWorkspaceExecutionAuthorityError`,其稳定 code 为 -`managed_workspace_execution_scope_expired`;伪造 scope 的 code 为 -`managed_workspace_execution_scope_invalid`。 - -M1.1 没有接入 Desktop、CLI 或 ToolRuntime,只建立 host 后续接线必须消费的唯一准入 API。M1.2 已把该 -authority 接入 Runtime Host 的只读 worker 与 lifecycle composition,但仍未由 Desktop/CLI 默认启用。 - -## 2. Owner、原子性边界、失败状态与回滚 - -| 项目 | 决策 | -|---|---| -| execution admission owner | 只有签发 handle 的同一个 `ManagedWorkspaceOwner` 可以消费它 | -| durable authority | handle/scope 都不是 durable fact;canonical head 通过注册时捕获的 storage-internal reader 读取,不调用可被 caller shadow 的 public method | -| 原子性边界 | 不虚构 Git 与 SQLite 之外的新事务;在 owner/root lease residency 内先完成 exact receipt/binding/Git verification,再以 immutable SQLite workspace head 作为 scope 签发前最后一次 durable reread;随后执行 DB pathname/inode guard 和纯内存 identity compare,不再运行慢速 Git 命令 | -| 执行期间 | 每个 callback 分别计入 owner active operation;同一 handle 可并发多个 `workspaceEffect: none` scope;`close()` 等全部 scope drain,新 admission 在 closing 后被拒绝 | -| invalid handle | `managed_workspace_execution_handle_invalid`;不签发 scope | -| foreign / expired scope | `managed_workspace_execution_scope_invalid` / `managed_workspace_execution_scope_expired`;bridge 不解析 cwd、不 dispatch worker | -| worker 缺失 | `managed_workspace_worker_unavailable`;不从 managed mode fallback 到 host-local I/O | -| mutation / unknown operation | `managed_workspace_operation_denied`;Write/Edit/Format/Bash/未知 operation 在 worker dispatch 前 fail closed | -| canonical head 漂移/缺失 | fail closed;旧 handle 不能自行选择新 head | -| artifact drift | 不签发 scope;所有 Git verification 阶段的 `managed_workspace_drifted` 统一 quarantine | -| mutation permission | scope 固定 `workspaceEffect: none`;M2 前没有 raw cwd consumer,Write/Edit/Bash/未知工具不得接入 | -| 回滚 | 无新增 schema 或 durable admission row;停止调用 API 即回滚能力,已接受的 baseline history 与 artifact 不变 | - -## 3. 每次执行的时序 - -```mermaid -sequenceDiagram - participant H as "Runtime host(后续消费者)" - participant O as "ManagedWorkspaceOwner" - participant S as "SQLite workspace authority" - participant G as "Git artifact owner" - participant T as "Scope callback" - - H->>O: withManagedWorkspaceExecution(handle, callback) - O->>O: 验证 handle 属于本 owner,进入 drain residency - O->>S: internal reader 读取 exact workspace/epoch canonical head - S-->>O: accepted WorkspaceHead - O->>O: exact compare frozen expected head + full cross-plane identity - O->>G: one-shot exact receipt/binding/HEAD/tree/lock verification - G-->>O: verified artifact - O->>S: final internal head reread - O->>O: runtime.sqlite pathname+inode guard + pure identity compare - O->>T: callback(active opaque scope) - T-->>O: result / error - O->>O: revoke scope;释放 active residency;允许 close 收敛 -``` - -这不是跨 SQLite/Git/filesystem 的共同事务。它关闭 cooperating Maka writer 的 proof-to-scope seam;任意外部 -进程仍可能在最后一次 filesystem observation 后制造 drift,后续 admission 会 fail closed。普通生产路径只执行 -图中的一次 exact Git verification,随后以 SQLite head 作为最后一次 durable reread;只有配置 production-shaped -crash failpoint 的测试路径才先执行一次 preliminary verification,以便在真实 proof 完成后 `SIGKILL`,随后仍必须 -再次执行 exact Git verification 并完成 final head reread 才能签发 scope。M1.2 的 worker/ -sandbox 建立实际 I/O 隔离,M2 才负责 mutating tool 产生 successor candidate 并接受新 workspace version。 - -## 4. Provisioning 与实际可用性边界 - -本切片 scope 的 provisioning 固定为 `canonical_tree_only_v1`,且 `workspaceEffect` 固定为 `none`。底层 -artifact 只包含 accepted Git tree: - -- 不复制 source checkout 的 ignored/untracked 文件; -- 不复制 `.env`、credential、`node_modules` 或 build cache; -- 不自动运行安装命令; -- 不把 scratch/build output 偷偷写入 canonical baseline; -- 不从 managed profile 静默 fallback 到 attached checkout。 - -因此 M1.1 可用于验证纯 tracked-tree 读取和后续受控工具接线,但还不宣称一般开发任务已经可用。 -ignored dependency、secret 与 scratch overlay 必须作为独立 M1 provisioning 切片,写明数据来源、生命周期、 -泄露边界和清理方式后才能接入。 - -## 5. Crash、并发与外部 drift 矩阵 - -| 场景 | 必须结果 | -|---|---| -| baseline 已接受,签发 handle 前崩溃 | 重启后从 canonical head 与 receipt 重新签发新 handle | -| execution verification 中崩溃 | 没有 durable “half admission”;旧 handle 随进程消失 | -| crash-test preliminary verification 后 drift | final exact receipt/Git verification 检测并 quarantine;scope 不签发 | -| 同一 handle 的两个只读 callback 并发 | 两个 scope 可同时 active,彼此独立 revoke | -| 多个 callback 运行时 owner close | close 等全部 callback drain;不取消已 admission operation | -| closing 后新 execution 请求 | `managed_workspace_owner_closing` | -| forged / cross-owner handle | `managed_workspace_execution_handle_invalid` | -| callback 保存 scope 后再次使用 | scope 已 expired,internal consumer 必须拒绝 | -| callback 抛错 | scope 仍在 finally 中 revoke,owner residency 正常释放 | -| worker 在返回结果前失败 | worker error 结束本次 operation;callback 的 finally revoke scope,owner residency 被释放,后续 close 可收敛 | -| worker timeout / abort | production `FilesystemWorkerClient` 终止并等待 one-shot worker process tree 与 I/O drain 后才 reject;scope 在此之后 revoke | -| worker 已启动时 host process crash | M1.2 不承诺跨平台 parent-death kill;worker 仍受 read-only sandbox 与单次请求约束,不能留下 workspace mutation,且完成或超时后退出;新 host 必须重新 admission。Shell/Write/Edit 不得借此 seam 执行 | -| 用户直接编辑 Maka-owned worktree | 系统不能物理阻止;下一次 admission 检测 drift 并 fail closed/quarantine | -| 重启后恢复 | 新 root owner、新 managed owner、新 handle;不可恢复旧进程内 capability | - -production-shaped 测试使用真实 pinned Git、真实 SQLite、真实子进程和 `SIGKILL`,证明 execution artifact -verification 中断后,重启只能经完整 reopen/revalidate 获得新 scope authority;真实 Git 并发测试证明两个 -只读 scope 可以并行,且 `close()` 必须等待二者全部退出。 - -filesystem worker 是 one-shot request/response process。它的 `execute()` 合同要求 Promise 只有在该次操作及其 -拥有的 process lifecycle 已终止后才能 settle;storage owner 因而以这个 Promise 作为 execution residency, -不再增加一个无法约束真实进程的装饰性 lease。正常返回、worker error、timeout 与 abort 都必须先由现有 -`FilesystemWorkerClient` / process runner 收敛,再退出 callback 并 revoke scope。host 被 `SIGKILL` 时无法执行 -JavaScript finally,因此本切片的安全保证来自“只读 operation allowlist + read-only sandbox”,而不是虚构所有平台 -都具备 parent-death cleanup。需要 durable handle 的 ShellRun 继续留在后续独立阶段。 - -## 6. 平台能力矩阵 - -| 能力 | Linux | macOS | Windows | -|---|---|---|---| -| owner-bound opaque handle | 支持 | 支持 | 支持 | -| exact SQLite/Git/head/tree revalidation | 支持 | 支持 | 支持 | -| callback drain | 支持 | 支持 | 支持 | -| process crash 后重新准入 | 支持 | 支持 | 支持(进程级;不宣称断电 durability) | -| external drift quarantine | 支持 | 支持 | 有限支持,沿用 Git artifact owner 的 Windows 保证 | -| managed Read/Glob/Grep worker bridge | 需要可用的 Linux sandbox backend;缺失时 fail closed | 支持 seatbelt worker | 当前不支持;managed Host composition fail closed,不回退 host-local | -| runtime-host lifecycle composition | 支持 | 支持 | 支持生命周期与 typed profile,但 managed I/O 因 worker 不可用而拒绝 | -| power-loss durability | 不承诺 | 不承诺 | 不承诺 | - -## 7. 已交付边界与后续切片 - -1. M1.2(已实现):owner-bound storage worker bridge 消费 active scope 并在内部解析 cwd;公共 caller 仍不获得 path。 - managed profile 在 M2 前只允许 `workspaceEffect: none` 的 Read/Glob/Grep;Write/Edit/Format/Bash/未知工具在 - worker dispatch 前 fail closed。无 ownerToken 的 inspect API 仅保留在显式 test support 中;callback 内 - reentrant `owner.close()` 由 execution-context guard 拒绝。runtime-host 使用不可混淆的 attached/managed - typed profile;managed profile 只携带 opaque handle,绝不携带 attached cwd,也绝不 fallback。Host 先 drain - tool operations,再关闭 workspace composition/managed owner,最后由 kernel 关闭 root owner。只有显式提供 - verified Git runtime 且 sandbox filesystem worker 可用时才组合 managed owner;否则保持未启用或 fail closed。 -2. M1.3:显式 dependency/secret/scratch provisioning;首版若无法安全提供则保持 - `canonical_tree_only_v1`,不能 silent fallback。 -3. M2:mutation candidate capture/accept;T1 前冻结 profile/base version,SQLite 原子接受 tool outcome - 与 successor workspace version。 - -M1.2 提供真实 runtime-host composition seam,但本切片不修改 Desktop/CLI 默认配置,因此不默认开启 managed -execution,也不改变 attached mode。ASF Desktop 已停止分发 bundled Git;在兼容许可证的 verified runtime -接入前,managed execution 必须在 admission/T1 前保持不可用,且不得回退系统 Git。 diff --git a/docs/architecture/runtime-managed-workspace-owner-v1.zh-CN.md b/docs/architecture/runtime-managed-workspace-owner-v1.zh-CN.md deleted file mode 100644 index 5502e57085..0000000000 --- a/docs/architecture/runtime-managed-workspace-owner-v1.zh-CN.md +++ /dev/null @@ -1,136 +0,0 @@ - - -# Managed Workspace Owner v1:M0 生命周期门 - -- 状态:已合并;M1 execution admission 继续复用本 owner 的 lifecycle/drain 门 -- 更新日期:2026-08-04 -- 主要不变量:一个 authenticated interactive storage-root owner 在其生命周期内至多发布一个 - managed workspace owner;已经 admission 的 workspace 操作必须在关闭前 drain -- artifact owner:`GitWorkspaceService` -- lifecycle owner:`ManagedWorkspaceOwner` -- canonical workspace history:仍由 Workspace Version Authority 拥有;M0 composition 只允许 owner 的 - `openManagedWorkspaceBaseline(...)` 通过 storage-internal writer 写入 baseline RuntimeEvents - -## 1. 为什么需要独立 owner - -`GitWorkspaceService` 能创建、验证、repair 与 quarantine Maka-owned Git artifacts,但它是一个 -operation-scoped service;仅有它还不能回答: - -- 哪一个 host 有权在当前 storage root 上驱动这些操作; -- 初始化进行中或失败时,是否可能发布半个可用 owner; -- shutdown 与进行中的 Git 操作谁先完成; -- Desktop、CLI 与 runtime-host 是否可能各自绕过同一生命周期门。 - -本切片把现有 authenticated `InteractiveRootOwner` 作为上层 lease authority。它不增加第二个 OS -owner lock,也不通过路径自行证明 ownership。 - -## 2. Owner、边界、失败状态与回滚 - -| 项目 | 决策 | -|---|---| -| 唯一 owner | 一个真实 `InteractiveRootOwner` 对象只能组合一个 `ManagedWorkspaceOwner` | -| 初始化边界 | pinned Git digest 验证与 storage-root authority probe 全部运行在 root write lease 内 | -| operation admission | 仅 `ready` 可 admission;每项操作同时持有 managed-owner residency 与 root lease operation | -| shutdown | `ready -> closing -> closed`;`closing` 拒绝新操作并等待已 admission 操作 drain | -| 初始化失败 | 返回 `managed_workspace_owner_unavailable`,释放未发布 claim,允许同一 root owner 修正后重试 | -| 重复组合 | 返回 `managed_workspace_owner_conflict` | -| drift | 不返回 drifted cwd;receipt/artifact 复验发现 drift 时 fail closed;复验后 reopen 竞态发现 drift 时 durable quarantine | -| 回滚 | 不接 Desktop/CLI/runtime-host,不改变 attached mode;可删除本 owner 而不改变 Git artifacts 或 RuntimeEvents | - -owner 不关闭外层 `InteractiveRootOwner`。Runtime Host 仍拥有 root owner 的最终关闭顺序;managed owner -必须先关闭。反过来,如果 root owner 已开始关闭,lease revalidation 会阻止新的 managed operation。 - -## 3. 公开状态机 - -```mermaid -stateDiagram-v2 - [*] --> opening - opening --> ready: pinned Git + root lease probe 成功 - opening --> failed: 初始化失败,释放 unpublished claim - ready --> closing: close() - closing --> closing: 拒绝新 operation,等待 residency drain - closing --> closed: active operations = 0 - closed --> closed: repeated close() -``` - -`opening` 与 `failed` 不作为已发布 owner 的可见状态。factory 只有在初始化完成并再次确认 root owner -仍然有效后才返回 `ready` owner。 - -## 4. Workspace gate - -owner 的 public surface 开放两个连续、不可绕过的 workspace admission 操作: - -1. `openManagedWorkspaceBaseline(store, identity)` 从 eligible clean source 创建/exact-adopt artifact, - 持久化并复验 receipt,再由 storage-internal writer 接受 canonical baseline,返回 owner-bound execution handle; -2. `withManagedWorkspaceExecution(handle, callback)` 在每次执行前重新证明 canonical head、receipt、HEAD/tree、 - ownership 与 root identity,只在 drain-managed callback 中签发可撤销 opaque scope;raw cwd 不进入 - public API。 - -artifact-only create/open 和 `GitWorkspaceService` factory 不从 package root 导出。调用者不能在 SQLite -acceptance 前取得 `worktreePath` 或裸 `ManagedWorkspaceBinding`。入口返回前必须验证 worktree、index、 -HEAD、tree、ownership lock、canonical `runtime.sqlite` pathname/inode、durable receipt,以及最终时刻的 -`InteractiveRootOwner`/root marker identity;任何失败都不能把 cwd 交给工具。SQLite 已提交而最终 owner -复验失败时,canonical history 保留,但本次调用不得发布 usable workspace。 - -本切片不扫描目录来猜测 workspace identity。Baseline Open Bundle 通过 Git artifact owner 的 durable -receipt 与 canonical workspace authority 绑定 exact identity;未接受 Git artifact 属于 orphan GC 范畴。 - -M1 execution admission 的详细合同见 -[Managed Workspace Execution Admission v1](./runtime-managed-workspace-execution-admission-v1.zh-CN.md)。 - -## 5. Crash 与并发证明 - -| 场景 | 必须结果 | -|---|---| -| 同一 root owner 两次 open | 一个 ready;另一个 owner conflict | -| pinned Git 初始化失败 | 不发布 owner;修正 digest 后可重试 | -| operation admission 后 close | close 等待 operation;新 operation 被拒绝 | -| root owner 同时 close | root close 与 managed close 都等待同一 lease-bound operation | -| external drift 后 reopen | receipt/artifact 复验 fail closed;若发生在复验与 reopen 之间则 durable quarantine | -| post-commit artifact 复验后 root marker 被替换 | admission 时捕获的 lease identity guard 最终复验并拒绝;保留 canonical head,不发布 cwd;owner closing 只阻止新 admission,不误杀正在 drain 的操作 | -| repeated close | exact no-op,不重复释放外层 root owner | - -Git artifact create/quarantine 的进程崩溃矩阵继续由 `GitWorkspaceService` 负责;本 owner 不复制第二套 -repair 状态机。Baseline Open Bundle 将补充“startup 时先验证 canonical receipt,再按 exact binding -reopen/repair,最后才允许 baseline authority read”的组合顺序。 - -## 6. 平台能力矩阵 - -| 能力 | Linux | macOS | Windows | -|---|---|---|---| -| owner uniqueness / lifecycle | 支持 | 支持 | 支持 | -| root lease-bound operation drain | 支持 | 支持 | 支持 | -| pinned Git initialization | 支持 | 支持 | 支持 | -| external drift quarantine | 支持 | 支持 | 有限支持,沿用 Git service 的 Windows 承诺 | -| power-loss durability | 不承诺 | 不承诺 | 不承诺 | - -## 7. 明确延期 - -- Desktop、CLI、runtime-host 接线与 managed-mode 设置;execution admission API 已建立,但尚无生产 host consumer; -- filesystem worker、mutation coordinator 与工具 cwd 切换; -- candidate refs、mutation repair、GC、replication outbox; -- ignored dependencies、build/test environment provisioning; -- Durable Write、workspace-bound continuation 与自动 resume。 -- whole-root import 后既有 linked worktree 的 relocation/adoption; -- 非空 legacy database 的显式备份与 root-binding migration 工具。 - -这些能力不能借 owner lifecycle PR 顺手接入。Baseline Open Bundle 已作为本 owner 的第一个 -canonical-fact consumer 完成组合,证明 Git baseline 与 RuntimeEvent baseline 不会只成功一半;后续 -M1 execution admission 已从该 bundle 返回的 owner-bound handle 进入,未重新开放 artifact-only 旁路。 diff --git a/docs/architecture/runtime-resume-architecture.md b/docs/architecture/runtime-resume-architecture.md index 3d5754d026..32cede1e3c 100644 --- a/docs/architecture/runtime-resume-architecture.md +++ b/docs/architecture/runtime-resume-architecture.md @@ -7,7 +7,7 @@ counterpart: ./runtime-resume-architecture.zh-CN.md implementation_status: phase_0_2_and_phase_3a_authority_current document_status: current translation_status: synced -last_verified: 2026-07-28 +last_verified: 2026-08-29 owners: - maka-backend --- @@ -777,18 +777,6 @@ Layer responsibilities: The Runtime host uses strict recovery stores. It does not silently turn an unreadable ledger into best-effort fallback before admitting new writes. -### Managed workspace execution admission and read bridge (M1.1–M1.2) - -The workspace plane now has storage-owned execution admission and an owner-bound, read-only Runtime Host bridge: - -- baseline open returns an opaque handle bound to its `ManagedWorkspaceOwner`, never a raw cwd; -- every admission reproves storage-root identity, the exact Git receipt/binding/HEAD/tree/ownership, and the exact SQLite canonical head; the ordinary path performs its slow Git verification first, then makes the immutable SQLite head the final durable reread before the DB identity guard and pure in-memory comparisons; -- one admission issues one callback-scoped opaque scope with `workspaceEffect: none`; the same handle may have multiple concurrent read-only scopes; -- `close()` rejects new admissions and drains every active scope; a scope expires when its callback exits, with typed `managed_workspace_execution_scope_invalid` and `managed_workspace_execution_scope_expired` codes for forged and retained scopes; -- the crash harness may enable a preliminary-verification failpoint, but that test path must still pass the final verification before any scope is issued. - -M1.2 wires that authority into the owner-bound storage worker bridge and Runtime Host lifecycle composition. It limits managed execution to Read/Glob/Grep, demotes unchecked scope inspection to explicit test support, rejects reentrant owner close, keeps attached and managed profiles structurally distinct, and orders shutdown as tool operations → managed owner → root owner. Desktop and CLI do not enable it by default; Write/Edit/Format/Bash/unknown tools fail closed, and managed mode never silently falls back to the attached checkout. See [Managed Workspace Execution Admission v1](./runtime-managed-workspace-execution-admission-v1.zh-CN.md) for the detailed contract. - ### Eval Eval does not resume or reconstruct Runtime execution. It asks Runtime Host to execute a Maka subject. Infrastructure replacement appends a new attempt to the same experiment cell; Runtime continuation stays inside that subject and is not observable as a repetition or retry. diff --git a/docs/architecture/runtime-resume-architecture.zh-CN.md b/docs/architecture/runtime-resume-architecture.zh-CN.md index 4e8d97f03e..c0dbccaf6f 100644 --- a/docs/architecture/runtime-resume-architecture.zh-CN.md +++ b/docs/architecture/runtime-resume-architecture.zh-CN.md @@ -7,7 +7,7 @@ counterpart: ./runtime-resume-architecture.md implementation_status: phase_0_2_and_phase_3a_authority_current document_status: current translation_status: synced -last_verified: 2026-07-28 +last_verified: 2026-08-29 owners: - maka-backend --- @@ -803,18 +803,6 @@ flowchart LR Runtime host 使用 strict recovery stores 执行 startup repair。严格模式不会把 unreadable ledger 吞成 best-effort fallback,适合服务端 composition 在接收新写入前建立清楚的恢复边界。 -### Managed workspace execution admission 与只读桥(M1.1–M1.2) - -Resume 的 workspace plane 已经有 storage-owned execution admission,并已接入 owner-bound、只读的 Runtime Host bridge: - -- baseline open 只返回绑定 `ManagedWorkspaceOwner` 的 opaque handle,不公开 raw cwd; -- 每次 admission 都重新证明 storage-root identity、exact SQLite canonical head 和 exact Git receipt/binding/HEAD/tree/ownership;普通路径先完成一次慢速 Git verification,再以 immutable SQLite head 作为最后一次 durable reread,随后只执行 DB identity guard 与纯内存比较; -- 一次 admission 签发一个 callback-scoped、`workspaceEffect: none` 的 opaque scope;同一 handle 可以并发多个只读 scope; -- `close()` 拒绝新 admission,并等待所有 active scope drain;callback 退出后 scope 立即失效,伪造与过期分别返回 typed `managed_workspace_execution_scope_invalid` / `managed_workspace_execution_scope_expired`; -- crash harness 可配置 preliminary-verification failpoint,但该测试路径仍须再次通过 final verification 才能签发 scope。 - -M1.2 已完成 owner-bound storage worker bridge 与 Runtime Host lifecycle composition:只允许 Read/Glob/Grep,把无 owner 校验的 inspect 降为显式 test support,拒绝 active callback 内 reentrant `owner.close()`,用不可混淆的 attached/managed typed profile 阻止 silent fallback,并固定 tool operations → managed owner → root owner 的关闭顺序。Desktop/CLI 仍不默认开启 managed execution;Write/Edit/Format/Bash/未知工具必须 fail closed。详细合同见 [Managed Workspace Execution Admission v1](./runtime-managed-workspace-execution-admission-v1.zh-CN.md)。 - ### Eval Eval 不恢复或重建 Runtime execution,只请求 Runtime Host 执行 Maka subject。基础设施替换会向同一个 experiment cell 追加新 attempt;Runtime continuation 留在该 subject 内部,不成为 repetition 或 retry。 diff --git a/docs/architecture/runtime-resume-extraction-ledger.zh-CN.md b/docs/architecture/runtime-resume-extraction-ledger.zh-CN.md index c0da7e8c3e..de04bc6a98 100644 --- a/docs/architecture/runtime-resume-extraction-ledger.zh-CN.md +++ b/docs/architecture/runtime-resume-extraction-ledger.zh-CN.md @@ -468,7 +468,7 @@ Schema 9 进一步要求该 authority stream 在首次写入前绑定 authentica - SQLite/JSONL/tool/recovery/continuation/copy writer bypass; - 两进程 exact/conflicting baseline arbitration; - 两进程 schema 6/7→8 migration; -- DB rootId exact binding、跨 root 单文件复制拒绝、whole-root storage authority import/adopt 与 unbound operational data fail closed;既有 managed worktree relocation 和 legacy DB binding 仍是独立维护协议,不由 M0 baseline open 暗中完成; +- DB rootId exact binding、跨 root 单文件复制拒绝、whole-root storage authority import/adopt 与 unbound operational data fail closed;legacy DB binding 仍是独立维护协议;旧 baseline-open producer 已删除,后续 workspace artifact admission 由 Gitoxide data plane 单独建立; - Linux/macOS process-kill crash harness; - workspace fact 不进入 UI/provider message projection。 diff --git a/docs/architecture/runtime-resume-phase3-phase4-workspace-checkpoint-design.zh-CN.md b/docs/architecture/runtime-resume-phase3-phase4-workspace-checkpoint-design.zh-CN.md index aecb8def28..86b726c66e 100644 --- a/docs/architecture/runtime-resume-phase3-phase4-workspace-checkpoint-design.zh-CN.md +++ b/docs/architecture/runtime-resume-phase3-phase4-workspace-checkpoint-design.zh-CN.md @@ -19,14 +19,14 @@ # Runtime Resume Phase 3–4 实施路线 -> 路线更新(2026-08-02):workspace 主线已从“通用 checkpoint 抽象优先”切换为 -> **Git-native managed workspace**。本文保留 Recovery/Continuation 已落地协议与旧路线的历史论证; -> 新实现不再以通用 per-file checkpoint、native manifest 或 CAS object store 为前置。 -> 当前 M0 的 baseline-only 权威合同见 +> 路线更新(2026-08-29):旧 Git executable managed-workspace path 已删除,后续 workspace 主线从 +> **Gitoxide data plane** 重新建立 production admission。本文保留 Recovery/Continuation 已落地协议与旧路线的历史论证; +> 新实现不再以通用 per-file checkpoint、native manifest、CAS object store 或旧 Git CLI owner 为前置。 +> 已保留的 baseline authority 合同见 > [Workspace Version Authority v1](./runtime-workspace-version-authority-v1.zh-CN.md)。 - 状态:Implementation tracked -- 更新日期:2026-08-02 +- 更新日期:2026-08-29 - 事实权威:immutable RuntimeEvents - 主要平台:Linux、macOS;Windows 有限支持 - 拆分审计:`runtime-resume-extraction-ledger.zh-CN.md` @@ -47,7 +47,7 @@ RuntimeEvent 是语义事实的唯一权威,但不能替代执行所有权的 | recovery semantics | immutable RuntimeEvents | call、dispatch、outcome、observation、decision | | execution ownership | admission/claim CAS | 一个 source boundary 只能有一个执行者 | | projection | SQLite tables | 可删除、可从事件重建 | -| workspace artifact | managed Git artifact owner(强模式) | 保存/验证 workspace 状态,不能单独授权 continuation | +| workspace artifact | 当前无生产 writer;后续 Gitoxide data plane | 保存/验证 workspace 状态,不能单独授权 continuation | ## 2. Phase 3A:工具恢复 @@ -539,95 +539,23 @@ Attached 模式继续服务现有用户目录与兼容场景,但不冒充强 w - 旧的 generic checkpoint provider、per-file carrier 与 observe-only user-repository Git 路线只保留为 attached/legacy research,不再是 `managed_worktree` 的实现前置。 -### Git-native managed workspace +### Gitoxide managed workspace -强模式以 Maka-owned repository/worktree 为 artifact carrier。Git 证明文件世界,SQLite immutable -RuntimeEvents 决定 Maka 是否接受某个文件版本;Git commit、ref、receipt 与 projection 都不是第二套真相。 -内置并校验的 Git CLI 是该模式的运行能力,不要求用户自行安装 Git,也不修改用户 repository 的 index/ref。 +旧 Git executable-backed managed workspace 从未获得生产 baseline/profile 调用方,其 service、owner、 +receipt、worktree materialization 与 Runtime Host admission path 已删除。schema 9 RuntimeEvents、workspace +projection reader/rebuild 与升级合同继续保留;它们是历史事实 authority,不是可执行 Git path。 -## 4. Git-native managed workspace 演进 +## 4. Gitoxide managed workspace 后续边界 -### M0 — Baseline admission(已合并) +后续强模式必须从 Gitoxide data plane 建立新的 production composition,并分别证明: -M0 只证明一条接受链: +- packaged helper 的 trust root、artifact identity 与 bounded invocation; +- repository admission 与 schema 9 workspace authority 的单一写入边界; +- mutation T1/T2、workspace version 与 tool outcome 的原子接受; +- workspace-bound continuation、restore、rebaseline 与 product workflow。 -```text -authenticated storage-root owner -→ create/reopen Maka-owned managed Git workspace -→ durable exact receipt -→ SQLite atomic epoch + baseline acceptance -→ post-commit DB/Git/root-owner revalidation -→ publish usable baseline -``` - -M0 不接 Desktop、CLI、runtime host 或工具执行。Whole-root import 当前只恢复 storage-root identity 与 -SQLite ownership;既有 linked worktree 的路径迁移需要独立 relocation/rematerialization 协议。非空且未绑定的 -legacy DB 继续 fail closed,未来由显式、备份后、可审计的 root-binding maintenance flow 处理,不能藏在首次 -baseline open 中。 - -### M1 — Execution admission 与环境可用性 - -只允许 host 从 M0 成功返回的 accepted handle 取得工具 cwd,并证明: - -- managed owner 与 runtime host 的启动、drain、关闭顺序; -- 每次执行前 exact instance/HEAD/tree/ownership revalidation; -- ignored dependencies、`.env` 与 build scratch 使用明确 provisioning/overlay policy,不污染 canonical tree; -- 外部修改 Maka-owned worktree 时检测 drift、fail closed 并 quarantine; -- attached 与 managed execution profile 在类型和配置上不可静默互相 fallback。 - -M1 拆成独立不变量,避免一次 PR 同时跨越 host lifecycle、runtime protocol 与 platform I/O: - -1. **M1.1 execution scope admission(已合并)**:M0 只返回 owner-bound opaque handle;同一 owner 每次 - execution 都在 drain residency 内重新证明 canonical head、Git receipt、HEAD/tree/ownership 与 root - identity,最后只签发 callback 生命周期内有效的 opaque scope,不公开 raw cwd。provisioning 固定为 - `canonical_tree_only_v1`,`workspaceEffect` 固定为 `none`。同一 handle 可以并发多个只读 admission, - `close()` 必须等待全部 active scope drain;普通 production admission 先做一次 exact Git verification,再以 - SQLite head 作为最后一次 durable reread;preliminary verification 仅存在于配置 crash failpoint 的 - production-shaped 测试路径;过期或伪造 scope 通过 typed - `ManagedWorkspaceExecutionAuthorityError` 的稳定 code fail closed; -2. **M1.2 owner-bound worker 与 runtime-host composition(已合并)**:storage-internal bridge 只能用同 owner 的 active - scope 解析 cwd,M2 前只允许 Read/Glob/Grep;建立不可混淆的 managed/attached typed profile 与 - startup/drain/shutdown 顺序,关闭顺序固定为 tool operations → managed owner → root owner; -3. **M1.3 environment provisioning**:单独设计 ignored dependency、secret 与 scratch overlay。M1.1 不复制 - `.env`、`node_modules` 或 build output,也不以 attached checkout silent fallback 掩盖能力缺失。 - -M1.1 合同见 -[Managed Workspace Execution Admission v1](./runtime-managed-workspace-execution-admission-v1.zh-CN.md)。 - -### M2 — Mutation version acceptance - -每个 mutating tool 使用 candidate ref/version 协议: - -1. T1 前冻结 execution profile、base workspace version 与 mutation identity; -2. 工具只在 owned worktree 中执行; -3. Git artifact owner 捕获并验证 candidate commit/tree; -4. SQLite 在一个 authority transaction 中接受 tool outcome 与 successor workspace version; -5. artifact orphan 可回收;canonical fact 已接受而 artifact 缺失时 fail closed; -6. crash matrix 覆盖执行前后、candidate durable 前后、SQLite commit 前后与返回前复验。 - -这一步取代旧的“先做通用 checkpoint contract,再接 observe-only Git carrier”。不得同时维护两套 -managed workspace version writer。 - -### M3 — Workspace-bound continuation / resume - -Continuation boundary 同时绑定 immutable RuntimeEvent cursor 与 accepted workspace version/epoch: - -- planner 与 execution revalidation 读取同一 canonical boundary; -- startup reopen 必须重新证明 exact Git artifact、storage root 与 owner; -- operation effect 无法证明、artifact 漂移或 execution profile 不匹配时 park; -- 自动 resume 仍由独立用户设置控制,默认关闭;手动恢复与自动恢复不能共享一个含糊开关。 - -### M4 — Product workflows - -在 M0–M3 的事实与 owner 边界稳定后,再分别交付: - -- isolated restore,不覆盖用户当前目录; -- explicit rebaseline,创建新 epoch 并要求模型重新读取; -- publish/merge/undo、retention 与 orphan GC; -- whole-root managed-workspace relocation; -- replication/cross-device 与 multi-agent merge。 - -每项都必须是独立 PR;不能借“恢复产品化”一次跨越 schema、runtime protocol、host lifecycle 与 platform I/O。 +不能恢复旧 Git CLI、receipt、quarantine、write owner 或 `managed_worktree_v1` profile,也不能让 Gitoxide +foundation 在尚无 Desktop/CLI/T1 consumer 时冒充产品能力。 ## 5. 依赖顺序 @@ -635,25 +563,15 @@ Continuation boundary 同时绑定 immutable RuntimeEvent cursor 与 accepted wo PR A recovery persistence authority (merged) └─> PR B continuation correctness (merged) -Git-native workspace: -M0.1 Git artifact owner (merged) - + M0.2 workspace version authority (merged) - + M0.3 managed owner lifecycle (merged) - └─> M0.4 baseline open bundle (merged) - └─> M1.1 execution scope admission (merged) - └─> M1.2 runtime-host composition (merged) - └─> M1.3 explicit environment provisioning - └─> M2 mutation version acceptance - └─> M3 workspace-bound continuation - └─> M4 restore / rebaseline / publish / replication - -Independent maintenance gates before broad production enablement: - - legacy non-empty DB root-binding adoption - - whole-root managed-workspace relocation/rematerialization +Gitoxide foundation/data plane +└─> production artifact admission + └─> schema 9 workspace authority writer composition + └─> mutation acceptance + └─> workspace-bound continuation and product workflows ``` -旧 PR C–H 的论证可作为历史材料,但不再代表 `managed_worktree` 的施工依赖图;其中不具备生产消费者的 -generic checkpoint、observe-only carrier 与预设抽象不得迁入新主线。 +旧 PR C–H 与 Git-CLI managed-workspace 路线只作为历史论证;其中没有真实消费者的 checkpoint、carrier、 +receipt 与 owner 抽象不得迁入新主线。 ## 6. 工程门槛 diff --git a/docs/architecture/runtime-workspace-version-authority-v1.zh-CN.md b/docs/architecture/runtime-workspace-version-authority-v1.zh-CN.md index 5fd0d508e1..3757c4cfce 100644 --- a/docs/architecture/runtime-workspace-version-authority-v1.zh-CN.md +++ b/docs/architecture/runtime-workspace-version-authority-v1.zh-CN.md @@ -19,12 +19,11 @@ # Workspace Version Authority v1:Baseline 事实权威 -- 状态:authority foundation 已由 M0 Baseline Open Bundle 接入;仍待前置 PR 合并后从最新 `main` - 平铺并完成最终 CI -- 更新日期:2026-08-02 +- 状态:authority foundation 已合并;schema 9 reader/migration、RuntimeEvents、projection reader/rebuild 继续受支持,当前没有生产 baseline writer consumer +- 更新日期:2026-08-29 - 主要不变量:经专用 writer 提交的一个 workspace epoch,其 baseline canonical facts 与三个 SQLite projection 对外只能全可见或全不可见 - 事实权威:immutable RuntimeEvents -- artifact owner:后续 `GitWorkspaceService`;本切片不执行 Git 命令 +- artifact owner:后续 Gitoxide data plane;本切片不执行 Git 命令 - 主要发布证明平台:Linux、macOS;Windows 当前仅验证 SQLite 事务与多进程路径 ## 1. 本切片为什么存在 @@ -48,21 +47,20 @@ epoch opened RuntimeEvent 可删除、可重建、每次读取都要与 canonical facts 交叉验证的 projection。若维护操作或外部损坏只删除 projection,公开 reader 会 fail closed,直到显式 rebuild;这种损坏态不被伪装成合法的“全不可见”。 -本切片只接受 baseline,不接受 mutation。它不创建 internal repository、不创建 worktree、不调用工具, -也不改变 Desktop/CLI 行为。这种收缩是刻意的:一个 PR 只证明一个主要不变量。 +本节定义的 baseline transaction 只接受 baseline;独立的 successor/mutation authority extension 不属于这个 +历史切片。它不创建 internal repository、不创建 worktree、不调用工具,也不改变 Desktop/CLI 行为。 -authority slice 本身只证明事实合同、SQLite 原子写入与 projection 可重建性;M0 Baseline Open Bundle -在其上增加了生产 composition seam:`ManagedWorkspaceOwner` 持有未导出的 Git receipt capability, -先持久化并复验 exact receipt,再调用 storage-internal raw writer,SQLite COMMIT 后还会再次复验 Git -artifact。裸 OID、TypeScript brand、caller 自报的 `verified: true` 或 caller 提供的 policy hash 都不 -构成证据。 +authority slice 本身只证明事实合同、SQLite 原子写入与 projection 可重建性。旧 Git-CLI-shaped +Baseline Open composition 从未获得生产调用方,现已删除;后续生产 writer 必须由 Gitoxide data plane +建立新的窄 admission 边界。裸 OID、TypeScript brand、caller 自报的 `verified: true` 或 caller 提供的 +policy hash 都不构成证据。 ## 2. Owner、边界、失败状态与回滚 | 项目 | 决策 | |---|---| | 协议 owner | `@maka/core` 的 strict fact contract 与 pure scanner | -| 写入 owner | storage-internal WeakMap writer;不属于 package API,只能由 `ManagedWorkspaceOwner` composition seam 到达 | +| baseline 写入 owner | storage-internal WeakMap writer;不属于 package API,当前没有 production baseline producer;独立 successor/mutation writer 不由旧 owner 提供 | | 原子性边界 | 单个 `BEGIN IMMEDIATE ... COMMIT` SQLite transaction | | canonical source | store-owned authority stream 中的两条 immutable RuntimeEvents | | disposable state | `runtime_workspace_epochs`、`runtime_workspace_versions`、`runtime_workspace_heads` | @@ -192,45 +190,15 @@ Writer reservation 同时覆盖: storage 内部 writer 接受 typed baseline input,由 store 自己构造 RuntimeEvents。它不接受 caller 拼好的 event,因此 caller 没有机会夹带另一条 semantic lane。该 seam 所在模块不从 `@maka/storage` 导出; -公开 store 只暴露 capability、reader 与 projection rebuild。当前唯一生产入口由 -`ManagedWorkspaceOwner` 重新验证 durable Git receipt 后调用 raw seam;receipt issuer 与 raw writer -都不进入 package root exports。 - -## 6. Baseline Open Bundle 时序 - -```mermaid -sequenceDiagram - participant G as GitWorkspaceService - participant O as ManagedWorkspaceOwner - participant S as SqliteRuntimeStore - participant E as immutable RuntimeEvents - participant P as workspace projections - - G->>G: 验证 source repository、commit/tree、fixed materialization profile - G->>G: 导入并验证 internal baseline commit/tree - G->>O: verified durable receipt (internal capability) - O->>S: commitWorkspaceBaselineInternal(receipt-derived input) - S->>S: BEGIN IMMEDIATE - S->>S: 扫描全部 canonical workspace facts - alt exact bundle 已存在 - S-->>O: created=false - else identity 或 payload 冲突 - S-->>O: fail closed / rollback - else authority 为空 - S->>E: append epoch event (seq=1) - S->>E: append baseline event (seq=2) - S->>P: insert epoch - S->>P: insert version - S->>P: set head=baseline - S->>S: COMMIT - S-->>O: created=true - end -``` +公开 store 只暴露 capability、reader 与 projection rebuild。baseline raw writer 不进入 package root exports, +当前没有生产 composition 可以调用它。 + +## 6. Baseline writer 状态 -Git 验证与 tree-delta 计算故意不放进 SQLite transaction。Baseline Open Bundle 在调用 writer 前读取 -并重新验证 durable receipt,COMMIT 后再次复验;SQLite writer 负责冻结已验证 identity,并保证 -facts/projections 原子提交。该 composition 仍未接 Desktop、CLI、tool 或自动恢复入口。 -`treeDeltaDigest` 必须是 canonical empty-tree → baseline-tree delta 的摘要,不能由 caller 随意填写。 +`commitWorkspaceBaselineInternal` 继续作为 schema 9 authority 的 storage-internal 测试 seam,用于证明 +atomic bundle、exact retry、conflict、crash rollback 与 projection rebuild。旧 Git executable receipt → +writer composition 已删除;后续 Gitoxide producer 必须重新建立 artifact admission,不能复用或恢复旧 +owner、receipt 或 worktree materialization path。 ## 7. Schema 7–8 与 projection @@ -307,9 +275,9 @@ SQLite read transaction/snapshot;否则并发 writer 可能让读者拼接两 本表只描述 authority persistence,不能推导 managed worktree 已跨平台可用。 -## 10. 明确延期 +## 10. Baseline slice 之外的扩展 -以下能力不属于 M0 baseline authority: +以下能力不由本文的 baseline authority slice 定义;它们可以由独立 authority extension 提供: - verified Git runtime 探测、source eligibility 与 internal bare repository; - managed worktree 创建、owner lifecycle、quarantine 与 repair; @@ -321,17 +289,13 @@ SQLite read transaction/snapshot;否则并发 writer 可能让读者拼接两 - continuation boundary 绑定 workspace version; - Desktop/CLI 设置、默认启用或自动恢复。 -尤其不能在本协议中提前定义无生产消费者的 mutation 抽象。Mutation fact 必须与真实 Durable Write -的 T1/T2、tool outcome 引用、session retention 和 head CAS 一起冻结。 - -## 11. 后续 M0 交付顺序 +本文不定义 mutation。Successor/mutation fact 必须由其独立 authority 与真实 Durable Write 的 T1/T2、 +tool outcome 引用、session retention 和 head CAS 一起冻结。 -### Slice 2:Managed Workspace Owner(已完成) +## 11. 后续生产接线 -只证明:Maka 能用一个显式注入且经过校验的 Git runtime 创建并独占 private internal -repository/worktree lifecycle;外部 drift 被检测后 quarantine。ASF Desktop 不再提供该 runtime,后续实现将 -验证 Apache-2.0/MIT 的 gitoxide backend。旧 Git-CLI-shaped service 仅作为历史/测试实现,不能成为 -新生产 backend 的 identity owner。 +旧 Git-CLI-shaped service、owner、receipt 与 worktree materialization path 已删除,不能成为新生产 +backend 的 identity owner。 当前 Gitoxide 验证栈已拆成三个窄、可独立合并的 enabling-infrastructure 层:isolated short-lived Rust helper、exact helper artifact → opaque invocation capability、bounded invocation → opaque repository admission capability。分别见: @@ -342,29 +306,6 @@ opaque invocation capability、bounded invocation → opaque repository admissio - [`gitoxide-repository-admission-capability-v1.zh-CN.md`](./gitoxide-repository-admission-capability-v1.zh-CN.md) 这些基础设施尚未建立 signed packaged-release trust root,也没有 Desktop/CLI/T1 消费者,因此不能据此 -恢复 managed mode。后续生产接线仍需先拍板 ignored dependencies/scratch、identity marker、 -symlink/LFS/submodule/case/filemode 平台政策。 - -### Slice 3:Baseline Open Bundle(实现中) - -只证明:从 eligible clean source HEAD 导入 Maka-owned objects、materialize baseline、验证 tree/cleanliness, -然后调用本切片的 atomic authority writer。Git artifact 在失败时可作为 orphan GC;只有 RuntimeEvent -接受后的 commit 才是 canonical workspace version。 - -完成这两片以后,系统才拥有一个可供工具使用的 managed baseline。真正的 Durable Write 与 -workspace-version/T2 原子接受仍是下一组独立 PR,不能在 host 接线中顺手补入。 - -Baseline Open 的详细 receipt 合同、组合 owner、crash matrix 与平台承诺见 -[`runtime-managed-workspace-baseline-open-v1.zh-CN.md`](./runtime-managed-workspace-baseline-open-v1.zh-CN.md)。 - -## 12. 当前验收记录 - -本分支完成的短门禁: - -- core/storage 定向集合:93/93; -- 其中 workspace authority:3 个 pure contract + 12 个 persistence + 4 个新增多进程/升级场景; -- runtime read-model 定向集合:59/59; -- `@maka/storage` 与 `@maka/runtime` build 通过; -- `git diff --check` 通过。 - -未运行全仓测试;Windows 本地没有执行被 suite 明确跳过的 Unix process-kill crash proof。 +恢复 managed mode。后续 production writer 必须从真实 Gitoxide artifact admission 出发,并继续复用本 +文档定义的 schema 9 RuntimeEvents 与 projection authority;接线前仍需先拍板 ignored dependencies/scratch、 +identity marker、symlink/LFS/submodule/case/filemode 平台政策。 diff --git a/docs/windows-support.md b/docs/windows-support.md index 5d72aae23f..07865a1436 100644 --- a/docs/windows-support.md +++ b/docs/windows-support.md @@ -185,9 +185,8 @@ parent-directory update through volatile device caches. | Surface | Current Windows guarantee | Boundary | |---|---|---| -| SQLite operational state | Databases use WAL journaling with `synchronous=FULL`. Real-process failpoint tests verify that committed runtime, continuation, memory, and managed-workspace facts survive owner death while incomplete transactions do not become authoritative. | The guarantee is the SQLite and Windows filesystem contract on supported local storage. Maka does not claim protection from storage hardware or drivers that acknowledge flushes before data is stable. | +| SQLite operational state | Databases use WAL journaling with `synchronous=FULL`. Real-process failpoint tests verify that committed runtime, continuation, and memory facts survive owner death while incomplete transactions do not become authoritative. Workspace RuntimeEvents and projections retain their schema 9 reader/rebuild contract. | The guarantee is the SQLite and Windows filesystem contract on supported local storage. Maka does not claim protection from storage hardware or drivers that acknowledge flushes before data is stable. | | Artifact payload publication | Payload bytes are written to a same-directory staging file and the file is synchronized before publication. Recovery reconciles staged payloads, metadata, deletes, and owner death without accepting uncommitted residue. | Windows evidence covers forced process termination and restart. It does not establish a separately forced parent-directory entry after sudden system power loss. | -| Marker and managed-workspace control files | Writers synchronize temporary file contents before same-directory `link` or `rename` publication. Readers validate file and root identity and fail closed on unsupported replacement. | Node can synchronize the file on Windows, but the repository's directory synchronization barrier is intentionally unavailable on Windows. | | Root and open-database replacement | Live owners retain exclusive authority and cleanup closes stores and leases before deleting their roots. | Windows does not permit the POSIX test technique of renaming or unlinking an open SQLite database or replacing a directory that contains open files. Those tests remain classified as platform contracts rather than portable recovery gates. | On POSIX, stable-storage paths synchronize changed parent directories after publishing or removing a @@ -206,7 +205,6 @@ Environment: Windows 11 x64, Node.js 22.23.1, npm 11, Git for Windows. |---|---:|---| | Workspace build | PASS | All root `build:test` workspace builds completed. | | Repository script tests | 110 pass, 0 fail, 1 skip | The skip is a real macOS `pgrep` probe. | -| Managed workspace baseline tests | 17 pass, 0 fail, 5 skip | Passed after enabling Git for Windows long paths. | | Storage suite | 514 pass, 100 fail, 40 skip | Failures are dominated by `EBUSY` cleanup while SQLite files remain open. | | Complete repository test plan | TIMEOUT | `npm test` did not exit within 10 minutes and left the workspace test runner alive. | diff --git a/docs/windows-test-inventory.md b/docs/windows-test-inventory.md index 53ffc8563a..c218eb71b2 100644 --- a/docs/windows-test-inventory.md +++ b/docs/windows-test-inventory.md @@ -17,9 +17,9 @@ Locations intentionally omit line numbers so unrelated edits do not invalidate t |---|---:| | windows-backend-gap | 25 | | portable-candidate | 10 | -| platform-contract | 35 | +| platform-contract | 31 | -Total Windows-excluded declarations: **70** +Total Windows-excluded declarations: **66** ## Inventory @@ -74,10 +74,6 @@ Total Windows-excluded declarations: **70** | platform-contract | `packages/runtime/src/__tests__/shell-run-manager.test.ts` keeps SIGTERM final output and escalates an ignored SIGTERM without leaking slots | `process.platform === 'win32' ? 'Windows tree termination has no graceful SIGTERM phase' : false` | | portable-candidate | `packages/storage/src/__tests__/managed-dependency-environment.test.ts` accepts a POSIX package bin symlink whose target remains inside the dependency root | `process.platform === 'win32'` | | portable-candidate | `packages/storage/src/__tests__/managed-dependency-environment.test.ts` isolates published POSIX content from a producer-retained writable handle | `process.platform === 'win32'` | -| platform-contract | `packages/storage/src/__tests__/managed-workspace-baseline.test.ts` rejects an authority database whose file identity changes after registration | `process.platform === 'win32' ? 'Windows cannot rename an open SQLite database to replace its file identity' : false` | -| platform-contract | `packages/storage/src/__tests__/managed-workspace-baseline.test.ts` does not return an accepted baseline when runtime.sqlite is replaced after the initial root check | `process.platform === 'win32' ? 'Windows cannot rename an open SQLite database during the verification race' : false` | -| platform-contract | `packages/storage/src/__tests__/managed-workspace-baseline.test.ts` rejects a source tree containing a non-UTF-8 Git path | `process.platform === 'win32'` | -| platform-contract | `packages/storage/src/__tests__/managed-workspace-owner.test.ts` rejects execution when runtime.sqlite detaches from its canonical path after verification | `process.platform === 'win32' ? 'Open SQLite files cannot be renamed reliably on Windows' : false` | | platform-contract | `packages/storage/src/__tests__/operational-state-store.test.ts` does not classify a SQLite write failure as a migration blocker | `process.platform === 'win32' ? 'POSIX permissions are required to make the SQLite database read-only' : false` | | platform-contract | `packages/storage/src/__tests__/pet-pack-store.test.ts` detects sprite sheets redirected outside the installed pack | `process.platform === 'win32' ? 'Windows file-symlink permissions are not guaranteed in CI' : false` | | platform-contract | `packages/storage/src/__tests__/root-authority.test.ts` preserves unexpected marker I/O failures at the public authority boundary | `process.platform === 'win32' ? 'POSIX permissions are required to make the marker unreadable' : typeof process.getuid === 'function' && process.getuid() === 0` | diff --git a/packages/runtime-host/src/__tests__/execution-composition.test.ts b/packages/runtime-host/src/__tests__/execution-composition.test.ts index ad3b9db508..12c83148e5 100644 --- a/packages/runtime-host/src/__tests__/execution-composition.test.ts +++ b/packages/runtime-host/src/__tests__/execution-composition.test.ts @@ -19,7 +19,6 @@ import assert from 'node:assert/strict'; import { parseNoRealConnectionError } from '@maka/core/connection-error-copy'; -import { execFile } from 'node:child_process'; import { createRequire } from 'node:module'; import { mkdir, mkdtemp, rm, stat, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; @@ -55,10 +54,6 @@ import { createExecutionRuntimeHostComposition, runtimeHostFilesystemWorkerRuntime, } from '../server/execution-composition.js'; -import { - createManagedWorkspaceExecutionProfile, - RuntimeHostWorkspaceExecutionError, -} from '../server/workspace-execution-composition.js'; const require = createRequire(import.meta.url); const FAKE_CONNECTION_ID = 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb'; @@ -68,34 +63,6 @@ test('filesystem worker follows the candidate executable runtime', () => { assert.equal(runtimeHostFilesystemWorkerRuntime({}), 'node'); }); -test('production composition never discovers PATH Git when no runtime is admitted', async () => { - await new Promise((resolve, reject) => { - execFile('git', ['--version'], (error) => (error ? reject(error) : resolve())); - }); - - await withCompositionRoot(async ({ owner }) => { - const composition = await createExecutionRuntimeHostComposition(compositionContext(owner)); - try { - assert.equal(composition.workspaceExecution.state, 'ready'); - const managed = createManagedWorkspaceExecutionProfile( - Object.freeze({ kind: 'managed_workspace_execution_handle_v1' }), - ); - await assert.rejects( - () => - composition.workspaceExecution.executeReadOnly(managed, { - kind: 'read', - path: 'README.md', - }), - (error) => - error instanceof RuntimeHostWorkspaceExecutionError && - error.code === 'managed_workspace_profile_unavailable', - ); - } finally { - await composition.close(); - } - }); -}); - test('production composition owns the long-term memory database lifecycle', async () => { await withCompositionRoot(async ({ root, owner }) => { const databasePath = join(root, LONG_TERM_MEMORY_DATABASE_NAME); diff --git a/packages/runtime-host/src/__tests__/workspace-execution-composition.test.ts b/packages/runtime-host/src/__tests__/workspace-execution-composition.test.ts index a9e958f016..a94dea9c6f 100644 --- a/packages/runtime-host/src/__tests__/workspace-execution-composition.test.ts +++ b/packages/runtime-host/src/__tests__/workspace-execution-composition.test.ts @@ -19,74 +19,33 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import type { - ManagedWorkspaceExecutionHandle, - ManagedWorkspaceExecutionScope, - ManagedWorkspaceOwner, -} from '@maka/storage/managed-workspace-owner'; import { createAttachedWorkspaceExecutionProfile, - createManagedWorkspaceExecutionProfile, createRuntimeHostWorkspaceExecutionComposition, RuntimeHostWorkspaceExecutionError, } from '../server/workspace-execution-composition.js'; -test('keeps attached and managed execution profiles explicit', async () => { +test('executes read-only operations in the attached checkout', async () => { const calls: string[] = []; - const handle = Object.freeze({ kind: 'managed_workspace_execution_handle_v1' as const }); - const scope = Object.freeze({ kind: 'managed_workspace_execution_scope_v1' as const }); - const managedOwner = fakeManagedOwner({ handle, scope, calls }); const composition = createRuntimeHostWorkspaceExecutionComposition({ filesystemWorker: { async execute(input) { - calls.push(`worker:${input.cwd}:${input.operation.kind}`); + calls.push(`${input.cwd}:${input.operation.kind}`); return { kind: 'read', content: 'attached' }; }, }, - managedOwner, }); - const attached = createAttachedWorkspaceExecutionProfile('/attached'); + const profile = createAttachedWorkspaceExecutionProfile('/attached'); assert.deepEqual( - await composition.executeReadOnly(attached, { kind: 'read', path: 'README.md' }), + await composition.executeReadOnly(profile, { kind: 'read', path: 'README.md' }), { kind: 'read', content: 'attached' }, ); - - const managed = createManagedWorkspaceExecutionProfile(handle); - assert.deepEqual( - await composition.executeReadOnly(managed, { kind: 'read', path: 'README.md' }), - { kind: 'read', content: 'managed' }, - ); - assert.deepEqual(calls, ['worker:/attached:read', 'managed:admit', 'managed:read']); - + assert.deepEqual(calls, ['/attached:read']); await composition.close(); }); -test('never falls back a managed profile to attached execution', async () => { - let workerCalls = 0; - const composition = createRuntimeHostWorkspaceExecutionComposition({ - filesystemWorker: { - async execute() { - workerCalls += 1; - return { kind: 'read', content: 'unsafe fallback' }; - }, - }, - }); - const managed = createManagedWorkspaceExecutionProfile( - Object.freeze({ kind: 'managed_workspace_execution_handle_v1' }), - ); - - await assert.rejects( - () => composition.executeReadOnly(managed, { kind: 'read', path: 'README.md' }), - (error) => - error instanceof RuntimeHostWorkspaceExecutionError && - error.code === 'managed_workspace_profile_unavailable', - ); - assert.equal(workerCalls, 0); - await composition.close(); -}); - -test('rejects forged or malformed profiles before worker dispatch', async () => { +test('rejects malformed profiles and mutating operations before worker dispatch', async () => { let workerCalls = 0; const composition = createRuntimeHostWorkspaceExecutionComposition({ filesystemWorker: { @@ -102,34 +61,43 @@ test('rejects forged or malformed profiles before worker dispatch', async () => { kind: 'attached_checkout_v1', cwd: '' }, ]) { await assert.rejects( - () => - composition.executeReadOnly(profile as never, { - kind: 'read', - path: 'README.md', - }), + () => composition.executeReadOnly(profile as never, { kind: 'read', path: 'README.md' }), (error) => error instanceof RuntimeHostWorkspaceExecutionError && error.code === 'workspace_operation_denied', ); } + await assert.rejects( + () => + composition.executeReadOnly(createAttachedWorkspaceExecutionProfile('/attached'), { + kind: 'write', + path: 'unsafe.txt', + } as never), + (error) => + error instanceof RuntimeHostWorkspaceExecutionError && + error.code === 'workspace_operation_denied', + ); assert.equal(workerCalls, 0); await composition.close(); }); -test('drains tool operations before closing the managed owner', async () => { - const calls: string[] = []; +test('drains active attached operations before closing', async () => { let releaseWorker!: () => void; const workerBlocked = new Promise((resolve) => { releaseWorker = resolve; }); - const handle = Object.freeze({ kind: 'managed_workspace_execution_handle_v1' as const }); - const scope = Object.freeze({ kind: 'managed_workspace_execution_scope_v1' as const }); - const managedOwner = fakeManagedOwner({ handle, scope, calls, workerBlocked }); - const composition = createRuntimeHostWorkspaceExecutionComposition({ managedOwner }); - const profile = createManagedWorkspaceExecutionProfile(handle); + const composition = createRuntimeHostWorkspaceExecutionComposition({ + filesystemWorker: { + async execute() { + await workerBlocked; + return { kind: 'read', content: 'attached' }; + }, + }, + }); + const profile = createAttachedWorkspaceExecutionProfile('/attached'); const executing = composition.executeReadOnly(profile, { kind: 'read', path: 'README.md' }); - await waitFor(() => calls.includes('managed:read')); + await new Promise((resolve) => setImmediate(resolve)); const closing = composition.close(); await assert.rejects( () => composition.executeReadOnly(profile, { kind: 'read', path: 'other.md' }), @@ -137,46 +105,10 @@ test('drains tool operations before closing the managed owner', async () => { error instanceof RuntimeHostWorkspaceExecutionError && error.code === 'workspace_execution_draining', ); - assert.equal(calls.includes('managed:close'), false); + assert.equal(composition.state, 'draining'); releaseWorker(); await executing; await closing; - assert.deepEqual(calls, ['managed:admit', 'managed:read', 'managed:close']); + assert.equal(composition.state, 'closed'); }); - -function fakeManagedOwner(input: { - handle: ManagedWorkspaceExecutionHandle; - scope: ManagedWorkspaceExecutionScope; - calls: string[]; - workerBlocked?: Promise; -}): ManagedWorkspaceOwner { - return { - state: 'ready', - async openManagedWorkspaceBaseline() { - throw new Error('not used'); - }, - async withManagedWorkspaceExecution(handle, operation) { - assert.equal(handle, input.handle); - input.calls.push('managed:admit'); - return await operation(input.scope); - }, - async executeReadOnlyFilesystemOperation(scope) { - assert.equal(scope, input.scope); - input.calls.push('managed:read'); - await input.workerBlocked; - return { kind: 'read', content: 'managed' }; - }, - async close() { - input.calls.push('managed:close'); - }, - }; -} - -async function waitFor(predicate: () => boolean): Promise { - const deadline = Date.now() + 1_000; - while (!predicate()) { - if (Date.now() >= deadline) throw new Error('condition was not reached'); - await new Promise((resolve) => setTimeout(resolve, 1)); - } -} diff --git a/packages/runtime-host/src/server/execution-composition.ts b/packages/runtime-host/src/server/execution-composition.ts index bbfd74ac82..2468419635 100644 --- a/packages/runtime-host/src/server/execution-composition.ts +++ b/packages/runtime-host/src/server/execution-composition.ts @@ -86,7 +86,6 @@ import { runWithStorageRootLease } from '@maka/storage/root-authority'; import { openStorageWriterComposition } from '@maka/storage/storage-writer-composition'; import type { RuntimePolicyStoresWriter } from '@maka/storage/runtime-policy-stores'; import { resolveWorkspaceIdentity } from '@maka/storage/workspace-identity'; -import { type ManagedWorkspaceFilesystemWorker } from '@maka/storage/managed-workspace-owner'; import { CanonicalSessionProjectionReader } from './canonical-session-projection.js'; import { bindHostChildAgentBackend, @@ -190,6 +189,7 @@ import { createRuntimeHostWorkspaceExecutionComposition, RuntimeHostWorkspaceExecutionError, type RuntimeHostWorkspaceExecutionComposition, + type RuntimeHostWorkspaceFilesystemWorker, } from './workspace-execution-composition.js'; export interface ExecutionRuntimeHostComposition extends RuntimeHostComposition { @@ -328,11 +328,11 @@ export async function createExecutionRuntimeHostComposition( getLaunchSpec: filesystemWorkerLaunchSpecProvider, }) : undefined; - const managedFilesystemWorker = filesystemWorker - ? adaptManagedWorkspaceFilesystemWorker(filesystemWorker) + const workspaceFilesystemWorker = filesystemWorker + ? adaptWorkspaceFilesystemWorker(filesystemWorker) : undefined; workspaceExecution = createRuntimeHostWorkspaceExecutionComposition({ - ...(managedFilesystemWorker ? { filesystemWorker: managedFilesystemWorker } : {}), + ...(workspaceFilesystemWorker ? { filesystemWorker: workspaceFilesystemWorker } : {}), }); const taskLedger = new HostTaskLedgerCoordinator( taskLedgerStore, @@ -1817,9 +1817,9 @@ function requireWorkspaceExecution( return composition; } -function adaptManagedWorkspaceFilesystemWorker( +function adaptWorkspaceFilesystemWorker( worker: Pick, -): ManagedWorkspaceFilesystemWorker { +): RuntimeHostWorkspaceFilesystemWorker { return { async execute(input) { // Read-only operations never participate in CAS; the adapter says so diff --git a/packages/runtime-host/src/server/workspace-execution-composition.ts b/packages/runtime-host/src/server/workspace-execution-composition.ts index 570532efbb..2142bbd8ce 100644 --- a/packages/runtime-host/src/server/workspace-execution-composition.ts +++ b/packages/runtime-host/src/server/workspace-execution-composition.ts @@ -18,29 +18,42 @@ */ import { createReadOnlyPermissionProfile } from '@maka/core/permission-profile'; -import { createManagedExecutionBoundary } from '@maka/core/sandbox-boundary'; +import { + createManagedExecutionBoundary, + type ExecutionBoundary, +} from '@maka/core/sandbox-boundary'; import type { - ManagedWorkspaceExecutionHandle, - ManagedWorkspaceFilesystemWorker, - ManagedWorkspaceOwner, - ManagedWorkspaceReadOnlyOperation, - ManagedWorkspaceReadOnlyResult, -} from '@maka/storage/managed-workspace-owner'; + FilesystemWorkerClient, + FilesystemWorkerClientOperation, +} from '@maka/runtime/filesystem-worker'; -export type RuntimeHostWorkspaceExecutionProfile = - | { - readonly kind: 'attached_checkout_v1'; - readonly cwd: string; - } - | { - readonly kind: 'managed_worktree_v1'; - readonly executionHandle: ManagedWorkspaceExecutionHandle; - }; +export interface RuntimeHostWorkspaceExecutionProfile { + readonly kind: 'attached_checkout_v1'; + readonly cwd: string; +} + +export type RuntimeHostWorkspaceReadOnlyOperation = Extract< + FilesystemWorkerClientOperation, + { kind: 'read' | 'glob' | 'grep' } +>; + +export type RuntimeHostWorkspaceReadOnlyResult = Extract< + Awaited>, + { kind: 'read' | 'read_image' | 'glob' | 'grep' } +>; + +export interface RuntimeHostWorkspaceFilesystemWorker { + execute(input: { + readonly operation: RuntimeHostWorkspaceReadOnlyOperation; + readonly cwd: string; + readonly executionBoundary: ExecutionBoundary; + readonly abortSignal?: AbortSignal; + }): Promise; +} export type RuntimeHostWorkspaceExecutionErrorCode = | 'workspace_execution_draining' | 'filesystem_worker_unavailable' - | 'managed_workspace_profile_unavailable' | 'workspace_operation_denied'; export class RuntimeHostWorkspaceExecutionError extends Error { @@ -57,16 +70,15 @@ export interface RuntimeHostWorkspaceExecutionComposition { readonly state: 'ready' | 'draining' | 'closed'; executeReadOnly( profile: RuntimeHostWorkspaceExecutionProfile, - operation: ManagedWorkspaceReadOnlyOperation, + operation: RuntimeHostWorkspaceReadOnlyOperation, abortSignal?: AbortSignal, - ): Promise; + ): Promise; beginDrain(): void; close(): Promise; } export interface CreateRuntimeHostWorkspaceExecutionCompositionInput { - readonly filesystemWorker?: ManagedWorkspaceFilesystemWorker; - readonly managedOwner?: ManagedWorkspaceOwner; + readonly filesystemWorker?: RuntimeHostWorkspaceFilesystemWorker; } export function createAttachedWorkspaceExecutionProfile( @@ -81,12 +93,6 @@ export function createAttachedWorkspaceExecutionProfile( return Object.freeze({ kind: 'attached_checkout_v1', cwd }); } -export function createManagedWorkspaceExecutionProfile( - executionHandle: ManagedWorkspaceExecutionHandle, -): RuntimeHostWorkspaceExecutionProfile { - return Object.freeze({ kind: 'managed_worktree_v1', executionHandle }); -} - export function createRuntimeHostWorkspaceExecutionComposition( input: CreateRuntimeHostWorkspaceExecutionCompositionInput, ): RuntimeHostWorkspaceExecutionComposition { @@ -134,19 +140,6 @@ export function createRuntimeHostWorkspaceExecutionComposition( } activeOperations += 1; try { - if (profile.kind === 'managed_worktree_v1') { - if (!input.managedOwner) { - throw new RuntimeHostWorkspaceExecutionError( - 'managed_workspace_profile_unavailable', - 'Managed workspace execution is not composed for this Runtime Host', - ); - } - return await input.managedOwner.withManagedWorkspaceExecution( - profile.executionHandle, - (scope) => - input.managedOwner!.executeReadOnlyFilesystemOperation(scope, operation, abortSignal), - ); - } if (!input.filesystemWorker) { throw new RuntimeHostWorkspaceExecutionError( 'filesystem_worker_unavailable', @@ -168,7 +161,6 @@ export function createRuntimeHostWorkspaceExecutionComposition( closeTask ??= (async () => { beginDrain(); await waitForDrain(); - await input.managedOwner?.close(); state = 'closed'; })(); return closeTask; @@ -176,7 +168,7 @@ export function createRuntimeHostWorkspaceExecutionComposition( }; } -function isReadOnlyOperation(input: unknown): input is ManagedWorkspaceReadOnlyOperation { +function isReadOnlyOperation(input: unknown): input is RuntimeHostWorkspaceReadOnlyOperation { if (!input || typeof input !== 'object') return false; const kind = (input as { kind?: unknown }).kind; return kind === 'read' || kind === 'glob' || kind === 'grep'; @@ -189,13 +181,10 @@ function isWorkspaceExecutionProfile( const candidate = input as { kind?: unknown; cwd?: unknown; - executionHandle?: { kind?: unknown }; }; - if (candidate.kind === 'attached_checkout_v1') { - return typeof candidate.cwd === 'string' && candidate.cwd.length > 0; - } return ( - candidate.kind === 'managed_worktree_v1' && - candidate.executionHandle?.kind === 'managed_workspace_execution_handle_v1' + candidate.kind === 'attached_checkout_v1' && + typeof candidate.cwd === 'string' && + candidate.cwd.length > 0 ); } diff --git a/packages/storage/package.json b/packages/storage/package.json index 47ea5c4206..2c4bc7853d 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -25,7 +25,6 @@ "./interaction-store": "./dist/interaction-store-public.js", "./long-term-memory-store": "./dist/long-term-memory-store.js", "./managed-secret-store": "./dist/managed-secret-store.js", - "./managed-workspace-owner": "./dist/managed-workspace-owner.js", "./mcp-config-store": "./dist/mcp-config-store.js", "./memory-bundle-store": "./dist/memory-bundle-store.js", "./model-call-ledger": "./dist/model-call-ledger.js", diff --git a/packages/storage/src/__tests__/dugite-native-environment.test.ts b/packages/storage/src/__tests__/dugite-native-environment.test.ts deleted file mode 100644 index f9f06697b8..0000000000 --- a/packages/storage/src/__tests__/dugite-native-environment.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import assert from 'node:assert/strict'; -import test from 'node:test'; -import { bundledGitEnvironment } from '../dugite-native-environment.js'; - -test('constructs a Windows MinGit environment without a system PATH', () => { - assert.deepEqual( - bundledGitEnvironment({ - platform: 'win32', - arch: 'arm64', - rootPath: 'C:\\Maka\\resources\\git', - executablePath: 'C:\\Maka\\resources\\git\\cmd\\git.exe', - }), - { - PATH: [ - 'C:\\Maka\\resources\\git\\cmd', - 'C:\\Maka\\resources\\git\\clangarm64\\bin', - 'C:\\Maka\\resources\\git\\clangarm64\\usr\\bin', - ].join(';'), - GIT_EXEC_PATH: 'C:\\Maka\\resources\\git\\clangarm64\\libexec\\git-core', - }, - ); -}); - -test('constructs relocatable macOS and Linux Git environments', () => { - assert.deepEqual( - bundledGitEnvironment({ - platform: 'darwin', - arch: 'arm64', - rootPath: '/Applications/Maka.app/Contents/Resources/git', - executablePath: '/Applications/Maka.app/Contents/Resources/git/bin/git', - }), - { - PATH: '/Applications/Maka.app/Contents/Resources/git/bin', - GIT_EXEC_PATH: '/Applications/Maka.app/Contents/Resources/git/libexec/git-core', - GIT_TEMPLATE_DIR: '/Applications/Maka.app/Contents/Resources/git/share/git-core/templates', - }, - ); - assert.deepEqual( - bundledGitEnvironment({ - platform: 'linux', - arch: 'x64', - rootPath: '/opt/maka/resources/git', - executablePath: '/opt/maka/resources/git/bin/git', - }), - { - PATH: '/opt/maka/resources/git/bin', - GIT_EXEC_PATH: '/opt/maka/resources/git/libexec/git-core', - GIT_TEMPLATE_DIR: '/opt/maka/resources/git/share/git-core/templates', - PREFIX: '/opt/maka/resources/git', - GIT_SSL_CAINFO: '/opt/maka/resources/git/ssl/cacert.pem', - }, - ); -}); diff --git a/packages/storage/src/__tests__/fixtures/git-workspace-service-crash-child.ts b/packages/storage/src/__tests__/fixtures/git-workspace-service-crash-child.ts deleted file mode 100644 index 5491beef18..0000000000 --- a/packages/storage/src/__tests__/fixtures/git-workspace-service-crash-child.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { writeFileSync, writeSync } from 'node:fs'; -import { join } from 'node:path'; -import { createGitWorkspaceService } from '../../git-workspace-service.js'; -import { openManagedWorkspaceOwner } from '../../managed-workspace-owner.js'; -import { resolveStorageRoot, tryAcquireInteractiveRootOwner } from '../../root-authority.js'; -import { createSqliteRuntimeStore } from '../../sqlite-runtime-store.js'; - -const storageRoot = requiredEnv('MAKA_GIT_WORKSPACE_STORAGE'); -const gitRuntime = { - executablePath: requiredEnv('MAKA_GIT_WORKSPACE_EXECUTABLE'), - expectedSha256: requiredEnv('MAKA_GIT_WORKSPACE_SHA256') as `sha256:${string}`, -}; -const failpoint = (point: string) => { - if (point !== requiredEnv('MAKA_GIT_WORKSPACE_FAILPOINT')) return; - writeSync(1, 'READY\n'); - Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0); -}; - -const request = { - repositoryId: 'repository_11111111111111111111111111111111', - workspaceId: 'workspace_22222222222222222222222222222222', - workspaceEpochId: 'epoch_33333333333333333333333333333333', - workspaceInstanceId: 'instance_44444444444444444444444444444444', - sourceRoot: requiredEnv('MAKA_GIT_WORKSPACE_SOURCE'), -} as const; - -if (process.env.MAKA_GIT_WORKSPACE_ACTION === 'baseline-receipt') { - const capability = await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - if (!rootOwner) throw new Error('Unable to acquire crash-child root owner'); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - const owner = await openManagedWorkspaceOwner({ rootOwner, gitRuntime, failpoint }); - await owner.openManagedWorkspaceBaseline(runtimeStore, request); - throw new Error('Managed baseline crash child missed its failpoint'); -} - -if (process.env.MAKA_GIT_WORKSPACE_ACTION === 'execution-admission') { - const capability = await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - if (!rootOwner) throw new Error('Unable to acquire crash-child root owner'); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - const owner = await openManagedWorkspaceOwner({ rootOwner, gitRuntime, failpoint }); - const accepted = await owner.openManagedWorkspaceBaseline(runtimeStore, request); - await owner.withManagedWorkspaceExecution(accepted.executionHandle, async () => undefined); - throw new Error('Managed execution admission crash child missed its failpoint'); -} - -const service = createGitWorkspaceService({ - storageRoot, - gitRuntime, - failpoint, -}); - -const binding = await service.createManagedWorkspaceFromSource(request); - -if (process.env.MAKA_GIT_WORKSPACE_ACTION === 'quarantine') { - writeFileSync( - requiredEnv('MAKA_GIT_WORKSPACE_BINDING_OUTPUT'), - `${JSON.stringify(binding)}\n`, - 'utf8', - ); - await service.quarantineManagedWorkspace(binding, 'crash_convergence_test'); -} - -throw new Error('Git workspace crash child missed its failpoint'); - -function requiredEnv(name: string): string { - const value = process.env[name]; - if (!value) throw new Error(`Missing ${name}`); - return value; -} diff --git a/packages/storage/src/__tests__/git-workspace-service.test.ts b/packages/storage/src/__tests__/git-workspace-service.test.ts deleted file mode 100644 index 831b88d97f..0000000000 --- a/packages/storage/src/__tests__/git-workspace-service.test.ts +++ /dev/null @@ -1,898 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import assert from 'node:assert/strict'; -import { execFile, spawn } from 'node:child_process'; -import { createHash } from 'node:crypto'; -import { createReadStream, existsSync } from 'node:fs'; -import { - mkdir, - mkdtemp, - readFile, - readdir, - realpath, - rename, - rm, - stat, - symlink, - writeFile, -} from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { dirname, join, normalize } from 'node:path'; -import { promisify } from 'node:util'; -import { afterEach, before, describe, test } from 'node:test'; -import { fileURLToPath } from 'node:url'; -import { - GitWorkspaceServiceError, - createGitWorkspaceService, - type GitWorkspaceService, - type ManagedWorkspaceBinding, -} from '../git-workspace-service.js'; - -const execFileAsync = promisify(execFile); -const cleanup: string[] = []; -let gitExecutablePath: string; -let gitExecutableSha256: `sha256:${string}`; - -// These probes fork a second Node process, stop it at a durable failpoint, and -// kill it to verify restart convergence. Keep the full crash matrix on this -// service's owning stress route; the default suite still covers recovery, -// quarantine, corruption, ownership, and symlink boundaries in-process. -const RUN_REAL_PROCESS_CRASH_TESTS = process.env.MAKA_STORAGE_STRESS === '1'; - -before(async () => { - gitExecutablePath = await findGitExecutable(); - gitExecutableSha256 = await sha256File(gitExecutablePath); -}); - -afterEach(async () => { - await Promise.all(cleanup.splice(0).map((path) => rm(path, { recursive: true, force: true }))); -}); - -describe('Git workspace service', () => { - test('fails before creating storage when the explicit Git artifact digest is wrong', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const service = createGitWorkspaceService({ - storageRoot, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: `sha256:${'0'.repeat(64)}`, - }, - }); - - await assert.rejects( - service.createManagedWorkspaceFromSource(openRequest(sourceRoot)), - isWorkspaceError('git_runtime_integrity_mismatch'), - ); - assert.equal(existsSync(storageRoot), false); - }); - - test('imports only the clean source HEAD tree into an independent fixed-config workspace', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - await git(sourceRoot, 'config', 'core.hooksPath', join(root, 'attacker-hooks')); - await git(sourceRoot, 'config', 'core.sshCommand', 'attacker-ssh-command'); - await git(sourceRoot, 'config', 'credential.helper', '!attacker-credential-helper'); - const sourceSnapshot = await snapshotSource(sourceRoot); - const service = await serviceAt(join(root, 'storage')); - - const binding = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - const inspection = await service.inspectManagedWorkspace(binding); - - assert.equal(inspection.state, 'ready'); - assert.equal(inspection.commitOid, binding.baselineCommitOid); - assert.equal(inspection.treeOid, binding.baselineTreeOid); - assert.equal(binding.sourceHeadCommitOid, sourceSnapshot.head); - assert.equal(binding.sourceTreeOid, sourceSnapshot.tree); - assert.equal(binding.baselineTreeOid, sourceSnapshot.tree); - assert.match(binding.materializationProfileDigest, /^sha256:[a-f0-9]{64}$/u); - assert.notEqual(normalize(binding.repositoryPath), normalize(sourceSnapshot.commonDir)); - assert.equal(await readFile(join(binding.worktreePath, 'tracked.txt'), 'utf8'), 'tracked\n'); - assert.equal(existsSync(join(binding.worktreePath, 'ignored.env')), false); - assert.equal(existsSync(join(binding.worktreePath, '.maka-workspace.json')), false); - assert.equal(await git(binding.worktreePath, 'status', '--porcelain=v1'), ''); - assert.equal( - await gitBare( - binding.repositoryPath, - 'rev-list', - '--parents', - '-n', - '1', - binding.baselineCommitOid, - ), - binding.baselineCommitOid, - ); - assert.equal( - await gitBare(binding.repositoryPath, 'config', '--local', '--get', 'core.autocrlf'), - 'false', - ); - assert.equal( - await gitBare(binding.repositoryPath, 'config', '--local', '--get', 'core.hooksPath'), - binding.hooksPath, - ); - assert.equal( - await gitBare(binding.repositoryPath, 'config', '--local', '--get', 'core.sshCommand'), - '', - ); - assert.equal( - await gitBare(binding.repositoryPath, 'config', '--local', '--get', 'protocol.allow'), - 'never', - ); - assert.equal(existsSync(join(binding.repositoryPath, 'objects', 'info', 'alternates')), false); - await assert.rejects(gitBare(binding.repositoryPath, 'cat-file', '-e', sourceSnapshot.head)); - await assertSourceUnchanged(sourceRoot, sourceSnapshot); - }); - - test('does not inherit a poisoned process-level Git config path', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const poisonedConfig = join(root, 'poisoned-global-gitconfig'); - await writeFile(poisonedConfig, 'this is not valid Git config\n'); - const previous = process.env.GIT_CONFIG_GLOBAL; - process.env.GIT_CONFIG_GLOBAL = poisonedConfig; - try { - const service = await serviceAt(join(root, 'storage')); - const binding = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - assert.equal((await service.inspectManagedWorkspace(binding)).state, 'ready'); - } finally { - if (previous === undefined) delete process.env.GIT_CONFIG_GLOBAL; - else process.env.GIT_CONFIG_GLOBAL = previous; - } - }); - - test('adopts the same binding across service restart and concurrent duplicate opens', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const storageRoot = join(root, 'storage'); - const request = openRequest(sourceRoot); - const firstService = await serviceAt(storageRoot); - const [left, right] = await Promise.all([ - firstService.createManagedWorkspaceFromSource(request), - firstService.createManagedWorkspaceFromSource(request), - ]); - const restarted = await serviceAt(storageRoot); - const adopted = await restarted.openManagedWorkspaceFromBinding(left); - - assert.deepEqual(right, left); - assert.deepEqual(adopted, left); - assert.equal((await restarted.inspectManagedWorkspace(adopted)).state, 'ready'); - }); - - test('reopens an existing managed epoch after the source checkout advances', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const storageRoot = join(root, 'storage'); - const request = openRequest(sourceRoot); - const firstService = await serviceAt(storageRoot); - const created = await firstService.createManagedWorkspaceFromSource(request); - - await writeFile(join(sourceRoot, 'tracked.txt'), 'source advanced\n', 'utf8'); - await git(sourceRoot, 'add', 'tracked.txt'); - await git( - sourceRoot, - '-c', - 'user.name=Maka Test', - '-c', - 'user.email=test@maka.invalid', - 'commit', - '--quiet', - '-m', - 'advance source after managed epoch creation', - ); - - const restarted = await serviceAt(storageRoot); - const reopened = await restarted.openManagedWorkspaceFromBinding(created); - - assert.deepEqual(reopened, created); - assert.equal((await restarted.inspectManagedWorkspace(reopened)).state, 'ready'); - assert.equal(await readFile(join(reopened.worktreePath, 'tracked.txt'), 'utf8'), 'tracked\n'); - }); - - test('creates a second epoch with a different baseline in the same managed repository', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const service = await serviceAt(join(root, 'storage')); - const first = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - - await writeFile(join(sourceRoot, 'tracked.txt'), 'second epoch\n', 'utf8'); - await git(sourceRoot, 'add', 'tracked.txt'); - await git( - sourceRoot, - '-c', - 'user.name=Maka Test', - '-c', - 'user.email=test@maka.invalid', - 'commit', - '--quiet', - '-m', - 'second epoch source', - ); - const secondRequest = { - ...openRequest(sourceRoot), - workspaceEpochId: 'epoch_55555555555555555555555555555555', - workspaceInstanceId: 'instance_66666666666666666666666666666666', - } as const; - - const second = await service.createManagedWorkspaceFromSource(secondRequest); - - assert.equal(second.repositoryId, first.repositoryId); - assert.equal(second.repositoryPath, first.repositoryPath); - assert.notEqual(second.baselineCommitOid, first.baselineCommitOid); - assert.notEqual(second.baselineTreeOid, first.baselineTreeOid); - assert.equal(await readFile(join(first.worktreePath, 'tracked.txt'), 'utf8'), 'tracked\n'); - assert.equal( - await readFile(join(second.worktreePath, 'tracked.txt'), 'utf8'), - 'second epoch\n', - ); - assert.equal((await service.inspectManagedWorkspace(first)).state, 'ready'); - assert.equal((await service.inspectManagedWorkspace(second)).state, 'ready'); - }); - - test('removes an unpublished baseline ref when the source changes and permits retry', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const storageRoot = join(root, 'storage'); - let changed = false; - const service = createGitWorkspaceService({ - storageRoot, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - failpoint: async (point) => { - if (point !== 'after_baseline_ref_created' || changed) return; - changed = true; - await writeFile(join(sourceRoot, 'tracked.txt'), 'raced source\n', 'utf8'); - await git(sourceRoot, 'add', 'tracked.txt'); - await git( - sourceRoot, - '-c', - 'user.name=Maka Test', - '-c', - 'user.email=test@maka.invalid', - 'commit', - '--quiet', - '-m', - 'source changed during import', - ); - }, - }); - - await assert.rejects( - service.createManagedWorkspaceFromSource(openRequest(sourceRoot)), - isWorkspaceError('source_changed_during_baseline_import'), - ); - const repositoryPath = await onlyManagedRepositoryPath(storageRoot); - await assert.rejects( - gitBare( - repositoryPath, - 'rev-parse', - '--verify', - 'refs/maka/baselines/epoch_33333333333333333333333333333333', - ), - ); - - const retried = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - assert.equal( - await readFile(join(retried.worktreePath, 'tracked.txt'), 'utf8'), - 'raced source\n', - ); - assert.equal((await service.inspectManagedWorkspace(retried)).state, 'ready'); - }); - - test('repairs a crash after baseline ref publication even when the source later advances', { - skip: !RUN_REAL_PROCESS_CRASH_TESTS, - timeout: 60_000, - }, async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const storageRoot = join(root, 'storage'); - const child = spawn( - process.execPath, - [fileURLToPath(new URL('./fixtures/git-workspace-service-crash-child.js', import.meta.url))], - { - env: { - ...process.env, - MAKA_GIT_WORKSPACE_STORAGE: storageRoot, - MAKA_GIT_WORKSPACE_SOURCE: sourceRoot, - MAKA_GIT_WORKSPACE_EXECUTABLE: gitExecutablePath, - MAKA_GIT_WORKSPACE_SHA256: gitExecutableSha256, - MAKA_GIT_WORKSPACE_FAILPOINT: 'after_baseline_ref_created', - }, - stdio: ['ignore', 'pipe', 'pipe'], - }, - ); - try { - await waitForReady(child, 30_000); - child.kill('SIGKILL'); - await waitForExit(child); - await writeFile(join(sourceRoot, 'tracked.txt'), 'advanced after crash\n', 'utf8'); - await git(sourceRoot, 'add', 'tracked.txt'); - await git( - sourceRoot, - '-c', - 'user.name=Maka Test', - '-c', - 'user.email=test@maka.invalid', - 'commit', - '--quiet', - '-m', - 'advance after baseline publication crash', - ); - - const service = await serviceAt(storageRoot); - const binding = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - assert.equal( - await readFile(join(binding.worktreePath, 'tracked.txt'), 'utf8'), - 'advanced after crash\n', - ); - assert.equal((await service.inspectManagedWorkspace(binding)).state, 'ready'); - } finally { - if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL'); - } - }); - - for (const failpoint of [ - 'after_worktree_materialized', - 'after_worktree_locked', - 'after_head_ref_updated', - ] as const) { - test(`repairs a real-process crash at ${failpoint} without accepting residue`, { - skip: !RUN_REAL_PROCESS_CRASH_TESTS, - timeout: 60_000, - }, async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const sourceSnapshot = await snapshotSource(sourceRoot); - const storageRoot = join(root, 'storage'); - const child = spawn( - process.execPath, - [ - fileURLToPath( - new URL('./fixtures/git-workspace-service-crash-child.js', import.meta.url), - ), - ], - { - env: { - ...process.env, - MAKA_GIT_WORKSPACE_STORAGE: storageRoot, - MAKA_GIT_WORKSPACE_SOURCE: sourceRoot, - MAKA_GIT_WORKSPACE_EXECUTABLE: gitExecutablePath, - MAKA_GIT_WORKSPACE_SHA256: gitExecutableSha256, - MAKA_GIT_WORKSPACE_FAILPOINT: failpoint, - }, - stdio: ['ignore', 'pipe', 'pipe'], - }, - ); - try { - await waitForReady(child, 30_000); - child.kill('SIGKILL'); - await waitForExit(child); - - const service = await serviceAt(storageRoot); - const binding = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - assert.equal((await service.inspectManagedWorkspace(binding)).state, 'ready'); - const quarantineEntries = await readdir( - join(storageRoot, 'managed-workspaces', 'quarantine'), - ); - assert.ok(quarantineEntries.some((entry) => entry.startsWith('incomplete-'))); - await assertSourceUnchanged(sourceRoot, sourceSnapshot); - } finally { - if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL'); - } - }); - } - - for (const failpoint of [ - 'after_quarantine_intent', - 'after_quarantine_unlock', - 'after_quarantine_move', - 'after_quarantine_binding_removed', - 'after_quarantine_pruned', - ] as const) { - test(`converges quarantine after a real-process crash at ${failpoint}`, { - skip: !RUN_REAL_PROCESS_CRASH_TESTS, - timeout: 60_000, - }, async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const sourceSnapshot = await snapshotSource(sourceRoot); - const storageRoot = join(root, 'storage'); - const bindingOutput = join(root, 'binding.json'); - const child = spawn( - process.execPath, - [ - fileURLToPath( - new URL('./fixtures/git-workspace-service-crash-child.js', import.meta.url), - ), - ], - { - env: { - ...process.env, - MAKA_GIT_WORKSPACE_STORAGE: storageRoot, - MAKA_GIT_WORKSPACE_SOURCE: sourceRoot, - MAKA_GIT_WORKSPACE_EXECUTABLE: gitExecutablePath, - MAKA_GIT_WORKSPACE_SHA256: gitExecutableSha256, - MAKA_GIT_WORKSPACE_FAILPOINT: failpoint, - MAKA_GIT_WORKSPACE_ACTION: 'quarantine', - MAKA_GIT_WORKSPACE_BINDING_OUTPUT: bindingOutput, - }, - stdio: ['ignore', 'pipe', 'pipe'], - }, - ); - try { - await waitForReady(child, 30_000); - child.kill('SIGKILL'); - await waitForExit(child); - const binding = JSON.parse( - await readFile(bindingOutput, 'utf8'), - ) as ManagedWorkspaceBinding; - const service = await serviceAt(storageRoot); - - await assert.rejects( - service.openManagedWorkspaceFromBinding(binding), - isWorkspaceError('managed_workspace_unavailable'), - ); - const first = await service.quarantineManagedWorkspace(binding, 'crash_convergence_test'); - const retry = await service.quarantineManagedWorkspace(binding, 'crash_convergence_test'); - - assert.deepEqual(retry, first); - assert.equal((await stat(first.quarantinePath)).isDirectory(), true); - assert.equal(existsSync(binding.worktreePath), false); - await assertSourceUnchanged(sourceRoot, sourceSnapshot); - } finally { - if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL'); - } - }); - } - - test('rejects dirty or unsupported source state without falling back to attached execution', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - await writeFile(join(sourceRoot, 'untracked.txt'), 'not committed\n', 'utf8'); - const service = await serviceAt(join(root, 'storage')); - - await assert.rejects( - service.createManagedWorkspaceFromSource(openRequest(sourceRoot)), - isWorkspaceError('repository_ineligible'), - ); - }); - - test('rejects nested attributes that could change fixed Git materialization', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - await mkdir(join(sourceRoot, 'nested')); - await writeFile(join(sourceRoot, 'nested', '.gitattributes'), '*.txt filter=custom\n', 'utf8'); - await git(sourceRoot, 'add', 'nested/.gitattributes'); - await git( - sourceRoot, - '-c', - 'user.name=Maka Test', - '-c', - 'user.email=test@maka.invalid', - 'commit', - '--quiet', - '-m', - 'unsupported attributes', - ); - const service = await serviceAt(join(root, 'storage')); - - await assert.rejects( - service.createManagedWorkspaceFromSource(openRequest(sourceRoot)), - isWorkspaceError('repository_ineligible'), - ); - }); - - test('detects external managed-worktree drift and quarantines it without touching source', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const sourceSnapshot = await snapshotSource(sourceRoot); - const service = await serviceAt(join(root, 'storage')); - const binding = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - await writeFile(join(binding.worktreePath, 'tracked.txt'), 'external writer\n', 'utf8'); - - const drifted = await service.inspectManagedWorkspace(binding); - assert.equal(drifted.state, 'drifted'); - const quarantined = await service.quarantineManagedWorkspace(binding, 'external_drift'); - - assert.equal(existsSync(binding.worktreePath), false); - assert.equal((await stat(quarantined.quarantinePath)).isDirectory(), true); - assert.equal( - await readFile(join(quarantined.quarantinePath, 'tracked.txt'), 'utf8'), - 'external writer\n', - ); - await assertSourceUnchanged(sourceRoot, sourceSnapshot); - await assert.rejects( - service.inspectManagedWorkspace(binding), - isWorkspaceError('managed_workspace_unavailable'), - ); - }); - - test('rejects quarantine resume when its owned root is replaced by a symlink', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - let interruptOnce = true; - const interrupted = createGitWorkspaceService({ - storageRoot, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - failpoint(point) { - if (point === 'after_quarantine_intent' && interruptOnce) { - interruptOnce = false; - throw new Error('stop after durable quarantine intent'); - } - }, - }); - const binding = await interrupted.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - await assert.rejects( - interrupted.quarantineManagedWorkspace(binding, 'owned_root_symlink'), - /stop after durable quarantine intent/u, - ); - const quarantineRoot = join(storageRoot, 'managed-workspaces', 'quarantine'); - const externalRoot = join(root, 'external-quarantine'); - await rm(quarantineRoot, { recursive: true, force: true }); - await mkdir(externalRoot, { recursive: true }); - await symlink(externalRoot, quarantineRoot, process.platform === 'win32' ? 'junction' : 'dir'); - - const service = await serviceAt(storageRoot); - await assert.rejects( - service.quarantineManagedWorkspace(binding, 'owned_root_symlink'), - isWorkspaceError('managed_workspace_identity_conflict'), - ); - assert.equal(existsSync(binding.worktreePath), true); - assert.deepEqual(await readdir(externalRoot), []); - }); - - test('rejects control records reached through a replaced instance-root symlink', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const service = await serviceAt(storageRoot); - const binding = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - const instanceRoot = dirname(binding.worktreePath); - const externalRoot = join(root, 'external-instance'); - await mkdir(externalRoot, { recursive: true }); - const movedInstance = join(externalRoot, 'instance'); - await rename(instanceRoot, movedInstance); - await symlink(movedInstance, instanceRoot, process.platform === 'win32' ? 'junction' : 'dir'); - - await assert.rejects( - service.inspectManagedWorkspace(binding), - isWorkspaceError('managed_workspace_identity_conflict'), - ); - }); - - test('rejects a source repository that delegates object lookup through alternates', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const commonDirRaw = await git(sourceRoot, 'rev-parse', '--git-common-dir'); - const commonDir = await realpath(join(sourceRoot, commonDirRaw)); - const alternateObjects = join(root, 'alternate-objects'); - await mkdir(alternateObjects, { recursive: true }); - await mkdir(join(commonDir, 'objects', 'info'), { recursive: true }); - await writeFile( - join(commonDir, 'objects', 'info', 'alternates'), - `${alternateObjects}\n`, - 'utf8', - ); - const service = await serviceAt(join(root, 'storage')); - - await assert.rejects( - service.createManagedWorkspaceFromSource(openRequest(sourceRoot)), - isWorkspaceError('repository_ineligible'), - ); - }); - - test('rejects unsafe worktree-scoped source configuration', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - await git(sourceRoot, 'config', 'extensions.worktreeConfig', 'true'); - await git(sourceRoot, 'config', '--worktree', 'core.fsmonitor', 'untrusted-monitor'); - const service = await serviceAt(join(root, 'storage')); - - await assert.rejects( - service.createManagedWorkspaceFromSource(openRequest(sourceRoot)), - isWorkspaceError('repository_ineligible'), - ); - }); - - test('rejects a local includeIf source configuration after Git key normalization', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - await git( - sourceRoot, - 'config', - '--local', - 'includeIf.gitdir:/definitely-never/.path', - '../untrusted-config', - ); - const service = await serviceAt(join(root, 'storage')); - - await assert.rejects( - service.createManagedWorkspaceFromSource(openRequest(sourceRoot)), - isWorkspaceError('repository_ineligible'), - ); - }); - - test('rejects a worktree partial-clone source configuration after key normalization', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - await git(sourceRoot, 'config', 'extensions.worktreeConfig', 'true'); - await git(sourceRoot, 'config', '--worktree', 'extensions.partialClone', 'origin'); - const service = await serviceAt(join(root, 'storage')); - - await assert.rejects( - service.createManagedWorkspaceFromSource(openRequest(sourceRoot)), - isWorkspaceError('repository_ineligible'), - ); - }); - - test('rejects adoption when the Maka worktree ownership lock is removed', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const service = await serviceAt(join(root, 'storage')); - const binding = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - await gitBare(binding.repositoryPath, 'worktree', 'unlock', binding.worktreePath); - - await assert.rejects( - service.inspectManagedWorkspace(binding), - isWorkspaceError('managed_workspace_identity_conflict'), - ); - }); - - test('does not overwrite a conflicting managed head ref', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const service = await serviceAt(join(root, 'storage')); - const first = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - const conflictingEpochId = 'epoch_55555555555555555555555555555555'; - const conflictingHeadRef = `refs/maka/workspaces/${first.workspaceId}/epochs/${conflictingEpochId}/head`; - await gitBare(first.repositoryPath, 'update-ref', conflictingHeadRef, first.baselineCommitOid); - await writeFile(join(sourceRoot, 'tracked.txt'), 'conflicting second epoch\n', 'utf8'); - await git(sourceRoot, 'add', 'tracked.txt'); - await git( - sourceRoot, - '-c', - 'user.name=Maka Test', - '-c', - 'user.email=test@maka.invalid', - 'commit', - '--quiet', - '-m', - 'conflicting second epoch source', - ); - const secondRequest = { - ...openRequest(sourceRoot), - workspaceEpochId: conflictingEpochId, - workspaceInstanceId: 'instance_66666666666666666666666666666666', - } as const; - - await assert.rejects( - service.createManagedWorkspaceFromSource(secondRequest), - isWorkspaceError('managed_workspace_identity_conflict'), - ); - assert.equal( - await gitBare(first.repositoryPath, 'rev-parse', '--verify', conflictingHeadRef), - first.baselineCommitOid, - ); - }); - - test('fails closed when a durable binding gains unknown or self-authenticating fields', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const service = await serviceAt(join(root, 'storage')); - const binding = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - const bindingPath = join(dirname(binding.worktreePath), 'binding.json'); - const corrupted = { - ...JSON.parse(await readFile(bindingPath, 'utf8')), - acceptedByCaller: true, - }; - await writeFile(bindingPath, `${JSON.stringify(corrupted)}\n`, 'utf8'); - - await assert.rejects( - service.inspectManagedWorkspace(binding), - isWorkspaceError('managed_workspace_identity_conflict'), - ); - }); -}); - -async function serviceAt(storageRoot: string): Promise { - return createGitWorkspaceService({ - storageRoot, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); -} - -async function onlyManagedRepositoryPath(storageRoot: string): Promise { - const repositoryRoots = await readdir(join(storageRoot, 'managed-workspaces', 'r')); - assert.equal(repositoryRoots.length, 1); - return join(storageRoot, 'managed-workspaces', 'r', repositoryRoots[0]!, 'repository.git'); -} - -function openRequest(sourceRoot: string) { - return { - repositoryId: 'repository_11111111111111111111111111111111', - workspaceId: 'workspace_22222222222222222222222222222222', - workspaceEpochId: 'epoch_33333333333333333333333333333333', - workspaceInstanceId: 'instance_44444444444444444444444444444444', - sourceRoot, - } as const; -} - -async function createEligibleSource(sourceRoot: string): Promise { - await mkdir(sourceRoot, { recursive: true }); - await git(sourceRoot, 'init', '--quiet'); - await writeFile(join(sourceRoot, 'tracked.txt'), 'tracked\n', 'utf8'); - await writeFile(join(sourceRoot, '.gitignore'), 'ignored.env\n.maka-workspace.json\n', 'utf8'); - await writeFile(join(sourceRoot, 'ignored.env'), 'SECRET=not-imported\n', 'utf8'); - await writeFile(join(sourceRoot, '.maka-workspace.json'), '{"host":"metadata"}\n', 'utf8'); - await git(sourceRoot, 'add', 'tracked.txt', '.gitignore'); - await git( - sourceRoot, - '-c', - 'user.name=Maka Test', - '-c', - 'user.email=test@maka.invalid', - 'commit', - '--quiet', - '-m', - 'source baseline', - ); - return realpath(sourceRoot); -} - -interface SourceSnapshot { - readonly root: string; - readonly commonDir: string; - readonly head: string; - readonly tree: string; - readonly branch: string; - readonly status: string; - readonly refs: string; - readonly indexDigest: `sha256:${string}`; - readonly trackedContents: string; -} - -async function snapshotSource(sourceRoot: string): Promise { - const commonDirRaw = await git(sourceRoot, 'rev-parse', '--git-common-dir'); - const commonDir = await realpath(join(sourceRoot, commonDirRaw)); - return { - root: await realpath(sourceRoot), - commonDir, - head: await git(sourceRoot, 'rev-parse', 'HEAD'), - tree: await git(sourceRoot, 'rev-parse', 'HEAD^{tree}'), - branch: await git(sourceRoot, 'branch', '--show-current'), - status: await git(sourceRoot, 'status', '--porcelain=v1', '--untracked-files=all'), - refs: await git(sourceRoot, 'for-each-ref', '--format=%(refname) %(objectname)'), - indexDigest: await sha256File(join(commonDir, 'index')), - trackedContents: await readFile(join(sourceRoot, 'tracked.txt'), 'utf8'), - }; -} - -async function assertSourceUnchanged(sourceRoot: string, before: SourceSnapshot): Promise { - const after = await snapshotSource(sourceRoot); - assert.deepEqual(after, before); -} - -function isWorkspaceError(code: string): (error: unknown) => boolean { - return (error) => error instanceof GitWorkspaceServiceError && error.code === code; -} - -async function temporaryRoot(): Promise { - const root = await mkdtemp(join(tmpdir(), 'maka-git-workspace-service-')); - cleanup.push(root); - return root; -} - -async function findGitExecutable(): Promise { - const command = process.platform === 'win32' ? 'where.exe' : 'which'; - const { stdout } = await execFileAsync(command, ['git'], { - encoding: 'utf8', - }); - const first = stdout - .split(/\r?\n/u) - .map((line) => line.trim()) - .find(Boolean); - if (!first) throw new Error('Git executable is unavailable for integration tests'); - return realpath(first); -} - -async function sha256File(path: string): Promise<`sha256:${string}`> { - const hash = createHash('sha256'); - for await (const chunk of createReadStream(path)) hash.update(chunk); - return `sha256:${hash.digest('hex')}`; -} - -async function git(cwd: string, ...args: string[]): Promise { - const { stdout } = await execFileAsync('git', args, { - cwd, - encoding: 'utf8', - maxBuffer: 8 * 1024 * 1024, - }); - return stdout.trim(); -} - -async function gitBare(repositoryPath: string, ...args: string[]): Promise { - const { stdout } = await execFileAsync( - 'git', - ['-c', 'core.longpaths=true', '--git-dir', repositoryPath, ...args], - { - cwd: dirname(repositoryPath), - encoding: 'utf8', - maxBuffer: 8 * 1024 * 1024, - }, - ); - return stdout.trim(); -} - -function waitForReady(child: ReturnType, timeoutMs: number): Promise { - return new Promise((resolve, reject) => { - let stdout = ''; - let stderr = ''; - const timeout = setTimeout(() => { - cleanupListeners(); - reject(new Error(`Git workspace crash child did not become ready: ${stderr}`)); - }, timeoutMs); - const onStdout = (chunk: unknown) => { - stdout += String(chunk); - if (stdout.includes('READY\n')) { - cleanupListeners(); - resolve(); - } - }; - const onStderr = (chunk: unknown) => { - stderr += String(chunk); - }; - const onExit = (code: number | null, signal: NodeJS.Signals | null) => { - cleanupListeners(); - reject(new Error(`Git workspace crash child exited early: ${code}/${signal} ${stderr}`)); - }; - const onError = (error: Error) => { - cleanupListeners(); - reject(error); - }; - const cleanupListeners = () => { - clearTimeout(timeout); - child.stdout?.off('data', onStdout); - child.stderr?.off('data', onStderr); - child.off('exit', onExit); - child.off('error', onError); - }; - child.stdout?.on('data', onStdout); - child.stderr?.on('data', onStderr); - child.once('exit', onExit); - child.once('error', onError); - }); -} - -function waitForExit(child: ReturnType): Promise { - if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve(); - return new Promise((resolve, reject) => { - child.once('exit', () => resolve()); - child.once('error', reject); - }); -} diff --git a/packages/storage/src/__tests__/managed-workspace-baseline.test.ts b/packages/storage/src/__tests__/managed-workspace-baseline.test.ts deleted file mode 100644 index eb8b008b0b..0000000000 --- a/packages/storage/src/__tests__/managed-workspace-baseline.test.ts +++ /dev/null @@ -1,1326 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import assert from 'node:assert/strict'; -import { execFile, execFileSync, spawn } from 'node:child_process'; -import { createHash } from 'node:crypto'; -import { createReadStream } from 'node:fs'; -import { - cp, - copyFile, - link, - mkdir, - mkdtemp, - readFile, - realpath, - rename, - rm, - symlink, - writeFile, -} from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { promisify } from 'node:util'; -import { afterEach, before, test } from 'node:test'; -import { openManagedWorkspaceOwner } from '../managed-workspace-owner.js'; -import { managedWorkspaceExecutionAuthorityTestSupport } from '../managed-workspace-execution-authority-internal.js'; -import { createGitWorkspaceService } from '../git-workspace-service.js'; -import { acquireOperationalStateDatabase } from '../operational-state-store.js'; -import { - adoptStorageRootOnImport, - resolveStorageRoot, - STORAGE_ROOT_MARKER_FILE, - tryAcquireInteractiveRootOwner, -} from '../root-authority.js'; -import { createSqliteRuntimeStore } from '../sqlite-runtime-store.js'; -import { - removeTrackedControlDirectories, - trackControlDirectory, -} from './fixtures/control-directory-hygiene.js'; - -const execFileAsync = promisify(execFile); -const cleanup: string[] = []; -const { - inspectHandle: inspectManagedWorkspaceExecutionHandleInternal, - inspectScope: inspectManagedWorkspaceExecutionScopeInternal, -} = managedWorkspaceExecutionAuthorityTestSupport; -let gitExecutablePath: string; -let gitExecutableSha256: `sha256:${string}`; - -before(async () => { - gitExecutablePath = await findGitExecutable(); - gitExecutableSha256 = await sha256File(gitExecutablePath); -}); - -test('does not expose baseline receipt issuance on the public Git workspace service', () => { - const service = createGitWorkspaceService({ - storageRoot: join(tmpdir(), 'maka-public-receipt-authority-test'), - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - assert.equal('openManagedWorkspaceBaselineReceipt' in service, false); - assert.equal('requireManagedWorkspaceBaselineReceipt' in service, false); - assert.equal('verifyManagedWorkspaceBaselineReceipt' in service, false); -}); - -test('does not expose artifact-only workspace creation through the public storage API', async () => { - // The package has no barrel: reachability is decided by the `exports` map. - // Asserting on the map's targets alone would miss a published entrypoint - // re-exporting an internal module, so this loads every published entrypoint - // and checks the union of the symbols a consumer can actually reach. - const manifest = JSON.parse( - await readFile(new URL('../../package.json', import.meta.url), 'utf8'), - ) as { exports: Record }; - const reachable = new Set(); - await Promise.all( - Object.values(manifest.exports).map(async (target) => { - const entrypoint = (await import(new URL(`../../${target}`, import.meta.url).href)) as object; - for (const name of Object.keys(entrypoint)) reachable.add(name); - }), - ); - for (const internalSymbol of [ - 'createGitWorkspaceService', - 'GitWorkspaceServiceError', - 'createManagedWorkspaceWorkerBridgeInternal', - 'ManagedWorkspaceWorkerBridgeError', - 'ManagedWorkspaceExecutionAuthorityError', - 'issueManagedWorkspaceExecutionHandleInternal', - 'requireManagedWorkspaceExecutionHandleInternal', - 'issueManagedWorkspaceExecutionScopeInternal', - 'revokeManagedWorkspaceExecutionScopeInternal', - 'requireManagedWorkspaceExecutionScopeInternal', - 'managedWorkspaceExecutionAuthorityTestSupport', - 'createSqliteArtifactStoreWriteAuthority', - 'migrateOperationalStateDatabaseInternal', - 'inspectOperationalStateSchema', - 'OperationalStateMigrationBlockedError', - ]) { - assert.equal(reachable.has(internalSymbol), false, internalSymbol); - } - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - assert.equal('createManagedWorkspaceFromSource' in owner, false); - assert.equal('openManagedWorkspaceFromBinding' in owner, false); - await owner.close(); - } finally { - await rootOwner.close(); - } -}); - -afterEach(async () => { - await Promise.all( - cleanup.splice(0).map(async (path) => { - await rm(path, { recursive: true, force: true }); - }), - ); - // Control directories live outside the temporary roots, so they outlive the - // removal above and have to be reclaimed from the recorded rootIds. - await removeTrackedControlDirectories(); -}); - -test('opens one canonical baseline from a durable verified Git receipt', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const input = openRequest(sourceRoot); - - const first = await owner.openManagedWorkspaceBaseline(runtimeStore, input); - const retry = await owner.openManagedWorkspaceBaseline(runtimeStore, input); - const firstEvidence = inspectManagedWorkspaceExecutionHandleInternal(first.executionHandle); - const retryEvidence = inspectManagedWorkspaceExecutionHandleInternal(retry.executionHandle); - - assert.equal(first.created, true); - assert.equal(retry.created, false); - assert.equal('committedAt' in firstEvidence.receipt, false); - assert.deepEqual(retryEvidence.receipt, firstEvidence.receipt); - assert.equal(first.head.commitOid, firstEvidence.binding.baselineCommitOid); - assert.equal(first.head.treeOid, firstEvidence.binding.baselineTreeOid); - assert.equal(firstEvidence.receipt.changedFileCount, 2); - assert.equal(firstEvidence.receipt.deletedFileCount, 0); - assert.equal(firstEvidence.receipt.policyVersion, 1); - assert.equal( - firstEvidence.receipt.policyHash, - 'sha256:48eb80c9c8dd6c4d1e7a635dcc187488e0e0b20e2296b81bbaae0be7c5467341', - ); - assert.match(firstEvidence.receipt.treeDeltaDigest, /^sha256:[a-f0-9]{64}$/u); - assert.equal( - firstEvidence.receipt.treeDeltaDigest, - 'sha256:84a37411a467c40c9fd763ab128a6b23e56ec74ab3da2bd54de8d61e375244e8', - ); - assert.deepEqual( - await runtimeStore.readWorkspaceHead(input.workspaceId, input.workspaceEpochId), - first.head, - ); - const unrelatedStore = createSqliteRuntimeStore(join(root, 'other-root', 'runtime.sqlite')); - try { - await assert.rejects( - owner.openManagedWorkspaceBaseline(unrelatedStore, input), - /belongs to a different storage root/u, - ); - } finally { - unrelatedStore.close(); - } - - const receiptPath = join(dirname(firstEvidence.binding.worktreePath), 'baseline-receipt.json'); - await writeFile( - receiptPath, - `${JSON.stringify({ - ...firstEvidence.receipt, - treeDeltaDigest: `sha256:${'f'.repeat(64)}`, - })}\n`, - 'utf8', - ); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, input), - /does not match its verified Git boundary/u, - ); - await writeFile(receiptPath, `${JSON.stringify(firstEvidence.receipt)}\n`, 'utf8'); - - await writeFile( - receiptPath, - `${JSON.stringify({ - ...firstEvidence.receipt, - workspaceVersionId: `version_${'a'.repeat(32)}`, - epochOpenedEventId: `workspace_epoch_opened_${'b'.repeat(64)}`, - baselineAcceptedEventId: `workspace_baseline_accepted_${'c'.repeat(64)}`, - policyHash: `sha256:${'d'.repeat(64)}`, - })}\n`, - 'utf8', - ); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, input), - /does not match its verified Git boundary/u, - ); - await writeFile(receiptPath, `${JSON.stringify(firstEvidence.receipt)}\n`, 'utf8'); - - await writeFile( - receiptPath, - `${JSON.stringify({ ...firstEvidence.receipt, committedAt: Date.now() })}\n`, - 'utf8', - ); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, input), - /Invalid managed workspace baseline receipt/u, - ); - await writeFile(receiptPath, `${JSON.stringify(firstEvidence.receipt)}\n`, 'utf8'); - - const bindingPath = join(dirname(firstEvidence.binding.worktreePath), 'binding.json'); - await rm(receiptPath); - await rm(bindingPath); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, input), - /baseline receipt is unavailable/u, - ); - await assert.rejects(readFile(bindingPath, 'utf8'), { code: 'ENOENT' }); - assert.deepEqual( - await runtimeStore.readWorkspaceHead(input.workspaceId, input.workspaceEpochId), - first.head, - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('reuses an orphan receipt after interruption before SQLite acceptance', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - let interruptOnce = true; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - failpoint(point) { - if (point === 'after_baseline_receipt' && interruptOnce) { - interruptOnce = false; - throw new Error('simulated interruption after durable baseline receipt'); - } - }, - }); - const input = openRequest(sourceRoot); - - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, input), - /simulated interruption/u, - ); - assert.equal( - await runtimeStore.readWorkspaceHead(input.workspaceId, input.workspaceEpochId), - undefined, - ); - - const recovered = await owner.openManagedWorkspaceBaseline(runtimeStore, input); - const recoveredEvidence = inspectManagedWorkspaceExecutionHandleInternal( - recovered.executionHandle, - ); - assert.equal(recovered.created, true); - assert.equal(recoveredEvidence.receipt.baselineAcceptedEventId, recovered.head.acceptedEventId); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects a receipt read through a replaced instance-root symlink before parsing it', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - const acceptedEvidence = inspectManagedWorkspaceExecutionHandleInternal( - accepted.executionHandle, - ); - const instanceRoot = dirname(acceptedEvidence.binding.worktreePath); - const externalRoot = join(root, 'external-instance'); - await mkdir(externalRoot, { recursive: true }); - const movedInstance = join(externalRoot, 'instance'); - await rename(instanceRoot, movedInstance); - await symlink(movedInstance, instanceRoot, process.platform === 'win32' ? 'junction' : 'dir'); - await writeFile(join(movedInstance, 'baseline-receipt.json'), '{invalid-json', 'utf8'); - - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, openRequest(sourceRoot)), - /owned directory escaped or changed identity/u, - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('serializes concurrent baseline opens under the same managed workspace owner', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const [left, right] = await Promise.all([ - owner.openManagedWorkspaceBaseline(runtimeStore, openRequest(sourceRoot)), - owner.openManagedWorkspaceBaseline(runtimeStore, openRequest(sourceRoot)), - ]); - assert.deepEqual([left.created, right.created].sort(), [false, true]); - assert.deepEqual( - inspectManagedWorkspaceExecutionHandleInternal(left.executionHandle).receipt, - inspectManagedWorkspaceExecutionHandleInternal(right.executionHandle).receipt, - ); - assert.deepEqual(left.head, right.head); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects a leased authority database whose real root differs from its claimed path', async () => { - const root = await temporaryRoot(); - const actualRoot = join(root, 'actual-storage'); - const claimedRoot = join(root, 'claimed-storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: claimedRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const databaseLease = acquireOperationalStateDatabase(actualRoot); - const runtimeStore = createSqliteRuntimeStore(join(claimedRoot, 'runtime.sqlite'), { - databaseLease, - }); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, { - ...openRequest(sourceRoot), - }), - /belongs to a different storage root/u, - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects a non-canonical SQLite file in the authenticated storage root', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'other.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, { - ...openRequest(join(root, 'unused-source')), - }), - /unavailable for this storage root/u, - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects an in-memory SQLite authority store', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(':memory:'); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, { - ...openRequest(join(root, 'unused-source')), - }), - /unavailable for this storage root/u, - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects a cross-root hard link to the canonical SQLite authority database', async () => { - const root = await temporaryRoot(); - const sourceStorageRoot = join(root, 'source-storage'); - const claimedStorageRoot = join(root, 'claimed-storage'); - await mkdir(claimedStorageRoot, { recursive: true }); - const sourceDatabasePath = join(sourceStorageRoot, 'runtime.sqlite'); - const sourceStore = createSqliteRuntimeStore(sourceDatabasePath); - sourceStore.close(); - await link(sourceDatabasePath, join(claimedStorageRoot, 'runtime.sqlite')); - - const capability = trackControlDirectory( - await resolveStorageRoot({ path: claimedStorageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(claimedStorageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, { - ...openRequest(join(root, 'unused-source')), - }), - /unavailable for this storage root/u, - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects a canonical SQLite database copied from a different durable storage root', async () => { - const root = await temporaryRoot(); - const sourceStorageRoot = join(root, 'storage-a'); - const claimedStorageRoot = join(root, 'storage-b'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const request = openRequest(sourceRoot); - const sourceCapability = trackControlDirectory( - await resolveStorageRoot({ path: sourceStorageRoot, kind: 'interactive' }), - ); - const sourceRootOwner = await tryAcquireInteractiveRootOwner(sourceCapability); - assert.ok(sourceRootOwner); - const sourceStore = createSqliteRuntimeStore(join(sourceStorageRoot, 'runtime.sqlite')); - try { - const sourceOwner = await openManagedWorkspaceOwner({ - rootOwner: sourceRootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await sourceOwner.openManagedWorkspaceBaseline(sourceStore, request); - await sourceOwner.close(); - } finally { - sourceStore.close(); - await sourceRootOwner.close(); - } - - const claimedCapability = trackControlDirectory( - await resolveStorageRoot({ path: claimedStorageRoot, kind: 'interactive' }), - ); - await copyFile( - join(sourceStorageRoot, 'runtime.sqlite'), - join(claimedStorageRoot, 'runtime.sqlite'), - ); - const claimedRootOwner = await tryAcquireInteractiveRootOwner(claimedCapability); - assert.ok(claimedRootOwner); - const claimedStore = createSqliteRuntimeStore(join(claimedStorageRoot, 'runtime.sqlite')); - try { - const claimedOwner = await openManagedWorkspaceOwner({ - rootOwner: claimedRootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await assert.rejects( - claimedOwner.openManagedWorkspaceBaseline(claimedStore, request), - /different durable storage root/u, - ); - await claimedOwner.close(); - } finally { - claimedStore.close(); - await claimedRootOwner.close(); - } -}); - -test('preserves the durable database root binding across formal whole-root import', async () => { - const root = await temporaryRoot(); - const sourceStorageRoot = join(root, 'storage-a'); - const importedStorageRoot = join(root, 'storage-imported'); - const sourceCapability = trackControlDirectory( - await resolveStorageRoot({ path: sourceStorageRoot, kind: 'interactive' }), - ); - const sourceRootOwner = await tryAcquireInteractiveRootOwner(sourceCapability); - assert.ok(sourceRootOwner); - const sourceStore = createSqliteRuntimeStore(join(sourceStorageRoot, 'runtime.sqlite')); - try { - const sourceOwner = await openManagedWorkspaceOwner({ - rootOwner: sourceRootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await assert.rejects( - sourceOwner.openManagedWorkspaceBaseline( - sourceStore, - openRequest(join(root, 'missing-source')), - ), - /Directory is unavailable/u, - ); - await sourceOwner.close(); - } finally { - sourceStore.close(); - await sourceRootOwner.close(); - } - - await cp(sourceStorageRoot, importedStorageRoot, { recursive: true }); - await rm(sourceStorageRoot, { recursive: true, force: true }); - const importedCapability = await adoptStorageRootOnImport({ - path: importedStorageRoot, - kind: 'interactive', - expectedRootId: sourceCapability.rootId, - }); - const importedRootOwner = await tryAcquireInteractiveRootOwner(importedCapability); - assert.ok(importedRootOwner); - const importedStore = createSqliteRuntimeStore(join(importedStorageRoot, 'runtime.sqlite')); - try { - const importedOwner = await openManagedWorkspaceOwner({ - rootOwner: importedRootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await assert.rejects( - importedOwner.openManagedWorkspaceBaseline( - importedStore, - openRequest(join(root, 'missing-source')), - ), - /Directory is unavailable/u, - ); - await importedOwner.close(); - } finally { - importedStore.close(); - await importedRootOwner.close(); - } -}); - -test('rejects an authenticated owner after its durable root marker id is replaced', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - const markerPath = join(storageRoot, STORAGE_ROOT_MARKER_FILE); - const originalMarker = await readFile(markerPath, 'utf8'); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const marker = JSON.parse(originalMarker) as { rootId: string }; - marker.rootId = `${marker.rootId.startsWith('0') ? '1' : '0'}${marker.rootId.slice(1)}`; - await writeFile(markerPath, `${JSON.stringify(marker)}\n`, 'utf8'); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, openRequest(join(root, 'missing-source'))), - /storage root|root identity|marker/u, - ); - await writeFile(markerPath, originalMarker, 'utf8'); - await owner.close(); - } finally { - await writeFile(markerPath, originalMarker, 'utf8'); - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects final admission when the root marker changes after post-commit artifact verification', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const request = openRequest(sourceRoot); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - const markerPath = join(storageRoot, STORAGE_ROOT_MARKER_FILE); - const originalMarker = await readFile(markerPath, 'utf8'); - let replaceOnce = true; - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - async failpoint(point) { - if ((point as string) !== 'after_post_commit_artifact_verification' || !replaceOnce) return; - replaceOnce = false; - const marker = JSON.parse(originalMarker) as { rootId: string }; - marker.rootId = `${marker.rootId.startsWith('0') ? '1' : '0'}${marker.rootId.slice(1)}`; - await writeFile(markerPath, `${JSON.stringify(marker)}\n`, 'utf8'); - }, - }); - try { - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, request), - /storage root|root identity|marker/u, - ); - assert.ok(await runtimeStore.readWorkspaceHead(request.workspaceId, request.workspaceEpochId)); - } finally { - await writeFile(markerPath, originalMarker, 'utf8'); - await owner.close(); - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects an authority database whose file identity changes after registration', { - skip: - process.platform === 'win32' - ? 'Windows cannot rename an open SQLite database to replace its file identity' - : false, -}, async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const databasePath = join(storageRoot, 'runtime.sqlite'); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(databasePath); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await rename(databasePath, `${databasePath}.replaced`); - await writeFile(databasePath, 'replacement database identity\n', 'utf8'); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, openRequest(join(root, 'unused-source'))), - /database file identity changed/u, - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('does not return an accepted baseline when runtime.sqlite is replaced after the initial root check', { - skip: - process.platform === 'win32' - ? 'Windows cannot rename an open SQLite database during the verification race' - : false, -}, async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const databasePath = join(storageRoot, 'runtime.sqlite'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const request = openRequest(sourceRoot); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(databasePath); - let replaceOnce = true; - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - async failpoint(point) { - if ((point as string) !== 'after_initial_store_root_validation' || !replaceOnce) return; - replaceOnce = false; - await rename(databasePath, `${databasePath}.detached`); - await writeFile(databasePath, 'replacement database identity\n', 'utf8'); - }, - }); - try { - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, request), - /database file identity changed/u, - ); - } finally { - await owner.close(); - runtimeStore.close(); - await rootOwner.close(); - } - - await Promise.all([ - rm(databasePath, { force: true }), - rm(`${databasePath}-wal`, { force: true }), - rm(`${databasePath}-shm`, { force: true }), - ]); - // SQLite may keep the accepted transaction in a WAL that remains associated - // with the original pathname, so the detached main file is not a portable - // authority read after this adversarial rename. The safety invariant is that - // the replacement canonical database never receives that head and no usable - // baseline was returned above. - const replacementStore = createSqliteRuntimeStore(databasePath); - try { - assert.equal( - await replacementStore.readWorkspaceHead(request.workspaceId, request.workspaceEpochId), - undefined, - ); - } finally { - replacementStore.close(); - } -}); - -test('rejects runtime.sqlite when the canonical database path is a symlink', async (context) => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const externalRoot = join(root, 'external'); - await mkdir(storageRoot, { recursive: true }); - const externalPath = join(externalRoot, 'runtime.sqlite'); - createSqliteRuntimeStore(externalPath).close(); - try { - await symlink(externalPath, join(storageRoot, 'runtime.sqlite'), 'file'); - } catch (error) { - const code = (error as NodeJS.ErrnoException).code; - if (process.platform === 'win32' && (code === 'EPERM' || code === 'EACCES')) { - context.skip('File symlinks require Windows developer mode or elevated privileges'); - return; - } - throw error; - } - - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, { - ...openRequest(join(root, 'unused-source')), - }), - /unavailable for this storage root/u, - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('keeps an orphan receipt across SQLite rollback and accepts it on retry', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - let interruptOnce = true; - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite'), { - failpoint(point) { - if (point === 'after_workspace_version_event_insert' && interruptOnce) { - interruptOnce = false; - throw new Error('simulated SQLite baseline rollback'); - } - }, - }); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const input = openRequest(sourceRoot); - - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, input), - /simulated SQLite baseline rollback/u, - ); - assert.equal( - await runtimeStore.readWorkspaceHead(input.workspaceId, input.workspaceEpochId), - undefined, - ); - - const recovered = await owner.openManagedWorkspaceBaseline(runtimeStore, input); - const recoveredEvidence = inspectManagedWorkspaceExecutionHandleInternal( - recovered.executionHandle, - ); - assert.equal(recovered.created, true); - assert.equal(recoveredEvidence.receipt.workspaceVersionId, recovered.head.workspaceVersionId); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('accepts the same durable receipt after a real process crash before SQLite authority', { - timeout: 60_000, -}, async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const child = spawn( - process.execPath, - [fileURLToPath(new URL('./fixtures/git-workspace-service-crash-child.js', import.meta.url))], - { - env: { - ...process.env, - MAKA_GIT_WORKSPACE_STORAGE: storageRoot, - MAKA_GIT_WORKSPACE_SOURCE: sourceRoot, - MAKA_GIT_WORKSPACE_EXECUTABLE: gitExecutablePath, - MAKA_GIT_WORKSPACE_SHA256: gitExecutableSha256, - MAKA_GIT_WORKSPACE_FAILPOINT: 'after_baseline_receipt', - MAKA_GIT_WORKSPACE_ACTION: 'baseline-receipt', - }, - stdio: ['ignore', 'pipe', 'pipe'], - }, - ); - try { - await waitForReady(child, 30_000); - const durableBeforeCrash = JSON.parse( - await readFile(baselineReceiptPath(storageRoot, openRequest(sourceRoot)), 'utf8'), - ); - child.kill('SIGKILL'); - await waitForExit(child); - - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline(runtimeStore, { - ...openRequest(sourceRoot), - }); - const acceptedEvidence = inspectManagedWorkspaceExecutionHandleInternal( - accepted.executionHandle, - ); - assert.equal(accepted.created, true); - assert.equal(acceptedEvidence.receipt.baselineAcceptedEventId, accepted.head.acceptedEventId); - assert.deepEqual(acceptedEvidence.receipt, durableBeforeCrash); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } - } finally { - if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL'); - } -}); - -test('reopens the exact accepted baseline after a real crash before post-commit verification', { - timeout: 60_000, -}, async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const child = spawn( - process.execPath, - [fileURLToPath(new URL('./fixtures/git-workspace-service-crash-child.js', import.meta.url))], - { - env: { - ...process.env, - MAKA_GIT_WORKSPACE_STORAGE: storageRoot, - MAKA_GIT_WORKSPACE_SOURCE: sourceRoot, - MAKA_GIT_WORKSPACE_EXECUTABLE: gitExecutablePath, - MAKA_GIT_WORKSPACE_SHA256: gitExecutableSha256, - MAKA_GIT_WORKSPACE_FAILPOINT: 'after_baseline_authority_commit', - MAKA_GIT_WORKSPACE_ACTION: 'baseline-receipt', - }, - stdio: ['ignore', 'pipe', 'pipe'], - }, - ); - try { - await waitForReady(child, 30_000); - const durableBeforeCrash = JSON.parse( - await readFile(baselineReceiptPath(storageRoot, openRequest(sourceRoot)), 'utf8'), - ); - child.kill('SIGKILL'); - await waitForExit(child); - - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const reopened = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - const reopenedEvidence = inspectManagedWorkspaceExecutionHandleInternal( - reopened.executionHandle, - ); - assert.equal(reopened.created, false); - assert.deepEqual(reopenedEvidence.receipt, durableBeforeCrash); - assert.equal(reopened.head.acceptedEventId, reopenedEvidence.receipt.baselineAcceptedEventId); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } - } finally { - if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL'); - } -}); - -test('reissues execution authority after a real process crash during admission verification', { - timeout: 60_000, -}, async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const child = spawn( - process.execPath, - [fileURLToPath(new URL('./fixtures/git-workspace-service-crash-child.js', import.meta.url))], - { - env: { - ...process.env, - MAKA_GIT_WORKSPACE_STORAGE: storageRoot, - MAKA_GIT_WORKSPACE_SOURCE: sourceRoot, - MAKA_GIT_WORKSPACE_EXECUTABLE: gitExecutablePath, - MAKA_GIT_WORKSPACE_SHA256: gitExecutableSha256, - MAKA_GIT_WORKSPACE_FAILPOINT: 'after_execution_artifact_verification', - MAKA_GIT_WORKSPACE_ACTION: 'execution-admission', - }, - stdio: ['ignore', 'pipe', 'pipe'], - }, - ); - try { - await waitForReady(child, 30_000); - child.kill('SIGKILL'); - await waitForExit(child); - - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const reopened = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - let observedCwd: string | undefined; - await owner.withManagedWorkspaceExecution(reopened.executionHandle, async (scope) => { - const context = inspectManagedWorkspaceExecutionScopeInternal(scope); - observedCwd = context.cwd; - assert.equal(await readFile(join(context.cwd, 'tracked.txt'), 'utf8'), 'tracked\n'); - }); - assert.equal( - observedCwd, - inspectManagedWorkspaceExecutionHandleInternal(reopened.executionHandle).binding - .worktreePath, - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } - } finally { - if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL'); - } -}); - -test('rejects a source tree containing a non-UTF-8 Git path', { - skip: process.platform === 'win32', -}, async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - await commitInvalidUtf8Path(sourceRoot); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, openRequest(sourceRoot)), - /requires Git paths to be valid UTF-8/u, - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -async function temporaryRoot(): Promise { - const root = await mkdtemp(join(tmpdir(), 'maka-managed-workspace-baseline-')); - cleanup.push(root); - return root; -} - -async function findGitExecutable(): Promise { - const command = process.platform === 'win32' ? 'where.exe' : 'which'; - const { stdout } = await execFileAsync(command, ['git'], { encoding: 'utf8' }); - const first = stdout - .split(/\r?\n/u) - .map((line) => line.trim()) - .find(Boolean); - if (!first) throw new Error('Git executable is unavailable for integration tests'); - return realpath(first); -} - -async function sha256File(path: string): Promise<`sha256:${string}`> { - const hash = createHash('sha256'); - for await (const chunk of createReadStream(path)) hash.update(chunk); - return `sha256:${hash.digest('hex')}`; -} - -async function createEligibleSource(sourceRoot: string): Promise { - await mkdir(sourceRoot, { recursive: true }); - await git(sourceRoot, 'init', '--quiet'); - await writeFile(join(sourceRoot, 'tracked.txt'), 'tracked\n', 'utf8'); - await writeFile(join(sourceRoot, '.gitignore'), '.maka-workspace.json\n', 'utf8'); - await writeFile(join(sourceRoot, '.maka-workspace.json'), '{"host":"metadata"}\n', 'utf8'); - await git(sourceRoot, 'add', 'tracked.txt', '.gitignore'); - await git( - sourceRoot, - '-c', - 'user.name=Maka Test', - '-c', - 'user.email=test@maka.invalid', - 'commit', - '--quiet', - '-m', - 'source baseline', - ); - return realpath(sourceRoot); -} - -async function commitInvalidUtf8Path(sourceRoot: string): Promise { - const invalidPath = Buffer.from([0xff]); - const blobOid = gitWithInput(sourceRoot, ['hash-object', '-w', '--stdin'], 'invalid\n') - .toString('ascii') - .trim(); - const existingEntries = gitWithInput(sourceRoot, ['ls-tree', '-z', 'HEAD']); - const invalidEntry = Buffer.concat([ - Buffer.from(`100644 blob ${blobOid}\t`, 'ascii'), - invalidPath, - Buffer.from([0]), - ]); - const treeOid = gitWithInput( - sourceRoot, - ['mktree', '-z'], - Buffer.concat([existingEntries, invalidEntry]), - ) - .toString('ascii') - .trim(); - const parentOid = await git(sourceRoot, 'rev-parse', 'HEAD'); - const commitOid = gitWithInput( - sourceRoot, - [ - '-c', - 'user.name=Maka Test', - '-c', - 'user.email=test@maka.invalid', - 'commit-tree', - treeOid, - '-p', - parentOid, - ], - 'add invalid path\n', - ) - .toString('ascii') - .trim(); - - await git(sourceRoot, 'update-ref', 'HEAD', commitOid, parentOid); - await git(sourceRoot, 'read-tree', treeOid); - // Keep Git status clean without asking the filesystem to materialize the invalid path. - gitWithInput( - sourceRoot, - ['update-index', '--skip-worktree', '-z', '--stdin'], - Buffer.concat([invalidPath, Buffer.from([0])]), - ); -} - -function openRequest(sourceRoot: string) { - return { - repositoryId: 'repository_11111111111111111111111111111111', - workspaceId: 'workspace_22222222222222222222222222222222', - workspaceEpochId: 'epoch_33333333333333333333333333333333', - workspaceInstanceId: 'instance_44444444444444444444444444444444', - sourceRoot, - } as const; -} - -function baselineReceiptPath( - storageRoot: string, - identity: ReturnType, -): string { - const compact = (value: string) => createHash('sha256').update(value).digest('hex').slice(0, 20); - return join( - storageRoot, - 'managed-workspaces', - 'w', - compact(identity.workspaceId), - 'e', - compact(identity.workspaceEpochId), - 'i', - compact(identity.workspaceInstanceId), - 'baseline-receipt.json', - ); -} - -async function git(cwd: string, ...args: string[]): Promise { - const { stdout } = await execFileAsync('git', args, { - cwd, - encoding: 'utf8', - maxBuffer: 8 * 1024 * 1024, - }); - return stdout.trim(); -} - -function gitWithInput( - cwd: string, - args: readonly string[], - input: string | Buffer = Buffer.alloc(0), -): Buffer { - return execFileSync('git', args, { - cwd, - input, - maxBuffer: 8 * 1024 * 1024, - }); -} - -function waitForReady(child: ReturnType, timeoutMs: number): Promise { - return new Promise((resolve, reject) => { - let stdout = ''; - let stderr = ''; - const timeout = setTimeout(() => { - cleanup(); - reject(new Error(`Managed baseline crash child did not become ready: ${stderr}`)); - }, timeoutMs); - const onStdout = (chunk: unknown) => { - stdout += String(chunk); - if (stdout.includes('READY\n')) { - cleanup(); - resolve(); - } - }; - const onStderr = (chunk: unknown) => { - stderr += String(chunk); - }; - const onExit = (code: number | null, signal: NodeJS.Signals | null) => { - cleanup(); - reject(new Error(`Managed baseline crash child exited early: ${code}/${signal} ${stderr}`)); - }; - const cleanup = () => { - clearTimeout(timeout); - child.stdout?.off('data', onStdout); - child.stderr?.off('data', onStderr); - child.off('exit', onExit); - }; - child.stdout?.on('data', onStdout); - child.stderr?.on('data', onStderr); - child.once('exit', onExit); - }); -} - -function waitForExit(child: ReturnType): Promise { - if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve(); - return new Promise((resolve, reject) => { - child.once('exit', () => resolve()); - child.once('error', reject); - }); -} diff --git a/packages/storage/src/__tests__/managed-workspace-execution-authority-internal.test.ts b/packages/storage/src/__tests__/managed-workspace-execution-authority-internal.test.ts deleted file mode 100644 index 4c8a603404..0000000000 --- a/packages/storage/src/__tests__/managed-workspace-execution-authority-internal.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import assert from 'node:assert/strict'; -import test from 'node:test'; -import { - issueManagedWorkspaceExecutionScopeInternal, - ManagedWorkspaceExecutionAuthorityError, - requireManagedWorkspaceExecutionScopeInternal, - revokeManagedWorkspaceExecutionScopeInternal, -} from '../managed-workspace-execution-authority-internal.js'; - -test('resolves an active execution scope only for its issuing owner', () => { - const issuer = {}; - const otherOwner = {}; - const scope = issueManagedWorkspaceExecutionScopeInternal(issuer, { - provisioning: 'canonical_tree_only_v1', - workspaceEffect: 'none', - cwd: '/managed/worktree', - binding: Object.freeze({}) as never, - head: Object.freeze({}) as never, - }); - - assert.equal( - requireManagedWorkspaceExecutionScopeInternal(issuer, scope).cwd, - '/managed/worktree', - ); - assert.throws( - () => requireManagedWorkspaceExecutionScopeInternal(otherOwner, scope), - (error) => - error instanceof ManagedWorkspaceExecutionAuthorityError && - error.code === 'managed_workspace_execution_scope_invalid', - ); - - revokeManagedWorkspaceExecutionScopeInternal(issuer, scope); - assert.throws( - () => requireManagedWorkspaceExecutionScopeInternal(issuer, scope), - (error) => - error instanceof ManagedWorkspaceExecutionAuthorityError && - error.code === 'managed_workspace_execution_scope_expired', - ); -}); diff --git a/packages/storage/src/__tests__/managed-workspace-owner.test.ts b/packages/storage/src/__tests__/managed-workspace-owner.test.ts deleted file mode 100644 index 91073a12a2..0000000000 --- a/packages/storage/src/__tests__/managed-workspace-owner.test.ts +++ /dev/null @@ -1,1013 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import assert from 'node:assert/strict'; -import { execFile } from 'node:child_process'; -import { createHash } from 'node:crypto'; -import { createReadStream, existsSync } from 'node:fs'; -import { mkdir, mkdtemp, realpath, rename, rm, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import { setTimeout as delay } from 'node:timers/promises'; -import { promisify } from 'node:util'; -import { afterEach, before, test } from 'node:test'; -import { - ManagedWorkspaceOwnerError, - openManagedWorkspaceOwner, - type ManagedWorkspaceExecutionHandle, - type ManagedWorkspaceExecutionScope, -} from '../managed-workspace-owner.js'; -import { - managedWorkspaceExecutionAuthorityTestSupport, - ManagedWorkspaceExecutionAuthorityError, -} from '../managed-workspace-execution-authority-internal.js'; -import { resolveStorageRoot, tryAcquireInteractiveRootOwner } from '../root-authority.js'; -import { createSqliteRuntimeStore } from '../sqlite-runtime-store.js'; -import { - removeTrackedControlDirectories, - trackControlDirectory, -} from './fixtures/control-directory-hygiene.js'; - -const execFileAsync = promisify(execFile); -const cleanup: string[] = []; -const { - inspectHandle: inspectManagedWorkspaceExecutionHandleInternal, - inspectScope: inspectManagedWorkspaceExecutionScopeInternal, -} = managedWorkspaceExecutionAuthorityTestSupport; -let gitExecutablePath: string; -let gitExecutableSha256: `sha256:${string}`; - -before(async () => { - gitExecutablePath = await findGitExecutable(); - gitExecutableSha256 = await sha256File(gitExecutablePath); -}); - -afterEach(async () => { - await Promise.all( - cleanup.splice(0).map(async (path) => { - await rm(path, { recursive: true, force: true }); - }), - ); - // Control directories live outside the temporary roots, so they outlive the - // removal above and have to be reclaimed from the recorded rootIds. - await removeTrackedControlDirectories(); -}); - -test('admits exactly one managed workspace owner for an interactive root owner', async () => { - const storageRoot = await temporaryRoot(); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - try { - const managedOwner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - - await assert.rejects( - openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }), - isOwnerError('managed_workspace_owner_conflict'), - ); - await managedOwner.close(); - await managedOwner.close(); - } finally { - await rootOwner.close(); - } -}); - -test('releases a partial owner claim when pinned Git initialization fails', async () => { - const storageRoot = await temporaryRoot(); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - try { - await assert.rejects( - openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: `sha256:${'0'.repeat(64)}`, - }, - }), - isOwnerError('managed_workspace_owner_unavailable'), - ); - - const retry = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - assert.equal(retry.state, 'ready'); - await retry.close(); - } finally { - await rootOwner.close(); - } -}); - -test('creates an accepted managed baseline only through the active owner', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - const { binding, receipt } = inspectManagedWorkspaceExecutionHandleInternal( - accepted.executionHandle, - ); - - assert.equal(binding.sourceTreeOid, binding.baselineTreeOid); - assert.notEqual(binding, receipt.binding); - assert.deepEqual(binding, receipt.binding); - assert.equal(existsSync(join(binding.worktreePath, '.maka-workspace.json')), false); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('publishes only a revocable execution scope through its accepted handle', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - - assert.equal('binding' in accepted, false); - assert.equal('receipt' in accepted, false); - let retainedScope: unknown; - const execution = await owner.withManagedWorkspaceExecution( - accepted.executionHandle, - async (scope) => { - retainedScope = scope; - assert.equal('cwd' in scope, false); - assert.equal(scope.kind, 'managed_workspace_execution_scope_v1'); - await assert.rejects( - () => - owner.executeReadOnlyFilesystemOperation(scope, { - kind: 'read', - path: 'README.md', - }), - (error) => - error instanceof ManagedWorkspaceOwnerError && - error.code === 'managed_workspace_worker_unavailable', - ); - return 'admitted'; - }, - ); - - assert.equal(execution, 'admitted'); - assert.deepEqual(retainedScope, { kind: 'managed_workspace_execution_scope_v1' }); - assert.throws( - () => - inspectManagedWorkspaceExecutionScopeInternal({ - kind: 'managed_workspace_execution_scope_v1', - }), - (error) => - error instanceof ManagedWorkspaceExecutionAuthorityError && - error.code === 'managed_workspace_execution_scope_invalid', - ); - assert.throws( - () => - inspectManagedWorkspaceExecutionScopeInternal( - retainedScope as ManagedWorkspaceExecutionScope, - ), - (error) => - error instanceof ManagedWorkspaceExecutionAuthorityError && - error.code === 'managed_workspace_execution_scope_expired', - ); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('does not let caller-mutated head state or a shadowed public reader forge execution authority', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - let callbackCalled = false; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - const forgedHead = { - ...accepted.head, - workspaceVersionId: `version_${'a'.repeat(32)}`, - commitOid: 'a'.repeat(40), - treeOid: 'b'.repeat(40), - revision: accepted.head.revision + 1, - }; - assert.throws(() => Object.assign(accepted.head, forgedHead), TypeError); - Object.defineProperty(runtimeStore, 'readWorkspaceHead', { - configurable: true, - value: async () => forgedHead, - }); - - await owner.withManagedWorkspaceExecution(accepted.executionHandle, async (scope) => { - callbackCalled = true; - const context = inspectManagedWorkspaceExecutionScopeInternal(scope); - assert.equal(context.head.workspaceVersionId, accepted.head.workspaceVersionId); - assert.equal(context.head.commitOid, accepted.head.commitOid); - assert.equal(context.head.treeOid, accepted.head.treeOid); - }); - assert.equal(callbackCalled, true); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('quarantines drift introduced after execution artifact verification before publishing cwd', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - let executionFailpointArmed = false; - let callbackCalled = false; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - async failpoint(point) { - if ( - executionFailpointArmed && - (point as string) === 'after_execution_artifact_verification' - ) { - executionFailpointArmed = false; - const acceptedEvidence = inspectManagedWorkspaceExecutionHandleInternal( - accepted.executionHandle, - ); - await writeFile( - join(acceptedEvidence.binding.worktreePath, 'tracked.txt'), - 'external drift after verification\n', - 'utf8', - ); - } - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - executionFailpointArmed = true; - - await assert.rejects( - owner.withManagedWorkspaceExecution(accepted.executionHandle, async () => { - callbackCalled = true; - }), - isOwnerError('managed_workspace_quarantined'), - ); - - assert.equal(callbackCalled, false); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects execution when runtime.sqlite detaches from its canonical path after verification', { - skip: - process.platform === 'win32' - ? 'Open SQLite files cannot be renamed reliably on Windows' - : false, -}, async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const databasePath = join(storageRoot, 'runtime.sqlite'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(databasePath); - let executionFailpointArmed = false; - let callbackCalled = false; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - async failpoint(point) { - if ( - executionFailpointArmed && - (point as string) === 'after_execution_artifact_verification' - ) { - executionFailpointArmed = false; - await rename(databasePath, `${databasePath}.detached`); - } - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - executionFailpointArmed = true; - - await assert.rejects( - owner.withManagedWorkspaceExecution(accepted.executionHandle, async () => { - callbackCalled = true; - }), - /database file identity changed|belongs to a different storage root/u, - ); - assert.equal(callbackCalled, false); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects a forged execution handle before invoking the tool callback', async () => { - const storageRoot = await temporaryRoot(); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - let callbackCalled = false; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - - await assert.rejects( - owner.withManagedWorkspaceExecution( - Object.freeze({ - kind: 'managed_workspace_execution_handle_v1', - }) as ManagedWorkspaceExecutionHandle, - async () => { - callbackCalled = true; - }, - ), - isOwnerError('managed_workspace_execution_handle_invalid'), - ); - - assert.equal(callbackCalled, false); - await owner.close(); - } finally { - await rootOwner.close(); - } -}); - -test('rejects an execution handle issued by another managed workspace owner', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const leftCapability = trackControlDirectory( - await resolveStorageRoot({ path: join(root, 'left-storage'), kind: 'interactive' }), - ); - const rightCapability = trackControlDirectory( - await resolveStorageRoot({ path: join(root, 'right-storage'), kind: 'interactive' }), - ); - const leftRootOwner = await tryAcquireInteractiveRootOwner(leftCapability); - const rightRootOwner = await tryAcquireInteractiveRootOwner(rightCapability); - assert.ok(leftRootOwner); - assert.ok(rightRootOwner); - const leftStore = createSqliteRuntimeStore(join(leftCapability.canonicalPath, 'runtime.sqlite')); - try { - const leftOwner = await openManagedWorkspaceOwner({ - rootOwner: leftRootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const rightOwner = await openManagedWorkspaceOwner({ - rootOwner: rightRootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const accepted = await leftOwner.openManagedWorkspaceBaseline( - leftStore, - openRequest(sourceRoot), - ); - - await assert.rejects( - rightOwner.withManagedWorkspaceExecution(accepted.executionHandle, async () => {}), - isOwnerError('managed_workspace_execution_handle_invalid'), - ); - - await leftOwner.close(); - await rightOwner.close(); - } finally { - leftStore.close(); - await leftRootOwner.close(); - await rightRootOwner.close(); - } -}); - -test('drains an admitted managed execution before owner close completes', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - let releaseExecution!: () => void; - const executionMayFinish = new Promise((resolve) => { - releaseExecution = resolve; - }); - let executionAdmitted!: () => void; - const executionStarted = new Promise((resolve) => { - executionAdmitted = resolve; - }); - let executing: Promise | undefined; - let closing: Promise | undefined; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - executing = owner.withManagedWorkspaceExecution(accepted.executionHandle, async () => { - executionAdmitted(); - await executionMayFinish; - }); - await executionStarted; - - let closeSettled = false; - closing = owner.close().then(() => { - closeSettled = true; - }); - assert.equal( - await Promise.race([closing.then(() => 'closed'), delay(250, 'pending')]), - 'pending', - ); - assert.equal(closeSettled, false); - await assert.rejects( - owner.withManagedWorkspaceExecution(accepted.executionHandle, async () => {}), - isOwnerError('managed_workspace_owner_closing'), - ); - - releaseExecution(); - await executing; - await closing; - assert.equal(owner.state, 'closed'); - } finally { - releaseExecution(); - await Promise.allSettled([executing, closing].filter((value) => value !== undefined)); - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('routes read-only execution through the owner-bound worker bridge', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - const workerCalls: Array<{ cwd: string; operation: { kind: string } }> = []; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - filesystemWorker: { - async execute(input) { - workerCalls.push(input); - return { kind: 'read', content: 'ok' }; - }, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - - await owner.withManagedWorkspaceExecution(accepted.executionHandle, async (scope) => { - assert.deepEqual( - await owner.executeReadOnlyFilesystemOperation(scope, { - kind: 'read', - path: 'README.md', - }), - { kind: 'read', content: 'ok' }, - ); - }); - - assert.equal(workerCalls.length, 1); - assert.equal(workerCalls[0]?.operation.kind, 'read'); - assert.equal(workerCalls[0]?.cwd.endsWith('worktree'), true); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('revokes the scope and releases owner residency when the filesystem worker fails', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - let retainedScope: ManagedWorkspaceExecutionScope | undefined; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - filesystemWorker: { - async execute() { - throw new Error('simulated filesystem worker crash'); - }, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - - await assert.rejects( - owner.withManagedWorkspaceExecution(accepted.executionHandle, async (scope) => { - retainedScope = scope; - return await owner.executeReadOnlyFilesystemOperation(scope, { - kind: 'read', - path: 'README.md', - }); - }), - /simulated filesystem worker crash/u, - ); - - if (!retainedScope) throw new Error('Execution callback did not expose its scope'); - const expiredScope = retainedScope; - assert.throws( - () => inspectManagedWorkspaceExecutionScopeInternal(expiredScope), - /execution scope has expired/u, - ); - await owner.close(); - assert.equal(owner.state, 'closed'); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects owner close reentrancy from an active execution callback', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - - await owner.withManagedWorkspaceExecution(accepted.executionHandle, async () => { - const disposition = await Promise.race([ - owner.close().then( - () => 'closed' as const, - (error: unknown) => error, - ), - delay(250, 'pending' as const), - ]); - assert.ok(isOwnerError('managed_workspace_owner_reentrant_close')(disposition)); - }); - - assert.equal(owner.state, 'ready'); - await owner.close(); - assert.equal(owner.state, 'closed'); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('allows concurrent read-only scopes for one handle and close drains both', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - let releaseFirst!: () => void; - const firstReleased = new Promise((resolve) => { - releaseFirst = resolve; - }); - let releaseSecond!: () => void; - const secondReleased = new Promise((resolve) => { - releaseSecond = resolve; - }); - let bothAdmitted!: () => void; - const bothScopesActive = new Promise((resolve) => { - bothAdmitted = resolve; - }); - let activeScopes = 0; - let executions: Promise[] = []; - let closing: Promise | undefined; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - const execute = (released: Promise) => - owner.withManagedWorkspaceExecution(accepted.executionHandle, async (scope) => { - assert.equal(inspectManagedWorkspaceExecutionScopeInternal(scope).workspaceEffect, 'none'); - activeScopes += 1; - if (activeScopes === 2) bothAdmitted(); - await released; - activeScopes -= 1; - }); - - executions = [execute(firstReleased), execute(secondReleased)]; - assert.equal( - await Promise.race([bothScopesActive.then(() => 'active'), delay(20_000, 'timeout')]), - 'active', - ); - assert.equal(activeScopes, 2); - - closing = owner.close(); - assert.equal( - await Promise.race([closing.then(() => 'closed'), delay(250, 'pending')]), - 'pending', - ); - releaseFirst(); - assert.equal( - await Promise.race([closing.then(() => 'closed'), delay(250, 'pending')]), - 'pending', - ); - releaseSecond(); - await Promise.all(executions); - await closing; - assert.equal(owner.state, 'closed'); - } finally { - releaseFirst(); - releaseSecond(); - await Promise.allSettled([...executions, closing].filter((value) => value !== undefined)); - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('expires the execution scope and releases owner residency when its callback rejects', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - let retainedScope: ManagedWorkspaceExecutionScope | undefined; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - - await assert.rejects( - owner.withManagedWorkspaceExecution(accepted.executionHandle, async (scope) => { - retainedScope = scope; - throw new Error('simulated managed tool failure'); - }), - /simulated managed tool failure/u, - ); - if (!retainedScope) throw new Error('Execution callback did not expose its scope'); - const expiredScope = retainedScope; - assert.throws( - () => inspectManagedWorkspaceExecutionScopeInternal(expiredScope), - /execution scope has expired/u, - ); - await owner.close(); - assert.equal(owner.state, 'closed'); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('drains an admitted workspace operation before closing and rejects new work', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - let releaseOperation!: () => void; - const operationMayFinish = new Promise((resolve) => { - releaseOperation = resolve; - }); - let operationAdmitted!: () => void; - const operationReachedFailpoint = new Promise((resolve) => { - operationAdmitted = resolve; - }); - let creating: - | ReturnType< - Awaited>['openManagedWorkspaceBaseline'] - > - | undefined; - let closing: Promise | undefined; - let rootClosing: Promise | undefined; - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - failpoint: async (point) => { - if (point !== 'after_worktree_materialized') return; - operationAdmitted(); - await operationMayFinish; - }, - }); - creating = owner.openManagedWorkspaceBaseline(runtimeStore, openRequest(sourceRoot)); - await operationReachedFailpoint; - - let closeSettled = false; - closing = owner.close().then(() => { - closeSettled = true; - }); - let rootCloseSettled = false; - rootClosing = rootOwner.close().then(() => { - rootCloseSettled = true; - }); - const rootCloseDisposition = await Promise.race([ - rootClosing.then(() => 'closed' as const), - delay(250, 'pending' as const), - ]); - - assert.equal(owner.state, 'closing'); - assert.equal(closeSettled, false); - assert.equal(rootCloseSettled, false); - assert.equal(rootCloseDisposition, 'pending'); - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, openRequest(sourceRoot)), - isOwnerError('managed_workspace_owner_closing'), - ); - - releaseOperation(); - await creating; - await closing; - await rootClosing; - assert.equal(owner.state, 'closed'); - } finally { - releaseOperation(); - await Promise.allSettled( - [creating, closing, rootClosing].filter((value) => value !== undefined), - ); - runtimeStore.close(); - await rootOwner.close(); - } -}); - -test('rejects external drift instead of reopening a non-ready workspace', async () => { - const root = await temporaryRoot(); - const storageRoot = join(root, 'storage'); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const capability = trackControlDirectory( - await resolveStorageRoot({ path: storageRoot, kind: 'interactive' }), - ); - const rootOwner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(rootOwner); - const runtimeStore = createSqliteRuntimeStore(join(storageRoot, 'runtime.sqlite')); - try { - const owner = await openManagedWorkspaceOwner({ - rootOwner, - gitRuntime: { - executablePath: gitExecutablePath, - expectedSha256: gitExecutableSha256, - }, - }); - const accepted = await owner.openManagedWorkspaceBaseline( - runtimeStore, - openRequest(sourceRoot), - ); - const { binding } = inspectManagedWorkspaceExecutionHandleInternal(accepted.executionHandle); - await writeFile(join(binding.worktreePath, 'tracked.txt'), 'external drift\n', 'utf8'); - - await assert.rejects( - owner.openManagedWorkspaceBaseline(runtimeStore, openRequest(sourceRoot)), - (error: unknown) => - error instanceof Error && - error.name === 'GitWorkspaceServiceError' && - 'code' in error && - error.code === 'managed_workspace_drifted', - ); - - assert.equal(existsSync(binding.worktreePath), true); - await owner.close(); - } finally { - runtimeStore.close(); - await rootOwner.close(); - } -}); - -function isOwnerError(code: string): (error: unknown) => boolean { - return (error) => error instanceof ManagedWorkspaceOwnerError && error.code === code; -} - -async function temporaryRoot(): Promise { - const root = await mkdtemp(join(tmpdir(), 'maka-managed-workspace-owner-')); - cleanup.push(root); - return root; -} - -async function findGitExecutable(): Promise { - const command = process.platform === 'win32' ? 'where.exe' : 'which'; - const { stdout } = await execFileAsync(command, ['git'], { encoding: 'utf8' }); - const first = stdout - .split(/\r?\n/u) - .map((line) => line.trim()) - .find(Boolean); - if (!first) throw new Error('Git executable is unavailable for integration tests'); - return realpath(first); -} - -async function sha256File(path: string): Promise<`sha256:${string}`> { - const hash = createHash('sha256'); - for await (const chunk of createReadStream(path)) hash.update(chunk); - return `sha256:${hash.digest('hex')}`; -} - -async function createEligibleSource(sourceRoot: string): Promise { - await mkdir(sourceRoot, { recursive: true }); - await git(sourceRoot, 'init', '--quiet'); - await writeFile(join(sourceRoot, 'tracked.txt'), 'tracked\n', 'utf8'); - await writeFile(join(sourceRoot, '.gitignore'), '.maka-workspace.json\n', 'utf8'); - await writeFile(join(sourceRoot, '.maka-workspace.json'), '{"host":"metadata"}\n', 'utf8'); - await git(sourceRoot, 'add', 'tracked.txt', '.gitignore'); - await git( - sourceRoot, - '-c', - 'user.name=Maka Test', - '-c', - 'user.email=test@maka.invalid', - 'commit', - '--quiet', - '-m', - 'source baseline', - ); - return realpath(sourceRoot); -} - -function openRequest(sourceRoot: string) { - return { - repositoryId: 'repository_11111111111111111111111111111111', - workspaceId: 'workspace_22222222222222222222222222222222', - workspaceEpochId: 'epoch_33333333333333333333333333333333', - workspaceInstanceId: 'instance_44444444444444444444444444444444', - sourceRoot, - } as const; -} - -async function git(cwd: string, ...args: string[]): Promise { - const { stdout } = await execFileAsync('git', args, { - cwd, - encoding: 'utf8', - maxBuffer: 8 * 1024 * 1024, - }); - return stdout.trim(); -} diff --git a/packages/storage/src/__tests__/managed-workspace-worker-bridge-internal.test.ts b/packages/storage/src/__tests__/managed-workspace-worker-bridge-internal.test.ts deleted file mode 100644 index 188271d2ae..0000000000 --- a/packages/storage/src/__tests__/managed-workspace-worker-bridge-internal.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import assert from 'node:assert/strict'; -import test from 'node:test'; -import { createReadOnlyPermissionProfile } from '@maka/core/permission-profile'; -import { - issueManagedWorkspaceExecutionScopeInternal, - revokeManagedWorkspaceExecutionScopeInternal, -} from '../managed-workspace-execution-authority-internal.js'; -import { - createManagedWorkspaceWorkerBridgeInternal, - ManagedWorkspaceWorkerBridgeError, -} from '../managed-workspace-worker-bridge-internal.js'; - -function scopeFor(ownerToken: object) { - return issueManagedWorkspaceExecutionScopeInternal(ownerToken, { - provisioning: 'canonical_tree_only_v1', - workspaceEffect: 'none', - cwd: '/managed/worktree', - binding: Object.freeze({}) as never, - head: Object.freeze({}) as never, - }); -} - -test('injects the owner-bound cwd and read-only boundary for allowed operations', async () => { - const ownerToken = {}; - const calls: unknown[] = []; - const bridge = createManagedWorkspaceWorkerBridgeInternal(ownerToken, { - async execute(input) { - calls.push(input); - switch (input.operation.kind) { - case 'read': - return { kind: 'read', content: 'ok' }; - case 'glob': - return { kind: 'glob', files: [] }; - case 'grep': - return { kind: 'grep', matches: [] }; - } - }, - }); - const scope = scopeFor(ownerToken); - - assert.deepEqual(await bridge.execute(scope, { kind: 'read', path: 'README.md' }), { - kind: 'read', - content: 'ok', - }); - assert.deepEqual(await bridge.execute(scope, { kind: 'glob', path: '.', pattern: '**/*.ts' }), { - kind: 'glob', - files: [], - }); - assert.deepEqual( - await bridge.execute(scope, { - kind: 'grep', - path: '.', - pattern: 'TODO', - maxCountPerFile: 10, - limit: 100, - timeoutMs: 1_000, - }), - { kind: 'grep', matches: [] }, - ); - - for (const call of calls as Array<{ - cwd: string; - executionBoundary: unknown; - }>) { - assert.equal(call.cwd, '/managed/worktree'); - assert.deepEqual(call.executionBoundary, { - kind: 'managed', - profile: createReadOnlyPermissionProfile(), - revision: 0, - }); - } -}); - -test('rejects foreign, expired, mutating, and unknown operations before worker dispatch', async () => { - const ownerToken = {}; - let dispatches = 0; - const bridge = createManagedWorkspaceWorkerBridgeInternal(ownerToken, { - async execute() { - dispatches += 1; - return { kind: 'read', content: 'unexpected' }; - }, - }); - const foreignScope = scopeFor({}); - await assert.rejects(() => bridge.execute(foreignScope, { kind: 'read', path: 'README.md' })); - - const expiredScope = scopeFor(ownerToken); - revokeManagedWorkspaceExecutionScopeInternal(ownerToken, expiredScope); - await assert.rejects(() => bridge.execute(expiredScope, { kind: 'read', path: 'README.md' })); - - const activeScope = scopeFor(ownerToken); - for (const operation of [ - { kind: 'write', path: 'a.txt', content: 'unsafe' }, - { kind: 'edit', path: 'a.txt', oldString: 'a', newString: 'b' }, - { kind: 'format_json', path: 'a.json', sortKeys: true }, - { kind: 'unknown', path: '.' }, - ]) { - await assert.rejects( - () => bridge.execute(activeScope, operation as never), - (error) => - error instanceof ManagedWorkspaceWorkerBridgeError && - error.code === 'managed_workspace_operation_denied', - ); - } - assert.equal(dispatches, 0); -}); diff --git a/packages/storage/src/__tests__/public-entrypoints.test.ts b/packages/storage/src/__tests__/public-entrypoints.test.ts index d3d185cb93..bdbb85bf7c 100644 --- a/packages/storage/src/__tests__/public-entrypoints.test.ts +++ b/packages/storage/src/__tests__/public-entrypoints.test.ts @@ -55,7 +55,6 @@ const SQLITE_BACKED_ENTRYPOINTS = [ './git-worktree-child-executor', './goal-authority', './interaction-store', - './managed-workspace-owner', './model-call-ledger', './operational-state-store', './plan-authority', @@ -68,7 +67,6 @@ const SQLITE_BACKED_ENTRYPOINTS = [ './session-store', './shell-run-authority', './shell-run-store', - './sqlite-runtime-store', './sqlite-session-metadata-store', './storage-writer-composition', './task-ledger-authority', diff --git a/packages/storage/src/dugite-native-environment.ts b/packages/storage/src/dugite-native-environment.ts deleted file mode 100644 index 2797207445..0000000000 --- a/packages/storage/src/dugite-native-environment.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { posix, win32 } from 'node:path'; - -export interface BundledGitEnvironmentInput { - readonly platform: NodeJS.Platform; - readonly arch: string; - readonly rootPath: string; - readonly executablePath: string; -} - -export function bundledGitEnvironment(input: BundledGitEnvironmentInput): NodeJS.ProcessEnv { - const path = input.platform === 'win32' ? win32 : posix; - const env: NodeJS.ProcessEnv = { - PATH: path.dirname(input.executablePath), - }; - if (input.platform === 'win32') { - const platformRoot = path.join( - input.rootPath, - input.arch === 'x64' ? 'mingw64' : input.arch === 'arm64' ? 'clangarm64' : 'mingw32', - ); - env.PATH = [ - path.dirname(input.executablePath), - path.join(platformRoot, 'bin'), - path.join(platformRoot, 'usr', 'bin'), - ].join(';'); - env.GIT_EXEC_PATH = path.join(platformRoot, 'libexec', 'git-core'); - return env; - } - env.GIT_EXEC_PATH = path.join(input.rootPath, 'libexec', 'git-core'); - env.GIT_TEMPLATE_DIR = path.join(input.rootPath, 'share', 'git-core', 'templates'); - if (input.platform === 'linux') { - env.PREFIX = input.rootPath; - env.GIT_SSL_CAINFO = path.join(input.rootPath, 'ssl', 'cacert.pem'); - } - return env; -} diff --git a/packages/storage/src/git-workspace-service.ts b/packages/storage/src/git-workspace-service.ts deleted file mode 100644 index 9d4954f5bb..0000000000 --- a/packages/storage/src/git-workspace-service.ts +++ /dev/null @@ -1,2933 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { execFile, spawn, type ChildProcess } from 'node:child_process'; -import { createHash, randomBytes, randomUUID } from 'node:crypto'; -import { createReadStream } from 'node:fs'; -import { - lstat, - mkdir, - open as openFile, - readFile, - realpath, - rename, - rm, - stat, -} from 'node:fs/promises'; -import { dirname, isAbsolute, join, normalize, relative, resolve } from 'node:path'; -import { promisify } from 'node:util'; -import { withArtifactWriterLock } from './artifact-writer-lock.js'; -import { bundledGitEnvironment } from './dugite-native-environment.js'; -import { registerManagedBaselineReceiptAuthorityInternal } from './managed-baseline-receipt-authority-internal.js'; - -const execFileAsync = promisify(execFile); -const GIT_TIMEOUT_MS = 2 * 60 * 1_000; -const GIT_MAX_BUFFER_BYTES = 16 * 1024 * 1024; -const BINDING_SCHEMA_VERSION = 1; -const REPOSITORY_SCHEMA_VERSION = 1; -const EPOCH_ARTIFACT_SCHEMA_VERSION = 1; -const QUARANTINE_INTENT_SCHEMA_VERSION = 1; -const BASELINE_RECEIPT_SCHEMA_VERSION = 1; -const IDENTIFIER_PATTERN = /^(repository|workspace|epoch|instance)_[a-f0-9]{32}$/u; -const SHA256_PATTERN = /^sha256:[a-f0-9]{64}$/u; -const OID_PATTERN = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u; -const BINDING_KEYS = [ - 'schemaVersion', - 'protocol', - 'repositoryId', - 'workspaceId', - 'workspaceEpochId', - 'workspaceInstanceId', - 'sourceRoot', - 'sourceGitCommonDir', - 'sourceHeadCommitOid', - 'sourceTreeOid', - 'repositoryPath', - 'worktreePath', - 'hooksPath', - 'baselineCommitOid', - 'baselineTreeOid', - 'headRef', - 'gitRuntimeSha256', - 'objectFormat', - 'materializationProfileDigest', - 'materializationSemantics', -] as const; -const REPOSITORY_KEYS = [ - 'schemaVersion', - 'protocol', - 'repositoryId', - 'repositoryPath', - 'hooksPath', - 'gitRuntimeSha256', - 'objectFormat', - 'repositoryCapabilityDigest', -] as const; -const EPOCH_ARTIFACT_KEYS = [ - 'schemaVersion', - 'protocol', - 'repositoryId', - 'workspaceId', - 'workspaceEpochId', - 'sourceRoot', - 'sourceGitCommonDir', - 'sourceHeadCommitOid', - 'sourceTreeOid', - 'baselineCommitOid', - 'baselineTreeOid', - 'baselineRef', - 'headRef', - 'gitRuntimeSha256', - 'objectFormat', - 'materializationProfileDigest', - 'materializationSemantics', -] as const; -const QUARANTINE_INTENT_KEYS = [ - 'schemaVersion', - 'protocol', - 'reason', - 'quarantinePath', - 'binding', -] as const; -const QUARANTINE_RECORD_KEYS = ['protocol', 'reason', 'binding'] as const; -const BASELINE_RECEIPT_KEYS = [ - 'schemaVersion', - 'protocol', - 'binding', - 'workspaceVersionId', - 'policyVersion', - 'policyHash', - 'epochOpenedEventId', - 'baselineAcceptedEventId', - 'treeDeltaDigest', - 'changedFileCount', - 'deletedFileCount', -] as const; -const MATERIALIZATION_SEMANTICS = 'git_tree_materialized_with_fixed_config_v1'; -const MANAGED_BASELINE_POLICY_V1 = { - protocol: 'maka_managed_workspace_baseline_policy_v1', - source: 'source_head_tree', - sourceWorkingTree: 'clean_tracked_and_untracked_except_ignored', - trackedFiles: 'include', - untrackedFiles: 'exclude', - ignoredFiles: 'exclude', - pathEncoding: 'utf8_lossless_roundtrip_required', - caseCollisions: 'reject_nfc_casefold_v1', - symlinks: 'reject', - submodules: 'reject', - attributes: 'reject', - specialModes: 'reject', - materialization: MATERIALIZATION_SEMANTICS, -} as const; -const MANAGED_BASELINE_POLICY_HASH_V1 = hashCanonicalJson(MANAGED_BASELINE_POLICY_V1); -const BASELINE_MESSAGE = 'maka managed workspace baseline v1\n'; -const BASELINE_DATE = '2000-01-01T00:00:00Z'; - -export type GitWorkspaceServiceErrorCode = - | 'git_runtime_unavailable' - | 'git_runtime_integrity_mismatch' - | 'git_workspace_operation_failed' - | 'repository_ineligible' - | 'source_changed_during_baseline_import' - | 'managed_workspace_identity_conflict' - | 'managed_workspace_unavailable' - | 'managed_workspace_drifted'; - -export class GitWorkspaceServiceError extends Error { - constructor( - readonly code: GitWorkspaceServiceErrorCode, - message: string, - options?: ErrorOptions, - ) { - super(message, options); - this.name = 'GitWorkspaceServiceError'; - } -} - -interface GitRuntimeExecutableIdentity { - readonly executablePath: string; - readonly expectedSha256: `sha256:${string}`; -} - -export type VerifiedGitRuntimeInput = GitRuntimeExecutableIdentity & - ( - | { - readonly distribution?: undefined; - readonly runtimeIdentitySha256?: undefined; - } - | { - readonly distribution: { - readonly kind: 'dugite_native_v1'; - readonly rootPath: string; - }; - /** Stable identity of the complete declared distribution. */ - readonly runtimeIdentitySha256: `sha256:${string}`; - } - ); - -export interface CreateGitWorkspaceServiceInput { - readonly storageRoot: string; - readonly gitRuntime: VerifiedGitRuntimeInput; - readonly failpoint?: (point: GitWorkspaceServiceFailpoint) => void | Promise; -} - -export type GitWorkspaceServiceFailpoint = - | 'after_repository_record' - | 'after_baseline_ref_created' - | 'after_worktree_materialized' - | 'after_worktree_locked' - | 'after_head_ref_updated' - | 'after_quarantine_intent' - | 'after_quarantine_unlock' - | 'after_quarantine_move' - | 'after_quarantine_binding_removed' - | 'after_quarantine_pruned' - | 'after_baseline_receipt'; - -export interface ManagedWorkspaceIdentity { - /** - * Identifies the Maka-owned Git object universe. It is not the identity of the - * source checkout; sourceRoot, sourceGitCommonDir, HEAD, and tree OIDs carry - * source provenance and must be validated independently. - */ - readonly repositoryId: string; - readonly workspaceId: string; - readonly workspaceEpochId: string; - readonly workspaceInstanceId: string; -} - -export interface CreateManagedWorkspaceFromSourceInput extends ManagedWorkspaceIdentity { - readonly sourceRoot: string; -} - -export interface ManagedWorkspaceBinding { - readonly schemaVersion: 1; - readonly protocol: 'git_managed_workspace_v1'; - readonly repositoryId: string; - readonly workspaceId: string; - readonly workspaceEpochId: string; - readonly workspaceInstanceId: string; - readonly sourceRoot: string; - readonly sourceGitCommonDir: string; - readonly sourceHeadCommitOid: string; - readonly sourceTreeOid: string; - readonly repositoryPath: string; - readonly worktreePath: string; - readonly hooksPath: string; - readonly baselineCommitOid: string; - readonly baselineTreeOid: string; - readonly headRef: string; - readonly gitRuntimeSha256: `sha256:${string}`; - readonly objectFormat: 'sha1' | 'sha256'; - readonly materializationProfileDigest: `sha256:${string}`; - readonly materializationSemantics: typeof MATERIALIZATION_SEMANTICS; -} - -export type ManagedWorkspaceInspection = - | { - readonly state: 'ready'; - readonly commitOid: string; - readonly treeOid: string; - } - | { - readonly state: 'drifted'; - readonly commitOid?: string; - readonly treeOid?: string; - readonly status: string; - }; - -export interface ManagedWorkspaceQuarantine { - readonly quarantinePath: string; - readonly reason: string; -} - -export interface ManagedWorkspaceBaselineReceiptV1 { - readonly schemaVersion: 1; - readonly protocol: 'maka_managed_workspace_baseline_receipt_v1'; - readonly binding: ManagedWorkspaceBinding; - readonly workspaceVersionId: string; - readonly policyVersion: 1; - readonly policyHash: `sha256:${string}`; - readonly epochOpenedEventId: string; - readonly baselineAcceptedEventId: string; - readonly treeDeltaDigest: `sha256:${string}`; - readonly changedFileCount: number; - readonly deletedFileCount: 0; -} - -export interface GitWorkspaceService { - assertAvailable(): Promise; - createManagedWorkspaceFromSource( - input: CreateManagedWorkspaceFromSourceInput, - ): Promise; - openManagedWorkspaceFromBinding( - input: ManagedWorkspaceIdentity, - ): Promise; - inspectManagedWorkspace(binding: ManagedWorkspaceBinding): Promise; - quarantineManagedWorkspace( - binding: ManagedWorkspaceBinding, - reason: string, - ): Promise; -} - -export function createGitWorkspaceService( - input: CreateGitWorkspaceServiceInput, -): GitWorkspaceService { - return new GitWorkspaceServiceImpl(input); -} - -interface SourceRepositoryInspection { - readonly sourceRoot: string; - readonly gitCommonDir: string; - readonly headCommitOid: string; - readonly treeOid: string; - readonly objectFormat: 'sha1' | 'sha256'; -} - -interface GitTreeEntry { - readonly mode: string; - readonly objectType: string; - readonly oid: string; - readonly path: string; - readonly pathBytesBase64: string; -} - -interface BaselineTreeSummary { - readonly treeDeltaDigest: `sha256:${string}`; - readonly changedFileCount: number; -} - -interface ManagedRepositoryRecord { - readonly schemaVersion: 1; - readonly protocol: 'maka_managed_git_repository_v1'; - readonly repositoryId: string; - readonly repositoryPath: string; - readonly hooksPath: string; - readonly gitRuntimeSha256: `sha256:${string}`; - readonly objectFormat: 'sha1' | 'sha256'; - readonly repositoryCapabilityDigest: `sha256:${string}`; -} - -interface ManagedWorkspaceEpochArtifact { - readonly schemaVersion: 1; - readonly protocol: 'maka_managed_workspace_epoch_artifact_v1'; - readonly repositoryId: string; - readonly workspaceId: string; - readonly workspaceEpochId: string; - readonly sourceRoot: string; - readonly sourceGitCommonDir: string; - readonly sourceHeadCommitOid: string; - readonly sourceTreeOid: string; - readonly baselineCommitOid: string; - readonly baselineTreeOid: string; - readonly baselineRef: string; - readonly headRef: string; - readonly gitRuntimeSha256: `sha256:${string}`; - readonly objectFormat: 'sha1' | 'sha256'; - readonly materializationProfileDigest: `sha256:${string}`; - readonly materializationSemantics: typeof MATERIALIZATION_SEMANTICS; -} - -interface ManagedWorkspaceQuarantineIntent { - readonly schemaVersion: 1; - readonly protocol: 'maka_managed_workspace_quarantine_intent_v1'; - readonly reason: string; - readonly quarantinePath: string; - readonly binding: ManagedWorkspaceBinding; -} - -interface ManagedWorkspaceQuarantineRecord { - readonly protocol: 'maka_managed_workspace_quarantine_v1'; - readonly reason: string; - readonly binding: ManagedWorkspaceBinding; -} - -interface WorkspaceLayout { - readonly managedRoot: string; - readonly repositoryRoot: string; - readonly repositoryPath: string; - readonly repositoryRecordPath: string; - readonly hooksPath: string; - readonly homePath: string; - readonly epochRoot: string; - readonly epochArtifactPath: string; - readonly instanceRoot: string; - readonly bindingPath: string; - readonly baselineReceiptPath: string; - readonly worktreePath: string; - readonly quarantineRoot: string; - readonly quarantineIntentRoot: string; - readonly quarantineIntentPath: string; -} - -class GitWorkspaceServiceImpl implements GitWorkspaceService { - private readonly runtime: VerifiedGitRuntime; - - constructor(private readonly input: CreateGitWorkspaceServiceInput) { - if (!isAbsolute(input.storageRoot)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace storage root must be absolute', - ); - } - this.runtime = new VerifiedGitRuntime(input.gitRuntime); - registerManagedBaselineReceiptAuthorityInternal(this, { - issue: (binding) => this.#createOrReuseManagedWorkspaceBaselineReceipt(binding), - require: (request) => this.#requireManagedWorkspaceBaselineReceipt(request), - verify: (receipt) => this.#verifyManagedWorkspaceBaselineReceipt(receipt), - }); - } - - async assertAvailable(): Promise { - await this.runtime.verify(); - await withArtifactWriterLock(this.input.storageRoot, async () => undefined); - } - - async createManagedWorkspaceFromSource( - input: CreateManagedWorkspaceFromSourceInput, - ): Promise { - assertOpenIdentity(input); - const runtime = await this.runtime.verify(); - - return withArtifactWriterLock(this.input.storageRoot, async (canonicalStorageRoot) => { - const layout = workspaceLayout(canonicalStorageRoot, input); - await ensureOwnedDirectory(layout.managedRoot, canonicalStorageRoot); - await ensureOwnedDirectory(layout.quarantineRoot, layout.managedRoot); - await ensureOwnedDirectory(layout.homePath, layout.managedRoot); - await assertOwnedManagedWorkspaceLayout(canonicalStorageRoot, layout); - - const quarantined = await this.resumePendingQuarantine(input, layout, runtime.digest); - if (quarantined) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed workspace instance was quarantined: ${input.workspaceInstanceId}`, - ); - } - - const existingBinding = await readBinding(layout.bindingPath); - if (existingBinding) { - assertBindingMatches(existingBinding, input, layout, runtime.digest); - return this.adoptStoredBinding(input, existingBinding, layout); - } - - const sourceRoot = await canonicalDirectory(input.sourceRoot, 'repository_ineligible'); - const source = await this.inspectSourceRepository(sourceRoot); - const repository = await this.openRepository(input, source, layout, runtime.digest); - const epoch = await this.openEpochArtifact(input, source, repository, layout); - await this.clearIncompleteInstance(input, epoch, layout); - await ensureOwnedDirectory(layout.instanceRoot, layout.managedRoot); - await this.runtime.run( - [ - '--git-dir', - repository.repositoryPath, - 'worktree', - 'add', - '--quiet', - '--detach', - layout.worktreePath, - epoch.baselineCommitOid, - ], - layout.homePath, - ); - await this.input.failpoint?.('after_worktree_materialized'); - - await this.runtime.run( - [ - '--git-dir', - repository.repositoryPath, - 'worktree', - 'lock', - '--reason', - worktreeLockReason(input), - layout.worktreePath, - ], - layout.homePath, - ); - await this.input.failpoint?.('after_worktree_locked'); - await this.updateRefCas( - repository.repositoryPath, - epoch.headRef, - epoch.baselineCommitOid, - repository.objectFormat, - layout.homePath, - ); - await this.input.failpoint?.('after_head_ref_updated'); - - const binding: ManagedWorkspaceBinding = { - schemaVersion: BINDING_SCHEMA_VERSION, - protocol: 'git_managed_workspace_v1', - repositoryId: input.repositoryId, - workspaceId: input.workspaceId, - workspaceEpochId: input.workspaceEpochId, - workspaceInstanceId: input.workspaceInstanceId, - sourceRoot: epoch.sourceRoot, - sourceGitCommonDir: epoch.sourceGitCommonDir, - sourceHeadCommitOid: epoch.sourceHeadCommitOid, - sourceTreeOid: epoch.sourceTreeOid, - repositoryPath: repository.repositoryPath, - worktreePath: normalize(layout.worktreePath), - hooksPath: repository.hooksPath, - baselineCommitOid: epoch.baselineCommitOid, - baselineTreeOid: epoch.baselineTreeOid, - headRef: epoch.headRef, - gitRuntimeSha256: runtime.digest, - objectFormat: repository.objectFormat, - materializationProfileDigest: epoch.materializationProfileDigest, - materializationSemantics: MATERIALIZATION_SEMANTICS, - }; - const inspection = await this.inspectBinding(binding, layout); - if (inspection.state !== 'ready') { - await this.beginQuarantine(binding, layout, 'initial_materialization_drift'); - throw new GitWorkspaceServiceError( - 'managed_workspace_drifted', - 'Git materialization did not produce a clean managed workspace', - ); - } - await atomicWriteJson(layout.bindingPath, binding); - return binding; - }); - } - - async openManagedWorkspaceFromBinding( - input: ManagedWorkspaceIdentity, - ): Promise { - assertOpenIdentity(input); - const runtime = await this.runtime.verify(); - return withArtifactWriterLock(this.input.storageRoot, async (canonicalStorageRoot) => { - const layout = workspaceLayout(canonicalStorageRoot, input); - await assertOwnedManagedWorkspaceLayout(canonicalStorageRoot, layout); - const quarantined = await this.resumePendingQuarantine(input, layout, runtime.digest); - if (quarantined) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed workspace instance was quarantined: ${input.workspaceInstanceId}`, - ); - } - const binding = await readBinding(layout.bindingPath); - if (!binding) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed workspace binding is unavailable: ${input.workspaceInstanceId}`, - ); - } - assertBindingIdentity(binding, input, layout, runtime.digest); - return this.adoptStoredBinding(input, binding, layout); - }); - } - - private async adoptStoredBinding( - input: ManagedWorkspaceIdentity, - binding: ManagedWorkspaceBinding, - layout: WorkspaceLayout, - ): Promise { - const repository = await this.requireRepository(input, layout); - assertBindingRepository(binding, repository); - const epoch = await this.requireEpochArtifact(input, repository, layout); - assertBindingEpoch(binding, epoch); - const inspection = await this.inspectBinding(binding, layout); - if (inspection.state !== 'ready') { - throw new GitWorkspaceServiceError( - 'managed_workspace_drifted', - `Managed workspace contains unaccepted changes: ${binding.worktreePath}`, - ); - } - return binding; - } - - async inspectManagedWorkspace( - binding: ManagedWorkspaceBinding, - ): Promise { - const runtime = await this.runtime.verify(); - assertBindingShape(binding); - assertOpenIdentity(binding); - return withArtifactWriterLock(this.input.storageRoot, async (canonicalStorageRoot) => { - const layout = workspaceLayout(canonicalStorageRoot, binding); - await assertOwnedManagedWorkspaceLayout(canonicalStorageRoot, layout); - assertBindingPaths(binding, layout); - const quarantined = await this.resumePendingQuarantine(binding, layout, runtime.digest); - if (quarantined) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed workspace instance was quarantined: ${binding.workspaceInstanceId}`, - ); - } - const stored = await readBinding(layout.bindingPath); - if (!stored || !sameBinding(stored, binding)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed workspace binding is unavailable: ${binding.workspaceInstanceId}`, - ); - } - const repository = await readRepositoryRecord(layout.repositoryRecordPath); - if (!repository) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed Git repository record is unavailable: ${binding.repositoryId}`, - ); - } - assertBindingRepository(binding, repository); - await this.assertRepositoryArtifact(repository); - const epoch = await this.requireEpochArtifact(binding, repository, layout); - assertBindingEpoch(binding, epoch); - return this.inspectBinding(binding, layout); - }); - } - - async #requireManagedWorkspaceBaselineReceipt( - input: CreateManagedWorkspaceFromSourceInput, - ): Promise { - const runtime = await this.runtime.verify(); - assertOpenIdentity(input); - return withArtifactWriterLock(this.input.storageRoot, async (canonicalStorageRoot) => { - const layout = workspaceLayout(canonicalStorageRoot, input); - await assertOwnedManagedWorkspaceLayout(canonicalStorageRoot, layout); - const receipt = await readBaselineReceipt(layout.baselineReceiptPath); - if (!receipt) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - 'Canonical workspace baseline receipt is unavailable', - ); - } - assertBindingMatches(receipt.binding, input, layout, runtime.digest); - const summary = await this.requireVerifiedBaselineContext( - receipt.binding, - layout, - runtime.digest, - ); - assertBaselineReceiptMatches(receipt, receipt.binding, summary); - return receipt; - }); - } - - async #createOrReuseManagedWorkspaceBaselineReceipt( - binding: ManagedWorkspaceBinding, - ): Promise { - const runtime = await this.runtime.verify(); - assertBindingShape(binding); - assertOpenIdentity(binding); - return withArtifactWriterLock(this.input.storageRoot, async (canonicalStorageRoot) => { - const layout = workspaceLayout(canonicalStorageRoot, binding); - const summary = await this.requireVerifiedBaselineContext(binding, layout, runtime.digest); - const existing = await readBaselineReceipt(layout.baselineReceiptPath); - if (existing) { - assertBaselineReceiptMatches(existing, binding, summary); - return existing; - } - const identities = deriveBaselineReceiptIdentities(binding); - const receipt: ManagedWorkspaceBaselineReceiptV1 = { - schemaVersion: BASELINE_RECEIPT_SCHEMA_VERSION, - protocol: 'maka_managed_workspace_baseline_receipt_v1', - binding, - workspaceVersionId: identities.workspaceVersionId, - policyVersion: 1, - policyHash: MANAGED_BASELINE_POLICY_HASH_V1, - epochOpenedEventId: identities.epochOpenedEventId, - baselineAcceptedEventId: identities.baselineAcceptedEventId, - treeDeltaDigest: summary.treeDeltaDigest, - changedFileCount: summary.changedFileCount, - deletedFileCount: 0, - }; - await atomicWriteJson(layout.baselineReceiptPath, receipt); - await this.input.failpoint?.('after_baseline_receipt'); - const durable = await readBaselineReceipt(layout.baselineReceiptPath); - if (!durable) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - 'Managed workspace baseline receipt was not durable', - ); - } - assertBaselineReceiptMatches(durable, binding, summary); - return durable; - }); - } - - async #verifyManagedWorkspaceBaselineReceipt( - receipt: ManagedWorkspaceBaselineReceiptV1, - ): Promise { - const runtime = await this.runtime.verify(); - assertBaselineReceiptShape(receipt); - assertOpenIdentity(receipt.binding); - await withArtifactWriterLock(this.input.storageRoot, async (canonicalStorageRoot) => { - const layout = workspaceLayout(canonicalStorageRoot, receipt.binding); - const summary = await this.requireVerifiedBaselineContext( - receipt.binding, - layout, - runtime.digest, - ); - const durable = await readBaselineReceipt(layout.baselineReceiptPath); - if (!durable || !sameBaselineReceipt(durable, receipt)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace baseline receipt does not match its durable artifact', - ); - } - assertBaselineReceiptMatches(durable, receipt.binding, summary); - }); - } - - async quarantineManagedWorkspace( - binding: ManagedWorkspaceBinding, - reason: string, - ): Promise { - const runtime = await this.runtime.verify(); - assertBindingShape(binding); - assertOpenIdentity(binding); - if (!reason.trim()) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace quarantine reason is required', - ); - } - return withArtifactWriterLock(this.input.storageRoot, async (canonicalStorageRoot) => { - const layout = workspaceLayout(canonicalStorageRoot, binding); - await assertOwnedManagedWorkspaceLayout(canonicalStorageRoot, layout); - assertBindingPaths(binding, layout); - const pending = await readQuarantineIntent(layout.quarantineIntentPath); - if (pending) { - assertQuarantineIntentMatches(pending, binding, reason, layout); - await this.assertQuarantineBindingAuthority(binding, layout, runtime.digest); - return this.resumeQuarantineIntent(pending, layout); - } - const stored = await readBinding(layout.bindingPath); - if (!stored || !sameBinding(stored, binding)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed workspace binding is unavailable: ${binding.workspaceInstanceId}`, - ); - } - const repository = await readRepositoryRecord(layout.repositoryRecordPath); - if (!repository) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed Git repository record is unavailable: ${binding.repositoryId}`, - ); - } - assertBindingRepository(binding, repository); - await this.assertRepositoryArtifact(repository); - const epoch = await this.requireEpochArtifact(binding, repository, layout); - assertBindingEpoch(binding, epoch); - return this.beginQuarantine(binding, layout, reason); - }); - } - - private async requireVerifiedBaselineContext( - binding: ManagedWorkspaceBinding, - layout: WorkspaceLayout, - runtimeDigest: `sha256:${string}`, - ): Promise { - assertBindingPaths(binding, layout); - const quarantined = await this.resumePendingQuarantine(binding, layout, runtimeDigest); - if (quarantined) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed workspace instance was quarantined: ${binding.workspaceInstanceId}`, - ); - } - const stored = await readBinding(layout.bindingPath); - if (!stored || !sameBinding(stored, binding)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed workspace binding is unavailable: ${binding.workspaceInstanceId}`, - ); - } - const repository = await this.requireRepository(binding, layout); - assertBindingRepository(binding, repository); - const epoch = await this.requireEpochArtifact(binding, repository, layout); - assertBindingEpoch(binding, epoch); - const inspection = await this.inspectBinding(binding, layout); - if (inspection.state !== 'ready') { - throw new GitWorkspaceServiceError( - 'managed_workspace_drifted', - `Managed workspace contains unaccepted changes: ${binding.worktreePath}`, - ); - } - return this.readBaselineTreeSummary(binding, layout); - } - - private async readBaselineTreeSummary( - binding: ManagedWorkspaceBinding, - layout: WorkspaceLayout, - ): Promise { - const entries = parseTreeEntries( - await this.runtime.runBuffer( - ['--git-dir', binding.repositoryPath, 'ls-tree', '-r', '-z', binding.baselineCommitOid], - layout.homePath, - ), - ); - assertSupportedTree(entries); - return baselineTreeSummary(entries); - } - - private async inspectSourceRepository(sourceRoot: string): Promise { - try { - const topLevelRaw = await this.runtime.run([ - '-C', - sourceRoot, - 'rev-parse', - '--show-toplevel', - ]); - const topLevel = await realpath(topLevelRaw.trim()); - if (!samePath(topLevel, sourceRoot)) { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Managed workspace source must be the Git worktree root', - ); - } - const status = await this.runtime.run([ - '-C', - sourceRoot, - 'status', - '--porcelain=v1', - '--untracked-files=all', - '--ignore-submodules=none', - ]); - if (status.trim()) { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Managed workspace source must have no tracked or untracked changes', - ); - } - const sparse = await this.runtime.runOptional( - ['-C', sourceRoot, 'config', '--bool', '--get', 'core.sparseCheckout'], - 1, - ); - if (sparse?.trim() === 'true') { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Sparse Git worktrees are not supported by managed workspace v1', - ); - } - const entries = parseTreeEntries( - await this.runtime.runBuffer(['-C', sourceRoot, 'ls-tree', '-r', '-z', 'HEAD']), - ); - assertSupportedTree(entries); - - const gitCommonDirRaw = ( - await this.runtime.run(['-C', sourceRoot, 'rev-parse', '--git-common-dir']) - ).trim(); - const gitCommonDir = await realpath(resolveGitPath(sourceRoot, gitCommonDirRaw)); - await this.assertSourceConfigurationFences(sourceRoot, gitCommonDir); - const [headCommitOid, treeOid, objectFormat] = await Promise.all([ - this.runtime.run(['-C', sourceRoot, 'rev-parse', '--verify', 'HEAD']), - this.runtime.run(['-C', sourceRoot, 'rev-parse', '--verify', 'HEAD^{tree}']), - this.runtime.run(['-C', sourceRoot, 'rev-parse', '--show-object-format']), - ]); - const normalizedObjectFormat = objectFormat.trim(); - if (normalizedObjectFormat !== 'sha1' && normalizedObjectFormat !== 'sha256') { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - `Unsupported Git object format: ${normalizedObjectFormat}`, - ); - } - return { - sourceRoot: normalize(sourceRoot), - gitCommonDir: normalize(gitCommonDir), - headCommitOid: headCommitOid.trim(), - treeOid: treeOid.trim(), - objectFormat: normalizedObjectFormat, - }; - } catch (error) { - if (error instanceof GitWorkspaceServiceError) throw error; - throw new GitWorkspaceServiceError( - 'repository_ineligible', - `Unable to inspect managed workspace source: ${sourceRoot}`, - { cause: error }, - ); - } - } - - private async openRepository( - input: CreateManagedWorkspaceFromSourceInput, - source: SourceRepositoryInspection, - layout: WorkspaceLayout, - runtimeDigest: `sha256:${string}`, - ): Promise { - const existing = await readRepositoryRecord(layout.repositoryRecordPath); - if (existing) { - assertRepositoryMatches(existing, input, source.objectFormat, layout, runtimeDigest); - await this.assertRepositoryArtifact(existing); - return existing; - } - - if (await pathExists(layout.repositoryRoot)) { - await moveToQuarantine( - layout.repositoryRoot, - layout.quarantineRoot, - `${input.repositoryId}-incomplete-repository`, - ); - } - await ensureOwnedDirectory(layout.repositoryRoot, layout.managedRoot); - await ensureOwnedDirectory(layout.hooksPath, layout.repositoryRoot); - const stagingRepository = join( - layout.repositoryRoot, - `r.tmp-${randomBytes(6).toString('hex')}`, - ); - try { - const templatePath = join(layout.repositoryRoot, 'empty-template'); - await mkdir(templatePath, { recursive: true }); - await this.runtime.run( - [ - 'init', - '--quiet', - '--bare', - `--object-format=${source.objectFormat}`, - `--template=${templatePath}`, - stagingRepository, - ], - layout.homePath, - ); - await this.configureManagedRepository(stagingRepository, layout.hooksPath); - await rename(stagingRepository, layout.repositoryPath); - - const record: ManagedRepositoryRecord = { - schemaVersion: REPOSITORY_SCHEMA_VERSION, - protocol: 'maka_managed_git_repository_v1', - repositoryId: input.repositoryId, - repositoryPath: normalize(layout.repositoryPath), - hooksPath: normalize(layout.hooksPath), - gitRuntimeSha256: runtimeDigest, - objectFormat: source.objectFormat, - repositoryCapabilityDigest: repositoryCapabilityDigest(runtimeDigest, source.objectFormat), - }; - await this.assertRepositoryArtifact(record); - await atomicWriteJson(layout.repositoryRecordPath, record); - await this.input.failpoint?.('after_repository_record'); - return record; - } catch (error) { - await rm(stagingRepository, { recursive: true, force: true }); - if (error instanceof GitWorkspaceServiceError) throw error; - throw new GitWorkspaceServiceError( - 'git_workspace_operation_failed', - 'Unable to create the Maka-owned Git repository', - { cause: error }, - ); - } - } - - private async assertSourceConfigurationFences( - sourceRoot: string, - gitCommonDir: string, - ): Promise { - if (await pathExists(join(gitCommonDir, 'objects', 'info', 'alternates'))) { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Managed workspace source must not use Git object alternates', - ); - } - const unsafeConfig = await this.runtime.runOptional( - ['-C', sourceRoot, 'config', '--no-includes', '--local', '--name-only', '--get-regexp', '.*'], - 1, - ); - const worktreeConfigEnabled = ( - await this.runtime.runOptional( - [ - '-C', - sourceRoot, - 'config', - '--no-includes', - '--local', - '--bool', - '--get', - 'extensions.worktreeConfig', - ], - 1, - ) - )?.trim(); - const unsafeWorktreeConfig = - worktreeConfigEnabled === 'true' - ? await this.runtime.runOptional( - [ - '-C', - sourceRoot, - 'config', - '--no-includes', - '--worktree', - '--name-only', - '--get-regexp', - '.*', - ], - 1, - ) - : undefined; - const replaceRefs = await this.runtime.run( - ['-C', sourceRoot, 'for-each-ref', '--format=%(refname)', 'refs/replace/'], - undefined, - undefined, - [1], - ); - if ( - hasUnsafeSourceConfig(unsafeConfig) || - hasUnsafeSourceConfig(unsafeWorktreeConfig) || - replaceRefs.trim() - ) { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Managed workspace source contains unsupported Git indirection or runtime configuration', - ); - } - } - - private async openEpochArtifact( - input: CreateManagedWorkspaceFromSourceInput, - source: SourceRepositoryInspection, - repository: ManagedRepositoryRecord, - layout: WorkspaceLayout, - ): Promise { - const existing = await readEpochArtifact(layout.epochArtifactPath); - if (existing) { - assertEpochArtifactMatches(existing, input, source, repository); - await this.assertEpochArtifact(existing, repository, layout); - return existing; - } - - const baselineRef = managedBaselineRef(input.workspaceEpochId); - await this.clearIncompleteBaselineRef(repository.repositoryPath, baselineRef, layout.homePath); - await ensureOwnedDirectory(layout.epochRoot, layout.managedRoot); - await this.runtime.importTree( - source.sourceRoot, - source.treeOid, - repository.repositoryPath, - layout.homePath, - ); - const importedTree = ( - await this.runtime.run( - ['--git-dir', repository.repositoryPath, 'rev-parse', `${source.treeOid}^{tree}`], - layout.homePath, - ) - ).trim(); - if (importedTree !== source.treeOid) { - throw new GitWorkspaceServiceError( - 'git_workspace_operation_failed', - 'Imported baseline tree does not match the source HEAD tree', - ); - } - const baselineCommitOid = ( - await this.runtime.run( - [ - '--git-dir', - repository.repositoryPath, - 'commit-tree', - importedTree, - '-m', - BASELINE_MESSAGE.trim(), - ], - layout.homePath, - baselineCommitEnvironment(), - ) - ).trim(); - const observedBeforeRef = await this.inspectSourceRepository(source.sourceRoot); - assertSameSourceObservation(source, observedBeforeRef); - await this.updateRefCas( - repository.repositoryPath, - baselineRef, - baselineCommitOid, - repository.objectFormat, - layout.homePath, - ); - - await this.input.failpoint?.('after_baseline_ref_created'); - try { - const observedAgain = await this.inspectSourceRepository(source.sourceRoot); - assertSameSourceObservation(source, observedAgain); - } catch (error) { - await this.deleteRefCas( - repository.repositoryPath, - baselineRef, - baselineCommitOid, - layout.homePath, - ); - throw error; - } - - const artifact: ManagedWorkspaceEpochArtifact = { - schemaVersion: EPOCH_ARTIFACT_SCHEMA_VERSION, - protocol: 'maka_managed_workspace_epoch_artifact_v1', - repositoryId: input.repositoryId, - workspaceId: input.workspaceId, - workspaceEpochId: input.workspaceEpochId, - sourceRoot: source.sourceRoot, - sourceGitCommonDir: source.gitCommonDir, - sourceHeadCommitOid: source.headCommitOid, - sourceTreeOid: source.treeOid, - baselineCommitOid, - baselineTreeOid: importedTree, - baselineRef, - headRef: managedHeadRef(input.workspaceId, input.workspaceEpochId), - gitRuntimeSha256: repository.gitRuntimeSha256, - objectFormat: repository.objectFormat, - materializationProfileDigest: materializationProfileDigest( - repository.gitRuntimeSha256, - repository.objectFormat, - ), - materializationSemantics: MATERIALIZATION_SEMANTICS, - }; - await this.assertEpochArtifact(artifact, repository, layout); - await atomicWriteJson(layout.epochArtifactPath, artifact); - return artifact; - } - - private async requireRepository( - input: ManagedWorkspaceIdentity, - layout: WorkspaceLayout, - ): Promise { - const repository = await readRepositoryRecord(layout.repositoryRecordPath); - if (!repository) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed Git repository record is unavailable: ${input.repositoryId}`, - ); - } - if ( - repository.repositoryId !== input.repositoryId || - !samePath(repository.repositoryPath, layout.repositoryPath) || - !samePath(repository.hooksPath, layout.hooksPath) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed Git repository does not match the requested repository identity', - ); - } - await this.assertRepositoryArtifact(repository); - return repository; - } - - private async requireEpochArtifact( - input: Pick, - repository: ManagedRepositoryRecord, - layout: WorkspaceLayout, - ): Promise { - const epoch = await readEpochArtifact(layout.epochArtifactPath); - if (!epoch) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed workspace epoch artifact is unavailable: ${input.workspaceEpochId}`, - ); - } - if ( - epoch.repositoryId !== input.repositoryId || - epoch.workspaceId !== input.workspaceId || - epoch.workspaceEpochId !== input.workspaceEpochId - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace epoch artifact does not match the requested identity', - ); - } - await this.assertEpochArtifact(epoch, repository, layout); - return epoch; - } - - private async assertEpochArtifact( - epoch: ManagedWorkspaceEpochArtifact, - repository: ManagedRepositoryRecord, - layout: WorkspaceLayout, - ): Promise { - if ( - epoch.repositoryId !== repository.repositoryId || - epoch.gitRuntimeSha256 !== repository.gitRuntimeSha256 || - epoch.objectFormat !== repository.objectFormat || - epoch.materializationProfileDigest !== - materializationProfileDigest(repository.gitRuntimeSha256, repository.objectFormat) || - epoch.baselineRef !== managedBaselineRef(epoch.workspaceEpochId) || - epoch.headRef !== managedHeadRef(epoch.workspaceId, epoch.workspaceEpochId) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace epoch artifact does not match its repository capability', - ); - } - const [commit, tree, baselineRef] = await Promise.all([ - this.runtime.run( - ['--git-dir', repository.repositoryPath, 'rev-parse', '--verify', epoch.baselineCommitOid], - layout.homePath, - ), - this.runtime.run( - [ - '--git-dir', - repository.repositoryPath, - 'rev-parse', - '--verify', - `${epoch.baselineCommitOid}^{tree}`, - ], - layout.homePath, - ), - this.runtime.run( - ['--git-dir', repository.repositoryPath, 'rev-parse', '--verify', epoch.baselineRef], - layout.homePath, - ), - ]); - if ( - commit.trim() !== epoch.baselineCommitOid || - tree.trim() !== epoch.baselineTreeOid || - baselineRef.trim() !== epoch.baselineCommitOid - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace epoch baseline identity changed', - ); - } - } - - private async updateRefCas( - repositoryPath: string, - ref: string, - desiredOid: string, - objectFormat: string, - homePath: string, - ): Promise { - const current = ( - await this.runtime.runOptional( - ['--git-dir', repositoryPath, 'rev-parse', '--verify', '--quiet', ref], - 1, - ) - )?.trim(); - if (current === desiredOid) return; - if (current) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Managed Git ref already points at a different object: ${ref}`, - ); - } - const expected = '0'.repeat(objectFormat === 'sha256' ? 64 : 40); - try { - await this.runtime.run( - ['--git-dir', repositoryPath, 'update-ref', ref, desiredOid, expected], - homePath, - ); - } catch (error) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Managed Git ref changed concurrently: ${ref}`, - { cause: error }, - ); - } - } - - private async deleteRefCas( - repositoryPath: string, - ref: string, - expectedOid: string, - homePath: string, - ): Promise { - try { - await this.runtime.run( - ['--git-dir', repositoryPath, 'update-ref', '-d', ref, expectedOid], - homePath, - ); - } catch (error) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Managed Git ref changed before cleanup: ${ref}`, - { cause: error }, - ); - } - } - - private async clearIncompleteBaselineRef( - repositoryPath: string, - baselineRef: string, - homePath: string, - ): Promise { - const current = ( - await this.runtime.runOptional( - ['--git-dir', repositoryPath, 'rev-parse', '--verify', '--quiet', baselineRef], - 1, - ) - )?.trim(); - if (!current) return; - const commitBody = await this.runtime.run( - ['--git-dir', repositoryPath, 'cat-file', 'commit', current], - homePath, - ); - if (!isOwnedBaselineCommit(commitBody)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Incomplete managed baseline ref is not owned by this protocol: ${baselineRef}`, - ); - } - await this.deleteRefCas(repositoryPath, baselineRef, current, homePath); - } - - private async configureManagedRepository( - repositoryPath: string, - hooksPath: string, - ): Promise { - const entries: readonly [string, string][] = [ - ['core.autocrlf', 'false'], - ['core.safecrlf', 'true'], - ['core.hooksPath', normalize(hooksPath)], - ['core.sshCommand', ''], - ['credential.helper', ''], - ['credential.interactive', 'never'], - ['protocol.allow', 'never'], - ['gc.auto', '0'], - ]; - for (const [key, value] of entries) { - await this.runtime.run( - ['--git-dir', repositoryPath, 'config', '--local', key, value], - dirname(repositoryPath), - ); - } - } - - private async assertRepositoryArtifact(record: ManagedRepositoryRecord): Promise { - if (!(await isNonSymlinkDirectory(record.repositoryPath))) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed Git repository is unavailable: ${record.repositoryPath}`, - ); - } - if (!(await isNonSymlinkDirectory(record.hooksPath))) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Managed Git hooks fence is unavailable: ${record.hooksPath}`, - ); - } - const objectFormat = ( - await this.runtime.run([ - '--git-dir', - record.repositoryPath, - 'rev-parse', - '--show-object-format', - ]) - ).trim(); - if ( - objectFormat !== record.objectFormat || - record.repositoryCapabilityDigest !== - repositoryCapabilityDigest(record.gitRuntimeSha256, record.objectFormat) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed Git repository capability identity changed', - ); - } - if (await pathExists(join(record.repositoryPath, 'objects', 'info', 'alternates'))) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed Git repository must not use object alternates', - ); - } - } - - private async inspectBinding( - binding: ManagedWorkspaceBinding, - layout: WorkspaceLayout, - ): Promise { - assertBindingPaths(binding, layout); - if ( - !(await isNonSymlinkDirectory(binding.worktreePath)) || - !(await isNonSymlinkDirectory(binding.repositoryPath)) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Managed workspace is unavailable: ${binding.worktreePath}`, - ); - } - try { - const commonDirRaw = ( - await this.runtime.run( - ['-C', binding.worktreePath, 'rev-parse', '--git-common-dir'], - layout.homePath, - ) - ).trim(); - const commonDir = await realpath(resolveGitPath(binding.worktreePath, commonDirRaw)); - if (!samePath(commonDir, binding.repositoryPath)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed worktree is attached to a different Git repository', - ); - } - const [commitRaw, treeRaw, statusRaw, headRefRaw, worktreeListRaw] = await Promise.all([ - this.runtime.run( - ['-C', binding.worktreePath, 'rev-parse', '--verify', 'HEAD'], - layout.homePath, - ), - this.runtime.run( - ['-C', binding.worktreePath, 'rev-parse', '--verify', 'HEAD^{tree}'], - layout.homePath, - ), - this.runtime.run( - [ - '-C', - binding.worktreePath, - 'status', - '--porcelain=v1', - '--untracked-files=all', - '--ignored=matching', - ], - layout.homePath, - ), - this.runtime.run( - ['--git-dir', binding.repositoryPath, 'rev-parse', '--verify', binding.headRef], - layout.homePath, - ), - this.runtime.run( - ['--git-dir', binding.repositoryPath, 'worktree', 'list', '--porcelain'], - layout.homePath, - ), - ]); - const commitOid = commitRaw.trim(); - const treeOid = treeRaw.trim(); - const status = statusRaw.trim(); - const headRef = headRefRaw.trim(); - assertWorktreeRegistrationLocked( - worktreeListRaw, - binding.worktreePath, - worktreeLockReason(binding), - ); - if ( - commitOid !== binding.baselineCommitOid || - treeOid !== binding.baselineTreeOid || - headRef !== binding.baselineCommitOid || - status - ) { - return { state: 'drifted', commitOid, treeOid, status }; - } - return { state: 'ready', commitOid, treeOid }; - } catch (error) { - if (error instanceof GitWorkspaceServiceError) throw error; - throw new GitWorkspaceServiceError( - 'managed_workspace_unavailable', - `Unable to inspect managed workspace: ${binding.worktreePath}`, - { cause: error }, - ); - } - } - - private async clearIncompleteInstance( - input: ManagedWorkspaceIdentity, - epoch: ManagedWorkspaceEpochArtifact, - layout: WorkspaceLayout, - ): Promise { - const registration = findWorktreeRegistration( - await this.runtime.run( - ['--git-dir', layout.repositoryPath, 'worktree', 'list', '--porcelain'], - layout.homePath, - ), - layout.worktreePath, - ); - if (registration) { - if ( - registration.headOid !== epoch.baselineCommitOid || - (registration.lockReason !== undefined && - registration.lockReason !== worktreeLockReason(input)) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Incomplete managed worktree registration does not match its durable identity', - ); - } - // Git cannot unlock a registration after its worktree path has been moved - // (notably on Windows), so unlock only after exact identity validation and - // immediately before quarantining the path under the global writer lock. - if (registration.lockReason !== undefined) { - await this.runtime.run( - ['--git-dir', layout.repositoryPath, 'worktree', 'unlock', layout.worktreePath], - layout.homePath, - ); - } - } - if (await pathExists(layout.instanceRoot)) { - await moveToQuarantine( - layout.instanceRoot, - layout.quarantineRoot, - `incomplete-${randomUUID()}`, - ); - } - await this.runtime.run( - ['--git-dir', layout.repositoryPath, 'worktree', 'prune', '--expire=now'], - layout.homePath, - ); - const remaining = findWorktreeRegistration( - await this.runtime.run( - ['--git-dir', layout.repositoryPath, 'worktree', 'list', '--porcelain'], - layout.homePath, - ), - layout.worktreePath, - ); - if (remaining) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Incomplete managed worktree registration could not be removed safely', - ); - } - } - - private async resumePendingQuarantine( - input: ManagedWorkspaceIdentity, - layout: WorkspaceLayout, - runtimeDigest: `sha256:${string}`, - ): Promise { - await assertOwnedManagedWorkspaceLayout(this.input.storageRoot, layout); - const intent = await readQuarantineIntent(layout.quarantineIntentPath); - if (!intent) return undefined; - assertQuarantineIntentMatches(intent, intent.binding, intent.reason, layout); - assertBindingIdentity(intent.binding, input, layout, runtimeDigest); - await this.assertQuarantineBindingAuthority(intent.binding, layout, runtimeDigest); - return this.resumeQuarantineIntent(intent, layout); - } - - private async assertQuarantineBindingAuthority( - binding: ManagedWorkspaceBinding, - layout: WorkspaceLayout, - runtimeDigest: `sha256:${string}`, - ): Promise { - assertBindingIdentity(binding, binding, layout, runtimeDigest); - const repository = await this.requireRepository(binding, layout); - assertBindingRepository(binding, repository); - const epoch = await this.requireEpochArtifact(binding, repository, layout); - assertBindingEpoch(binding, epoch); - } - - private async beginQuarantine( - binding: ManagedWorkspaceBinding, - layout: WorkspaceLayout, - reason: string, - ): Promise { - await ensureOwnedDirectory(layout.quarantineIntentRoot, layout.managedRoot); - const existing = await readQuarantineIntent(layout.quarantineIntentPath); - if (existing) { - assertQuarantineIntentMatches(existing, binding, reason, layout); - return this.resumeQuarantineIntent(existing, layout); - } - const intent: ManagedWorkspaceQuarantineIntent = { - schemaVersion: QUARANTINE_INTENT_SCHEMA_VERSION, - protocol: 'maka_managed_workspace_quarantine_intent_v1', - reason, - quarantinePath: join( - layout.quarantineRoot, - `${compactIdentity(binding.workspaceInstanceId)}-${sanitizeReason(reason)}-${randomUUID()}`, - ), - binding, - }; - assertQuarantineIntentMatches(intent, binding, reason, layout); - await atomicWriteJson(layout.quarantineIntentPath, intent); - await this.input.failpoint?.('after_quarantine_intent'); - return this.resumeQuarantineIntent(intent, layout); - } - - private async resumeQuarantineIntent( - intent: ManagedWorkspaceQuarantineIntent, - layout: WorkspaceLayout, - ): Promise { - await assertOwnedManagedWorkspaceLayout(this.input.storageRoot, layout); - const { binding, reason, quarantinePath } = intent; - assertQuarantineIntentMatches(intent, binding, reason, layout); - const registration = findWorktreeRegistration( - await this.runtime.run( - ['--git-dir', binding.repositoryPath, 'worktree', 'list', '--porcelain'], - layout.homePath, - ), - binding.worktreePath, - ); - if ( - registration && - (registration.headOid !== binding.baselineCommitOid || - (registration.lockReason !== undefined && - registration.lockReason !== worktreeLockReason(binding))) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Quarantine worktree registration does not match its durable intent', - ); - } - if (registration?.lockReason !== undefined) { - await this.runtime.run( - ['--git-dir', binding.repositoryPath, 'worktree', 'unlock', binding.worktreePath], - layout.homePath, - ); - } - await this.input.failpoint?.('after_quarantine_unlock'); - - const sourceExists = await pathEntryExists(binding.worktreePath); - const targetExists = await pathEntryExists(quarantinePath); - if (sourceExists && targetExists) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Quarantine source and target both exist', - ); - } - if (sourceExists) { - await ensureOwnedDirectory(layout.quarantineRoot, layout.managedRoot); - await assertOwnedManagedWorkspaceLayout(this.input.storageRoot, layout); - await rename(binding.worktreePath, quarantinePath); - } else if (!targetExists) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Quarantine intent has neither a source worktree nor a target artifact', - ); - } - await assertOwnedDirectoryEntry(quarantinePath, layout.quarantineRoot, true); - if (!samePath(await realpath(dirname(quarantinePath)), await realpath(layout.quarantineRoot))) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Quarantine target parent does not match the owned quarantine root', - ); - } - await this.input.failpoint?.('after_quarantine_move'); - - const stored = await readBinding(layout.bindingPath); - if (stored && !sameBinding(stored, binding)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Quarantine binding changed after its durable intent was recorded', - ); - } - await rm(layout.bindingPath, { force: true }); - await this.input.failpoint?.('after_quarantine_binding_removed'); - await this.runtime.run( - ['--git-dir', binding.repositoryPath, 'worktree', 'prune', '--expire=now'], - layout.homePath, - ); - const remaining = findWorktreeRegistration( - await this.runtime.run( - ['--git-dir', binding.repositoryPath, 'worktree', 'list', '--porcelain'], - layout.homePath, - ), - binding.worktreePath, - ); - if (remaining) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Quarantine could not remove the managed worktree registration', - ); - } - await this.input.failpoint?.('after_quarantine_pruned'); - - const record: ManagedWorkspaceQuarantineRecord = { - protocol: 'maka_managed_workspace_quarantine_v1', - reason, - binding, - }; - const existingRecord = await readQuarantineRecord(`${quarantinePath}.json`); - if (existingRecord && !sameQuarantineRecord(existingRecord, record)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Quarantine record conflicts with its durable intent', - ); - } - if (!existingRecord) await atomicWriteJson(`${quarantinePath}.json`, record); - return { quarantinePath, reason }; - } -} - -class VerifiedGitRuntime { - constructor(private readonly input: VerifiedGitRuntimeInput) { - if ( - !isAbsolute(input.executablePath) || - !SHA256_PATTERN.test(input.expectedSha256) || - (input.distribution !== undefined && !SHA256_PATTERN.test(input.runtimeIdentitySha256 ?? '')) - ) { - throw new GitWorkspaceServiceError( - 'git_runtime_unavailable', - 'Managed workspace requires an absolute Git executable and SHA-256 digest', - ); - } - if (input.distribution) { - const rootPath = input.distribution.rootPath; - const executableRelativePath = relative(rootPath, input.executablePath); - if ( - !isAbsolute(rootPath) || - executableRelativePath === '' || - executableRelativePath === '..' || - executableRelativePath.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`) || - isAbsolute(executableRelativePath) - ) { - throw new GitWorkspaceServiceError( - 'git_runtime_unavailable', - 'Bundled Git executable must belong to its declared distribution root', - ); - } - } - } - - verify(): Promise<{ executablePath: string; digest: `sha256:${string}` }> { - return this.verifyOnce(); - } - - async run( - args: readonly string[], - homePath?: string, - extraEnv?: NodeJS.ProcessEnv, - acceptedExitCodes: readonly number[] = [], - ): Promise { - const runtime = await this.verify(); - const hooksPath = homePath ? join(homePath, 'empty-hooks') : dirname(runtime.executablePath); - if (homePath) { - await mkdir(homePath, { recursive: true }); - await mkdir(hooksPath, { recursive: true }); - } - const env = isolatedGitEnvironment(this.input, homePath ?? dirname(runtime.executablePath)); - try { - const { stdout } = await execFileAsync( - runtime.executablePath, - [...fixedGitArguments(hooksPath), ...args], - { - cwd: homePath ?? dirname(runtime.executablePath), - // Operation-scoped values (currently only deterministic commit - // identity) cannot override the hermetic Git execution profile. - env: { ...extraEnv, ...env }, - encoding: 'utf8', - maxBuffer: GIT_MAX_BUFFER_BYTES, - timeout: GIT_TIMEOUT_MS, - windowsHide: true, - }, - ); - return stdout; - } catch (error) { - if (acceptedExitCodes.includes(exitCode(error) ?? -1)) return ''; - throw error; - } - } - - async runBuffer( - args: readonly string[], - homePath?: string, - extraEnv?: NodeJS.ProcessEnv, - ): Promise { - const runtime = await this.verify(); - const hooksPath = homePath ? join(homePath, 'empty-hooks') : dirname(runtime.executablePath); - if (homePath) { - await mkdir(homePath, { recursive: true }); - await mkdir(hooksPath, { recursive: true }); - } - const env = isolatedGitEnvironment(this.input, homePath ?? dirname(runtime.executablePath)); - const { stdout } = await execFileAsync( - runtime.executablePath, - [...fixedGitArguments(hooksPath), ...args], - { - cwd: homePath ?? dirname(runtime.executablePath), - env: { ...extraEnv, ...env }, - encoding: 'buffer', - maxBuffer: GIT_MAX_BUFFER_BYTES, - timeout: GIT_TIMEOUT_MS, - windowsHide: true, - }, - ); - return stdout; - } - - async runOptional( - args: readonly string[], - acceptedMissingExitCode: number, - ): Promise { - try { - return await this.run(args); - } catch (error) { - if (exitCode(error) === acceptedMissingExitCode) return undefined; - throw error; - } - } - - async importTree( - sourceRoot: string, - treeOid: string, - repositoryPath: string, - homePath: string, - ): Promise { - const packRuntime = await this.verify(); - const hooksPath = join(homePath, 'empty-hooks'); - await mkdir(hooksPath, { recursive: true }); - const fixed = fixedGitArguments(hooksPath); - const pack = spawn( - packRuntime.executablePath, - [...fixed, '-C', sourceRoot, 'pack-objects', '--stdout', '--revs'], - { - cwd: homePath, - env: isolatedGitEnvironment(this.input, homePath), - stdio: ['pipe', 'pipe', 'pipe'], - windowsHide: true, - }, - ); - let indexRuntime: Awaited>; - try { - // Each Git process is an independent trust decision. Re-check the - // executable immediately before index-pack instead of extending the - // pack-objects verification across a second invocation. - indexRuntime = await this.verify(); - } catch (error) { - pack.kill('SIGKILL'); - await waitForChildExit(pack).catch(() => undefined); - throw error; - } - const index = spawn( - indexRuntime.executablePath, - [...fixed, '--git-dir', repositoryPath, 'index-pack', '--stdin', '--fix-thin'], - { - cwd: homePath, - env: isolatedGitEnvironment(this.input, homePath), - stdio: ['pipe', 'ignore', 'pipe'], - windowsHide: true, - }, - ); - if (!pack.stdin || !pack.stdout || !index.stdin) { - pack.kill('SIGKILL'); - index.kill('SIGKILL'); - throw new Error('Git tree import did not create the required process pipes'); - } - const packStderr = collectBoundedStderr(pack.stderr); - const indexStderr = collectBoundedStderr(index.stderr); - index.stdin.on('error', () => { - // The index process exit status/stderr is the authoritative pipeline failure. - }); - pack.stdout.pipe(index.stdin); - pack.stdin.end(`${treeOid}\n`); - const timeout = setTimeout(() => { - pack.kill('SIGKILL'); - index.kill('SIGKILL'); - }, GIT_TIMEOUT_MS); - try { - const [packCode, indexCode] = await Promise.all([ - waitForChildExit(pack), - waitForChildExit(index), - ]); - if (packCode !== 0 || indexCode !== 0) { - throw new Error( - `Git tree import failed: pack=${packCode} index=${indexCode} ` + - `${await packStderr} ${await indexStderr}`, - ); - } - } catch (error) { - pack.kill('SIGKILL'); - index.kill('SIGKILL'); - throw error; - } finally { - clearTimeout(timeout); - } - } - - private async verifyOnce(): Promise<{ - executablePath: string; - digest: `sha256:${string}`; - }> { - try { - const executablePath = normalize(await realpath(this.input.executablePath)); - const info = await stat(executablePath); - if (!info.isFile()) throw new Error('not a regular file'); - const executableDigest = await sha256File(executablePath); - if (executableDigest !== this.input.expectedSha256) { - throw new GitWorkspaceServiceError( - 'git_runtime_integrity_mismatch', - `Git executable digest mismatch: ${executablePath}`, - ); - } - return { - executablePath, - digest: this.input.runtimeIdentitySha256 ?? executableDigest, - }; - } catch (error) { - if (error instanceof GitWorkspaceServiceError) throw error; - throw new GitWorkspaceServiceError( - 'git_runtime_unavailable', - `Git runtime is unavailable: ${this.input.executablePath}`, - { cause: error }, - ); - } - } -} - -function workspaceLayout( - canonicalStorageRoot: string, - identity: Pick< - ManagedWorkspaceIdentity, - 'repositoryId' | 'workspaceId' | 'workspaceEpochId' | 'workspaceInstanceId' - >, -): WorkspaceLayout { - const managedRoot = join(canonicalStorageRoot, 'managed-workspaces'); - const quarantineIntentRoot = join(managedRoot, 'quarantine-intents'); - const repositoryRoot = join(managedRoot, 'r', compactIdentity(identity.repositoryId)); - const epochRoot = join( - managedRoot, - 'w', - compactIdentity(identity.workspaceId), - 'e', - compactIdentity(identity.workspaceEpochId), - ); - const instanceRoot = join(epochRoot, 'i', compactIdentity(identity.workspaceInstanceId)); - return { - managedRoot, - repositoryRoot, - repositoryPath: join(repositoryRoot, 'repository.git'), - repositoryRecordPath: join(repositoryRoot, 'repository.json'), - hooksPath: join(repositoryRoot, 'hooks'), - homePath: join(managedRoot, 'git-home'), - epochRoot, - epochArtifactPath: join(epochRoot, 'epoch.json'), - instanceRoot, - bindingPath: join(instanceRoot, 'binding.json'), - baselineReceiptPath: join(instanceRoot, 'baseline-receipt.json'), - worktreePath: join(instanceRoot, 'worktree'), - quarantineRoot: join(managedRoot, 'quarantine'), - quarantineIntentRoot, - quarantineIntentPath: join( - quarantineIntentRoot, - `${compactIdentity(identity.workspaceInstanceId)}.json`, - ), - }; -} - -function assertOpenIdentity(input: ManagedWorkspaceIdentity): void { - for (const [name, value] of Object.entries({ - repositoryId: input.repositoryId, - workspaceId: input.workspaceId, - workspaceEpochId: input.workspaceEpochId, - workspaceInstanceId: input.workspaceInstanceId, - })) { - if (!IDENTIFIER_PATTERN.test(value)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Invalid ${name}: ${value}`, - ); - } - } -} - -function assertBindingShape(value: unknown): asserts value is ManagedWorkspaceBinding { - if (!isBinding(value)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Invalid managed workspace binding', - ); - } -} - -function assertBindingMatches( - binding: ManagedWorkspaceBinding, - input: CreateManagedWorkspaceFromSourceInput, - layout: WorkspaceLayout, - runtimeDigest: `sha256:${string}`, -): void { - assertBindingIdentity(binding, input, layout, runtimeDigest); - if (!samePath(binding.sourceRoot, input.sourceRoot)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace binding does not match the requested source provenance', - ); - } -} - -function assertBindingIdentity( - binding: ManagedWorkspaceBinding, - input: ManagedWorkspaceIdentity, - layout: WorkspaceLayout, - runtimeDigest: `sha256:${string}`, -): void { - assertBindingShape(binding); - assertBindingPaths(binding, layout); - if ( - binding.repositoryId !== input.repositoryId || - binding.workspaceId !== input.workspaceId || - binding.workspaceEpochId !== input.workspaceEpochId || - binding.workspaceInstanceId !== input.workspaceInstanceId || - binding.gitRuntimeSha256 !== runtimeDigest || - binding.materializationProfileDigest !== - materializationProfileDigest(runtimeDigest, binding.objectFormat) || - binding.headRef !== managedHeadRef(input.workspaceId, input.workspaceEpochId) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace binding does not match the requested managed identity', - ); - } -} - -function assertBindingRepository( - binding: ManagedWorkspaceBinding, - repository: ManagedRepositoryRecord, -): void { - if ( - binding.repositoryId !== repository.repositoryId || - !samePath(binding.repositoryPath, repository.repositoryPath) || - !samePath(binding.hooksPath, repository.hooksPath) || - binding.gitRuntimeSha256 !== repository.gitRuntimeSha256 || - binding.objectFormat !== repository.objectFormat || - repository.repositoryCapabilityDigest !== - repositoryCapabilityDigest(repository.gitRuntimeSha256, repository.objectFormat) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace binding does not match its Git repository record', - ); - } -} - -function assertBindingEpoch( - binding: ManagedWorkspaceBinding, - epoch: ManagedWorkspaceEpochArtifact, -): void { - if ( - binding.repositoryId !== epoch.repositoryId || - binding.workspaceId !== epoch.workspaceId || - binding.workspaceEpochId !== epoch.workspaceEpochId || - !samePath(binding.sourceRoot, epoch.sourceRoot) || - !samePath(binding.sourceGitCommonDir, epoch.sourceGitCommonDir) || - binding.sourceHeadCommitOid !== epoch.sourceHeadCommitOid || - binding.sourceTreeOid !== epoch.sourceTreeOid || - binding.baselineCommitOid !== epoch.baselineCommitOid || - binding.baselineTreeOid !== epoch.baselineTreeOid || - binding.headRef !== epoch.headRef || - binding.materializationProfileDigest !== epoch.materializationProfileDigest - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace binding does not match its epoch artifact', - ); - } -} - -function assertBindingPaths(binding: ManagedWorkspaceBinding, layout: WorkspaceLayout): void { - if ( - !samePath(binding.repositoryPath, layout.repositoryPath) || - !samePath(binding.worktreePath, layout.worktreePath) || - !samePath(binding.hooksPath, layout.hooksPath) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace binding escapes its owned storage namespace', - ); - } -} - -function assertRepositoryMatches( - record: ManagedRepositoryRecord, - input: CreateManagedWorkspaceFromSourceInput, - objectFormat: string, - layout: WorkspaceLayout, - runtimeDigest: `sha256:${string}`, -): void { - if ( - record.repositoryId !== input.repositoryId || - record.gitRuntimeSha256 !== runtimeDigest || - record.objectFormat !== objectFormat || - record.repositoryCapabilityDigest !== repositoryCapabilityDigest(runtimeDigest, objectFormat) || - !samePath(record.repositoryPath, layout.repositoryPath) || - !samePath(record.hooksPath, layout.hooksPath) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed Git repository does not match the requested repository capability', - ); - } -} - -async function readBinding(path: string): Promise { - const value = await readJson(path); - if (value === undefined) return undefined; - assertBindingShape(value); - return value; -} - -async function readBaselineReceipt( - path: string, -): Promise { - const value = await readJson(path); - if (value === undefined) return undefined; - assertBaselineReceiptShape(value); - return value; -} - -async function readRepositoryRecord(path: string): Promise { - const value = await readJson(path); - if (value === undefined) return undefined; - if (!isRepositoryRecord(value)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Invalid managed Git repository record: ${path}`, - ); - } - return value; -} - -function assertEpochArtifactMatches( - artifact: ManagedWorkspaceEpochArtifact, - input: CreateManagedWorkspaceFromSourceInput, - source: SourceRepositoryInspection, - repository: ManagedRepositoryRecord, -): void { - if ( - artifact.repositoryId !== input.repositoryId || - artifact.workspaceId !== input.workspaceId || - artifact.workspaceEpochId !== input.workspaceEpochId || - !samePath(artifact.sourceRoot, source.sourceRoot) || - !samePath(artifact.sourceGitCommonDir, source.gitCommonDir) || - artifact.sourceHeadCommitOid !== source.headCommitOid || - artifact.sourceTreeOid !== source.treeOid || - artifact.baselineTreeOid !== source.treeOid || - artifact.gitRuntimeSha256 !== repository.gitRuntimeSha256 || - artifact.objectFormat !== repository.objectFormat - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace epoch artifact does not match the requested source boundary', - ); - } -} - -function assertSameSourceObservation( - captured: SourceRepositoryInspection, - current: SourceRepositoryInspection, -): void { - if ( - !samePath(captured.sourceRoot, current.sourceRoot) || - !samePath(captured.gitCommonDir, current.gitCommonDir) || - captured.headCommitOid !== current.headCommitOid || - captured.treeOid !== current.treeOid || - captured.objectFormat !== current.objectFormat - ) { - throw new GitWorkspaceServiceError( - 'source_changed_during_baseline_import', - 'Managed workspace source changed while the baseline was being imported', - ); - } -} - -async function readEpochArtifact(path: string): Promise { - const value = await readJson(path); - if (value === undefined) return undefined; - if (!isEpochArtifact(value)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Invalid managed workspace epoch artifact: ${path}`, - ); - } - return value; -} - -async function readQuarantineIntent( - path: string, -): Promise { - const value = await readJson(path); - if (value === undefined) return undefined; - if (!isQuarantineIntent(value)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Invalid managed workspace quarantine intent: ${path}`, - ); - } - return value; -} - -async function readQuarantineRecord( - path: string, -): Promise { - const value = await readJson(path); - if (value === undefined) return undefined; - if (!isQuarantineRecord(value)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Invalid managed workspace quarantine record: ${path}`, - ); - } - return value; -} - -function assertQuarantineIntentMatches( - intent: ManagedWorkspaceQuarantineIntent, - binding: ManagedWorkspaceBinding, - reason: string, - layout: WorkspaceLayout, -): void { - assertBindingShape(intent.binding); - assertBindingPaths(intent.binding, layout); - if ( - !sameBinding(intent.binding, binding) || - intent.reason !== reason || - !isPathWithin(intent.quarantinePath, layout.quarantineRoot) || - !samePath(dirname(intent.quarantinePath), layout.quarantineRoot) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace quarantine intent does not match its binding or owned namespace', - ); - } -} - -function isBinding(value: unknown): value is ManagedWorkspaceBinding { - if (!isRecord(value)) return false; - return ( - hasExactKeys(value, BINDING_KEYS) && - value.schemaVersion === BINDING_SCHEMA_VERSION && - value.protocol === 'git_managed_workspace_v1' && - typeof value.repositoryId === 'string' && - IDENTIFIER_PATTERN.test(value.repositoryId) && - typeof value.workspaceId === 'string' && - IDENTIFIER_PATTERN.test(value.workspaceId) && - typeof value.workspaceEpochId === 'string' && - IDENTIFIER_PATTERN.test(value.workspaceEpochId) && - typeof value.workspaceInstanceId === 'string' && - IDENTIFIER_PATTERN.test(value.workspaceInstanceId) && - typeof value.sourceRoot === 'string' && - isAbsolute(value.sourceRoot) && - typeof value.sourceGitCommonDir === 'string' && - isAbsolute(value.sourceGitCommonDir) && - typeof value.sourceHeadCommitOid === 'string' && - OID_PATTERN.test(value.sourceHeadCommitOid) && - typeof value.sourceTreeOid === 'string' && - OID_PATTERN.test(value.sourceTreeOid) && - typeof value.repositoryPath === 'string' && - isAbsolute(value.repositoryPath) && - typeof value.worktreePath === 'string' && - isAbsolute(value.worktreePath) && - typeof value.hooksPath === 'string' && - isAbsolute(value.hooksPath) && - typeof value.baselineCommitOid === 'string' && - OID_PATTERN.test(value.baselineCommitOid) && - typeof value.baselineTreeOid === 'string' && - OID_PATTERN.test(value.baselineTreeOid) && - typeof value.headRef === 'string' && - value.headRef === managedHeadRef(value.workspaceId, value.workspaceEpochId) && - typeof value.gitRuntimeSha256 === 'string' && - SHA256_PATTERN.test(value.gitRuntimeSha256) && - (value.objectFormat === 'sha1' || value.objectFormat === 'sha256') && - typeof value.materializationProfileDigest === 'string' && - SHA256_PATTERN.test(value.materializationProfileDigest) && - value.materializationSemantics === MATERIALIZATION_SEMANTICS - ); -} - -function assertBaselineReceiptShape( - value: unknown, -): asserts value is ManagedWorkspaceBaselineReceiptV1 { - if (!isBaselineReceipt(value)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Invalid managed workspace baseline receipt', - ); - } -} - -function isBaselineReceipt(value: unknown): value is ManagedWorkspaceBaselineReceiptV1 { - if (!isRecord(value)) return false; - return ( - hasExactKeys(value, BASELINE_RECEIPT_KEYS) && - value.schemaVersion === BASELINE_RECEIPT_SCHEMA_VERSION && - value.protocol === 'maka_managed_workspace_baseline_receipt_v1' && - isBinding(value.binding) && - typeof value.workspaceVersionId === 'string' && - /^version_[a-f0-9]{32}$/u.test(value.workspaceVersionId) && - value.policyVersion === 1 && - typeof value.policyHash === 'string' && - SHA256_PATTERN.test(value.policyHash) && - typeof value.epochOpenedEventId === 'string' && - /^[A-Za-z0-9_-]{1,128}$/u.test(value.epochOpenedEventId) && - typeof value.baselineAcceptedEventId === 'string' && - /^[A-Za-z0-9_-]{1,128}$/u.test(value.baselineAcceptedEventId) && - value.epochOpenedEventId !== value.baselineAcceptedEventId && - typeof value.treeDeltaDigest === 'string' && - SHA256_PATTERN.test(value.treeDeltaDigest) && - typeof value.changedFileCount === 'number' && - Number.isSafeInteger(value.changedFileCount) && - value.changedFileCount >= 0 && - value.deletedFileCount === 0 - ); -} - -function assertBaselineReceiptMatches( - receipt: ManagedWorkspaceBaselineReceiptV1, - binding: ManagedWorkspaceBinding, - summary: BaselineTreeSummary, -): void { - const identities = deriveBaselineReceiptIdentities(binding); - if ( - !sameBinding(receipt.binding, binding) || - receipt.workspaceVersionId !== identities.workspaceVersionId || - receipt.epochOpenedEventId !== identities.epochOpenedEventId || - receipt.baselineAcceptedEventId !== identities.baselineAcceptedEventId || - receipt.policyVersion !== 1 || - receipt.policyHash !== MANAGED_BASELINE_POLICY_HASH_V1 || - receipt.treeDeltaDigest !== summary.treeDeltaDigest || - receipt.changedFileCount !== summary.changedFileCount || - receipt.deletedFileCount !== 0 - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed workspace baseline receipt does not match its verified Git boundary', - ); - } -} - -function deriveBaselineReceiptIdentities(binding: ManagedWorkspaceBinding): { - readonly workspaceVersionId: string; - readonly epochOpenedEventId: string; - readonly baselineAcceptedEventId: string; -} { - const identity = { - repositoryId: binding.repositoryId, - workspaceId: binding.workspaceId, - workspaceEpochId: binding.workspaceEpochId, - workspaceInstanceId: binding.workspaceInstanceId, - baselineCommitOid: binding.baselineCommitOid, - baselineTreeOid: binding.baselineTreeOid, - policyVersion: 1, - policyHash: MANAGED_BASELINE_POLICY_HASH_V1, - } as const; - const derive = (purpose: string): string => - createHash('sha256') - .update( - JSON.stringify({ - protocol: 'maka_managed_workspace_baseline_identity_v1', - purpose, - identity, - }), - ) - .digest('hex'); - return { - workspaceVersionId: `version_${derive('workspace_version').slice(0, 32)}`, - epochOpenedEventId: `workspace_epoch_opened_${derive('epoch_opened').slice(0, 64)}`, - baselineAcceptedEventId: `workspace_baseline_accepted_${derive('baseline_accepted').slice(0, 64)}`, - }; -} - -function hashCanonicalJson(value: unknown): `sha256:${string}` { - return `sha256:${createHash('sha256').update(JSON.stringify(value)).digest('hex')}`; -} - -function isRepositoryRecord(value: unknown): value is ManagedRepositoryRecord { - if (!isRecord(value)) return false; - return ( - hasExactKeys(value, REPOSITORY_KEYS) && - value.schemaVersion === REPOSITORY_SCHEMA_VERSION && - value.protocol === 'maka_managed_git_repository_v1' && - typeof value.repositoryId === 'string' && - IDENTIFIER_PATTERN.test(value.repositoryId) && - typeof value.repositoryPath === 'string' && - isAbsolute(value.repositoryPath) && - typeof value.hooksPath === 'string' && - isAbsolute(value.hooksPath) && - typeof value.gitRuntimeSha256 === 'string' && - SHA256_PATTERN.test(value.gitRuntimeSha256) && - (value.objectFormat === 'sha1' || value.objectFormat === 'sha256') && - typeof value.repositoryCapabilityDigest === 'string' && - SHA256_PATTERN.test(value.repositoryCapabilityDigest) - ); -} - -function isEpochArtifact(value: unknown): value is ManagedWorkspaceEpochArtifact { - if (!isRecord(value)) return false; - return ( - hasExactKeys(value, EPOCH_ARTIFACT_KEYS) && - value.schemaVersion === EPOCH_ARTIFACT_SCHEMA_VERSION && - value.protocol === 'maka_managed_workspace_epoch_artifact_v1' && - typeof value.repositoryId === 'string' && - IDENTIFIER_PATTERN.test(value.repositoryId) && - typeof value.workspaceId === 'string' && - IDENTIFIER_PATTERN.test(value.workspaceId) && - typeof value.workspaceEpochId === 'string' && - IDENTIFIER_PATTERN.test(value.workspaceEpochId) && - typeof value.sourceRoot === 'string' && - isAbsolute(value.sourceRoot) && - typeof value.sourceGitCommonDir === 'string' && - isAbsolute(value.sourceGitCommonDir) && - typeof value.sourceHeadCommitOid === 'string' && - OID_PATTERN.test(value.sourceHeadCommitOid) && - typeof value.sourceTreeOid === 'string' && - OID_PATTERN.test(value.sourceTreeOid) && - typeof value.baselineCommitOid === 'string' && - OID_PATTERN.test(value.baselineCommitOid) && - typeof value.baselineTreeOid === 'string' && - OID_PATTERN.test(value.baselineTreeOid) && - value.baselineRef === managedBaselineRef(value.workspaceEpochId) && - value.headRef === managedHeadRef(value.workspaceId, value.workspaceEpochId) && - typeof value.gitRuntimeSha256 === 'string' && - SHA256_PATTERN.test(value.gitRuntimeSha256) && - (value.objectFormat === 'sha1' || value.objectFormat === 'sha256') && - typeof value.materializationProfileDigest === 'string' && - SHA256_PATTERN.test(value.materializationProfileDigest) && - value.materializationSemantics === MATERIALIZATION_SEMANTICS - ); -} - -function isQuarantineIntent(value: unknown): value is ManagedWorkspaceQuarantineIntent { - if (!isRecord(value)) return false; - return ( - hasExactKeys(value, QUARANTINE_INTENT_KEYS) && - value.schemaVersion === QUARANTINE_INTENT_SCHEMA_VERSION && - value.protocol === 'maka_managed_workspace_quarantine_intent_v1' && - typeof value.reason === 'string' && - value.reason.length > 0 && - typeof value.quarantinePath === 'string' && - isAbsolute(value.quarantinePath) && - isBinding(value.binding) - ); -} - -function isQuarantineRecord(value: unknown): value is ManagedWorkspaceQuarantineRecord { - if (!isRecord(value)) return false; - return ( - hasExactKeys(value, QUARANTINE_RECORD_KEYS) && - value.protocol === 'maka_managed_workspace_quarantine_v1' && - typeof value.reason === 'string' && - value.reason.length > 0 && - isBinding(value.binding) - ); -} - -function isRecord(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !Array.isArray(value); -} - -function hasExactKeys(value: Record, expected: readonly string[]): boolean { - const actual = Object.keys(value).sort(); - const canonical = [...expected].sort(); - return ( - actual.length === canonical.length && actual.every((key, index) => key === canonical[index]) - ); -} - -function sameBinding(left: ManagedWorkspaceBinding, right: ManagedWorkspaceBinding): boolean { - return JSON.stringify(left) === JSON.stringify(right); -} - -function sameBaselineReceipt( - left: ManagedWorkspaceBaselineReceiptV1, - right: ManagedWorkspaceBaselineReceiptV1, -): boolean { - return JSON.stringify(left) === JSON.stringify(right); -} - -function sameQuarantineRecord( - left: ManagedWorkspaceQuarantineRecord, - right: ManagedWorkspaceQuarantineRecord, -): boolean { - return JSON.stringify(left) === JSON.stringify(right); -} - -async function readJson(path: string): Promise { - try { - const info = await lstat(path); - if (!info.isFile() || info.isSymbolicLink()) { - throw new Error('control record is not one regular non-symlink file'); - } - return JSON.parse(await readFile(path, 'utf8')) as unknown; - } catch (error) { - if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined; - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Invalid managed workspace control record: ${path}`, - { cause: error }, - ); - } -} - -async function atomicWriteJson(path: string, value: unknown): Promise { - await mkdir(dirname(path), { recursive: true }); - const temporaryPath = `${path}.tmp-${randomUUID()}`; - try { - const file = await openFile(temporaryPath, 'wx', 0o600); - try { - await file.writeFile(`${JSON.stringify(value, null, 2)}\n`, 'utf8'); - await file.sync(); - } finally { - await file.close(); - } - await rename(temporaryPath, path); - if (process.platform !== 'win32') { - const directory = await openFile(dirname(path), 'r'); - try { - await directory.sync(); - } finally { - await directory.close(); - } - } - } finally { - await rm(temporaryPath, { force: true }); - } -} - -async function moveToQuarantine( - source: string, - quarantineRoot: string, - label: string, -): Promise { - await mkdir(quarantineRoot, { recursive: true }); - const target = join(quarantineRoot, `${sanitizeReason(label)}-${Date.now()}-${randomUUID()}`); - await rename(source, target); - return target; -} - -async function canonicalDirectory( - path: string, - code: GitWorkspaceServiceErrorCode, -): Promise { - try { - const canonical = normalize(await realpath(path)); - if (!(await stat(canonical)).isDirectory()) throw new Error('not a directory'); - return canonical; - } catch (error) { - throw new GitWorkspaceServiceError(code, `Directory is unavailable: ${path}`, { cause: error }); - } -} - -async function ensureOwnedDirectory(path: string, ownerRoot: string): Promise { - const requested = normalize(resolve(path)); - const canonicalOwner = normalize(await realpath(ownerRoot)); - const relation = relative(normalize(resolve(ownerRoot)), requested); - if (relation.startsWith('..') || isAbsolute(relation)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Managed workspace control path escapes its owner root: ${path}`, - ); - } - let current = normalize(resolve(ownerRoot)); - for (const segment of relation.split(/[\\/]/u).filter(Boolean)) { - current = join(current, segment); - try { - await mkdir(current); - } catch (error) { - if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error; - } - const info = await lstat(current); - const canonical = normalize(await realpath(current)); - if (!info.isDirectory() || info.isSymbolicLink() || !isPathWithin(canonical, canonicalOwner)) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Managed workspace control path is not one owned directory: ${current}`, - ); - } - } -} - -async function assertOwnedManagedWorkspaceLayout( - storageRoot: string, - layout: WorkspaceLayout, -): Promise { - const canonicalStorageRoot = normalize(await realpath(storageRoot)); - await assertOwnedDirectoryEntry(layout.managedRoot, canonicalStorageRoot, true); - await assertOwnedDirectoryEntry(layout.quarantineRoot, layout.managedRoot, true); - await assertOwnedDirectoryEntry(layout.homePath, layout.managedRoot, true); - await assertOwnedDirectoryEntry(layout.quarantineIntentRoot, layout.managedRoot, false); - await assertOwnedDirectoryEntry(layout.repositoryRoot, layout.managedRoot, false); - await assertOwnedDirectoryEntry(layout.repositoryPath, layout.repositoryRoot, false); - await assertOwnedDirectoryEntry(layout.hooksPath, layout.repositoryRoot, false); - await assertOwnedDirectoryEntry(layout.epochRoot, layout.managedRoot, false); - await assertOwnedDirectoryEntry(layout.instanceRoot, layout.epochRoot, false); - await assertOwnedDirectoryEntry(layout.worktreePath, layout.instanceRoot, false); -} - -async function assertOwnedDirectoryEntry( - path: string, - ownerRoot: string, - required: boolean, -): Promise { - let info; - try { - info = await lstat(path); - } catch (error) { - if (!required && (error as NodeJS.ErrnoException).code === 'ENOENT') return; - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Managed workspace owned directory is unavailable: ${path}`, - { cause: error }, - ); - } - let canonicalOwner: string; - let canonicalPath: string; - try { - canonicalOwner = normalize(await realpath(ownerRoot)); - canonicalPath = normalize(await realpath(path)); - } catch (error) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Managed workspace owned directory cannot be canonicalized: ${path}`, - { cause: error }, - ); - } - if ( - !info.isDirectory() || - info.isSymbolicLink() || - !isPathWithin(canonicalPath, canonicalOwner) - ) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - `Managed workspace owned directory escaped or changed identity: ${path}`, - ); - } -} - -async function pathExists(path: string): Promise { - try { - await stat(path); - return true; - } catch (error) { - if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false; - throw error; - } -} - -async function pathEntryExists(path: string): Promise { - try { - await lstat(path); - return true; - } catch (error) { - if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false; - throw error; - } -} - -async function isNonSymlinkDirectory(path: string): Promise { - try { - const info = await lstat(path); - return info.isDirectory() && !info.isSymbolicLink(); - } catch (error) { - if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false; - throw error; - } -} - -function resolveGitPath(cwd: string, path: string): string { - return isAbsolute(path) ? path : resolve(cwd, path); -} - -function samePath(left: string, right: string): boolean { - const normalizedLeft = normalize(resolve(left)); - const normalizedRight = normalize(resolve(right)); - return process.platform === 'win32' - ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() - : normalizedLeft === normalizedRight; -} - -function isPathWithin(path: string, root: string): boolean { - const candidate = normalize(resolve(path)); - const owner = normalize(resolve(root)); - const comparisonCandidate = process.platform === 'win32' ? candidate.toLowerCase() : candidate; - const comparisonOwner = process.platform === 'win32' ? owner.toLowerCase() : owner; - const relation = relative(comparisonOwner, comparisonCandidate); - return relation === '' || (!relation.startsWith('..') && !isAbsolute(relation)); -} - -function sanitizeReason(value: string): string { - return ( - value - .toLowerCase() - .replace(/[^a-z0-9_-]+/gu, '-') - .replace(/^-+|-+$/gu, '') - .slice(0, 64) || 'unknown' - ); -} - -function hasUnsafeSourceConfig(raw: string | undefined): boolean { - if (!raw) return false; - return raw.split(/\r?\n/u).some((line) => { - const key = line.trim().toLowerCase(); - return ( - key.startsWith('include.') || - key.startsWith('includeif.') || - key === 'extensions.objectformat' || - key === 'extensions.partialclone' || - key === 'core.fsmonitor' || - (key.startsWith('remote.') && key.endsWith('.promisor')) - ); - }); -} - -function compactIdentity(value: string): string { - return createHash('sha256').update(value).digest('hex').slice(0, 20); -} - -function managedHeadRef(workspaceId: string, workspaceEpochId: string): string { - return `refs/maka/workspaces/${workspaceId}/epochs/${workspaceEpochId}/head`; -} - -function managedBaselineRef(workspaceEpochId: string): string { - return `refs/maka/baselines/${workspaceEpochId}`; -} - -function worktreeLockReason( - identity: Pick, -): string { - return `maka managed workspace ${identity.workspaceInstanceId}`; -} - -function assertWorktreeRegistrationLocked( - porcelain: string, - worktreePath: string, - expectedReason: string, -): void { - const registration = findWorktreeRegistration(porcelain, worktreePath); - if (!registration || registration.lockReason !== expectedReason) { - throw new GitWorkspaceServiceError( - 'managed_workspace_identity_conflict', - 'Managed worktree registration or Maka ownership lock is unavailable', - ); - } -} - -interface WorktreeRegistration { - readonly headOid?: string; - readonly lockReason?: string; -} - -function findWorktreeRegistration( - porcelain: string, - worktreePath: string, -): WorktreeRegistration | undefined { - for (const block of porcelain.split(/\r?\n\r?\n/u)) { - const lines = block.split(/\r?\n/u); - const pathLine = lines.find((line) => line.startsWith('worktree ')); - if (!pathLine || !samePath(pathLine.slice('worktree '.length), worktreePath)) continue; - const headLine = lines.find((line) => line.startsWith('HEAD ')); - const lockedLine = lines.find((line) => line === 'locked' || line.startsWith('locked ')); - return { - ...(headLine ? { headOid: headLine.slice('HEAD '.length) } : {}), - ...(lockedLine ? { lockReason: lockedLine.slice('locked'.length).trim() } : {}), - }; - } - return undefined; -} - -function repositoryCapabilityDigest( - gitRuntimeSha256: `sha256:${string}`, - objectFormat: string, -): `sha256:${string}` { - const capability = JSON.stringify({ - protocol: 'maka_managed_git_repository_capability_v1', - gitRuntimeSha256, - objectFormat, - hooks: 'disabled_v1', - credentials: 'disabled_v1', - autoGc: false, - alternates: 'forbidden_v1', - }); - return `sha256:${createHash('sha256').update(capability).digest('hex')}`; -} - -function materializationProfileDigest( - gitRuntimeSha256: `sha256:${string}`, - objectFormat: string, -): `sha256:${string}` { - const profile = JSON.stringify({ - protocol: 'git_materialization_profile_v1', - gitRuntimeSha256, - objectFormat, - platform: process.platform, - autocrlf: false, - safecrlf: true, - attributes: 'reject_v1', - symlinks: 'reject_v1', - submodules: 'reject_v1', - caseCollisions: 'reject_v1', - ignoredInputs: 'exclude_v1', - }); - return `sha256:${createHash('sha256').update(profile).digest('hex')}`; -} - -function parseTreeEntries(output: Buffer): GitTreeEntry[] { - const entries: GitTreeEntry[] = []; - let start = 0; - while (start < output.length) { - const end = output.indexOf(0, start); - if (end < 0) { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Git source tree contains an unterminated entry', - ); - } - const record = output.subarray(start, end); - start = end + 1; - if (record.length === 0) continue; - const tab = record.indexOf(0x09); - const metadata = tab >= 0 ? record.subarray(0, tab).toString('ascii') : ''; - const pathBytes = tab >= 0 ? record.subarray(tab + 1) : Buffer.alloc(0); - const [mode = '', objectType = '', oid = ''] = metadata.split(' '); - let path = ''; - try { - path = new TextDecoder('utf-8', { fatal: true }).decode(pathBytes); - } catch { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Managed workspace v1 requires Git paths to be valid UTF-8', - ); - } - if ( - !mode || - !objectType || - !OID_PATTERN.test(oid) || - pathBytes.length === 0 || - !Buffer.from(path, 'utf8').equals(pathBytes) - ) { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Git source tree contains an unreadable entry', - ); - } - entries.push({ mode, objectType, oid, path, pathBytesBase64: pathBytes.toString('base64') }); - } - return entries; -} - -function baselineTreeSummary(entries: readonly GitTreeEntry[]): BaselineTreeSummary { - const manifest = JSON.stringify({ - protocol: 'maka_git_empty_tree_delta_v1', - entries: entries.map(({ mode, objectType, oid, pathBytesBase64 }) => ({ - mode, - objectType, - oid, - pathBytesBase64, - })), - }); - return { - treeDeltaDigest: `sha256:${createHash('sha256').update(manifest).digest('hex')}`, - changedFileCount: entries.length, - }; -} - -function assertSupportedTree(entries: readonly GitTreeEntry[]): void { - const caseFolded = new Set(); - for (const entry of entries) { - if ((entry.mode !== '100644' && entry.mode !== '100755') || entry.objectType !== 'blob') { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Symlinks, submodules, and special Git modes are not supported by managed workspace v1', - ); - } - if (entry.path === '.gitattributes' || entry.path.endsWith('/.gitattributes')) { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Tracked .gitattributes require a materialization profile not supported by v1', - ); - } - const folded = entry.path.normalize('NFC').toLocaleLowerCase('en-US'); - if (caseFolded.has(folded)) { - throw new GitWorkspaceServiceError( - 'repository_ineligible', - 'Case-colliding Git paths are not supported by managed workspace v1', - ); - } - caseFolded.add(folded); - } -} - -function baselineCommitEnvironment(): NodeJS.ProcessEnv { - return { - GIT_AUTHOR_NAME: 'Maka Workspace Service', - GIT_AUTHOR_EMAIL: 'workspace@maka.invalid', - GIT_AUTHOR_DATE: BASELINE_DATE, - GIT_COMMITTER_NAME: 'Maka Workspace Service', - GIT_COMMITTER_EMAIL: 'workspace@maka.invalid', - GIT_COMMITTER_DATE: BASELINE_DATE, - GIT_WORKSPACE_BASELINE_MESSAGE: BASELINE_MESSAGE, - }; -} - -function isOwnedBaselineCommit(raw: string): boolean { - const normalized = raw.replace(/\r\n/gu, '\n'); - const separator = normalized.indexOf('\n\n'); - if (separator < 0) return false; - const headers = normalized.slice(0, separator).split('\n'); - const message = normalized.slice(separator + 2).trimEnd(); - return ( - headers.length === 3 && - /^tree (?:[a-f0-9]{40}|[a-f0-9]{64})$/u.test(headers[0] ?? '') && - headers[1] === 'author Maka Workspace Service 946684800 +0000' && - headers[2] === 'committer Maka Workspace Service 946684800 +0000' && - message === BASELINE_MESSAGE.trimEnd() - ); -} - -function isolatedGitEnvironment( - input: VerifiedGitRuntimeInput, - homePath: string, -): NodeJS.ProcessEnv { - const executablePath = input.executablePath; - const env: NodeJS.ProcessEnv = { - HOME: homePath, - XDG_CONFIG_HOME: join(homePath, 'xdg'), - GIT_CONFIG_NOSYSTEM: '1', - GIT_TERMINAL_PROMPT: '0', - GCM_INTERACTIVE: 'never', - GIT_OPTIONAL_LOCKS: '0', - GIT_NO_REPLACE_OBJECTS: '1', - GIT_NO_LAZY_FETCH: '1', - LANG: 'C', - LC_ALL: 'C', - PATH: dirname(executablePath), - }; - if (input.distribution?.kind === 'dugite_native_v1') { - Object.assign( - env, - bundledGitEnvironment({ - platform: process.platform, - arch: process.arch, - rootPath: input.distribution.rootPath, - executablePath, - }), - ); - } - for (const name of ['SystemRoot', 'WINDIR', 'COMSPEC', 'TMP', 'TEMP', 'TMPDIR']) { - if (process.env[name]) env[name] = process.env[name]; - } - return env; -} - -function fixedGitArguments(hooksPath: string): string[] { - return [ - '--no-pager', - '-c', - 'core.autocrlf=false', - '-c', - 'core.safecrlf=true', - '-c', - 'core.fsmonitor=false', - '-c', - 'core.longpaths=true', - '-c', - `core.hooksPath=${hooksPath}`, - '-c', - 'credential.helper=', - '-c', - 'credential.interactive=never', - '-c', - 'core.sshCommand=', - '-c', - 'protocol.allow=never', - '-c', - 'gc.auto=0', - '-c', - 'submodule.recurse=false', - ]; -} - -function waitForChildExit(child: ChildProcess): Promise { - return new Promise((resolve, reject) => { - child.once('error', reject); - child.once('exit', (code) => resolve(code)); - }); -} - -function collectBoundedStderr(stream: NodeJS.ReadableStream | null): Promise { - if (!stream) return Promise.resolve(''); - return new Promise((resolve) => { - let output = ''; - stream.on('data', (chunk) => { - if (output.length < 64 * 1024) output += String(chunk); - }); - stream.on('end', () => resolve(output.slice(0, 64 * 1024).trim())); - }); -} - -async function sha256File(path: string): Promise<`sha256:${string}`> { - const hash = createHash('sha256'); - for await (const chunk of createReadStream(path)) hash.update(chunk); - return `sha256:${hash.digest('hex')}`; -} - -function exitCode(error: unknown): number | undefined { - if (!isRecord(error)) return undefined; - return typeof error.code === 'number' ? error.code : undefined; -} diff --git a/packages/storage/src/managed-baseline-receipt-authority-internal.ts b/packages/storage/src/managed-baseline-receipt-authority-internal.ts deleted file mode 100644 index e1c18ee458..0000000000 --- a/packages/storage/src/managed-baseline-receipt-authority-internal.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import type { - CreateManagedWorkspaceFromSourceInput, - GitWorkspaceService, - ManagedWorkspaceBaselineReceiptV1, - ManagedWorkspaceBinding, -} from './git-workspace-service.js'; - -export interface ManagedBaselineReceiptAuthorityInternal { - issue(binding: ManagedWorkspaceBinding): Promise; - require(input: CreateManagedWorkspaceFromSourceInput): Promise; - verify(receipt: ManagedWorkspaceBaselineReceiptV1): Promise; -} - -const receiptAuthorities = new WeakMap< - GitWorkspaceService, - ManagedBaselineReceiptAuthorityInternal ->(); - -export function registerManagedBaselineReceiptAuthorityInternal( - service: GitWorkspaceService, - authority: ManagedBaselineReceiptAuthorityInternal, -): void { - if (receiptAuthorities.has(service)) { - throw new Error('Managed baseline receipt authority is already registered'); - } - receiptAuthorities.set(service, authority); -} - -export function requireManagedBaselineReceiptAuthorityInternal( - service: GitWorkspaceService, -): ManagedBaselineReceiptAuthorityInternal { - const authority = receiptAuthorities.get(service); - if (!authority) { - throw new Error('Managed baseline receipt authority is unavailable'); - } - return authority; -} diff --git a/packages/storage/src/managed-workspace-execution-authority-internal.ts b/packages/storage/src/managed-workspace-execution-authority-internal.ts deleted file mode 100644 index 19676b9cde..0000000000 --- a/packages/storage/src/managed-workspace-execution-authority-internal.ts +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import type { RuntimeWorkspaceVersionAuthorityStore } from '@maka/core/runtime-event-store'; -import type { WorkspaceHeadRecordV1 } from '@maka/core/workspace-version-authority'; -import type { - ManagedWorkspaceBaselineReceiptV1, - ManagedWorkspaceBinding, -} from './git-workspace-service.js'; - -export interface ManagedWorkspaceExecutionHandle { - readonly kind: 'managed_workspace_execution_handle_v1'; -} - -export interface ManagedWorkspaceExecutionScope { - readonly kind: 'managed_workspace_execution_scope_v1'; -} - -export type ManagedWorkspaceExecutionAuthorityErrorCode = - | 'managed_workspace_execution_scope_invalid' - | 'managed_workspace_execution_scope_expired'; - -export class ManagedWorkspaceExecutionAuthorityError extends Error { - constructor( - readonly code: ManagedWorkspaceExecutionAuthorityErrorCode, - message: string, - ) { - super(message); - this.name = 'ManagedWorkspaceExecutionAuthorityError'; - } -} - -export interface ManagedWorkspaceExecutionAuthorityStateInternal { - readonly store: RuntimeWorkspaceVersionAuthorityStore; - readonly binding: Readonly; - readonly receipt: Readonly; - readonly head: Readonly; -} - -const states = new WeakMap< - object, - ManagedWorkspaceExecutionAuthorityStateInternal & { - readonly ownerToken: object; - } ->(); - -export interface ManagedWorkspaceExecutionScopeStateInternal { - readonly provisioning: 'canonical_tree_only_v1'; - readonly workspaceEffect: 'none'; - readonly cwd: string; - readonly binding: Readonly; - readonly head: Readonly; -} - -const scopes = new WeakMap< - object, - ManagedWorkspaceExecutionScopeStateInternal & { - readonly ownerToken: object; - active: boolean; - } ->(); - -export function issueManagedWorkspaceExecutionHandleInternal( - ownerToken: object, - state: ManagedWorkspaceExecutionAuthorityStateInternal, -): ManagedWorkspaceExecutionHandle { - const handle = Object.freeze({ kind: 'managed_workspace_execution_handle_v1' as const }); - states.set(handle, { ...state, ownerToken }); - return handle; -} - -export function requireManagedWorkspaceExecutionHandleInternal( - ownerToken: object, - handle: ManagedWorkspaceExecutionHandle, -): ManagedWorkspaceExecutionAuthorityStateInternal { - const state = states.get(handle); - if (!state || state.ownerToken !== ownerToken) { - throw new Error('Managed workspace execution handle is invalid for this owner'); - } - return state; -} - -function inspectManagedWorkspaceExecutionHandleForTest( - handle: ManagedWorkspaceExecutionHandle, -): ManagedWorkspaceExecutionAuthorityStateInternal { - const state = states.get(handle); - if (!state) throw new Error('Managed workspace execution handle is invalid'); - return state; -} - -export function issueManagedWorkspaceExecutionScopeInternal( - ownerToken: object, - state: ManagedWorkspaceExecutionScopeStateInternal, -): ManagedWorkspaceExecutionScope { - const scope = Object.freeze({ kind: 'managed_workspace_execution_scope_v1' as const }); - scopes.set(scope, { ...state, ownerToken, active: true }); - return scope; -} - -export function revokeManagedWorkspaceExecutionScopeInternal( - ownerToken: object, - scope: ManagedWorkspaceExecutionScope, -): void { - const state = scopes.get(scope); - if (!state || state.ownerToken !== ownerToken) { - throw new ManagedWorkspaceExecutionAuthorityError( - 'managed_workspace_execution_scope_invalid', - 'Managed workspace execution scope is invalid for this owner', - ); - } - state.active = false; -} - -export function requireManagedWorkspaceExecutionScopeInternal( - ownerToken: object, - scope: ManagedWorkspaceExecutionScope, -): ManagedWorkspaceExecutionScopeStateInternal { - const state = scopes.get(scope); - if (!state || state.ownerToken !== ownerToken) { - throw new ManagedWorkspaceExecutionAuthorityError( - 'managed_workspace_execution_scope_invalid', - 'Managed workspace execution scope is invalid for this owner', - ); - } - if (!state.active) { - throw new ManagedWorkspaceExecutionAuthorityError( - 'managed_workspace_execution_scope_expired', - 'Managed workspace execution scope has expired', - ); - } - return state; -} - -function inspectManagedWorkspaceExecutionScopeForTest( - scope: ManagedWorkspaceExecutionScope, -): ManagedWorkspaceExecutionScopeStateInternal { - const state = scopes.get(scope); - if (!state) { - throw new ManagedWorkspaceExecutionAuthorityError( - 'managed_workspace_execution_scope_invalid', - 'Managed workspace execution scope is invalid', - ); - } - if (!state.active) { - throw new ManagedWorkspaceExecutionAuthorityError( - 'managed_workspace_execution_scope_expired', - 'Managed workspace execution scope has expired', - ); - } - return state; -} - -/** - * Test-only evidence access. Production consumers must use the owner-bound - * require* functions and may never decode a handle or scope without its owner token. - */ -export const managedWorkspaceExecutionAuthorityTestSupport = Object.freeze({ - inspectHandle: inspectManagedWorkspaceExecutionHandleForTest, - inspectScope: inspectManagedWorkspaceExecutionScopeForTest, -}); diff --git a/packages/storage/src/managed-workspace-owner.ts b/packages/storage/src/managed-workspace-owner.ts deleted file mode 100644 index 5ba63d7293..0000000000 --- a/packages/storage/src/managed-workspace-owner.ts +++ /dev/null @@ -1,584 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { AsyncLocalStorage } from 'node:async_hooks'; -import { - createGitWorkspaceService, - type CreateManagedWorkspaceFromSourceInput, - GitWorkspaceServiceError, - type GitWorkspaceService, - type GitWorkspaceServiceFailpoint, - type ManagedWorkspaceBaselineReceiptV1, - type ManagedWorkspaceBinding, - type VerifiedGitRuntimeInput, -} from './git-workspace-service.js'; -import { - requireManagedBaselineReceiptAuthorityInternal, - type ManagedBaselineReceiptAuthorityInternal, -} from './managed-baseline-receipt-authority-internal.js'; -import { - issueManagedWorkspaceExecutionHandleInternal, - issueManagedWorkspaceExecutionScopeInternal, - requireManagedWorkspaceExecutionHandleInternal, - revokeManagedWorkspaceExecutionScopeInternal, - type ManagedWorkspaceExecutionHandle, - type ManagedWorkspaceExecutionScope, -} from './managed-workspace-execution-authority-internal.js'; -import { - createManagedWorkspaceWorkerBridgeInternal, - type ManagedWorkspaceFilesystemWorker, - type ManagedWorkspaceReadOnlyOperation, - type ManagedWorkspaceReadOnlyResult, - type ManagedWorkspaceWorkerBridgeInternal, -} from './managed-workspace-worker-bridge-internal.js'; -import type { RuntimeWorkspaceVersionAuthorityStore } from '@maka/core/runtime-event-store'; -import type { WorkspaceHeadRecordV1 } from '@maka/core/workspace-version-authority'; -import { - assertInteractiveRootOwner, - authenticateInteractiveRootOwner, - createStorageRootLeaseIdentityGuard, - runWithStorageRootLease, - type InteractiveRootOwner, -} from './root-authority.js'; -import { - assertWorkspaceBaselineAuthorityStoreRootInternal, - bindWorkspaceBaselineAuthorityStoreRootInternal, - commitWorkspaceBaselineInternal, - readWorkspaceHeadInternal, -} from './workspace-version-authority-internal.js'; - -// RuntimeEvent order is assigned by the SQLite authority spine. M0 therefore -// uses a protocol-fixed logical timestamp and keeps unauthenticated wall-clock -// time out of the durable Git receipt. -const MANAGED_BASELINE_LOGICAL_TIMESTAMP_V1 = 0; - -export type ManagedWorkspaceOwnerErrorCode = - | 'managed_workspace_owner_conflict' - | 'managed_workspace_owner_unavailable' - | 'managed_workspace_owner_closing' - | 'managed_workspace_owner_reentrant_close' - | 'managed_workspace_worker_unavailable' - | 'managed_workspace_quarantined' - | 'managed_workspace_execution_handle_invalid'; - -export class ManagedWorkspaceOwnerError extends Error { - constructor( - readonly code: ManagedWorkspaceOwnerErrorCode, - message: string, - options?: ErrorOptions, - ) { - super(message, options); - this.name = 'ManagedWorkspaceOwnerError'; - } -} - -export interface OpenManagedWorkspaceOwnerInput { - readonly rootOwner: InteractiveRootOwner; - readonly gitRuntime: VerifiedGitRuntimeInput; - readonly failpoint?: (point: ManagedWorkspaceOwnerFailpoint) => void | Promise; - readonly filesystemWorker?: ManagedWorkspaceFilesystemWorker; -} - -export type ManagedWorkspaceOwnerFailpoint = - | GitWorkspaceServiceFailpoint - | 'after_initial_store_root_validation' - | 'after_baseline_authority_commit' - | 'after_post_commit_artifact_verification' - | 'after_execution_artifact_verification'; - -export type OpenManagedWorkspaceBaselineInput = CreateManagedWorkspaceFromSourceInput; - -export interface OpenManagedWorkspaceBaselineResult { - readonly created: boolean; - readonly head: WorkspaceHeadRecordV1; - readonly executionHandle: ManagedWorkspaceExecutionHandle; -} - -export interface ManagedWorkspaceOwner { - readonly state: 'ready' | 'closing' | 'closed'; - openManagedWorkspaceBaseline( - store: RuntimeWorkspaceVersionAuthorityStore, - input: OpenManagedWorkspaceBaselineInput, - ): Promise; - withManagedWorkspaceExecution( - handle: ManagedWorkspaceExecutionHandle, - operation: (scope: ManagedWorkspaceExecutionScope) => Promise, - ): Promise; - executeReadOnlyFilesystemOperation( - scope: ManagedWorkspaceExecutionScope, - operation: ManagedWorkspaceReadOnlyOperation, - abortSignal?: AbortSignal, - ): Promise; - close(): Promise; -} - -export type { - ManagedWorkspaceExecutionHandle, - ManagedWorkspaceExecutionScope, - ManagedWorkspaceFilesystemWorker, - ManagedWorkspaceReadOnlyOperation, - ManagedWorkspaceReadOnlyResult, - VerifiedGitRuntimeInput, -}; - -const owners = new WeakMap(); - -export async function openManagedWorkspaceOwner( - input: OpenManagedWorkspaceOwnerInput, -): Promise { - const rootOwner = authenticateInteractiveRootOwner(input.rootOwner); - await assertInteractiveRootOwner(rootOwner); - if (owners.has(rootOwner)) { - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_owner_conflict', - 'This storage root owner already has a managed workspace owner', - ); - } - - const claim = {}; - owners.set(rootOwner, claim); - try { - const service = createGitWorkspaceService({ - storageRoot: rootOwner.capability.canonicalPath, - gitRuntime: input.gitRuntime, - ...(input.failpoint ? { failpoint: input.failpoint } : {}), - }); - await runWithStorageRootLease(rootOwner.lease, 'interactive', 'write', () => - service.assertAvailable(), - ); - // The root owner may begin closing while capability initialization is in - // flight. Revalidate after the lease-bound operation so a stale lifecycle - // owner is never published as ready. - await assertInteractiveRootOwner(rootOwner); - const owner = new ManagedWorkspaceOwnerImpl( - rootOwner, - service, - requireManagedBaselineReceiptAuthorityInternal(service), - input.failpoint, - input.filesystemWorker, - ); - owners.set(rootOwner, owner); - return owner; - } catch (error) { - if (owners.get(rootOwner) === claim) owners.delete(rootOwner); - if (error instanceof ManagedWorkspaceOwnerError) throw error; - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_owner_unavailable', - 'Unable to initialize the managed workspace owner', - { cause: error }, - ); - } -} - -class ManagedWorkspaceOwnerImpl implements ManagedWorkspaceOwner { - #state: 'ready' | 'closing' | 'closed' = 'ready'; - #activeOperations = 0; - readonly #drainWaiters = new Set<() => void>(); - readonly #assertCurrentRootIdentity: () => Promise; - readonly #executionContext = new AsyncLocalStorage(); - #closeTask: Promise | undefined; - readonly #executionOwnerToken = {}; - readonly #workerBridge: ManagedWorkspaceWorkerBridgeInternal | undefined; - - constructor( - private readonly rootOwner: InteractiveRootOwner, - private readonly service: GitWorkspaceService, - private readonly receiptAuthority: ManagedBaselineReceiptAuthorityInternal, - private readonly failpoint?: (point: ManagedWorkspaceOwnerFailpoint) => void | Promise, - filesystemWorker?: ManagedWorkspaceFilesystemWorker, - ) { - // Capture the identity guard while the lease is active. Unlike a fresh - // admission check, this guard remains valid for an already-admitted - // operation while owner.close() waits for that operation to drain. - this.#assertCurrentRootIdentity = createStorageRootLeaseIdentityGuard( - rootOwner.lease, - 'interactive', - 'write', - ); - this.#workerBridge = filesystemWorker - ? createManagedWorkspaceWorkerBridgeInternal(this.#executionOwnerToken, filesystemWorker) - : undefined; - } - - get state(): 'ready' | 'closing' | 'closed' { - return this.#state; - } - - async openManagedWorkspaceBaseline( - store: RuntimeWorkspaceVersionAuthorityStore, - input: OpenManagedWorkspaceBaselineInput, - ): Promise { - return this.#run(async () => { - await assertWorkspaceBaselineAuthorityStoreRootInternal( - store, - this.rootOwner.capability.canonicalPath, - ); - bindWorkspaceBaselineAuthorityStoreRootInternal(store, this.rootOwner.capability.rootId); - await this.failpoint?.('after_initial_store_root_validation'); - const existingHead = await readWorkspaceHeadInternal( - store, - input.workspaceId, - input.workspaceEpochId, - ); - const receipt = existingHead ? await this.receiptAuthority.require(input) : undefined; - const binding = receipt - ? await this.#openReadyBinding(receipt.binding) - : await this.#requireReady(await this.service.createManagedWorkspaceFromSource(input)); - const durableReceipt = receipt ?? (await this.receiptAuthority.issue(binding)); - if ( - existingHead && - (existingHead.workspaceVersionId !== durableReceipt.workspaceVersionId || - existingHead.acceptedEventId !== durableReceipt.baselineAcceptedEventId || - existingHead.commitOid !== binding.baselineCommitOid || - existingHead.treeOid !== binding.baselineTreeOid) - ) { - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_owner_unavailable', - 'Canonical workspace head does not match its durable Git baseline receipt', - ); - } - const committed = await commitWorkspaceBaselineInternal(store, { - epochOpenedEventId: durableReceipt.epochOpenedEventId, - baselineAcceptedEventId: durableReceipt.baselineAcceptedEventId, - committedAt: MANAGED_BASELINE_LOGICAL_TIMESTAMP_V1, - epoch: { - repositoryId: binding.repositoryId, - workspaceId: binding.workspaceId, - workspaceEpochId: binding.workspaceEpochId, - workspaceInstanceId: binding.workspaceInstanceId, - mode: 'managed_worktree', - objectFormat: binding.objectFormat, - sourceCommitOid: binding.sourceHeadCommitOid, - sourceTreeOid: binding.sourceTreeOid, - materializationProfileDigest: binding.materializationProfileDigest, - materializationSemantics: binding.materializationSemantics, - policyHash: durableReceipt.policyHash, - }, - baseline: { - workspaceVersionId: durableReceipt.workspaceVersionId, - commitOid: binding.baselineCommitOid, - treeOid: binding.baselineTreeOid, - treeDeltaDigest: durableReceipt.treeDeltaDigest, - changedFileCount: durableReceipt.changedFileCount, - deletedFileCount: durableReceipt.deletedFileCount, - }, - }); - await this.failpoint?.('after_baseline_authority_commit'); - // The transaction writes through the already-open SQLite handle. Rebind - // that handle to the canonical pathname after COMMIT so an external - // rename/replacement cannot make an orphan database look accepted. - await assertWorkspaceBaselineAuthorityStoreRootInternal( - store, - this.rootOwner.capability.canonicalPath, - ); - bindWorkspaceBaselineAuthorityStoreRootInternal(store, this.rootOwner.capability.rootId); - // Canonical acceptance never makes a missing Git artifact acceptable. - // Reverify after the SQLite transaction so post-accept artifact loss is - // reported fail-closed instead of returning a usable workspace head. - await this.receiptAuthority.verify(durableReceipt); - await this.failpoint?.('after_post_commit_artifact_verification'); - // The root marker is mutable host state and is not covered by receipt - // verification. Revalidate its identity at the final return gate without - // rejecting an operation that owner.close() is legitimately draining. - await this.#assertCurrentRootIdentity(); - const internalHead = freezeWorkspaceHead(committed.head); - const internalBinding = freezeManagedWorkspaceBinding(binding); - const internalReceipt = freezeManagedWorkspaceReceipt(durableReceipt); - return { - created: committed.created, - head: freezeWorkspaceHead(committed.head), - executionHandle: issueManagedWorkspaceExecutionHandleInternal(this.#executionOwnerToken, { - store, - binding: internalBinding, - receipt: internalReceipt, - head: internalHead, - }), - }; - }); - } - - async withManagedWorkspaceExecution( - handle: ManagedWorkspaceExecutionHandle, - operation: (scope: ManagedWorkspaceExecutionScope) => Promise, - ): Promise { - return this.#run(async () => { - let accepted; - try { - accepted = requireManagedWorkspaceExecutionHandleInternal( - this.#executionOwnerToken, - handle, - ); - } catch (error) { - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_execution_handle_invalid', - 'Managed workspace execution handle is invalid for this owner', - { cause: error }, - ); - } - assertExecutionCrossPlaneIdentity(accepted.binding, accepted.receipt, accepted.head); - // Test builds with a failpoint retain a preliminary proof so crash tests - // can stop after a real, completed artifact verification. The ordinary - // production path performs only the final proof below. - if (this.failpoint) { - await this.#verifyExecutionArtifactOrQuarantine(accepted.binding, accepted.receipt); - await this.failpoint('after_execution_artifact_verification'); - } - - // The root write lease excludes cooperating Maka writers across this - // proof bundle. Verify the durable Git artifact first, then make the - // immutable workspace head the final durable reread before scope issue. - await this.#assertCurrentRootIdentity(); - await this.#verifyExecutionArtifactOrQuarantine(accepted.binding, accepted.receipt); - const currentHead = await readWorkspaceHeadInternal( - accepted.store, - accepted.binding.workspaceId, - accepted.binding.workspaceEpochId, - ); - // Rebind only after the head read. This catches a database pathname - // detach at the admission boundary without putting a mutable DB guard - // ahead of the durable head evidence it protects. - await assertWorkspaceBaselineAuthorityStoreRootInternal( - accepted.store, - this.rootOwner.capability.canonicalPath, - ); - bindWorkspaceBaselineAuthorityStoreRootInternal( - accepted.store, - this.rootOwner.capability.rootId, - ); - if (!currentHead || !sameWorkspaceHead(currentHead, accepted.head)) { - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_owner_unavailable', - 'Managed workspace execution handle no longer matches the canonical workspace head', - ); - } - assertExecutionCrossPlaneIdentity(accepted.binding, accepted.receipt, currentHead); - const binding = accepted.binding; - const scope = issueManagedWorkspaceExecutionScopeInternal(this.#executionOwnerToken, { - provisioning: 'canonical_tree_only_v1', - workspaceEffect: 'none', - cwd: binding.worktreePath, - binding: Object.freeze({ ...binding }), - head: freezeWorkspaceHead(currentHead), - }); - try { - return await this.#executionContext.run(this.#executionOwnerToken, () => operation(scope)); - } finally { - revokeManagedWorkspaceExecutionScopeInternal(this.#executionOwnerToken, scope); - } - }); - } - - async executeReadOnlyFilesystemOperation( - scope: ManagedWorkspaceExecutionScope, - operation: ManagedWorkspaceReadOnlyOperation, - abortSignal?: AbortSignal, - ): Promise { - if (!this.#workerBridge) { - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_worker_unavailable', - 'Managed workspace filesystem worker is unavailable', - ); - } - return await this.#workerBridge.execute(scope, operation, abortSignal); - } - - close(): Promise { - if (this.#executionContext.getStore() === this.#executionOwnerToken) { - return Promise.reject( - new ManagedWorkspaceOwnerError( - 'managed_workspace_owner_reentrant_close', - 'Managed workspace owner cannot close from its own execution callback', - ), - ); - } - this.#closeTask ??= (async () => { - this.#state = 'closing'; - await this.#waitForDrain(); - this.#state = 'closed'; - })(); - return this.#closeTask; - } - - #assertReady(): void { - if (this.#state !== 'ready') { - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_owner_closing', - 'Managed workspace owner is closing or closed', - ); - } - } - - async #run(operation: () => Promise): Promise { - this.#assertReady(); - this.#activeOperations += 1; - try { - return await runWithStorageRootLease(this.rootOwner.lease, 'interactive', 'write', operation); - } finally { - this.#activeOperations -= 1; - if (this.#activeOperations === 0) { - for (const resolve of this.#drainWaiters) resolve(); - this.#drainWaiters.clear(); - } - } - } - - #waitForDrain(): Promise { - if (this.#activeOperations === 0) return Promise.resolve(); - return new Promise((resolve) => this.#drainWaiters.add(resolve)); - } - - async #requireReady(binding: ManagedWorkspaceBinding): Promise { - const inspection = await this.service.inspectManagedWorkspace(binding); - if (inspection.state === 'ready') return binding; - const quarantine = await this.service.quarantineManagedWorkspace( - binding, - 'external_workspace_drift', - ); - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_quarantined', - `Managed workspace drift was quarantined at ${quarantine.quarantinePath}`, - ); - } - - async #openReadyBinding(binding: ManagedWorkspaceBinding): Promise { - try { - return await this.#requireReady(await this.service.openManagedWorkspaceFromBinding(binding)); - } catch (error) { - if ( - !(error instanceof GitWorkspaceServiceError) || - error.code !== 'managed_workspace_drifted' - ) { - throw error; - } - const quarantine = await this.service.quarantineManagedWorkspace( - binding, - 'external_workspace_drift', - ); - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_quarantined', - `Managed workspace drift was quarantined at ${quarantine.quarantinePath}`, - { cause: error }, - ); - } - } - - async #verifyExecutionArtifactOrQuarantine( - binding: ManagedWorkspaceBinding, - receipt: ManagedWorkspaceBaselineReceiptV1, - ): Promise { - try { - await this.receiptAuthority.verify(receipt); - } catch (error) { - if ( - !(error instanceof GitWorkspaceServiceError) || - error.code !== 'managed_workspace_drifted' - ) { - throw error; - } - const quarantine = await this.service.quarantineManagedWorkspace( - binding, - 'external_workspace_drift', - ); - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_quarantined', - `Managed workspace drift was quarantined at ${quarantine.quarantinePath}`, - { cause: error }, - ); - } - } -} - -function sameWorkspaceHead(left: WorkspaceHeadRecordV1, right: WorkspaceHeadRecordV1): boolean { - return ( - left.workspaceId === right.workspaceId && - left.repositoryId === right.repositoryId && - left.workspaceEpochId === right.workspaceEpochId && - left.workspaceVersionId === right.workspaceVersionId && - left.acceptedEventId === right.acceptedEventId && - left.commitOid === right.commitOid && - left.treeOid === right.treeOid && - left.revision === right.revision - ); -} - -function freezeWorkspaceHead(head: WorkspaceHeadRecordV1): Readonly { - return Object.freeze({ ...head }); -} - -function freezeManagedWorkspaceBinding( - binding: ManagedWorkspaceBinding, -): Readonly { - return Object.freeze({ ...binding }); -} - -function freezeManagedWorkspaceReceipt( - receipt: ManagedWorkspaceBaselineReceiptV1, -): Readonly { - return Object.freeze({ ...receipt, binding: freezeManagedWorkspaceBinding(receipt.binding) }); -} - -function assertExecutionCrossPlaneIdentity( - binding: ManagedWorkspaceBinding, - receipt: ManagedWorkspaceBaselineReceiptV1, - head: Readonly, -): void { - if ( - !sameManagedWorkspaceBinding(binding, receipt.binding) || - head.repositoryId !== binding.repositoryId || - head.workspaceId !== binding.workspaceId || - head.workspaceEpochId !== binding.workspaceEpochId || - head.workspaceVersionId !== receipt.workspaceVersionId || - head.acceptedEventId !== receipt.baselineAcceptedEventId || - head.commitOid !== binding.baselineCommitOid || - head.treeOid !== binding.baselineTreeOid - ) { - throw new ManagedWorkspaceOwnerError( - 'managed_workspace_owner_unavailable', - 'Managed workspace execution evidence does not identify one exact accepted boundary', - ); - } -} - -function sameManagedWorkspaceBinding( - left: ManagedWorkspaceBinding, - right: ManagedWorkspaceBinding, -): boolean { - return ( - left.schemaVersion === right.schemaVersion && - left.protocol === right.protocol && - left.repositoryId === right.repositoryId && - left.workspaceId === right.workspaceId && - left.workspaceEpochId === right.workspaceEpochId && - left.workspaceInstanceId === right.workspaceInstanceId && - left.sourceRoot === right.sourceRoot && - left.sourceGitCommonDir === right.sourceGitCommonDir && - left.sourceHeadCommitOid === right.sourceHeadCommitOid && - left.sourceTreeOid === right.sourceTreeOid && - left.repositoryPath === right.repositoryPath && - left.worktreePath === right.worktreePath && - left.hooksPath === right.hooksPath && - left.baselineCommitOid === right.baselineCommitOid && - left.baselineTreeOid === right.baselineTreeOid && - left.headRef === right.headRef && - left.gitRuntimeSha256 === right.gitRuntimeSha256 && - left.objectFormat === right.objectFormat && - left.materializationProfileDigest === right.materializationProfileDigest && - left.materializationSemantics === right.materializationSemantics - ); -} diff --git a/packages/storage/src/managed-workspace-worker-bridge-internal.ts b/packages/storage/src/managed-workspace-worker-bridge-internal.ts deleted file mode 100644 index bf07b53e29..0000000000 --- a/packages/storage/src/managed-workspace-worker-bridge-internal.ts +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - createManagedExecutionBoundary, - type ExecutionBoundary, -} from '@maka/core/sandbox-boundary'; -import { createReadOnlyPermissionProfile } from '@maka/core/permission-profile'; -import { - requireManagedWorkspaceExecutionScopeInternal, - type ManagedWorkspaceExecutionScope, -} from './managed-workspace-execution-authority-internal.js'; - -export type ManagedWorkspaceReadOnlyOperation = - | { - readonly kind: 'read'; - readonly path: string; - readonly offset?: number; - readonly limit?: number; - } - | { - readonly kind: 'glob'; - readonly path: string; - readonly pattern: string; - readonly limit?: number; - } - | { - readonly kind: 'grep'; - readonly path: string; - readonly pattern: string; - readonly glob?: string; - readonly maxCountPerFile: number; - readonly limit: number; - readonly timeoutMs: number; - }; - -interface ManagedWorkspaceFilesystemWorkerInput { - readonly operation: ManagedWorkspaceReadOnlyOperation; - readonly cwd: string; - readonly executionBoundary: ExecutionBoundary; - readonly abortSignal?: AbortSignal; -} - -export type ManagedWorkspaceReadOnlyResult = - | { readonly kind: 'read'; readonly content: string } - | { - readonly kind: 'read_image'; - readonly base64: string; - readonly mimeType: 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp'; - } - | { readonly kind: 'glob'; readonly files: readonly string[] } - | { readonly kind: 'grep'; readonly matches: readonly string[] }; - -export interface ManagedWorkspaceFilesystemWorker { - /** - * Resolves only after the one-shot filesystem operation and every process it - * owns have reached a terminal lifecycle state. Implementations must not - * return a detached filesystem effect to the caller. The production adapter - * satisfies this contract through FilesystemWorkerClient; M1.2 admits only - * read-only operations, so a host crash cannot leave a workspace mutation. - */ - execute(input: ManagedWorkspaceFilesystemWorkerInput): Promise; -} - -export type ManagedWorkspaceWorkerBridgeErrorCode = 'managed_workspace_operation_denied'; - -export class ManagedWorkspaceWorkerBridgeError extends Error { - constructor( - readonly code: ManagedWorkspaceWorkerBridgeErrorCode, - message: string, - ) { - super(message); - this.name = 'ManagedWorkspaceWorkerBridgeError'; - } -} - -export interface ManagedWorkspaceWorkerBridgeInternal { - execute( - scope: ManagedWorkspaceExecutionScope, - operation: ManagedWorkspaceReadOnlyOperation, - abortSignal?: AbortSignal, - ): Promise; -} - -/** - * Owner-bound bridge from an opaque, revocable scope to the filesystem worker. - * The caller never supplies or receives cwd. Runtime validation intentionally - * repeats the TypeScript allowlist so JavaScript/forged inputs fail closed. - */ -export function createManagedWorkspaceWorkerBridgeInternal( - ownerToken: object, - worker: ManagedWorkspaceFilesystemWorker, -): ManagedWorkspaceWorkerBridgeInternal { - const bridge: ManagedWorkspaceWorkerBridgeInternal = { - async execute( - scope: ManagedWorkspaceExecutionScope, - operation: ManagedWorkspaceReadOnlyOperation, - abortSignal?: AbortSignal, - ) { - if (!isReadOnlyOperation(operation)) { - throw new ManagedWorkspaceWorkerBridgeError( - 'managed_workspace_operation_denied', - 'Managed workspace execution currently permits only Read, Glob, and Grep operations', - ); - } - const state = requireManagedWorkspaceExecutionScopeInternal(ownerToken, scope); - if (state.workspaceEffect !== 'none' || state.provisioning !== 'canonical_tree_only_v1') { - throw new ManagedWorkspaceWorkerBridgeError( - 'managed_workspace_operation_denied', - 'Managed workspace execution scope does not permit filesystem mutation', - ); - } - return await worker.execute({ - operation, - cwd: state.cwd, - executionBoundary: createManagedExecutionBoundary(createReadOnlyPermissionProfile(), 0), - ...(abortSignal ? { abortSignal } : {}), - }); - }, - }; - return Object.freeze(bridge); -} - -function isReadOnlyOperation(input: unknown): input is ManagedWorkspaceReadOnlyOperation { - if (!input || typeof input !== 'object') return false; - const kind = (input as { kind?: unknown }).kind; - return kind === 'read' || kind === 'glob' || kind === 'grep'; -} diff --git a/packages/storage/src/sqlite-runtime-store.ts b/packages/storage/src/sqlite-runtime-store.ts index 4ebc1b4832..91633b4197 100644 --- a/packages/storage/src/sqlite-runtime-store.ts +++ b/packages/storage/src/sqlite-runtime-store.ts @@ -280,7 +280,7 @@ export class SqliteRuntimeStore assertContinuationAuthorityCapability(this.db); assertWorkspaceVersionAuthorityCapability(this.db); if (!options.readOnly) { - this.registerWorkspaceBaselineAuthorityWriter(options.databaseLease.databasePath); + this.registerWorkspaceBaselineAuthorityWriter(); this.refreshToolLedgerHealth(); } return; @@ -306,7 +306,7 @@ export class SqliteRuntimeStore assertContinuationAuthorityCapability(this.db); assertWorkspaceVersionAuthorityCapability(this.db); if (!options.readOnly) { - this.registerWorkspaceBaselineAuthorityWriter(path); + this.registerWorkspaceBaselineAuthorityWriter(); this.refreshToolLedgerHealth(); } } catch (error) { @@ -1230,11 +1230,10 @@ export class SqliteRuntimeStore }); } - private registerWorkspaceBaselineAuthorityWriter(databasePath: string): void { + private registerWorkspaceBaselineAuthorityWriter(): void { const readWorkspaceHead = this.readWorkspaceHead.bind(this); registerWorkspaceBaselineAuthorityWriterInternal( this, - databasePath, (input, rootId) => this.#commitWorkspaceBaseline(input, rootId), (rootId) => this.#bindWorkspaceStorageRoot(rootId), readWorkspaceHead, diff --git a/packages/storage/src/workspace-version-authority-internal.ts b/packages/storage/src/workspace-version-authority-internal.ts index ed77104ee1..9346d8c1ce 100644 --- a/packages/storage/src/workspace-version-authority-internal.ts +++ b/packages/storage/src/workspace-version-authority-internal.ts @@ -22,10 +22,6 @@ import type { WorkspaceBaselineCommitResult, WorkspaceHeadRecordV1, } from '@maka/core/workspace-version-authority'; -import { lstatSync } from 'node:fs'; -import { realpath } from 'node:fs/promises'; -import { basename, dirname, join, normalize, resolve } from 'node:path'; -import { OPERATIONAL_STATE_DATABASE_NAME } from './operational-state-store.js'; type WorkspaceBaselineAuthorityWriter = ( input: WorkspaceBaselineAuthorityInput, @@ -41,8 +37,6 @@ interface WorkspaceBaselineAuthorityRegistration { readonly writer: WorkspaceBaselineAuthorityWriter; readonly readHead: WorkspaceHeadReader; readonly bindStorageRoot: WorkspaceStorageRootBinder; - readonly databasePath: string; - readonly databaseFileIdentity?: string; boundRootId?: string; } @@ -53,7 +47,6 @@ const workspaceBaselineAuthorityWriters = new WeakMap< export function registerWorkspaceBaselineAuthorityWriterInternal( store: object, - databasePath: string, writer: WorkspaceBaselineAuthorityWriter, bindStorageRoot: WorkspaceStorageRootBinder, readHead: WorkspaceHeadReader, @@ -61,13 +54,10 @@ export function registerWorkspaceBaselineAuthorityWriterInternal( if (workspaceBaselineAuthorityWriters.has(store)) { throw new Error('Workspace baseline authority writer is already registered'); } - const resolvedDatabasePath = resolve(databasePath); workspaceBaselineAuthorityWriters.set(store, { writer, readHead, bindStorageRoot, - databasePath: resolvedDatabasePath, - databaseFileIdentity: captureRegularFileIdentity(resolvedDatabasePath), }); } @@ -83,9 +73,10 @@ export function readWorkspaceHeadInternal( /** * Storage-internal authority seam. This module is deliberately absent from the - * @maka/storage package exports. Production composition reaches it only through - * ManagedWorkspaceOwner after durable Git receipt verification; focused - * persistence/crash tests use it directly to prove the SQLite transaction. + * @maka/storage package exports. The schema-9 reader, migration, and projection + * rebuild remain supported even though no production baseline writer is + * currently composed. Focused persistence tests use this seam to prove the + * SQLite transaction and historical read contract. */ export function commitWorkspaceBaselineInternal( store: object, @@ -111,65 +102,3 @@ export function bindWorkspaceBaselineAuthorityStoreRootInternal( registration.bindStorageRoot(rootId); registration.boundRootId = rootId; } - -export async function assertWorkspaceBaselineAuthorityStoreRootInternal( - store: object, - storageRoot: string, -): Promise { - const registration = workspaceBaselineAuthorityWriters.get(store); - if ( - !registration || - !registration.databaseFileIdentity || - basename(registration.databasePath) !== OPERATIONAL_STATE_DATABASE_NAME - ) { - throw new Error('Workspace baseline authority store is unavailable for this storage root'); - } - const expectedDatabasePath = join(storageRoot, OPERATIONAL_STATE_DATABASE_NAME); - const currentIdentity = captureRegularFileIdentity(registration.databasePath); - if (currentIdentity !== registration.databaseFileIdentity) { - throw new Error('Workspace baseline authority database file identity changed'); - } - let databasePath: string; - let expectedPath: string; - let expectedRoot: string; - try { - [databasePath, expectedPath, expectedRoot] = await Promise.all([ - realpath(registration.databasePath), - realpath(expectedDatabasePath), - realpath(storageRoot), - ]); - } catch (error) { - throw new Error('Workspace baseline authority store belongs to a different storage root', { - cause: error, - }); - } - const canonicalDatabasePath = normalize(databasePath); - const canonicalExpectedPath = normalize(expectedPath); - const canonicalExpectedRoot = normalize(expectedRoot); - if ( - !sameFilesystemPath(canonicalDatabasePath, canonicalExpectedPath) || - !sameFilesystemPath(dirname(canonicalDatabasePath), canonicalExpectedRoot) - ) { - throw new Error('Workspace baseline authority store belongs to a different storage root'); - } -} - -function captureRegularFileIdentity(path: string): string | undefined { - try { - const info = lstatSync(path, { bigint: true }); - // SQLite sidecars are pathname-scoped. Opening the same main database - // inode through a second hard-linked storage root can split its WAL/SHM - // coordination across two directories, so a canonical authority database - // must have exactly one directory entry. - if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1n) return undefined; - return `${info.dev}:${info.ino}:${info.nlink}`; - } catch { - return undefined; - } -} - -function sameFilesystemPath(left: string, right: string): boolean { - return process.platform === 'win32' - ? left.toLocaleLowerCase('en-US') === right.toLocaleLowerCase('en-US') - : left === right; -} diff --git a/scripts/ci-test-plan.mjs b/scripts/ci-test-plan.mjs index b6fd9431e6..3b19f8616f 100644 --- a/scripts/ci-test-plan.mjs +++ b/scripts/ci-test-plan.mjs @@ -249,7 +249,6 @@ function isE2eProductPath(path) { const STORAGE_STRESS_FILES = new Set([ 'packages/storage/src/agent-run-store.ts', - 'packages/storage/src/git-workspace-service.ts', 'packages/storage/src/runtime-event-invariants.ts', 'packages/storage/src/root-authority.ts', 'packages/storage/src/operational-state-store.ts', @@ -260,10 +259,8 @@ const STORAGE_STRESS_FILES = new Set([ 'packages/storage/src/sqlite-usage-schema.ts', 'packages/storage/src/sqlite-workflow-schema.ts', 'packages/storage/src/__tests__/agent-run-store.test.ts', - 'packages/storage/src/__tests__/git-workspace-service.test.ts', 'packages/storage/src/__tests__/root-authority.test.ts', 'packages/storage/src/__tests__/sqlite-recovery-concurrency.test.ts', - 'packages/storage/src/__tests__/fixtures/git-workspace-service-crash-child.ts', 'packages/storage/src/__tests__/fixtures/sqlite-recovery-concurrency-child.ts', 'packages/storage/src/__tests__/fixtures/root-lock-holder.ts', 'packages/storage/src/__tests__/fixtures/root-resolver.ts',