methodology-m 0.3.1
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/bin/m.mjs +76 -0
- package/dist-m/CHANGELOG.md +45 -0
- package/dist-m/capabilities/bootstrap-root-repo/SKILL.md +138 -0
- package/dist-m/capabilities/decompose-story/SKILL.md +299 -0
- package/dist-m/capabilities/generate-acceptance-tests/SKILL.md +305 -0
- package/dist-m/capabilities/generate-pats/SKILL.md +131 -0
- package/dist-m/capabilities/scaffold-repo/SKILL.md +641 -0
- package/dist-m/capabilities/setup-workspace/SKILL.md +70 -0
- package/dist-m/capabilities/tag-release/SKILL.md +121 -0
- package/dist-m/capabilities/wire-orchestration/SKILL.md +351 -0
- package/dist-m/m.md +126 -0
- package/dist-m/providers/provider-interface.md +191 -0
- package/dist-m/providers/scm/gitlab.md +377 -0
- package/dist-m/schemas/pat.schema.json +161 -0
- package/dist-m/schemas/project.schema.json +177 -0
- package/package.json +27 -0
- package/src/commands/changelog.mjs +58 -0
- package/src/commands/clone.mjs +199 -0
- package/src/commands/diff.mjs +29 -0
- package/src/commands/init.mjs +51 -0
- package/src/commands/update.mjs +41 -0
- package/src/commands/version.mjs +43 -0
- package/src/lib/copy.mjs +20 -0
- package/src/lib/detect-agent.mjs +25 -0
- package/src/lib/diff-trees.mjs +95 -0
- package/src/lib/topology.mjs +62 -0
- package/src/lib/version-file.mjs +25 -0
- package/src/lib/workspace.mjs +40 -0
- package/src/lib/wrappers/claude.mjs +54 -0
- package/templates/claude/skills/bootstrap-root-repo/SKILL.md +13 -0
- package/templates/claude/skills/decompose-story/SKILL.md +13 -0
- package/templates/claude/skills/generate-acceptance-tests/SKILL.md +13 -0
- package/templates/claude/skills/generate-pats/SKILL.md +13 -0
- package/templates/claude/skills/scaffold-repo/SKILL.md +13 -0
- package/templates/claude/skills/setup-workspace/SKILL.md +13 -0
- package/templates/claude/skills/tag-release/SKILL.md +13 -0
- package/templates/claude/skills/wire-orchestration/SKILL.md +13 -0
- package/templates/claude/steering/m-steering.md +3 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# generate-acceptance-tests
|
|
2
|
+
|
|
3
|
+
**Capability:** Transform PAT stubs into executable acceptance test code
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
Reads the PAT stub file (`pats/<sub-task-id>.stub.js`) and the sub-task
|
|
8
|
+
file, then generates real, executable acceptance tests (CATs) using the
|
|
9
|
+
appropriate framework for the component type. After this step, `npm test`
|
|
10
|
+
proves the implementation meets its contract.
|
|
11
|
+
|
|
12
|
+
The stub is the specification. The spec is the compiled proof. This
|
|
13
|
+
capability bridges the two.
|
|
14
|
+
|
|
15
|
+
**Scope:** This capability produces CATs — compiled acceptance tests that
|
|
16
|
+
prove the PAT contract is met. Unit tests (vitest, Testing Library, etc.)
|
|
17
|
+
are a separate developer-level concern created during implementation, not
|
|
18
|
+
by this capability. Both layers coexist in the repo but serve different
|
|
19
|
+
purposes and are authored at different times.
|
|
20
|
+
|
|
21
|
+
## Parameters
|
|
22
|
+
|
|
23
|
+
| Parameter | Type | Required | Description |
|
|
24
|
+
|---------------|--------|----------|----------------------------------------------------|
|
|
25
|
+
| sub-task-id | string | Yes | Sub-task identifier (e.g. TODOM-000c) |
|
|
26
|
+
| repo-path | path | No | Local path to the managed repo (inferred from CWD if omitted) |
|
|
27
|
+
|
|
28
|
+
## Prerequisites
|
|
29
|
+
|
|
30
|
+
- Managed repo is scaffolded (`scaffold-repo`)
|
|
31
|
+
- Implementation exists (`implement-component` has been run)
|
|
32
|
+
- PAT stub exists at `pats/<sub-task-id>.stub.js`
|
|
33
|
+
- Sub-task file exists at `jira/<sub-task-id>.md`
|
|
34
|
+
|
|
35
|
+
## Execution
|
|
36
|
+
|
|
37
|
+
### Step 1 — Read the contract and implementation
|
|
38
|
+
|
|
39
|
+
Read `pats/<sub-task-id>.stub.js` — this is the test specification
|
|
40
|
+
in pseudocode form. Each `it()` block contains comments describing
|
|
41
|
+
what to assert.
|
|
42
|
+
|
|
43
|
+
Read `jira/<sub-task-id>.md` — for acceptance criteria context and
|
|
44
|
+
component role.
|
|
45
|
+
|
|
46
|
+
Read the implementation source files (`src/`) — to understand the
|
|
47
|
+
API surface, exports, and how to import the component under test.
|
|
48
|
+
|
|
49
|
+
### Step 2 — Determine test framework
|
|
50
|
+
|
|
51
|
+
Based on the component role:
|
|
52
|
+
|
|
53
|
+
| Role | CAT Framework | Test Runner | Why |
|
|
54
|
+
|------|--------------|-------------|-----|
|
|
55
|
+
| backend (API) | supertest | vitest | HTTP contract testing without starting a server |
|
|
56
|
+
| frontend (MFE) | Cypress | cypress | PATs are user-level criteria — must validate through the real UI in a browser |
|
|
57
|
+
| frontend-host (shell) | Cypress | cypress | Story-level, composed system, browser required |
|
|
58
|
+
|
|
59
|
+
**Why Cypress for MFEs?** PATs are acceptance criteria written from the
|
|
60
|
+
user's perspective. For any UI component, that means verifying behaviour
|
|
61
|
+
through the rendered UI in a real browser — not through jsdom unit tests.
|
|
62
|
+
Unit tests (vitest + Testing Library) are valuable during implementation
|
|
63
|
+
for fast feedback on component logic, but they are NOT PAT compilations.
|
|
64
|
+
Only browser-based tests satisfy the PAT contract for frontend components.
|
|
65
|
+
|
|
66
|
+
### Step 3 — Add test dependencies
|
|
67
|
+
|
|
68
|
+
Add the required devDependencies to `package.json`:
|
|
69
|
+
|
|
70
|
+
**For backend API repos:**
|
|
71
|
+
- `vitest` — test runner
|
|
72
|
+
- `supertest` — HTTP assertion library
|
|
73
|
+
|
|
74
|
+
**For frontend repos (MFE, shell):**
|
|
75
|
+
- `cypress` — browser-based acceptance testing
|
|
76
|
+
|
|
77
|
+
Also add runtime helpers needed by the Cypress test job:
|
|
78
|
+
- `express` — for API stubs (if not already present)
|
|
79
|
+
- `cors` — for API stubs (if not already present)
|
|
80
|
+
- `serve` — static file server for the built frontend
|
|
81
|
+
- `wait-on` — wait for services to be ready before running tests
|
|
82
|
+
|
|
83
|
+
### Step 4 — Create test config
|
|
84
|
+
|
|
85
|
+
**For backend API repos:** If the repo doesn't already have a
|
|
86
|
+
`vitest.config.js`, create one:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
import { defineConfig } from 'vitest/config'
|
|
90
|
+
|
|
91
|
+
export default defineConfig({
|
|
92
|
+
test: {
|
|
93
|
+
globals: true,
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**For frontend repos (MFE, shell):** If the repo doesn't already have a
|
|
99
|
+
`cypress.config.js`, create one:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
const { defineConfig } = require('cypress')
|
|
103
|
+
|
|
104
|
+
module.exports = defineConfig({
|
|
105
|
+
e2e: {
|
|
106
|
+
baseUrl: 'http://localhost:3001',
|
|
107
|
+
supportFile: false,
|
|
108
|
+
specPattern: 'pats/**/*.cy.js',
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The `baseUrl` should match the port the built frontend is served on.
|
|
114
|
+
The `specPattern` points to the PAT directory where CATs live.
|
|
115
|
+
|
|
116
|
+
### Step 5 — Transform stubs into specs
|
|
117
|
+
|
|
118
|
+
Create `pats/<sub-task-id>.spec.js` (backend) or
|
|
119
|
+
`pats/<sub-task-id>.cy.js` (frontend) alongside the stub file.
|
|
120
|
+
|
|
121
|
+
**Transformation rules:**
|
|
122
|
+
- Keep the same `describe()` and `it()` structure from the stub
|
|
123
|
+
- Replace comment placeholders with real assertions
|
|
124
|
+
- Import the component under test (app, component, etc.)
|
|
125
|
+
- For API tests: use supertest to make HTTP requests against the
|
|
126
|
+
imported app (not a running server)
|
|
127
|
+
- For frontend tests (MFE, shell): use Cypress to visit the page,
|
|
128
|
+
interact with elements via `data-testid` attributes, and assert
|
|
129
|
+
on visible UI state
|
|
130
|
+
|
|
131
|
+
### Step 5a — Handle PAT supersession (replaces/removes)
|
|
132
|
+
|
|
133
|
+
Before writing the new spec, check the sub-task file for `replaces`
|
|
134
|
+
or `removes` declarations in the acceptance criteria.
|
|
135
|
+
|
|
136
|
+
**For `replaces`:** Find the existing test file for the superseded AC
|
|
137
|
+
(e.g. `pats/TODOM-000b.cy.js` if replacing `TODOM-000b/AC-001`).
|
|
138
|
+
Update or rewrite the affected test(s) in that file to match the new
|
|
139
|
+
behaviour. Do NOT leave old assertions that test superseded behaviour
|
|
140
|
+
— they will fail in CI.
|
|
141
|
+
|
|
142
|
+
**For `removes`:** Find the existing test file and delete the test(s)
|
|
143
|
+
for the removed AC. If the entire file becomes empty, delete the file.
|
|
144
|
+
|
|
145
|
+
**This is not optional.** If the sub-task declares supersession and
|
|
146
|
+
you skip this step, old tests will fail in CI. The test suite must
|
|
147
|
+
always reflect the current active PAT set — not the historical one.
|
|
148
|
+
|
|
149
|
+
**For backend API repos (supertest pattern):**
|
|
150
|
+
```
|
|
151
|
+
import request from 'supertest'
|
|
152
|
+
import app from '../src/app.js'
|
|
153
|
+
|
|
154
|
+
describe('...', () => {
|
|
155
|
+
it('...', async () => {
|
|
156
|
+
const res = await request(app).get('/endpoint')
|
|
157
|
+
expect(res.status).toBe(200)
|
|
158
|
+
expect(res.body).toHaveProperty('expectedField')
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Key:** Import `app.js` directly, not `server.js`. Supertest
|
|
164
|
+
binds to the app internally — no port needed, no server to clean up.
|
|
165
|
+
|
|
166
|
+
**For frontend repos (Cypress pattern):**
|
|
167
|
+
```
|
|
168
|
+
describe('<sub-task-id>: <description>', () => {
|
|
169
|
+
beforeEach(() => {
|
|
170
|
+
cy.visit('/')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('<acceptance criterion>', () => {
|
|
174
|
+
cy.get('[data-testid="<element>"]').should('be.visible')
|
|
175
|
+
// ... assertions matching the PAT stub
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Key:** Cypress tests run against the built frontend served statically.
|
|
181
|
+
API stubs provide the backend. The test validates the full user-facing
|
|
182
|
+
behaviour described in the PAT.
|
|
183
|
+
|
|
184
|
+
### Step 6 — Update test script and CI pipeline
|
|
185
|
+
|
|
186
|
+
**For backend API repos:**
|
|
187
|
+
|
|
188
|
+
Ensure `package.json` has:
|
|
189
|
+
```
|
|
190
|
+
"test": "vitest run"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The `run` flag ensures vitest executes once and exits (no watch mode).
|
|
194
|
+
This is critical for CI — watch mode would hang the pipeline.
|
|
195
|
+
|
|
196
|
+
**For frontend repos (MFE, shell):**
|
|
197
|
+
|
|
198
|
+
Ensure `package.json` has:
|
|
199
|
+
```
|
|
200
|
+
"test": "cypress run"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Then update `.gitlab-ci.yml` to handle the Cypress test job properly.
|
|
204
|
+
The test job needs a Cypress Docker image, stub startup, a static
|
|
205
|
+
server for the built frontend, and health checks before running tests.
|
|
206
|
+
|
|
207
|
+
**CI image pinning rule:** The `cypress/included` image tag MUST match
|
|
208
|
+
the Cypress version in `package.json`. Read the Cypress version from
|
|
209
|
+
`devDependencies` and use that exact version as the image tag. Never
|
|
210
|
+
use `:latest` — it drifts independently of the npm package and causes
|
|
211
|
+
"binary not found" failures when the versions diverge.
|
|
212
|
+
|
|
213
|
+
Updated test job for frontend repos:
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
test:
|
|
217
|
+
stage: test
|
|
218
|
+
image:
|
|
219
|
+
name: cypress/included:<cypress-version-from-package.json>
|
|
220
|
+
entrypoint: [""]
|
|
221
|
+
script:
|
|
222
|
+
- node pats/stubs/api-read.js &
|
|
223
|
+
- node pats/stubs/api-write.js &
|
|
224
|
+
- npm run build --if-present
|
|
225
|
+
- npx serve dist -l 3001 &
|
|
226
|
+
- npx wait-on http://localhost:3002/health http://localhost:3003/health http://localhost:3001
|
|
227
|
+
- npx cypress run
|
|
228
|
+
needs: [install]
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Stub startup:** Start all API stubs in `pats/stubs/api-*.js` as
|
|
232
|
+
background processes. Each stub exposes a `/health` endpoint.
|
|
233
|
+
|
|
234
|
+
**Build and serve:** Build the frontend, then serve the `dist/` folder
|
|
235
|
+
on the component's port (e.g. 3001 for MFE).
|
|
236
|
+
|
|
237
|
+
**Wait-on:** Wait for all stub health endpoints and the static server
|
|
238
|
+
before running Cypress. Without this, tests hit connection refused.
|
|
239
|
+
|
|
240
|
+
**Image entrypoint:** The `cypress/included` image has a default
|
|
241
|
+
entrypoint that runs `cypress run` immediately. Setting
|
|
242
|
+
`entrypoint: [""]` overrides this so the CI script controls execution.
|
|
243
|
+
|
|
244
|
+
### Step 7 — Install dependencies and run tests
|
|
245
|
+
|
|
246
|
+
Run `npm install` to install the new devDependencies and update
|
|
247
|
+
`package-lock.json`.
|
|
248
|
+
|
|
249
|
+
Run `npm test` to verify the generated tests pass against the
|
|
250
|
+
implementation.
|
|
251
|
+
|
|
252
|
+
**If tests fail:** Review the spec, fix the assertions or the
|
|
253
|
+
implementation, and re-run. The goal is green tests before moving on.
|
|
254
|
+
|
|
255
|
+
### Step 8 — Present for review
|
|
256
|
+
|
|
257
|
+
Show the user:
|
|
258
|
+
- The stub file (what was specified)
|
|
259
|
+
- The spec file (what was generated)
|
|
260
|
+
- The test output (passing/failing)
|
|
261
|
+
|
|
262
|
+
Ask the user to review and confirm. They may want to adjust
|
|
263
|
+
assertions, add edge cases, or rename test descriptions.
|
|
264
|
+
|
|
265
|
+
### Step 9 — Report
|
|
266
|
+
|
|
267
|
+
Output:
|
|
268
|
+
- Spec file created (`pats/<sub-task-id>.spec.js` or `pats/<sub-task-id>.cy.js`)
|
|
269
|
+
- Test framework and dependencies added
|
|
270
|
+
- CI pipeline updated (for frontend repos: pinned Cypress image, stub startup)
|
|
271
|
+
- Test results (pass/fail count)
|
|
272
|
+
- Reminder: do NOT delete the stub file — it's the human-readable
|
|
273
|
+
contract. The spec is the machine-readable proof. Both live in `pats/`.
|
|
274
|
+
|
|
275
|
+
## Notes
|
|
276
|
+
|
|
277
|
+
- The stub file is never deleted or modified. It remains as the
|
|
278
|
+
human-readable specification alongside the executable spec.
|
|
279
|
+
- Tests must import the app/component directly, not via a running
|
|
280
|
+
server. This is why `implement-component` separates app from server.
|
|
281
|
+
- For API repos, supertest + vitest is the standard. Supertest handles
|
|
282
|
+
the HTTP layer; vitest provides the test runner and assertions.
|
|
283
|
+
- For frontend repos (MFE, shell), Cypress is the CAT framework.
|
|
284
|
+
It validates the real UI in a real browser — the only way to prove
|
|
285
|
+
user-facing acceptance criteria are met.
|
|
286
|
+
- **Unit tests vs CATs:** Frontend repos will typically have BOTH
|
|
287
|
+
vitest unit tests (created during implementation for fast feedback)
|
|
288
|
+
and Cypress CATs (created by this capability to prove the PAT
|
|
289
|
+
contract). They coexist. Unit tests run via `npm run test:unit`,
|
|
290
|
+
CATs run via `npm test` (which maps to `cypress run`). CI runs
|
|
291
|
+
`npm test` — the CATs are what gate the MR.
|
|
292
|
+
- The `vitest run` flag is essential for backend repos. Without it,
|
|
293
|
+
vitest starts in watch mode, which hangs CI pipelines indefinitely.
|
|
294
|
+
- PAT stubs use `describe`/`it` syntax even though they're pseudocode.
|
|
295
|
+
This makes the transformation mechanical — same structure, real
|
|
296
|
+
assertions replacing comments.
|
|
297
|
+
- **CI image pinning:** Always pin `cypress/included` to the exact
|
|
298
|
+
version from `package.json`. The `:latest` tag drifts independently
|
|
299
|
+
of the npm package — when they diverge, the Cypress binary path
|
|
300
|
+
doesn't match and CI fails with "binary not found." This is a
|
|
301
|
+
silent, intermittent failure that's hard to debug in a live demo.
|
|
302
|
+
- **Stub/CI coupling:** Every stub in `pats/stubs/api-*.js` must be
|
|
303
|
+
started in the CI test job and its `/health` endpoint must be in
|
|
304
|
+
the `wait-on` list. If you add or modify a stub, update the CI
|
|
305
|
+
config to match.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# generate-pats
|
|
2
|
+
|
|
3
|
+
**Capability:** Generate story-level PATs from a story file
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
Reads a story file, generates a `pat.yaml` file following the PAT.yaml schema
|
|
8
|
+
defined in Methodology M. The PAT file is the machine-readable, structured
|
|
9
|
+
expression of the story's acceptance criteria — the source of truth that drives
|
|
10
|
+
decomposition, implementation, and validation.
|
|
11
|
+
|
|
12
|
+
Story-level PATs are topology-agnostic. They describe user outcomes, not
|
|
13
|
+
component behaviour. No technical implementation details, no component names.
|
|
14
|
+
|
|
15
|
+
## Parameters
|
|
16
|
+
|
|
17
|
+
| Parameter | Type | Required | Description |
|
|
18
|
+
|-----------|------|----------|-------------|
|
|
19
|
+
| `story-file` | path | Yes | Path to the source story markdown file |
|
|
20
|
+
| `workspace` | path | No | Output folder for generated artefacts |
|
|
21
|
+
|
|
22
|
+
## Execution
|
|
23
|
+
|
|
24
|
+
### Step 1 — Read the story
|
|
25
|
+
|
|
26
|
+
Read the story file. Extract:
|
|
27
|
+
- Story ID
|
|
28
|
+
- Acceptance criteria (from the story's AC section)
|
|
29
|
+
|
|
30
|
+
### Step 2 — Generate PAT draft
|
|
31
|
+
|
|
32
|
+
Transform each acceptance criterion into a PAT entry following the schema:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
story: <story-id>
|
|
36
|
+
version: 1
|
|
37
|
+
|
|
38
|
+
acceptance:
|
|
39
|
+
- id: AC-001
|
|
40
|
+
when: <user action or state>
|
|
41
|
+
then: <expected outcome>
|
|
42
|
+
steps:
|
|
43
|
+
- navigate: <path>
|
|
44
|
+
- assert: "[data-testid='<id>']" <check>
|
|
45
|
+
...
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Rules for generation:
|
|
49
|
+
- `when`/`then` are plain English, topology-agnostic
|
|
50
|
+
- `steps` are ordered and deterministic, suitable for direct test translation
|
|
51
|
+
- All interactive elements use `data-testid` attributes for stable selectors
|
|
52
|
+
- Each AC gets a unique `id` within the story (AC-001, AC-002, etc.)
|
|
53
|
+
- No component names, no API paths, no technical details in `when`/`then`
|
|
54
|
+
- `steps` may reference technical details (URLs, selectors) as these are
|
|
55
|
+
the concrete validation mechanism
|
|
56
|
+
|
|
57
|
+
### Step 2a — Detect PAT conflicts with existing stories
|
|
58
|
+
|
|
59
|
+
Before finalising the PAT draft, scan existing PAT files in the workspace
|
|
60
|
+
(or root repo `pats/` folder) for conflicts with the new story's ACs.
|
|
61
|
+
|
|
62
|
+
A conflict exists when:
|
|
63
|
+
- A new AC changes behaviour that an existing AC asserts (e.g. "renders
|
|
64
|
+
Hello" → "renders Todo list")
|
|
65
|
+
- A new AC removes a feature that an existing AC validates
|
|
66
|
+
- A new AC changes the UI structure that existing ACs depend on (e.g.
|
|
67
|
+
different data-testid attributes, different page layout)
|
|
68
|
+
|
|
69
|
+
For each conflict, add a `replaces` or `removes` declaration to the
|
|
70
|
+
new AC:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
- id: AC-001
|
|
74
|
+
when: user opens the app
|
|
75
|
+
then: todo list is displayed with item count
|
|
76
|
+
replaces: TODOM-000/AC-001 # was: MFE renders Hello component
|
|
77
|
+
steps:
|
|
78
|
+
...
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- `replaces: <story-id>/AC-<id>` — the new AC supersedes the old one.
|
|
82
|
+
The old AC's compiled test should be updated or replaced.
|
|
83
|
+
- `removes: <story-id>/AC-<id>` — the old AC is no longer valid and
|
|
84
|
+
its compiled test should be deleted.
|
|
85
|
+
|
|
86
|
+
**This step is critical for test suite integrity.** Without it, old
|
|
87
|
+
compiled tests (CATs) will fail when new stories change behaviour,
|
|
88
|
+
causing CI failures that look like bugs but are actually stale tests.
|
|
89
|
+
|
|
90
|
+
### Step 3 — Present for review
|
|
91
|
+
|
|
92
|
+
Show the generated PAT to the user. Wait for confirmation or changes.
|
|
93
|
+
|
|
94
|
+
The PAT is a critical artefact — it drives everything downstream. The user
|
|
95
|
+
must review and approve before it's written.
|
|
96
|
+
|
|
97
|
+
### Step 4 — Write to workspace
|
|
98
|
+
|
|
99
|
+
On confirmation, write `<story-id>.pat.yaml` to the workspace folder.
|
|
100
|
+
|
|
101
|
+
## PAT.yaml schema
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
story: <story-id>
|
|
105
|
+
version: 1
|
|
106
|
+
|
|
107
|
+
acceptance:
|
|
108
|
+
- id: <unique-id>
|
|
109
|
+
when: <trigger condition>
|
|
110
|
+
then: <expected outcome>
|
|
111
|
+
steps:
|
|
112
|
+
- navigate: <path>
|
|
113
|
+
- click: "[data-testid='<id>']"
|
|
114
|
+
- type: "[data-testid='<id>']" value "<text>"
|
|
115
|
+
- assert: "[data-testid='<id>']" is visible
|
|
116
|
+
- assert: "[data-testid='<id>']" contains "<value>"
|
|
117
|
+
- assert: "[data-testid='<id>']" count > 0
|
|
118
|
+
- wait: "[data-testid='<id>']" is visible
|
|
119
|
+
replaces: <old-ac-id> # optional
|
|
120
|
+
removes: <old-ac-id> # optional
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Notes
|
|
124
|
+
|
|
125
|
+
- Story-level PATs are topology-agnostic — pure user outcomes
|
|
126
|
+
- The PAT file is the input to `decompose-story`, not the story prose
|
|
127
|
+
- PATs live in the root repo under `pats/` in a real project; in the
|
|
128
|
+
workshop they land in the workspace folder
|
|
129
|
+
- `replaces` and `removes` are used when stories modify existing behaviour
|
|
130
|
+
- The `version` field is the schema version (always 1 for now), not the
|
|
131
|
+
story version
|