axstack 0.11.5 → 0.11.7
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.
package/package.json
CHANGED
|
@@ -75,7 +75,8 @@ Read `cursor.json` and `decisions/`. Work is due for:
|
|
|
75
75
|
- a `deferred[]` entry whose head still matches discovery;
|
|
76
76
|
- an expired repair cap;
|
|
77
77
|
- a dispatch marker older than 3 h;
|
|
78
|
-
- an unsettled Orca delivery in `pending_settlement[]
|
|
78
|
+
- an unsettled Orca delivery in `pending_settlement[]`;
|
|
79
|
+
- a `runtime_refusal` record, because its re-test needs a launched tick.
|
|
79
80
|
|
|
80
81
|
An `open` decision is not due. Write `pending.json` with the fingerprint,
|
|
81
82
|
`observed_at`, `seen[]`, full discovery list, and hashed subset. Append
|
|
@@ -123,6 +124,66 @@ The driver performs this order and exits:
|
|
|
123
124
|
|
|
124
125
|
## Dispatch and repair selection
|
|
125
126
|
|
|
127
|
+
Claude Code trusts a folder per git toplevel and stops at its "Quick safety
|
|
128
|
+
check" dialog otherwise; every per-PR child worktree is a new toplevel, and
|
|
129
|
+
the driver must never answer that dialog for a worker. The user decided on
|
|
130
|
+
2026-09-17 that a worktree the driver itself creates from an allowlisted
|
|
131
|
+
clone at the pinned head is trusted by policy: immediately after creating
|
|
132
|
+
it and before `worker-start`, the driver runs the trust helper
|
|
133
|
+
(`docs/plans/pr-automations-trust.js`, deployed beside the precheck and run
|
|
134
|
+
with Bun), which
|
|
135
|
+
owns the only write the automation makes to `~/.claude.json`. It writes the path's `hasTrustDialogAccepted` entry — what a
|
|
136
|
+
manual acceptance writes. The helper
|
|
137
|
+
enforces the scope mechanically before it writes anything: the path must be
|
|
138
|
+
a git worktree whose common dir is the named allowlisted clone's, must not
|
|
139
|
+
be the clone itself, and must sit at exactly the pinned head — anything
|
|
140
|
+
else exits 3 and is never seeded. It is one process on purpose: lock, refresher,
|
|
141
|
+
render and rename all happen in the same process, so nothing can outlive the
|
|
142
|
+
owner and commit after it dies. It writes under Claude Code's own config
|
|
143
|
+
lock — the `mkdir`-based `~/.claude.json.lock` directory its sessions take —
|
|
144
|
+
following Claude's own lease rules, with a bounded retry. Only a successful
|
|
145
|
+
`mkdir` counts as holding it; a fresh foreign lock is never broken, and the only lock it will
|
|
146
|
+
reclaim is one whose mtime is past Claude's 10 s stale threshold, which is
|
|
147
|
+
exactly what Claude itself treats as abandoned. It keeps the lease alive the
|
|
148
|
+
way Claude does: a refresher touches the lock's mtime every second for as
|
|
149
|
+
long as it is held, so the lock cannot age into staleness under it even if a
|
|
150
|
+
rename stalls. The refresher runs with the owner and dies with it, exits the
|
|
151
|
+
moment the lock is no longer ours, and treats a failed refresh as a
|
|
152
|
+
compromised lease by terminating the owner before it can commit. The helper
|
|
153
|
+
re-reads under the lock, refuses a store that does not parse, writes a
|
|
154
|
+
unique temp file, preserves the store's mode, re-verifies at commit that the
|
|
155
|
+
lease is healthy — unchanged inode, refresher alive, refreshed within the
|
|
156
|
+
last few seconds — and otherwise discards the temp and commits nothing,
|
|
157
|
+
treats a failed chmod or rename as failure with the temp removed, and
|
|
158
|
+
releases only a lock it still owns, stopping the refresher first. A busy or unreadable
|
|
159
|
+
store exits 2: the worktree is retained and nothing is dispatched. The
|
|
160
|
+
worktree cleanup removes the entry through the same helper; if that
|
|
161
|
+
removal fails after the worktree is gone, the marker stays in
|
|
162
|
+
`pending_settlement[]` as `untrust-pending` — which keeps the PR
|
|
163
|
+
undispatchable, so the reused path can never inherit a dead trust entry —
|
|
164
|
+
and the removal is retried next tick. A retained worktree keeps its entry
|
|
165
|
+
while retained; it is the same driver-created path. This is scoped
|
|
166
|
+
exactly there — never for any other path, never for a worktree it did not
|
|
167
|
+
create — because that dialog is the last guard between PR content and a
|
|
168
|
+
worker running with permissions bypassed. Trusting a folder activates the
|
|
169
|
+
full project surface: its `.claude/settings.json` and the hooks it defines,
|
|
170
|
+
its `.mcp.json` servers, marketplace plugin auto-install, and `CLAUDE.md`;
|
|
171
|
+
a hostile branch's hooks or MCP servers would run the moment the folder
|
|
172
|
+
opens. So the worker is launched in Claude Code's own isolation mode,
|
|
173
|
+
`--safe-mode`, through `terminal create` and `worker-start --terminal`,
|
|
174
|
+
since `worker-start` cannot pass argv. Safe mode is the binary's sanctioned
|
|
175
|
+
"all customizations disabled" path: no `CLAUDE.md`, skills, plugins, hooks,
|
|
176
|
+
MCP servers, custom commands or agents load from anywhere, project or user;
|
|
177
|
+
built-in tools and authentication are untouched, and the brief loads the
|
|
178
|
+
skill files it needs by path. The driver confirms readiness from the
|
|
179
|
+
rendered frame — `wait.satisfied`, the prompt marker present, the dialog
|
|
180
|
+
absent — before dispatching. What remains live is the repository's files as
|
|
181
|
+
data the worker reads and the commands the worker itself chooses to run,
|
|
182
|
+
which it already runs today; nothing from the branch loads, executes, or is
|
|
183
|
+
offered for invocation on its own. The allowlist,
|
|
184
|
+
the pinned head, and that reduced surface are what make pre-trust
|
|
185
|
+
acceptable, and nothing else does.
|
|
186
|
+
|
|
126
187
|
Every selected PR receives one dispatch marker with task id, dispatch id,
|
|
127
188
|
worktree, head, `started_at`, reservation (`verdict` or `repair`), and trigger:
|
|
128
189
|
`{kind: check, name, app_id}` or `{kind: review, review_id, digest}`. There is at
|
|
@@ -328,7 +389,8 @@ shared clone can fake durability, a failed fetch retaining the worktree — or h
|
|
|
328
389
|
abandon path, the worktree has no uncommitted changes.
|
|
329
390
|
Only then it closes any terminal tab still listed, clears untracked
|
|
330
391
|
artefacts, removes the child worktree and its directory, deletes the branch
|
|
331
|
-
the worktree created,
|
|
392
|
+
the worktree created, removes the Claude Code trust entry it seeded for that
|
|
393
|
+
path, and verifies the directory is gone. On the settled path
|
|
332
394
|
the worker has finished, so untracked files are artefacts by definition and
|
|
333
395
|
are cleared; a candidate there is already pushed or token-held. An abandoned
|
|
334
396
|
worktree that is dirty or holds an unproven candidate is **retained**, named
|
|
@@ -344,8 +406,11 @@ its dispatch without such a retention record is a health finding. The run direct
|
|
|
344
406
|
`repair_caps{url: {expires_at}}`, `abandon_count{head: n}`,
|
|
345
407
|
`processed_reviews[]` (`review_id`, `pr`, `head`, `digest`),
|
|
346
408
|
`deploy_on_push{repo: [branches]}`,
|
|
347
|
-
`legacy_automation_reviews[]`, `health[]
|
|
409
|
+
`legacy_automation_reviews[]`, `health[]`,
|
|
410
|
+
`runtime_refusal{code, first_seen, last_seen}` (absent when no runtime
|
|
411
|
+
hold is open);
|
|
348
412
|
- `pending.json`, `precheck.log` — driver precheck only;
|
|
413
|
+
- `trust.js` — the trust transaction helper, run by the driver only;
|
|
349
414
|
- `decisions/<token>.json` — writers assigned by the lifecycle table;
|
|
350
415
|
- `watchdog.log` and `watchdog-state.json` (occurrence `first_observed` values
|
|
351
416
|
and send receipts) — watchdog only;
|
|
@@ -371,7 +436,34 @@ delivery uses [axstack-relay](../../axstack-relay/SKILL.md).
|
|
|
371
436
|
- Enumerate deploy-on-push branches from both repair repositories before
|
|
372
437
|
enabling and store them in `cursor.json`; re-check on allowlist changes.
|
|
373
438
|
- A later tick observing resolution or an explicit user decision clears a
|
|
374
|
-
hold. Silence never clears one.
|
|
439
|
+
hold. Silence never clears one. For a hold caused by an Orca runtime
|
|
440
|
+
refusal — a sub-worker dispatch rejected for depth, a launch capability the
|
|
441
|
+
runtime declines — observing resolution means re-attempting the refused
|
|
442
|
+
operation, once per tick, on the next eligible PR: success clears the hold.
|
|
443
|
+
The hold is keyed on Orca's structured error code (for the depth case,
|
|
444
|
+
`nested_worker_depth_exceeded`), stored in `cursor.json` as
|
|
445
|
+
`runtime_refusal {code, first_seen, last_seen}`; the same code keeps the
|
|
446
|
+
hold and updates `last_seen` without a new health line, a different code is
|
|
447
|
+
a new finding. Prose is never the key. When `worker-start` itself is
|
|
448
|
+
refused after the child worktree was created, there is no worker, so the
|
|
449
|
+
settlement proof does not apply; the driver reads the receipt's `failedStage`
|
|
450
|
+
and `residualResources` first. With no Dispatch and no residual resources
|
|
451
|
+
the no-worker branch applies: the worktree's HEAD must equal the pinned
|
|
452
|
+
head and `git status --porcelain` must be empty, and only then does the
|
|
453
|
+
driver remove the worktree, its directory and branch in the same tick.
|
|
454
|
+
With a Dispatch or any residual resource the failed start owns runtime
|
|
455
|
+
state, and retaining alone is not recovery: the driver follows the
|
|
456
|
+
runtime's recovery guide. With a Dispatch: `worker-list` for that run, and
|
|
457
|
+
the row's `nextAction` is an object `{kind, argv}` — a non-empty `argv` is
|
|
458
|
+
run verbatim through the same Orca executable and nothing else, while
|
|
459
|
+
`kind: none` authorizes no action beyond inspection and retention. With
|
|
460
|
+
residual resources but no Dispatch there is no row: the mutation itself is
|
|
461
|
+
recovered through `request-show` on the receipt's request id. The no-worker
|
|
462
|
+
branch applies only after the resources are proven gone. It never retries
|
|
463
|
+
in the same tick. Anything unproven retains the worktree with a health line
|
|
464
|
+
naming the stage and the resources. Persisted configuration such
|
|
465
|
+
as `orca-data.json` is never evidence either way; it is a snapshot that
|
|
466
|
+
lags the live setting, and the driver never reads it.
|
|
375
467
|
|
|
376
468
|
## Cutover
|
|
377
469
|
|