@sabaiway/agent-workflow-kit 1.44.0 → 1.45.1

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 (52) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +6 -3
  3. package/SKILL.md +9 -1
  4. package/bridges/antigravity-cli-bridge/SKILL.md +1 -1
  5. package/bridges/antigravity-cli-bridge/bin/agy-review.sh +87 -16
  6. package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +3 -0
  7. package/bridges/antigravity-cli-bridge/bin/agy.test.mjs +4 -2
  8. package/bridges/antigravity-cli-bridge/capability.json +3 -2
  9. package/bridges/codex-cli-bridge/SKILL.md +7 -7
  10. package/bridges/codex-cli-bridge/bin/codex-exec.sh +3 -3
  11. package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +4 -1
  12. package/bridges/codex-cli-bridge/bin/codex-review.sh +93 -22
  13. package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +4 -0
  14. package/bridges/codex-cli-bridge/capability.json +4 -3
  15. package/bridges/codex-cli-bridge/references/driving-codex.md +5 -4
  16. package/bridges/codex-cli-bridge/references/sandbox-and-flags.md +5 -4
  17. package/capability.json +1 -1
  18. package/package.json +1 -1
  19. package/references/modes/bootstrap.md +1 -1
  20. package/references/modes/bridge-settings.md +1 -1
  21. package/references/modes/grounding.md +8 -7
  22. package/references/modes/recommendations.md +14 -0
  23. package/references/modes/review-state.md +1 -1
  24. package/references/modes/sandbox-masks.md +15 -0
  25. package/references/modes/upgrade.md +10 -7
  26. package/references/modes/velocity.md +13 -2
  27. package/references/shared/composition-handoff.md +21 -16
  28. package/references/shared/report-footer.md +5 -1
  29. package/references/templates/AGENTS.md +2 -1
  30. package/references/templates/autonomy.json +3 -0
  31. package/tools/autonomy-config.mjs +13 -3
  32. package/tools/commands.mjs +15 -1
  33. package/tools/delegation.mjs +9 -5
  34. package/tools/detect-backends.mjs +2 -2
  35. package/tools/doc-parity.mjs +8 -0
  36. package/tools/engine-source.mjs +6 -0
  37. package/tools/family-registry.mjs +21 -7
  38. package/tools/fold-completeness-run.mjs +5 -0
  39. package/tools/grounding.mjs +139 -22
  40. package/tools/inject-methodology.mjs +117 -28
  41. package/tools/manifest/schema.md +20 -0
  42. package/tools/manifest/validate.mjs +26 -0
  43. package/tools/procedures.mjs +61 -8
  44. package/tools/recipes.mjs +94 -15
  45. package/tools/recommendations.mjs +469 -0
  46. package/tools/review-ledger-write.mjs +14 -0
  47. package/tools/review-ledger.mjs +3 -2
  48. package/tools/review-state.mjs +101 -24
  49. package/tools/run-gates.mjs +3 -0
  50. package/tools/sandbox-masks.mjs +370 -0
  51. package/tools/set-recipe.mjs +13 -1
  52. package/tools/velocity-profile.mjs +228 -22
@@ -36,6 +36,7 @@ import { plansInFlight, PLANS_REL } from './review-state.mjs';
36
36
  // (an import-split test pins it). CONFIG_REL is RE-EXPORTED so existing importers (procedures.test.mjs,
37
37
  // historically) keep their import site working.
38
38
  import { CONFIG_REL, fail, loadConfig, assertSlotRecipe } from './orchestration-config.mjs';
39
+ import { AUTONOMY_REL, loadAutonomy, resolveAutonomy, isSparseSeedConfig } from './autonomy-config.mjs';
39
40
  export { CONFIG_REL };
40
41
 
41
42
  // ── argument + override parsing (usage errors → exit 2) ─────────────────────────────
@@ -224,7 +225,10 @@ const reviewLoopAdvice = (slots, activity) =>
224
225
  // (the review-state plan-in-flight detector): exactly ONE plan in flight → render it populated;
225
226
  // zero or several → the explicit `--plan <path>` placeholder + a one-line discovery caveat. The
226
227
  // suggested --out lives OUTSIDE the repo (/tmp) — grounding.mjs refuses a non-scratch destination.
227
- const GROUNDING_TOOL = join(dirname(fileURLToPath(import.meta.url)), 'grounding.mjs');
228
+ // Exported for the bridge-tier byte-parity pin (AD-044 Plan 4): the velocity tier seeds the
229
+ // grounding rule in EXACTLY this rendered spelling — `node "${GROUNDING_TOOL}"` — so seeded and
230
+ // rendered forms can never drift apart.
231
+ export const GROUNDING_TOOL = join(dirname(fileURLToPath(import.meta.url)), 'grounding.mjs');
228
232
  const GROUNDING_FACTS_OUT = '/tmp/review-facts.md';
229
233
  const groundingPreStepAdvice = (activity, slots, plans) => {
230
234
  if (!slots.some((s) => (s.backends ?? []).includes('agy-review'))) return [];
@@ -242,17 +246,42 @@ const groundingPreStepAdvice = (activity, slots, plans) => {
242
246
  // Path arguments are double-quoted — a skill dir or plan name with a space must stay copy-pasteable.
243
247
  const lines = [
244
248
  'Grounding pre-step (agy is dispatched — assemble the verified facts BEFORE the review; grounding.mjs slices verbatim, judgment additions stay yours):',
245
- ` run: node "${GROUNDING_TOOL}" --constraints ${planArg} --out ${GROUNDING_FACTS_OUT}`,
249
+ ` run: node "${GROUNDING_TOOL}" --constraints --autonomy ${planArg} --out ${GROUNDING_FACTS_OUT}`,
246
250
  ` then: ${reviewForm} --facts @${GROUNDING_FACTS_OUT}`,
247
251
  ];
248
252
  if (plans.length === 0) {
249
- lines.push(` ↳ plan discovery: no plan in flight under ${PLANS_REL} — substitute the plan file you are reviewing against, or drop --plan for constraints-only facts.`);
253
+ lines.push(` ↳ plan discovery: no plan in flight under ${PLANS_REL} — substitute the plan file you are reviewing against, or drop --plan for constraints+autonomy facts.`);
250
254
  } else if (plans.length > 1) {
251
255
  lines.push(` ↳ plan discovery: ${plans.length} plans in flight under ${PLANS_REL} (${plans.join(', ')}) — populate --plan with the one under review.`);
252
256
  }
253
257
  return lines;
254
258
  };
255
259
 
260
+ // The per-activity autonomy block (AD-044 Plan 4): the resolved level for THIS activity + what it
261
+ // implies, rendered from resolveAutonomy — never retyped constants. Read at session start beside
262
+ // the resolved recipes (the AGENTS.md autonomy pointer's read contract). A malformed policy
263
+ // surfaces LOUDLY here (the session-start read is exactly where "malformed → STOP, never guess"
264
+ // must be visible); an absent file states the computed-defaults origin honestly.
265
+ const autonomyAdvice = (activity, facts) => {
266
+ if (facts == null) return [];
267
+ if (facts.error) return [`Autonomy (${AUTONOMY_REL}): MALFORMED — ${facts.error} — STOP and fix the policy file, never guess around it.`];
268
+ const level = facts.activities?.[activity]?.autonomy;
269
+ if (!level) return [];
270
+ const origin = facts.source === 'none'
271
+ ? `computed defaults — no ${AUTONOMY_REL}`
272
+ : facts.defaultsEquivalent
273
+ ? `computed defaults — ${AUTONOMY_REL} is the sparse defaults-equivalent seed`
274
+ : `from ${facts.source}`;
275
+ const redlines = Object.entries(facts.redlines).map(([k, v]) => `${k}=${v}`).join(', ');
276
+ const implies = level === 'sandbox'
277
+ ? 'the OS sandbox confines and auto-allows confined commands — work runs to the next checkpoint without per-command prompts'
278
+ : 'every non-allowlisted command prompts (the sandbox, where enabled, still confines)';
279
+ return [
280
+ `Autonomy for "${activity}" (${origin}): ${level} — ${implies}.`,
281
+ ` red-lines (always): ${redlines} — commit/push/publish keep their maintainer asks regardless of level.`,
282
+ ];
283
+ };
284
+
256
285
  // The cost-lane advisory block (cost-tiered execution — orchestration.md §5 canon, paraphrased
257
286
  // at the point of use like reviewLoopAdvice paraphrases §9/§4). Rendered UNCONDITIONALLY for
258
287
  // every activity — the lanes route EVERY step, review-backed or not (unlike reviewLoopAdvice,
@@ -267,6 +296,7 @@ const costLanesAdvice = () => [
267
296
  ' • L2 subscription bridge (codex / agy) — reviews per the resolved recipe above, on frontier bridge models (quality-first).',
268
297
  ' • L3 frontier — judgment: plan/fold/synthesis, ADR/handover/changelog-entry wording, persuasive copy, go/no-go, real code.',
269
298
  ' • A step with no named guardrail does not move down a lane; red lines never move down (council review models · real code · memory/copy wording · the maintainer approval asks).',
299
+ ' • Sandbox lanes (under an OS sandbox): the L0 surfaces are sandbox-safe — gates/ledger/state/fold checks, git reads, plain no-network tests; the bridge wrappers are genuinely unsandboxed (network); npm-cache-touching commands are COMMAND-SHAPE dependent — first try the sandbox-safe shape (cache under $TMPDIR, offline/notifier off). Move ONLY the failing command out of the sandbox, never its class; BATCH consecutive unsandboxed calls.',
270
300
  ];
271
301
 
272
302
  // The verbatim per-backend DRIVING CONTRACT block (M-contract): the exact invocation descriptor(s),
@@ -297,7 +327,7 @@ const contractLines = ({ cmd, contract, settings }) => {
297
327
  return lines;
298
328
  };
299
329
 
300
- const formatHuman = ({ activity, section, slots, warnings, plans }) => {
330
+ const formatHuman = ({ activity, section, slots, warnings, plans, autonomy }) => {
301
331
  const lines = [
302
332
  section,
303
333
  '',
@@ -309,6 +339,8 @@ const formatHuman = ({ activity, section, slots, warnings, plans }) => {
309
339
  if (s.reason) lines.push(` ↳ ${s.reason}`);
310
340
  for (const c of s.contracts ?? []) lines.push(...contractLines(c));
311
341
  }
342
+ const autonomyBlock = autonomyAdvice(activity, autonomy);
343
+ if (autonomyBlock.length) lines.push('', ...autonomyBlock);
312
344
  const grounding = groundingPreStepAdvice(activity, slots, plans);
313
345
  if (grounding.length) lines.push('', ...grounding);
314
346
  const advice = reviewLoopAdvice(slots, activity);
@@ -321,7 +353,7 @@ const formatHuman = ({ activity, section, slots, warnings, plans }) => {
321
353
  return lines.join('\n');
322
354
  };
323
355
 
324
- const buildJson = ({ activity, section, slots, configSource, warnings, plans }) => ({
356
+ const buildJson = ({ activity, section, slots, configSource, warnings, plans, autonomy }) => ({
325
357
  activity,
326
358
  section,
327
359
  slots: Object.fromEntries(
@@ -334,6 +366,8 @@ const buildJson = ({ activity, section, slots, configSource, warnings, plans })
334
366
  groundingPreStep: groundingPreStepAdvice(activity, slots, plans),
335
367
  // ADDITIVE (cost-tiered execution): the unconditional cost-lane advisory, structured.
336
368
  costLanes: costLanesAdvice(),
369
+ // ADDITIVE (AD-044 Plan 4): the per-activity autonomy block, structured (empty when unresolvable).
370
+ autonomy: autonomyAdvice(activity, autonomy),
337
371
  configSource,
338
372
  warnings,
339
373
  });
@@ -354,7 +388,8 @@ ${CONFIG_REL} + the read-only backend detector, and prints both. A per-run
354
388
  Read-only: never writes, never commits, never runs a subscription CLI.
355
389
 
356
390
  Exit codes: 0 success (an unsatisfiable override degrades loudly, still 0);
357
- 2 usage (unknown activity / bad --override); 1 config or engine error.`;
391
+ 2 usage (unknown activity / bad --override); 1 config or engine error
392
+ (incl. a malformed ${AUTONOMY_REL} — the advisory still renders, the exit flips).`;
358
393
 
359
394
  // ── main ───────────────────────────────────────────────────────────────────────────
360
395
 
@@ -387,9 +422,27 @@ export const main = (argv, ctx = {}) => {
387
422
  const slots = resolveAllSlots({ activity, config, detection, overrides });
388
423
  const warnings = [...detectWarnings, ...collectWarnings(slots)];
389
424
  const plans = plansInFlight(cwd);
425
+ // The autonomy facts (AD-044 Plan 4): resolved levels + red-lines from the policy file. A
426
+ // malformed policy renders LOUDLY in the block AND flips the exit to 1 (config error) — the
427
+ // recipes and contracts still print, only the code changes.
428
+ const autonomy = (() => {
429
+ try {
430
+ const { config: autonomyConfig, source } = loadAutonomy(cwd, readFile, lstat);
431
+ const resolved = resolveAutonomy(autonomyConfig);
432
+ // Structural seed detection (shared predicate) — a declared-defaults policy reads as
433
+ // "from docs/ai/autonomy.json", never as the seed (codex, Segment B closing).
434
+ const defaultsEquivalent = source !== 'none' && isSparseSeedConfig(autonomyConfig);
435
+ return { source, defaultsEquivalent, ...resolved };
436
+ } catch (err) {
437
+ return { error: (err && err.message) || String(err) };
438
+ }
439
+ })();
390
440
  const stdout = json
391
- ? JSON.stringify(buildJson({ activity, section, slots, configSource, warnings, plans }), null, 2)
392
- : formatHuman({ activity, section, slots, warnings, plans });
441
+ ? JSON.stringify(buildJson({ activity, section, slots, configSource, warnings, plans, autonomy }), null, 2)
442
+ : formatHuman({ activity, section, slots, warnings, plans, autonomy });
443
+ if (autonomy?.error) {
444
+ return { code: 1, stdout, stderr: `procedures: malformed ${AUTONOMY_REL} — ${autonomy.error}` };
445
+ }
393
446
  return { code: 0, stdout, stderr: '' };
394
447
  } catch (err) {
395
448
  return { code: err.exitCode ?? 1, stdout: '', stderr: `procedures: ${err.message}` };
package/tools/recipes.mjs CHANGED
@@ -14,6 +14,8 @@
14
14
  // orchestrator (the main agent) executes it via the bridge skills and always makes the single commit;
15
15
  // a backend is advisory or delegated, never autonomous. Dependency-free, Node >= 18.
16
16
 
17
+ import { existsSync } from 'node:fs';
18
+ import { dirname, join, resolve } from 'node:path';
17
19
  import { pathToFileURL } from 'node:url';
18
20
  // The host-level bridge-settings snapshot (fact-only, best-effort). READ-ONLY core only — never the
19
21
  // writer — so this read-only advisor never pulls in the atomic-write core.
@@ -296,7 +298,7 @@ export const resolveActivityRecipe = ({ config = {}, readiness = [], activity, s
296
298
  // DISPLAY_ALIASES — the ONE alias table the recommendation clause already uses; ordering is the
297
299
  // deterministic BACKEND_PRIORITY (codex before agy), independent of detection emission order.
298
300
  // Always exactly one line: no part may carry a newline (pinned by tests).
299
- export const composeStatusLine = (detection, recommendation, settings = null) => {
301
+ export const composeStatusLine = (detection, recommendation, settings = null, autonomy = null) => {
300
302
  const backends = [...detection]
301
303
  .sort((a, b) => priorityIndex(a.name) - priorityIndex(b.name))
302
304
  .map((b) => `${DISPLAY_ALIASES[b.name] ?? b.name} ${b.readiness === READY ? '✓' : '✗'} ${b.readiness}`)
@@ -308,7 +310,71 @@ export const composeStatusLine = (detection, recommendation, settings = null) =>
308
310
  const oneLine = (s) => String(s).replace(/[\s]+/g, ' ').trim();
309
311
  const active = settings?.active ?? [];
310
312
  const suffix = active.length ? ` · settings: ${active.map((s) => `${oneLine(s.key)}=${oneLine(s.value)}`).join(' · ')}` : '';
311
- return base + suffix;
313
+ // The autonomy segment (AD-044 Plan 4): rendered ONLY when the caller supplies the computed
314
+ // facts (composeAutonomyFacts) — an omitted param keeps the line byte-identical (the settings-
315
+ // suffix precedent). Fact-only: effective per-activity levels + the render-sync state; an absent
316
+ // policy says "computed defaults" honestly; a malformed policy surfaces LOUDLY, never omitted.
317
+ const autonomySegment = autonomy == null ? '' : ` · autonomy: ${oneLine(formatAutonomySegment(autonomy))}`;
318
+ return base + suffix + autonomySegment;
319
+ };
320
+
321
+ // The one-segment autonomy renderer behind composeStatusLine's 4th param.
322
+ const formatAutonomySegment = (a) => {
323
+ if (a.error) return `MALFORMED policy — ${a.error}`;
324
+ const levels = Object.entries(a.activities ?? {}).map(([k, v]) => `${k}=${v.autonomy}`).join(', ');
325
+ const state = a.source === 'none'
326
+ ? 'computed defaults — no policy file; declare with /agent-workflow-kit set-autonomy'
327
+ : a.defaultsEquivalent
328
+ ? 'declared, defaults-equivalent — computed defaults apply; declare levels with /agent-workflow-kit set-autonomy'
329
+ : `declared; render ${a.renderState}`;
330
+ return `${levels} (${state})`;
331
+ };
332
+
333
+ // composeAutonomyFacts(cwd) → the fact object the status/active lines render (AD-044 Plan 4):
334
+ // { source, redlines, activities, renderState } or { error }. Lazy imports — autonomy-config
335
+ // statically imports THIS module (ACTIVITIES) and velocity-profile is heavy, so both load at call
336
+ // time only (the loadConfig precedent). Never throws: a malformed policy becomes { error } so the
337
+ // one-line surfaces render it loudly instead of dying.
338
+ // The policy lives at the PROJECT root; the report-footer invokes the paste surfaces without
339
+ // --cwd, so a subdirectory shell must still find it. Nearest-.git walk-up (dir or worktree file),
340
+ // fs-only — this advisor stays spawn-free; no repo found → the cwd itself (fixture behavior).
341
+ const projectTopOf = (cwd) => {
342
+ let dir = resolve(cwd);
343
+ for (;;) {
344
+ if (existsSync(join(dir, '.git'))) return dir;
345
+ const parent = dirname(dir);
346
+ if (parent === dir) return resolve(cwd);
347
+ dir = parent;
348
+ }
349
+ };
350
+
351
+ export const composeAutonomyFacts = async (cwd) => {
352
+ try {
353
+ const root = projectTopOf(cwd);
354
+ const { loadAutonomy, resolveAutonomy, isSparseSeedConfig } = await import('./autonomy-config.mjs');
355
+ const { config, source } = loadAutonomy(root);
356
+ const resolved = resolveAutonomy(config);
357
+ if (source === 'none') return { source, redlines: resolved.redlines, activities: resolved.activities, renderState: null };
358
+ // The STRUCTURAL seed (_README only) is a fresh-deployment NORMAL — treating it as "declared"
359
+ // would report "render DRIFT" on every fresh upgrade (codex, Segment B). An EXPLICIT policy
360
+ // declaring the default values is a real declaration — its render state IS computed
361
+ // (structural detection, codex Segment B closing).
362
+ if (isSparseSeedConfig(config)) {
363
+ return { source, defaultsEquivalent: true, redlines: resolved.redlines, activities: resolved.activities, renderState: null };
364
+ }
365
+ let renderState;
366
+ try {
367
+ const { checkAutonomyProfile } = await import('./velocity-profile.mjs');
368
+ renderState = checkAutonomyProfile({ cwd: root }).inSync
369
+ ? 'in sync'
370
+ : 'DRIFT — re-run the velocity --autonomy render';
371
+ } catch (err) {
372
+ renderState = `unchecked (${err?.message ?? err})`;
373
+ }
374
+ return { source, redlines: resolved.redlines, activities: resolved.activities, renderState };
375
+ } catch (err) {
376
+ return { error: err?.message ?? String(err) };
377
+ }
312
378
  };
313
379
 
314
380
  // ── the one-line ACTIVE-recipe line (the discovery line — configured, never recommended) ───────────
@@ -321,17 +387,21 @@ export const composeStatusLine = (detection, recommendation, settings = null) =>
321
387
  // session-start checklist + the handover "Active recipes:" slot paste it verbatim, so no agent composes
322
388
  // the configured-recipe facts by hand. `{ config, source }` is exactly what loadConfig returns (source
323
389
  // 'none' when no config file exists). Always exactly one line: no part may carry a newline (pinned).
324
- export const composeActiveRecipeLine = ({ config, source } = {}, detection) => {
390
+ export const composeActiveRecipeLine = ({ config, source } = {}, detection, autonomy = null) => {
325
391
  const cells = [];
326
392
  for (const [activity, def] of Object.entries(ACTIVITIES)) {
393
+ // The per-activity autonomy level rides each cell when the caller supplies the facts (AD-044
394
+ // Plan 4) — an omitted param keeps the line byte-identical (the composeStatusLine precedent).
395
+ const level = autonomy?.activities?.[activity]?.autonomy;
396
+ const auto = level ? `; autonomy ${level}` : '';
327
397
  for (const slot of Object.keys(def.slots)) {
328
398
  const r = resolveActivityRecipe({ config: config ?? {}, readiness: detection, activity, slot });
329
399
  const { dispatch } = planRecipe(r.recipe, detection);
330
400
  const wrappers = dispatch.map((d) => wrapperCmdFor(d.backend, d.role)).filter(Boolean);
331
401
  const srcLabel = r.source === 'config' ? 'configured' : 'computed default';
332
402
  const head = r.degradedFrom
333
- ? `${activity}.${slot} = ${r.degradedFrom} (${srcLabel}; degrades here to ${r.recipe} — ${r.reason})`
334
- : `${activity}.${slot} = ${r.recipe} (${srcLabel})`;
403
+ ? `${activity}.${slot} = ${r.degradedFrom} (${srcLabel}; degrades here to ${r.recipe} — ${r.reason}${auto})`
404
+ : `${activity}.${slot} = ${r.recipe} (${srcLabel}${auto})`;
335
405
  const suffix =
336
406
  wrappers.length >= 2
337
407
  ? ` → every backend every round: ${wrappers.join(' + ')}`
@@ -343,14 +413,19 @@ export const composeActiveRecipeLine = ({ config, source } = {}, detection) => {
343
413
  }
344
414
  const rec = recommendRecipe(detection);
345
415
  const origin = source === 'none' || config == null ? 'no config file — computed defaults apply' : `from ${source}`;
346
- return `active recipes (${origin}): ${cells.join(' · ')} the configured recipes above are what runs; readiness-recommended here: ${rec.recipe} (informational)`;
416
+ // A MALFORMED policy must surface LOUDLY on this paste surface too silently rendering cells
417
+ // without levels would hide the required STOP signal (codex R1, Segment B). One line always.
418
+ const malformed = autonomy?.error
419
+ ? ` · autonomy: MALFORMED policy — ${String(autonomy.error).replace(/[\s]+/g, ' ').trim()}`
420
+ : '';
421
+ return `active recipes (${origin}): ${cells.join(' · ')} — the configured recipes above are what runs; readiness-recommended here: ${rec.recipe} (informational)${malformed}`;
347
422
  };
348
423
 
349
424
  // ── report + CLI ─────────────────────────────────────────────────────────────────
350
425
 
351
426
  // The structured report behind `--json` — the recipes, the recommendation, a plan per recipe, and
352
427
  // (additive) the pasteable one-line backend status composed from the same detection.
353
- export const buildReport = (detection, settings = null) => {
428
+ export const buildReport = (detection, settings = null, autonomy = null) => {
354
429
  const recommendation = recommendRecipe(detection);
355
430
  return {
356
431
  recipes: RECIPES.map(({ id, title, role, minBackends, degradesTo, summary }) => ({
@@ -363,7 +438,9 @@ export const buildReport = (detection, settings = null) => {
363
438
  })),
364
439
  recommendation,
365
440
  plans: RECIPES.map((r) => planRecipe(r.id, detection)),
366
- statusLine: composeStatusLine(detection, recommendation, settings),
441
+ // The SAME autonomy facts the --status-line surface renders — the --json envelope must never
442
+ // expose a stale machine-composed status line (codex R1, Segment B).
443
+ statusLine: composeStatusLine(detection, recommendation, settings, autonomy),
367
444
  };
368
445
  };
369
446
 
@@ -404,10 +481,12 @@ Usage:
404
481
 
405
482
  Lists the four recipes (Solo / Reviewed / Council / Delegated) and, from the read-only backend
406
483
  detector, plans + recommends one for the current environment. --status-line prints exactly ONE
407
- line — the machine-composed backend-status summary the bootstrap/upgrade reports paste verbatim.
408
- --active-line prints exactly ONE line the CONFIGURED recipe per activity/slot, resolved from the
409
- per-project docs/ai/orchestration.json (read from the current directory) + live readiness, with
410
- degradation stated; paste it verbatim at session start / into the handover "Active recipes:" slot.
484
+ line — the machine-composed backend-status summary the bootstrap/upgrade reports paste verbatim
485
+ (incl. the per-activity autonomy segment: effective levels + render-sync state, honest
486
+ computed-defaults wording when no policy file exists). --active-line prints exactly ONE line — the
487
+ CONFIGURED recipe per activity/slot, resolved from the per-project docs/ai/orchestration.json (read
488
+ from the current directory) + live readiness, with degradation stated and each activity's autonomy
489
+ level beside its cells; paste it verbatim at session start / into the handover "Active recipes:" slot.
411
490
  --json emits the structured report (incl. the same line as \`statusLine\`); the three are mutually
412
491
  exclusive. Detection only — never writes, never commits, never runs a subscription CLI.`);
413
492
  return;
@@ -428,13 +507,13 @@ exclusive. Detection only — never writes, never commits, never runs a subscrip
428
507
  // so the config reader is pulled in at run time only — no static import cycle.
429
508
  const { loadConfig } = await import('./orchestration-config.mjs');
430
509
  try {
431
- console.log(composeActiveRecipeLine(loadConfig(process.cwd()), detection));
510
+ console.log(composeActiveRecipeLine(loadConfig(process.cwd()), detection, await composeAutonomyFacts(process.cwd())));
432
511
  } catch (err) {
433
512
  console.error(`[agent-workflow-kit] ${err.message}`);
434
513
  return err.exitCode ?? 1;
435
514
  }
436
- } else if (argv.includes('--status-line')) console.log(composeStatusLine(detection, recommendRecipe(detection), settingsSnapshot()));
437
- else if (argv.includes('--json')) console.log(JSON.stringify(buildReport(detection, settingsSnapshot()), null, 2));
515
+ } else if (argv.includes('--status-line')) console.log(composeStatusLine(detection, recommendRecipe(detection), settingsSnapshot(), await composeAutonomyFacts(process.cwd())));
516
+ else if (argv.includes('--json')) console.log(JSON.stringify(buildReport(detection, settingsSnapshot(), await composeAutonomyFacts(process.cwd())), null, 2));
438
517
  else console.log(formatRecipes(detection));
439
518
  return 0;
440
519
  };