mandrel 1.90.0 → 1.92.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 (115) hide show
  1. package/.agents/README.md +5 -5
  2. package/.agents/docs/SDLC.md +75 -107
  3. package/.agents/docs/configuration.md +1 -1
  4. package/.agents/docs/quality-gates.md +8 -7
  5. package/.agents/docs/workflows.md +4 -10
  6. package/.agents/instructions.md +2 -2
  7. package/.agents/schemas/agentrc.schema.json +1 -1
  8. package/.agents/schemas/lifecycle/README.md +2 -1
  9. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  10. package/.agents/schemas/risk-verdict.schema.json +14 -1
  11. package/.agents/scripts/README.md +0 -46
  12. package/.agents/scripts/analyze-execution.js +76 -1
  13. package/.agents/scripts/check-doc-links.js +3 -0
  14. package/.agents/scripts/check-workflow-cli-lint.js +208 -0
  15. package/.agents/scripts/epic-audit-recheck.js +1 -1
  16. package/.agents/scripts/epic-deliver-preflight.js +37 -1
  17. package/.agents/scripts/epic-plan-clarity.js +16 -6
  18. package/.agents/scripts/epic-plan-decompose.js +37 -59
  19. package/.agents/scripts/epic-plan-healthcheck.js +39 -6
  20. package/.agents/scripts/epic-plan-spec.js +41 -168
  21. package/.agents/scripts/epic-reconcile.js +7 -1
  22. package/.agents/scripts/lib/Logger.js +4 -4
  23. package/.agents/scripts/lib/audit-suite/index.js +4 -5
  24. package/.agents/scripts/lib/audit-suite/runner.js +6 -4
  25. package/.agents/scripts/lib/audit-suite/selector.js +2 -3
  26. package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
  27. package/.agents/scripts/lib/command-header.js +20 -0
  28. package/.agents/scripts/lib/config/github.js +1 -3
  29. package/.agents/scripts/lib/config-settings-schema.js +4 -3
  30. package/.agents/scripts/lib/degraded-mode.js +1 -1
  31. package/.agents/scripts/lib/label-constants.js +18 -3
  32. package/.agents/scripts/lib/label-taxonomy.js +14 -3
  33. package/.agents/scripts/lib/mutation/baseline-snapshot.js +7 -6
  34. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +18 -8
  35. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +3 -4
  36. package/.agents/scripts/lib/orchestration/docs-digest.js +2 -2
  37. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +4 -0
  38. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +22 -12
  39. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +6 -1
  40. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +18 -2
  41. package/.agents/scripts/{lifecycle-diff.js → lib/orchestration/lifecycle/ledger-diff.js} +10 -76
  42. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +1 -1
  43. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +32 -0
  44. package/.agents/scripts/lib/orchestration/plan-context.js +510 -0
  45. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +177 -0
  46. package/.agents/scripts/lib/orchestration/plan-metrics.js +445 -0
  47. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +359 -0
  48. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +127 -0
  49. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +978 -0
  50. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +191 -0
  51. package/.agents/scripts/lib/orchestration/plan-reachability.js +160 -0
  52. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +3 -3
  53. package/.agents/scripts/lib/orchestration/planning-risk.js +1 -1
  54. package/.agents/scripts/lib/orchestration/remote-verifier.js +165 -0
  55. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +10 -0
  56. package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
  57. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +1 -1
  58. package/.agents/scripts/lib/orchestration/ticketing/reads.js +11 -2
  59. package/.agents/scripts/lib/plan-phase-cleanup.js +16 -0
  60. package/.agents/scripts/lib/presentation/manifest-persistence.js +2 -3
  61. package/.agents/scripts/lib/templates/spec-author-prompts.js +74 -0
  62. package/.agents/scripts/plan-context.js +186 -0
  63. package/.agents/scripts/plan-critics.js +227 -0
  64. package/.agents/scripts/plan-persist.js +383 -0
  65. package/.agents/scripts/pr-watch-with-update.js +8 -7
  66. package/.agents/scripts/run-lint.js +10 -11
  67. package/.agents/scripts/single-story-init.js +22 -0
  68. package/.agents/scripts/story-plan.js +19 -7
  69. package/.agents/scripts/sync-claude-commands.js +10 -5
  70. package/.agents/skills/core/knowledge-transfer/SKILL.md +11 -7
  71. package/.agents/workflows/audit-documentation.md +5 -7
  72. package/.agents/workflows/audit-lighthouse.md +1 -0
  73. package/.agents/workflows/audit-security.md +1 -0
  74. package/.agents/workflows/audit-to-stories.md +16 -5
  75. package/.agents/workflows/deliver.md +8 -0
  76. package/.agents/workflows/git-deliver.md +76 -243
  77. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  78. package/.agents/workflows/helpers/code-review.md +7 -6
  79. package/.agents/workflows/helpers/deliver-epic.md +16 -4
  80. package/.agents/workflows/helpers/plan-epic-reference.md +109 -96
  81. package/.agents/workflows/helpers/plan-epic.md +246 -999
  82. package/.agents/workflows/helpers/plan-story.md +14 -14
  83. package/.agents/workflows/helpers/scope-triage-gate.md +6 -4
  84. package/.agents/workflows/helpers/single-story-deliver.md +13 -4
  85. package/.agents/workflows/helpers/worktree-lifecycle.md +9 -7
  86. package/.agents/workflows/mandrel-update.md +144 -466
  87. package/.agents/workflows/plan.md +73 -143
  88. package/docs/CHANGELOG.md +20 -0
  89. package/lib/cli/registry.js +21 -3
  90. package/package.json +1 -1
  91. package/.agents/schemas/loop-unit.schema.json +0 -70
  92. package/.agents/scripts/assert-branch.js +0 -81
  93. package/.agents/scripts/check-loop-units.js +0 -204
  94. package/.agents/scripts/detect-merges.js +0 -111
  95. package/.agents/scripts/git-pr-quality-gate.js +0 -205
  96. package/.agents/scripts/git-rebase-and-resolve.js +0 -234
  97. package/.agents/scripts/hierarchy-gate.js +0 -192
  98. package/.agents/scripts/hydrate-context.js +0 -179
  99. package/.agents/scripts/lib/loop-units/validate-loop-unit.js +0 -197
  100. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/cli.js +0 -167
  101. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/cli-args.js +0 -69
  102. package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +0 -86
  103. package/.agents/scripts/loc-delta.js +0 -205
  104. package/.agents/scripts/render-manifest.js +0 -143
  105. package/.agents/scripts/run-audit-suite.js +0 -97
  106. package/.agents/scripts/select-audits.js +0 -155
  107. package/.agents/scripts/update-mutation-baseline.js +0 -189
  108. package/.agents/workflows/explain.md +0 -118
  109. package/.agents/workflows/git-merge-pr.md +0 -377
  110. package/.agents/workflows/helpers/epic-plan-decompose.md +0 -22
  111. package/.agents/workflows/helpers/epic-plan-spec.md +0 -22
  112. package/.agents/workflows/loops/README.md +0 -65
  113. package/.agents/workflows/loops/fix-failing-tests.md +0 -74
  114. package/.agents/workflows/loops/nightly-audit.md +0 -81
  115. package/.agents/workflows/loops/watch-ci.md +0 -68
@@ -5,9 +5,12 @@
5
5
  /**
6
6
  * epic-plan-healthcheck.js — Post-Plan Readiness Check
7
7
  *
8
- * Runs at the end of /plan (Phase 10) to validate the backlog and
9
- * optionally prime the execution environment before handing off to
10
- * /epic-deliver.
8
+ * Library home of `runPlanHealthcheck`, the **inline** blocking check the
9
+ * collapsed persist surface runs as the `agent::ready` exit condition
10
+ * (`plan-persist.js` step 9 — the authoritative invocation since Epic
11
+ * #4474 retired the standalone Phase 10 workflow step). The CLI form
12
+ * remains for manual re-validation and the opt-in slow paths below; no
13
+ * workflow mandates running it standalone.
11
14
  *
12
15
  * Modes (additive — the fast checks below always run):
13
16
  * (default) — config validation + git remote check only.
@@ -30,7 +33,7 @@
30
33
  * node epic-plan-healthcheck.js --epic <EPIC_ID> \
31
34
  * [--paranoid] [--prime-install] [--dry-run]
32
35
  *
33
- * @see .agents/workflows/helpers/plan-epic.md Phase 10
36
+ * @see .agents/workflows/helpers/plan-epic.md (persist step — inline gate)
34
37
  */
35
38
 
36
39
  import { spawnSync } from 'node:child_process';
@@ -47,6 +50,7 @@ import {
47
50
  import { gitSpawn } from './lib/git-utils.js';
48
51
  import { Logger } from './lib/Logger.js';
49
52
  import { TYPE_LABELS } from './lib/label-constants.js';
53
+ import { recordPlanInvocation } from './lib/orchestration/plan-metrics.js';
50
54
  import { createProvider } from './lib/provider-factory.js';
51
55
 
52
56
  const progress = Logger.createProgress('plan-healthcheck', { stderr: true });
@@ -454,7 +458,11 @@ async function timed(name, fn) {
454
458
  * checks: Array<{name: string, ok: boolean, durationMs: number, detail: string}>}>}
455
459
  */
456
460
  // exported for tests — direct-unit coverage of the reachability semantics.
457
- export { checkReachability, extractStoryPaths, globToRegExp };
461
+ // (`resolveNavConfig` / `extractStoryPaths` / `globToRegExp` are also the
462
+ // shared mechanics of the persist-side draft reachability check —
463
+ // `lib/orchestration/plan-reachability.js`, Epic #4474 PR6 — so the two
464
+ // surfaces can never drift apart.)
465
+ export { checkReachability, extractStoryPaths, globToRegExp, resolveNavConfig };
458
466
 
459
467
  // exported for tests — Story-level reuse runner reserved for future test coverage
460
468
  export async function runPlanHealthcheck(opts = {}) {
@@ -543,6 +551,31 @@ export async function runPlanHealthcheck(opts = {}) {
543
551
  // Main guard
544
552
  // ---------------------------------------------------------------------------
545
553
 
546
- runAsCli(import.meta.url, runPlanHealthcheck, {
554
+ /**
555
+ * CLI wrapper: stamp the plan-metrics ledger (#4474 PR1) around the direct
556
+ * invocation only. In-process callers (the decompose persist gate imports
557
+ * `runPlanHealthcheck` directly) are already covered by their own CLI's
558
+ * stamp — wrapping here too would double-count the inline gate.
559
+ */
560
+ async function cliMain() {
561
+ const { epicId } = parseHealthcheckArgs();
562
+ let config;
563
+ try {
564
+ config = resolveConfig();
565
+ } catch {
566
+ config = undefined;
567
+ }
568
+ return recordPlanInvocation(
569
+ {
570
+ cli: 'epic-plan-healthcheck',
571
+ mode: 'healthcheck',
572
+ epicId: epicId ?? null,
573
+ config,
574
+ },
575
+ () => runPlanHealthcheck(),
576
+ );
577
+ }
578
+
579
+ runAsCli(import.meta.url, cliMain, {
547
580
  source: 'epic-plan-healthcheck',
548
581
  });
@@ -1,191 +1,64 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ /* node:coverage ignore file */
4
+
3
5
  /**
4
- * epic-plan-spec.js — Phase 7 (spec) entry point for the split planning flow.
5
- *
6
- * Two idempotent modes and a single-purpose label lifecycle:
6
+ * epic-plan-spec.js — RETIRED delegate CLI (Epic #4474, PR7).
7
7
  *
8
- * 1. --emit-context Prints the planner authoring context (Epic body,
9
- * scraped project docs, recommended system prompts) as
10
- * JSON. The authoring middle is the
11
- * `epic-plan-spec-author` Skill (see
12
- * `.agents/skills/core/epic-plan-spec-author/SKILL.md`),
13
- * which consumes this envelope and writes the Tech Spec
14
- * markdown file.
8
+ * The 12-phase plan pipeline collapsed to context author → persist:
15
9
  *
16
- * 2. (default) Given author-provided Tech Spec and risk-verdict
17
- * files, validates the risk verdict against
18
- * `risk-verdict.schema.json`, derives the planningRisk
19
- * envelope, folds the authored content into managed
20
- * sections of the Epic body (Story #4324 — no separate
21
- * context tickets), records the verdict as a
22
- * `risk-verdict` structured comment, flips the Epic to
23
- * `agent::review-spec`, and upserts the
24
- * `epic-plan-state` structured comment.
10
+ * - `--emit-context` moved to `plan-context.js` (single authoring
11
+ * envelope: Epic body + docs digest + codebase snapshot + duplicate
12
+ * search + clarity + system prompts).
13
+ * - The persist half (section gate, risk verdict, managed sections,
14
+ * checkpoint) moved to `plan-persist.js` (single GitHub-write surface).
25
15
  *
26
- * --force regenerates the existing Tech Spec.
27
- * --steal forcibly transfers a foreign Epic-lease claim (the plan-lease guard
28
- * fails closed, so any foreign assignee blocks the run unless stolen).
16
+ * This file is a **re-export shim only** — it carries external importers of
17
+ * the historic named-export surface one more release (#4474 design §6 PR7
18
+ * risk note) and is deleted in the next release. Internal consumers import
19
+ * the phase modules directly; do not add new imports of this file.
29
20
  *
30
- * Exit codes:
31
- * 0 — phase complete, Epic is now `agent::review-spec`.
32
- * 1 — fatal error (see stderr).
33
- *
34
- * The phase implementations live under
35
- * `lib/orchestration/epic-plan-spec/phases/`. This file is now a thin CLI
36
- * entry that wires argv → phases.
21
+ * Invoking it as a CLI is refused with a pointer to the successor CLIs.
37
22
  */
38
23
 
39
- // Fail-fast if the framework's runtime deps are not installed must be the
40
- // first import so the check runs before any third-party-importing sibling
41
- // module is evaluated (Story #3432).
42
- import './lib/runtime-deps/ensure-installed.js';
43
- import { readFile } from 'node:fs/promises';
44
- import {
24
+ // cli-opt-out: retired delegate shim (Epic #4474 PR7)deliberately
25
+ // refuses CLI execution with a pointer to plan-context.js/plan-persist.js
26
+ // instead of wiring runAsCli around a dead main().
27
+ import { pathToFileURL } from 'node:url';
28
+
29
+ export {
45
30
  forkAndCommitEpicSnapshot,
46
31
  forkMainToEpic,
47
32
  } from './lib/baseline-snapshot.js';
48
-
49
- // Re-exported so the historic import path
50
- // (`epic-plan-spec.js#forkAndCommitEpicSnapshot`) and existing tests keep
51
- // working after Story #1585 relocated the wrapper into the lower-level
52
- // `lib/baseline-snapshot.js` module. `forkMainToEpic` is also re-exported
53
- // for the same reason.
54
- export { forkAndCommitEpicSnapshot, forkMainToEpic };
55
-
56
- import { runAsCli } from './lib/cli-utils.js';
57
- import {
58
- PROJECT_ROOT,
59
- resolveConfig,
60
- validateOrchestrationConfig,
61
- } from './lib/config-resolver.js';
62
- import { Logger, routeAllOutputToStderr, STDERR_LOGGER } from './lib/Logger.js';
63
- import {
33
+ export {
64
34
  buildAuthoringContext,
65
35
  resolveMemoryDir,
66
36
  } from './lib/orchestration/epic-plan-spec/phases/authoring-context.js';
67
- import { parseEpicPlanSpecArgs } from './lib/orchestration/epic-plan-spec/phases/cli-args.js';
68
- import { drainPendingCleanupAtBoot } from './lib/orchestration/epic-plan-spec/phases/drain.js';
69
- import {
37
+ export { drainPendingCleanupAtBoot } from './lib/orchestration/epic-plan-spec/phases/drain.js';
38
+ export {
70
39
  planEpic,
71
40
  resolveAcceptancePersistence,
72
41
  } from './lib/orchestration/epic-plan-spec/phases/plan-epic.js';
73
- import {
74
- loadRiskVerdict,
75
- validateRiskVerdict,
76
- } from './lib/orchestration/epic-plan-spec/phases/risk-verdict.js';
77
- import { runSpecPhase } from './lib/orchestration/epic-plan-spec/phases/run-spec-phase.js';
78
- import { runSpecFreshnessCheck } from './lib/orchestration/epic-plan-spec/phases/spec-freshness.js';
79
- import { resolveReviewRouting } from './lib/orchestration/plan-review-routing.js';
80
- import { createProvider } from './lib/provider-factory.js';
81
-
82
- // Re-exports for stable public API: tests and external callers import these
83
- // from `epic-plan-spec.js`. The implementations live in `phases/`.
84
42
  export {
85
- buildAuthoringContext,
86
- drainPendingCleanupAtBoot,
87
43
  loadRiskVerdict,
88
- planEpic,
89
- resolveAcceptancePersistence,
90
- resolveMemoryDir,
91
- resolveReviewRouting,
92
- runSpecFreshnessCheck,
93
- runSpecPhase,
94
44
  validateRiskVerdict,
95
- };
96
-
97
- async function main() {
98
- const { values, epicId } = parseEpicPlanSpecArgs();
99
-
100
- let config;
101
- let settings;
102
- try {
103
- config = resolveConfig();
104
- // `settings` retains the legacy bag shape used by buildAuthoringContext
105
- // and friends: `{ baseBranch, paths, planning, ... }`. Build it from the
106
- // canonical blocks rather than the legacy shim.
107
- settings = {
108
- baseBranch: config.project?.baseBranch,
109
- paths: config.project?.paths,
110
- planning: config.planning,
111
- docsContextFiles: config.project?.docsContextFiles,
112
- };
113
- validateOrchestrationConfig(config);
114
- } catch (err) {
115
- throw new Error(`Config schema validation failed:\n${err.message}`);
116
- }
117
- const provider = createProvider(config);
118
-
119
- const emitContext = values['emit-context'];
120
- // Story #2278 — in --emit-context mode stdout is reserved for the JSON
121
- // envelope. Flip every Logger sink that could land on stdout to stderr
122
- // *before* any pipeline code runs (drainPendingCleanupAtBoot,
123
- // buildAuthoringContext → ensureDocsDigest — Story #4433 cut the digest
124
- // build over from the old buildDocsContext/scrapeProjectDocs full-content
125
- // read path), so a captured file is unconditionally parseable by
126
- // `JSON.parse`.
127
- if (emitContext) routeAllOutputToStderr();
128
-
129
- try {
130
- await drainPendingCleanupAtBoot({
131
- repoRoot: PROJECT_ROOT,
132
- config,
133
- provider,
134
- // In --emit-context mode stdout is reserved for the JSON envelope;
135
- // route every drain/sweep log line through stderr so the captured
136
- // file is unconditionally parseable.
137
- logger: emitContext ? STDERR_LOGGER : undefined,
138
- });
139
- } catch (err) {
140
- Logger.warn(
141
- `[epic-plan-spec] pending-cleanup drain skipped: ${err.message}`,
142
- );
143
- }
144
-
145
- if (emitContext) {
146
- const ctx = await buildAuthoringContext(epicId, provider, settings, {
147
- fullContext: values['full-context'],
148
- github: config.github ?? null,
149
- });
150
- const json = values.pretty
151
- ? JSON.stringify(ctx, null, 2)
152
- : JSON.stringify(ctx);
153
- process.stdout.write(`${json}\n`);
154
- return;
155
- }
156
-
157
- if (!values['tech-spec'] || !values['risk-verdict']) {
158
- throw new Error(
159
- 'Missing --tech-spec and/or --risk-verdict file paths. (Use --emit-context first to gather authoring context; the epic-plan-spec-author Skill writes all artifacts including risk-verdict.json.)',
160
- );
161
- }
162
-
163
- // Read + schema-validate the planner-authored risk verdict before any
164
- // GitHub mutation: a malformed verdict fails closed here (Epic #3865).
165
- const riskVerdict = loadRiskVerdict(values['risk-verdict']);
166
-
167
- const readPromises = [readFile(values['tech-spec'], 'utf8')];
168
- if (values['acceptance-table']) {
169
- readPromises.push(readFile(values['acceptance-table'], 'utf8'));
170
- }
171
- const [techSpecContent, acceptanceSpecContent = null] =
172
- await Promise.all(readPromises);
173
-
174
- const result = await runSpecPhase(
175
- epicId,
176
- provider,
177
- { techSpecContent, acceptanceSpecContent },
178
- settings,
179
- {
180
- force: values.force,
181
- forceReview: values['force-review'],
182
- steal: values.steal === true,
183
- config,
184
- riskVerdict,
185
- },
45
+ } from './lib/orchestration/epic-plan-spec/phases/risk-verdict.js';
46
+ export { runSpecPhase } from './lib/orchestration/epic-plan-spec/phases/run-spec-phase.js';
47
+ export { runSpecFreshnessCheck } from './lib/orchestration/epic-plan-spec/phases/spec-freshness.js';
48
+ export { resolveReviewRouting } from './lib/orchestration/plan-review-routing.js';
49
+
50
+ // CLI execution is retired — fail loudly with the successor surface instead
51
+ // of silently doing nothing (a stale automation script should break visibly).
52
+ if (
53
+ process.argv[1] &&
54
+ import.meta.url === pathToFileURL(process.argv[1]).href
55
+ ) {
56
+ process.stderr.write(
57
+ '[epic-plan-spec] retired (Epic #4474): the plan pipeline is ' +
58
+ 'context → author → persist.\n' +
59
+ ' - authoring envelope: node .agents/scripts/plan-context.js --epic <id>\n' +
60
+ ' - persist (all gates): node .agents/scripts/plan-persist.js --epic <id> ...\n' +
61
+ 'This file survives one release as an import shim only.\n',
186
62
  );
187
-
188
- process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
63
+ process.exit(1);
189
64
  }
190
-
191
- runAsCli(import.meta.url, main, { source: 'epic-plan-spec' });
@@ -291,7 +291,13 @@ export function reseedMappingFromGh(state, spec, ghState) {
291
291
  }
292
292
 
293
293
  for (const entity of flattenSpecForReseed(spec)) {
294
- if (mapping[entity.slug]) continue; // already mapped
294
+ // Epic #4474 (PR3) a mapping entry WITHOUT an issue number is a
295
+ // partial-failure tombstone (the apply's state writer persists every
296
+ // spec slug; only completed creations carry `issueNumber`). Treat it
297
+ // as unmapped here so a title-matched live issue reseeds it instead
298
+ // of leaving a numberless entry the diff engine can neither update
299
+ // nor (post-fix) skip-create.
300
+ if (Number.isInteger(mapping[entity.slug]?.issueNumber)) continue; // already mapped
295
301
  const candidates = openByTitle.get(entity.title) ?? [];
296
302
  // Pick the lowest unclaimed candidate for determinism.
297
303
  const match = candidates
@@ -106,8 +106,8 @@ let progressStdoutSink = (msg) => console.log(msg);
106
106
  * Flip every Logger output that can land on stdout (`info`, `warn`, and the
107
107
  * stdout branch of `createProgress`) to stderr for the lifetime of the
108
108
  * process. Idempotent. Use when stdout is reserved for a structured payload
109
- * — for example the `--emit-context` JSON envelopes emitted by
110
- * `epic-plan-spec.js` and `epic-plan-decompose.js`, where any interleaved
109
+ * — for example the JSON envelope emitted by
110
+ * `plan-context.js`, where any interleaved
111
111
  * `[Orchestrator] ℹ️ …` log line corrupts the captured file
112
112
  * (Story #2278).
113
113
  */
@@ -180,8 +180,8 @@ export const NOOP_LOGGER = Object.freeze({
180
180
 
181
181
  /**
182
182
  * Frozen logger that routes every level to **stderr**. Use this when a
183
- * caller's stdout is a structured payload (e.g. `--emit-context` JSON
184
- * envelopes from `epic-plan-spec.js` / `epic-plan-decompose.js`) and any
183
+ * caller's stdout is a structured payload (e.g. the authoring-context JSON
184
+ * envelope from `plan-context.js`) and any
185
185
  * progress/telemetry log must not interleave with the payload. Mirrors the
186
186
  * `{ info, warn, error, debug }` shape that the orchestration helpers
187
187
  * accept via optional `logger` arguments.
@@ -3,12 +3,11 @@
3
3
  *
4
4
  * Single library entry point for the audit-suite. Exports the two public
5
5
  * functions (`runAuditSuite`, `selectAudits`) plus the pure rule-matching
6
- * helpers (`matchesFilePattern`, `matchesAnyFilePattern`) used by the
7
- * top-level CLIs (`run-audit-suite.js`, `select-audits.js`).
6
+ * helpers (`matchesFilePattern`, `matchesAnyFilePattern`).
8
7
  *
9
- * Consumers import from this file rather than reaching upward into the
10
- * CLI scripts (`../../run-audit-suite.js`, `../../select-audits.js`)
11
- * that direction inversion is the goal of Story #1083 / Epic #1072.
8
+ * Consumers import from this file the former top-level CLI wrappers
9
+ * (`run-audit-suite.js`, `select-audits.js`) were retired in #4482; this
10
+ * barrel is the only supported entry point (Story #1083 / Epic #1072).
12
11
  *
13
12
  * @example
14
13
  * import { runAuditSuite, selectAudits } from './lib/audit-suite/index.js';
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * lib/audit-suite/runner.js — `runAuditSuite` aggregation core.
3
3
  *
4
- * Extracted from `.agents/scripts/run-audit-suite.js` (Story #963, Epic #946).
4
+ * Extracted from the former `run-audit-suite.js` CLI (Story #963, Epic #946;
5
+ * the CLI wrapper itself was retired in #4482 — `runAuditSuite` via the
6
+ * barrel is the only supported entry point).
5
7
  *
6
8
  * The runner composes the focused helpers from this directory:
7
9
  * - frontmatter.js → `summarizeWorkflow`
@@ -10,9 +12,9 @@
10
12
  * - workflow-loader.js → `loadWorkflow`, `defaultWriteArtifact`
11
13
  *
12
14
  * It owns the audit envelope shape (`metadata`, `findings`, `workflows`) and
13
- * the per-audit fan-out + result reduction. The CLI entry-point in
14
- * `.agents/scripts/run-audit-suite.js` reduces to argument parsing and
15
- * stdout/stderr glue around this function.
15
+ * the per-audit fan-out + result reduction. The former CLI entry-point
16
+ * (`run-audit-suite.js`) was retired in #4482; callers invoke
17
+ * `runAuditSuite` via the `lib/audit-suite/index.js` barrel.
16
18
  */
17
19
 
18
20
  import fs from 'node:fs/promises';
@@ -11,9 +11,8 @@
11
11
  * - selectAudits — main entry; reads audit-rules.json, runs `git
12
12
  * diff --name-only`, applies keyword + glob rules.
13
13
  *
14
- * The CLI wrapper at `.agents/scripts/select-audits.js` reduces to argv
15
- * parsing, provider construction, JSON stdout, and degraded-mode exit-code
16
- * mapping. All rule-matching lives here.
14
+ * All rule-matching lives here; the former `select-audits.js` CLI wrapper
15
+ * was retired in #4482 (consumers call `selectAudits` via the barrel).
17
16
  */
18
17
 
19
18
  import { readFileSync } from 'node:fs';
@@ -2,7 +2,7 @@
2
2
  * BDD runner detection + pending-tag verification (Epic #2001 Story #2094
3
3
  * Task #2103; workspace-aware extension from Story #2956).
4
4
  *
5
- * Used by `epic-plan-spec.js#buildAuthoringContext` to decide whether the
5
+ * Used by `buildAuthoringContext` (the `plan-context.js` envelope) to decide whether the
6
6
  * acceptance-table section should plan **features-first** Story ordering (a real
7
7
  * pending-tag is available, so the features-first Story can ship `.feature`
8
8
  * files marked `@pending` / `@skip` ahead of the implementation Stories) or
@@ -31,3 +31,23 @@ export function applyHeader(content, header) {
31
31
  const body = content.slice(block.length).replace(/^\r?\n/, '');
32
32
  return `${block}\n${header}${body}`;
33
33
  }
34
+
35
+ /**
36
+ * True when a workflow opts out of slash-command projection via a
37
+ * `command: false` key in its YAML frontmatter (#4482). Used for dual-use
38
+ * lens files (e.g. `audit-security.md`, `audit-lighthouse.md`) that stay in
39
+ * the payload as `/deliver` audit-suite prompts but must NOT surface as
40
+ * standalone slash commands because the host ships a native equivalent.
41
+ *
42
+ * Both `sync-claude-commands.js` (projection + orphan-reap) and the
43
+ * `commands-in-sync` doctor check (parity expectation) consult this flag so
44
+ * an excluded workflow never reads as "not synced".
45
+ *
46
+ * @param {string} content - Raw workflow `.md` content.
47
+ * @returns {boolean}
48
+ */
49
+ export function isCommandExcluded(content) {
50
+ const frontmatter = String(content).match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
51
+ if (!frontmatter) return false;
52
+ return /^command:\s*false\s*$/m.test(frontmatter[1]);
53
+ }
@@ -8,15 +8,13 @@
8
8
  * drives both:
9
9
  *
10
10
  * - `/agents-bootstrap-github` — registers GitHub required-status checks.
11
- * - `/git-merge-pr` → `git-pr-quality-gate.js` — runs each check locally
12
- * before merge.
13
11
  */
14
12
 
15
13
  /**
16
14
  * Default required-check suite. Mirrors the live CI required-check set
17
15
  * (`lint` + `test` + `baselines`); consumers override via
18
16
  * `github.branchProtection.requiredChecks` in `.agentrc.json`. Kept in sync
19
- * with `DEFAULT_CHECKS` in `git-pr-quality-gate.js` and the CI job names in
17
+ * with the CI job names in
20
18
  * `.github/workflows/ci.yml` — the retired `format:check` folded into `lint`
21
19
  * (Story #1829) and `lifecycle-doc-drift` collapsed into `lint`/`docs:check`
22
20
  * (Epic #1943), so neither belongs in the default set.
@@ -297,9 +297,10 @@ const PLANNING_SCHEMA = {
297
297
  failOnRegistryConflicts: { type: 'boolean' },
298
298
  failOnLargeFanOut: { type: 'boolean' },
299
299
  largeFanOutThreshold: { type: 'integer', minimum: 0 },
300
- // Navigability-reachability config consumed by the epic-plan-healthcheck
301
- // --paranoid reachability check (Epic #4131, F7). Opt-in: absent or empty
302
- // routeGlobs degrades to a silent no-op.
300
+ // Navigability-reachability config consumed by the plan-persist draft
301
+ // reachability gate (Epic #4131 F7; demoted into persist by #4474 PR6 —
302
+ // the manual epic-plan-healthcheck --paranoid re-check reads it too).
303
+ // Opt-in: absent or empty routeGlobs degrades to a silent no-op.
303
304
  navigation: {
304
305
  type: 'object',
305
306
  properties: {
@@ -2,7 +2,7 @@
2
2
  * Shared helper for the explicit-degraded contract used by soft-failing gates.
3
3
  *
4
4
  * Tech Spec #819 §"Degraded-mode contract (Story 3)" — three soft-fail sites
5
- * (select-audits diff-timeout, lint-baseline JSON-parse, baseline-refresh
5
+ * (audit-suite selector diff-timeout, lint-baseline JSON-parse, baseline-refresh
6
6
  * guardrail git-diff) historically returned a silent zero/empty result. The
7
7
  * new contract is:
8
8
  *
@@ -123,9 +123,9 @@ export const META_LABELS = {
123
123
  /**
124
124
  * Planning-axis labels (Epic #2880 F7). Currently scoped to the
125
125
  * `planning::healthcheck-waived` operator-applied waiver, which is the
126
- * documented escape hatch for the `/plan` Phase 10 readiness
127
- * healthcheck (`epic-plan-healthcheck.js`). The persist half of
128
- * `epic-plan-decompose.js` refuses to flip an Epic to `agent::ready`
126
+ * documented escape hatch for the inline post-plan readiness
127
+ * healthcheck (`runPlanHealthcheck`). The persist surface
128
+ * (`plan-persist.js`) refuses to flip an Epic to `agent::ready`
129
129
  * when the healthcheck returned `ok: false` unless this label is
130
130
  * present — see `.agents/docs/SDLC.md` § "`agent::ready` exit conditions"
131
131
  * for the full handoff contract.
@@ -147,6 +147,20 @@ export const PLANNING_LABELS = {
147
147
  */
148
148
  export const PLANNING_HEALTHCHECK_WAIVED = 'planning::healthcheck-waived';
149
149
 
150
+ /**
151
+ * Delivery-routing labels (Epic #4474 PR4 — design §2 mode matrix).
152
+ * `delivery::single` marks an Epic whose plan was persisted in the
153
+ * spec-only single-delivery mode: no Story tree exists and the Delivery
154
+ * Slicing table of the Epic body's Tech Spec is the audit trail.
155
+ * `plan-persist.js` applies it when the risk verdict declares
156
+ * `deliveryShape: "single"`; a fan-out re-persist over the same Epic
157
+ * removes it. The marker is **inert until #4475** lands the deliver-side
158
+ * reader — nothing in the deliver path consumes it yet.
159
+ */
160
+ export const DELIVERY_LABELS = {
161
+ SINGLE: 'delivery::single',
162
+ };
163
+
150
164
  /** Palette for the taxonomy; consumed by label-taxonomy.js. */
151
165
  export const LABEL_COLORS = {
152
166
  TYPE: '#7057FF',
@@ -155,4 +169,5 @@ export const LABEL_COLORS = {
155
169
  PERSONA: '#C5DEF5',
156
170
  ACCEPTANCE: '#FBCA04',
157
171
  PLANNING: '#FEF2C0',
172
+ DELIVERY: '#BFD4F2',
158
173
  };
@@ -12,6 +12,7 @@ import { fileURLToPath } from 'node:url';
12
12
  import {
13
13
  ACCEPTANCE_LABELS,
14
14
  AGENT_LABELS,
15
+ DELIVERY_LABELS,
15
16
  LABEL_COLORS,
16
17
  PERSONA_LABEL_PREFIX,
17
18
  PLANNING_LABELS,
@@ -108,15 +109,25 @@ export const LABEL_TAXONOMY = [
108
109
 
109
110
  // Planning axis — operator-applied waivers for the planning → delivery
110
111
  // handoff gates. Currently the sole entry is the `healthcheck-waived`
111
- // override consumed by the persist half of `epic-plan-decompose.js`
112
- // when `epic-plan-healthcheck.js` returned `ok: false` for a reason
113
- // the operator has triaged and accepted.
112
+ // override consumed by `plan-persist.js` when the inline healthcheck
113
+ // (`runPlanHealthcheck`) returned `ok: false` for a reason the
114
+ // operator has triaged and accepted.
114
115
  {
115
116
  name: PLANNING_LABELS.HEALTHCHECK_WAIVED,
116
117
  color: LABEL_COLORS.PLANNING,
117
118
  description:
118
119
  'Operator override — allows agent::ready handoff despite a failing post-plan healthcheck',
119
120
  },
121
+
122
+ // Delivery routing — applied by `plan-persist.js` when the risk verdict
123
+ // declares `deliveryShape: "single"` (Epic #4474 PR4). Inert until #4475
124
+ // lands the deliver-side reader.
125
+ {
126
+ name: DELIVERY_LABELS.SINGLE,
127
+ color: LABEL_COLORS.DELIVERY,
128
+ description:
129
+ 'Single-delivery plan — no Story tree; the Delivery Slicing table is the audit trail (#4474/#4475)',
130
+ },
120
131
  ];
121
132
 
122
133
  /** @type {Array<{ name: string, type: 'single_select', options?: string[] }>} */
@@ -28,9 +28,10 @@
28
28
  * `lib/baseline-snapshot.js`: the fs surface and the clock are injected so
29
29
  * unit tests never touch real files. The two writer paths
30
30
  * (`readBaseline`, `writeBaseline`) are split because the typical caller
31
- * sequence is read-then-merge-then-write — for instance,
32
- * `update-mutation-baseline.js` reads the prior baseline to preserve the
33
- * configured `tolerancePct` if the Stryker run does not override it.
31
+ * sequence is read-then-merge-then-write — a refresh CLI reads the prior
32
+ * baseline to preserve the configured `tolerancePct` if the Stryker run
33
+ * does not override it (the former `update-mutation-baseline.js` wrapper
34
+ * was retired in #4482; the gate ships dormant).
34
35
  */
35
36
 
36
37
  import fs from 'node:fs';
@@ -89,9 +90,9 @@ export function readBaseline(baselinePath, opts = {}) {
89
90
  * The on-disk shape is canonicalised: `workspaces` keys are sorted
90
91
  * alphabetically (with `"*"` always first) and the file ends with a
91
92
  * trailing newline. This produces a byte-stable serialisation, which
92
- * the `update-mutation-baseline.js` entry-point relies on to detect
93
- * "no change" runs (skip the baseline-refresh commit when re-running
94
- * Stryker produces an identical baseline).
93
+ * lets a refresh caller detect "no change" runs (skip the
94
+ * baseline-refresh commit when re-running Stryker produces an identical
95
+ * baseline).
95
96
  *
96
97
  * @param {string} baselinePath
97
98
  * @param {{ generatedAt?: string, tolerancePct?: number, workspaces: Record<string, number> }} payload