@sabaiway/agent-workflow-kit 10.4.0 → 10.5.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/README.md +1 -1
- package/SKILL.md +1 -1
- package/bridges/antigravity-cli-bridge/SKILL.md +7 -1
- package/bridges/antigravity-cli-bridge/bin/agy-review.sh +69 -17
- package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +73 -2
- package/bridges/antigravity-cli-bridge/capability.json +2 -2
- package/bridges/antigravity-cli-bridge/references/review-prompt.md +3 -0
- package/bridges/codex-cli-bridge/SKILL.md +8 -1
- package/bridges/codex-cli-bridge/bin/codex-exec.sh +1 -1
- package/bridges/codex-cli-bridge/bin/codex-review-honesty.test.mjs +1 -1
- package/bridges/codex-cli-bridge/bin/codex-review.sh +89 -18
- package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +55 -2
- package/bridges/codex-cli-bridge/capability.json +2 -2
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/agents/review-lens.md +5 -3
- package/references/modes/agents.md +1 -1
- package/references/modes/procedures.md +9 -5
- package/references/modes/recipes.md +2 -2
- package/references/modes/set-recipe.md +4 -4
- package/references/modes/status.md +1 -1
- package/references/modes/velocity.md +1 -0
- package/references/templates/orchestration.json +1 -1
- package/tools/bridge-posture.mjs +48 -0
- package/tools/carriers.mjs +21 -9
- package/tools/cheap-agents-read.mjs +86 -24
- package/tools/cheap-agents.mjs +47 -7
- package/tools/detect-backends.mjs +2 -2
- package/tools/direct-run.mjs +3 -0
- package/tools/fold-scope.mjs +5 -60
- package/tools/grounding.mjs +2 -2
- package/tools/orchestration-config.mjs +19 -78
- package/tools/orchestration-readme.mjs +70 -0
- package/tools/plan-shape-cli.mjs +112 -0
- package/tools/plan-shape-facts.mjs +204 -0
- package/tools/plan-shape.mjs +348 -0
- package/tools/procedures.mjs +132 -31
- package/tools/recipes.mjs +60 -79
- package/tools/repo-lex.mjs +40 -0
- package/tools/review-roster-resolve.mjs +104 -0
- package/tools/review-roster.mjs +128 -0
- package/tools/review-rounds-cli.mjs +92 -0
- package/tools/review-rounds.mjs +115 -0
- package/tools/set-recipe-roster.mjs +167 -0
- package/tools/set-recipe.mjs +80 -23
- package/tools/velocity-profile.mjs +8 -22
|
@@ -60,8 +60,14 @@ Receipt:
|
|
|
60
60
|
untracked-not-ignored contents — the review-payload domain; never-committable untracked paths —
|
|
61
61
|
character/block devices, FIFOs, sockets — are excluded from the domain entirely, untracked
|
|
62
62
|
symlinks/directories ride as name-only notes) in code mode, the artifact-file
|
|
63
|
-
sha256 in plan mode;
|
|
64
|
-
|
|
63
|
+
sha256 in plan mode; durationS = integer wall-clock seconds from CLI start through verdict parsing
|
|
64
|
+
and the wrapper prints review duration: <n>s; blocking = the count of [blocker] and [major] lines
|
|
65
|
+
(schema mode: findings with either severity; a payload whose findings cannot be counted fails
|
|
66
|
+
the run, no receipt); artifactPath = the normalized realpath on plan receipts only (repo-relative
|
|
67
|
+
inside the work tree, absolute otherwise), while a double quote,
|
|
68
|
+
backslash or control byte refuses pre-spend because the receipt encoder cannot carry it; verdict
|
|
69
|
+
parsed from the mandated literal verdict line (schema mode: the verdict field); always
|
|
70
|
+
fresh:true (one-shot) + grounded:true (native AGENTS.md auto-merge,
|
|
65
71
|
factsHash null); probe = whether the run relaxed the quality guards (CODEX_PROBE=1), written on
|
|
66
72
|
EVERY receipt so it self-declares — the kit's review-state gate rejects a probe-marked receipt (a
|
|
67
73
|
probe review never attests) and equally rejects an unmarked one (silence is not a declaration);
|
|
@@ -107,7 +113,7 @@ Honesty + posture (D4/D5):
|
|
|
107
113
|
refuses pre-spend in every mode.
|
|
108
114
|
|
|
109
115
|
Environment: CODEX_REVIEW_SCHEMA=1 (structured JSON findings), CODEX_HARD_TIMEOUT (seconds, default 1800), CODEX_PROBE=1 (throwaway probe only), AW_REVIEW_RECEIPTS (receipt file override), AW_REVIEW_NONCE (dispatch nonce — mints the finding manifest beside the receipt; the --nonce <n> flag is its plain-argument equivalent).
|
|
110
|
-
Requires at run time: the codex CLI on PATH, a ChatGPT-subscription login, a git work tree with a root AGENTS.md (--help needs none of these).
|
|
116
|
+
Requires at run time: the codex CLI on PATH, a ChatGPT-subscription login, Node >= 22, a git work tree with a root AGENTS.md (--help needs none of these).
|
|
111
117
|
HELP
|
|
112
118
|
exit 0
|
|
113
119
|
;;
|
|
@@ -278,7 +284,7 @@ DEFAULT_CODEX_EFFORT="xhigh"
|
|
|
278
284
|
# Review-receipt identity (AD-038). AW_BRIDGE_VERSION mirrors this bridge's SKILL.md/capability.json
|
|
279
285
|
# version (drift-guarded by codex-review.test.mjs against capability.json).
|
|
280
286
|
AW_RECEIPT_BACKEND="codex"
|
|
281
|
-
AW_BRIDGE_VERSION="3.
|
|
287
|
+
AW_BRIDGE_VERSION="3.6.0" # aw-version-anchor
|
|
282
288
|
CODEX_MODEL="${CODEX_MODEL:-$DEFAULT_CODEX_MODEL}"
|
|
283
289
|
CODEX_EFFORT="${CODEX_EFFORT:-$DEFAULT_CODEX_EFFORT}"
|
|
284
290
|
# Generous hard cap for a slow xhigh review (subscription latency varies).
|
|
@@ -550,6 +556,42 @@ receipt_json_scalar() {
|
|
|
550
556
|
if [[ -z "${1:-}" ]]; then printf 'null'; else printf '"%s"' "$1"; fi
|
|
551
557
|
}
|
|
552
558
|
|
|
559
|
+
# refuse_uncarriable_artifact_byte <label> <value> — the receipt encoder escapes nothing beyond its
|
|
560
|
+
# grammar-bound fields, so an artifact path is BOUNDED, never escaped: a byte the line could not
|
|
561
|
+
# carry refuses pre-spend, by name.
|
|
562
|
+
refuse_uncarriable_artifact_byte() {
|
|
563
|
+
local label="$1" value="$2" byte=""
|
|
564
|
+
case "$value" in
|
|
565
|
+
*'"'*) byte="a double quote" ;;
|
|
566
|
+
*'\'*) byte="a backslash" ;;
|
|
567
|
+
*[$'\x01'-$'\x1f'$'\x7f']*) byte="a control" ;;
|
|
568
|
+
esac
|
|
569
|
+
if [[ -z "$byte" ]]; then return 0; fi
|
|
570
|
+
echo "error: $label contains $byte byte, which the receipt encoder cannot carry." >&2
|
|
571
|
+
return 2
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
normalize_artifact_path() {
|
|
575
|
+
local input="$1" normalized
|
|
576
|
+
refuse_uncarriable_artifact_byte "artifact path" "$input" || return 2
|
|
577
|
+
if ! normalized="$(node -e '
|
|
578
|
+
const { realpathSync } = require("node:fs");
|
|
579
|
+
const { isAbsolute, relative, sep } = require("node:path");
|
|
580
|
+
const { spawnSync } = require("node:child_process");
|
|
581
|
+
const absolute = realpathSync(process.argv[1]);
|
|
582
|
+
const git = spawnSync("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" });
|
|
583
|
+
const root = git.status === 0 ? realpathSync(git.stdout.replace(/\r?\n$/, "")) : null;
|
|
584
|
+
const rel = root === null ? null : relative(root, absolute);
|
|
585
|
+
const contained = rel !== null && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
586
|
+
process.stdout.write((contained ? rel : absolute).split(sep).join("/"));
|
|
587
|
+
' -- "$input" 2>/dev/null)"; then
|
|
588
|
+
echo "error: artifact path '$input' could not be normalized to a real path." >&2
|
|
589
|
+
return 2
|
|
590
|
+
fi
|
|
591
|
+
refuse_uncarriable_artifact_byte "normalized artifact path" "$normalized" || return 2
|
|
592
|
+
printf '%s' "$normalized"
|
|
593
|
+
}
|
|
594
|
+
|
|
553
595
|
# STRICT JSON string encoding for the free-form posture fields (model/effort ride env/settings):
|
|
554
596
|
# backslash then double-quote escaped. Control bytes never reach here — the D5 pre-spend gate
|
|
555
597
|
# refuses them, so these two escapes make the encoding total.
|
|
@@ -665,7 +707,7 @@ process.exit(code);
|
|
|
665
707
|
return 0
|
|
666
708
|
}
|
|
667
709
|
|
|
668
|
-
# write_review_receipt <artifact|""> <fresh: true|false> <fingerprint|""> <verdict> <grounded: true|false> <factsHash|""> [probe: true|false] [delivery|""] [findings-file]
|
|
710
|
+
# write_review_receipt <artifact|""> <fresh: true|false> <fingerprint|""> <verdict> <grounded: true|false> <factsHash|""> [probe: true|false] [delivery|""] [findings-file] [artifact-path] [durationS] [blocking]
|
|
669
711
|
# Appends ONE receipt line (the AD-038 fixture shape) as a side effect of a SUCCESSFUL review —
|
|
670
712
|
# to $AW_REVIEW_RECEIPTS when set, else <git dir>/agent-workflow-review-receipts.jsonl (inside the
|
|
671
713
|
# git dir by construction, so it is never committable). Fail-safe: every failure here warns loudly
|
|
@@ -679,6 +721,7 @@ process.exit(code);
|
|
|
679
721
|
# minted FIRST (atomic, no-clobber, ORDERED) and a failed mint EXCLUDES the receipt append.
|
|
680
722
|
write_review_receipt() {
|
|
681
723
|
local artifact="$1" fresh="$2" fingerprint="$3" verdict="$4" grounded="$5" facts_hash="$6" probe="${7:-false}" delivery="${8:-}" findings_file="${9:-}"
|
|
724
|
+
local artifact_path="${10:-}" duration_s="${11:-0}" blocking="${12:-0}"
|
|
682
725
|
local receipts="${AW_REVIEW_RECEIPTS:-}"
|
|
683
726
|
if [[ -z "$receipts" ]]; then
|
|
684
727
|
local receipt_git_dir
|
|
@@ -696,16 +739,16 @@ write_review_receipt() {
|
|
|
696
739
|
fi
|
|
697
740
|
# A nonce-SUPPLIED dispatch stamps its nonce into the receipt too (the flow round-land matcher
|
|
698
741
|
# requires exact {backend, nonce} equality — dispatch identity end-to-end); the nonce is
|
|
699
|
-
# grammar-safe by the pre-spend check
|
|
700
|
-
|
|
701
|
-
local line probe_field=',"probe":false' delivery_field="" nonce_field=""
|
|
742
|
+
# grammar-safe by the pre-spend check; a nonce-less receipt adds NO nonce field.
|
|
743
|
+
local line probe_field=',"probe":false' artifact_path_field="" delivery_field="" nonce_field=""
|
|
702
744
|
if [[ "$probe" == "true" ]]; then probe_field=',"probe":true'; fi
|
|
745
|
+
if [[ -n "$artifact_path" ]]; then artifact_path_field=",\"artifactPath\":\"$artifact_path\""; fi
|
|
703
746
|
if [[ -n "$delivery" ]]; then delivery_field=",\"delivery\":\"$delivery\""; fi
|
|
704
747
|
if [[ -n "${AW_REVIEW_NONCE:-}" ]]; then nonce_field=",\"nonce\":\"${AW_REVIEW_NONCE}\""; fi
|
|
705
|
-
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%s%s}' \
|
|
748
|
+
line="$(printf '{"schema":1,"artifact":%s,"fresh":%s,"fingerprint":%s,"backend":"%s","verdict":"%s","grounded":%s,"factsHash":%s,"wrapperVersion":"%s","timestamp":"%s"%s,"durationS":%s,"blocking":%s%s,"posture":%s%s%s}' \
|
|
706
749
|
"$(receipt_json_scalar "$artifact")" "$fresh" "$(receipt_json_scalar "$fingerprint")" \
|
|
707
750
|
"$AW_RECEIPT_BACKEND" "$verdict" "$grounded" "$(receipt_json_scalar "$facts_hash")" \
|
|
708
|
-
"$AW_BRIDGE_VERSION" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$probe_field" "$(posture_json)" "$delivery_field" "$nonce_field")"
|
|
751
|
+
"$AW_BRIDGE_VERSION" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$probe_field" "$duration_s" "$blocking" "$artifact_path_field" "$(posture_json)" "$delivery_field" "$nonce_field")"
|
|
709
752
|
if ! printf '%s\n' "$line" >>"$receipts" 2>/dev/null; then
|
|
710
753
|
echo "warning: could not append the review receipt to $receipts — the review itself succeeded;" >&2
|
|
711
754
|
echo " the review-state gate will read the current tree as un-receipted." >&2
|
|
@@ -835,8 +878,27 @@ if [[ "$nonce_flag_set" == "1" ]]; then
|
|
|
835
878
|
fi
|
|
836
879
|
set -- "${scan_args[@]+"${scan_args[@]}"}"
|
|
837
880
|
|
|
881
|
+
# Pre-spend node probe (the agy twin's posture): the plan artifact-path normalization, the schema
|
|
882
|
+
# parse and the nonce finding-manifest mint run in node, so a missing or too-old node refuses by
|
|
883
|
+
# name BEFORE any codex run — never a spent run with the wrong diagnostic.
|
|
884
|
+
AW_MIN_NODE_MAJOR=22
|
|
885
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
886
|
+
echo "error: 'node' is not on PATH. This review runs node for the plan artifact-path normalization," >&2
|
|
887
|
+
echo " the CODEX_REVIEW_SCHEMA parse and the nonce finding-manifest mint (family floor: Node >= ${AW_MIN_NODE_MAJOR})." >&2
|
|
888
|
+
echo " Refusing BEFORE any run is spent. Install Node >= ${AW_MIN_NODE_MAJOR}, then re-run." >&2
|
|
889
|
+
exit 127
|
|
890
|
+
fi
|
|
891
|
+
aw_node_version="$(node --version 2>/dev/null || true)"
|
|
892
|
+
aw_node_major="${aw_node_version#v}"; aw_node_major="${aw_node_major%%.*}"
|
|
893
|
+
if [[ ! "$aw_node_major" =~ ^[0-9]+$ ]] || (( aw_node_major < AW_MIN_NODE_MAJOR )); then
|
|
894
|
+
echo "error: node '${aw_node_version:-<unreadable>}' is below the family floor (Node >= ${AW_MIN_NODE_MAJOR}) this review needs." >&2
|
|
895
|
+
echo " Refusing BEFORE any run is spent. Upgrade Node to >= ${AW_MIN_NODE_MAJOR}, then re-run." >&2
|
|
896
|
+
exit 127
|
|
897
|
+
fi
|
|
898
|
+
|
|
838
899
|
REVIEW_ARTIFACT=""
|
|
839
900
|
REVIEW_FINGERPRINT=""
|
|
901
|
+
REVIEW_ARTIFACT_PATH=""
|
|
840
902
|
|
|
841
903
|
case "$mode" in
|
|
842
904
|
plan)
|
|
@@ -850,6 +912,7 @@ case "$mode" in
|
|
|
850
912
|
echo "error: unexpected arguments after plan file: $*" >&2
|
|
851
913
|
exit 2
|
|
852
914
|
fi
|
|
915
|
+
if ! REVIEW_ARTIFACT_PATH="$(normalize_artifact_path "$target")"; then exit 2; fi
|
|
853
916
|
# Plan-mode receipt identity: the artifact-file sha256 (informational-only for the tree checker).
|
|
854
917
|
REVIEW_ARTIFACT="plan"
|
|
855
918
|
REVIEW_FINGERPRINT="$(sha256_stdin <"$target" || true)"
|
|
@@ -974,6 +1037,7 @@ invoke_codex() {
|
|
|
974
1037
|
}
|
|
975
1038
|
|
|
976
1039
|
codex_cmd=("${codex_flags[@]}" -)
|
|
1040
|
+
review_started_at="$(date +%s)"
|
|
977
1041
|
invoke_codex
|
|
978
1042
|
|
|
979
1043
|
# Raw-text fallback: if the structured-findings run failed (not a timeout), retry
|
|
@@ -1028,22 +1092,29 @@ fi
|
|
|
1028
1092
|
verdict=""
|
|
1029
1093
|
if [[ -f "$out" && -s "$out" ]]; then
|
|
1030
1094
|
if [[ "$verdict_parse_mode" == "schema" ]]; then
|
|
1031
|
-
# STRUCTURAL parse
|
|
1032
|
-
#
|
|
1033
|
-
# inside a findings string never substitutes, malformed/out-of-enum
|
|
1034
|
-
# failed-run arm)
|
|
1035
|
-
|
|
1095
|
+
# STRUCTURAL parse: the TOP-LEVEL `verdict` inside the closed enum AND `findings` an array whose
|
|
1096
|
+
# every item carries a severity inside the closed enum, read ONCE — a legal multiline layout
|
|
1097
|
+
# parses, a decoy "verdict" inside a findings string never substitutes, malformed / out-of-enum /
|
|
1098
|
+
# uncountable stays empty (the D4 failed-run arm): a payload the wrapper cannot count is never
|
|
1099
|
+
# receipted as "0 blocking". Node >=22 is a family floor, so the one-liner adds no dependency.
|
|
1100
|
+
schema_parse="$(node -e 'try{const v=JSON.parse(require("node:fs").readFileSync(process.argv[1],"utf8"));const sev=["blocker","major","minor","nit"];if(["ship","revise","rethink"].includes(v.verdict)&&Array.isArray(v.findings)&&v.findings.every((f)=>f!==null&&typeof f==="object"&&sev.includes(f.severity)))process.stdout.write(v.verdict+"\t"+v.findings.filter((f)=>f.severity==="blocker"||f.severity==="major").length);}catch{}' "$out" 2>/dev/null || true)"
|
|
1101
|
+
verdict="${schema_parse%%$'\t'*}"
|
|
1102
|
+
blocking_count="${schema_parse#*$'\t'}"
|
|
1036
1103
|
else
|
|
1037
1104
|
verdict="$(sed -nE 's/^Verdict: (ship|revise|rethink)[[:space:]]*$/\1/p' "$out" | tail -n1)"
|
|
1105
|
+
blocking_count="$(awk '/^\[(blocker|major)\]([[:space:]]|$)/{n++} END{print n+0}' "$out")"
|
|
1038
1106
|
fi
|
|
1039
1107
|
fi
|
|
1040
1108
|
if [[ -z "$verdict" ]]; then
|
|
1041
1109
|
echo "error: the review produced no recognized 'Verdict: <ship|revise|rethink>' line (empty or" >&2
|
|
1042
|
-
echo " verdict-less final message
|
|
1043
|
-
echo "
|
|
1110
|
+
echo " verdict-less final message; in schema mode also a findings array the wrapper cannot" >&2
|
|
1111
|
+
echo " count) — a FAILED review; NO receipt was written. Re-run the review; if it recurs," >&2
|
|
1112
|
+
echo " inspect the captured output for what the model produced." >&2
|
|
1044
1113
|
exit 4
|
|
1045
1114
|
fi
|
|
1115
|
+
review_duration_s=$(( $(date +%s) - review_started_at ))
|
|
1046
1116
|
# codex is grounded by construction (AGENTS.md auto-merge + the precomputed change set): grounded
|
|
1047
1117
|
# true, factsHash null (native grounding — no separate facts payload exists). Every codex run is a
|
|
1048
1118
|
# full fresh run (one-shot, no resume) → fresh:true. $REVIEW_PROBE marks a guards-relaxed run.
|
|
1049
|
-
write_review_receipt "$REVIEW_ARTIFACT" true "$REVIEW_FINGERPRINT" "$verdict" true "" "$REVIEW_PROBE" "" "$out"
|
|
1119
|
+
write_review_receipt "$REVIEW_ARTIFACT" true "$REVIEW_FINGERPRINT" "$verdict" true "" "$REVIEW_PROBE" "" "$out" "$REVIEW_ARTIFACT_PATH" "$review_duration_s" "$blocking_count"
|
|
1120
|
+
echo "review duration: ${review_duration_s}s" >&2
|
|
@@ -315,6 +315,25 @@ describe('codex-review.sh — hard timeout (1.3)', { concurrency: 2 }, () => {
|
|
|
315
315
|
assert.match(r.stderr, /hard-timeout preflight fails CLOSED/);
|
|
316
316
|
assert.equal(r.capStdin, '', 'codex must NOT be invoked when the preflight refuses');
|
|
317
317
|
});
|
|
318
|
+
|
|
319
|
+
it('refuses by name BEFORE any run when node is missing or below the family floor', async () => {
|
|
320
|
+
const missing = makeSandbox();
|
|
321
|
+
const r1 = await run(missing, { path: `${missing.bin}:${farmFor(['node'])}` });
|
|
322
|
+
rmSync(missing.root, { recursive: true, force: true });
|
|
323
|
+
assert.equal(r1.status, 127);
|
|
324
|
+
assert.match(r1.stderr, /'node' is not on PATH/);
|
|
325
|
+
assert.equal(r1.capStdin, '', 'codex must NOT be invoked without node');
|
|
326
|
+
|
|
327
|
+
const old = makeSandbox();
|
|
328
|
+
const shim = join(old.root, 'node-shim');
|
|
329
|
+
mkdirSync(shim);
|
|
330
|
+
writeFileSync(join(shim, 'node'), '#!/bin/sh\necho v18.0.0\n', { mode: 0o755 });
|
|
331
|
+
const r2 = await run(old, { path: `${shim}:${old.bin}:${farmFor(['node'])}` });
|
|
332
|
+
rmSync(old.root, { recursive: true, force: true });
|
|
333
|
+
assert.equal(r2.status, 127);
|
|
334
|
+
assert.match(r2.stderr, /node 'v18\.0\.0' is below the family floor/);
|
|
335
|
+
assert.equal(r2.capStdin, '', 'codex must NOT be invoked on a too-old node');
|
|
336
|
+
});
|
|
318
337
|
});
|
|
319
338
|
|
|
320
339
|
describe('codex-review.sh — precomputed diff for code mode (2.1)', { concurrency: 2 }, () => {
|
|
@@ -901,7 +920,7 @@ describe('codex-review.sh — mode catalog ⟷ wrapper reality (manifest-pinned)
|
|
|
901
920
|
// The normative fixture: the AD-038 shape + the D3 self-declaring probe marker (field VALUES with
|
|
902
921
|
// dynamic content are asserted by shape):
|
|
903
922
|
const RECEIPT_FIXTURE = JSON.parse(
|
|
904
|
-
'{"schema":1,"artifact":"code","fresh":true,"fingerprint":"<sha256hex>","backend":"codex","verdict":"revise","grounded":true,"factsHash":null,"wrapperVersion":"2.3.0","timestamp":"2026-07-03T12:00:00Z","probe":false,"posture":{"model":"<m>","effort":"<e>","tier":null}}',
|
|
923
|
+
'{"schema":1,"artifact":"code","fresh":true,"fingerprint":"<sha256hex>","backend":"codex","verdict":"revise","grounded":true,"factsHash":null,"wrapperVersion":"2.3.0","timestamp":"2026-07-03T12:00:00Z","probe":false,"durationS":0,"blocking":0,"posture":{"model":"<m>","effort":"<e>","tier":null}}',
|
|
905
924
|
);
|
|
906
925
|
const RECEIPTS_REL = join('.git', 'agent-workflow-review-receipts.jsonl');
|
|
907
926
|
const readReceipts = (repo) => {
|
|
@@ -913,6 +932,7 @@ const sha256Hex = (buf) => createHash('sha256').update(buf).digest('hex');
|
|
|
913
932
|
|
|
914
933
|
describe('codex-review.sh — review receipts (AD-038)', { concurrency: 2 }, () => {
|
|
915
934
|
it('a successful code review appends ONE fixture-shaped receipt (text-mode verdict parse)', async () => {
|
|
935
|
+
// spec:plan-review-loop/S20
|
|
916
936
|
const sb = makeSandbox();
|
|
917
937
|
const r = await run(sb, { env: { CODEX_FAKE_FINAL: '[major] — a.txt:1 — x — y\nVerdict: revise' } });
|
|
918
938
|
const receipts = readReceipts(sb.repo);
|
|
@@ -931,6 +951,11 @@ describe('codex-review.sh — review receipts (AD-038)', { concurrency: 2 }, ()
|
|
|
931
951
|
assert.equal(receipt.factsHash, null, 'native grounding — no separate facts payload');
|
|
932
952
|
assert.equal(receipt.wrapperVersion, MANIFEST.version, 'receipt version ⟷ capability.json version');
|
|
933
953
|
assert.match(receipt.timestamp, /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/);
|
|
954
|
+
assert.ok(Number.isInteger(receipt.durationS) && receipt.durationS >= 0);
|
|
955
|
+
assert.equal(receipt.blocking, 1, 'one [major] line is blocking in the codex vocabulary');
|
|
956
|
+
assert.equal(Object.hasOwn(receipt, 'artifactPath'), false, 'a code receipt carries no artifact path');
|
|
957
|
+
assert.match(r.stderr, /review duration: \d+s/);
|
|
958
|
+
for (const field of ['durationS', 'blocking', 'artifactPath']) assert.ok(REVIEW_CONTRACT.receipt.includes(`${field} = `), `the manifest receipt contract declares ${field}`);
|
|
934
959
|
});
|
|
935
960
|
|
|
936
961
|
// The probe marker (BRIDGE-MODES-CATALOG, D3): a CODEX_PROBE=1 review runs with the
|
|
@@ -986,12 +1011,26 @@ describe('codex-review.sh — review receipts (AD-038)', { concurrency: 2 }, ()
|
|
|
986
1011
|
it('CODEX_REVIEW_SCHEMA=1 reads the schema verdict field', async () => {
|
|
987
1012
|
const sb = makeSandbox();
|
|
988
1013
|
const r = await run(sb, {
|
|
989
|
-
env: { CODEX_REVIEW_SCHEMA: '1', CODEX_FAKE_FINAL: '{"findings":[],"verdict":"ship","notes":"ok"}' },
|
|
1014
|
+
env: { CODEX_REVIEW_SCHEMA: '1', CODEX_FAKE_FINAL: '{"findings":[{"severity":"blocker"},{"severity":"major"},{"severity":"minor"}],"verdict":"ship","notes":"ok"}' },
|
|
990
1015
|
});
|
|
991
1016
|
const receipts = readReceipts(sb.repo);
|
|
992
1017
|
rmSync(sb.root, { recursive: true, force: true });
|
|
993
1018
|
assert.equal(r.status, 0, r.stderr);
|
|
994
1019
|
assert.equal(receipts[0].verdict, 'ship');
|
|
1020
|
+
assert.equal(receipts[0].blocking, 2, 'schema mode counts blocker and major findings only');
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
it('schema mode: findings the wrapper cannot count fail the run with NO receipt', async () => {
|
|
1024
|
+
// spec:plan-review-loop/S28
|
|
1025
|
+
for (const payload of ['{"findings":"nope","verdict":"ship"}', '{"findings":[{"severity":"huge"}],"verdict":"ship"}']) {
|
|
1026
|
+
const sb = makeSandbox();
|
|
1027
|
+
const r = await run(sb, { env: { CODEX_REVIEW_SCHEMA: '1', CODEX_FAKE_FINAL: payload } });
|
|
1028
|
+
const receipts = readReceipts(sb.repo);
|
|
1029
|
+
rmSync(sb.root, { recursive: true, force: true });
|
|
1030
|
+
assert.equal(r.status, 4, payload);
|
|
1031
|
+
assert.match(r.stderr, /findings array the wrapper cannot/, payload);
|
|
1032
|
+
assert.equal(receipts.length, 0, `${payload}: an uncountable payload never reaches the receipt store`);
|
|
1033
|
+
}
|
|
995
1034
|
});
|
|
996
1035
|
|
|
997
1036
|
it('no parseable verdict is a FAILED run — never recorded as "unknown" (D4 owns the arm)', async () => {
|
|
@@ -1004,6 +1043,7 @@ describe('codex-review.sh — review receipts (AD-038)', { concurrency: 2 }, ()
|
|
|
1004
1043
|
});
|
|
1005
1044
|
|
|
1006
1045
|
it('plan mode: artifact "plan", fingerprint = the artifact-file sha256', async () => {
|
|
1046
|
+
// spec:plan-review-loop/S21
|
|
1007
1047
|
const sb = makeSandbox();
|
|
1008
1048
|
const planBytes = readFileSync(join(sb.repo, 'plan.md'));
|
|
1009
1049
|
const r = await run(sb, { args: ['plan', 'plan.md'], env: { CODEX_FAKE_FINAL: 'Verdict: ship' } });
|
|
@@ -1012,6 +1052,19 @@ describe('codex-review.sh — review receipts (AD-038)', { concurrency: 2 }, ()
|
|
|
1012
1052
|
assert.equal(r.status, 0, r.stderr);
|
|
1013
1053
|
assert.equal(receipts[0].artifact, 'plan');
|
|
1014
1054
|
assert.equal(receipts[0].fingerprint, sha256Hex(planBytes), 'plan fingerprint = file sha256');
|
|
1055
|
+
assert.equal(receipts[0].artifactPath, 'plan.md', 'realpath inside the work tree normalizes repo-relative');
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
it('plan mode refuses an uncarriable artifact-path byte before codex runs', async () => {
|
|
1059
|
+
for (const [name, diagnostic] of [['bad"name.md', /double quote/], ['bad\\name.md', /backslash/], ['bad\nname.md', /control byte/]]) {
|
|
1060
|
+
const sb = makeSandbox();
|
|
1061
|
+
writeFileSync(join(sb.repo, name), '# plan\n');
|
|
1062
|
+
const r = await run(sb, { args: ['plan', name] });
|
|
1063
|
+
rmSync(sb.root, { recursive: true, force: true });
|
|
1064
|
+
assert.equal(r.status, 2, name);
|
|
1065
|
+
assert.match(r.stderr, diagnostic, name);
|
|
1066
|
+
assert.equal(r.capStdin, '', `${name}: refusal is pre-spend`);
|
|
1067
|
+
}
|
|
1015
1068
|
});
|
|
1016
1069
|
|
|
1017
1070
|
it('AW_REVIEW_RECEIPTS overrides the receipt destination', async () => {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"schema": 1,
|
|
4
4
|
"name": "codex-cli-bridge",
|
|
5
5
|
"kind": "execution-backend",
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.6.0",
|
|
7
7
|
"posture": { "model": "gpt-5.6-sol", "effort": "xhigh", "tier": null },
|
|
8
8
|
"provides": ["execute", "review"],
|
|
9
9
|
"roles": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
],
|
|
50
50
|
"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",
|
|
51
51
|
"continue": [],
|
|
52
|
-
"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",
|
|
52
|
+
"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; durationS = integer wall-clock seconds from CLI start through verdict parsing and the wrapper prints review duration: <n>s; blocking = the count of [blocker] and [major] lines (schema mode: findings with either severity; a payload whose findings cannot be counted fails the run, no receipt); artifactPath = the normalized realpath on plan receipts only (repo-relative inside the work tree, absolute otherwise), while a double quote, backslash or control byte refuses pre-spend because the receipt encoder cannot carry it; 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",
|
|
53
53
|
"notes": [
|
|
54
54
|
"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",
|
|
55
55
|
"quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated posture; a prose re-type drifts"
|
package/capability.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sabaiway/agent-workflow-kit",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.5.0",
|
|
4
4
|
"description": "Portable, cross-agent memory & workflow for AI coding agents — Claude Code, Codex, Cursor, Devin Desktop. One command deploys an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement into any repo.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review-lens
|
|
3
|
-
description: Read-only ADDITIONAL review lens — an
|
|
3
|
+
description: Read-only ADDITIONAL review lens — an independent opinion additional to the orchestrator's own review and to whatever configured backends ran; never a substitute for either. Grants no shell, so it can never turn a review into a wave of approval prompts. Never for writing code or running gates.
|
|
4
4
|
model: sonnet
|
|
5
5
|
effort: high
|
|
6
6
|
tools: Read, Grep, Glob
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
You are an ADDITIONAL, INDEPENDENT review lens.
|
|
10
|
-
|
|
9
|
+
You are an ADDITIONAL, INDEPENDENT review lens. Your opinion is additional to the orchestrator's own
|
|
10
|
+
review and to whatever configured backends ran; never a substitute for either. Find what those
|
|
11
|
+
reviews MISSED, rather than restating what they found.
|
|
11
12
|
|
|
12
13
|
You have `Read`, `Grep` and `Glob` and **no `Bash`**. That is deliberate and it is the whole point of
|
|
13
14
|
this vehicle: a read-only fan-out that can reach for a shell turns one review into a wave of approval
|
|
@@ -37,3 +38,4 @@ How to review:
|
|
|
37
38
|
Output: a numbered list of findings, most severe first, each as
|
|
38
39
|
`[severity] — file:line — the defect — the failure scenario — the fix direction`.
|
|
39
40
|
Then one line: `no further findings` or the angles you deliberately did not cover.
|
|
41
|
+
Verdict: ship | ship with nits | revise | rethink
|
|
@@ -12,7 +12,7 @@ The opt-in **subagent-vehicle writer** — the family's second `.claude/` writer
|
|
|
12
12
|
|
|
13
13
|
Writing code, running gates, and user-facing copy never move to these four vehicles.
|
|
14
14
|
|
|
15
|
-
**The fifth vehicle, `executor`, is the ONE full-tool one** (`model: opus`, every tool — a shell included). It is the instrument behind the **Subagent** recipe, dispatched ONLY for a **bounded, file-disjoint slice** the orchestrator verifies afterwards by running its suites: a slice of execution (`plan-execution.execute`), a plan/contract authoring brief (`plan-authoring.author`), or a bounded chore (`routine.carrier`). It **never commits** (no git write at all), is **never a review backend**, is **never a bridge substitute**, and is **never dispatched for read-only work** — a sweep, an inventory or a review rides a read-only vehicle instead.
|
|
15
|
+
**The fifth vehicle, `executor`, is the ONE full-tool one** (`model: opus`, every tool — a shell included). It is the instrument behind the **Subagent** recipe, dispatched ONLY for a **bounded, file-disjoint slice** the orchestrator verifies afterwards by running its suites: a slice of execution (`plan-execution.execute`), a plan/contract authoring brief (`plan-authoring.author`), an authoring-round fold (`plan-authoring.fold`), or a bounded chore (`routine.carrier`). It **never commits** (no git write at all), is **never a review backend**, is **never a bridge substitute**, and is **never dispatched for read-only work** — a sweep, an inventory or a review rides a read-only vehicle instead.
|
|
16
16
|
|
|
17
17
|
**Its readiness is the vehicle FILE.** `surveyExecutorVehicle` answers exactly one of four states: **`placed`** (the bundled bytes at `.claude/agents/executor.md`), **`customized`** (a readable regular file whose frontmatter names `executor` and whose tools are not read-only), **`unusable`** (a symlink, a non-regular or unreadable file, or one failing that check — with the reason), **`missing`**. `placed` and `customized` are dispatchable; on `unusable` or `missing` a `subagent` slot **degrades to Solo** with a reason naming this mode's apply command. `/agent-workflow-kit status` shows the state in its `agents` block and the deployment advisor raises it when a configured slot names `subagent` and the vehicle is `unusable` or `missing` — that item is **`executor-vehicle`** (attention-class: a configured declaration that is broken), a DIFFERENT capability from this mode's placement offer (`agents`), which converges the moment nothing is left to place and therefore can never observe a present-but-unusable vehicle. It is a claim about the FILE, never about the host: a Claude Code lane, like this mode itself.
|
|
18
18
|
|
|
@@ -6,19 +6,19 @@ Read-only **activity-procedures advisor**. Answers *"what are the steps of this
|
|
|
6
6
|
|
|
7
7
|
The three activities (canon in the **installed engine**, `references/procedures.md`; the registry is `tools/carriers.mjs`):
|
|
8
8
|
|
|
9
|
-
- **`plan-authoring`** (slots: `author`, `review`) — research → draft (the resolved `author` carrier drafts: Solo writes it, Subagent drafts from the orchestrator's brief) → self-review → **review {recipe}** → fold/loop → present for approval; enforce the mandatory Cleanup.
|
|
9
|
+
- **`plan-authoring`** (slots: `author`, `fold`, `review`) — research → draft (the resolved `author` carrier drafts: Solo writes it, Subagent drafts from the orchestrator's brief) → self-review with the readers sweep first → **review {recipe}** → consult, then fold/loop through the resolved `fold` carrier → present for approval; enforce the mandatory Cleanup.
|
|
10
10
|
- **`plan-execution`** (slots: `execute`, `review`) — per ledger row: resolve the recipe → if Delegated, dispatch execution first; if Subagent, dispatch file-disjoint slices to the executor vehicle and verify each returned slice → implement → self-review → **review {recipe}** → gates → commit boundary; after the last row, the project-declared stages and Phase: Cleanup run as rows of their own.
|
|
11
11
|
- **`routine`** (slots: `carrier`, `parallel`) — name the chore and its file-disjoint slices → resolve → carry it (Solo, or Subagent: a read-only slice rides a placed read-only vehicle, a write-capable one rides the executor; `parallel` on → concurrently) → verify every returned slice; the commit boundary is unchanged.
|
|
12
12
|
|
|
13
|
-
Value sets per slot type: `review` accepts `solo | reviewed | council`; `execute` accepts `solo | delegated | subagent`; a carrier slot (`author`, `carrier`) accepts `solo | subagent`; `parallel` is a **switch**, `on | off`, resolved outside the recipe lattice. Defaults when the config is silent: `review` → Reviewed if a review-capable backend is ready, else Solo; `author`, `execute`, `carrier` → Solo (a placed executor never flips a default); `parallel` → on. The orchestrator owns every commit; every other carrier — a bridge or a subagent — never commits.
|
|
13
|
+
Value sets per slot type: `review` accepts `solo | reviewed | council`; `execute` accepts `solo | delegated | subagent`; a carrier slot (`author`, `fold`, `carrier`) accepts `solo | subagent`; `parallel` is a **switch**, `on | off`, resolved outside the recipe lattice. Defaults when the config is silent: `review` → Reviewed if a review-capable backend is ready, else Solo; `author`, `fold`, `execute`, `carrier` → Solo (a placed executor never flips a default); `parallel` → on. The orchestrator owns every commit; every other carrier — a bridge or a subagent — never commits.
|
|
14
14
|
|
|
15
15
|
Run **`node ${CLAUDE_SKILL_DIR}/tools/procedures.mjs <activity> [--override <slot>=<value>]… [--json]`**. It reads the activity's steps live from the engine and prints them **verbatim**, then the **resolved effective recipe per slot** from the per-project config + the read-only backend detector:
|
|
16
16
|
|
|
17
|
-
1. **Config = `docs/ai/orchestration.json`** — strict JSON, **agent-writable via `/agent-workflow-kit set-recipe` (`${CLAUDE_SKILL_DIR}/references/modes/set-recipe.md`) OR hand-edited** (the kit reads + validates it; `procedures`/`recipes` stay read-only — the writer is `set-recipe`). Shape: `{ "<activity>": { "<slot>": "<value>" } }`; all slots optional (an absent slot → its computed default, stated); an optional `"_README"` string is allowed + ignored; a `"flow"` object must carry the NUMERIC `"schema": 1` (the kit's accepted flow schema version) and validates against the CLOSED structural schema-1 key set (unknown flow keys and malformed per-key shapes refuse loudly; deep environment floors stay on the `set-flow` arming path). `review` accepts `solo|reviewed|council`; `execute` accepts `solo|delegated|subagent`; a carrier slot (`author`, `carrier`) accepts `solo|subagent`; `parallel` accepts `on|off`. Seeded by `init` (a user-editable template) — see `${CLAUDE_SKILL_DIR}/references/modes/bootstrap.md`.
|
|
18
|
-
2. **Default resolution (config silent):** `review` → Reviewed if any review-capable backend is `ready`, else Solo (never Council by default); `execute`, `author`, `carrier` → Solo (Delegated and Subagent are opt-in; a placed executor never flips a default); `parallel` → on. **Degradation:** a config/computed default degrades **gracefully with a stated reason** (Council → Reviewed → Solo; Delegated → Solo; Subagent → Solo when the executor vehicle is missing or unusable, the reason naming the apply command; a switch never degrades); a per-run **`--override <slot>=<value>`** that can't be satisfied degrades **loudly** (a flagged warning, so you tell the user) — but is **still exit 0** (a valid request that gracefully degraded).
|
|
17
|
+
1. **Config = `docs/ai/orchestration.json`** — strict JSON, **agent-writable via `/agent-workflow-kit set-recipe` (`${CLAUDE_SKILL_DIR}/references/modes/set-recipe.md`) OR hand-edited** (the kit reads + validates it; `procedures`/`recipes` stay read-only — the writer is `set-recipe`). Shape: `{ "<activity>": { "<slot>": "<value>" } }`; all slots optional (an absent slot → its computed default, stated); an optional `"_README"` string is allowed + ignored; a `"flow"` object must carry the NUMERIC `"schema": 1` (the kit's accepted flow schema version) and validates against the CLOSED structural schema-1 key set (unknown flow keys and malformed per-key shapes refuse loudly; deep environment floors stay on the `set-flow` arming path). `review` accepts `solo|reviewed|council`; `execute` accepts `solo|delegated|subagent`; a carrier slot (`author`, `fold`, `carrier`) accepts `solo|subagent`; `parallel` accepts `on|off`. Seeded by `init` (a user-editable template) — see `${CLAUDE_SKILL_DIR}/references/modes/bootstrap.md`.
|
|
18
|
+
2. **Default resolution (config silent):** `review` → Reviewed if any review-capable backend is `ready`, else Solo (never Council by default); `execute`, `author`, `fold`, `carrier` → Solo (Delegated and Subagent are opt-in; a placed executor never flips a default); `parallel` → on. **Degradation:** a config/computed default degrades **gracefully with a stated reason** (Council → Reviewed → Solo; Delegated → Solo; Subagent → Solo when the executor vehicle is missing or unusable, the reason naming the apply command; a switch never degrades); a per-run **`--override <slot>=<value>`** that can't be satisfied degrades **loudly** (a flagged warning, so you tell the user) — but is **still exit 0** (a valid request that gracefully degraded).
|
|
19
19
|
3. **Exit codes:** `0` success; `2` usage (unknown `<activity>` / bad `--override` — a bare `--override <recipe>`, an unknown slot, a value outside the slot's list, or a duplicate slot); `1` config error (malformed / schema-invalid / unreadable `orchestration.json`) **or** engine error (the installed engine is absent / invalid / **too old** to ship `references/procedures.md` — upgrade it with `npx @sabaiway/agent-workflow-engine@latest init`). A `1`/`2` failure is loud (`path: reason`), never a silent fallback. **Lagging-kit honesty:** a kit predating the `"flow"` key that reads a config carrying one fails this config load loudly (exit `1`, reddening its full gate matrix); the `set-flow` arming path now enforces the declared `kitMinVersion` floor with a null-guarded comparison (an unparseable version never passes), while tolerate-first ordering remains the only protection for readers older than the `"flow"` key itself — no in-config floor can reach a kit that dies on the unknown key.
|
|
20
20
|
|
|
21
|
-
**The dispatch form (a slot resolved to `subagent`).** A carrier that is only NAMED is a name, not an instruction, so after such a slot's resolved-recipe line the advisor prints — indented under it, in place of the one-line vehicle mention — the form the dispatch must take, in five parts: (1) what a SLICE is for THIS
|
|
21
|
+
**The dispatch form (a slot resolved to `subagent`).** A carrier that is only NAMED is a name, not an instruction, so after such a slot's resolved-recipe line the advisor prints — indented under it, in place of the one-line vehicle mention — the form the dispatch must take, in five parts: (1) what a SLICE is for THIS slot — for `execute`, a set of file-disjoint ledger rows, wording copied verbatim where wording is a red line; for `author`, a brief naming the goal, the governing spec(s) and the ledger constraints, whose returned draft the orchestrator reviews as its own; for `fold`, the round's findings with their dispositions, whose edits the orchestrator follows with the self-consistency read; for `routine.carrier`, a bounded mechanical task, where a read-only chore (a sweep, gate triage) rides its placed read-only vehicle — or is carried solo with a stated reason when that vehicle is absent — a write-capable one (a regeneration, a fixture build) rides the executor, and the changelog stays the orchestrator's; (2) the vehicle and its surveyed state — `.claude/agents/executor.md` (`placed` / `customized` / `unusable` / `missing`, from the same survey `status` and the advisor read) — dispatched **in the background**; (3) that the orchestrator **verifies every returned slice by running its suites itself**; (4) that the subagent is **never told to commit, never a review backend, never a bridge substitute**; (5) the **honest limit** — a Claude Code lane: on a host that cannot dispatch the vehicle, follow this form BY HAND and say so, never report it as a subagent dispatch. The wording is the kit's own (`tools/carriers.mjs`) and is printed, never re-worded. A slot resolved to `solo` or `delegated` renders exactly what it rendered before.
|
|
22
22
|
|
|
23
23
|
**`routine`'s `parallel` switch renders against the EFFECTIVE carrier.** The flag always renders with its source (`parallel: on` / `parallel: off`); the concurrency claim — *file-disjoint slices dispatch concurrently* — renders **only** while the resolved `carrier` is `subagent`. Under a `solo` carrier the line states its own inertness instead, `(no effect while the carrier is solo)`: a switch never degrades, so the flag keeps the value you set and the render simply stops promising a concurrency nothing can perform. That makes four cells (`on`/`off` x `subagent`/`solo`), and only the `subagent`+`on` cell claims concurrency.
|
|
24
24
|
|
|
@@ -28,6 +28,10 @@ Run **`node ${CLAUDE_SKILL_DIR}/tools/procedures.mjs <activity> [--override <slo
|
|
|
28
28
|
|
|
29
29
|
**Declared source-size practice block (upfront knowledge, D-17 U1).** When the project declares a source-size practice in `docs/ai/source-size.json`, the advisor also renders `Declared source-size practice (docs/ai/source-size.json) — known BEFORE the code is written:` — the caps, what is recorded and the canonical WHY sentence (the plan-time layout itself is the planning canon's Module ledger). `--json` carries the SAME lines under **`declaredPractice`** (an empty array when nothing is declared), so the human and structured renders never diverge. The four config states each read as themselves: **ABSENT** renders nothing at all (a project declaring no practice is handed no invented limits), **AUTHORED** and **INCOMPLETE** are pre-mint (the second names the half-written machine key), **MINTED** carries the recorded counts. A declaration that cannot be read (malformed, unknown-keyed, a dangling symlink) renders ONE loud `UNREADABLE` line and the advisor **still exits 0** — the practice's own checker owns the exit code for its config (exit 2 there) and its declared gate reds the gate matrix. Read through the practice's pure read core, so this advisor never reaches its writer half.
|
|
30
30
|
|
|
31
|
+
**Readers-sweep block (`plan-authoring` only, unconditional).** Before the review-loop block the advisor renders `Readers sweep (before the first review) — …`: the search form for every config key, registry entry, exported constant, receipt field or canon sentence the plan changes, and the classification rule (a ledger row, a stated non-goal, or unchanged with its proof). The command line `run: node <kit>/tools/repo-search.mjs --pattern <the literal>` ALWAYS renders — bare wherever the `--kit-tools` tier could seed that path (the tier's own byte-form, the same predicate), and single-quoted otherwise (an unseedable path has no rule to match); `--pattern-file <f>` only for a pattern carrying a shell-significant byte; when the project's MCP registration is complete (`/agent-workflow-kit mcp`) a `use: repo_search {"pattern": "<the literal>"}` line renders ABOVE it — registration on disk, never a claim the tool is loaded in this session. `--json` carries the SAME lines under **`readersSweep`** (an empty array for the other activities).
|
|
32
|
+
|
|
33
|
+
**Slot-skew warning.** When the installed engine's canon `Slots:` line for the activity differs from this kit's registry, the advisor adds a `warnings` entry (a `⚠` line in the human render; exit 0; the resolved lines follow the registry) naming both lists — the engine and the kit are out of step; `/agent-workflow-kit status` names which member is behind. A section without a `Slots:` line is a customized canon and stays silent.
|
|
34
|
+
|
|
31
35
|
**Spec-store block (the feature-spec layer, `plan-execution` only).** After a `plan-execution` render the advisor also prints `Spec store (the feature-spec layer) — …`: the two POPULATED `spec-check` commands and the session register they read. **Session lane** — `--ops-file docs/plans/spec-ops.list` (or repeated `--op <add|modify|remove>=<path>` / `rename=<old>:<new>`) judges the closure of the declared change: the targets plus each one's listing parent, on their post-state, their reader verdict, their per-kind line cap, their scenario bindings and the containment of every path they name. **Store lane** — `--all` judges the whole store instead and adds the four cross-document invariants (an unlisted child, DISTINCT from an orphan · acyclicity · store-wide slug uniqueness · module overlap); an absent store root refuses the run rather than reporting an empty clean store. The two lanes are exclusive. **Register lifecycle:** `docs/plans/spec-ops.list` is *session scratch* — the session writes it, the plan's Phase Cleanup deletes it — and it is **never defaulted** by the tool, because an unnamed register would attest a post-state nobody declared (the `fold-scope --queue` precedent). `--json` carries the same lines under **`specCheck`** (an empty array outside `plan-execution`). Advisory like the finding-scope block: nothing records that it ran. Exit `0` clean · `1` findings · `2` usage.
|
|
32
36
|
|
|
33
37
|
**Cap-soft-skip degradation (the feature's only AUTO route).** The activity procedures are auto-discoverable only through the one-line **`workflow:methodology`** pointer (this kit + the engine carry `disable-model-invocation:true`, so NL like "write a plan" does **not** auto-load this skill). On a deployment whose methodology pointer was cap-soft-skipped — or whose pre-existing customized pointer lacks the procedures clause — the procedures are still reachable by **explicitly** invoking `/agent-workflow-kit procedures`; surface that plainly rather than treating it as a gap.
|
|
@@ -10,13 +10,13 @@ The five recipes (defined over each bridge's `provides` roles — `codex`: execu
|
|
|
10
10
|
- **Reviewed** — you execute; **one** backend reviews the result (advisory). Prefers `codex` when both are ready (`agy` carries a standing health caveat).
|
|
11
11
|
- **Council** — **both** backends review independently; you synthesize the two opinions.
|
|
12
12
|
- **Delegated** — you hand a **bounded** execution sub-task to a backend (`codex exec`), then review the returned diff and commit.
|
|
13
|
-
- **Subagent** — a **full-tool frontier subagent**, dispatched from the placed executor vehicle (`.claude/agents/executor.md`), carries a **bounded, file-disjoint** slice — of execution, of plan/contract authoring, or of routine work; you verify the returned slice by running its suites yourself and you make the commit. It is **never a review backend**, **never a bridge substitute**, and is **never told to commit**; it **degrades to Solo** when the vehicle is `missing` or `unusable`, with the reason. Readiness here is the **vehicle FILE, never the host** — a **Claude Code lane**, like the `agents` writer itself: on a host that cannot dispatch the vehicle, the rendered dispatch form is an instruction you follow by hand and report as such, never as a subagent dispatch.
|
|
13
|
+
- **Subagent** — a **full-tool frontier subagent**, dispatched from the placed executor vehicle (`.claude/agents/executor.md`), carries a **bounded, file-disjoint** slice — of execution, of plan/contract authoring, of an authoring fold, or of routine work; you verify the returned slice by running its suites yourself and you make the commit. It is **never a review backend**, **never a bridge substitute**, and is **never told to commit**; it **degrades to Solo** when the vehicle is `missing` or `unusable`, with the reason. Readiness here is the **vehicle FILE, never the host** — a **Claude Code lane**, like the `agents` writer itself: on a host that cannot dispatch the vehicle, the rendered dispatch form is an instruction you follow by hand and report as such, never as a subagent dispatch.
|
|
14
14
|
|
|
15
15
|
1. Run **`node ${CLAUDE_SKILL_DIR}/tools/recipes.mjs`** (the read-only planner; `--json` for the structured form). It runs the backend detector, lists the five recipes, and prints — for the current environment — a **per-recipe dispatch plan that degrades with a stated reason** when a carrier isn't `ready` (Council → Reviewed → Solo; Delegated → Solo; Subagent → Solo when the executor vehicle is `missing`/`unusable`), plus advisory **quota/health notes** (prefer the cheapest model; Council spends two backends' quota; `agy` may stall on substantive prompts — Issue-001, prefer `codex`).
|
|
16
16
|
2. **Offer the choice** via **`AskUserQuestion` where your agent supports it** (`AskUserQuestion` in Claude Code) — one option per recipe, the `recommendRecipe` choice listed **first** — otherwise in prose. Then print `planRecipe(chosen, detection)` (the per-stage dispatch + degradation reasons + quota/health notes) so the user sees exactly what running it entails.
|
|
17
17
|
3. **Availability = `readiness === ready`, full stop.** Every other readiness supplies the human reason (needs-skill → "not installed — `/agent-workflow-kit setup`"; needs-cli → "install the CLI"; needs-credentials → "log in"; degraded → "wrapper not on PATH — `/agent-workflow-kit setup`"). This is set-up state only — **never** a claim that a backend's service is responsive (the detector cannot observe a runtime stall; `agy`'s Issue-001 is a *standing advisory*, not a readiness signal).
|
|
18
18
|
|
|
19
|
-
**The activities and their slots.** One registry (`tools/carriers.mjs`, re-exported by `recipes.mjs`) names **three** activities and their typed slots: `plan-authoring` → `author`, `review`; `plan-execution` → `execute`, `review`; `routine` → `carrier`, `parallel`. Each slot's accepted values follow its TYPE — `review` accepts `solo|reviewed|council`; `execute` accepts `solo|delegated|subagent`; `carrier` accepts `solo|subagent`. **`parallel` is a flag (`on|off`), not a recipe:** it resolves OUTSIDE the recipe lattice, never degrades, and never reaches the planner. A silent slot takes its **computed default** — `solo` for `author`, `execute` and `carrier` (placing the executor vehicle never flips a default; the subagent carrier is opt-in exactly as `delegated` is), `reviewed`/`solo` by readiness for `review`, and `on` for `parallel`.
|
|
19
|
+
**The activities and their slots.** One registry (`tools/carriers.mjs`, re-exported by `recipes.mjs`) names **three** activities and their typed slots: `plan-authoring` → `author`, `fold`, `review`; `plan-execution` → `execute`, `review`; `routine` → `carrier`, `parallel`. Each slot's accepted values follow its TYPE — `review` accepts `solo|reviewed|council`; `execute` accepts `solo|delegated|subagent`; `carrier` accepts `solo|subagent`. **`parallel` is a flag (`on|off`), not a recipe:** it resolves OUTSIDE the recipe lattice, never degrades, and never reaches the planner. A silent slot takes its **computed default** — `solo` for `author`, `fold`, `execute` and `carrier` (placing the executor vehicle never flips a default; the subagent carrier is opt-in exactly as `delegated` is), `reviewed`/`solo` by readiness for `review`, and `on` for `parallel`.
|
|
20
20
|
|
|
21
21
|
**The configured-recipe line (`--active-line`, read-only).** `node ${CLAUDE_SKILL_DIR}/tools/recipes.mjs --active-line` prints exactly **one** machine-composed line: the **CONFIGURED** recipe of every activity/slot, resolved from the target project's `docs/ai/orchestration.json` (read from the current directory) + live readiness — each slot with its source (configured vs computed default), its degradation stated, and its dispatched wrapper set — explicitly contrasted with the readiness **recommendation** (which is informational; the configured recipes are what runs). Paste it verbatim: it fills the session-start discovery step (the deployed `agent_rules.md` §1.1) and the handover "Active recipes:" slot; `set-recipe` echoes the same line after every successful write. A malformed config fails loud (exit 1), never a silent fallback.
|
|
22
22
|
|
|
@@ -15,7 +15,7 @@ mapping their wording onto them is your job, not a shipped phrase list.
|
|
|
15
15
|
| "both review" | `--set <activity>.review=council` | **disambiguate**: which activity? If both, pass `--set plan-authoring.review=council --set plan-execution.review=council`. |
|
|
16
16
|
| "one reviewer" | `--set <activity>.review=reviewed` | per the named activity, else ask |
|
|
17
17
|
| "delegate execution" | `--set plan-execution.execute=delegated` | execution only (a **bridge** runs the change) |
|
|
18
|
-
| "let a subagent do it" | `--set <activity>.<slot>=subagent` | **disambiguate**: which work? `--set plan-execution.execute=subagent` (a slice of the change), `--set plan-authoring.author=subagent` (a plan/contract brief), `--set routine.carrier=subagent` (a bounded chore) |
|
|
18
|
+
| "let a subagent do it" | `--set <activity>.<slot>=subagent` | **disambiguate**: which work? `--set plan-execution.execute=subagent` (a slice of the change), `--set plan-authoring.author=subagent` (a plan/contract brief), `--set plan-authoring.fold=subagent` (the round's findings and dispositions), `--set routine.carrier=subagent` (a bounded chore) |
|
|
19
19
|
| "one slice at a time" | `--set routine.parallel=off` | `routine` only — `parallel` is a **flag**, not a recipe |
|
|
20
20
|
| "revert / do it myself" | `--unset <activity>.<slot>` | the named slot → its computed default |
|
|
21
21
|
|
|
@@ -25,7 +25,7 @@ Run **`node ${CLAUDE_SKILL_DIR}/tools/set-recipe.mjs [--set <activity>.<slot>=<v
|
|
|
25
25
|
|
|
26
26
|
```
|
|
27
27
|
Activities and their slots:
|
|
28
|
-
plan-authoring → author, review
|
|
28
|
+
plan-authoring → author, fold, review
|
|
29
29
|
plan-execution → execute, review
|
|
30
30
|
routine → carrier, parallel
|
|
31
31
|
|
|
@@ -36,10 +36,10 @@ Run **`node ${CLAUDE_SKILL_DIR}/tools/set-recipe.mjs [--set <activity>.<slot>=<v
|
|
|
36
36
|
switch slots accept on | off
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
`parallel` is a **flag**, not a recipe: it resolves outside the recipe lattice and is never degraded. Examples: `--set routine.carrier=subagent`, `--set routine.parallel=off`, `--set plan-authoring.author=subagent`. A value outside its slot's list is a **usage** error (`2`) naming the accepted values, never a coercion to a neighbour.
|
|
39
|
+
`parallel` is a **flag**, not a recipe: it resolves outside the recipe lattice and is never degraded. Examples: `--set routine.carrier=subagent`, `--set routine.parallel=off`, `--set plan-authoring.author=subagent`, `--set plan-authoring.fold=subagent`. A value outside its slot's list is a **usage** error (`2`) naming the accepted values, never a coercion to a neighbour.
|
|
40
40
|
2. **Preview by default** — prints `current → proposed` for the **changed** slots only, plus the **effective recipe resolved against live carrier readiness** (degradation stated honestly, e.g. *council requested, 1 ready reviewer → runs reviewed until a 2nd backend is ready*; *subagent requested, the executor vehicle is `missing` → runs solo until the vehicle is placed, naming the apply command*). A `parallel` value is reported as requested — a flag has nothing to degrade against. It writes **nothing**. Re-run with **`--write`** to apply (same effective/degradation note — a direct `--write` is never quieter than the preview). `--unset` returns a slot to its computed default (reverting needs no hand-edit either). A no-op `--set` (slot already equals) writes nothing and never re-seeds the onboarding note.
|
|
41
41
|
3. **`--write`** applies via a hardened, atomic write (deployment-gated — refuses to scatter a config into a repo with no `docs/ai`; exclusive-create temp + rename; symlink/TOCTOU-safe; last-writer-wins). It preserves the onboarding note + every untouched slot, normalizing to canonical 2-space JSON.
|
|
42
|
-
**Standing-consent advisory (after a successful `--write` only, and only when the written config names a `reviewed`/`council` recipe — a solo recipe gets NO advisory):** advise the ONE-TIME **hand-adds** to the maintainer's `.claude/settings.local.json` — `Bash(codex-review:*)` + `Bash(agy-review:*)` + `Bash(node <skill-dir>/tools/grounding.mjs:*)`, with the path your project actually reaches the kit by. State plainly: (a) auto-approving a review wrapper **spends subscription quota without a per-run prompt** — that is exactly what standing consent means here, so it is the maintainer's call; (b) the kit **never writes that file** — these stay hand-adds; (c) `grounding.mjs --out` writes ONE scratch facts file — that write is what the standing consent covers; (d) the entry must match the invocation **byte-form your project actually uses, INCLUDING quoting** — the procedures advisor renders a QUOTED
|
|
42
|
+
**Standing-consent advisory (after a successful `--write` only, and only when the written config names a `reviewed`/`council` recipe — a solo recipe gets NO advisory):** advise the ONE-TIME **hand-adds** to the maintainer's `.claude/settings.local.json` — `Bash(codex-review:*)` + `Bash(agy-review:*)` + `Bash(node <skill-dir>/tools/grounding.mjs:*)`, with the path your project actually reaches the kit by. State plainly: (a) auto-approving a review wrapper **spends subscription quota without a per-run prompt** — that is exactly what standing consent means here, so it is the maintainer's call; (b) the kit **never writes that file** — these stay hand-adds; (c) `grounding.mjs --out` writes ONE scratch facts file — that write is what the standing consent covers; (d) the entry must match the invocation **byte-form your project actually uses, INCLUDING quoting** — the procedures advisor renders a QUOTED path for the grounding pre-step (the readers-sweep command renders bare, matching the kit-tools tier), and a hand-add covering it must use the same spelling (a mismatched spelling is a dead rule that simply prompts).
|
|
43
43
|
4. **Exit codes:** `0` success (an explicit recipe that gracefully degrades is still `0`); `2` usage (a bare/duplicate op, or `--write` with no ops); `1` config error (malformed/unreadable config — the file is left **untouched**, never clobbered) or a write STOP (no deployment / a symlinked config). A `1`/`2` failure is loud; on a malformed config, offer to show the parse error so you can help the user fix the JSON.
|
|
44
44
|
|
|
45
45
|
Output is **English/structured** — **localize it to the user's conversational language** when you narrate. Surface the effective-recipe/degradation note plainly.
|
|
@@ -13,7 +13,7 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/family-registry.mjs --json [--dir <project>]
|
|
|
13
13
|
> **Status reads `refresh`; the shared version block + the bootstrap/upgrade footers stay `notes`-based (unchanged this release).** `${CLAUDE_SKILL_DIR}/references/modes/status.md` has its OWN status-only render (above), keyed on `installed[].refresh.behind` / `refresh.recommend`. The shared **version block** (under *The version block + welcome mat* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`) and the bootstrap (step 11) + every upgrade (steps 4 / 8) report footer still consume `installed[].notes` verbatim — that wiring is deliberately **untouched** here (their migration onto `refresh` is deferred). Do not rewrite those footers onto `refresh`.
|
|
14
14
|
2. **Deployment (`--dir`)** (from `project`): whether `docs/ai/` is deployed + the deploy stamps by `display`; and **visibility** — render `project.visibility.state` in **user-safe words only**: *visible (tracked)* / *hidden (git-ignored, local-only)* / *unclear (uncommitted or partially set up)* — **never** the words "hidden fence" or any marker term. A `visibility.error` → surface it plainly. When `project.adrLayout` is **`old`** or **`old-unrotated`**, add a plain-language note that the project still uses an **older ADR layout** and should run the opt-in **`/agent-workflow-kit migrate-adr-store`** to move to the one-file-per-ADR store (preview first; it never commits) — the note is the SAME for both, they differ only in how the older layout was detected (`old` = a retired archive file is still on disk; `old-unrotated` = the project's deployed rotation script predates the store); `migrated` / `none` need no note. Then **one `specs` line, always** (from `project.specs` — the feature-spec layer's adoption state, judged from the store alone): `not adopted` (no `docs/ai/specs/` — the layer was never seeded), `adopting (N draft)` (a store with no live contract yet), `adopted (N live, M draft)`, or `could not be read — <reason>` (the store or a document in it could not be observed; never counted as any of the other three). A recorded decline (`project.specs.declined`) appends ` — declined` to the first two. The internal tokens are `not-adopted` / `adopting` / `adopted` / `unreadable`; render the plain phrases above, never the tokens. An envelope without the field says the installed kit predates it — say so rather than inventing a state.
|
|
15
15
|
3. **Settings (`--dir`, one line each)** (from `project.settings`):
|
|
16
|
-
- **recipes** — the effective recipe per slot, **one line per slot** over the three activities (`plan-authoring.author` / `.review`, `plan-execution.execute` / `.review`, `routine.carrier` / `.parallel`); each line also carries **where the value came from** (`source` — the project's config, an explicit override, or a computed default) and, when the resolved recipe is not the one that was asked for, **what it degraded from** (`degradedFrom`). Say both in plain words (never the field names): an effective recipe alone cannot tell a deliberate choice from a default, nor a degrade from a config that really names the resolved recipe — and a slot showing `solo` after a degrade from `subagent` is exactly the case the **subagents** line below explains. Detail → `/agent-workflow-kit procedures` / `recipes`; a `recipes.detectError` → say the backends couldn't be checked, so bridge-backed recipes floored at solo; the executor vehicle is unaffected.
|
|
16
|
+
- **recipes** — the effective recipe per slot, **one line per slot** over the three activities (`plan-authoring.author` / `.fold` / `.review`, `plan-execution.execute` / `.review`, `routine.carrier` / `.parallel`); each line also carries **where the value came from** (`source` — the project's config, an explicit override, or a computed default) and, when the resolved recipe is not the one that was asked for, **what it degraded from** (`degradedFrom`). Say both in plain words (never the field names): an effective recipe alone cannot tell a deliberate choice from a default, nor a degrade from a config that really names the resolved recipe — and a slot showing `solo` after a degrade from `subagent` is exactly the case the **subagents** line below explains. Detail → `/agent-workflow-kit procedures` / `recipes`; a `recipes.detectError` → say the backends couldn't be checked, so bridge-backed recipes floored at solo; the executor vehicle is unaffected.
|
|
17
17
|
- **attribution** — `includeCoAuthoredBy` effective; call out a **local override** only when `local` is non-null **and** differs from `project` (a `null` `local` means the key is absent there, so the project value stands — that is not an override).
|
|
18
18
|
- **velocity** — the effective `permissions.defaultMode` + whether an allowlist is seeded (detail → `/agent-workflow-kit velocity`).
|
|
19
19
|
- **subagents** — how many of the kit's subagent vehicles are placed (`agents.placed` of `agents.bundled`; zero placed → the optional `/agent-workflow-kit agents` opt-in), then the **split the line always states separately**: the **read-only vehicles** (`agents.readOnly` — four; none of them grants a shell, three ride a cheap model for mechanical work and one is a read-only review lens) and the **ONE full-tool `executor`**, whose own state is `agents.executor`: *placed* / *customized* (both dispatchable) / *unusable* (`agents.executorReason` carries the reason — say it) / *missing*. That state is what decides whether a slot configured `subagent` can ride it: on **unusable** or **missing** every such slot runs solo instead, so surface it plainly beside the recipes lines and point at `/agent-workflow-kit agents` to place it. It is a claim about the FILE, never about the host. An envelope with no executor state says the installed kit predates the field — say so rather than inventing a state.
|
|
@@ -35,6 +35,7 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/velocity-profile.mjs [--dry-run | --apply] [
|
|
|
35
35
|
- `node ${CLAUDE_SKILL_DIR}/tools/release-scan.mjs <path>` (wildcard)
|
|
36
36
|
- `node ${CLAUDE_SKILL_DIR}/tools/repo-search.mjs --pattern <literal> --path <p>` (wildcard — the LITERAL search lane; for a pattern carrying a shell-significant byte use `--pattern-file <p>` instead, and for a TARGET PATH carrying one use `--paths-file <p>` — either way the bytes never enter the command string. one of the TWO tier tools that carry hook residual coverage — see the honesty note below)
|
|
37
37
|
- `node ${CLAUDE_SKILL_DIR}/tools/path-inventory.mjs --path <p>` (wildcard — the read-only inventory lane: exists / type / size / `wc -l` lines / one-level listing / small-file contents for N paths in ONE call, so small path questions never need a composed shell. `--paths-file <p>` is its out-of-band target lane; hook residual coverage as with repo-search)
|
|
38
|
+
- `node ${CLAUDE_SKILL_DIR}/tools/review-rounds-cli.mjs --artifact <plan>` (wildcard — the read-only round table over the review receipts, the verdict half of the per-round emission the `procedures` advisor names every round; the path argument is resolved and compared, never opened as a pattern, so it rides the settings-level posture like the rest)
|
|
38
39
|
- `node ${CLAUDE_SKILL_DIR}/tools/run-gates.mjs --cwd ${PROJECT_ROOT}` — **EXACT byte-string only**, and honestly **project-exec, not read-only**: it runs YOUR declared `docs/ai/gates.json` commands — the same trust boundary the opt-in hook grants byte-exact per-cmd. A wildcard would be BROADER than that boundary (`--cwd <dir>` executes another project's declared gates), so the bare cwd-defaulting form, any other `--cwd`, `--only`, and **`--final`** forms all still prompt (`--final` WRITES the final-run receipt into the core-evidence store — a recording run is never auto-approved).
|
|
39
40
|
- `node ${CLAUDE_SKILL_DIR}/tools/source-size-check.mjs --check` — **EXACT byte-string only**, and only this READ-ONLY mode: the tool's `--write-baseline` and `--adopt` modes WRITE (the recorded ratchet, and a gate declaration) and are deliberately uncovered — they still prompt, always. Every other form, `--cwd` included, keeps prompting. **What it covers, precisely:** the agent's own direct invocation. It is **not** the byte-string a DECLARED gate carries — the fill emits that path double-quoted (a kit path with a space must survive), and a seedable allow rule may carry no quotes at all, so the two spellings cannot be one string; a declared gate is auto-approved by the opt-in hook instead, byte-exact against `docs/ai/gates.json`.
|
|
40
41
|
- Writer previews, **exact arg-free dry-run byte-strings only** (the SEEDED tier byte-string is the arg-free preview of each): `node ${CLAUDE_SKILL_DIR}/tools/velocity-profile.mjs` · `node ${CLAUDE_SKILL_DIR}/tools/cheap-agents.mjs` · `node ${CLAUDE_SKILL_DIR}/tools/gate-hook.mjs` — every `--apply`/`--write`/`--yes` still prompts, always. (`gate-hook` also has a **`--read-lane`** flagged preview — the opt-in read-only compound lane, `${CLAUDE_SKILL_DIR}/references/modes/hook.md`; that flagged form is NOT the seeded arg-free byte-string, so it may **prompt once** — it IS a consent flow, stated, no silent cap.)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_README": "Per-project orchestration config: the recipe used at each step (slot) of each named activity. Easiest: tell the agent in plain language and run the `set-recipe` writer — it interprets your intent, previews the change, and writes valid JSON for you. You can still hand-edit this file directly whenever you prefer; that option never goes away. Three activities are configured independently, and so is each slot within them: 'plan-authoring' (slots author, review), 'plan-execution' (slots execute, review) and 'routine' (slots carrier, parallel). A slot's value is a recipe: a 'review' slot accepts solo | reviewed | council (you self-review / one backend reviews / both review and you synthesize); an 'execute' slot accepts solo | delegated | subagent (you implement / a backend runs a bounded sub-task / a full-tool frontier subagent carries a bounded slice you verify); the carrier slots 'plan-authoring.author' and 'routine.carrier' accept solo | subagent. 'routine.parallel' is a flag rather than a recipe: it accepts on | off and decides whether file-disjoint subagent slices dispatch concurrently. The default below is 'solo' for every recipe and carrier slot, and 'on' for the parallel switch — no execution backend required. Raise a slot to reviewed or council for a second opinion, or to delegated to hand off execution; those need an execution backend set up first. 'subagent' needs the executor vehicle placed in this project — the composition root's `agents` writer places it; without it the slot resolves to solo with the reason stated. Remove a slot's line, or a whole activity block (or run `set-recipe --unset <activity>.<slot>`), to fall back to the computed default: reviewed when a review backend is ready and otherwise solo for a review slot, solo for author, execute and carrier, on for parallel. Run the read-only procedures advisor to see an activity's steps plus the recipe resolved for your environment. Strict JSON — no comments.",
|
|
2
|
+
"_README": "Per-project orchestration config: the recipe used at each step (slot) of each named activity. Easiest: tell the agent in plain language and run the `set-recipe` writer — it interprets your intent, previews the change, and writes valid JSON for you. You can still hand-edit this file directly whenever you prefer; that option never goes away. Three activities are configured independently, and so is each slot within them: 'plan-authoring' (slots author, fold, review), 'plan-execution' (slots execute, review) and 'routine' (slots carrier, parallel). A slot's value is a recipe: a 'review' slot accepts solo | reviewed | council (you self-review / one backend reviews / both review and you synthesize), or an explicit roster array such as [\"codex-review\", \"agy-review\", \"review-lens\"] in hand-edit form; an 'execute' slot accepts solo | delegated | subagent (you implement / a backend runs a bounded sub-task / a full-tool frontier subagent carries a bounded slice you verify); the carrier slots 'plan-authoring.author', 'plan-authoring.fold' and 'routine.carrier' accept solo | subagent. 'routine.parallel' is a flag rather than a recipe: it accepts on | off and decides whether file-disjoint subagent slices dispatch concurrently. The default below is 'solo' for every recipe and carrier slot, and 'on' for the parallel switch — no execution backend required. Raise a slot to reviewed or council for a second opinion, or to delegated to hand off execution; those need an execution backend set up first. 'subagent' needs the executor vehicle placed in this project — the composition root's `agents` writer places it; without it the slot resolves to solo with the reason stated. Remove a slot's line, or a whole activity block (or run `set-recipe --unset <activity>.<slot>`), to fall back to the computed default: reviewed when a review backend is ready and otherwise solo for a review slot, solo for author, fold, execute and carrier, on for parallel. Run the read-only procedures advisor to see an activity's steps plus the recipe resolved for your environment. Strict JSON — no comments.",
|
|
3
3
|
"plan-authoring": {
|
|
4
4
|
"review": "solo"
|
|
5
5
|
},
|