Skip to content

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

Open
zhiiw wants to merge 7 commits into
apache:mainfrom
zhiiw:codex/gitoxide-accepted-tree-data-plane
Open

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

Conversation

@zhiiw

@zhiiw zhiiw 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 fully revalidate the direct ref, receipt commit, parent, signatures, request digest, tree, 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: 63 passed on the current source.
  • Runtime Host TypeScript build passed.
  • Focused Host boundary tests: 6 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@bfba25361.
中文说明

概要

本 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;相同请求 exact retry,不同请求稳定返回 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:63/63 通过;
  • Runtime Host TypeScript 构建通过;
  • focused Host 边界:6 通过,16 个依赖真实目标平台 helper 的条件测试按环境选择;
  • Rust format 与 git diff --check 通过;
  • 分支已重放到 main@bfba25361

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

zhiiw commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

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

zhiiw commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

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-Han Astro-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
M4n5ter force-pushed the codex/gitoxide-accepted-tree-data-plane branch from 08df97b to 16405e1 Compare August 26, 2026 08:50
@zhiiw
zhiiw force-pushed the codex/gitoxide-accepted-tree-data-plane branch from 16405e1 to e7356d9 Compare August 26, 2026 09:54
@zhiiw

zhiiw commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants