mandrel 2.58.0 → 2.60.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.
- package/.agents/README.md +17 -12
- package/.agents/agents/acceptance-critic.md +24 -43
- package/.agents/agents/story-worker.md +18 -19
- package/.agents/docs/SDLC.md +12 -13
- package/.agents/docs/agentrc-reference.json +1 -2
- package/.agents/docs/configuration.md +29 -46
- package/.agents/docs/quality-gates.md +9 -5
- package/.agents/docs/workflows.md +1 -1
- package/.agents/instructions.md +5 -7
- package/.agents/rules/ci-remediation.md +41 -8
- package/.agents/rules/known-tooling-behavior.md +65 -15
- package/.agents/runtime-deps.json +7 -2
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +6 -11
- package/.agents/schemas/crap-baseline.schema.json +1 -1
- package/.agents/schemas/crap-report.schema.json +1 -1
- package/.agents/schemas/story-deliver-terminal.schema.json +3 -3
- package/.agents/scripts/README.md +11 -1
- package/.agents/scripts/acceptance-eval.js +25 -27
- package/.agents/scripts/ceremony-derive.js +15 -10
- package/.agents/scripts/check-context-budget.js +148 -228
- package/.agents/scripts/check-schema-references.js +5 -3
- package/.agents/scripts/check-workflow-citations.js +33 -147
- package/.agents/scripts/coverage-capture.js +7 -4
- package/.agents/scripts/deliver-light.js +41 -100
- package/.agents/scripts/deliver-run.js +631 -0
- package/.agents/scripts/file-ci-gap.js +59 -11
- package/.agents/scripts/install-matrix-assert.js +48 -3
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +51 -33
- package/.agents/scripts/lib/baselines/crap-preview-incremental.js +6 -2
- package/.agents/scripts/lib/baselines/kinds/_crap-read.js +0 -8
- package/.agents/scripts/lib/baselines/kinds/crap.js +35 -18
- package/.agents/scripts/lib/changed-files.js +30 -0
- package/.agents/scripts/lib/config/delivery-routing.js +5 -4
- package/.agents/scripts/lib/config/explain.js +1 -3
- package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +1 -1
- package/.agents/scripts/lib/config-resolver.js +1 -0
- package/.agents/scripts/lib/config-settings-schema-delivery.js +28 -21
- package/.agents/scripts/lib/coverage-capture-fullscope.js +10 -2
- package/.agents/scripts/lib/coverage-capture-incremental.js +3 -2
- package/.agents/scripts/lib/coverage-capture-usage.js +4 -1
- package/.agents/scripts/lib/crap-engine.js +2 -2
- package/.agents/scripts/lib/crap-utils.js +21 -5
- package/.agents/scripts/lib/doc-tiers.js +4 -2
- package/.agents/scripts/lib/escomplex-ast-compat.js +39 -17
- package/.agents/scripts/lib/escomplex-kernel.js +298 -0
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +7 -6
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +7 -5
- package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
- package/.agents/scripts/lib/gh-exec.js +160 -0
- package/.agents/scripts/lib/maintainability-engine.js +3 -3
- package/.agents/scripts/lib/observability/source-classifier.js +1 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +74 -132
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +123 -12
- package/.agents/scripts/lib/orchestration/complexity-gate.js +180 -352
- package/.agents/scripts/lib/orchestration/light-suitability.js +71 -136
- package/.agents/scripts/lib/orchestration/plan-context.js +44 -50
- package/.agents/scripts/lib/orchestration/plan-persist/changes-repair.js +8 -6
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +104 -119
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +41 -25
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +11 -11
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +63 -29
- package/.agents/scripts/lib/orchestration/plan-persist/wave-collision-gate.js +107 -0
- package/.agents/scripts/lib/orchestration/review-depth.js +14 -11
- package/.agents/scripts/lib/orchestration/run-epilogue.js +260 -182
- package/.agents/scripts/lib/orchestration/run-scoped-config.js +63 -99
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +3 -3
- package/.agents/scripts/lib/orchestration/single-story-close/phases/graphql-preflight.js +137 -0
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +105 -18
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +4 -3
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +156 -39
- package/.agents/scripts/lib/orchestration/story-init-envelope.js +71 -0
- package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -17
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +25 -209
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +8 -5
- package/.agents/scripts/lib/orchestration/ticket-validator.js +44 -183
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +14 -25
- package/.agents/scripts/lib/runtime-deps/dep-resolution.js +155 -0
- package/.agents/scripts/lib/runtime-deps/ensure-installed.js +44 -9
- package/.agents/scripts/lib/runtime-deps/parser-major.js +110 -0
- package/.agents/scripts/lib/runtime-deps/preflight.js +6 -25
- package/.agents/scripts/lib/runtime-deps/scan-imports.js +46 -1
- package/.agents/scripts/lib/skills/walk-skill-files.js +1 -1
- package/.agents/scripts/lib/story-body/body-format-lints.js +58 -12
- package/.agents/scripts/lib/story-body/story-body.js +83 -29
- package/.agents/scripts/lib/templates/decomposer-prompts.js +28 -33
- package/.agents/scripts/lib/wave-runner/live-probe.js +31 -5
- package/.agents/scripts/merge-baseline.js +4 -5
- package/.agents/scripts/plan-context.js +117 -28
- package/.agents/scripts/plan-persist.js +79 -39
- package/.agents/scripts/plan-run-epilogue.js +11 -8
- package/.agents/scripts/pr-watch-with-update.js +9 -2
- package/.agents/scripts/run-verify.js +13 -6
- package/.agents/scripts/single-story-init.js +7 -57
- package/.agents/scripts/stories-wave-tick.js +160 -26
- package/.agents/skills/core/gates-and-baselines/reference.md +0 -1
- package/.agents/skills/skills.index.json +2 -12
- package/.agents/skills/stack/qa/playwright/SKILL.md +26 -0
- package/.agents/workflows/audit-to-stories.md +14 -11
- package/.agents/workflows/helpers/acceptance-self-eval.md +84 -157
- package/.agents/workflows/helpers/code-review.md +4 -2
- package/.agents/workflows/helpers/deliver-digest.md +31 -24
- package/.agents/workflows/helpers/deliver-light.md +92 -101
- package/.agents/workflows/helpers/deliver-reference.md +116 -100
- package/.agents/workflows/helpers/deliver-story-reference.md +58 -124
- package/.agents/workflows/helpers/deliver-story.md +17 -18
- package/.agents/workflows/helpers/plan-reference.md +82 -60
- package/.agents/workflows/mandrel-deliver.md +47 -31
- package/.agents/workflows/mandrel-plan.md +32 -30
- package/.agents/workflows/mandrel-update.md +36 -21
- package/README.md +3 -3
- package/docs/CHANGELOG.md +43 -0
- package/lib/cli/registry.js +45 -25
- package/lib/cli/update.js +376 -17
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.60.0-retire-audit-results-autofile.js +40 -0
- package/package.json +8 -2
- package/.agents/schemas/model-attribution.schema.json +0 -53
- package/.agents/scripts/lib/orchestration/model-attribution.js +0 -418
- package/.agents/scripts/lib/orchestration/split-policy-validator.js +0 -188
- package/.agents/scripts/lib/orchestration/story-plan-state.js +0 -33
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +0 -67
- package/.agents/scripts/lib/templates/spec-author-prompts.js +0 -76
- package/.agents/skills/core/scope-triage/SKILL.md +0 -48
|
@@ -16,35 +16,29 @@
|
|
|
16
16
|
*
|
|
17
17
|
* ## Four invariants keep it proportional, not a planning bypass
|
|
18
18
|
*
|
|
19
|
-
* 1. **
|
|
20
|
-
*
|
|
19
|
+
* 1. **Risk gate ({@link deriveLightSuitability}).** The prompt's predicted
|
|
20
|
+
* footprint is judged for **risk** by the shared machinery
|
|
21
21
|
* ({@link module:lib/orchestration/complexity-gate.deriveStoryShape} over
|
|
22
|
-
*
|
|
23
|
-
* **and** a ledgered
|
|
24
|
-
* ({@link resolveLedgeredVerdict}).
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* prediction is a guess, and invariant 3 bounds the real change set. Only
|
|
34
|
-
* the two things no re-slicing can fix still refuse: an un-ledgered
|
|
35
|
-
* verdict and an un-waivable risk rule (a sensitive-path class or a
|
|
36
|
-
* migration span), which route `full` through an `escalated` terminal.
|
|
37
|
-
* The former `ask-operator` outcome and its `--operator-proceed-light`
|
|
38
|
-
* answer are gone with the gate they answered.
|
|
22
|
+
* the `audit-rules.json` sensitive-path classes and the
|
|
23
|
+
* migration-with-consumers span) **and** a ledgered verdict carrying a
|
|
24
|
+
* recorded reason ({@link resolveLedgeredVerdict}). A tripped risk rule
|
|
25
|
+
* or an unrecorded reason fails closed to `full`.
|
|
26
|
+
* 2. **Nothing the caller declares about its own size decides (Story
|
|
27
|
+
* #5344).** The predicted-shape ceilings — declared change kinds, a
|
|
28
|
+
* magnitude bucket, an uncertainty bucket, a deployable span — are gone,
|
|
29
|
+
* along with the `warnings[]` Story #5313 had already demoted them to.
|
|
30
|
+
* They were self-declared by the agent asking to proceed, and once they
|
|
31
|
+
* only warned they decided nothing at all. What is left reads evidence:
|
|
32
|
+
* the predicted PATHS at the gate, and the actual diff at invariant 3.
|
|
39
33
|
* 3. **Diff-derived backstop ({@link checkLightDiffBackstop}).** After
|
|
40
34
|
* implementation the **actual** change set is re-checked with
|
|
41
35
|
* {@link module:lib/orchestration/review-depth.deriveChangeLevel} plus the
|
|
42
36
|
* implementation-only magnitude ceilings of {@link LIGHT_DIFF_CEILINGS} —
|
|
43
|
-
* the diff is the real scope signal
|
|
44
|
-
*
|
|
37
|
+
* the diff is the real scope signal, and since Story #5344 it is the ONLY
|
|
38
|
+
* size block on the path. Story #4856 moved it from a `maxFiles: 4`
|
|
45
39
|
* cardinality ceiling to changed lines over implementation files, and made
|
|
46
|
-
* a block **recycle** its receipt Story through `/mandrel-plan` tickets
|
|
47
|
-
* instead of orphaning it.
|
|
40
|
+
* a block **recycle** its receipt Story through `/mandrel-plan` tickets
|
|
41
|
+
* mode instead of orphaning it.
|
|
48
42
|
* 4. **Minimal receipt Story ({@link buildReceiptStoryTicket}).** A
|
|
49
43
|
* `type::story` ticket is authored inline so `refs #`, history, telemetry,
|
|
50
44
|
* and the `agent::executing -> agent::done` state machine survive.
|
|
@@ -56,11 +50,7 @@
|
|
|
56
50
|
* @module lib/orchestration/light-suitability
|
|
57
51
|
*/
|
|
58
52
|
|
|
59
|
-
import {
|
|
60
|
-
deriveStoryShape,
|
|
61
|
-
SHAPE_CODES,
|
|
62
|
-
STORY_SHAPE_CEILINGS,
|
|
63
|
-
} from './complexity-gate.js';
|
|
53
|
+
import { deriveStoryShape, SHAPE_CODES } from './complexity-gate.js';
|
|
64
54
|
import { deriveChangeLevel } from './review-depth.js';
|
|
65
55
|
|
|
66
56
|
/**
|
|
@@ -71,17 +61,16 @@ import { deriveChangeLevel } from './review-depth.js';
|
|
|
71
61
|
* No re-slicing, shrinking, or operator answer satisfies one: a footprint
|
|
72
62
|
* intersecting a sensitive-path class routes `full` however small the change,
|
|
73
63
|
* and the diff backstop refuses the same footprint again at the end. But
|
|
74
|
-
* {@link deriveStoryShape} reports only the **first** rule a
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* way to an implementation the backstop then refuses.
|
|
64
|
+
* {@link deriveStoryShape} reports only the **first** rule a footprint trips,
|
|
65
|
+
* and it can reject on an unknown footprint (a glob, an absent acceptance
|
|
66
|
+
* list) before either risk rule is reached — so the recorded `code` is not a
|
|
67
|
+
* reliable answer to "is this risk or is this something I can fix?".
|
|
79
68
|
*
|
|
80
|
-
* The recovery is that the shape decision attaches the built
|
|
69
|
+
* The recovery is that the shape decision attaches the built risk shape to
|
|
81
70
|
* every footprint it can judge at all, and that shape carries the risk facts
|
|
82
|
-
* (`sensitiveClasses`, `migrationSpan`) whether or not a risk rule
|
|
83
|
-
* Reading them here surfaces the objection first-hit reporting
|
|
84
|
-
* difference between a wasted session and a redirected one.
|
|
71
|
+
* (`sensitiveClasses`, `migrationSpan`) whether or not a risk rule was the
|
|
72
|
+
* recorded one. Reading them here surfaces the objection first-hit reporting
|
|
73
|
+
* hides — the difference between a wasted session and a redirected one.
|
|
85
74
|
*
|
|
86
75
|
* Pure and total.
|
|
87
76
|
*
|
|
@@ -193,96 +182,87 @@ function resolveDiffCeilings(ceilings) {
|
|
|
193
182
|
}
|
|
194
183
|
|
|
195
184
|
/**
|
|
196
|
-
* Resolve the model's
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
185
|
+
* Resolve the model's ledgered light verdict: the **recorded reason** is the
|
|
186
|
+
* whole of it. A prompt arriving with no reason fails closed to `full` — an
|
|
187
|
+
* unaudited "trust me, it's small" never buys the light path.
|
|
188
|
+
*
|
|
189
|
+
* Story #5344 removed the `--route lite|full` half. It was a second
|
|
190
|
+
* self-declaration on top of the reason, and it carried no information the
|
|
191
|
+
* reason did not: a caller writing a reason is claiming `lite`, and one that
|
|
192
|
+
* meant `full` would not be invoking this gate. What survives is the part that
|
|
193
|
+
* leaves a record a human can read afterwards.
|
|
194
|
+
*
|
|
195
|
+
* Story #5366 removed the `route` field this used to carry beside `recorded`.
|
|
196
|
+
* It was a second spelling of the same boolean — `recorded === false` IS the
|
|
197
|
+
* fail-closed route — and two fields that must agree are a chance for them
|
|
198
|
+
* not to.
|
|
200
199
|
*
|
|
201
200
|
* Pure and total.
|
|
202
201
|
*
|
|
203
|
-
* @param {{
|
|
202
|
+
* @param {{ reason?: unknown }} [verdict]
|
|
204
203
|
* @returns {{
|
|
205
|
-
* route: 'lite'|'full',
|
|
206
204
|
* reason: string|null,
|
|
207
205
|
* recorded: boolean,
|
|
208
206
|
* note: string,
|
|
209
207
|
* }}
|
|
210
208
|
*/
|
|
211
|
-
export function resolveLedgeredVerdict({
|
|
209
|
+
export function resolveLedgeredVerdict({ reason } = {}) {
|
|
212
210
|
const recordedReason = typeof reason === 'string' ? reason.trim() : '';
|
|
213
|
-
if (route !== 'lite') {
|
|
214
|
-
return {
|
|
215
|
-
route: 'full',
|
|
216
|
-
reason: recordedReason || null,
|
|
217
|
-
recorded: recordedReason !== '',
|
|
218
|
-
note: 'model verdict is not lite — standard /mandrel-plan route',
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
211
|
if (recordedReason === '') {
|
|
222
212
|
return {
|
|
223
|
-
route: 'full',
|
|
224
213
|
reason: null,
|
|
225
214
|
recorded: false,
|
|
226
|
-
note: '
|
|
215
|
+
note: 'no recorded reason — fails closed to full (the light verdict must be ledgered)',
|
|
227
216
|
};
|
|
228
217
|
}
|
|
229
218
|
return {
|
|
230
|
-
route: 'lite',
|
|
231
219
|
reason: recordedReason,
|
|
232
220
|
recorded: true,
|
|
233
|
-
note: `
|
|
221
|
+
note: `light verdict (recorded reason): ${recordedReason}`,
|
|
234
222
|
};
|
|
235
223
|
}
|
|
236
224
|
|
|
237
225
|
/**
|
|
238
226
|
* Judge whether an operator prompt's predicted footprint is suitable for the
|
|
239
|
-
* light path.
|
|
240
|
-
*
|
|
241
|
-
* sensitive-path
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
227
|
+
* light path. Two things can refuse, and both are checks the caller cannot
|
|
228
|
+
* satisfy by re-describing its own request: an **un-waivable risk rule** read
|
|
229
|
+
* off the predicted paths (a sensitive-path class, a migration paired with its
|
|
230
|
+
* consumers) and an **un-ledgered verdict** (no recorded reason). Everything
|
|
231
|
+
* else proceeds light and is bounded for real by
|
|
232
|
+
* {@link checkLightDiffBackstop} against the actual diff.
|
|
233
|
+
*
|
|
234
|
+
* Story #5344 removed the declared effort axes — `predictedKinds`,
|
|
235
|
+
* `predictedMagnitude`, `predictedUncertainty` — and the `warnings[]` Story
|
|
236
|
+
* #5313 had demoted them to. A bucket the caller picks about its own request
|
|
237
|
+
* is not a measurement, and once it only warned it was not even a gate.
|
|
238
|
+
* Story #5366 removed `predictedAcceptance` for the same reason from the
|
|
239
|
+
* other end: its zero-check was the only thing that read it, and the
|
|
240
|
+
* `--acceptance` flag that fed it clamped to a floor of one.
|
|
241
|
+
*
|
|
242
|
+
* The result reports `suitable` and nothing that restates it. The `route`
|
|
243
|
+
* field it used to carry was a second spelling of that same boolean, and no
|
|
244
|
+
* caller read it — {@link resolveLightGateOutcome} branches on `suitable`.
|
|
251
245
|
*
|
|
252
246
|
* Pure and total: never throws, never mutates its inputs.
|
|
253
247
|
*
|
|
254
248
|
* @param {{
|
|
255
249
|
* predictedChanges?: unknown,
|
|
256
|
-
*
|
|
257
|
-
* predictedKinds?: unknown,
|
|
258
|
-
* predictedMagnitude?: unknown,
|
|
259
|
-
* predictedUncertainty?: unknown,
|
|
260
|
-
* verdict?: { route?: unknown, reason?: unknown },
|
|
250
|
+
* verdict?: { reason?: unknown },
|
|
261
251
|
* injectedRules?: object,
|
|
262
252
|
* selectSensitivePathClassesFn?: Function,
|
|
263
253
|
* }} [args]
|
|
264
254
|
* @returns {{
|
|
265
255
|
* suitable: boolean,
|
|
266
|
-
* route: 'lite'|'full',
|
|
267
256
|
* shape: ReturnType<typeof deriveStoryShape>,
|
|
268
257
|
* ledger: ReturnType<typeof resolveLedgeredVerdict>,
|
|
269
258
|
* unwaivable: ReturnType<typeof deriveUnwaivableRisk>,
|
|
270
|
-
* ceilings: typeof STORY_SHAPE_CEILINGS,
|
|
271
259
|
* reasons: string[],
|
|
272
|
-
* warnings: string[],
|
|
273
260
|
* }} `unwaivable` names an absolute risk rule the predicted footprint trips
|
|
274
|
-
* even when the recorded `shape.code` is
|
|
261
|
+
* even when the recorded `shape.code` is something else (Story #4875), so
|
|
275
262
|
* the operator learns at prediction time that no re-slicing can help.
|
|
276
|
-
* `warnings` carries the predicted-shape objection when the shape is past a
|
|
277
|
-
* light ceiling (Story #5313): it names the exceeded axis, and it never
|
|
278
|
-
* decides `suitable` — the diff backstop bounds the real change set.
|
|
279
263
|
*/
|
|
280
264
|
export function deriveLightSuitability({
|
|
281
265
|
predictedChanges,
|
|
282
|
-
predictedAcceptance,
|
|
283
|
-
predictedKinds,
|
|
284
|
-
predictedMagnitude,
|
|
285
|
-
predictedUncertainty,
|
|
286
266
|
verdict,
|
|
287
267
|
injectedRules,
|
|
288
268
|
selectSensitivePathClassesFn,
|
|
@@ -290,59 +270,22 @@ export function deriveLightSuitability({
|
|
|
290
270
|
const ledger = resolveLedgeredVerdict(verdict ?? {});
|
|
291
271
|
const shape = deriveStoryShape({
|
|
292
272
|
changes: predictedChanges,
|
|
293
|
-
acceptance: predictedAcceptance,
|
|
294
|
-
kinds: predictedKinds,
|
|
295
|
-
magnitude: predictedMagnitude,
|
|
296
|
-
uncertainty: predictedUncertainty,
|
|
297
273
|
injectedRules,
|
|
298
274
|
selectSensitivePathClassesFn,
|
|
299
275
|
});
|
|
300
276
|
const unwaivable = deriveUnwaivableRisk(shape);
|
|
301
|
-
|
|
302
|
-
// decisive on its own — a sensitive footprint can never be lite — and the
|
|
303
|
-
// ledgered verdict must still be lite; everything the shape ceilings say is
|
|
304
|
-
// carried as a warning for the operator and bounded for real by the backstop.
|
|
305
|
-
const suitable = ledger.route === 'lite' && !unwaivable.present;
|
|
277
|
+
const suitable = ledger.recorded && !unwaivable.present;
|
|
306
278
|
const reasons = [`shape: ${shape.reasons[0]}`];
|
|
307
279
|
if (unwaivable.present) reasons.push(unwaivable.reason);
|
|
308
280
|
reasons.push(`verdict: ${ledger.note}`);
|
|
309
|
-
return {
|
|
310
|
-
suitable,
|
|
311
|
-
route: suitable ? 'lite' : 'full',
|
|
312
|
-
shape,
|
|
313
|
-
ledger,
|
|
314
|
-
unwaivable,
|
|
315
|
-
ceilings: STORY_SHAPE_CEILINGS,
|
|
316
|
-
reasons,
|
|
317
|
-
warnings: shapeWarnings(shape),
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* The predicted-shape objection as a warning (Story #5313): one entry naming
|
|
323
|
-
* the exceeded axis (`shape.code`) and the shape's own reason, or none when
|
|
324
|
-
* the prediction is within every light ceiling.
|
|
325
|
-
*
|
|
326
|
-
* @param {ReturnType<typeof deriveStoryShape>} shape
|
|
327
|
-
* @returns {string[]}
|
|
328
|
-
*/
|
|
329
|
-
function shapeWarnings(shape) {
|
|
330
|
-
if (shape?.route === 'lite') return [];
|
|
331
|
-
const axis = shape?.code ?? 'unknown';
|
|
332
|
-
const reason = shape?.reasons?.[0] ?? 'no reason recorded';
|
|
333
|
-
return [
|
|
334
|
-
`predicted shape exceeds a light ceiling on "${axis}": ${reason} — ` +
|
|
335
|
-
'proceeding light; the diff backstop bounds the actual change set',
|
|
336
|
-
];
|
|
281
|
+
return { suitable, shape, ledger, unwaivable, reasons };
|
|
337
282
|
}
|
|
338
283
|
|
|
339
284
|
/**
|
|
340
285
|
* Resolve what the light gate does with a suitability decision (Story #4740
|
|
341
|
-
* AC-3; Story #5313).
|
|
286
|
+
* AC-3; Story #5313; Story #5344).
|
|
342
287
|
*
|
|
343
|
-
* - suitable → `proceed-light
|
|
344
|
-
* `warnings[]` (possibly empty) so an over-ceiling
|
|
345
|
-
* prediction is stated, never silent.
|
|
288
|
+
* - suitable → `proceed-light`.
|
|
346
289
|
* - not suitable → `escalate-plan` — only an un-ledgered verdict or an
|
|
347
290
|
* un-waivable risk rule gets here, and neither has an
|
|
348
291
|
* answer an operator could give, so there is no
|
|
@@ -351,11 +294,10 @@ function shapeWarnings(shape) {
|
|
|
351
294
|
* Pure and total.
|
|
352
295
|
*
|
|
353
296
|
* @param {{
|
|
354
|
-
* suitability?: { suitable?: boolean, reasons?: string[]
|
|
297
|
+
* suitability?: { suitable?: boolean, reasons?: string[] },
|
|
355
298
|
* }} [args]
|
|
356
299
|
* @returns {{
|
|
357
300
|
* action: 'proceed-light'|'escalate-plan',
|
|
358
|
-
* warnings: string[],
|
|
359
301
|
* reasons: string[],
|
|
360
302
|
* }}
|
|
361
303
|
*/
|
|
@@ -363,29 +305,22 @@ export function resolveLightGateOutcome({ suitability } = {}) {
|
|
|
363
305
|
const reasons = Array.isArray(suitability?.reasons)
|
|
364
306
|
? [...suitability.reasons]
|
|
365
307
|
: [];
|
|
366
|
-
const warnings = Array.isArray(suitability?.warnings)
|
|
367
|
-
? [...suitability.warnings]
|
|
368
|
-
: [];
|
|
369
308
|
|
|
370
309
|
if (suitability?.suitable === true) {
|
|
371
310
|
return {
|
|
372
311
|
action: 'proceed-light',
|
|
373
|
-
warnings,
|
|
374
312
|
reasons: [
|
|
375
313
|
...reasons,
|
|
376
|
-
|
|
377
|
-
? 'ledgered verdict lite and no un-waivable risk — proceeding light with a predicted-shape warning'
|
|
378
|
-
: 'predicted shape and ledgered verdict both lite — proceed light',
|
|
314
|
+
'ledgered verdict recorded and no un-waivable risk rule fired — proceed light; the diff backstop bounds the actual change set',
|
|
379
315
|
],
|
|
380
316
|
};
|
|
381
317
|
}
|
|
382
318
|
|
|
383
319
|
return {
|
|
384
320
|
action: 'escalate-plan',
|
|
385
|
-
warnings,
|
|
386
321
|
reasons: [
|
|
387
322
|
...reasons,
|
|
388
|
-
'the
|
|
323
|
+
'the verdict is un-ledgered or an un-waivable risk rule fired — fails closed to /mandrel-plan (never silently proceeds light)',
|
|
389
324
|
],
|
|
390
325
|
};
|
|
391
326
|
}
|
|
@@ -31,10 +31,6 @@ import {
|
|
|
31
31
|
renderStorySplitRules,
|
|
32
32
|
ticketsModePromptField,
|
|
33
33
|
} from '../templates/decomposer-prompts.js';
|
|
34
|
-
import {
|
|
35
|
-
renderAcceptanceSpecSystemPrompt,
|
|
36
|
-
renderTechSpecSystemPrompt,
|
|
37
|
-
} from '../templates/spec-author-prompts.js';
|
|
38
34
|
import { concurrentMap, FANOUT_CONCURRENCY } from '../util/concurrent-map.js';
|
|
39
35
|
import { buildComplexitySignals } from './complexity-gate.js';
|
|
40
36
|
import { findDependencyCandidates } from './dependency-candidates.js';
|
|
@@ -260,18 +256,17 @@ export const STORIES_TEMPLATE_FILENAME = 'stories.template.json';
|
|
|
260
256
|
|
|
261
257
|
/**
|
|
262
258
|
* Build the template's `changes[]` entries from the envelope's advisory
|
|
263
|
-
* complexity signals (Story #4723). Each seed-predicted path is
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
259
|
+
* complexity signals (Story #4723). Each seed-predicted path is emitted as a
|
|
260
|
+
* **bare path string** — the default authored form since Story #5342. The
|
|
261
|
+
* template used to pre-resolve each one to a creates-vs-refactors assumption
|
|
262
|
+
* against the repo snapshot; persist re-derives it against the base-branch
|
|
263
|
+
* ref, which is the authoritative probe, so the skeleton no longer carries a
|
|
264
|
+
* second answer for the author to keep in sync. Order follows
|
|
267
265
|
* `predictedPaths` (first appearance in the seed). Falls back to the
|
|
268
266
|
* single instructive placeholder entry when the seed predicted no paths.
|
|
269
267
|
*
|
|
270
|
-
* @param {{
|
|
271
|
-
*
|
|
272
|
-
* repoState?: { existingPaths?: string[], missingPaths?: string[] },
|
|
273
|
-
* }|null|undefined} complexitySignals
|
|
274
|
-
* @returns {Array<{ path: string, assumption: string }>}
|
|
268
|
+
* @param {{ predictedPaths?: string[] }|null|undefined} complexitySignals
|
|
269
|
+
* @returns {string[]}
|
|
275
270
|
*/
|
|
276
271
|
function buildTemplateChanges(complexitySignals) {
|
|
277
272
|
const predicted = Array.isArray(complexitySignals?.predictedPaths)
|
|
@@ -279,18 +274,7 @@ function buildTemplateChanges(complexitySignals) {
|
|
|
279
274
|
(p) => typeof p === 'string' && p.length > 0,
|
|
280
275
|
)
|
|
281
276
|
: [];
|
|
282
|
-
|
|
283
|
-
return [{ path: 'path/to/file.ext', assumption: 'refactors-existing' }];
|
|
284
|
-
}
|
|
285
|
-
const existing = new Set(
|
|
286
|
-
Array.isArray(complexitySignals?.repoState?.existingPaths)
|
|
287
|
-
? complexitySignals.repoState.existingPaths
|
|
288
|
-
: [],
|
|
289
|
-
);
|
|
290
|
-
return predicted.map((path) => ({
|
|
291
|
-
path,
|
|
292
|
-
assumption: existing.has(path) ? 'refactors-existing' : 'creates',
|
|
293
|
-
}));
|
|
277
|
+
return predicted.length === 0 ? ['path/to/file.ext'] : predicted;
|
|
294
278
|
}
|
|
295
279
|
|
|
296
280
|
/**
|
|
@@ -309,10 +293,10 @@ function buildTemplateChanges(complexitySignals) {
|
|
|
309
293
|
*
|
|
310
294
|
* Correct-by-construction skeleton (Story #4723): when the envelope's
|
|
311
295
|
* `complexitySignals` predicted a footprint the `changes[]` entries arrive
|
|
312
|
-
*
|
|
313
|
-
* faithfully-filled skeleton passes the persist ticket validators without a
|
|
314
|
-
* mechanical round-trip.
|
|
315
|
-
*
|
|
296
|
+
* already filled in, as the bare paths Story #5342 made the default form —
|
|
297
|
+
* a faithfully-filled skeleton passes the persist ticket validators without a
|
|
298
|
+
* mechanical round-trip. Persist derives each assumption by probing the base
|
|
299
|
+
* branch ref and reports the derivation.
|
|
316
300
|
*
|
|
317
301
|
* Pure and deterministic; the output is valid JSON (parseable as-is), with
|
|
318
302
|
* instructive placeholder values rather than comments.
|
|
@@ -329,6 +313,17 @@ export function renderStoriesTemplate({ complexitySignals = null } = {}) {
|
|
|
329
313
|
title: 'Fill: short descriptive title',
|
|
330
314
|
body: {
|
|
331
315
|
goal: 'Fill: one sentence stating why this Story exists.',
|
|
316
|
+
// A filled, multi-checkpoint example rather than a placeholder
|
|
317
|
+
// (Story #5332): `## Slicing` is how one cohesive sweep stays one
|
|
318
|
+
// Story, so the skeleton has to show an author what a checkpoint
|
|
319
|
+
// list looks like. Each line is a stage of the work — a commit
|
|
320
|
+
// boundary inside one session — never a restatement of an
|
|
321
|
+
// acceptance item, which states what is true once the Story lands.
|
|
322
|
+
slicing:
|
|
323
|
+
'1. Re-anchor the shared constant and its consumers.\n' +
|
|
324
|
+
'2. Move the gate ahead of the first write and arm the refusal.\n' +
|
|
325
|
+
'3. Delete the superseded module, its test and its flag.\n' +
|
|
326
|
+
'4. Regenerate the affected baselines; run the full gate chain.',
|
|
332
327
|
spec:
|
|
333
328
|
'Optional — contract and invariants only: interfaces, status ' +
|
|
334
329
|
'codes, security invariants, and load-bearing constraints with ' +
|
|
@@ -340,7 +335,7 @@ export function renderStoriesTemplate({ complexitySignals = null } = {}) {
|
|
|
340
335
|
non_goals: [],
|
|
341
336
|
},
|
|
342
337
|
acceptance: [
|
|
343
|
-
'Fill: an outcome a PR reviewer can confirm from the diff and the verify output
|
|
338
|
+
'Fill: an outcome a PR reviewer can confirm from the diff and the verify output — as many as the capability has, no target and no ceiling',
|
|
344
339
|
],
|
|
345
340
|
verify: [
|
|
346
341
|
'Fill: exact command or test path — the mechanical check the acceptance item rests on',
|
|
@@ -367,25 +362,23 @@ function countEnumeratedItems(text) {
|
|
|
367
362
|
}
|
|
368
363
|
|
|
369
364
|
/**
|
|
370
|
-
* Delta-shaped change-request verbs —
|
|
371
|
-
*
|
|
372
|
-
* footprint stays inside Story width.
|
|
365
|
+
* Delta-shaped change-request verbs — a change request naming one of these
|
|
366
|
+
* stays a Story by default when the footprint stays inside Story width.
|
|
373
367
|
*/
|
|
374
368
|
const DELTA_VERB_RE =
|
|
375
369
|
/\b(fix(?:es)?|tweak(?:s)?|extend(?:s)?|update(?:s)?|adjust(?:s)?|rename(?:s)?|correct(?:s)?|patch(?:es)?|bug|regression|flaky)\b/i;
|
|
376
370
|
|
|
377
371
|
/**
|
|
378
|
-
* Deterministic, CLI-applied scope
|
|
379
|
-
* (#4496 fix 6). Embedding
|
|
380
|
-
*
|
|
381
|
-
* from the headless path; the attended path keeps the skill-based judgment.
|
|
372
|
+
* Deterministic, CLI-applied scope signal over a raw `--seed` text
|
|
373
|
+
* (#4496 fix 6). Embedding it in the `--seed` envelope keeps the headless
|
|
374
|
+
* path from needing a judgment pass of its own.
|
|
382
375
|
*
|
|
383
|
-
* The heuristics anchor to the
|
|
376
|
+
* The heuristics anchor to the granularity SSOT —
|
|
384
377
|
* `DELIVERABLE_GRANULARITY_GUIDANCE` in `ticket-validator-sizing.js` (one
|
|
385
378
|
* Story = one coherent capability slice; multiple independent capabilities =
|
|
386
|
-
* an Epic) — and to the
|
|
387
|
-
*
|
|
388
|
-
*
|
|
379
|
+
* an Epic) — and to the change-request delta rubric above. The verdict is
|
|
380
|
+
* **advisory**: being wrong in the `epic` direction is cheap, and
|
|
381
|
+
* `borderline` is a first-class output, not a forced call.
|
|
389
382
|
*
|
|
390
383
|
* @param {{ seedText?: string }} args
|
|
391
384
|
* @returns {{ verdict: 'epic'|'story'|'borderline', reasons: string[], advisory: true, appliedBy: 'cli' }}
|
|
@@ -634,12 +627,15 @@ function withAdvisorySignals(complexitySignals, { config, cwd } = {}) {
|
|
|
634
627
|
|
|
635
628
|
/**
|
|
636
629
|
* Render the authoring system prompts the collapsed pipeline's single
|
|
637
|
-
* authoring pass consumes
|
|
638
|
-
* `lib/templates/
|
|
639
|
-
*
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
*
|
|
630
|
+
* authoring pass consumes: the N=1 core from
|
|
631
|
+
* `lib/templates/decomposer-prompts.js`, with the schedule rules and the
|
|
632
|
+
* collision refusal a planner reads only when the default-single split policy
|
|
633
|
+
* clears carried separately as `storySplitRules` (Story #5312).
|
|
634
|
+
*
|
|
635
|
+
* Story #5332 deleted the `spec` / `acceptance` fields with the module that
|
|
636
|
+
* rendered them: nothing read either, and both contradicted the current
|
|
637
|
+
* contract — one asserting Spec budgets that no longer exist, the other
|
|
638
|
+
* demanding the verify tier suffixes tickets mode now strips.
|
|
643
639
|
*
|
|
644
640
|
* `storyTicketsRules` is the one mode-conditional field (Story #5323): it
|
|
645
641
|
* only means anything when the seed is an existing ticket, and an envelope
|
|
@@ -647,12 +643,10 @@ function withAdvisorySignals(complexitySignals, { config, cwd } = {}) {
|
|
|
647
643
|
* ticket that a `--seed` run does not have.
|
|
648
644
|
*
|
|
649
645
|
* @param {{ mode?: string }} [args]
|
|
650
|
-
* @returns {{
|
|
646
|
+
* @returns {{ story: string, storySplitRules: string, storyTicketsRules?: string }}
|
|
651
647
|
*/
|
|
652
648
|
export function buildSystemPrompts({ mode } = {}) {
|
|
653
649
|
return {
|
|
654
|
-
spec: renderTechSpecSystemPrompt(),
|
|
655
|
-
acceptance: renderAcceptanceSpecSystemPrompt(),
|
|
656
650
|
story: renderStoryAuthorCore(),
|
|
657
651
|
storySplitRules: renderStorySplitRules(),
|
|
658
652
|
...ticketsModePromptField(mode),
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
* @module lib/orchestration/plan-persist/changes-repair
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
|
+
import { matchBarePathToken } from '../../story-body/body-format-lints.js';
|
|
39
40
|
import { FILE_ASSUMPTION_VALUES } from '../file-assumption-enum.js';
|
|
40
41
|
|
|
41
42
|
/** The `## Changes` heading (either level the parser accepts). */
|
|
@@ -50,11 +51,6 @@ const TRAILING_PARENTHETICAL_RE = /\s*\([^)]*\)\s*$/;
|
|
|
50
51
|
/** The humanized canonical bullet: `` `path` — assumption ``. */
|
|
51
52
|
const HUMANIZED_RE = /^`([^`]+)`\s+—\s+(\S+)$/;
|
|
52
53
|
|
|
53
|
-
// A token that looks like a file path / glob / module id: it carries a `/` or a
|
|
54
|
-
// `.`-separated segment. Deliberately loose — a false positive only produces a
|
|
55
|
-
// `{ path, assumption }` entry the base-branch probes then judge.
|
|
56
|
-
const PATH_LIKE_RE = /^[\w@*-]*[/.][\w@./*-]+$/;
|
|
57
|
-
|
|
58
54
|
/**
|
|
59
55
|
* Strip a trailing parenthetical from a path token, reporting whether one
|
|
60
56
|
* was present.
|
|
@@ -73,6 +69,12 @@ function stripParenthetical(raw) {
|
|
|
73
69
|
* backticks, strip a trailing parenthetical. Returns `null` when nothing
|
|
74
70
|
* path-shaped survives.
|
|
75
71
|
*
|
|
72
|
+
* What counts as path-shaped is `matchBarePathToken` — the same grammar the
|
|
73
|
+
* story-body parser admits a bare bullet under, imported rather than
|
|
74
|
+
* restated (Story #5361). The repair pass scoring a narrower class than the
|
|
75
|
+
* parser is what let a route-segment path be repaired on one surface and
|
|
76
|
+
* refused on the other.
|
|
77
|
+
*
|
|
76
78
|
* @param {string} raw
|
|
77
79
|
* @returns {string|null}
|
|
78
80
|
*/
|
|
@@ -87,7 +89,7 @@ function salvagePath(raw) {
|
|
|
87
89
|
.replace(/[`'"]+$/, '')
|
|
88
90
|
.trim();
|
|
89
91
|
s = stripParenthetical(s).path;
|
|
90
|
-
return
|
|
92
|
+
return matchBarePathToken(s);
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
/**
|