feat(git): establish Gitoxide repository admission and import - #3561
feat(git): establish Gitoxide repository admission and import#3561zhiiw wants to merge 27 commits into
Conversation
|
Addressed the unsupported-format admission gap at the open-error boundary.
I also tested the adjacent replacement-ref identity hypothesis. With Verification: 4/4 Rust protocol tests pass; ASF header audit and diff check pass. 中文说明已在 repository open-error 边界修复未知 object format 的分类缺口。
同时验证了相邻的 replacement-ref 身份假设:存在 验证结果:Rust 协议测试 4/4 通过,ASF header audit 与 diff check 通过。 |
|
The follow-up Gitoxide validation stack is now complete as three narrowly scoped stacked Drafts:
The combined real-helper contract now covers, on the same Runtime Host path:
The three-platform workflow builds the real Rust helper and runs the artifact, invocation, and admission contracts. These PRs intentionally remain Draft: they do not claim a signed packaged-release trust root, a protected installation root, Desktop/CLI consumption, T1 persistence, source import, projection, candidate, or ref CAS. Those are production integration gates, not hidden assumptions in the helper validation stack. 中文说明Gitoxide 后续验证链已经按三个窄边界完成:
联合真实 helper 合同已经证明:伪造 claim、错误 owner、symlink/junction、admission 后字节变化全部 fail closed;helper 使用固定 argv、最小环境、I/O 上限、超时/取消且不经过 shell;exit 0/1/2 严格对应 SHA-1 observation、稳定 operational failure、unsupported-format policy rejection;裸 path/OID 不能自证 admission;SHA-256 默认拒绝且不签发 capability。 三平台 workflow 会构建真实 Rust helper,并运行 artifact、invocation、admission 合同。这些 PR 仍保持 Draft,因为正式发布签名信任根、受保护安装路径、Desktop/CLI、T1、source import、projection/candidate/ref CAS 尚未接入;这些是后续生产接线门槛,不是当前验证栈里的隐藏假设。 |
98d5106 to
3453011
Compare
3453011 to
28a6b22
Compare
|
I expanded this Draft from the helper-only validation into one complete repository admission/import boundary. The updated head now includes the previously isolated artifact authority, short-lived invocation owner, repository admission capability, and exact source-import data plane. The primary invariant is therefore reviewable end to end: only an admitted helper invocation can establish and import the immutable repository identity used by later managed-workspace work. The branch was rebuilt from the latest main. Git range-diff reports all 12 extracted commits as patch-equivalent to the previously reviewed helper and fork PR #11-#14 slices. Successor publication, projection, packaging/product composition, Write/Edit, and resume remain out of scope. Local Core/Storage/Runtime/Runtime Host builds pass. The dedicated Linux/macOS/Windows helper jobs are now running against the expanded head. 中文说明我已经把这个 Draft 从“只有 helper 的验证切片”扩大成一条完整的 repository admission/import 边界。 当前 head 包含此前拆开的 artifact authority、短生命周期 invocation owner、repository admission capability 和 exact source-import data plane。现在可以端到端审查同一个主要不变量:只有经过 admission 的 helper invocation,才能确定并导入后续 managed workspace 使用的 immutable repository identity。 分支已经基于最新 main 重建。Git range-diff 显示 12 个提取提交与此前评审过的 helper 以及 fork #11-#14 patch 完全等价。Successor publication、projection、packaging/product composition、Write/Edit 和 resume 仍不在本 PR 范围内。 本地 Core、Storage、Runtime、Runtime Host 构建通过;Linux、macOS、Windows 专用 helper CI 正在针对新 head 运行。 |
6485980 to
4e6337f
Compare
4e6337f to
1377437
Compare
|
Thanks for the adversarial review. I addressed the correctness findings in
Regression coverage now includes source==destination, foreign bare/non-bare destinations, parent symlink/junction tampering, unsupported policy versions, helper-identity substitution, operation-specific deadlines, and Rust/TypeScript reason parity. Local Core/Storage/Runtime/Runtime Host builds, focused Node tests, Biome, and diff checks pass. The real Gitoxide workflow is green on macOS and Windows; Ubuntu is currently waiting for a runner. 中文说明感谢这次对抗性审查。
新增回归覆盖 source==destination、foreign bare/non-bare、父路径 symlink/junction、未知 policy、helper identity 替换、operation-specific deadline 和错误协议一致性。 本地四个 workspace build、Node 定向测试、Biome、diff check 均通过。真实 Gitoxide workflow 的 macOS 与 Windows 已绿色;Ubuntu 仍在等待 runner。 |
|
Thanks for the adversarial review. I addressed the findings in
Local CI-planner tests, Runtime Host build, protocol parity, Biome, and diff checks pass. The updated dedicated gate is green on macOS and Windows; Ubuntu is currently waiting for a runner. The general package/test/audit checks are still running or queued. The previously documented later boundaries remain intentionally unchanged: verify-to-spawn replacement resistance needs the packaged-release trust root; destination ownership still needs a state-root lease/capability; and partial artifact receipt/quarantine belongs to the future Storage owner. 中文说明感谢这次对抗性审查。
本地 CI planner、Runtime Host build、错误协议一致性、Biome 和 diff check 均通过。更新后的专用 gate 在 macOS、Windows 已绿色;Ubuntu 正在等待 runner,通用 package/test/audit 仍在运行或排队。 原先明确记录的后续边界保持不变:verify→spawn 的替换抵抗要依赖 packaged-release trust root;destination ownership 要等 state-root lease/capability;partial artifact 的 receipt/quarantine 由未来 Storage owner 负责。 |
|
Addressed the remaining object-identity, graph-walker, portability, protocol, CI, and documentation findings in
Verification at head
The Draft boundary remains unchanged: packaged-release trust, protected installation, state-root destination capability, partial-artifact recovery ownership, and Desktop/CLI/Write/Edit consumers remain follow-up gates. 中文说明已在
当前 head
Draft 的后续边界没有伪装完成:packaged-release trust、受保护安装目录、state-root destination capability、partial artifact recovery owner 以及 Desktop/CLI/Write/Edit consumer 仍属于后续切片。 |
|
Thanks for the detailed re-review. I addressed the remaining import-authority findings in
Regression coverage now includes corrupt blob storage identity, raw noncanonical/zero-padded modes, unsorted raw trees, Unicode fold collisions, lowercase reserved-device paths, and mismatched helper responses. All current checks pass: the fresh Rust helper and real invocation contract on Ubuntu/macOS/Windows, the main CI suite, dependency audit, and the Windows release/update/rollback contract. This PR can now be treated as merge-ready enabling infrastructure rather than product-ready functionality. The absence of a currently activated Desktop/CLI/Write/Edit consumer does not, by itself, require Draft status: the slice proves a complete repository-admission/import invariant, remains inert in production, and has concrete extracted downstream consumers for successor/projection, packaged authority, and Runtime Host composition. Those later slices must still land before managed mode is exposed to users. GitHub currently reports the PR as mergeable, and a merge-tree check against current 中文说明感谢这次细致的复审。本轮剩余问题已经在
新增回归覆盖 corrupt blob storage identity、非 canonical/zero-padded raw mode、未排序 raw tree、Unicode fold collision、小写 reserved-device path 和错配 helper response。当前 Gitoxide 三平台 gate、通用 CI、Dependency audit、Windows release/update/rollback contract 均已通过。 当前 PR 可以视为可合并的 enabling infrastructure,但不能视为已经产品化的功能。没有已经激活的 Desktop/CLI/Write/Edit 消费者,本身不再构成必须保持 Draft 的理由:本切片已经独立证明完整的 repository admission/import 不变量,默认不会激活生产行为,而且 successor/projection、packaged authority 和 Runtime Host composition 都已有明确的下游 extraction slice。只有这些下游能力完成后,managed mode 才能向用户开放。 GitHub 当前报告 PR 可合并,和最新 |
|
Addressed both remaining findings in
The dedicated Gitoxide admission gate is green on Ubuntu, macOS, and Windows, including Rust 1.98 formatting, the new real-helper metadata regression, Runtime Host build, and the invocation/process-tree contract. Dependency audit is also green; the broader CI and Windows package jobs are still running. 中文说明两个剩余问题已在
Gitoxide 专用 gate 已在 Ubuntu、macOS、Windows 全部通过,包含 Rust 1.98 formatting、新增真实 helper metadata 回归、Runtime Host build 与 invocation/process-tree 合同。Dependency audit 也已通过;通用 CI 和 Windows package job 仍在运行。 |
M4n5ter
left a comment
There was a problem hiding this comment.
English
Thanks for the careful work on this boundary. The overall direction is coherent: the opaque authority chain, exact object verification, shared preflight/copy walker, and fresh-destination behavior for honest contenders all make sense.
After reviewing b1604d358, I do not think the PR is merge-ready yet. Two issues break its primary bounded-admission contract.
Blocking
- The operation deadline does not cover the complete invocation lifecycle.
In gitoxide-helper-invocation-internal.ts, the 5-second/10-minute timer starts only inside invokeHelper(). Artifact revalidation and repository realpath() happen before that timer and cannot be interrupted while their filesystem I/O is pending. A repository on a stalled filesystem can therefore exceed the advertised deadline indefinitely.
After timeout, cancellation, or output overflow, the code records the termination reason and fires terminateChildProcessTree() without awaiting a bounded exit acknowledgement. The returned promise still waits indefinitely for close; if signaling/taskkill fails or the child is stuck in uninterruptible I/O, the operation never settles.
Please use one absolute deadline from the public operation entry through preflight, spawn, termination acknowledgement, and output drain. The existing child-process-lifecycle implementation already owns bounded forced-termination acknowledgement and should preferably be reused rather than creating a second lifecycle state machine. The resulting behavior also needs a Windows-native timeout/termination contract; the current test skips that path on Windows.
- The repository metadata admission budget is not authoritative over what Gitoxide actually reads.
assert_repository_metadata_budget() infers .git/bare/commondir paths independently from gix::open(), and those discovery rules can disagree.
I reproduced this on the current head with a valid bare SHA-1 repository whose root config was 1,100,249 bytes and which also contained an invalid empty .git/ child directory. The preflight budget inspected the child path, while Gitoxide opened the bare root and parsed the oversized root config. The helper returned repository_inspected instead of repository_metadata_limit_exceeded.
The later head_id() path is also outside this budget: the pinned gix ref reader uses an unbounded read_to_end(Vec) for HEAD and loose refs. A malformed but reachable local repository can therefore trigger large allocations before the helper's object limits or timeout protect the caller.
The fix should establish one authoritative repository-layout resolution shared with Gitoxide and bound the actual config/HEAD/ref metadata that will be read. Merely adding more guessed paths to the current preflight would remain fragile.
Important current-scope issues
- Protected filenames do not use the equivalence relation defined by policy v1.
The collision policy uses:
NFC → Default Full Case Folding → NFC
but .git and .gitattributes are checked only with eq_ignore_ascii_case().
I verified that a repository containing the single path .gitattributeſ—ending with U+017F LATIN SMALL LETTER LONG S—is successfully imported, although its v1 folded key is exactly .gitattributes.
Please extract one versioned fold function and use it both for collision keys and protected-name comparison. This should be covered by one raw-tree regression rather than a character-specific exception.
- The PR's readiness contract is contradictory.
The PR is no longer a Draft and the latest update calls it “merge-ready enabling infrastructure,” but the PR body and several architecture documents still state that it “remains Draft” or “must remain Draft” until the release owner and production consumers exist.
Please choose one status and make it authoritative everywhere. I think “merge-ready enabling infrastructure, not product-ready” is a reasonable position, provided the production gates remain explicit.
Contract clarification
create_dir() atomically claims a pathname only at one instant; it does not retain the directory identity throughout a potentially ten-minute import. A concurrent actor with rename rights can move the claimed directory and replace the leaf or an ancestor with a symlink/junction. The later path-based hooks deletion, object writes, and ref publication can then affect a foreign repository and may even return success for a different artifact.
I would not require a full retained-directory-handle implementation in this inert slice if concurrent same-user filesystem mutation is explicitly outside the threat model. However, the current unconditional claim that existing destinations are never modified should then be narrowed, and production composition must remain gated on a stable destination owner. Otherwise this needs to be fixed at the storage/path-identity boundary.
Simplification opportunities
I recommend removing some complexity while addressing the findings:
- Store the exact admitted helper capability inside the repository admission record. Import should consume the repository capability and internally reuse that helper, rather than requiring callers to resubmit a helper token/capability and comparing a mirrored
{sha256, bytes, protocolVersion}identity. - Remove the fresh repository's hooks remove/recreate sequence unless an actual consumer requires an empty hooks directory. The helper never executes hooks, and this path adds a destructive operation plus
import_hooks_cleanup_failed. - Remove the unreachable
import_destination_unreadableprotocol reason. - Keep temporary Draft/stacked-PR lifecycle status in one overview instead of duplicating it across five architecture contracts.
The deterministic zero-parent baseline is implemented correctly, but its test does not lock that invariant. The existing import test should import the same source into two fresh destinations, assert identical baseline OIDs, and verify zero parents plus the fixed identity/message.
Product compatibility gate
Policy v1 rejects every .gitattributes file, including the one in apache/maka itself. I do not consider that a blocker for deliberately inert enabling infrastructure, but it means v1 cannot yet be presented as suitable for ordinary managed workspaces. The eventual production consumer needs an explicit attributes/materialization decision or a policy v2.
Once the two boundedness blockers, the Unicode policy inconsistency, and the readiness contract are resolved, I would be happy to re-review.
中文
感谢作者对这条边界所做的细致工作。整体方向是成立的:opaque authority chain、精确 object 校验、preflight/copy 共用 walker,以及正常并发参与者下的 fresh destination 语义都比较合理。
审查 b1604d358 后,我认为当前版本还没有达到可合并状态。有两个问题直接破坏了本 PR 的 bounded admission 主要合同。
阻塞问题
- operation deadline 没有覆盖完整的 invocation lifecycle。
在 gitoxide-helper-invocation-internal.ts 中,5 秒/10 分钟 timer 直到进入 invokeHelper() 才开始。此前的 artifact 重验和 repository realpath() 都不受该 deadline 约束,其文件系统 I/O pending 时也无法响应 abort。位于卡顿文件系统上的 repository 因此可以无限超过所声明的 deadline。
timeout、cancel 或 output overflow 发生后,代码只是记录 termination reason,并以 fire-and-forget 方式调用 terminateChildProcessTree();最终 Promise 仍然无限等待 close。如果 signal/taskkill 失败,或者子进程卡在不可中断 I/O,operation 永远不会 settle。
建议从 operation public entry 开始,以一个 absolute deadline 统一拥有 preflight、spawn、termination acknowledgement 和 output drain。仓库现有的 child-process-lifecycle 已经实现了有界的强制终止确认,应优先复用,而不是再维护第二套较弱的 lifecycle state machine。修复后还需要 Windows 原生 timeout/termination contract;当前测试会在 Windows 跳过这条路径。
- repository metadata admission budget 并不能约束 Gitoxide 实际读取的 metadata。
assert_repository_metadata_budget() 独立推断 .git、bare repository 和 commondir 路径,其 discovery 规则可能与 gix::open() 不一致。
我在当前 head 上完成了真实复现:一个合法 bare SHA-1 repository,根 config 大小为 1,100,249 bytes,同时包含一个无效的空 .git/ 子目录。preflight 统计的是该子路径,但 Gitoxide 最终打开 bare root 并解析超大的根 config。helper 返回了 repository_inspected,而不是 repository_metadata_limit_exceeded。
后续 head_id() 也不在该预算内:锁定版本的 gix ref reader 会对 HEAD 和 loose ref 使用无界的 read_to_end(Vec)。因此,一个畸形但可达的本地 repository 可以在 object limit 或 timeout 保护调用方之前制造巨大的内存分配。
修复应建立一套与 Gitoxide 共用的 authoritative repository-layout resolution,并约束实际会读取的 config、HEAD 和 ref metadata。仅继续向当前 preflight 追加猜测路径仍然很脆弱。
当前范围内的重要问题
- protected filename 没有使用 policy v1 自己定义的等价关系。
collision policy 使用:
NFC → Default Full Case Folding → NFC
但 .git 和 .gitattributes 只通过 eq_ignore_ascii_case() 检查。
我已经验证:只包含 .gitattributeſ 的 repository 可以成功导入,其中最后一个字符是 U+017F LATIN SMALL LETTER LONG S;但它的 v1 folded key 正是 .gitattributes。
建议提取唯一、版本化的 fold 函数,同时用于 collision key 和 protected-name 判断,并通过一个 raw-tree regression 覆盖,不要添加针对单个字符的特判。
- PR 的 readiness contract 自相矛盾。
PR 已不再是 Draft,最新更新也称其为“merge-ready enabling infrastructure”;但 PR body 和多份架构文档仍明确写着“remains Draft”或在 release owner、生产消费者落地前“must remain Draft”。
需要选择唯一状态并在所有位置保持一致。我认为“可合并的 enabling infrastructure,但尚未 product-ready”是合理定位,前提是仍明确保留所有 production gate。
合同需要澄清的边界
create_dir() 只在某个瞬间原子领取 pathname,并没有在最长十分钟的导入期间持续持有该目录的 identity。具有 rename 权限的并发参与者可以移动已领取目录,并用 symlink/junction 替换 leaf 或 ancestor。此后基于 path 执行的 hooks 删除、object 写入和 ref 发布可能作用于 foreign repository,甚至可能针对另一个 artifact 返回成功。
如果明确把同用户并发文件系统篡改排除在当前 inert slice 的 threat model 外,我不认为本 PR 必须立即实现 retained directory handle。但当前“existing destinations are never modified”的无条件描述需要收窄,生产接线也必须继续等待稳定的 destination owner。否则应在 storage/path identity 边界从根因修复。
简化机会
建议在处理上述问题时顺带收回以下复杂度:
- 将 admission 时使用的 exact helper capability 直接保存到 repository admission record。import 只消费 repository capability,并在内部复用绑定的 helper;不再要求 caller 重交 helper token/capability,再比较一份镜像的
{sha256, bytes, protocolVersion}identity。 - 除非真实消费者要求 hooks 目录必须为空,否则删除 fresh repository 的 hooks remove/recreate。helper 本身不会执行 hooks,这条路径只增加了一个破坏性操作和
import_hooks_cleanup_failed状态。 - 删除完全不可达的
import_destination_unreadableprotocol reason。 - 不要在五份架构合同中重复维护临时的 Draft/stacked-PR 状态,将当前生命周期状态集中到一份总览。
deterministic zero-parent baseline 的当前实现正确,但测试没有锁定该不变量。建议让现有 import test 把同一 source 导入两个 fresh destination,断言 baseline OID 相同,并验证零 parent、固定 identity 和 message。
产品兼容门槛
policy v1 会拒绝所有 .gitattributes,包括 apache/maka 自身的 .gitattributes。对于刻意保持 inert 的 enabling infrastructure,我不认为这本身阻塞合并;但这意味着 v1 还不能被描述为适用于普通 managed workspace。未来生产消费者必须明确决定 attributes/materialization 语义,或者引入 policy v2。
处理两个 boundedness 阻塞问题、Unicode policy 不一致和 readiness contract 后,我愿意继续复核。
Summary
Establish one owner-bound Gitoxide repository admission and fresh source-import boundary without restoring bundled Git or discovering system Git.
managedTreePolicyVersion: 2, admitting only a deterministic.gitattributessubset and rejecting the retired version 1;Primary invariant
Only an admitted short-lived Gitoxide helper invocation may establish the immutable repository identity and source graph used by later managed-workspace operations. A claimed Git OID is accepted only when the object bytes recompute to that exact SHA-1. The admission capability prevents later callers from resubmitting the source path, HEAD/tree identity, helper identity, or tree policy.
Source preflight and copy use the same bounded verified graph walker. Policy v2 rejects path components that cannot be safely materialized across Linux, macOS, and Windows, including Windows device names, reserved/control characters, trailing dots/spaces,
.gitaliases, and folded path collisions. Canonical.gitattributesfiles are bounded to 64 KiB and may contain only comments/blank lines,* text=auto eol=lf, and literal portable-pathexport-ignorerules; filters, encodings, ident expansion, wildcards, and unknown attributes fail closed before destination claim.Failure and trust boundaries
unknownand fail closed without system-Git fallback.create_dir()destination claim.Verification
git diff --checkpass locally.中文说明
摘要
本 PR 建立一条 owner-bound 的 Gitoxide repository admission 与 fresh source-import 边界,不恢复 bundled Git,也不从系统 PATH 发现 Git。
managedTreePolicyVersion: 2;未发布的 version 1 已删除并明确拒绝;create_dir()领取,并发布确定性的 zero-parent baseline;主要不变量
只有已 admission 的短生命周期 Gitoxide helper 才能建立后续 managed workspace 使用的 immutable repository identity 与 source graph。Git OID 不能自证身份:对象 bytes 必须重新计算得到 exact claimed SHA-1。
source preflight 和 copy 由同一 walker 执行相同的 checksum、资源预算与路径策略。portable policy v2 在所有平台统一拒绝 Windows device names、保留/控制字符、尾部点或空格、
.git别名以及折叠路径冲突;规范.gitattributes每个最多 64 KiB,只允许注释/空行、* text=auto eol=lf与 literal portable-pathexport-ignore。filter、encoding、ident、wildcard 和未知 attribute 均在 destination claim 前 fail closed。本 PR 不宣称完整 Git attributes 兼容。仍保留的边界
unknown并 fail closed;验证
git diff --check通过。