@sabaiway/agent-workflow-kit 2.1.0 → 3.0.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 +45 -0
- package/README.md +6 -5
- package/SKILL.md +12 -8
- package/bin/install.mjs +1 -1
- package/bridges/antigravity-cli-bridge/SKILL.md +8 -1
- package/bridges/antigravity-cli-bridge/bin/agy-review-honesty.test.mjs +150 -0
- package/bridges/antigravity-cli-bridge/bin/agy-review-model-screen.test.mjs +76 -0
- package/bridges/antigravity-cli-bridge/bin/agy-review.sh +80 -15
- package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +95 -4
- package/bridges/antigravity-cli-bridge/capability.json +3 -2
- package/bridges/codex-cli-bridge/SKILL.md +8 -1
- package/bridges/codex-cli-bridge/bin/codex-review-honesty.test.mjs +143 -0
- package/bridges/codex-cli-bridge/bin/codex-review.sh +71 -10
- package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +93 -10
- package/bridges/codex-cli-bridge/capability.json +3 -2
- package/capability.json +1 -1
- package/migrations/3.0.0-hardened-core-loop.md +44 -0
- package/migrations/README.md +1 -1
- package/package.json +2 -2
- package/references/hooks/gate-approve.mjs +1 -1
- package/references/modes/bootstrap.md +3 -3
- package/references/modes/commit-guard.md +19 -0
- package/references/modes/core-evidence.md +15 -0
- package/references/modes/coverage-check.md +12 -0
- package/references/modes/doc-parity.md +7 -6
- package/references/modes/gates.md +7 -6
- package/references/modes/grounding.md +1 -2
- package/references/modes/hook.md +1 -1
- package/references/modes/review-state.md +3 -3
- package/references/modes/upgrade.md +6 -6
- package/references/modes/velocity.md +2 -2
- package/references/scripts/archive-decisions.mjs +1 -1
- package/references/scripts/install-git-hooks-repo-exec.test.mjs +82 -0
- package/references/scripts/install-git-hooks.mjs +90 -18
- package/references/scripts/install-git-hooks.test.mjs +102 -0
- package/references/scripts/migrate-gates-branches.test.mjs +157 -0
- package/references/scripts/migrate-gates.mjs +395 -0
- package/references/scripts/migrate-gates.test.mjs +284 -0
- package/references/templates/agent_rules.md +2 -2
- package/tools/ack-write.mjs +1 -1
- package/tools/atomic-write.mjs +2 -2
- package/tools/autonomy-config.mjs +1 -1
- package/tools/autonomy-doctor.mjs +1 -1
- package/tools/autonomy-write.mjs +1 -1
- package/tools/bridge-settings-read.mjs +1 -1
- package/tools/bridge-settings.mjs +1 -1
- package/tools/changed-surface.mjs +8 -69
- package/tools/cheap-agents.mjs +2 -2
- package/tools/commands.mjs +17 -10
- package/tools/commit-guard.mjs +167 -0
- package/tools/core-evidence.mjs +914 -0
- package/tools/coverage-check.mjs +260 -0
- package/tools/delegation.mjs +1 -1
- package/tools/detect-backends.mjs +3 -3
- package/tools/doc-parity.mjs +11 -27
- package/tools/engine-source.mjs +1 -1
- package/tools/family-members.mjs +1 -1
- package/tools/family-registry.mjs +1 -1
- package/tools/fs-safe.mjs +1 -1
- package/tools/gate-hook.mjs +1 -1
- package/tools/{seed-gates.mjs → gates-init.mjs} +58 -52
- package/tools/grounding.mjs +6 -83
- package/tools/hide-footprint.mjs +1 -1
- package/tools/inject-methodology.mjs +1 -1
- package/tools/known-footprint.mjs +1 -1
- package/tools/labels.mjs +1 -1
- package/tools/lcov.mjs +6 -10
- package/tools/lens-region.mjs +1 -1
- package/tools/manifest/validate.mjs +19 -0
- package/tools/migrate-adr-store.mjs +2 -2
- package/tools/orchestration-config.mjs +1 -1
- package/tools/orchestration-write.mjs +1 -1
- package/tools/presentation.mjs +1 -1
- package/tools/procedures.mjs +2 -2
- package/tools/recipes.mjs +60 -9
- package/tools/recommendations.mjs +63 -7
- package/tools/release-scan.mjs +1 -1
- package/tools/renderers.mjs +1 -1
- package/tools/review-state.mjs +288 -340
- package/tools/run-gates.mjs +216 -92
- package/tools/sandbox-masks.mjs +1 -1
- package/tools/semver-lite.mjs +1 -1
- package/tools/set-autonomy.mjs +1 -1
- package/tools/set-recipe.mjs +1 -1
- package/tools/setup-backends.mjs +1 -1
- package/tools/surface.mjs +1 -1
- package/tools/uninstall.mjs +1 -1
- package/tools/velocity-profile.mjs +2 -2
- package/tools/view-model.mjs +1 -1
- package/references/modes/fold-completeness.md +0 -30
- package/references/modes/review-ledger.md +0 -34
- package/references/templates/verification-profile.json +0 -10
- package/tools/fold-completeness-run.mjs +0 -1120
- package/tools/fold-completeness.mjs +0 -672
- package/tools/review-ledger-core.mjs +0 -428
- package/tools/review-ledger-write.mjs +0 -647
- package/tools/review-ledger.mjs +0 -630
- package/tools/sarif.mjs +0 -52
- package/tools/verification-profile.mjs +0 -219
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,51 @@ 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
|
+
## 3.0.0 — strip-the-kit: the hardened computed core replaces the ledger machinery (AD-059)
|
|
8
|
+
|
|
9
|
+
> ### ⚠ BREAKING — the review loop is now computed, not recorded
|
|
10
|
+
>
|
|
11
|
+
> The review-ledger / fold-completeness / verification-profile / sarif / seed-gates tools, their
|
|
12
|
+
> modes, and their git-dir stores are **DELETED**. `run-gates --record` is a usage error (exit 7
|
|
13
|
+
> retired); the loop's ONE receipt is `run-gates --final`. Review receipts without a self-declared
|
|
14
|
+
> `posture` field (any pre-3.0.0/4.0.0 bridge wrapper) **stop satisfying** the review-state gate —
|
|
15
|
+
> fail-closed, like the AD-057 probe marker; re-run the reviews on the refreshed bridges. Node
|
|
16
|
+
> floor is **>= 22** family-wide. The consented `migrate-gates.mjs` migrates an existing
|
|
17
|
+
> `docs/ai/gates.json` (see `migrations/3.0.0-hardened-core-loop.md`).
|
|
18
|
+
|
|
19
|
+
The MAJOR family release (memory **3.0.0** / engine **2.0.0** / bundled codex bridge **3.0.0** /
|
|
20
|
+
bundled antigravity bridge **4.0.0**; deployment-lineage head **3.0.0**). One theme: **every claim
|
|
21
|
+
the loop makes is computed from artifacts it can re-verify — never remembered, never recorded prose.**
|
|
22
|
+
|
|
23
|
+
- **The hardened core (new tools):** `core-evidence.mjs` — the ONE git-dir evidence writer
|
|
24
|
+
(red-proof records with observed-red 3/3 custody + content hashes; explicit per-backend degrade
|
|
25
|
+
records; the stateless end-of-loop `summary`) · `coverage-check.mjs` — the D3(d) final-run
|
|
26
|
+
checker (changed-line lcov coverage + red-proof verification + the `lcov-sha256=` machine line) ·
|
|
27
|
+
`commit-guard.mjs` — the D10 read-only pre-commit (binds a commit to the LATEST green final
|
|
28
|
+
receipt at the exact tree fingerprint; `--no-verify` stays a stated residual).
|
|
29
|
+
- **`run-gates --final`:** canonical realpath-anchored core checks (review-state + coverage-check,
|
|
30
|
+
checker LAST), evidence-store drift teeth, the checker-bound lcov sha with an end re-hash, ONE
|
|
31
|
+
attempt-linked receipt (green/red DERIVED, integrity failures explicit), `AW_GIT_DIR` exported
|
|
32
|
+
to every gate child on every run.
|
|
33
|
+
- **`review-state` D3(b):** ship-class-only on the latest NORMAL receipt; a recognized negative is
|
|
34
|
+
an authoritative VETO; unknown verdicts, probe-marked, unmarked, posture-less, and malformed
|
|
35
|
+
receipts never attest (each with its stated recovery); explicit degrade records are the only
|
|
36
|
+
escape and never all backends.
|
|
37
|
+
- **Consumer lanes:** `gates-init.mjs` (D9 consented fill preview; the coverage-check candidate
|
|
38
|
+
appended LAST so a whole-offer apply is final-run-ready) · `migrate-gates.mjs` (D8, mirrored
|
|
39
|
+
from the memory canon: canonical-anchor matching with move/collision semantics, the full lcov
|
|
40
|
+
reporter flag set, retired-store cleanup, symlink/control-byte STOPs) · the hook installer's
|
|
41
|
+
explicit `--commit-guard <path>` arm with strict carry-forward · a `commit-guard`
|
|
42
|
+
Recommendations item gated on final-run-capability.
|
|
43
|
+
- **Bundled bridges (D4/D5):** a verdict-less review run exits 4 with NO receipt (exact agy
|
|
44
|
+
`### Verdict` parse; structural top-level JSON verdict in codex schema mode); one stderr banner
|
|
45
|
+
states the ACTUAL dispatch posture and every receipt records the same `posture{}`; control-byte
|
|
46
|
+
and unknowable-model postures refuse pre-spend. Manifests carry VALIDATED posture pins; the kit
|
|
47
|
+
renders the CONFIGURED posture (pins + the bridge-settings tier overlay) in the backend status
|
|
48
|
+
line, drift-guarded end to end (`posture-parity`).
|
|
49
|
+
- **Suite economics note:** the supervisory ledger cluster (~98% of the old 94s unit matrix) is
|
|
50
|
+
gone with its machinery; the dedicated speedup pass (plan Phase 5) follows post-release.
|
|
51
|
+
|
|
7
52
|
## 2.1.0 — agy code review fails CLOSED pre-spend: grounded facts required, `--ungrounded` is the explicit escape (AD-058)
|
|
8
53
|
|
|
9
54
|
> ### ⚠ the bundled antigravity bridge takes MAJOR 3.0.0 — bare `agy-review code` now refuses
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ decisions. Works with Claude Code, Codex, Cursor, and any agent that reads `AGEN
|
|
|
13
13
|
[](./LICENSE)
|
|
14
14
|
[](https://nodejs.org)
|
|
15
15
|
|
|
16
|
-
`Node ≥
|
|
16
|
+
`Node ≥ 22` · `dependency-free scripts` · `no telemetry in family code`
|
|
17
17
|
|
|
18
18
|
**One command to start:**
|
|
19
19
|
|
|
@@ -229,7 +229,7 @@ file), or run the guarded `/agent-workflow-kit uninstall`.
|
|
|
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). |
|
|
232
|
-
| `/agent-workflow-kit gates` | any time | **project gate runner** — runs the verification commands **your project itself declares** in `docs/ai/gates.json` (seeded at deploy; hand-editable `{ id, title, cmd }` entries, each `cmd` one bash line) as one batch: a per-gate **PASS/FAIL table** + one machine-readable summary line, exit 0 iff all green; a failing gate's own output is shown verbatim; `--only <id>` re-runs one. Honest distinct outcomes for a missing / empty / malformed declaration — never a silent green. The runner writes nothing **by default** and never commits — opt-in `--
|
|
232
|
+
| `/agent-workflow-kit gates` | any time | **project gate runner** — runs the verification commands **your project itself declares** in `docs/ai/gates.json` (seeded at deploy; hand-editable `{ id, title, cmd }` entries, each `cmd` one bash line) as one batch: a per-gate **PASS/FAIL table** + one machine-readable summary line, exit 0 iff all green; a failing gate's own output is shown verbatim; `--only <id>` re-runs one. Honest distinct outcomes for a missing / empty / malformed declaration — never a silent green. The runner writes nothing **by default** and never commits — opt-in `--final` runs the FULL declared matrix and mints ONE final-run receipt into the git-dir core-evidence store (status green/red, pre/post tree fingerprints, the declaration, the consumed lcov's sha; the receipt the commit-guard binds); it executes only your own declared commands (a batching convenience, not a sandbox). The velocity tier auto-approves only the exact no-`--final` form — the recording run stays explicit. |
|
|
233
233
|
| `/agent-workflow-kit setup [backend]` | opt-in, any time | **link-only** auto-setup of a bridge: places the bundled bridge skill (only into an absent / empty / managed dir — never overwrites an unmanaged one) + links its wrappers onto `PATH` via managed symlinks (idempotent; refuses to clobber a non-symlink; try `--dry-run` to preview). The binary install + the one-time subscription login stay **manual**: it prints the exact **login** command and points the binary install at each bridge's `setup/README.md`. POSIX wrappers — on Windows use WSL. Never commits, never runs a subscription CLI. |
|
|
234
234
|
| `/agent-workflow-kit status` | any time | **read-only** single view of **versions + deployment + settings + bridges**: which members (kit / memory / engine / the two bridges) are installed and at what version (with an honest "installed on this machine" note when one is behind) and — in a project — what's deployed (`docs/ai`, the version stamps, and the **visibility**: visible / hidden / unclear), plus your settings (orchestration recipes, attribution, velocity) and the bridges' readiness. The two version axes (package number vs deployment-structure head) stay decoupled. Never writes, never commits, never runs a subscription CLI. |
|
|
235
235
|
| `/agent-workflow-kit recipes` | any time | **read-only** orchestration advisor: presents four named recipes for composing the bridges into plan → execute → review — **Solo / Reviewed / Council / Delegated** — plans + recommends one for your environment (degrading with a stated reason when a backend isn't ready), and offers the choice. The orchestrator runs it via the bridge skills and **always commits**; the kit never executes a recipe, never runs a subscription CLI, never commits. |
|
|
@@ -237,9 +237,10 @@ file), or run the guarded `/agent-workflow-kit uninstall`.
|
|
|
237
237
|
| `/agent-workflow-kit set-recipe` | any time | **config writer** for `docs/ai/orchestration.json`: tell the agent your preference in plain language and it maps it to explicit `--set <activity>.<slot>=<recipe>` / `--unset` ops; the kit validates, **previews by default**, and writes only on `--write` (deployment-gated, atomic, symlink/TOCTOU-safe), resolving the effective recipe vs live readiness. Writes **only** that file — **never runs a backend, never commits**; hand-editing stays fully supported. |
|
|
238
238
|
| `/agent-workflow-kit review-state` | any time | **read-only review-receipt checker** — makes "reviewed ≠ shipped" detectable: the bridge review wrappers append a receipt per successful review (into a file inside the git dir — never committable); this checks that every backend your configured `plan-execution.review` recipe names holds a **fresh, grounded receipt for the current uncommitted tree** (any later edit moves the fingerprint and stales the receipt; a review continuation never re-attests a folded tree). `--check` gives a gate exit code to declare in `docs/ai/gates.json` **by hand or via the consent-gated seeder** (preview → your explicit yes; never without consent). `--await [--timeout <s>]` (AD-049) BLOCKS until every recipe-named backend has receipted the current tree — the durable completion signal is the receipt, never a process event — so you wait for the bridges instead of hand-polling. Never writes, never commits, never runs a subscription CLI; it spawns read-only `git` queries to fingerprint the tree — and `git commit --no-verify` stays possible (discipline, not a sandbox). |
|
|
239
239
|
| `/agent-workflow-kit sandbox-masks` | any time | **cosmetic exclude lane for sandbox device masks** — an OS sandbox (Claude Code) injects character-device masks into the work tree as untracked `git status` noise; the review domain already ignores them **by construction** (never-committable untracked classes — char/block devices, FIFOs, sockets — are excluded from the fingerprint, the assembled review payload, and the clean checks). This mode hides them from `git status` too: flagless = read-only probe (derives the CURRENT mask set from the unfiltered walk + lstat — never a frozen list — and revalidates fenced entries, loudly flagging one that became a real path); `--apply` = consent-gated FULL-BLOCK replace of its own fenced block in `git rev-parse --git-path info/exclude` (stale masks drop by construction; `--clear` always means REMOVE the block — it takes precedence over the derivation). Writes ONLY its fence — never `.gitignore`, never global config; symlinked/non-regular exclude paths and malformed fences fail closed. Watch note: a real file at an excluded path is silently skipped by bulk staging (`git add -A`/`git add .`) — delete the stale line first; the probe flags exactly this case. |
|
|
240
|
-
| `/agent-workflow-kit grounding` | any time | **grounded-review facts assembler** — mechanizes populating `agy-review --facts @f`: slices your entry-point's **Hard Constraints** section verbatim (exactly one match, else a loud stop) and/or a plan's decision-bearing sections (`## Approach` + `## Verification` required, `## Decisions (locked)` when present; duplicates stop), under the same byte budget the agy wrapper enforces (minus `--reserve-bytes` for the artifact share), with a loud tail-trim on overflow. `--autonomy` (AD-044) appends the COMPUTED effective autonomy policy from the git-top `docs/ai/autonomy.json` (every red-line + per-activity level, stated source line; absent file → the computed defaults ARE the policy, exit 0; a malformed policy fails CLOSED, exit 1).
|
|
241
|
-
| `/agent-workflow-kit
|
|
242
|
-
| `/agent-workflow-kit
|
|
240
|
+
| `/agent-workflow-kit grounding` | any time | **grounded-review facts assembler** — mechanizes populating `agy-review --facts @f`: slices your entry-point's **Hard Constraints** section verbatim (exactly one match, else a loud stop) and/or a plan's decision-bearing sections (`## Approach` + `## Verification` required, `## Decisions (locked)` when present; duplicates stop), under the same byte budget the agy wrapper enforces (minus `--reserve-bytes` for the artifact share), with a loud tail-trim on overflow. `--autonomy` (AD-044) appends the COMPUTED effective autonomy policy from the git-top `docs/ai/autonomy.json` (every red-line + per-activity level, stated source line; absent file → the computed defaults ARE the policy, exit 0; a malformed policy fails CLOSED, exit 1). Prints to stdout; `--out` writes **one scratch file only** — system-temp outside the repo ($TMPDIR / /tmp, rewritable) or a **fresh** gitignored in-repo path (create-only, exclusive write; an existing in-repo file, even gitignored, is refused — the `.env` clobber class); tracked, not-ignored-in-repo, other outside-repo, and symlink/non-regular destinations are all refused. Never commits, never runs a subscription CLI. |
|
|
241
|
+
| `/agent-workflow-kit core-evidence` | any time | **the ONE loop-evidence writer** (strip-the-kit) — every core evidence record lands in a single append-only JSONL store inside the git dir (never committable; versioned schema, latest-per-key supersession, byte-identical duplicates refused, malformed lines fail every reader closed). `red-proof "<file>#<pattern>"` declares an observed-red **BEFORE a bugfix** (N/N red runs + content custody + base + the pre-fix fingerprint; green/mixed/timeout are DISTINGUISHED refusals — nothing written); `degrade --backend --reason` is the ONLY escape for an unavailable review backend (per-tree, never all backends); `summary` renders the whole loop state statelessly (gate result · per-backend verdicts · red-proofs · degrades) — no ledger, no rounds, nothing remembered. Honest residual: records are forgeable — self-discipline, not a security boundary. Never commits, never runs a subscription CLI. |
|
|
242
|
+
| `/agent-workflow-kit coverage-check` | any time | **the final-run checker** (D3(c)+(d)) — reads the lcov the declared `unit-tests` gate produced at the FIXED git-dir path and fails on any uncovered CHANGED executable Node line (listed `file:line`; a changed file absent from the map is a file-level red; out-of-domain/unsupported files are LISTED — the claim narrowed honestly); VERIFIES every current-base red-proof declaration (bound test exists · custody hash unchanged · green N/N now · pre-fix fingerprint differs); prints `lcov-sha256=<hex|none>` of the exact bytes it consumed — the sha the `--final` receipt binds and re-hashes. An absent lcov is a LOUD `skipped-no-lcov`; a symlinked path is a refusal. `--check` is the gate exit code — declare it as the LAST gate (`run-gates --final` refuses otherwise). Read-only. |
|
|
243
|
+
| `/agent-workflow-kit commit-guard` | any time | **the read-only pre-commit guard** (D10) — 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. |
|
|
243
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 (sandbox-lane only) · 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. |
|
|
244
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. |
|
|
245
246
|
| `/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. |
|
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: '3.0.0'
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# agent-workflow-kit
|
|
@@ -71,14 +71,14 @@ Pick the mode from the user's invocation — the mapping is pinned by `tools/com
|
|
|
71
71
|
Before acting, read `docs/ai/.workflow-version` (the project's stamp) to decide the route — an **internal** routing decision, **not** a line you print (version display: *Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`). Route:
|
|
72
72
|
|
|
73
73
|
- **absent** → bootstrap (a fresh deployment).
|
|
74
|
-
- **stamp < `
|
|
75
|
-
- **stamp == `
|
|
74
|
+
- **stamp < `3.0.0`** (the deployment-lineage head) → `upgrade`.
|
|
75
|
+
- **stamp == `3.0.0`** → already current; only the stamp-independent reconciles may run — the FULL set lives in `${CLAUDE_SKILL_DIR}/references/modes/upgrade.md` step 3; run step 3 (never enumerate the reconciles from memory).
|
|
76
76
|
- **stamp > head / unparseable** → STOP — never-downgrade gate (see `${CLAUDE_SKILL_DIR}/references/modes/upgrade.md` step 2).
|
|
77
77
|
- **Exempt:** `autonomy-doctor` provisions the OS, not the deployment — stamp-independent (stated in its mode file).
|
|
78
78
|
|
|
79
79
|
**Two independent version axes — never conflate them:**
|
|
80
80
|
|
|
81
|
-
1. **Project deployment** — `docs/ai/.workflow-version` vs the lineage head (`
|
|
81
|
+
1. **Project deployment** — `docs/ai/.workflow-version` vs the lineage head (`3.0.0`). This is the **only** axis this skill compares.
|
|
82
82
|
2. **Kit freshness** — this skill's own files vs the published npm package. That is the **npx installer's** job: `npx @sabaiway/agent-workflow-kit@latest init` (it refuses a stale-cache downgrade by comparing the version on disk — **no network**). This skill never checks npm, and the package version is **not** the lineage head.
|
|
83
83
|
|
|
84
84
|
**Refreshed the kit but nothing changed?** After the npx installer updates `~/.claude/skills/agent-workflow-kit/`, **restart the session** so the agent reloads the new skill files.
|
|
@@ -171,13 +171,17 @@ read-only — read `${CLAUDE_SKILL_DIR}/references/modes/review-state.md` before
|
|
|
171
171
|
|
|
172
172
|
writer — read `${CLAUDE_SKILL_DIR}/references/modes/grounding.md` before acting.
|
|
173
173
|
|
|
174
|
-
### Mode:
|
|
174
|
+
### Mode: core-evidence
|
|
175
175
|
|
|
176
|
-
writer — read `${CLAUDE_SKILL_DIR}/references/modes/
|
|
176
|
+
writer — read `${CLAUDE_SKILL_DIR}/references/modes/core-evidence.md` before acting.
|
|
177
177
|
|
|
178
|
-
### Mode:
|
|
178
|
+
### Mode: coverage-check
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
project-exec — read `${CLAUDE_SKILL_DIR}/references/modes/coverage-check.md` before acting.
|
|
181
|
+
|
|
182
|
+
### Mode: commit-guard
|
|
183
|
+
|
|
184
|
+
read-only — read `${CLAUDE_SKILL_DIR}/references/modes/commit-guard.md` before acting.
|
|
181
185
|
|
|
182
186
|
### Mode: doc-parity
|
|
183
187
|
|
package/bin/install.mjs
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
// them from this package's own bundled copies — a purely LOCAL copy, no third server contact —
|
|
29
29
|
// skippable with `--no-bridges`. No tracking either way.
|
|
30
30
|
//
|
|
31
|
-
// Dependency-free, Node >=
|
|
31
|
+
// Dependency-free, Node >= 22.
|
|
32
32
|
|
|
33
33
|
import { readFile, mkdir, rm } from 'node:fs/promises';
|
|
34
34
|
import { existsSync, lstatSync, realpathSync } from 'node:fs';
|
|
@@ -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 no-JSON headless behaviour.
|
|
4
4
|
metadata:
|
|
5
|
-
version: '
|
|
5
|
+
version: '4.0.0'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# antigravity-cli-bridge
|
|
@@ -132,6 +132,13 @@ agy-review diff <diff-file> [--facts @f] … # review a supplied diff
|
|
|
132
132
|
agy-review --continue --decided @round1.md --focus "still-open items" # round-2 delta, no re-assembly
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
+
**Honesty + posture (D4/D5):** a run with no recognized `### Verdict` section — empty output
|
|
136
|
+
included — **exits 4 with NO receipt**: treat it as a *failed review to re-run*, never a fatal
|
|
137
|
+
session error. One stderr banner states the actual posture (`review posture: model=…`) and the
|
|
138
|
+
receipt records the same `posture {model}`; an attesting review with `AGY_MODEL` explicitly
|
|
139
|
+
emptied refuses pre-spend (`AGY_PROBE=1` exempt), and control bytes in a model string refuse
|
|
140
|
+
pre-spend in every mode.
|
|
141
|
+
|
|
135
142
|
Frontier default `Gemini 3.1 Pro (High)`; **any** model is allowed (a sub-frontier one earns a
|
|
136
143
|
silenceable `AGY_PROBE=1` advisory). An oversized `code` review trips the byte ceiling with trim/split
|
|
137
144
|
guidance; `AGY_REVIEW_ALLOW_ADDDIR=1` offloads only the change set to a private `--add-dir` staging dir
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// agy-review-honesty.test.mjs — the round-1 hardening pins for the D4/D5 arms (strip Phase 4):
|
|
2
|
+
// the verdict parse is EXACT (an inexact heading or a token buried mid-line never attests —
|
|
3
|
+
// `NOT SHIP` must not read as SHIP), and the empty-AGY_MODEL pre-spend refusal is scoped to the
|
|
4
|
+
// ATTESTING branch only (plan / diff / --ungrounded code never attest, so they run and record
|
|
5
|
+
// posture.model null). Colocated separately from agy-review.test.mjs — that file is
|
|
6
|
+
// red-proof-frozen; this one carries its own minimal standalone harness (the family idiom).
|
|
7
|
+
|
|
8
|
+
import { describe, it } from 'node:test';
|
|
9
|
+
import assert from 'node:assert/strict';
|
|
10
|
+
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, chmodSync, existsSync, readdirSync, symlinkSync } from 'node:fs';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { join, dirname, resolve } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { spawnSync } from 'node:child_process';
|
|
15
|
+
|
|
16
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const WRAPPER = join(HERE, 'agy-review.sh');
|
|
18
|
+
|
|
19
|
+
const FAKE_AGY = [
|
|
20
|
+
'#!/usr/bin/env bash',
|
|
21
|
+
'set -u',
|
|
22
|
+
'printf invoked > "${AGY_FAKE_SENTINEL:-/dev/null}"',
|
|
23
|
+
'printf "%s\\n" "${AGY_FAKE_OUTPUT:-### Verdict}"',
|
|
24
|
+
'exit 0',
|
|
25
|
+
'',
|
|
26
|
+
].join('\n');
|
|
27
|
+
|
|
28
|
+
const makePathWithout = (root, exclude = []) => {
|
|
29
|
+
const skip = new Set(exclude);
|
|
30
|
+
const dir = mkdtempSync(join(root, 'nobin-'));
|
|
31
|
+
for (const d of (process.env.PATH || '').split(':').filter(Boolean)) {
|
|
32
|
+
let names;
|
|
33
|
+
try { names = readdirSync(d); } catch { continue; }
|
|
34
|
+
for (const name of names) {
|
|
35
|
+
if (skip.has(name)) continue;
|
|
36
|
+
const link = join(dir, name);
|
|
37
|
+
if (existsSync(link)) continue;
|
|
38
|
+
try { symlinkSync(resolve(d, name), link); } catch { /* dup / race — ignore */ }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return dir;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const makeSandbox = () => {
|
|
45
|
+
const home = mkdtempSync(join(tmpdir(), 'agy-honesty-'));
|
|
46
|
+
const bin = join(home, '.local', 'bin');
|
|
47
|
+
mkdirSync(bin, { recursive: true });
|
|
48
|
+
writeFileSync(join(bin, 'agy'), FAKE_AGY, { mode: 0o755 });
|
|
49
|
+
chmodSync(join(bin, 'agy'), 0o755);
|
|
50
|
+
const repo = join(home, 'repo');
|
|
51
|
+
mkdirSync(repo);
|
|
52
|
+
const g = (...args) => spawnSync('git', args, { cwd: repo, encoding: 'utf8' });
|
|
53
|
+
g('init', '-q');
|
|
54
|
+
g('config', 'user.email', 'probe@example.com');
|
|
55
|
+
g('config', 'user.name', 'probe');
|
|
56
|
+
writeFileSync(join(repo, 'base.txt'), 'committed base\n');
|
|
57
|
+
g('add', '-A');
|
|
58
|
+
g('commit', '-qm', 'base');
|
|
59
|
+
writeFileSync(join(repo, 'pending.txt'), 'PENDING\n');
|
|
60
|
+
writeFileSync(join(repo, 'plan.md'), '# a plan artifact\n');
|
|
61
|
+
return { home, bin, repo };
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const run = (sb, { args, env = {} } = {}) => {
|
|
65
|
+
const sentinel = join(sb.home, 'cap-sentinel');
|
|
66
|
+
const r = spawnSync('bash', [WRAPPER, ...args], {
|
|
67
|
+
cwd: sb.repo,
|
|
68
|
+
encoding: 'utf8',
|
|
69
|
+
timeout: 30000,
|
|
70
|
+
env: {
|
|
71
|
+
HOME: sb.home,
|
|
72
|
+
PATH: `${sb.bin}:${makePathWithout(sb.home, ['agy', 'agy-run'])}`,
|
|
73
|
+
TMPDIR: process.env.TMPDIR ?? '/tmp',
|
|
74
|
+
AGY_FAKE_SENTINEL: sentinel,
|
|
75
|
+
...env,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
return { ...r, invoked: existsSync(sentinel) };
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const readReceipts = (repo) => {
|
|
82
|
+
const p = join(repo, '.git', 'agent-workflow-review-receipts.jsonl');
|
|
83
|
+
if (!existsSync(p)) return [];
|
|
84
|
+
return readFileSync(p, 'utf8').trim().split('\n').filter(Boolean).map((l) => JSON.parse(l));
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
describe('agy-review — the verdict parse is EXACT (M1): a buried token never attests', () => {
|
|
88
|
+
it('`NOT SHIP` on the verdict line is a FAILED run — never a SHIP receipt', () => {
|
|
89
|
+
const sb = makeSandbox();
|
|
90
|
+
const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: '### Verdict\nNOT SHIP — blocked.' } });
|
|
91
|
+
const receipts = readReceipts(sb.repo);
|
|
92
|
+
rmSync(sb.home, { recursive: true, force: true });
|
|
93
|
+
assert.notEqual(r.status, 0, 'NOT SHIP must never read as SHIP');
|
|
94
|
+
assert.equal(receipts.length, 0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('an INEXACT heading (`### Verdicts`) never parses — the D4 failed-run arm owns it', () => {
|
|
98
|
+
const sb = makeSandbox();
|
|
99
|
+
const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: '### Verdicts\nSHIP — but under the wrong heading.' } });
|
|
100
|
+
const receipts = readReceipts(sb.repo);
|
|
101
|
+
rmSync(sb.home, { recursive: true, force: true });
|
|
102
|
+
assert.notEqual(r.status, 0);
|
|
103
|
+
assert.equal(receipts.length, 0);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('a WORD-PREFIX (`SHIPPING it`) never matches; punctuation forms (`SHIP.` / `SHIP — …`) do', () => {
|
|
107
|
+
const sb1 = makeSandbox();
|
|
108
|
+
const bad = run(sb1, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_OUTPUT: '### Verdict\nSHIPPING it today.' } });
|
|
109
|
+
const badReceipts = readReceipts(sb1.repo);
|
|
110
|
+
rmSync(sb1.home, { recursive: true, force: true });
|
|
111
|
+
assert.notEqual(bad.status, 0, 'SHIPPING is not in the closed vocabulary');
|
|
112
|
+
assert.equal(badReceipts.length, 0);
|
|
113
|
+
for (const [out, want] of [['### Verdict\nSHIP.', 'SHIP'], ['### Verdict\nSHIP — clean.', 'SHIP'], ['### Verdict\nSHIP WITH NITS — two nits.', 'SHIP WITH NITS']]) {
|
|
114
|
+
const sb = makeSandbox();
|
|
115
|
+
const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_OUTPUT: out } });
|
|
116
|
+
const receipts = readReceipts(sb.repo);
|
|
117
|
+
rmSync(sb.home, { recursive: true, force: true });
|
|
118
|
+
assert.equal(r.status, 0, r.stderr);
|
|
119
|
+
assert.equal(receipts[0].verdict, want);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('agy-review — the empty-model refusal is scoped to the ATTESTING branch (M3)', () => {
|
|
125
|
+
it('plan and diff modes with AGY_MODEL= RUN (never attest) and record posture.model null', () => {
|
|
126
|
+
for (const args of [['plan', 'plan.md'], ['diff', 'plan.md']]) {
|
|
127
|
+
const sb = makeSandbox();
|
|
128
|
+
const r = run(sb, { args, env: { AGY_MODEL: '', AGY_FAKE_OUTPUT: '### Verdict\nSHIP — fine.' } });
|
|
129
|
+
const receipts = readReceipts(sb.repo);
|
|
130
|
+
rmSync(sb.home, { recursive: true, force: true });
|
|
131
|
+
assert.equal(r.status, 0, `${args[0]} must run with an emptied model: ${r.stderr}`);
|
|
132
|
+
assert.equal(r.invoked, true);
|
|
133
|
+
assert.deepEqual(receipts[0].posture, { model: null }, 'an unknowable model is recorded null');
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('code --ungrounded with AGY_MODEL= RUNS (its receipt never attests); grounded code still refuses', () => {
|
|
138
|
+
const sb = makeSandbox();
|
|
139
|
+
const un = run(sb, { args: ['code', '--ungrounded'], env: { AGY_MODEL: '', AGY_FAKE_OUTPUT: '### Verdict\nSHIP — throwaway.' } });
|
|
140
|
+
const unReceipts = readReceipts(sb.repo);
|
|
141
|
+
rmSync(sb.home, { recursive: true, force: true });
|
|
142
|
+
assert.equal(un.status, 0, un.stderr);
|
|
143
|
+
assert.deepEqual(unReceipts[0].posture, { model: null });
|
|
144
|
+
const sb2 = makeSandbox();
|
|
145
|
+
const grounded = run(sb2, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: '' } });
|
|
146
|
+
rmSync(sb2.home, { recursive: true, force: true });
|
|
147
|
+
assert.notEqual(grounded.status, 0, 'the ATTESTING branch still refuses pre-spend');
|
|
148
|
+
assert.equal(grounded.invoked, false);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// agy-review-model-screen.test.mjs — the round-2 ordering pin (strip Phase 4, M6): the
|
|
2
|
+
// control-byte screen fires IMMEDIATELY after AGY_MODEL resolution — BEFORE the off-frontier
|
|
3
|
+
// advisory (or any other interpolation) can echo raw control bytes into stderr/the terminal.
|
|
4
|
+
// Colocated separately: both earlier agy spec files are red-proof-frozen. Standalone harness.
|
|
5
|
+
|
|
6
|
+
import { describe, it } from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, chmodSync, existsSync, readdirSync, symlinkSync } from 'node:fs';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { join, dirname, resolve } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { spawnSync } from 'node:child_process';
|
|
13
|
+
|
|
14
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const WRAPPER = join(HERE, 'agy-review.sh');
|
|
16
|
+
|
|
17
|
+
const FAKE_AGY = '#!/usr/bin/env bash\nset -u\nprintf invoked > "${AGY_FAKE_SENTINEL:-/dev/null}"\nprintf "### Verdict\\nSHIP\\n"\nexit 0\n';
|
|
18
|
+
|
|
19
|
+
const makePathWithout = (root, exclude = []) => {
|
|
20
|
+
const skip = new Set(exclude);
|
|
21
|
+
const dir = mkdtempSync(join(root, 'nobin-'));
|
|
22
|
+
for (const d of (process.env.PATH || '').split(':').filter(Boolean)) {
|
|
23
|
+
let names;
|
|
24
|
+
try { names = readdirSync(d); } catch { continue; }
|
|
25
|
+
for (const name of names) {
|
|
26
|
+
if (skip.has(name)) continue;
|
|
27
|
+
const link = join(dir, name);
|
|
28
|
+
if (existsSync(link)) continue;
|
|
29
|
+
try { symlinkSync(resolve(d, name), link); } catch { /* dup / race — ignore */ }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return dir;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe('agy-review — the control-byte screen precedes EVERY interpolation of AGY_MODEL (M6)', () => {
|
|
36
|
+
it('a control-byte NON-frontier model refuses pre-spend with NO advisory echo and NO raw byte on stderr', () => {
|
|
37
|
+
const home = mkdtempSync(join(tmpdir(), 'agy-model-screen-'));
|
|
38
|
+
const bin = join(home, '.local', 'bin');
|
|
39
|
+
mkdirSync(bin, { recursive: true });
|
|
40
|
+
writeFileSync(join(bin, 'agy'), FAKE_AGY, { mode: 0o755 });
|
|
41
|
+
chmodSync(join(bin, 'agy'), 0o755);
|
|
42
|
+
const repo = join(home, 'repo');
|
|
43
|
+
mkdirSync(repo);
|
|
44
|
+
const g = (...args) => spawnSync('git', args, { cwd: repo, encoding: 'utf8' });
|
|
45
|
+
g('init', '-q');
|
|
46
|
+
g('config', 'user.email', 'probe@example.com');
|
|
47
|
+
g('config', 'user.name', 'probe');
|
|
48
|
+
writeFileSync(join(repo, 'base.txt'), 'base\n');
|
|
49
|
+
g('add', '-A');
|
|
50
|
+
g('commit', '-qm', 'base');
|
|
51
|
+
writeFileSync(join(repo, 'pending.txt'), 'PENDING\n');
|
|
52
|
+
const sentinel = join(home, 'cap-sentinel');
|
|
53
|
+
const r = spawnSync('bash', [WRAPPER, 'code', '--facts', 'a tiny fact'], {
|
|
54
|
+
cwd: repo,
|
|
55
|
+
encoding: 'utf8',
|
|
56
|
+
timeout: 30000,
|
|
57
|
+
env: {
|
|
58
|
+
HOME: home,
|
|
59
|
+
PATH: `${bin}:${makePathWithout(home, ['agy', 'agy-run'])}`,
|
|
60
|
+
TMPDIR: process.env.TMPDIR ?? '/tmp',
|
|
61
|
+
AGY_FAKE_SENTINEL: sentinel,
|
|
62
|
+
AGY_MODEL: `hostile${String.fromCharCode(27)}model`, // ESC — a terminal-control byte
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
const invoked = existsSync(sentinel);
|
|
66
|
+
const receiptsPath = join(repo, '.git', 'agent-workflow-review-receipts.jsonl');
|
|
67
|
+
const receipts = existsSync(receiptsPath) ? readFileSync(receiptsPath, 'utf8').trim() : '';
|
|
68
|
+
rmSync(home, { recursive: true, force: true });
|
|
69
|
+
assert.notEqual(r.status, 0, 'a control-byte model refuses pre-spend');
|
|
70
|
+
assert.equal(invoked, false, 'agy is never invoked');
|
|
71
|
+
assert.equal(receipts, '', 'no receipt is minted');
|
|
72
|
+
assert.match(r.stderr, /control/i, 'named as the control-byte class');
|
|
73
|
+
assert.doesNotMatch(r.stderr, /non-frontier model/, 'the advisory never fires on a value the screen must refuse');
|
|
74
|
+
assert.ok(!r.stderr.includes(String.fromCharCode(27)), 'no raw control byte ever reaches stderr');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -98,14 +98,28 @@ Receipt:
|
|
|
98
98
|
payload; a continuation receipt is fresh:false (informational-only — it cannot attest the
|
|
99
99
|
folded tree); probe = whether the run relaxed the quality guards (AGY_PROBE=1), written on EVERY
|
|
100
100
|
receipt so it self-declares — the kit's review-state gate rejects a probe-marked receipt (a probe
|
|
101
|
-
review never attests) and equally rejects an unmarked one (silence is not a declaration);
|
|
102
|
-
|
|
101
|
+
review never attests) and equally rejects an unmarked one (silence is not a declaration);
|
|
102
|
+
posture = the ACTUAL run posture {model} (agy has no tier), written on EVERY receipt (D5) — the
|
|
103
|
+
gate rejects a receipt with an absent/invalid posture (a pre-D5 wrapper minted it; re-run the
|
|
104
|
+
review), one stderr banner line states the same posture, an ATTESTING review with AGY_MODEL
|
|
105
|
+
explicitly emptied refuses pre-spend, and a model string carrying control bytes refuses
|
|
106
|
+
pre-spend in every mode; a run whose output carries NO recognized '### Verdict' section — empty
|
|
107
|
+
output included — exits 4 with NO receipt (D4: a FAILED review to RE-RUN, never a fatal session
|
|
108
|
+
error); a write failure warns, never fails the review
|
|
103
109
|
|
|
104
110
|
Settings file (KEY=VALUE, parsed never sourced; env wins over file, file wins over built-in default):
|
|
105
111
|
${XDG_CONFIG_HOME:-~/.config}/agent-workflow/bridge-settings.conf
|
|
106
112
|
AGY_HARD_TIMEOUT — hard wall-clock cap, duration string like 5m/30m/90s (built-in default 30m)
|
|
107
113
|
AGY_REVIEW_ALLOW_ADDDIR — boolean 0/1: 1 arms the oversized --add-dir escape (re-enables the Issue-001 stall risk; default 0)
|
|
108
114
|
|
|
115
|
+
Honesty + posture (D4/D5):
|
|
116
|
+
a run whose output carries NO recognized '### Verdict' section — empty output included — exits 4
|
|
117
|
+
with NO receipt: a FAILED review to RE-RUN, never a fatal session error. One stderr banner line
|
|
118
|
+
states the ACTUAL run posture (review posture: model=…) and the receipt records the same
|
|
119
|
+
posture {model} (agy has no tier). An ATTESTING review with AGY_MODEL explicitly emptied
|
|
120
|
+
refuses pre-spend (the actual model would be unknowable; AGY_PROBE=1 is exempt), and a model
|
|
121
|
+
string carrying control bytes refuses pre-spend in every mode.
|
|
122
|
+
|
|
109
123
|
Closed grammar: unknown flags are rejected; no '--' passthrough (the flag escape is --ungrounded; the env escapes are AGY_PROBE=1 and AGY_REVIEW_ALLOW_ADDDIR=1).
|
|
110
124
|
Requires at run time: the agy CLI on PATH + a Google AI subscription login (--help needs neither).
|
|
111
125
|
HELP
|
|
@@ -225,9 +239,15 @@ DEFAULT_AGY_REVIEW_MODEL="Gemini 3.1 Pro (High)"
|
|
|
225
239
|
# Review-receipt identity (AD-038). AW_BRIDGE_VERSION mirrors this bridge's SKILL.md/capability.json
|
|
226
240
|
# version (drift-guarded by agy-review.test.mjs against capability.json).
|
|
227
241
|
AW_RECEIPT_BACKEND="agy"
|
|
228
|
-
AW_BRIDGE_VERSION="
|
|
242
|
+
AW_BRIDGE_VERSION="4.0.0"
|
|
229
243
|
# `-` not `:-` so an EXPLICIT empty AGY_MODEL= survives (drop --model, use settings.json — agy.sh:52).
|
|
230
244
|
AGY_MODEL="${AGY_MODEL-$DEFAULT_AGY_REVIEW_MODEL}"
|
|
245
|
+
# D5 control-byte screen — IMMEDIATELY after resolution, BEFORE the off-frontier advisory (or any
|
|
246
|
+
# other interpolation) can echo raw newline/ESC bytes into stderr/the terminal (round-2 fold).
|
|
247
|
+
if [[ "$AGY_MODEL" == *[$'\x01'-$'\x1f']* ]]; then
|
|
248
|
+
echo "error: AGY_MODEL contains control bytes — fix the setting (env or bridge-settings.conf) and re-run." >&2
|
|
249
|
+
exit 2
|
|
250
|
+
fi
|
|
231
251
|
# Frontier review models. ANY model is allowed; a sub-frontier one only earns a soft, silenceable warning.
|
|
232
252
|
FRONTIER_SET=("Gemini 3.1 Pro (High)" "Claude Opus 4.6 (Thinking)" "Claude Sonnet 4.6 (Thinking)")
|
|
233
253
|
|
|
@@ -444,6 +464,26 @@ receipt_json_scalar() {
|
|
|
444
464
|
if [[ -z "${1:-}" ]]; then printf 'null'; else printf '"%s"' "$1"; fi
|
|
445
465
|
}
|
|
446
466
|
|
|
467
|
+
# STRICT JSON string encoding for the ONE free-form receipt field (the posture model display
|
|
468
|
+
# string): backslash then double-quote escaped. Control bytes never reach here — the posture
|
|
469
|
+
# resolution refuses them pre-spend (D5), so these two escapes make the encoding total.
|
|
470
|
+
json_string_escape() {
|
|
471
|
+
local s="${1//\\/\\\\}"
|
|
472
|
+
s="${s//\"/\\\"}"
|
|
473
|
+
printf '%s' "$s"
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
# The D5 posture object this wrapper writes into EVERY receipt (agy has no tier — the label is
|
|
477
|
+
# the model display string). An unknowable model (explicitly emptied on an exempt probe run) is
|
|
478
|
+
# recorded null, never guessed.
|
|
479
|
+
posture_json() {
|
|
480
|
+
if [[ -z "${AGY_MODEL:-}" ]]; then
|
|
481
|
+
printf '{"model":null}'
|
|
482
|
+
else
|
|
483
|
+
printf '{"model":"%s"}' "$(json_string_escape "$AGY_MODEL")"
|
|
484
|
+
fi
|
|
485
|
+
}
|
|
486
|
+
|
|
447
487
|
# write_review_receipt <artifact|""> <fresh: true|false> <fingerprint|""> <verdict> <grounded: true|false> <factsHash|""> [probe: true|false]
|
|
448
488
|
# Appends ONE receipt line (the AD-038 fixture shape) as a side effect of a SUCCESSFUL review —
|
|
449
489
|
# to $AW_REVIEW_RECEIPTS when set, else <git dir>/agent-workflow-review-receipts.jsonl (inside the
|
|
@@ -467,28 +507,29 @@ write_review_receipt() {
|
|
|
467
507
|
fi
|
|
468
508
|
local line probe_field=',"probe":false'
|
|
469
509
|
if [[ "$probe" == "true" ]]; then probe_field=',"probe":true'; fi
|
|
470
|
-
line="$(printf '{"schema":1,"artifact":%s,"fresh":%s,"fingerprint":%s,"backend":"%s","verdict":"%s","grounded":%s,"factsHash":%s,"wrapperVersion":"%s","timestamp":"%s"%s}' \
|
|
510
|
+
line="$(printf '{"schema":1,"artifact":%s,"fresh":%s,"fingerprint":%s,"backend":"%s","verdict":"%s","grounded":%s,"factsHash":%s,"wrapperVersion":"%s","timestamp":"%s"%s,"posture":%s}' \
|
|
471
511
|
"$(receipt_json_scalar "$artifact")" "$fresh" "$(receipt_json_scalar "$fingerprint")" \
|
|
472
512
|
"$AW_RECEIPT_BACKEND" "$verdict" "$grounded" "$(receipt_json_scalar "$facts_hash")" \
|
|
473
|
-
"$AW_BRIDGE_VERSION" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$probe_field")"
|
|
513
|
+
"$AW_BRIDGE_VERSION" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$probe_field" "$(posture_json)")"
|
|
474
514
|
if ! printf '%s\n' "$line" >>"$receipts" 2>/dev/null; then
|
|
475
515
|
echo "warning: could not append the review receipt to $receipts — the review itself succeeded;" >&2
|
|
476
516
|
echo " the review-state gate will read the current tree as un-receipted." >&2
|
|
477
517
|
fi
|
|
478
518
|
}
|
|
479
519
|
|
|
480
|
-
# Parse the mandated '### Verdict' section of a captured review: the first non-empty line after
|
|
481
|
-
#
|
|
482
|
-
#
|
|
520
|
+
# Parse the mandated '### Verdict' section of a captured review: the first non-empty line after
|
|
521
|
+
# the EXACT heading (`### Verdicts` or any suffix never matches), tested against the closed
|
|
522
|
+
# vocabulary as an ANCHORED PREFIX with a word boundary — the verdict line must START with the
|
|
523
|
+
# token and the next char must be non-word/EOL (SHIP WITH NITS before SHIP; `NOT SHIP` and
|
|
524
|
+
# `SHIPPING` never match). No heading / no match → "unknown" (the D4 failed-run arm owns it).
|
|
483
525
|
parse_agy_verdict() { # $1 = captured-output file
|
|
484
526
|
local line
|
|
485
|
-
line="$(awk '/^### Verdict
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
esac
|
|
527
|
+
line="$(awk '/^### Verdict[[:space:]]*$/{flag=1; next} flag && NF {print; exit}' "$1" 2>/dev/null)"
|
|
528
|
+
if [[ "$line" =~ ^SHIP\ WITH\ NITS([^[:alnum:]_]|$) ]]; then printf 'SHIP WITH NITS'
|
|
529
|
+
elif [[ "$line" =~ ^REWORK([^[:alnum:]_]|$) ]]; then printf 'REWORK'
|
|
530
|
+
elif [[ "$line" =~ ^SHIP([^[:alnum:]_]|$) ]]; then printf 'SHIP'
|
|
531
|
+
else printf 'unknown'
|
|
532
|
+
fi
|
|
492
533
|
}
|
|
493
534
|
|
|
494
535
|
# Emit the full review surface to stdout: repo map, status (never-committable untracked records
|
|
@@ -828,6 +869,21 @@ else
|
|
|
828
869
|
fi
|
|
829
870
|
fi
|
|
830
871
|
|
|
872
|
+
# --- D5 pre-spend posture gate + banner (one line, the ACTUAL run posture) ------------------
|
|
873
|
+
# The control-byte screen already ran at AGY_MODEL resolution (before any interpolation). An
|
|
874
|
+
# ATTESTING review (fresh code mode, guards on) whose wrapper cannot know the actual model
|
|
875
|
+
# (AGY_MODEL explicitly emptied → the CLI's own settings default decides) refuses pre-spend;
|
|
876
|
+
# AGY_PROBE=1 runs are exempt (their receipts never attest and record model null).
|
|
877
|
+
# Scoped to the ATTESTING branch only (grounded code, guards on): plan / diff / --ungrounded
|
|
878
|
+
# code mint receipts that never attest, so an emptied model just records posture.model null.
|
|
879
|
+
if [[ -z "$AGY_MODEL" && -z "$resume_mode" && "$REVIEW_PROBE" != "true" && "$REVIEW_ARTIFACT" == "code" && -n "$FACTS_CONTENT" ]]; then
|
|
880
|
+
echo "error: AGY_MODEL is explicitly empty, so the ACTUAL review model is unknowable (the agy CLI's" >&2
|
|
881
|
+
echo " own settings default decides) — an attesting review refuses pre-spend. Fix: unset" >&2
|
|
882
|
+
echo " AGY_MODEL (wrapper default), or set the real model display string; AGY_PROBE=1 is exempt." >&2
|
|
883
|
+
exit 2
|
|
884
|
+
fi
|
|
885
|
+
echo "review posture: model=${AGY_MODEL:-<agy settings default>}" >&2
|
|
886
|
+
|
|
831
887
|
# --- Execute via agy-run (single home of timeout + subscription + byte ceiling) ---
|
|
832
888
|
# The output is teed into the private staging dir so the mandated '### Verdict' section can be
|
|
833
889
|
# parsed into the review receipt — the user-facing stream is unchanged.
|
|
@@ -845,8 +901,17 @@ fi
|
|
|
845
901
|
set -e
|
|
846
902
|
|
|
847
903
|
# --- Review receipt (AD-038): only a SUCCESSFUL review attests --------------------
|
|
904
|
+
# D4 (wrapper honesty): a run that produced NO recognized '### Verdict' section — empty output
|
|
905
|
+
# included — is a FAILED review: non-zero exit, NO receipt. This is a failed review to RE-RUN,
|
|
906
|
+
# never a fatal session error (documented in --help).
|
|
848
907
|
if [[ $rc -eq 0 ]]; then
|
|
849
908
|
verdict="$(parse_agy_verdict "$review_out_file")"
|
|
909
|
+
if [[ "$verdict" == "unknown" ]]; then
|
|
910
|
+
echo "error: the review output carries no recognized '### Verdict' section (closed vocabulary:" >&2
|
|
911
|
+
echo " SHIP / SHIP WITH NITS / REWORK) — a FAILED review; NO receipt was written. Re-run" >&2
|
|
912
|
+
echo " the review; if it recurs, inspect the captured output for what the model produced." >&2
|
|
913
|
+
exit 4
|
|
914
|
+
fi
|
|
850
915
|
if [[ -n "$resume_mode" ]]; then
|
|
851
916
|
# A continuation never re-embeds the current artifact (agy holds the ORIGINAL round server-side;
|
|
852
917
|
# --facts is rejected above), so it cannot attest the folded tree: fresh:false, artifact /
|