mandrel 2.55.0 → 2.57.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/agents/plan-critic.md +13 -18
- package/.agents/agents/story-worker.md +25 -34
- package/.agents/docs/agentrc-reference.json +4 -30
- package/.agents/docs/configuration.md +11 -28
- package/.agents/docs/execution-reference.md +5 -5
- package/.agents/docs/quality-gates.md +8 -7
- package/.agents/instructions.md +9 -10
- package/.agents/rules/ci-remediation.md +39 -21
- package/.agents/schemas/agentrc.schema.json +28 -185
- package/.agents/schemas/story-deliver-terminal.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +107 -17
- package/.agents/scripts/audit-to-stories.js +222 -75
- package/.agents/scripts/ceremony-derive.js +191 -0
- package/.agents/scripts/check-context-budget.js +28 -33
- package/.agents/scripts/check-cyclomatic.js +4 -3
- package/.agents/scripts/deliver-light.js +31 -94
- package/.agents/scripts/file-ci-gap.js +306 -0
- package/.agents/scripts/lib/audit-suite/checklist-threading.js +15 -2
- package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +25 -1
- package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +40 -52
- package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +5 -1
- package/.agents/scripts/lib/audit-to-stories/issue-corpus.js +162 -0
- package/.agents/scripts/lib/audit-to-stories/issues-file.js +121 -0
- package/.agents/scripts/lib/audit-to-stories/ledger-commit.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/ledger-record.js +126 -0
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +11 -0
- package/.agents/scripts/lib/baselines/coverage-updater-cli.js +110 -0
- package/.agents/scripts/lib/baselines/crap-preview-scan.js +25 -0
- package/.agents/scripts/lib/baselines/crap-updater-cli.js +223 -0
- package/.agents/scripts/lib/bdd-scenario-budget.js +21 -3
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -1
- package/.agents/scripts/lib/close-validation/gates.js +52 -1
- package/.agents/scripts/lib/config/acceptance-eval.js +25 -57
- package/.agents/scripts/lib/config/delivery-routing.js +7 -33
- package/.agents/scripts/lib/config/explain.js +0 -19
- package/.agents/scripts/lib/config/limits.js +18 -78
- package/.agents/scripts/lib/config/quality.js +6 -3
- package/.agents/scripts/lib/config/runners.js +3 -2
- package/.agents/scripts/lib/config-settings-schema-delivery.js +15 -68
- package/.agents/scripts/lib/config-settings-schema-quality.js +0 -14
- package/.agents/scripts/lib/config-settings-schema.js +49 -143
- package/.agents/scripts/lib/crap-engine.js +35 -4
- package/.agents/scripts/lib/crap-utils.js +17 -1
- package/.agents/scripts/lib/cyclomatic-ceiling.js +19 -7
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +53 -13
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +71 -25
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +18 -25
- package/.agents/scripts/lib/{audit-to-stories/ledger.js → findings/audit-ledger.js} +131 -24
- package/.agents/scripts/lib/findings/route-finding.js +38 -0
- package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
- package/.agents/scripts/lib/github/framework-repo.js +148 -2
- package/.agents/scripts/lib/label-constants.js +6 -1
- package/.agents/scripts/lib/observability/runtime-friction.js +1 -1
- package/.agents/scripts/lib/observability/source-classifier.js +2 -0
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +5 -4
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +19 -73
- package/.agents/scripts/lib/orchestration/ci-gap-intake.js +605 -0
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +13 -8
- package/.agents/scripts/lib/orchestration/complexity-gate.js +46 -212
- package/.agents/scripts/lib/orchestration/file-assumptions.js +32 -17
- package/.agents/scripts/lib/orchestration/light-escalation.js +3 -3
- package/.agents/scripts/lib/orchestration/light-suitability.js +66 -233
- package/.agents/scripts/lib/orchestration/plan-context.js +181 -387
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +42 -153
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +14 -70
- package/.agents/scripts/lib/orchestration/plan-persist/audit-provenance.js +197 -0
- package/.agents/scripts/lib/orchestration/plan-persist/changes-repair.js +300 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +131 -168
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +133 -299
- package/.agents/scripts/lib/orchestration/plan-persist/soft-findings.js +55 -0
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +16 -65
- package/.agents/scripts/lib/orchestration/plan-persist/wave-serialisation.js +22 -35
- package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +30 -139
- package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +61 -223
- package/.agents/scripts/lib/orchestration/run-epilogue.js +4 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +5 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pre-gate-steps.js +46 -16
- package/.agents/scripts/lib/orchestration/story-close/context-budget-writeback.js +213 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +32 -20
- package/.agents/scripts/lib/orchestration/task-body-validator.js +10 -63
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +33 -539
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +21 -414
- package/.agents/scripts/lib/orchestration/ticket-validator.js +54 -118
- package/.agents/scripts/lib/orchestration/verify-credit.js +69 -24
- package/.agents/scripts/lib/story-body/body-format-lints.js +15 -85
- package/.agents/scripts/lib/story-body/story-body.js +17 -237
- package/.agents/scripts/lib/templates/decomposer-prompts.js +84 -121
- package/.agents/scripts/lib/test-isolate/cli-options.js +93 -0
- package/.agents/scripts/lib/test-isolate/progress-log.js +45 -0
- package/.agents/scripts/lib/test-isolate/render-report.js +97 -0
- package/.agents/scripts/lib/test-isolate/run-isolate.js +87 -0
- package/.agents/scripts/lib/test-run-credit.js +266 -0
- package/.agents/scripts/lib/wave-runner/footprint.js +48 -358
- package/.agents/scripts/lib/wave-runner/ready-set.js +6 -5
- package/.agents/scripts/lib/workers/crap-worker.js +32 -41
- package/.agents/scripts/plan-context.js +7 -9
- package/.agents/scripts/plan-critics.js +28 -54
- package/.agents/scripts/plan-persist.js +25 -68
- package/.agents/scripts/pr-watch-with-update.js +3 -2
- package/.agents/scripts/quality-preview.js +51 -0
- package/.agents/scripts/run-tests.js +12 -0
- package/.agents/scripts/stories-wave-tick.js +23 -45
- package/.agents/scripts/test-isolate.js +13 -180
- package/.agents/scripts/update-coverage-baseline.js +25 -70
- package/.agents/scripts/update-crap-baseline.js +19 -123
- package/.agents/skills/core/scope-triage/SKILL.md +3 -3
- package/.agents/workflows/audit-clean-code.md +4 -3
- package/.agents/workflows/audit-to-stories.md +63 -27
- package/.agents/workflows/helpers/acceptance-self-eval.md +41 -41
- package/.agents/workflows/helpers/code-quality-guardrails.md +4 -4
- package/.agents/workflows/helpers/code-review.md +2 -3
- package/.agents/workflows/helpers/deliver-digest.md +41 -57
- package/.agents/workflows/helpers/deliver-light.md +40 -105
- package/.agents/workflows/helpers/deliver-reference.md +1 -1
- package/.agents/workflows/helpers/deliver-story-reference.md +56 -62
- package/.agents/workflows/helpers/deliver-story.md +9 -13
- package/.agents/workflows/helpers/plan-reference.md +132 -196
- package/.agents/workflows/mandrel-plan.md +28 -41
- package/.agents/workflows/memory-consolidate.md +9 -13
- package/docs/CHANGELOG.md +33 -0
- package/lib/migrations/index.js +4 -0
- package/lib/migrations/steps/2.57.0-retire-delivery-limit-knobs.js +45 -0
- package/lib/migrations/steps/2.57.0-retire-planning-limit-knobs.js +59 -0
- package/package.json +1 -1
- package/.agents/scripts/lib/framework-version.js +0 -39
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +0 -223
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +0 -97
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +0 -26
- package/.agents/scripts/lib/orchestration/spec-budget.js +0 -89
- package/.agents/scripts/lib/orchestration/spec-spill.js +0 -74
- package/.agents/scripts/lib/orchestration/verify-tier-repair.js +0 -107
|
@@ -27,13 +27,15 @@
|
|
|
27
27
|
* and epic-scope span — never artifact counts (Story #4764), so this gate
|
|
28
28
|
* is deliberately **coarse**: it rejects clearly-epic work, and invariant
|
|
29
29
|
* 3 below does the real enforcement against ground truth.
|
|
30
|
-
* 2. **
|
|
31
|
-
* resolveLightGateOutcome}).** An over-ceiling
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
30
|
+
* 2. **The predicted shape is a warning, not a gate ({@link
|
|
31
|
+
* resolveLightGateOutcome}, Story #5313).** An over-ceiling prediction
|
|
32
|
+
* proceeds light with a `warnings[]` entry naming the exceeded axis — the
|
|
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.
|
|
37
39
|
* 3. **Diff-derived backstop ({@link checkLightDiffBackstop}).** After
|
|
38
40
|
* implementation the **actual** change set is re-checked with
|
|
39
41
|
* {@link module:lib/orchestration/review-depth.deriveChangeLevel} plus the
|
|
@@ -61,32 +63,10 @@ import {
|
|
|
61
63
|
} from './complexity-gate.js';
|
|
62
64
|
import { deriveChangeLevel } from './review-depth.js';
|
|
63
65
|
|
|
64
|
-
/**
|
|
65
|
-
* The shape objections an attended operator may waive (Story #4815) — an
|
|
66
|
-
* **allowlist**, deliberately, so a rule added to
|
|
67
|
-
* {@link module:lib/orchestration/complexity-gate.SHAPE_CODES} later is
|
|
68
|
-
* non-negotiable until someone decides otherwise here. A denylist would make
|
|
69
|
-
* every new rule silently overridable.
|
|
70
|
-
*
|
|
71
|
-
* These four are the *size predictions*: coarse by design (§ Scope by effort),
|
|
72
|
-
* and already bounded for real by {@link checkLightDiffBackstop} against the
|
|
73
|
-
* landed diff. Everything absent is absent on purpose — `migration-span` and
|
|
74
|
-
* `sensitive-path` are **risk**, not size, and the unknown-footprint codes
|
|
75
|
-
* describe a shape that was never judged, so there is no false positive to
|
|
76
|
-
* appeal.
|
|
77
|
-
*/
|
|
78
|
-
export const OVERRIDABLE_SHAPE_CODES = Object.freeze([
|
|
79
|
-
SHAPE_CODES.CHANGE_KINDS,
|
|
80
|
-
SHAPE_CODES.MAGNITUDE,
|
|
81
|
-
SHAPE_CODES.UNCERTAINTY,
|
|
82
|
-
SHAPE_CODES.DEPLOYABLE_SPAN,
|
|
83
|
-
]);
|
|
84
|
-
|
|
85
66
|
/**
|
|
86
67
|
* Detect the **un-waivable** risk rules a predicted footprint trips —
|
|
87
|
-
* `sensitive-path` and `migration-span
|
|
88
|
-
*
|
|
89
|
-
* decision happened to record** (Story #4875).
|
|
68
|
+
* `sensitive-path` and `migration-span` — **independent of which rule the
|
|
69
|
+
* shape decision happened to record** (Story #4875).
|
|
90
70
|
*
|
|
91
71
|
* No re-slicing, shrinking, or operator answer satisfies one: a footprint
|
|
92
72
|
* intersecting a sensitive-path class routes `full` however small the change,
|
|
@@ -129,7 +109,7 @@ function deriveUnwaivableRisk(decision) {
|
|
|
129
109
|
reason:
|
|
130
110
|
`un-waivable: the predicted footprint intersects sensitive-path ` +
|
|
131
111
|
`class(es) ${classes.join(', ')} — this is risk, not size, so no ` +
|
|
132
|
-
`re-slicing
|
|
112
|
+
`re-slicing or shrinking satisfies it and the ` +
|
|
133
113
|
`diff backstop would refuse the same footprint after the work is ` +
|
|
134
114
|
`finished; take this to /mandrel-plan now`,
|
|
135
115
|
};
|
|
@@ -141,8 +121,8 @@ function deriveUnwaivableRisk(decision) {
|
|
|
141
121
|
classes: [],
|
|
142
122
|
reason:
|
|
143
123
|
`un-waivable: the predicted footprint pairs a migration with its ` +
|
|
144
|
-
`consumers — this is risk, not size, so no re-slicing
|
|
145
|
-
`
|
|
124
|
+
`consumers — this is risk, not size, so no re-slicing ` +
|
|
125
|
+
`satisfies it; take this to /mandrel-plan now`,
|
|
146
126
|
};
|
|
147
127
|
}
|
|
148
128
|
return { present: false, code: null, classes: [], reason: null };
|
|
@@ -213,10 +193,8 @@ function resolveDiffCeilings(ceilings) {
|
|
|
213
193
|
}
|
|
214
194
|
|
|
215
195
|
/**
|
|
216
|
-
* Resolve the model's trivial-vs-standard verdict, held to
|
|
217
|
-
* contract
|
|
218
|
-
* ({@link module:lib/orchestration/complexity-gate.resolvePlannerRouteVerdict}):
|
|
219
|
-
* a `lite` route counts **only** with a non-empty recorded reason. A lite claim
|
|
196
|
+
* Resolve the model's trivial-vs-standard verdict, held to a ledgering
|
|
197
|
+
* contract: a `lite` route counts **only** with a non-empty recorded reason. A lite claim
|
|
220
198
|
* without a recorded reason, or any non-`lite` route, fails closed to `full` —
|
|
221
199
|
* an unaudited "trust me, it's small" never buys the light path.
|
|
222
200
|
*
|
|
@@ -291,9 +269,13 @@ export function resolveLedgeredVerdict({ route, reason } = {}) {
|
|
|
291
269
|
* unwaivable: ReturnType<typeof deriveUnwaivableRisk>,
|
|
292
270
|
* ceilings: typeof STORY_SHAPE_CEILINGS,
|
|
293
271
|
* reasons: string[],
|
|
272
|
+
* warnings: string[],
|
|
294
273
|
* }} `unwaivable` names an absolute risk rule the predicted footprint trips
|
|
295
274
|
* even when the recorded `shape.code` is a size prediction (Story #4875), so
|
|
296
|
-
* the operator learns at prediction time that no
|
|
275
|
+
* 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.
|
|
297
279
|
*/
|
|
298
280
|
export function deriveLightSuitability({
|
|
299
281
|
predictedChanges,
|
|
@@ -316,12 +298,11 @@ export function deriveLightSuitability({
|
|
|
316
298
|
selectSensitivePathClassesFn,
|
|
317
299
|
});
|
|
318
300
|
const unwaivable = deriveUnwaivableRisk(shape);
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
const suitable =
|
|
324
|
-
shape.route === 'lite' && ledger.route === 'lite' && !unwaivable.present;
|
|
301
|
+
// Story #5313: the predicted shape no longer decides. A tripped risk rule is
|
|
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;
|
|
325
306
|
const reasons = [`shape: ${shape.reasons[0]}`];
|
|
326
307
|
if (unwaivable.present) reasons.push(unwaivable.reason);
|
|
327
308
|
reasons.push(`verdict: ${ledger.note}`);
|
|
@@ -333,193 +314,78 @@ export function deriveLightSuitability({
|
|
|
333
314
|
unwaivable,
|
|
334
315
|
ceilings: STORY_SHAPE_CEILINGS,
|
|
335
316
|
reasons,
|
|
317
|
+
warnings: shapeWarnings(shape),
|
|
336
318
|
};
|
|
337
319
|
}
|
|
338
320
|
|
|
339
321
|
/**
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
* so the only way past a coarse prediction was to re-shape the declaration
|
|
344
|
-
* until the gate stopped objecting — precisely the under-declaring the coarse
|
|
345
|
-
* design anticipates.
|
|
346
|
-
*
|
|
347
|
-
* Applying it takes **all** of:
|
|
348
|
-
*
|
|
349
|
-
* 1. **The gate actually objected.** An override cannot pre-authorize a
|
|
350
|
-
* scope nothing rejected.
|
|
351
|
-
* 2. **The run is attended.** `--yes` means nobody is at the keyboard, so
|
|
352
|
-
* there is no operator whose answer this could be (§ Escalation is
|
|
353
|
-
* terminal). Checked here as well as at the CLI, so the pure core carries
|
|
354
|
-
* the guarantee rather than the shell.
|
|
355
|
-
* 3. **The objection is a size prediction** — a code in
|
|
356
|
-
* {@link OVERRIDABLE_SHAPE_CODES}, **and** the footprint trips no
|
|
357
|
-
* un-waivable risk rule ({@link deriveUnwaivableRisk}, Story #4875).
|
|
358
|
-
* Sensitivity and migration
|
|
359
|
-
* span are risk and stay absolute however small the change — including
|
|
360
|
-
* when an earlier ceiling rule is the one the shape recorded.
|
|
361
|
-
* 4. **The ledgered verdict is already `lite`.** The override substitutes for
|
|
362
|
-
* the *shape* half of the conjunction only; an unaudited "trust me, it's
|
|
363
|
-
* small" buys nothing it did not buy before.
|
|
364
|
-
*
|
|
365
|
-
* A refusal is reported, never silent — an operator who typed the flag must
|
|
366
|
-
* learn why it did not take. Pure and total.
|
|
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.
|
|
367
325
|
*
|
|
368
|
-
* @param {
|
|
369
|
-
*
|
|
370
|
-
* yes?: boolean,
|
|
371
|
-
* operatorOverride?: unknown,
|
|
372
|
-
* }} [args] `operatorOverride` is the operator's recorded reason; blank or
|
|
373
|
-
* absent means no override was requested.
|
|
374
|
-
* @returns {{
|
|
375
|
-
* applied: boolean,
|
|
376
|
-
* record: { recordedReason: string, overriddenCode: string, overriddenReason: string }|null,
|
|
377
|
-
* note: string|null,
|
|
378
|
-
* }}
|
|
326
|
+
* @param {ReturnType<typeof deriveStoryShape>} shape
|
|
327
|
+
* @returns {string[]}
|
|
379
328
|
*/
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (recordedReason === '') return refuse(null);
|
|
390
|
-
if (suitability?.suitable === true) {
|
|
391
|
-
return refuse(
|
|
392
|
-
'operator override ignored — the gate raised no objection to override',
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
if (yes === true) {
|
|
396
|
-
return refuse(
|
|
397
|
-
'operator override refused — it is attended-only, and --yes means nobody is at the keyboard; over-scope still fails closed to /mandrel-plan',
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const code = suitability?.shape?.code ?? null;
|
|
402
|
-
// Checked BEFORE the overridable-code test on purpose (Story #4875): when a
|
|
403
|
-
// footprint trips both a ceiling rule and a risk rule, the shape records the
|
|
404
|
-
// ceiling rule, which IS overridable — so testing the recorded code alone
|
|
405
|
-
// would apply the override and send un-landable work to the backstop.
|
|
406
|
-
const unwaivable = suitability?.unwaivable;
|
|
407
|
-
if (unwaivable?.present === true) {
|
|
408
|
-
return refuse(
|
|
409
|
-
`operator override refused — the predicted footprint also trips the ` +
|
|
410
|
-
`un-waivable "${unwaivable.code}" rule${
|
|
411
|
-
unwaivable.classes.length > 0
|
|
412
|
-
? ` (${unwaivable.classes.join(', ')})`
|
|
413
|
-
: ''
|
|
414
|
-
}; waiving the size prediction cannot make this land light, and the ` +
|
|
415
|
-
`diff backstop would refuse the finished work. Escalate to /mandrel-plan.`,
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
if (!OVERRIDABLE_SHAPE_CODES.includes(code)) {
|
|
419
|
-
return refuse(
|
|
420
|
-
`operator override refused — "${code ?? 'unknown'}" is not an overridable size prediction (overridable: ${OVERRIDABLE_SHAPE_CODES.join(', ')}); risk rules and unknown footprints are non-negotiable`,
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
|
-
if (suitability?.ledger?.route !== 'lite') {
|
|
424
|
-
return refuse(
|
|
425
|
-
'operator override refused — it substitutes for the predicted shape only; the model verdict must still be a ledgered lite with a recorded reason',
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
return {
|
|
430
|
-
applied: true,
|
|
431
|
-
record: {
|
|
432
|
-
recordedReason,
|
|
433
|
-
overriddenCode: code,
|
|
434
|
-
overriddenReason: suitability?.shape?.reasons?.[0] ?? '',
|
|
435
|
-
},
|
|
436
|
-
note: `operator override applied — proceeding light despite "${code}"; recorded reason: ${recordedReason}. The diff backstop still bounds the actual change set.`,
|
|
437
|
-
};
|
|
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
|
+
];
|
|
438
337
|
}
|
|
439
338
|
|
|
440
339
|
/**
|
|
441
340
|
* Resolve what the light gate does with a suitability decision (Story #4740
|
|
442
|
-
* AC-3).
|
|
443
|
-
* unless the run is unattended (`--yes`), where it fails closed to recommending
|
|
444
|
-
* `/mandrel-plan` rather than silently proceeding light.
|
|
445
|
-
*
|
|
446
|
-
* - suitable → `proceed-light`
|
|
447
|
-
* - over-scope + attended (`yes:false`) → `ask-operator` (escalate | proceed)
|
|
448
|
-
* - over-scope + attended + an applied operator override (Story #4815)
|
|
449
|
-
* → `proceed-light`, carrying the
|
|
450
|
-
* decision in `override`
|
|
451
|
-
* - over-scope + unattended (`yes:true`) → `escalate-plan`
|
|
341
|
+
* AC-3; Story #5313).
|
|
452
342
|
*
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
*
|
|
343
|
+
* - suitable → `proceed-light`, carrying the predicted-shape
|
|
344
|
+
* `warnings[]` (possibly empty) so an over-ceiling
|
|
345
|
+
* prediction is stated, never silent.
|
|
346
|
+
* - not suitable → `escalate-plan` — only an un-ledgered verdict or an
|
|
347
|
+
* un-waivable risk rule gets here, and neither has an
|
|
348
|
+
* answer an operator could give, so there is no
|
|
349
|
+
* attended/unattended split any more.
|
|
456
350
|
*
|
|
457
351
|
* Pure and total.
|
|
458
352
|
*
|
|
459
353
|
* @param {{
|
|
460
|
-
* suitability?: { suitable?: boolean, reasons?: string[] },
|
|
461
|
-
* yes?: boolean,
|
|
462
|
-
* operatorOverride?: unknown,
|
|
354
|
+
* suitability?: { suitable?: boolean, reasons?: string[], warnings?: string[] },
|
|
463
355
|
* }} [args]
|
|
464
356
|
* @returns {{
|
|
465
|
-
* action: 'proceed-light'|'
|
|
466
|
-
*
|
|
467
|
-
* override?: object,
|
|
357
|
+
* action: 'proceed-light'|'escalate-plan',
|
|
358
|
+
* warnings: string[],
|
|
468
359
|
* reasons: string[],
|
|
469
360
|
* }}
|
|
470
361
|
*/
|
|
471
|
-
export function resolveLightGateOutcome({
|
|
472
|
-
suitability,
|
|
473
|
-
yes = false,
|
|
474
|
-
operatorOverride,
|
|
475
|
-
} = {}) {
|
|
476
|
-
const override = resolveOperatorOverride({
|
|
477
|
-
suitability,
|
|
478
|
-
yes,
|
|
479
|
-
operatorOverride,
|
|
480
|
-
});
|
|
362
|
+
export function resolveLightGateOutcome({ suitability } = {}) {
|
|
481
363
|
const reasons = Array.isArray(suitability?.reasons)
|
|
482
364
|
? [...suitability.reasons]
|
|
483
365
|
: [];
|
|
484
|
-
|
|
366
|
+
const warnings = Array.isArray(suitability?.warnings)
|
|
367
|
+
? [...suitability.warnings]
|
|
368
|
+
: [];
|
|
485
369
|
|
|
486
370
|
if (suitability?.suitable === true) {
|
|
487
371
|
return {
|
|
488
372
|
action: 'proceed-light',
|
|
373
|
+
warnings,
|
|
489
374
|
reasons: [
|
|
490
375
|
...reasons,
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
if (yes === true) {
|
|
497
|
-
return {
|
|
498
|
-
action: 'escalate-plan',
|
|
499
|
-
reasons: [
|
|
500
|
-
...reasons,
|
|
501
|
-
'--yes on over-scope fails closed to /mandrel-plan (never silently proceeds light)',
|
|
502
|
-
],
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
if (override.applied) {
|
|
507
|
-
return {
|
|
508
|
-
action: 'proceed-light',
|
|
509
|
-
override: override.record,
|
|
510
|
-
reasons: [
|
|
511
|
-
...reasons,
|
|
512
|
-
'predicted scope exceeded a light ceiling and the operator answered proceed-light — proceeding on the recorded override',
|
|
376
|
+
warnings.length > 0
|
|
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',
|
|
513
379
|
],
|
|
514
380
|
};
|
|
515
381
|
}
|
|
516
382
|
|
|
517
383
|
return {
|
|
518
|
-
action: '
|
|
519
|
-
|
|
384
|
+
action: 'escalate-plan',
|
|
385
|
+
warnings,
|
|
520
386
|
reasons: [
|
|
521
387
|
...reasons,
|
|
522
|
-
'
|
|
388
|
+
'the ledgered verdict is not lite or an un-waivable risk rule fired — fails closed to /mandrel-plan (never silently proceeds light)',
|
|
523
389
|
],
|
|
524
390
|
};
|
|
525
391
|
}
|
|
@@ -852,53 +718,22 @@ function toReceiptChanges(changedFiles) {
|
|
|
852
718
|
return entries;
|
|
853
719
|
}
|
|
854
720
|
|
|
855
|
-
/**
|
|
856
|
-
* Render an applied operator override as an audit paragraph for the receipt
|
|
857
|
-
* body (Story #4815). An override that leaves no trace on the ticket is an
|
|
858
|
-
* invisible decision: the whole point of routing it through the receipt is
|
|
859
|
-
* that a later reader can see the gate objected, on what grounds, and who
|
|
860
|
-
* decided to proceed anyway.
|
|
861
|
-
*
|
|
862
|
-
* @param {unknown} override The `record` from {@link resolveOperatorOverride}.
|
|
863
|
-
* @returns {string} A leading-space-prefixed sentence, or `''` when absent.
|
|
864
|
-
*/
|
|
865
|
-
function renderOverrideNote(override) {
|
|
866
|
-
if (!override || typeof override !== 'object') return '';
|
|
867
|
-
const { overriddenCode, overriddenReason, recordedReason } = override;
|
|
868
|
-
if (typeof recordedReason !== 'string' || recordedReason.trim() === '') {
|
|
869
|
-
return '';
|
|
870
|
-
}
|
|
871
|
-
return (
|
|
872
|
-
` OPERATOR SCOPE OVERRIDE: the suitability gate objected on ` +
|
|
873
|
-
`"${overriddenCode}" (${overriddenReason}) and the operator answered ` +
|
|
874
|
-
`proceed-light — recorded reason: ${recordedReason.trim()}. The ` +
|
|
875
|
-
`prediction was waived, not the diff backstop, which still bounds the ` +
|
|
876
|
-
`landed change set.`
|
|
877
|
-
);
|
|
878
|
-
}
|
|
879
|
-
|
|
880
721
|
/**
|
|
881
722
|
* Build the minimal receipt `type::story` ticket for the light path
|
|
882
723
|
* (Story #4740 AC-5) — the input `assemblePlanStories` / `createStoryIssues`
|
|
883
724
|
* consume, so the light path reuses the plan-persist story-creation surface
|
|
884
725
|
* rather than reimplementing issue authoring. The body carries the operator
|
|
885
|
-
* prompt (goal + spec)
|
|
886
|
-
*
|
|
726
|
+
* prompt (goal + spec) and the diff-derived footprint (`changes[]`), so
|
|
727
|
+
* history and `refs #<id>` on the commit survive.
|
|
887
728
|
*
|
|
888
729
|
* @param {{
|
|
889
730
|
* prompt?: unknown,
|
|
890
731
|
* changedFiles?: unknown,
|
|
891
732
|
* amends?: unknown,
|
|
892
|
-
* override?: unknown,
|
|
893
733
|
* }} [args]
|
|
894
734
|
* @returns {{ slug: string, title: string, body: object, labels: string[] }}
|
|
895
735
|
*/
|
|
896
|
-
export function buildReceiptStoryTicket({
|
|
897
|
-
prompt,
|
|
898
|
-
changedFiles,
|
|
899
|
-
amends,
|
|
900
|
-
override,
|
|
901
|
-
} = {}) {
|
|
736
|
+
export function buildReceiptStoryTicket({ prompt, changedFiles, amends } = {}) {
|
|
902
737
|
const text = typeof prompt === 'string' ? prompt.trim() : '';
|
|
903
738
|
if (text === '') {
|
|
904
739
|
throw new Error(
|
|
@@ -908,7 +743,6 @@ export function buildReceiptStoryTicket({
|
|
|
908
743
|
const amendsId = normalizeAmends(amends);
|
|
909
744
|
const amendNote = amendsId !== null ? ` Amends #${amendsId}.` : '';
|
|
910
745
|
const changes = toReceiptChanges(changedFiles);
|
|
911
|
-
const overrideNote = renderOverrideNote(override);
|
|
912
746
|
|
|
913
747
|
return {
|
|
914
748
|
slug: slugifyPrompt(text),
|
|
@@ -919,8 +753,7 @@ export function buildReceiptStoryTicket({
|
|
|
919
753
|
spec:
|
|
920
754
|
`Delivered via /deliver-light as a validated single-session change — ` +
|
|
921
755
|
`the /mandrel-plan session is removed for genuinely small work while every ` +
|
|
922
|
-
`single-story-close gate runs byte-identical.${amendNote}` +
|
|
923
|
-
`${overrideNote} ` +
|
|
756
|
+
`single-story-close gate runs byte-identical.${amendNote} ` +
|
|
924
757
|
`Operator prompt: ${text}`,
|
|
925
758
|
changes,
|
|
926
759
|
acceptance: [
|