mandrel 2.31.0 → 2.33.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 (267) hide show
  1. package/.agents/README.md +13 -17
  2. package/.agents/agents/acceptance-critic.md +1 -2
  3. package/.agents/docs/SDLC.md +12 -9
  4. package/.agents/docs/agentrc-reference.json +63 -58
  5. package/.agents/docs/configuration.md +275 -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/git-conventions-reference.md +27 -27
  10. package/.agents/rules/git-conventions.md +4 -2
  11. package/.agents/rules/known-tooling-behavior.md +66 -30
  12. package/.agents/rules/testing-standards.md +35 -71
  13. package/.agents/runtime-deps.json +2 -2
  14. package/.agents/schemas/agentrc.schema.json +1955 -1410
  15. package/.agents/schemas/lifecycle/README.md +21 -14
  16. package/.agents/schemas/lifecycle/ledger-record.schema.json +76 -22
  17. package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
  18. package/.agents/scripts/README.md +16 -29
  19. package/.agents/scripts/apply-quality-bootstrap.js +27 -34
  20. package/.agents/scripts/audit-to-stories.js +160 -41
  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 +159 -0
  27. package/.agents/scripts/check-lifecycle-lint.js +108 -124
  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 +81 -35
  38. package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +185 -0
  39. package/.agents/scripts/lib/baselines/envelope.js +41 -60
  40. package/.agents/scripts/lib/baselines/git-base.js +30 -37
  41. package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +103 -0
  42. package/.agents/scripts/lib/baselines/kinds/_crap-read.js +150 -0
  43. package/.agents/scripts/lib/baselines/kinds/crap.js +25 -65
  44. package/.agents/scripts/lib/baselines/orphan-pruner.js +233 -0
  45. package/.agents/scripts/lib/baselines/refresh-service.js +6 -8
  46. package/.agents/scripts/lib/baselines/scope-assert.js +223 -0
  47. package/.agents/scripts/lib/baselines/scope-inventory.js +314 -0
  48. package/.agents/scripts/lib/bdd-step-index.js +326 -0
  49. package/.agents/scripts/lib/bootstrap/install-ledger.js +5 -3
  50. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +4 -6
  51. package/.agents/scripts/lib/bootstrap/manifest.js +17 -40
  52. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +12 -59
  53. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +62 -2
  54. package/.agents/scripts/lib/checks/loop-health.js +9 -37
  55. package/.agents/scripts/lib/child-exec.js +193 -0
  56. package/.agents/scripts/lib/cli/standard-args.js +1 -1
  57. package/.agents/scripts/lib/cli-args.js +64 -0
  58. package/.agents/scripts/lib/close-validation/gates.js +2 -2
  59. package/.agents/scripts/lib/close-validation/runner.js +3 -3
  60. package/.agents/scripts/lib/config/acceptance-eval.js +5 -52
  61. package/.agents/scripts/lib/config/commands.js +3 -5
  62. package/.agents/scripts/lib/config/explain.js +5 -7
  63. package/.agents/scripts/lib/config/gates/bundle-size.schema.js +32 -6
  64. package/.agents/scripts/lib/config/gates/coverage.schema.js +25 -5
  65. package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +12 -2
  66. package/.agents/scripts/lib/config/gates/crap.schema.js +68 -23
  67. package/.agents/scripts/lib/config/gates/duplication.schema.js +29 -17
  68. package/.agents/scripts/lib/config/gates/index.js +5 -2
  69. package/.agents/scripts/lib/config/gates/lighthouse.schema.js +34 -6
  70. package/.agents/scripts/lib/config/gates/lint.schema.js +11 -2
  71. package/.agents/scripts/lib/config/gates/maintainability.schema.js +37 -15
  72. package/.agents/scripts/lib/config/gates/mutation.schema.js +15 -3
  73. package/.agents/scripts/lib/config/gates/shared.js +58 -9
  74. package/.agents/scripts/lib/config/github.js +0 -1
  75. package/.agents/scripts/lib/config/limits.js +3 -48
  76. package/.agents/scripts/lib/config/qa.js +105 -0
  77. package/.agents/scripts/lib/config/runners.js +38 -16
  78. package/.agents/scripts/lib/config/temp-paths.js +6 -5
  79. package/.agents/scripts/lib/config-settings-schema-delivery.js +245 -56
  80. package/.agents/scripts/lib/config-settings-schema-quality.js +209 -29
  81. package/.agents/scripts/lib/config-settings-schema.js +386 -39
  82. package/.agents/scripts/lib/crap-baseline-join.js +126 -9
  83. package/.agents/scripts/lib/crap-utils.js +84 -520
  84. package/.agents/scripts/lib/dead-exports-knip.js +79 -10
  85. package/.agents/scripts/lib/degraded-mode.js +2 -2
  86. package/.agents/scripts/lib/dependency-parser.js +20 -7
  87. package/.agents/scripts/lib/doc-tiers.js +3 -3
  88. package/.agents/scripts/lib/feedback-loop/graduator-core.js +46 -104
  89. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +10 -8
  90. package/.agents/scripts/lib/findings/provenance-field.js +135 -0
  91. package/.agents/scripts/lib/findings/route-finding.js +57 -8
  92. package/.agents/scripts/lib/fs-walk.js +52 -0
  93. package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
  94. package/.agents/scripts/lib/git-utils.js +16 -36
  95. package/.agents/scripts/lib/knip-config-resolver.js +181 -0
  96. package/.agents/scripts/lib/knip-entry-sync.js +508 -0
  97. package/.agents/scripts/lib/observability/metrics-ledger.js +1 -1
  98. package/.agents/scripts/lib/observability/runtime-friction.js +10 -0
  99. package/.agents/scripts/lib/observability/signal-validator.js +5 -85
  100. package/.agents/scripts/lib/observability/signals-writer.js +19 -62
  101. package/.agents/scripts/lib/observability/source-classifier.js +5 -7
  102. package/.agents/scripts/lib/observability/terse-result.js +3 -3
  103. package/.agents/scripts/lib/orchestration/behind-recovery.js +114 -0
  104. package/.agents/scripts/lib/orchestration/ceremony-routing.js +7 -8
  105. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +34 -33
  106. package/.agents/scripts/lib/orchestration/code-review.js +2 -2
  107. package/.agents/scripts/lib/orchestration/complexity-gate.js +43 -161
  108. package/.agents/scripts/lib/orchestration/diff-magnitude.js +4 -4
  109. package/.agents/scripts/lib/orchestration/label-transitions.js +3 -2
  110. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +12 -38
  111. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +5 -6
  112. package/.agents/scripts/lib/orchestration/plan-metrics.js +2 -3
  113. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +7 -26
  114. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +69 -5
  115. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +69 -13
  116. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +49 -0
  117. package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
  118. package/.agents/scripts/lib/orchestration/resolve-stories.js +77 -50
  119. package/.agents/scripts/lib/orchestration/review-providers/codex.js +1 -1
  120. package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +130 -0
  121. package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -16
  122. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +1 -1
  123. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +37 -26
  124. package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +376 -0
  125. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +161 -151
  126. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +15 -3
  127. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +10 -15
  128. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +5 -0
  129. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
  130. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
  131. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +59 -25
  132. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
  133. package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
  134. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
  135. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
  136. package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
  137. package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
  138. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +116 -1
  139. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
  140. package/.agents/scripts/lib/orchestration/ticket-validator.js +38 -0
  141. package/.agents/scripts/lib/orchestration/ticketing/reads.js +5 -5
  142. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -4
  143. package/.agents/scripts/lib/orchestration/verify-tier-repair.js +107 -0
  144. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -87
  145. package/.agents/scripts/lib/signals/detectors/common.js +1 -1
  146. package/.agents/scripts/lib/signals/index.js +8 -6
  147. package/.agents/scripts/lib/signals/schema.js +20 -25
  148. package/.agents/scripts/lib/signals/write.js +8 -8
  149. package/.agents/scripts/lib/story-body/footer-block.js +97 -0
  150. package/.agents/scripts/lib/story-body/story-body.js +18 -81
  151. package/.agents/scripts/lib/temp-retention.js +1 -1
  152. package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -14
  153. package/.agents/scripts/lib/ticket-body-sections.js +4 -5
  154. package/.agents/scripts/lib/wave-runner/footprint.js +306 -0
  155. package/.agents/scripts/lib/wave-runner/ready-set.js +198 -181
  156. package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +13 -45
  157. package/.agents/scripts/lib/worktree/lifecycle/reap.js +4 -5
  158. package/.agents/scripts/lib/worktree-manager.js +2 -3
  159. package/.agents/scripts/lint-label-vocabulary.js +2 -24
  160. package/.agents/scripts/pr-watch-with-update.js +7 -5
  161. package/.agents/scripts/providers/github/blocked-by-add.js +25 -10
  162. package/.agents/scripts/providers/github/cache.js +2 -2
  163. package/.agents/scripts/providers/github/comments.js +6 -28
  164. package/.agents/scripts/providers/github/compose.js +0 -15
  165. package/.agents/scripts/providers/github/errors.js +10 -27
  166. package/.agents/scripts/providers/github/request-helpers.js +1 -2
  167. package/.agents/scripts/providers/github/sub-issues.js +10 -218
  168. package/.agents/scripts/providers/github.js +4 -7
  169. package/.agents/scripts/prune-baseline-orphans.js +181 -0
  170. package/.agents/scripts/resolve-stories.js +21 -7
  171. package/.agents/scripts/run-lint.js +61 -61
  172. package/.agents/scripts/run-test-profile.js +6 -6
  173. package/.agents/scripts/run-verify.js +48 -30
  174. package/.agents/scripts/single-story-close.js +20 -0
  175. package/.agents/scripts/single-story-init.js +12 -35
  176. package/.agents/scripts/stories-wave-tick.js +192 -9
  177. package/.agents/scripts/update-dead-exports-baseline.js +321 -0
  178. package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
  179. package/.agents/skills/skills.index.json +1 -11
  180. package/.agents/workflows/audit-documentation.md +5 -6
  181. package/.agents/workflows/audit-to-stories.md +28 -2
  182. package/.agents/workflows/helpers/audit-lens-core.md +11 -12
  183. package/.agents/workflows/helpers/code-quality-guardrails.md +15 -14
  184. package/.agents/workflows/helpers/code-review.md +3 -8
  185. package/.agents/workflows/helpers/deliver-reference.md +30 -2
  186. package/.agents/workflows/helpers/deliver-story-reference.md +84 -16
  187. package/.agents/workflows/helpers/plan-reference.md +76 -0
  188. package/.agents/workflows/helpers/worktree-lifecycle.md +1 -2
  189. package/.agents/workflows/mandrel-update.md +10 -10
  190. package/.agents/workflows/qa-assist.md +15 -20
  191. package/.agents/workflows/qa-explore.md +9 -8
  192. package/README.md +1 -1
  193. package/docs/CHANGELOG.md +58 -0
  194. package/lib/migrations/index.js +2 -0
  195. package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +127 -0
  196. package/package.json +14 -5
  197. package/.agents/schemas/lifecycle/checkpoint.written.schema.json +0 -13
  198. package/.agents/schemas/lifecycle/close-validate.end.schema.json +0 -18
  199. package/.agents/schemas/lifecycle/close-validate.start.schema.json +0 -13
  200. package/.agents/schemas/lifecycle/code-review.end.schema.json +0 -30
  201. package/.agents/schemas/lifecycle/code-review.start.schema.json +0 -12
  202. package/.agents/schemas/lifecycle/intervention.recorded.schema.json +0 -15
  203. package/.agents/schemas/lifecycle/loop.tick.schema.json +0 -20
  204. package/.agents/schemas/lifecycle/notification.emitted.schema.json +0 -18
  205. package/.agents/schemas/lifecycle/pr.created.schema.json +0 -14
  206. package/.agents/schemas/lifecycle/retro.end.schema.json +0 -16
  207. package/.agents/schemas/lifecycle/retro.start.schema.json +0 -12
  208. package/.agents/schemas/lifecycle/story.blocked.schema.json +0 -13
  209. package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +0 -17
  210. package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +0 -15
  211. package/.agents/schemas/lifecycle/story.merged.schema.json +0 -13
  212. package/.agents/scripts/check-gherkin-placeholders.js +0 -663
  213. package/.agents/scripts/check-lifecycle-doc-drift.js +0 -411
  214. package/.agents/scripts/lib/audit-suite/cli.js +0 -64
  215. package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +0 -202
  216. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +0 -212
  217. package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +0 -104
  218. package/.agents/scripts/lib/checks/push-hook-parity.js +0 -106
  219. package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +0 -92
  220. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +0 -81
  221. package/.agents/scripts/lib/checks/worktree-residue-biome.js +0 -55
  222. package/.agents/scripts/lib/crap-baseline-index.js +0 -46
  223. package/.agents/scripts/lib/crap-utils-incremental.js +0 -113
  224. package/.agents/scripts/lib/dynamic-workflow/capability.js +0 -396
  225. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -335
  226. package/.agents/scripts/lib/mutation/baseline-snapshot.js +0 -239
  227. package/.agents/scripts/lib/mutation/config-detector.js +0 -119
  228. package/.agents/scripts/lib/mutation/stryker-runner.js +0 -306
  229. package/.agents/scripts/lib/mutation/survivor-report.js +0 -160
  230. package/.agents/scripts/lib/observability/active-story-env.js +0 -170
  231. package/.agents/scripts/lib/observability/tool-trace-hook.js +0 -456
  232. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +0 -111
  233. package/.agents/scripts/lib/orchestration/context-envelope.js +0 -277
  234. package/.agents/scripts/lib/orchestration/detectors-phase.js +0 -194
  235. package/.agents/scripts/lib/orchestration/lifecycle/bus.js +0 -309
  236. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +0 -181
  237. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +0 -229
  238. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +0 -54
  239. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +0 -344
  240. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +0 -114
  241. package/.agents/scripts/lib/orchestration/pr-base-guard.js +0 -37
  242. package/.agents/scripts/lib/orchestration/resolves-token.js +0 -127
  243. package/.agents/scripts/lib/orchestration/spec-section-validator.js +0 -130
  244. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +0 -55
  245. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +0 -211
  246. package/.agents/scripts/lib/planning-corpus.js +0 -37
  247. package/.agents/scripts/lib/qa/coverage-report.js +0 -181
  248. package/.agents/scripts/lib/qa/propose-missing-test.js +0 -95
  249. package/.agents/scripts/lib/qa/qa-context-hydrator.js +0 -217
  250. package/.agents/scripts/lib/signals/detectors/index.js +0 -14
  251. package/.agents/scripts/lib/signals/detectors/retry.js +0 -253
  252. package/.agents/scripts/lib/signals/detectors/rework.js +0 -167
  253. package/.agents/scripts/lib/signals/read.js +0 -268
  254. package/.agents/scripts/lib/signals/span-tree.js +0 -291
  255. package/.agents/scripts/lib/story-lifecycle.js +0 -194
  256. package/.agents/scripts/lib/story-plan.js +0 -379
  257. package/.agents/scripts/lib/util/phase-timer-state.js +0 -72
  258. package/.agents/scripts/lib/util/phase-timer.js +0 -163
  259. package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +0 -169
  260. package/.agents/scripts/lint-baseline.js +0 -507
  261. package/.agents/scripts/providers/github/prs.js +0 -103
  262. package/.agents/scripts/signals-view.js +0 -309
  263. package/.agents/scripts/story-plan.js +0 -370
  264. package/.agents/scripts/sync-branch-from-base.js +0 -149
  265. package/.agents/scripts/validate-docs-freshness.js +0 -314
  266. package/.agents/skills/core/diagnose-friction/SKILL.md +0 -78
  267. 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
- }