@staix/agent-hub 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +4 -4
  3. package/docs/agent-notes/README.md +11 -0
  4. package/docs/agent-notes/adapters.md +10 -6
  5. package/docs/agent-notes/benchmarks.md +1 -0
  6. package/docs/agent-notes/daemon.md +6 -2
  7. package/docs/agent-notes/local-worker.md +3 -1
  8. package/docs/agent-notes/tasks.md +11 -2
  9. package/docs/agent-notes/tests.md +7 -3
  10. package/docs/dogfood.md +22 -2
  11. package/docs/memory.md +1 -1
  12. package/docs/operations.md +162 -35
  13. package/docs/profiles.md +36 -0
  14. package/docs/quickstart.md +15 -5
  15. package/docs/sdlc.md +29 -3
  16. package/docs/security.md +44 -13
  17. package/docs/smoke.md +3 -3
  18. package/docs/specs/2026-09-19-agent-hub-design.md +31 -10
  19. package/docs/verification/2026-10-25-0.18.0.md +76 -0
  20. package/docs/verification/2026-10-25-codex-review-live-verdict.md +35 -0
  21. package/docs/verification/2026-10-25-remote-docker-transport.md +56 -0
  22. package/docs/verification/2026-10-26-agy-1.4.7.md +69 -0
  23. package/docs/verification/2026-10-26-claude-implementer-2.1.310.md +69 -0
  24. package/docs/verification/2026-10-26-headless-claude-shell-context.md +62 -0
  25. package/docs/verification/2026-10-26-jcode-0.99.1.md +49 -0
  26. package/docs/verification/2026-10-26-kimi-232-reviewer-compat.md +53 -0
  27. package/docs/verification/2026-10-26-pi-140-compat.md +41 -0
  28. package/docs/verification/2026-10-26-qwen-0261-acp.md +60 -0
  29. package/docs/verification/2026-10-27-0.19.0.md +86 -0
  30. package/docs/verification/claude-permission-transport-native.py +187 -0
  31. package/docs/verification/claude-shell-wrapper-host.ts +20 -0
  32. package/docs/verification/claude-shell-wrapper-native.py +145 -0
  33. package/docs/verification/pi-140-model-free.ts +57 -0
  34. package/docs/verification/pi-140-native.mjs +21 -0
  35. package/docs/verification/profiles/YYYY-MM-DD-general.md +46 -0
  36. package/docs/verification/profiles/YYYY-MM-DD-research.md +61 -0
  37. package/docs/verification/profiles/YYYY-MM-DD-writing.md +46 -0
  38. package/docs/verified.json +24 -18
  39. package/package.json +2 -2
  40. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  41. package/plugins/agent-hub/server.js +364 -98
  42. package/src/adapters/agy/entry.ts +1 -1
  43. package/src/adapters/claude-channel.ts +7 -3
  44. package/src/adapters/claude-implementer.ts +5 -3
  45. package/src/adapters/compat.ts +32 -18
  46. package/src/adapters/conformance/fakes/pi-rpc.ts +1 -0
  47. package/src/adapters/definitions/builtin-metadata.ts +3 -3
  48. package/src/adapters/definitions/native-factories.ts +7 -2
  49. package/src/adapters/definitions/reviewer-variants.ts +2 -2
  50. package/src/adapters/local-worker.ts +5 -3
  51. package/src/adapters/pi.ts +39 -3
  52. package/src/adapters/qwen-dual.ts +25 -0
  53. package/src/adapters/registry-types.ts +6 -2
  54. package/src/adapters/registry.ts +1 -0
  55. package/src/cli/agents.ts +69 -7
  56. package/src/cli/claude-shell-wrapper.ts +54 -0
  57. package/src/cli/console-state.ts +9 -2
  58. package/src/cli/help.ts +29 -28
  59. package/src/cli/identity.ts +87 -6
  60. package/src/cli/main.ts +198 -71
  61. package/src/cli/output.ts +11 -2
  62. package/src/cli/sdlc-output.ts +242 -0
  63. package/src/cli/task-output.ts +212 -0
  64. package/src/cli/upgrade-output.ts +57 -0
  65. package/src/hub/agent-drift.ts +61 -0
  66. package/src/hub/board.ts +31 -2
  67. package/src/hub/check-runner.ts +385 -25
  68. package/src/hub/claude-shell-wrapper.ts +349 -0
  69. package/src/hub/conductor.ts +5 -0
  70. package/src/hub/config-apply.ts +16 -7
  71. package/src/hub/config-edit-host.ts +1 -1
  72. package/src/hub/config-edit.ts +2 -2
  73. package/src/hub/config-tier.ts +3 -3
  74. package/src/hub/daemon.ts +530 -87
  75. package/src/hub/dogfood-profile.ts +248 -0
  76. package/src/hub/dogfood-read.ts +77 -14
  77. package/src/hub/dogfood.ts +5 -1
  78. package/src/hub/events.ts +1 -1
  79. package/src/hub/host-executable.ts +21 -1
  80. package/src/hub/hub-tools.ts +2 -2
  81. package/src/hub/instances.ts +18 -2
  82. package/src/hub/lifecycle.ts +1 -1
  83. package/src/hub/operational-authority.ts +67 -0
  84. package/src/hub/operational-view.ts +137 -0
  85. package/src/hub/orphan-process.ts +69 -0
  86. package/src/hub/permission-mode.ts +1 -1
  87. package/src/hub/read-only-shell-proof.ts +81 -0
  88. package/src/hub/read-only-shell.ts +162 -0
  89. package/src/hub/review-scope.ts +13 -0
  90. package/src/hub/reviewer-pool-config.ts +3 -2
  91. package/src/hub/reviewer-pool.ts +3 -0
  92. package/src/hub/routing.ts +23 -6
  93. package/src/hub/sdlc-merge.ts +299 -0
  94. package/src/hub/sdlc-metrics.ts +6 -4
  95. package/src/hub/sdlc-mirror.ts +129 -0
  96. package/src/hub/sdlc.ts +58 -12
  97. package/src/hub/settings.ts +11 -5
  98. package/src/hub/snapshots.ts +60 -0
  99. package/src/hub/task-repository.ts +31 -5
  100. package/src/hub/task-shell-command.ts +85 -0
  101. package/src/hub/task-shell-default.ts +78 -0
  102. package/src/hub/task-shell-proof.ts +171 -0
  103. package/src/hub/task-shell-sandbox.ts +61 -0
  104. package/src/hub/tasks.ts +378 -27
  105. package/src/local/deny.ts +91 -11
  106. package/src/local/sandbox.ts +67 -28
  107. package/src/local/shell-environment.ts +10 -0
  108. package/src/local/tools.ts +17 -4
  109. package/src/ui/index.html +3 -1
@@ -2070,7 +2070,8 @@ reviewer may also call it for that task. They get exactly the conductor's
2070
2070
  view: `publicConductorTask` over `Tasks.publicView(task, true)`, so the whole
2071
2071
  history with its done summary and check line, notes that match a PII pattern
2072
2072
  withheld, and a PII task as a stub with no history. Any other peer is refused
2073
- with the conductor-role error, whatever the task. That refusal is about the
2073
+ with the conductor-role error, whatever the task, except the configured planner,
2074
+ which reads any task through the same view (#812, below). That refusal is about the
2074
2075
  tool, not a secret: the view holds nothing private beyond the PII stub, and the
2075
2076
  control op `task_show` (`ahub task show` from an agent shell) already returns
2076
2077
  the same public view to any peer. These reads write no conduct audit event, as
@@ -2461,7 +2462,7 @@ acts on the stopped state directory.
2461
2462
  - A console-role `{ t: "peer_stop", peer }` request stops a hub-owned headless ACP, Pi or local runtime through its adapter's owned-runtime teardown. Success is `{ ok: true, state: "offline" }`; refusal or incomplete stop is `{ ok: false, error }` naming the reason and next action. New requests already receive an unknown-request reply on older hubs, so `PROTOCOL` stays unchanged.
2462
2463
  - Refuse agent/tools-role callers, unknown or offline peers, Claude/Codex and Pi TUI owners, and recovery/shutdown-held mutations. Starts, requested stops and permission changes are fenced per peer; recovery preparation waits until requested stops finish. A TUI is ended in its terminal, or by the upgrade plan's verified terminal binding.
2463
2464
  - A requested stop marks each in-flight delivery `needs_review` with a fixed ids-only stop reason, cancels pending approvals, and fences stale native completion/failure callbacks. It never completes or implicitly redelivers interrupted work, invokes failure escalation, or triggers Pi's idle auto-restart. The operator inspects effects and explicitly resolves the delivery before replay. Queued unattempted work remains for the next manual start under existing holds/pauses.
2464
- - `ahub stop <peer>` is human-only in `classifyPeerCommand`, prints the returned state, and reports an older hub's unknown request as requiring an upgrade, with a nonzero exit. The upgrade plan's headless `endPlannedPeer` uses the same console request; the daemon does not close a native terminal.
2465
+ - `ahub stop <peer>` is available to the person or the selected project's configured conductor in `classifyPeerCommand` (#812). The conductor retains its authenticated peer identity and audit; other agents receive the person guidance promptly, including when the hub is unreachable. It prints the returned state and reports an older hub's unknown request as requiring an upgrade, with a nonzero exit. The upgrade plan's headless `endPlannedPeer` uses the same console request; the daemon does not close a native terminal.
2465
2466
 
2466
2467
  - Requested stop admission also holds per-peer drain, pull and steering before any teardown await, without changing operator/budget pauses. Arriving work remains queued for explicit restart. Idle Pi captures live empty-session proof before stopping; a first accepted prompt with unpersisted history is not silently recreated as empty.
2467
2468
 
@@ -3284,7 +3285,7 @@ PII authority, complete-row host screening, capture exclusion, byte budgets and
3284
3285
 
3285
3286
  ### Bounded sandboxed-peer capabilities (#418)
3286
3287
 
3287
- The frozen issue #418 contract supplies task-bound `hub_task_spec` and the finite `hub_check_start/result/cancel` tools; memory reads reuse #412 P2. `checks.runner = "container"` remains an untracked, person-owned opt-in. Specs include validated canonical GitHub intake identity as well as bounded textual references and remain complete-or-refused; checks run immutable Git source in a credential-free, internal-network Linux VM with fixed commands and binding-scoped receipts. Default sandbox permissions remain unchanged.
3288
+ The frozen issue #418 contract supplies task-bound `hub_task_spec` and the finite `hub_check_start/result/cancel` tools; memory reads reuse #412 P2. `checks.runner = "container"` or `"remote"` remains an untracked, person-owned opt-in; the remote grant and destination require `config.local.json`. Specs include validated canonical GitHub intake identity as well as bounded textual references and remain complete-or-refused; checks run immutable Git source in a credential-free, isolated Linux VM/container with fixed commands and binding-scoped receipts. Default sandbox permissions remain unchanged. The remote backend (#793) streams a metadata-stripped, owner-normalized archive to non-root `docker exec -i ... tar` extraction in its work tmpfs, rather than Docker cp into a read-only-root container. Readiness proves that same transfer and readback path plus network isolation; project/name-filtered inventory reads individual hub labels and confirms cleanup. Each remote run proves or prepares a Docker volume of `node_modules` keyed by the commit's `bun.lock` hash, a digest of every manifest and install configuration file (`package.json`, `bunfig.toml`, `.npmrc`, `patches/`), and the Bun version, architecture and image digest the readiness probe observed (#793b). The one container with a network runs exactly `bun install --frozen-lockfile`; a separate offline container verifies the volume read-only; the check runs offline with the volume mounted read-only and never installs. A commit whose manifests declare an install lifecycle script is refused, because that output depends on source outside the key. Pruning keeps the newest three of the project's own volumes and touches only volumes whose name, project label and key label agree. Preparation is one `prepareDeps` seam on the transport (ownership, install, marker, verify and discard are internal steps, so none can be skipped), `dependencyKey` and `dependencyVolume(projectKey, key)` name the volume, and `dependencyManifest(source)` is asynchronous because the inputs are several files in the staged source.
3288
3289
 
3289
3290
  Apple container 1.5.0 requires a running VM for `copy`. The implementation therefore uses `run --detach --rm` with the fixed bounded idle process, copies the validated source/dependencies, executes only the selected finite check argv and confirms cleanup. A create/copy/start probe failed on the stopped-container copy and left no container; the run/copy/exec probe passed. `--internal` is verified as network configuration `mode: hostOnly`, not an inferred network name. Inactive sandboxed peers establish the cached spec authentication probe at first start/use rather than making unrelated host-embedding fixtures contact real accounts. Source and dependency staging ceilings are 100,000 entries each, 512 MiB source and 1 GiB dependencies; exceeding one refuses the check.
3290
3291
 
@@ -3302,7 +3303,7 @@ Evidence: `docs/verification/2026-10-17-418-probes.md`. The feasibility probe is
3302
3303
  - Ready ownerless tasks retain the ordinary routing decision and reservation guards. A persisted cause/replanning signature admits one ids-only console and supervision notice for an ineligible reserved peer, no eligible peer or a cancelled dependency. Repeated release passes with the same cause add neither history nor notices; a changed cause or explicit replanning admits one new notice. The designated planner (the settled configured planner roles) receives bounded ids-only advice in its next preface, while the configured supervision stream receives the structured item under its existing scope. Advice does not manufacture an ordinary task turn beside a stale backlog batch. Without a planner, the console attention list remains the recipient. Existing owner/reviewer ladders remain unchanged.
3303
3304
  - A current proposed offer to a pull-only peer remains console attention until acceptance, decline, supersession or positive delivery evidence for that exact offer envelope and recipient. Tasks records the original envelope id, assignment revision and offer-time pull-only classification before publication. This classification survives detach and restart; current attachment is not evidence that an unread offer disappeared. Actual adapter completion, a correlated reply or a completed pull records positive evidence atomically with journal completion, surviving terminal pruning and reopening. Accepted-only receipts, unrelated inbox activity and operator/hub completion are not read evidence. Advisory queries never dequeue, steer or settle work. Decoded offer/stall candidates are cached by board revision and live eligibility inputs; positive journal read evidence is checked afresh. The derived revision advances atomically with task and stall writes, independently of unrelated delivery-journal writes.
3304
3305
  - The disabled-by-default sweep adds `ready-unassigned` with `ready_min: 60`. A ready proposed ownerless task follows the existing deterministic, restart-safe ladder, without treating quiet stall/offer bookkeeping as activity. A person-unassigned task stays suppressed until explicit person assignment or reservation. Planner-pulled work retains reserved-only/WIP admission, and a saved-but-undelivered reassignment does not stop the remaining sweep. Cancellation does not satisfy a dependency. This change does not enable any project's live sweep configuration.
3305
- - `cancelled` is terminal and retained. The person may cancel backlog, proposed, in-progress, in-review or changes-requested work, never approved work. A configured planner tool may cancel only its own backlog/proposed task with no historical acceptance. `Tasks.cancel` validates the optional flow revision, screens the reason/reference and rechecks authority, revision and recovery/shutdown exclusion after asynchronous screening before the sole board mutation. History retains actor, reason and optional supersession reference. After the cancellation write commits, advisory cleanup and notices cannot change its successful result. No outcome memory, native cancellation, branch deletion or dependent release occurs.
3306
+ - `cancelled` is terminal and retained. The person may cancel backlog, proposed, in-progress, in-review or changes-requested work, never approved work. A configured planner tool may cancel only its own backlog/proposed task with no execution evidence: never started, or accepted and then declined or returned untouched (#812, which replaces the earlier rule of no historical acceptance). `Tasks.cancel` validates the optional flow revision, screens the reason/reference and rechecks authority, revision and recovery/shutdown exclusion after asynchronous screening before the sole board mutation. History retains actor, reason and optional supersession reference. After the cancellation write commits, advisory cleanup and notices cannot change its successful result. No outcome memory, native cancellation, branch deletion or dependent release occurs.
3306
3307
  - Cancellation releases overlap/reservation participation and sends each distinct former owner/reviewer one stop notice. Its trusted cancellation revision marker keeps that stop notice relevant; earlier notices for the cancelled task become moot under #434. The classifier never uses timestamps alone to identify the stop notice. Dependents keep waiting and receive the cancelled-dependency cause. List, show, explain, dashboard and release projections treat cancellation as closed, retaining the current privacy screen and distinguishing cancellation from successful approval.
3307
3308
  - The daemon advertises `task_cancel` in hello and status features. Current clients refuse cancellation without that feature. Upgrade and restart persist the verified target's exported features with its immutable root/digest before prepare, including cached targets, and reuse that receipt on resume after verifying the same bytes. A legacy staged receipt without feature evidence is probed only while a project is not yet past commit; fully committed projects do not require another probe. This preserves #215: a transient probe failure cannot refuse a target that was already staged with verified evidence. Preflight checks only uncommitted sources and refuses a target lacking `task_cancel` while cancelled rows exist. Source prepare/commit recheck before holds/snapshots and around asynchronous work; cancellation cannot write through recovery preparation. Refusals name task ids and never rewrite rows. An older binary started manually can misclassify cancelled rows as open or fail to move them; this is the explicit unsupported compatibility ceiling. Never map cancellation to approval.
3308
3309
  - The post-release live step remains separate: do not cancel the historical task or enable the project's sweep from implementation tests. AC7 needs installed-runtime receipts.
@@ -3868,7 +3869,9 @@ Owner decision 2026-10-18 (#450 comment 6248184754), R1-R5. R3's mechanism, chos
3868
3869
  - **R1/R5, routing.** `assign()` prefers a dedicated reviewer (holds `reviewer`, not `planner`) among the eligible peers, busy or not; family independence and the PII rule (the person reviews) are unchanged. When a dedicated reviewer is declared but not eligible (offline, not started, paused) the planner reviews as before and the assignment note says `reviewer=planner (fallback)`. With no dedicated reviewer declared nothing changes. The reviewer is chosen at assignment, as before (ponytail: a task assigned during a fallback keeps the planner; re-pick at done if that matters).
3869
3870
  - **Budget.** The reviewer shares the `claude` account and its pauses; usage from each child's `result` event is added to the peer's tokens. No quota is classified from its text.
3870
3871
  - **Privacy.** The diff lists changed paths first, with rename and copy detection, and leaves out every change touching a path that `local.deny` or `pii.paths` covers (a move or copy whole, when either side is private), noting only how many were withheld (ponytail: git's 50% similarity decides what counts as a move); a diff that matches a private-data pattern is withheld whole. Those entries are also passed to the child as Claude `Read` deny rules (Claude evaluates them, not the hub's path policy). A private review is never given to it.
3871
- - **Narrative on the read tool.** `hub_task_show` from a review-only instance returns the task with history notes kept only on `approved` and `changes_requested` entries; the done summary and other notes are dropped.
3872
+ - **Narrative on the read tool.** `hub_task_show` from a review-only instance keeps history notes on `approved` and `changes_requested` entries and the bounded planner-ruling exception below; the done summary and all other notes are dropped.
3873
+
3874
+ - **Planner-ruling exception (#835).** Review requests and review-only task views also carry `plan reviewed` notes after the current ownership boundary, the latest `accepted` or `claimed` event (none without either). The author must be the current effective `taskPlanner(task)`, including its legacy role-holder fallback, and still hold the planner role. Keep the oldest five rulings, screen and sanitize each full note before cutting its displayed text to 1000 characters, and retain the withheld marker. PII tasks show none. Each ruling names its author and timestamp; these rulings inform the reviewer and amend review context only. They do not accept a plan departure: departure acceptance stays the person's and `accepted: false` remains unchanged.
3872
3875
  - **Verdict authority.** A headless reviewer's `hub_review` is accepted only while the child that started for that task still runs (a per-child token, so a replacement child for the same task is a new authority). It is captured when the call starts and rechecked before every write, after each await. The leader's exit, a watchdog or a stop revokes it first. A review ends only when the child's whole process group is confirmed gone; survivors hold the delivery as `needs_review` and keep the peer busy with its handle: a stop fails and leaves it busy (never offline), and a start is refused, until they exit.
3873
3876
  - **Settlement.** A start failure before any review in a delivery ran is `failed_safe`; after one ran, the delivery is held as `needs_review`. A failing facts or verdict-check callback is logged, and the review counts as without a recorded verdict.
3874
3877
  - **Never an owner.** The reviewer variant carries `routing.reviewOnly`; assignment refuses it as an owner for every task class and when named.
@@ -4028,7 +4031,7 @@ pure decision and name the chosen instance. Reviewer independence compares
4028
4031
  families (the kind); an eligible other family takes precedence even when busy.
4029
4032
  Only when no other family is eligible may an assignment use its own family,
4030
4033
  recording `sameFamilyFallback=true` in assignment history. This independence rule
4031
- also applies to an explicit same-family reviewer nomination. A declared owner of
4034
+ also applies to an explicit same-family reviewer nomination. `review.same_family = "never"` (#820, default `"fallback"`) removes that last resort in the pool and the legacy path: the planner reviews when it holds the reviewer role, is not the owner, is of another family and is not blocked, else the person, and a same-family nomination is refused with `same family as <owner>; review.same_family is never`. A declared owner of
4032
4035
  implement, bulk-edit or test work requires the current implementer role and positive
4033
4036
  host evidence tied to the actual adapter generation started in a verified noncanonical
4034
4037
  worktree; a role-only reload cannot promote a canonical reviewer process. External
@@ -4480,8 +4483,8 @@ Declaration: `"claude-impl": { "kind": "claude", "roles": ["implementer"], "star
4480
4483
  - **Hub tools.** The implementer set (`hub_task_accept`, `hub_task_decline`, `hub_task_done`, `hub_checkpoint`, `hub_send`, `hub_task_show`, `hub_task_propose`, `hub_remember`, `hub_inbox`, `hub_permission_request`) through the ordinary tools-mode server with `AGENTHUB_PEER_ID=<id>` and a per-child `AGENTHUB_LAUNCH_ID`. The daemon accepts a `tools` hello for this id only with the current child's launch id; it is revoked when the child's group is confirmed gone. `hub_inbox` may pull only a batch whose host-resolved task attribution matches the current child, using the same unique-task rule for unscoped chat as delivery. A mixed-task batch is refused before any queue removal, so other tasks reach their own child/session.
4481
4484
  - **Permissions.**
4482
4485
  - `ask`: the hook decides nothing; every write, edit and shell call reaches the prompt tool.
4483
- - Both modes run Claude in its `default` permission mode, never `acceptEdits`: `acceptEdits` auto-approves in-worktree edits before the hook's path gate or the prompt tool is consulted, so protected paths (`.agenthub`, agent configuration, unsafe aliases) could be written without a person. Every automatic grant comes from the hook's explicit `allow`; other writes and shell calls require the person's answer through the prompt tool.
4484
- - `ask-when-needed`: the hook answers `allow` only for `projectFileTool` targets with `projectRoot` = the execution root (worktree), so `.git`, `.agenthub` and agent configuration stay protected (`src/cli/facts-hook.ts:20-54`, `:91-96`). Shell, git writes and every path outside the worktree reach the prompt tool. This is the Pi/local ask-when-needed grant ("bash and mutating git retain console approval", operator permission modes amendment), without its sandbox, so `never-ask` is refused (R1).
4486
+ - Both modes run Claude in its `default` permission mode, never `acceptEdits`: `acceptEdits` auto-approves in-worktree edits before the hook's path gate or the prompt tool is consulted, so protected paths (`.agenthub`, agent configuration, unsafe aliases) could be written without a person. File grants come from the hook's explicit `allow`; automatic shell grants use only the host wrapper described below (#845).
4487
+ - `ask-when-needed`: the hook answers `allow` only for `projectFileTool` targets with `projectRoot` = the execution root (worktree), so `.git`, `.agenthub` and agent configuration stay protected (`src/cli/facts-hook.ts:20-54`, `:91-96`). Bash reaches the prompt tool; a complete, public command may get the #845 wrapper grant below. Unproven calls and every enabled action-policy class still ask. `never-ask` remains refused (R1).
4485
4488
  - The daemon puts `permission` in a `facts` reply for this instance's current child launch id, as it does for the recorded `ahub claude` launch (daemon.md facts-hook rule extended to this one case).
4486
4489
  - **Person approvals.** `hub_permission_request` (tools-mode server, callable only by this variant's current child) puts the request on the existing approval path: console `permission` push, macOS notification (#5), `approvals.timeout_s`. Payload is the tool name plus a public rendering of the arguments made by a bounded generic sanitizer, not `Tasks.publicView` (that takes a `Task`): JSON-stringify, `sanitize()`, the PII screen used for public metadata text (a match replaces the value with `[withheld]`), path values through the same screen, then a cut at a character boundary with a `[cut, N chars]` marker inside the 600-character title bound (#72 rule). It never includes task text. The original arguments stay in the child's pending request and are what Claude's allow response refers to; they are not logged or shown. Options are allow-once and deny; no allow-always. While a request waits, the watchdog is kept alive (as ACP, #5).
4487
4490
  - **No answer.** At `approvals.timeout_s` the tool returns deny with "no person answered"; Claude continues the turn with the denial; the delivery is not failed by the timeout itself; a console and log line is written.
@@ -4493,6 +4496,16 @@ Declaration: `"claude-impl": { "kind": "claude", "roles": ["implementer"], "star
4493
4496
  - **Done.** `hub_task_done` from the child completes the task through `Tasks` with the ordinary owner check (`by === task.owner`), then the reviewer is notified (I-2 excludes this instance from reviewing it).
4494
4497
  - **Recovery.** `session.freshOnRestore` is false; crash resume reuses the per-task map, and each task's session is checked by transcript as above. No terminal row. Controlled recovery requires target feature `role_stages` (below).
4495
4498
 
4499
+ ### Headless implementer host shell execution (#845)
4500
+
4501
+ The 2026-10-26 amendment permits automatic Bash execution only in `ask-when-needed`, for a current non-private task owned by this child in its verified registered worktree. `ask`, PII handling, native `default` mode and enabled action-policy classes retain person approval. A client must explicitly support wrapper protocol 1; an older MCP client cannot receive an automatic allow that would run the original native command.
4502
+
4503
+ - The existing #816 `proveReadOnlyShell` supplies read-only authorization, including its parser, executable and path/config proof; there is no second read-only policy. Task commands use the same simple-shell parser and a closed argv grammar: `scripts/check.sh --changed`, `bun run build`, `bun test` with 1 to 16 explicit `test/**/*.test.ts` paths, `git add` with named paths, `git commit -m <message>`, `git push origin <branch>` or `git push -u origin <branch>`, and branch-selected `gh pr view`, `checks` or `create`. Creation requires the default base, bound head, title and exactly one body or body-file. Operators, expansions, extra flags, another remote/branch, background execution and model cwd fields ask.
4504
+ - The host captures the canonical repository default branch once at daemon startup, before task execution, from a bounded remote `ls-remote --symref` probe against the captured GitHub repository URL with private HOME/cwd and isolated Git configuration. Each request reads attached HEAD against that captured name; a later mutable local origin/HEAD never changes this authority. No credentials are supplied to this probe. Missing/private-remote startup evidence asks until a restart with available remote evidence. A recorded task branch must match. Otherwise the first eligible task-command request may bind a non-default branch, excluding `main`, `master`, detached/unknown HEAD and another open task's branch. `Tasks.bindShellBranch` records `refs.branch` plus a host history entry containing the branch and a hash of the canonical worktree. Restart retains the binding; a later branch or root mismatch cannot rebind it. Path guards and instruction/data policy still apply. Git effects use the core hardened argv/environment; origin fetch and every push URL must match the project's captured GitHub repository for push/PR grants, and `gh` gets an explicit host repository selector. Push executes the explicit same-branch refspec `refs/heads/<bound>:refs/heads/<bound>`; configured remote.origin.push or mirror refuses. A body-file is bounded, public-text screened and captured into the executed `--body` bytes, so the privileged CLI never reopens a mutable file.
4505
+ - An automatic allow returns fresh `updatedInput` containing a fixed absolute hub wrapper invocation and a random single-use ticket. Native shell cwd, PATH and environment select neither the command nor its authority. The grant record is held in bounded, expiring host memory; its opaque nonce is visible to the requesting MCP call and wrapper argv. A model-selected permission call can therefore receive a ticket and record a branch binding after the same host checks; ticket possession is not native-dispatch provenance. The record binds task, peer, current child generation, canonical root, branch, exact parsed/effective argv, executable, environment and proof recheck. It is consumed before validation or execution; restart revokes all tickets. Unknown, expired, reused or mismatched tickets run nothing and return the fixed refusal `host shell execution refused`.
4506
+ - The private Unix-socket execution service rechecks current child/task/root/branch and proof immediately before spawning the resolved absolute executable at the proven cwd. Read-only grants and check/test/build task grants retain the local-worker Seatbelt profile and own-temp rules. An additive deny-only profile forbids every write to project Git common/current metadata, including loose/packed objects, HEAD/index, refs/logs, other worktrees and shared/private submodule config/hooks/config.worktree. Directory entries cannot be renamed or replaced. Unknown layouts ask. Closed host Git add/commit run outside that profile, like push/gh: only parsed bounded paths/messages, with root/task/branch proof rechecked. Configured filter and external diff/textconv drivers refuse add/commit only; push/gh do not run clean/smudge and remain eligible with global filter.lfs. The chosen policy is person approval for add/commit, not neutralizing filters. Config includes and partial clones still refuse privileged Git/gh; fixed argv disables signing, automatic maintenance, hooks and fsmonitor. Credential denies remain for code, and canonical live hub state is denied even with a primary-checkout read allowance. Network follows local.bash_network: normal enabled mode uses the configured proxy, while the explicit "direct" setting gives gate code direct network access. Without macOS sandbox-exec these commands ask. Only closed host git-add/git-commit/git-push and gh operations remain outside that profile. Its environment is the #816 host baseline (`PATH`, `HOME`, `LANG`, `TERM`), fixed proxy and peer identity, plus an owned command TMPDIR; Project/worktree PATH entries and aliases are removed before proof and execution. Git receives only the core's fixed hardening variables; gh receives that same core configuration through GIT_CONFIG_COUNT/KEY/VALUE for its Git children. No inherited native environment or authentication value is copied into `updatedInput`. Output streams and numeric exit status pass through. Child abort, disconnect and shutdown terminate only the command's owned process group. A target that deliberately starts a new session with setsid can outlive that cleanup; such a descendant keeps its Seatbelt restrictions and may keep writing the permitted worktree paths. This is not an all-descendants termination guarantee.
4507
+ - The dated model-free native receipt must cover modified input, an earlier persistent-shell `cd`, spaces/apostrophes, poisoned native `RIPGREP_CONFIG_PATH` and `GIT_DIR`, and ticket replay refusal. Native transport evidence, host fixture tests, package gate and real-account behavior are distinct evidence classes. The executable task commands use the host profile above; this does not sandbox the entire native Claude process.
4508
+
4496
4509
  ### R5b. Terminal Claude implementer
4497
4510
 
4498
4511
  Declaration: `"claude-impl-t": { "kind": "claude", "roles": ["implementer"], "worktree": ".worktrees/claude-impl-t" }`.
@@ -4962,8 +4975,9 @@ Native CLIs update often: Codex 0.146.0 → 0.163.0, and Kimi 2.2.0 → 2.3.0 by
4962
4975
  - **Named reasons.** A version refusal starts with `VERSION_REFUSAL` and names only versions and the trust command. The daemon passes it through `sanitize()` for every reviewer backend, while other Codex prepare failures stay generic. The Jcode refusal is named before the owned daemon starts.
4963
4976
  - **Seeing drift.**
4964
4977
  - `ahub agents` (allowed for agents) and `ahub doctor` show each verdict.
4965
- - A weekly workflow compares the npm-published versions of the npm-distributed kinds with `verified` and keeps one `agent-compat` issue current.
4966
- - Jcode and agy have no public registry and are not watched; their launcher verdict is the signal.
4978
+ - A daily workflow compares the latest published version of every public-source kind with `verified` and keeps one `agent-compat` issue current (#843 widened it from weekly to daily and from npm-only to every public source).
4979
+ - Sources: the npm-distributed kinds read npm `latest`; Jcode reads its GitHub releases' latest tag (leading `v` stripped, #843). agy has no public source and is reported as an informational note, never drift; pi has an entry since #847 and is watched like the rest.
4980
+ - Local drift (#843 AC3): at hub start and daily the daemon reads each installed CLI's version with the `ahub agents` probe and tells the effective planner once per (id, version) when it is not `verified`: id, version, state and the last receipt path only, deduplicated per planner in `hub.db` across restarts, so a planner that could not be queued is retried alone. A missing executable or an unreadable version is silent. `ahub status` shows one attention line per drifted id.
4967
4981
  - **Moving a verified set.** A receipt in `docs/verification/` adds the version to `verified` in the same PR.
4968
4982
 
4969
4983
  ## Amendment: lifecycle identity linked by the hub (#694)
@@ -5153,3 +5167,10 @@ Ceiling: a scan lists the board once per status revision (at most one per flush)
5153
5167
  - Default CodeFacts review/completion queries have priority over optional neighbours. Interrupt an active optional CLI, await all existing owned cleanup and then admit priority work; do not run native tree preparations concurrently. Optional unknowns have a bounded five-second TTL and never poison priority cache lookups.
5154
5168
  - Dashboard code work requires the Memory view to be open and visible. Derive starts share a five-second floor; known topology lives at most thirty seconds, with current note rows refreshed before publication. A single hardened native HEAD probe establishes a bounded HEAD/ref proof; cache checks do not launch Git.
5155
5169
  - Console task counts use cache-only optional context and return unknown on a miss within the existing task-detail deadline. Snapshot the native brief cap before awaits. Preserve stored nodes when adding derived edges and apply tier/kind/peer filters to neighbour notes; a non-symbol selection resets the toggle.
5170
+
5171
+ ## Amendment: the planner reads any task and cancels its untouched declined task (#812 AC5)
5172
+
5173
+ - **Supersedes** the #208 sentence that refuses every peer but the conductor, a task's owner and its reviewer, and the #438 sentence that limits a planner's cancellation to work with no historical acceptance.
5174
+ - `hub_task_show` also admits the configured planner (a peer holding the `planner` role, `Tasks.isConfiguredPlanner`, `ConductorHooks.isPlanner`) for any task outside the backlog (a staged backlog row stays hidden from agent task tools, #263, and is read through the planner tools; a peer that is also the conductor takes the audited conductor path). It gets exactly the conductor's view (`publicConductorTask` over `Tasks.publicView(task, true)`), so a PII task is still a stub with no history. The read writes no conduct audit event and grants nothing else: the planner cannot assign, escalate, hold or start through the conductor tools, and every other peer is refused with the conductor-role error as before.
5175
+ - `hub_task_cancel` by the planner keeps every check it had (its own proposal, `backlog` or `proposed` state, planner authority, no private turn on ordinary work, the flow revision, recovery and shutdown exclusion) and replaces only the acceptance test with the evidence test `hub_backlog_return_untouched` uses (`taskHasWorkEvidence`): a branch, commit, PR, recorded local check, done, verdict or reopen refuses with the unchanged text `planner may cancel only its own historically unaccepted backlog or proposed work`. An accepted-then-declined task with none of them (the observed case, #454) is cancellable; the history keeps the acceptance and the decline.
5176
+ - Reason: the planner could not read a task it had proposed once an owner took it, and could not clear the board of a task its owner declined untouched. The person's cancellation is unchanged.
@@ -0,0 +1,76 @@
1
+ # 0.18.0 release verification
2
+
3
+ This record covers release preparation and source review. It claims no installation, no running-hub upgrade to 0.18.0 and no live TUI launch.
4
+
5
+ ## Candidate
6
+
7
+ Version 0.18.0, built from merged main f0e9589a (26 commits since v0.17.0, 19 of them `feat`). The release commit carries the version, the CHANGELOG section, the bundle (plugin 0.18.0, built with the pinned Bun 1.4.2 and the worktree's own frozen `node_modules`) and the document corrections, and every document stamp names it. Control protocol stays 16. `@staix/agent-mem` stays exact-pinned at 0.8.0. The task asked for a release once the in-progress work finished; the release PR is merged with a merge commit by the conductor, then tagged `v0.18.0`.
8
+
9
+ What changed (see `CHANGELOG.md` for the full list):
10
+ - Readable CLI output by default for the `sdlc` read commands, the task, backlog and release commands, and restart, upgrade, recovery, models, `queue resolve` and `budget execution` (#814: #831, #833, #834), with `--json` byte-identical and two guard scans (#837).
11
+ - Guarded merge, merge records and post-merge cleanup guards, and one GitHub COMMENT review per hub verdict (#786: #815, #823, #825).
12
+ - Exact-head CI evidence and current planner rulings in the review request (#824, #826; #835, #836); `review.same_family = "never"` (#820, #828).
13
+ - Agent shell authority: read-only `ahub` forms for every agent and a closed operational family for the conductor (#812: #818, #827, #832); planner task reads and untouched cancel (#822); proven read-only shell grants for Pi and the local worker (#816, #829).
14
+ - Planner WIP split (#784, #819), the isolated remote Docker check runner (#793: #806, #807), dirty-submodule turn facts (#798, #808), and dogfood profile metrics (#799: #810, #817, #830).
15
+
16
+ ## Reviews
17
+
18
+ Every PR below merged after hub reviews and bot review threads were answered, with exact-head CI green at merge (the conductor read it). The review counts are the GitHub COMMENT reviews each PR carries.
19
+
20
+ | PR | Merge | GitHub reviews |
21
+ | --- | --- | --- |
22
+ | #806 (#793) | 170199d6 | 4 |
23
+ | #807 (#793) | fd94621a | 2 |
24
+ | #808 (#798) | 79b33c0d | 2 |
25
+ | #810 (#799) | 860b0c66 | 4 |
26
+ | #811 | edcc059d | 2 |
27
+ | #813 (#525) | 577668de | 2 |
28
+ | #815 (#786) | f236aa59 | 4 |
29
+ | #817 (#799) | b23ad7b7 | 2 |
30
+ | #818 (#812) | 61e2fd3f | 4 |
31
+ | #819 (#784) | b454e527 | 2 |
32
+ | #821 | 7d539061 | 2 |
33
+ | #822 | 97473e85 | 4 |
34
+ | #823 (#786) | be35c0c8 | 2 |
35
+ | #825 (#786) | 10a784f9 | 4 |
36
+ | #826 (#824) | 85559fc8 | 2 |
37
+ | #827 (#812) | 2e77055f | 5 |
38
+ | #828 (#820) | abb3aa93 | 5 |
39
+ | #829 (#816) | 0ee33764 | 4 |
40
+ | #830 (#799) | 3d9a42b3 | 2 |
41
+ | #831 (#814) | 2e56ed37 | 4 |
42
+ | #832 (#812) | 7ae412f9 | 2 |
43
+ | #833 (#814) | e95e052a | 2 |
44
+ | #834 (#814) | 486d47a6 | 5 |
45
+ | #836 (#835) | 5f2fd0c6 | 4 |
46
+ | #837 (#814) | f0e9589a | 2 |
47
+
48
+ ## Document review
49
+
50
+ Fourteen documents were stale against the 0.17.0 stamps (66db7605), 28 commits earlier. Each was rechecked against `git log 66db7605..HEAD` over its paths, the merged PRs above and the source itself, by this implementer. Most features were already documented by the PR that introduced them (operations.md carries 12 of those commits, tasks.md 7, daemon.md 3); the recheck looked for what no PR had updated.
51
+
52
+ | Document | Result | Checked claims and correction |
53
+ | --- | --- | --- |
54
+ | `src/cli/help.ts` | corrected | Every usage string in `main.ts`, `launch.ts`, `identity.ts` and `upgrade*.ts` was cross-checked against the help rows for its command and flags; the top-level command words of help and of the `commands` table match both ways (`pause\|resume` documented as one row). Corrections: `--full` added to `projects`, `queue list` and `task show` (accepted by their usage and by `operations.md`); the authority wording now matches `classifyPeerCommand` and the operations tables (`sdlc status\|explain\|queue\|release`, `queue list` and `release show` open to any agent as public views; `task cancel` and `export` also the conductor's; `task show` gives an agent the public view). `test/help.test.ts` and `test/identity.test.ts` pass unchanged. |
55
+ | `docs/security.md` | corrected | The conductor paragraph said a conductor cannot resolve deliveries or stop a peer; #818 and #827 grant both through the closed operational family, each admitted by `admitOperational` (role recheck, closed audit label), so the paragraph now lists the family, its bounds and what stays with the person, and the read-only forms any agent may run. The agent-mem pin sentence said 0.7.0; it is 0.8.0. |
56
+ | `docs/operations.md` | corrected | The header said 0.13.0; it describes 0.18.0 (protocol 16). The upgrade compatibility sentence and row said protocol 16 ended at 0.12.43; every tag from 0.12.20 through 0.17.0 carries `PROTOCOL = 16` (checked with `git show <tag>:src/hub/control-client.ts`), so both now read 0.18.0. The #812, #816, #820, #784, #793, #786 and #799 sections were already current. |
57
+ | `docs/quickstart.md` | corrected | Install pin `v0.18.0`; "Upgrading to the next release" is now "Upgrading to 0.18.0" and gains the agent and conductor authority, `auto_merge` (off by default), `review.same_family`, the Pi and local read-only shell grants, the opt-in remote runner and the 0.8.0 pin. |
58
+ | `README.md` | corrected | Status 0.18.0, control protocol 16, and the verification link points to this report. Feature bullets unaffected. |
59
+ | `docs/agent-notes/adapters.md` | corrected | New rules for the proven read-only shell grant and its synchronous recheck (#816) and for agent shell authority tables (#812). Scope gains `src/hub/read-only-shell.ts`, `read-only-shell-proof.ts`, `operational-authority.ts` (AGENTS.md and the manifest together). |
60
+ | `docs/agent-notes/local-worker.md` | corrected | The `bash` tool's permit, host recheck and one `forceAsk` retry (#816), and the turn-generation binding. |
61
+ | `docs/agent-notes/benchmarks.md` | corrected | The one covered file that changed, `src/local/tools.ts` (bash permit path), needs no benchmark driver change; stated. |
62
+ | `docs/agent-notes/tests.md` | corrected | The two CLI print scans and the per-command output tests (#814). Scope gains `test/cli-json-guard.test.ts`. |
63
+ | `docs/agent-notes/tasks.md` | no change | Already records the planner WIP breakdown, hub-verified CI block, verdict mirror, guarded merge and `review.same_family`. Scope gains `src/hub/sdlc-merge.ts` and `sdlc-mirror.ts`, which it already names. |
64
+ | `docs/agent-notes/daemon.md` | no change | Already records the CLI output default, `upgrade-output.ts` and the generic print guard (merged with #833 and #834). |
65
+ | `docs/agent-notes/bus.md` | no change | `daemon.ts` changed in operational admission, review and tool handling; the bus, digest and reply paths this note covers were not touched (no `bus.ts` change since the stamp). |
66
+ | `docs/agent-notes/budget.md` | no change | Same: no `budget.ts`, `usage-rollup.ts` or pause-persistence change since the stamp. |
67
+ | `docs/agent-notes/README.md` | corrected | Background for the CLI print guards and agent shell authority; `console-state.ts` changed only to show the planner WIP split in the Peers detail. |
68
+
69
+ Also corrected, outside the stamped set: `docs/memory.md` named agent-mem pin 0.7.0 and now says 0.8.0.
70
+
71
+ ## Known gaps, not blocking
72
+
73
+ - The recheck was by one implementer reading source and PRs, not three independent reviewers; the reviewer of this PR is the independent check on it.
74
+ - No running hub has been upgraded to 0.18.0 here. `auto_merge`, the verdict mirror and the conductor operational family are source and fixture tested; their live receipts follow the upgrade (see `docs/dogfood.md` rows #786, #812, #835).
75
+ - `docs/operations.md` has hand-written statements about older releases (for example "removed in 0.13.0") that were not touched.
76
+ - Flaky tests seen on main: `test/memory-code-head-684.test.ts` "#367 cached HEAD proof" (hub fail note d13a75e3). If the full gate hits it, the run is repeated and both results are reported in the task.
@@ -0,0 +1,35 @@
1
+ # Live codex-review verdict receipt (0.17.0)
2
+
3
+ Date: 2026-10-25, macOS arm64. Refs #783 AC3. Result: the production hub's `codex-review` instance reviewed one real pull request and recorded an approved verdict through `hub_review`. This is the first verdict recorded by `codex-review` on a running hub since the no-verdict failure of #783; the [model-backed fixture receipt](2026-10-25-codex-0164-reviewer-tool-call.md) covered the same path in a temporary fixture only.
4
+
5
+ ## Setup
6
+
7
+ | Item | Value |
8
+ |---|---|
9
+ | Hub | 0.17.0 release runtime, the project's own daemon (not a fixture) |
10
+ | Native reviewer | `codex-cli 0.164.0` (`ahub agents`: verified) |
11
+ | Declaration | `codex-review`, kind `codex`, roles `["reviewer"]`, `start_mode: "headless"`, `review_mode: "fresh"` (untracked `config.local.json`) |
12
+ | Enabled with | `ahub --live-test roster add codex-review`, then `ahub --live-test codex --as codex-review` (user-authorized live test) |
13
+ | Status after start | `codex-review idle attached`, no `reviewer isolation unavailable` line |
14
+
15
+ ## The review
16
+
17
+ | Item | Value |
18
+ |---|---|
19
+ | Hub task / PR | #476 / #809 (docs only, `docs/operations.md`, +9 lines) |
20
+ | Owner / reviewer | `claude` / `codex-review` (assigned by routing) |
21
+ | Reviewed head | `ce16b44be52161a950b66f9220e3cb565ae4781f` |
22
+ | Review request sent | 09:12:17 UTC |
23
+ | Reviewer busy | 09:12:17 UTC |
24
+ | Verdict recorded | 09:13:03 UTC, `approved` (46 s after the request) |
25
+ | Reviewer idle | 09:13:08 UTC |
26
+ | Board state after | `approved`, reviewer `codex-review` |
27
+
28
+ The verdict note cited the two code locations the doc change describes (`validateInstances`, `resolveInstanceExecutionCwd`), reported a read-only `check-docs` run and `git diff --check`, and listed one unmet item: it could not run `scripts/check.sh --changed` because the read-only sandbox refuses the gate's lock, install and build writes. The hub logged `premature review: exact-head full CI is not green` at the request; the review ran anyway, and the merge waited for CI separately.
29
+
30
+ ## Limits
31
+
32
+ - One review of one small docs-only change. A code review with findings and a `changes_requested` verdict were not exercised.
33
+ - Token use for this single review is unknown: `ahub usage` reports a cumulative counted total for `codex-review`, not per review.
34
+ - The reviewer cannot run the repository gate; its approvals rely on CI and the owner's reported gate for that.
35
+ - macOS only; native Linux behavior is not covered.
@@ -0,0 +1,56 @@
1
+ # Remote Docker extraction and cleanup receipt
2
+
3
+ Date: 2026-10-25. Refs #793, transport slice AC1/AC2/AC4/AC5. This is a local Docker transport-component proof, not an SSH or pi tool-call receipt. No machine opt-in, dependency preparation, account or model call occurred.
4
+
5
+ ## Versions and proof boundary
6
+
7
+ - Docker daemon 29.5.2, Linux arm64, through an existing Colima VM.
8
+ - Preinstalled image: oven/bun@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895. The fixture asserted the actual container Bun version was 1.4.2. No image pull occurred.
9
+ - Host Bun 1.4.3; macOS bsdtar 3.5.3, libarchive 3.7.4.
10
+ - Tested src/hub/check-runner.ts SHA-256: 58f6a9f2a86acbf4fb9165ca2315c8df2aaca27b871e6ed3eaef79a28d94f3ad.
11
+ - Private driver SHA-256: 07000fd8aadf00625255784f5ac7b27f6ec6f56166c1e6118031bf963545f091.
12
+
13
+ The existing VM exposes only two CPUs. Two initial attempts with the unchanged production four-CPU argument failed before transfer, and both cleaned their owned resources. The final component driver therefore lowered only the CPU cap from four to two. This is a stricter native-test limit; the product still requests four CPUs. The VM was not reconfigured or restarted. The exact four-CPU launch on the eventual remote host remains unverified.
14
+
15
+ The driver used the production remoteRunnerTransport with ContainerRunner's shared lifecycle and a direct Docker command port. It supplied no always-true dependency-ready predicate. A separate actual RemoteRunner.available() call returned remote dependencies unprepared, so this proof grants no sandboxed-peer tests capability.
16
+
17
+ ## Transfer and negative controls
18
+
19
+ A private synthetic Git fixture contained a nested canary, a lockfile and a bare bun:test file. The working-tree canary was changed after commit. The component returned the committed content, not the dirty working-tree version:
20
+
21
+ - Source commit: abac005b3a543eb3f7d10c74222009e027055776.
22
+ - Tree: 7cb96483df51495ff14e3ae2ad58b1f1398e2385.
23
+ - Lockfile SHA-256: 87b7aa7d4cc3d62bbabeb5971dac92dcf6f0a52815d11afb027fbed4b769c946.
24
+
25
+ Before extraction, the driver attached a benign macOS extended attribute to the staged canary. A baseline host archive retained that attribute, and the former docker cp - ...:/work path failed against the same running, read-only-root/tmpfs container. The production normalized archive omitted the custom attribute, macOS provenance metadata and host username. Its headers used uid/gid 65532 and static check owner/group names.
26
+
27
+ The unchanged production copy function streamed that archive through non-root docker exec -i ... tar --no-same-owner -xf - -C /work. Readback asserted exact committed content and source ownership uid 65532. The real Bun test exited zero; its process reported uid 65532 and six threads under the 64-pid limit. This is one small workload, not a capacity proof for every permitted check.
28
+
29
+ Docker documents the tmpfs copy limitation and recommends an in-container tar stream for this boundary: [Docker cp corner cases](https://docs.docker.com/reference/cli/docker/container/cp/#corner-cases).
30
+
31
+ ## Readiness, labels and teardown
32
+
33
+ The production readiness probe started a detached, project-labeled container, transferred its own tiny source using that same copy function, then checked non-root content readback and network isolation. The driver also inspected its run container and confirmed:
34
+
35
+ - Read-only root; network none; uid/gid 65532:65532.
36
+ - Capabilities dropped with ALL; no-new-privileges.
37
+ - 64 pids, 4 GiB memory and the disclosed two-CPU native-test cap.
38
+ - Source-only transfer; no host path, home, socket or credential mount.
39
+
40
+ The image and an added fixture label contained commas. The production inventory still found the owned container using project/name filters and individual JSON-encoded .Label fields. It never parsed the comma-joined .Labels display.
41
+
42
+ The shared lifecycle persisted a finished remote-shaped receipt, exit zero and linux/arm64 (remote docker 29.5.2). That is the library's platform string; this run used local Docker and no SSH connection. Production cleanup confirmed container absence and released the fixture repository heavy lease. Final project-scoped inventory was empty. The driver removed its own placeholder volume and fixture source, then released its separate native-verification reservation. No other project containers were stopped.
43
+
44
+ ## Remaining work
45
+
46
+ The production runner remains unavailable until the separate admitted, lockfile/Bun-keyed dependency preparation exists (AC3). A real remote SSH leg, the unchanged four-CPU launch there, person opt-in and pi's live hub_check_start receipt (AC6) remain separate work. The fake transport/config regressions and final packaging gate are recorded with the PR's exact head.
47
+
48
+ ## Dependency volume (793b)
49
+
50
+ Date: 2026-10-26. Same local Docker daemon (29.5.2, Colima, two CPUs), same preinstalled pinned image, no pull, and the same disclosed change: the driver lowered only the CPU cap to two. The driver used the production `remoteRunnerTransport` and `ContainerRunner` lifecycle with a direct Docker command port, on a synthetic repository whose only dependency was `is-number@7.0.0` and whose test asserted both that the dependency imports and that a write into `/work/node_modules` fails.
51
+
52
+ - First run: one `volume create`, one networked container, one `bun install --frozen-lockfile`, then the check exited zero with both tests passing.
53
+ - Second run, same commit: no volume create, no install, no container with a network; the check exited zero against the same volume.
54
+ - Afterwards no labelled container remained and exactly one labelled volume existed; the driver removed it.
55
+
56
+ This does not verify the remote SSH leg, the four-CPU launch, a person opt-in or pi's live `hub_check_start` receipt (AC6).
@@ -0,0 +1,69 @@
1
+ # Antigravity 1.4.7 T0 re-run (#843 AC5)
2
+
3
+ Date: 2026-10-26. Refs #843. The account-free legs behind the 1.4.2 verification ([P1 T0 receipt](2026-10-19-agy-p1-t0.md)) were repeated on the installed `agy 1.4.7`, following the 1.4.3 re-run ([1.4.3 receipt](2026-10-21-agy-1.4.3-t0.md)). Every account-free leg passed, so 1.4.7 is added to agy `verified` in `src/adapters/compat.ts` in the same change. 1.4.3 stays an unverified, warned patch.
4
+
5
+ ## Environment and isolation
6
+
7
+ - Observed CLI version: `agy 1.4.7`, from `--version`, exit 0.
8
+ - Executable SHA-256: `b7a13e53c9f49921692676ef8edc2e60ceb67064c00bc732d9a364ec3ad75b32` (the person's `~/.local/bin/agy`, 192171376 bytes, modified 2026-10-24). It was hashed before the first run and after the last and did not change; agy self-updates, so this is the exact binary these results belong to.
9
+ - Bun 1.4.3 ran the probes, the plugin MCP server and the hook scripts. The 1.4.2 receipt used Bun 1.4.2; as in the 1.4.3 receipt this is a second variable that is not separated from the agy change. Host macOS 27.0.1 arm64.
10
+ - Every run used a new temporary HOME with its own `.gemini`, workspace and temporary directory, an explicit environment, the Seatbelt profile from `src/local/sandbox.ts` denying the person's home except the executable, and a loopback Gemini protocol fixture. No real account or cloud model call is part of this receipt. The scratch hub had memory, inference, snapshots and auto-start disabled and explicit agy roster admission.
11
+ - The per-user temporary directory (`getconf DARWIN_USER_TEMP_DIR`) was used as `TMPDIR`. A first actual-hub run with the agent's scratch directory (under the person's home, so inside the Seatbelt deny) loaded no plugin hooks and made no MCP call; it is the same setup mistake the 1.4.3 receipt records, it is not execution evidence and is not used below.
12
+ - SHA-256 of the person's Gemini CLI files (`antigravity-cli/settings.json`, `config/config.json`, `config/mcp_config.json` and the legacy `settings.json`) taken before the first run and after the last run are equal; `config/import_manifest.json` is absent on this machine. The probes' own before and after hash was equal on every completed run, no owned process was left running, the scratch hub stopped and every temporary root was removed.
13
+ - The probe scripts are copies of the 1.4.3 receipt's adapted scripts (`actual-hub-adapted.ts`, `native-hooks.ts`, `tui-actual.ts`), re-pointed at a clean worktree of main. The originals were not edited. The only changes are the output path, the Bun path, an `agySha256` field, a native-read tool name chosen from what the model-facing declarations list, and a normal-close step in the TUI copy (below). Raw bounded receipts and scripts remain in the local scratch workspace; they are not public artifacts.
14
+
15
+ ## CLI surface (fresh HOME, no session)
16
+
17
+ | Item | 1.4.7 observation | vs 1.4.3 |
18
+ | --- | --- | --- |
19
+ | `--version` | prints `1.4.7`, exit 0 | same (new number) |
20
+ | `--help` flags | `--add-dir`, `--agent`, `--continue`/`-c`, `--conversation`, `--dangerously-skip-permissions`, `--disable-slash-commands`, `--effort`, `--input-format`, `--json-schema`, `--log-file`, `--mode`, `--model`, `--new-project`, `--output-format`, `--print`/`-p`, `--print-timeout`, `--project`, `--prompt`, `--prompt-interactive`/`-i`, `--remote-control`, `--sandbox`; no `--new` | every flag the hub relies on or refuses is still defined; `--agent`, `--effort`, `--json-schema`, `--model`, `--project`, `--remote-control` and `--sandbox` are listed, none is passed or enabled by the launcher |
21
+ | `--acp` | exit 2, `flags provided but not defined: -acp`; no ACP subcommand | same. Refusal of the tested flag only |
22
+ | `--new` | exit 2, `flags provided but not defined: -new` | same |
23
+ | subcommands | `agent`, `agents`, `changelog`, `help`, `install`, `mcp`, `mic-serve`, `models`, `plugin`, `plugins`, `remote-control`, `update` | no ACP subcommand |
24
+ | `agy mcp list` | `No MCP servers configured.` while the workspace plugin is loaded | same |
25
+
26
+ ## Plugin MCP and hook contract (print mode, loopback fixture)
27
+
28
+ Actual scratch-hub run (`hub_inbox` through the shipped bundle and the workspace plugin), probe adapted exactly as for 1.4.3:
29
+
30
+ - Exit 0, empty stderr. `call_mcp_tool` was called once with `ServerName` `agent-hub_agent-hub`, `ToolName` `hub_inbox`; the random nonce held only in the scratch bus preface appeared in the following fixture-provider request (`nonceInModelRequest` true, 3 provider requests, 1 MCP call). Native output SHA-256 `751fc6590c1341b5fc1c195c051caa7556e51bf1c9e825257935a6cffaa6734b`.
31
+ - All six hooks ran from the plugin-local `.agents/plugins/agent-hub/hooks.json` with no project-level or global hook file: `SessionStart`, `PreInvocation`, `PreToolUse`, `PostToolUse`, `PostInvocation`, `Stop`.
32
+ - The `init` event still lists 60 tools and `permission_mode: request-review`.
33
+
34
+ Native hook contract run (two hub MCP calls and one native read, fixture MCP server):
35
+
36
+ | Item | 1.4.7 observation | vs 1.4.3 |
37
+ | --- | --- | --- |
38
+ | Plugin discovery | `.agents/plugins/agent-hub/` loaded without install or global write; MCP log `server/discover`, `initialize`, `notifications/initialized`, `tools/list`, `tools/call`, `tools/call` | same |
39
+ | Hook stdin shared fields | `SessionStart`: `artifactDirectoryPath`, `conversationId`, `lastUserInput`, `modelName`, `transcriptPath`, `workspacePaths`; `PreInvocation` and `PostInvocation` add `initialNumSteps` and `invocationNum` | same |
40
+ | Tool hook stdin | `toolCall: { name, args }`; `PostToolUse` has an empty `error` on success | same shape |
41
+ | Native read | `read_file` with `Path`, `StartLine`, `EndLine` | same as 1.4.3 (1.4.2: `view_file` with `AbsolutePath`). The hub's decoder already classifies both |
42
+ | `call_mcp_tool` arguments | `ServerName`, `ToolName`, `Arguments`, no `toolSummary` or `toolAction` | same as 1.4.3 |
43
+ | Model-facing declarations | 15 tools: `ask_question`, `call_mcp_tool`, `define_subagent`, `edit_file`, `invoke_subagent`, `list_resources`, `manage_subagents`, `manage_task`, `read_file`, `read_resource`, `run_command`, `schedule`, `search_web`, `send_message`, `write_file` | same set as 1.4.3 |
44
+ | `Stop` | `executionNum` 0, `terminationReason` `NO_TOOL_CALL`, `error` empty, `fullyIdle` true | same |
45
+ | `injectSteps` | `SessionStart` and `PreInvocation` `ephemeralMessage` accepted | same |
46
+ | Tool hook matcher | tool hooks ran with `matcher: ".*"` and a nested `hooks` array | same |
47
+
48
+ ## Interactive TUI and its normal close
49
+
50
+ The same PTY probe as the 1.4.3 receipt ran the interactive TUI under the Seatbelt profile and fixture. Fresh-HOME onboarding was answered with the same key presses (Enter, Down, Right, Enter, Enter).
51
+
52
+ - **Two prompts**: the plugin MCP server attached lazily on the first prompt, exactly as on 1.4.3. Provider requests carried 0, 12, 15 and 15 tools; `call_mcp_tool` was absent from the first agent request (12 tools) and present from the second (15). `hub_inbox` was called once, the nonce reached the fixture provider, and `PreToolUse` and `PostToolUse` fired for the MCP call. A single-prompt run (0 and 12 tools, no MCP call) shows the same first-turn gap. The hub's launch notice ("hub tools appear from the second turn") remains accurate.
53
+ - **Normal close**: after the last answer the probe typed `/exit` and Enter. The agy process exited on its own with code 0, with no signal sent (`signalFallbackNeeded` false), and printed its resume line. The scratch hub then stopped and the temporary root was removed; no probe process remained.
54
+ - The close here is agy's own exit and the probe's cleanup. It is not the hub launcher's `ahub agy` cleanup of the three managed plugin files and its socket detach; that launcher leg needs the person's daemon and account and stays below.
55
+
56
+ ## Hub-side check
57
+
58
+ `agyVersionGate` and the decoder were not changed. `decodeAgyHook` already classifies `read_file(Path, StartLine, EndLine)` as a read (covered by `test/agy-hooks.test.ts`), and the version gate reads `COMPAT.agy`. A new test pins `verified` and the verdicts: 1.4.7 verified, 1.4.3 and 1.4.8 inside the 1.4 line but unverified, 1.5.0 refused.
59
+
60
+ ## Not verified
61
+
62
+ - A real account, a real model turn and agy's own permission prompts. The fixture provider replays fixed tool calls, so this receipt proves the transport, plugin, hook and decoder contract, not how a real model uses the hub tools.
63
+ - The person's `ahub agy` launch against a running compatible daemon: managed three-file plugin write, pull-only attach, `hub_inbox` and `hub_send` through the real TUI, and the launcher's own close cleanup. That is the live leg in `docs/smoke.md` "Antigravity P1", pending until the person runs it.
64
+ - Permission prompt mappings, whether pre-tool `allow` overrides a native approval, `Stop` continuation and post-invocation termination overrides. The peer stays unmanaged.
65
+ - The IDE form and the Linux build.
66
+
67
+ ## Decision
68
+
69
+ All account-free legs passed on `agy 1.4.7` (SHA-256 above), so `1.4.7` joins `verified` for agy. The accepted line (1.4.0 up to, not including, 1.5.0) is unchanged. Another 1.4.x patch still launches with the unverified warning, and the person may trust an exact version with `ahub agents trust`.
@@ -0,0 +1,69 @@
1
+ # Claude Code 2.1.310 headless implementer permission-transport receipt
2
+
3
+ Date: 2026-10-26. Scope: #843 AC5, task #507. This repeats the model-free procedure of [the 2.1.309 receipt](2026-10-23-headless-claude-permissions.md) on the installed Claude Code 2.1.310, with 2.1.309 run through the same producer as a control.
4
+
5
+ | Native version | Binary SHA-256 |
6
+ | --- | --- |
7
+ | Claude Code 2.1.309 (control) | `5eadc0009cdcdbc0d06e58110d7b2baf88ced6088b1c33a60a21217fe410619c` |
8
+ | Claude Code 2.1.310 | `ffb64331bc0ee1b69efb7c2c428bd3ab516bc31b75bf60886b356c97c1c25ffd` |
9
+
10
+ The 2.1.309 hash is the one the 2026-10-23 receipt records, so the control is the same binary that receipt certified.
11
+
12
+ ## Upstream changes since 2.1.309
13
+
14
+ Read from the upstream [CHANGELOG](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md): 2.1.310 lists 26 items, and most are VSCode, cloud-session, Claude Tag and Code Review admin fixes that this hub's `claude -p` child never runs. The ones that touch permissions, MCP or hooks:
15
+
16
+ - **Monitor tool now available when telemetry or nonessential traffic is disabled** (matters). In this run the native init tool inventory grew from 28 tools on 2.1.309 to 29 on 2.1.310, the one difference being `Monitor`. The producer sets `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`; the hub's own child does not set it, and a repeat of the Monitor probe below without that variable on 2.1.310 reached `Monitor` and the host prompt the same way, so availability here does not depend on it. It is a deferred tool: it is not in the request's tool list and has to be loaded through `ToolSearch` first, which a model can do. A probe that did this on 2.1.310 reached the host permission prompt for `Monitor` with the same `tool_name`, `input` and `tool_use_id` shape as `Bash`, and a deny reply stopped it (no process started, tool result an error, one permission denial). 2.1.309 answered the same attempt with "No such tool available: Monitor". So a new native tool now reaches `hub_permission_request`, which then sees `tool_name: "Monitor"`. See "Monitor" below.
17
+ - **Windows PreToolUse and PermissionRequest hooks no longer let a call through when Git Bash is missing** (does not matter on macOS and Linux; the receipt's hook-failure legs are the equivalent check here and pass).
18
+ - **MCP tool call that times out in a non-interactive session while the server's elicitation is unanswered now tells the model no answer came** (touches MCP error text only). The transport legs below, MCP error, disconnect and pending, are unchanged in outcome.
19
+ - **File read permission prompts show where a symbolic link leads** (a prompt rendering change; the hub's prompt tool receives the same arguments).
20
+ - **Auto mode prompt text**: the hub never uses auto mode (`--permission-mode default`).
21
+ - **Compaction, memory, tmux and Linux sandbox warnings, gateway retry of the `effort` beta and mid-conversation system messages**: no permission, MCP or hook contract.
22
+
23
+ Nothing in 2.1.310 changes `--permission-prompts host`, `--permission-prompt-tool`, `--setting-sources`, `--strict-mcp-config` or hook semantics, and none of the 26 items says so. This reading is of the changelog text, not of the binary's source, which is not public.
24
+
25
+ ## Method
26
+
27
+ The producer [claude-permission-transport-native.py](claude-permission-transport-native.py) ran both installed native binaries with an isolated HOME and `CLAUDE_CONFIG_DIR`, a disposable work directory, a fixture stdio MCP server, an empty settings-source selection and fixture settings. A loopback HTTP server supplied a synthetic model-protocol response requesting one `Write` and, after the tool result, a completion. The launch used the hub's own flags: `-p --output-format stream-json --verbose --strict-mcp-config --mcp-config … --setting-sources '' --settings … --permission-mode default --permission-prompts host --permission-prompt-tool mcp__fixture__approve`. No `--bare`, `acceptEdits`, `dontAsk` or `bypassPermissions` was used. The API key in the child's environment was the literal string `synthetic-probe-not-a-credential`, pointing at the loopback server. No account, provider, login or model inference was involved, and each native child ran in its own process group, which was stopped and confirmed empty after every case. Each binary's SHA-256 was identical before and after.
28
+
29
+ The 2.1.309 driver the earlier receipt hashed (`173c108f…`) was a temporary local diagnostic that was not kept, and no copy was found. This producer is therefore new, written against that receipt's case table and reusing the loopback provider and process-group handling of the wrapper producer added by PR #857. Because it is new, it was first run on 2.1.309, where it had to reproduce the 2026-10-23 table before its 2.1.310 result meant anything. It did, on all nine cases. An early run of it failed `allow` and `hook-allow` on both binaries identically because the producer asserted `is_error` was `false`; native stream-json omits the key on success. The assertion was corrected to check `is_error is True` only for failures, and the producer was rerun in full.
30
+
31
+ ## Results
32
+
33
+ All nine cases passed on both binaries (18 of 18), with identical outcomes:
34
+
35
+ | Fixture case | 2.1.309 (control) | 2.1.310 |
36
+ | --- | --- | --- |
37
+ | Prompt tool allows the Write | 1 MCP permission call; file written | same |
38
+ | Prompt tool denies | no write; 1 permission denial | same |
39
+ | Prompt tool missing from MCP inventory | no write; 0 MCP calls; native exit 1 | same |
40
+ | PreToolUse command exits 1, prompt tool denies | hook decides nothing; 1 MCP call; no write | same |
41
+ | PreToolUse command exits 1, prompt tool allows | hook decides nothing; 1 MCP call; file written | same |
42
+ | MCP permission request returns a JSON-RPC error | no write; tool-result error | same |
43
+ | MCP server disconnects during permission request | no write; tool-result error | same |
44
+ | MCP permission reply remains pending | no write before the external 25 s owned-group stop; native internal timeout not established | same |
45
+ | Simulated approval deadline returns deny after 100 ms | 1 MCP call; no write; native exit 0; 1 permission denial | same |
46
+
47
+ Each call's permission arguments were exactly `tool_name: "Write"`, the original `input` object and `tool_use_id`. Native exit codes, MCP call counts and write/no-write outcomes matched the 2026-10-23 table on 2.1.309, and the 2.1.310 column matched the 2.1.309 column row for row.
48
+
49
+ ### Monitor
50
+
51
+ The 2.1.310 inventory difference is the only behavioural difference this run found, and it is a new tool name, not a changed contract. A separate one-off probe (not part of the producer, retained with the evidence) loaded `Monitor` through `ToolSearch` and called it with a write command, then with `echo`, on 2.1.310. The write command reached the host permission prompt once and a deny reply prevented it. The read-only `echo` made no permission call on either `Monitor` or `Bash`, which matches native auto-allow of read-only commands rather than a bypass. This probe covers only that one deny and one read-only path. It does not prove that every `Monitor` form (long-running sources, persistent watches) asks, that approval of one `Monitor` call authorises no later one, or how the hub's `hub_permission_request` renders and decides a `tool_name` other than `Bash`. The host-side rendering is generic (`permissionPayload` accepts any tool name), so nothing in the hub depends on the name being `Bash`, but this receipt does not claim a hub-side `Monitor` fixture. That is a follow-up if `Monitor` is to be allowed in a task child, and the hub's `--allowedTools` list names only the hub's own MCP tools.
52
+
53
+ ## Ceilings
54
+
55
+ The result certifies the permission transport on this binary and version, as the 2.1.309 receipt did for 2.1.309. It does not certify real provider authentication, a real model's tool selection, a person's approval interaction, every native tool type (`Monitor` is covered only as stated above), native internal permission timeout, or filesystem sandbox isolation. The pending case was bounded externally. The host shell wrapper procedure of #845 is a separate receipt added by PR #857, which ran on 2.1.309 and 2.1.310 and states that a version upgrade repeats both; this receipt is the transport half. The exact-head package gate, daemon integration and CI are recorded in the PR, and this document claims none of them. Native version admission and the per-start binary fence remain runtime checks; this dated receipt is evidence for the supported contract, not a per-start receipt gate.
56
+
57
+ The MCP wire still does not authenticate that a schema-valid permission call came from native dispatch, as the 2.1.309 receipt says; a person's fresh allow-once for the original arguments remains required for either path.
58
+
59
+ ## Evidence
60
+
61
+ Kept outside the repository in `scratchpad/temp/agent-hub-507-run4` (the final full run of the committed producer), with the earlier runs beside it: `agent-hub-507-run1` (the failed first producer), `run2` and `run3` (identical results from the producer before a review fix gave each binary its own indexed output directory, so two binaries with the same file name no longer collide):
62
+
63
+ | Item | SHA-256 |
64
+ | --- | --- |
65
+ | `claude-permission-transport-native.py` (the committed producer) | `b3f39acce1eb3713a5f01191702f1f217d704039d3a86452c6dfd311cb274efe` |
66
+ | `receipt.json` of the final run (18 cases, `certified: true`, `account: false`, `inference: false`) | `edefd6d7e18dd28a22ad63489c51db0e9636586dadedc686738b0ff89b5113e9` |
67
+ | Monitor one-off probe | `1ebd2ff6b4b8a587b212cde221d035d0a0d37e24d6acdef37dce55d7a71111d8` |
68
+
69
+ The run retains each case's native stdout and stderr, the MCP permission records, and the request tool schemas. The host used Python 3 and the installed binaries; no Bun is involved in this producer.
@@ -0,0 +1,62 @@
1
+ # Headless Claude host shell wrapper receipt
2
+
3
+ Date: 2026-10-26. Scope: #845 AC6-AC8. A fresh model-free native run passed on both binaries below.
4
+
5
+ | Native version | Binary SHA-256 |
6
+ | --- | --- |
7
+ | Claude Code 2.1.309 | `5eadc0009cdcdbc0d06e58110d7b2baf88ced6088b1c33a60a21217fe410619c` |
8
+ | Claude Code 2.1.310 | `ffb64331bc0ee1b69efb7c2c428bd3ab516bc31b75bf60886b356c97c1c25ffd` |
9
+
10
+ ## Method
11
+
12
+ The installed native binaries ran sequentially with isolated HOME and CLAUDE_CONFIG_DIR directories, empty settings sources, explicit fixture settings asking for Bash, a strict fixture MCP inventory and a deterministic loopback provider. The provider supplied Bash calls and a final completion response. No personal authentication files, real provider request, account or model inference was used. Each native case had an external 25-second deadline. Native leaders were reaped, remaining live group members were checked, and the host fixture shut down the production wrapper through its owned cleanup path. Binary hashes were unchanged after every case.
13
+
14
+ The producers are [claude-shell-wrapper-native.py](claude-shell-wrapper-native.py) and [claude-shell-wrapper-host.ts](claude-shell-wrapper-host.ts). The host fixture imports the actual `HostShellWrapper` and fixed CLI entry from production source. It supplies synthetic task, generation and branch bindings and a current-root predicate; it does not simulate a real task-board assignment or certify the daemon's authorization policy. Real daemon fixtures separately cover task/child/root/branch, mode, privacy and action-policy admission.
15
+
16
+ For each version, the control ran `cd sub && pwd` followed by `pwd`, returning the original input in both permission replies. Both outputs named `sub`, demonstrating persistent native shell cwd. The wrapper case first performed the same `cd`, then requested `pwd` twice. Both later permission replies returned fresh `updatedInput` objects containing the same issued fixed-wrapper command and single-use ticket. The selected host executable, outside the worktree, printed its identity, cwd and environment fields and incremented an effect counter. Root and executable paths included spaces and apostrophes.
17
+
18
+ Native HOME, PATH and cwd differed from the host execution context. Native `GIT_DIR` and `RIPGREP_CONFIG_PATH` were injected with poison paths. The actual target received the host's fixed PATH/HOME/LANG/TERM and peer identity, with its owned command TMPDIR. Helper startup uses fixed private HOME/cwd, config and dotenv suppression. The host supervisor receives exact target argv/env over private fd4 rather than shell interpolation or argv serialization, and preserves the target environment without adding shell variables.
19
+
20
+ ## Native observations
21
+
22
+ | Leg | 2.1.309 | 2.1.310 |
23
+ | --- | --- | --- |
24
+ | Persistent-shell control reports `sub` twice | pass | pass |
25
+ | Fresh rewritten input executes the fixed root after the earlier `cd` | pass | pass |
26
+ | Target identity equals the absolute host-selected executable | pass | pass |
27
+ | Injected native GIT_DIR and RIPGREP_CONFIG_PATH are absent | pass | pass |
28
+ | Target HOME/PATH/TERM and peer identity equal the host fixture values | pass | pass |
29
+ | Owned TMPDIR replaces native state | pass | pass |
30
+ | Same ticket's second execution returns the fixed refusal and native tool error | pass | pass |
31
+ | Effect counter remains exactly 1 after replay | pass | pass |
32
+ | Spaces and apostrophes in root/executable paths | pass | pass |
33
+ | Native exit 0, host stopped, no remaining live native group members | pass | pass |
34
+ | Binary unchanged across the case | pass | pass |
35
+
36
+ All four cases passed. Each control made three loopback requests and two MCP permission calls; each wrapper case made four requests and three permission calls. The verifier requires exact original native permission arguments and exact rewritten replies. Successful output comes from native stream-json `tool_use_result.stdout`, with the corresponding tool-result block equal to that stdout, empty stderr and no interruption. Replay must be a native error containing the fixed refusal, and the counter must still be 1. Provider-facing text with native system reminders is not treated as raw command output.
37
+
38
+ ## Reproduction and retained evidence
39
+
40
+ Run from the repository with explicit installed binary paths and a new private output directory:
41
+
42
+ ```sh
43
+ python3 docs/verification/claude-shell-wrapper-native.py NEW_OUTPUT --bun ABSOLUTE_BUN --claude INSTALLED_2.1.309 --claude INSTALLED_2.1.310
44
+ ```
45
+
46
+ The successful local diagnostic directory is `claude513-wrapper-native-20261026-r1`. It retains native stdout/stderr, exact MCP request/reply records, loopback request bodies, the host fixture diagnostics and `receipt.json`. No actual account credentials are in those fixtures. The framework used host Bun 1.4.3; separate Linux fixtures exercised the same wrapper on pinned Bun 1.4.2. Package and CI evidence remain separate.
47
+
48
+ | Artifact | SHA-256 |
49
+ | --- | --- |
50
+ | Native producer | `ecf818acd4b4d42db87416a83dc3f5d2dfbfff3a07c70915fabd25abd8ed2649` |
51
+ | Host fixture | `616e6711e392f0eee7e3fa1c7bdc47a537dfa86bcc8ffb30e549ca94e5a71cef` |
52
+ | Production wrapper service | `a573956e9c408546aa618afe6b776932fb0d5dd462c089e02993e2629adaf675` |
53
+ | Fixed CLI wrapper | `8ff56514a6e3f25bce0784775d396df199b58b1b93f55ad0e8747bbc729d2fd8` |
54
+ | Four-case receipt JSON | `3db74019a71be0b556d88e21b8273da93e1fdbeace200190e1a5c801a23b6ea8` |
55
+
56
+ The earlier direct-rewrite diagnostic and its initial provider-text parsing failure are preserved in this document's Git history and retained raw artifacts. This receipt uses a fresh run of the strict structured-output verifier, not a restamp of that earlier failed producer result.
57
+
58
+ ## Host fixtures and ceilings
59
+
60
+ Mac fixtures separately passed the existing #816 Seatbelt recursive-deny boundary, profile/read-only-data mutation refusal and actual environment parity against `sandboxedExec`. The sandbox source remains byte-identical to its accepted baseline. Current host policy requires the local-worker Seatbelt profile plus all-Git-metadata denial for read-only and check/test/build grants; unsupported hosts ask. Closed hardened host Git add/commit/push and gh remain outside it. This native transport fixture does not exercise those privileged Git mutators; their helper/config and authorization regressions are separate. Additional daemon fixtures cover a model-written test that cannot read the live control token or SSH key even when the canonical checkout is readable. These authorization fixtures are separate from this generic native wrapper fixture. Portable fixtures cover binary fd output, numeric status, expiry/replay/quotas, helper config/env isolation, current proof/root/argv/env mutation, cancellation, deadline, disconnection and descendant pipe cleanup. Task and daemon fixtures cover persistent branch binding, action-policy asks, unchanged ask behavior and old-client protocol refusal.
61
+
62
+ The [earlier permission transport receipt](2026-10-23-headless-claude-permissions.md) remains evidence for its original allow/deny/error/hook cases. A native version upgrade repeats both this wrapper procedure and the original permission-transport procedure before extending the verified set. This wrapper receipt adds no version to COMPAT and does not prove real provider authentication, model tool selection, task-session resume, a person's approval interaction or isolation of the entire native Claude process. The executable task-code profile is host policy verified separately by Darwin fixtures. It is not a runtime receipt gate. Native version admission and binary fencing remain required. Exact-head package gate and CI results are recorded separately in the PR handoff.