mandrel 2.21.0 → 2.23.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 +1 -1
- package/.agents/agents/story-worker.md +5 -0
- package/.agents/docs/configuration.md +1 -0
- package/.agents/instructions.md +14 -17
- package/.agents/rules/git-conventions.md +1 -1
- package/.agents/rules/known-tooling-behavior.md +114 -0
- package/.agents/schemas/agentrc.schema.json +6 -0
- package/.agents/scripts/check-context-budget.js +134 -2
- package/.agents/scripts/diagnose-friction.js +95 -4
- package/.agents/scripts/lib/audit-suite/selector.js +275 -162
- package/.agents/scripts/lib/config/temp-paths.js +51 -7
- package/.agents/scripts/lib/config-settings-schema-delivery.js +8 -0
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +604 -57
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +72 -21
- package/.agents/scripts/lib/label-constants.js +12 -1
- package/.agents/scripts/lib/observability/runtime-friction.js +41 -1
- package/.agents/scripts/lib/observability/signals-writer.js +133 -14
- package/.agents/scripts/lib/observability/source-classifier.js +131 -1
- package/.agents/scripts/lib/orchestration/code-review.js +12 -0
- package/.agents/scripts/lib/orchestration/complexity-gate.js +51 -46
- package/.agents/scripts/lib/orchestration/resolve-stories.js +17 -14
- package/.agents/scripts/lib/orchestration/retro-proposals.js +0 -0
- package/.agents/scripts/lib/orchestration/review-providers/degraded-gates.js +222 -0
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +18 -3
- package/.agents/scripts/lib/orchestration/review-providers/native.js +82 -126
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +10 -0
- package/.agents/scripts/lib/orchestration/review-providers/scoped-lint.js +300 -0
- package/.agents/scripts/lib/orchestration/run-epilogue.js +69 -7
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +18 -8
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-outcome.js +66 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +5 -1
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +380 -13
- package/.agents/scripts/lib/story-body/story-body.js +248 -174
- package/.agents/scripts/lib/templates/decomposer-prompts.js +1 -1
- package/.agents/scripts/resolve-stories.js +52 -33
- package/.agents/scripts/single-story-confirm-merge.js +5 -7
- package/.agents/workflows/helpers/deliver-digest.md +8 -6
- package/.agents/workflows/helpers/deliver-reference.md +15 -12
- package/.agents/workflows/helpers/deliver-story-reference.md +23 -21
- package/.agents/workflows/helpers/deliver-story.md +2 -2
- package/.agents/workflows/helpers/plan-reference.md +45 -4
- package/.agents/workflows/plan.md +21 -16
- package/docs/CHANGELOG.md +34 -0
- package/package.json +1 -1
|
@@ -20,16 +20,18 @@ description: >-
|
|
|
20
20
|
|
|
21
21
|
## 1. Dispatch — where the engine runs
|
|
22
22
|
|
|
23
|
-
Read `stories[].dispatchMode` from the `resolve-stories.js` envelope.
|
|
24
|
-
|
|
23
|
+
Read `stories[].dispatchMode` from the `resolve-stories.js` envelope.
|
|
24
|
+
`inline` names one indivisible resource — **the router's own session** — so one
|
|
25
|
+
rule produces it:
|
|
25
26
|
|
|
26
27
|
1. **Run topology.** A run resolving **one** Story is `inline`
|
|
27
28
|
whatever its shape — sub-agent isolation is load-bearing only against a
|
|
28
29
|
*concurrent* sibling racing the same checkout, and a one-Story run has none.
|
|
29
|
-
2. **
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
2. **Every other run is `subagent`.** A multi-Story run dispatches every Story
|
|
31
|
+
as a sub-agent however trivial its shape: a lite body does not conjure a
|
|
32
|
+
second session for a sibling to run in, and the wave tick may hand you the
|
|
33
|
+
whole set on one beat. Shape still sets ceremony and is reported alongside;
|
|
34
|
+
the `route::lite` label is a human-visible hint, never the control signal.
|
|
33
35
|
|
|
34
36
|
`inline` removes model-side fan-out only — no `story-worker` boot, no fresh
|
|
35
37
|
acceptance-critic spawn. **`subagent` and `inline` run the same engine**: same
|
|
@@ -61,23 +61,26 @@ a cache write at full rate; an inline continuation is a cache read at ~10%).
|
|
|
61
61
|
retained in full for multi-Story waves, and the rule changes **where** the
|
|
62
62
|
engine runs, never what runs — gates, PR, and terminal envelope are identical.
|
|
63
63
|
|
|
64
|
-
**
|
|
65
|
-
read the Story's `dispatchMode` from the resolver envelope
|
|
66
|
-
(`stories[].dispatchMode`,
|
|
67
|
-
`lib/orchestration/complexity-gate.js`
|
|
68
|
-
|
|
69
|
-
sensitive-path classes; the `route::lite` label is a human-visible hint only,
|
|
70
|
-
never the control signal, so a lost or never-written label cannot misroute
|
|
71
|
-
delivery): a Story with `dispatchMode: "inline"` executes
|
|
72
|
-
[`deliver-story.md`](deliver-story.md) **inline in this session** — no
|
|
64
|
+
**Read the mode; never infer it from shape.** Before spawning
|
|
65
|
+
anything, read the Story's `dispatchMode` from the resolver envelope
|
|
66
|
+
(`stories[].dispatchMode`, produced by `resolveStoryDispatchMode` in
|
|
67
|
+
`lib/orchestration/complexity-gate.js`). A Story with `dispatchMode: "inline"`
|
|
68
|
+
executes [`deliver-story.md`](deliver-story.md) **inline in this session** — no
|
|
73
69
|
`story-worker` sub-agent boot and no fresh acceptance-critic sub-agents
|
|
74
70
|
(sub-agent boots are the dominant deliver-phase token cost at trivial scope) —
|
|
75
71
|
threading the same `docsDigestPath` / `checklistPath` / change-set discipline
|
|
76
72
|
as a spawned worker. Inline removes model-side fan-out only: every
|
|
77
73
|
`single-story-close.js` gate, the PR to `main`, and the terminal envelope are
|
|
78
|
-
identical.
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
identical.
|
|
75
|
+
|
|
76
|
+
**A trivial shape does not buy that session.** Only the
|
|
77
|
+
one-Story rule above yields `inline`; every Story of a multi-Story run comes
|
|
78
|
+
back `subagent` however lite its body, because the ready set below may offer
|
|
79
|
+
several Stories on one beat and a session cannot be split between them. The
|
|
80
|
+
Story's derived shape is still reported (it sets ceremony, and a sensitive
|
|
81
|
+
footprint keeps the fresh acceptance critic), and the `route::lite` label
|
|
82
|
+
remains a human-visible hint only, never the control signal — a lost or
|
|
83
|
+
never-written label cannot misroute delivery.
|
|
81
84
|
|
|
82
85
|
**Dispatch each `ready` Story (role-scoped by default).** When
|
|
83
86
|
`delivery.routing.roleScopedAgents` is enabled (the **default**) and the host
|
|
@@ -133,23 +133,25 @@ and the `rules/security-baseline.md` MUSTs all run exactly as for a
|
|
|
133
133
|
full-ceremony Story. The lite route's `preserves` field is the machine-readable
|
|
134
134
|
record of those non-negotiables; there is no lite-specific gate bypass.
|
|
135
135
|
|
|
136
|
-
**Deliver derives the route from the Story body's shape
|
|
137
|
-
Persist stamps a lite cohort's Stories with the
|
|
138
|
-
*human-visible hint only* (and ledgers the authored
|
|
139
|
-
reason plus per-Story shape evidence — on the
|
|
140
|
-
checkpoint); the label is never the control signal.
|
|
141
|
-
route from the fetched Story body via
|
|
142
|
-
(`lib/orchestration/complexity-gate.js`) — the same
|
|
143
|
-
`deriveChangeLevel` applies to the landed diff at close:
|
|
144
|
-
acceptance count, creates-vs-refactors mix, sensitive-path
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
136
|
+
**Deliver derives the route from the Story body's shape — and the
|
|
137
|
+
dispatch mode from the run.** Persist stamps a lite cohort's Stories with the
|
|
138
|
+
`route::lite` label as a *human-visible hint only* (and ledgers the authored
|
|
139
|
+
verdict — recorded reason plus per-Story shape evidence — on the
|
|
140
|
+
`story-plan-state` checkpoint); the label is never the control signal.
|
|
141
|
+
`/deliver` computes the route from the fetched Story body via
|
|
142
|
+
`resolveStoryDispatchMode` (`lib/orchestration/complexity-gate.js`) — the same
|
|
143
|
+
shape taxonomy `deriveChangeLevel` applies to the landed diff at close:
|
|
144
|
+
`changes[]` count, acceptance count, creates-vs-refactors mix, sensitive-path
|
|
145
|
+
classes. A footprint intersecting a sensitive-path class derives `full` —
|
|
146
|
+
sensitivity wins, and the Story keeps its fresh acceptance critic.
|
|
147
|
+
|
|
148
|
+
That derived route sets ceremony. It does **not** set the dispatch mode,
|
|
149
|
+
because `inline` names one indivisible resource — the router's own session —
|
|
150
|
+
and only run topology can say whether it is free: a **single-Story run**
|
|
151
|
+
executes inline, and every Story of a multi-Story run dispatches as a
|
|
152
|
+
`story-worker` sub-agent whatever its shape — a lite shape makes the work
|
|
153
|
+
cheap, it does not conjure a second session for a sibling. Inline
|
|
154
|
+
execution changes the isolation only: the engine, every script gate, and the
|
|
153
155
|
terminal envelope are byte-identical either way.
|
|
154
156
|
|
|
155
157
|
---
|
|
@@ -247,10 +249,10 @@ Resolve fresh-vs-inline acceptance critics per AC-cluster with
|
|
|
247
249
|
floor forces `fresh`). Review depth reads the same derived level via
|
|
248
250
|
`review-depth.js` inside close, so the two decisions cannot disagree.
|
|
249
251
|
|
|
250
|
-
**
|
|
251
|
-
|
|
252
|
-
acceptance critic **inline** — do not spawn fresh-context critic
|
|
253
|
-
regardless of what the profile would otherwise resolve. The self-eval rigor
|
|
252
|
+
**Inline-dispatch override.** When the Story dispatches
|
|
253
|
+
`inline` (`resolveStoryDispatchMode` → `inline`, i.e. a single-Story run), run
|
|
254
|
+
every acceptance critic **inline** — do not spawn fresh-context critic
|
|
255
|
+
sub-agents regardless of what the profile would otherwise resolve. The self-eval rigor
|
|
254
256
|
(scoring each `acceptance[]` item against the one computed change set, with
|
|
255
257
|
`verify[]` output as evidence) is unchanged; only the sub-agent boot is
|
|
256
258
|
removed. Hard gates are untouched.
|
|
@@ -22,8 +22,8 @@ mandatoryReads: [deliver-digest.md]
|
|
|
22
22
|
|
|
23
23
|
## Overview
|
|
24
24
|
|
|
25
|
-
The **one** delivery engine in v2 — every Story (a
|
|
26
|
-
|
|
25
|
+
The **one** delivery engine in v2 — every Story (a **one-Story** run
|
|
26
|
+
goes inline with inline critics; engine, gates, envelope byte-identical):
|
|
27
27
|
|
|
28
28
|
```text
|
|
29
29
|
single-story-init.js → implement + commits → derived-level ceremony
|
|
@@ -36,6 +36,46 @@ 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
|
+
## Unknown triage — AFK vs HITL
|
|
40
|
+
|
|
41
|
+
Every open question interrogation surfaces is triaged by **who can resolve
|
|
42
|
+
it**, not parked in one bucket (a shape borrowed from the Wayfinder skill's
|
|
43
|
+
HITL/AFK ticket typing):
|
|
44
|
+
|
|
45
|
+
- **AFK** (away from keyboard — the agent resolves it alone): the answer is a
|
|
46
|
+
fact something already records — third-party docs, a dependency's API
|
|
47
|
+
surface, observable behavior of this repo. Research it during interrogation
|
|
48
|
+
(per `.agents/instructions.md` § 1.C) and fold the answer into the plan as a
|
|
49
|
+
verified claim. An AFK unknown never becomes a Key Assumption — an
|
|
50
|
+
assumption standing in for a checkable fact is just an unchecked fact.
|
|
51
|
+
- **HITL** (human in the loop — only the operator can resolve it): a genuine
|
|
52
|
+
product or architecture call — what to support, what to drop, which
|
|
53
|
+
trade-off to prefer. Nothing the agent reads can answer it; presenting a
|
|
54
|
+
researched recommendation is fine, deciding is not.
|
|
55
|
+
|
|
56
|
+
Boundary examples: *"does library X support streaming?"* is AFK (read its
|
|
57
|
+
docs); *"should we drop Node 18 support?"* is HITL (a support-policy call);
|
|
58
|
+
*"does our CLI already validate this flag?"* is AFK (read the code);
|
|
59
|
+
*"which of two valid schema shapes should the new field use?"* is HITL when
|
|
60
|
+
both fit — but first verify it is not settled by an existing convention,
|
|
61
|
+
which would make it AFK.
|
|
62
|
+
|
|
63
|
+
**Attended runs** present the HITL list at Gate #1 as "needs your decision",
|
|
64
|
+
one line each, alongside the sharpened intent. **Under `--yes`** nobody is at
|
|
65
|
+
the keyboard: AFK unknowns are researched exactly as in an attended run, and
|
|
66
|
+
each HITL unknown degrades to a declarative Key Assumption that names the
|
|
67
|
+
default chosen and marks it a decision-made-by-default, e.g.:
|
|
68
|
+
|
|
69
|
+
> **Key Assumption (decision-made-by-default):** new-style envelopes only;
|
|
70
|
+
> re-emitting legacy envelopes was ruled out by default, not by the operator.
|
|
71
|
+
|
|
72
|
+
(Keep the assumption itself declarative — "flag if wrong" phrasing trips the
|
|
73
|
+
open-question hygiene lint, and the deliverer cannot answer it anyway.)
|
|
74
|
+
|
|
75
|
+
The marker keeps the operator's undelegated decisions findable after the
|
|
76
|
+
fact: reviewing a `--yes` plan means scanning its decisions-made-by-default,
|
|
77
|
+
not re-deriving which assumptions were really the agent's to make.
|
|
78
|
+
|
|
39
79
|
## Gate #1 → the light path (in-session handoff)
|
|
40
80
|
|
|
41
81
|
On a confirmed `deliverLightSuggestion`, `/plan` routes into
|
|
@@ -120,10 +160,11 @@ decision:
|
|
|
120
160
|
lite cohort's Stories with **`route::lite`** as a *human-visible hint only* —
|
|
121
161
|
`/deliver` computes the route from each fetched Story body via the same shape
|
|
122
162
|
function at dispatch, so neither a lost label nor an unread marker can
|
|
123
|
-
misroute delivery: a lite-shaped Story
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
the
|
|
163
|
+
misroute delivery: a lite-shaped Story derives `lite` even with the label
|
|
164
|
+
absent, and a sensitive-footprint Story routes `full` and keeps its fresh
|
|
165
|
+
critic even with the label present. The derived route sets ceremony, not
|
|
166
|
+
where the engine runs — sub-agent boots are collapsed by a **single-Story
|
|
167
|
+
run**, never by a trivial shape. The `route::*` axis stays runtime-derived: hand-authored
|
|
127
168
|
`route::*` entries in `labels[]` are dropped by persist.
|
|
128
169
|
|
|
129
170
|
The knobs (`planning.complexityGate.{enabled, maxArtifacts}`) are documented
|
|
@@ -14,7 +14,7 @@ description:
|
|
|
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, then act
|
|
17
|
+
it, then act**:
|
|
18
18
|
|
|
19
19
|
| Invocation | Mode | Behavior |
|
|
20
20
|
| --- | --- | --- |
|
|
@@ -24,11 +24,10 @@ it, then act**; there is nothing for them to remember:
|
|
|
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
|
-
**Resolving a bare id.**
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
wasted run. Ask **only** for an open Story already at `agent::ready`.
|
|
27
|
+
**Resolving a bare id.** Read live state rather than asking: `agent::done` can
|
|
28
|
+
only be amended, an open unplanned issue can only be planned. **Announce the
|
|
29
|
+
derivation** — "4712 is `agent::done` → amending" — so a wrong read costs one
|
|
30
|
+
correction. Ask **only** for an open Story already at `agent::ready`.
|
|
32
31
|
|
|
33
32
|
`--body` is **not** a `/plan` entry; persist goes through `plan-persist.js`.
|
|
34
33
|
|
|
@@ -62,19 +61,26 @@ node .agents/scripts/plan-context.js --seed "<seed>" \
|
|
|
62
61
|
|
|
63
62
|
**Always pass `--out`.** Persist auto-discovers the envelope from `--plan-dir`
|
|
64
63
|
and derives source ids from its `sourceTickets[]`; the CLI also writes
|
|
65
|
-
**`stories.template.json`**,
|
|
64
|
+
**`stories.template.json`**, step 2's skeleton.
|
|
66
65
|
|
|
67
66
|
The envelope carries docs context, the story-author
|
|
68
67
|
prompt, `sourceTickets[]`, `duplicates[]` (open **Stories**, never Epics), and
|
|
69
68
|
advisory `complexitySignals` (**no routing authority**). A trivial scope earns
|
|
70
69
|
`--route-downgrade-reason "<why>"` at persist — shape-validated, failing closed
|
|
71
|
-
to `full` ([detail](helpers/plan-reference.md)).
|
|
72
|
-
|
|
70
|
+
to `full` ([detail](helpers/plan-reference.md)).
|
|
71
|
+
|
|
72
|
+
**Triage each unknown by resolver**
|
|
73
|
+
([detail](helpers/plan-reference.md)): an **AFK** unknown (research settles
|
|
74
|
+
it) is resolved before authoring, never assumed; a **HITL** unknown (an
|
|
75
|
+
operator call) goes to Gate #1 as "needs your decision". Under `--yes`, do
|
|
76
|
+
not ask free-form operator questions — AFK unknowns are still researched;
|
|
77
|
+
only HITL unknowns land in Key Assumptions, each marked a
|
|
78
|
+
decision-made-by-default.
|
|
73
79
|
|
|
74
80
|
**Gate #1** — STOP to confirm the sharpened plan intent and any
|
|
75
81
|
duplicate-candidate review. Under `--yes`, auto-proceed.
|
|
76
82
|
|
|
77
|
-
|
|
83
|
+
On a truthy `deliverLightSuggestion.suggested`, offer —
|
|
78
84
|
**advisory, never an automatic reroute** — to deliver the seed instead of
|
|
79
85
|
planning it. On confirm, route **in this session** into
|
|
80
86
|
[`helpers/deliver-light.md`](helpers/deliver-light.md), filling its gate from
|
|
@@ -88,9 +94,9 @@ A truthy `complexitySignals.uiSurface` marks a UI-touching plan: name
|
|
|
88
94
|
### 2. Author
|
|
89
95
|
|
|
90
96
|
**One-shot authoring.** Start from `stories.template.json`; author
|
|
91
|
-
`stories.json` in one pass.
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
`stories.json` in one pass. `body` is a markdown string **or** a structured
|
|
98
|
+
object; persist parses either, serializes the canonical markdown, and syncs
|
|
99
|
+
top-level `acceptance[]` /
|
|
94
100
|
`verify[]` into it — never dual-author those lists.
|
|
95
101
|
|
|
96
102
|
**Grounding = your reads + Phase 8.** Nothing inventories the repo for you:
|
|
@@ -149,9 +155,8 @@ node .agents/scripts/plan-persist.js \
|
|
|
149
155
|
[--source-tickets 123,456]
|
|
150
156
|
```
|
|
151
157
|
|
|
152
|
-
At lite shape, `--chain-on-clean` chains
|
|
153
|
-
one round-trip
|
|
154
|
-
review round-trip.
|
|
158
|
+
At lite shape, `--chain-on-clean` chains a clean dry-run into the real persist
|
|
159
|
+
in one round-trip; a full plan keeps its review round-trip.
|
|
155
160
|
|
|
156
161
|
Persist creates `type::story` issue(s) plus a `plan-run::<id>` grouping label
|
|
157
162
|
(**metadata only**); N>1 `depends_on` edges become `blocked by #<id>` footers.
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [2.23.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.22.0...mandrel-v2.23.0) (2026-07-30)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
* **plan:** triage interrogation unknowns by resolver — AFK research vs HITL operator decision (refs [#4845](https://github.com/dsj1984/mandrel/issues/4845)) ([#4846](https://github.com/dsj1984/mandrel/issues/4846)) ([3cfcd00](https://github.com/dsj1984/mandrel/commit/3cfcd00f3885fc588bdf2bb4597118a790f9b270))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
* **diagnose-friction:** name the signal that killed a child instead of reporting "Unknown exit code null" and exiting 0 ([#4851](https://github.com/dsj1984/mandrel/issues/4851)) ([#4853](https://github.com/dsj1984/mandrel/issues/4853)) ([1985be1](https://github.com/dsj1984/mandrel/commit/1985be1b97d751d094acf7dd371b97124039cc6d))
|
|
16
|
+
* **rollup:** describe the friction corpus by its own window, not the triggering run (refs [#4850](https://github.com/dsj1984/mandrel/issues/4850)) ([#4852](https://github.com/dsj1984/mandrel/issues/4852)) ([d866697](https://github.com/dsj1984/mandrel/commit/d8666976be30d218115b9db32602359d55379f88))
|
|
17
|
+
* **rollup:** track anchorKind in the triggering-anchor label ([#4854](https://github.com/dsj1984/mandrel/issues/4854)) ([b7e4ddf](https://github.com/dsj1984/mandrel/commit/b7e4ddf8c362f7c31c2abd73ee181a94f78f6dd2))
|
|
18
|
+
|
|
19
|
+
## [2.22.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.21.0...mandrel-v2.22.0) (2026-07-30)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
* **context-budget:** hold agentBoot baseline rows in sync with the tree (refs [#4830](https://github.com/dsj1984/mandrel/issues/4830)) ([#4831](https://github.com/dsj1984/mandrel/issues/4831)) ([8a14f8b](https://github.com/dsj1984/mandrel/commit/8a14f8b85951819ef391382355fa2ba4d7998d43))
|
|
25
|
+
* correct the always-on instruction spine and give its budget one honest definition (refs [#4821](https://github.com/dsj1984/mandrel/issues/4821)) ([#4822](https://github.com/dsj1984/mandrel/issues/4822)) ([436d7f0](https://github.com/dsj1984/mandrel/commit/436d7f05d36bd35a6b1a934f19e9a85b37b69f93))
|
|
26
|
+
* feedback-loop: give an auto-filed issue a stable identity, an actionable body, and no path to the live API from a test ([#4837](https://github.com/dsj1984/mandrel/issues/4837)) ([#4838](https://github.com/dsj1984/mandrel/issues/4838)) ([43bbf57](https://github.com/dsj1984/mandrel/commit/43bbf578fc129c9b15ad068925722732fd264c7f))
|
|
27
|
+
* make an inline dispatch verdict mean the engine can actually run inline (refs [#4829](https://github.com/dsj1984/mandrel/issues/4829)) ([#4832](https://github.com/dsj1984/mandrel/issues/4832)) ([f554830](https://github.com/dsj1984/mandrel/commit/f55483085eb201aa126275bb0ed6250fc5a452a3))
|
|
28
|
+
* make the framework limb of the feedback loop reachable (refs [#4824](https://github.com/dsj1984/mandrel/issues/4824)) ([#4827](https://github.com/dsj1984/mandrel/issues/4827)) ([91816a2](https://github.com/dsj1984/mandrel/commit/91816a2dceda127a0cdf1ad3f48f53d7770cbf0b))
|
|
29
|
+
* make the friction roll-up file its proposals and make an all-empty result legible (refs [#4828](https://github.com/dsj1984/mandrel/issues/4828)) ([#4835](https://github.com/dsj1984/mandrel/issues/4835)) ([c017f9a](https://github.com/dsj1984/mandrel/commit/c017f9a9145122b15e41b55a9126a35171f3f1b9))
|
|
30
|
+
* review: stop the scoped-lint gate failing open silently, and fix why its runner cannot execute ([#4839](https://github.com/dsj1984/mandrel/issues/4839)) ([#4840](https://github.com/dsj1984/mandrel/issues/4840)) ([8f3f007](https://github.com/dsj1984/mandrel/commit/8f3f0072abef020439d555af698829ebfc8900f8))
|
|
31
|
+
* rules: ship a known-tooling-behavior rule so measured gate quirks stop being rediscovered per session ([#4825](https://github.com/dsj1984/mandrel/issues/4825)) ([#4826](https://github.com/dsj1984/mandrel/issues/4826)) ([2c20403](https://github.com/dsj1984/mandrel/commit/2c2040318055026307518de57b1433c11c56efc1))
|
|
32
|
+
* **test:** probe bins on either path separator so [#4839](https://github.com/dsj1984/mandrel/issues/4839)'s tests pass on Windows ([#4841](https://github.com/dsj1984/mandrel/issues/4841)) ([98e6048](https://github.com/dsj1984/mandrel/commit/98e6048774b7483717931d0b4a63179800815941))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
* restore clean-code gate headroom: cover CRAP-capacity shells, flatten selectAudits, table-drive story-body parsing ([#4842](https://github.com/dsj1984/mandrel/issues/4842)) ([#4843](https://github.com/dsj1984/mandrel/issues/4843)) ([cc5dc5e](https://github.com/dsj1984/mandrel/commit/cc5dc5e7eaf423612495441f0f8f9007dbb9b8a4))
|
|
38
|
+
|
|
5
39
|
## [2.21.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.20.0...mandrel-v2.21.0) (2026-07-28)
|
|
6
40
|
|
|
7
41
|
|
package/package.json
CHANGED