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
|
@@ -1,76 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/orchestration/complexity-gate.js — shape-derived
|
|
3
|
-
* (Story #4722
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* **
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* them **fails closed to `full`** (`run-plan-persist.js`). Artifact
|
|
31
|
-
* cardinality is deliberately not an axis (Story #4764).
|
|
32
|
-
* 4. **Deliver dispatches on topology alone.** The dispatch *mode*
|
|
33
|
-
* ({@link resolveStoryDispatchMode}) answers a different question from
|
|
34
|
-
* the route: may the engine run in the router's own session? Only a
|
|
35
|
-
* **single-Story run** may (Story #4736) — sub-agent isolation buys
|
|
36
|
-
* nothing when there is no concurrent sibling to isolate from. Shape
|
|
37
|
-
* cannot grant that session (Story #4829): a lite body makes work cheap,
|
|
38
|
-
* it does not conjure a second session for a sibling to run in. Story
|
|
39
|
-
* #5006 removed the shape derivation that survived there for reporting,
|
|
40
|
-
* since no consumer read it. The `route::lite` label is a
|
|
41
|
-
* **human-visible hint only**, never the control signal. Either way every
|
|
42
|
-
* `single-story-close.js` gate runs unchanged.
|
|
2
|
+
* lib/orchestration/complexity-gate.js — shape-derived Story routing for the
|
|
3
|
+
* deliver side (Story #4722; plan-side lite claim deleted by Story #5312).
|
|
4
|
+
*
|
|
5
|
+
* Three surfaces survive, all read at delivery time:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Seed signals ({@link buildComplexitySignals}).** `/mandrel-plan`'s
|
|
8
|
+
* context envelope carries the paths a seed predicts, their repo state
|
|
9
|
+
* (existing paths predict refactors; missing ones predict creates) and
|
|
10
|
+
* the `audit-rules.json` sensitive-path classes the footprint intersects.
|
|
11
|
+
* They ground the authoring template's `changes[]` skeleton and the
|
|
12
|
+
* `/prototype` offer; they route nothing.
|
|
13
|
+
* 2. **Story shape ({@link deriveStoryShape}).** The light path
|
|
14
|
+
* (`deliver-light`) reads a predicted footprint's effort and risk —
|
|
15
|
+
* distinct change kinds, declared magnitude, uncertainty,
|
|
16
|
+
* deployable/migration span, sensitive-path classes — against
|
|
17
|
+
* {@link STORY_SHAPE_CEILINGS} to decide whether a prompt may skip the
|
|
18
|
+
* Story-authoring ceremony. Artifact cardinality is deliberately not an
|
|
19
|
+
* axis (Story #4764).
|
|
20
|
+
* 3. **Dispatch mode ({@link resolveStoryDispatchMode}).** `/mandrel-deliver`
|
|
21
|
+
* answers a different question from the route: may the engine run in the
|
|
22
|
+
* router's own session? Only a **single-Story run** may (Story #4736).
|
|
23
|
+
*
|
|
24
|
+
* Story #5312 deleted the plan-side half: the planner's authored lite claim
|
|
25
|
+
* (`--route-downgrade-reason`), the persist-time shape backstop that
|
|
26
|
+
* validated it, the `route::lite` hint label, and the
|
|
27
|
+
* `planning.complexityGate` knobs. Persist no longer routes; every Story
|
|
28
|
+
* lands through the same engine and the same close gates, and the shape
|
|
29
|
+
* ceilings below are read only where a shape is actually decided on.
|
|
43
30
|
*
|
|
44
31
|
* The shape taxonomy is deliberately the one `review-depth.js` already
|
|
45
32
|
* applies to the landed diff at close (`deriveChangeLevel` over the
|
|
46
33
|
* `audit-rules.json` sensitive-path classes): **predicted shape at dispatch,
|
|
47
|
-
* actual diff at close** — one taxonomy, two read points.
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* The lite route collapses the **advisory ceremony** only: the story-worker
|
|
55
|
-
* sub-agent boot and the fresh acceptance-critic spawn. It **never** relaxes
|
|
56
|
-
* a non-negotiable. {@link LITE_PATH_INVARIANTS} is the machine-readable
|
|
57
|
-
* contract that the lite path still produces a Story ticket, still lands via
|
|
58
|
-
* a PR to `main`, still runs every repo quality gate, and still honours
|
|
34
|
+
* actual diff at close** — one taxonomy, two read points. Sensitivity always
|
|
35
|
+
* wins: a small change whose footprint intersects a sensitive-path class
|
|
36
|
+
* routes `full`, which keeps its fresh acceptance critic.
|
|
37
|
+
*
|
|
38
|
+
* {@link LITE_PATH_INVARIANTS} is the machine-readable contract that the
|
|
39
|
+
* light path still produces a Story ticket, still lands via a PR to `main`,
|
|
40
|
+
* still runs every repo quality gate, and still honours
|
|
59
41
|
* `rules/security-baseline.md`. Those gates run in `single-story-close.js`
|
|
60
42
|
* regardless of route; the router cannot and does not switch them off.
|
|
61
43
|
*
|
|
62
|
-
* ## Configuration
|
|
63
|
-
*
|
|
64
|
-
* Operators tune the surface via `planning.complexityGate` in `.agentrc.json`:
|
|
65
|
-
*
|
|
66
|
-
* - `enabled` (default `true`) — `false` disables lite routing
|
|
67
|
-
* everywhere: persist refuses lite claims and dispatch always takes the
|
|
68
|
-
* sub-agent path.
|
|
69
|
-
* - `maxArtifacts` (default `1`) — enumerated-artifact signal threshold;
|
|
70
|
-
* an **input signal** for the planner, no longer a deterministic router.
|
|
71
|
-
*
|
|
72
|
-
* `maxSeedWords` is **removed** (hard cutover): word count routes nothing.
|
|
73
|
-
*
|
|
74
44
|
* @typedef {'lite'|'full'} ComplexityRoute
|
|
75
45
|
*/
|
|
76
46
|
|
|
@@ -79,27 +49,6 @@ import path from 'node:path';
|
|
|
79
49
|
import { extractChangePaths } from '../story-body/story-body.js';
|
|
80
50
|
import { deriveChangeLevel } from './review-depth.js';
|
|
81
51
|
|
|
82
|
-
/**
|
|
83
|
-
* Framework defaults for the complexity-routing surface. The SSOT the config
|
|
84
|
-
* schema mirror and the configuration reference both cite. `maxSeedWords` is
|
|
85
|
-
* gone: seed word count carries no routing authority (Story #4722).
|
|
86
|
-
*/
|
|
87
|
-
const DEFAULT_COMPLEXITY_GATE = Object.freeze({
|
|
88
|
-
enabled: true,
|
|
89
|
-
maxArtifacts: 1,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* The persisted route marker for a lite-routed Story.
|
|
94
|
-
*
|
|
95
|
-
* **A human-visible hint only (Story #4722)** — never the control signal.
|
|
96
|
-
* Persist applies it so a lite cohort is filterable in the GitHub UI, and
|
|
97
|
-
* `deliver-light` reads the Story's own shape ({@link deriveStoryShape}) when
|
|
98
|
-
* it needs one. Nothing routes on the label: a lost label or an unread marker
|
|
99
|
-
* cannot misroute delivery.
|
|
100
|
-
*/
|
|
101
|
-
export const LITE_ROUTE_LABEL = 'route::lite';
|
|
102
|
-
|
|
103
52
|
/**
|
|
104
53
|
* Effort/risk ceilings a Story's work must fit for the `lite` route
|
|
105
54
|
* ({@link deriveStoryShape}). Framework constants, not operator knobs — a
|
|
@@ -378,54 +327,6 @@ const LITE_PATH_INVARIANTS = Object.freeze({
|
|
|
378
327
|
securityBaseline: true,
|
|
379
328
|
});
|
|
380
329
|
|
|
381
|
-
/**
|
|
382
|
-
* Coerce a candidate ceiling into a non-negative integer, falling back to the
|
|
383
|
-
* framework default for anything malformed — a stray `-1` or `NaN` must never
|
|
384
|
-
* widen the lite path (fail conservative).
|
|
385
|
-
*
|
|
386
|
-
* @param {unknown} value
|
|
387
|
-
* @param {number} fallback
|
|
388
|
-
* @returns {number}
|
|
389
|
-
*/
|
|
390
|
-
function normalizeCeiling(value, fallback) {
|
|
391
|
-
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
|
|
392
|
-
return fallback;
|
|
393
|
-
}
|
|
394
|
-
return Math.floor(value);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Resolve the effective complexity-gate config, shallow-overlaying an
|
|
399
|
-
* operator `planning.complexityGate` block onto
|
|
400
|
-
* {@link DEFAULT_COMPLEXITY_GATE}. Accepts the full resolved config, the bare
|
|
401
|
-
* `planning` bag, or the bare `complexityGate` bag, mirroring the tolerant
|
|
402
|
-
* unwrap the other routing accessors use.
|
|
403
|
-
*
|
|
404
|
-
* Exported for persist (`run-plan-persist.js#resolveEffectiveRoute`), which
|
|
405
|
-
* consults `enabled` to refuse a planner lite claim when the gate is off —
|
|
406
|
-
* the schema's documented contract. It is the only read point: Story #5006
|
|
407
|
-
* removed the second one in {@link resolveStoryDispatchMode}, where the switch
|
|
408
|
-
* gated a shape derivation whose result no consumer read.
|
|
409
|
-
*
|
|
410
|
-
* @param {object | null | undefined} config
|
|
411
|
-
* @returns {{ enabled: boolean, maxArtifacts: number }}
|
|
412
|
-
*/
|
|
413
|
-
export function resolveComplexityGate(config) {
|
|
414
|
-
const raw =
|
|
415
|
-
config?.planning?.complexityGate ?? config?.complexityGate ?? config ?? {};
|
|
416
|
-
const bag = raw && typeof raw === 'object' ? raw : {};
|
|
417
|
-
return {
|
|
418
|
-
enabled:
|
|
419
|
-
typeof bag.enabled === 'boolean'
|
|
420
|
-
? bag.enabled
|
|
421
|
-
: DEFAULT_COMPLEXITY_GATE.enabled,
|
|
422
|
-
maxArtifacts: normalizeCeiling(
|
|
423
|
-
bag.maxArtifacts,
|
|
424
|
-
DEFAULT_COMPLEXITY_GATE.maxArtifacts,
|
|
425
|
-
),
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
|
|
429
330
|
/**
|
|
430
331
|
* Count top-level enumerated items (`- `, `* `, `1. `) in a free-form seed —
|
|
431
332
|
* each enumerated line is one predicted artifact.
|
|
@@ -464,20 +365,17 @@ function extractPredictedPaths(text) {
|
|
|
464
365
|
}
|
|
465
366
|
|
|
466
367
|
/**
|
|
467
|
-
* Build the advisory complexity **signals** for a planning seed
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
* - `artifactCount` — enumerated items in the seed,
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
* pre-mortem critic uses).
|
|
479
|
-
* - `predictedPaths` / `repoState` — path-like tokens in the seed and
|
|
480
|
-
* which of them exist in the repo (existing
|
|
368
|
+
* Build the advisory complexity **signals** for a planning seed. Signals,
|
|
369
|
+
* not routing: the result carries `routingAuthority: false` and no `route`
|
|
370
|
+
* field — they ground the authoring template's pre-resolved `changes[]` and
|
|
371
|
+
* the `/prototype` offer, nothing else (Story #5312 deleted the risk-heuristic
|
|
372
|
+
* hits and the `planning.complexityGate` echo that used to ride alongside).
|
|
373
|
+
*
|
|
374
|
+
* - `artifactCount` — enumerated items in the seed, a rough width
|
|
375
|
+
* signal for the operator's eye only.
|
|
376
|
+
* - `predictedPaths` — path-like tokens the seed names, in order of
|
|
377
|
+
* first appearance (capped).
|
|
378
|
+
* - `repoState` — which predicted paths exist in the repo (existing
|
|
481
379
|
* paths predict refactors; missing predict
|
|
482
380
|
* creates).
|
|
483
381
|
* - `sensitivePathClasses` — `audit-rules.json` sensitive-path classes the
|
|
@@ -489,8 +387,6 @@ function extractPredictedPaths(text) {
|
|
|
489
387
|
*
|
|
490
388
|
* @param {{
|
|
491
389
|
* seedText?: string,
|
|
492
|
-
* config?: object,
|
|
493
|
-
* riskHeuristics?: string[],
|
|
494
390
|
* cwd?: string,
|
|
495
391
|
* pathExistsFn?: (absPath: string) => boolean,
|
|
496
392
|
* injectedRules?: object,
|
|
@@ -498,37 +394,21 @@ function extractPredictedPaths(text) {
|
|
|
498
394
|
* }} [args]
|
|
499
395
|
* @returns {{
|
|
500
396
|
* artifactCount: number,
|
|
501
|
-
* maxArtifacts: number,
|
|
502
|
-
* riskHeuristicHits: string[],
|
|
503
397
|
* predictedPaths: string[],
|
|
504
398
|
* repoState: { existingPaths: string[], missingPaths: string[] },
|
|
505
399
|
* sensitivePathClasses: string[],
|
|
506
|
-
* gate: { enabled: boolean },
|
|
507
400
|
* advisory: true,
|
|
508
401
|
* routingAuthority: false,
|
|
509
402
|
* }}
|
|
510
403
|
*/
|
|
511
404
|
export function buildComplexitySignals({
|
|
512
405
|
seedText = '',
|
|
513
|
-
config,
|
|
514
|
-
riskHeuristics = [],
|
|
515
406
|
cwd,
|
|
516
407
|
pathExistsFn = existsSync,
|
|
517
408
|
injectedRules,
|
|
518
409
|
selectSensitivePathClassesFn,
|
|
519
410
|
} = {}) {
|
|
520
|
-
const gate = resolveComplexityGate(config);
|
|
521
411
|
const text = typeof seedText === 'string' ? seedText : '';
|
|
522
|
-
const haystack = text.toLowerCase();
|
|
523
|
-
|
|
524
|
-
const riskHeuristicHits = (
|
|
525
|
-
Array.isArray(riskHeuristics) ? riskHeuristics : []
|
|
526
|
-
).filter(
|
|
527
|
-
(phrase) =>
|
|
528
|
-
typeof phrase === 'string' &&
|
|
529
|
-
phrase.trim().length > 0 &&
|
|
530
|
-
haystack.includes(phrase.trim().toLowerCase()),
|
|
531
|
-
);
|
|
532
412
|
|
|
533
413
|
const predictedPaths = extractPredictedPaths(text);
|
|
534
414
|
const root = typeof cwd === 'string' && cwd !== '' ? cwd : process.cwd();
|
|
@@ -552,60 +432,14 @@ export function buildComplexitySignals({
|
|
|
552
432
|
|
|
553
433
|
return {
|
|
554
434
|
artifactCount: countSeedArtifacts(text),
|
|
555
|
-
maxArtifacts: gate.maxArtifacts,
|
|
556
|
-
riskHeuristicHits,
|
|
557
435
|
predictedPaths,
|
|
558
436
|
repoState: { existingPaths, missingPaths },
|
|
559
437
|
sensitivePathClasses: classes,
|
|
560
|
-
gate: { enabled: gate.enabled },
|
|
561
438
|
advisory: /** @type {const} */ (true),
|
|
562
439
|
routingAuthority: /** @type {const} */ (false),
|
|
563
440
|
};
|
|
564
441
|
}
|
|
565
442
|
|
|
566
|
-
/**
|
|
567
|
-
* Resolve the planner's authored trivial-vs-standard verdict
|
|
568
|
-
* (Story #4722 AC-2, generalizing the former one-way `applyPlannerDowngrade`
|
|
569
|
-
* seam into the verdict itself).
|
|
570
|
-
*
|
|
571
|
-
* The planner — not a word count — owns the judgment, and the contract keeps
|
|
572
|
-
* it auditable: `lite` **only** with a non-empty recorded reason (carried on
|
|
573
|
-
* `authored` and ledgered on every created Story's `story-plan-state`
|
|
574
|
-
* checkpoint by persist). Absent a recorded reason the conservative default
|
|
575
|
-
* stands: `full`, with `authored: null`. Pure and total.
|
|
576
|
-
*
|
|
577
|
-
* The verdict is a **claim**, not the decision — persist validates it against
|
|
578
|
-
* the authored Story's shape ({@link deriveStoryShape}) and fails closed to
|
|
579
|
-
* `full` when the shape exceeds the ceilings.
|
|
580
|
-
*
|
|
581
|
-
* @param {{ reason?: unknown }} [args]
|
|
582
|
-
* @returns {{
|
|
583
|
-
* route: ComplexityRoute,
|
|
584
|
-
* reasons: string[],
|
|
585
|
-
* authored: Readonly<{ route: 'lite', reason: string }>|null,
|
|
586
|
-
* preserves: typeof LITE_PATH_INVARIANTS,
|
|
587
|
-
* }}
|
|
588
|
-
*/
|
|
589
|
-
export function resolvePlannerRouteVerdict({ reason } = {}) {
|
|
590
|
-
const recorded = typeof reason === 'string' ? reason.trim() : '';
|
|
591
|
-
if (recorded === '') {
|
|
592
|
-
return {
|
|
593
|
-
route: 'full',
|
|
594
|
-
reasons: [
|
|
595
|
-
'no authored lite verdict (no recorded reason) — standard full route',
|
|
596
|
-
],
|
|
597
|
-
authored: null,
|
|
598
|
-
preserves: LITE_PATH_INVARIANTS,
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
return {
|
|
602
|
-
route: 'lite',
|
|
603
|
-
reasons: [`planner verdict: lite (recorded reason): ${recorded}`],
|
|
604
|
-
authored: Object.freeze({ route: 'lite', reason: recorded }),
|
|
605
|
-
preserves: LITE_PATH_INVARIANTS,
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
|
|
609
443
|
/**
|
|
610
444
|
* Assemble the effort/risk shape of a footprint — the evidence
|
|
611
445
|
* {@link deriveStoryShape} decides on and carries on its result.
|
|
@@ -13,23 +13,23 @@
|
|
|
13
13
|
* layer — so the gate scans `type === 'story'` tickets and reads the
|
|
14
14
|
* `{ path, assumption }` entries inlined on each Story body.
|
|
15
15
|
*
|
|
16
|
-
* Rules (one
|
|
17
|
-
*
|
|
16
|
+
* Rules (one finding per mismatched path). Story #5312 demoted every
|
|
17
|
+
* mismatch but one to a **warning** the dry-run lists and the persist
|
|
18
|
+
* proceeds past — `changes[]` is an advisory sketch the deliverer revises
|
|
19
|
+
* against the real tree, so refusing a plan on it cost re-authoring rounds
|
|
20
|
+
* for a footprint nobody was bound to:
|
|
21
|
+
* - `creates` + path **exists** → warning (Story may clobber).
|
|
18
22
|
* - `refactors-existing` (via `changes`) + path **absent and never
|
|
19
23
|
* tracked** at `baseBranchRef` → auto-normalized to `creates` with a
|
|
20
24
|
* logged warning (#4496 fix 5): a refactor declaration against a
|
|
21
|
-
* path with no history is deterministically a create
|
|
22
|
-
* only forces a reject→amend→re-persist cycle for a mechanical rewrite.
|
|
25
|
+
* path with no history is deterministically a create.
|
|
23
26
|
* - `refactors-existing` (via `changes`) + path **absent but present in
|
|
24
|
-
* that ref's history** →
|
|
25
|
-
* git detects one, the rename target (Story #5265)
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* and stays an error.
|
|
31
|
-
* - `exists` + path **absent** → error (read dependency missing).
|
|
32
|
-
* - `deletes` + path **absent** → error (nothing to delete).
|
|
27
|
+
* that ref's history** → warning naming the removing commit and, when
|
|
28
|
+
* git detects one, the rename target (Story #5265) — a plan authored
|
|
29
|
+
* against a file the tree deleted is worth saying out loud.
|
|
30
|
+
* - `exists` + path **absent** → warning (read dependency missing).
|
|
31
|
+
* - `deletes` + path **absent** → **error** (nothing to delete —
|
|
32
|
+
* the one declaration a deliverer cannot act on at all).
|
|
33
33
|
*
|
|
34
34
|
* Wave awareness (Story #3960): the base-branch-only rules above produce
|
|
35
35
|
* false signals once an earlier Story in the same epic creates (or deletes)
|
|
@@ -386,8 +386,9 @@ function predecessorMutator(index, path, predecessors) {
|
|
|
386
386
|
* predecessors (Story #3960). Returns an envelope:
|
|
387
387
|
*
|
|
388
388
|
* {
|
|
389
|
-
* errors: string[] //
|
|
390
|
-
*
|
|
389
|
+
* errors: string[] // a `deletes` naming an absent path, or a
|
|
390
|
+
* // legacy string-bullet body — the refusals
|
|
391
|
+
* warnings: string[] // every other mismatch (Story #5312) +
|
|
391
392
|
* // auto-normalization notices (#4496 fix 5)
|
|
392
393
|
* mismatches: object[] // structured payload for downstream tooling
|
|
393
394
|
* normalizations: object[] // `refactors-existing`→`creates`
|
|
@@ -513,7 +514,7 @@ export function validateStoryFileAssumptions(opts) {
|
|
|
513
514
|
continue;
|
|
514
515
|
}
|
|
515
516
|
mismatches.push(finding);
|
|
516
|
-
|
|
517
|
+
routeMismatch(finding, { errors, warnings });
|
|
517
518
|
continue;
|
|
518
519
|
}
|
|
519
520
|
// Wave-aware concurrent-create check (Story #3960): two Stories with
|
|
@@ -541,7 +542,7 @@ export function validateStoryFileAssumptions(opts) {
|
|
|
541
542
|
producerSlug: concurrent,
|
|
542
543
|
};
|
|
543
544
|
mismatches.push(conflict);
|
|
544
|
-
|
|
545
|
+
warnings.push(renderMismatch(conflict));
|
|
545
546
|
}
|
|
546
547
|
}
|
|
547
548
|
}
|
|
@@ -549,6 +550,20 @@ export function validateStoryFileAssumptions(opts) {
|
|
|
549
550
|
return { errors, warnings, mismatches, normalizations };
|
|
550
551
|
}
|
|
551
552
|
|
|
553
|
+
/**
|
|
554
|
+
* Route one confirmed mismatch to the channel it belongs on (Story #5312):
|
|
555
|
+
* a `deletes` on an absent path is the one declaration nothing can act on and
|
|
556
|
+
* stays an error; every other mismatch is advisory and lands on `warnings`.
|
|
557
|
+
*
|
|
558
|
+
* @param {object} finding
|
|
559
|
+
* @param {{ errors: string[], warnings: string[] }} channels
|
|
560
|
+
* @returns {void}
|
|
561
|
+
*/
|
|
562
|
+
function routeMismatch(finding, { errors, warnings }) {
|
|
563
|
+
const channel = finding.assumption === 'deletes' ? errors : warnings;
|
|
564
|
+
channel.push(renderMismatch(finding));
|
|
565
|
+
}
|
|
566
|
+
|
|
552
567
|
/**
|
|
553
568
|
* Route one mismatch to the errors channel or the normalization channel
|
|
554
569
|
* (Story #5265).
|
|
@@ -76,7 +76,7 @@ function buildRerunBackstopCommand(storyId) {
|
|
|
76
76
|
* number, or `null` when absent/malformed.
|
|
77
77
|
*
|
|
78
78
|
* This is the only Story context a **gate-stage** rejection can legitimately
|
|
79
|
-
* claim: the signals stream is keyed on a Story id, and an
|
|
79
|
+
* claim: the signals stream is keyed on a Story id, and an escalating gate
|
|
80
80
|
* has authored no receipt yet — deliberately, since not creating one is the
|
|
81
81
|
* point of that outcome. A bare prompt's rejection therefore has no stream to
|
|
82
82
|
* land in, and attributing it to a fabricated id would be worse than recording
|
|
@@ -93,8 +93,8 @@ function normalizeAmendsId(amends) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
|
-
* Record a suitability-gate refusal (`
|
|
97
|
-
* the `--amends` target when there is one.
|
|
96
|
+
* Record a suitability-gate refusal (`escalate-plan`) as friction, attributed
|
|
97
|
+
* to the `--amends` target when there is one.
|
|
98
98
|
*
|
|
99
99
|
* @param {{
|
|
100
100
|
* gate: object,
|