mandrel 2.24.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/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 +3 -4
- package/.agents/scripts/deliver-recover.js +13 -0
- 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/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/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/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/lifecycle/listeners/watcher.js +71 -20
- package/.agents/scripts/lib/orchestration/light-backstop.js +20 -3
- package/.agents/scripts/lib/orchestration/light-escalation.js +82 -3
- package/.agents/scripts/lib/orchestration/light-suitability.js +103 -5
- 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/pull-request.js +129 -21
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +50 -13
- 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/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-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 +29 -0
- package/lib/cli/update.js +14 -11
- package/lib/cli/version-check.js +9 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Audit architectural boundaries, module coupling, and
|
|
2
|
+
description: Audit architectural boundaries, module coupling, layering violations, and shipped-but-uncalled seams; emit a structured findings report keyed to the canonical severity scale.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Architecture & Clean Code Audit
|
|
@@ -57,9 +57,14 @@ the failure mode this lens exists to prevent.
|
|
|
57
57
|
node .agents/scripts/check-dead-exports.js
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
Each unreferenced export is a grounded candidate. **Cede
|
|
61
|
-
audit-clean-code's Dead Code dimension rather
|
|
62
|
-
the deferral in Step 2)
|
|
60
|
+
Each unreferenced export is a grounded candidate. **Cede the
|
|
61
|
+
merely-unreferenced ones** to audit-clean-code's Dead Code dimension rather
|
|
62
|
+
than re-deriving them here (see the deferral in Step 2); keep the ones that
|
|
63
|
+
are dead *wiring* — a seam a delivery shipped that no live production path
|
|
64
|
+
reaches — for this lens's Shipped-But-Never-Wired dimension. Note that this
|
|
65
|
+
checker only sees unreferenced symbols, so it cannot surface the worst shape
|
|
66
|
+
(a writer with no reader, both fully referenced); that one you must trace by
|
|
67
|
+
hand. When the shipped checker is unavailable, fall back
|
|
63
68
|
to `npx knip --production` — and heed the `!`-suffix entry-pattern caveat
|
|
64
69
|
that [`audit-clean-code`](audit-clean-code.md) documents, since
|
|
65
70
|
`knip --production` is a silent no-op without it.
|
|
@@ -116,21 +121,35 @@ legitimately have no layered architecture to guard.
|
|
|
116
121
|
|
|
117
122
|
## Step 2: Analysis Dimensions
|
|
118
123
|
|
|
119
|
-
For every finding you surface, grade **Impact** on
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
For every finding you surface, grade **Impact** on the **canonical severity
|
|
125
|
+
scale** — `Critical | High | Medium | Low | Info`, defined in the core's
|
|
126
|
+
[Severity scale](helpers/audit-lens-core.md#severity-scale) and owned by
|
|
127
|
+
`lib/findings/severity.js`. This lens relabels the axis `Impact` (it reflects how
|
|
128
|
+
much correctness, maintainability, or testability the gap erodes), but the
|
|
129
|
+
*levels* are the shared five and nothing else: a narrower or invented vocabulary
|
|
130
|
+
resolves to no severity and the finding is dropped from every severity-filtered
|
|
131
|
+
run. In particular an architectural defect that is actively data-losing or
|
|
132
|
+
release-blocking grades **Critical**, and a grounded observation that asks for no
|
|
133
|
+
scheduled work grades **Info** — neither collapses into `High` or `Low`.
|
|
134
|
+
|
|
135
|
+
Impact is independent of the **Category** effort axis — a Quick Win can still be
|
|
136
|
+
High Impact, and a Structural Change can be Medium. As a loose default, Quick
|
|
137
|
+
Wins typically land High (cheap to fix, real payoff) and Structural Changes
|
|
138
|
+
Medium/High, but grade Impact on the risk itself rather than deriving it
|
|
139
|
+
mechanically from Category.
|
|
126
140
|
|
|
127
141
|
> **Boundary with `audit-clean-code`.** The clean-code-overlapping smells
|
|
128
142
|
> (over-engineering & abstractions, cognitive load & nesting, dead code &
|
|
129
143
|
> redundancy, naming & self-documentation, coupling & cohesion) are owned by
|
|
130
144
|
> [`audit-clean-code`](audit-clean-code.md); the Step 0 dead-export candidates
|
|
131
145
|
> flow into its Dead Code dimension. Do **not** duplicate them here. This lens
|
|
132
|
-
> keeps only the
|
|
133
|
-
> testable-surface boundary
|
|
146
|
+
> keeps only the three structural dimensions no other lens owns — the
|
|
147
|
+
> testable-surface boundary, the automated-guardrail maturity, and
|
|
148
|
+
> shipped-but-never-wired seams. The split with clean-code's Dead Code dimension
|
|
149
|
+
> is by *question asked*: clean-code asks whether a symbol is referenced at all,
|
|
150
|
+
> this lens asks whether a **live production path** reaches it. An unreferenced
|
|
151
|
+
> helper is clean-code's; a fully-referenced writer whose reader was never built
|
|
152
|
+
> is this lens's.
|
|
134
153
|
|
|
135
154
|
Evaluate the gathered context against the following architecture dimensions:
|
|
136
155
|
|
|
@@ -208,6 +227,43 @@ Evaluate the gathered context against the following architecture dimensions:
|
|
|
208
227
|
full maturity assessment runs only in codebase-wide mode (when
|
|
209
228
|
`{{changedFiles}}` renders as the literal substitution token).
|
|
210
229
|
|
|
230
|
+
3. **Shipped-But-Never-Wired Seams (mandatory).** Report code this repository
|
|
231
|
+
ships that **no live production caller ever reaches**. This is the failure
|
|
232
|
+
mode the per-Story suite structurally cannot catch: every piece passes its own
|
|
233
|
+
unit tests, the diff looks complete, and the assembled path is dead — so it is
|
|
234
|
+
found only after delivery, if at all. A green suite is not evidence of a live
|
|
235
|
+
path; only a caller is.
|
|
236
|
+
|
|
237
|
+
Walk the seam **from the consumer backwards**, not from the producer forwards.
|
|
238
|
+
For each candidate, name the production entry point you traced to — or state
|
|
239
|
+
that you could not reach one, which is the finding. Cover at least:
|
|
240
|
+
|
|
241
|
+
- **A produced-but-never-consumed artifact.** Something the code computes,
|
|
242
|
+
stamps, writes, or returns that nothing downstream ever reads: an envelope
|
|
243
|
+
field no consumer parses, a file or log written and never opened, a
|
|
244
|
+
provenance marker stamped by the writer with no reader. Both halves must
|
|
245
|
+
exist for the feature to work, and shipping only the writer looks exactly
|
|
246
|
+
like shipping the feature.
|
|
247
|
+
- **An optional field nothing populates.** The mirror image: a parameter,
|
|
248
|
+
config key, or schema property a consumer reads and branches on that no
|
|
249
|
+
caller ever sets. The branch is unreachable, so the behaviour it guards has
|
|
250
|
+
never once run, and the default silently *is* the behaviour.
|
|
251
|
+
- **An exported seam with no in-tree caller** that the core's exclusion list
|
|
252
|
+
does not bless — not a test seam, not a CLI/`exports` entry point, not
|
|
253
|
+
dynamically reached. Cite the exclusion and drop it when it is one of those;
|
|
254
|
+
the Step 0 dead-export reading is the grounding instrument here, and the
|
|
255
|
+
candidates it surfaces that are genuinely dead **internal** wiring belong in
|
|
256
|
+
this dimension rather than ceded to clean-code's Dead Code dimension.
|
|
257
|
+
|
|
258
|
+
Grade Impact by what the dead wiring was supposed to do: a dead *guard, gate,
|
|
259
|
+
or enforcement path* is **High** or **Critical** — the protection it was
|
|
260
|
+
shipped to provide has never been in force, and the gate reads green because
|
|
261
|
+
it never runs. Dead reporting or convenience wiring is **Medium**. Each
|
|
262
|
+
finding's **Acceptance signal** must be the observable that proves the path is
|
|
263
|
+
live: a test that fails when the caller is removed, or a trace from the
|
|
264
|
+
production entry point to the seam. "Added a unit test for the seam" is not
|
|
265
|
+
that signal — the seam already had one.
|
|
266
|
+
|
|
211
267
|
### Maturity Rubric
|
|
212
268
|
|
|
213
269
|
Use these definitions to classify the project's `Architecture Guardrail
|
|
@@ -107,6 +107,32 @@ Evaluate the gathered context against the following test quality dimensions:
|
|
|
107
107
|
criteria to ensure they have corresponding and complete test coverage.
|
|
108
108
|
Verify that the implementation found in the codebase correctly matches the
|
|
109
109
|
architectural requirements and highlight any inconsistencies or gaps.
|
|
110
|
+
7. **Unwired Seams — Coverage Without a Caller (mandatory).** Report code the
|
|
111
|
+
suite **covers** but no live production path **calls**. This is the blind spot
|
|
112
|
+
this dimension exists for, and it is a property of the suite, not of the code:
|
|
113
|
+
a seam with its own passing unit test reports as covered, contributes to the
|
|
114
|
+
coverage number, and is never exercised in assembly — so the suite reads green
|
|
115
|
+
over wiring that has never run once. High coverage is therefore not evidence
|
|
116
|
+
of a live path; it is what conceals a dead one. Cover at least:
|
|
117
|
+
|
|
118
|
+
- **A produced-but-never-consumed artifact** — a field, file, or marker the
|
|
119
|
+
code writes that no test and no consumer ever reads back. A writer test that
|
|
120
|
+
asserts on the writer's own output is not a reader.
|
|
121
|
+
`assert(written === expected)` proves the write, never the round-trip.
|
|
122
|
+
- **An optional field nothing populates** — a parameter or config key a
|
|
123
|
+
consumer branches on that only *tests* ever set. The suite covers both
|
|
124
|
+
branches; production has only ever taken the default.
|
|
125
|
+
|
|
126
|
+
For each, name the missing test rather than the missing caller: the gap is
|
|
127
|
+
that **no test would fail if the wiring were deleted**. That is the assertion
|
|
128
|
+
to recommend — an integration-tier test that drives the real production entry
|
|
129
|
+
point and fails when the seam is unwired. Grade a dead guard/gate **High** or
|
|
130
|
+
**Critical** (the enforcement has never been in force and the bar reads green
|
|
131
|
+
because it never runs), other dead wiring **Medium**. The core's exclusion
|
|
132
|
+
list still applies: a sanctioned test seam or a declared entry point is not a
|
|
133
|
+
finding here. Route the *architectural* framing of the same defect to
|
|
134
|
+
[`audit-architecture`](audit-architecture.md)'s Shipped-But-Never-Wired
|
|
135
|
+
dimension; this lens owns the **missing-test** framing.
|
|
110
136
|
|
|
111
137
|
## Constraint (lens-specific carve-out)
|
|
112
138
|
|
|
@@ -143,9 +143,24 @@ Chain into the existing planning entrypoint:
|
|
|
143
143
|
|
|
144
144
|
(`/plan --seed "$(cat <path>)"` also works for small seeds). `/plan`
|
|
145
145
|
then runs its author → persist path, as documented in its workflow.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
|
|
147
|
+
**Dedup provenance is carried mechanically — do not hand-copy it.** The seed's
|
|
148
|
+
MVP Scope bullets carry each group's `audit-fingerprints` and
|
|
149
|
+
`audit-semantic-keys` footers as HTML comments (invisible in the rendered
|
|
150
|
+
one-pager). `plan-persist` harvests them out of the seed on the
|
|
151
|
+
`plan-context.json` envelope and appends them to **every** Story body it
|
|
152
|
+
persists, via `carryProvenanceFooters`
|
|
153
|
+
([`lib/findings/route-finding.js`](../scripts/lib/findings/route-finding.js)).
|
|
154
|
+
The carry is additive, union-preserving and idempotent, so a resumed persist
|
|
155
|
+
cannot stack footers and a hand-authored fingerprint is never dropped.
|
|
156
|
+
|
|
157
|
+
This is deliberately not an authoring step. It used to be: the footers reached
|
|
158
|
+
the seed and stopped there, leaving the authoring agent to notice HTML comments
|
|
159
|
+
in a one-pager and copy them forward — a remembered step, which is to say no
|
|
160
|
+
step at all. Stories filed on the recommended path were therefore invisible to
|
|
161
|
+
the next sweep's Phase 6 dedup, which re-filed work it had already planned. If
|
|
162
|
+
you find yourself copying a footer by hand, the carry is broken — fix it there
|
|
163
|
+
rather than papering over it in the body.
|
|
149
164
|
|
|
150
165
|
## Phase 5b — Standalone-Stories path
|
|
151
166
|
|
|
@@ -261,7 +276,18 @@ When the single-plan path ran, link the Story (or plan-run) the chained
|
|
|
261
276
|
dedup/route implementation, shared with `qa-explore`.
|
|
262
277
|
- **Always** stamp the fingerprint footer (via the helper's
|
|
263
278
|
`fingerprintFooter`) in the body of every created Story. Without it,
|
|
264
|
-
the next run cannot dedupe.
|
|
279
|
+
the next run cannot dedupe. On the Single-plan path this is mechanical
|
|
280
|
+
(`carryProvenanceFooters`, Phase 5a) — never an authoring step.
|
|
281
|
+
- **Never** re-mint a finding fingerprint while normalising a finding.
|
|
282
|
+
`severity` and `labels` are identity fields folded into the sha, so
|
|
283
|
+
normalising either without holding the hash stable silently breaks
|
|
284
|
+
dedup for every finding already filed. The projection that keeps the
|
|
285
|
+
fingerprint invariant under normalisation is
|
|
286
|
+
`severity.js#fingerprintSeverity`; the contract is pinned by
|
|
287
|
+
`tests/lib/findings/route-finding.contract.test.js`.
|
|
288
|
+
- **Always** grade findings on the canonical five-level scale
|
|
289
|
+
(`lib/findings/severity.js`). A level outside it parses as no severity
|
|
290
|
+
and the finding is dropped by every severity-filtered run.
|
|
265
291
|
- **Always** present the Phase 2, 3, and 4 HITL gates. Do not bypass —
|
|
266
292
|
even when "obvious" — because the severity / grouping / mode picks
|
|
267
293
|
are operator decisions that the workflow's UX contract relies on.
|
|
@@ -8,73 +8,64 @@ description:
|
|
|
8
8
|
# /deliver
|
|
9
9
|
|
|
10
10
|
> **Lean spine.** Happy path + gate list. Sequencing, dispatch mechanics,
|
|
11
|
-
> intent phrases, ceremony
|
|
12
|
-
> [`helpers/deliver-reference.md`](helpers/deliver-reference.md)
|
|
13
|
-
>
|
|
14
|
-
>
|
|
15
|
-
> [`helpers/deliver-digest.md`](helpers/deliver-digest.md).
|
|
11
|
+
> intent phrases, ceremony and the epilogue live in on-demand
|
|
12
|
+
> [`helpers/deliver-reference.md`](helpers/deliver-reference.md) ("reference"
|
|
13
|
+
> below); the unplanned path in
|
|
14
|
+
> [`helpers/deliver-light.md`](helpers/deliver-light.md). Every delivery reads
|
|
15
|
+
> [`helpers/deliver-digest.md`](helpers/deliver-digest.md) once.
|
|
16
16
|
|
|
17
17
|
## Role
|
|
18
18
|
|
|
19
|
-
One delivery door. `/deliver` owns input resolution
|
|
20
|
-
|
|
19
|
+
One delivery door. `/deliver` owns input resolution, sequencing and the
|
|
20
|
+
close-and-land tail; Stories are implemented via
|
|
21
|
+
[`helpers/deliver-story.md`](helpers/deliver-story.md).
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
`
|
|
26
|
-
there is no graph to hand it and no batch label, which is what lets you deliver
|
|
27
|
-
Stories **across plan runs and over time**.
|
|
28
|
-
`plan-run::<id>` is filter metadata, never a resolution input; `route::lite` is
|
|
29
|
-
a body-derived hint only. Ahead of all of it, a **single-Story run runs the
|
|
30
|
-
engine inline** whatever the shape — sub-agent isolation only earns its cost
|
|
31
|
-
against a concurrent sibling.
|
|
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
|
+
over time**. `plan-run::<id>` is filter metadata, never a resolution input;
|
|
26
|
+
`route::lite` a body-derived hint only.
|
|
32
27
|
|
|
33
28
|
## Inputs
|
|
34
29
|
|
|
35
|
-
Classify what the operator typed **before**
|
|
36
|
-
|
|
30
|
+
Classify what the operator typed **before** anything else, and say which shape
|
|
31
|
+
you read:
|
|
37
32
|
|
|
38
33
|
| Invocation | Shape | Behavior |
|
|
39
34
|
| --- | --- | --- |
|
|
40
35
|
| `/deliver` | bare | List the open `agent::ready` Stories and ask which to deliver. Deliver nothing until answered. |
|
|
41
36
|
| `/deliver 4712` | ids | One Story via `helpers/deliver-story.md`, **inline in this session** — no `story-worker` spawn. |
|
|
42
|
-
| `/deliver 4712 4713 …` | ids | Resolve the set,
|
|
37
|
+
| `/deliver 4712 4713 …` | ids | Resolve the set, sequence by the discovered graph via `stories-wave-tick.js`, dispatch sub-agents. |
|
|
43
38
|
| `/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). |
|
|
44
39
|
|
|
45
40
|
**The discriminator is lexical and total.** Every positional argument matching
|
|
46
41
|
`^#?\d+$` means ids; anything else means a prompt. A **mixed** invocation (ids
|
|
47
|
-
*and* prose) is a **hard error** — refuse it and ask which was meant
|
|
48
|
-
|
|
49
|
-
that is not `type::story`, or carries an `Epic: #N` footer, is a hard error too.
|
|
42
|
+
*and* prose) is a **hard error** — refuse it and ask which was meant. A ticket
|
|
43
|
+
not `type::story`, or carrying an `Epic: #N` footer, is a hard error too.
|
|
50
44
|
|
|
51
45
|
## Saying what you want
|
|
52
46
|
|
|
53
|
-
No flags to remember: state intent — *"…but I'll merge it myself"*, *"…
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
[`helpers/deliver-reference.md` § Intent phrases](helpers/deliver-reference.md).
|
|
47
|
+
No flags to remember: state intent — *"…but I'll merge it myself"*, *"…one at
|
|
48
|
+
a time"* — and announce what you read. Phrasings and the flags they fill in:
|
|
49
|
+
reference § Intent phrases.
|
|
57
50
|
|
|
58
|
-
`--yes` is **runner-set, never operator-typed**: cron, `/loop
|
|
59
|
-
dispatch set it to mean *nobody is at the keyboard*, which
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
`--yes` is **runner-set, never operator-typed**: cron, `/loop` and headless
|
|
52
|
+
dispatch set it to mean *nobody is at the keyboard*, which fails the unplanned
|
|
53
|
+
path's over-scope stop closed to an envelope instead of a question. Never offer
|
|
54
|
+
it to an operator or add it to an attended run.
|
|
62
55
|
|
|
63
56
|
## Procedure
|
|
64
57
|
|
|
65
58
|
0. **Classify and announce.** Read the invocation per § Inputs and state the
|
|
66
|
-
shape
|
|
67
|
-
[`helpers/deliver-light.md`](helpers/deliver-light.md); bare asks; ids
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
`
|
|
72
|
-
the
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
whose native edges cannot be read — a missing gate would co-dispatch against
|
|
77
|
-
an unlanded blocker.
|
|
59
|
+
shape. A prompt leaves for
|
|
60
|
+
[`helpers/deliver-light.md`](helpers/deliver-light.md); bare asks; ids go on.
|
|
61
|
+
|
|
62
|
+
1. **Resolve the set.** One command, one Story or many:
|
|
63
|
+
`node .agents/scripts/resolve-stories.js --ids <id,id,...>`. It validates the
|
|
64
|
+
set and shows what will run: read `stories[]`, `dag[]` and `done[]` to
|
|
65
|
+
present the order in step 2, but do **not** thread them into step 3 — the
|
|
66
|
+
tick re-resolves the graph every beat. It hard-errors (exit 1) on an id that
|
|
67
|
+
is not a Story, carries an `Epic: #N` footer, or whose edges cannot be
|
|
68
|
+
read — a missing gate would co-dispatch against an unlanded blocker.
|
|
78
69
|
|
|
79
70
|
2. **Confirm (N>1).** Present the order; wait unless `--yes`.
|
|
80
71
|
|
|
@@ -87,74 +78,69 @@ a question. Never offer it to an operator or add it to an attended run.
|
|
|
87
78
|
```
|
|
88
79
|
|
|
89
80
|
**Do not add `--concurrency` unless the operator explicitly asked for a
|
|
90
|
-
per-run cap** — an explicit value wins over config, so a
|
|
91
|
-
|
|
81
|
+
per-run cap** — an explicit value wins over config, so a literal defeats a
|
|
82
|
+
`.agentrc.local.json` override.
|
|
92
83
|
|
|
93
84
|
Each beat re-probes live state to derive done / in-flight itself; you never
|
|
94
85
|
compute them. `--dispatched` is the one thing you must supply — the
|
|
95
|
-
append-only list of every id you spawned this run
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
- **
|
|
102
|
-
`
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
(`gh issue view <id> --comments`) and resume only once the operator
|
|
114
|
-
unblocks it (`update-ticket-state.js --ticket <id> --state agent::ready`).
|
|
115
|
-
Blocked outranks a wedge but not a cycle (fix the graph first).
|
|
116
|
-
|
|
117
|
-
4. **Per-run epilogue (N>1).** Once step 3 reports `epilogueDue: true`, run
|
|
118
|
-
`node .agents/scripts/plan-run-epilogue.js --stories 101,102` — audit
|
|
119
|
-
roster, follow-up roll-up, sibling coherence. A single-Story run skips it
|
|
86
|
+
append-only list of every id you spawned this run. Cross-run de-confliction
|
|
87
|
+
via the assignee lease is automatic (reference §§ Sequencing edge cases,
|
|
88
|
+
Dispatch mechanics). Branch on the exit code:
|
|
89
|
+
- **0** — dispatch each `ready` id (already capped and overlap-free); an
|
|
90
|
+
empty `ready` with work in flight means "waiting", so keep looping;
|
|
91
|
+
`epilogueDue: true` means every Story is done — step 4.
|
|
92
|
+
- **2** — `cycleError`: the graph is self-referential; fix `depends_on`, do
|
|
93
|
+
not retry. **3** — `wedged`: nothing dispatchable, nothing in flight, the
|
|
94
|
+
undone Stories and their unmet blockers named; land a blocker or add it to
|
|
95
|
+
`--ids`. **4** — `blocked`: a Story carries `agent::blocked` with
|
|
96
|
+
`blockedReason`, the protocol's HITL pause
|
|
97
|
+
([`instructions.md` § 1.J](../instructions.md)) — **stop the loop and
|
|
98
|
+
surface it; do not poll**, resuming once the operator unblocks it. Blocked
|
|
99
|
+
outranks a wedge, not a cycle.
|
|
100
|
+
|
|
101
|
+
4. **Close each hand-off** (§ Closing what the workers hand back), then, with
|
|
102
|
+
every Story landed, run the **per-run epilogue (N>1)**:
|
|
103
|
+
`node .agents/scripts/plan-run-epilogue.js --stories 101,102`. N=1 skips it
|
|
120
104
|
([reference § Per-run epilogue](helpers/deliver-reference.md)).
|
|
121
105
|
|
|
122
|
-
##
|
|
106
|
+
## Closing what the workers hand back {#tail}
|
|
123
107
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
[`helpers/deliver-reference.md` § Ceremony](helpers/deliver-reference.md).
|
|
108
|
+
**The tail is the orchestrator's, not the worker's.** A dispatched
|
|
109
|
+
`story-worker` stops at a pushed branch and returns a hand-off; **you** run
|
|
110
|
+
[`helpers/deliver-story.md`](helpers/deliver-story.md) Step 3
|
|
111
|
+
(`single-story-close.js`) for it, foreground, and relay the envelope.
|
|
129
112
|
|
|
130
|
-
|
|
113
|
+
**Serialize the tail.** Implementation runs in parallel; closing does not. Close
|
|
114
|
+
one Story at a time — closes contend on the base branch, the merge queue and
|
|
115
|
+
the checkout. A worker handing back mid-close waits its turn.
|
|
131
116
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
117
|
+
**A worker returning no terminal envelope is the expected shape**, not a failure
|
|
118
|
+
to answer with a re-dispatch: only close mints one. Close the pushed branch, or
|
|
119
|
+
probe read-only with `node .agents/scripts/deliver-recover.js --story <storyId>`
|
|
120
|
+
and resume the worker or close it names.
|
|
135
121
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
122
|
+
**Reading the outcome.** Each close ends the Story in one schema-validated
|
|
123
|
+
envelope — `landed` | `pending` | `blocked` | `failed`; statuses, exits and
|
|
124
|
+
fields are digest § 5. `pending` is **not** a failure — `nextCommand` resumes
|
|
125
|
+
it; run that, do not re-dispatch.
|
|
139
126
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
`
|
|
127
|
+
**Branch model (authoritative).** `story-<id>` → PR → `main` (squash +
|
|
128
|
+
required checks), per digest § 2; dependent Stories land sequentially. Ceremony
|
|
129
|
+
depth (profiles + derived level via `ceremony-routing.js`, review depth reading
|
|
130
|
+
it): reference § Ceremony.
|
|
143
131
|
|
|
144
132
|
## Constraints
|
|
145
133
|
|
|
146
|
-
- **Land or block — never a silent local build** (digest § 2). Attended
|
|
147
|
-
|
|
148
|
-
|
|
134
|
+
- **Land or block — never a silent local build** (digest § 2). Attended delivers
|
|
135
|
+
default to close-and-land (`delivery.routing.closeAndLand: true`); rest at
|
|
136
|
+
`agent::closing` only when a human owns it.
|
|
149
137
|
- **`/deliver` never plans.** Planned tickets come from [`/plan`](plan.md), and
|
|
150
138
|
an over-scope prompt **escalates and ends** — never invoke `/plan` in this
|
|
151
139
|
session to rescue it ([`helpers/deliver-light.md`](helpers/deliver-light.md)
|
|
152
|
-
§ Escalation is terminal).
|
|
153
|
-
`deliver-story` owns every script.
|
|
140
|
+
§ Escalation is terminal).
|
|
154
141
|
|
|
155
142
|
## See also
|
|
156
143
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
prompt path, shared with `/plan` Gate #1.
|
|
144
|
+
[`/plan`](plan.md), [`helpers/deliver-story.md`](helpers/deliver-story.md) (the
|
|
145
|
+
engine), [`helpers/deliver-light.md`](helpers/deliver-light.md) (the unplanned
|
|
146
|
+
prompt path, shared with `/plan` Gate #1).
|
|
@@ -76,7 +76,7 @@ the primary file the finding lives in:]
|
|
|
76
76
|
### `path/to/primary-file.ext` — [Short title of the issue]
|
|
77
77
|
|
|
78
78
|
- **Dimension:** [the lens-specific dimension — see the lens's own list]
|
|
79
|
-
- **Severity:** [Critical | High | Medium | Low]
|
|
79
|
+
- **Severity:** [Critical | High | Medium | Low | Info]
|
|
80
80
|
- **Location:** `path/to/primary-file.ext:line`
|
|
81
81
|
- **Current State:** [the specific file/line/module and why it is problematic]
|
|
82
82
|
- **Recommendation & Rationale:** [how to remediate and why it matters]
|
|
@@ -89,11 +89,18 @@ the primary file the finding lives in:]
|
|
|
89
89
|
|
|
90
90
|
## Severity scale {#severity-scale}
|
|
91
91
|
|
|
92
|
-
Every finding grades its severity (labelled `Severity` or `Impact`) on
|
|
93
|
-
ordered scale
|
|
94
|
-
|
|
92
|
+
Every finding grades its severity (labelled `Severity` or `Impact`) on the
|
|
93
|
+
ordered scale below. **The code owns this vocabulary**: it is defined in
|
|
94
|
+
[`lib/findings/severity.js`](../../scripts/lib/findings/severity.js)
|
|
95
|
+
(`SEVERITIES`), and every level here — including its accepted spellings — comes
|
|
96
|
+
from that module. A surviving **Critical** finding halts the delivery gate
|
|
95
97
|
(`lib/audit-suite/findings.js#hasSurvivingCritical`).
|
|
96
98
|
|
|
99
|
+
Grade on **exactly** these five levels. A level of your own invention does not
|
|
100
|
+
parse: it resolves to no severity, tallies as `unknown`, and the finding is
|
|
101
|
+
dropped by every severity-filtered run — including the most permissive one. A
|
|
102
|
+
dropped finding is indistinguishable from a finding you never wrote.
|
|
103
|
+
|
|
97
104
|
- **Critical** — an active, exploitable, or data-losing defect that must be
|
|
98
105
|
fixed before the change can ship (e.g. a leaked secret, an auth bypass, a
|
|
99
106
|
guaranteed production outage or data-loss path).
|
|
@@ -102,6 +109,11 @@ ordered scale. `parse-audit-md.js` recognizes every level, and a surviving
|
|
|
102
109
|
- **Medium** — a real problem worth scheduling; contained blast radius, or a
|
|
103
110
|
reasonable workaround exists.
|
|
104
111
|
- **Low** — minor or cosmetic; fix opportunistically.
|
|
112
|
+
- **Info** — the canonical floor: a real, grounded observation worth recording
|
|
113
|
+
that asks for no scheduled work (a documented deviation worth noting, a
|
|
114
|
+
measurement that is fine today and worth watching). Accepts `Informational`.
|
|
115
|
+
Use it instead of inventing a below-`Low` word of your own; a finding that
|
|
116
|
+
cannot clear the evidence bar below is **dropped**, not filed as `Info`.
|
|
105
117
|
|
|
106
118
|
## Self-cross-check (mandatory — filter false positives before you finalize) {#self-cross-check}
|
|
107
119
|
|
|
@@ -149,6 +161,15 @@ that rests on one of them:
|
|
|
149
161
|
- **Style-only nits already enforced by a formatter/linter** — do not
|
|
150
162
|
re-litigate what the committed tooling already governs.
|
|
151
163
|
|
|
164
|
+
> **Boundary with the dead-wiring mandate.** The architecture and quality lenses
|
|
165
|
+
> are required to report shipped seams with no live production caller (their own
|
|
166
|
+
> bodies carry the mandate). The exclusions above **bound** that mandate rather
|
|
167
|
+
> than cancelling it: a test seam, a CLI entry point, a declared `exports`
|
|
168
|
+
> surface, or a dynamically-reached symbol is still never a finding. What the
|
|
169
|
+
> mandate targets is the case none of those cover — an **internal** seam that a
|
|
170
|
+
> delivery shipped and nothing in production ever calls. When a candidate is
|
|
171
|
+
> genuinely one of the exclusions, cite the exclusion and drop it.
|
|
172
|
+
|
|
152
173
|
### Final re-open-and-drop pass (mandatory)
|
|
153
174
|
|
|
154
175
|
1. Walk your Detailed Findings once more, applying the bar and the exclusion
|
|
@@ -14,6 +14,20 @@ it is the mechanics an operator consults when the matching lever is engaged.
|
|
|
14
14
|
|
|
15
15
|
## Sequencing edge cases (`stories-wave-tick.js`)
|
|
16
16
|
|
|
17
|
+
**What "discovered, not declared" means concretely.** `resolve-stories.js` reads
|
|
18
|
+
the graph from live state as the union of the Story bodies' `depends_on` edges
|
|
19
|
+
and GitHub's native `blocked_by` edges, resolving each blocker against its real
|
|
20
|
+
issue state rather than against anything you hand it. That is why there is no
|
|
21
|
+
batch label to pass and why a blocker that landed in an unrelated run is simply
|
|
22
|
+
seen as done.
|
|
23
|
+
|
|
24
|
+
**Resuming an exit-4 `blocked`.** Read the friction comment with
|
|
25
|
+
`gh issue view <id> --comments`, and resume only once the operator has
|
|
26
|
+
unblocked it:
|
|
27
|
+
`node .agents/scripts/update-ticket-state.js --ticket <id> --state agent::ready`.
|
|
28
|
+
Do not poll the label yourself while waiting — the HITL pause is the operator's
|
|
29
|
+
turn, not a slow beat.
|
|
30
|
+
|
|
17
31
|
Each beat re-probes live state: it re-resolves the graph, classifies **done**
|
|
18
32
|
(`agent::done` or a closed issue — including foreign blockers that landed in
|
|
19
33
|
another run), and derives **in-flight** from live `agent::executing` /
|
|
@@ -88,9 +102,10 @@ exposes agent dispatch, spawn each ready Story as its own
|
|
|
88
102
|
`subagent_type: story-worker` sub-agent — it boots on the role-scoped
|
|
89
103
|
[`story-worker`](../../agents/story-worker.md) context (its own system prompt, no
|
|
90
104
|
`CLAUDE.md` @-closure) carrying the load-bearing delivery MUSTs standalone. The
|
|
91
|
-
sub-agent executes [`deliver-story.md`](deliver-story.md)
|
|
92
|
-
(init → implement → acceptance self-eval →
|
|
93
|
-
|
|
105
|
+
sub-agent executes [`deliver-story.md`](deliver-story.md) Steps 0–2.5
|
|
106
|
+
(init → implement → acceptance self-eval → **push**) and stops there; **you**
|
|
107
|
+
own Step 3, serialized — see `/deliver` § Closing what the workers hand back.
|
|
108
|
+
Thread into its prompt: `storyId`; `docsDigestPath` (the per-run docs digest, null when
|
|
94
109
|
`project.docsContextFiles` is unset); `checklistPath` (the footprint-matched
|
|
95
110
|
write-time audit checklist, produced at dispatch, below); and the
|
|
96
111
|
**change-set discipline** — the worker computes the change set once with
|