@sabaiway/agent-workflow-kit 5.10.0 → 5.11.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.
- package/CHANGELOG.md +91 -0
- package/README.md +2 -2
- package/SKILL.md +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/modes/commit-guard.md +11 -8
- package/references/modes/core-evidence.md +1 -1
- package/references/modes/dispatch.md +32 -10
- package/references/modes/worktrees.md +47 -3
- package/tools/advisor-matrix.mjs +165 -0
- package/tools/commands.mjs +2 -2
- package/tools/commit-guard.mjs +74 -17
- package/tools/core-evidence.mjs +10 -0
- package/tools/dispatch-advisor.mjs +323 -0
- package/tools/dispatch.mjs +174 -109
- package/tools/doc-parity.mjs +68 -14
- package/tools/flow-check-cores.mjs +35 -6
- package/tools/flow-check-rungs.mjs +20 -2
- package/tools/flow-check.mjs +20 -5
- package/tools/observation-builder.mjs +123 -0
- package/tools/satellite-locator.mjs +179 -0
- package/tools/worktree-handoff-return.mjs +369 -0
- package/tools/worktree-prompt.mjs +190 -0
- package/tools/worktrees-record.mjs +171 -0
- package/tools/worktrees.mjs +308 -297
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,97 @@ Semantically versioned ([semver](https://semver.org)), newest first. The `versio
|
|
|
4
4
|
is the current release. `upgrade` mode reads a project's `docs/ai/.workflow-version` and applies
|
|
5
5
|
every `migrations/<version>-<slug>.md` newer than it, in semver order.
|
|
6
6
|
|
|
7
|
+
## 5.11.0 — the fan-out half: which vehicle carries a sub-task, what a satellite is told, and what its handoff brings back (AD-100)
|
|
8
|
+
|
|
9
|
+
**A delegated thread could be recorded end to end, and the two questions around it still had no
|
|
10
|
+
mechanism.** The one BEFORE it — which vehicle carries this step class, and is that vehicle even
|
|
11
|
+
present on this host — lived in canon prose and in a remembered rule. The one AFTER it, one level up:
|
|
12
|
+
`worktrees` provisions a satellite and lands its diff, but nothing composed the prompt that satellite
|
|
13
|
+
starts from, and the handoff coming back was a convention with no rung — nothing read it, nothing
|
|
14
|
+
delivered what it held, nothing counted what it bought. Three new verbs close both.
|
|
15
|
+
|
|
16
|
+
- **`dispatch advise --step-class <c>`, and the same block as a footer on a form-VALID `dispatch
|
|
17
|
+
check`.** It answers which vehicle carries the class on THIS host, what the ledger has recorded for
|
|
18
|
+
it, and what the fallback is — and it can never decide. `check`'s exit code and FIRST line are
|
|
19
|
+
byte-identical whether the advised vehicle is present or absent, and a form-INVALID contract still
|
|
20
|
+
prints only its first violated field, so the advice cannot mask a refusal. Where the advice differs
|
|
21
|
+
from `vehicle.selected`, it NOTEs the divergence — never a refusal — and names `vehicle.requested`
|
|
22
|
+
only where that in turn differs from what the contract selected.
|
|
23
|
+
- **Capability is read off the filesystem, and each kind of ignorance keeps its own name.** The
|
|
24
|
+
advisor module itself writes nothing and spawns nothing, and the verb's only subprocesses are
|
|
25
|
+
read-only git probes: the repository top-level the cheap vehicles are anchored at, and the
|
|
26
|
+
delegation store path unless `AW_DELEGATION_STORE` names it outright. It never runs a vehicle, a
|
|
27
|
+
subscription CLI, or anything that writes. The execute backend resolves through the existing
|
|
28
|
+
detector, the cheap vehicles through the
|
|
29
|
+
presence of `.claude/agents/<name>.md` anchored at the repository top-level. That lane is
|
|
30
|
+
FOUR-valued — present, missing, `unanchored` (no root resolved, so a file found here proves nothing
|
|
31
|
+
against a nested shadow copy) and `probe-error` — because folding either ignorance into "missing"
|
|
32
|
+
asserts something about a root the module does not know. The bundled vehicles are portable rows;
|
|
33
|
+
`doc-research` renders HOST-LOCAL and names the solo fallback when absent; the harness-subagent lane
|
|
34
|
+
renders ASSUMED/manual with no availability verdict at all.
|
|
35
|
+
- **Recorded history arrives through the ledger's existing single door**, over the closed state set
|
|
36
|
+
`folded · failure-terminal · degrade-closed · open` — `open` counted separately, never as a closed
|
|
37
|
+
thread. An absent store prints `history: no recorded history`; an unreadable one answers
|
|
38
|
+
`history: unavailable — ` and then the store's own reason, verbatim. The advice prints either way,
|
|
39
|
+
and the advisor never refuses.
|
|
40
|
+
- **`worktrees prompt <slug>` — the satellite's cold-start prompt, read-only — and `provision` now
|
|
41
|
+
ends its report with the same composed text.** It carries the worktree path and branch, the seeded
|
|
42
|
+
plan, where the shared series index lives, that landing runs from MAIN, and that the handoff is the
|
|
43
|
+
one channel back. Every value is derived LIVE: the record freezes MAIN's paths at provision time, so
|
|
44
|
+
a moved MAIN or a hand-edited field would otherwise put a stale RUNNABLE command in front of a
|
|
45
|
+
satellite — a recorded value that no longer matches is NAMED beside the live one, and the install
|
|
46
|
+
posture, which is probed on the satellite itself, carries its own cause rather than borrowing
|
|
47
|
+
"MAIN moved". Commands are attributed: `MAIN $ …` / `HERE $ …`, because the landing command mutates
|
|
48
|
+
MAIN — exactly what the satellite is forbidden to do — while a dependency-bearing checkout's install
|
|
49
|
+
command runs HERE, and an unattributed `$` line read as an instruction to whoever held the prompt.
|
|
50
|
+
- **`dispatch handoff-return` — deliver, prove, then count.** It prints every user-owned fragment of
|
|
51
|
+
the handoff — the content before and after `## Provision record` alike — BYTE VERBATIM, each
|
|
52
|
+
fragment's OPENING boundary carrying its byte length so a fragment that itself imitates a boundary
|
|
53
|
+
line cannot hide where it really ends, and names the MAIN-owned destinations that content folds into;
|
|
54
|
+
the fold stays yours and the rung claims nothing about it. Its window is bound by TWO facts, because
|
|
55
|
+
one cannot close it: a clean post-commit index reproduces the committed tree, so `prepared-tree`
|
|
56
|
+
equality survives the commit. `land --prepare` now also records `prepared-head`, and the rung
|
|
57
|
+
requires an UNCHANGED HEAD and a staged write-tree equal to `prepared-tree`, re-attesting both
|
|
58
|
+
immediately before either answer. A record written by an earlier kit, carrying no `prepared-head`,
|
|
59
|
+
refuses by name and names `land --prepare` as the fix. It is MAIN-side by construction and refuses
|
|
60
|
+
from inside a satellite, where the shared common dir would let it measure the wrong tree.
|
|
61
|
+
- **One observation, recorded WHOLLY or not at all.** The `worktree-stream` numerator is the ATTESTED
|
|
62
|
+
tree's blob bytes read through a fail-closed `cat-file` — never off disk, so an edit made after the
|
|
63
|
+
prepare cannot move it — with component identity the canonical path. Anything unrepresentable is a
|
|
64
|
+
NAMED `observation: NOT RECORDED` at exit 0: a deletion, a rename's absent old side, a symlink, a
|
|
65
|
+
submodule, a non-UTF-8 path name, and a mode-only change, which has no measurable byte change at all
|
|
66
|
+
and gets its own rule. A regular BINARY file is inside the domain. A number that silently omitted
|
|
67
|
+
the deletions half of a landing would be worse than a named non-record.
|
|
68
|
+
- **A fold that lands AFTER the gates leaves those gates stale, and the fix is a printed command.**
|
|
69
|
+
The rung and `references/modes/worktrees.md` carry one order: fold, re-stage, the configured review,
|
|
70
|
+
`run-gates --final` over the current staged tree, `commit-guard --check`, the commit ask. "One
|
|
71
|
+
writer per worktree" is stated at both points of use and in the composed prompt as the BAR it is —
|
|
72
|
+
nothing new refuses a second writer.
|
|
73
|
+
- **Content-free evidence stops deciding commits on the two rungs where it had failed closed.**
|
|
74
|
+
A degrade minted on a CLEAN tree binds the empty-payload fingerprint — the one value every clean
|
|
75
|
+
moment of every repository shares — and the `#65` correlation resolved it to 19 distinct bases and
|
|
76
|
+
failed closed forever, blocking every commit through `commit-guard`. The `#65` red rung and the `#64` ordering rung now
|
|
77
|
+
step over content-free records and RECORD the step in the advisory channel, and `commit-guard`
|
|
78
|
+
splits its content-free lanes by the INDEX rather than the payload: a dirty index means staged bytes
|
|
79
|
+
the fingerprint cannot see and REFUSES, naming the `submodule.<name>.ignore` /
|
|
80
|
+
`diff.ignoreSubmodules` configuration as the recovery; a clean one SKIPS the RECEIPT arms and PASSES
|
|
81
|
+
while stating it attests NOTHING. That pass is scoped, never blanket — store HEALTH is deliberately
|
|
82
|
+
not waived, and a flow refusal still refuses there, because neither is a correlation. The tree-bound
|
|
83
|
+
correlations drop out only where the CALLER declares `treeCarriesBytes: false` — a routine
|
|
84
|
+
clean-tree check still wants those rungs.
|
|
85
|
+
- **Seven new modules, and every moved name is re-exported.** `dispatch-advisor.mjs`,
|
|
86
|
+
`advisor-matrix.mjs`, `worktrees-record.mjs`, `satellite-locator.mjs`, `worktree-prompt.mjs`,
|
|
87
|
+
`worktree-handoff-return.mjs`, and `observation-builder.mjs` — the last extracted so `observe` and
|
|
88
|
+
the rung build the identical record by ONE path. `worktrees.mjs` re-exports everything the leaves
|
|
89
|
+
took, and the characterization claim held literally: no existing assertion or fixture in the
|
|
90
|
+
worktrees suites changed.
|
|
91
|
+
|
|
92
|
+
**Known limitation, accepted.** The `observation` record's key set is closed and carries no artifact
|
|
93
|
+
digest, so the handoff digest and the two attested OIDs are the rung's PRINTED proof rather than
|
|
94
|
+
ledger fields — a later reader of the ledger alone cannot re-derive which handoff bytes and which tree
|
|
95
|
+
an observation was taken over. Widening a closed key set is a schema change with its own migration; it
|
|
96
|
+
is tracked as a follow-up.
|
|
97
|
+
|
|
7
98
|
## 5.10.0 — the bundled agy review reads the CLI's own envelope, and refuses a host that cannot honour the dispatch (AD-098; antigravity-cli-bridge 5.3.0, memory 4.5.0)
|
|
8
99
|
|
|
9
100
|
**A review used to read whatever the CLI happened to print, and recover the conversation id by
|
package/README.md
CHANGED
|
@@ -243,8 +243,8 @@ file), or run the guarded `/agent-workflow-kit uninstall`.
|
|
|
243
243
|
| `/agent-workflow-kit commit-guard` | any time | **the read-only pre-commit guard** (D10) — makes the commit capture the whole current working tree, so «verified» and «about to be committed» are the same bytes (the receipt itself has a stated residual — see the mode doc). FIRST it refuses an **INDEX that lags the verified working tree** (the gates and the fingerprint describe the WORKING tree while `git commit` takes the INDEX alone, and the fingerprint cannot tell them apart — so a lagging index used to ship a strict subset of what was verified): unstaged tracked paths or reviewable untracked-not-ignored paths, named up to a bounded cap with the remainder stated, a dirty tracked **submodule** named separately with its own recovery, and fail-closed on an undecidable probe. This deliberately blocks a partial commit. Then it binds the LATEST completed `run-gates --final` receipt to the EXACT current tree: refuses on a missing/red/stale receipt, fingerprint drift under the run, a dangling later attempt, declaration content drift, evidence-hash or lcov drift, or unsatisfied review obligations (the same review-state decision, recomputed over a sanitized env — forged out-of-repo stores never satisfy). Re-runs NO gate or test. Wire it into `.git/hooks/pre-commit` (the installer writes the RESOLVED invocation). `git commit --no-verify` stays the stated residual. |
|
|
244
244
|
| `/agent-workflow-kit recommendations` | any time (every `upgrade` ends with it) | **read-only deployment advisor** (AD-044) — computes what in THIS deployment is configured sub-optimally (allowlist not seeded, autonomy render drifted, OS sandbox unavailable, gates undeclared, bridge friction, sandbox-mask clutter, an unacknowledged sandbox recipe) and renders **verdict-first**: one composed verdict line (does anything need attention?), then each item as **{severity · what · one-line benefit · an optional `recipe:` line (the sandbox-lane live recipe, the worktrees-dir hand-apply-first grant advice, or the agents hidden-mode reconcile follow-up) · the exact consent-gated apply one-liner}**. The agent PRESENTS the section in the user's conversational language — every fact and count, nothing added or dropped; commands, paths, hosts and rule strings byte-exact; raw tool block on request — and runs EXACTLY the rendered one-liners only on your yes, surfacing each item's posture note first. Renders **present-even-when-empty** (`no recommendations — flow optimal.`); a failed probe degrades to a stated skip line. Registry strings are fact-true frozen one-line data (posture/risk notes live in the mode doc at the consent moment); the kit never seeds `sandbox.network.allowedDomains` / `filesystem.allowWrite` (**HAND-APPLY** territory), and the sandbox-lane item's convergence is a neutral fingerprint acknowledgement recorded by a consent-gated ack writer into `docs/ai/acks.json` — never a security key (the recipe is documented per bridge in `capability.json` `networkHosts` + `writableDirs`). `--cwd` is required (the target project is explicit); never writes, never commits, never runs a subscription CLI. |
|
|
245
245
|
| `/agent-workflow-kit doc-parity` | any time | **read-only doc-parity lint** (AD-049) — kills the doc-drift class where a mode-contract doc silently lags a code constant (a `--check` doc still reading `300` after the diff cap moved to `400`): a **closed, exported registry** binds each live constant (review caps, schema versions, the ledger's own class/scope vocabulary, and the autonomy-doctor EXIT/status/trusted-dir contract) to the exact token its `references/modes/*.md` contract must carry, and asserts the CURRENT value renders into every bound file — a drifted doc, an unreadable file, or an absent token **fails closed**. The values are sourced from the live imports (never re-typed), so the lint can't itself go stale; adding a binding is adding a checked entry (closed-world, edit-safe). `--check` is a gate exit code for `docs/ai/gates.json`. Never writes, never commits, never runs a subscription CLI. |
|
|
246
|
-
| `/agent-workflow-kit dispatch` | any time | **the delegation engine** — makes "how much does delegating a sub-task actually buy?" a measured number instead of a feeling. `check <file>` validates a sub-task brief's contract block and exits 0/1 naming the first violated field — **form only**: whether the task is genuinely bounded, its design decided and its acceptance adequate stays your judgment, and a well-formed absurdity passes here by construction. `register` pre-registers an acceptance wave (step classes, pairing key, minimum observations per class, the mean and first-pass thresholds) so the bar can never be chosen after the results it judges — immutable per wave. `observe` records ONE hand-written observation (`solo-construction`, the baseline whose ratio is 1 by construction — except over a scope measuring zero bytes, which has no ratio at all and is recorded ineligible by name — or `self-reported`) — delegated numbers are never hand-written, they are derived. The writer verbs are where that derivation happens: `open` puts a delegated thread on the record with every mint-time field copied from the brief's own header, refusing a deadline the wrapper's cap plus its kill grace would not fit inside, and recording whether the tree it started from was clean (a dirty one makes the result honestly unmeasurable rather than quietly counted); `await` waits for that one dispatch to answer and writes nothing — only the finished receipt satisfies it, a run still holding the nonce means keep waiting, and a wait that runs out says so with its own exit code, names whether the deadline or your own timeout ended it, and authorizes nothing: a wait that ended without an answer is a question for you, never permission to dispatch again; `return` absorbs the receipt the wrapper minted — only a finished one, checked against the contract it actually ran, its deadline and its own report, and refused outright when the tree hides changes from git or moves while the return is being computed — and derives the bytes from git rather than from anyone's claim; `fold` is the acceptance, and it refuses if the tree moved since the return — precisely, it re-confirms **equality of the visible canonical payload**, which is a change detector rather than a cryptographic identity of the tree: that payload is unframed and carries no file mode, so a content or symlink target that imitates the marker opening the next entry can alias two trees, and an executable-bit flip moves nothing (both named as residuals in the mode doc, with the fix queued). Where the payload cannot follow an object's bytes at all — a binary, a non-regular path, a submodule, a symlink whose target is not valid UTF-8 — `return` and `fold` **refuse** rather than promise what they cannot check. `degrade` closes a thread that never earned a fold, on the record and with its reason. `aggregate` prints one wave: the registered thresholds, every observation as context, and per step class the finished threads — a folded success with git-provable bytes contributes its ratio, one whose bytes are unprovable is excluded from the mean but still counts in the first-pass rate, and a failed thread, a degrade-closed one, or one folded after failing acceptance each count as a real zero. Below the registered minimum nothing is computed (insufficient); at or above it is computed and labeled PILOT evidence. It REFUSES rather than guessing: no pre-registration, an unfinished thread in scope, a recorded refusal-to-delegate that never opened a thread, or an ambiguous wave each stop the computation by name. Writes only its own append-only ledger inside the git dir (never committable); never commits, never runs a subscription CLI. |
|
|
247
|
-
| `/agent-workflow-kit worktrees` | any time | **parallel feature worktrees** — run several features in DIFFERENT agent sessions on one repo, zero interference on working-tree files (the ONE exception is the dependency cache, below): `provision <slug> --plan <file>` creates a sibling git worktree on branch `aw/<slug>` and populates it (registry-derived footprint copy-if-missing — a tracked file is never overwritten; EXACTLY ONE seeded feature plan; the `handoff-<slug>.md` record from minute zero; `node_modules` symlinked where the link stays ignored — a shared MUTABLE dependency cache: writes through it hit MAIN's node_modules; for isolation run the printed isolated-install command (`--install` only PRINTS it; on `--resume` run the printed unlink-first recovery first); absolute root-pinned gate commands rebased on untracked copies only, and only while their bytes equal the MAIN source or its rebased form — user-modified copies stay untouched); `list` is read-only (slug, branch, base, dirty, handoff); `land <slug> --prepare` locks the common git dir, fail-closes on divergence or incomplete satellite state, transfers the complete accepted satellite diff onto a CLEAN main, runs sync plus the declared gates, and reports HEAD/TRANSFER/PREPARED OIDs — the commit ALWAYS stays a dialogue ask; `cleanup <slug>` takes the same lock and removes a LANDED worktree only after live landed-verification against main HEAD, while `--abandon` is the ONE destructive arm (destroys unlanded work; **no preview step** on any writer). The parent dir is the `docs/ai/worktrees.json` `parentDir` setting (default: the repo's sibling parent); an unwritable parent degrades to printed maintainer-pasted commands, and the one-time host consent that makes it promptless surfaces via `recommendations`. Never commits, never pushes, never runs a subscription CLI. |
|
|
246
|
+
| `/agent-workflow-kit dispatch` | any time | **the delegation engine** — makes "how much does delegating a sub-task actually buy?" a measured number instead of a feeling. `check <file>` validates a sub-task brief's contract block and exits 0/1 naming the first violated field — **form only**: whether the task is genuinely bounded, its design decided and its acceptance adequate stays your judgment, and a well-formed absurdity passes here by construction. `advise --step-class <c>` answers the question that comes BEFORE that one — which vehicle should carry this kind of sub-task, whether it is even present on this machine, and what the ledger has already recorded for threads of that class (finished, failed, closed without a fold, and still open — counted separately) — and it decides nothing: it refuses no dispatch, gates no verb, and a choice that diverges from the advice is noted rather than blocked. It reads the filesystem only: nothing is launched to find out what is installed, an absent ledger prints "no recorded history", an unreadable one prints the store's own words, and either way the advice still prints. The same block appears as a footer under a form-valid `check` — never under a refusal, so it can never hide one. `register` pre-registers an acceptance wave (step classes, pairing key, minimum observations per class, the mean and first-pass thresholds) so the bar can never be chosen after the results it judges — immutable per wave. `observe` records ONE hand-written observation (`solo-construction`, the baseline whose ratio is 1 by construction — except over a scope measuring zero bytes, which has no ratio at all and is recorded ineligible by name — or `self-reported`) — delegated numbers are never hand-written, they are derived. The writer verbs are where that derivation happens: `open` puts a delegated thread on the record with every mint-time field copied from the brief's own header, refusing a deadline the wrapper's cap plus its kill grace would not fit inside, and recording whether the tree it started from was clean (a dirty one makes the result honestly unmeasurable rather than quietly counted); `await` waits for that one dispatch to answer and writes nothing — only the finished receipt satisfies it, a run still holding the nonce means keep waiting, and a wait that runs out says so with its own exit code, names whether the deadline or your own timeout ended it, and authorizes nothing: a wait that ended without an answer is a question for you, never permission to dispatch again; `return` absorbs the receipt the wrapper minted — only a finished one, checked against the contract it actually ran, its deadline and its own report, and refused outright when the tree hides changes from git or moves while the return is being computed — and derives the bytes from git rather than from anyone's claim; `fold` is the acceptance, and it refuses if the tree moved since the return — precisely, it re-confirms **equality of the visible canonical payload**, which is a change detector rather than a cryptographic identity of the tree: that payload is unframed and carries no file mode, so a content or symlink target that imitates the marker opening the next entry can alias two trees, and an executable-bit flip moves nothing (both named as residuals in the mode doc, with the fix queued). Where the payload cannot follow an object's bytes at all — a binary, a non-regular path, a submodule, a symlink whose target is not valid UTF-8 — `return` and `fold` **refuse** rather than promise what they cannot check. `degrade` closes a thread that never earned a fold, on the record and with its reason. `aggregate` prints one wave: the registered thresholds, every observation as context, and per step class the finished threads — a folded success with git-provable bytes contributes its ratio, one whose bytes are unprovable is excluded from the mean but still counts in the first-pass rate, and a failed thread, a degrade-closed one, or one folded after failing acceptance each count as a real zero. Below the registered minimum nothing is computed (insufficient); at or above it is computed and labeled PILOT evidence. It REFUSES rather than guessing: no pre-registration, an unfinished thread in scope, a recorded refusal-to-delegate that never opened a thread, or an ambiguous wave each stop the computation by name. `handoff-return --slug <s>` closes the worktree loop after `land --prepare`: it re-attests that main still holds exactly the prepared tree under the same HEAD (both recorded in the handoff at prepare time), prints the satellite handoff's user-owned content byte for byte with its boundaries, byte lengths and the main-owned destinations each part folds into, prints the handoff digest and both OIDs as its proof, states the after-the-fold order (a fold landed after the gates leaves them stale), and records ONE self-reported worktree-stream observation only when the whole prepared change set is measurable — a deletion, a rename, a symlink, a submodule or a mode-only change ends with a named NOT RECORDED instead, never a partial number. Writes only its own append-only ledger inside the git dir (never committable); never commits, never runs a subscription CLI. |
|
|
247
|
+
| `/agent-workflow-kit worktrees` | any time | **parallel feature worktrees** — run several features in DIFFERENT agent sessions on one repo, zero interference on working-tree files (the ONE exception is the dependency cache, below): `provision <slug> --plan <file>` creates a sibling git worktree on branch `aw/<slug>` and populates it (registry-derived footprint copy-if-missing — a tracked file is never overwritten; EXACTLY ONE seeded feature plan; the `handoff-<slug>.md` record from minute zero; `node_modules` symlinked where the link stays ignored — a shared MUTABLE dependency cache: writes through it hit MAIN's node_modules; for isolation run the printed isolated-install command (`--install` only PRINTS it; on `--resume` run the printed unlink-first recovery first — printed only for a link whose RAW TARGET is MAIN's `node_modules`, since a foreign or unreadable link is reported as such with no removal advised); absolute root-pinned gate commands rebased on untracked copies only, and only while their bytes equal the MAIN source or its rebased form — user-modified copies stay untouched); `list` is read-only (slug, branch, base, dirty, handoff); `prompt <slug>` is read-only too and re-prints the satellite's cold-start prompt — the same text `provision` ends its report with: where the worktree is, its ONE seeded plan, the handoff as the one channel back, MAIN's orientation (shared series index, landing) and, under its own heading because it is probed on the satellite, that checkout's install posture — every value derived LIVE rather than replayed from the frozen provision record, a recorded value that no longer matches NAMED as a divergence (with the cause its source makes likely) instead of printed as the runnable one, every offered command marked with WHO runs it (`MAIN $ …` for the landing, `HERE $ …` for this checkout's own install, a posture with nothing to run staying prose), and a control character in any rendered value a typed STOP rather than a forged prompt line; `land <slug> --prepare` locks the common git dir, fail-closes on divergence or incomplete satellite state, transfers the complete accepted satellite diff onto a CLEAN main, runs sync plus the declared gates, and reports HEAD/TRANSFER/PREPARED OIDs — the commit ALWAYS stays a dialogue ask; `cleanup <slug>` takes the same lock and removes a LANDED worktree only after live landed-verification against main HEAD, while `--abandon` is the ONE destructive arm (destroys unlanded work; **no preview step** on any writer). The parent dir is the `docs/ai/worktrees.json` `parentDir` setting (default: the repo's sibling parent); an unwritable parent degrades to printed maintainer-pasted commands, and the one-time host consent that makes it promptless surfaces via `recommendations`. Never commits, never pushes, never runs a subscription CLI. |
|
|
248
248
|
| `/agent-workflow-kit uninstall` | opt-in, any time | **guarded teardown** — the inverse of `init` / `setup`. Removes only what's **provably ours** (managed skill dirs + bridge wrappers; in a project, the hidden-mode git-ignore block it added + the pre-commit hook it installed); **never deletes** your `docs/ai` / `AGENTS.md` (prints the exact `rm` to run by hand) or your `.claude/settings.json` (prints an **edit** — remove the attribution key, review any velocity `permissions.*` — never an `rm`). Always `--dry-run` first; preflight-then-mutate; never commits. |
|
|
249
249
|
| `/agent-workflow-kit velocity` | Claude Code · opt-in | **onboarding velocity profile** — seeds a fixed, audited **read-only** allowlist into `.claude/settings.json` so routine read-only commands stop idling on approval prompts while you're away; opt-in `acceptEdits`; plus a **read-only advisory** of likely project gate commands to add by hand. Writes **only** `.claude/settings.json` — **never** allowlists commit/push/publish, never writes `settings.local.json`, never commits. A seeded entry is a **trust posture, not a sandbox** (a runtime residual remains at the settings level — its guard ships as the opt-in `hook` command); a direct commit/push/publish still asks. `--dry-run` first. |
|
|
250
250
|
| `/agent-workflow-kit agents` | Claude Code · opt-in | **read-only subagents** — places bundled subagent definitions into `.claude/agents/`. **No vehicle gets `Bash`**, and that is the load-bearing property: a read-only fan-out on a full-tool subagent shells out for facts it could have read, and every shelled command is an approval prompt you never needed to see. Three ride a **cheap model** (haiku, low effort) for mechanical work — extraction sweeps, changelog fact-skeletons, gate-failure triage; the fourth, **`review-lens`**, is an ADDITIONAL independent read-only review opinion on code your configured backends have already seen (never a replacement for your review recipe, advisory like every review). Writing code and running gates stay on your main lane, and every vehicle's output is verified. Preview by default (`--apply` writes); an existing customized file is **preserved, never overwritten**; never touches `settings*.json`, never commits. |
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: agent-workflow-kit
|
|
|
3
3
|
description: Deploy or upgrade a portable AI-agent memory-and-workflow system in any project. Use when the user wants to bootstrap `docs/ai/` + an entry-point `AGENTS.md` (+ `CLAUDE.md` alias) + cap/archive/index enforcement in a new or existing repo, set up the Memory Map and session protocols, install the docs-rotation pre-commit hook, or run `/agent-workflow-kit` / `/agent-workflow-kit upgrade`. Triggers on phrases like "set up the memory system", "deploy the AI workflow here", "bootstrap docs/ai", "upgrade the workflow".
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
metadata:
|
|
6
|
-
version: '5.
|
|
6
|
+
version: '5.11.0'
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# agent-workflow-kit
|
package/capability.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sabaiway/agent-workflow-kit",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.11.0",
|
|
4
4
|
"description": "Portable, cross-agent memory & workflow for AI coding agents — Claude Code, Codex, Cursor, Devin Desktop. One command deploys an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement into any repo.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -7,13 +7,16 @@ The **read-only pre-commit guard** (strip-the-kit D10) — the last line of the
|
|
|
7
7
|
Run `node ${CLAUDE_SKILL_DIR}/tools/commit-guard.mjs --check [--cwd <dir>]` — it refuses, each with a named recovery, on:
|
|
8
8
|
|
|
9
9
|
1. **an INDEX that lags the verified working tree** — the gates and the fingerprint both describe the WORKING tree, while `git commit` builds the commit from the INDEX alone, and the fingerprint domain is identical whether a hunk sits staged or unstaged (staging a lone tracked modification does not even move it). So this arm runs FIRST, before the fingerprint is computed: it refuses when the working tree holds anything the index does not — tracked paths differing between index and worktree, or reviewable untracked-not-ignored paths (the SAME never-committable stat filter the fingerprint applies; ignored paths never refuse). An index entry carrying **skip-worktree** or **assume-unchanged** is invisible to `git diff`, so those entries are compared DIRECTLY against the worktree (type, symlink target, executable bit where `core.fileMode` says so, and the blob oid through git's own clean filters) — a de-materialised skip-worktree path is an ordinary sparse checkout and never refuses, while a missing assume-unchanged path does. The submodule probe forces `--ignore-submodules=none`, so `diff.ignoreSubmodules` / `submodule.<name>.ignore` cannot erase a dirty submodule from the comparison. Offending paths are named in THREE categories — plain lagging paths, paths held back by an index bit, and submodules — because their recoveries differ; ONE shared cap spans all of them, each category reserves a slot so no clause is ever nameless, the remainder is stated once, and every path is rendered as one escaped line. Recovery is ONE ordered sequence, and `git add -A` alone is NOT it: a bit-carrying entry must have its `skip-worktree` / `assume-unchanged` bit cleared FIRST (scoped to the paths the refusal names — never to everything `git ls-files -v` reports, which includes de-materialised sparse paths whose deletions would then be staged), then `git add -A`, then re-run `run-gates --final` at its RESOLVED path beside this tool, then commit the WHOLE tree. The listed paths are capped, so the loop is the completion signal: re-run the guard until it names none. A tracked **submodule** the index cannot prove current is named separately with its own recovery — commit or clean inside the submodule and stage the gitlink — because a root-level `git add -A` cannot capture a submodule's internal worktree changes. A submodule whose gitlink itself carries one of those index bits is **not probed at all**: it lags by construction. That is a deliberate REDUCTION — three consecutive review rounds each found a new way for a nested probe to answer "clean" wrongly (inherited superproject `GIT_*`, status config blindness, the submodule's OWN flagged entries, a symlink standing in for the directory) — and it stays a CONVERGING refusal, since clearing the bit is a recovery the guard prints and then falls silent on. An UNflagged submodule is judged by the ordinary probe exactly as before. **Fail-closed:** an undecidable git probe refuses with its own named cause, never a silent pass;
|
|
10
|
-
2. no
|
|
11
|
-
3.
|
|
12
|
-
4.
|
|
13
|
-
5.
|
|
14
|
-
6.
|
|
15
|
-
7.
|
|
16
|
-
8.
|
|
10
|
+
2. **a CONTENT-FREE fingerprint over a DIRTY index** — the payload carries no bytes while the index really carries a change, which means staged content the fingerprint domain cannot see: a submodule gitlink hidden from `git diff` by `submodule.<name>.ignore` or `diff.ignoreSubmodules`. No receipt can describe what that commit will carry, so the guard refuses and names the CONFIGURATION as the recovery — clear the ignore setting (or set it to `none`) until `git diff --cached --no-ext-diff` shows the change, then re-run `run-gates --final`. Re-staging is not the recovery: the change is already staged;
|
|
11
|
+
3. no completed final record for the CURRENT fingerprint (the tree moved after the final run — any edit re-stales it);
|
|
12
|
+
4. a RED latest attempt (a dead green never revives — the latest attempt at a fingerprint is authoritative);
|
|
13
|
+
5. fingerprint before ≠ after on the receipt (the tree moved UNDER the final run);
|
|
14
|
+
6. a LATER `final-start` whose attempt never completed (interrupted run / failed receipt append — an attempt of unknown outcome never lets an earlier green stand);
|
|
15
|
+
7. declaration content drift (the current `docs/ai/gates.json` {id, cmd} array no longer matches the receipt's recorded one);
|
|
16
|
+
8. evidence-hash drift (the store's canonical red-proof/degrade serializations moved under the receipt) or lcov drift (the consumed file's sha moved or vanished);
|
|
17
|
+
9. unsatisfied review obligations — the SAME normative decision `review-state --check` computes (configured recipe backends, ship-class-only on the latest normal receipt, veto, the explicit degrade escape), recomputed over a SANITIZED env: the guard resolves FIXED git-dir paths for its own reads and ignores `AW_REVIEW_RECEIPTS`/`AW_CORE_EVIDENCE` (producer test seams are never guard inputs — a forged out-of-repo store never satisfies).
|
|
18
|
+
|
|
19
|
+
**The empty-commit lane is a PASS that attests NOTHING — and it consults no receipt.** A content-free fingerprint over a CLEAN index means the index contributes no tree-content delta, so the commit changes no tree content: `git commit --allow-empty`, a message- or signature-only `--amend`, an empty merge. (What such a commit DOES change — its message, its parents, its signature — was never in the guard's domain.) The guard passes and says exactly that, because its whole claim is about the bytes a commit carries and here there are none. The receipt arms are SKIPPED rather than satisfied: the content-free fingerprint is the one value every clean moment shares, so a receipt found there cannot be correlated to THIS moment or base — letting it decide would make the outcome depend on which stray moment the store recorded last, the same reason `flow-check`'s `#65` rung steps over a content-free RED. The flow arm still runs (the commit still moves HEAD, and the chain bookkeeping is about that), with its own fingerprint-keyed correlations dropped through the explicit `treeCarriesBytes: false` the guard declares — the checker never derives that fact, because a routine clean-tree check still wants those rungs.
|
|
17
20
|
|
|
18
21
|
**Wiring:** this repo's dogfood rides `scripts/install-git-hooks.mjs`; a consumer install is a consented surface (init/recommendations) — the hook INSTALLER resolves the installed kit location at install time and writes the RESOLVED invocation into the hook it places (no runtime guessing). The final-run ordering that keeps the guard green is D13: stage everything FIRST → run the reviews on the staged tree → `run-gates --final` → commit immediately (any index/worktree mutation after the final run re-stales the receipt).
|
|
19
22
|
|
|
@@ -21,7 +24,7 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/commit-guard.mjs --check [--cwd <dir>]` —
|
|
|
21
24
|
|
|
22
25
|
**Boundary — what «the commit captures the tree» means for submodules.** The guard proves the SUPERPROJECT commit captures the SUPERPROJECT working tree. A superproject commit stores a submodule as a gitlink OID and never captures its file content — that content is the submodule's own commit boundary. The dirty-submodule refusal is therefore a best-effort COURTESY beyond the guard's boundary, and its completeness is bounded by what the submodule itself reports: a file held behind a `skip-worktree` / `assume-unchanged` bit in the SUBMODULE's own index is invisible to the submodule's status and so to this refusal. Deepening the nested probe was tried and abandoned — three consecutive review rounds each produced a new way for it to answer wrongly — so the honest contract is the boundary, not a completeness claim. Tracked as its own class.
|
|
23
26
|
|
|
24
|
-
**Stated residual — the fingerprint can still be blinded by config.** Refusal 1 makes the COMMIT capture the whole current working tree. It does not make the RECEIPT unforgeable: `computeFingerprintPayload` still runs its diffs without `--ignore-submodules=none`, so under `diff.ignoreSubmodules=all` a submodule can be changed and its gitlink staged AFTER a green `--final` while the fingerprint stays put, and the stale receipt is reused. That is a receipt-collision defect, not an under-capture one — no commit ships less than the working tree because of it — and closing it means moving the node payload and both bash twins together (the AD-044 lockstep). Tracked as its own class.
|
|
27
|
+
**Stated residual — the fingerprint can still be blinded by config.** Refusal 1 makes the COMMIT capture the whole current working tree. It does not make the RECEIPT unforgeable: `computeFingerprintPayload` still runs its diffs without `--ignore-submodules=none`, so under `diff.ignoreSubmodules=all` a submodule can be changed and its gitlink staged AFTER a green `--final` while the fingerprint stays put, and the stale receipt is reused. That is a receipt-collision defect, not an under-capture one — no commit ships less than the working tree because of it — and closing it means moving the node payload and both bash twins together (the AD-044 lockstep). Tracked as its own class. **Narrowed:** the case where the hidden gitlink is the ONLY staged change is now closed by refusal 2 — there the payload is empty and the index is not, which is decidable without touching the payload definition. A hidden gitlink riding ALONGSIDE other visible changes still produces an ordinary fingerprint and still reuses its receipt; that remainder is what the lockstep owns.
|
|
25
28
|
|
|
26
29
|
**Human residual (stated, accepted):** `git commit --no-verify` bypasses any pre-commit hook — a self-discipline mechanism, not a security boundary.
|
|
27
30
|
|
|
@@ -7,7 +7,7 @@ The ONE **loop-evidence writer** (strip-the-kit D6a/D7) — every core evidence
|
|
|
7
7
|
Run `node ${CLAUDE_SKILL_DIR}/tools/core-evidence.mjs <verb> …`:
|
|
8
8
|
|
|
9
9
|
1. **`red-proof "<test-file>#<test-name-pattern>"`** — the D3(c) observed-red DECLARATION, minted **BEFORE a bugfix is applied**: it runs the named test N times (default 3, `AW_CORE_EVIDENCE_RERUNS`; per-run timeout `AW_CORE_EVIDENCE_TIMEOUT_S`) on the CURRENT (pre-fix) tree, requires red N/N (observed green / unresolvable / mixed / timed-out are DISTINGUISHED refusals — nothing is written; mixed/timeout is QUARANTINE, no override lane), and records { testId · repo-relative file · content sha256 (custody) · N/N red · base = HEAD sha · the pre-fix tree fingerprint }. The final run (`coverage-check --check`) then requires every current-base record's test green N/N with the hash unchanged; a record whose pre-fix fingerprint EQUALS the current tree never satisfies (nothing changed — reuse/forgery); a commit expires records (base moves); editing a bound test file means re-observing red (the new record supersedes the old — same {base, testId} key).
|
|
10
|
-
2. **`degrade --backend <name> --reason "<why>"`** — the ONLY escape from a required review backend (D3(b)): an explicit per-backend
|
|
10
|
+
2. **`degrade --backend <name> --reason "<why>"`** — the ONLY escape from a required review backend (D3(b)): an explicit per-backend record at the CURRENT fingerprint. Never all backends: the review gate still requires ≥1 non-degraded ship-class receipt whenever ≥1 backend is configured. Any tree edit that MOVES the fingerprint re-stales the record — which is every edit the payload can see, and no more: a change the payload cannot see (a gitlink hidden from `git diff` by an ignore configuration) leaves the record standing. **The per-tree claim has one honest exception, and it is not a corner case:** a degrade minted while the work tree is CLEAN binds the CONTENT-FREE fingerprint — the one value every clean moment of every repository shares — so it identifies no working state and, since the record carries no base, no moment either. Such a record is therefore OUTSIDE the `#64` degrade-before-final ordering rung (`flow-check` records the skip as an advisory naming how many were skipped), because the ORDER of two records that cannot be shown to belong to the same moment states nothing about any tree. The writer still accepts it — refusing would strand the one lane that honestly records an unavailable backend during a plan round, which attests no tree at all. Closing the exception needs a durable `base` on the record, which is the queued store migration.
|
|
11
11
|
3. **`summary`** — the ONE stateless end-of-loop render (D6): gate result from the latest final attempt, per-backend verdicts from the review receipts, red-proof outcomes, degrade records. Computed from the stores at read time — no ledger, no rounds, nothing remembered. A malformed store exits non-zero and WITHHOLDS the affected section, never renders a partial as complete. The final-run line NAMES an absent coverage verdict from the `coverage` token the run RECORDED (an ADDITIVE optional field on the `final` record — the closed `certified | not-run | unknown` set, never `none`, and `certified` requires a bound `lcovSha256`): GREEN never renders unqualified over a run that issued no verdict. A LEGACY receipt carrying no token and no lcov digest is named as exactly that, never as a claim about what it read — the withheld verdict travels here too, as DETAIL beside the unchanged status word.
|
|
12
12
|
|
|
13
13
|
**Promptless writer lane (D7):** both verbs ride plain `node ${CLAUDE_SKILL_DIR}/tools/core-evidence.mjs …` invocations — surface the paste-ready allow entries once (`.claude/settings.json`), and a full loop transcript runs at zero writer approval prompts.
|
|
@@ -4,17 +4,39 @@
|
|
|
4
4
|
|
|
5
5
|
The **delegation engine** (delegation Plans 1–2): the sub-task **contract check**, the records a human hand-writes into the delegation ledger, the four **writer verbs** that put a delegated thread on the record, the **arrival waiter**, and the L0 **acceptance report** over one wave. It exists because nothing today records `{dispatched → returned → folded}` for EXEC work, so "how much does delegating actually buy?" is unanswerable. The ledger answers it; this mode is the surface a number enters it through, and the surface it is read back from.
|
|
6
6
|
|
|
7
|
-
Run **`node ${CLAUDE_SKILL_DIR}/tools/dispatch.mjs <check|register|observe|open|await|return|fold|degrade|aggregate> …`**:
|
|
7
|
+
Run **`node ${CLAUDE_SKILL_DIR}/tools/dispatch.mjs <check|advise|register|observe|open|await|return|fold|degrade|handoff-return|aggregate> …`**:
|
|
8
8
|
|
|
9
9
|
1. **`check <dispatch-file>`** — validates the ONE ```` ```aw-dispatch-contract ```` fenced block the dispatch file carries: exit `0` when its form is valid, `1` naming the FIRST violated field. The contract of the whole mode, in one sentence: the contract check is FORM-only — fields present, grammars respected, never boundedness, design-decidedness or acceptance adequacy — and `aggregate` REFUSES instead of computing acceptance for a wave with no pre-registration record, over an OPEN thread in scope, over a PRE-DISPATCH degrade that opens no thread, or across several waves with no `--wave`; the writer verbs add NO second legality door — the store's preflight is the only one, and its refusals travel verbatim — while `open` copies every mint-time field from the contract header and refuses a deadline below the wrapper cap plus the kill grace, `return` absorbs only a TERMINAL exec receipt whose backend, nonce and independently computed contractDigest match the dispatch it answers, and `fold` binds the CURRENT tree to the folded return's postTreeDigest, so a tree that moved between the two never folds, and `await` is satisfied ONLY by the TERMINAL exec receipt of its own dispatch's {backend, nonce} — never by a review receipt, a ledger line or a finding manifest — while an expiry names a supervision question and releases NO writer slot. Whether the sub-task is genuinely bounded, whether its design is decided, and whether its acceptance criteria are adequate stay **explicit orchestrator judgment** — a well-formed absurdity passes here by construction.
|
|
10
|
-
2. **`
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
2. **`advise --step-class <c>`** — the **vehicle-routing advisory**, and the question that comes BEFORE a contract check: *which vehicle carries this step class on THIS host, and what has the ledger already recorded for it.* It prints one block — the advised vehicle with its availability, the solo fallback, the recorded history, and the note below — and **decides nothing**: the advice never gates a dispatch: which vehicle carries a sub-task stays orchestrator judgment, and a divergence from the advice is recorded as a note rather than refused. Exit `0` for every legal step class, `2` only on usage. Its posture, stated exactly: the advisor module itself writes nothing, spawns nothing and opens no second ledger door; the VERB may run read-only git probes — the delegation store path (unless AW_DELEGATION_STORE names it outright) and the repository top-level the cheap vehicles are anchored at — and it never runs a vehicle, a subscription CLI, or anything that writes. Host capability is read from the filesystem — the execute backend from the bridge install (the detector spawns nothing at all), a cheap vehicle from the presence of `.claude/agents/<name>.md` **at the repository top-level**; where that root is not resolved, the lane answers **`unknown`** rather than claiming a vehicle is unplaced, because an absent file under an unanchored directory is not evidence of absence and a present one there is not evidence it is this repository's vehicle. The recorded history is the ledger's OWN thread walk, over the closed state set `folded | failure-terminal | degrade-closed | open` with **`open` counted separately** (a live thread is not evidence about a finished one). An **absent** ledger prints `no recorded history`; an **unreadable** one prints the store's own words and the advice still prints; neither is a refusal. The same block prints as a **footer on a form-valid `check`** (never under a refusal, so it cannot mask one), where the advised vehicle is compared against the contract's `vehicle.selected` and a difference is printed as a divergence NOTE naming `vehicle.requested` when the pair itself differs.
|
|
11
|
+
|
|
12
|
+
The vehicle-routing matrix — one row per step class, and this copy is held to the code's registry CELL for cell by a structure check (a reorder, a duplicate, a dropped row, a mis-bound vehicle and a drifted availability or returns cell all leave every token present, so a token check would pass every one of them). The check reads only the anchored surface between the two markers below, so a copy of this table anywhere else in the doc can neither stand in for it nor mask its drift:
|
|
13
|
+
|
|
14
|
+
<!-- advisor-matrix:begin -->
|
|
15
|
+
|
|
16
|
+
| step class | vehicle | availability | returns |
|
|
17
|
+
| --- | --- | --- | --- |
|
|
18
|
+
| `code` | codex-exec | execute backend — readiness read from the bridge install, never from a spawn | a diff plus the wrapper's exec receipt |
|
|
19
|
+
| `extraction` | mechanical-sweep | bundled vehicle — present once placed in .claude/agents/ | an extraction report you verify |
|
|
20
|
+
| `triage` | gate-triage | bundled vehicle — present once placed in .claude/agents/ | a structured gate-failure classification |
|
|
21
|
+
| `draft` | changelog-skeleton | bundled vehicle — present once placed in .claude/agents/ | a factual skeleton |
|
|
22
|
+
| `research` | doc-research | HOST-LOCAL — a per-host web grant, never bundled with the kit | cited findings |
|
|
23
|
+
| `review-opinion` | review-lens | bundled vehicle — present once placed in .claude/agents/ | one additional review opinion |
|
|
24
|
+
| `worktree-stream` | worktrees | ships with the kit — available wherever the kit is deployed | a prepared satellite diff plus its handoff |
|
|
25
|
+
| harness subagent | the host's own | the harness's own subagent lane is ASSUMED/manual — it is not kit-detectable, so it carries no availability verdict here and no acceptance weight | not measured |
|
|
26
|
+
|
|
27
|
+
<!-- advisor-matrix:end -->
|
|
28
|
+
|
|
29
|
+
**SIX of the seven rows are portable, for three different reasons, and exactly ONE is not.** Four are the bundled cheap-agent vehicles — the kit ships them and `/agent-workflow-kit agents` places them, so their availability is the presence of a file. One is the execute backend, portable because the bridge is installable anywhere, so its availability is that install. One is the kit's own worktrees mode, portable because it IS the kit. The single non-portable row is `doc-research`: its web access is a deliberate per-host grant, never bundled, so it renders **HOST-LOCAL** in both states and where the file is absent it names the solo fallback instead. The last row of the table is not a step class at all — it carries no availability verdict and no acceptance weight. The `worktree-stream` row inherits the **one-writer bar** the worktrees mode states: one writing session per worktree is a BAR, not a mechanism, exactly as D10 stands for the exec lane below.
|
|
30
|
+
|
|
31
|
+
3. **`register`** — appends the wave's **pre-registration** record (`--wave --step-classes --pairing-key --min-per-class --mean-l-threshold --first-pass-num --first-pass-den`). Acceptance is pre-registered so thresholds can never be chosen after the observations that will be judged against them; a registration is **immutable per wave** and a second one refuses. `--pairing-key` accepts only a key the aggregator actually implements (today `stepClass`) — a wave registered under any other key would record a contract the computation never follows, and the aggregator re-checks the registration independently, since a store can be hand-written.
|
|
32
|
+
4. **`observe`** — appends ONE **observation** (`--wave --step-class --scope --plan --phase --provenance [--denominator-bytes]`). Provenance is `solo-construction` or `self-reported` **only**: delegated accounting is DERIVED from nonce threads and is never hand-appended. The numerator is the post-image bytes of the objects the scope names; a **solo** observation takes that same number as its denominator — `L = 1` by construction, so `--denominator-bytes` is refused — while a self-reported one states its own. One stated exception: a solo scope that measures **zero** bytes has no ratio at all, and is recorded INELIGIBLE by name (`zero-denominator`) rather than as `L = 1` — `0/0` is undefined before it is one, and a named ineligibility is what this family records instead of a silent number. **`--scope` is repeatable**, one repo-relative path per occurrence: no separator is safe inside a POSIX path, so a split list could not express `docs/my file.md` and would silently measure the wrong set wherever its fragments happened to exist. Paths are anchored at the **git top-level**, never at the caller's cwd, and the record carries a **JSON-encoded array of canonical repo-relative paths, carried in the schema's string field** (the record's `scope` is a string by D3, so the array travels encoded). An object's identity IS its canonical path, so two files with identical bytes are two objects and count twice, while one path reached twice (a repeated listing, an in-repo ancestor symlink) is one object and counts once. The run refuses by name outside a git work tree, for an absent or non-regular path, and for a path whose **real** location leaves the repository (an ancestor symlink is not a way out).
|
|
33
|
+
5. **`open`** — appends the **dispatch** record that opens a thread (`--contract <dispatch-file> --wave --backend --rationale --wrapper-cap-s --kill-grace-s [--retry-of <nonce>]`). Every mint-time field — `nonce`, `stepClass`, `vehicle`, `deadlineS`, `retry.index`, `retry.cap` — is **COPIED from the contract header** and bound by `contractDigest`, so a dispatch can never disagree with the header it claims to carry. It fingerprints the tree (`preTreeDigest`) and probes it for a CLEAN baseline; a **dirty** one is recorded as such, and the eventual return is then metric-INELIGIBLE by the name `dirty-baseline` — the uncommitted-state fingerprint is blind to the index↔worktree split, so a dirty baseline cannot attribute bytes to a dispatch. It **refuses a `deadlineS` below `--wrapper-cap-s` + `--kill-grace-s`** (both explicit operands: the kit never reads another package's default), because a dispatch that cannot be capped inside its own deadline could never honour the terminal-exit rule. Every retry rule — one successor per thread, the cap read off the chain's ORIGIN, wave and class equality, a changed contract after a `contract-refusal` — is the **store's**, and `open` surfaces its refusals verbatim.
|
|
34
|
+
6. **`await`** — watches for ONE dispatch to **answer**, and writes nothing at all (`--nonce [--timeout <s>]`). Satisfaction is the arrival of the **TERMINAL** exec receipt for the dispatch's own `{backend, nonce}`: a `reserved` artifact means the run holds the nonce and has published nothing about how it ended, so the wait continues — the same artifact that is a **supervision refusal** at `return`, because absorbing it would record an outcome nobody observed. Satisfaction is decided **positively**, by the exec-receipt reader itself, so an artifact of a neighbouring family standing at that path — a review receipt, a delegation ledger line, a finding manifest — **refuses** rather than satisfies: only an exec receipt answers an exec dispatch, which is the D10 rule the review waiter states from its own side. The bound is the **ABSOLUTE** deadline, measured from the DISPATCH record's timestamp rather than from whenever this wait began: `--timeout` defaults to the time **remaining**, and one reaching past the deadline is **refused** rather than silently clamped (a shortened wait would let a caller believe they waited longer than they did). An already-expired dispatch is **answered immediately**, never waited on. **Arrival is read first, before EVERY bound** — the two wait bounds and the `--timeout` admissibility check alike, so a receipt already on disk answers `ARRIVED` even when the supplied `--timeout` is inadmissible, and the check runs only once there genuinely IS a wait to bound. A receipt already on disk is a fact this verb reports; whether a *late* one may be absorbed is the absorb door's question, refused there by name — **lateness has exactly one decision site, and it is `return`**. The review waiter checks its deadline first for the opposite reason: a receipt *line* carries no dispatch-bound timestamp at all, so there the clock is the only evidence there is. Each pass consults the clock over the poll that has already happened, then sleeps and polls again, so a receipt landing during the last sleep is reported rather than lost to a cutoff a moment later. An unanswered wait exits **`3`** — its own status so a caller that **branches** on the code can tell it from a refusal, never a defence against one that discards failure wholesale — names whether the **deadline** or the `--timeout` ended it, and states that **no writer slot was released**: a wait that ended without an answer never authorizes the next dispatch. Every artifact read rides the store's own no-follow reader, so a symlinked, FIFO, unreadable or malformed artifact refuses **by class** instead of standing in for an answer.
|
|
35
|
+
7. **`return`** — absorbs the wrapper's exec receipt and appends the **return** record (`--nonce [--outcome <o>] [--no-receipt --exit-status <n>]`). The receipt and its report live **beside the ledger**, in the store's own directory. It reads the **TERMINAL** receipt — a `reserved` one is a **supervision question**, not a timeout, and refuses by saying so — then checks the receipt's independently computed `contractDigest` against the dispatch's (a run that executed a *different* contract would otherwise correlate cleanly), its `{backend, nonce}` body against the dispatch it answers (the filename alone is composed by this side), its `capS + killGraceS` against the recorded `deadlineS`, and its timestamp against the **ABSOLUTE** deadline (`dispatch.timestamp + deadlineS`) — a late return refuses by name. It re-verifies the report's digest and length against the receipt, refuses a change set **hidden from the plain `git diff`** the metric is computed over (assume-unchanged, skip-worktree, `diff.ignoreSubmodules` — the numerator and the denominator would both be blind to it), and then enumerates the returned objects and frames the canonical integration bundle, **bracketed by the tree fingerprint**: a tree that moves mid-computation refuses rather than mixing two change sets. `--outcome` records an orchestrator judgment under the **closed override form** — a wrapper outcome either stays itself or moves to `contract-refusal | partial-edit | acceptance-failure | stale-return | store-failure`, and `success` is recordable only from a receipt that already says success; the record vocabulary's own cross-field rules cut the remaining incompatible pairs. **`--no-receipt`** means "no TERMINAL receipt", never "no evidence": it absorbs a **reservation ONLY** — over a `terminal` artifact it refuses, because a hand-stated outcome there would discard an exit status, a session id and a report digest the run actually proved, and skip the report check that guards them. It builds the return from that reservation (`wrapperVersion` and `posture`, never hand-typed), requires `--exit-status` and an `--outcome` from `transport-failure | contract-refusal | store-failure | missing-identity` (so `partial-edit` and `stale-return` are inexpressible there by name), and reads the report **if present** — an absent one records length 0 and the metric is then ineligible **by name**: `empty-report` where there IS a diff, and `no-op-diff` where the tree is unchanged too, since the eligibility rule names the diff first. That keeps a failed REPORT write and a failed TERMINAL write two separately named lanes. With **no artifact at all** there is no honest return — `--no-receipt` reads the same path and would refuse the same way — so close the thread with `degrade`.
|
|
36
|
+
8. **`fold`** — appends the integration re-confirmation and CLOSES the thread (`--nonce --verdict <text>`). It runs the **same hidden-path guard** as `return`, and here for a sharper reason: `treeDigestAtFold` is computed from the very payload that is blind to those paths, so a change made behind an `assume-unchanged` / `skip-worktree` bit between the return and the fold leaves the digest **equal** and the fold would accept bytes nobody returned — the digest cannot catch what the payload cannot see. It then computes the **current** tree digest, which the store binds to the folded return's `postTreeDigest`: a tree that moved between the return and the fold refuses in the store's own words, and the recovery it names is a fresh dispatch, never a fold. Staging usually moves the tree too — every change set carrying an untracked path does — so the order is `return` → review (own eyes + the configured review recipe) → a plain gate run → `fold` → `git add`. **The one shape staging does not move** is a tracked-only change passing from the worktree into a **clean** index: the payload concatenates the staged and unstaged diffs, so those bytes are identical either side of `git add` (the uncommitted-state fingerprint's stated blindness to the index↔worktree split). The fold is still honest there — identical payload bytes mean the folded content IS the returned content — so the rule is "**the fold precedes staging**", never "staging refuses the fold by construction".
|
|
37
|
+
9. **`degrade`** — appends the recorded no-fold closure (`--wave --step-class --rationale [--nonce]`), **threaded** (with `--nonce`) or **pre-dispatch** (without). The pre-dispatch form opens no nonce thread, so once one is recorded `aggregate` **REFUSES the whole wave by name** until the wave's counting unit is decided — said here and on the line that writes it, because it is a live cost of the record, not a surprise to discover later.
|
|
38
|
+
10. **`handoff-return --slug <s> --wave <id> --plan <id> --phase <n>`** — the **worktree-stream return rung**, run FROM MAIN after `land --prepare` (from inside a linked worktree it refuses: the git dir is not the git common dir, and the shared common dir would let it attest the wrong tree). It locates the satellite through the handoff identity (the same locator leaf the worktrees mode binds on), **DELIVERS** every user-owned fragment of the handoff **byte verbatim** — the content before and after `## Provision record` alike, each between boundary lines carrying its byte length, so the delivery is verifiable rather than asserted — and names the MAIN-owned destinations (findings → `docs/plans/queue.md`; decisions and session records → the `docs/ai` records); the fold itself stays orchestrator judgment and the rung claims nothing about whether it happened. It requires **BOTH** `prepared-tree` and `prepared-head` from the record and re-attests them against MAIN — the staged write-tree must equal the recorded tree and HEAD must equal the recorded head, because a clean post-commit index reproduces the committed tree, so a tree comparison alone cannot close the window; a record with no `prepared-head` was written by an earlier kit and refuses by name pointing at `land --prepare`. Then it classifies the prepared change set against the **observation domain** (present regular files only — a regular BINARY file is inside, its bytes read like any other) and appends ONE `self-reported` `worktree-stream` observation through the store's single door — numerator: the change set's **blob bytes, read from the attested tree itself** (`git cat-file` over the diff-tree entries' new OIDs, every answer validated fail-closed) — never from disk, which an unstaged edit after the prepare moves silently; denominator: the handoff byte count. The staged write-tree and HEAD are re-checked once more **immediately before either answer** (the pre-append idiom — it narrows the race window rather than closing it, this family's stated posture). A **deletion**, a **rename's absent old side**, a **symlink**, a **submodule**, a **mode-only change** (no measurable byte change at all), a **path whose name is not valid UTF-8** (the record's string domain cannot carry it; it renders as hex) and every other unrepresentable form end instead with `observation: NOT RECORDED — <form> at <path> is outside the observation domain` at exit `0`, delivery and proof still printed — **no partial scope is ever recorded**: a number that silently omitted the deletions half of a landing would be worse than a named non-record. **Honest limit (double-recorded):** the closed observation key set carries no artifact digest, so the handoff's sha256 and the two OIDs are the rung's **printed proof, never ledger fields** — an ACCEPTED limitation, recorded in the ADR with its follow-up row in the queue. The printed next-step order is the worktrees mode's after-the-fold order: a fold landed after the gates leaves them stale.
|
|
39
|
+
11. **`aggregate [--wave <id>]`** — reports one wave: the registered thresholds, every observation (context — never part of the acceptance number), and per registered step class the delegated threads with the **D7 inclusion table** applied. A folded **success** with an eligible `wrapper-git` metric contributes its `L`; a folded success whose metric is ineligible or self-reported is EXCLUDED from the mean and from `n` while still counting in the first-pass rate; a failure-terminal thread, a degrade-closed one, and a folded **acceptance-failure** (the fold-fix judgment — the orchestrator paid for the same work twice, so its bytes are not leverage) are each in `n` at `L = 0`. The first-pass rate is per retry CHAIN: its denominator is the `retryIndex`-0 threads, so a retry never reports one piece of work twice. Below the registered minimum acceptance is **NOT computed** (insufficient); at or above it is computed and labeled **PILOT** evidence. The primary statistic is the per-class MEAN of `L`; the byte-weighted aggregate prints as SECONDARY only.
|
|
18
40
|
|
|
19
41
|
**Exit codes:** `0` success (a computed report and an insufficient one both exit 0 — go/no-go is judgment, never an exit code); `1` a refusal (a form violation, an unreadable dispatch file, a supervision question, any store STOP verbatim); `2` usage; `3` an `await` that ended with no terminal receipt — its own status so a caller that **branches** on the exit code can tell an unanswered wait from a refusal; a caller that discards every failure discards this one too.
|
|
20
42
|
|
|
@@ -26,4 +48,4 @@ Run **`node ${CLAUDE_SKILL_DIR}/tools/dispatch.mjs <check|register|observe|open|
|
|
|
26
48
|
|
|
27
49
|
**What the fold's binding IS, and its stated residuals.** The binding is **equality of the visible unframed payload** between the return and the fold — never an identity of the tree, and the difference is not academic. The payload is an unframed concatenation, so **two different trees can produce the same bytes**: probed live, a single file containing the line `untracked:two.txt` yields the same fingerprint as a tree of two files, and a symlink's target can imitate the marker that opens the next untracked entry. It also carries **no git-relevant mode**, so `chmod 0644 → 0755` on an untracked file moves neither the fingerprint nor the enumeration — a blind attribute, not a fourth unsupported content class. Regular-file content and symlink targets are deliberately **not** subtracted: they are present in the payload, only ambiguously framed, and refusing them would refuse `new` and `symlink`, which is every delegated change set there is. The real fix — a framed payload carrying mode bits — is queued against that frozen surface, since it revalues every stored fingerprint in the family. A **receipt is forgeable**, exactly like every record in this family, so what the absorb door defends against is a buggy or interrupted producer, not a hostile one; the numerator **over-counts a rename** (a delegate's rename arrives as a deletion plus a creation, and no identity matching is done — that machinery was built and deliberately subtracted), so the metric reads optimistic, except in the rename-then-recreate case, which is indeterminate and named in the producer's own header; and **D10 is a bar, not a mechanism** — at most ONE in-tree exec dispatch runs at a time, and nothing here refuses a second one.
|
|
28
50
|
|
|
29
|
-
**Invariants:** writer — appends only to the delegation ledger, `<git common dir>/agent-workflow-delegation.jsonl` (`AW_DELEGATION_STORE` overrides, absolute only), through the store's own lock-serialized append; the store's preflight is the single legality door and this mode adds no second validator · every verb that DERIVES anything from the ledger (`return`, `fold`, `degrade`, `aggregate`) replays the store's preflight over the whole file first and refuses at the first record the append path would have refused, naming its physical line — a verb never computes over a "legal prefix" · **never commits**, never runs a subscription CLI, spawns nothing but git READS · the ledger is separate from the review receipts and the flow store, and a line of one family never satisfies a reader of another.
|
|
51
|
+
**Invariants:** writer — appends only to the delegation ledger, `<git common dir>/agent-workflow-delegation.jsonl` (`AW_DELEGATION_STORE` overrides, absolute only), through the store's own lock-serialized append; the store's preflight is the single legality door and this mode adds no second validator · every verb that DERIVES anything from the ledger (`return`, `fold`, `degrade`, `aggregate`) replays the store's preflight over the whole file first and refuses at the first record the append path would have refused, naming its physical line — a verb never computes over a "legal prefix" · **never commits**, never runs a subscription CLI, spawns nothing but git READS — with ONE stated exception: `handoff-return` attests MAIN's index with `git write-tree`, which may write a tree OBJECT into the odb and moves no ref (the same probe `land --prepare` itself uses; the ledger stays the only thing this mode APPENDS to) · the ledger is separate from the review receipts and the flow store, and a line of one family never satisfies a reader of another.
|
|
@@ -22,7 +22,16 @@ own verbatim error through the existing Git-error surface.
|
|
|
22
22
|
`node_modules` symlink when main has one and the link stays ignored — a SHARED MUTABLE cache:
|
|
23
23
|
writes through it hit MAIN's node_modules; for isolation RUN the printed isolated-install
|
|
24
24
|
command (`--install` only prints it), and on `--resume` an existing symlink is kept — run the
|
|
25
|
-
printed unlink-first recovery first.
|
|
25
|
+
printed unlink-first recovery first. That recovery is printed ONLY for a link this tool can prove
|
|
26
|
+
it placed — BOTH halves of the ownership rule below: raw target bytes equal to MAIN's
|
|
27
|
+
`node_modules`, AND the ignored lane. Every other state is reported in its own words and advises no
|
|
28
|
+
removal, because deleting a node whose provenance was never established is not a recovery: a
|
|
29
|
+
FOREIGN target (read, and not equal to that path — which says where it points, never who placed
|
|
30
|
+
it), a TRACKED path (matching target, but repository content the landing lane protects), a
|
|
31
|
+
LANE-UNPROVEN path (matching target, but the ignore probe did not settle, carrying the probe's own
|
|
32
|
+
cause), and an UNREADABLE link (the target read itself failed, carrying its errno). The same five
|
|
33
|
+
verdicts render the same way in `provision`'s report and in the `prompt` output, so the two can
|
|
34
|
+
never describe one link differently. Absolute root-pinned gate commands are rebased on UNTRACKED copies only — and only
|
|
26
35
|
while their bytes still equal the MAIN source (or its rebased form); user-modified copies stay
|
|
27
36
|
byte-untouched. `--install` only PRINTS the install command — zero spawn, zero write.
|
|
28
37
|
`--resume <slug>` completes a half-done provision (identity fail-closed; handoff user sections,
|
|
@@ -97,6 +106,31 @@ own verbatim error through the existing Git-error surface.
|
|
|
97
106
|
symlinked docs/plans (or ancestor), a handoff-named entry that is not a regular file, a
|
|
98
107
|
vanished worktree dir, or any other read failure renders `handoff: (unreadable)` — never a
|
|
99
108
|
silent "no".
|
|
109
|
+
- `prompt <slug>` — read-only: re-print the satellite's cold-start prompt, the same text
|
|
110
|
+
`provision` ends its report with. It carries the worktree path and branch, the ONE seeded plan,
|
|
111
|
+
the handoff as the one return channel, MAIN's orientation (the shared series index and the landing)
|
|
112
|
+
and — under its own heading, because it is probed on the SATELLITE and not in MAIN — that
|
|
113
|
+
checkout's install posture. Every value is derived LIVE, never replayed from the provision record:
|
|
114
|
+
the record FREEZES them at provision time, so a moved MAIN or a hand-edited field would otherwise
|
|
115
|
+
put a stale runnable command in front of a satellite. A recorded value that no longer matches is
|
|
116
|
+
NAMED as a divergence beside the live one, with the cause its own source makes likely (a moved MAIN
|
|
117
|
+
or a hand edit for the MAIN-derived pair; a changed dependency declaration for the install
|
|
118
|
+
posture), and a field an earlier kit never wrote is no divergence at all. Every command the prompt
|
|
119
|
+
offers is a marked line naming WHO runs it: `MAIN $ …` for the landing, `HERE $ …` for this
|
|
120
|
+
checkout's own install — including the removal of a provisioned `node_modules` symlink, which is
|
|
121
|
+
runnable even where the install command itself is not derivable. A posture with nothing to run
|
|
122
|
+
stays prose, and the recorded posture string is never printed loose: it IS a command in the
|
|
123
|
+
ordinary case, and an unattributed one reads as an instruction. A `node_modules` symlink is
|
|
124
|
+
claimed to point into MAIN only when its RAW TARGET says so — the same evidence cleanup binds
|
|
125
|
+
ownership on; any other target reads as a link this tool did not place, with nothing claimed about
|
|
126
|
+
it and no removal advised. A control character in any value it would render is a
|
|
127
|
+
typed STOP, not a sanitized string: the prompt is line-oriented, so a newline in a filename would
|
|
128
|
+
otherwise forge a line — including a line imitating a command. Run it from MAIN — a cwd inside a
|
|
129
|
+
linked worktree is the same refusal `provision`/`land`/`cleanup` make. It resolves the satellite
|
|
130
|
+
through the handoff identity (the duplicate-identity, missing-record and slug/branch-mismatch STOPs
|
|
131
|
+
are the same ones `cleanup` binds on), and it writes nothing.
|
|
132
|
+
One writer per worktree is a BAR the prompt states and nothing enforces:
|
|
133
|
+
ONE writer per worktree: this session is the only agent writing in this checkout, and nothing enforces that — a second session writing here interleaves two agents into one tree, which neither this tool nor git can detect or undo. It is a bar you keep, not a lock you hold.
|
|
100
134
|
- `land <slug> --prepare` — stage the satellite's finished diff onto a CLEAN main; the commit is
|
|
101
135
|
NEVER run by the tool — it stays a dialogue ask at the primary session. Land takes the transient
|
|
102
136
|
common-git-dir lock, refuses a dirty main, graph divergence, visible `docs/ai` drift, excluded
|
|
@@ -133,7 +167,11 @@ own verbatim error through the existing Git-error surface.
|
|
|
133
167
|
**Provision record (`docs/plans/handoff-<slug>.md`, `## Provision record` — tool-owned):** resume
|
|
134
168
|
IDENTITY (`slug`, `branch`, and the seeded plan name — a mismatch STOPs) · recorded provision FACTS
|
|
135
169
|
that never authorize a resume (`include`, `node_modules`, `vscode-settings`) · and, after a prepare,
|
|
136
|
-
`prepared-tree
|
|
170
|
+
`prepared-tree` plus `prepared-head` (MAIN's HEAD at prepare time — what lets the handoff-return
|
|
171
|
+
rung tell a still-pending prepared set from an already-committed one, since a clean post-commit
|
|
172
|
+
index reproduces the committed tree; a record from an earlier kit simply lacks it and the rung
|
|
173
|
+
refuses by name), a land/cleanup/handoff-return attestation-and-recovery surface, not resume
|
|
174
|
+
identity —
|
|
137
175
|
PLUS the three facts a fresh satellite session cannot derive from its own checkout:
|
|
138
176
|
|
|
139
177
|
- `shared-queue` — the ABSOLUTE path to MAIN's `docs/plans/queue.md`, followed by the rule the record states verbatim: the series index is SHARED and lives ONLY in main: read it at the absolute path above, and never copy it into this worktree, because docs/plans is git-ignored and machine-local, so a copy silently diverges from what main and every other worktree are writing. This worktree never WRITES that file: reaching outside it is an fs_outside_repo action the autonomy policy denies by default. Put new findings in THIS handoff record instead — it is the channel that survives the landing, and main appends them to the index from here. Provision never seeds a copy: the queue is deliberately absent from the satellite, and the absolute path is the only pointer — `--include` refuses to copy the index (or any directory containing it) into the worktree.
|
|
@@ -173,7 +211,13 @@ preview prints the exact `--apply`). Neither is proof of write
|
|
|
173
211
|
capability: the create+delete probe above stays the runtime truth, and the ack binds to the
|
|
174
212
|
resolved probe dir, so the item re-fires only when that resolved dir changes.
|
|
175
213
|
|
|
176
|
-
**Landing flow:** provision → work → handoff → land →
|
|
214
|
+
**Landing flow:** provision → work → handoff → land --prepare → handoff-return → fold → re-stage →
|
|
215
|
+
configured review → run-gates --final → commit-guard --check → commit ask → cleanup. The
|
|
216
|
+
`dispatch handoff-return` rung (see the dispatch mode) delivers the handoff's user-owned content
|
|
217
|
+
byte verbatim, re-attests the prepared pair against MAIN, and records the worktree-stream
|
|
218
|
+
observation where the change set allows one; what follows it is one order, stated exactly:
|
|
219
|
+
a fold landed AFTER the gates leaves those gates STALE — the order is fold → re-stage (git add) → the configured review → run-gates --final (the receipt is minted over the CURRENT post-fold staged tree) → commit-guard --check (the final re-attestation) → the commit ask.
|
|
220
|
+
Satellite
|
|
177
221
|
commits are outside v1: graph divergence stops land and prints cherry-pick/rebase recovery. A gate
|
|
178
222
|
failure keeps the prepared main tree and names both recovery lanes. A second prepare is reset-only:
|
|
179
223
|
the STOP prints the current staged write-tree, compares it with the PREPARED OID recorded in the
|