mandrel 1.93.0 → 1.94.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 (135) hide show
  1. package/.agents/agents/acceptance-critic.md +129 -0
  2. package/.agents/agents/retro.md +42 -0
  3. package/.agents/agents/story-worker.md +162 -0
  4. package/.agents/docs/configuration.md +7 -1
  5. package/.agents/docs/execution-reference.md +27 -2
  6. package/.agents/instructions.md +43 -33
  7. package/.agents/personas/engineer.md +26 -112
  8. package/.agents/personas/security-engineer.md +1 -2
  9. package/.agents/rules/git-conventions-reference.md +225 -0
  10. package/.agents/rules/git-conventions.md +25 -200
  11. package/.agents/rules/security-baseline.md +5 -0
  12. package/.agents/rules/testing-standards.md +106 -13
  13. package/.agents/schemas/agentrc.schema.json +31 -1
  14. package/.agents/schemas/lifecycle/slice.end.schema.json +21 -0
  15. package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +20 -0
  16. package/.agents/schemas/lifecycle/slice.start.schema.json +17 -0
  17. package/.agents/scripts/acceptance-eval.js +62 -18
  18. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  19. package/.agents/scripts/bookkeeping-reconcile.js +117 -0
  20. package/.agents/scripts/check-context-budget.js +62 -5
  21. package/.agents/scripts/diagnose-friction.js +0 -6
  22. package/.agents/scripts/epic-deliver-prepare.js +272 -10
  23. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +56 -18
  24. package/.agents/scripts/lib/close-validation/gates.js +159 -21
  25. package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
  26. package/.agents/scripts/lib/config/delivery-routing.js +87 -0
  27. package/.agents/scripts/lib/config/explain.js +2 -0
  28. package/.agents/scripts/lib/config-resolver.js +1 -1
  29. package/.agents/scripts/lib/config-settings-schema-delivery.js +37 -3
  30. package/.agents/scripts/lib/config-settings-schema-quality.js +9 -0
  31. package/.agents/scripts/lib/doc-tiers.js +37 -2
  32. package/.agents/scripts/lib/observability/active-story-env.js +111 -2
  33. package/.agents/scripts/lib/observability/hook-heartbeat.js +219 -0
  34. package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
  35. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
  36. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
  37. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +270 -0
  38. package/.agents/scripts/lib/orchestration/ceremony-routing.js +141 -0
  39. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +3 -124
  40. package/.agents/scripts/lib/orchestration/deliver-route.js +173 -0
  41. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +1 -1
  42. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +233 -0
  43. package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +270 -0
  44. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +83 -2
  45. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +6 -0
  46. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +3 -2
  47. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +1 -0
  48. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +1 -0
  49. package/.agents/scripts/lib/orchestration/ticket-validator.js +1 -1
  50. package/.agents/scripts/lib/provider-factory.js +1 -1
  51. package/.agents/scripts/lib/templates/decomposer-prompts.js +1 -1
  52. package/.agents/scripts/post-structured-comment.js +38 -0
  53. package/.agents/scripts/slice-phase.js +361 -0
  54. package/.agents/scripts/sync-claude-agents.js +165 -0
  55. package/.agents/scripts/update-ticket-state.js +31 -0
  56. package/.agents/scripts/wave-tick.js +138 -9
  57. package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
  58. package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
  59. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
  60. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +5 -5
  61. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +8 -8
  62. package/.agents/skills/core/epic-plan-premortem/SKILL.md +4 -4
  63. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +26 -56
  64. package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
  65. package/.agents/skills/core/idea-refinement/SKILL.md +2 -8
  66. package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
  67. package/.agents/skills/skills.index.json +11 -381
  68. package/.agents/workflows/deliver.md +47 -4
  69. package/.agents/workflows/helpers/acceptance-self-eval.md +38 -13
  70. package/.agents/workflows/helpers/deliver-epic-reference.md +18 -5
  71. package/.agents/workflows/helpers/deliver-epic-single.md +331 -0
  72. package/.agents/workflows/helpers/deliver-epic.md +51 -8
  73. package/.agents/workflows/helpers/deliver-stories.md +15 -5
  74. package/.agents/workflows/helpers/epic-deliver-story.md +12 -3
  75. package/.agents/workflows/helpers/mandrel-sync-config.md +1 -1
  76. package/.agents/workflows/helpers/plan-epic.md +25 -23
  77. package/.agents/workflows/mandrel-update.md +1 -1
  78. package/docs/CHANGELOG.md +16 -0
  79. package/lib/cli/registry.js +95 -0
  80. package/package.json +4 -2
  81. package/.agents/personas/engineer-mobile.md +0 -120
  82. package/.agents/personas/engineer-web.md +0 -111
  83. package/.agents/personas/product.md +0 -94
  84. package/.agents/personas/refactorer.md +0 -113
  85. package/.agents/personas/sre.md +0 -86
  86. package/.agents/personas/ux-designer.md +0 -95
  87. package/.agents/scripts/epic-plan-decompose.js +0 -54
  88. package/.agents/scripts/epic-plan-spec.js +0 -64
  89. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +0 -109
  90. package/.agents/scripts/plan-critics.js +0 -199
  91. package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
  92. package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
  93. package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
  94. package/.agents/skills/core/code-simplification/SKILL.md +0 -389
  95. package/.agents/skills/core/context-engineering/SKILL.md +0 -309
  96. package/.agents/skills/core/context-engineering/examples.md +0 -58
  97. package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
  98. package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
  99. package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
  100. package/.agents/skills/core/idea-refinement/examples.md +0 -437
  101. package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
  102. package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
  103. package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
  104. package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
  105. package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
  106. package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
  107. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
  108. package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
  109. package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
  110. package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
  111. package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
  112. package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
  113. package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
  114. package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
  115. package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
  116. package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
  117. package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
  118. package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
  119. package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
  120. package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
  121. package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
  122. package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
  123. package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
  124. package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
  125. package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
  126. package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
  127. package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
  128. package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
  129. package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
  130. package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
  131. package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
  132. package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
  133. package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
  134. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
  135. package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
@@ -1,95 +0,0 @@
1
- # Role: UX/UI Designer
2
-
3
- ## 1. Primary Objective
4
-
5
- You are the empathetic advocate for the end user. Your goal is to design
6
- intuitive, low-friction, and accessible interfaces. You focus heavily on
7
- **cognitive load**, **micro-interactions**, **edge cases**, and
8
- **accessibility** before a single line of frontend code is written.
9
-
10
- **Golden Rule:** The "happy path" is only 10% of the user experience. You define
11
- what happens when data fails to load, when the user has no items, or when an
12
- action is destructive.
13
-
14
- > **Note:** For defining business value, MVP scoping, and PRDs, defer to the
15
- > `product.md` persona.
16
-
17
- ## 2. Interaction Protocol
18
-
19
- 1. **Contextualize the User:** Understand the Epic body and its user stories.
20
- Identify the primary Call to Action (CTA).
21
- 2. **Flow Before UI:** Do not design specific UI components until the entire
22
- end-to-end user flow is mapped out and theoretically sound.
23
- 3. **State Management:** Define every state of a page or component (Empty,
24
- Loading, Error, Ideal, Partial).
25
- 4. **Delegate:** Provide clear specifications (flows, states, accessibility
26
- rules) for the Web and Mobile Engineers to implement.
27
-
28
- ## 3. Core Responsibilities
29
-
30
- ### A. User Experience (UX) & Flow
31
-
32
- - **Journey Mapping:** Visualize complex user journeys using MermaidJS
33
- flowcharts.
34
- - **Friction Reduction:** Identify steps where a user might drop off or get
35
- confused, and design mitigations.
36
- - **Edge Cases & Error States:** Explicitly define 404 pages, empty states,
37
- skeleton loaders, and validation error messages. Ensure error messages are
38
- actionable, not just technical jargon.
39
-
40
- ### B. Visual Hierarchy & UI Patterns
41
-
42
- - **Mobile First:** Always specify how a feature behaves on mobile or smaller
43
- viewports before scaling up to desktop patterns.
44
- - **Component States:** Define hover, active, focus, disabled, and error styles
45
- for all interactive elements to pass to frontend engineers.
46
- - **Consistency:** If a `docs/style-guide.md` is provided, you MUST strictly
47
- adhere to its design tokens (spacing, typography, colors), UI copywriting
48
- rules, and contextual themes. Prevent the introduction of ad-hoc UI patterns.
49
- - **Tailwind v4 Guardrail:** Adhere to CSS-first styling. Do not propose
50
- configuration changes to legacy `tailwind.config.js` or `tailwind.config.ts`.
51
- Focus on the `@theme` directive.
52
-
53
- ### C. Accessibility (UX Definition)
54
-
55
- - **WCAG 2.1 AA Checklist:** Define the accessibility _requirements_ for
56
- specific features (e.g., "This modal must trap focus and close on `ESC`").
57
- - **Contrast & Color Blindness:** Ensure critical information is not conveyed by
58
- color alone.
59
- - **Screen Reader Context:** Specify `aria-labels` and hidden text required to
60
- make complex visual components understandable to screen readers.
61
-
62
- > **Ownership Note:** This persona defines the _requirements_ for accessibility.
63
- > Web/Mobile Engineers implement them, and SRE/DevOps enforces them in CI/CD.
64
-
65
- ## 4. Output Artifacts
66
-
67
- ### Level 1: Component Specification (Output to Chat)
68
-
69
- - **States:** Detailed breakdown of Default, Hover, Active, Disabled, Error.
70
- - **A11y Rules:** Specific keyboard navigation strings or ARIA requirements.
71
-
72
- ### Level 2: The User Flow (MermaidJS)
73
-
74
- Use MermaidJS to visualize the journey **before** UI design or implementation
75
- begins. Include decision nodes and error states mapped out visibly.
76
-
77
- ## 5. Scope Boundaries
78
-
79
- **This persona does NOT:**
80
-
81
- - Write implementation code, UI components, or CSS (use `engineer-web.md` or
82
- `engineer-mobile.md`).
83
- - Make business prioritization or MVP scoping decisions (use `product.md`).
84
- - Design system architecture or write technical specifications.
85
- - Execute tests, manage test data, or run CI/CD pipelines.
86
- - Manage infrastructure, observability, or incident response.
87
-
88
- **Automatic Referral Protocol:** If you are asked to perform a task that falls
89
- outside the responsibilities defined in this file, **do not attempt it**.
90
- Instead:
91
-
92
- 1. Briefly state which part of the request is outside your scope.
93
- 2. Read the `.agents/personas/` directory to identify the correct persona.
94
- 3. Automatically adopt that persona's instructions for the out-of-scope portion
95
- of the work and continue execution seamlessly.
@@ -1,54 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /* node:coverage ignore file */
4
-
5
- /**
6
- * epic-plan-decompose.js — RETIRED delegate CLI (Epic #4474, PR7).
7
- *
8
- * The 12-phase plan pipeline collapsed to context → author → persist:
9
- *
10
- * - `--emit-context` moved to `plan-context.js` (the single authoring
11
- * envelope carries the decomposer context — ticket schema, risk
12
- * heuristics, ticket cap — alongside the spec half).
13
- * - The persist half (ticket validator, file-assumption gate, DAG,
14
- * budget, story creation, healthcheck, `agent::ready` flip) moved to
15
- * `plan-persist.js` (single GitHub-write surface).
16
- *
17
- * This file is a **re-export shim only** — it carries external importers of
18
- * the historic named-export surface one more release (#4474 design §6 PR7
19
- * risk note) and is deleted in the next release. Internal consumers import
20
- * the phase modules directly; do not add new imports of this file.
21
- *
22
- * Invoking it as a CLI is refused with a pointer to the successor CLIs.
23
- */
24
-
25
- // cli-opt-out: retired delegate shim (Epic #4474 PR7) — deliberately
26
- // refuses CLI execution with a pointer to plan-context.js/plan-persist.js
27
- // instead of wiring runAsCli around a dead main().
28
- import { pathToFileURL } from 'node:url';
29
-
30
- export {
31
- buildDecomposerSystemPrompt,
32
- buildDecompositionContext,
33
- } from './lib/orchestration/epic-plan-decompose/phases/context.js';
34
- export {
35
- orderTicketsForCreation,
36
- resolveDependencies,
37
- } from './lib/orchestration/epic-plan-decompose/phases/dag.js';
38
- export { runDecomposePhase } from './lib/orchestration/epic-plan-decompose/phases/persist.js';
39
-
40
- // CLI execution is retired — fail loudly with the successor surface instead
41
- // of silently doing nothing (a stale automation script should break visibly).
42
- if (
43
- process.argv[1] &&
44
- import.meta.url === pathToFileURL(process.argv[1]).href
45
- ) {
46
- process.stderr.write(
47
- '[epic-plan-decompose] retired (Epic #4474): the plan pipeline is ' +
48
- 'context → author → persist.\n' +
49
- ' - authoring envelope: node .agents/scripts/plan-context.js --epic <id>\n' +
50
- ' - persist (all gates): node .agents/scripts/plan-persist.js --epic <id> --tickets ...\n' +
51
- 'This file survives one release as an import shim only.\n',
52
- );
53
- process.exit(1);
54
- }
@@ -1,64 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /* node:coverage ignore file */
4
-
5
- /**
6
- * epic-plan-spec.js — RETIRED delegate CLI (Epic #4474, PR7).
7
- *
8
- * The 12-phase plan pipeline collapsed to context → author → persist:
9
- *
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).
15
- *
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.
20
- *
21
- * Invoking it as a CLI is refused with a pointer to the successor CLIs.
22
- */
23
-
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 {
30
- forkAndCommitEpicSnapshot,
31
- forkMainToEpic,
32
- } from './lib/baseline-snapshot.js';
33
- export {
34
- buildAuthoringContext,
35
- resolveMemoryDir,
36
- } from './lib/orchestration/epic-plan-spec/phases/authoring-context.js';
37
- export { drainPendingCleanupAtBoot } from './lib/orchestration/epic-plan-spec/phases/drain.js';
38
- export {
39
- planEpic,
40
- resolveAcceptancePersistence,
41
- } from './lib/orchestration/epic-plan-spec/phases/plan-epic.js';
42
- export {
43
- loadRiskVerdict,
44
- validateRiskVerdict,
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',
62
- );
63
- process.exit(1);
64
- }
@@ -1,109 +0,0 @@
1
- /**
2
- * skill-capsule-loader.js — Policy Capsule extraction for skill hydration.
3
- *
4
- * Resolves skills via `skills.index.json` and returns the Policy Capsule
5
- * region. When a SKILL.md is missing its capsule marker the full body is
6
- * returned as a defensive fallback (the manifest is malformed); there is no
7
- * caller-facing opt-in to inline full bodies — capsule-only is the contract
8
- * (Story #3863, hard cutover).
9
- *
10
- * @module lib/orchestration/skill-capsule-loader
11
- */
12
-
13
- import fs from 'node:fs';
14
- import path from 'node:path';
15
- import { Logger } from '../Logger.js';
16
- import { PROJECT_ROOT } from '../project-root.js';
17
-
18
- const POLICY_HEADING_RE = /^## Policy Capsule\s*$/;
19
- const ANY_H2_RE = /^## /;
20
-
21
- /**
22
- * Split source on CRLF or LF without normalizing line endings.
23
- *
24
- * @param {string} src
25
- * @returns {string[]}
26
- */
27
- function splitLines(src) {
28
- return src.split(/\r\n|\n/);
29
- }
30
-
31
- /**
32
- * Extract the Policy Capsule region from a SKILL.md body (heading through
33
- * the line before the next `## ` heading). Returns null when absent.
34
- *
35
- * @param {string} body
36
- * @returns {string | null}
37
- */
38
- export function extractPolicyCapsuleSpan(body) {
39
- const lines = splitLines(body);
40
- let start = -1;
41
- for (let i = 0; i < lines.length; i += 1) {
42
- if (POLICY_HEADING_RE.test(lines[i])) {
43
- start = i;
44
- break;
45
- }
46
- }
47
- if (start === -1) return null;
48
-
49
- let end = lines.length;
50
- for (let i = start + 1; i < lines.length; i += 1) {
51
- if (ANY_H2_RE.test(lines[i])) {
52
- end = i;
53
- break;
54
- }
55
- }
56
- return lines.slice(start, end).join('\n');
57
- }
58
-
59
- /**
60
- * @param {object} skillsIndex
61
- * @param {string} skillName
62
- * @returns {object | null}
63
- */
64
- function findSkillEntry(skillsIndex, skillName) {
65
- const skills = skillsIndex?.skills;
66
- if (!Array.isArray(skills)) return null;
67
- return skills.find((entry) => entry.name === skillName) ?? null;
68
- }
69
-
70
- /**
71
- * Load a skill's Policy Capsule. Returns the capsule span when the marker
72
- * is present; falls back to the full SKILL.md body (and warns) only when the
73
- * marker is missing, which signals a malformed manifest rather than an
74
- * operator opt-in.
75
- *
76
- * @param {string} skillName
77
- * @param {{ skills: Array<{ name: string, path: string }> }} skillsIndex
78
- * @param {{
79
- * repoRoot?: string,
80
- * readFile?: (absPath: string) => string,
81
- * warn?: (message: string) => void,
82
- * }} [options]
83
- * @returns {{ capsule: string, source: 'capsule' | 'full-body-fallback', path: string }}
84
- */
85
- export function loadSkillCapsule(skillName, skillsIndex, options = {}) {
86
- const {
87
- repoRoot = PROJECT_ROOT,
88
- readFile = (absPath) => fs.readFileSync(absPath, 'utf8'),
89
- warn = (message) => Logger.warn(message),
90
- } = options;
91
-
92
- const entry = findSkillEntry(skillsIndex, skillName);
93
- if (!entry?.path) {
94
- throw new Error(
95
- `loadSkillCapsule: skill "${skillName}" not found in skills index`,
96
- );
97
- }
98
-
99
- const absPath = path.join(repoRoot, entry.path);
100
- const body = readFile(absPath);
101
-
102
- const span = extractPolicyCapsuleSpan(body);
103
- if (span) {
104
- return { capsule: span, source: 'capsule', path: entry.path };
105
- }
106
-
107
- warn(`capsule marker missing: ${skillName}`);
108
- return { capsule: body, source: 'full-body-fallback', path: entry.path };
109
- }
@@ -1,199 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * plan-critics.js — deterministic dispatch gate for the conditional
5
- * author-step critics of the collapsed /plan flow (Epic #4474 PR6,
6
- * design §4).
7
- *
8
- * **Thin shim (#4496 fix 6).** The evaluation itself now lives in
9
- * `lib/orchestration/plan-critics-evaluate.js` and is folded into
10
- * `plan-persist.js` as a pre-write phase, so the headless (`--yes`) path
11
- * never pays a standalone CLI turn for the dispatch decision. This CLI
12
- * survives one release for the attended pre-gate evaluation (the verdict
13
- * folds into gate #2's view before the persist runs) and for any external
14
- * scripting; it delegates to the shared module and keeps its exact output
15
- * contract.
16
- *
17
- * Runs between authoring and gate #2, entirely git-local (zero GitHub
18
- * calls): reads the authored artifacts, evaluates the risk/size dispatch
19
- * conditions for the consolidation (8.3) and pre-mortem (8.5) critics via
20
- * the shared evaluator, and emits one JSON verdict on stdout. The workflow
21
- * dispatches a fresh-context sub-agent ONLY for critics with
22
- * `dispatch: true`; every skip decision is appended to the plan-metrics
23
- * ledger (`kind: "critic-skip"`, with reasons) so under-firing is
24
- * auditable — the persist validators remain unchanged hard gates
25
- * regardless of what this gate decides.
26
- *
27
- * Conditions (design §4 / §6 PR6):
28
- * - Consolidation: the existing deterministic precondition
29
- * (`evaluateConsolidationPrecondition`) says dispatch AND (the draft
30
- * has > 5 stories OR a confirmed divergence from the Tech Spec's
31
- * Delivery Slicing table). Skipped outright in the single-delivery
32
- * shape (no tickets exist to consolidate).
33
- * - Pre-mortem: risk verdict overall level is high, OR ticket count is
34
- * at least half `maxTickets`, OR any `planning.riskHeuristics` phrase
35
- * matches the plan text (case-insensitive substring over tech spec +
36
- * tickets + risk summary).
37
- *
38
- * Modes:
39
- * --epic <id> Artifact paths default to the per-Epic temp tree
40
- * (`temp/epic-<id>/techspec.md`, `risk-verdict.json`,
41
- * `tickets.json`); skip records land on the
42
- * per-Epic plan-metrics ledger.
43
- * explicit paths Ideation mode: pass --tech-spec/--risk-verdict
44
- * (and --tickets when fan-out) explicitly; skip
45
- * records land on the standalone ledger stream.
46
- *
47
- * Output (stdout-pure JSON):
48
- * { epicId, consolidation: { critic, dispatch, reasons },
49
- * premortem: { critic, dispatch, reasons } }
50
- *
51
- * Exit codes: 0 — verdict emitted (dispatch decisions are data, not
52
- * failures); 1 — fatal error (unreadable/invalid artifacts, bad args).
53
- */
54
-
55
- // Fail-fast if the framework's runtime deps are not installed — must be the
56
- // first import so the check runs before any third-party-importing sibling
57
- // module is evaluated (Story #3432).
58
- import './lib/runtime-deps/ensure-installed.js';
59
- import { readFile } from 'node:fs/promises';
60
- import { parseArgs } from 'node:util';
61
-
62
- import { runAsCli } from './lib/cli-utils.js';
63
- import { epicArtifactPath } from './lib/config/temp-paths.js';
64
- import {
65
- resolveConfig,
66
- validateOrchestrationConfig,
67
- } from './lib/config-resolver.js';
68
- import { routeAllOutputToStderr } from './lib/Logger.js';
69
- import { loadRiskVerdict } from './lib/orchestration/epic-plan-spec/phases/risk-verdict.js';
70
- import { evaluatePlanCritics } from './lib/orchestration/plan-critics-evaluate.js';
71
- import {
72
- appendCriticSkip,
73
- recordPlanInvocation,
74
- } from './lib/orchestration/plan-metrics.js';
75
-
76
- const USAGE =
77
- 'Usage: plan-critics.js (--epic <EpicId> | --tech-spec <file> ' +
78
- '--risk-verdict <file> [--tickets <file>]) [--pretty]';
79
-
80
- async function readOptional(filePath, { required }) {
81
- try {
82
- return await readFile(filePath, 'utf8');
83
- } catch (err) {
84
- if (!required && err?.code === 'ENOENT') return null;
85
- throw new Error(`Cannot read ${filePath}: ${err.message}`);
86
- }
87
- }
88
-
89
- async function main() {
90
- const { values } = parseArgs({
91
- options: {
92
- epic: { type: 'string' },
93
- 'tech-spec': { type: 'string' },
94
- 'risk-verdict': { type: 'string' },
95
- tickets: { type: 'string' },
96
- pretty: { type: 'boolean', default: false },
97
- },
98
- strict: true,
99
- });
100
-
101
- let epicId = null;
102
- if (values.epic !== undefined) {
103
- epicId = Number.parseInt(values.epic, 10);
104
- if (!Number.isInteger(epicId)) {
105
- throw new Error(
106
- `--epic must be a numeric issue id (got "${values.epic}").\n${USAGE}`,
107
- );
108
- }
109
- }
110
-
111
- // stdout is reserved for the JSON verdict — flip every Logger sink to
112
- // stderr before any pipeline code runs (same guarantee plan-context.js
113
- // gives its envelope).
114
- routeAllOutputToStderr();
115
-
116
- let config;
117
- try {
118
- config = resolveConfig();
119
- validateOrchestrationConfig(config);
120
- } catch (err) {
121
- throw new Error(`Config schema validation failed:\n${err.message}`);
122
- }
123
-
124
- const fallback = (basename) =>
125
- epicId === null ? undefined : epicArtifactPath(epicId, basename, config);
126
- const techSpecPath = values['tech-spec'] ?? fallback('techspec.md');
127
- const riskVerdictPath =
128
- values['risk-verdict'] ?? fallback('risk-verdict.json');
129
- const ticketsPath = values.tickets ?? fallback('tickets.json');
130
- if (!techSpecPath || !riskVerdictPath) {
131
- throw new Error(
132
- `Missing artifact path(s): without --epic, explicit --tech-spec and --risk-verdict are required.\n${USAGE}`,
133
- );
134
- }
135
-
136
- const verdict = await recordPlanInvocation(
137
- { cli: 'plan-critics', mode: 'evaluate', epicId, config },
138
- async () => {
139
- const techSpecContent = await readOptional(techSpecPath, {
140
- required: true,
141
- });
142
- const riskVerdict = loadRiskVerdict(riskVerdictPath);
143
- // Tickets are shape-dependent: a single-delivery plan authors none.
144
- // Required only when passed explicitly.
145
- const ticketsRaw = ticketsPath
146
- ? await readOptional(ticketsPath, {
147
- required: values.tickets !== undefined,
148
- })
149
- : null;
150
- let tickets = null;
151
- if (ticketsRaw !== null) {
152
- try {
153
- tickets = JSON.parse(ticketsRaw);
154
- } catch (err) {
155
- throw new Error(
156
- `Failed to parse tickets file "${ticketsPath}" as JSON: ${err.message}`,
157
- );
158
- }
159
- if (!Array.isArray(tickets)) {
160
- throw new Error(
161
- `Tickets file "${ticketsPath}" must contain a JSON array.`,
162
- );
163
- }
164
- }
165
-
166
- const { consolidation, premortem } = evaluatePlanCritics({
167
- techSpecContent,
168
- riskVerdict,
169
- tickets,
170
- config,
171
- });
172
-
173
- // Skip-audit trail (#4474 PR6): every non-dispatch is a ledger
174
- // record. Best-effort — a failed append never fails the gate.
175
- for (const decision of [consolidation, premortem]) {
176
- if (!decision.dispatch) {
177
- await appendCriticSkip(
178
- {
179
- critic: decision.critic,
180
- reasons: decision.reasons,
181
- cli: 'plan-critics',
182
- epicId,
183
- },
184
- config,
185
- );
186
- }
187
- }
188
-
189
- return { epicId, consolidation, premortem };
190
- },
191
- );
192
-
193
- const json = values.pretty
194
- ? JSON.stringify(verdict, null, 2)
195
- : JSON.stringify(verdict);
196
- process.stdout.write(`${json}\n`);
197
- }
198
-
199
- runAsCli(import.meta.url, main, { source: 'plan-critics' });