@sabaiway/agent-workflow-kit 5.1.0 → 5.2.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 +55 -0
- package/SKILL.md +13 -1
- package/bridges/antigravity-cli-bridge/SKILL.md +14 -3
- package/bridges/antigravity-cli-bridge/bin/agy-review.sh +220 -30
- package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +264 -8
- package/bridges/antigravity-cli-bridge/bin/agy.sh +12 -2
- package/bridges/antigravity-cli-bridge/bin/agy.test.mjs +18 -0
- package/bridges/antigravity-cli-bridge/capability.json +19 -13
- package/bridges/antigravity-cli-bridge/references/driving-agy.md +3 -2
- package/bridges/codex-cli-bridge/SKILL.md +8 -5
- package/bridges/codex-cli-bridge/bin/codex-exec.sh +3 -2
- package/bridges/codex-cli-bridge/bin/codex-review.sh +205 -34
- package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +276 -5
- package/bridges/codex-cli-bridge/capability.json +8 -6
- package/bridges/codex-cli-bridge/references/driving-codex.md +2 -2
- package/bridges/codex-cli-bridge/references/sandbox-and-flags.md +2 -2
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/modes/flow-writer.md +37 -0
- package/references/modes/gates.md +4 -4
- package/references/modes/procedures.md +4 -2
- package/references/modes/receipt-deadline.md +16 -0
- package/references/modes/review-state.md +1 -1
- package/references/modes/set-flow.md +22 -0
- package/tools/cheap-agents.mjs +8 -2
- package/tools/commands.mjs +24 -2
- package/tools/commit-guard.mjs +44 -9
- package/tools/core-evidence.mjs +25 -22
- package/tools/detect-backends.mjs +32 -11
- package/tools/doc-parity.mjs +21 -6
- package/tools/flow-check.mjs +806 -0
- package/tools/flow-record.mjs +795 -0
- package/tools/flow-store-read.mjs +114 -0
- package/tools/flow-store.mjs +1178 -0
- package/tools/flow-writer.mjs +1265 -0
- package/tools/fs-read-nofollow.mjs +128 -0
- package/tools/gates-declaration.mjs +184 -0
- package/tools/gates-init.mjs +59 -17
- package/tools/orchestration-config.mjs +87 -10
- package/tools/orchestration-write.mjs +3 -3
- package/tools/plan-files.mjs +35 -0
- package/tools/procedures.mjs +75 -11
- package/tools/receipt-deadline.mjs +242 -0
- package/tools/recipes.mjs +21 -0
- package/tools/repo-lex.mjs +22 -0
- package/tools/review-state.mjs +240 -80
- package/tools/run-gates.mjs +361 -139
- package/tools/set-flow.mjs +465 -0
- package/tools/velocity-profile.mjs +8 -2
package/tools/cheap-agents.mjs
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
29
29
|
import { join, resolve, dirname } from 'node:path';
|
|
30
30
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
31
|
-
import { shellQuoteArg } from './
|
|
31
|
+
import { shellQuoteArg } from './repo-lex.mjs';
|
|
32
32
|
|
|
33
33
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
34
34
|
|
|
@@ -48,6 +48,12 @@ export const CHEAP_AGENTS_STAMP = 'CHEAP_AGENTS_STAMP';
|
|
|
48
48
|
export const CHEAP_AGENTS_SYMLINK = 'CHEAP_AGENTS_SYMLINK';
|
|
49
49
|
export const CHEAP_AGENTS_BUNDLE = 'CHEAP_AGENTS_BUNDLE';
|
|
50
50
|
|
|
51
|
+
// The fallback-lens contract, formalized where it lives (flow-orchestration #15/#3, Phase 4.3):
|
|
52
|
+
// the internal-attestation evaluation consumes this sentence — a lens set claiming a configured
|
|
53
|
+
// backend's slot without a then-active down-mark REFUSES, quoting it (substitution is recorded,
|
|
54
|
+
// never silent).
|
|
55
|
+
export const FALLBACK_LENS_ADDITIONAL_ONLY = 'review-lens is an ADDITIONAL read-only review opinion, not a replacement for your configured review recipe.';
|
|
56
|
+
|
|
51
57
|
const USAGE = `usage: cheap-agents [--dry-run | --apply] [--cwd <dir>] [--help]
|
|
52
58
|
|
|
53
59
|
Places the bundled READ-ONLY subagent definitions into the project's ${AGENTS_DIR}/. No vehicle
|
|
@@ -191,7 +197,7 @@ export const formatResult = (result) => {
|
|
|
191
197
|
}
|
|
192
198
|
lines.push(
|
|
193
199
|
'the vehicles are Claude Code subagents with READ-ONLY tools and NO shell — so a fan-out can never turn into a wave of approval prompts.',
|
|
194
|
-
|
|
200
|
+
`three ride the cheap lane (model: haiku, effort: low) for mechanical work; ${FALLBACK_LENS_ADDITIONAL_ONLY} Writing code and running gates stay on your main lane.`,
|
|
195
201
|
);
|
|
196
202
|
// A preview must print the EXACT command that applies it. The advisor renders this dry-run as an
|
|
197
203
|
// item's one-liner, and that flow's contract is "run the printed command, no improvisation" — a
|
package/tools/commands.mjs
CHANGED
|
@@ -196,6 +196,20 @@ const CATALOG = [
|
|
|
196
196
|
kind: WRITER,
|
|
197
197
|
oneLine: 'Set the per-project autonomy policy from plain language — which actions always ask (commit/push/publish/network) and how autonomously each activity runs; previews the change, then writes the policy when you confirm.',
|
|
198
198
|
},
|
|
199
|
+
{
|
|
200
|
+
key: 'set-flow',
|
|
201
|
+
invocation: invocationOf('set-flow'),
|
|
202
|
+
group: 'Orchestrate',
|
|
203
|
+
kind: WRITER,
|
|
204
|
+
oneLine: 'Arm the per-project review-flow settings from a preset plus explicit keys — previews the merged flow block, verifies the declared bookkeeping files and the kit version floor, then writes the config when you confirm.',
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
key: 'flow-writer',
|
|
208
|
+
invocation: invocationOf('flow-writer'),
|
|
209
|
+
group: 'Orchestrate',
|
|
210
|
+
kind: WRITER,
|
|
211
|
+
oneLine: 'Record an explicit review-flow step (adoption/park/resume/complete of a plan, re-baseline, a rerun cause, a reviewer down-mark, a degrade justification, a checkpoint-approved override, a manifest-bound consult attestation) into the shared flow record store — flow-check refusals print the exact command to paste, and the store itself validates every append.',
|
|
212
|
+
},
|
|
199
213
|
{
|
|
200
214
|
key: 'review-state',
|
|
201
215
|
invocation: invocationOf('review-state'),
|
|
@@ -203,6 +217,13 @@ const CATALOG = [
|
|
|
203
217
|
kind: READ_ONLY,
|
|
204
218
|
oneLine: 'Check that every configured review backend has receipted the current uncommitted tree with a fresh grounded review; --check turns it into a gate exit code.',
|
|
205
219
|
},
|
|
220
|
+
{
|
|
221
|
+
key: 'receipt-deadline',
|
|
222
|
+
invocation: invocationOf('receipt-deadline'),
|
|
223
|
+
group: 'Orchestrate',
|
|
224
|
+
kind: READ_ONLY,
|
|
225
|
+
oneLine: 'Wait for ONE dispatched review to answer — a strictly-newer receipt line from that backend past the pre-dispatch watermark (or its nonce-named finding manifest); a shrunken or rewritten receipts file refuses loudly, and the timeout names the watermark.',
|
|
226
|
+
},
|
|
206
227
|
{
|
|
207
228
|
key: 'grounding',
|
|
208
229
|
invocation: invocationOf('grounding'),
|
|
@@ -271,8 +292,9 @@ export const kindOf = (key) => byKey.get(key)?.kind ?? null;
|
|
|
271
292
|
// (`upgrade`) OR the full slash form (`/agent-workflow-kit upgrade`); the first word is significant
|
|
272
293
|
// and trailing args are ignored. Precise semantics:
|
|
273
294
|
// undefined / null / '' / whitespace-only / the exact bare invocation → 'bootstrap'
|
|
274
|
-
// a known first token (upgrade/status/setup/backends/recipes/
|
|
275
|
-
// gates/set-recipe/
|
|
295
|
+
// a known first token (any non-bootstrap catalog key — upgrade/status/setup/backends/recipes/
|
|
296
|
+
// procedures/velocity/agents/hook/gates/set-recipe/set-flow/flow-writer/uninstall/
|
|
297
|
+
// migrate-adr-store/help/…) → that mode
|
|
276
298
|
// anything else (unrecognized / ambiguous) → 'help' (read-only — NEVER a writer/guarded mode)
|
|
277
299
|
export const routeInvocation = (token) => {
|
|
278
300
|
if (token == null) return BARE_INVOCATION_MODE;
|
package/tools/commit-guard.mjs
CHANGED
|
@@ -20,8 +20,15 @@
|
|
|
20
20
|
// from the recorded one · evidence hashes that no longer match the store's canonical
|
|
21
21
|
// red-proof/degrade serializations · an lcov file whose sha moved. The guard's own reads
|
|
22
22
|
// resolve FIXED git-dir paths (env overrides are producer test seams, never guard inputs);
|
|
23
|
-
// 3.
|
|
24
|
-
//
|
|
23
|
+
// 3. consults the flow decision (Plan 3 Phase 2, two-tier): with NO flow store file the guard is
|
|
24
|
+
// byte-identical to the pre-flow guard; a PRESENT store must read clean and its refusals
|
|
25
|
+
// (open own chain, base motion, coverage, ordering) refuse the commit with the flow-check
|
|
26
|
+
// reason verbatim — a foreign worktree's chain stays advisory; the armed state extends the
|
|
27
|
+
// PASS line;
|
|
28
|
+
// 4. re-computes the review-state decision (the ship-receipt arm) — a missing/vetoed ship
|
|
29
|
+
// receipt refuses. The env it hands over is SANITIZED (receipts/evidence/flow-store producer
|
|
30
|
+
// seams stripped) so a poisoned override can neither redirect nor mask any store this guard
|
|
31
|
+
// reads.
|
|
25
32
|
// `git commit --no-verify` stays the stated residual (a self-discipline mechanism, not a security
|
|
26
33
|
// boundary). Read-only; dependency-free; Node >= 22. No side effects on import.
|
|
27
34
|
|
|
@@ -34,6 +41,7 @@ import { computeTreeFingerprint, buildState, decideCheck, quoteReportName, shell
|
|
|
34
41
|
import { resolveEvidencePath, readEvidence, authoritativeOfKind, canonicalKindSerialization, computeWorkingState } from './core-evidence.mjs';
|
|
35
42
|
import { resolveLcovPath } from './coverage-check.mjs';
|
|
36
43
|
import { GATES_REL, loadDeclaration } from './run-gates.mjs';
|
|
44
|
+
import { computeFlowDecision } from './flow-check.mjs';
|
|
37
45
|
|
|
38
46
|
const usageFail = (message) => Object.assign(new Error(`[agent-workflow-kit] ${message}`), { exitCode: 2 });
|
|
39
47
|
const sha = (text) => createHash('sha256').update(text).digest('hex');
|
|
@@ -238,18 +246,42 @@ export const runGuard = ({ cwd = process.cwd(), env = process.env } = {}) => {
|
|
|
238
246
|
return { code: 1, lines: ['commit-guard: REFUSED — the lcov file the receipt consumed moved or vanished; re-run run-gates.mjs --final'] };
|
|
239
247
|
}
|
|
240
248
|
}
|
|
241
|
-
// The
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
249
|
+
// The flow arm (#43/P3, two-tier over FIXED git-derived paths): no store file ⇒ byte-exact
|
|
250
|
+
// prior behavior; a present store's refusals (malformed reads included) refuse with the
|
|
251
|
+
// flow-check reason verbatim. The commit-guard consumer mode arms the D10 flow→final
|
|
252
|
+
// comparison (Plan 4 Decision 2) — the in-matrix flow-check gate stays inert on it.
|
|
253
|
+
const flow = computeFlowDecision({ cwd, consumer: 'commit-guard' });
|
|
254
|
+
// NOT gated on flow.present: the D10 binding refusal fires precisely when a receipt carries
|
|
255
|
+
// evidenceHashes.flow and the store has since VANISHED (present=false) — a deletion must
|
|
256
|
+
// never un-arm the binding. A no-store repo with no flow-bearing receipt still yields zero
|
|
257
|
+
// refusals (byte-exact pre-flow behavior).
|
|
258
|
+
if (flow.refusals.length > 0) {
|
|
259
|
+
return {
|
|
260
|
+
code: 1,
|
|
261
|
+
lines: [
|
|
262
|
+
`commit-guard: REFUSED — the flow store refuses this commit: ${flow.refusals[0]}`,
|
|
263
|
+
...flow.refusals.slice(1).map((r) => `commit-guard: flow refusal — ${r}`),
|
|
264
|
+
],
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
// The ship-receipt arm: the SAME normative decision review-state --check computes, over a
|
|
268
|
+
// SANITIZED env — the receipts/evidence/flow-store overrides are producer test seams, and
|
|
269
|
+
// honoring them HERE would let a forged store bypass the fixed-path reads above.
|
|
245
270
|
const reviewEnv = { ...env };
|
|
246
271
|
delete reviewEnv.AW_REVIEW_RECEIPTS;
|
|
247
272
|
delete reviewEnv.AW_CORE_EVIDENCE;
|
|
273
|
+
delete reviewEnv.AW_FLOW_STORE;
|
|
248
274
|
const review = decideCheck(buildState({ cwd, env: reviewEnv }));
|
|
249
275
|
if (review.code !== 0) {
|
|
250
276
|
return { code: 1, lines: [`commit-guard: REFUSED — the review obligations are not satisfied: ${review.reason}`] };
|
|
251
277
|
}
|
|
252
|
-
|
|
278
|
+
const flowSuffix = flow.present && flow.armed
|
|
279
|
+
? ` — flow: armed${review.flowLabels?.length ? ` (${review.flowLabels.join('; ')})` : ''}`
|
|
280
|
+
: '';
|
|
281
|
+
const flowAdvisoryLines = flow.present && flow.armed
|
|
282
|
+
? flow.advisories.map((a) => `commit-guard: flow advisory — ${a}`)
|
|
283
|
+
: [];
|
|
284
|
+
return { code: 0, lines: [`commit-guard: PASS — a green final receipt binds this exact tree (${fingerprint.slice(0, 12)}…), the declaration and evidence hashes match, and the review obligations are satisfied${flowSuffix}`, ...flowAdvisoryLines] };
|
|
253
285
|
};
|
|
254
286
|
|
|
255
287
|
const HELP = `commit-guard — the read-only pre-commit guard (agent-workflow family, D10).
|
|
@@ -262,8 +294,11 @@ paths, reviewable untracked paths, or a dirty tracked submodule, each named with
|
|
|
262
294
|
this deliberately blocks a partial commit), then recomputes the current tree fingerprint and binds
|
|
263
295
|
the LATEST completed run-gates --final receipt — refusing on { no receipt for this tree · a red
|
|
264
296
|
latest attempt · before≠after · declaration content drift · evidence-hash drift · lcov drift ·
|
|
265
|
-
|
|
266
|
-
|
|
297
|
+
a flow-store refusal (a PRESENT store's open own chain / base motion / coverage — verbatim; no
|
|
298
|
+
store file = byte-exact pre-flow behavior) · unsatisfied review obligations (the review-state
|
|
299
|
+
decision, over a sanitized env — receipts/evidence/flow-store seams stripped) }. Wire it into
|
|
300
|
+
pre-commit; \`git commit --no-verify\` stays the stated residual (self-discipline, not a security
|
|
301
|
+
boundary).
|
|
267
302
|
|
|
268
303
|
Exit codes: 0 pass; 1 refused (reason named); 2 usage.`;
|
|
269
304
|
|
package/tools/core-evidence.mjs
CHANGED
|
@@ -34,12 +34,14 @@
|
|
|
34
34
|
// checker, not here. Dependency-free. No side effects on import.
|
|
35
35
|
|
|
36
36
|
import { readFileSync, lstatSync, realpathSync, readlinkSync, openSync, readSync, closeSync } from 'node:fs';
|
|
37
|
-
import { join, dirname,
|
|
37
|
+
import { join, dirname, normalize, sep, basename } from 'node:path';
|
|
38
38
|
import { pathToFileURL } from 'node:url';
|
|
39
39
|
import { spawnSync } from 'node:child_process';
|
|
40
40
|
import { createHash } from 'node:crypto';
|
|
41
41
|
import { writeContainedFileAtomic } from './atomic-write.mjs';
|
|
42
42
|
import { parsePositiveIntKnob, probeVerdict } from './changed-surface.mjs';
|
|
43
|
+
import { readRegularFileNoFollow } from './fs-read-nofollow.mjs';
|
|
44
|
+
import { lexicalRepoRelative } from './repo-lex.mjs';
|
|
43
45
|
|
|
44
46
|
export const CORE_EVIDENCE_STOP = 'CORE_EVIDENCE_STOP';
|
|
45
47
|
const stop = (message) => Object.assign(new Error(`[agent-workflow-kit] ${message}`), { name: 'CoreEvidenceStop', code: CORE_EVIDENCE_STOP });
|
|
@@ -399,17 +401,20 @@ export const resolveReceiptsPath = (cwd, env = process.env) => {
|
|
|
399
401
|
};
|
|
400
402
|
|
|
401
403
|
// Parse the receipt file → { receipts, malformed, readError? }. Absent file → empty (not an
|
|
402
|
-
// error: no review ever ran).
|
|
403
|
-
//
|
|
404
|
-
//
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
404
|
+
// error: no review ever ran). The read rides the fs-read-nofollow leaf (RECEIPTS-READER-NOFOLLOW):
|
|
405
|
+
// a symlinked/FIFO/directory receipts path surfaces as readError — never content, never an empty
|
|
406
|
+
// success — and any NON-ENOENT failure surfaces as readError too; an unreadable store must never
|
|
407
|
+
// silently read as "no receipts" (the summary withholds its verdicts section on it). A malformed
|
|
408
|
+
// line is counted + reported, never silently dropped. `io` is the injectable-read test seam
|
|
409
|
+
// (readRegularFileNoFollow's descriptor-level io).
|
|
410
|
+
export const readReceipts = (path, io = {}) => {
|
|
411
|
+
const read = readRegularFileNoFollow(path, io);
|
|
412
|
+
if (read.outcome === 'absent') return { receipts: [], malformed: 0 };
|
|
413
|
+
if (read.outcome === 'foreign') {
|
|
414
|
+
return { receipts: [], malformed: 0, readError: `the receipts store is a ${read.className}, not a regular file — refusing to read it (fail closed)` };
|
|
412
415
|
}
|
|
416
|
+
if (read.outcome === 'error') return { receipts: [], malformed: 0, readError: read.code };
|
|
417
|
+
const raw = read.content;
|
|
413
418
|
const receipts = [];
|
|
414
419
|
let malformed = 0;
|
|
415
420
|
for (const line of raw.split('\n')) {
|
|
@@ -593,17 +598,10 @@ const isNonEmptyString = (v) => typeof v === 'string' && v.length > 0;
|
|
|
593
598
|
const HEX64_RE = /^[0-9a-f]{64}$/;
|
|
594
599
|
const HEX40_RE = /^[0-9a-f]{40}$/;
|
|
595
600
|
|
|
596
|
-
// The LEXICAL half of the repo-relative rule
|
|
597
|
-
// no fs to resolve against
|
|
598
|
-
//
|
|
599
|
-
|
|
600
|
-
export const lexicalRepoRelative = (rel) => {
|
|
601
|
-
if (typeof rel !== 'string' || rel.length === 0) return { ok: false, reason: 'empty file path' };
|
|
602
|
-
if (isAbsolute(rel)) return { ok: false, reason: `absolute path "${rel}" — the testId file half must be repo-relative` };
|
|
603
|
-
const norm = normalize(rel);
|
|
604
|
-
if (norm === '..' || norm.startsWith(`..${sep}`)) return { ok: false, reason: `path "${rel}" escapes the repo root` };
|
|
605
|
-
return { ok: true };
|
|
606
|
-
};
|
|
601
|
+
// The LEXICAL half of the repo-relative rule lives in the repo-lex.mjs LEAF (ONE home shared by
|
|
602
|
+
// the record validators — flow-record has no fs to resolve against — and the fs resolver below,
|
|
603
|
+
// so the two can never drift); re-exported here so every historical consumer keeps its import site.
|
|
604
|
+
export { lexicalRepoRelative } from './repo-lex.mjs';
|
|
607
605
|
|
|
608
606
|
export const validateEvidenceRecord = (record) => {
|
|
609
607
|
if (!isPlainObject(record)) return { ok: false, reason: 'record is not an object' };
|
|
@@ -664,6 +662,11 @@ export const validateEvidenceRecord = (record) => {
|
|
|
664
662
|
|| typeof record.evidenceHashes.degrade !== 'string' || !HEX64_RE.test(record.evidenceHashes.degrade)) {
|
|
665
663
|
return { ok: false, reason: 'final: evidenceHashes must carry 64-hex sha256 of the canonical red-proof and degrade serializations' };
|
|
666
664
|
}
|
|
665
|
+
// The D10 flow binding (Plan 4 Decision 2) is ADDITIVE: absent = a pre-flow-binding final
|
|
666
|
+
// (still valid); present must be the 64-hex owner-scoped projection hash.
|
|
667
|
+
if ('flow' in record.evidenceHashes && (typeof record.evidenceHashes.flow !== 'string' || !HEX64_RE.test(record.evidenceHashes.flow))) {
|
|
668
|
+
return { ok: false, reason: 'final: evidenceHashes.flow, when present, must be a 64-hex sha256 of the owner-scoped flow projection' };
|
|
669
|
+
}
|
|
667
670
|
if (record.lcovSha256 !== null && (typeof record.lcovSha256 !== 'string' || !HEX64_RE.test(record.lcovSha256))) {
|
|
668
671
|
return { ok: false, reason: 'final: lcovSha256 must be a 64-hex sha256 of the consumed lcov file, or null when none was produced' };
|
|
669
672
|
}
|
|
@@ -90,19 +90,22 @@ const RAW_BACKENDS = [
|
|
|
90
90
|
},
|
|
91
91
|
review: {
|
|
92
92
|
invocations: [
|
|
93
|
-
'codex-review plan <plan-file>',
|
|
94
|
-
'codex-review code [extra focus...]',
|
|
93
|
+
'codex-review plan <plan-file> [--nonce <n>]',
|
|
94
|
+
'codex-review code [--nonce <n>] [extra focus...]',
|
|
95
95
|
],
|
|
96
96
|
grounding: 'automatic — the wrapper precomputes the full working-tree change set (repo map, status, diffs, untracked contents) and codex auto-merges the root AGENTS.md; no grounding flags',
|
|
97
97
|
continue: [],
|
|
98
|
-
receipt: 'side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain; never-committable untracked paths — character/block devices, FIFOs, sockets — are excluded from the domain entirely, untracked symlinks/directories ride as name-only notes) in code mode, the artifact-file sha256 in plan mode; verdict parsed from the mandated literal verdict line (schema mode: the verdict field); always fresh:true (one-shot) + grounded:true (native AGENTS.md auto-merge, factsHash null); probe = whether the run relaxed the quality guards (CODEX_PROBE=1), written on EVERY receipt so it self-declares — the kit\'s review-state gate rejects a probe-marked receipt (a probe review never attests) and equally rejects an unmarked one (silence is not a declaration); posture = the ACTUAL run posture {model, effort, tier} (tier null on the standard tier), written on EVERY receipt (D5) — the gate rejects a receipt with an absent/invalid posture (a pre-D5 wrapper minted it; re-run the review), one stderr banner line states the same posture, and a posture value carrying control bytes refuses pre-spend in every mode; a run whose final message carries NO recognized \'Verdict: <ship|revise|rethink>\' line — empty or missing output included — exits 4 with NO receipt (D4: a FAILED review to RE-RUN, never a fatal session error); a write failure warns, never fails the review',
|
|
98
|
+
receipt: 'side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain; never-committable untracked paths — character/block devices, FIFOs, sockets — are excluded from the domain entirely, untracked symlinks/directories ride as name-only notes) in code mode, the artifact-file sha256 in plan mode; verdict parsed from the mandated literal verdict line (schema mode: the verdict field); always fresh:true (one-shot) + grounded:true (native AGENTS.md auto-merge, factsHash null); probe = whether the run relaxed the quality guards (CODEX_PROBE=1), written on EVERY receipt so it self-declares — the kit\'s review-state gate rejects a probe-marked receipt (a probe review never attests) and equally rejects an unmarked one (silence is not a declaration); posture = the ACTUAL run posture {model, effort, tier} (tier null on the standard tier), written on EVERY receipt (D5) — the gate rejects a receipt with an absent/invalid posture (a pre-D5 wrapper minted it; re-run the review), one stderr banner line states the same posture, and a posture value carrying control bytes refuses pre-spend in every mode; a run whose final message carries NO recognized \'Verdict: <ship|revise|rethink>\' line — empty or missing output included — exits 4 with NO receipt (D4: a FAILED review to RE-RUN, never a fatal session error); when the dispatch nonce seam is supplied — the AW_REVIEW_NONCE environment value or its plain-argument equivalent --nonce <n> (one seam: the flag assigns the same value; supplying both with different values refuses pre-spend) — under the safe grammar [A-Za-z0-9._-]{1,64} (anything else refuses pre-spend), the wrapper first mints the finding MANIFEST {schema, backend, nonce, fingerprint, findings} beside the receipts file (agent-workflow-finding-manifest-<backend>-<nonce>.json; atomic, no-clobber — a byte-identical rewrite is an idempotent no-op, different bytes refuse loudly) ORDERED before the receipt append — a failed manifest write EXCLUDES the receipt append, so a nonce-supplied dispatch can never land a receipt without its readable manifest; a nonce-less invocation adds NO nonce field and mints NO finding manifest (the existing wrapperVersion field still changes with each bridge release); a write failure warns, never fails the review',
|
|
99
99
|
notes: [
|
|
100
|
-
'the review posture banner appends a banner-only timeout=<duration
|
|
100
|
+
'the review posture banner appends a banner-only timeout=<duration> field — exactly the duration handed to timeout(1); the hard-timeout preflight fails CLOSED when no timeout/gtimeout binary exists (the wrapper refuses by name before any CLI run, so an uncapped review run can no longer happen), and the field never enters the receipt posture or the D5 banner↔receipt parity',
|
|
101
101
|
'quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated posture; a prose re-type drifts',
|
|
102
102
|
],
|
|
103
103
|
},
|
|
104
104
|
},
|
|
105
105
|
bin: 'codex',
|
|
106
|
+
// The per-backend receipt-deadline default (seconds) the capability block reports — the review
|
|
107
|
+
// wrapper's built-in hard cap (CODEX_HARD_TIMEOUT review default), an OFFLINE registry fact.
|
|
108
|
+
deadlineDefaultS: 1800,
|
|
106
109
|
credential: { env: 'CODEX_HOME', default: '~/.codex', file: 'auth.json' },
|
|
107
110
|
setupUrl: 'https://github.com/sabaiway/agent-workflow/blob/main/codex-cli-bridge/setup/README.md',
|
|
108
111
|
setupPathLocal: 'setup/README.md',
|
|
@@ -118,9 +121,9 @@ const RAW_BACKENDS = [
|
|
|
118
121
|
roleContracts: {
|
|
119
122
|
review: {
|
|
120
123
|
invocations: [
|
|
121
|
-
'agy-review code [--facts @f] [--ungrounded] [--decided @f] [--focus "…"] [extra focus…]',
|
|
122
|
-
'agy-review plan <plan-file> [--facts @f] [--decided @f] [--focus "…"]',
|
|
123
|
-
'agy-review diff <diff-file> [--facts @f] [--decided @f] [--focus "…"]',
|
|
124
|
+
'agy-review code [--facts @f] [--ungrounded] [--decided @f] [--focus "…"] [--nonce <n>] [extra focus…]',
|
|
125
|
+
'agy-review plan <plan-file> [--facts @f] [--decided @f] [--focus "…"] [--nonce <n>]',
|
|
126
|
+
'agy-review diff <diff-file> [--facts @f] [--decided @f] [--focus "…"] [--nonce <n>]',
|
|
124
127
|
],
|
|
125
128
|
grounding: 'grounded review — agy reads NOTHING by default, an ungrounded review GUESSES: --facts @f = the verified facts to review AGAINST; --decided @f = decisions already made, do NOT re-raise (anti-circling). code mode REQUIRES a non-empty --facts payload and refuses BEFORE spending a run (escapes: --ungrounded, AGY_PROBE=1); plan/diff proceed with a loud warning',
|
|
126
129
|
flags: [
|
|
@@ -128,20 +131,23 @@ const RAW_BACKENDS = [
|
|
|
128
131
|
'--ungrounded — deliberately ungrounded CODE review, a throwaway opinion (code mode only, contradicts --facts; the receipt records grounded:false and never attests)',
|
|
129
132
|
'--decided @f — already-decided / already-addressed list; do NOT re-raise (anti-circling; the round-2 payload)',
|
|
130
133
|
'--focus "…" — extra focus (repeatable; code mode also takes trailing focus words)',
|
|
134
|
+
'--nonce <n> — the flow dispatch nonce, the plain-argument lane onto the AW_REVIEW_NONCE seam (one seam: flag and a non-empty env must agree; a disagreeing pair refuses pre-spend)',
|
|
131
135
|
],
|
|
132
136
|
continue: [
|
|
133
|
-
'agy-review --continue [--decided @f] [--focus "…"]',
|
|
134
|
-
'agy-review --conversation <id> [--decided @f] [--focus "…"]',
|
|
137
|
+
'agy-review --continue [--decided @f] [--focus "…"] [--nonce <n>]',
|
|
138
|
+
'agy-review --conversation <id> [--decided @f] [--focus "…"] [--nonce <n>]',
|
|
135
139
|
],
|
|
136
|
-
receipt: "side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides; plan/diff outside a git tree: warn + skip unless overridden): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain; never-committable untracked paths — character/block devices, FIFOs, sockets — are excluded from the domain entirely, untracked symlinks/directories ride as name-only notes) in code mode, the artifact-file sha256 in plan/diff mode; verdict recorded verbatim from the mandated '### Verdict' section (SHIP / SHIP WITH NITS / REWORK); grounded = whether a NON-EMPTY --facts payload was supplied (code mode refuses pre-spend without one — no run, no receipt — unless --ungrounded/AGY_PROBE=1; in plan/diff an empty payload records grounded:false — fail-closed, the state gate rejects it), factsHash = sha256 of the facts payload; a continuation receipt is fresh:false (informational-only — it cannot attest the folded tree); probe = whether the run relaxed the quality guards (AGY_PROBE=1), written on EVERY receipt so it self-declares — the kit's review-state gate rejects a probe-marked receipt (a probe review never attests) and equally rejects an unmarked one (silence is not a declaration); posture = the ACTUAL run posture {model} (agy has no tier), written on EVERY receipt (D5) — the gate rejects a receipt with an absent/invalid posture (a pre-D5 wrapper minted it; re-run the review), one stderr banner line states the same posture, an ATTESTING review with AGY_MODEL explicitly emptied refuses pre-spend, and a model string carrying control bytes refuses pre-spend in every mode; delivery = how the change set REACHED the model, currently emitted as 'inline' (the whole set rode one prompt — proven by construction) or 'fed' (a chunked feed whose per-part echo proof verified); REQUIRED on every agy code receipt and its ABSENCE is what stops a pre-fed-lane receipt attesting, while the gate accepts any well-formed declaration rather than a particular value; absent by construction on plan/diff/continuation receipts, which carry no change set; a run whose output carries NO recognized '### Verdict' section — empty output included — exits 4 with NO receipt (D4: a FAILED review to RE-RUN, never a fatal session error); a write failure warns, never fails the review",
|
|
140
|
+
receipt: "side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides; plan/diff outside a git tree: warn + skip unless overridden): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain; never-committable untracked paths — character/block devices, FIFOs, sockets — are excluded from the domain entirely, untracked symlinks/directories ride as name-only notes) in code mode, the artifact-file sha256 in plan/diff mode; verdict recorded verbatim from the mandated '### Verdict' section (SHIP / SHIP WITH NITS / REWORK); grounded = whether a NON-EMPTY --facts payload was supplied (code mode refuses pre-spend without one — no run, no receipt — unless --ungrounded/AGY_PROBE=1; in plan/diff an empty payload records grounded:false — fail-closed, the state gate rejects it), factsHash = sha256 of the facts payload; a continuation receipt is fresh:false (informational-only — it cannot attest the folded tree); probe = whether the run relaxed the quality guards (AGY_PROBE=1), written on EVERY receipt so it self-declares — the kit's review-state gate rejects a probe-marked receipt (a probe review never attests) and equally rejects an unmarked one (silence is not a declaration); posture = the ACTUAL run posture {model} (agy has no tier), written on EVERY receipt (D5) — the gate rejects a receipt with an absent/invalid posture (a pre-D5 wrapper minted it; re-run the review), one stderr banner line states the same posture, an ATTESTING review with AGY_MODEL explicitly emptied refuses pre-spend, and a model string carrying control bytes refuses pre-spend in every mode; delivery = how the change set REACHED the model, currently emitted as 'inline' (the whole set rode one prompt — proven by construction) or 'fed' (a chunked feed whose per-part echo proof verified); REQUIRED on every agy code receipt and its ABSENCE is what stops a pre-fed-lane receipt attesting, while the gate accepts any well-formed declaration rather than a particular value; absent by construction on plan/diff/continuation receipts, which carry no change set; a run whose output carries NO recognized '### Verdict' section — empty output included — exits 4 with NO receipt (D4: a FAILED review to RE-RUN, never a fatal session error); when the dispatch nonce seam is supplied — the AW_REVIEW_NONCE environment value or its plain-argument equivalent --nonce <n> (one seam: the flag assigns the same value; supplying both with different values refuses pre-spend) — under the safe grammar [A-Za-z0-9._-]{1,64} (anything else refuses pre-spend), the wrapper first mints the finding MANIFEST {schema, backend, nonce, fingerprint, findings} beside the receipts file (agent-workflow-finding-manifest-<backend>-<nonce>.json; atomic, no-clobber — a byte-identical rewrite is an idempotent no-op, different bytes refuse loudly) ORDERED before the receipt append — a failed manifest write EXCLUDES the receipt append, so a nonce-supplied dispatch can never land a receipt without its readable manifest; a nonce-less invocation adds NO nonce field and mints NO finding manifest (the existing wrapperVersion field still changes with each bridge release); a write failure warns, never fails the review",
|
|
137
141
|
notes: [
|
|
138
142
|
'pre-dispatch host-diff: before the FIRST dispatch of this bridge, diff its declared networkHosts against the live sandbox allow-list — a missing host is surfaced to the maintainer BEFORE dispatching, never fired into a known prompt',
|
|
139
|
-
'the review posture banner appends a banner-only timeout=<duration
|
|
143
|
+
'the review posture banner appends a banner-only timeout=<duration> field — exactly the duration agy-run hands to timeout(1); the hard-timeout preflight fails CLOSED when no timeout/gtimeout binary exists (the wrapper refuses by name before any CLI run, so an uncapped review run can no longer happen), and the field never enters the receipt posture or the D5 banner↔receipt parity',
|
|
140
144
|
'quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated posture; a prose re-type drifts',
|
|
141
145
|
],
|
|
142
146
|
},
|
|
143
147
|
},
|
|
144
148
|
bin: 'agy',
|
|
149
|
+
// AGY_HARD_TIMEOUT's built-in review default is 30m — reported in seconds, an OFFLINE registry fact.
|
|
150
|
+
deadlineDefaultS: 1800,
|
|
145
151
|
credential: { env: null, default: '~/.gemini/antigravity-cli', file: 'antigravity-oauth-token' },
|
|
146
152
|
setupUrl: 'https://github.com/sabaiway/agent-workflow/blob/main/antigravity-cli-bridge/setup/README.md',
|
|
147
153
|
setupPathLocal: 'setup/README.md',
|
|
@@ -169,6 +175,20 @@ export const wrapperCmdFor = (backendName, role) =>
|
|
|
169
175
|
export const wrapperContractFor = (backendName, role) =>
|
|
170
176
|
KNOWN_BACKENDS.find((b) => b.name === backendName)?.roleContracts?.[role] ?? null;
|
|
171
177
|
|
|
178
|
+
// The declared per-backend CAPABILITY block (flow-orchestration #15): roles, review-contract
|
|
179
|
+
// presence, and the receipt-deadline default — sourced OFFLINE from the registry alone. PURE over
|
|
180
|
+
// KNOWN_BACKENDS: no fs probe, no spawn, and never a live subscription CLI run (the detector as a
|
|
181
|
+
// whole spawns nothing — a source-level pin holds that).
|
|
182
|
+
export const backendCapability = (backendName) => {
|
|
183
|
+
const entry = KNOWN_BACKENDS.find((b) => b.name === backendName);
|
|
184
|
+
if (entry === undefined) return null;
|
|
185
|
+
return {
|
|
186
|
+
roles: Object.keys(entry.roleCmds ?? {}),
|
|
187
|
+
reviewContract: (entry.roleContracts?.review ?? null) !== null,
|
|
188
|
+
deadlineDefaultS: entry.deadlineDefaultS,
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
|
|
172
192
|
// ── pure helpers ─────────────────────────────────────────────────────────────
|
|
173
193
|
|
|
174
194
|
// Expand a leading "~" / "~/x" against home; absolute and relative paths pass through untouched.
|
|
@@ -329,6 +349,7 @@ export const detectBackend = (entry, deps = {}) => {
|
|
|
329
349
|
wrappers,
|
|
330
350
|
readiness,
|
|
331
351
|
setupHint,
|
|
352
|
+
capability: backendCapability(entry.name),
|
|
332
353
|
};
|
|
333
354
|
};
|
|
334
355
|
|
package/tools/doc-parity.mjs
CHANGED
|
@@ -35,9 +35,13 @@ import {
|
|
|
35
35
|
import { SKIPPED_READONLY } from './setup-backends.mjs';
|
|
36
36
|
import { LATENT_ARM_NOTICE } from './review-state.mjs';
|
|
37
37
|
import { QUEUE_SHARED_RULE, LANDING_FROM_MAIN, NO_DEPENDENCIES_POSTURE, CLEANUP_OWNERSHIP_RULE, INCLUDE_IDENTITY_RULE, RESUME_VERIFY_RULE } from './worktrees.mjs';
|
|
38
|
-
// The flow
|
|
39
|
-
//
|
|
38
|
+
// The flow contract constants: the accepted schema version + the honest lagging-kit sentence
|
|
39
|
+
// (owned by the config validator), and the set-flow bookkeeping-floor residual (owned by the
|
|
40
|
+
// arming writer) — each pinned byte-exact into its mode doc(s).
|
|
40
41
|
import { FLOW_SCHEMA_VERSION, FLOW_LAGGING_KIT_CONTRACT } from './orchestration-config.mjs';
|
|
42
|
+
import { FLOW_BOOKKEEPING_FLOOR_RESIDUAL } from './set-flow.mjs';
|
|
43
|
+
import { FLOW_ARMED_HALVES_HEADER } from './procedures.mjs';
|
|
44
|
+
import { RECEIPT_DEADLINE_CONTRACT } from './receipt-deadline.mjs';
|
|
41
45
|
|
|
42
46
|
const KIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
43
47
|
|
|
@@ -49,6 +53,8 @@ const SETUP_DOC = 'references/modes/setup.md';
|
|
|
49
53
|
const REVIEW_STATE_DOC = 'references/modes/review-state.md';
|
|
50
54
|
const WORKTREES_DOC = 'references/modes/worktrees.md';
|
|
51
55
|
const PROCEDURES_DOC = 'references/modes/procedures.md';
|
|
56
|
+
const SET_FLOW_DOC = 'references/modes/set-flow.md';
|
|
57
|
+
const RECEIPT_DEADLINE_DOC = 'references/modes/receipt-deadline.md';
|
|
52
58
|
|
|
53
59
|
// A typed usage failure (exit 2) for the CLI parser — the codebase's typed-error idiom (no classes).
|
|
54
60
|
const usageFail = (message) => Object.assign(new Error(message), { exitCode: 2 });
|
|
@@ -120,13 +126,22 @@ export const BINDINGS = Object.freeze([
|
|
|
120
126
|
// a reworded mode doc dropping the per-owned-path × session-never-probed contract fails this pin
|
|
121
127
|
// plus the gate.
|
|
122
128
|
valueBinding('resume-verify-rule', RESUME_VERIFY_RULE, RESUME_VERIFY_RULE, [WORKTREES_DOC]),
|
|
123
|
-
// The flow
|
|
129
|
+
// The flow contract pins: (a) the accepted NUMERIC `flow` schema version renders into the
|
|
124
130
|
// procedures.md allowed-shape contract line — a bumped constant with an unchanged doc fails this
|
|
125
131
|
// pin plus the gate; (b) the honest lagging-kit sentence — what a pre-flow kit does on meeting a
|
|
126
|
-
// `flow` block, and
|
|
127
|
-
// exact exported sentence into
|
|
132
|
+
// `flow` block, and exactly what the now-armed set-flow floor can and cannot reach — renders as
|
|
133
|
+
// the exact exported sentence into BOTH flow-facing mode docs (amended with set-flow, P12);
|
|
134
|
+
// (c) the set-flow bookkeeping-floor residual — the honest boundary of what the arming floors
|
|
135
|
+
// decide — renders byte-exact into the set-flow mode doc; (d) the procedures armed-halves header —
|
|
136
|
+
// the session-start flow-state surface (P8) — renders into procedures.md.
|
|
128
137
|
valueBinding('flow-schema-version', FLOW_SCHEMA_VERSION, `\`"schema": ${FLOW_SCHEMA_VERSION}\``, [PROCEDURES_DOC]),
|
|
129
|
-
valueBinding('flow-lagging-kit', FLOW_LAGGING_KIT_CONTRACT, FLOW_LAGGING_KIT_CONTRACT, [PROCEDURES_DOC]),
|
|
138
|
+
valueBinding('flow-lagging-kit', FLOW_LAGGING_KIT_CONTRACT, FLOW_LAGGING_KIT_CONTRACT, [PROCEDURES_DOC, SET_FLOW_DOC]),
|
|
139
|
+
valueBinding('flow-bookkeeping-floor-residual', FLOW_BOOKKEEPING_FLOOR_RESIDUAL, FLOW_BOOKKEEPING_FLOOR_RESIDUAL, [SET_FLOW_DOC]),
|
|
140
|
+
valueBinding('flow-armed-halves-header', FLOW_ARMED_HALVES_HEADER, FLOW_ARMED_HALVES_HEADER, [PROCEDURES_DOC]),
|
|
141
|
+
// The receipt-deadline runner's contract sentence (Plan-3 Phase 4.1): arrival — never obligation
|
|
142
|
+
// satisfaction — is the tool's identity; a mode doc silently drifting off it would re-open the
|
|
143
|
+
// #50 misclassification this runner exists to close.
|
|
144
|
+
valueBinding('receipt-deadline-contract', RECEIPT_DEADLINE_CONTRACT, RECEIPT_DEADLINE_CONTRACT, [RECEIPT_DEADLINE_DOC]),
|
|
130
145
|
].map((b) => Object.freeze(b)));
|
|
131
146
|
|
|
132
147
|
// ── the pure checker (readText is injectable for hermetic tests) ────────────────────────
|