mandrel 2.30.0 → 2.32.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 (253) hide show
  1. package/.agents/README.md +13 -17
  2. package/.agents/agents/acceptance-critic.md +1 -2
  3. package/.agents/docs/SDLC.md +4 -4
  4. package/.agents/docs/agentrc-reference.json +61 -57
  5. package/.agents/docs/configuration.md +274 -227
  6. package/.agents/docs/execution-reference.md +13 -14
  7. package/.agents/docs/quality-gates.md +195 -23
  8. package/.agents/instructions.md +2 -5
  9. package/.agents/rules/ci-remediation.md +44 -1
  10. package/.agents/rules/git-conventions-reference.md +27 -27
  11. package/.agents/rules/git-conventions.md +4 -2
  12. package/.agents/rules/known-tooling-behavior.md +66 -30
  13. package/.agents/rules/testing-standards.md +35 -71
  14. package/.agents/runtime-deps.json +0 -1
  15. package/.agents/schemas/agentrc.schema.json +1939 -1400
  16. package/.agents/schemas/lifecycle/README.md +21 -14
  17. package/.agents/schemas/lifecycle/ledger-record.schema.json +76 -22
  18. package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
  19. package/.agents/scripts/README.md +7 -29
  20. package/.agents/scripts/apply-quality-bootstrap.js +27 -34
  21. package/.agents/scripts/bootstrap.js +28 -26
  22. package/.agents/scripts/check-baseline-drift.js +73 -13
  23. package/.agents/scripts/check-baseline-scope.js +362 -0
  24. package/.agents/scripts/check-dead-exports.js +9 -1
  25. package/.agents/scripts/check-gherkin-corpus.js +508 -0
  26. package/.agents/scripts/check-knip-entries.js +136 -0
  27. package/.agents/scripts/check-lifecycle-lint.js +36 -112
  28. package/.agents/scripts/check-schema-references.js +1 -1
  29. package/.agents/scripts/diagnose-friction.js +7 -4
  30. package/.agents/scripts/generate-config-docs.js +263 -171
  31. package/.agents/scripts/install-matrix-assert.js +0 -1
  32. package/.agents/scripts/lib/ITicketingProvider.js +0 -58
  33. package/.agents/scripts/lib/audit-baselines/staleness.js +6 -6
  34. package/.agents/scripts/lib/audit-baselines/trend.js +7 -8
  35. package/.agents/scripts/lib/audit-baselines/weights.js +4 -5
  36. package/.agents/scripts/lib/audit-suite/checklist-threading.js +1 -1
  37. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +0 -1
  38. package/.agents/scripts/lib/baselines/envelope.js +41 -60
  39. package/.agents/scripts/lib/baselines/git-base.js +30 -37
  40. package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +103 -0
  41. package/.agents/scripts/lib/baselines/kinds/_crap-read.js +150 -0
  42. package/.agents/scripts/lib/baselines/kinds/crap.js +25 -65
  43. package/.agents/scripts/lib/baselines/orphan-pruner.js +233 -0
  44. package/.agents/scripts/lib/baselines/refresh-service.js +6 -8
  45. package/.agents/scripts/lib/baselines/scope-assert.js +223 -0
  46. package/.agents/scripts/lib/baselines/scope-inventory.js +314 -0
  47. package/.agents/scripts/lib/bdd-step-index.js +326 -0
  48. package/.agents/scripts/lib/bootstrap/install-ledger.js +5 -3
  49. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +4 -6
  50. package/.agents/scripts/lib/bootstrap/manifest.js +17 -40
  51. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +12 -59
  52. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +62 -2
  53. package/.agents/scripts/lib/checks/loop-health.js +9 -37
  54. package/.agents/scripts/lib/child-exec.js +193 -0
  55. package/.agents/scripts/lib/cli/standard-args.js +1 -1
  56. package/.agents/scripts/lib/cli-args.js +64 -0
  57. package/.agents/scripts/lib/close-validation/gates.js +2 -2
  58. package/.agents/scripts/lib/close-validation/runner.js +3 -3
  59. package/.agents/scripts/lib/config/acceptance-eval.js +5 -52
  60. package/.agents/scripts/lib/config/commands.js +3 -5
  61. package/.agents/scripts/lib/config/explain.js +5 -7
  62. package/.agents/scripts/lib/config/gates/bundle-size.schema.js +32 -6
  63. package/.agents/scripts/lib/config/gates/coverage.schema.js +25 -5
  64. package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +12 -2
  65. package/.agents/scripts/lib/config/gates/crap.schema.js +68 -23
  66. package/.agents/scripts/lib/config/gates/duplication.schema.js +29 -17
  67. package/.agents/scripts/lib/config/gates/index.js +5 -2
  68. package/.agents/scripts/lib/config/gates/lighthouse.schema.js +34 -6
  69. package/.agents/scripts/lib/config/gates/lint.schema.js +11 -2
  70. package/.agents/scripts/lib/config/gates/maintainability.schema.js +37 -15
  71. package/.agents/scripts/lib/config/gates/mutation.schema.js +15 -3
  72. package/.agents/scripts/lib/config/gates/shared.js +58 -9
  73. package/.agents/scripts/lib/config/github.js +0 -1
  74. package/.agents/scripts/lib/config/limits.js +3 -48
  75. package/.agents/scripts/lib/config/qa.js +105 -0
  76. package/.agents/scripts/lib/config/temp-paths.js +6 -5
  77. package/.agents/scripts/lib/config-settings-schema-delivery.js +237 -56
  78. package/.agents/scripts/lib/config-settings-schema-quality.js +209 -29
  79. package/.agents/scripts/lib/config-settings-schema.js +386 -39
  80. package/.agents/scripts/lib/crap-baseline-join.js +126 -9
  81. package/.agents/scripts/lib/crap-utils.js +84 -520
  82. package/.agents/scripts/lib/dead-exports-knip.js +79 -10
  83. package/.agents/scripts/lib/degraded-mode.js +2 -2
  84. package/.agents/scripts/lib/doc-tiers.js +3 -3
  85. package/.agents/scripts/lib/feedback-loop/graduator-core.js +46 -104
  86. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +10 -8
  87. package/.agents/scripts/lib/fs-walk.js +52 -0
  88. package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
  89. package/.agents/scripts/lib/git-utils.js +16 -36
  90. package/.agents/scripts/lib/knip-entry-sync.js +469 -0
  91. package/.agents/scripts/lib/observability/metrics-ledger.js +1 -1
  92. package/.agents/scripts/lib/observability/runtime-friction.js +10 -0
  93. package/.agents/scripts/lib/observability/signal-validator.js +5 -85
  94. package/.agents/scripts/lib/observability/signals-writer.js +19 -62
  95. package/.agents/scripts/lib/observability/source-classifier.js +5 -7
  96. package/.agents/scripts/lib/observability/terse-result.js +3 -3
  97. package/.agents/scripts/lib/orchestration/behind-recovery.js +114 -0
  98. package/.agents/scripts/lib/orchestration/ceremony-routing.js +7 -8
  99. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +34 -33
  100. package/.agents/scripts/lib/orchestration/code-review.js +2 -2
  101. package/.agents/scripts/lib/orchestration/complexity-gate.js +43 -161
  102. package/.agents/scripts/lib/orchestration/diff-magnitude.js +4 -4
  103. package/.agents/scripts/lib/orchestration/label-transitions.js +3 -2
  104. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +12 -38
  105. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +5 -6
  106. package/.agents/scripts/lib/orchestration/plan-metrics.js +2 -3
  107. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +6 -0
  108. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +0 -1
  109. package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
  110. package/.agents/scripts/lib/orchestration/resolve-stories.js +5 -15
  111. package/.agents/scripts/lib/orchestration/review-providers/codex.js +1 -1
  112. package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +130 -0
  113. package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -16
  114. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +1 -1
  115. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +37 -26
  116. package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +376 -0
  117. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +161 -151
  118. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +15 -3
  119. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +10 -15
  120. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +17 -2
  121. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +5 -0
  122. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
  123. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
  124. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +65 -25
  125. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
  126. package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
  127. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
  128. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
  129. package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
  130. package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
  131. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
  132. package/.agents/scripts/lib/orchestration/ticketing/reads.js +5 -5
  133. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -4
  134. package/.agents/scripts/lib/orchestration/verify-tier-repair.js +107 -0
  135. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -87
  136. package/.agents/scripts/lib/signals/detectors/common.js +1 -1
  137. package/.agents/scripts/lib/signals/index.js +8 -6
  138. package/.agents/scripts/lib/signals/schema.js +20 -25
  139. package/.agents/scripts/lib/signals/write.js +8 -8
  140. package/.agents/scripts/lib/story-body/story-body.js +12 -59
  141. package/.agents/scripts/lib/temp-retention.js +1 -1
  142. package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -14
  143. package/.agents/scripts/lib/ticket-body-sections.js +4 -5
  144. package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +13 -45
  145. package/.agents/scripts/lib/worktree/lifecycle/reap.js +4 -5
  146. package/.agents/scripts/lib/worktree-manager.js +2 -3
  147. package/.agents/scripts/lint-label-vocabulary.js +2 -24
  148. package/.agents/scripts/pr-watch-with-update.js +7 -5
  149. package/.agents/scripts/providers/github/cache.js +2 -2
  150. package/.agents/scripts/providers/github/comments.js +6 -28
  151. package/.agents/scripts/providers/github/compose.js +0 -15
  152. package/.agents/scripts/providers/github/errors.js +10 -27
  153. package/.agents/scripts/providers/github/request-helpers.js +1 -2
  154. package/.agents/scripts/providers/github/sub-issues.js +10 -218
  155. package/.agents/scripts/providers/github.js +4 -7
  156. package/.agents/scripts/prune-baseline-orphans.js +181 -0
  157. package/.agents/scripts/resolve-stories.js +0 -2
  158. package/.agents/scripts/run-lint.js +61 -61
  159. package/.agents/scripts/run-test-profile.js +6 -6
  160. package/.agents/scripts/run-verify.js +48 -30
  161. package/.agents/scripts/single-story-close.js +20 -0
  162. package/.agents/scripts/single-story-init.js +12 -35
  163. package/.agents/scripts/update-dead-exports-baseline.js +321 -0
  164. package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
  165. package/.agents/skills/skills.index.json +2 -12
  166. package/.agents/skills/stack/qa/playwright/SKILL.md +48 -0
  167. package/.agents/workflows/audit-documentation.md +5 -6
  168. package/.agents/workflows/audit-to-stories.md +2 -2
  169. package/.agents/workflows/helpers/audit-lens-core.md +11 -12
  170. package/.agents/workflows/helpers/code-quality-guardrails.md +15 -14
  171. package/.agents/workflows/helpers/code-review.md +3 -8
  172. package/.agents/workflows/helpers/deliver-reference.md +2 -1
  173. package/.agents/workflows/helpers/deliver-story-reference.md +27 -16
  174. package/.agents/workflows/helpers/worktree-lifecycle.md +1 -2
  175. package/.agents/workflows/mandrel-update.md +10 -10
  176. package/.agents/workflows/qa-assist.md +15 -20
  177. package/.agents/workflows/qa-explore.md +9 -8
  178. package/README.md +1 -1
  179. package/docs/CHANGELOG.md +49 -0
  180. package/lib/migrations/index.js +2 -0
  181. package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +127 -0
  182. package/package.json +12 -3
  183. package/.agents/schemas/lifecycle/checkpoint.written.schema.json +0 -13
  184. package/.agents/schemas/lifecycle/close-validate.end.schema.json +0 -18
  185. package/.agents/schemas/lifecycle/close-validate.start.schema.json +0 -13
  186. package/.agents/schemas/lifecycle/code-review.end.schema.json +0 -30
  187. package/.agents/schemas/lifecycle/code-review.start.schema.json +0 -12
  188. package/.agents/schemas/lifecycle/intervention.recorded.schema.json +0 -15
  189. package/.agents/schemas/lifecycle/loop.tick.schema.json +0 -20
  190. package/.agents/schemas/lifecycle/notification.emitted.schema.json +0 -18
  191. package/.agents/schemas/lifecycle/pr.created.schema.json +0 -14
  192. package/.agents/schemas/lifecycle/retro.end.schema.json +0 -16
  193. package/.agents/schemas/lifecycle/retro.start.schema.json +0 -12
  194. package/.agents/schemas/lifecycle/story.blocked.schema.json +0 -13
  195. package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +0 -17
  196. package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +0 -15
  197. package/.agents/schemas/lifecycle/story.merged.schema.json +0 -13
  198. package/.agents/scripts/check-gherkin-placeholders.js +0 -663
  199. package/.agents/scripts/check-lifecycle-doc-drift.js +0 -411
  200. package/.agents/scripts/lib/audit-suite/cli.js +0 -64
  201. package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +0 -202
  202. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +0 -212
  203. package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +0 -104
  204. package/.agents/scripts/lib/checks/push-hook-parity.js +0 -106
  205. package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +0 -92
  206. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +0 -81
  207. package/.agents/scripts/lib/checks/worktree-residue-biome.js +0 -55
  208. package/.agents/scripts/lib/crap-baseline-index.js +0 -46
  209. package/.agents/scripts/lib/crap-utils-incremental.js +0 -113
  210. package/.agents/scripts/lib/dynamic-workflow/capability.js +0 -396
  211. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -335
  212. package/.agents/scripts/lib/mutation/baseline-snapshot.js +0 -239
  213. package/.agents/scripts/lib/mutation/config-detector.js +0 -119
  214. package/.agents/scripts/lib/mutation/stryker-runner.js +0 -306
  215. package/.agents/scripts/lib/mutation/survivor-report.js +0 -160
  216. package/.agents/scripts/lib/observability/active-story-env.js +0 -170
  217. package/.agents/scripts/lib/observability/tool-trace-hook.js +0 -456
  218. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +0 -111
  219. package/.agents/scripts/lib/orchestration/context-envelope.js +0 -277
  220. package/.agents/scripts/lib/orchestration/detectors-phase.js +0 -194
  221. package/.agents/scripts/lib/orchestration/lifecycle/bus.js +0 -309
  222. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +0 -181
  223. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +0 -229
  224. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +0 -54
  225. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +0 -344
  226. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +0 -114
  227. package/.agents/scripts/lib/orchestration/pr-base-guard.js +0 -37
  228. package/.agents/scripts/lib/orchestration/resolves-token.js +0 -127
  229. package/.agents/scripts/lib/orchestration/spec-section-validator.js +0 -130
  230. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +0 -55
  231. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +0 -211
  232. package/.agents/scripts/lib/planning-corpus.js +0 -37
  233. package/.agents/scripts/lib/qa/coverage-report.js +0 -181
  234. package/.agents/scripts/lib/qa/propose-missing-test.js +0 -95
  235. package/.agents/scripts/lib/qa/qa-context-hydrator.js +0 -217
  236. package/.agents/scripts/lib/signals/detectors/index.js +0 -14
  237. package/.agents/scripts/lib/signals/detectors/retry.js +0 -253
  238. package/.agents/scripts/lib/signals/detectors/rework.js +0 -167
  239. package/.agents/scripts/lib/signals/read.js +0 -268
  240. package/.agents/scripts/lib/signals/span-tree.js +0 -291
  241. package/.agents/scripts/lib/story-lifecycle.js +0 -194
  242. package/.agents/scripts/lib/story-plan.js +0 -379
  243. package/.agents/scripts/lib/util/phase-timer-state.js +0 -72
  244. package/.agents/scripts/lib/util/phase-timer.js +0 -163
  245. package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +0 -169
  246. package/.agents/scripts/lint-baseline.js +0 -507
  247. package/.agents/scripts/providers/github/prs.js +0 -103
  248. package/.agents/scripts/signals-view.js +0 -309
  249. package/.agents/scripts/story-plan.js +0 -370
  250. package/.agents/scripts/sync-branch-from-base.js +0 -149
  251. package/.agents/scripts/validate-docs-freshness.js +0 -314
  252. package/.agents/skills/core/diagnose-friction/SKILL.md +0 -78
  253. package/.agents/workflows/helpers/signals.md +0 -112
@@ -1,277 +0,0 @@
1
- /**
2
- * context-envelope.js — typed ContextEnvelope SDK for the hydration pipeline
3
- * (Epic #2648, Story #2762).
4
- *
5
- * Pure functions with no I/O. Downstream Stories assemble sections from ticket
6
- * fetches and file reads, then serialize via {@link envelopeToPrompt}.
7
- *
8
- * @module lib/orchestration/context-envelope
9
- */
10
-
11
- /** @typedef {'protocolPolicy' | 'skillCapsules' | 'hierarchy' | 'acceptanceCriteria' | 'verificationCommands' | 'taskInstructions'} SectionName */
12
-
13
- /**
14
- * @typedef {object} SectionSource
15
- * @property {'file' | 'ticket' | 'derived'} kind
16
- * @property {string} ref
17
- */
18
-
19
- /**
20
- * @typedef {object} Section
21
- * @property {SectionName} name
22
- * @property {number} priority Lower numbers are elided first.
23
- * @property {'drop' | 'summarize'} elideWhenOverBudget
24
- * @property {string} content
25
- * @property {number} estimatedTokens
26
- * @property {SectionSource} [source]
27
- */
28
-
29
- /**
30
- * @typedef {object} TicketSnapshot
31
- * @property {number} id
32
- * @property {string} version ISO 8601 ticket updatedAt
33
- * @property {string} hash sha256(body) truncated to 12 hex chars
34
- * @property {string} retrievedAt ISO 8601 at fetch time
35
- */
36
-
37
- /**
38
- * @typedef {object} ContextEnvelope
39
- * @property {'1'} schemaVersion
40
- * @property {{ id: number, title: string, skills?: string[], protocolVersion?: string }} task
41
- * @property {Section[]} sections
42
- * @property {TicketSnapshot[]} provenance
43
- * @property {{ maxTokens: number, used: number, elided: string[] }} budget
44
- * @property {string[]} warnings
45
- */
46
-
47
- /** Separator between rendered sections — matches legacy hydrateContext join. */
48
- export const PROMPT_SECTION_SEPARATOR =
49
- '\n\n========================================================================\n\n';
50
-
51
- /** Rendering order for {@link envelopeToPrompt} (not elision priority). */
52
- export const SECTION_RENDER_ORDER = Object.freeze([
53
- 'protocolPolicy',
54
- 'skillCapsules',
55
- 'hierarchy',
56
- 'acceptanceCriteria',
57
- 'verificationCommands',
58
- 'taskInstructions',
59
- ]);
60
-
61
- /** Default elision priority per section name (lower drops first). */
62
- export const DEFAULT_SECTION_PRIORITIES = Object.freeze({
63
- skillCapsules: 10,
64
- hierarchy: 20,
65
- acceptanceCriteria: 30,
66
- verificationCommands: 40,
67
- protocolPolicy: 80,
68
- taskInstructions: 100,
69
- });
70
-
71
- /** Default elide policy per section name. */
72
- export const DEFAULT_ELIDE_POLICIES = Object.freeze({
73
- skillCapsules: 'summarize',
74
- hierarchy: 'summarize',
75
- acceptanceCriteria: 'summarize',
76
- verificationCommands: 'drop',
77
- protocolPolicy: 'drop',
78
- taskInstructions: 'drop',
79
- });
80
-
81
- const SUMMARIZE_HEAD_CHARS = 200;
82
- const TASK_INSTRUCTIONS = 'taskInstructions';
83
-
84
- /**
85
- * Rough token estimate matching legacy {@link truncateToTokenBudget}.
86
- *
87
- * @param {string} text
88
- * @returns {number}
89
- */
90
- export function estimateTokens(text) {
91
- return Math.ceil(String(text ?? '').length / 4);
92
- }
93
-
94
- /**
95
- * Normalize a section: stamp estimatedTokens when absent.
96
- *
97
- * @param {Section} section
98
- * @returns {Section}
99
- */
100
- function normalizeSection(section) {
101
- const content = String(section.content ?? '');
102
- return {
103
- ...section,
104
- content,
105
- estimatedTokens:
106
- typeof section.estimatedTokens === 'number'
107
- ? section.estimatedTokens
108
- : estimateTokens(content),
109
- };
110
- }
111
-
112
- /**
113
- * Sum estimatedTokens across sections.
114
- *
115
- * @param {Section[]} sections
116
- * @returns {number}
117
- */
118
- function totalSectionTokens(sections) {
119
- return sections.reduce((sum, s) => sum + s.estimatedTokens, 0);
120
- }
121
-
122
- /**
123
- * Build a {@link ContextEnvelope} from assembled parts.
124
- *
125
- * @param {{
126
- * task: ContextEnvelope['task'],
127
- * sections: Section[],
128
- * provenance?: TicketSnapshot[],
129
- * warnings?: string[],
130
- * maxTokens?: number,
131
- * }} parts
132
- * @returns {ContextEnvelope}
133
- */
134
- export function buildEnvelope(parts) {
135
- const sections = (parts.sections ?? []).map(normalizeSection);
136
- const used = totalSectionTokens(sections);
137
-
138
- return {
139
- schemaVersion: '1',
140
- task: parts.task,
141
- sections,
142
- provenance: parts.provenance ?? [],
143
- budget: {
144
- maxTokens: parts.maxTokens ?? used,
145
- used,
146
- elided: [],
147
- },
148
- warnings: parts.warnings ?? [],
149
- };
150
- }
151
-
152
- /**
153
- * Deterministic summary for over-budget sections.
154
- *
155
- * @param {string} content
156
- * @returns {{ content: string, estimatedTokens: number, droppedTokens: number }}
157
- */
158
- function summarizeSectionContent(content) {
159
- const originalTokens = estimateTokens(content);
160
- if (content.length <= SUMMARIZE_HEAD_CHARS) {
161
- return { content, estimatedTokens: originalTokens, droppedTokens: 0 };
162
- }
163
- const head = content.slice(0, SUMMARIZE_HEAD_CHARS);
164
- const droppedTokens = originalTokens - estimateTokens(head);
165
- const summary = `${head}…[elided, ${droppedTokens} tokens dropped]`;
166
- return {
167
- content: summary,
168
- estimatedTokens: estimateTokens(summary),
169
- droppedTokens,
170
- };
171
- }
172
-
173
- /**
174
- * Apply section-aware elision against a token budget.
175
- *
176
- * Sections iterate in ascending priority order. `taskInstructions` is never
177
- * dropped or summarized. When still over budget after all eligible sections
178
- * are elided, a warning is appended and task instructions remain intact.
179
- *
180
- * @param {ContextEnvelope} envelope
181
- * @param {number} maxTokens
182
- * @returns {ContextEnvelope}
183
- */
184
- export function elideEnvelope(envelope, maxTokens) {
185
- const sections = envelope.sections.map((s) => ({ ...s }));
186
- let used = totalSectionTokens(sections);
187
-
188
- if (used <= maxTokens) {
189
- return {
190
- ...envelope,
191
- budget: { maxTokens, used, elided: [] },
192
- };
193
- }
194
-
195
- const elided = [];
196
- const byPriority = [...sections].sort((a, b) => a.priority - b.priority);
197
-
198
- for (const template of byPriority) {
199
- if (used <= maxTokens) break;
200
- if (template.name === TASK_INSTRUCTIONS) continue;
201
-
202
- const idx = sections.findIndex((s) => s.name === template.name);
203
- if (idx === -1) continue;
204
-
205
- const current = sections[idx];
206
- if (!current.content) continue;
207
-
208
- if (current.elideWhenOverBudget === 'summarize') {
209
- const { content, estimatedTokens } = summarizeSectionContent(
210
- current.content,
211
- );
212
- sections[idx] = { ...current, content, estimatedTokens };
213
- } else {
214
- sections[idx] = { ...current, content: '', estimatedTokens: 0 };
215
- }
216
-
217
- if (!elided.includes(template.name)) {
218
- elided.push(template.name);
219
- }
220
- used = totalSectionTokens(sections);
221
- }
222
-
223
- const warnings = [...envelope.warnings];
224
- if (used > maxTokens) {
225
- warnings.push(
226
- `⚠️ Context envelope still over token budget after elision (${used}/${maxTokens} tokens). Task instructions preserved intact.`,
227
- );
228
- }
229
-
230
- return {
231
- ...envelope,
232
- sections,
233
- warnings,
234
- budget: { maxTokens, used, elided },
235
- };
236
- }
237
-
238
- /**
239
- * Serialize a {@link ContextEnvelope} to the legacy prose prompt form.
240
- *
241
- * Warnings are prepended, then non-empty sections are joined in
242
- * {@link SECTION_RENDER_ORDER} with {@link PROMPT_SECTION_SEPARATOR}.
243
- * Identical envelope input yields byte-identical output.
244
- *
245
- * @param {ContextEnvelope} envelope
246
- * @returns {string}
247
- */
248
- export function envelopeToPrompt(envelope) {
249
- const sectionByName = new Map(
250
- envelope.sections.map((section) => [section.name, section]),
251
- );
252
-
253
- const parts = [];
254
-
255
- const warningsText = (envelope.warnings ?? [])
256
- .map((w) => String(w).trim())
257
- .filter(Boolean)
258
- .join('\n\n');
259
- if (warningsText) {
260
- parts.push(warningsText);
261
- }
262
-
263
- for (const name of SECTION_RENDER_ORDER) {
264
- const section = sectionByName.get(name);
265
- if (section?.content) {
266
- parts.push(section.content);
267
- }
268
- }
269
-
270
- for (const section of envelope.sections) {
271
- if (!SECTION_RENDER_ORDER.includes(section.name) && section.content) {
272
- parts.push(section.content);
273
- }
274
- }
275
-
276
- return parts.join(PROMPT_SECTION_SEPARATOR);
277
- }
@@ -1,194 +0,0 @@
1
- /**
2
- * detectors-phase.js — pure phase that runs the per-Story signal detectors
3
- * (rework + retry) over the local NDJSON stream.
4
- *
5
- * Story #4545 — this phase has no production caller. Its sequencer
6
- * (`post-merge-pipeline.js`) went in the v2.0.0 cutover, and the
7
- * `analyze-execution.js` consumer it fed was deleted with the
8
- * execution-analysis surface. Kept alive only by its own test — the
9
- * test-importer blind spot the dead-exports ratchet cannot see.
10
- *
11
- * Extracted from `post-merge-pipeline.js` (Story #1770 / Task #1779) to
12
- * keep the parent sequencer's maintainability score above its baseline
13
- * after the new wiring layer landed. The phase contract is identical to
14
- * every other entry in `DEFAULT_POST_MERGE_PHASES`: take a context bag,
15
- * return a value the runner stitches under a `stateKey`.
16
- *
17
- * ### Contract
18
- *
19
- * - **Pure detectors.** Both `detectRework` and `detectRetry` are pure
20
- * modules under `lib/signals/detectors/` — they read the per-Story
21
- * `traces.ndjson` and return SignalEvent arrays. This phase resolves
22
- * the operator-tunable thresholds via `getSignals(config)` (Epic
23
- * #1720 — `delivery.signals.*`), invokes each detector, and persists
24
- * each event through `appendSignal` so the per-Story `signals.ndjson`
25
- * stream covers both kinds before the analyzer reads it.
26
- *
27
- * - **Failure isolation.** A failing detector or a per-event append
28
- * MUST NOT block Task close. Each call is wrapped in try/catch;
29
- * failures degrade to a warn and contribute 0 to the offending
30
- * detector's count. The phase always resolves with a stable
31
- * `{ rework: N, retry: N }` shape.
32
- *
33
- * - **One info line per close.** The summary log
34
- * (`detectors: rework=N retry=M`) is the only stdout signal — one
35
- * line per Story rather than two per detector + N per event.
36
- *
37
- * - **2-tier (Storyless) closure (Story #3127).** When `tasks` is
38
- * empty (the 2-tier hierarchy shape), `resolveLastTaskId` returns
39
- * `null` and both detectors run with `taskId: null`. The detector
40
- * modules already accept a nullable `taskId` (see
41
- * `lib/signals/detectors/{rework,retry}.js`), so no branching on
42
- * hierarchy mode is required here.
43
- *
44
- * @module lib/orchestration/detectors-phase
45
- */
46
-
47
- import path from 'node:path';
48
- import { getSignals } from '../config/limits.js';
49
- import { storyTempDir } from '../config/temp-paths.js';
50
- import { Logger } from '../Logger.js';
51
- import { appendSignal } from '../observability/signals-writer.js';
52
- import { detectRetry, detectRework } from '../signals/detectors/index.js';
53
-
54
- function reapPhaseLogger(progress) {
55
- return progress ?? (() => {});
56
- }
57
-
58
- function resolveLastTaskId(tasks) {
59
- if (!Array.isArray(tasks) || tasks.length === 0) return null;
60
- const last = tasks[tasks.length - 1];
61
- const id = Number(last?.id ?? last);
62
- return Number.isInteger(id) && id > 0 ? id : null;
63
- }
64
-
65
- async function persistDetectorEvents({
66
- events,
67
- epicId,
68
- storyId,
69
- config,
70
- appendSignalFn,
71
- logger,
72
- kind,
73
- }) {
74
- let count = 0;
75
- for (const evt of events) {
76
- try {
77
- await appendSignalFn({ epicId, storyId, signal: evt, config });
78
- count += 1;
79
- } catch (err) {
80
- logger?.warn?.(
81
- `[detectors-phase] ${kind} appendSignal failed (${err?.message ?? err})`,
82
- );
83
- }
84
- }
85
- return count;
86
- }
87
-
88
- async function runOneDetector({
89
- detectorFn,
90
- args,
91
- epicId,
92
- storyId,
93
- config,
94
- appendSignalFn,
95
- logger,
96
- kind,
97
- }) {
98
- try {
99
- const events = await detectorFn(args);
100
- return await persistDetectorEvents({
101
- events,
102
- epicId,
103
- storyId,
104
- config,
105
- appendSignalFn,
106
- logger,
107
- kind,
108
- });
109
- } catch (err) {
110
- logger?.warn?.(
111
- `[detectors-phase] ${kind} detector threw (${err?.message ?? err})`,
112
- );
113
- return 0;
114
- }
115
- }
116
-
117
- function isValidIdPair(eid, sid) {
118
- return Number.isInteger(eid) && eid > 0 && Number.isInteger(sid) && sid > 0;
119
- }
120
-
121
- /**
122
- * Run the rework + retry detectors for one Story and persist every
123
- * emission to `temp/run-<id>/stories/story-<sid>/signals.ndjson`.
124
- *
125
- * @param {{
126
- * epicId: number|string,
127
- * storyId: number|string,
128
- * tasks?: Array<{ id?: number|string }>,
129
- * config?: object,
130
- * progress?: Function,
131
- * logger?: object,
132
- * detectorsImpl?: { detectRework?: typeof detectRework, detectRetry?: typeof detectRetry },
133
- * appendSignalFn?: typeof appendSignal,
134
- * }} ctx
135
- * @returns {Promise<{ rework: number, retry: number }>}
136
- */
137
- export async function detectorsPhase(ctx) {
138
- const {
139
- epicId,
140
- storyId,
141
- tasks,
142
- config,
143
- progress,
144
- logger = Logger,
145
- detectorsImpl,
146
- appendSignalFn = appendSignal,
147
- } = ctx;
148
- const log = reapPhaseLogger(progress);
149
- const eid = Number(epicId);
150
- const sid = Number(storyId);
151
- if (!isValidIdPair(eid, sid)) {
152
- logger?.warn?.(
153
- `[detectors-phase] skipped: invalid epicId=${epicId} / storyId=${storyId}`,
154
- );
155
- return { rework: 0, retry: 0 };
156
- }
157
-
158
- let signalsCfg;
159
- try {
160
- signalsCfg = getSignals(config);
161
- } catch (err) {
162
- logger?.warn?.(
163
- `[detectors-phase] getSignals failed (${err?.message ?? err}); using zero counts`,
164
- );
165
- return { rework: 0, retry: 0 };
166
- }
167
-
168
- const tracesPath = path.join(storyTempDir(eid, sid, config), 'traces.ndjson');
169
- const taskId = resolveLastTaskId(tasks);
170
- const baseArgs = { tracesPath, epicId: eid, storyId: sid, taskId };
171
- const common = {
172
- epicId: eid,
173
- storyId: sid,
174
- config,
175
- appendSignalFn,
176
- logger,
177
- };
178
-
179
- const rework = await runOneDetector({
180
- ...common,
181
- detectorFn: detectorsImpl?.detectRework ?? detectRework,
182
- args: { ...baseArgs, threshold: signalsCfg.rework.editsPerFile },
183
- kind: 'rework',
184
- });
185
- const retry = await runOneDetector({
186
- ...common,
187
- detectorFn: detectorsImpl?.detectRetry ?? detectRetry,
188
- args: { ...baseArgs, threshold: signalsCfg.retry.repeatCount },
189
- kind: 'retry',
190
- });
191
-
192
- log('DETECTORS', `detectors: rework=${rework} retry=${retry}`);
193
- return { rework, retry };
194
- }