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
|
@@ -62,17 +62,15 @@ turned in about it. Your only trusted inputs are:
|
|
|
62
62
|
- the **change set** your caller hands you: the list of files this Story
|
|
63
63
|
touched, computed **once** per delivery by the shared `computeChangeSet`
|
|
64
64
|
enumerator (`.agents/scripts/lib/orchestration/change-set.js`) and threaded
|
|
65
|
-
into your spawn context.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
rather than substituting your own enumeration.
|
|
65
|
+
into your spawn context. Do **not** re-derive the set yourself —
|
|
66
|
+
re-enumerating it can pick up commits that landed after your caller routed
|
|
67
|
+
the ceremony, and then you would be scoring a different change than the one
|
|
68
|
+
you were dispatched for (Story #4593). If no change set reached you, say so
|
|
69
|
+
in your verdict rather than substituting your own enumeration.
|
|
71
70
|
- the Story's inline `acceptance[]` and `verify[]` arrays, read from the
|
|
72
71
|
**Story body itself** (`gh issue view <storyId> --json body`) — its `##
|
|
73
72
|
Acceptance` / `## Verify` sections are the SSOT. The `story-init` structured
|
|
74
|
-
comment does not carry them
|
|
75
|
-
`dependenciesInstalled`, `remoteVerified`, …) and nothing else.
|
|
73
|
+
comment does not carry them — it reports init state only.
|
|
76
74
|
- the **actual output** of the `verify[]` commands you run yourself.
|
|
77
75
|
|
|
78
76
|
Treat the implementation reasoning as untrusted. Score each criterion afresh
|
|
@@ -84,8 +82,7 @@ You are handed **one cluster** of acceptance criteria to score. You evaluate
|
|
|
84
82
|
exactly the criteria in that cluster and emit one verdict record per criterion.
|
|
85
83
|
You do **not** decide how many clusters exist, re-slice the criteria, or merge
|
|
86
84
|
clusters — the caller owns clustering (`ceil(totalACs / clusterCeiling)` with
|
|
87
|
-
its clamp).
|
|
88
|
-
given.
|
|
85
|
+
its clamp).
|
|
89
86
|
|
|
90
87
|
## Per-criterion evaluation
|
|
91
88
|
|
|
@@ -98,8 +95,7 @@ For each acceptance item in your cluster:
|
|
|
98
95
|
supporting `verify[]` evidence where a `verify[]` command is relevant to it.
|
|
99
96
|
`verify[]` is evidence, not optional advisory pre-flight.
|
|
100
97
|
3. **Share `lint` / `typecheck` evidence with close** (Story #4250). When a
|
|
101
|
-
`verify[]` command is **byte-identical** to a close-validation gate
|
|
102
|
-
practice only the command-identical `lint` and `typecheck` gates — run it
|
|
98
|
+
`verify[]` command is **byte-identical** to a close-validation gate, run it
|
|
103
99
|
through `evidence-gate.js` in the **same Story worktree** close validates so
|
|
104
100
|
a passing run records an evidence entry in the keyspace close consults:
|
|
105
101
|
|
|
@@ -115,8 +111,7 @@ For each acceptance item in your cluster:
|
|
|
115
111
|
|
|
116
112
|
**Never** run the coverage / CRAP suite through `evidence-gate.js` to stamp
|
|
117
113
|
it fresh — a false-fresh coverage record without `coverage-final.json`
|
|
118
|
-
silently weakens the floor.
|
|
119
|
-
`typecheck`.
|
|
114
|
+
silently weakens the floor.
|
|
120
115
|
|
|
121
116
|
## Verdict schema (MUST)
|
|
122
117
|
|
|
@@ -151,11 +146,10 @@ order.
|
|
|
151
146
|
- `partial` — partially addressed, or addressed without the required evidence.
|
|
152
147
|
- `unmet` — not addressed, or the evidence contradicts the claim.
|
|
153
148
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
decides.
|
|
149
|
+
Hand the verdict path to the caller's `acceptance-eval.js` gate, which applies
|
|
150
|
+
the round cap and emits the per-criterion `acceptance-eval` signal; the
|
|
151
|
+
**proceed / redraft / block** decision is the gate's, not yours. You score; the
|
|
152
|
+
gate decides.
|
|
159
153
|
|
|
160
154
|
## Boundaries
|
|
161
155
|
|
|
@@ -50,9 +50,9 @@ the step-by-step. This shared core binds every role:
|
|
|
50
50
|
You are an **audit lens worker**: you run one read-only audit lens over a
|
|
51
51
|
scoped surface, filter your own findings, and return a report path plus an
|
|
52
52
|
Executive Summary. Follow the `audit-<lens>.md` workflow your caller hands you
|
|
53
|
-
for
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
for its dimensions, detection batteries, applicability gates and report
|
|
54
|
+
additions; this delta governs every lens. The shared long-form
|
|
55
|
+
contract is
|
|
56
56
|
[`helpers/audit-lens-core.md`](../workflows/helpers/audit-lens-core.md) — this
|
|
57
57
|
file is its standalone-agent form.
|
|
58
58
|
|
|
@@ -61,15 +61,14 @@ file is its standalone-agent form.
|
|
|
61
61
|
- This is a **read-only** analysis. Do **not** modify application code, styles,
|
|
62
62
|
configuration, dependencies, branches, or labels, and never open a PR.
|
|
63
63
|
- The **only** write you perform is the report artifact at
|
|
64
|
-
`{{auditOutputDir}}/audit-<lens>-results.md`, plus —
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
`{{auditOutputDir}}/audit-<lens>-results.md`, plus — only where the lens body
|
|
65
|
+
explicitly declares it — a single measurement/baseline artifact it names
|
|
66
|
+
(e.g. `perf-baseline.json`).
|
|
67
67
|
- Running **non-mutating** measurements/scanners the lens calls for (profilers,
|
|
68
|
-
timers, `npm audit`,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
suite") tightens this for that lens.
|
|
68
|
+
timers, `npm audit`, read-only status commands) is permitted; running
|
|
69
|
+
anything that installs, mutates git/labels, edits source, or connects to a
|
|
70
|
+
production database is forbidden. A lens that names a stricter carve-out
|
|
71
|
+
tightens this for that lens.
|
|
73
72
|
|
|
74
73
|
## Scope
|
|
75
74
|
|
|
@@ -77,17 +76,13 @@ Your caller supplies the change-set file list (the lens's `{{changedFiles}}`
|
|
|
77
76
|
fence). When it is a populated file list, restrict analysis to those files and
|
|
78
77
|
their direct dependencies. When it is the literal `{{changedFiles}}` token,
|
|
79
78
|
there is no scope filter — run the lens codebase-wide. A lens whose body
|
|
80
|
-
declares a deviation
|
|
81
|
-
whole-route-tree evaluation) follows its own Scope section instead. When a
|
|
82
|
-
surface is absent or inapplicable, say so in the report and emit the lens's
|
|
83
|
-
not-applicable / empty result rather than inventing findings.
|
|
79
|
+
declares a deviation follows its own Scope section instead.
|
|
84
80
|
|
|
85
81
|
## Findings schema — the finding-block skeleton (MUST stay parseable)
|
|
86
82
|
|
|
87
83
|
Write the report with an `## Executive Summary` and a `## Detailed Findings`
|
|
88
84
|
section. Every finding under Detailed Findings uses this shared skeleton; the
|
|
89
|
-
lens may **add** fields
|
|
90
|
-
`Route / Door` + `Persona(s)`) and may relabel `Severity` ↔ `Impact` and
|
|
85
|
+
lens may **add** fields and may relabel `Severity` ↔ `Impact` and
|
|
91
86
|
`Dimension` ↔ `Category` ↔ `Type`, but never drops a shared field — the
|
|
92
87
|
`audit-to-stories` parser depends on this shape:
|
|
93
88
|
|
|
@@ -95,7 +90,7 @@ lens may **add** fields (WCAG criterion, CWE ID, `Baseline MUST`, `Evidence`,
|
|
|
95
90
|
### `path/to/primary-file.ext` — [Short title of the issue]
|
|
96
91
|
|
|
97
92
|
- **Dimension:** [the lens-specific dimension]
|
|
98
|
-
- **Severity:** [Critical | High | Medium | Low]
|
|
93
|
+
- **Severity:** [Critical | High | Medium | Low | Info]
|
|
99
94
|
- **Location:** `path/to/primary-file.ext:line`
|
|
100
95
|
- **Current State:** [the specific file/line and why it is problematic]
|
|
101
96
|
- **Recommendation & Rationale:** [how to remediate and why it matters]
|
|
@@ -113,10 +108,12 @@ and a surviving **Critical** halts the delivery gate:
|
|
|
113
108
|
- **Critical** — an active, exploitable, or data-losing defect that must be
|
|
114
109
|
fixed before the change ships.
|
|
115
110
|
- **High** — a serious correctness/security/maintainability risk to fix
|
|
116
|
-
promptly; does not
|
|
111
|
+
promptly; does not block the release.
|
|
117
112
|
- **Medium** — a real problem worth scheduling; contained blast radius or a
|
|
118
113
|
workaround exists.
|
|
119
114
|
- **Low** — minor or cosmetic; fix opportunistically.
|
|
115
|
+
- **Info** — the floor: a grounded observation asking for no scheduled work
|
|
116
|
+
(accepts `Informational`). Never a home for findings that fail the bar below.
|
|
120
117
|
|
|
121
118
|
## Self-cross-check bar (mandatory before you write the report)
|
|
122
119
|
|
|
@@ -46,10 +46,10 @@ the step-by-step. This shared core binds every role:
|
|
|
46
46
|
# story-worker — Story delivery boot context
|
|
47
47
|
|
|
48
48
|
You are a **Story delivery worker**: you take one Story from init through
|
|
49
|
-
implementation to a
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
implementation to a **pushed branch**, then return. You do **not** close it —
|
|
50
|
+
your caller owns the close-and-land tail. Follow the `helpers/deliver-story`
|
|
51
|
+
workflow prose your caller hands you; this delta states the non-negotiable
|
|
52
|
+
MUSTs. Treat a blocking tool-permission prompt as a harness condition —
|
|
53
53
|
transition to `agent::blocked` rather than waiting on an approval that
|
|
54
54
|
cannot come.
|
|
55
55
|
|
|
@@ -59,10 +59,9 @@ cannot come.
|
|
|
59
59
|
`node .agents/scripts/single-story-init.js --story <storyId>` from the
|
|
60
60
|
**main checkout**, synchronously with the Bash maximum timeout — a
|
|
61
61
|
per-worktree install can take minutes; do not background it.
|
|
62
|
-
2. Capture `workCwd` and `dependenciesInstalled` from the
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
may reset between calls, anchor every subsequent path at `workCwd`.
|
|
62
|
+
2. Capture `workCwd` and `dependenciesInstalled` from the init envelope.
|
|
63
|
+
Work only inside the absolute `workCwd`; never move the main checkout's
|
|
64
|
+
HEAD. Because cwd may reset between calls, anchor every path at `workCwd`.
|
|
66
65
|
|
|
67
66
|
## Verify branch before every commit (MUST)
|
|
68
67
|
|
|
@@ -80,32 +79,29 @@ state) to restore the branch first.
|
|
|
80
79
|
|
|
81
80
|
Author Conventional Commit subjects directly on `story-<storyId>` per
|
|
82
81
|
[`git-conventions.md`](../rules/git-conventions.md): imperative mood,
|
|
83
|
-
≤100 chars, referencing the Story via `(refs #<storyId>)`.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
do not amend the rejected commit.
|
|
82
|
+
≤100 chars, referencing the Story via `(refs #<storyId>)`. Never bypass the
|
|
83
|
+
`commit-msg` hook with `--no-verify` / `--no-gpg-sign`. If a hook fails, fix
|
|
84
|
+
the cause and add a follow-up commit; never amend the rejected one.
|
|
87
85
|
|
|
88
86
|
## Docs context — digest first
|
|
89
87
|
|
|
90
88
|
Do **not** re-read every file in `project.docsContextFiles`. Read the
|
|
91
89
|
`docsDigestPath` digest your caller passes, then pull full files on demand
|
|
92
|
-
at the line numbers it names. A null `docsDigestPath` means no
|
|
93
|
-
|
|
94
|
-
one.
|
|
90
|
+
at the line numbers it names. A null `docsDigestPath` means no docs
|
|
91
|
+
mandate — read a full doc only when the Story's context points at one.
|
|
95
92
|
|
|
96
93
|
## Close gates — do not pre-run
|
|
97
94
|
|
|
98
95
|
`single-story-close.js` runs the canonical close-validation chain
|
|
99
96
|
(**typecheck, lint, test, format, maintainability, coverage, crap**) before
|
|
100
|
-
it merges. Advisory pre-flight
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
it merges. Advisory pre-flight is fine; the close pipeline is the
|
|
98
|
+
authoritative gate. The acceptance self-eval loop may share `lint` /
|
|
99
|
+
`typecheck` evidence with close via `evidence-gate.js`; never stamp
|
|
100
|
+
coverage / CRAP fresh that way.
|
|
104
101
|
|
|
105
102
|
Before trusting a gate's output — or diagnosing a red one — read
|
|
106
103
|
[`known-tooling-behavior.md`](../rules/known-tooling-behavior.md): measured
|
|
107
|
-
cases where a command prints what it does not mean
|
|
108
|
-
`0 error(s)` summary; a green `check-baselines.js` is not `baselines`).
|
|
104
|
+
cases where a command prints what it does not mean.
|
|
109
105
|
|
|
110
106
|
## Acceptance self-eval before close (MUST)
|
|
111
107
|
|
|
@@ -115,15 +111,14 @@ run the bounded acceptance self-eval loop
|
|
|
115
111
|
It scores the change set you computed **once** and injected into the critic
|
|
116
112
|
— never one the critic re-derives (Story #4593) — against each
|
|
117
113
|
`acceptance[]` item, consuming `verify[]` output as required evidence. Gate
|
|
118
|
-
outcomes: **proceed** → flip to `closing
|
|
119
|
-
flagged criteria, commit, re-eval; **block** → take the blocked path
|
|
120
|
-
Never silently
|
|
114
|
+
outcomes: **proceed** → flip to `closing`, push, hand off; **redraft** → fix
|
|
115
|
+
the flagged criteria, commit, re-eval; **block** → take the blocked path
|
|
116
|
+
below. Never silently hand off an unscored branch.
|
|
121
117
|
|
|
122
118
|
## Lifecycle: progress & blocked (MUST)
|
|
123
119
|
|
|
124
120
|
- **Progress.** Relay one terse line per phase transition (e.g.
|
|
125
|
-
`Story #<id>: implementing → closing`)
|
|
126
|
-
those lines are the progress surface.
|
|
121
|
+
`Story #<id>: implementing → closing`).
|
|
127
122
|
- **Blocked.** When you genuinely cannot proceed, transition the Story to
|
|
128
123
|
`agent::blocked`, post a `friction` comment naming the decision needed
|
|
129
124
|
(or the unmet criteria and their evidence), and **exit non-zero**.
|
|
@@ -134,31 +129,23 @@ Never silently proceed to close.
|
|
|
134
129
|
|
|
135
130
|
The Story's init envelope carries `remoteVerified` + `remoteProbe`. When
|
|
136
131
|
`remoteVerified` is `false`, transition the Story to `agent::blocked`
|
|
137
|
-
quoting `remoteProbe.detail` and stop.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
`single-story-close.js` emits a validated envelope between its
|
|
158
|
-
`--- STORY DELIVER TERMINAL ---` markers — **relay it**, never
|
|
159
|
-
hand-compose one. Status ↔ exit code: `landed` → 0; `pending` → 3
|
|
160
|
-
(**resumable, not a failure** — its `nextCommand` resumes it; the only
|
|
161
|
-
sanctioned no-merge ending); `blocked` / `failed` → exit non-zero via the
|
|
162
|
-
blocked path above. Stranded? Probe, don't guess:
|
|
163
|
-
`node .agents/scripts/deliver-recover.js --story <id>` (read-only, prints
|
|
164
|
-
one next command).
|
|
132
|
+
quoting `remoteProbe.detail` and stop. A PR opened by
|
|
133
|
+
`single-story-close.js` is the only sanctioned landing.
|
|
134
|
+
|
|
135
|
+
## Your turn ends at a pushed branch (MUST)
|
|
136
|
+
|
|
137
|
+
You do **not** run close. Push `story-<storyId>` to `origin` — confirming
|
|
138
|
+
the remote ref moved — and return. The dispatching orchestrator runs
|
|
139
|
+
`single-story-close.js` in its own session, serialized against your
|
|
140
|
+
siblings. Do not open the PR, do not flip `agent::done`, and do not spawn
|
|
141
|
+
a child to close on your behalf.
|
|
142
|
+
|
|
143
|
+
## Return contract — the hand-off report
|
|
144
|
+
|
|
145
|
+
Return a short, literal hand-off your caller can act on: the Story id,
|
|
146
|
+
`workCwd`, the branch name, the pushed head SHA, the self-eval verdict,
|
|
147
|
+
and the `verify[]` evidence you gathered.
|
|
148
|
+
Say plainly that the branch is pushed and unclosed. Never hand-compose a
|
|
149
|
+
terminal envelope — that document belongs to close, and inventing one
|
|
150
|
+
makes an unlanded Story look landed. If the push itself fails, take the
|
|
151
|
+
blocked path above rather than returning a hand-off you cannot back.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
# Architecture & Clean Code Audit — authoring checklist
|
|
8
8
|
|
|
9
|
-
> Audit architectural boundaries, module coupling, and
|
|
9
|
+
> Audit architectural boundaries, module coupling, layering violations, and shipped-but-uncalled seams; emit a structured findings report keyed to the canonical severity scale.
|
|
10
10
|
|
|
11
11
|
Self-check your change against this lens's concerns before you ship:
|
|
12
12
|
|
|
@@ -21,3 +21,7 @@ Self-check your change against this lens's concerns before you ship:
|
|
|
21
21
|
- [ ] Medium
|
|
22
22
|
- [ ] Low
|
|
23
23
|
- [ ] Automated Architecture Guardrails
|
|
24
|
+
- [ ] Shipped-But-Never-Wired Seams (mandatory).
|
|
25
|
+
- [ ] A produced-but-never-consumed artifact.
|
|
26
|
+
- [ ] An optional field nothing populates.
|
|
27
|
+
- [ ] An exported seam with no in-tree caller
|
|
@@ -18,3 +18,6 @@ Self-check your change against this lens's concerns before you ship:
|
|
|
18
18
|
- [ ] Test Data Management
|
|
19
19
|
- [ ] Performance & Execution
|
|
20
20
|
- [ ] Requirement Alignment
|
|
21
|
+
- [ ] Unwired Seams — Coverage Without a Caller (mandatory).
|
|
22
|
+
- [ ] A produced-but-never-consumed artifact
|
|
23
|
+
- [ ] An optional field nothing populates
|
|
@@ -278,10 +278,11 @@ top-level keys are validation errors.
|
|
|
278
278
|
| `acceptanceEval.maxRounds` | No | `integer` | — | Maximum number of redraft rounds before escalation. Default 2; clamped into [1, hard ceiling] by lib/config/acceptance-eval.js so the cap can never be disabled (maxRounds: 0 clamps up to 1). |
|
|
279
279
|
| `acceptanceEval.clusterCeiling` | No | `integer` | — | Epic #4475 (M4-B). Max acceptance criteria one single-delivery acceptance critic scores in a single fresh-context pass. Single delivery clusters the Epic ## Acceptance Table ACs into ceil(totalACs / clusterCeiling) groups and spawns one maker-blind critic per cluster, restoring the distributed acceptance coverage the per-Story critic fan-out gave for free. Default 4; clamped into [1, 8] by lib/config/acceptance-eval.js so a large value cannot collapse the fan-out to a single diluted critic. Ignored on the fan-out route. |
|
|
280
280
|
| `ci` | No | `object` | — | Nested configuration block. |
|
|
281
|
-
| `ci.watch` | No | `object` | — | Story #4356 (Epic #4355). Poll-loop tuning for the merge/CI watch. pollIntervalMs is the cadence between check probes; maxPolls caps total probes before the watcher gives up; maxResumes caps how many times the watcher may resume after a transient stall. |
|
|
281
|
+
| `ci.watch` | No | `object` | — | Story #4356 (Epic #4355). Poll-loop tuning for the merge/CI watch. pollIntervalMs is the cadence between check probes; maxPolls caps total probes before the watcher gives up; maxResumes caps how many times the watcher may resume after a transient stall; attachWindowMs bounds the wait for a required context to attach at all. |
|
|
282
282
|
| `ci.watch.pollIntervalMs` | No | `integer` | — | — |
|
|
283
283
|
| `ci.watch.maxPolls` | No | `integer` | — | — |
|
|
284
284
|
| `ci.watch.maxResumes` | No | `integer` | — | — |
|
|
285
|
+
| `ci.watch.attachWindowMs` | No | `integer` | — | Story #4890. How long (ms) the watch keeps re-resolving an EMPTY `gh pr checks --required` set before it stops waiting for a required context to attach. A ruleset attaches its contexts asynchronously and the arrival latency is set by the slowest one, so a required context that is an aggregator job gated on every other tier is the last to appear — measured at 16m52s on this repository. Default 1200000 (20 minutes). Raise it for a repository whose contexts arrive later still; exhausting the window is never reported as a red check (the watch exits 2, not-yet-started). |
|
|
285
286
|
| `ci.autoMerge` | No | `"trust-ci"` \| `"strict"` | — | Story #4356 (Epic #4355). Merge posture. 'trust-ci' (default) merges once required checks pass; 'strict' additionally requires a clean review gate. |
|
|
286
287
|
| `routing` | No | `object` | — | v2 delivery-spawn routing: role-scoped boot contexts and maker-checker sampling. The v1 singleDelivery epic-route kill-switch was removed in Stage 6. |
|
|
287
288
|
| `routing.roleScopedAgents` | No | `boolean` | — | Epic #4478 (M7-B). Kill-switch for the role-scoped boot contexts. When true (default), a converted delivery spawn (`story-worker`, `acceptance-critic`) boots on its own `.claude/agents/<role>.md` system prompt instead of re-paying the full CLAUDE.md @-import closure (≈50KB → ≈8KB per spawn — the payoff of the context diet). When false, every converted spawn falls back to `subagent_type: general-purpose` — the instant, code-rollback-free per-consumer revert, and the universal escape for hosts that ignore `.claude/agents/`. The fallback is the full-closure agent that ran before M7-B, so flipping it off never drops a gate. |
|
|
@@ -37,7 +37,7 @@ description, edit the workflow file’s front-matter and regenerate.
|
|
|
37
37
|
| Command | Description |
|
|
38
38
|
| --- | --- |
|
|
39
39
|
| `/audit-accessibility` | Audit WCAG accessibility conformance (static-first) with an optional runtime verification pass, and produce a structured findings report |
|
|
40
|
-
| `/audit-architecture` | Audit architectural boundaries, module coupling, and
|
|
40
|
+
| `/audit-architecture` | Audit architectural boundaries, module coupling, layering violations, and shipped-but-uncalled seams; emit a structured findings report keyed to the canonical severity scale. |
|
|
41
41
|
| `/audit-clean-code` | Audit code smells, dead code, complexity hotspots, and maintainability-index outliers; emit a structured findings report. |
|
|
42
42
|
| `/audit-data-model` | Audit the persistence layer as a first-class artifact — model↔migration↔seed drift, constraint completeness, migration hygiene, type fidelity, and access-pattern fit; gated by a persistence-layer applicability probe so DB-less repos skip cleanly. |
|
|
43
43
|
| `/audit-dependencies` | Audit `package.json` for unused, outdated, and major-version-stale dependencies; surface Node-engine drift and propose upgrade batches. |
|
|
@@ -19,8 +19,10 @@ exactly one of two ways, and no others:
|
|
|
19
19
|
caused by the diff under review — **verify the same check against an
|
|
20
20
|
unmodified `main` checkout**; if it also fails on `main` the defect is
|
|
21
21
|
pre-existing and belongs in a separate change — then fix it at source,
|
|
22
|
-
commit on `story-<storyId>`, push, and re-run the watcher. Auto-merge
|
|
23
|
-
|
|
22
|
+
commit on `story-<storyId>`, push, and re-run the watcher. Auto-merge is
|
|
23
|
+
**disarmed on the first red** and re-armed only by a green on a **new head
|
|
24
|
+
SHA**, so the fix must be a new commit. Route deterministic per-check
|
|
25
|
+
failures (lint/format,
|
|
24
26
|
maintainability/CRAP baseline drift, test failure, coverage threshold)
|
|
25
27
|
through the fix table in
|
|
26
28
|
[`deliver-story-reference.md` § Step 4](../workflows/helpers/deliver-story-reference.md#step-4--ci-watch--fix-recovery);
|
|
@@ -39,6 +41,51 @@ through the same two options; bisect environment (runner OS, Node version,
|
|
|
39
41
|
concurrency, a platform-conditional branch, an external service) vs. code (an
|
|
40
42
|
order-dependent test, a race, a shared-state assumption) to decide which.
|
|
41
43
|
|
|
44
|
+
## Verdicts
|
|
45
|
+
|
|
46
|
+
Every red check reaches exactly one verdict, and each verdict routes to one of
|
|
47
|
+
the two options above. Name the verdict you reached in the `friction` comment.
|
|
48
|
+
|
|
49
|
+
| Verdict | Evidence | Routes to |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| **defect-in-diff** | The failure reproduces on the branch and not on an unmodified `main` | Option 1 — fix at source |
|
|
52
|
+
| **pre-existing** | The same check fails on an unmodified `main` too | Option 2 — file `meta::framework-gap`; remediate here only if it blocks this delivery |
|
|
53
|
+
| **capacity** | Proven exhaustion of a runner resource, not a property of the diff (see below) | Option 2 — file `meta::framework-gap` **and** escalate to the operator |
|
|
54
|
+
|
|
55
|
+
### The `capacity` verdict
|
|
56
|
+
|
|
57
|
+
A job can fail because the runner ran out of something, not because the code is
|
|
58
|
+
wrong: no runner could be provisioned, the disk or memory ceiling was hit, a
|
|
59
|
+
process/PTY/file-descriptor limit was exhausted, the job wall-clock timed out
|
|
60
|
+
with no progress, or a self-hosted pool was saturated. Nothing on the branch
|
|
61
|
+
causes it and nothing on the branch can fix it.
|
|
62
|
+
|
|
63
|
+
This verdict exists because the rule previously offered no landing for that
|
|
64
|
+
case. The honest reading of "a red check is a defect until proven otherwise" is
|
|
65
|
+
that capacity failures are the *otherwise* — but with no verdict for them the
|
|
66
|
+
only shapes on offer were "fix the diff" (impossible) and "it's flaky, re-run
|
|
67
|
+
it" (forbidden), so the rule got broken rather than followed. Naming the verdict
|
|
68
|
+
removes the incentive to launder a capacity failure as a rerun.
|
|
69
|
+
|
|
70
|
+
**Capacity must be proven, not inferred.** A green on re-run is the single
|
|
71
|
+
weakest form of evidence for it and never establishes it — that is precisely the
|
|
72
|
+
observation a flaky test produces. Cite the resource and the reading: the log
|
|
73
|
+
line naming the exhausted limit (an OOM kill, `ENOSPC`, `EMFILE`,
|
|
74
|
+
`forkpty/sudo: Device not configured`, a provisioning error, a no-output
|
|
75
|
+
timeout), plus the fact that the failure is not specific to this diff. Absent
|
|
76
|
+
that reading the verdict is **flaky, not capacity**, and it routes to Option 1.
|
|
77
|
+
|
|
78
|
+
On a `capacity` verdict: file the `meta::framework-gap` issue with the run link,
|
|
79
|
+
the failure signature, and the resource reading; flip the Story to
|
|
80
|
+
`agent::blocked` with a `friction` comment naming the verdict; and hand back to
|
|
81
|
+
the operator, who owns the runner pool. Do not sit in a retry loop waiting for
|
|
82
|
+
capacity to return.
|
|
83
|
+
|
|
84
|
+
**Rerunning a failed job to reach green stays forbidden under every verdict,
|
|
85
|
+
`capacity` included.** The verdict changes who owns the fix and where it is
|
|
86
|
+
filed; it never licenses a re-run, and it is not a route to a green bar. A
|
|
87
|
+
capacity-blocked delivery ends `agent::blocked` — not merged.
|
|
88
|
+
|
|
42
89
|
## Verifier
|
|
43
90
|
|
|
44
91
|
The check is resolved only when it is **green with zero reruns of the failed
|
|
@@ -48,6 +95,23 @@ re-run a failed job to "see if it goes green," and you may **not** skip,
|
|
|
48
95
|
`.only`, or quarantine a flaky test to get a green bar. Both mask the defect
|
|
49
96
|
and are prohibited by this rule.
|
|
50
97
|
|
|
98
|
+
**The enforcement point is
|
|
99
|
+
[`pr-watch-with-update.js`](../scripts/pr-watch-with-update.js)**, and it acts
|
|
100
|
+
on the **first red** — GitHub's native auto-merge fires server-side, so it
|
|
101
|
+
races any attempt to detect a rerun-green and block it after the fact. On the
|
|
102
|
+
first red the watcher **disarms native auto-merge** (a disarm failure is a
|
|
103
|
+
blocker, not a warning) and records the PR **head SHA** in the digest
|
|
104
|
+
alongside the failing check-run identity. On green it adjudicates:
|
|
105
|
+
|
|
106
|
+
- **Same head SHA** → the green came from re-running the failed job. The
|
|
107
|
+
watcher exits non-zero, flips the Story to `agent::blocked` with a
|
|
108
|
+
`friction` comment, and requires the `meta::framework-gap` issue (run link +
|
|
109
|
+
failure signature, both already in the digest) before the delivery proceeds.
|
|
110
|
+
- **New head SHA** → fix at source. The digest is retired, auto-merge is
|
|
111
|
+
re-armed, and the delivery continues unobstructed.
|
|
112
|
+
|
|
113
|
+
A delivery that never went red has no digest and is untouched.
|
|
114
|
+
|
|
51
115
|
## Escalation
|
|
52
116
|
|
|
53
117
|
Flip the ticket to `agent::blocked`, post a `friction` comment (naming the
|
|
@@ -65,4 +129,5 @@ operator under **any** of:
|
|
|
65
129
|
registry/network outage, a branch-protection or CI misconfiguration, an
|
|
66
130
|
expired credential) — file the `meta::framework-gap` issue (with run link +
|
|
67
131
|
signature) and escalate on the first encounter rather than burning iterations
|
|
68
|
-
trying to code around it.
|
|
132
|
+
trying to code around it. A proven-capacity failure is this case: reach the
|
|
133
|
+
`capacity` verdict above and escalate on the first encounter.
|
|
@@ -1367,7 +1367,7 @@
|
|
|
1367
1367
|
"properties": {
|
|
1368
1368
|
"watch": {
|
|
1369
1369
|
"type": "object",
|
|
1370
|
-
"description": "Story #4356 (Epic #4355). Poll-loop tuning for the merge/CI watch. pollIntervalMs is the cadence between check probes; maxPolls caps total probes before the watcher gives up; maxResumes caps how many times the watcher may resume after a transient stall.",
|
|
1370
|
+
"description": "Story #4356 (Epic #4355). Poll-loop tuning for the merge/CI watch. pollIntervalMs is the cadence between check probes; maxPolls caps total probes before the watcher gives up; maxResumes caps how many times the watcher may resume after a transient stall; attachWindowMs bounds the wait for a required context to attach at all.",
|
|
1371
1371
|
"properties": {
|
|
1372
1372
|
"pollIntervalMs": {
|
|
1373
1373
|
"type": "integer",
|
|
@@ -1380,6 +1380,11 @@
|
|
|
1380
1380
|
"maxResumes": {
|
|
1381
1381
|
"type": "integer",
|
|
1382
1382
|
"minimum": 0
|
|
1383
|
+
},
|
|
1384
|
+
"attachWindowMs": {
|
|
1385
|
+
"type": "integer",
|
|
1386
|
+
"minimum": 1,
|
|
1387
|
+
"description": "Story #4890. How long (ms) the watch keeps re-resolving an EMPTY `gh pr checks --required` set before it stops waiting for a required context to attach. A ruleset attaches its contexts asynchronously and the arrival latency is set by the slowest one, so a required context that is an aggregator job gated on every other tier is the last to appear — measured at 16m52s on this repository. Default 1200000 (20 minutes). Raise it for a repository whose contexts arrive later still; exhausting the window is never reported as a red check (the watch exits 2, not-yet-started)."
|
|
1383
1388
|
}
|
|
1384
1389
|
},
|
|
1385
1390
|
"additionalProperties": false
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"type": "string",
|
|
26
26
|
"description": "Optional per-kind stamp identifying the SCORING SEMANTICS that produced these rows — the dimension kernelVersion cannot express, because a kind's own scoring can change while the upstream package it stamps does not. A baseline whose stamp differs from the running scorer's is incomparable and the gate fails closed with re-baseline guidance rather than comparing across the boundary."
|
|
27
27
|
},
|
|
28
|
+
"tsTranspilerVersion": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "Optional per-kind stamp identifying the TypeScript transpiler that produced the sourcemap these rows' line coordinates were resolved through (Story #4866). Only meaningful for kinds whose rows carry a line coordinate derived from transpiled sources; a transpiler change can move every such coordinate, and for kinds that key rows on a line that coordinate is half the row identity."
|
|
31
|
+
},
|
|
28
32
|
"rollup": {
|
|
29
33
|
"type": "object",
|
|
30
34
|
"description": "Aggregate metrics keyed by component. The '*' key is reserved for the whole-repo rollup and is REQUIRED; additional keys MAY be present, one per declared component.",
|
|
@@ -48,7 +48,11 @@
|
|
|
48
48
|
"path": { "type": "string" },
|
|
49
49
|
"method": { "type": "string" },
|
|
50
50
|
"startLine": { "type": "integer", "minimum": 1 },
|
|
51
|
-
"crap": { "type": "number", "minimum": 0 }
|
|
51
|
+
"crap": { "type": "number", "minimum": 0 },
|
|
52
|
+
"coordinateSystem": {
|
|
53
|
+
"enum": ["transpiled"],
|
|
54
|
+
"description": "Coordinate provenance of startLine (Story #4866). Written ONLY when the row kept transpiled coordinates because its sourcemap lookup did not resolve — the absent-means-'original' default keeps a pure-JavaScript baseline byte-identical, since JavaScript coordinates already ARE original-source coordinates. A row carrying this key is not comparable to one without it: the compare refuses to resolve the two through its nearest-line drift heuristic rather than manufacturing a regression from the gap between two coordinate systems."
|
|
55
|
+
}
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
58
|
}
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
"regressions",
|
|
24
24
|
"newViolations",
|
|
25
25
|
"drifted",
|
|
26
|
+
"incomparable",
|
|
27
|
+
"provenanceMismatched",
|
|
28
|
+
"unscorable",
|
|
26
29
|
"removed",
|
|
27
30
|
"skippedNoCoverage",
|
|
28
31
|
"scope",
|
|
@@ -34,6 +37,21 @@
|
|
|
34
37
|
"regressions": { "type": "integer", "minimum": 0 },
|
|
35
38
|
"newViolations": { "type": "integer", "minimum": 0 },
|
|
36
39
|
"drifted": { "type": "integer", "minimum": 0 },
|
|
40
|
+
"incomparable": {
|
|
41
|
+
"type": "integer",
|
|
42
|
+
"minimum": 0,
|
|
43
|
+
"description": "Story #4866: scanned methods the compare REFUSED to resolve because their line coordinate provenance differs from the matching baseline rows'. Reported, never scored — resolving them through the nearest-line drift heuristic is what manufactures a regression no change can satisfy."
|
|
44
|
+
},
|
|
45
|
+
"provenanceMismatched": {
|
|
46
|
+
"type": "integer",
|
|
47
|
+
"minimum": 0,
|
|
48
|
+
"description": "Story #4871: scanned methods whose baseline candidates included at least one row expressed in a DIFFERENT line coordinate system, counted BEFORE the compare's provenance filter discards that evidence. This — not the drifted-row count, which by construction only ever counts rows whose coordinates agreed — is the quantity the unsound-basis backstop compares against its threshold."
|
|
49
|
+
},
|
|
50
|
+
"unscorable": {
|
|
51
|
+
"type": "integer",
|
|
52
|
+
"minimum": 0,
|
|
53
|
+
"description": "Story #4871: methods the scan could not score because no coverage artifact covered them. Reported rather than computed from an assumed zero percent coverage (which would drive CRAP maximal on untouched files), and excluded from the denominator of the unsound-basis ratio."
|
|
54
|
+
},
|
|
37
55
|
"removed": { "type": "integer", "minimum": 0 },
|
|
38
56
|
"skippedNoCoverage": { "type": "integer", "minimum": 0 },
|
|
39
57
|
"scope": {
|
|
@@ -97,6 +115,25 @@
|
|
|
97
115
|
}
|
|
98
116
|
}
|
|
99
117
|
}
|
|
118
|
+
},
|
|
119
|
+
"diagnostics": {
|
|
120
|
+
"type": "array",
|
|
121
|
+
"description": "Story #4866: named, gate-level findings emitted INSTEAD of per-method verdicts when the gate has established that no verdict it could produce would be meaningful — an incomparable baseline, or a comparison basis whose drifted-row ratio proves the two sides disagree on line coordinates. Present only in that case; `violations` is then empty and the preview gate exits 0.",
|
|
122
|
+
"items": {
|
|
123
|
+
"type": "object",
|
|
124
|
+
"required": ["name", "message"],
|
|
125
|
+
"additionalProperties": false,
|
|
126
|
+
"properties": {
|
|
127
|
+
"name": {
|
|
128
|
+
"type": "string",
|
|
129
|
+
"enum": [
|
|
130
|
+
"crap-unsound-comparison-basis",
|
|
131
|
+
"crap-baseline-incompatible"
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"message": { "type": "string" }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
100
137
|
}
|
|
101
138
|
}
|
|
102
139
|
}
|