@sabaiway/agent-workflow-kit 10.6.0 → 11.1.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 +133 -0
- package/README.md +7 -6
- package/SKILL.md +9 -1
- package/bridges/antigravity-cli-bridge/SKILL.md +1 -1
- package/bridges/antigravity-cli-bridge/bin/agy-review.sh +6 -6
- package/bridges/antigravity-cli-bridge/capability.json +1 -1
- package/bridges/codex-cli-bridge/SKILL.md +1 -1
- package/bridges/codex-cli-bridge/bin/codex-exec.sh +1 -1
- package/bridges/codex-cli-bridge/bin/codex-review.sh +6 -6
- package/bridges/codex-cli-bridge/capability.json +1 -1
- package/capability.json +1 -1
- package/launchers/windsurf-workflow.md +2 -2
- package/package.json +1 -1
- package/references/contracts.md +1 -1
- package/references/modes/bootstrap.md +5 -5
- package/references/modes/commit-guard.md +2 -2
- package/references/modes/control-bytes.md +91 -0
- package/references/modes/flow-writer.md +16 -4
- package/references/modes/procedures.md +2 -0
- package/references/modes/recommendations.md +1 -0
- package/references/modes/review-state.md +1 -1
- package/references/modes/robustness-brief.md +61 -0
- package/references/modes/velocity.md +2 -0
- package/references/robustness-literals.json +107 -0
- package/references/scripts/archiver-structure.test.mjs +48 -3
- package/references/scripts/install-git-hooks.test.mjs +31 -1
- package/references/shared/deploy-tail.md +1 -1
- package/references/templates/agent_rules.md +1 -1
- package/references/templates/changelog.md +1 -1
- package/references/templates/decisions.md +2 -2
- package/tools/changed-surface.mjs +1 -1
- package/tools/commands.mjs +14 -0
- package/tools/commit-guard.mjs +20 -9
- package/tools/control-bytes.mjs +264 -0
- package/tools/core-evidence.mjs +33 -20
- package/tools/coverage-check.mjs +1 -1
- package/tools/dispatch-store-read.mjs +2 -2
- package/tools/flow-check-git-lane.mjs +2 -1
- package/tools/flow-delta-proof.mjs +4 -4
- package/tools/flow-record-shape.mjs +44 -2
- package/tools/flow-round-gates.mjs +151 -0
- package/tools/flow-store-read.mjs +1 -1
- package/tools/flow-writer.mjs +235 -82
- package/tools/fold-scope.mjs +7 -3
- package/tools/fs-read-nofollow.mjs +56 -4
- package/tools/git-env.mjs +120 -0
- package/tools/grounding.mjs +4 -3
- package/tools/plan-shape-facts.mjs +23 -10
- package/tools/plan-shape.mjs +8 -0
- package/tools/procedures.mjs +18 -4
- package/tools/recommendations.mjs +53 -3
- package/tools/repo-lex.mjs +29 -0
- package/tools/review-state.mjs +43 -34
- package/tools/robustness-brief.mjs +209 -0
- package/tools/robustness-literals.mjs +101 -0
- package/tools/sandbox-masks.mjs +2 -1
- package/tools/set-flow.mjs +2 -1
- package/tools/source-size-scope.mjs +19 -25
- package/tools/tracked-tree-census.mjs +3 -3
- package/tools/velocity-profile.mjs +5 -2
- package/tools/worktree-handoff-return.mjs +2 -2
- package/tools/worktrees.mjs +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,139 @@ 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
|
+
## 11.1.0 — the bootstrap offers enforcement on every project, and the advisor closes the loop (AD-131)
|
|
8
|
+
|
|
9
|
+
Bootstrap steps 8–9 (the enforcement scripts + the pre-commit hook) used to decide by the step-1
|
|
10
|
+
recon — a Python, Go or Rust project was denied the caps gate, the archivers and the hook for no
|
|
11
|
+
proven reason, then `upgrade` reported `skipped-no-node-evidence` on the very tree the bootstrap left
|
|
12
|
+
bare. The scripts are dependency-free Node the agent host already runs for step 7's navigator.
|
|
13
|
+
Contract: `node-evidence.md` rev 2 (S8–S10 bound).
|
|
14
|
+
|
|
15
|
+
**The offer, on every project.** `references/modes/bootstrap.md` steps 8–9 offer the scripts and the
|
|
16
|
+
hook preview-first with a one-line why: the bootstrap seeds on `package-json` OR
|
|
17
|
+
`deployed-node-scripts` OR the fact that the agent host is running the kit; the only skip condition
|
|
18
|
+
is that the agent host cannot run them; the residual is stated (the hook runs `node scripts/<x>.mjs`,
|
|
19
|
+
so a committer without `node` on PATH gets the hook's own loud failure). `deploy-tail.md` carries the
|
|
20
|
+
same sentence once; the Windsurf launcher's step list names the offer once and copies nothing
|
|
21
|
+
unconditionally; the by-hand "Non-Node projects follow the policy by hand" claims left the READMEs.
|
|
22
|
+
The step-1 recon is a stack fact, never the criterion.
|
|
23
|
+
|
|
24
|
+
**The advisor's `enforcement` item.** On a `docs/ai` tree whose node evidence answers `none` and
|
|
25
|
+
whose `scripts/` is absent or holds no basename of the kit's own bundle (listed at run time through
|
|
26
|
+
the probe's own `scriptsBundleRoot` seam, never a hardcoded list), Recommendations renders ONE
|
|
27
|
+
optional offer: the exact bootstrap tail as a `HAND-APPLY:` line with both paths shell-quoted.
|
|
28
|
+
Every other cell is silence or a stated skip naming the path — `package-json` /
|
|
29
|
+
`deployed-node-scripts` render nothing and never list the bundle; a wrong-kind path, an unreadable
|
|
30
|
+
probe (`<root>: <code> on <rel>`), an existing, linked or unreadable destination each skip by name;
|
|
31
|
+
no `docs/ai` renders nothing. The probe spawns no git; the installer's refusal over a hook it did not
|
|
32
|
+
write is stated in the posture note (pinned). The commit-guard item's installer-absent skip carries
|
|
33
|
+
its own 155-char remedy (deploy the scripts, bootstrap steps 8–9, install the hook, re-run) on ANY
|
|
34
|
+
evidence — never "run upgrade first".
|
|
35
|
+
|
|
36
|
+
**A declined offer leaves no false claim.** Every install claim in the family is conditional on
|
|
37
|
+
the yes — `installed on a yes` in the READMEs and the seeded `changelog.md` / `decisions.md`
|
|
38
|
+
templates, `where installed` in the seeded `agent_rules.md`, `and the enforcement offer was
|
|
39
|
+
accepted` in both visibility contracts — pinned by one (file, literal, count) table in
|
|
40
|
+
`test/enforcement-offer-docs.test.mjs`; the visible wiring of `package.json` scripts is conditional
|
|
41
|
+
on an existing `package.json` AND an accepted offer; on a no the docs command slots of
|
|
42
|
+
`env_commands.md` and `AGENTS.md` carry a note (the caps + index gate run from the agent's own skill
|
|
43
|
+
copy; the archive rotations need the scripts deployed) and never the skill's absolute path.
|
|
44
|
+
|
|
45
|
+
**Pins, unchanged scripts.** The deployed scripts' basename fallback (no or a malformed
|
|
46
|
+
`package.json` → the directory basename) was already there — `install-git-hooks.test.mjs` and
|
|
47
|
+
`archiver-structure.test.mjs` pin it with a package.json-named control; no script changed.
|
|
48
|
+
|
|
49
|
+
**Measured on itself (the first-pass-quality series' acceptance metric).** Executed delegated on ONE
|
|
50
|
+
held codex session, eight accounted dispatches, every fold through it. Diff council: round 1 codex
|
|
51
|
+
revise 0 blockers + 3 majors, agy SHIP, lens 0 + 2 majors; rounds 2–3 resolved the decline-path
|
|
52
|
+
subarea at altitude; round 4 CLEAN from all three — four rounds against the 13-round baseline
|
|
53
|
+
(AD-130). Three fold-induced findings, each caught by a test or gate before a receipt attested it.
|
|
54
|
+
|
|
55
|
+
## 11.0.0 — the per-fold walk is a record, the cap is a refusal at the round table's own signal, and the fingerprint argv has one home (AD-130)
|
|
56
|
+
|
|
57
|
+
Plan 2's two attestation rounds on CONVERGED bytes each found one member of the kit's own robustness
|
|
58
|
+
list that the executor never received: the tagged sweep row got its brief, the untagged suite and
|
|
59
|
+
generator rows did not. Nothing asked "does the tag cover the row" before the dispatch, nothing
|
|
60
|
+
recorded the adversarial walk a fold owes, and past the round table's `cap reached` / `crossover`
|
|
61
|
+
signal nothing refused the next round — on the release-run feature the table printed CROSSOVER from
|
|
62
|
+
round 2 for eleven rounds and nothing acted on it. Rungs 4 and 5 of the first-pass-quality series
|
|
63
|
+
close that, and the two fingerprint classes plan 1 measured RED (Issue-023 / Issue-024) close with
|
|
64
|
+
them. Contracts: `robustness-literals.md` rev 2 (S10–S11 bound), `plan-review-loop/index.md` rev 6
|
|
65
|
+
(S30–S33 bound), `release-run/index.md` rev 2.
|
|
66
|
+
|
|
67
|
+
**Coverage is the generator's second mode.** `robustness-brief.mjs --plan <plan> --coverage` judges
|
|
68
|
+
every `create`/`modify` row except a document (`.md`, `.txt`) or the shipped list itself: a class is
|
|
69
|
+
PRESENT when the row file's BYTES carry a member literal of kind env/flag/ref/syscall/errno/argv as a
|
|
70
|
+
BOUNDED token (a comment counts, `state` members are never searched), `uncovered` = present minus
|
|
71
|
+
tagged, a `delete` row and an absent file are LISTED, a sweep row is judged over every path the facts
|
|
72
|
+
leaf expands (`expandSweepPaths`, one home in `plan-shape-facts.mjs`); exit 0 covered / 1 naming each
|
|
73
|
+
`row:class:literal` / 2 on a row file the 1 MiB no-follow read cannot deliver. Measured on itself: the
|
|
74
|
+
first run over this very plan named `R01:spawn-outcome:ENOENT` and the tag grew before the next
|
|
75
|
+
dispatch. `references/modes/robustness-brief.md` states the mode, its read surface and the bounds.
|
|
76
|
+
|
|
77
|
+
**Every fold owes a walk, recorded.** `flow-writer internal-attestation <planId> --walk <file>` reads a
|
|
78
|
+
closed JSON walk (`{ listVersion, rows: [{ id, class, checked }] }`) relative to the git COMMON dir,
|
|
79
|
+
derives the obligation from the ADOPTED plan (every judged row, every class in TAGGED ∪ PRESENT needs a
|
|
80
|
+
non-empty `checked`), records `uncovered` and never refuses on it; `round-open` for every round after
|
|
81
|
+
the first refuses without an authoritative walk-carrying attestation at round N's coordinates, and
|
|
82
|
+
`--justification` lifts that refusal as an echoed INPUT whose durable trail is the attestation's
|
|
83
|
+
absence. A plan with no judgeable row owes no walk; a plan that lost adoption coverage fails closed
|
|
84
|
+
by name (`resolveAdoptedPlan`, one home).
|
|
85
|
+
|
|
86
|
+
**The cap is the round table's own signal, and terminal is digest equality.** The new pure leaf
|
|
87
|
+
`tools/flow-round-gates.mjs` (`blockingItems`, `roundsForSignal`, `capIssue`, `walkIssue`,
|
|
88
|
+
`escalationIssue`, `validateWalk`; `signalFor` imported from `review-rounds.mjs`, never modified)
|
|
89
|
+
builds one round per authoritative head from its LANDED dispatches; on `cap reached` or `crossover`
|
|
90
|
+
only, `round-open` refuses unless every blocking ITEM of every landed receipt of the latest head is
|
|
91
|
+
covered by a disposition whose `findingDigest` equals `sha256(itemText)` — text mode the wrapper's own
|
|
92
|
+
line, schema mode the `issue` field verbatim — and prints every uncovered item beside its digest. An
|
|
93
|
+
enumeration failure on a readable manifest falls back to the `>= 1` floor with an ADVISORY; the
|
|
94
|
+
checks run cap → walk → floor in the lock-free pass and again on the locked snapshot.
|
|
95
|
+
|
|
96
|
+
**A finding with no proven narrow fix is never queued; a lost manifest is a record.** `round-land
|
|
97
|
+
--dispose queued --finding <quote> --claim <invariant> --proof-kind consult-attestation|red-proof
|
|
98
|
+
--proof-digest <d>` mints only on `decideFoldScope` ACCEPT `new-invariant`, with `debtId`/`debtDigest`
|
|
99
|
+
DERIVED from the ONE resolved queue row `fold-scope` now returns (`normalize` exported, one home); a
|
|
100
|
+
consult-attestation proof binds through `proposedFixDigest === sha256(itemDigest + "\n" + claim)`, a
|
|
101
|
+
red-proof through the head's `base`, the tree `round-land` runs on and the row's `proof` field. The
|
|
102
|
+
fourth arm `escalated --finding <quote> --override-digest <d>` resolves a `maintainer-override` of THIS round's vetoing
|
|
103
|
+
receipt; the FIFTH arm `custody-lost --receipt <receiptDigest>` mints only on a REAL loss (`absent |
|
|
104
|
+
foreign | error | malformed | foreign-identity | swapped`), covers every item of that receipt for the
|
|
105
|
+
cap and is recordable whenever the loss is real. `flow-record-shape.mjs` admits `walk`, the queued
|
|
106
|
+
trio (all-or-none), `escalated` and `custody-lost`; `FLOW_SCHEMA_VERSION` does not move.
|
|
107
|
+
|
|
108
|
+
**The fingerprint argv has one home.** `core-evidence.mjs` exports `FINGERPRINT_CACHED_DIFF_ARGV` /
|
|
109
|
+
`FINGERPRINT_UNSTAGED_DIFF_ARGV` carrying `--no-ext-diff --no-textconv --ignore-submodules=none`;
|
|
110
|
+
`flow-delta-proof.mjs` imports them; both wrappers' bash twins (`emit_fingerprint_payload`,
|
|
111
|
+
`assemble_code_diff`, the no-diff preflight) carry the flags, mirrored into `bridges/`, and
|
|
112
|
+
`test/review-fingerprint-parity.test.mjs` EXTRACTS the preflight line and proves it on real git with a
|
|
113
|
+
gitlink under `diff.ignoreSubmodules=all`. Issue-023 and Issue-024 close; `commit-guard`'s
|
|
114
|
+
content-free refusal 2 narrows to a fail-closed backstop with a generic message that names no flag
|
|
115
|
+
and no config key (`references/modes/commit-guard.md`).
|
|
116
|
+
|
|
117
|
+
**Tests and records.** New suite `flow-round-gates.test.mjs`; the touched suites (`robustness-brief`,
|
|
118
|
+
`flow-record`, `flow-writer`, `flow-dogfood.integration`, `core-evidence-hostile`,
|
|
119
|
+
`commit-guard-content-free`, `review-fingerprint-parity`, `package-content` — the tarball pin 284 →
|
|
120
|
+
285 — and `fold-scope`) green with real git; thirteen red-proof cases (34 `core-evidence red-proof`
|
|
121
|
+
re-observations on the parked pre-fix trees); the source-size baseline re-recorded under one stated reason. Every fold of the three diff
|
|
122
|
+
rounds rode the delegate's held session (AD-127): fourteen accounted dispatches, one session.
|
|
123
|
+
|
|
124
|
+
### BREAKING
|
|
125
|
+
|
|
126
|
+
- **Minimum bridge versions: codex-cli-bridge 3.7.0, antigravity-cli-bridge 5.6.0.** The fingerprint
|
|
127
|
+
domain moves on a repository configuring `textconv` (neutralised by `--no-textconv`) or
|
|
128
|
+
`diff.ignoreSubmodules` / `submodule.<name>.ignore` in `.git/config` or `.gitmodules` (both overridden
|
|
129
|
+
by `--ignore-submodules=none`): a kit-11 receipt and a pre-3.7.0 / pre-5.6.0 wrapper receipt hash the
|
|
130
|
+
same tree differently. `init` and `upgrade`
|
|
131
|
+
refresh a placed bridge (`bin/install.mjs`); a bridge never placed is never placed by them.
|
|
132
|
+
- **`round-land --dispose queued` changes its operands:** `--debt-id` / `--debt-digest` are gone;
|
|
133
|
+
`--claim`, `--proof-kind` and `--proof-digest` are required together with `--finding`.
|
|
134
|
+
- **`round-open` refuses where `--justification` used to lift:** past the round table's signal the
|
|
135
|
+
cap is never lifted by a justification; a missing walk is.
|
|
136
|
+
- **The one-way door:** a flow store carrying any post-rung record form — an `internal-attestation`
|
|
137
|
+
with `walk`, a `queued` disposition with `claim`/`proofKind`/`proofDigest`, an `escalated` or a
|
|
138
|
+
`custody-lost` disposition — reads under kit >= 11 only. A pre-rung store still reads.
|
|
139
|
+
|
|
7
140
|
## 10.6.0 — a fold on delegated code rides the delegate's held session, judged from the ledger the kit already mints (AD-127)
|
|
8
141
|
|
|
9
142
|
The 2026-08-30 `release-run` execution took thirteen diff-review rounds, and every fold was made by
|
package/README.md
CHANGED
|
@@ -117,12 +117,12 @@ your-repo/
|
|
|
117
117
|
├── tech_reference.md ← configs & patterns
|
|
118
118
|
├── pages/ ← one spec per page/route
|
|
119
119
|
└── history/ ← changelog archive (HOT→WARM→COLD)
|
|
120
|
-
+ scripts/ ← caps · index · archive (
|
|
121
|
-
+ pre-commit hook ← keeps it all honest (
|
|
120
|
+
+ scripts/ ← caps · index · archive (installed on a yes)
|
|
121
|
+
+ pre-commit hook ← keeps it all honest (installed on a yes)
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
The Markdown memory is **stack-agnostic**; the `scripts/` + pre-commit hook are the **Node path**
|
|
125
|
-
(dependency-free, `node --test`).
|
|
125
|
+
(dependency-free, `node --test`). They are offered on every project because the agent host runs them; a committer without Node on PATH gets a loud hook failure.
|
|
126
126
|
|
|
127
127
|
Two **visibility** modes, chosen at deploy time: **visible** (committed with the repo) or **hidden**
|
|
128
128
|
(same files in-tree but git-ignored via the project-local `.git/info/exclude`, so the repo "looks
|
|
@@ -225,7 +225,7 @@ file), or run the guarded `/agent-workflow-kit uninstall`.
|
|
|
225
225
|
|
|
226
226
|
| Command | When | What happens |
|
|
227
227
|
|---------|------|--------------|
|
|
228
|
-
| `/agent-workflow-kit` | new / empty project | recon → **asks visible-or-hidden** + **conversational language** + **agent attribution** (default off) → deploys `AGENTS.md` + `docs/ai/` filled with real recon data →
|
|
228
|
+
| `/agent-workflow-kit` | new / empty project | recon → **asks visible-or-hidden** + **conversational language** + **agent attribution** (default off) → deploys `AGENTS.md` + `docs/ai/` filled with real recon data → offers enforcement (installed on a yes) → **asks before committing** |
|
|
229
229
|
| `/agent-workflow-kit upgrade` | existing deployment | reads `docs/ai/.workflow-version`, shows the changelog diff, preserves your authored memory, applies migrations, re-stamps — then prints a **read-only** one-line backend-status line (what's set up vs missing); refreshes the already-placed bridges from the kit's bundled copies (never installs a new one — set one up with `/agent-workflow-kit setup`) |
|
|
230
230
|
| `/agent-workflow-kit help` | any time | **read-only command index** — every command, grouped (Inspect / Configure / Orchestrate / Lifecycle) and tagged read-only / writer / guarded. The discoverable entry point, and where any unrecognized invocation lands (always read-only — a garbage invocation never writes). Never writes, never commits, never runs a subscription CLI. |
|
|
231
231
|
| `/agent-workflow-kit backends` | any time | **read-only** check of the optional execution-backends (the `codex` / `agy` bridges): what's set up vs missing and the next step. Never writes, never commits, never runs a subscription CLI (credentials = marker-file presence, not a live login). |
|
|
@@ -270,7 +270,7 @@ It **never auto-commits** and **never overwrites** an existing `AGENTS.md` witho
|
|
|
270
270
|
- **Plan lifecycle** — a capped plan (goal and boundary, module ledger, verification), ephemeral plan files, and a mandatory Cleanup phase.
|
|
271
271
|
- **No silent failures** — every guard that rejects an action logs structured context.
|
|
272
272
|
|
|
273
|
-
|
|
273
|
+
The dependency-free **Node** enforcement scripts (`node --test`, no package manager assumed) and their hook are offered on every project because the agent host runs them; a committer without Node on PATH gets a loud hook failure.
|
|
274
274
|
|
|
275
275
|
---
|
|
276
276
|
|
|
@@ -284,7 +284,8 @@ agent-workflow-kit — the composition root (installed via npx … init)
|
|
|
284
284
|
on /agent-workflow-kit in a repo, the kit:
|
|
285
285
|
├─ delegates ─▶ memory substrate (healthy copy, else bundled fallback)
|
|
286
286
|
├─ injects ─▶ workflow methodology (live from the installed engine)
|
|
287
|
-
├─ deploys ─▶ AGENTS.md + docs/ai/
|
|
287
|
+
├─ deploys ─▶ AGENTS.md + docs/ai/
|
|
288
|
+
│ + Node scripts + pre-commit hook (installed on a yes)
|
|
288
289
|
├─ detects ─▶ optional backends (codex / agy, read-only)
|
|
289
290
|
└─ sets up ─▶ a bridge (opt-in) (place skill + link wrappers)
|
|
290
291
|
```
|
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: '
|
|
6
|
+
version: '11.1.0'
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# agent-workflow-kit
|
|
@@ -183,6 +183,14 @@ guarded — read `${CLAUDE_SKILL_DIR}/references/modes/bridge-settings.md` befor
|
|
|
183
183
|
|
|
184
184
|
read-only — read `${CLAUDE_SKILL_DIR}/references/modes/review-state.md` before acting.
|
|
185
185
|
|
|
186
|
+
### Mode: control-bytes
|
|
187
|
+
|
|
188
|
+
read-only — read `${CLAUDE_SKILL_DIR}/references/modes/control-bytes.md` before acting.
|
|
189
|
+
|
|
190
|
+
### Mode: robustness-brief
|
|
191
|
+
|
|
192
|
+
read-only — read `${CLAUDE_SKILL_DIR}/references/modes/robustness-brief.md` before acting.
|
|
193
|
+
|
|
186
194
|
### Mode: receipt-deadline
|
|
187
195
|
|
|
188
196
|
read-only — read `${CLAUDE_SKILL_DIR}/references/modes/receipt-deadline.md` before acting.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: antigravity-cli-bridge
|
|
3
3
|
description: Delegate work to Google's Antigravity CLI (`agy`) — the successor to Gemini CLI — to reach Gemini, Claude, and GPT-OSS models under a Google AI Pro/Ultra subscription from the terminal. Use when the user wants to run a headless `agy` prompt, hand a focused task or second-opinion review to `agy`, install or authenticate Antigravity CLI, check or economise its quota/models, bridge project context into `agy`, set up a second delegated-execution backend beside Codex, or troubleshoot `agy` flags, models, auth, conversations, or its headless behaviour.
|
|
4
4
|
metadata:
|
|
5
|
-
version: '5.
|
|
5
|
+
version: '5.6.0'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# antigravity-cli-bridge
|
|
@@ -343,7 +343,7 @@ DEFAULT_AGY_REVIEW_MODEL="Gemini 3.7 Flash (High)"
|
|
|
343
343
|
# Review-receipt identity (AD-038). AW_BRIDGE_VERSION mirrors this bridge's SKILL.md/capability.json
|
|
344
344
|
# version (drift-guarded by agy-review.test.mjs against capability.json).
|
|
345
345
|
AW_RECEIPT_BACKEND="agy"
|
|
346
|
-
AW_BRIDGE_VERSION="5.
|
|
346
|
+
AW_BRIDGE_VERSION="5.6.0" # aw-version-anchor
|
|
347
347
|
# `-` not `:-` so an EXPLICIT empty AGY_MODEL= survives (drop --model, use settings.json — agy.sh:52).
|
|
348
348
|
AGY_MODEL="${AGY_MODEL-$DEFAULT_AGY_REVIEW_MODEL}"
|
|
349
349
|
# D5 control-byte screen — IMMEDIATELY after resolution, BEFORE the off-frontier advisory (or any
|
|
@@ -867,8 +867,8 @@ warn_never_committable_untracked() {
|
|
|
867
867
|
# implements the SAME serialization in node — cross-checked by the kit's
|
|
868
868
|
# review-fingerprint-parity.test.mjs.
|
|
869
869
|
emit_fingerprint_payload() {
|
|
870
|
-
git diff --cached --no-ext-diff
|
|
871
|
-
git diff --no-ext-diff
|
|
870
|
+
git diff --cached --no-ext-diff --no-textconv --ignore-submodules=none
|
|
871
|
+
git diff --no-ext-diff --no-textconv --ignore-submodules=none
|
|
872
872
|
local path
|
|
873
873
|
while IFS= read -r -d '' path; do
|
|
874
874
|
if [[ -L "$path" ]]; then
|
|
@@ -1556,10 +1556,10 @@ assemble_code_diff() {
|
|
|
1556
1556
|
emit_status_porcelain_filtered
|
|
1557
1557
|
echo
|
|
1558
1558
|
echo "=== staged diff (git diff --cached) ==="
|
|
1559
|
-
git diff --cached --no-ext-diff
|
|
1559
|
+
git diff --cached --no-ext-diff --no-textconv --ignore-submodules=none
|
|
1560
1560
|
echo
|
|
1561
1561
|
echo "=== unstaged diff (git diff) ==="
|
|
1562
|
-
git diff --no-ext-diff
|
|
1562
|
+
git diff --no-ext-diff --no-textconv --ignore-submodules=none
|
|
1563
1563
|
echo
|
|
1564
1564
|
echo "=== untracked file contents ==="
|
|
1565
1565
|
local path
|
|
@@ -1834,7 +1834,7 @@ else
|
|
|
1834
1834
|
cd "$(git rev-parse --show-toplevel)"
|
|
1835
1835
|
# No-diff preflight — never spend a run on a clean tree. Never-committable untracked masks do
|
|
1836
1836
|
# not count: the FILTERED domain is the review surface.
|
|
1837
|
-
if git diff --quiet && git diff --cached --quiet && ! has_reviewable_untracked; then
|
|
1837
|
+
if git diff --quiet --no-ext-diff --no-textconv --ignore-submodules=none && git diff --cached --quiet --no-ext-diff --no-textconv --ignore-submodules=none && ! has_reviewable_untracked; then
|
|
1838
1838
|
echo "agy-review: no uncommitted changes to review — the working tree is clean." >&2
|
|
1839
1839
|
warn_never_committable_untracked
|
|
1840
1840
|
exit 0
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: codex-cli-bridge
|
|
3
3
|
description: Delegate work to the OpenAI Codex CLI (`codex`) under a ChatGPT subscription — run plan/instruction EXECUTION in a sandboxed workspace, or get a read-only ADVISORY review of a plan or working-tree diff — as a second delegated-execution backend beside Antigravity. Use when the user wants to hand a bounded coding task or plan to `codex exec`, get a second-opinion review from codex, install or authenticate Codex CLI, understand its sandbox/network/approval policy, drive codex efficiently from the main agent (exec vs review, resume, the commit boundary), bridge project context (`AGENTS.md`) into codex, or troubleshoot codex flags, models, auth, or its no-TTY headless behaviour.
|
|
4
4
|
metadata:
|
|
5
|
-
version: '3.
|
|
5
|
+
version: '3.7.0'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# codex-cli-bridge
|
|
@@ -372,7 +372,7 @@ CHATGPT_LOGIN_GUARD="Logged in using ChatGPT"
|
|
|
372
372
|
# receipt this wrapper mints; scripts/release/version-sync.mjs bumps it under the one-anchor-per-file
|
|
373
373
|
# rule, so a release can never leave it behind (the AD-053 drift class).
|
|
374
374
|
AW_RECEIPT_BACKEND="codex"
|
|
375
|
-
AW_BRIDGE_VERSION="3.
|
|
375
|
+
AW_BRIDGE_VERSION="3.7.0" # aw-version-anchor
|
|
376
376
|
# The kill grace handed to timeout(1) as --kill-after, and recorded in the receipt as killGraceS:
|
|
377
377
|
# ONE constant, so the number the ledger checks against the dispatch deadline is the number the run
|
|
378
378
|
# actually applied.
|
|
@@ -284,7 +284,7 @@ DEFAULT_CODEX_EFFORT="xhigh"
|
|
|
284
284
|
# Review-receipt identity (AD-038). AW_BRIDGE_VERSION mirrors this bridge's SKILL.md/capability.json
|
|
285
285
|
# version (drift-guarded by codex-review.test.mjs against capability.json).
|
|
286
286
|
AW_RECEIPT_BACKEND="codex"
|
|
287
|
-
AW_BRIDGE_VERSION="3.
|
|
287
|
+
AW_BRIDGE_VERSION="3.7.0" # aw-version-anchor
|
|
288
288
|
CODEX_MODEL="${CODEX_MODEL:-$DEFAULT_CODEX_MODEL}"
|
|
289
289
|
CODEX_EFFORT="${CODEX_EFFORT:-$DEFAULT_CODEX_EFFORT}"
|
|
290
290
|
# Generous hard cap for a slow xhigh review (subscription latency varies).
|
|
@@ -527,8 +527,8 @@ warn_never_committable_untracked() {
|
|
|
527
527
|
# implements the SAME serialization in node — cross-checked by the kit's
|
|
528
528
|
# review-fingerprint-parity.test.mjs.
|
|
529
529
|
emit_fingerprint_payload() {
|
|
530
|
-
git diff --cached --no-ext-diff
|
|
531
|
-
git diff --no-ext-diff
|
|
530
|
+
git diff --cached --no-ext-diff --no-textconv --ignore-submodules=none
|
|
531
|
+
git diff --no-ext-diff --no-textconv --ignore-submodules=none
|
|
532
532
|
local path
|
|
533
533
|
while IFS= read -r -d '' path; do
|
|
534
534
|
if [[ -L "$path" ]]; then
|
|
@@ -811,10 +811,10 @@ assemble_code_diff() {
|
|
|
811
811
|
emit_status_porcelain_filtered
|
|
812
812
|
echo
|
|
813
813
|
echo "=== staged diff (git diff --cached) ==="
|
|
814
|
-
git diff --cached --no-ext-diff
|
|
814
|
+
git diff --cached --no-ext-diff --no-textconv --ignore-submodules=none
|
|
815
815
|
echo
|
|
816
816
|
echo "=== unstaged diff (git diff) ==="
|
|
817
|
-
git diff --no-ext-diff
|
|
817
|
+
git diff --no-ext-diff --no-textconv --ignore-submodules=none
|
|
818
818
|
echo
|
|
819
819
|
echo "=== untracked file contents ==="
|
|
820
820
|
local path
|
|
@@ -923,7 +923,7 @@ case "$mode" in
|
|
|
923
923
|
code)
|
|
924
924
|
# No-diff preflight — never spend a subscription run on a clean tree. Never-committable
|
|
925
925
|
# untracked masks do not count: the FILTERED domain is the review surface.
|
|
926
|
-
if git diff --quiet && git diff --cached --quiet && ! has_reviewable_untracked; then
|
|
926
|
+
if git diff --quiet --no-ext-diff --no-textconv --ignore-submodules=none && git diff --cached --quiet --no-ext-diff --no-textconv --ignore-submodules=none && ! has_reviewable_untracked; then
|
|
927
927
|
echo "codex-review: no uncommitted changes to review — the working tree is clean." >&2
|
|
928
928
|
warn_never_committable_untracked
|
|
929
929
|
exit 0
|
package/capability.json
CHANGED
|
@@ -26,8 +26,8 @@ reinvent any of it here; the kit's `SKILL.md` is the single source of truth.
|
|
|
26
26
|
attribution) as ONE structured multi-question prompt; record each answer individually,
|
|
27
27
|
write nothing until ALL are answered** →
|
|
28
28
|
create `AGENTS.md` (+ `CLAUDE.md` symlink) from `<KIT_DIR>/references/templates/` →
|
|
29
|
-
deploy `docs/ai/` → copy `<KIT_DIR>/references/scripts/*.mjs
|
|
30
|
-
wire/hide per visibility →
|
|
29
|
+
deploy `docs/ai/` → offer the enforcement scripts + pre-commit hook (on a yes: copy `<KIT_DIR>/references/scripts/*.mjs`, install the hook) →
|
|
30
|
+
wire/hide per visibility → stamp `docs/ai/.workflow-version`.
|
|
31
31
|
4. Report what was filled with real data vs left as TODO, then **ask before committing** —
|
|
32
32
|
never auto-commit.
|
|
33
33
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sabaiway/agent-workflow-kit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.1.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",
|
package/references/contracts.md
CHANGED
|
@@ -17,7 +17,7 @@ The user chooses at bootstrap whether the AI artifacts are visible in the repo o
|
|
|
17
17
|
**explicit up-front question** (bootstrap step 2), never an assumed default. The two modes then
|
|
18
18
|
diverge:
|
|
19
19
|
|
|
20
|
-
- **visible** — artifacts are committed.
|
|
20
|
+
- **visible** — artifacts are committed. When a `package.json` exists and the enforcement offer was accepted, wire the project's `package.json` scripts (`docs:check` / `docs:index` / `docs:index:check` / `docs:archive` / `docs:archive:check` / `docs:archive:issues` / `docs:archive:issues:check` / `prepare: node scripts/install-git-hooks.mjs`); add a minimal `.gitignore` (`docs/plans/`, `.claude/settings.local.json`) either way. This is the canonical model.
|
|
21
21
|
- **hidden** (in-tree) — same files on disk, but the repo "looks normal": the AI/agent footprint is git-ignored via **one managed block in the project-local `.git/info/exclude`** (resolved with `git rev-parse --git-path info/exclude` — never the machine-global `core.excludesFile`, which would affect every repo on the host; **AD-014** amends **AD-006**). The kit's `tools/hide-footprint.mjs` is the single writer: it covers `KIT_OWN_PATHS ∪ the present subset of KNOWN_FOOTPRINT` (the full footprint table below — the kit's own artifacts **and** every other AI/agent tool's files). Per path: a **tracked** file → **ASK** (an exclude does nothing for it; only `git rm --cached` un-tracks it — the tool prints that command and never runs it); an untracked path already covered by a **tracked `.gitignore`** → dropped (redundant); a **present** file whose name is generic enough to be ambiguous (`falsePositiveRisk`) → **ASK**; everything else → **hidden**. `asks` are excluded from the block unless explicitly opted in. **Verify** treats a path as hidden only when it is **untracked AND ignored by our project-local block** (or a tracked `.gitignore`) — being ignored by the global excludes does **not** count. Re-running re-derives the block wholesale (sorted/deduped) → a clean re-run is a **zero-diff** no-op. On an existing global-excludes deployment the tool **detects + reports the residual legacy machine-global block and keeps it by default** (a harmless double-ignore; the local block wins precedence); removal is the explicit `--remove-global` (it prints the removed lines as a restorable backup), which **the agent only runs after asking** — another of the user's hidden repos on the same host may rely on the same root-anchored global lines. **Do not edit `package.json`** — that is a tracked change and would leak; the pre-commit hook (always untracked in `.git/hooks/`) calls the scripts via `node scripts/<x>.mjs` directly. Windows is supported (text edit, no symlinks; CRLF preserved).
|
|
22
22
|
|
|
23
23
|
**Visibility changes what git sees — never how the agent works (the load-bearing invariant).** Both modes carry the **same** working memory and run the **same** session protocol: `docs/ai` and the deployed workflow artifacts (`AGENTS.md`, the added `scripts/`) are read at session start and maintained on task completion **identically** whether visible or hidden. (Hidden mode git-ignores a *wider* footprint — other AI/agent tools' files too — but that is only a **visibility** surface; the working memory the agent actually reads and maintains is `docs/ai` plus those artifacts.) Hidden mode only makes the repo *look* normal to git; it is **never** a license to skip, defer, or down-prioritise a doc/state update. The single difference is the destination: in hidden mode those updates live on disk and never enter a commit (the footprint is excluded), so a commit there carries only the project's own tracked files while `docs/ai` is still kept current locally — **git-ignored ≠ optional-to-maintain**. An agent that reads "git-ignored / `git status` clean" and infers the working memory is optional has the model backwards. (This is why bootstrap/upgrade never wire hidden-mode docs into a commit, yet the session protocol still mandates maintaining them.)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
### Mode: bootstrap
|
|
2
2
|
|
|
3
|
-
<!-- opt-in-capability:
|
|
3
|
+
<!-- opt-in-capability: enforcement -->
|
|
4
4
|
|
|
5
5
|
Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKILL_DIR}/references/shared/composition-handoff.md · ${CLAUDE_SKILL_DIR}/references/shared/deploy-tail.md · ${CLAUDE_SKILL_DIR}/references/shared/command-shapes.md
|
|
6
6
|
|
|
@@ -23,9 +23,9 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
|
|
|
23
23
|
5. **Entry-point doc.** If `AGENTS.md` / `CLAUDE.md` already exist (step-1 recon), do **not** overwrite — show the user and ask whether to merge or replace. Otherwise create `AGENTS.md` (the cross-agent standard — Codex / Cursor / Devin Desktop / Copilot read it natively) from `${CLAUDE_SKILL_DIR}/references/templates/AGENTS.md`, and symlink `CLAUDE.md -> AGENTS.md` (`ln -s AGENTS.md CLAUDE.md`) for Claude Code — single source, no duplication. For nested context, add a subdir `AGENTS.md` (+ a `CLAUDE.md` symlink beside it for Claude Code).
|
|
24
24
|
6. **Deploy `docs/ai/`.** Create every `docs/ai/` file + `pages/` + the seed `adr/` store + the seed `specs/` store from `${CLAUDE_SKILL_DIR}/references/templates/` (the template loop deploys every template except `AGENTS.md`, `adr-record.md` and `SPEC_TEMPLATE.md` — the latter two are skill-home authoring references, never deployed). This deploys the HOT ADR window **`docs/ai/decisions.md`** and the seed navigator **`docs/ai/adr/log.md`** (its `adr/` subdir copies in wholesale), the spec store root **`docs/ai/specs/index.md`** (its `specs/` subdir copies in wholesale), the `.md` docs, **and** the three seeded, user-editable strict-JSON configs: **`docs/ai/orchestration.json`** (the per-project recipe defaults the `procedures` advisor reads), **`docs/ai/gates.json`** (the project's gate declaration — seeded as the no-consent EMPTY list; FILLING it is the consented `gates-init` preview, `${CLAUDE_SKILL_DIR}/references/modes/gates.md`), and **`docs/ai/autonomy.json`** (the per-project autonomy policy — seeded SPARSE with the onboarding note only, so it is exactly the computed defaults until the user declares levels via `/agent-workflow-kit set-autonomy` or by hand). Keep each `.md` file's frontmatter (`type / lastUpdated / scope / staleAfter / owner / maxLines`); the `.json` seeds carry no frontmatter (the docs cap-validator globs `*.md` only, so they are inherently skipped).
|
|
25
25
|
7. **Fill templates** per the table below. **Then materialize the navigator, as the last `docs/ai` step:** the entry point declares `docs/ai/index.md` always-loaded and it is a GENERATED artifact with no template, so run `node ${CLAUDE_SKILL_DIR}/references/scripts/check-docs-size.mjs --ensure-index --root=<project>` and relay its one outcome line (`ensure-index: regenerated` / `already-current`, or a `write-refused` / `probe-failed` line naming the offending path — either is a STOP: resolve it before reporting the deploy, since the navigator the entry point declares does not exist yet). The generator runs from **this skill's own directory**, so a project with **no Node runtime** is covered too: the agent host runs it, and step 8's copy is not a precondition.
|
|
26
|
-
8. **
|
|
27
|
-
9. **Wire / hide** per visibility (see [Visibility contract](${CLAUDE_SKILL_DIR}/references/contracts.md#visibility-contract)).
|
|
28
|
-
- **visible** —
|
|
26
|
+
8. **Offer enforcement on every project, preview-first.** Explain in one line that the dependency-free Node scripts are available because the agent host already runs their step-7 navigator; the only skip condition is that the agent host cannot run them. Preview the source and destination list and ask; on a yes copy `${CLAUDE_SKILL_DIR}/references/scripts/*.mjs` (+ `*.test.mjs`) into the project's `scripts/`. On a no, skip the hook in step 9 and say so; the docs command slots then name no project-local command — fill `env_commands.md` and the `Docs caps + index freshness` row of `AGENTS.md` with the same note: the caps + index gate run from the agent's own skill copy (the step-7 `--root=<project>` form) and the archive rotations need the scripts deployed. Never write the skill's absolute path into a deployed doc. They self-configure (project name from `package.json` or, without one, the directory basename; hierarchical/on-demand sections auto-discovered). The hook calls `node scripts/<x>.mjs` directly, so a committer without `node` on PATH gets the hook's own loud failure.
|
|
27
|
+
9. **Wire / hide** per visibility (see [Visibility contract](${CLAUDE_SKILL_DIR}/references/contracts.md#visibility-contract)). When the user accepts the enforcement offer, install the pre-commit hook: `node scripts/install-git-hooks.mjs`. If the installer reports a pre-existing non-marker hook, stop and ask the user to merge it manually rather than overwriting. Once the gate declaration is final-run-capable, the installer's optional consented `--commit-guard <kit commit-guard.mjs path>` arm binds commits to the green `run-gates --final` receipt (the Recommendations advisor surfaces the exact one-liner).
|
|
28
|
+
- **visible** — when a `package.json` exists and the offer was accepted, wire its scripts (never create one for this); add the minimal `.gitignore` either way. **Do not run the hide tool.**
|
|
29
29
|
- **hidden** — run the kit's hide writer (one managed block in the **project-local** `.git/info/exclude`, covering the kit's own artifacts **and** every known AI/agent tool's footprint — never the machine-global excludes): `node ${CLAUDE_SKILL_DIR}/tools/hide-footprint.mjs --dir <project> --dry-run` **first** (changes nothing, prints the plan), then the same without `--dry-run`. Handle what it surfaces, in plain language (never the tool's internal terms):
|
|
30
30
|
- A path it reports as **already committed** can't be hidden by ignoring it and is **never un-tracked silently** — show the user the printed `git rm --cached <path>`, let them decide, then opt it in with `--include=<path>`.
|
|
31
31
|
- A **present file with a generic name** it flags (e.g. `GEMINI.md`) → ask before `--include=<path>`.
|
|
@@ -58,5 +58,5 @@ Fill strategy:
|
|
|
58
58
|
| `tech_reference.md` | Carry over real configs/patterns found in deps. |
|
|
59
59
|
| `active_plan.md`, `handover.md` | TODO seed (e.g. "Bootstrap session — fill domain sections after first real work"). |
|
|
60
60
|
| `decisions.md` | The HOT ADR window — seed `AD-001` (adopting this memory system), newest at the bottom. |
|
|
61
|
-
| `adr/log.md` | The seed navigator — deployed as-is (the generator over the seeded HOT); regenerated by `archive-decisions.mjs
|
|
61
|
+
| `adr/log.md` | The seed navigator — deployed as-is (the generator over the seeded HOT); regenerated by `archive-decisions.mjs` where the enforcement scripts are deployed (installed on a yes), otherwise it stays the seed — never hand-edited either way. |
|
|
62
62
|
| `known_issues.md`, `changelog.md`, `pages/shared-patterns.md` | Empty template / first bootstrap entry. |
|
|
@@ -7,7 +7,7 @@ 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. **a CONTENT-FREE fingerprint over a DIRTY index** — the payload carries no bytes while the index really carries a change
|
|
10
|
+
2. **a CONTENT-FREE fingerprint over a DIRTY index** — the payload carries no bytes while the index really carries a change: staged content the payload cannot see. No receipt can describe what that commit will carry, so the guard refuses fail-closed. Recovery names no flag or config key: make the staged change visible to `git diff --cached`, then re-run the resolved `run-gates --final`; re-staging alone is not the recovery because the change is already staged;
|
|
11
11
|
3. no completed final record for the CURRENT fingerprint (the tree moved after the final run — any edit re-stales it);
|
|
12
12
|
4. a RED latest attempt (a dead green never revives — the latest attempt at a fingerprint is authoritative);
|
|
13
13
|
5. fingerprint before ≠ after on the receipt (the tree moved UNDER the final run);
|
|
@@ -24,7 +24,7 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/commit-guard.mjs --check [--cwd <dir>]` —
|
|
|
24
24
|
|
|
25
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.
|
|
26
26
|
|
|
27
|
-
**Stated residual — the
|
|
27
|
+
**Stated residual — the payload flags close the config-blind class.** The fingerprint argv in the ONE node home, `core-evidence.mjs`, carries `--no-textconv --ignore-submodules=none` beside `--no-ext-diff`; both bash twins are parity-pinned to that set for their fingerprint and review-payload diffs, so host textconv and submodule-ignore configuration cannot hide staged bytes. Refusal 2 remains the fail-closed backstop for any DIRTY index whose staged content the payload still cannot see: it names making the change visible to `git diff --cached` and re-running the final gate, without guessing a flag or configuration key.
|
|
28
28
|
|
|
29
29
|
**Human residual (stated, accepted):** `git commit --no-verify` bypasses any pre-commit hook — a self-discipline mechanism, not a security boundary.
|
|
30
30
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
### Mode: control-bytes
|
|
2
|
+
|
|
3
|
+
<!-- opt-in-capability: none — a read-only gate entry; declaring the gate matrix is covered by gates-declaration -->
|
|
4
|
+
|
|
5
|
+
Read-only **control-byte gate** over the work tree — it makes *"a raw control byte in a source"* a
|
|
6
|
+
mechanical refusal instead of a reviewer's find. Measured 2026-08-30: an editor tool wrote a raw NUL
|
|
7
|
+
into a test source, git classed the file as binary, and a NUL-carrying blob is exactly what a
|
|
8
|
+
letter-level checker skips by kind. This mode judges BYTES, never letters, so it is language-neutral:
|
|
9
|
+
every consumer of the kit can declare it, whatever language the project is written in.
|
|
10
|
+
|
|
11
|
+
**What it judges.** The review-payload domain over the WORK TREE, from the work-tree root: tracked
|
|
12
|
+
paths (`git ls-files -z`) plus untracked-not-ignored paths (`git ls-files --others --exclude-standard
|
|
13
|
+
-z`). Three surfaces per path, in this order: the NAME is always judged; a regular file's CONTENT is
|
|
14
|
+
judged unless the attribute skip below covers it (the skip covers content only); a symlink's TARGET
|
|
15
|
+
string is always judged and the link is never followed. A path's kind is the index mode for a tracked
|
|
16
|
+
path (`git ls-files -s -z`; a gitlink is name-only) and the lstat for an untracked one; the descriptor
|
|
17
|
+
the read opens must report the same kind, or the path is unreadable. The predicate is over raw bytes:
|
|
18
|
+
any byte in 0x00–0x08, 0x0B, 0x0C, 0x0E–0x1F or 0x7F refuses; TAB, LF and CR are the only admitted C0
|
|
19
|
+
bytes; bytes at or above 0x80 are not judged (UTF-8 validity and C1 controls are not this tool's
|
|
20
|
+
business). A finding names the path, the surface, the byte offset and the byte in hex; the render is
|
|
21
|
+
injective — bytes decode as UTF-8 where valid, a control or line-separator code point (and a valid
|
|
22
|
+
U+FFFD) renders as `\u{XXXX}`, an invalid byte as `\xNN`, a backslash as `\\` — so no output line carries a raw control
|
|
23
|
+
byte or U+FFFD and two names differing only in an unrenderable byte render differently.
|
|
24
|
+
|
|
25
|
+
**Binary by KIND, never by guess.** A path whose git attribute says `binary` or `-diff` has its
|
|
26
|
+
CONTENT skipped by kind and is NAMED in the summary (one `git check-attr --stdin -z binary diff`
|
|
27
|
+
over the whole domain — git's own answer, global and system attributes files included, because the
|
|
28
|
+
tool runs under your env); its name and target are still judged. There is no extension allow-list
|
|
29
|
+
and no size heuristic — a NUL in an unattributed file is the finding this gate exists for. A fixture
|
|
30
|
+
that genuinely needs a control byte keeps it as an escape in source (`\x00`, `String.fromCharCode(0)`):
|
|
31
|
+
the runtime string is byte-identical and the SOURCE stays clean. A never-committable stat class
|
|
32
|
+
(device, FIFO, socket) and an untracked nested repository (git's `nested/` entry) are skipped by
|
|
33
|
+
kind and counted — name judged, no content surface. On a host that materialises symlinks
|
|
34
|
+
(`core.symlinks=false`) a tracked symlink's placeholder file is judged as its target string.
|
|
35
|
+
|
|
36
|
+
Run `node ${CLAUDE_SKILL_DIR}/tools/control-bytes.mjs [--check] [--cwd <dir>]` (`--cwd` anchors at
|
|
37
|
+
that directory's work-tree root):
|
|
38
|
+
|
|
39
|
+
1. Plain run → the human report: the work-tree root, the counts (paths judged · skipped by attribute,
|
|
40
|
+
each named · skipped by kind · findings), then every finding as one line
|
|
41
|
+
`<path> <surface> offset <n>: 0x<hex>` (`(+N more)` when that surface carries more than one) —
|
|
42
|
+
under the SAME exit table as `--check`: the plain run is never a silent 0.
|
|
43
|
+
2. **`--check`** → the gate spelling: the same report (counts, the named attribute skips, the
|
|
44
|
+
finding lines), then the gate exit code. **Exit 0** clean. **Exit 1** on a finding OR on a refusal —
|
|
45
|
+
refusals never pass and never skip silently, each with its own message: an UNREADABLE path
|
|
46
|
+
(EACCES, EIO, a descriptor whose kind disagrees with the named kind — distinct from an ABSENT
|
|
47
|
+
path git named that vanished before the read; a TRACKED absent path with the skip-worktree bit is
|
|
48
|
+
a sparse checkout, skipped by kind and counted); a git command that fails, is killed by a signal or
|
|
49
|
+
is not on PATH; an UNMERGED index; an EMPTY domain; a file over the read cap (16 MiB — read as at
|
|
50
|
+
most cap+1 bytes on the descriptor, never trusting the fstat size, so a file that grows after the
|
|
51
|
+
fstat still refuses; the message names the cap and the path); a git LOCATION that is not a work
|
|
52
|
+
tree. The location is a closed table of six states shared with the fingerprint and index
|
|
53
|
+
consumers through the kit's git-location leaf, judged on the REALPATH identity of the git dir,
|
|
54
|
+
the common dir and the work-tree top under the ambient env against the discovery from cwd with
|
|
55
|
+
every variable whose upper-cased name starts with `GIT_` removed (a case-insensitive host honours
|
|
56
|
+
`git_dir` too): `work-tree` (all three agree) proceeds; `not-a-repository` (git
|
|
57
|
+
itself answers not-a-git-repository, exit 128); `error` (ENOENT, a synchronous throw, a signal, a
|
|
58
|
+
realpath that cannot resolve, any other non-zero or unparsable answer); `redirected` (any of the
|
|
59
|
+
three differs — `GIT_WORK_TREE` at a second tree with the same git dir, or `GIT_COMMON_DIR` at a
|
|
60
|
+
second repository); `no-work-tree` (the git
|
|
61
|
+
dir agrees but `--show-toplevel` fails: bare, or cwd inside `.git`); `env-only` (the ambient env
|
|
62
|
+
reaches a repository the stripped discovery does not) — each refuses by its own state name. A
|
|
63
|
+
pre-commit hook's own env (`GIT_INDEX_FILE`, `GIT_PREFIX`, `GIT_CONFIG_PARAMETERS`,
|
|
64
|
+
`GIT_EXEC_PATH`) agrees and passes: the check is agreement, never a variable ban. **Exit 2**
|
|
65
|
+
usage — every operand is validated at parse; an unknown argument is usage. Every content read is
|
|
66
|
+
descriptor-bound (open, fstat, read on the descriptor), never a path read that a symlink swap
|
|
67
|
+
could redirect.
|
|
68
|
+
3. **Wire it as a gate by hand — never without consent (AD-021/D9).** The candidate line for your
|
|
69
|
+
own `docs/ai/gates.json`: `{ "id": "control-bytes", "title": "No raw control byte in the work
|
|
70
|
+
tree", "cmd": "node \"<path-to-this-skill>/tools/control-bytes.mjs\" --check" }` — with the path
|
|
71
|
+
your project actually reaches the kit by, double-quoted inside the JSON string so a path with
|
|
72
|
+
spaces survives, executable from the project root. `gates-init` does not offer this entry; the full consent-fill contract for the
|
|
73
|
+
matrix is in `${CLAUDE_SKILL_DIR}/references/modes/gates.md`. Once declared, the opt-in
|
|
74
|
+
`${CLAUDE_SKILL_DIR}/references/modes/hook.md` auto-approves it like any other declared gate.
|
|
75
|
+
|
|
76
|
+
**Honest bounds (stated, accepted):** the work tree is judged at the moment of the read — a byte
|
|
77
|
+
written after the run is caught on the next run, which is why the gate belongs in the pre-commit
|
|
78
|
+
matrix; the attribute is the only "binary" signal, so a binary file with no attribute refuses until
|
|
79
|
+
the project declares it; the tool reports and never edits — the remedy is the escape in source; a
|
|
80
|
+
checkout PATH that is itself not valid UTF-8 is unreachable by any Node consumer (cwd is a string
|
|
81
|
+
before git ever answers) and a non-UTF-8 LINKED git dir or common dir path refuses as `error` — a
|
|
82
|
+
stated bound, not a case this tool judges; `O_NOFOLLOW` guards the
|
|
83
|
+
LEAF only — a parent directory swapped for a symlink between git's answer and the open redirects the
|
|
84
|
+
read (Node exposes no `openat2`/component-wise no-follow walk), so a mid-run parent swap is the same
|
|
85
|
+
moment-of-the-read race bound as a byte written after the run: caught on the next run, inside the
|
|
86
|
+
work tree the gate's verdict still refuses-or-reports, never silently skips.
|
|
87
|
+
|
|
88
|
+
**Invariants:** read-only · never writes · never commits · spawns read-only `git` queries only
|
|
89
|
+
(`ls-files`, `check-attr`, `config --get core.symlinks`, `rev-parse`) · judges bytes, never decoded text · skips by kind only, every
|
|
90
|
+
skip named or counted · exit 0 clean / 1 finding or refusal / 2 usage · `main(argv, deps)` returns,
|
|
91
|
+
never exits.
|