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,641 @@
|
|
|
1
|
+
# scaffold-repo
|
|
2
|
+
|
|
3
|
+
**Capability:** Create and configure a managed repo for an M-type project
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
Creates a managed repo on GitLab from a sub-task file, seeds it with
|
|
8
|
+
project files, and installs a pluggable CI pipeline. After this step,
|
|
9
|
+
the repo is ready to receive implementation work — a dev can create a
|
|
10
|
+
branch, raise an MR, and the pipeline will build, test, publish a
|
|
11
|
+
snapshot artefact, and auto-tag on merge.
|
|
12
|
+
|
|
13
|
+
## Parameters
|
|
14
|
+
|
|
15
|
+
| Parameter | Type | Required | Description |
|
|
16
|
+
|----------------|--------|----------|-----------------------------------------------------|
|
|
17
|
+
| sub-task-file | path | Yes | Path to the sub-task markdown file |
|
|
18
|
+
| project-yaml | path | Yes | Path to project.yaml (for group path, project name) |
|
|
19
|
+
| repo-type | string | No | "node", "python", "rust", etc. (default: node) |
|
|
20
|
+
| namespace-id | string | No | GitLab namespace ID (extracted from project if omitted) |
|
|
21
|
+
|
|
22
|
+
The sub-task file is the primary input. It contains the component name,
|
|
23
|
+
repo name, parent story reference, and repo-level PAT stubs. The
|
|
24
|
+
project.yaml provides the group path and project-level context.
|
|
25
|
+
|
|
26
|
+
## Execution
|
|
27
|
+
|
|
28
|
+
### Step 1 — Extract metadata
|
|
29
|
+
|
|
30
|
+
Read the sub-task file. Extract:
|
|
31
|
+
- Component name (from `Component:` field)
|
|
32
|
+
- Repo name (from `Repo:` field)
|
|
33
|
+
- Parent story ID (from `Parent:` field)
|
|
34
|
+
- Repo-level PAT stubs (from `## PAT Stubs` section)
|
|
35
|
+
|
|
36
|
+
Read project.yaml. Extract:
|
|
37
|
+
- Group path (from `group:` field)
|
|
38
|
+
- Project name (from `project:` field)
|
|
39
|
+
|
|
40
|
+
### Step 2 — Create the repo
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
scm.create_repo(
|
|
44
|
+
name: <repo-name>,
|
|
45
|
+
namespace_id: <resolved-from-group-path>,
|
|
46
|
+
initialize_readme: false
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Do NOT initialise with README — the seed commit includes a project-specific README.
|
|
51
|
+
|
|
52
|
+
### Step 3 — Seed project files
|
|
53
|
+
|
|
54
|
+
Push all seed files in a single commit:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
scm.push_files(repo: <repo>, branch: "main", files: [...], commit_message: "seed: scaffold <component>")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- `README.md` — component name, parent story reference, repo-level PATs
|
|
61
|
+
- CI config — pluggable lifecycle pipeline (see Pipeline Template)
|
|
62
|
+
- `.gitignore` — standard ignores for the repo type (e.g. `node_modules/`)
|
|
63
|
+
- `package.json` (or equivalent for repo-type) — with lifecycle scripts
|
|
64
|
+
- `package-lock.json` — minimal lockfile so `npm ci` works from day zero
|
|
65
|
+
- `jira/<sub-task-id>.md` — copy of the sub-task file
|
|
66
|
+
- `pats/<sub-task-id>.stub` — PAT stub file extracted from the sub-task
|
|
67
|
+
- `.m/steering/m-managed-repo.md` — M development guide (see Steering Template)
|
|
68
|
+
|
|
69
|
+
**For frontend repos with API dependencies (role: frontend, frontend-host):**
|
|
70
|
+
|
|
71
|
+
- `pats/stubs/<api-name>.js` — API stub servers for PAT validation (see API Stubs below)
|
|
72
|
+
|
|
73
|
+
The webpack config and Dockerfile must declare one env var per API
|
|
74
|
+
dependency, derived from the topology in project.yaml:
|
|
75
|
+
|
|
76
|
+
| Dependency role | Env var | Default |
|
|
77
|
+
|----------------|---------|---------|
|
|
78
|
+
| api-read | `API_READ_URL` | `http://localhost:3002` |
|
|
79
|
+
| api-write | `API_WRITE_URL` | `http://localhost:3003` |
|
|
80
|
+
| (general pattern) | `<ROLE_UPPER>_URL` | `http://localhost:<port>` |
|
|
81
|
+
|
|
82
|
+
Ports follow the convention: shell=3000, MFE=3001, then backends in
|
|
83
|
+
project.yaml order starting at 3002.
|
|
84
|
+
|
|
85
|
+
**Important:** Because the repo was created without a default README
|
|
86
|
+
(Step 2), all files are new and `push_files` works cleanly. If any files
|
|
87
|
+
already exist (e.g. re-running scaffold on a partially seeded repo), use
|
|
88
|
+
`scm.create_or_update_file()` per file instead.
|
|
89
|
+
|
|
90
|
+
### Step 4 — Reconfigure branch protection
|
|
91
|
+
|
|
92
|
+
The methodology requires that `main` is merge-only — no direct pushes.
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
scm.protect_branch(
|
|
96
|
+
repo: <repo>,
|
|
97
|
+
branch: "main",
|
|
98
|
+
push: none,
|
|
99
|
+
merge: maintainer,
|
|
100
|
+
force_push: false
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Ordering matters:** Step 3 (seed files) must complete before Step 4
|
|
105
|
+
(protect branch). Once push is blocked, even the automation token
|
|
106
|
+
cannot push directly — only MR merges work.
|
|
107
|
+
|
|
108
|
+
### Step 5 — Create access token for merge transaction
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
scm.create_access_token(
|
|
112
|
+
repo: <repo>,
|
|
113
|
+
name: "m-merge-transaction",
|
|
114
|
+
scopes: [api, read_repo, write_repo],
|
|
115
|
+
access_level: maintainer,
|
|
116
|
+
expiry: 1 year
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This token is used by the root repo's merge transaction pipeline to
|
|
121
|
+
merge MRs on this managed repo. Store the token value — it will be
|
|
122
|
+
passed to `wire-orchestration` for installation as a root repo CI secret.
|
|
123
|
+
|
|
124
|
+
### Step 6 — Report
|
|
125
|
+
|
|
126
|
+
Output:
|
|
127
|
+
- Repo URL
|
|
128
|
+
- Branch protection status (push: no one, merge: maintainers)
|
|
129
|
+
- Access token status (project token created / free tier — use group PAT)
|
|
130
|
+
- Summary of seeded files
|
|
131
|
+
|
|
132
|
+
Note that `wire-orchestration` should be run after all managed repos
|
|
133
|
+
are scaffolded to connect them to the root repo.
|
|
134
|
+
|
|
135
|
+
## Pipeline Template
|
|
136
|
+
|
|
137
|
+
The `.gitlab-ci.yml` uses a pluggable lifecycle model. The pipeline
|
|
138
|
+
defines WHEN things run. The project's build scripts define WHAT runs.
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
# .gitlab-ci.yml — M-type managed repo pipeline
|
|
142
|
+
# Lifecycle phases are delegated to project scripts.
|
|
143
|
+
# The pipeline orchestrates; the scripts implement.
|
|
144
|
+
|
|
145
|
+
image: node:20
|
|
146
|
+
|
|
147
|
+
# Workflow rules prevent duplicate pipelines (one for branch push,
|
|
148
|
+
# one for MR) and ensure all jobs — including those with per-job
|
|
149
|
+
# rules — participate correctly in MR pipelines.
|
|
150
|
+
workflow:
|
|
151
|
+
rules:
|
|
152
|
+
- if: $CI_MERGE_REQUEST_IID # MR pipelines
|
|
153
|
+
- if: $CI_COMMIT_BRANCH == "main" # post-merge on main
|
|
154
|
+
|
|
155
|
+
stages:
|
|
156
|
+
- install
|
|
157
|
+
- build
|
|
158
|
+
- test
|
|
159
|
+
- snapshot
|
|
160
|
+
- tag
|
|
161
|
+
|
|
162
|
+
# Default cache: every job pulls node_modules from cache.
|
|
163
|
+
# Only the install job pushes (pull-push policy).
|
|
164
|
+
cache: &default_cache
|
|
165
|
+
key: ${CI_COMMIT_REF_SLUG}
|
|
166
|
+
paths:
|
|
167
|
+
- node_modules/
|
|
168
|
+
policy: pull
|
|
169
|
+
|
|
170
|
+
# --- Always run ---
|
|
171
|
+
|
|
172
|
+
install:
|
|
173
|
+
stage: install
|
|
174
|
+
script:
|
|
175
|
+
- npm ci
|
|
176
|
+
cache:
|
|
177
|
+
<<: *default_cache
|
|
178
|
+
policy: pull-push
|
|
179
|
+
|
|
180
|
+
build:
|
|
181
|
+
stage: build
|
|
182
|
+
script:
|
|
183
|
+
- npm run build --if-present
|
|
184
|
+
needs: [install]
|
|
185
|
+
|
|
186
|
+
test:
|
|
187
|
+
stage: test
|
|
188
|
+
script:
|
|
189
|
+
- npm test
|
|
190
|
+
needs: [build]
|
|
191
|
+
|
|
192
|
+
# --- MR pipelines only: publish snapshot artefact ---
|
|
193
|
+
|
|
194
|
+
snapshot:
|
|
195
|
+
stage: snapshot
|
|
196
|
+
script:
|
|
197
|
+
- npm run snapshot --if-present
|
|
198
|
+
rules:
|
|
199
|
+
- if: $CI_MERGE_REQUEST_IID
|
|
200
|
+
needs: [test]
|
|
201
|
+
|
|
202
|
+
# --- Merge to main only: auto-tag ---
|
|
203
|
+
|
|
204
|
+
tag:
|
|
205
|
+
stage: tag
|
|
206
|
+
script:
|
|
207
|
+
- npm run tag --if-present
|
|
208
|
+
rules:
|
|
209
|
+
- if: $CI_COMMIT_BRANCH == "main"
|
|
210
|
+
needs: [test]
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Lifecycle scripts (package.json)
|
|
214
|
+
|
|
215
|
+
The scaffold creates placeholder scripts. The implementation step
|
|
216
|
+
replaces them with real commands.
|
|
217
|
+
|
|
218
|
+
**Backend repos (role: backend):**
|
|
219
|
+
```
|
|
220
|
+
{
|
|
221
|
+
"scripts": {
|
|
222
|
+
"start": "node src/server.js",
|
|
223
|
+
"build": "echo 'no build step configured'",
|
|
224
|
+
"test": "echo 'no tests configured' && exit 0",
|
|
225
|
+
"snapshot": "echo 'snapshot: not yet implemented'",
|
|
226
|
+
"tag": "echo 'auto-tag: not yet implemented'"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Frontend repos (role: frontend, frontend-host):**
|
|
232
|
+
```
|
|
233
|
+
{
|
|
234
|
+
"scripts": {
|
|
235
|
+
"start": "webpack serve --mode development",
|
|
236
|
+
"build": "webpack --mode production",
|
|
237
|
+
"test": "echo 'no tests configured' && exit 0",
|
|
238
|
+
"snapshot": "echo 'snapshot: not yet implemented'",
|
|
239
|
+
"tag": "echo 'auto-tag: not yet implemented'"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The `start` script is required for local compose — the root repo's
|
|
245
|
+
`start-all.sh` calls each component's start script. Backend repos
|
|
246
|
+
get a Node server start; frontend repos get webpack dev server.
|
|
247
|
+
Implementation may refine these but the scaffold must provide
|
|
248
|
+
working defaults.
|
|
249
|
+
|
|
250
|
+
### Lifecycle phase contract
|
|
251
|
+
|
|
252
|
+
| Phase | Trigger | Purpose | Failure behaviour |
|
|
253
|
+
|------------|--------------------------|--------------------------------------|--------------------------|
|
|
254
|
+
| `install` | Every pipeline | Install dependencies | Pipeline fails |
|
|
255
|
+
| `build` | Every pipeline | Compile, bundle, transpile | Pipeline fails |
|
|
256
|
+
| `test` | Every pipeline | Run repo-level PATs and unit tests | Pipeline fails, MR blocked |
|
|
257
|
+
| `snapshot` | MR pipelines only | Publish pre-release artefact | MR can't shadow-integrate |
|
|
258
|
+
| `tag` | Merge to main only | Create semver tag on the new commit | Manual tag required |
|
|
259
|
+
|
|
260
|
+
**CI image:** The pipeline must specify `image: node:20` (or the
|
|
261
|
+
appropriate runtime for the repo-type). Without an explicit image,
|
|
262
|
+
GitLab falls back to the runner's default (typically `ruby:3.1`),
|
|
263
|
+
which won't have npm/node available.
|
|
264
|
+
|
|
265
|
+
The `--if-present` flag on npm run means phases without a script
|
|
266
|
+
silently succeed. This lets the scaffold work out of the box before
|
|
267
|
+
implementation fills in the real scripts.
|
|
268
|
+
|
|
269
|
+
### Snapshot artefact convention
|
|
270
|
+
|
|
271
|
+
On MR pipelines, the `snapshot` phase publishes a pre-release artefact
|
|
272
|
+
that the root repo's shadow integration can reference. The convention:
|
|
273
|
+
|
|
274
|
+
- Git tag: `v0.0.0-mr.<MR_IID>` on the MR branch head commit
|
|
275
|
+
- The root repo's topology MR pins to this tag
|
|
276
|
+
- The tag is lightweight (not annotated) — it's ephemeral
|
|
277
|
+
|
|
278
|
+
This works on GitLab free tier without a package registry. The root
|
|
279
|
+
repo clones the managed repo at the snapshot tag to compose the system.
|
|
280
|
+
|
|
281
|
+
### Auto-tag convention
|
|
282
|
+
|
|
283
|
+
On merge to main, the `tag` phase creates an annotated semver tag.
|
|
284
|
+
Version bumping strategy is determined by the project — the scaffold
|
|
285
|
+
provides the hook, the implementation decides the logic.
|
|
286
|
+
|
|
287
|
+
## API Stubs for Frontend Repos
|
|
288
|
+
|
|
289
|
+
When scaffolding a frontend component (role: frontend or frontend-host)
|
|
290
|
+
that depends on API components, generate stub servers in `pats/stubs/`.
|
|
291
|
+
These stubs enable PAT validation without running real API services.
|
|
292
|
+
|
|
293
|
+
### Generation rules
|
|
294
|
+
|
|
295
|
+
1. Read the API sub-task files to extract the endpoint contracts
|
|
296
|
+
(routes, methods, request/response shapes)
|
|
297
|
+
2. Generate one stub file per API dependency in `pats/stubs/`
|
|
298
|
+
3. Stubs use Express with CORS enabled
|
|
299
|
+
4. Read stubs return mock data matching the contract
|
|
300
|
+
5. Write stubs accept and store data in memory
|
|
301
|
+
6. Read and write stubs sharing the same story must share state
|
|
302
|
+
(same in-memory array, via require/import)
|
|
303
|
+
|
|
304
|
+
### Shared state pattern
|
|
305
|
+
|
|
306
|
+
When a story involves both a read and write API, the stubs must share
|
|
307
|
+
an in-memory data store so that writes are visible to subsequent reads.
|
|
308
|
+
Pattern:
|
|
309
|
+
|
|
310
|
+
- `pats/stubs/api-read.js` — exports the shared data array, starts
|
|
311
|
+
the read server as a side effect
|
|
312
|
+
- `pats/stubs/api-write.js` — requires api-read to get the shared
|
|
313
|
+
array reference, starts the write server
|
|
314
|
+
|
|
315
|
+
Running `node pats/stubs/api-write.js` starts both servers (api-read
|
|
316
|
+
is loaded via require, which triggers its listen call).
|
|
317
|
+
|
|
318
|
+
### Stub template (read API)
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
const express = require('express')
|
|
322
|
+
const cors = require('cors')
|
|
323
|
+
|
|
324
|
+
const app = express()
|
|
325
|
+
app.use(cors())
|
|
326
|
+
|
|
327
|
+
const <collection> = []
|
|
328
|
+
|
|
329
|
+
app.get('/<resource>', (req, res) => {
|
|
330
|
+
res.json(<collection>)
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
app.get('/health', (req, res) => {
|
|
334
|
+
res.json({ status: 'ok' })
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
const PORT = process.env.STUB_PORT || <read-port>
|
|
338
|
+
const server = app.listen(PORT, () => {
|
|
339
|
+
console.log('<api-name> stub on port ' + PORT)
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
module.exports = { app, <collection>, server }
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Stub template (write API)
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
const express = require('express')
|
|
349
|
+
const cors = require('cors')
|
|
350
|
+
const { <collection> } = require('./api-read')
|
|
351
|
+
|
|
352
|
+
const app = express()
|
|
353
|
+
app.use(cors())
|
|
354
|
+
app.use(express.json())
|
|
355
|
+
|
|
356
|
+
let nextId = 1
|
|
357
|
+
|
|
358
|
+
app.post('/<resource>', (req, res) => {
|
|
359
|
+
const { <field> } = req.body || {}
|
|
360
|
+
if (!<field> || !<field>.trim()) {
|
|
361
|
+
return res.status(400).json({ error: '<Field> is required' })
|
|
362
|
+
}
|
|
363
|
+
const item = { id: nextId++, <field>: <field>.trim() }
|
|
364
|
+
<collection>.push(item)
|
|
365
|
+
res.status(201).json(item)
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
app.get('/health', (req, res) => {
|
|
369
|
+
res.json({ status: 'ok' })
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
const PORT = process.env.STUB_WRITE_PORT || <write-port>
|
|
373
|
+
app.listen(PORT, () => {
|
|
374
|
+
console.log('<api-name> stub on port ' + PORT)
|
|
375
|
+
})
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Dependencies
|
|
379
|
+
|
|
380
|
+
Add `express` and `cors` as devDependencies in the frontend repo's
|
|
381
|
+
`package.json` if not already present (they're needed for stubs only,
|
|
382
|
+
not for the frontend build).
|
|
383
|
+
|
|
384
|
+
## Notes
|
|
385
|
+
|
|
386
|
+
- The pipeline template shown is for `repo-type: node`. Other repo
|
|
387
|
+
types follow the same lifecycle phases but use different package
|
|
388
|
+
managers (pip, cargo, etc.)
|
|
389
|
+
- The pipeline is intentionally minimal — no Docker, no registry, no
|
|
390
|
+
deployment. Those are concerns for later stages.
|
|
391
|
+
- Branch protection requires an unprotect/re-protect sequence because
|
|
392
|
+
GitLab auto-protects `main` on repo creation and there is no API to
|
|
393
|
+
update existing protection settings.
|
|
394
|
+
- On free tier, project access tokens are unavailable. Use a group-level
|
|
395
|
+
personal access token as fallback. Document this in the report step.
|
|
396
|
+
- When re-running scaffold on a partially seeded repo, use
|
|
397
|
+
`create_or_update_file` instead of `push_files` for any files that
|
|
398
|
+
may already exist. `push_files` rejects commits with existing files.
|
|
399
|
+
- `scaffold-repo` does NOT set up webhooks or root repo CI — that's
|
|
400
|
+
`wire-orchestration`.
|
|
401
|
+
- Always include a `package-lock.json` in the seed commit (even with no
|
|
402
|
+
dependencies). Without it, `npm ci` fails immediately. The lockfile
|
|
403
|
+
is trivial for an empty project — just name, version, lockfileVersion.
|
|
404
|
+
- **CI cache with DAG (`needs:`):** GitLab `needs:` creates a DAG but
|
|
405
|
+
does NOT propagate cache between jobs. Each job that requires
|
|
406
|
+
`node_modules/` must declare its own cache block. The template uses a
|
|
407
|
+
YAML anchor (`&default_cache`) with `policy: pull` as the default, and
|
|
408
|
+
the install job overrides to `policy: pull-push`. Without this, jobs
|
|
409
|
+
downstream of install (build, test, etc.) won't find `node_modules/`
|
|
410
|
+
and commands like `vitest` will fail with "not found".
|
|
411
|
+
- **CORS for backend repos:** When `role: backend`, the seed `app.js`
|
|
412
|
+
must include `cors` middleware (`app.use(cors())`) and `cors` must be
|
|
413
|
+
listed as a dependency in `package.json`. Without this, any frontend
|
|
414
|
+
on a different port will fail to fetch from the API — the default
|
|
415
|
+
development scenario for distributed topologies. CORS is permissive
|
|
416
|
+
by default for development; production lockdown is a deployment concern.
|
|
417
|
+
- **Port convention:** Backend APIs must use port defaults that don't
|
|
418
|
+
collide with frontend dev servers. The convention is deterministic
|
|
419
|
+
based on component order in `project.yaml`:
|
|
420
|
+
- shell (frontend-host): 3000
|
|
421
|
+
- first frontend: 3001
|
|
422
|
+
- first backend: 3002
|
|
423
|
+
- second backend: 3003
|
|
424
|
+
- ...and so on
|
|
425
|
+
The port is set via `PORT` env var with the conventional default in
|
|
426
|
+
`server.js`. Frontend components referencing APIs must use the correct
|
|
427
|
+
port in their `API_URL` default.
|
|
428
|
+
|
|
429
|
+
## Steering Template
|
|
430
|
+
|
|
431
|
+
The `.m/steering/m-managed-repo.md` file gives any AI agent the context
|
|
432
|
+
needed to work in an M-managed repo. It is generated from the sub-task
|
|
433
|
+
metadata and project configuration, and placed in the repo's `.m/steering/`
|
|
434
|
+
directory. Agent-specific adapters can symlink or include it from their
|
|
435
|
+
own steering locations (e.g. `.kiro/steering/`, `.claude/steering/`).
|
|
436
|
+
|
|
437
|
+
### Template
|
|
438
|
+
|
|
439
|
+
The following is parameterised. Replace `<placeholders>` with values
|
|
440
|
+
extracted from the sub-task file and project.yaml.
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
---
|
|
444
|
+
inclusion: auto
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
# M-Managed Repo — AI Development Guide
|
|
448
|
+
|
|
449
|
+
This repo is managed by Methodology M. This steering file gives you the
|
|
450
|
+
context needed to work effectively in this codebase.
|
|
451
|
+
|
|
452
|
+
## Repo Identity
|
|
453
|
+
|
|
454
|
+
- **Component:** <component-name>
|
|
455
|
+
- **Role:** <component-role> (e.g. backend, frontend, frontend-host)
|
|
456
|
+
- **Type:** referenced (tracked by version in the root repo)
|
|
457
|
+
- **Root repo:** <root-repo-name> (under the same GitLab group)
|
|
458
|
+
- **Story management:** Sub-task files in `jira/`, linked to parent stories in the root repo
|
|
459
|
+
|
|
460
|
+
## Methodology M Essentials
|
|
461
|
+
|
|
462
|
+
This is an M-type managed repo. Key concepts:
|
|
463
|
+
|
|
464
|
+
- **PATs (Pseudo Acceptance Tests)** are structured, machine-readable
|
|
465
|
+
validation criteria. They describe what to verify without prescribing
|
|
466
|
+
a test framework. PAT stubs in `pats/` are pseudocode — human-readable
|
|
467
|
+
intent that must be transformed into executable acceptance tests.
|
|
468
|
+
|
|
469
|
+
- **Repo-level PATs** answer: "does this component fulfil its contract?"
|
|
470
|
+
They run in this repo's CI pipeline and test the component in isolation.
|
|
471
|
+
|
|
472
|
+
- **Story-level PATs** live in the root repo and test the composed system.
|
|
473
|
+
This repo doesn't run those — it only owns its own contract.
|
|
474
|
+
|
|
475
|
+
- **Acceptance tests (ATs)** are the executable form of PATs. The PAT stub
|
|
476
|
+
is the specification; the AT is the compiled, CI-runnable proof. By the
|
|
477
|
+
time an MR is raised, every PAT must have a corresponding passing AT.
|
|
478
|
+
|
|
479
|
+
## Artefact Layout
|
|
480
|
+
|
|
481
|
+
jira/ Sub-task files (acceptance criteria, parent story link)
|
|
482
|
+
pats/ PAT stubs (.stub.js) and acceptance tests (.spec.js)
|
|
483
|
+
src/ Application source code
|
|
484
|
+
.gitlab-ci.yml CI pipeline (lifecycle phases, not hardcoded commands)
|
|
485
|
+
package.json Lifecycle scripts (build, test, start, snapshot, tag)
|
|
486
|
+
|
|
487
|
+
## Sub-Task Files
|
|
488
|
+
|
|
489
|
+
Files in `jira/` describe what this repo must deliver for a given story.
|
|
490
|
+
Each sub-task has:
|
|
491
|
+
|
|
492
|
+
- A parent story reference
|
|
493
|
+
- Acceptance criteria (repo-level PATs in prose)
|
|
494
|
+
- A summary of the contract this component owns
|
|
495
|
+
|
|
496
|
+
When asked to implement something, read the sub-task file first. It is the
|
|
497
|
+
contract. Build exactly what it specifies.
|
|
498
|
+
|
|
499
|
+
## PAT Stubs and Acceptance Tests
|
|
500
|
+
|
|
501
|
+
Files in `pats/` come in two forms:
|
|
502
|
+
|
|
503
|
+
- `*.stub.js` — PAT stubs. Pseudocode describing what to test. These are
|
|
504
|
+
generated during story decomposition and represent the contract in
|
|
505
|
+
human-readable form. Do not delete them after transformation.
|
|
506
|
+
|
|
507
|
+
- `*.spec.js` — Acceptance tests. Real, executable test code that proves
|
|
508
|
+
the implementation meets the contract. Generated by transforming stubs.
|
|
509
|
+
|
|
510
|
+
### Transforming PAT Stubs into Acceptance Tests
|
|
511
|
+
|
|
512
|
+
When asked to "generate acceptance tests" or "transform PAT stubs":
|
|
513
|
+
|
|
514
|
+
1. Read the stub file to understand the contract
|
|
515
|
+
2. Read the sub-task file for additional context
|
|
516
|
+
3. Check `package.json` `"test"` script and any test config files
|
|
517
|
+
(e.g. `cypress.config.js`, `vitest.config.js`) to determine the
|
|
518
|
+
repo's acceptance test framework. Do NOT guess from the component
|
|
519
|
+
role — use what the repo is actually configured to run.
|
|
520
|
+
4. **PATs are user-level acceptance criteria.** For frontend components,
|
|
521
|
+
PAT→CAT compilation MUST produce browser-based tests (Cypress,
|
|
522
|
+
Playwright, etc.) that verify behaviour through the real UI — not
|
|
523
|
+
vitest/jsdom unit tests. Unit tests are optional developer-level
|
|
524
|
+
tests, not PAT compilations.
|
|
525
|
+
- Frontend (any role) → Cypress or configured browser test framework
|
|
526
|
+
- Backend API → supertest + vitest (HTTP contract testing against
|
|
527
|
+
the real app, not mocked)
|
|
528
|
+
5. Write the spec file alongside the stub (same directory, matching the
|
|
529
|
+
configured spec pattern — e.g. *.cy.js for Cypress)
|
|
530
|
+
|
|
531
|
+
## API Stubs and CI Coupling
|
|
532
|
+
|
|
533
|
+
API stubs in `pats/stubs/` enable testing without real API services.
|
|
534
|
+
They run both locally (for PAT validation) and in CI (for acceptance tests).
|
|
535
|
+
|
|
536
|
+
**CRITICAL — stubs and CI are coupled:**
|
|
537
|
+
|
|
538
|
+
- Every stub file in `pats/stubs/api-*.js` MUST be started in the CI
|
|
539
|
+
test job. If you add or modify a stub, you MUST update `.gitlab-ci.yml`
|
|
540
|
+
to start it and wait on its health endpoint.
|
|
541
|
+
- Every stub MUST expose a `GET /health` endpoint so CI can `wait-on` it.
|
|
542
|
+
- Stubs sharing the same resource (e.g. read + write on todos) MUST share
|
|
543
|
+
state via `pats/stubs/shared-state.js`. Stateless stubs cannot validate
|
|
544
|
+
write→read round-trips.
|
|
545
|
+
- Before raising an MR, verify that `.gitlab-ci.yml` test job starts ALL
|
|
546
|
+
stubs in `pats/stubs/` and waits on ALL their health endpoints.
|
|
547
|
+
|
|
548
|
+
## CI Pipeline
|
|
549
|
+
|
|
550
|
+
The .gitlab-ci.yml uses lifecycle phases delegated to package.json scripts:
|
|
551
|
+
|
|
552
|
+
| Phase | Script | When |
|
|
553
|
+
|-------|--------|------|
|
|
554
|
+
| start | npm start | Local development (compose) |
|
|
555
|
+
| install | npm ci | Every pipeline |
|
|
556
|
+
| build | npm run build | Every pipeline |
|
|
557
|
+
| test | npm test | Every pipeline |
|
|
558
|
+
| snapshot | npm run snapshot | MR pipelines only |
|
|
559
|
+
| tag | npm run tag | Merge to main only |
|
|
560
|
+
|
|
561
|
+
The pipeline orchestrates; the scripts implement. When updating functionality,
|
|
562
|
+
update the package.json scripts — not the CI file.
|
|
563
|
+
|
|
564
|
+
## Branch and MR Conventions
|
|
565
|
+
|
|
566
|
+
- Feature branches: feat/<subtask-id>-<description>
|
|
567
|
+
- MR titles must include the sub-task ID
|
|
568
|
+
- Branch protection: push to main is blocked; all changes go through MRs
|
|
569
|
+
- Managed repo MRs stay open until the root repo's merge transaction lands
|
|
570
|
+
the full story — do not merge individually
|
|
571
|
+
|
|
572
|
+
## Development Workflow
|
|
573
|
+
|
|
574
|
+
The typical cycle for implementing a sub-task:
|
|
575
|
+
|
|
576
|
+
1. Read the sub-task file in jira/ — understand the contract
|
|
577
|
+
2. Create a feature branch from main
|
|
578
|
+
3. Implement towards the PATs — this is a continuous validation loop:
|
|
579
|
+
- Write code that addresses the acceptance criteria
|
|
580
|
+
- **UX fidelity rule:** if the sub-task includes a UX Reference section
|
|
581
|
+
with screenshots or mockups, the implementation must visually match
|
|
582
|
+
the design — layout, colours, shadows, rounded corners, spacing,
|
|
583
|
+
typography weight. Not pixel-perfect, but recognisably the same
|
|
584
|
+
design. Use the screenshots as your visual target. When no UX
|
|
585
|
+
reference exists, minimal functional styling is acceptable.
|
|
586
|
+
- Validate against PATs as you go using appropriate tools:
|
|
587
|
+
- **Frontend:** open in browser, verify visually, check data-testid
|
|
588
|
+
attributes, test interactions (use Chrome DevTools MCP if available)
|
|
589
|
+
- **API:** curl the endpoints, verify responses match the contract
|
|
590
|
+
- Use API stubs in `pats/stubs/` to test against dependency contracts
|
|
591
|
+
without needing real services running
|
|
592
|
+
- **Shared-state rule:** if the sub-task involves both reading and
|
|
593
|
+
writing the same resource (e.g. GET /todos and POST /todos), the
|
|
594
|
+
stubs MUST share state. A write through the write stub must be
|
|
595
|
+
visible to a subsequent read through the read stub. Stateless stubs
|
|
596
|
+
that return hardcoded data CANNOT validate write→read round-trips.
|
|
597
|
+
Check `pats/stubs/` for a shared-state module — if one doesn't
|
|
598
|
+
exist and the story requires it, create one before validating.
|
|
599
|
+
- **Round-trip validation rule:** any acceptance criterion that says
|
|
600
|
+
"new item appears in the list" or "data persists after action"
|
|
601
|
+
MUST be validated end-to-end: perform the write action in the UI,
|
|
602
|
+
then verify the result appears via the read path. If the item
|
|
603
|
+
doesn't appear, the PAT is NOT satisfied — do not hand-wave with
|
|
604
|
+
"the stub is stateless." Fix the stubs.
|
|
605
|
+
- A passing build is NOT PAT validation — you must demonstrate that
|
|
606
|
+
the implementation satisfies the acceptance criteria
|
|
607
|
+
- Never declare implementation complete without this demonstration
|
|
608
|
+
4. Compile PAT stubs into acceptance tests (CATs) using the repo's
|
|
609
|
+
configured test framework (check package.json and test config files)
|
|
610
|
+
5. Run npm test locally — all acceptance tests must pass
|
|
611
|
+
6. **Verify CI readiness:** check `.gitlab-ci.yml` test job starts all
|
|
612
|
+
stubs in `pats/stubs/` and waits on all health endpoints. If you
|
|
613
|
+
added or changed stubs, update the CI config.
|
|
614
|
+
7. Commit, push, open MR with sub-task ID in the title
|
|
615
|
+
8. CI runs: install → build → test → snapshot
|
|
616
|
+
9. Wait for story-level integration (managed by root repo)
|
|
617
|
+
|
|
618
|
+
## Code Conventions
|
|
619
|
+
|
|
620
|
+
- Separate app logic from server binding (app.js / server.js pattern)
|
|
621
|
+
so tests can import the app without starting a server
|
|
622
|
+
- Keep implementations minimal — deliver exactly what the sub-task specifies
|
|
623
|
+
- No framework-specific magic — keep it readable and testable
|
|
624
|
+
- Backend APIs must include CORS middleware (`app.use(cors())`) — required
|
|
625
|
+
for cross-origin requests from frontends in development
|
|
626
|
+
- Use `PORT` env var for server binding with a conventional default that
|
|
627
|
+
avoids collisions (shell=3000, MFE=3001, api-read=3002, api-write=3003)
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### Parameterisation
|
|
631
|
+
|
|
632
|
+
The scaffold capability fills in these placeholders from the inputs:
|
|
633
|
+
|
|
634
|
+
| Placeholder | Source |
|
|
635
|
+
|-------------|--------|
|
|
636
|
+
| `<component-name>` | Sub-task file `Component:` field |
|
|
637
|
+
| `<component-role>` | Inferred from component type (API → backend, MFE → frontend, shell → frontend-host) |
|
|
638
|
+
| `<root-repo-name>` | project.yaml `project:` field + `-root` suffix |
|
|
639
|
+
|
|
640
|
+
Everything else in the template is generic M methodology content that
|
|
641
|
+
applies to all managed repos regardless of project.
|