mandrel 2.38.0 → 2.40.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 +51 -11
- package/.agents/agents/auditor.md +5 -0
- package/.agents/docs/SDLC.md +21 -12
- package/.agents/docs/agentrc-reference.json +1 -4
- package/.agents/docs/configuration.md +2 -2
- package/.agents/instructions.md +17 -16
- package/.agents/schemas/agentrc.schema.json +6 -7
- package/.agents/scripts/audit-to-stories.js +510 -66
- package/.agents/scripts/generate-skills-index.js +158 -75
- package/.agents/scripts/lib/audit-to-stories/epic-grouping-directive.js +39 -0
- package/.agents/scripts/lib/audit-to-stories/ledger-commit.js +290 -0
- package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +94 -3
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +10 -0
- package/.agents/scripts/lib/changed-files.js +100 -9
- package/.agents/scripts/lib/config-settings-schema.js +25 -7
- package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
- package/.agents/scripts/lib/label-constants.js +18 -0
- package/.agents/scripts/lib/label-taxonomy.js +18 -5
- package/.agents/scripts/lib/orchestration/epic-container.js +186 -0
- package/.agents/scripts/lib/orchestration/epic-expansion.js +148 -0
- package/.agents/scripts/lib/orchestration/plan-persist/epic-ops.js +320 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +18 -0
- package/.agents/scripts/lib/orchestration/run-epilogue.js +130 -1
- package/.agents/scripts/lib/qa/resolve-qa-contract.js +58 -6
- package/.agents/scripts/lib/skills/skills-index.js +168 -0
- package/.agents/scripts/lib/skills/walk-skill-files.js +133 -9
- package/.agents/scripts/plan-persist.js +39 -1
- package/.agents/scripts/providers/github/sub-issue-add.js +218 -0
- package/.agents/scripts/quality-preview.js +50 -9
- package/.agents/scripts/resolve-stories.js +42 -2
- package/.agents/scripts/validate-skills.js +53 -66
- package/.agents/templates/docs/audit-sweep-runbook.md +169 -0
- package/.agents/workflows/audit-to-stories.md +85 -7
- package/.agents/workflows/helpers/audit-lens-core.md +24 -4
- package/.agents/workflows/helpers/deliver-reference.md +8 -0
- package/.agents/workflows/helpers/plan-reference.md +28 -0
- package/.agents/workflows/mandrel-deliver.md +47 -43
- package/.agents/workflows/mandrel-plan.md +44 -38
- package/.agents/workflows/qa-run.md +13 -5
- package/docs/CHANGELOG.md +28 -0
- package/package.json +1 -1
|
@@ -51,7 +51,8 @@ path). The report MUST include every section its lens template mandates — writ
|
|
|
51
51
|
`_No findings._` rather than omitting a section — and always an
|
|
52
52
|
`## Executive Summary` and a `## Detailed Findings` section. The Executive
|
|
53
53
|
Summary carries the self-cross-check `kept <k> / dropped <d>` line
|
|
54
|
-
([below](#self-cross-check))
|
|
54
|
+
([below](#self-cross-check)) and, beside it, the machine-readable severity
|
|
55
|
+
tally ([below](#severity-tally)).
|
|
55
56
|
|
|
56
57
|
Every finding under `## Detailed Findings` uses the shared 7-field skeleton
|
|
57
58
|
below. A lens may **add** fields (e.g. a WCAG success criterion, a CWE ID, a
|
|
@@ -64,9 +65,11 @@ drops or renames a shared field.
|
|
|
64
65
|
```markdown
|
|
65
66
|
## Executive Summary
|
|
66
67
|
|
|
67
|
-
[The lens's headline read
|
|
68
|
-
line. A lens may mandate additional report
|
|
69
|
-
findings — its own body names them.]
|
|
68
|
+
[The lens's headline read, the self-cross-check `kept <k> / dropped <d>` line,
|
|
69
|
+
and the severity tally line below. A lens may mandate additional report
|
|
70
|
+
sections between here and the findings — its own body names them.]
|
|
71
|
+
|
|
72
|
+
Severity tally: Critical <n> / High <n> / Medium <n> / Low <n>
|
|
70
73
|
|
|
71
74
|
## Detailed Findings
|
|
72
75
|
|
|
@@ -188,6 +191,23 @@ that rests on one of them:
|
|
|
188
191
|
A lens that keeps every finding still records `dropped 0` — the line's absence
|
|
189
192
|
is itself a defect (it means the pass did not run).
|
|
190
193
|
|
|
194
|
+
## Severity tally (mandatory, machine-readable) {#severity-tally}
|
|
195
|
+
|
|
196
|
+
The Executive Summary MUST also carry exactly one tally line, counting the
|
|
197
|
+
findings you kept:
|
|
198
|
+
|
|
199
|
+
```text
|
|
200
|
+
Severity tally: Critical <n> / High <n> / Medium <n> / Low <n>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Write every bucket, zeros included, and never count `Info` — the scale already
|
|
204
|
+
excludes it from scheduled work. This line is the report's own checksum:
|
|
205
|
+
`audit-to-stories --scan` cross-checks it against the findings its parser
|
|
206
|
+
extracted and names any disagreement as a **report failure**
|
|
207
|
+
(`summary.reportFailures[]`), and `--auto` refuses to file anything from a
|
|
208
|
+
report whose line is missing or wrong. A parse that silently drops findings is
|
|
209
|
+
otherwise indistinguishable from a clean audit.
|
|
210
|
+
|
|
191
211
|
## Execution strategy {#execution-strategy}
|
|
192
212
|
|
|
193
213
|
A lens is a self-contained, read-only unit of work — exactly the shape a
|
|
@@ -292,6 +292,14 @@ This executes, in order:
|
|
|
292
292
|
(files issues when auto-file is on; posts `follow-ups`).
|
|
293
293
|
- `sibling-coherence` — Spec/Acceptance coherence check across sibling bodies
|
|
294
294
|
(`plan-run-sibling-coherence`).
|
|
295
|
+
- `epic-close` — closes a container Epic once **every** child Story is
|
|
296
|
+
`agent::done`, as `completed`. This is the only completion cascade v2 has:
|
|
297
|
+
it closes the container and nothing else — no child status roll-up, no label
|
|
298
|
+
inheritance, no reopening. Because linkage is parent→child only, the parent
|
|
299
|
+
is found by scanning open `type::epic` issues, and only an Epic containing
|
|
300
|
+
one of *this run's* Stories is considered, so an unrelated container is
|
|
301
|
+
never swept. An Epic with an outstanding child is reported `pending` and
|
|
302
|
+
left open.
|
|
295
303
|
|
|
296
304
|
A single-Story run skips the epilogue — follow-ups are captured on merge
|
|
297
305
|
confirm instead (`captureStoryFollowUps`).
|
|
@@ -414,6 +414,34 @@ That is the whole point of running it first: a dry run that comes back clean
|
|
|
414
414
|
has already paid for every deterministic refusal, so the real persist has
|
|
415
415
|
nothing left to discover except network failure.
|
|
416
416
|
|
|
417
|
+
## The container Epic (Gate #3, N>2)
|
|
418
|
+
|
|
419
|
+
Above two Stories, `/mandrel-plan` offers to group them under one `type::epic`
|
|
420
|
+
container. Confirmed, persist opens it **after** the Stories — its body embeds
|
|
421
|
+
their issue numbers and its sub-issue edges need their database ids — and
|
|
422
|
+
links every created Story both ways it can: a `- [ ] #N` body checklist and a
|
|
423
|
+
native GitHub sub-issue edge. Both are written because each survives what the
|
|
424
|
+
other does not; the delivery-side reader unions them.
|
|
425
|
+
|
|
426
|
+
What the Epic must never carry: an `agent::*` label (that absence keeps it out
|
|
427
|
+
of the bare `/mandrel-deliver` ready list and outside the `type::story`-scoped
|
|
428
|
+
body lint), a `## Spec`, an `acceptance[]` / `verify[]`, or any path, finding
|
|
429
|
+
or rationale a child does not already hold. It is a container; unique content
|
|
430
|
+
here is content no delivering agent reads.
|
|
431
|
+
|
|
432
|
+
What the **Stories** never gain is an `Epic: #N` footer. Linkage is
|
|
433
|
+
parent→child only, which is exactly why every existing refusal of that footer
|
|
434
|
+
still stands and each Story stays independently deliverable (ADR
|
|
435
|
+
`20260905-5139`).
|
|
436
|
+
|
|
437
|
+
Degradation is deliberate: an unensurable `type::epic` label skips the Epic
|
|
438
|
+
entirely (an unlabelled container is not a container), while a failed
|
|
439
|
+
sub-issue edge only warns — the checklist still lists every child. Either way
|
|
440
|
+
the Stories are untouched and deliver by id. A resumed persist adopts an
|
|
441
|
+
existing Epic carrying the same fingerprint, which is keyed on the title **and
|
|
442
|
+
the exact child set**, so a run grouping different Stories never adopts the
|
|
443
|
+
wrong container.
|
|
444
|
+
|
|
417
445
|
## Ready means fully persisted
|
|
418
446
|
|
|
419
447
|
`agent::ready` is the **terminal** step, not part of the creating POST.
|
|
@@ -7,8 +7,8 @@ description:
|
|
|
7
7
|
|
|
8
8
|
# /mandrel-deliver
|
|
9
9
|
|
|
10
|
-
> **Lean spine.** Happy path + gate list. Sequencing, dispatch mechanics,
|
|
11
|
-
>
|
|
10
|
+
> **Lean spine.** Happy path + gate list. Sequencing, dispatch mechanics, intent
|
|
11
|
+
> phrases, ceremony and the epilogue live in on-demand
|
|
12
12
|
> [`helpers/deliver-reference.md`](helpers/deliver-reference.md) ("reference"
|
|
13
13
|
> below); the unplanned path in
|
|
14
14
|
> [`helpers/deliver-light.md`](helpers/deliver-light.md). Every delivery reads
|
|
@@ -21,9 +21,8 @@ close-and-land tail; Stories are implemented via
|
|
|
21
21
|
[`helpers/deliver-story.md`](helpers/deliver-story.md).
|
|
22
22
|
|
|
23
23
|
The dependency graph is **discovered, not declared** — `resolve-stories.js`
|
|
24
|
-
reads it from live state, so you can deliver Stories **across plan runs and
|
|
25
|
-
|
|
26
|
-
`route::lite` a body-derived hint only.
|
|
24
|
+
reads it from live state, so you can deliver Stories **across plan runs and over
|
|
25
|
+
time**. `plan-run::<id>` is filter metadata, never a resolution input.
|
|
27
26
|
|
|
28
27
|
## Inputs
|
|
29
28
|
|
|
@@ -36,25 +35,28 @@ you read:
|
|
|
36
35
|
| `/mandrel-deliver 4712` | ids | One Story via `helpers/deliver-story.md`, **inline in this session** — no `story-worker` spawn. |
|
|
37
36
|
| `/mandrel-deliver 4712 4713 …` | ids | Resolve the set, sequence by the discovered graph via `stories-wave-tick.js`, dispatch sub-agents. |
|
|
38
37
|
| `/mandrel-deliver 4712 - 4716` | ids | A **range** — every id in the inclusive span. |
|
|
38
|
+
| `/mandrel-deliver 4700` (a `type::epic`) | ids | The Epic's **open** child Stories. Mixes with Story ids. |
|
|
39
39
|
| `/mandrel-deliver add a --json flag to doctor` | prompt | Unplanned work: gate, author a receipt Story, land it — [`helpers/deliver-light.md`](helpers/deliver-light.md). |
|
|
40
40
|
|
|
41
41
|
**The discriminator is lexical and total.** An argument matching `^#?\d+$` is an
|
|
42
42
|
id, and `^#?\d+\s*[-–—]\s*#?\d+$` an inclusive **range** — pass one on as a
|
|
43
|
-
single unspaced token, never hand-expanded (reference
|
|
43
|
+
single unspaced token, never hand-expanded (reference). Either shape
|
|
44
44
|
means ids; anything else means a prompt. A **mixed** invocation (ids *and*
|
|
45
|
-
prose) is a **hard error** — refuse it and ask which was meant. A ticket
|
|
46
|
-
`type::story`, or
|
|
45
|
+
prose) is a **hard error** — refuse it and ask which was meant. A ticket that
|
|
46
|
+
is neither `type::story` nor `type::epic`, or that carries an `Epic: #N`
|
|
47
|
+
footer, is a hard error too — container Epics link parent→child only, so that
|
|
48
|
+
footer stays refused.
|
|
47
49
|
|
|
48
50
|
## Saying what you want
|
|
49
51
|
|
|
50
|
-
No flags to remember: state intent — *"…but I'll merge it myself"*, *"…one at
|
|
51
|
-
|
|
52
|
+
No flags to remember: state intent — *"…but I'll merge it myself"*, *"…one at a
|
|
53
|
+
time"* — and announce what you read. Phrasings and the flags they fill in:
|
|
52
54
|
reference § Intent phrases.
|
|
53
55
|
|
|
54
56
|
`--yes` is **runner-set, never operator-typed**: cron, `/loop` and headless
|
|
55
57
|
dispatch set it to mean *nobody is at the keyboard*, which fails the unplanned
|
|
56
|
-
path's over-scope stop closed to an envelope instead of a question. Never
|
|
57
|
-
|
|
58
|
+
path's over-scope stop closed to an envelope instead of a question. Never add it
|
|
59
|
+
to an attended run.
|
|
58
60
|
|
|
59
61
|
## Procedure
|
|
60
62
|
|
|
@@ -66,9 +68,11 @@ it to an operator or add it to an attended run.
|
|
|
66
68
|
`node .agents/scripts/resolve-stories.js --ids <id,id,...>`. It validates the
|
|
67
69
|
set and shows what will run: read `stories[]`, `dag[]` and `done[]` to
|
|
68
70
|
present the order in step 2, but do **not** thread them into step 3 — the
|
|
69
|
-
tick re-resolves the graph every beat.
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
tick re-resolves the graph every beat. An Epic id expands to its open child
|
|
72
|
+
Stories first — **announce it**. It hard-errors (exit 1) on an id that is
|
|
73
|
+
neither a Story nor an Epic, on an `Epic: #N` footer, on an Epic with no
|
|
74
|
+
open children, or on edges it cannot read — a missing gate would co-dispatch
|
|
75
|
+
against an unlanded blocker.
|
|
72
76
|
|
|
73
77
|
2. **Confirm (N>1).** Present the order; wait unless `--yes`.
|
|
74
78
|
|
|
@@ -87,62 +91,62 @@ it to an operator or add it to an attended run.
|
|
|
87
91
|
Each beat re-probes live state to derive done / in-flight itself; you never
|
|
88
92
|
compute them. `--dispatched` is the one thing you must supply — the
|
|
89
93
|
append-only list of every id you spawned this run. Cross-run de-confliction
|
|
90
|
-
via the assignee lease is automatic
|
|
91
|
-
Dispatch mechanics). Branch on the exit code:
|
|
94
|
+
via the assignee lease is automatic. Branch on the exit code:
|
|
92
95
|
- **0** — dispatch each `ready` id (already capped and overlap-free); an
|
|
93
96
|
empty `ready` with work in flight means "waiting", so keep looping;
|
|
94
97
|
`epilogueDue: true` means every Story is done — step 4.
|
|
95
98
|
- **2 / 3 / 4** — `cycleError` / `wedged` / `blocked`: stop the loop and
|
|
96
|
-
route per reference
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
route per reference. **4** is the protocol's HITL pause
|
|
100
|
+
([`instructions.md` § 1.J](../instructions.md)) — surface it and wait for
|
|
101
|
+
the operator; never poll.
|
|
99
102
|
|
|
100
103
|
4. **Close each hand-off** (§ Closing what the workers hand back), then, with
|
|
101
104
|
every Story landed, run the **per-run epilogue (N>1)**:
|
|
102
|
-
`node .agents/scripts/plan-run-epilogue.js --stories 101,102
|
|
103
|
-
|
|
105
|
+
`node .agents/scripts/plan-run-epilogue.js --stories 101,102`, which also
|
|
106
|
+
closes a container Epic whose children all landed. N=1 skips it
|
|
107
|
+
([reference](helpers/deliver-reference.md)).
|
|
104
108
|
|
|
105
109
|
5. **Correct what the change invalidated.** If a memory you recalled this
|
|
106
|
-
session is now wrong — a trap this landed, a budget it moved — fix that
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
session is now wrong — a trap this landed, a budget it moved — fix that entry
|
|
111
|
+
now, while both the old belief and the new fact are in context, and say so
|
|
112
|
+
when you report. No memory substrate → skip silently. Sweeping the whole pool
|
|
113
|
+
is [`/memory-consolidate`](memory-consolidate.md), not this step.
|
|
110
114
|
|
|
111
115
|
## Closing what the workers hand back {#tail}
|
|
112
116
|
|
|
113
117
|
**The tail is the orchestrator's, not the worker's.** A dispatched
|
|
114
118
|
`story-worker` stops at a pushed branch and returns a hand-off; **you** run
|
|
115
119
|
[`helpers/deliver-story.md`](helpers/deliver-story.md) Step 3
|
|
116
|
-
(`single-story-close.js`) for it, foreground, and relay
|
|
120
|
+
(`single-story-close.js`) for it, foreground, and relay its envelope.
|
|
117
121
|
|
|
118
|
-
**Serialize the tail.** Implementation runs in parallel; closing does not.
|
|
119
|
-
one Story at a time — closes contend on the base branch, the merge queue
|
|
120
|
-
the checkout. A worker handing back mid-close waits its turn.
|
|
122
|
+
**Serialize the tail.** Implementation runs in parallel; closing does not.
|
|
123
|
+
Close one Story at a time — closes contend on the base branch, the merge queue
|
|
124
|
+
and the checkout. A worker handing back mid-close waits its turn.
|
|
121
125
|
|
|
122
|
-
**A worker returning no terminal envelope is
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
**A worker returning no terminal envelope is expected**, not a failure to answer
|
|
127
|
+
with a re-dispatch: only close mints one. Close the pushed branch, or probe
|
|
128
|
+
read-only with `node .agents/scripts/deliver-recover.js --story <storyId>` and
|
|
129
|
+
resume what it names.
|
|
126
130
|
|
|
127
131
|
**Reading the outcome.** Each close ends the Story in one schema-validated
|
|
128
132
|
envelope — `landed` | `pending` | `blocked` | `failed`; statuses, exits and
|
|
129
|
-
fields are digest § 5. `pending` is **not** a failure — `nextCommand
|
|
130
|
-
it; run that, do not re-dispatch.
|
|
133
|
+
fields are digest § 5. `pending` is **not** a failure — run its `nextCommand`.
|
|
131
134
|
|
|
132
135
|
**Branch model (authoritative).** `story-<id>` → PR → `main` (squash +
|
|
133
136
|
required checks), per digest § 2; dependent Stories land sequentially. Ceremony
|
|
134
|
-
depth (profiles + derived level via `ceremony-routing.js`, review
|
|
135
|
-
|
|
137
|
+
depth (profiles + the derived level via `ceremony-routing.js`, which review
|
|
138
|
+
depth reads): reference § Ceremony.
|
|
136
139
|
|
|
137
140
|
## Constraints
|
|
138
141
|
|
|
139
142
|
- **Land or block — never a silent local build** (digest § 2). Attended delivers
|
|
140
143
|
default to close-and-land (`delivery.routing.closeAndLand: true`); rest at
|
|
141
|
-
`agent::closing` only when a human owns
|
|
142
|
-
- **`/mandrel-deliver` never plans.** Planned tickets come from
|
|
143
|
-
an over-scope prompt **escalates and
|
|
144
|
-
session to rescue it
|
|
145
|
-
§ Escalation is
|
|
144
|
+
`agent::closing` only when a human owns the merge.
|
|
145
|
+
- **`/mandrel-deliver` never plans.** Planned tickets come from
|
|
146
|
+
[`/mandrel-plan`](mandrel-plan.md), and an over-scope prompt **escalates and
|
|
147
|
+
ends** — never invoke `/mandrel-plan` in this session to rescue it
|
|
148
|
+
([`helpers/deliver-light.md`](helpers/deliver-light.md) § Escalation is
|
|
149
|
+
terminal).
|
|
146
150
|
|
|
147
151
|
## See also
|
|
148
152
|
|
|
@@ -7,14 +7,14 @@ description:
|
|
|
7
7
|
|
|
8
8
|
# /mandrel-plan
|
|
9
9
|
|
|
10
|
-
> **Lean spine.** Happy path + gate list; edge-case detail
|
|
10
|
+
> **Lean spine.** Happy path + gate list; edge-case detail is 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
|
-
`epic|story` verdict
|
|
17
|
-
it, act**:
|
|
16
|
+
`epic|story` verdict (Gate #3's container groups, never routes). **Derive the
|
|
17
|
+
mode from what the operator typed, announce it, act**:
|
|
18
18
|
|
|
19
19
|
| Invocation | Mode | Behavior |
|
|
20
20
|
| --- | --- | --- |
|
|
@@ -25,9 +25,9 @@ it, act**:
|
|
|
25
25
|
| `/mandrel-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
|
-
only be amended, an open unplanned issue
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
only be amended, an open unplanned issue only planned. **Announce the derivation** —
|
|
29
|
+
"4712 is `agent::done` → amending". Ask **only** for an open Story already at
|
|
30
|
+
`agent::ready`.
|
|
31
31
|
|
|
32
32
|
## Saying what you want
|
|
33
33
|
|
|
@@ -36,7 +36,7 @@ No flags to remember — state intent; the workflow fills in the CLI
|
|
|
36
36
|
|
|
37
37
|
`--yes` is **runner-set, never operator-typed** — cron, `/loop`, and headless
|
|
38
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
|
|
39
|
+
gates below (#1 and #2) and skips #3. Never offer it to an operator.
|
|
40
40
|
|
|
41
41
|
## Default-single split policy
|
|
42
42
|
|
|
@@ -62,46 +62,44 @@ and derives source ids from its `sourceTickets[]`; the CLI also writes
|
|
|
62
62
|
The envelope carries docs context, the story-author prompt, `sourceTickets[]`,
|
|
63
63
|
`duplicates[]` (open **Stories**, never Epics) and advisory `complexitySignals`
|
|
64
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)).
|
|
65
|
+
persist — shape-validated, failing closed to `full` ([detail](helpers/plan-reference.md)).
|
|
67
66
|
|
|
68
67
|
**Triage each unknown by resolver** ([detail](helpers/plan-reference.md)): an
|
|
69
68
|
**AFK** unknown (research settles it) is resolved before authoring, never
|
|
70
|
-
assumed; a **HITL** unknown
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
decision-made-by-default.
|
|
69
|
+
assumed; a **HITL** unknown goes to Gate #1. Under `--yes` do not ask free-form
|
|
70
|
+
operator questions — AFK unknowns are still researched; only HITL unknowns land
|
|
71
|
+
in Key Assumptions, each a decision-made-by-default.
|
|
74
72
|
|
|
75
73
|
**Gate #1** — STOP to confirm the sharpened plan intent and any
|
|
76
74
|
duplicate-candidate review. Under `--yes`, auto-proceed.
|
|
77
75
|
|
|
78
|
-
On a truthy `memoryPoolAdvisory.recommend`, name
|
|
79
|
-
|
|
76
|
+
On a truthy `memoryPoolAdvisory.recommend`, name
|
|
77
|
+
[`/memory-consolidate`](memory-consolidate.md) quoting its `reasons[]` —
|
|
78
|
+
advisory, never invoked here.
|
|
80
79
|
|
|
81
80
|
On a truthy `deliverLightSuggestion.suggested`, offer — advisory, never an
|
|
82
|
-
automatic reroute — to deliver the seed instead; on confirm
|
|
81
|
+
automatic reroute — to deliver the seed instead; on confirm route **in this
|
|
83
82
|
session** into [`helpers/deliver-light.md`](helpers/deliver-light.md), its gate
|
|
84
|
-
filled from this envelope. A truthy `complexitySignals.uiSurface`
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
filled from this envelope. A truthy `complexitySignals.uiSurface` names
|
|
84
|
+
[`/prototype`](prototype.md) as an option; never invoke it here.
|
|
85
|
+
[Both offers](helpers/plan-reference.md).
|
|
87
86
|
|
|
88
87
|
### 2. Author
|
|
89
88
|
|
|
90
89
|
**One-shot authoring.** Start from `stories.template.json`; author
|
|
91
90
|
`stories.json` in one pass. `body` is markdown **or** a structured object;
|
|
92
91
|
persist parses either, serializes canonical markdown and syncs top-level
|
|
93
|
-
`acceptance[]` / `verify[]`
|
|
92
|
+
`acceptance[]` / `verify[]` in — never dual-author those lists.
|
|
94
93
|
|
|
95
94
|
**Grounding = your reads + Phase 8.** Nothing inventories the repo: read each
|
|
96
|
-
file you cite; persist
|
|
97
|
-
|
|
98
|
-
[reference](helpers/plan-reference.md).
|
|
95
|
+
file you cite; persist hard-errors on any `{path, assumption}` absent from
|
|
96
|
+
the tree. Fields: [reference](helpers/plan-reference.md).
|
|
99
97
|
|
|
100
98
|
Artifacts under `temp/plan-<slug>/`: `stories.json` (**length 1 by default**;
|
|
101
99
|
over-budget Specs fail closed — split or tighten, never under `docs/`); optional
|
|
102
|
-
`techspec.md` (**N===1 only
|
|
103
|
-
`acceptance-manifest.json` (N>1
|
|
104
|
-
|
|
100
|
+
`techspec.md` (**N===1 only**, folded into `## Spec`) and
|
|
101
|
+
`acceptance-manifest.json` (N>1 — `--plan-acceptance`). Use the envelope
|
|
102
|
+
`systemPrompts.story`; split only under the policy above.
|
|
105
103
|
|
|
106
104
|
**Tickets mode:** every Story authors a top-level `supersedes[]`; persist
|
|
107
105
|
refuses a partial map ([shape](helpers/plan-reference.md)).
|
|
@@ -115,9 +113,9 @@ node .agents/scripts/plan-critics.js \
|
|
|
115
113
|
```
|
|
116
114
|
|
|
117
115
|
Run **before** persist — the last point a finding folds into a re-author.
|
|
118
|
-
It exits 0 on **any** verdict (verdicts route work, they do not gate) and
|
|
119
|
-
**1** only on a usage/IO error — no critic ran
|
|
120
|
-
|
|
116
|
+
It exits 0 on **any** verdict (verdicts route work, they do not gate) and
|
|
117
|
+
exits **1** only on a usage/IO error — no critic ran: **do not proceed to
|
|
118
|
+
Persist**, fix and re-run.
|
|
121
119
|
|
|
122
120
|
- **Both `dispatch: false`** — proceed to Persist (each skip is ledgered).
|
|
123
121
|
- **Either `dispatch: true`** — dispatch **one fresh-context, maker-blind
|
|
@@ -125,16 +123,19 @@ proceed to Persist**, fix and re-run.
|
|
|
125
123
|
authoring transcript), fold findings into Gate #2 or a re-author round, re-run
|
|
126
124
|
this step. Pre-mortem triggers (incl. the external-dependency probe), the
|
|
127
125
|
advisory-only `textHygiene.findings[]` lints and dispatch shape:
|
|
128
|
-
[reference
|
|
126
|
+
[reference](helpers/plan-reference.md).
|
|
129
127
|
|
|
130
128
|
### 3. Persist
|
|
131
129
|
|
|
132
130
|
**Gate #2** — STOP for approval before persist **only** when the operator asked
|
|
133
131
|
to review (`--force-review`). Under `--yes`, auto-proceed.
|
|
134
132
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
([
|
|
133
|
+
**Gate #3 (N>2 only)** — offer a **container Epic**; on a yes add
|
|
134
|
+
`--epic-title` / `--epic-goal` below. It carries nothing a child does not
|
|
135
|
+
([shape](helpers/plan-reference.md)). Never below 3 Stories, never unasked.
|
|
136
|
+
|
|
137
|
+
Run persist with `--dry-run` **first** — same command, writes suppressed; every
|
|
138
|
+
gate runs before the first `createIssue` ([list](helpers/plan-reference.md)):
|
|
138
139
|
|
|
139
140
|
```bash
|
|
140
141
|
node .agents/scripts/plan-persist.js \
|
|
@@ -142,24 +143,29 @@ node .agents/scripts/plan-persist.js \
|
|
|
142
143
|
--plan-dir temp/plan-<slug> \
|
|
143
144
|
[--plan-acceptance temp/plan-<slug>/acceptance-manifest.json] \
|
|
144
145
|
[--tech-spec temp/plan-<slug>/techspec.md] \
|
|
145
|
-
[--source-tickets 123,456]
|
|
146
|
+
[--source-tickets 123,456] \
|
|
147
|
+
[--epic-title "<name>" --epic-goal "<one paragraph>"]
|
|
146
148
|
```
|
|
147
149
|
|
|
148
|
-
At lite shape
|
|
150
|
+
At lite shape `--chain-on-clean` folds a clean dry-run into the real persist;
|
|
149
151
|
a full plan keeps its review trip.
|
|
150
152
|
|
|
151
153
|
Persist creates `type::story` issue(s), a **metadata-only** `plan-run::<id>`
|
|
152
|
-
label,
|
|
154
|
+
label, `blocked by #<id>` footers for N>1 `depends_on` edges, and on a confirmed
|
|
155
|
+
Gate #3 a `type::epic` container linking every created Story.
|
|
153
156
|
`agent::ready` is the **terminal** flip after receipts land; stdout is pure
|
|
154
157
|
JSON. Tickets mode also comments on and closes each source id
|
|
155
158
|
([detail](helpers/plan-reference.md)).
|
|
156
159
|
|
|
157
160
|
## Constraints
|
|
158
161
|
|
|
159
|
-
- `/mandrel-plan` starts delivery **only** through a confirmed Gate #1 light
|
|
160
|
-
never off its own authored Stories, which land via
|
|
162
|
+
- `/mandrel-plan` starts delivery **only** through a confirmed Gate #1 light
|
|
163
|
+
route — never off its own authored Stories, which land via
|
|
164
|
+
[`/mandrel-deliver`](mandrel-deliver.md).
|
|
161
165
|
- Duplicate search targets open Stories (`type::story`), not Epics; and
|
|
162
166
|
deterministic gates still fail closed under `--yes`.
|
|
167
|
+
- A container Epic is never a work item, and no Story body gains an `Epic: #N`
|
|
168
|
+
footer: linkage is parent→child only.
|
|
163
169
|
|
|
164
170
|
## See also
|
|
165
171
|
|
|
@@ -106,7 +106,11 @@ browser surface") and stop. Never attempt a headless fallback.
|
|
|
106
106
|
runs against via
|
|
107
107
|
[`resolveQaEnvironment`](../scripts/lib/qa/resolve-qa-contract.js), yielding
|
|
108
108
|
`{ name, baseUrl, signInSeam, allowWrites }` (`allowWrites` defaults to an
|
|
109
|
-
explicit boolean — `true` only for the conventional `local` environment
|
|
109
|
+
explicit boolean — `true` only for the conventional `local` environment;
|
|
110
|
+
`signInSeam` is `null` when the environment declares none). A `{ skill }` seam
|
|
111
|
+
is resolved here too: the resolver throws when the id resolves to no
|
|
112
|
+
`SKILL.md`, so a dangling seam fails before the browser is driven, never
|
|
113
|
+
mid-sweep. When
|
|
110
114
|
`<env>` is supplied, pass it straight through (an exact name wins; a raw URL
|
|
111
115
|
matches by origin). When it is omitted (bare `/qa-run`), **prompt** the
|
|
112
116
|
operator, enumerating every environment as `name → baseUrl` (marking
|
|
@@ -152,10 +156,14 @@ resolved environment's discriminated-union seam (anchored on `baseUrl`):
|
|
|
152
156
|
input; under a `urlTemplate` seam the contract is authored as a plain name
|
|
153
157
|
array and no per-persona auth material is read.
|
|
154
158
|
- **`kind: 'skill'`** — invoke the named consumer sign-in skill (procedural /
|
|
155
|
-
non-URL sign-in).
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
non-URL sign-in), reading the `SKILL.md` at the seam's resolved `skillPath`.
|
|
160
|
+
Real auth uses **only `credentialRef`-indirected material** the skill
|
|
161
|
+
dereferences; raw passwords, tokens, or API keys are never inlined into the
|
|
162
|
+
contract, the workflow, or chat, and captured evidence is redacted per
|
|
163
|
+
[`helpers/qa-core.md`](helpers/qa-core.md) before persistence.
|
|
164
|
+
- **No seam (`signInSeam` absent → `null`)** — a declarable state, not a
|
|
165
|
+
defect. Drive only the unauthenticated surface and record the gap in the
|
|
166
|
+
envelope; never fabricate a session or enter real credentials by hand.
|
|
159
167
|
|
|
160
168
|
**Verification (the envelope's proof).** After sign-in, confirm the
|
|
161
169
|
authenticated state with a `take_snapshot` showing the persona badge (the user
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,34 @@ All notable changes to this project will be documented in this file.
|
|
|
15
15
|
-->
|
|
16
16
|
<!-- markdownlint-disable-file MD004 MD012 MD037 -->
|
|
17
17
|
|
|
18
|
+
## [2.40.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.39.0...mandrel-v2.40.0) (2026-09-06)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
* audit-to-stories --auto: persist the cross-run ledger via a `--ledger-commit` PR and ship the audit-sweep runbook template ([#5145](https://github.com/dsj1984/mandrel/issues/5145)) ([#5151](https://github.com/dsj1984/mandrel/issues/5151)) ([7274e6a](https://github.com/dsj1984/mandrel/commit/7274e6abe1bbf4fca6e1ed09abc8b4ebc9598d89))
|
|
24
|
+
* audit-to-stories: parse `####` findings under grouping headers and fail closed on a missing or mismatched `Severity tally:` line ([#5144](https://github.com/dsj1984/mandrel/issues/5144)) ([#5149](https://github.com/dsj1984/mandrel/issues/5149)) ([d9d8275](https://github.com/dsj1984/mandrel/commit/d9d827584989406e85d3e22110e6b741dab837e9))
|
|
25
|
+
* container Epic: /mandrel-plan offers one above 2 Stories, /mandrel-deliver expands an Epic id, /audit-to-stories requests one by default ([#5139](https://github.com/dsj1984/mandrel/issues/5139)) ([#5147](https://github.com/dsj1984/mandrel/issues/5147)) ([60dd912](https://github.com/dsj1984/mandrel/commit/60dd912f9d00dbf2fb7c17f8c98639b4008b5892))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
* audit-to-stories --wire-edges: carry the live provider's write ports through the dedup adapter ([#5143](https://github.com/dsj1984/mandrel/issues/5143)) ([#5146](https://github.com/dsj1984/mandrel/issues/5146)) ([380577e](https://github.com/dsj1984/mandrel/commit/380577e5e8f215bf28bb15ef8f2ac03c5958f0b8))
|
|
31
|
+
* **tests:** assert the report path separator-agnostically for Windows ([#5153](https://github.com/dsj1984/mandrel/issues/5153)) ([afe7fda](https://github.com/dsj1984/mandrel/commit/afe7fda7aa257bfb5e0745160363189ed84c2ad1))
|
|
32
|
+
* **tests:** import the provider fixture as a file URL for Windows ([#5150](https://github.com/dsj1984/mandrel/issues/5150)) ([797af46](https://github.com/dsj1984/mandrel/commit/797af46269e22e878fbd13293e5e5df6a044a1d3))
|
|
33
|
+
|
|
34
|
+
## [2.39.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.38.0...mandrel-v2.39.0) (2026-09-05)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
* qa: make the `signInSeam` `{ skill }` arm authorable via a consumer-writable `.agents/local/skills/` root, and declarable-absent ([#5135](https://github.com/dsj1984/mandrel/issues/5135)) ([#5136](https://github.com/dsj1984/mandrel/issues/5136)) ([4a7c89e](https://github.com/dsj1984/mandrel/commit/4a7c89e55b83c20637c518950e3cdee5620a4442))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
* quality-preview --staged: re-base the staged scope to MERGE_HEAD so a base-sync merge commit is not scored for the base branch's work ([#5131](https://github.com/dsj1984/mandrel/issues/5131)) ([#5132](https://github.com/dsj1984/mandrel/issues/5132)) ([d6fccc1](https://github.com/dsj1984/mandrel/commit/d6fccc15738fe22965ff8e9c819a55fe69b39815))
|
|
45
|
+
|
|
18
46
|
## [2.38.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.37.0...mandrel-v2.38.0) (2026-09-04)
|
|
19
47
|
|
|
20
48
|
|
package/package.json
CHANGED