mandrel 2.25.0 → 2.26.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 +10 -6
- package/.agents/audit-checklists/baselines.md +21 -0
- package/.agents/docs/quality-gates.md +80 -18
- package/.agents/docs/workflows.md +3 -1
- package/.agents/instructions.md +1 -1
- package/.agents/schemas/audit-rules.json +15 -0
- package/.agents/schemas/baselines/audit-baselines-envelope.schema.json +242 -0
- package/.agents/schemas/baselines/baseline-envelope.schema.json +4 -0
- package/.agents/schemas/baselines/crap.schema.json +8 -0
- package/.agents/schemas/model-attribution.schema.json +4 -0
- package/.agents/scripts/acceptance-eval.js +89 -6
- package/.agents/scripts/audit-baselines.js +136 -0
- package/.agents/scripts/check-arch-cycles.js +12 -93
- package/.agents/scripts/check-baseline-drift.js +16 -3
- package/.agents/scripts/check-baselines.js +19 -3
- package/.agents/scripts/check-cyclomatic.js +214 -0
- package/.agents/scripts/check-schema-references.js +392 -0
- package/.agents/scripts/check-test-temp-hygiene.js +38 -1
- package/.agents/scripts/check-workflow-timeouts.js +291 -0
- package/.agents/scripts/diagnose-friction.js +85 -19
- package/.agents/scripts/lib/audit-baselines/engine.js +177 -0
- package/.agents/scripts/lib/audit-baselines/gate-surface.js +63 -0
- package/.agents/scripts/lib/audit-baselines/headroom.js +72 -0
- package/.agents/scripts/lib/audit-baselines/hotspots.js +69 -0
- package/.agents/scripts/lib/audit-baselines/kinds.js +313 -0
- package/.agents/scripts/lib/audit-baselines/outliers.js +100 -0
- package/.agents/scripts/lib/audit-baselines/read.js +87 -0
- package/.agents/scripts/lib/audit-baselines/staleness.js +123 -0
- package/.agents/scripts/lib/audit-baselines/surface-entry.js +106 -0
- package/.agents/scripts/lib/audit-baselines/trend.js +125 -0
- package/.agents/scripts/lib/audit-baselines/weights.js +193 -0
- package/.agents/scripts/lib/audit-suite/index.js +0 -5
- package/.agents/scripts/lib/audit-suite/selector.js +9 -62
- package/.agents/scripts/lib/audit-to-stories/audit-lenses.js +1 -0
- package/.agents/scripts/lib/baseline-schema-registry.js +13 -1
- package/.agents/scripts/lib/baselines/diff-scope-cli.js +22 -160
- package/.agents/scripts/lib/baselines/duplication-scanner.js +27 -0
- package/.agents/scripts/lib/baselines/git-base.js +26 -4
- package/.agents/scripts/lib/baselines/kinds/crap.js +112 -15
- package/.agents/scripts/lib/baselines/refresh-service.js +69 -11
- package/.agents/scripts/lib/baselines/scope.js +39 -90
- package/.agents/scripts/lib/baselines/writer.js +16 -11
- package/.agents/scripts/lib/changed-files.js +8 -1
- package/.agents/scripts/lib/cli-args.js +115 -1
- package/.agents/scripts/lib/close-validation/runner.js +70 -25
- package/.agents/scripts/lib/crap-engine.js +32 -13
- package/.agents/scripts/lib/crap-method-identity.js +153 -0
- package/.agents/scripts/lib/crap-utils.js +13 -0
- package/.agents/scripts/lib/cyclomatic-ceiling.js +265 -0
- package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -2
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +0 -2
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +0 -2
- package/.agents/scripts/lib/git-utils.js +136 -80
- package/.agents/scripts/lib/import-graph.js +156 -0
- package/.agents/scripts/lib/observability/runtime-friction.js +17 -2
- package/.agents/scripts/lib/observability/source-classifier.js +175 -2
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +17 -12
- package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +36 -6
- package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +5 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +12 -1
- package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +8 -1
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +10 -5
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +39 -3
- package/.agents/scripts/lib/orchestration/plan-context.js +119 -66
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +31 -5
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +209 -109
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +48 -12
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +79 -22
- package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +51 -20
- package/.agents/scripts/lib/orchestration/planning/authoring-context.js +70 -74
- package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +231 -0
- package/.agents/scripts/lib/orchestration/resolve-stories.js +18 -17
- package/.agents/scripts/lib/orchestration/run-epilogue.js +12 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +29 -3
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +6 -6
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +42 -38
- package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +6 -1
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +245 -140
- package/.agents/scripts/lib/orchestration/spec-budget.js +16 -5
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +182 -95
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +22 -0
- package/.agents/scripts/lib/orchestration/ticket-validator.js +5 -11
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +4 -4
- package/.agents/scripts/lib/story-adjacency.js +3 -3
- package/.agents/scripts/lib/test-runner-contract.js +134 -0
- package/.agents/scripts/lib/test-tiers.js +11 -2
- package/.agents/scripts/lib/util/concurrent-map.js +17 -0
- package/.agents/scripts/lib/util/parse-id-list.js +103 -0
- package/.agents/scripts/lib/wave-runner/live-probe.js +24 -14
- package/.agents/scripts/lib/wave-runner/ready-set.js +189 -42
- package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +4 -10
- package/.agents/scripts/lib/workers/crap-worker.js +2 -10
- package/.agents/scripts/lib/workers/maintainability-report-worker.js +4 -10
- package/.agents/scripts/lib/workers/maintainability-worker.js +4 -10
- package/.agents/scripts/lib/workers/serve-worker-messages.js +35 -0
- package/.agents/scripts/lib/worktree/git-hooks.js +206 -0
- package/.agents/scripts/lib/worktree/lifecycle/creation.js +6 -0
- package/.agents/scripts/lib/worktree-manager.js +14 -0
- package/.agents/scripts/plan-run-epilogue.js +17 -5
- package/.agents/scripts/providers/github/tickets.js +33 -10
- package/.agents/scripts/provision-git-hooks.js +85 -0
- package/.agents/scripts/quality-preview.js +112 -28
- package/.agents/scripts/resolve-stories.js +4 -1
- package/.agents/scripts/run-coverage.js +86 -35
- package/.agents/scripts/run-lint.js +20 -0
- package/.agents/scripts/run-tests.js +26 -36
- package/.agents/scripts/single-story-close.js +28 -2
- package/.agents/scripts/single-story-confirm-merge.js +22 -6
- package/.agents/scripts/stories-wave-tick.js +214 -38
- package/.agents/scripts/update-coverage-baseline.js +34 -4
- package/.agents/scripts/update-duplication-baseline.js +209 -83
- package/.agents/scripts/validate-docs-freshness.js +1 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +4 -1
- package/.agents/skills/core/gates-and-baselines/SKILL.md +17 -11
- package/.agents/skills/skills.index.json +2 -2
- package/.agents/workflows/audit-baselines.md +289 -0
- package/.agents/workflows/audit-navigability.md +5 -4
- package/.agents/workflows/deliver.md +13 -4
- package/.agents/workflows/helpers/acceptance-self-eval.md +47 -10
- package/.agents/workflows/helpers/code-quality-guardrails.md +9 -2
- package/.agents/workflows/helpers/deliver-digest.md +41 -21
- package/.agents/workflows/helpers/deliver-reference.md +77 -1
- package/.agents/workflows/helpers/deliver-story-reference.md +47 -6
- package/.agents/workflows/helpers/plan-reference.md +15 -5
- package/.agents/workflows/memory-consolidate.md +116 -0
- package/.agents/workflows/plan.md +3 -0
- package/README.md +13 -6
- package/docs/CHANGELOG.md +64 -0
- package/package.json +9 -4
- package/.agents/schemas/friction-event.schema.json +0 -56
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +0 -707
|
@@ -115,10 +115,13 @@ For each acceptance item in your cluster:
|
|
|
115
115
|
|
|
116
116
|
## Verdict schema (MUST)
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
Write a verdict file under `temp/` at a **cluster-unique path** (e.g.
|
|
119
|
+
`temp/acceptance-verdict-<storyId>-r<round>-c<clusterIndex>.json`) so parallel
|
|
120
|
+
sibling critics cannot overwrite each other, conforming to
|
|
119
121
|
[`acceptance-eval-verdict.schema.json`](../schemas/acceptance-eval-verdict.schema.json):
|
|
120
122
|
one `criteria[]` record per acceptance item in your cluster, in acceptance-array
|
|
121
|
-
order.
|
|
123
|
+
order. Each `index` is the criterion's position in the Story's **full**
|
|
124
|
+
`acceptance[]` array, not within your cluster — the caller merges on it.
|
|
122
125
|
|
|
123
126
|
```json
|
|
124
127
|
{
|
|
@@ -146,10 +149,11 @@ order.
|
|
|
146
149
|
- `partial` — partially addressed, or addressed without the required evidence.
|
|
147
150
|
- `unmet` — not addressed, or the evidence contradicts the claim.
|
|
148
151
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
**Return the verdict file's absolute path to your caller — never invoke
|
|
153
|
+
`acceptance-eval.js` yourself.** The caller merges every cluster's records into
|
|
154
|
+
one verdict and calls the gate **once** per round; a per-cluster call would burn
|
|
155
|
+
a Story-level round per cluster. The **proceed / redraft / block** decision is
|
|
156
|
+
the gate's, not yours. You score; the gate decides.
|
|
153
157
|
|
|
154
158
|
## Boundaries
|
|
155
159
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!-- GENERATED FILE — do not edit by hand.
|
|
2
|
+
Source of truth: .agents/workflows/audit-baselines.md
|
|
3
|
+
Regenerate: node .agents/scripts/generate-lens-checklists.js
|
|
4
|
+
Drift is gated by: npm run docs:check
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Baseline & Ratchet Audit — authoring checklist
|
|
8
|
+
|
|
9
|
+
> Audit the committed baseline surface — dead instruments, stale baselines, cross-gate hotspot clusters, trend drift, and floor-tightening headroom — and emit findings whose remediation burns the measured debt down and tightens the ratchet behind it.
|
|
10
|
+
|
|
11
|
+
Self-check your change against this lens's concerns before you ship:
|
|
12
|
+
|
|
13
|
+
- [ ] `configError` non-null.
|
|
14
|
+
- [ ] `degradations`.
|
|
15
|
+
- [ ] Dead Instruments.
|
|
16
|
+
- [ ] Staleness.
|
|
17
|
+
- [ ] Hotspot Clusters.
|
|
18
|
+
- [ ] Trend Drift.
|
|
19
|
+
- [ ] Tightening Headroom.
|
|
20
|
+
- [ ] Hotspot Cluster template
|
|
21
|
+
- [ ] Tightening Headroom template
|
|
@@ -102,13 +102,18 @@ npm run coverage:update # writes baselines/coverage.json from the run
|
|
|
102
102
|
runners that orchestrate coverage capture separately).
|
|
103
103
|
|
|
104
104
|
The files-out-of-scope list is declared in [`.c8rc.cjs`](../../.c8rc.cjs) —
|
|
105
|
-
thin CLI shells
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
thin CLI shells plus the larger Story #1702 carve-out of
|
|
106
|
+
top-level/orchestration/git CLIs and `lib/*` glue. The `exclude[]` array is
|
|
107
|
+
the **single** declaration: each entry carries its rationale as an inline
|
|
108
|
+
comment on the line above it. Story #4922 removed the prose inventory the
|
|
109
|
+
header used to duplicate — two copies of one list in one file, 27 files
|
|
110
|
+
apart by the time it was measured. Do not reintroduce one. Every excluded
|
|
111
|
+
file also carries `/* node:coverage ignore file */` at the top of its source
|
|
112
|
+
as a second line of defence.
|
|
113
|
+
|
|
114
|
+
`.c8rc.cjs`'s `include` globs and `delivery.quality.gates.coverage.targetDirs`
|
|
115
|
+
in [`.agentrc.json`](../../.agentrc.json) MUST name the same roots — the gate
|
|
116
|
+
scores what c8 measures. `tests/c8rc-scope.test.js` asserts both invariants.
|
|
112
117
|
|
|
113
118
|
---
|
|
114
119
|
|
|
@@ -123,11 +128,30 @@ touched it:
|
|
|
123
128
|
|
|
124
129
|
| Metric | Floor | Scope |
|
|
125
130
|
| --- | --- | --- |
|
|
126
|
-
| Coverage — lines | ≥
|
|
127
|
-
| Coverage — branches | ≥ 85 % |
|
|
128
|
-
| Coverage — functions | ≥
|
|
129
|
-
| Maintainability Index | ≥ 70 |
|
|
130
|
-
| CRAP | ≤
|
|
131
|
+
| Coverage — lines | ≥ 94 % | repo rollup |
|
|
132
|
+
| Coverage — branches | ≥ 85 % | repo rollup |
|
|
133
|
+
| Coverage — functions | ≥ 87 % | repo rollup |
|
|
134
|
+
| Maintainability Index | ≥ 70 | repo rollup |
|
|
135
|
+
| CRAP — methods above 20 | ≤ 13 | repo rollup |
|
|
136
|
+
|
|
137
|
+
Floors are enforced against the baseline's `rollup` components — the
|
|
138
|
+
`applyFloors` phase compares `rollup["*"]` (and any named component), never
|
|
139
|
+
individual rows. Story #4922 corrected this table, which previously read
|
|
140
|
+
"per file" and quoted 90/85/90 for coverage; those numbers came from the
|
|
141
|
+
example in `.agents/docs/agentrc-reference.json`, which is validated only
|
|
142
|
+
against itself, and the coverage gate was not configured at all.
|
|
143
|
+
|
|
144
|
+
The live coverage floors are derived from the measurement in
|
|
145
|
+
[`baselines/coverage.json`](../../baselines/coverage.json) — a full-tier run
|
|
146
|
+
scored 95.65 / 86.16 / 88.52, and each floor sits ~1–1.7 points under its
|
|
147
|
+
axis. Re-derive them, do not invent them, whenever the baseline is
|
|
148
|
+
regenerated wholesale.
|
|
149
|
+
|
|
150
|
+
The coverage gate deliberately declares **no `tolerance`**, so its
|
|
151
|
+
head-vs-base ratchet arm reports regressions without failing the build (the
|
|
152
|
+
same shape the `crap` gate uses). Story #4922's scope was making the
|
|
153
|
+
instrument honest; arming the ratchet belongs with the debt burn-down that
|
|
154
|
+
the widened measurement newly exposes.
|
|
131
155
|
|
|
132
156
|
The floors are declared in [`.agentrc.json`](../../.agentrc.json) under
|
|
133
157
|
`delivery.quality.gates.<gate>.floors.*` (defaults baked into the helper
|
|
@@ -173,11 +197,11 @@ The floor gate is only as strict as its scope, so the `exclude` list in
|
|
|
173
197
|
[`.c8rc.cjs`](../../.c8rc.cjs) carries three hard requirements that are
|
|
174
198
|
enforced by review (and partially by the audit suite):
|
|
175
199
|
|
|
176
|
-
1. **One-line rationale per entry.** Every file in `exclude[]` MUST
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
200
|
+
1. **One-line rationale per entry.** Every file in `exclude[]` MUST carry
|
|
201
|
+
an inline comment on the line(s) directly above it naming *why* it is
|
|
202
|
+
excluded — typically "thin CLI shell, meaningful logic lives in
|
|
203
|
+
`lib/<X>` and is unit-tested there." A bare path with no rationale is a
|
|
204
|
+
review-block, and `tests/c8rc-scope.test.js` fails on one.
|
|
181
205
|
2. **`/* node:coverage ignore file */` pragma at source.** Every
|
|
182
206
|
excluded file MUST carry the Node coverage pragma at the top of its
|
|
183
207
|
own source. This is the second line of defence: when `c8 report` and
|
|
@@ -260,6 +284,42 @@ default and the deep-merge extender form).
|
|
|
260
284
|
|
|
261
285
|
---
|
|
262
286
|
|
|
287
|
+
## Cyclomatic ceiling ratchet
|
|
288
|
+
|
|
289
|
+
`delivery.quality.codingGuardrails.cyclomaticMustFix` (default `12`) is the
|
|
290
|
+
per-function complexity ceiling, enforced by `check-cyclomatic.js`. It is a
|
|
291
|
+
**standalone ratchet** — the same slot as `check-arch-cycles.js`,
|
|
292
|
+
`check-dead-exports.js`, and `check-context-budget.js` — not a
|
|
293
|
+
`delivery.quality.gates` kind, so it needs no gate block and no floor.
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
node .agents/scripts/check-cyclomatic.js # the gate
|
|
297
|
+
node .agents/scripts/check-cyclomatic.js --update # re-record the breaches
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
`baselines/cyclomatic.json` records, per file, how many functions currently
|
|
301
|
+
sit above the ceiling and how bad the worst one is. The gate fails when a
|
|
302
|
+
file's over-ceiling count rises (including `0 → 1`, a brand-new breach) or
|
|
303
|
+
when its worst function gets worse than recorded. Shrinking and disappearing
|
|
304
|
+
are the success signals and never fail.
|
|
305
|
+
|
|
306
|
+
Recording existing breaches is what makes the ceiling adoptable: a repository
|
|
307
|
+
with dozens of over-ceiling functions can turn the gate on today and burn them
|
|
308
|
+
down on its own schedule, instead of disabling a gate that fails on the first
|
|
309
|
+
commit. Re-run `--update` after a deliberate refactor; that is the only motion
|
|
310
|
+
allowed to raise a recorded count, and it shows up in review as a baseline
|
|
311
|
+
diff.
|
|
312
|
+
|
|
313
|
+
The scan reuses `delivery.quality.gates.maintainability.targetDirs` /
|
|
314
|
+
`ignoreGlobs` — both instruments read the same coverage-free escomplex
|
|
315
|
+
surface, so a separate scope declaration could only ever restate it.
|
|
316
|
+
|
|
317
|
+
`cyclomaticFlag` (default `8`) is the softer half of the pair: it is not
|
|
318
|
+
gated, and names the ceiling `quality:preview` counts new methods against in
|
|
319
|
+
its `new-method count over c=<flag>` column.
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
263
323
|
## CRAP gate — Consumer onboarding
|
|
264
324
|
|
|
265
325
|
> Baseline envelope, axes, and component model: see the
|
|
@@ -436,7 +496,9 @@ are not comparable to rows scored by this one, and neither `kernelVersion` nor
|
|
|
436
496
|
therefore carries a `scoringSemantics` stamp; `check-baselines` fails closed on
|
|
437
497
|
a mismatch with the exact re-baseline command rather than comparing across the
|
|
438
498
|
boundary. Bump the stamp whenever the coverage join, the line coordinate
|
|
439
|
-
system,
|
|
499
|
+
system, the unresolved-method policy, or the method identity rule changes —
|
|
500
|
+
Story #4969 bumped it for the last of these, replacing escomplex's positional
|
|
501
|
+
`<anon method-N>` label with an enclosing-scope-path identity.
|
|
440
502
|
|
|
441
503
|
---
|
|
442
504
|
|
|
@@ -32,12 +32,13 @@ by `node .agents/scripts/generate-workflows-doc.js`; `npm run docs:check`
|
|
|
32
32
|
fails when it drifts from the on-disk workflow set. To change a command’s
|
|
33
33
|
description, edit the workflow file’s front-matter and regenerate.
|
|
34
34
|
|
|
35
|
-
## Commands (
|
|
35
|
+
## Commands (27)
|
|
36
36
|
|
|
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
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
|
+
| `/audit-baselines` | Audit the committed baseline surface — dead instruments, stale baselines, cross-gate hotspot clusters, trend drift, and floor-tightening headroom — and emit findings whose remediation burns the measured debt down and tightens the ratchet behind it. |
|
|
41
42
|
| `/audit-clean-code` | Audit code smells, dead code, complexity hotspots, and maintainability-index outliers; emit a structured findings report. |
|
|
42
43
|
| `/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
44
|
| `/audit-dependencies` | Audit `package.json` for unused, outdated, and major-version-stale dependencies; surface Node-engine drift and propose upgrade batches. |
|
|
@@ -56,6 +57,7 @@ description, edit the workflow file’s front-matter and regenerate.
|
|
|
56
57
|
| `/git-cleanup` | Tidy the local checkout in four phases: fast-forward `main`, prune stale remote-tracking refs, sweep merged branches (squash-aware), and triage `git stash` entries — each step gated by operator confirmation. |
|
|
57
58
|
| `/git-deliver` | Single ad-hoc delivery command for working-tree changes. Detects the git setup and escalates to the right terminal step — commit only, commit + push, or commit + push + open a PR with native auto-merge — picking the default from observable state and letting flags pin any level explicitly. Replaces the retired git-commit-all, git-push, and git-pr-all trio. |
|
|
58
59
|
| `/mandrel-update` | npm-era upgrade wraparound for a Mandrel consumer. Runs `npx mandrel update` (resolve newest published version → install → re-materialize `.agents/` → migrate → doctor → surface changelog) as the single mechanical step, then walks the operator through the judgment wraparound the CLI deliberately leaves unowned: reconcile `.agentrc.json`, install the stabilized quality-gate surface, refresh the harness permission allowlist, reconcile the consumer's `AGENTS.md` / runbooks against the surfaced changelog, and stage + commit the staged lockfile bump. |
|
|
60
|
+
| `/memory-consolidate` | Attended consolidation pass over this project's agent memory pool — merge duplicates, verify claims against the current tree, prune with operator confirmation, rewrite the index, and stamp the pool so the /plan advisory goes quiet. |
|
|
59
61
|
| `/plan` | Unified planning entry point. Interrogate → author → persist. Emits one Story by default; splits into N>1 only under the default-single split policy. |
|
|
60
62
|
| `/prototype` | Operator-invoked UI prototype pass. Discovers the consumer's design-system SSOT first, then — only after the operator confirms — writes exactly one self-contained HTML file under the gitignored workspace-root temp tree, so a layout can be reviewed before its UI acceptance criteria are authored. |
|
|
61
63
|
| `/qa-assist` | Human-led QA assist loop — set up, then ride a rolling multi-observation intake session. The operator reports observations in any order; the agent enriches each (repro + root-cause file:line + coverage verdict for bugs; analysis + options + recommendation for enhancements), asks clarifying questions only when ambiguous, and appends a redacted ledger item — recording, never planning — to a persistent, resumable session under temp/qa/. Only when the operator says they are done does it review the full ledger and hand off to /plan. |
|
package/.agents/instructions.md
CHANGED
|
@@ -79,7 +79,7 @@ present, else the **Tech Stack** section of `docs/architecture.md`.
|
|
|
79
79
|
|
|
80
80
|
You MUST log operational friction (repeated tool errors, unrecoverable
|
|
81
81
|
command failures, self-corrected ambiguity, automatable boilerplate):
|
|
82
|
-
`node .agents/scripts/diagnose-friction.js --story [STORY_ID] --cmd
|
|
82
|
+
`node .agents/scripts/diagnose-friction.js --story [STORY_ID] --cmd <cmd> <args...>`
|
|
83
83
|
— a **local NDJSON signal** (not posted to the ticket). Schema and the
|
|
84
84
|
`AGENT_LOG_LEVEL` table:
|
|
85
85
|
[`docs/execution-reference.md`](docs/execution-reference.md#friction-telemetry).
|
|
@@ -167,6 +167,21 @@
|
|
|
167
167
|
"scope": "cumulative",
|
|
168
168
|
"substitutionKeys": []
|
|
169
169
|
},
|
|
170
|
+
"audit-baselines": {
|
|
171
|
+
"triggers": {
|
|
172
|
+
"gates": ["gate3"],
|
|
173
|
+
"keywords": [
|
|
174
|
+
"baseline",
|
|
175
|
+
"ratchet",
|
|
176
|
+
"floor",
|
|
177
|
+
"headroom",
|
|
178
|
+
"quality gate"
|
|
179
|
+
],
|
|
180
|
+
"filePatterns": ["baselines/**", ".agentrc.json"]
|
|
181
|
+
},
|
|
182
|
+
"scope": "cumulative",
|
|
183
|
+
"substitutionKeys": []
|
|
184
|
+
},
|
|
170
185
|
"audit-documentation": {
|
|
171
186
|
"triggers": {
|
|
172
187
|
"gates": ["gate1", "gate3"],
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://mandrel/.agents/schemas/baselines/audit-baselines-envelope.schema.json",
|
|
4
|
+
"title": "AuditBaselinesEnvelope",
|
|
5
|
+
"description": "Evidence envelope emitted by `.agents/scripts/audit-baselines.js` (Story #4902). Unlike its siblings in this directory it does NOT describe a committed baseline — it describes an engine's read-only report ABOUT the baseline surface, so it deliberately does not extend baseline-envelope.schema.json. Four sections: gateSurface (instrument health per kind), hotspots (ranked per-file clusters joining top-N outliers across gates), trend (rollup deltas from each baseline file's git history), and headroom (configured floor vs measured rollup).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"kind",
|
|
9
|
+
"schemaVersion",
|
|
10
|
+
"generatedAt",
|
|
11
|
+
"cwd",
|
|
12
|
+
"topN",
|
|
13
|
+
"configError",
|
|
14
|
+
"degradations",
|
|
15
|
+
"gateSurface",
|
|
16
|
+
"hotspots",
|
|
17
|
+
"trend",
|
|
18
|
+
"headroom"
|
|
19
|
+
],
|
|
20
|
+
"additionalProperties": false,
|
|
21
|
+
"properties": {
|
|
22
|
+
"kind": { "const": "audit-baselines-envelope" },
|
|
23
|
+
"schemaVersion": { "type": "string", "minLength": 1 },
|
|
24
|
+
"generatedAt": { "type": "string", "format": "date-time" },
|
|
25
|
+
"cwd": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"minLength": 1,
|
|
28
|
+
"description": "Repository root the engine ran against."
|
|
29
|
+
},
|
|
30
|
+
"topN": {
|
|
31
|
+
"type": "integer",
|
|
32
|
+
"minimum": 0,
|
|
33
|
+
"description": "Per-gate bound on extracted outlier rows. The envelope never embeds a full baseline; crap.json alone is ~650KB of per-method rows."
|
|
34
|
+
},
|
|
35
|
+
"configError": {
|
|
36
|
+
"type": ["string", "null"],
|
|
37
|
+
"description": "Message from a failed config resolution. Non-null means floors, targetDirs, and ignoreGlobs were unavailable and the engine fell back to default baseline paths."
|
|
38
|
+
},
|
|
39
|
+
"degradations": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"description": "Which optional inputs were unavailable. Each degradation collapses its rank multiplier to exactly 1.0 and never changes the exit code.",
|
|
42
|
+
"required": ["gitHistory", "importGraph", "frictionLedger"],
|
|
43
|
+
"additionalProperties": false,
|
|
44
|
+
"properties": {
|
|
45
|
+
"gitHistory": { "type": "boolean" },
|
|
46
|
+
"importGraph": { "type": "boolean" },
|
|
47
|
+
"frictionLedger": { "type": "boolean" }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"gateSurface": {
|
|
51
|
+
"type": "array",
|
|
52
|
+
"description": "One entry per kind across BOTH halves of the surface: the closed delivery.quality.gates kinds and the out-of-band ratchet baselines the CI baselines job owns.",
|
|
53
|
+
"items": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"required": [
|
|
56
|
+
"kind",
|
|
57
|
+
"surface",
|
|
58
|
+
"baselinePath",
|
|
59
|
+
"configured",
|
|
60
|
+
"baselineExists",
|
|
61
|
+
"stub",
|
|
62
|
+
"rowCount",
|
|
63
|
+
"measured",
|
|
64
|
+
"generatedAt",
|
|
65
|
+
"staleDays",
|
|
66
|
+
"staleCommits",
|
|
67
|
+
"surfaceStale",
|
|
68
|
+
"deadIgnoreGlobs",
|
|
69
|
+
"parseError"
|
|
70
|
+
],
|
|
71
|
+
"additionalProperties": false,
|
|
72
|
+
"properties": {
|
|
73
|
+
"kind": { "type": "string", "minLength": 1 },
|
|
74
|
+
"surface": { "enum": ["gate", "ratchet"] },
|
|
75
|
+
"baselinePath": { "type": "string", "minLength": 1 },
|
|
76
|
+
"configured": {
|
|
77
|
+
"type": "boolean",
|
|
78
|
+
"description": "A gate block for this kind is present in the resolved config."
|
|
79
|
+
},
|
|
80
|
+
"baselineExists": { "type": "boolean" },
|
|
81
|
+
"stub": {
|
|
82
|
+
"type": "boolean",
|
|
83
|
+
"description": "Zero rows AND an all-zero rollup — an instrument that passes every run vacuously. Both halves are required, so a ratchet baseline with nothing to report (e.g. zero import cycles) is never mistaken for a dead instrument."
|
|
84
|
+
},
|
|
85
|
+
"rowCount": {
|
|
86
|
+
"type": "integer",
|
|
87
|
+
"minimum": 0,
|
|
88
|
+
"description": "Rows AFTER the kind's per-file aggregation, so it is a file count for kinds whose on-disk grain is finer (dead exports are one row per {file, symbol}). Read `measured` for the quantity the instrument reports."
|
|
89
|
+
},
|
|
90
|
+
"measured": {
|
|
91
|
+
"type": ["object", "null"],
|
|
92
|
+
"description": "The whole-repo quantity in the unit the instrument measures — symbols for dead exports, bytes for context budget. Null when the baseline is absent or unreadable. Sits beside rowCount because the two legitimately disagree.",
|
|
93
|
+
"required": ["unit", "value"],
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"properties": {
|
|
96
|
+
"unit": { "type": "string", "minLength": 1 },
|
|
97
|
+
"value": { "type": "number" }
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"generatedAt": { "type": ["string", "null"] },
|
|
101
|
+
"staleDays": {
|
|
102
|
+
"type": ["integer", "null"],
|
|
103
|
+
"description": "Whole days since generatedAt. Null when the stamp is absent or unparseable — never a fabricated 0. Wall time only: a baseline can read 0 here and still predate the surface it scores, which is what staleCommits catches."
|
|
104
|
+
},
|
|
105
|
+
"staleCommits": {
|
|
106
|
+
"type": ["integer", "null"],
|
|
107
|
+
"description": "Commits touching the measured surface (the gate's targetDirs, else the baseline's own row paths) since the baseline was last committed. Null when git cannot answer or the surface is not path-keyed — an unknown count, never a reassuring 0.",
|
|
108
|
+
"minimum": 0
|
|
109
|
+
},
|
|
110
|
+
"surfaceStale": {
|
|
111
|
+
"type": ["boolean", "null"],
|
|
112
|
+
"description": "staleCommits > 0 — the baseline is older than the surface it scores, independent of wall-clock age. Null when staleCommits is unknown."
|
|
113
|
+
},
|
|
114
|
+
"deadIgnoreGlobs": {
|
|
115
|
+
"type": "array",
|
|
116
|
+
"description": "Configured ignoreGlobs matching zero files on disk.",
|
|
117
|
+
"items": { "type": "string" }
|
|
118
|
+
},
|
|
119
|
+
"parseError": { "type": ["string", "null"] }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"hotspots": {
|
|
124
|
+
"type": "array",
|
|
125
|
+
"description": "Per-file clusters, highest rank first. Severity sums across gate memberships, so a file that is an outlier in two gates outranks a single-gate outlier of equal per-gate severity.",
|
|
126
|
+
"items": {
|
|
127
|
+
"type": "object",
|
|
128
|
+
"required": [
|
|
129
|
+
"path",
|
|
130
|
+
"gates",
|
|
131
|
+
"gateKinds",
|
|
132
|
+
"gateCount",
|
|
133
|
+
"severityWeight",
|
|
134
|
+
"churnWeight",
|
|
135
|
+
"centralityWeight",
|
|
136
|
+
"frictionWeight",
|
|
137
|
+
"rank"
|
|
138
|
+
],
|
|
139
|
+
"additionalProperties": false,
|
|
140
|
+
"properties": {
|
|
141
|
+
"path": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"minLength": 1,
|
|
144
|
+
"description": "Cluster key. A repository file path for every kind except lighthouse (route) and bundle-size (bundle name)."
|
|
145
|
+
},
|
|
146
|
+
"gates": {
|
|
147
|
+
"type": "array",
|
|
148
|
+
"minItems": 1,
|
|
149
|
+
"items": {
|
|
150
|
+
"type": "object",
|
|
151
|
+
"required": [
|
|
152
|
+
"kind",
|
|
153
|
+
"metric",
|
|
154
|
+
"value",
|
|
155
|
+
"rowCount",
|
|
156
|
+
"severityWeight"
|
|
157
|
+
],
|
|
158
|
+
"additionalProperties": false,
|
|
159
|
+
"properties": {
|
|
160
|
+
"kind": { "type": "string", "minLength": 1 },
|
|
161
|
+
"metric": { "type": "string", "minLength": 1 },
|
|
162
|
+
"value": { "type": "number" },
|
|
163
|
+
"rowCount": { "type": "integer", "minimum": 1 },
|
|
164
|
+
"severityWeight": { "type": "number", "minimum": 0 }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"gateKinds": { "type": "array", "items": { "type": "string" } },
|
|
169
|
+
"gateCount": { "type": "integer", "minimum": 1 },
|
|
170
|
+
"severityWeight": { "type": "number", "minimum": 0 },
|
|
171
|
+
"churnWeight": { "type": "number", "minimum": 1 },
|
|
172
|
+
"centralityWeight": { "type": "number", "minimum": 1 },
|
|
173
|
+
"frictionWeight": { "type": "number", "minimum": 1 },
|
|
174
|
+
"rank": { "type": "number", "minimum": 0 }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"trend": {
|
|
179
|
+
"type": "array",
|
|
180
|
+
"description": "Newest-vs-previous rollup deltas parsed from each baseline file's git history. Empty when no history is readable.",
|
|
181
|
+
"items": {
|
|
182
|
+
"type": "object",
|
|
183
|
+
"required": [
|
|
184
|
+
"kind",
|
|
185
|
+
"baselinePath",
|
|
186
|
+
"sampleCount",
|
|
187
|
+
"from",
|
|
188
|
+
"to",
|
|
189
|
+
"deltas"
|
|
190
|
+
],
|
|
191
|
+
"additionalProperties": false,
|
|
192
|
+
"properties": {
|
|
193
|
+
"kind": { "type": "string", "minLength": 1 },
|
|
194
|
+
"baselinePath": { "type": "string", "minLength": 1 },
|
|
195
|
+
"sampleCount": { "type": "integer", "minimum": 2 },
|
|
196
|
+
"from": { "$ref": "#/definitions/commitRef" },
|
|
197
|
+
"to": { "$ref": "#/definitions/commitRef" },
|
|
198
|
+
"deltas": {
|
|
199
|
+
"type": "object",
|
|
200
|
+
"description": "Axis-by-axis `to - from`. Each key NAMES THE UNIT the delta is denominated in — `symbols` for dead exports, `bytes` for context budget, `filesTracked` where the metric is a non-additive score with no honest whole-repo sum. A kind carrying its own rollup keeps that rollup's axes.",
|
|
201
|
+
"additionalProperties": { "type": "number" }
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
"headroom": {
|
|
207
|
+
"type": "array",
|
|
208
|
+
"description": "Configured floor vs measured rollup per axis. Positive headroom is slack the floor could be tightened into; negative means the floor is already breached.",
|
|
209
|
+
"items": {
|
|
210
|
+
"type": "object",
|
|
211
|
+
"required": [
|
|
212
|
+
"kind",
|
|
213
|
+
"axis",
|
|
214
|
+
"floor",
|
|
215
|
+
"measured",
|
|
216
|
+
"direction",
|
|
217
|
+
"headroom"
|
|
218
|
+
],
|
|
219
|
+
"additionalProperties": false,
|
|
220
|
+
"properties": {
|
|
221
|
+
"kind": { "type": "string", "minLength": 1 },
|
|
222
|
+
"axis": { "type": "string", "minLength": 1 },
|
|
223
|
+
"floor": { "type": "number" },
|
|
224
|
+
"measured": { "type": ["number", "null"] },
|
|
225
|
+
"direction": { "enum": ["gte", "lte"] },
|
|
226
|
+
"headroom": { "type": ["number", "null"] }
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
"definitions": {
|
|
232
|
+
"commitRef": {
|
|
233
|
+
"type": "object",
|
|
234
|
+
"required": ["sha", "committedAt"],
|
|
235
|
+
"additionalProperties": false,
|
|
236
|
+
"properties": {
|
|
237
|
+
"sha": { "type": "string", "minLength": 7 },
|
|
238
|
+
"committedAt": { "type": "string", "minLength": 1 }
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
"type": "string",
|
|
30
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
31
|
},
|
|
32
|
+
"provenanceStamped": {
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"description": "Optional per-kind POSITIVE assertion that the writer which produced these rows recorded each row's coordinate provenance (Story #4901). Only meaningful for kinds whose rows carry a line coordinate that may be transpiled. Absence is the only evidence a baseline written before provenance existed leaves behind — such a baseline claims by omission that every row is an original-source coordinate — so the kind's compat axis fails it closed with re-seed guidance rather than letting the comparator key rows against a coordinate space they are not in."
|
|
35
|
+
},
|
|
32
36
|
"rollup": {
|
|
33
37
|
"type": "object",
|
|
34
38
|
"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.",
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"type": "string",
|
|
11
11
|
"description": "Identifier for the per-method coverage-join semantics that produced these rows (Story #4775). Deliberately NOT constrained to the current value here: an old-semantics baseline must reach the gate's compat axis, which fails it closed with an explicit re-baseline command, rather than being rejected with a raw schema error."
|
|
12
12
|
},
|
|
13
|
+
"provenanceStamped": {
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"description": "Positive assertion that the writer which produced these rows recorded coordinate provenance per row (Story #4901). Absence is the ONLY evidence a pre-Story-#4866 baseline leaves — such a baseline claims by omission that every row is an original-source coordinate while some are transpiled. Deliberately optional and unconstrained here for the same reason as scoringSemantics: an unstamped baseline must reach the `provenance-unstamped` compat axis, which fails it closed with the re-seed command when it also carries transpiled-source rows, rather than dying on a raw schema error. A pure-JavaScript baseline never needs it."
|
|
16
|
+
},
|
|
13
17
|
"rollup": {
|
|
14
18
|
"type": "object",
|
|
15
19
|
"required": ["*"],
|
|
@@ -49,6 +53,10 @@
|
|
|
49
53
|
"method": { "type": "string" },
|
|
50
54
|
"startLine": { "type": "integer", "minimum": 1 },
|
|
51
55
|
"crap": { "type": "number", "minimum": 0 },
|
|
56
|
+
"anonymous": {
|
|
57
|
+
"enum": [true],
|
|
58
|
+
"description": "Marks `method` as a DERIVED identity rather than a name the source carries (Story #4969). escomplex labels an unnamed function `<anon method-N>`, where N is a per-module counter, so inserting one anonymous function renumbers every later one and re-keys 34.6% of this baseline's rows; the derived identity is the method's enclosing-scope path plus its parameter list, which is invariant under edits elsewhere in the file. Written ONLY when true, mirroring `coordinateSystem`: a named row keeps the exact four-key shape it always had. Absence on a row whose `method` still looks anonymous is the signature of a half-migrated baseline — rows preserved verbatim by a diff-scoped refresh under an envelope stamped with the current semantics — which the `anon-identity-unstamped` compat axis fails closed."
|
|
59
|
+
},
|
|
52
60
|
"coordinateSystem": {
|
|
53
61
|
"enum": ["transpiled"],
|
|
54
62
|
"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."
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
"$id": "https://github.com/dsj1984/mandrel/blob/main/.agents/schemas/model-attribution.schema.json",
|
|
4
4
|
"title": "ModelAttribution",
|
|
5
5
|
"description": "Payload of the <!-- structured:model-attribution --> comment upserted onto a Task ticket at the moment it transitions to agent::executing (Story #2813). One entry per Task. Story- and Epic-level breakdowns are derived at query time from the child Tasks' attribution comments — there is no Story/Epic-scope emission.",
|
|
6
|
+
"x-mandrel-uncompiled": {
|
|
7
|
+
"reason": "Deliberate: this document is the SSOT for the shape, but no AJV instance compiles it. The framework does not pull AJV into the structured-comment path — that path uses hand-rolled shape guards — so the runtime gate below mirrors this document by hand. Keep the two in step: an edit here is only real once the validator enforces it. Declared in-file per Story #4938 so a reader is never left inferring authority from the file's mere existence.",
|
|
8
|
+
"runtimeGate": ".agents/scripts/lib/orchestration/model-attribution.js#validateModelAttributionPayload"
|
|
9
|
+
},
|
|
6
10
|
"type": "object",
|
|
7
11
|
"additionalProperties": false,
|
|
8
12
|
"required": ["kind", "ticketId", "model", "source", "recordedAt"],
|