mandrel 2.7.0 → 2.9.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.
Files changed (95) hide show
  1. package/.agents/README.md +104 -330
  2. package/.agents/agents/auditor.md +135 -0
  3. package/.agents/agents/plan-critic.md +80 -0
  4. package/.agents/audit-checklists/dependencies.md +7 -0
  5. package/.agents/audit-checklists/documentation.md +1 -0
  6. package/.agents/docs/SDLC.md +69 -162
  7. package/.agents/docs/configuration.md +159 -499
  8. package/.agents/docs/quality-gates.md +59 -180
  9. package/.agents/instructions.md +170 -295
  10. package/.agents/rules/changelog-style.md +8 -66
  11. package/.agents/rules/ci-remediation.md +65 -124
  12. package/.agents/rules/gherkin-standards.md +10 -31
  13. package/.agents/rules/git-conventions-reference.md +28 -61
  14. package/.agents/rules/git-conventions.md +1 -1
  15. package/.agents/rules/orchestration-error-handling.md +5 -15
  16. package/.agents/rules/security-baseline.md +7 -13
  17. package/.agents/rules/shell-conventions.md +4 -13
  18. package/.agents/rules/test-seams.md +2 -2
  19. package/.agents/rules/testing-standards.md +7 -17
  20. package/.agents/schemas/agentrc.schema.json +21 -0
  21. package/.agents/scripts/audit-to-stories.js +51 -0
  22. package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +120 -55
  23. package/.agents/scripts/lib/config-settings-schema.js +32 -0
  24. package/.agents/scripts/lib/findings/semantic-issue-search.js +43 -5
  25. package/.agents/scripts/lib/observability/terse-result.js +114 -0
  26. package/.agents/scripts/lib/orchestration/complexity-gate.js +207 -0
  27. package/.agents/scripts/lib/orchestration/plan-context.js +3 -0
  28. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +221 -8
  29. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +55 -14
  30. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +9 -3
  31. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +4 -1
  32. package/.agents/scripts/lib/orchestration/task-body-validator.js +13 -40
  33. package/.agents/scripts/lib/story-body/body-format-lints.js +215 -0
  34. package/.agents/scripts/lib/story-body/story-body.js +18 -2
  35. package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -0
  36. package/.agents/scripts/providers/github/issues.js +54 -7
  37. package/.agents/scripts/providers/github/search-budget.js +124 -0
  38. package/.agents/scripts/providers/github/search-query.js +71 -0
  39. package/.agents/scripts/single-story-confirm-merge.js +14 -5
  40. package/.agents/scripts/single-story-init.js +19 -3
  41. package/.agents/scripts/sync-branch-from-base.js +9 -3
  42. package/.agents/skills/core/api-and-interface-design/SKILL.md +23 -297
  43. package/.agents/skills/core/api-and-interface-design/reference.md +76 -0
  44. package/.agents/skills/core/browser-testing-with-devtools/SKILL.md +20 -327
  45. package/.agents/skills/core/browser-testing-with-devtools/reference.md +74 -0
  46. package/.agents/skills/core/code-review-and-quality/reference.md +16 -398
  47. package/.agents/skills/core/debugging-and-error-recovery/reference.md +14 -281
  48. package/.agents/skills/core/documentation-and-adrs/reference.md +42 -338
  49. package/.agents/skills/core/git-workflow-and-versioning/SKILL.md +28 -326
  50. package/.agents/skills/core/idea-refinement/SKILL.md +67 -193
  51. package/.agents/skills/core/security-and-hardening/SKILL.md +15 -31
  52. package/.agents/skills/core/security-and-hardening/reference.md +15 -273
  53. package/.agents/skills/skills.index.json +5 -5
  54. package/.agents/skills/stack/qa/gherkin-authoring/SKILL.md +16 -222
  55. package/.agents/skills/stack/qa/gherkin-authoring/reference.md +157 -0
  56. package/.agents/skills/stack/qa/playwright/SKILL.md +0 -29
  57. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +19 -23
  58. package/.agents/skills/stack/qa/qa-harness/SKILL.md +35 -53
  59. package/.agents/skills/stack/qa/vitest/SKILL.md +0 -29
  60. package/.agents/workflows/audit-accessibility.md +34 -108
  61. package/.agents/workflows/audit-architecture.md +35 -123
  62. package/.agents/workflows/audit-clean-code.md +19 -78
  63. package/.agents/workflows/audit-data-model.md +32 -100
  64. package/.agents/workflows/audit-dependencies.md +47 -111
  65. package/.agents/workflows/audit-devops.md +16 -83
  66. package/.agents/workflows/audit-documentation.md +46 -93
  67. package/.agents/workflows/audit-navigability.md +26 -80
  68. package/.agents/workflows/audit-performance.md +40 -106
  69. package/.agents/workflows/audit-privacy.md +17 -80
  70. package/.agents/workflows/audit-quality.md +35 -99
  71. package/.agents/workflows/audit-security.md +20 -78
  72. package/.agents/workflows/audit-seo.md +20 -98
  73. package/.agents/workflows/audit-sre.md +20 -88
  74. package/.agents/workflows/audit-to-stories.md +1 -8
  75. package/.agents/workflows/audit-ux-ui.md +17 -80
  76. package/.agents/workflows/deliver.md +54 -9
  77. package/.agents/workflows/git-cleanup.md +50 -275
  78. package/.agents/workflows/helpers/audit-lens-core.md +230 -0
  79. package/.agents/workflows/helpers/code-review.md +11 -23
  80. package/.agents/workflows/helpers/deliver-story-reference.md +114 -17
  81. package/.agents/workflows/helpers/deliver-story.md +36 -186
  82. package/.agents/workflows/helpers/qa-core.md +174 -0
  83. package/.agents/workflows/helpers/qa-run-scenario-reference.md +35 -0
  84. package/.agents/workflows/helpers/qa-run-scenario.md +11 -25
  85. package/.agents/workflows/helpers/worktree-lifecycle.md +6 -67
  86. package/.agents/workflows/mandrel-update.md +7 -13
  87. package/.agents/workflows/plan.md +44 -17
  88. package/.agents/workflows/qa-assist.md +140 -269
  89. package/.agents/workflows/qa-explore.md +125 -316
  90. package/.agents/workflows/qa-run.md +180 -380
  91. package/docs/CHANGELOG.md +30 -0
  92. package/package.json +1 -1
  93. package/.agents/workflows/helpers/audit-dual-path.md +0 -59
  94. package/.agents/workflows/helpers/audit-self-check.md +0 -70
  95. package/.agents/workflows/helpers/audit-severity-scale.md +0 -19
@@ -35,14 +35,13 @@ it once and reuse the context as you read through any individual gate.
35
35
 
36
36
  ## Concurrent close safety
37
37
 
38
- `/deliver` may close multiple Stories from separate Story branches in quick
39
- succession. Each Story rebases onto the latest `main` in its own base-sync
40
- phase (`phases/base-sync.js`) before the push, so concurrent closes serialize
38
+ `/deliver` may close multiple Stories from separate branches in quick
39
+ succession; each rebases onto the latest `main` in its own base-sync phase
40
+ (`phases/base-sync.js`) before the push, so concurrent closes serialize
41
41
  through their own worktrees rather than racing one shared branch. The push
42
- step itself does not retry: a rejected push fails the close non-zero and the
43
- operator resolves it, and a real content conflict (both Stories touched the
44
- same lines) surfaces at base-sync with a clear error, leaving the local tree
45
- clean for manual resolution.
42
+ does not retry a rejected push or a real content conflict fails the close
43
+ non-zero and leaves the tree clean for manual resolution. See
44
+ [`SDLC.md` § Concurrent close](SDLC.md#concurrent-close).
46
45
 
47
46
  ---
48
47
 
@@ -50,24 +49,18 @@ clean for manual resolution.
50
49
 
51
50
  `npm test` (via [`.agents/scripts/run-tests.js`](../scripts/run-tests.js))
52
51
  derives `--test-concurrency` from `os.availableParallelism()` at startup,
53
- clamped into the `[TEST_CONCURRENCY_MIN, TEST_CONCURRENCY_MAX]` range of
54
- `[1, 16]` (`resolveTestConcurrency`). The clamp keeps the value sane at
55
- both extremes: on the GitHub Actions 2-vCPU runner the derived value
56
- matches the host instead of leaving wall-clock on the table, and on
57
- very-wide dev hosts the cap of 16 bounds the filesystem-race surface
58
- from shared FS fixtures (`memfs` mounts, `temp/` snapshot dirs, the
59
- `coverage/` artifact directory shared with the CRAP gate).
52
+ clamped into `[1, 16]` (`resolveTestConcurrency`). The clamp keeps the value
53
+ sane at both extremes: on the GitHub Actions 2-vCPU runner the derived value
54
+ matches the host, and on very-wide dev hosts the cap of 16 bounds the
55
+ filesystem-race surface from shared FS fixtures (`memfs` mounts, `temp/`
56
+ snapshot dirs, the `coverage/` artifact directory shared with the CRAP gate).
60
57
 
61
58
  The coverage run is the exception: `npm run test:coverage`
62
- ([`.agents/scripts/run-coverage.js`](../scripts/run-coverage.js))
63
- still pins `--test-concurrency=8` so coverage timings stay comparable
64
- across hosts. That fixed 8 sits in the same neighborhood as the cap=8
65
- orchestration helpers (`SUBTICKET_HYDRATION_CONCURRENCY`, and
66
- historically the since-deleted wave-gate helper, removed in PR #3936)
67
- that settled on 8 as the project house-style ceiling. Any change to the
68
- clamp bounds or the coverage pin must be paired with a benchmark run on
69
- both a Windows dev host and a GitHub Actions runner to confirm it
70
- doesn't reintroduce concurrency flakes.
59
+ ([`.agents/scripts/run-coverage.js`](../scripts/run-coverage.js)) pins
60
+ `--test-concurrency=8` so coverage timings stay comparable across hosts. Any
61
+ change to the clamp bounds or the coverage pin should be validated on both a
62
+ Windows dev host and a GitHub Actions runner to confirm it doesn't reintroduce
63
+ concurrency flakes.
71
64
 
72
65
  ---
73
66
 
@@ -108,33 +101,14 @@ npm run coverage:update # writes baselines/coverage.json from the run
108
101
  `coverage-final.json` artifact (useful from CI hooks or close-validation
109
102
  runners that orchestrate coverage capture separately).
110
103
 
111
- The same files-out-of-scope list as before, declared in `.c8rc.cjs`:
112
-
113
- - `.agents/scripts/agents-bootstrap-github.js` one-shot bootstrap CLI
114
- whose meaningful logic (label taxonomy + project field defs) lives
115
- in `lib/label-taxonomy.js` and is unit-tested there. The CLI shell
116
- itself is integration-only against a live GitHub repo.
117
- - `plan-context.js`, `plan-persist.js`
118
- `/plan` CLI shells with no unit-test seam; the meaningful
119
- orchestration logic lives in `lib/orchestration/plan-context.js`,
120
- `lib/orchestration/plan-persist/*`, and the plan phase modules, and is
121
- unit-tested there.
122
- - A larger Story #1702 carve-out of top-level CLI gates, orchestration
123
- CLIs, git-manipulation CLIs, and `lib/*` glue (e.g. `lint-baseline.js`,
124
- `single-story-init.js`, `run-tests.js`,
125
- `lib/config-schema.js`) — see the `.c8rc.cjs` header comment for the
126
- per-category rationale and the authoritative entry list.
127
-
128
- Each excluded file also carries `/* node:coverage ignore file */` at
129
- the top of its source as a second line of defence; the full
130
- justification for each exclusion lives in the header comment of
131
- [`.c8rc.cjs`](../../.c8rc.cjs) and MUST be updated when the list changes.
132
-
133
- The current shape of this pipeline (NODE_V8_COVERAGE +
134
- `c8 report` instead of wrapping the run in `c8 <cmd>`) was chosen
135
- after a one-off A/B benchmark showed it was ~19 % faster end-to-end
136
- on a Windows dev host while producing the same `coverage-final.json`
137
- artifact.
104
+ The files-out-of-scope list is declared in [`.c8rc.cjs`](../../.c8rc.cjs) —
105
+ thin CLI shells (e.g. `agents-bootstrap-github.js`, `plan-context.js`,
106
+ `plan-persist.js`) plus the larger Story #1702 carve-out of
107
+ top-level/orchestration/git CLIs and `lib/*` glue, each with a per-entry
108
+ rationale in the `.c8rc.cjs` header comment (the authoritative list). Every
109
+ excluded file also carries `/* node:coverage ignore file */` at the top of its
110
+ source as a second line of defence; the header comment MUST be updated when the
111
+ list changes.
138
112
 
139
113
  ---
140
114
 
@@ -217,66 +191,31 @@ enforced by review (and partially by the audit suite):
217
191
  risk the floor gate exists to surface; the audit suite spot-checks
218
192
  the callee map at exclude-list churn time.
219
193
 
220
- Story #1602 audit pass (2026-05-13) removed two stale exclude entries
221
- (`epic-runner.js`, `ticket-decomposer.js`) whose source files had already
222
- been deleted in earlier refactors. Every remaining entry was re-verified
223
- against requirements 1 and 2 above.
224
-
225
- ### Discontinuity with v5 baselines
226
-
227
- The floor gate landed alongside a fresh baseline reset
228
- (Tasks #1623, #1625, #1626, #1629). Any direct numeric comparison
229
- against pre-floor-gate baseline snapshots is meaningless because the
230
- pre-rebrand scope included files the current tree excludes (CLI shells,
231
- generated artifacts) and because the absolute-floor gate is new —
232
- historical files that were "green" on the ratchet may now show as below
233
- floor and require either real test additions or an intentional
234
- `.c8rc.cjs` exclude. The Story #1602 close-out lists every file that
235
- flipped category in the reset.
236
-
237
194
  ---
238
195
 
239
196
  ## Anti-thrashing protocol
240
197
 
241
- Agents MUST halt, summarize blockers, and re-plan if they hit consecutive
242
- tool errors or perform consecutive analysis steps without modifying a
243
- file. When any threshold under
244
- the qualitative anti-thrashing cues in
245
- [`.agents/instructions.md`](../instructions.md) are tripped, the
246
- friction logger flips the Story to `agent::blocked` and
247
- posts a structured `friction` comment on the Story so the operator has
248
- the trace.
198
+ The qualitative anti-thrashing cues are owned by
199
+ [`.agents/instructions.md`](../instructions.md) § 1.I. When they trip, the
200
+ friction logger flips the Story to `agent::blocked` and posts a structured
201
+ `friction` comment on the Story so the operator has the trace.
249
202
 
250
203
  ---
251
204
 
252
205
  ## Per-Story acceptance self-eval gate
253
206
 
254
- After a Story's implementation commits land and **before** the Story
255
- proceeds to close, delivery runs a bounded acceptance self-eval loop
256
- (Step 1a of
257
- [`helpers/deliver-story`](../workflows/helpers/deliver-story.md); the shared
258
- per-round mechanic lives in
259
- [`helpers/acceptance-self-eval`](../workflows/helpers/acceptance-self-eval.md),
260
- with the gate CLI at
261
- [`.agents/scripts/acceptance-eval.js`](../scripts/acceptance-eval.js)).
262
- Each round, a fresh-context **critic pass** — independent of the
263
- implementing agent scores the change set its caller injected (never one
264
- it re-derives) against every inline `acceptance[]` item, using `verify[]`
265
- output as evidence, and yields one of three decisions:
266
-
267
- - **proceed** — all criteria met; the Story continues to close.
268
- - **redraft** — unmet criteria are redrafted and re-implemented, then
269
- re-evaluated in the next round.
270
- - **block** — criteria remain unmet after the round cap; the Story
271
- escalates to the blocked path (`agent::blocked`) for operator review.
272
-
273
- The loop is always on (hard cutover, no enable flag) and bounded by
274
- `delivery.acceptanceEval.maxRounds` (default 2, clamped to a minimum of
275
- 1 so the cap cannot be disabled). This gate is complementary to the
276
- close-validation chain above: that chain proves the code is *healthy*;
277
- this loop proves it satisfies *this Story's* acceptance criteria. See
278
- [`.agents/docs/configuration.md`](../docs/configuration.md) for
279
- the `delivery.acceptanceEval` field reference.
207
+ After a Story's implementation commits land and **before** it proceeds to
208
+ close, delivery runs a bounded acceptance self-eval loop: a fresh-context
209
+ critic scores the caller-injected change set against every inline
210
+ `acceptance[]` item (using `verify[]` output as evidence) and yields
211
+ **proceed** / **redraft** / **block**. This gate is complementary to the
212
+ close-validation chain above — that chain proves the code is *healthy*, this
213
+ loop proves it satisfies *this Story's* acceptance criteria. The per-round
214
+ mechanic is owned by
215
+ [`helpers/acceptance-self-eval`](../workflows/helpers/acceptance-self-eval.md)
216
+ (Step 1a of [`helpers/deliver-story`](../workflows/helpers/deliver-story.md));
217
+ the `delivery.acceptanceEval` field reference is in
218
+ [`configuration.md`](../docs/configuration.md).
280
219
 
281
220
  ---
282
221
 
@@ -298,8 +237,8 @@ node .agents/scripts/lint-baseline.js capture
298
237
 
299
238
  Refresh commits should use a `baseline-refresh:` subject + non-empty body so
300
239
  the operator can spot baseline edits in review — same convention as the CRAP
301
- and maintainability ratchets. The CI guardrail that mechanically enforced
302
- this was removed in a pre-npm-era release; the operator is now the gate.
240
+ and maintainability ratchets. There is no CI guardrail enforcing the
241
+ convention; the operator is the gate.
303
242
 
304
243
  ---
305
244
 
@@ -316,8 +255,8 @@ between Stories.
316
255
  Refresh with `npm run maintainability:update`.
317
256
 
318
257
  `delivery.quality.gates.maintainability.targetDirs` controls the scanned
319
- directories defaults to `["src"]`, accepts `{ "append": [...] }` /
320
- `{ "prepend": [...] }` for additive overrides.
258
+ directories (see [`configuration.md`](../docs/configuration.md) for the
259
+ default and the deep-merge extender form).
321
260
 
322
261
  ---
323
262
 
@@ -354,10 +293,8 @@ generate `baselines/crap.json`, and commit the file with a
354
293
  `baseline-refresh:` tagged subject + non-empty body so the
355
294
  refresh-guardrail accepts it on the next PR.
356
295
 
357
- The transitional informational mode (exit 0 on first sync) was retired in
358
- Story #791 because it allowed broken pipelines to ride green for an
359
- indeterminate window. If your test runner doesn't produce per-method
360
- coverage, see "Disabling the gate" below.
296
+ If your test runner doesn't produce per-method coverage, see "Disabling the
297
+ gate" below.
361
298
 
362
299
  ### Disabling the gate (single-flag opt-out)
363
300
 
@@ -381,28 +318,11 @@ source edits required. The maintainability ratchet keeps running.
381
318
 
382
319
  ### Extending `targetDirs` without re-listing framework defaults
383
320
 
384
- The config resolver supports deep-merge for list-valued keys. To add your
385
- own source dirs to the framework default (`["src"]`):
386
-
387
- ```jsonc
388
- {
389
- "delivery": {
390
- "quality": {
391
- "gates": {
392
- "crap": {
393
- "targetDirs": { "append": ["packages/foo/src", "packages/bar/src"] }
394
- }
395
- }
396
- }
397
- }
398
- }
399
- ```
400
-
401
- `{ "append": [...] }` and `{ "prepend": [...] }` are the deep-merge forms.
402
- Passing a plain array replaces the default entirely — useful when you
403
- want exactly your dirs and not the framework's. Unknown keys under
404
- `delivery.quality.gates.crap` warn but don't fail resolution, so you can
405
- extend forward-compatibly.
321
+ `targetDirs` (like the other list-valued gate keys) accepts the deep-merge
322
+ extender form `{ "append": [...] }` / `{ "prepend": [...] }` add to the
323
+ framework default (`["src"]`), while a plain array replaces it entirely. The
324
+ worked example and the general rule live once in
325
+ [`configuration.md` § How to extend](../docs/configuration.md#how-to-extend).
406
326
 
407
327
  ### Interpreting the JSON report
408
328
 
@@ -437,11 +357,9 @@ should land in a commit whose:
437
357
  `baseline-refresh:`).
438
358
  2. Body is non-empty and explains why the refresh is justified.
439
359
 
440
- The CI guardrail that mechanically rejected unlabeled baseline edits was
441
- removed in a pre-npm-era release alongside the bot-approver pipeline. The convention is
442
- preserved so the operator can grep refresh commits in PR diff, but
443
- self-policing is the operator's job during `/deliver`'s Phase 7
444
- watch loop — an unjustified baseline ratchet is no longer caught by CI.
360
+ There is no CI guardrail rejecting unlabeled baseline edits; the convention is
361
+ preserved so the operator can grep refresh commits in a PR diff, but
362
+ self-policing is the operator's job during `/deliver`'s watch loop.
445
363
 
446
364
  ---
447
365
 
@@ -508,50 +426,11 @@ correct shape for a gate with no rescoring path of its own.
508
426
 
509
427
  ## HITL blocker escalation
510
428
 
511
- `risk::high` is informational/planning metadata only. Runtime execution
512
- does not pause automatically on `risk::high`.
513
-
514
- The sole runtime HITL pause point is `agent::blocked`: when an agent
515
- encounters an unresolvable blocker (including unsafe destructive actions
516
- lacking explicit authorization), it flips the ticket to
517
- `agent::blocked`, posts friction context, and waits for operator resume
518
- (`agent::executing`).
519
-
520
- `planning.riskHeuristics` remains the rubric for identifying
521
- high-impact operations that should trigger blocker escalation.
522
-
523
- ---
524
-
525
- ## Post-floor-gate baseline reset (Story #1701)
526
-
527
- **Date:** 2026-05-14
528
- **Commit:** `0657272` (Story #1701, Epic #1653)
529
- **Files refreshed:** `baselines/coverage.json`,
530
- `baselines/maintainability.json`, `baselines/crap.json`.
531
-
532
- A one-time baseline reset captured fresh coverage, maintainability, and
533
- CRAP snapshots on the post-remediation `main` HEAD. The ratchet
534
- continues from this new floor, not from any pre-floor-gate history.
535
-
536
- **Policy:** these baselines are **non-comparable** to any prior
537
- baseline. Do not diff per-file numbers against pre-reset entries to
538
- reason about regressions — the post-remediation tree contains refactors,
539
- extractions, and coverage gains that shift the absolute numbers in ways
540
- the per-file ratchet cannot reconcile across the discontinuity. Use the
541
- post-reset capture as the new floor; ratchet from there.
542
-
543
- **Why:** Epic #1184 closed the floor-gate rollout. The absolute-floor
544
- gate (coverage 90/85/90, MI ≥ 70, CRAP ≤ 20) is wired into
545
- `.husky/pre-push` and the CI coverage workflow (see
546
- [`§ Absolute quality floors`](#absolute-quality-floors-epic-1184)).
547
- With the floor enforced on every in-scope file, every per-file baseline
548
- entry must clear the absolute floor — this snapshot is the first
549
- capture that holds that invariant repository-wide.
550
-
551
- **Operator action:** none. The baseline is committed and
552
- `maintainability:check` / `coverage:check` / `crap:check` pass against
553
- it out of the box. The next regression you see will be diffed against
554
- this baseline, not against pre-reset history.
429
+ `risk::high` is planning/audit metadata only — it never pauses runtime. The
430
+ sole runtime HITL pause point is `agent::blocked`; `planning.riskHeuristics`
431
+ is the rubric for what should escalate. The full model is owned by
432
+ [`.agents/instructions.md`](../instructions.md) § 1.J and
433
+ [`SDLC.md` § HITL model](SDLC.md#hitl-human-in-the-loop-model).
555
434
 
556
435
  ---
557
436