mandrel 1.82.0 → 1.83.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 (45) hide show
  1. package/.agents/README.md +41 -0
  2. package/.agents/docs/SDLC.md +4 -2
  3. package/.agents/docs/agentrc-reference.json +10 -2
  4. package/.agents/docs/execution-reference.md +52 -0
  5. package/.agents/instructions.md +76 -38
  6. package/.agents/schemas/agentrc.schema.json +31 -3
  7. package/.agents/schemas/qa-ledger.schema.json +2 -2
  8. package/.agents/scripts/epic-deliver-prepare.js +41 -1
  9. package/.agents/scripts/lib/config/explain.js +4 -1
  10. package/.agents/scripts/lib/config-settings-schema.js +25 -1
  11. package/.agents/scripts/lib/epic-body-sections.js +88 -0
  12. package/.agents/scripts/lib/findings/promote-finding.js +3 -3
  13. package/.agents/scripts/lib/findings/severity.js +5 -6
  14. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +65 -2
  15. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +96 -11
  16. package/.agents/scripts/lib/orchestration/doc-reader.js +29 -0
  17. package/.agents/scripts/lib/orchestration/docs-digest.js +134 -0
  18. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +15 -1
  19. package/.agents/scripts/lib/qa/console-allowlist.js +5 -4
  20. package/.agents/scripts/lib/qa/resolve-qa-contract.js +144 -8
  21. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +7 -5
  22. package/.agents/skills/core/epic-plan-consolidate/examples.md +51 -0
  23. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +4 -22
  24. package/.agents/skills/core/epic-plan-decompose-author/examples.md +47 -0
  25. package/.agents/skills/core/epic-plan-premortem/SKILL.md +9 -8
  26. package/.agents/skills/core/epic-plan-premortem/examples.md +53 -0
  27. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +21 -81
  28. package/.agents/skills/core/epic-plan-spec-author/examples.md +91 -0
  29. package/.agents/skills/skills.index.json +3 -3
  30. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +52 -38
  31. package/.agents/workflows/helpers/deliver-epic-reference.md +514 -0
  32. package/.agents/workflows/helpers/deliver-epic.md +164 -469
  33. package/.agents/workflows/helpers/epic-deliver-story.md +35 -11
  34. package/.agents/workflows/helpers/plan-epic-reference.md +136 -0
  35. package/.agents/workflows/helpers/plan-epic.md +56 -186
  36. package/.agents/workflows/helpers/plan-story.md +31 -61
  37. package/.agents/workflows/helpers/qa-run-scenario.md +194 -0
  38. package/.agents/workflows/helpers/scope-triage-gate.md +97 -0
  39. package/.agents/workflows/helpers/single-story-deliver-reference.md +423 -0
  40. package/.agents/workflows/helpers/single-story-deliver.md +128 -392
  41. package/.agents/workflows/qa-explore.md +63 -32
  42. package/.agents/workflows/qa-run.md +293 -130
  43. package/docs/CHANGELOG.md +14 -0
  44. package/package.json +1 -1
  45. package/.agents/schemas/qa-finding.schema.json +0 -133
@@ -0,0 +1,194 @@
1
+ ---
2
+ description: >-
3
+ Helper — not a slash command. Drive one Gherkin scenario through a real
4
+ browser (navigate, act, assert) under a defined input/output contract, and
5
+ return a single structured per-scenario result. Invoked per scenario by the
6
+ `/qa-run` sweep (Step 3); may also be dispatched in a deferred batched
7
+ sub-agent mode (spec-only).
8
+ caller: qa-run.md
9
+ ---
10
+
11
+ # helpers/qa-run-scenario — single-scenario driver
12
+
13
+ > **Not a slash command.** This file lives in `helpers/` and is not projected
14
+ > into the mandrel plugin command tree. It is invoked per scenario by the
15
+ > [`/qa-run`](../qa-run.md) sweep, which owns environment resolution, scope
16
+ > selection, sign-in, and the sweep-level report. This helper owns exactly one
17
+ > scenario: drive it, analyze it, and hand back one structured result.
18
+
19
+ ## Overview
20
+
21
+ `qa-run-scenario` is the **single-scenario worker** for the QA sweep. The
22
+ orchestrator ([`/qa-run`](../qa-run.md)) resolves the environment and scope,
23
+ signs in each persona once, then calls this helper once per scenario in
24
+ selection order. The helper never resolves the contract, never signs in from
25
+ cold, and never files tickets — it receives an already-authenticated session
26
+ and a single scenario ref, drives it, and returns one per-scenario result the
27
+ orchestrator folds into its report.
28
+
29
+ Factoring the per-scenario procedure here keeps the driving rules
30
+ (navigation-first, semantic `Then`, mandatory evidence redaction) in **one
31
+ prose home** and lets the orchestrator stay focused on invocation ergonomics,
32
+ write-safety, and reporting.
33
+
34
+ ## Input contract
35
+
36
+ The caller MUST pass a fully-resolved input envelope. The helper does not
37
+ re-resolve any of it:
38
+
39
+ | Field | Shape | Use |
40
+ | ----------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
41
+ | `environment` | `{ name, baseUrl, allowWrites }` | The resolved target (from `resolveQaEnvironment`). `baseUrl` is the navigation root; `allowWrites` gates any mutating `When` step. |
42
+ | `persona` | persona name **plus** a confirmed authenticated-session precondition | The caller MUST have signed this persona in and confirmed the authenticated state (Step 2 of the sweep) **before** calling the helper. This helper does not sign in from cold; it re-verifies the session on entry and drives as that persona. |
43
+ | `scenario` | scenario ref — the `.feature` file path and `(file, line)` locator | The single scenario to drive. Its `Given/When/Then` steps are the source of truth. |
44
+ | `consoleAllowlist` | `string[]` benign-console patterns | Passed to `filterConsoleMessages` when turning console output into findings. |
45
+ | `designTokens` | pointer to the token/style source, or `null` | When set, spot-check the surface against the tokens; gross violations become findings. |
46
+
47
+ **Authenticated-session precondition.** The `persona` input is not a request to
48
+ sign in — it is an assertion that the caller has already signed this persona in
49
+ and confirmed the authenticated state. On entry the helper re-verifies the
50
+ session with a `take_snapshot` (the user menu / persona badge is present)
51
+ before driving the scenario; if the session is not authenticated, the helper
52
+ returns a `blocked` result rather than attempting a cold sign-in.
53
+
54
+ ## Output contract
55
+
56
+ The helper returns **one structured per-scenario result**:
57
+
58
+ ```json
59
+ {
60
+ "scenario": "<featureRoot-relative file path>:<line>",
61
+ "intent": "<one plain-English line of business intent>",
62
+ "verdict": "pass" | "fail" | "blocked",
63
+ "surface": "<the surface the scenario ended on>",
64
+ "findings": [ /* zero or more F# findings (the console/network-derived shape) */ ]
65
+ }
66
+ ```
67
+
68
+ - **`intent`** — one plain-English line derived from the `Scenario:` name and
69
+ its `Given/When/Then` (what the user is trying to do and the outcome that
70
+ proves it). Stated for **every** scenario, pass or fail.
71
+ - **`verdict`** — `pass` when every `Then` asserted true; `fail` when a `Then`
72
+ did not hold (record the user-visible symptom); `blocked` when the scenario
73
+ could not be driven to a verdict (session not authenticated, a required
74
+ affordance missing, a mutating step excluded by the write guard).
75
+ - **`surface`** — the surface the scenario ended on (for failure triage).
76
+ - **`findings`** — zero or more `F#` findings in the console/network-derived
77
+ shape (`{ id, classification, surface, symptom, likelyRootCause, disposition,
78
+ acceptance, evidence: { console[], network[] } }`, the subset
79
+ [`console-allowlist.js`](../../scripts/lib/qa/console-allowlist.js) emits),
80
+ with evidence already redacted (below). The caller (`/qa-run` Step 4) records
81
+ each finding as a `QaLedgerItem` on the shared session ledger before routing
82
+ it through the classify/route/dedup/promote core; this helper does not touch
83
+ the ledger.
84
+
85
+ The caller aggregates these results into its sweep report (per-scenario
86
+ `intent + verdict` lines, totals, failure triage) and folds each scenario's
87
+ findings onto the shared ledger. The helper never files tickets and never
88
+ renders the sweep-level report.
89
+
90
+ ## Driving rules (non-negotiable)
91
+
92
+ These are the load-bearing invariants of a real-browser QA sweep. They are
93
+ **not** relaxable per scenario.
94
+
95
+ ### Navigation-first — never URL-jump
96
+
97
+ Start the scenario at a **root** (the app's home/dashboard after sign-in,
98
+ anchored on `environment.baseUrl`) and reach the surface under test **only by
99
+ navigating UI affordances** — click nav links, menu items, buttons, and follow
100
+ the same paths a real user would. **Never** `navigate_page` directly to a deep
101
+ link to set up a `Given`. URL-jumping bypasses the app's real authorization and
102
+ routing flows, which both masks access-control gaps and produces findings that
103
+ do not reflect a user-reachable state. Driving via affordances keeps the agent
104
+ inside the app's genuine flows and surfaces broken navigation, guard
105
+ redirects, and dead links as findings rather than hiding them.
106
+
107
+ Map the Gherkin steps to browser actions:
108
+
109
+ - **`Given`** — establish state by navigating from the root via affordances
110
+ (navigate to the starting surface, seed via UI where the manifest does not
111
+ pre-seed).
112
+ - **`When`** — perform the user action: `click`, `fill_form`,
113
+ `evaluate_script` (only for app-provided hooks, never to fabricate the
114
+ outcome), then `wait_for` the resulting transition. **Write guard:** when
115
+ `environment.allowWrites` is `false`, a `When` step that mutates persisted
116
+ state MUST NOT be executed — the orchestrator excludes mutating scenarios
117
+ from the selection under a read-only environment, so a mutating scenario
118
+ reaching this helper under `allowWrites: false` is returned `blocked`.
119
+ - **`Then`** — assert the outcome semantically (below).
120
+
121
+ ### Semantic Then assertion against the accessibility snapshot
122
+
123
+ Assert every `Then` **semantically** against the accessibility snapshot from
124
+ `take_snapshot` — match on roles, accessible names, labels, and visible text
125
+ that express the user-visible outcome ("a banner with text _Invoice sent_ is
126
+ visible", "a row for _ACME Corp_ appears in the invoices table"). **Do not**
127
+ assert against brittle DOM/CSS/XPath selectors, and **do not** assert on HTTP
128
+ status codes, response bodies, or DB rows — those are contract-tier concerns
129
+ that belong in contract tests, not in a user-journey sweep (see
130
+ [`.agents/rules/testing-standards.md`](../../rules/testing-standards.md)
131
+ § Assertion Placement). A `Then` that can only be expressed as a wire-shape or
132
+ DB check is a signal the scenario is mis-tiered, not a reason to break the
133
+ semantic rule.
134
+
135
+ Before driving the scenario, state its **business intent** in one plain-English
136
+ line (the `intent` output field). Keep it to one line sourced from the
137
+ `Scenario:` name and steps — the `.feature` file is the source of truth; do not
138
+ paraphrase every step or leak implementation detail.
139
+
140
+ ### Mandatory evidence redaction
141
+
142
+ Per surface visited, capture console (`list_console_messages`, filtered through
143
+ `consoleAllowlist` via
144
+ [`filterConsoleMessages`](../../scripts/lib/qa/console-allowlist.js)) and
145
+ network (`list_network_requests`; failed / error-status requests become
146
+ findings). Before any captured console/network evidence leaves this helper in a
147
+ finding, it **MUST** pass
148
+ [`redact-evidence.js`](../../scripts/lib/qa/redact-evidence.js)
149
+ (`redactEvidence`) to scrub tokens, session cookies, and PII per
150
+ [`.agents/rules/security-baseline.md`](../../rules/security-baseline.md).
151
+ Redaction is not optional — findings are posted to GitHub at the orchestrator's
152
+ approval time, so unredacted secrets must never reach the `findings` output.
153
+
154
+ ## Deferred: batched sub-agent dispatch mode (spec-only)
155
+
156
+ > **Not yet enabled.** This section specifies a future execution mode; the
157
+ > current `/qa-run` sweep calls this helper **inline**, one scenario at a time,
158
+ > in the orchestrator's own turn. The batched mode below is documented so the
159
+ > contract is stable when it is turned on — do not implement it as live
160
+ > behavior from this spec alone.
161
+
162
+ In the deferred mode, the orchestrator MAY dispatch scenarios to fresh-context
163
+ sub-agents to keep its own context window focused, under these hard rules:
164
+
165
+ - **Sequential, never parallel.** Sub-agents run **one at a time**, never
166
+ concurrently. A live browser surface is a single shared resource; parallel
167
+ drivers would race on navigation and cross-contaminate evidence.
168
+ - **One sub-agent per persona group.** Scenarios are grouped by persona and a
169
+ single sub-agent drives all of one persona's scenarios, so the persona is
170
+ signed in once per group rather than per scenario.
171
+ - **Re-verify auth on entry.** Each sub-agent MUST re-verify the
172
+ authenticated-session precondition (a `take_snapshot` confirming the persona
173
+ badge) when it starts, because it does not share the orchestrator's live
174
+ session state.
175
+ - **Same input/output contract.** Each sub-agent consumes the input contract
176
+ above and returns the per-scenario result shape above for every scenario it
177
+ drove — the orchestrator aggregates identically whether the helper ran inline
178
+ or via a batched sub-agent.
179
+
180
+ ## Constraints
181
+
182
+ - **Always** re-verify the authenticated session on entry; never sign in from
183
+ cold in this helper.
184
+ - **Always** navigate from the root via UI affordances. **Never** URL-jump to a
185
+ deep link to set up a scenario.
186
+ - **Always** assert `Then` outcomes semantically against the accessibility
187
+ snapshot. **Never** assert via DOM/CSS/XPath selectors, HTTP status codes,
188
+ response bodies, or DB rows inside a scenario.
189
+ - **Always** run captured evidence through `redact-evidence.js` before it
190
+ leaves the helper in a finding.
191
+ - **Never** execute a mutating `When` step under `environment.allowWrites:
192
+ false` — return `blocked` instead.
193
+ - **Never** file tickets or render the sweep report — return one structured
194
+ per-scenario result and let the orchestrator aggregate.
@@ -0,0 +1,97 @@
1
+ ---
2
+ description: >-
3
+ Shared scope-triage gate fragment — the single home for the verdict
4
+ meanings, the three-way operator choice, the --yes headless resolution,
5
+ and the no-re-triage rule that both /plan paths reference instead of
6
+ restating.
7
+ caller: plan-epic.md, plan-story.md
8
+ ---
9
+
10
+ # helpers/scope-triage-gate — shared scope-triage gate semantics
11
+
12
+ > **Not a slash command.** This file lives in `helpers/` and is a
13
+ > path-included fragment (not projected into the plugin command tree). It is
14
+ > referenced by [`plan-epic.md`](plan-epic.md) (Phases 1.5 and 5.5) and
15
+ > [`plan-story.md`](plan-story.md) (Phase 2), which supply only their
16
+ > path-specific firing conditions and defer the gate mechanics here.
17
+
18
+ ## What the gate is
19
+
20
+ Both `/plan` paths run the
21
+ [`core/scope-triage`](../../skills/core/scope-triage/SKILL.md) rubric over the
22
+ path-appropriate artifact (an ideation one-pager, an existing Epic body, or a
23
+ drafted Story body) to catch a wrong-sized scope **before** it is committed to
24
+ the wrong pipeline. The rubric is host-LLM judgment anchored **by reference**
25
+ to `DELIVERABLE_GRANULARITY_GUIDANCE` / `DEFAULT_TASK_SIZING` in
26
+ [`ticket-validator-sizing.js`](../../scripts/lib/orchestration/ticket-validator-sizing.js).
27
+ There is **no `--flag`**, no scorer, no schema, and no label transition behind
28
+ the gate. Each referencing path activates the skill by reading its `SKILL.md`
29
+ via the `Read` tool and applying its rubric — do **not** restate the skill's
30
+ sizing thresholds or copy its verdict prose.
31
+
32
+ ## Verdict meanings
33
+
34
+ The rubric emits exactly one verdict:
35
+
36
+ - **`epic`** — the scope is genuinely Epic-sized (multiple independent
37
+ capabilities, a plausible sizing-ceiling breach, or a real dependency
38
+ structure). Proceed on the Epic path; no downgrade offered.
39
+ - **`story`** — the scope is really a single capability-sized Story. The Epic
40
+ ceremony is not worth paying for; the gate offers a downgrade/handoff to the
41
+ standalone-Story path.
42
+ - **`borderline`** — the rubric cannot confidently place the scope on either
43
+ side. Treated the same as `story` at the gate: the three-way choice is
44
+ presented so the operator makes the call.
45
+
46
+ The verdict is **advisory**. Being wrong in the `epic` direction is cheap
47
+ (Phase 8.3 consolidation and the sizing validator catch an over-planned Story
48
+ later), so the gate never costs the operator a stop on an `epic` verdict; the
49
+ gate exists to avoid the ceremony tax of pushing a story-sized scope through
50
+ the full Epic pipeline.
51
+
52
+ ## The three-way operator choice
53
+
54
+ The referencing path folds the verdict into its **existing** HITL confirmation
55
+ stop — it never adds a second stop. On a `story` / `borderline` verdict, that
56
+ confirmation presents a **three-way operator choice**:
57
+
58
+ - **Recommended (the path-specific downgrade/handoff)** — with the triage
59
+ rationale. The referencing path defines the concrete action (persist a
60
+ notes/seed file and hand off to the sibling `/plan` path, identifying the
61
+ invocation as a **scope-triage handoff**), then **exits** the current path.
62
+ - **Proceed on the current path anyway** — ignore the recommendation and
63
+ continue with the artifact unchanged.
64
+ - **Abort** — stop planning entirely. Nothing is mutated.
65
+
66
+ **Never auto-route.** The verdict is advisory and the operator always decides;
67
+ no `agent::*` / label transition happens on either side of the choice. (The
68
+ one sanctioned exception is `--yes`, below.)
69
+
70
+ ## `--yes` (headless) resolution
71
+
72
+ `"Never auto-route"` is the **interactive** contract. When `/plan` was invoked
73
+ with `--yes`, the operator has *pre-authorized* the recommendation and the gate
74
+ does **not** STOP:
75
+
76
+ - The three-way choice resolves to its **Recommended** branch
77
+ deterministically — the handoff carries `--yes` so the receiving `/plan`
78
+ path also auto-proceeds; an `epic` verdict simply continues on the current
79
+ path with no wait.
80
+ - Display the artifact and the verdict line for the record, then proceed
81
+ without waiting.
82
+
83
+ This is the **only** sanctioned auto-route, and it exists solely to make
84
+ `/plan` driveable headlessly. `--yes` does not alter the rubric or the verdict
85
+ meanings; it only forces the Recommended resolution where the gate would
86
+ otherwise STOP. See
87
+ [`plan.md` § Headless / non-interactive mode](../plan.md#headless--non-interactive-mode---yes).
88
+
89
+ ## No-re-triage rule
90
+
91
+ A **scope-triage handoff** is a triage decision *already made*. When `/plan` is
92
+ entered via a handoff (from the ideation-path Phase 1.5, the existing-Epic
93
+ Phase 5.5 conversion, or the standalone-Story Phase 2 escalation), the
94
+ receiving path **MUST NOT** re-run this gate. Re-triaging a settled call would
95
+ re-litigate it and risk a ping-pong between the two `/plan` paths. Each
96
+ referencing path states its own skip-on-handoff condition and defers the
97
+ rationale here.