@sabaiway/agent-workflow-kit 5.1.0 → 5.3.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 +95 -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 +17 -7
- package/bridges/codex-cli-bridge/bin/codex-exec.sh +156 -36
- package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +228 -4
- 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 +10 -7
- package/bridges/codex-cli-bridge/references/driving-codex.md +7 -5
- package/bridges/codex-cli-bridge/references/sandbox-and-flags.md +26 -12
- 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/references/scripts/archive-decisions.mjs +340 -15
- package/references/scripts/archive-decisions.test.mjs +522 -2
- 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 +33 -11
- package/tools/dispatch-record.mjs +926 -0
- package/tools/doc-parity.mjs +21 -6
- package/tools/flow-check.mjs +842 -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/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
|
}
|
|
@@ -86,23 +86,27 @@ const RAW_BACKENDS = [
|
|
|
86
86
|
'threat model: the sidecar byte and grammar screens detect corrupted input under a trusted parent environment. A hostile parent environment — including exported shell functions or PATH substitution of core/backend commands — is outside the threat model and can substitute the backend itself. Targeted shadow-proof resolution protects banner/dispatch honesty from accidental shadowing; it is not an environment security boundary',
|
|
87
87
|
'the exec posture banner appends a banner-only timeout=<duration|uncapped> field — exactly the duration handed to timeout(1), uncapped when no timeout/gtimeout binary caps the run; INFORMATIONAL only: it is never persisted in a receipt or session sidecar',
|
|
88
88
|
'quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated posture; a prose re-type drifts',
|
|
89
|
+
'every-run nested-sandbox scan (DUAL policy, deliberately two different rules): the scan runs on EVERY completed run, not only a failed one, because a run that SURVIVES the nested-sandbox failure exits 0 with an ungrounded answer and nothing said so. Failed run (rc != 0): the existing loose whole-trace combination rule prints the recovery hint. Successful run (rc == 0): a warning fires ONLY on precise per-item evidence — both a sandbox-mechanism token AND a permission/read-only failure token inside the aggregated_output of ONE command_execution item whose failure is PROVEN (a nonzero exit_code, or the serialized status "failed"); a null exit_code is never failure by itself, tokens split across two items never fire, and a successful command\'s output never fires. The answer is printed FIRST on stdout, then the warning on stderr. HONEST RESIDUAL: the exit status does NOT change on that lane (a distinct nonzero exit would give a heuristic scan DENY polarity, refusing real work whenever the scan over-warns), so an orchestrator keying on exit status alone can still bank an ungrounded answer — the stderr warning is the signal',
|
|
89
90
|
],
|
|
90
91
|
},
|
|
91
92
|
review: {
|
|
92
93
|
invocations: [
|
|
93
|
-
'codex-review plan <plan-file>',
|
|
94
|
-
'codex-review code [extra focus...]',
|
|
94
|
+
'codex-review plan <plan-file> [--nonce <n>]',
|
|
95
|
+
'codex-review code [--nonce <n>] [extra focus...]',
|
|
95
96
|
],
|
|
96
97
|
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
98
|
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',
|
|
99
|
+
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
100
|
notes: [
|
|
100
|
-
'the review posture banner appends a banner-only timeout=<duration
|
|
101
|
+
'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
102
|
'quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated posture; a prose re-type drifts',
|
|
102
103
|
],
|
|
103
104
|
},
|
|
104
105
|
},
|
|
105
106
|
bin: 'codex',
|
|
107
|
+
// The per-backend receipt-deadline default (seconds) the capability block reports — the review
|
|
108
|
+
// wrapper's built-in hard cap (CODEX_HARD_TIMEOUT review default), an OFFLINE registry fact.
|
|
109
|
+
deadlineDefaultS: 1800,
|
|
106
110
|
credential: { env: 'CODEX_HOME', default: '~/.codex', file: 'auth.json' },
|
|
107
111
|
setupUrl: 'https://github.com/sabaiway/agent-workflow/blob/main/codex-cli-bridge/setup/README.md',
|
|
108
112
|
setupPathLocal: 'setup/README.md',
|
|
@@ -118,9 +122,9 @@ const RAW_BACKENDS = [
|
|
|
118
122
|
roleContracts: {
|
|
119
123
|
review: {
|
|
120
124
|
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 "…"]',
|
|
125
|
+
'agy-review code [--facts @f] [--ungrounded] [--decided @f] [--focus "…"] [--nonce <n>] [extra focus…]',
|
|
126
|
+
'agy-review plan <plan-file> [--facts @f] [--decided @f] [--focus "…"] [--nonce <n>]',
|
|
127
|
+
'agy-review diff <diff-file> [--facts @f] [--decided @f] [--focus "…"] [--nonce <n>]',
|
|
124
128
|
],
|
|
125
129
|
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
130
|
flags: [
|
|
@@ -128,20 +132,23 @@ const RAW_BACKENDS = [
|
|
|
128
132
|
'--ungrounded — deliberately ungrounded CODE review, a throwaway opinion (code mode only, contradicts --facts; the receipt records grounded:false and never attests)',
|
|
129
133
|
'--decided @f — already-decided / already-addressed list; do NOT re-raise (anti-circling; the round-2 payload)',
|
|
130
134
|
'--focus "…" — extra focus (repeatable; code mode also takes trailing focus words)',
|
|
135
|
+
'--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
136
|
],
|
|
132
137
|
continue: [
|
|
133
|
-
'agy-review --continue [--decided @f] [--focus "…"]',
|
|
134
|
-
'agy-review --conversation <id> [--decided @f] [--focus "…"]',
|
|
138
|
+
'agy-review --continue [--decided @f] [--focus "…"] [--nonce <n>]',
|
|
139
|
+
'agy-review --conversation <id> [--decided @f] [--focus "…"] [--nonce <n>]',
|
|
135
140
|
],
|
|
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",
|
|
141
|
+
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
142
|
notes: [
|
|
138
143
|
'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
|
|
144
|
+
'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
145
|
'quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated posture; a prose re-type drifts',
|
|
141
146
|
],
|
|
142
147
|
},
|
|
143
148
|
},
|
|
144
149
|
bin: 'agy',
|
|
150
|
+
// AGY_HARD_TIMEOUT's built-in review default is 30m — reported in seconds, an OFFLINE registry fact.
|
|
151
|
+
deadlineDefaultS: 1800,
|
|
145
152
|
credential: { env: null, default: '~/.gemini/antigravity-cli', file: 'antigravity-oauth-token' },
|
|
146
153
|
setupUrl: 'https://github.com/sabaiway/agent-workflow/blob/main/antigravity-cli-bridge/setup/README.md',
|
|
147
154
|
setupPathLocal: 'setup/README.md',
|
|
@@ -169,6 +176,20 @@ export const wrapperCmdFor = (backendName, role) =>
|
|
|
169
176
|
export const wrapperContractFor = (backendName, role) =>
|
|
170
177
|
KNOWN_BACKENDS.find((b) => b.name === backendName)?.roleContracts?.[role] ?? null;
|
|
171
178
|
|
|
179
|
+
// The declared per-backend CAPABILITY block (flow-orchestration #15): roles, review-contract
|
|
180
|
+
// presence, and the receipt-deadline default — sourced OFFLINE from the registry alone. PURE over
|
|
181
|
+
// KNOWN_BACKENDS: no fs probe, no spawn, and never a live subscription CLI run (the detector as a
|
|
182
|
+
// whole spawns nothing — a source-level pin holds that).
|
|
183
|
+
export const backendCapability = (backendName) => {
|
|
184
|
+
const entry = KNOWN_BACKENDS.find((b) => b.name === backendName);
|
|
185
|
+
if (entry === undefined) return null;
|
|
186
|
+
return {
|
|
187
|
+
roles: Object.keys(entry.roleCmds ?? {}),
|
|
188
|
+
reviewContract: (entry.roleContracts?.review ?? null) !== null,
|
|
189
|
+
deadlineDefaultS: entry.deadlineDefaultS,
|
|
190
|
+
};
|
|
191
|
+
};
|
|
192
|
+
|
|
172
193
|
// ── pure helpers ─────────────────────────────────────────────────────────────
|
|
173
194
|
|
|
174
195
|
// Expand a leading "~" / "~/x" against home; absolute and relative paths pass through untouched.
|
|
@@ -329,6 +350,7 @@ export const detectBackend = (entry, deps = {}) => {
|
|
|
329
350
|
wrappers,
|
|
330
351
|
readiness,
|
|
331
352
|
setupHint,
|
|
353
|
+
capability: backendCapability(entry.name),
|
|
332
354
|
};
|
|
333
355
|
};
|
|
334
356
|
|