@sabaiway/agent-workflow-kit 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +49 -1
  2. package/README.md +1 -0
  3. package/SKILL.md +5 -1
  4. package/bin/install.mjs +10 -1
  5. package/bridges/antigravity-cli-bridge/bin/agy-review-honesty.test.mjs +20 -7
  6. package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +76 -18
  7. package/bridges/antigravity-cli-bridge/bin/agy.test.mjs +59 -41
  8. package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +67 -22
  9. package/bridges/codex-cli-bridge/bin/codex-review-honesty.test.mjs +20 -7
  10. package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +65 -17
  11. package/capability.json +1 -1
  12. package/package.json +1 -1
  13. package/references/modes/recommendations.md +1 -0
  14. package/references/modes/worktrees.md +120 -0
  15. package/references/scripts/archive-decisions.mjs +2 -2
  16. package/references/scripts/archive-decisions.test.mjs +5 -5
  17. package/references/scripts/check-docs-size-cli.test.mjs +41 -0
  18. package/references/scripts/check-docs-size.mjs +46 -29
  19. package/tools/autonomy-doctor.mjs +1 -1
  20. package/tools/changed-surface.mjs +8 -8
  21. package/tools/commands.mjs +9 -0
  22. package/tools/grounding.mjs +13 -13
  23. package/tools/inject-methodology.mjs +71 -40
  24. package/tools/migrate-adr-store.mjs +3 -3
  25. package/tools/procedures.mjs +1 -1
  26. package/tools/recipes.mjs +2 -2
  27. package/tools/recommendations.mjs +34 -7
  28. package/tools/release-scan.mjs +9 -2
  29. package/tools/review-state.mjs +3 -3
  30. package/tools/sandbox-masks.mjs +13 -13
  31. package/tools/set-recipe.mjs +1 -1
  32. package/tools/velocity-profile.mjs +7 -7
  33. package/tools/worktrees.mjs +2292 -0
package/tools/recipes.mjs CHANGED
@@ -460,7 +460,7 @@ export const composeActiveRecipeLine = ({ config, source } = {}, detection, auto
460
460
  const rec = recommendRecipe(detection);
461
461
  const origin = source === 'none' || config == null ? 'no config file — computed defaults apply' : `from ${source}`;
462
462
  // A MALFORMED policy must surface LOUDLY on this paste surface too — silently rendering cells
463
- // without levels would hide the required STOP signal (codex R1, Segment B). One line always.
463
+ // without levels would hide the required STOP signal (Segment B). One line always.
464
464
  const malformed = autonomy?.error
465
465
  ? ` · autonomy: MALFORMED policy — ${String(autonomy.error).replace(/[\s]+/g, ' ').trim()}`
466
466
  : '';
@@ -485,7 +485,7 @@ export const buildReport = (detection, settings = null, autonomy = null, posture
485
485
  recommendation,
486
486
  plans: RECIPES.map((r) => planRecipe(r.id, detection)),
487
487
  // The SAME autonomy facts the --status-line surface renders — the --json envelope must never
488
- // expose a stale machine-composed status line (codex R1, Segment B).
488
+ // expose a stale machine-composed status line (Segment B).
489
489
  statusLine: composeStatusLine(detection, recommendation, settings, autonomy, posture),
490
490
  };
491
491
  };
@@ -59,6 +59,7 @@ import { resolveGitHooksPath } from './commit-guard.mjs';
59
59
  import { loadConfig } from './orchestration-config.mjs';
60
60
  import { DEFAULT_BUNDLE_ROOT, SETTINGS_FILENAME, settingsPath, parseSettings, duplicateKeys } from './bridge-settings-read.mjs';
61
61
  import { assertContainedRealPath } from './fs-safe.mjs';
62
+ import { loadWorktreesConfig, resolveProbeDir } from './worktrees.mjs';
62
63
 
63
64
  const HERE = dirname(fileURLToPath(import.meta.url));
64
65
  const toolPath = (rel) => join(HERE, rel);
@@ -108,6 +109,7 @@ export const SEVERITIES = Object.freeze({
108
109
  'agy-adddir': SEVERITY_OPTIONAL,
109
110
  'agy-adddir.invalid-env': SEVERITY_ATTENTION,
110
111
  'sandbox-lane': SEVERITY_OPTIONAL,
112
+ 'worktrees-dir': SEVERITY_OPTIONAL,
111
113
  });
112
114
  // The per-item render tags (frozen presentation data, same language contract as the templates).
113
115
  export const SEVERITY_LABELS = Object.freeze({
@@ -165,6 +167,7 @@ export const WHATS = Object.freeze({
165
167
  'agy-adddir': 'agy-review is placed but AGY_REVIEW_ALLOW_ADDDIR is not set ({file}) — an oversized code review refuses instead of offloading',
166
168
  'agy-adddir.invalid-env': 'AGY_REVIEW_ALLOW_ADDDIR is set to an INVALID value ({value}) — refuse-mode applies and the settings file is shadowed while it is set',
167
169
  'sandbox-lane': 'the wired review wrappers declare a session-sandbox recipe (egress hosts + writable state dirs) not yet acknowledged for this project',
170
+ 'worktrees-dir': 'write access to the worktrees parent dir {dir} is not confirmed — provision may still stop',
168
171
  });
169
172
 
170
173
  // ── the shape contract (D2): registry strings AND composed items stay one line under the cap ────
@@ -215,6 +218,7 @@ export const BENEFITS = Object.freeze({
215
218
  'sandbox-masks': 'zero clutter — git status shows only your changes (the review domain already ignores the masks by construction)',
216
219
  'agy-adddir': 'large reviews — an oversized agy code review offloads to a staging dir instead of refusing',
217
220
  'sandbox-lane': 'discoverability — the manifest-declared observed sandbox recipe for bridge runs surfaces itself instead of waiting to be asked',
221
+ 'worktrees-dir': 'parallel features — the host-specific write allowance or terminal fallback is surfaced before provision',
218
222
  });
219
223
 
220
224
  // A typed usage failure (exit 2) — the codebase's typed-error idiom (no classes).
@@ -297,7 +301,7 @@ const probeSandboxProvision = ({ root, deps, add, skip }) => {
297
301
  const variant = plan.tuple ? 'sandbox-provision.installable' : 'sandbox-provision';
298
302
  const reason = truncatedTo(oneLineOf(p.reason), templateBudget(WHATS[variant]) - (plan.tuple ? String(plan.tuple).length : 0));
299
303
  // The doctor reads process.cwd() (deployment-gated) and takes no --cwd flag — the one-liner
300
- // pins the target project via a cd prefix (codex R2, Segment B).
304
+ // pins the target project via a cd prefix (Segment B).
301
305
  add('sandbox-provision', fillTemplate(WHATS[variant], { reason, tuple: plan.tuple }), `cd ${q(root)} && node ${q(toolPath('autonomy-doctor.mjs'))}`);
302
306
  } catch (err) {
303
307
  skip('sandbox-provision', err);
@@ -306,7 +310,7 @@ const probeSandboxProvision = ({ root, deps, add, skip }) => {
306
310
 
307
311
  const probeReviewRecipe = ({ root, deps, add, skip }) => {
308
312
  try {
309
- // The VALIDATED reader (codex R2, Segment B): a schema-invalid config (unknown activity/slot,
313
+ // The VALIDATED reader (Segment B): a schema-invalid config (unknown activity/slot,
310
314
  // bad recipe) throws here and becomes a stated skip — raw JSON.parse would silently ignore it.
311
315
  const { config } = loadConfig(root, deps.readFile ?? readFileSync, deps.lstat ?? lstatSync);
312
316
  const detection = detectBackends(deps);
@@ -330,7 +334,7 @@ const probeGates = ({ root, deps, add, skip }) => {
330
334
  const sg = surveyGateHook(root, deps);
331
335
  if (sg.error) throw new Error(sg.error);
332
336
  if (sg.declarationPresent && sg.declaredGates === null) throw new Error(sg.declarationError ?? 'gates.json present but unreadable');
333
- // An ABSENT file and the seeded-EMPTY list are equally undeclared (codex R2, Segment B); the
337
+ // An ABSENT file and the seeded-EMPTY list are equally undeclared (Segment B); the
334
338
  // apply is the consent-gated gates-init PREVIEW (it proposes entries from the project's own
335
339
  // scripts and writes only on an explicit yes) — never the runner.
336
340
  if (!sg.declarationPresent || sg.declaredGates === 0) {
@@ -467,7 +471,7 @@ const probeMasksItem = ({ root, deps, add, skip }) => {
467
471
  if (!needsMasksApply(p)) return;
468
472
  const variant = p.staleReal.length > 0 ? 'sandbox-masks.stale-real' : 'sandbox-masks';
469
473
  // A stale-real-only fence (EMPTY derivation over a non-empty block) makes the plain --apply
470
- // REFUSE — the exact one-liner must carry --clear there (codex R1, Segment B).
474
+ // REFUSE — the exact one-liner must carry --clear there (Segment B).
471
475
  const apply = p.masks.length === 0 && p.staleReal.length > 0 ? `${p.applyCmd} --clear` : p.applyCmd;
472
476
  add('sandbox-masks', fillTemplate(WHATS[variant], { n: p.masks.length, m: p.staleReal.length }), apply);
473
477
  } catch (err) {
@@ -480,7 +484,7 @@ const probeAgyAdddir = ({ deps, add, skip }) => {
480
484
  const probePlaced = deps.findWrapper ?? ((cmd) => findOnPath(cmd, deps).state === 'present');
481
485
  if (!probePlaced('agy-review')) return;
482
486
  // Configured means a VALID boolean value (the wrapper validates and falls back to the default
483
- // on garbage — presence alone proves nothing; codex R3). An explicit valid 0 is a user CHOICE
487
+ // on garbage — presence alone proves nothing). An explicit valid 0 is a user CHOICE
484
488
  // (refuse mode) — respected, never nagged. env > file, the wrappers' own precedence.
485
489
  const isValidBool = (v) => v === '0' || v === '1';
486
490
  const env = deps.getenv ?? process.env;
@@ -681,7 +685,7 @@ const readReadLaneToggle = (root, deps) => {
681
685
  // D3: the risk-marked keys — every key here has a per-item posture note in the mode doc, surfaced
682
686
  // at the consent moment; the static contract test asserts EXACT bidirectional coverage
683
687
  // (risk-marked keys == mode-doc note keys — a dropped note goes red, not silent).
684
- export const RISK_NOTED_KEYS = Object.freeze(['agy-adddir', 'sandbox-lane', 'read-lane']);
688
+ export const RISK_NOTED_KEYS = Object.freeze(['agy-adddir', 'sandbox-lane', 'read-lane', 'worktrees-dir']);
685
689
 
686
690
  const probeSandboxLane = ({ root, deps, add, skip }) => {
687
691
  try {
@@ -738,6 +742,28 @@ const probeSandboxLane = ({ root, deps, add, skip }) => {
738
742
  }
739
743
  };
740
744
 
745
+ // The worktrees-dir arming item (parallel feature worktrees): fires when the resolved worktrees
746
+ // parent dir (docs/ai/worktrees.json parentDir, else the repo's own parent) is not confirmed
747
+ // writable by a trusted host signal. The provision preflight remains the real create+delete test.
748
+ const probeWorktreesDir = ({ root, deps, add, skip }) => {
749
+ try {
750
+ const config = loadWorktreesConfig(root, deps);
751
+ const parent = config.parentDir == null ? dirname(root) : resolve(root, config.parentDir);
752
+ // the SAME canonical derivation the provision preflight probes (worktrees.mjs) — an absent
753
+ // configured dir resolves to its nearest existing ancestor, never a false denial
754
+ const probeDir = resolveProbeDir(parent, deps);
755
+ if (deps.canWriteDir?.(probeDir) === true) return;
756
+ const dir = truncatedTo(oneLineOf(probeDir), templateBudget(WHATS['worktrees-dir']));
757
+ add(
758
+ 'worktrees-dir',
759
+ fillTemplate(WHATS['worktrees-dir'], { dir }),
760
+ `HAND-APPLY: add ${JSON.stringify(probeDir)} to sandbox.filesystem.allowWrite in .claude/settings.json on settings-native hosts; on harness-managed hosts grant this dir for the session or use the provision terminal fallback`,
761
+ );
762
+ } catch (err) {
763
+ skip('worktrees-dir', err);
764
+ }
765
+ };
766
+
741
767
  // ── assembly (frozen presentation order) ─────────────────────────────────────────────────────────
742
768
  const PROBES = Object.freeze([
743
769
  probeVelocityItems,
@@ -751,6 +777,7 @@ const PROBES = Object.freeze([
751
777
  probeMasksItem,
752
778
  probeAgyAdddir,
753
779
  probeSandboxLane,
780
+ probeWorktreesDir,
754
781
  ]);
755
782
 
756
783
  export const buildRecommendations = ({ cwd, deps = {} } = {}) => {
@@ -793,7 +820,7 @@ export const formatRecommendations = ({ items, skips }) => {
793
820
  const verdict = composeVerdict({ attention, optional: items.length - attention, skipped: skips.length });
794
821
  if (verdict == null) {
795
822
  // The flow-optimal claim renders ONLY when every probe ran and none fired — an empty item
796
- // list beside skipped checks would falsely attest optimality (codex R1, Segment B).
823
+ // list beside skipped checks would falsely attest optimality (Segment B).
797
824
  lines.push(RECOMMENDATIONS_EMPTY_LINE);
798
825
  return lines.join('\n');
799
826
  }
@@ -35,6 +35,9 @@ const ATTRIBUTION = [
35
35
  { re: /reviewed by (claude|codex|chatgpt|gpt|gemini|copilot|cursor|the (ai|model|agent))/i, label: 'AI review attribution' },
36
36
  { re: /authored[- ]by[: ][^\n]{0,40}\b(claude|chatgpt|gemini|copilot)\b/i, label: 'AI authorship attribution' },
37
37
  ];
38
+ const REVIEWER_IDENTITY = [
39
+ { re: /\b(?:agy|codex)(?:\s+|-)r\d+(?:(?:\+|\/)r?\d+)*(?:-[a-z0-9]+)*\b/i, label: 'reviewer-round identity' },
40
+ ];
38
41
 
39
42
  const allowlistCovers = (matched, allowlist) =>
40
43
  allowlist.some((term) => matched.toLowerCase().includes(term.toLowerCase()));
@@ -49,6 +52,10 @@ export const scanText = (text, { allowlist = ALLOWLIST } = {}) => {
49
52
  findings.push({ line: idx + 1, kind: 'attribution', detail: `${label}: "${m[0]}"` });
50
53
  }
51
54
  }
55
+ for (const { re, label } of REVIEWER_IDENTITY) {
56
+ const m = line.match(re);
57
+ if (m) findings.push({ line: idx + 1, kind: 'reviewer-identity', detail: `${label}: "${m[0]}"` });
58
+ }
52
59
  });
53
60
  return findings;
54
61
  };
@@ -83,7 +90,7 @@ export const scanPaths = (paths, opts = {}) => {
83
90
  return report;
84
91
  };
85
92
 
86
- const main = (argv) => {
93
+ export const main = (argv) => {
87
94
  const reportOnly = argv.includes('--report');
88
95
  const paths = argv.filter((a) => a !== '--report');
89
96
  if (paths.length === 0) {
@@ -93,7 +100,7 @@ const main = (argv) => {
93
100
  const report = scanPaths(paths);
94
101
  for (const r of report) console.log(`[${r.kind}] ${r.file}:${r.line} — ${r.detail}`);
95
102
  if (report.length === 0) {
96
- console.log('[release-scan] clean — no AI attribution found.');
103
+ console.log('[release-scan] clean — no AI attribution or reviewer-round identity found.');
97
104
  return;
98
105
  }
99
106
  console.log(`\n[release-scan] ${report.length} finding(s).`);
@@ -162,7 +162,7 @@ export const countNeverCommittableUntracked = (cwd, { lstat = lstatSync } = {})
162
162
 
163
163
  // Shell-quote one argument for a COPY-PASTE advisory command: plain safe tokens stay bare; anything
164
164
  // else rides single quotes (a space/metacharacter path must never render a dead or unsafe paste —
165
- // codex R1). Exported for the sandbox-masks probe, which renders the same apply one-liner.
165
+ // Exported for the sandbox-masks probe, which renders the same apply one-liner.
166
166
  export const shellQuoteArg = (s) => (/^[A-Za-z0-9_/.\-]+$/.test(s) ? s : `'${s.replace(/'/g, `'\\''`)}'`);
167
167
 
168
168
  const maskAdvisoryLine = (state) =>
@@ -282,7 +282,7 @@ export const degradeRecordSet = ({ cwd, env = process.env, fingerprint }) => {
282
282
  // EVERY project-relative read (orchestration config, docs/plans, receipts) anchors at the git
283
283
  // work-tree ROOT when one exists — the fingerprint is root-anchored, so a subdirectory invocation
284
284
  // must read the same config/plans or a dirty unreceipted tree could false-PASS as "no plan in
285
- // flight" (codex R1 finding). Outside a git tree the cwd is the only anchor (and --check exits 0).
285
+ // flight". Outside a git tree the cwd is the only anchor (and --check exits 0).
286
286
  export const buildState = ({ cwd, env = process.env, detect = detectBackends, lstat = lstatSync } = {}) => {
287
287
  const root = gitLine(['rev-parse', '--show-toplevel'], cwd) ?? cwd;
288
288
  const { config, source: configSource } = loadConfig(root);
@@ -618,7 +618,7 @@ export const mainAwait = async (argv, ctx = {}) => {
618
618
  };
619
619
 
620
620
  const emitResult = (r) => {
621
- // Exact writes + a natural exit: process.exit() can truncate unflushed piped stdio (codex R2).
621
+ // Exact writes + a natural exit: process.exit() can truncate unflushed piped stdio.
622
622
  if (r.stdout) process.stdout.write(r.stdout.endsWith('\n') ? r.stdout : `${r.stdout}\n`);
623
623
  if (r.stderr) process.stderr.write(r.stderr.endsWith('\n') ? r.stderr : `${r.stderr}\n`);
624
624
  process.exitCode = r.code;
@@ -69,7 +69,7 @@ const listUntrackedUnfilteredZ = (root) => {
69
69
  // name can never shadow a deeper real path), glob metacharacters + leading-! / leading-# handled
70
70
  // by the anchor, trailing whitespace escaped (gitignore trims it otherwise).
71
71
  // Trailing SPACES are escapable in gitignore (`\ `); a trailing TAB is not expressible — such a
72
- // name is classified unrenderable by the derivation (codex R12), so it never reaches this renderer.
72
+ // name is classified unrenderable by the derivation, so it never reaches this renderer.
73
73
  export const toExcludePattern = (rel) => {
74
74
  const escaped = rel.replace(/([\\*?[\]])/g, '\\$1').replace(/ +$/, (m) => '\\ '.repeat(m.length));
75
75
  return `/${escaped}`;
@@ -79,7 +79,7 @@ export const toExcludePattern = (rel) => {
79
79
  // lstat class is never-committable (char/block/FIFO/socket). Everything else is refused by
80
80
  // construction — tracked paths never appear in an --others walk; regular/dir/symlink/missing fail
81
81
  // the predicate. A CR/LF-carrying mask NAME cannot be expressed as ONE gitignore rule — rendering
82
- // it would split into several rules and could hide unrelated committable paths (codex R3) — so it
82
+ // it would split into several rules and could hide unrelated committable paths — so it
83
83
  // is a LOUD unrenderable skip, never written (the review domain already ignores the mask itself).
84
84
  export const deriveMasks = ({ root, lstat = lstatSync, listUntracked = listUntrackedUnfilteredZ } = {}) => {
85
85
  const entries = listUntracked(root);
@@ -95,7 +95,7 @@ export const deriveMasks = ({ root, lstat = lstatSync, listUntracked = listUntra
95
95
  }
96
96
  if (!isNeverCommittableStat(stat)) continue;
97
97
  // CR/LF anywhere, or a trailing TAB (inexpressible in gitignore — only trailing SPACES escape
98
- // as `\ `; codex R12), make the name unrenderable as ONE exclude rule.
98
+ // as `\ `), make the name unrenderable as ONE exclude rule.
99
99
  if (/[\r\n]/.test(rel) || /\t$/.test(rel)) unrenderable.push(rel);
100
100
  else masks.push(rel);
101
101
  }
@@ -115,11 +115,11 @@ export const findMasksFence = (lines) => {
115
115
 
116
116
  // A fenced pattern back to its relative path (the derivation writes only `/rel` root-anchored
117
117
  // escaped forms; unescape is its exact inverse — the escaped-space form `\ ` included, so a
118
- // trailing-space mask round-trips; codex R1). Non-pattern lines (blank/comment) are skipped;
118
+ // trailing-space mask round-trips). Non-pattern lines (blank/comment) are skipped;
119
119
  // only UNESCAPED trailing whitespace is insignificant (gitignore semantics).
120
120
  const patternToRel = (line) => {
121
121
  // A trailing CR (a CRLF-saved exclude file) strips FIRST — it would otherwise ride into the rel
122
- // name and false-ENOENT the revalidation lstat (agy R5); then only UNESCAPED trailing
122
+ // name and false-ENOENT the revalidation lstat; then only UNESCAPED trailing
123
123
  // whitespace is insignificant (gitignore semantics — an escaped `\ ` survives).
124
124
  const t = line.replace(/\r$/, '').replace(/^[ \t]+/, '').replace(/(?<!\\)[ \t]+$/, '');
125
125
  if (t === '' || t.startsWith('#') || !t.startsWith('/')) return null;
@@ -156,13 +156,13 @@ export const probeSandboxMasks = ({ cwd = process.cwd(), lstat = lstatSync, list
156
156
  const commonDirRaw = gitLine(['rev-parse', '--git-common-dir'], cwd);
157
157
  if (gitPathRaw == null || commonDirRaw == null) return null;
158
158
  const excludeFile = resolve(cwd, gitPathRaw);
159
- // The write-containment root (codex R2): info/exclude lives in the COMMON git dir (worktree-safe),
159
+ // The write-containment root: info/exclude lives in the COMMON git dir (worktree-safe),
160
160
  // and the apply must never write through a symlinked component or a non-regular leaf.
161
161
  const gitCommonDir = resolve(cwd, commonDirRaw);
162
162
  const derived = deriveMasks({ root, lstat, listUntracked });
163
163
  if (derived == null) return null;
164
164
  const { masks, unrenderable } = derived;
165
- // The WHOLE chain is guarded BEFORE any read (codex R7+R8): a symlinked exclude leaf OR a
165
+ // The WHOLE chain is guarded BEFORE any read: a symlinked exclude leaf OR a
166
166
  // symlinked parent (.git/info) must never be read through, and a FIFO/socket leaf would HANG
167
167
  // the read-only probe — parent-chain containment + leaf lstat first, fail closed on everything
168
168
  // but a plain absent file, then read the existing REGULAR content. The apply re-checks
@@ -190,7 +190,7 @@ export const probeSandboxMasks = ({ cwd = process.cwd(), lstat = lstatSync, list
190
190
  content = readFile(excludeFile, 'utf8');
191
191
  } catch (err) {
192
192
  // The file existed a moment ago — ANY read failure now fails CLOSED (an apply over a misread
193
- // "empty" file would overwrite hand content outside the managed block; codex R4).
193
+ // "empty" file would overwrite hand content outside the managed block).
194
194
  throw fail(1, `cannot read ${excludeFile} (${err?.code ?? err?.message ?? err}) — refusing to treat an unreadable exclude file as empty`);
195
195
  }
196
196
  }
@@ -222,7 +222,7 @@ export const planApply = (probe, { clear = false } = {}) => {
222
222
  }
223
223
  // --clear takes PRECEDENCE over the derivation (codex, Segment-A receipt): it means "remove the
224
224
  // managed block", even while masks are visible — re-writing the block instead of the requested
225
- // clear would be a silent divergence. An EMPTY fence (markers, no entries) clears too (codex R8);
225
+ // clear would be a silent divergence. An EMPTY fence (markers, no entries) clears too;
226
226
  // no fence at all is a stated no-op.
227
227
  if (clear && probe.fence.state !== 'ok') {
228
228
  return { action: 'noop', reason: 'no managed block present — nothing to clear' };
@@ -236,7 +236,7 @@ export const planApply = (probe, { clear = false } = {}) => {
236
236
  }
237
237
  const masks = clear ? [] : probe.masks;
238
238
  const block = masks.length === 0 ? [] : [MASKS_FENCE_START, ...masks.map(toExcludePattern), MASKS_FENCE_END];
239
- // EVERY hand byte outside the managed fence is preserved EXACTLY (codex R5+R6): the first-apply
239
+ // EVERY hand byte outside the managed fence is preserved EXACTLY: the first-apply
240
240
  // append adds at most ONE separator newline, and the existing-block replace splices via RAW
241
241
  // string offsets around the fence lines — a split/join('\n') rebuild would silently normalize
242
242
  // CRLF hand content outside the block. Only the fenced block itself is ours (always LF).
@@ -325,18 +325,18 @@ export const main = (argv, ctx = {}) => {
325
325
  return { code: 0, stdout: formatProbe(probe), stderr: '' };
326
326
  }
327
327
  const plan = planApply(probe, { clear: argv.includes('--clear') });
328
- // The unrenderable skips stay LOUD on EVERY apply path (codex R4) — the same warning the
328
+ // The unrenderable skips stay LOUD on EVERY apply path — the same warning the
329
329
  // probe prints; the renderable subset still applies (a refusal would block the useful masks).
330
330
  const skipWarnings = probe.unrenderable
331
331
  .map((rel) => `sandbox-masks: ⚠ mask name carries a newline or ends with a tab and cannot be expressed as ONE exclude rule — NOT written (the review domain already ignores the mask itself): ${JSON.stringify(rel)}`)
332
332
  .join('\n');
333
- // A refusal carries the unrenderable warnings too (codex R5) — they are never discarded.
333
+ // A refusal carries the unrenderable warnings too — they are never discarded.
334
334
  if (plan.action === 'refuse') throw fail(1, skipWarnings ? `${plan.reason}\n${skipWarnings}` : plan.reason);
335
335
  if (plan.action === 'noop') return { code: 0, stdout: `sandbox-masks: ${plan.reason}`, stderr: skipWarnings };
336
336
  const writeFile = ctx.deps?.writeFile ?? writeFileSync;
337
337
  const mkdir = ctx.deps?.mkdir ?? mkdirSync;
338
338
  const lstat = ctx.deps?.lstat ?? lstatSync;
339
- // The write never follows a symlink or clobbers a non-regular leaf (codex R2): every component
339
+ // The write never follows a symlink or clobbers a non-regular leaf: every component
340
340
  // from the common git dir down is guarded, and an existing exclude leaf must be a regular file.
341
341
  try {
342
342
  assertContainedRealPath(probe.gitCommonDir, probe.excludeFile, { lstat });
@@ -210,7 +210,7 @@ export const main = (argv, ctx = {}) => {
210
210
  const { writtenPath } = writeConfig(cwd, after, ctx);
211
211
  const fileBody = serializeConfig(after);
212
212
  // The echoed handover line carries the SAME autonomy levels recipes --active-line renders —
213
- // sync facts (no render-check needed for the cells; codex R1, Segment B). A malformed policy
213
+ // sync facts (no render-check needed for the cells; Segment B). A malformed policy
214
214
  // surfaces loudly through the line's own MALFORMED segment.
215
215
  const autonomyFacts = (() => {
216
216
  try {
@@ -170,7 +170,7 @@ const RUN_GATES_CWD_FLAG = '--cwd';
170
170
  // final council: a blanket exclusion at opt-in contradicts «never a preemptive blanket»), guided by
171
171
  // codex-exec.sh's own nested-sandbox detection hint.
172
172
  export const BRIDGE_REVIEW_WRAPPERS = Object.freeze(['codex-review', 'agy-review']);
173
- // Only the `code` review mode is auto-allowed (codex R2, Segment A): a bare `Bash(<wrapper>:*)`
173
+ // Only the `code` review mode is auto-allowed (Segment A): a bare `Bash(<wrapper>:*)`
174
174
  // prefix would also cover the plan/diff file-argument modes, whose targets can point OUTSIDE the
175
175
  // repo — the tier's stated surface is the unattended council CODE review, so the seeded prefix is
176
176
  // `<wrapper> code`. Plan/diff invocations keep their prompt.
@@ -210,7 +210,7 @@ export const deriveBridgeTierAllowlist = ({ findWrapper, groundingAbsPath } = {}
210
210
  const allow = placed.map((cmd) => `Bash(${cmd} ${BRIDGE_REVIEW_MODE}:*)`);
211
211
  const excludedCommands = [...placed];
212
212
  // The grounding pre-step exists FOR agy (its grounded --facts reviews; codex grounds natively via
213
- // the AGENTS.md auto-merge) — a codex-only install must not auto-allow an unused writer (codex R9).
213
+ // the AGENTS.md auto-merge) — a codex-only install must not auto-allow an unused writer.
214
214
  if (placed.includes('agy-review')) {
215
215
  // groundingAbsPath is a TEST seam only (an unseedable kit path — spaces — is not constructible
216
216
  // from a test against the real checkout); production callers never pass it.
@@ -420,7 +420,7 @@ const validateSettingsShape = (data, relPath) => {
420
420
  }
421
421
  // The bridge tier merges into sandbox.excludedCommands — a malformed (non-array) value is a STOP
422
422
  // with ZERO writes (the same fail-closed posture as permissions.allow above), never a silent
423
- // treat-as-empty overwrite (codex R1, Segment A).
423
+ // treat-as-empty overwrite (Segment A).
424
424
  if (isJsonObject(data.sandbox) && data.sandbox.excludedCommands !== undefined && !Array.isArray(data.sandbox.excludedCommands)) {
425
425
  throw makeVelocityProfileError(VELOCITY_MALFORMED, `${relPath}: sandbox.excludedCommands must be an array`);
426
426
  }
@@ -647,8 +647,8 @@ const formatKitTier = (result) =>
647
647
  ]
648
648
  : [];
649
649
 
650
- // The bridge tier's honest posture, printed on EVERY --bridge-tier run (test-pinned — the codex-R2
651
- // informed-consent resolution): the exfiltration surface is stated, never pretended away.
650
+ // The bridge tier's honest posture, printed on EVERY --bridge-tier run: the informed-consent
651
+ // resolution states the exfiltration surface, never pretends it away.
652
652
  export const KIT_BRIDGE_TIER_NOTICE =
653
653
  'bridge-wrappers tier: seeds the REVIEW wrappers only, and only their CODE mode (`codex-review code`, `agy-review code` — never codex-exec/agy-run: delegated execution keeps its human prompt; never the plan/diff modes: their file arguments can point outside the repo, so they keep their prompt), each derived ONLY when its bridge is PLACED on PATH, plus the grounding pre-step rule in its rendered quoted byte-form. POSTURE (what this consent covers): an auto-allowed review wrapper runs UNATTENDED — it reads any repo file it is pointed at and sends the assembled payload to its subscription backend, and prefix rules cannot inspect arguments, so a code-mode argument that names a readable file (agy\'s --facts/--decided) rides the same consent — the same documented residual class as the autonomy red-line rules; that is the tier\'s PURPOSE (unattended council review runs) and its residual — tier entries get NO PreToolUse-hook coverage. The grounding entry\'s writer surface is bounded by grounding.mjs\'s OWN scratch-destination guard (a tracked or in-repo-not-ignored --out is refused by the tool). The wrapper names are ALSO seeded into sandbox.excludedCommands IN THE PROJECT settings.json (an exclusion only in settings.local.json was live-observed NOT to route — the wrapper then runs sandboxed and dies on a read-only HOME): the harness runs an excluded command OUTSIDE the sandbox (the wrappers need network), so a plain allowlisted invocation triggers no sandbox-bypass approval. INVOCATION SHAPE: a prefix rule matches only a PLAIN invocation starting with the wrapper name — an env-var prefix or a compound chain never matches (redirects are fine).';
654
654
 
@@ -722,7 +722,7 @@ export const screenAllowlistEntry = (pattern) => {
722
722
  }
723
723
  // Bridge-review-wrapper class (AD-044 Plan 4): EXACTLY `<frozen-tier wrapper> code` + the args
724
724
  // wildcard — never codex-exec/agy-run, never the bare or plan/diff spellings (those file-argument
725
- // modes can read outside the repo; codex R2).
725
+ // modes can read outside the repo).
726
726
  if (BRIDGE_REVIEW_WRAPPERS.includes(tokens[0])) return tokens.length === 2 && tokens[1] === BRIDGE_REVIEW_MODE;
727
727
  return isSingleShellToken(wildcardCmd, tokens) && SHELL_READONLY.includes(wildcardCmd);
728
728
  }
@@ -1196,7 +1196,7 @@ const collectRedlineBypass = (sources) =>
1196
1196
  // sub-keys (merge-don't-clobber, never a silent clobber of the user's sandbox tuning), so a pre-existing
1197
1197
  // weakening sub-key is REPORTED loudly (remove it by hand) — never silently carried as security.
1198
1198
  const collectSandboxWeakenings = (sources) => {
1199
- // Tier-known PROOF (codex R4): an excludedCommands entry is downgraded to a note ONLY when it is
1199
+ // Tier-known PROOF: an excludedCommands entry is downgraded to a note ONLY when it is
1200
1200
  // demonstrably the consented tier's own output — it lives in the PROJECT settings.json (the file
1201
1201
  // the tier writes; a local-file exclusion is never tier output) AND the matching derived
1202
1202
  // code-mode allow rule is present there. A bare name match alone proves nothing.