mandrel 2.23.0 → 2.25.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/acceptance-critic.md +13 -19
- package/.agents/agents/auditor.md +16 -19
- package/.agents/agents/story-worker.md +41 -54
- package/.agents/audit-checklists/architecture.md +5 -1
- package/.agents/audit-checklists/quality.md +3 -0
- package/.agents/docs/agentrc-reference.json +2 -1
- package/.agents/docs/configuration.md +2 -1
- package/.agents/docs/workflows.md +1 -1
- package/.agents/rules/ci-remediation.md +68 -3
- package/.agents/schemas/agentrc.schema.json +6 -1
- package/.agents/schemas/baselines/baseline-envelope.schema.json +4 -0
- package/.agents/schemas/baselines/crap.schema.json +5 -1
- package/.agents/schemas/crap-report.schema.json +37 -0
- package/.agents/schemas/story-deliver-terminal.schema.json +6 -1
- package/.agents/scripts/acceptance-eval.js +35 -9
- package/.agents/scripts/audit-labels-bootstrap.js +16 -95
- package/.agents/scripts/audit-to-stories.js +33 -3
- package/.agents/scripts/check-context-budget.js +95 -14
- package/.agents/scripts/check-test-temp-hygiene.js +83 -0
- package/.agents/scripts/cleanup-repo-test-temp.js +7 -5
- package/.agents/scripts/deliver-light.js +22 -45
- package/.agents/scripts/deliver-recover.js +13 -0
- package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +10 -25
- package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +177 -0
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +35 -3
- package/.agents/scripts/lib/audit-to-stories/group-findings.js +20 -11
- package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +23 -9
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +9 -2
- package/.agents/scripts/lib/baseline-loader.js +0 -0
- package/.agents/scripts/lib/baselines/git-base.js +0 -0
- package/.agents/scripts/lib/baselines/kinds/crap.js +396 -51
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +20 -32
- package/.agents/scripts/lib/baselines/preview-gates.js +58 -15
- package/.agents/scripts/lib/baselines/reader.js +10 -0
- package/.agents/scripts/lib/baselines/writer.js +7 -3
- package/.agents/scripts/lib/config-settings-schema-delivery.js +5 -0
- package/.agents/scripts/lib/crap-engine.js +97 -19
- package/.agents/scripts/lib/crap-utils.js +73 -13
- package/.agents/scripts/lib/env-loader.js +46 -16
- package/.agents/scripts/lib/escomplex-ast-compat.js +360 -0
- package/.agents/scripts/lib/findings/route-finding.js +123 -17
- package/.agents/scripts/lib/findings/severity.js +80 -2
- package/.agents/scripts/lib/git/cached-fetch.js +0 -0
- package/.agents/scripts/lib/label-constants.js +17 -0
- package/.agents/scripts/lib/maintainability-engine.js +83 -11
- package/.agents/scripts/lib/maintainability-unscorable.js +60 -0
- package/.agents/scripts/lib/maintainability-utils.js +14 -5
- package/.agents/scripts/lib/observability/runtime-friction.js +8 -0
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +110 -17
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +548 -0
- package/.agents/scripts/lib/orchestration/deliver-recover.js +2 -2
- package/.agents/scripts/lib/orchestration/diff-magnitude.js +283 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +71 -20
- package/.agents/scripts/lib/orchestration/light-backstop.js +124 -0
- package/.agents/scripts/lib/orchestration/light-escalation.js +248 -0
- package/.agents/scripts/lib/orchestration/light-suitability.js +254 -51
- package/.agents/scripts/lib/orchestration/plan-context.js +12 -13
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +7 -0
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +13 -1
- package/.agents/scripts/lib/orchestration/retro-proposals.js +0 -0
- package/.agents/scripts/lib/orchestration/run-scoped-config.js +276 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +41 -10
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +96 -34
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +70 -2
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +129 -21
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +73 -18
- package/.agents/scripts/lib/reserved-test-ids.js +77 -0
- package/.agents/scripts/lib/story-plan.js +137 -42
- package/.agents/scripts/lib/templates/decomposer-prompts.js +4 -4
- package/.agents/scripts/lib/ticket-body-sections.js +1 -1
- package/.agents/scripts/lib/util/poll-loop.js +18 -2
- package/.agents/scripts/lib/wave-runner/live-probe.js +7 -1
- package/.agents/scripts/lib/wave-runner/ready-set.js +73 -9
- package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +6 -3
- package/.agents/scripts/lib/workers/crap-worker.js +6 -3
- package/.agents/scripts/lib/workers/maintainability-worker.js +14 -9
- package/.agents/scripts/pr-watch-with-update.js +568 -213
- package/.agents/scripts/quality-preview.js +25 -0
- package/.agents/scripts/run-tests.js +10 -0
- package/.agents/scripts/single-story-close.js +6 -0
- package/.agents/scripts/single-story-init.js +7 -0
- package/.agents/scripts/stories-wave-tick.js +95 -10
- package/.agents/scripts/story-plan.js +65 -9
- package/.agents/scripts/update-crap-baseline.js +42 -4
- package/.agents/scripts/update-maintainability-baseline.js +31 -4
- package/.agents/templates/single-story-body.md +16 -8
- package/.agents/workflows/audit-architecture.md +69 -13
- package/.agents/workflows/audit-quality.md +26 -0
- package/.agents/workflows/audit-to-stories.md +30 -4
- package/.agents/workflows/deliver.md +81 -95
- package/.agents/workflows/helpers/audit-lens-core.md +25 -4
- package/.agents/workflows/helpers/deliver-light.md +21 -4
- package/.agents/workflows/helpers/deliver-reference.md +18 -3
- package/.agents/workflows/helpers/deliver-story-reference.md +127 -46
- package/.agents/workflows/helpers/deliver-story.md +104 -119
- package/.agents/workflows/helpers/plan-reference.md +34 -0
- package/.agents/workflows/plan.md +60 -76
- package/docs/CHANGELOG.md +38 -0
- package/lib/cli/update.js +14 -11
- package/lib/cli/version-check.js +9 -1
- package/package.json +1 -1
|
@@ -36,6 +36,22 @@ whole surface exists to remove.
|
|
|
36
36
|
Mixed ids and prose in one invocation is a **hard error**: refuse and ask which
|
|
37
37
|
was meant, rather than guessing a mode and doing the wrong work.
|
|
38
38
|
|
|
39
|
+
## Default-single split policy — what the seam means
|
|
40
|
+
|
|
41
|
+
The spine's two escape hatches from N=1 are narrow on purpose:
|
|
42
|
+
|
|
43
|
+
- **Near-zero overlap** — the pieces touch disjoint files and neither's
|
|
44
|
+
acceptance criteria can be scored without the other having landed.
|
|
45
|
+
- **Architectural seam** — different deployables, or a migration and its
|
|
46
|
+
consumer: work that cannot share one branch and one PR without one half
|
|
47
|
+
sitting unverifiable behind the other.
|
|
48
|
+
|
|
49
|
+
Everything else is one Story with `## Slicing` checkpoints. When N>1 does
|
|
50
|
+
apply, **every acceptance criterion belongs to exactly one Story** —
|
|
51
|
+
`assertAcceptancePartition` refuses a split whose criteria repeat across
|
|
52
|
+
siblings, because a verbatim-shared criterion is the signature of coupled work
|
|
53
|
+
cut in half rather than genuinely separable work.
|
|
54
|
+
|
|
39
55
|
## Unknown triage — AFK vs HITL
|
|
40
56
|
|
|
41
57
|
Every open question interrogation surfaces is triaged by **who can resolve
|
|
@@ -98,6 +114,14 @@ Resume `/plan` at step 2 (Author) **in this same session** — the interrogation
|
|
|
98
114
|
is still valid and re-paying for it buys nothing. This bounce-back is not an
|
|
99
115
|
escalation.
|
|
100
116
|
|
|
117
|
+
**Under `--yes` the offer is recorded and planning proceeds** — it is *never*
|
|
118
|
+
auto-downgraded to light. An unattended run has nobody to confirm the reroute,
|
|
119
|
+
and a suggestion is not a confirmation. The same rule governs unknown triage
|
|
120
|
+
unattended: AFK unknowns are still researched, but no free-form operator
|
|
121
|
+
question is asked — each HITL unknown lands in Key Assumptions marked a
|
|
122
|
+
decision-made-by-default, so the record shows what was decided for the operator
|
|
123
|
+
rather than pretending it was decided with them.
|
|
124
|
+
|
|
101
125
|
Escalation in the *other* direction — an over-scope prompt on the light path —
|
|
102
126
|
is terminal and requires a fresh session. The rule that separates the two, and
|
|
103
127
|
why it must not be flattened into symmetry:
|
|
@@ -294,6 +318,16 @@ Either way the critic is **maker-blind**: hand it the draft artifacts
|
|
|
294
318
|
transcript or the reasons the planner believed its own draft is sound. A
|
|
295
319
|
critic that reads the maker's case grades the case, not the draft.
|
|
296
320
|
|
|
321
|
+
## What `--dry-run` actually gates
|
|
322
|
+
|
|
323
|
+
`plan-persist.js --dry-run` is the same command with GitHub writes suppressed,
|
|
324
|
+
and every gate runs before the first `createIssue` would fire — the validator,
|
|
325
|
+
the body parse, the DAG, the capacity and Spec-budget ceilings, the
|
|
326
|
+
reachability check, the split and supersede partitions, and the Tech Spec fold.
|
|
327
|
+
That is the whole point of running it first: a dry run that comes back clean
|
|
328
|
+
has already paid for every deterministic refusal, so the real persist has
|
|
329
|
+
nothing left to discover except network failure.
|
|
330
|
+
|
|
297
331
|
## Ready means fully persisted
|
|
298
332
|
|
|
299
333
|
`agent::ready` is the **terminal** step, not part of the creating POST.
|
|
@@ -7,47 +7,43 @@ description:
|
|
|
7
7
|
|
|
8
8
|
# /plan
|
|
9
9
|
|
|
10
|
-
> **Lean spine.** Happy path + gate list; edge-case detail lives
|
|
10
|
+
> **Lean spine.** Happy path + gate list; edge-case detail lives on demand in
|
|
11
11
|
> [`helpers/plan-reference.md`](helpers/plan-reference.md).
|
|
12
12
|
|
|
13
13
|
## Inputs
|
|
14
14
|
|
|
15
15
|
Single planning path — there is no Epic/Story router, no scope-triage
|
|
16
16
|
`epic|story` verdict. **Derive the mode from what the operator typed, announce
|
|
17
|
-
it,
|
|
17
|
+
it, act**:
|
|
18
18
|
|
|
19
19
|
| Invocation | Mode | Behavior |
|
|
20
20
|
| --- | --- | --- |
|
|
21
21
|
| `/plan` | ask | Ask what to plan; nothing runs first. |
|
|
22
|
-
| `/plan add a --json flag to doctor` | seed | Ideation from
|
|
23
|
-
| `/plan temp/notes/idea.md` | seed-file | Same, from notes. An
|
|
22
|
+
| `/plan add a --json flag to doctor` | seed | Ideation from prose: interrogate → author **one Story by default** → persist. |
|
|
23
|
+
| `/plan temp/notes/idea.md` | seed-file | Same, from notes. An existing file is a path, not prose. |
|
|
24
24
|
| `/plan 4712[,4713…]` | tickets | Fetch issue(s), analyze into proper Stories (prefer N=1 rewrite). |
|
|
25
25
|
| `/plan 4712`, already delivered | amends | Amend a shipped Story from a **delta envelope**, not a re-interrogation. |
|
|
26
26
|
|
|
27
27
|
**Resolving a bare id.** Read live state rather than asking: `agent::done` can
|
|
28
28
|
only be amended, an open unplanned issue can only be planned. **Announce the
|
|
29
|
-
derivation** — "4712 is `agent::done` → amending"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
`--body` is **not** a `/plan` entry; persist goes through `plan-persist.js`.
|
|
29
|
+
derivation** — "4712 is `agent::done` → amending". Ask **only** for an open
|
|
30
|
+
Story already at `agent::ready`.
|
|
33
31
|
|
|
34
32
|
## Saying what you want
|
|
35
33
|
|
|
36
|
-
No flags to remember — state intent
|
|
37
|
-
([reference](helpers/plan-reference.md)). Run
|
|
38
|
-
than copying its surface here.
|
|
34
|
+
No flags to remember — state intent; the workflow fills in the CLI
|
|
35
|
+
([reference](helpers/plan-reference.md)). Run scripts with `--help`.
|
|
39
36
|
|
|
40
37
|
`--yes` is **runner-set, never operator-typed** — cron, `/loop`, and headless
|
|
41
|
-
dispatch set it to mean *nobody is at the keyboard*, which
|
|
42
|
-
|
|
38
|
+
dispatch set it to mean *nobody is at the keyboard*, which auto-proceeds the
|
|
39
|
+
gates below (#1 and #2). Never offer it to an operator or an attended run.
|
|
43
40
|
|
|
44
41
|
## Default-single split policy
|
|
45
42
|
|
|
46
43
|
Author **one Story** unless the pieces have **near-zero overlap** or sit across
|
|
47
|
-
an **architectural seam
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
(`assertAcceptancePartition` refuses coupled splits). **N=1 is the lean path.**
|
|
44
|
+
an **architectural seam**. Coupled work stays one Story — `## Slicing`
|
|
45
|
+
checkpoints, not sibling tickets
|
|
46
|
+
([detail](helpers/plan-reference.md)). **N=1 is lean.**
|
|
51
47
|
|
|
52
48
|
## Procedure
|
|
53
49
|
|
|
@@ -63,54 +59,46 @@ node .agents/scripts/plan-context.js --seed "<seed>" \
|
|
|
63
59
|
and derives source ids from its `sourceTickets[]`; the CLI also writes
|
|
64
60
|
**`stories.template.json`**, step 2's skeleton.
|
|
65
61
|
|
|
66
|
-
The envelope carries docs context, the story-author
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
**Triage each unknown by resolver**
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
only HITL unknowns land in Key Assumptions, each marked a
|
|
62
|
+
The envelope carries docs context, the story-author prompt, `sourceTickets[]`,
|
|
63
|
+
`duplicates[]` (open **Stories**, never Epics) and advisory `complexitySignals`
|
|
64
|
+
(**no routing authority**). A trivial scope can claim the lite route at
|
|
65
|
+
persist — shape-validated, failing closed to `full`
|
|
66
|
+
([detail](helpers/plan-reference.md)).
|
|
67
|
+
|
|
68
|
+
**Triage each unknown by resolver** ([detail](helpers/plan-reference.md)): an
|
|
69
|
+
**AFK** unknown (research settles it) is resolved before authoring, never
|
|
70
|
+
assumed; a **HITL** unknown (an operator call) goes to Gate #1 as "needs your
|
|
71
|
+
decision". Under `--yes` do not ask free-form operator questions — AFK unknowns
|
|
72
|
+
are still researched; only HITL unknowns land in Key Assumptions, each marked a
|
|
78
73
|
decision-made-by-default.
|
|
79
74
|
|
|
80
75
|
**Gate #1** — STOP to confirm the sharpened plan intent and any
|
|
81
76
|
duplicate-candidate review. Under `--yes`, auto-proceed.
|
|
82
77
|
|
|
83
|
-
On a truthy `deliverLightSuggestion.suggested`, offer —
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
proceeds — never auto-downgraded to light.
|
|
90
|
-
|
|
91
|
-
A truthy `complexitySignals.uiSurface` marks a UI-touching plan: name
|
|
92
|
-
[`/prototype`](prototype.md) as an operator option — never invoke it here.
|
|
78
|
+
On a truthy `deliverLightSuggestion.suggested`, offer — advisory, never an
|
|
79
|
+
automatic reroute — to deliver the seed instead; on confirm, route **in this
|
|
80
|
+
session** into [`helpers/deliver-light.md`](helpers/deliver-light.md), its gate
|
|
81
|
+
filled from this envelope. A truthy `complexitySignals.uiSurface` marks a
|
|
82
|
+
UI-touching plan: name [`/prototype`](prototype.md) as an operator option,
|
|
83
|
+
never invoke it here. [Both offers](helpers/plan-reference.md).
|
|
93
84
|
|
|
94
85
|
### 2. Author
|
|
95
86
|
|
|
96
87
|
**One-shot authoring.** Start from `stories.template.json`; author
|
|
97
|
-
`stories.json` in one pass. `body` is
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
`verify[]` into it — never dual-author those lists.
|
|
88
|
+
`stories.json` in one pass. `body` is markdown **or** a structured object;
|
|
89
|
+
persist parses either, serializes canonical markdown and syncs top-level
|
|
90
|
+
`acceptance[]` / `verify[]` into it — never dual-author those lists.
|
|
101
91
|
|
|
102
|
-
**Grounding = your reads + Phase 8.** Nothing inventories the repo
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Entry fields (the `stories.template.json` shape):
|
|
92
|
+
**Grounding = your reads + Phase 8.** Nothing inventories the repo: read each
|
|
93
|
+
file you cite; persist's file-assumption gate hard-errors on any
|
|
94
|
+
`{path, assumption}` absent from the tree. Entry fields:
|
|
107
95
|
[reference](helpers/plan-reference.md).
|
|
108
96
|
|
|
109
97
|
Artifacts under `temp/plan-<slug>/`: `stories.json` (**length 1 by default**;
|
|
110
|
-
over-budget Specs fail closed — split or tighten, never under `docs/`);
|
|
111
|
-
|
|
112
|
-
`acceptance-manifest.json` (N>1 partition
|
|
113
|
-
|
|
98
|
+
over-budget Specs fail closed — split or tighten, never under `docs/`); optional
|
|
99
|
+
`techspec.md` (**N===1 only** — folded into `## Spec`); optional
|
|
100
|
+
`acceptance-manifest.json` (N>1 partition — `--plan-acceptance`). For N=1 use
|
|
101
|
+
the envelope `systemPrompts.story`; split only under the policy above.
|
|
114
102
|
|
|
115
103
|
**Tickets mode:** every Story authors a top-level `supersedes[]`; persist
|
|
116
104
|
refuses a partial map ([shape](helpers/plan-reference.md)).
|
|
@@ -123,28 +111,27 @@ node .agents/scripts/plan-critics.js \
|
|
|
123
111
|
[--tech-spec temp/plan-<slug>/techspec.md]
|
|
124
112
|
```
|
|
125
113
|
|
|
126
|
-
Run **before** persist — the last point a finding folds into a re-author
|
|
114
|
+
Run **before** persist — the last point a finding folds into a re-author.
|
|
127
115
|
It exits 0 on **any** verdict (verdicts route work, they do not gate) and exits
|
|
128
116
|
**1** only on a usage/IO error — no critic ran, no skip ledgered: **do not
|
|
129
|
-
proceed to Persist
|
|
117
|
+
proceed to Persist**, fix and re-run.
|
|
130
118
|
|
|
131
119
|
- **Both `dispatch: false`** — proceed to Persist (each skip is ledgered).
|
|
132
120
|
- **Either `dispatch: true`** — dispatch **one fresh-context, maker-blind
|
|
133
121
|
sub-agent per firing critic** (hand it only the draft artifacts, never the
|
|
134
122
|
authoring transcript), fold findings into Gate #2 or a re-author round, re-run
|
|
135
123
|
this step. Pre-mortem triggers (incl. the external-dependency probe), the
|
|
136
|
-
advisory-only `textHygiene.findings[]` lints
|
|
137
|
-
[
|
|
124
|
+
advisory-only `textHygiene.findings[]` lints and dispatch shape:
|
|
125
|
+
[reference § Critic dispatch detail](helpers/plan-reference.md).
|
|
138
126
|
|
|
139
127
|
### 3. Persist
|
|
140
128
|
|
|
141
129
|
**Gate #2** — STOP for approval before persist **only** when the operator asked
|
|
142
130
|
to review (`--force-review`). Under `--yes`, auto-proceed.
|
|
143
131
|
|
|
144
|
-
Run persist with `--dry-run` **first** — same command,
|
|
145
|
-
every gate
|
|
146
|
-
|
|
147
|
-
Then:
|
|
132
|
+
Run persist with `--dry-run` **first** — same command, writes suppressed;
|
|
133
|
+
every gate runs before the first `createIssue`
|
|
134
|
+
([the list](helpers/plan-reference.md)):
|
|
148
135
|
|
|
149
136
|
```bash
|
|
150
137
|
node .agents/scripts/plan-persist.js \
|
|
@@ -155,28 +142,25 @@ node .agents/scripts/plan-persist.js \
|
|
|
155
142
|
[--source-tickets 123,456]
|
|
156
143
|
```
|
|
157
144
|
|
|
158
|
-
At lite shape, `--chain-on-clean`
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
Persist creates `type::story` issue(s) plus a `plan-run::<id>` grouping label
|
|
162
|
-
(**metadata only**); N>1 `depends_on` edges become `blocked by #<id>` footers.
|
|
163
|
-
`agent::ready` is the **terminal** flip after all receipts land. stdout is pure
|
|
164
|
-
JSON.
|
|
145
|
+
At lite shape, `--chain-on-clean` folds a clean dry-run into the real persist;
|
|
146
|
+
a full plan keeps its review trip.
|
|
165
147
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
148
|
+
Persist creates `type::story` issue(s), a **metadata-only** `plan-run::<id>`
|
|
149
|
+
label, and `blocked by #<id>` footers for N>1 `depends_on` edges.
|
|
150
|
+
`agent::ready` is the **terminal** flip after receipts land; stdout is pure
|
|
151
|
+
JSON. Tickets mode also comments on and closes each source id
|
|
152
|
+
([detail](helpers/plan-reference.md)).
|
|
169
153
|
|
|
170
154
|
## Constraints
|
|
171
155
|
|
|
172
156
|
- `/plan` starts delivery **only** through a confirmed Gate #1 light route —
|
|
173
157
|
never off its own authored Stories, which land via [`/deliver`](deliver.md).
|
|
174
|
-
- Duplicate search targets open Stories (`type::story`), not Epics
|
|
175
|
-
|
|
158
|
+
- Duplicate search targets open Stories (`type::story`), not Epics; and
|
|
159
|
+
deterministic gates still fail closed under `--yes`.
|
|
176
160
|
|
|
177
161
|
## See also
|
|
178
162
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
163
|
+
[`/deliver`](deliver.md), [`/audit-to-stories`](audit-to-stories.md),
|
|
164
|
+
[`helpers/plan-reference.md`](helpers/plan-reference.md) (on-demand detail),
|
|
165
|
+
[`core/scope-triage`](../skills/core/scope-triage/SKILL.md) — optional
|
|
166
|
+
split-advisory notes only (no routing verdict).
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [2.25.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.24.0...mandrel-v2.25.0) (2026-08-01)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
* enforce the no-rerun verifier: disarm auto-merge on the first red and gate re-arming on the head SHA moving ([#4865](https://github.com/dsj1984/mandrel/issues/4865)) ([#4867](https://github.com/dsj1984/mandrel/issues/4867)) ([b95267f](https://github.com/dsj1984/mandrel/commit/b95267fe49cd27ca7cd42bb3bc7ae0c6ceaa5a3a))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
* **crap:** measure coordinate mixing, not ordinary drift, in the preview gate (refs [#4871](https://github.com/dsj1984/mandrel/issues/4871)) ([#4881](https://github.com/dsj1984/mandrel/issues/4881)) ([63bcb66](https://github.com/dsj1984/mandrel/commit/63bcb66ab46471cfc1b8ad957d60dd1526d0f679))
|
|
16
|
+
* escape the remaining raw NUL delimiters so these diffs are reviewable ([#4896](https://github.com/dsj1984/mandrel/issues/4896)) ([992fc46](https://github.com/dsj1984/mandrel/commit/992fc46b7675736c8fc7af546600cc59ef5ec96d))
|
|
17
|
+
* make the sanctioned CI watch survive a late-arriving required context, and pass --repo through as a real gh flag ([#4890](https://github.com/dsj1984/mandrel/issues/4890)) ([#4893](https://github.com/dsj1984/mandrel/issues/4893)) ([00c23f4](https://github.com/dsj1984/mandrel/commit/00c23f45f57f6b9d11c7314b92f337a31dda4c2b))
|
|
18
|
+
* normalize audit severity and labels without re-minting fingerprints, carry dedupe provenance, and give the lenses a built-but-not-wired mandate ([#4877](https://github.com/dsj1984/mandrel/issues/4877)) ([#4888](https://github.com/dsj1984/mandrel/issues/4888)) ([04f8937](https://github.com/dsj1984/mandrel/commit/04f89373e4dcaa0a47d5501381fb02b5e29bcef6))
|
|
19
|
+
* pin the run's base branch at init and fail closed when config changed mid-run (refs [#4891](https://github.com/dsj1984/mandrel/issues/4891)) ([#4894](https://github.com/dsj1984/mandrel/issues/4894)) ([8099020](https://github.com/dsj1984/mandrel/commit/8099020a9ea2e662c19a1c8a3d9e32d6269df863))
|
|
20
|
+
* **plan:** reconcile the persist body contract and stop acceptance-eval burning a round on every read (refs [#4874](https://github.com/dsj1984/mandrel/issues/4874)) ([#4882](https://github.com/dsj1984/mandrel/issues/4882)) ([33512b4](https://github.com/dsj1984/mandrel/commit/33512b48702e50d9ebaf98dffc396a9ce0091290))
|
|
21
|
+
* ratchet context-budget shrinkage and stop the baseline updaters writing on --help (refs [#4872](https://github.com/dsj1984/mandrel/issues/4872)) ([#4879](https://github.com/dsj1984/mandrel/issues/4879)) ([8d445a4](https://github.com/dsj1984/mandrel/commit/8d445a45e8a4c57b65573213042fb4aec557af08))
|
|
22
|
+
* stamp the version cache with the real clock and end unquoted env values at an unescaped comment marker (refs [#4878](https://github.com/dsj1984/mandrel/issues/4878)) ([#4883](https://github.com/dsj1984/mandrel/issues/4883)) ([d73ee10](https://github.com/dsj1984/mandrel/commit/d73ee10c2f15c3698705281a5979c7394eb03fa4))
|
|
23
|
+
* stop close-resume opening duplicate empty PRs and stop the PR watcher reporting green on a blocked PR ([#4873](https://github.com/dsj1984/mandrel/issues/4873)) ([#4884](https://github.com/dsj1984/mandrel/issues/4884)) ([9d12f8d](https://github.com/dsj1984/mandrel/commit/9d12f8dcd4badbe3b657f1526c284f7689bf04ee))
|
|
24
|
+
* stop the CRAP row builder silently mixing transpiled and original line coordinates, and refuse to compare on an unsound basis ([#4866](https://github.com/dsj1984/mandrel/issues/4866)) ([#4868](https://github.com/dsj1984/mandrel/issues/4868)) ([a6c8c88](https://github.com/dsj1984/mandrel/commit/a6c8c8866c72b1372eb5fd5b7232fb3833e33f93))
|
|
25
|
+
* stop the test suite writing fixture friction signals into the real ledger, and stop unresolvable Story ids reaching an auto-filed ticket ([#4892](https://github.com/dsj1984/mandrel/issues/4892)) ([#4895](https://github.com/dsj1984/mandrel/issues/4895)) ([a607de4](https://github.com/dsj1984/mandrel/commit/a607de49a09c778c1fbefe68d4c57d6a75f0b648))
|
|
26
|
+
* warn about the un-waivable light-path verdict up front, de-conflict on actual footprints, and reconcile the concurrency override ([#4875](https://github.com/dsj1984/mandrel/issues/4875)) ([#4880](https://github.com/dsj1984/mandrel/issues/4880)) ([16d4958](https://github.com/dsj1984/mandrel/commit/16d49584227d783695bb36043e29c64a58aaab39))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
* reclaim workflow-spine byte headroom and move the close-and-land tail to the orchestrator (refs [#4876](https://github.com/dsj1984/mandrel/issues/4876)) ([#4885](https://github.com/dsj1984/mandrel/issues/4885)) ([7d981cd](https://github.com/dsj1984/mandrel/commit/7d981cde4f8c3d1047a09e64d1f193b85fbfd4b6))
|
|
32
|
+
* widen workflow-spine and agent-boot headroom to a real editing margin (refs [#4876](https://github.com/dsj1984/mandrel/issues/4876)) ([#4889](https://github.com/dsj1984/mandrel/issues/4889)) ([45f5482](https://github.com/dsj1984/mandrel/commit/45f548238c9c82a1e36df11320b73c8f9dea27f5))
|
|
33
|
+
|
|
34
|
+
## [2.24.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.23.0...mandrel-v2.24.0) (2026-07-31)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
* **deliver:** hold the Story assignee-lease until the PR is confirmed merged (refs [#4860](https://github.com/dsj1984/mandrel/issues/4860)) ([#4861](https://github.com/dsj1984/mandrel/issues/4861)) ([d722a65](https://github.com/dsj1984/mandrel/commit/d722a6558f2fa1af6aa428d2f8549101c4bedc23))
|
|
40
|
+
* **maintainability:** score the Babel AST the escomplex kernel actually parses ([#4859](https://github.com/dsj1984/mandrel/issues/4859)) ([279b86f](https://github.com/dsj1984/mandrel/commit/279b86fa49c48454b5e8421ca1e0bb01f54a3bba))
|
|
41
|
+
* scope the light diff backstop by change magnitude, and recycle the receipt instead of orphaning it ([#4856](https://github.com/dsj1984/mandrel/issues/4856)) ([#4857](https://github.com/dsj1984/mandrel/issues/4857)) ([114b479](https://github.com/dsj1984/mandrel/commit/114b4797f248bf00064206f32f490413e28276fb))
|
|
42
|
+
|
|
5
43
|
## [2.23.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.22.0...mandrel-v2.23.0) (2026-07-30)
|
|
6
44
|
|
|
7
45
|
|
package/lib/cli/update.js
CHANGED
|
@@ -247,10 +247,15 @@ function resolveProjectRoot() {
|
|
|
247
247
|
* `mandrel` version via the daily freshness cache (`version-check.js`).
|
|
248
248
|
*
|
|
249
249
|
* When `bypassCache` is `true` (the default for an explicit `mandrel update`
|
|
250
|
-
* call — Story #4046 A1b),
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
250
|
+
* call — Story #4046 A1b), `isStale` is asked to skip its freshness read via
|
|
251
|
+
* `forceRefresh`, so any existing cache is ignored and exactly one network
|
|
252
|
+
* probe is issued. The cache is still written so the post-update
|
|
253
|
+
* `version-current` advisory has a fresh baseline.
|
|
254
|
+
*
|
|
255
|
+
* The bypass deliberately does **not** shift `now`: the same clock is stamped
|
|
256
|
+
* into the refreshed `checkedAt`, so a shifted clock would persist a
|
|
257
|
+
* ~48h-future timestamp and defeat the 24h window for every later passive
|
|
258
|
+
* check until real time caught up (Story #4878).
|
|
254
259
|
*
|
|
255
260
|
* When `bypassCache` is `false` (passive staleness checks only), the normal
|
|
256
261
|
* 24h-cache semantics apply: a fresh cache returns the cached version with
|
|
@@ -281,16 +286,14 @@ async function defaultResolveTargetVersion({
|
|
|
281
286
|
bypassCache = false,
|
|
282
287
|
log = () => {},
|
|
283
288
|
} = {}) {
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
-
const effectiveNow = bypassCache
|
|
288
|
-
? new Date(now.getTime() + 48 * 60 * 60 * 1000)
|
|
289
|
-
: now;
|
|
289
|
+
// Bypass the cache by telling isStale to skip its freshness read — never by
|
|
290
|
+
// shifting `now`, which is also the clock persisted as `checkedAt`
|
|
291
|
+
// (Story #4046 A1b forcing the probe; Story #4878 keeping the stamp honest).
|
|
290
292
|
const result = await isStale({
|
|
291
293
|
cachePath,
|
|
292
|
-
now
|
|
294
|
+
now,
|
|
293
295
|
runner,
|
|
296
|
+
forceRefresh: bypassCache,
|
|
294
297
|
fs,
|
|
295
298
|
log,
|
|
296
299
|
});
|
package/lib/cli/version-check.js
CHANGED
|
@@ -142,10 +142,17 @@ export function refreshCache({
|
|
|
142
142
|
* The freshness comparison uses `>=` so an exactly-24h-old cache counts as
|
|
143
143
|
* stale (the boundary is treated as "due for refresh").
|
|
144
144
|
*
|
|
145
|
+
* `forceRefresh` skips the freshness read entirely and always probes. It is
|
|
146
|
+
* the supported way to bypass the cache: callers must **not** fake staleness
|
|
147
|
+
* by shifting `now` forward, because `now` is also the clock stamped into the
|
|
148
|
+
* refreshed `checkedAt` — a shifted clock persists a future timestamp and
|
|
149
|
+
* defeats the freshness window for every later reader (Story #4878).
|
|
150
|
+
*
|
|
145
151
|
* @param {{
|
|
146
152
|
* cachePath: string,
|
|
147
153
|
* now?: Date,
|
|
148
154
|
* runner: () => (string | Promise<string>),
|
|
155
|
+
* forceRefresh?: boolean,
|
|
149
156
|
* fs?: typeof import('node:fs'),
|
|
150
157
|
* log?: (msg: string) => void,
|
|
151
158
|
* }} opts
|
|
@@ -160,10 +167,11 @@ export async function isStale({
|
|
|
160
167
|
cachePath,
|
|
161
168
|
now = new Date(),
|
|
162
169
|
runner,
|
|
170
|
+
forceRefresh = false,
|
|
163
171
|
fs = nodeFs,
|
|
164
172
|
log = () => {},
|
|
165
173
|
}) {
|
|
166
|
-
const cached = readCache({ cachePath, fs });
|
|
174
|
+
const cached = forceRefresh ? null : readCache({ cachePath, fs });
|
|
167
175
|
|
|
168
176
|
if (cached) {
|
|
169
177
|
const ageMs = now.getTime() - new Date(cached.checkedAt).getTime();
|
package/package.json
CHANGED