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.
- package/.agents/README.md +41 -0
- package/.agents/docs/SDLC.md +4 -2
- package/.agents/docs/agentrc-reference.json +10 -2
- package/.agents/docs/execution-reference.md +52 -0
- package/.agents/instructions.md +76 -38
- package/.agents/schemas/agentrc.schema.json +31 -3
- package/.agents/schemas/qa-ledger.schema.json +2 -2
- package/.agents/scripts/epic-deliver-prepare.js +41 -1
- package/.agents/scripts/lib/config/explain.js +4 -1
- package/.agents/scripts/lib/config-settings-schema.js +25 -1
- package/.agents/scripts/lib/epic-body-sections.js +88 -0
- package/.agents/scripts/lib/findings/promote-finding.js +3 -3
- package/.agents/scripts/lib/findings/severity.js +5 -6
- package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +65 -2
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +96 -11
- package/.agents/scripts/lib/orchestration/doc-reader.js +29 -0
- package/.agents/scripts/lib/orchestration/docs-digest.js +134 -0
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +15 -1
- package/.agents/scripts/lib/qa/console-allowlist.js +5 -4
- package/.agents/scripts/lib/qa/resolve-qa-contract.js +144 -8
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +7 -5
- package/.agents/skills/core/epic-plan-consolidate/examples.md +51 -0
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +4 -22
- package/.agents/skills/core/epic-plan-decompose-author/examples.md +47 -0
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +9 -8
- package/.agents/skills/core/epic-plan-premortem/examples.md +53 -0
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +21 -81
- package/.agents/skills/core/epic-plan-spec-author/examples.md +91 -0
- package/.agents/skills/skills.index.json +3 -3
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +52 -38
- package/.agents/workflows/helpers/deliver-epic-reference.md +514 -0
- package/.agents/workflows/helpers/deliver-epic.md +164 -469
- package/.agents/workflows/helpers/epic-deliver-story.md +35 -11
- package/.agents/workflows/helpers/plan-epic-reference.md +136 -0
- package/.agents/workflows/helpers/plan-epic.md +56 -186
- package/.agents/workflows/helpers/plan-story.md +31 -61
- package/.agents/workflows/helpers/qa-run-scenario.md +194 -0
- package/.agents/workflows/helpers/scope-triage-gate.md +97 -0
- package/.agents/workflows/helpers/single-story-deliver-reference.md +423 -0
- package/.agents/workflows/helpers/single-story-deliver.md +128 -392
- package/.agents/workflows/qa-explore.md +63 -32
- package/.agents/workflows/qa-run.md +293 -130
- package/docs/CHANGELOG.md +14 -0
- package/package.json +1 -1
- 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.
|