Skip to content

feat(runtime-host): add Gitoxide candidate and accepted-tree read data plane - #3857

Merged
zhiiw merged 11 commits into
apache:mainfrom
zhiiw:codex/gitoxide-accepted-tree-data-plane
Aug 30, 2026
Merged

feat(runtime-host): add Gitoxide candidate and accepted-tree read data plane#3857
zhiiw merged 11 commits into
apache:mainfrom
zhiiw:codex/gitoxide-accepted-tree-data-plane

Conversation

@zhiiw

@zhiiwzhiiw commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary Add a Gitoxide candidate/read data plane on top of repository admission and source import from #3561. This revision intentionally keeps filesystem projection out of the protocol. It provides two bounded capabilities: - publish an operation-bound immutable candidate outcome without advancing the accepted ref; - read one bounded UTF-8 file directly from an owner-bound accepted tree. ## Primary invariant An accepted-repository capability represents one exact accepted SHA-1 commit/tree at the fixed refs/maka/accepted namespace. Accepted and candidate refs must be direct refs to verified commit objects; symbolic refs and annotated tags are rejected. Candidate creation may publish only an operation-specific receipt ref and owner-bound outcome capability. A later SQLite acceptance/promotion owner remains the only component allowed to establish a new accepted head. ## Ownership and atomic boundaries - Source import captures the verified helper, managed repository, fixed accepted ref/commit/tree, and managed-tree policy v3. - The release claim must attest create_candidate and read_tree_file before destination import is claimed. - Candidate requests bind the exact accepted capability/repository, helper artifact, policy, base commit/tree, operation-derived candidate ref, path, request digest, result SHA-256, and Git object identities. - A checksum-verifying tree finder owns every tree load used by candidate construction. - Published and no-change outcomes both create a deterministic receipt commit and linearize at refs/maka/candidates/<sha256(operationId)>. - Existing candidate receipts are checked before any object write. Exact retries recompute the unique expected candidate tree from the verified base tree, inherited target mode, canonical path, and result blob without writing objects, then require the receipt tree to match exactly. They also revalidate the direct ref, receipt commit, parent, signatures, request digest, path, and result blob; conflicting requests return candidate_request_conflict without creating new objects. - CAS failures that cannot establish a readable direct receipt return candidate_publication_indeterminate. - Direct reads verify every traversed tree/blob identity and independently recompute the returned blob identity from the returned bytes. - Managed-tree policy v3 path semantics have one owner: the Rust helper. TypeScript enforces transport bounds and exact response correlation. ## Fail-closed behavior - Symbolic accepted/candidate refs and tag/non-commit targets fail closed with stable direct-ref errors. - Corrupt or replaced commit/tree/blob storage fails checksum verification. - The same operation cannot publish both no-change and changed outcomes. - Candidate ref conflicts never overwrite an existing outcome and are rejected before new object writes when already observable. - A candidate outcome cannot be consumed with a different accepted-repository capability, even when both repositories contain identical commits. - Helper timeout, abort, malformed output, result/blob mismatch, request-digest mismatch, artifact mismatch, or capability mismatch cannot issue an outcome capability. The second accepted-ref check and candidate-ref CAS are not one multi-ref transaction. Accepted-head drift in that narrow window may leave stale candidate evidence bound to the old base, but cannot advance accepted truth. The later SQLite acceptance owner must revalidate the canonical head and reject stale evidence. ## Durability scope Candidate receipt commits and refs are durable. Accepted and candidate capabilities are intentionally process-local in this enabling slice. Cross-Host reopen requires the later durable workspace-receipt owner to hold the storage-root lease and revalidate the exact repository, direct accepted/candidate refs, receipt checksum, request digest, base parent, candidate tree, and path/result blob before SQLite acceptance. Candidate retention/GC belongs to that later owner; this PR does not claim cross-Host reopen or GC. ## Verification - Rust unit and production-shaped helper tests: 68 passed on the current source (14 unit + 54 real-repository tests). - Runtime Host TypeScript build passed. - Focused Host boundary tests: 14 passed; 16 real-helper/platform tests remain conditionally selected by their required helper environment. - Rust formatting and git diff --check passed locally. - The branch was rebased onto main@6fca2f51e.
中文说明 ## 概要 本 PR 在 #3561 的 repository admission/source import 基础上,提供 Gitoxide candidate outcome 与 accepted-tree direct read 数据面;filesystem projection 仍留给后续 owner。 ## 主要不变量 accepted-repository capability 只代表固定 refs/maka/accepted 下的一个 exact SHA-1 commit/tree。accepted ref 与 candidate ref 都必须直接指向经过校验的 commit;symbolic ref 和 annotated tag 会被拒绝。candidate 只能发布 operation-specific receipt ref 与 owner-bound outcome capability,新的 accepted head 仍只能由后续 SQLite acceptance/promotion owner 建立。 ## 本轮收口 - 四个 ref 读取点统一改为 direct commit ref 校验; - 新增 symbolic accepted/candidate ref 与 annotated-tag target 的真实 helper 回归; - candidate ref 已存在时,在任何 object write 前重验 receipt;相同请求会从 verified base tree、目标继承 mode、canonical path 与 result blob 纯计算唯一 expected tree OID,并要求 receipt tree 完全一致;不同请求稳定返回 candidate_request_conflict; - publication 状态无法确定时返回 candidate_publication_indeterminate,不再混入笼统错误; - base tree inventory 在 successor walk 前释放,避免两套大型路径集合同时驻留; - 文档收窄 stale-ref 承诺:第二次 accepted 检查与 candidate CAS 不是同一个多 ref transaction,窗口内可能留下绑定旧 base 的 stale evidence,但不能推进 accepted truth,后续 SQLite owner 必须重新核验 canonical head。 ## 耐久范围 candidate receipt commit/ref 跨进程耐久,但 capability 仍是进程内 authority。跨 Host 重开需要后续 durable workspace-receipt owner 持有 storage-root lease,并重验 repository、direct refs、receipt checksum、request digest、base parent、candidate tree 与 path/result blob;candidate GC 也由该 owner负责。本 PR 不虚称已完成 cross-Host reopen 或 GC。 ## 验证 - Rust unit + production-shaped helper:68/68 通过(14 unit + 54 real-repository); - Runtime Host TypeScript 构建通过; - focused Host 边界:14 通过,16 个依赖真实目标平台 helper 的条件测试按环境选择; - Rust format 与 git diff --check 通过; - 分支已重放到 main@6fca2f51e

@zhiiw
zhiiw marked this pull request as draft August 26, 2026 04:09
@zhiiw
zhiiwforce-pushed the codex/gitoxide-accepted-tree-data-plane branch from 8de92c3 to 699e12dCompareAugust 26, 2026 05:44
@zhiiwzhiiw changed the title feat(runtime-host): add Gitoxide accepted-tree data planefeat(runtime-host): add Gitoxide candidate and accepted-tree read data planeAug 26, 2026
@zhiiw

Copy link
Copy Markdown
ContributorAuthor

Thanks for the adversarial review. I reworked the boundary around the findings instead of adding more checks to the previous combined capability model. The updated head is 699e12d39.

The main changes are:

  1. Accepted state and candidate state are now separate authorities.

    • GitoxideAcceptedRepositoryCapability only represents the immutable accepted ref/commit/tree.
    • Each mutation creates an owner-bound GitoxideCandidateCapability under an operation-specific candidate ref.
    • Candidate creation never advances the accepted ref and never returns a new accepted capability.
    • Direct reads through the original accepted capability therefore continue to observe the accepted baseline until a later acceptance owner explicitly commits a successor.
  2. Candidate creation now binds and verifies the exact base.

    • The request carries the accepted ref plus the expected base commit and tree OIDs.
    • The helper resolves the accepted ref and validates both identities before writing any object or ref.
    • The complete base tree is walked through the verified graph budget before mutation.
    • A stale base fails before candidate publication; exact retries converge on the same deterministic candidate ref and commit.
  3. Projection was removed from this PR and from the helper protocol.

    • materialize_projection / observe_projection and their Runtime Host authority surface were deleted.
    • This slice now owns only immutable candidate construction and accepted-tree reads.
    • Filesystem projection will be introduced with a storage-root-bound owner that can prove destination identity, atomic replacement, crash reconciliation, and platform semantics. The helper no longer accepts an arbitrary projection destination.
  4. Managed tree policy is now v3.

    • HFS-ignorable code points are removed before protected-name folding.
    • Aliases such as .g\u200cit and .gitattr\u200dibutes are rejected during both import and candidate creation.
    • v2 capabilities cannot be silently consumed by the v3 data plane.
  5. The remaining protocol/resource issues were tightened at the same boundary.

    • Helper attestation now advertises an exact supported-operation set, and admission checks required operations before claiming a destination.
    • No-change is a typed candidate_no_change response and publishes no candidate ref.
    • Candidate content uses bounded Base64 framing instead of embedding arbitrary text directly in JSON.
    • Commit/tree/object checksum mismatches have dedicated fail-closed reasons.

Regression coverage now includes accepted/candidate separation, stale-base rejection before writes, exact retry, corrupt base-tree storage, typed no-change, owner-bound candidate proof, legacy helper feature rejection before destination claim, and HFS alias rejection.

Verification for this head is green:

  • Gitoxide helper workflow: Ubuntu, macOS, and Windows passed.
  • Windows recovery gate passed.
  • Full CI passed, including build, typecheck, formatting, Runtime Host tests, Desktop e2e, Storybook, and installed CLI release-candidate validation.

The PR remains Draft while this authority boundary is reviewed.

中文说明

感谢这次对抗性审查。这轮没有继续给原来的混合 capability 叠加校验,而是重新划分了 owner 边界。当前 head 为 699e12d39

主要调整如下:

  1. accepted state 与 candidate state 已彻底分离。

    • GitoxideAcceptedRepositoryCapability 只代表不可变的 accepted ref/commit/tree。
    • 每次 mutation 只生成 operation-specific candidate ref 和 owner-bound GitoxideCandidateCapability
    • candidate 创建不会推进 accepted ref,也不会签发新的 accepted capability。
    • 因此在后续 acceptance owner 明确提交 successor 之前,原 accepted capability 的直接读取始终看到旧 baseline。
  2. candidate 创建绑定并验证 exact base。

    • 请求同时携带 accepted ref、expected base commit OID 和 tree OID。
    • helper 在写入任何 object/ref 前解析 accepted ref,并验证 commit/tree 身份。
    • mutation 前会在 graph budget 内完整遍历 base tree。
    • stale base 会在 candidate 发布前失败;精确重试收敛到同一个确定性 candidate ref/commit。
  3. projection 已从本 PR 和 helper protocol 中删除。

    • 删除了 materialize_projection / observe_projection 及其 Runtime Host authority。
    • 本切片现在只拥有 immutable candidate 构造和 accepted-tree 读取。
    • filesystem projection 将由后续 storage-root-bound owner 实现,届时单独证明 destination identity、原子替换、崩溃收敛和三平台语义;helper 不再接受任意 projection destination。
  4. managed tree policy 升级为 v3。

    • protected-name folding 前会删除 HFS ignorable code points。
    • import 与 candidate creation 都会拒绝 .g\u200cit.gitattr\u200dibutes 等别名。
    • v2 capability 无法静默进入 v3 data plane。
  5. 同一边界上的协议与资源问题也一起收紧。

    • helper attestation 明确声明支持的 operation 集合;destination claim 前先验证所需能力。
    • no-change 使用 typed candidate_no_change,且不发布 candidate ref。
    • candidate content 改用有界 Base64 framing,不再把任意文本直接嵌入 JSON。
    • commit/tree/object checksum mismatch 均有独立的 fail-closed reason。

新增回归覆盖 accepted/candidate 分离、写入前 stale-base 拒绝、exact retry、损坏 base tree、typed no-change、owner-bound candidate proof、旧 helper feature 在 destination claim 前拒绝,以及 HFS alias 拒绝。

当前 head 的验证全部通过:Gitoxide helper 的 Ubuntu/macOS/Windows、Windows recovery,以及完整 CI(build、typecheck、format、Runtime Host、Desktop e2e、Storybook、CLI release candidate)。

PR 继续保持 Draft,等待这次 authority 边界复审。

@zhiiw

Copy link
Copy Markdown
ContributorAuthor

Thanks for the adversarial re-review. I addressed the three protocol blockers at their shared evidence-owner boundary in dbbbdd807, and then fixed the resulting Windows inventory drift in 08df97b24.

1. Candidate construction now consumes verified tree bytes

The helper no longer calls Repository::find_tree() / edit_tree() after the verified graph walk. It now:

  • loads the root as an owned, checksum-verified tree;
  • uses a VerifiedTreeFinder for every lazy intermediate-tree lookup performed by the editor;
  • verifies object kind and checksum on each lookup;
  • reads the completed candidate commit back and verifies its exact parent, tree, request digest, and result path/blob edge before publication.

A regression performs the full verified walk, replaces a nested loose tree object under the old OID, and proves candidate editing fails instead of deriving from the replacement bytes.

2. Published and no-change now share one linear outcome authority

Both success dispositions now create a deterministic receipt commit and CAS the same operation-derived candidate ref. The receipt binds a domain-separated request digest covering accepted ref, base commit/tree, candidate ref, path, and result bytes.

This means:

  • no-change has a durable operation-bound linearization point;
  • no-change then changed, and changed then no-change, are both stable conflicts;
  • an exact concurrent retry converges to the same deterministic commit after CAS contention;
  • conflicting concurrent requests have one winner and one stable rejection.

Runtime Host issues one GitoxideCandidateOutcomeCapability for both published and no_change.

3. Candidate outcome is bound to the exact accepted lineage

The private capability record now binds:

  • the exact opaque accepted-repository capability object;
  • repository path and fixed accepted ref;
  • object format and policy version;
  • helper artifact SHA-256;
  • operation ID and request digest;
  • result-content SHA-256;
  • base/candidate commit/tree/ref/blob/path identities.

Consuming an outcome now requires presenting both owner tokens and the original accepted capability. A regression proves an outcome from repository A cannot be consumed with repository B's capability even when both repositories contain identical Git objects.

Additional P1/P2 hardening

  • TypeScript independently recomputes candidate resultBlobOid from the requested bytes.
  • Direct read independently recomputes blobOid from the returned content.
  • CAS failure re-reads the candidate ref and accepts only the identical deterministic commit.
  • The duplicated TypeScript policy-v3 parser was removed; Rust is the sole semantic policy owner, while TypeScript enforces only bounded transport and exact response correlation.
  • The accepted-ref choice was removed from the authority API. Accepted authority is fixed at refs/maka/accepted, disjoint from refs/maka/candidates/<operation-hash>.

Durability scope

The Git receipt commit/ref is durable, while accepted/candidate capabilities remain process-local in this slice. The architecture document now states this explicitly. Cross-Host reissuance remains owned by the later durable workspace-receipt/lifecycle layer, which must reopen the exact repository, revalidate the accepted ref/commit/tree, inspect the operation receipt, and then reissue capability. This PR no longer overstates same-process exact retry as cross-process reopen.

Verification

  • Rust unit + production-shaped helper tests: 59 passed.
  • Three-platform real-helper workflow: Ubuntu, macOS, and Windows passed.
  • Windows recovery: passed.
  • Runtime Host build, focused boundary tests, Biome, Rust formatting, ASF headers, Cargo notices, and git diff --check: passed.
  • The general CI job has passed build/typecheck/Runtime Host tests and is still running the repository-wide Desktop e2e step at the time of this reply.

The PR body has also been updated so it no longer claims that no-change omits a commit/ref, and it accurately records the deferred cross-Host reopen boundary.

中文说明

感谢这轮对抗性复审。这次没有继续分别补条件,而是把三个协议级问题统一收敛到 candidate evidence owner:

  1. verified tree 闭环:candidate editor 不再在 verified walk 后通过普通 ODB finder 重读 tree;root 与所有延迟加载的中间 tree 都必须经过 kind/checksum 校验,写完后还会读回 candidate commit,核对 parent、tree、request digest 与 path/blob 边。
  2. 统一终态 authority:changed 与 no-change 都创建 deterministic receipt commit,并在同一个 operation-derived ref 上 CAS。这样 no-change 也有耐久线性化点,同一 operation 不能再同时得到两种成功解释。
  3. 绑定 accepted lineage:统一的 GitoxideCandidateOutcomeCapability 私下绑定 exact accepted capability、repository/ref、helper identity、policy、operation/request digest、result SHA-256 和所有 Git identities;消费时必须同时出示原 accepted capability。相同 Git OID 的另一个 repository 也不能重放该 outcome。

同时完成了:

  • TypeScript 独立重算 candidate/direct-read 的 Git blob OID;
  • CAS 竞争失败后重读 ref,仅相同 deterministic commit 收敛为 exact retry;
  • 删除 TypeScript 中漂移的 policy-v3 语义实现,Rust helper 成为唯一 policy owner;
  • accepted ref 不再由 caller 传入,固定为 refs/maka/accepted,与 candidate namespace 完全分离。

跨 Host 的 capability 重签发没有假装在本 PR 中完成:Git receipt commit/ref 已耐久,但 capability 仍是进程内 authority;后续 durable workspace receipt/lifecycle owner 需要重开 exact repository、重验 accepted identity、检查 operation receipt 后再签发。文档和 PR body 已明确这个范围。

验证方面,Rust 59 个测试通过;Ubuntu/macOS/Windows 三平台真实 helper gate 与 Windows recovery 均通过。通用 CI 的 build、typecheck、Runtime Host tests 也已通过,当前只剩仓库统一 Desktop e2e 仍在运行。

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this head and found no blocking issues.

Implements two bounded capacities: immutable candidate result per operation (never touches accepted ref, TOCTOU double-check) and single UTF-8 read from owner-bound accepted tree (8MiB, canonical path). Hosted test/audit/owner/package/windows all SUCCESS.

No P0-P3.

简体中文该头无阻断。

Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

@M4n5ter
M4n5terforce-pushed the codex/gitoxide-accepted-tree-data-plane branch from 08df97b to 16405e1CompareAugust 26, 2026 08:50
@zhiiw
zhiiwforce-pushed the codex/gitoxide-accepted-tree-data-plane branch from 16405e1 to e7356d9CompareAugust 26, 2026 09:54
@zhiiw

Copy link
Copy Markdown
ContributorAuthor

Thanks for the careful re-review. I addressed the direct-ref blocker and the bounded follow-ups in e7356d96b.

  • All four accepted/candidate ref reads now use one strict direct-commit reader. Symbolic refs are rejected before peeling, and direct tag/non-commit targets are rejected after verified object loading.
  • Added real-helper regressions for symbolic and annotated-tag forms of both accepted and candidate refs.
  • Existing candidate receipts are now inspected before any object write. An exact request fully revalidates the receipt and returns the existing outcome; a different request returns candidate_request_conflict without adding objects.
  • Publication failures are split into deterministic conflict and candidate_publication_indeterminate instead of the previous generic publication error.
  • The base tree inventory is explicitly dropped before the successor walk, so both large path sets are not resident together.
  • I chose the documented contract-narrowing option for accepted-ref drift rather than introducing a multi-ref transaction here. A drift between the second accepted check and candidate CAS may leave stale evidence bound to its exact old base, but it cannot advance accepted truth. The later SQLite acceptance owner must revalidate canonical head before accepting it.
  • The PR body and architecture note now make the cross-Host boundary explicit: feat(runtime): establish managed mutation lifecycle authority #3741's composition owner must hold the storage-root lease and revalidate the direct refs, receipt checksum, request digest, parent, tree, and result blob. Candidate retention/GC stays with that durable owner.

Verification on the rebased head:

  • Rust helper: 63/63 passed.
  • Runtime Host TypeScript build: passed.
  • Focused Host boundary tests: 6 passed; 16 environment-selected real-helper/platform cases skipped locally.
  • Rust formatting and git diff --check: passed.
  • Rebased onto current main@bfba25361; GitHub platform checks are now running.
中文说明

感谢这轮细致复审。e7356d96b 已处理 direct-ref 阻塞和本切片内可以收口的几项问题:

  • accepted/candidate 的四个 ref 读取点统一使用严格 direct-commit reader;symbolic ref 不再 peel,tag/非 commit target 也会拒绝;
  • 增加 accepted/candidate 两侧 symbolic ref 与 annotated tag 的真实 helper 回归;
  • candidate ref 已存在时先于任何 object write 完整重验 receipt;同一请求 exact retry,不同请求返回 candidate_request_conflict,不再制造新的 orphan objects;
  • publication 错误区分为确定性 conflict 与 candidate_publication_indeterminate
  • successor walk 前显式释放 base tree inventory,避免两套大型路径集合同时驻留;
  • accepted-ref drift 选择了“收窄合同”而不是在本 PR 引入 multi-ref transaction:第二次检查与 candidate CAS 之间仍可能留下绑定旧 base 的 stale evidence,但它不能推进 accepted truth,后续 SQLite owner 必须重验 canonical head;
  • 文档明确了 feat(runtime): establish managed mutation lifecycle authority #3741 的跨 Host owner 必须持有 storage-root lease,并重验 direct refs、receipt checksum、request digest、parent、tree 和 result blob;candidate GC 也由该 durable owner承担。

重放到最新 main@bfba25361 后,本地验证为 Rust 63/63、Runtime Host 构建通过、focused Host 边界 6 个通过,Rust format 与 git diff --check 通过。GitHub 三平台检查正在运行。

@zhiiw
zhiiw marked this pull request as ready for review August 26, 2026 10:31
@github-actionsgithub-actionsBot added the effort/XL Over 1000 readable lines label Aug 27, 2026
@zhiiw
zhiiwforce-pushed the codex/gitoxide-accepted-tree-data-plane branch from e7356d9 to 26b4c4aCompareAugust 29, 2026 11:29

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the Gitoxide boundary seriously here. The accepted ref remains the sole repository authority, candidate creation returns a bounded receipt, and the read path validates object identity instead of introducing a parallel repository state. That overall direction is sound.

I reviewed exact head 26b4c4a31777c6b37b82362ac82b354cc627a311 and found two reachable issues:

  • P1 — category ②, reasonable release/capability mismatch:admitGitoxideRepositoryInternal() invokes inspect_repository without requiring that operation in the helper’s attested capability set. Likewise, importAdmittedGitoxideRepositoryInternal() checks only create_candidate and read_tree_file before invoking import_source_head. The invocation layer verifies the artifact but does not enforce operation support, so a helper can be accepted and then asked to execute an operation it never attested. Because operation-level release attestation is a load-bearing authority in this design, please require the actual operation at each entry point and add negative tests for a helper that lacks inspect_repository or import_source_head.
  • P2 — category ②, reasonable crash/object-store recovery path: candidate retry and the new self-check compare the result blob OID recorded in the tree, but do not load and checksum-verify that blob. A missing or corrupt blob can therefore still let the operation report success. Please verify the blob through the existing verified-object path, retain the 64 MiB bound, and cover missing/corrupt blob cases.

The first issue needs to be closed before approval; the second should be fixed but does not independently block. The rest of the authority model is notably clean, and I do not think this needs a broader redesign.

Review analysis was assisted by Codex and independent @reviewer agents; Astro-Han verified the exact-head call graph, capability checks, failure reachability, and CI state and owns this review.

中文对照

感谢你认真处理 Gitoxide 的权威边界。accepted ref 仍然是唯一的仓库权威,candidate creation 返回有界 receipt,读取路径也验证对象身份,没有引入平行的仓库状态;整体方向是对的。

我审查了当前提交 26b4c4a31777c6b37b82362ac82b354cc627a311,发现两个可达问题:

  • P1 — ② 合理的发布/能力不匹配路径:admitGitoxideRepositoryInternal() 调用 inspect_repository 前,没有要求 helper 的能力证明中包含该 operation;同样,importAdmittedGitoxideRepositoryInternal() 在调用 import_source_head 前只检查了 create_candidateread_tree_file。调用层只验证 artifact,没有校验 operation 支持,因此 helper 可能被接受后,又被要求执行它从未证明支持的操作。既然 operation 级发布证明是该设计中的关键权威,请在每个入口要求实际会调用的 operation,并补 helper 缺少 inspect_repositoryimport_source_head 的负向测试。
  • P2 — ② 合理的崩溃/对象存储恢复路径: candidate retry 和新的 self-check 会比较 tree 中记录的结果 blob OID,但不会真正加载并校验该 blob。这样在 blob 丢失或损坏时,操作仍可能报告成功。建议通过现有 verified-object 路径验证 blob,保留 64 MiB 上限,并覆盖 blob 丢失/损坏的用例。

第一个问题需要在 Approve 前关闭;第二个应该修,但它本身不单独阻塞。其余 authority 设计比较干净,我不认为需要更大范围的重构。

本次分析由 Codex 和独立的 @reviewer agents 协助;Astro-Han 核验了当前提交的调用图、能力检查、故障可达性和 CI 状态,并对本次评审负责。

@likun666661

Copy link
Copy Markdown
Member

从奥卡姆剃刀的角度重新捋了一遍,我认为这个 PR 的核心问题其实很简单,而且方向没有问题:

accepted A
-> 基于 A 生成 immutable staged candidate B
-> 用 operation-specific ref + CAS 原子发布 B
-> 后续 SQLite acceptance owner 决定是否 accept/promote A -> B

这个 PR 只拥有 stage,不拥有最后的 promote。因此 candidate creation 不能推进 accepted ref,也不能签发新的 accepted capability。按这个问题定义,下面这些复杂度都有直接必要性,并不属于无依据的过度设计:

  • exact accepted commit/tree:防止基于过期版本生成 candidate;
  • immutable candidate commit:保存 staged result;
  • operation-derived candidate ref + MustNotExist CAS:处理并发发布;
  • request digest:区分 exact retry 与 conflicting retry;
  • changed/no-change 共用 receipt authority:避免同一 operation 出现两个终态解释;
  • owner-bound outcome capability:让后续 acceptance 不能信任裸 OID;
  • accepted-tree direct read:在没有 filesystem projection 的情况下,为后续 Edit/pure transform 提供 exact base bytes。

所以我不认为这里需要重新设计更小的核心模型。当前更需要改进的是实现闭环和文档表达:

  1. 补齐 operation attestation 的实际 use-site 检查。
    admitGitoxideRepositoryInternal() 调用 inspect_repository 前应要求 inspect_repositoryimportAdmittedGitoxideRepositoryInternal() 在调用 import_source_head 前应要求 import_source_head。如果 import 成功后签发的 accepted capability 同时承诺 candidate/read 能力,那么这里可以继续要求 create_candidateread_tree_file,但不能漏掉当前实际调用的 operation。最好在 create/read 入口也分别要求对应 operation,使“调用哪个 operation,就在该入口验证哪个 operation”成为局部可见的不变量,并补缺失 operation 的负向测试。

  2. 完整验证 result blob,而不只是比较 tree entry 中的 OID。
    verify_existing_candidate_receipt()verify_candidate_commit_and_result() 当前确认 path edge 指向 expected blob OID,但没有真正加载 blob、验证 kind/checksum。missing/corrupt blob 因而可能在 self-check 或 exact retry 中仍报告成功。建议复用现有 verified-object path,保留 64 MiB bound,并补 fresh self-check 与 exact-retry 两侧的 missing/corrupt blob 回归。

  3. 把简单 causal spine 放到文档最前面。
    当前文档大量使用 owner/capability/receipt/identity 术语,容易掩盖本质。建议先明确写成“stage B via CAS; later owner promotes; this PR never changes accepted”,再展开安全边界。另请修正文档漂移:gitoxide-repository-admission-capability-v1.zh-CN.md 仍写“后续 candidate、tree read 必须消费 policy 2”,与当前 policy v3 合同不一致。

总结:stage + CAS + later promote 的问题定义已经足够简单;核心架构成立。当前阻塞点是 attestation 没有在实际 operation 入口闭环,完整性改进点是 result blob 没有被真正读取并 checksum 验证。

@zhiiw

Copy link
Copy Markdown
ContributorAuthor

Addressed both findings at their owning boundaries in e324fbf0c.

  1. Operation attestation is now enforced at every actual invocation entry.

    • inspect_repository, import_source_head, create_candidate, and read_tree_file each require their exact operation before helper verification/spawn.
    • The check remains inside the absolute-deadline/cancellation boundary, so a pre-aborted request still terminates before path preflight.
    • Added four negative tests using a valid admitted executable capability with the requested operation deliberately omitted. Each now fails with gitoxide_helper_release_claim_unsupported; import also proves the destination is not claimed.
  2. Candidate result blobs are now verified objects, not trusted tree edges.

    • Fresh candidate self-check and existing-receipt exact retry both load the result OID as a Blob through load_verified_object().
    • Both paths retain the 64 MiB bound and verify object kind, decoded size, and checksum.
    • Added production-shaped exact-retry regressions for a missing blob and a corrupt loose blob, plus a direct fresh-self-check regression with a corrupt object under the expected OID.
  3. Documentation was tightened.

    • The stage/CAS/later-promote causal spine is now first in the data-plane document.
    • The stale policy-2 reference now says policy 3.

Local verification:

  • Runtime Host build: passed.
  • Focused artifact/invocation/admission tests: 14 passed, 16 real-helper/platform-selected tests skipped locally.
  • Biome, rustfmt, Windows inventory (72 declarations), ASF header audit, and git diff --check: passed.
  • This Windows host does not have the MSVC linker, so the new Rust executable tests are delegated to the PR three-platform Gitoxide workflow now running.
中文说明

e324fbf0c 已在各自 owner 边界关闭两项问题:

  1. 四个真实 helper 入口现在分别要求 inspect_repositoryimport_source_headcreate_candidateread_tree_file 的 operation attestation,并且检查仍位于统一 deadline/cancellation 边界内。新增四条缺失 operation 的负向测试;import 还证明拒绝发生时 destination 尚未被占用。
  2. fresh candidate self-check 与 existing-receipt exact retry 不再只比较 tree edge OID,而是通过 load_verified_object() 真正加载 result blob,在 64 MiB 上限内验证 kind、大小与 checksum。新增 missing blob、corrupt loose blob 的真实 retry 回归,以及 fresh self-check 的 corrupt-object 回归。
  3. 文档把 stage B -> candidate-ref CAS -> later SQLite promote 的因果主线放到最前,并把遗留的 policy 2 更正为 policy 3。

本机 Runtime Host 构建和定向测试通过;Biome、rustfmt、Windows inventory、ASF header 与 diff check 通过。本 Windows 环境缺少 MSVC linker,因此新增 Rust 可执行测试交由正在运行的三平台 Gitoxide CI 验证。

@zhiiw

Copy link
Copy Markdown
ContributorAuthor

Follow-up: the first Ubuntu run exposed a portability issue in the new corruption fixture, not in the verifier. Gitoxide creates loose objects read-only on Linux, so the test could not overwrite the object (EACCES) before reaching the assertion. a147fa67e now moves the original object out of the ODB before placing corrupt bytes at the expected OID path. This preserves the intended missing/corrupt-object signal without depending on writable loose-object permissions. Rust formatting and git diff --check pass; the refreshed platform checks are starting.

中文说明

补充:首次 Ubuntu CI 暴露的是新增损坏夹具的跨平台权限假设,并非 verifier 失败。Gitoxide 在 Linux 上创建的 loose object 是只读文件,旧夹具直接覆盖时先触发 EACCES,尚未进入目标断言。a147fa67e 现在先把原对象移出 object database,再在预期 OID 路径放入错误对象;测试语义不变,同时不再依赖 loose object 可写。Rust 格式与 diff 检查已通过,新一轮三平台检查正在启动。

@zhiiw
zhiiwforce-pushed the codex/gitoxide-accepted-tree-data-plane branch from dee348f to 444481bCompareAugust 29, 2026 15:00
@zhiiw

Copy link
Copy Markdown
ContributorAuthor

Thanks for the precise retry-integrity review. The finding was valid: the prior exact-retry path proved the requested path/blob edge and general policy, but it did not prove that the receipt tree was exactly the deterministic one-path successor of the verified base.

I fixed this at the candidate-tree owner boundary in 444481b30:

  • fresh publication and exact retry now share the same verified tree editor;
  • retry uses that editor in a pure mode that serializes each edited tree and computes its Git tree OID without writing any objects;
  • the expected tree is derived only from the verified base tree, inherited target mode, canonical path, and verified result blob;
  • the receipt is accepted only when its candidate tree OID exactly equals that computed successor tree;
  • therefore an unrelated path addition, target mode flip, deletion, or any other same-request tree forgery fails with candidate_ref_target_invalid.

Two production-shaped real-helper regressions now forge otherwise valid receipt commits while preserving the parent, signatures, request digest, path, and result blob:

  1. one adds an unrelated file;
  2. one changes the target mode from 100644 to 100755.

Both exact retries fail closed. The full Rust suite passes: 14 unit + 54 real-repository tests (68 total).

I also rebuilt the complete PR stack onto current main@00a15abdc. The removed legacy baseline document stays removed because its only branch edit was the already-covered policy-v3 wording. The Windows skip inventory was regenerated from its owner script and now verifies at 68 declarations. Runtime Host builds after rebuilding its Core/Storage dependencies, and the focused Host boundary suite passes 14 tests with 16 environment-selected cases skipped locally. The refreshed GitHub platform checks are running.

中文说明

感谢这次精确定位 exact retry 的完整性缺口。这个问题成立:旧实现虽然证明了目标 path/blob edge 和通用 tree policy,却没有证明 receipt tree 恰好是 verified base 的唯一单路径 successor。

444481b30 已在 candidate tree owner 边界修复:

  • fresh publication 与 exact retry 复用同一个 verified tree editor;
  • retry 以纯计算模式序列化每层编辑后的 tree,并计算 Git tree OID,不写入任何 object;
  • expected tree 只由 verified base tree、目标继承 mode、canonical path 和已验证 result blob 推导;
  • receipt 的 candidate tree OID 必须与该 expected successor tree 完全一致;
  • 因此外加无关路径、目标 mode flip、删除或其他同请求 tree 伪造都会稳定返回 candidate_ref_target_invalid

新增两条真实 helper 回归:在保留 parent、signature、request digest、path 和 result blob 均合法的前提下,分别伪造“额外文件”和 100644 -> 100755 mode flip;两条 exact retry 都 fail closed。完整 Rust 套件为 14 unit + 54 real-repository,共 68/68 通过

同时整个 PR 已重建到当前 main@00a15abdc。主线删除的旧 baseline 文档没有复活,因为分支对它唯一的修改只是已被活跃文档覆盖的 policy-v3 表述;Windows inventory 由权威生成脚本重新生成并验证为 68。依赖重建后 Runtime Host 构建通过,focused Host 边界 14 个通过、16 个按环境选择的用例本机跳过。新的 GitHub 三平台检查正在运行。

@zhiiw
zhiiwforce-pushed the codex/gitoxide-accepted-tree-data-plane branch from 444481b to 2dfa1d8CompareAugust 29, 2026 15:59
@zhiiw

Copy link
Copy Markdown
ContributorAuthor

The failed general CI was isolated to Desktop WorkHub e2e, not the Gitoxide data plane. All dedicated Gitoxide and Windows recovery gates passed. The first run failed two late WorkHub rendering/reconstruction assertions; a failed-job rerun failed three WorkHub UI assertions, while build, typecheck, workspace tests, and Runtime Host tests all passed both times.

The branch was one commit behind current main, and that commit changes Runtime Host/session-context message and turn plumbing. Current main@6fca2f51e passed the same complete CI suite. I therefore used the repository's delivery rule as the discriminating fix: rebuilt the unchanged 11-commit PR stack onto main@6fca2f51e rather than modifying unrelated UI tests or increasing their timeouts.

range-diff confirms all 11 PR commits are patch-equivalent after the rebuild. The new head is 2dfa1d82e; refreshed checks are starting.

中文说明

通用 CI 的失败已定位到 Desktop WorkHub e2e,与 Gitoxide 数据面无关;专用 Gitoxide 三平台和 Windows recovery 均通过。首次运行有两个 WorkHub 渲染/重建断言在末段失败,重跑又有三个 WorkHub UI 断言失败,但两次的 build、typecheck、workspace tests 和 Runtime Host tests 都通过。

分支当时比最新主线少一个 Runtime Host/session-context message/turn 变更,而当前 main@6fca2f51e 已通过同一套完整 CI。于是我按“最终交付从最新 main 重建”的纪律处理:把原 11 个提交原样重放到 main@6fca2f51e,没有修改无关 UI 测试,也没有通过增加 timeout 掩盖问题。

range-diff 证明重建后的 11 个 PR commit 全部 patch-equivalent。新 head 为 2dfa1d82e,新的检查已启动。

@zhiiw

Copy link
Copy Markdown
ContributorAuthor

CI follow-up after the latest-main rebuild: the general job remains red only in unrelated Desktop WorkHub e2e.

Evidence:

  • the original head failed two late WorkHub rendering/reconstruction assertions;
  • rerunning that failed job produced three WorkHub UI failures;
  • rebuilding the patch-equivalent 11-commit stack onto current main@6fca2f51e produced the same three WorkHub failures;
  • current main@6fca2f51e itself has a successful complete CI run;
  • this PR changes no Desktop, WorkHub, or e2e files;
  • on every PR run, build, formatting, typecheck, affected workspace tests, and Runtime Host tests passed;
  • Gitoxide helper gates on Ubuntu/macOS/Windows and Windows recovery all pass on the current head.

The repeated failures are workhub-layout.spec.ts and the two workhub-reconstruction.spec.ts cases, all at the late UI element/stability boundary. I am not changing unrelated WorkHub production code, weakening assertions, or increasing timeouts in this Gitoxide PR. The PR head remains mergeable and patch-equivalent to the reviewed implementation; the remaining red check is a repository-level WorkHub e2e stability issue.

中文说明

更新到最新主线后,通用 CI 仍只在无关的 Desktop WorkHub e2e 变红:

  • 原 head 失败两个 WorkHub 渲染/重建断言;
  • 重跑失败 job 后出现三个 WorkHub UI 失败;
  • 将完全等价的 11 个提交重建到当前 main@6fca2f51e 后,仍是同三个 WorkHub 失败;
  • main@6fca2f51e 自身的完整 CI 是成功的;
  • 本 PR 没有修改 Desktop、WorkHub 或 e2e 文件;
  • 每轮 PR CI 的 build、format、typecheck、workspace tests、Runtime Host tests 都通过;
  • 当前 head 的 Ubuntu/macOS/Windows Gitoxide gate 与 Windows recovery 全部通过。

重复失败集中在 workhub-layout.spec.ts 和两个 workhub-reconstruction.spec.ts 的末段 UI 元素/稳定性边界。我不会在 Gitoxide PR 中修改无关 WorkHub 生产代码、放宽断言或增加 timeout。PR head 仍然 mergeable,且与已审核实现 patch-equivalent;剩余红灯属于仓库级 WorkHub e2e 稳定性问题。

@likun666661likun666661 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. The core boundary is appropriately narrow: construct immutable staged candidate B from accepted A, publish the operation receipt through CAS, and leave acceptance/promotion to the later lifecycle owner. The latest changes close the reviewed evidence gaps: exact operation attestation is enforced at each invocation, candidate result blobs are loaded and checksum-verified, and exact retry proves the entire deterministic one-path successor tree rather than only the requested path/blob edge. Dedicated Gitoxide checks pass on Ubuntu, macOS, and Windows, and Windows recovery passes. The remaining general-CI failure is isolated to unrelated WorkHub e2e and should still be handled according to repository merge policy.

@zhiiw
zhiiwforce-pushed the codex/gitoxide-accepted-tree-data-plane branch from 2dfa1d8 to 650c5a3CompareAugust 30, 2026 08:16
@zhiiw
zhiiwforce-pushed the codex/gitoxide-accepted-tree-data-plane branch from 650c5a3 to 58badaeCompareAugust 30, 2026 11:38
@zhiiw
zhiiw merged commit d07ff87 into apache:mainAug 30, 2026
5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XLOver 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@zhiiw@likun666661@Astro-Han