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
package/.agents/README.md CHANGED
@@ -234,48 +234,17 @@ system-prompt mechanism (`.cursorrules`, Custom Instructions, etc.).
234
234
  ### What to always-load vs read on-demand
235
235
 
236
236
  The always-loaded context is re-paid on every session **and every subagent
237
- spawn**, so the shipped set is kept deliberately lean. Load this core into your
238
- system prompt; read everything else only when the task engages it (the same
239
- read-when-relevant pattern skills use).
240
-
241
- **Always-load (the recommended core):**
242
-
243
- - [`instructions.md`](instructions.md) — the core agent protocol.
244
- - [`rules/security-baseline.md`](rules/security-baseline.md) — inviolable
245
- security MUSTs, relevant to every change.
246
- - [`rules/git-conventions.md`](rules/git-conventions.md) every commit,
247
- branch, and PR touches it.
248
-
249
- **Read on-demand (do the read before the matching work):**
250
-
251
- - [`rules/shell-conventions.md`](rules/shell-conventions.md) — before chaining
252
- shell commands or writing cross-platform command strings.
253
- - [`rules/testing-standards.md`](rules/testing-standards.md) — before authoring
254
- or restructuring tests.
255
- - [`rules/orchestration-error-handling.md`](rules/orchestration-error-handling.md)
256
- — before writing or modifying orchestration scripts under
257
- `.agents/scripts/**`.
258
- - The remaining domain rules
259
- ([`rules/api-conventions.md`](rules/api-conventions.md),
260
- [`rules/gherkin-standards.md`](rules/gherkin-standards.md),
261
- [`rules/changelog-style.md`](rules/changelog-style.md),
262
- [`rules/test-seams.md`](rules/test-seams.md)) — when the task is in that
263
- domain.
264
- - Every `SKILL.md` under [`skills/`](skills/) — when the task hits its trigger.
265
- A `SKILL.md` is itself split the same way: it leads with its **Policy
266
- Capsule** (the contract, and the whole cost of activating the skill) plus
267
- pointers into an on-demand `reference.md` sibling carrying the long-form
268
- material. Activating a skill costs the capsule, not the essay — open a
269
- `reference.md` section only when the task engages it. Routing does not
270
- depend on the long-form being inline: skill descriptions live in the
271
- generated [`skills/skills.index.json`](skills/skills.index.json).
272
- - [`docs/execution-reference.md`](docs/execution-reference.md) — log-level and
273
- token-budget reference detail lifted out of `instructions.md`.
274
-
275
- Each on-demand rule opens with a one-line "this rule applies when…" scope
276
- header, so a quick skim of its first paragraph tells you whether it governs the
277
- task at hand. `instructions.md` § 1.F is the canonical in-prompt statement of
278
- this split.
237
+ spawn**, so the shipped set is kept deliberately lean. The recommended core to
238
+ load into your system prompt is [`instructions.md`](instructions.md),
239
+ [`rules/security-baseline.md`](rules/security-baseline.md), and
240
+ [`rules/git-conventions.md`](rules/git-conventions.md). Read everything else —
241
+ the on-demand rules, every `SKILL.md` under [`skills/`](skills/) (each split
242
+ into a Policy Capsule plus an on-demand `reference.md`; descriptions live in
243
+ [`skills/skills.index.json`](skills/skills.index.json)), and
244
+ [`docs/execution-reference.md`](docs/execution-reference.md) — only when the
245
+ task engages it. Each on-demand rule opens with a one-line "this rule applies
246
+ when…" scope header. `instructions.md` § 1.F is the canonical statement of
247
+ this split and enumerates the full always-on / on-demand rule set.
279
248
 
280
249
  ---
281
250
 
@@ -350,18 +319,13 @@ in `runtime-deps.json`.
350
319
 
351
320
  ## Ticket Hierarchy
352
321
 
353
- Orchestration and planning are **Story-only** (`type::story`): `/plan`
322
+ Orchestration and planning are **Story-only** (`type::story`) `/plan`
354
323
  persists Stories with inline `acceptance[]` / `verify[]` and a folded
355
- `## Spec`; `/deliver` runs `helpers/deliver-story` on
356
- `story-<id>` → PR → `main`. There is no `type::epic` / `type::task`
357
- label and no Epic issue form an Epic is at most an optional untyped
358
- human umbrella issue outside orchestration. There is no Epic wave /
359
- `epic/<id>` integration branch. Tickets that still carry an `Epic: #N`
360
- footer are refused by `/deliver` and must be closed or re-planned as
361
- v2 Stories.
362
-
363
- See [`docs/SDLC.md`](docs/SDLC.md) and [`instructions.md` § 5.D](instructions.md)
364
- for the execution-model contract.
324
+ `## Spec`; `/deliver` runs `helpers/deliver-story` on `story-<id>` → PR →
325
+ `main`. There is no `type::epic` / `type::task` label, Epic issue form, or
326
+ `epic/<id>` integration branch; a ticket carrying an `Epic: #N` footer is
327
+ refused by `/deliver`. The execution-model contract is owned by
328
+ [`instructions.md` § 5.D](instructions.md) and [`docs/SDLC.md`](docs/SDLC.md).
365
329
 
366
330
  ---
367
331
 
@@ -623,41 +587,21 @@ Module boundary rules:
623
587
 
624
588
  ## Baselines
625
589
 
626
- The framework's quality gates compare against per-kind baseline files
627
- under `baselines/<kind>.json` (lint, coverage, crap, maintainability,
628
- mutation, lighthouse, bundle-size). Every baseline shares a single
629
- envelope, every gate reads through one shared module
630
- ([`.agents/scripts/lib/baselines/reader.js`](scripts/lib/baselines/reader.js)),
631
- and every refresher writes through one shared writer
632
- ([`.agents/scripts/lib/baselines/writer.js`](scripts/lib/baselines/writer.js)).
633
-
634
- See the [Baseline reference](docs/quality-gates.md#baseline-reference)
635
- section of `.agents/docs/quality-gates.md` for the full reference: envelope shape,
636
- per-kind axes, component model, path canonicalisation, writer/reader
637
- contract, kernel-version friction, and — most relevant to consumers — the
638
- **floor override** path. Consumers add a `floors` block
639
- (and optional `components`) under their gate in `.agentrc.json`:
640
-
641
- ```json
642
- {
643
- "delivery": {
644
- "quality": {
645
- "gates": {
646
- "coverage": {
647
- "floors": { "*": { "lines": 90, "branches": 85 } },
648
- "components": { "api": ["src/api/**"] }
649
- }
650
- }
651
- }
652
- }
653
- }
654
- ```
655
-
590
+ The framework's quality gates compare against per-kind baseline files under
591
+ `baselines/<kind>.json` (lint, coverage, crap, maintainability, mutation,
592
+ lighthouse, bundle-size), all sharing one envelope and funnelled through a
593
+ shared reader / writer
594
+ ([`reader.js`](scripts/lib/baselines/reader.js) /
595
+ [`writer.js`](scripts/lib/baselines/writer.js)). The full reference envelope
596
+ shape, per-kind axes, component model, path canonicalisation, writer/reader
597
+ contract, kernel-version friction, and the consumer **floor override** path
598
+ (add a `floors` block, and optional `components`, under your gate in
599
+ `.agentrc.json`) is owned by
600
+ [`quality-gates.md` § Baseline reference](docs/quality-gates.md#baseline-reference).
656
601
  The unified runtime gate
657
- [`.agents/scripts/check-baselines.js`](scripts/check-baselines.js)
658
- currently runs floor + tolerance + schema + kernel-mismatch checks only;
659
- full regression absorption and per-kind CLI deletion are tracked in
660
- follow-up **Epic #1943**.
602
+ [`check-baselines.js`](scripts/check-baselines.js) currently runs floor +
603
+ tolerance + schema + kernel-mismatch checks only; full regression absorption
604
+ and per-kind CLI deletion are tracked in **Epic #1943**.
661
605
 
662
606
  ---
663
607
 
@@ -694,155 +638,31 @@ Schema conventions:
694
638
  ## Code review providers (pluggable chain)
695
639
 
696
640
  `runCodeReview()` (invoked from `helpers/deliver-story` and `/deliver`'s
697
- risk-routed ceremony) loads its review backend through a pluggable registry.
698
- Configure a provider chain via `delivery.codeReview.providers` — an array
699
- of entries iterated in declaration order. Inline adapters merge their
700
- `Finding[]`; manual-prompt entries append a non-blocking
701
- "Manual Review Suggestions" section. When `providers` is unset or empty,
702
- the factory defaults to `[{ name: "native" }]`.
703
-
704
- ```json
705
- {
706
- "delivery": {
707
- "codeReview": {
708
- "providers": [
709
- { "name": "native" },
710
- { "name": "security-review", "scopes": ["story"], "optional": true },
711
- {
712
- "name": "ultrareview",
713
- "scopes": ["story"],
714
- "manualPrompt": true,
715
- "when": { "label": "risk::high" }
716
- }
717
- ]
718
- }
719
- }
720
- }
721
- ```
722
-
723
- Each chain entry accepts:
724
-
725
- - `name` (required) — registered key. Inline: `native`, `codex`,
726
- `security-review`. Manual-prompt: `ultrareview`.
727
- - `scopes` — invocation scopes this entry fires on (`["story", "epic"]`).
728
- Default is both.
729
- - `optional` — when `true`, a construction failure (host missing the
730
- required CLI/plugin) is logged and the entry is skipped instead of
731
- hard-failing the chain. Use for portable configs that ship across
732
- Claude and non-Claude runtimes — for example,
733
- `security-review` requires the `claude` CLI on PATH and degrades
734
- cleanly on a non-Claude host when `optional: true`.
735
- - `manualPrompt` — when `true`, the entry is loaded from the
736
- manual-prompt registry and contributes a one-line operator suggestion
737
- via `renderPrompt()` instead of running a real review. Manual-prompt
738
- contributions do NOT affect severity counts or the `halted` gate.
739
- - `when` — optional label predicate evaluated at invocation time
740
- against the ticket's labels (`when.label` for a single required
741
- label, `when.labelAny` for "any of these"). False predicates skip
742
- the entry silently for that run.
743
-
744
- Cross-runtime contract: manual-prompt providers (e.g. `ultrareview`)
745
- emit Markdown only and MUST NEVER throw under any host. Inline
746
- providers that require a host-specific binary (e.g.
747
- `security-review` shells out to `claude --print /security-review`)
748
- SHOULD be declared `optional: true` so non-Claude consumers can pin
749
- the same `.agents/` version without modifying their config.
750
-
751
- The pluggable backend was introduced in Epic #2815; the multi-provider
752
- chain, `security-review`, and `ultrareview` were added in Story #2871.
753
-
754
- ---
641
+ risk-routed ceremony) loads its review backend through a pluggable registry
642
+ configured via `delivery.codeReview.providers` — an array of entries iterated
643
+ in declaration order. The chain-entry field semantics (`name`, `scopes`,
644
+ `optional`, `manualPrompt`, `when`), the fix budget, and the cross-runtime
645
+ contract are documented once in
646
+ [`docs/configuration.md` § delivery.codeReview](docs/configuration.md#deliverycodereview--provider-chain).
755
647
 
756
648
  ## Feedback loop — verification-results auto-graduation
757
649
 
758
- When a Story (or plan-run) finalize path runs, non-blocking findings
759
- (severity `high`, `medium`, or `suggestion`) that survived merge are
760
- auto-graduated into follow-up issues in a SINGLE pass over the unified
761
- `verification-results` structured comment (Story #4411 folded the former
762
- `code-review` and `audit-results` comments into one), routed by source
763
- classification into the framework repo or the consumer repo. The toggle
764
- lives at `delivery.feedbackLoop.auditResultsAutoFile` and defaults to
765
- `true`. (The former `codeReviewAutoFile` key was retired with its
766
- graduator when the pass unified — a config carrying it fails validation;
767
- delete the key.)
768
-
769
- To opt out (for example, to triage findings manually during a
770
- stabilization window), set the toggle to `false` in your root
771
- `.agentrc.json`:
772
-
773
- ```json
774
- {
775
- "delivery": {
776
- "feedbackLoop": {
777
- "auditResultsAutoFile": false
778
- }
779
- }
780
- }
781
- ```
782
-
783
- When disabled, the listener short-circuits and leaves the
784
- `verification-results` comment on the Story ticket untouched. Re-enabling
785
- the toggle is safe: the graduator embeds a content-derived idempotency
786
- marker in each filed issue body, so re-runs skip findings that already
787
- have an issue.
788
-
789
- ---
650
+ When a Story (or plan-run) finalize path runs, non-blocking findings that
651
+ survived merge are auto-graduated into follow-up issues in a single pass over
652
+ the unified `verification-results` structured comment, routed by source
653
+ classification into the framework or consumer repo. The toggle
654
+ (`delivery.feedbackLoop.auditResultsAutoFile`, default `true`), the opt-out
655
+ example, and the idempotency-marker behaviour are documented once in
656
+ [`docs/configuration.md` § delivery.feedbackLoop](docs/configuration.md#deliveryfeedbackloop--verification-results-auto-graduation).
790
657
 
791
658
  ## Worktree dependency strategies
792
659
 
793
- When `delivery.worktreeIsolation.enabled` is `true`, each Story runs in
794
- its own worktree under `.worktrees/story-<id>/`. The
795
- `nodeModulesStrategy` field on `delivery.worktreeIsolation` controls how
796
- `node_modules` is populated in that worktree. Three values are supported,
797
- each with different cost/portability trade-offs:
798
-
799
- | Strategy | When to use | Cold-start cost | Notes |
800
- | -------------- | ---------------------------------------------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------- |
801
- | `clone` | **Shipped default on darwin/linux** — copy-on-write clone of donor `node_modules`. | Near-zero on APFS/reflink FS. | Falls back to `per-worktree` on unsupported filesystems or cross-volume clones. |
802
- | `per-worktree` | Default on Windows; also the safe fallback everywhere. | Full `npm ci` per Story. | Each worktree gets an independent `node_modules`. |
803
- | `symlink` | npm/yarn repos that want the fast path. **Opt-in.** | Near-zero. | Junctions a single donor `node_modules` into each worktree. Refuses on Windows unless explicitly opted in. |
804
- | `pnpm-store` | pnpm repos. **Opt-in.** | Fast (store-backed). | Runs `pnpm install --frozen-lockfile` against the shared content-addressable store. |
805
-
806
- The **shipped consumer default in
807
- [`.agents/docs/agentrc-reference.json`](./docs/agentrc-reference.json) is
808
- `clone`** (Windows resolves to `per-worktree` via the platform-aware
809
- accessor). Repos that use pnpm or want symlink semantics should set
810
- `nodeModulesStrategy` explicitly in their root `.agentrc.json`.
811
-
812
- ### Symlink opt-in (npm / yarn)
813
-
814
- To opt in, set three fields on `delivery.worktreeIsolation` in your root
815
- `.agentrc.json`:
816
-
817
- ```json
818
- {
819
- "delivery": {
820
- "worktreeIsolation": {
821
- "enabled": true,
822
- "nodeModulesStrategy": "symlink",
823
- "primeFromPath": ".",
824
- "allowSymlinkOnWindows": true
825
- }
826
- }
827
- }
828
- ```
829
-
830
- - **`nodeModulesStrategy: "symlink"`** — switch off the per-worktree
831
- install and link instead.
832
- - **`primeFromPath`** — relative path (from the repo root) to the donor
833
- worktree whose `node_modules/` is reused. `"."` means the root
834
- checkout, which must already have `node_modules/` populated before a
835
- Story initializes. `single-story-init.js` enforces this with a pre-check.
836
- - **`allowSymlinkOnWindows`** — required on Windows. The strategy uses
837
- junctions (no admin rights needed) on Windows when this is `true`; it
838
- refuses with an explanatory error otherwise, because symlink semantics
839
- vary by Windows version.
840
-
841
- Once these are set, `single-story-init.js` skips `npm ci` in the worktree and
842
- junctions/symlinks `node_modules` from the donor — typical cold-start
843
- falls from minutes to under a second.
844
-
845
- ---
660
+ When `delivery.worktreeIsolation.enabled` is `true`, each Story runs in its own
661
+ worktree under `.worktrees/story-<id>/`, and `nodeModulesStrategy` controls how
662
+ `node_modules` is populated there. The strategy trade-offs (`clone` /
663
+ `per-worktree` / `symlink` / `pnpm-store`) and the symlink opt-in are
664
+ documented once in
665
+ [`docs/configuration.md` § delivery.worktreeIsolation](docs/configuration.md#deliveryworktreeisolation--node_modules-strategies).
846
666
 
847
667
  ## Multi-developer coordination
848
668
 
@@ -921,72 +741,37 @@ diverge are documented in
921
741
  ## Adopting the QA harness
922
742
 
923
743
  Mandrel ships **three** complementary QA loops, all adopting the `qa-engineer`
924
- persona and all reading the same `qa.*` project contract from `.agentrc.json`.
925
- Two are exploratory siblings that differ on **who drives**; the third steps a
926
- known scenario set:
927
-
928
- - **`/qa-explore <surface>`** — an **agent-led**, open-ended
929
- **Plan → Capture → Triage** exploratory sweep. The operator names a surface;
930
- the **agent drives** it (through the browser MCP by default, or statically as
931
- a documented interim), probing for product bugs, environment-setup friction,
932
- tooling/DX gaps, missing tests, and enhancement ideas, recording each
933
- observation as a `QaLedgerItem` against the
934
- [`qa-ledger.schema.json`](schemas/qa-ledger.schema.json) contract. Capture is
935
- strictly read-only — the only write it performs is appending ledger lines to
936
- the session ledger at **`temp/qa/<sessionId>.ndjson`** (session scratch under
937
- `project.paths.tempRoot`, gitignored, never committed). Triage then
938
- classifies, dedups, and routes each item into a `file` / `defer` / `dismiss`
939
- disposition; the session is HITL-gated — every phase transition and every
940
- ticket-filing write is operator-gated. A resumed session
941
- (`--session-id <id>`) appends and carries its un-triaged backlog forward. The
942
- end-to-end procedure is the SSOT in
943
- [`workflows/qa-explore.md`](workflows/qa-explore.md), with the deterministic
944
- decision seams under `scripts/lib/qa/` (session, redaction, coverage,
945
- missing-test) and `scripts/lib/findings/` (classification, dedup/route).
946
- - **`/qa-assist`** the **human-led** sibling of `/qa-explore`: a
947
- single-observation **Intake Enrich Record** loop. Here the **human
948
- drives** the operator reports one thing they observed (a bug, a flaky
949
- behavior, a "this feels off") and the agent enriches it into a triage-ready
950
- `QaLedgerItem` (a clean repro, a `file:line` root-cause locus, a coverage
951
- verdict), asking clarifying questions when the observation is ambiguous, then
952
- appends it — after explicit confirmation — to a persistent, resumable rolling
953
- session under `temp/qa/`. It writes the **same** ledger contract `/qa-explore`
954
- produces, so a `/qa-assist` item flows through the identical dedup,
955
- classification, and promotion machinery later. The end-to-end procedure is
956
- the SSOT in [`workflows/qa-assist.md`](workflows/qa-assist.md). Reach for
957
- `/qa-assist` when you hit something mid-flight and want it captured well
958
- without breaking stride; reach for `/qa-explore` when you want the agent to
959
- go hunt a named surface.
960
- - **`/qa-run <selector>`** — the **automated complement**: it drives a
961
- consumer's Gherkin `.feature` scenarios through a real browser (the
962
- `chrome-devtools` MCP surface), captures per-surface console/network into
963
- structured `F#` findings, and drafts follow-up tickets for operator
964
- sign-off. The end-to-end procedure is the SSOT in
965
- [`workflows/qa-run.md`](workflows/qa-run.md); the
966
- instrumentation conventions live in the
967
- [`skills/stack/qa/qa-harness`](skills/stack/qa/qa-harness/SKILL.md) skill; the
968
- architectural overview (run pipeline, contract fields, finding shape) is in
969
- [`docs/architecture.md` § Agent-driven QA harness](../docs/architecture.md#agent-driven-qa-harness).
970
-
971
- Reach for `/qa-explore` when you want the **agent** to hunt a freshly delivered
972
- Story/Feature or run a structured bug-hunt captured into a triageable ledger;
973
- reach for `/qa-assist` when **you** hit something mid-flight and want it
974
- enriched into a single triage-ready ledger item; reach for `/qa-run` to
975
- step a **known** scenario set through the browser for a regression pass.
976
-
977
- Binding the QA contract is **opt-in**. All three workflows resolve the
978
- consumer's `qa` block through the single seam
979
- [`resolve-qa-contract.js`](scripts/lib/qa/resolve-qa-contract.js); a consumer
980
- that has not bound it gets a loud, actionable failure ("this project has not
981
- bound the QA harness") when either workflow runs — there is no auto-detection
982
- fallback. To adopt the QA surface, a consumer project takes three concrete
983
- steps.
744
+ persona and all reading the same `qa.*` project contract from `.agentrc.json`
745
+ through the single seam
746
+ [`resolve-qa-contract.js`](scripts/lib/qa/resolve-qa-contract.js) (a consumer
747
+ that has not bound a `qa` block gets a loud, actionable failure — there is no
748
+ auto-detection fallback):
749
+
750
+ - **`/qa-explore <surface>`** — **agent-led** open-ended Plan Capture
751
+ Triage sweep of a named surface. SSOT:
752
+ [`workflows/qa-explore.md`](workflows/qa-explore.md).
753
+ - **`/qa-assist`** — the **human-led** sibling: a single-observation Intake
754
+ Enrich → Record loop writing the same ledger contract. SSOT:
755
+ [`workflows/qa-assist.md`](workflows/qa-assist.md).
756
+ - **`/qa-run <selector>`** — the **automated complement**: steps a known set of
757
+ Gherkin `.feature` scenarios through the `chrome-devtools` browser MCP into
758
+ structured `F#` findings. SSOT: [`workflows/qa-run.md`](workflows/qa-run.md);
759
+ instrumentation conventions in
760
+ [`skills/stack/qa/qa-harness`](skills/stack/qa/qa-harness/SKILL.md).
761
+
762
+ All three record observations as a `QaLedgerItem`
763
+ ([`qa-ledger.schema.json`](schemas/qa-ledger.schema.json)) on a session ledger
764
+ under **`temp/qa/<sessionId>.ndjson`** (gitignored scratch under
765
+ `project.paths.tempRoot`), then triage each item into a `file` / `defer` /
766
+ `dismiss` disposition; every phase transition and ticket-filing write is
767
+ operator-gated. Binding the QA contract is **opt-in** a consumer adopts the
768
+ surface in three concrete steps.
984
769
 
985
770
  ### 1. Bind the `qa` block in `.agentrc.json`
986
771
 
987
- Add a top-level `qa` block. It is optional in the schema (so config
988
- validation never breaks a non-QA consumer), but the four core fields are
989
- required at run time by `resolveQaContract`. Copy the reference shape from
772
+ Add a top-level `qa` block. It is optional in the schema (so config validation
773
+ never breaks a non-QA consumer), but the four core fields are required at run
774
+ time by `resolveQaContract`. Copy the reference shape from
990
775
  [`agentrc-reference.json`](docs/agentrc-reference.json) and adapt the paths:
991
776
 
992
777
  ```jsonc
@@ -1002,22 +787,21 @@ required at run time by `resolveQaContract`. Copy the reference shape from
1002
787
  }
1003
788
  ```
1004
789
 
1005
- `featureRoot`, `fixturesManifest`, `signInSeam`, and `personas` are
1006
- mandatory; omitting any one makes the resolver throw a field-named error.
790
+ `featureRoot`, `fixturesManifest`, `signInSeam`, and `personas` are mandatory;
791
+ omitting any one makes the resolver throw a field-named error.
1007
792
  `consoleAllowlist` and `designTokens` default to `[]` and `null`.
1008
793
 
1009
794
  `personas` accepts **two shapes** (the resolver normalizes both to one
1010
795
  canonical internal map keyed by persona name):
1011
796
 
1012
- - **Name-only array** (above) — `["admin", "member"]`. This is the honest
1013
- shape under a `urlTemplate` dev-impersonation seam, where the persona name
1014
- is the only input the harness consumes (it is substituted into the URL) and
1015
- no per-persona auth material is ever read. Do **not** fabricate
1016
- `credentialRef`/`signInSkill` values a url-template seam ignores.
1017
- - **Object map** — keyed by persona name, each entry carrying per-persona
1018
- auth material (`{ credentialRef }` or `{ signInSkill }`). Use this only
1019
- under a `skill` (or credential) seam where that material is genuinely
1020
- consulted:
797
+ - **Name-only array** (above) — `["admin", "member"]`. The honest shape under a
798
+ `urlTemplate` dev-impersonation seam, where the persona name is the only input
799
+ the harness consumes (substituted into the URL) and no per-persona auth
800
+ material is read. Do **not** fabricate `credentialRef`/`signInSkill` values a
801
+ url-template seam ignores.
802
+ - **Object map** — keyed by persona name, each entry carrying per-persona auth
803
+ material (`{ credentialRef }` or `{ signInSkill }`). Use only under a `skill`
804
+ (or credential) seam where that material is genuinely consulted:
1021
805
 
1022
806
  ```jsonc
1023
807
  "signInSeam": { "skill": "stack/qa/sign-in" },
@@ -1029,10 +813,10 @@ canonical internal map keyed by persona name):
1029
813
 
1030
814
  ### 2. Author the fixtures manifest
1031
815
 
1032
- Create the file referenced by `fixturesManifest`. It binds each persona to
1033
- the seed data the harness loads before signing that persona in, so scenarios
1034
- start from a known state. Every persona named under `qa.personas` should have
1035
- a corresponding entry. Keep the manifest free of real secrets — it carries
816
+ Create the file referenced by `fixturesManifest`. It binds each persona to the
817
+ seed data the harness loads before signing that persona in, so scenarios start
818
+ from a known state. Every persona named under `qa.personas` should have a
819
+ corresponding entry. Keep the manifest free of real secrets — it carries
1036
820
  seed-data shape, not credentials (credentials resolve through `credentialRef`
1037
821
  or a sign-in skill).
1038
822
 
@@ -1041,25 +825,15 @@ or a sign-in skill).
1041
825
  The harness signs in once per persona using a **dev-only seam** — real
1042
826
  credentials are never entered. Expose one of two shapes:
1043
827
 
1044
- - **`{ urlTemplate }`** — a dev sign-in route where `{persona}` is
1045
- substituted (e.g. `/dev/sign-in-as/{persona}` → `/dev/sign-in-as/admin`).
1046
- Gate this route to non-production builds.
1047
- - **`{ skill }`** — when sign-in is multi-step or non-URL, point at a
1048
- consumer skill whose `SKILL.md` the harness reads and follows.
1049
-
1050
- Which seam kinds consult per-persona material: under a `{ urlTemplate }` seam
1051
- the persona **name** is the sole input, so author `personas` as a name-only
1052
- array and supply no auth material. Under a `{ skill }` (or credential) seam,
1053
- author `personas` as the object map and supply per-persona overrides:
1054
- `{ credentialRef }` points at a stored-credential reference (resolved from the
1055
- environment, never inlined) and `{ signInSkill }` points at a per-persona
1056
- sign-in skill.
1057
-
1058
- Once these three `qa.*` keys are in place, `/qa-explore <surface>`,
1059
- `/qa-assist`, and `/qa-run <selector>` all resolve the contract and
1060
- operate against the bound surface. For `/qa-run`, the `chrome-devtools`
1061
- MCP surface is a host-provided runtime dependency; when it is unavailable the
1062
- harness degrades with a clear error rather than falling back to a headless
1063
- runner. `/qa-explore` and `/qa-assist` read the same `qa.*` keys to scope their
1064
- work and to drive the deterministic coverage/missing-test verdicts, then record
1065
- each observation into the `temp/qa/` ledger described above.
828
+ - **`{ urlTemplate }`** — a dev sign-in route where `{persona}` is substituted
829
+ (e.g. `/dev/sign-in-as/{persona}` → `/dev/sign-in-as/admin`); gate it to
830
+ non-production builds. Pair with the name-only `personas` array.
831
+ - **`{ skill }`** — when sign-in is multi-step or non-URL, point at a consumer
832
+ skill whose `SKILL.md` the harness reads. Pair with the object-map
833
+ `personas` form (per step 1).
834
+
835
+ Once these three `qa.*` keys are in place, `/qa-explore <surface>`, `/qa-assist`,
836
+ and `/qa-run <selector>` all resolve the contract and operate against the bound
837
+ surface. For `/qa-run`, the `chrome-devtools` MCP surface is a host-provided
838
+ runtime dependency; when unavailable the harness degrades with a clear error
839
+ rather than falling back to a headless runner.
@@ -0,0 +1,135 @@
1
+ ---
2
+ name: auditor
3
+ description: >-
4
+ Role-scoped boot context for a single read-only audit lens, booted on its own
5
+ system prompt (no CLAUDE.md / instructions.md closure). Carries the shared
6
+ audit machinery standalone — the read-only MUSTs, the finding-block skeleton,
7
+ the severity scale, and the self-cross-check bar — so a lens dispatch needs
8
+ only the lens's own dimensions. Dispatched as subagent_type: auditor by every
9
+ audit-<lens> workflow's first-class execution path.
10
+ ---
11
+
12
+ # auditor — audit lens boot context
13
+
14
+ <!--
15
+ security-baseline stays inviolable and single-sourced — @-import it, never
16
+ inline-copy. The path resolves to the repo root from BOTH the payload source
17
+ (.agents/agents/) and the materialized destination (.claude/agents/) because
18
+ each is exactly two levels below the repo root.
19
+ -->
20
+
21
+ @../../.agents/rules/security-baseline.md
22
+
23
+ You are an **audit lens worker**: you run one read-only audit lens over a
24
+ scoped surface, filter your own findings, and return a report path plus an
25
+ Executive Summary. You run on this focused prompt alone — you do **not** have
26
+ the full project protocol chain loaded, so the invariants you need are stated
27
+ here. Follow the `audit-<lens>.md` workflow your caller hands you for the
28
+ lens-specific dimensions, detection batteries, applicability gates, and
29
+ report additions; this boot context governs what holds across every lens.
30
+ The shared long-form contract is
31
+ [`helpers/audit-lens-core.md`](../workflows/helpers/audit-lens-core.md) — this
32
+ file is its standalone-agent form.
33
+
34
+ ## Non-interactive contract
35
+
36
+ You run as a sub-agent with **no input channel** mid-run.
37
+
38
+ - **Never** ask clarifying questions. Audit what the scope gives you; when a
39
+ surface is absent or inapplicable, say so in the report and emit the lens's
40
+ not-applicable / empty result rather than inventing findings.
41
+ - **Absolute paths only.** Your shell's working directory is **not** guaranteed
42
+ to persist between Bash calls. Pass absolute paths for every file and script.
43
+
44
+ ## Read-only MUSTs (inviolable)
45
+
46
+ - This is a **read-only** analysis. Do **not** modify application code, styles,
47
+ configuration, dependencies, branches, or labels, and never open a PR.
48
+ - The **only** write you perform is the report artifact at
49
+ `{{auditOutputDir}}/audit-<lens>-results.md`, plus — where and only where the
50
+ lens body explicitly declares it — a single measurement/baseline artifact it
51
+ names (e.g. performance's `perf-baseline.json`).
52
+ - Running **non-mutating** measurements/scanners the lens calls for (profilers,
53
+ timers, `npm audit`, `actionlint`, read-only ORM status commands) is
54
+ permitted; running anything that installs, mutates git/labels, edits source,
55
+ or connects to a production database is forbidden. A lens that names a
56
+ stricter carve-out (data-model's no-database rule, quality's "do not run the
57
+ suite") tightens this for that lens.
58
+
59
+ ## Scope
60
+
61
+ Your caller supplies the change-set file list (the lens's `{{changedFiles}}`
62
+ fence). When it is a populated file list, restrict analysis to those files and
63
+ their direct dependencies. When it is the literal `{{changedFiles}}` token,
64
+ there is no scope filter — run the lens codebase-wide. A lens whose body
65
+ declares a deviation (documentation's target-set intersection, navigability's
66
+ whole-route-tree evaluation) follows its own Scope section instead.
67
+
68
+ ## Findings schema — the finding-block skeleton (MUST stay parseable)
69
+
70
+ Write the report with an `## Executive Summary` and a `## Detailed Findings`
71
+ section. Every finding under Detailed Findings uses this shared skeleton; the
72
+ lens may **add** fields (WCAG criterion, CWE ID, `Baseline MUST`, `Evidence`,
73
+ `Route / Door` + `Persona(s)`) and may relabel `Severity` ↔ `Impact` and
74
+ `Dimension` ↔ `Category` ↔ `Type`, but never drops a shared field — the
75
+ `audit-to-stories` parser depends on this shape:
76
+
77
+ ```markdown
78
+ ### `path/to/primary-file.ext` — [Short title of the issue]
79
+
80
+ - **Dimension:** [the lens-specific dimension]
81
+ - **Severity:** [Critical | High | Medium | Low]
82
+ - **Location:** `path/to/primary-file.ext:line`
83
+ - **Current State:** [the specific file/line and why it is problematic]
84
+ - **Recommendation & Rationale:** [how to remediate and why it matters]
85
+ - **Acceptance signal:** [the command or observable that proves this finding is
86
+ remediated]
87
+ - **Agent Prompt:**
88
+ `[A copy-pasteable, highly specific prompt to execute this remediation independently]`
89
+ ```
90
+
91
+ ## Severity scale
92
+
93
+ Grade every finding on this ordered scale — the parser recognizes each level,
94
+ and a surviving **Critical** halts the delivery gate:
95
+
96
+ - **Critical** — an active, exploitable, or data-losing defect that must be
97
+ fixed before the change ships.
98
+ - **High** — a serious correctness/security/maintainability risk to fix
99
+ promptly; does not by itself block the release.
100
+ - **Medium** — a real problem worth scheduling; contained blast radius or a
101
+ workaround exists.
102
+ - **Low** — minor or cosmetic; fix opportunistically.
103
+
104
+ ## Self-cross-check bar (mandatory before you write the report)
105
+
106
+ You are your own adversarial reviewer. After drafting the Detailed Findings and
107
+ **before** writing the artifact, re-open every finding and keep it only when
108
+ **all** hold: a **grounded** `path:line` you actually read; **reproducible
109
+ evidence** (a tool reading, a quoted snippet, or a specific standard it
110
+ violates) — never "this looks wrong"; **in-scope** under the scope filter; and
111
+ an **actionable** recommendation. Drop anything resting on a sanctioned test
112
+ seam, an entry point / public API surface, dynamic/framework reachability, an
113
+ intentional documented deviation, or a formatter-governed style nit.
114
+
115
+ Record the outcome in the Executive Summary as a single line —
116
+ `Self-cross-check: kept <k> / dropped <d>.` — and, when `d > 0`, name the
117
+ dropped findings with their reason. The line's absence is itself a defect.
118
+
119
+ ## Fan-out (heavyweight lenses)
120
+
121
+ When your caller dispatches you for a single dimension of a heavyweight lens
122
+ (`audit-architecture`, `audit-performance`, `audit-documentation`), audit only
123
+ that dimension and return its findings; the parent merges the per-dimension
124
+ results under this self-cross-check bar. Within the supported nesting-depth
125
+ budget you may apply `parallel-tooling.md` Rule 3 to your own independent
126
+ sub-units.
127
+
128
+ ## Return contract
129
+
130
+ Return the **report path** (`{{auditOutputDir}}/audit-<lens>-results.md`) and
131
+ the report's **Executive Summary** (including the self-cross-check line). Do not
132
+ inline the full findings — the report artifact is the record of truth, and
133
+ `audit-to-stories` reads it from disk. If the lens resolved not-applicable or
134
+ found nothing in scope, say so plainly and return the empty/not-applicable
135
+ report the lens mandates.