@thedecipherist/mdd 1.7.1 → 1.8.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/README.md +86 -0
- package/commands/mdd-audit.md +81 -6
- package/commands/mdd-bug.md +51 -12
- package/commands/mdd-build.md +88 -7
- package/commands/mdd-framework.md +101 -12
- package/commands/mdd-import-spec.md +51 -12
- package/commands/mdd-lifecycle.md +109 -12
- package/commands/mdd-manage.md +179 -10
- package/commands/mdd-manual.md +43 -18
- package/commands/mdd-ops.md +133 -12
- package/commands/mdd-plan.md +201 -14
- package/commands/mdd-rules-express.md +23 -0
- package/commands/mdd-rules-jwt.md +23 -0
- package/commands/mdd-rules-prisma.md +23 -0
- package/commands/mdd-rules-typescript.md +30 -0
- package/commands/mdd-security-rules.md +303 -0
- package/commands/mdd.md +88 -0
- package/package.json +1 -1
package/commands/mdd-plan.md
CHANGED
|
@@ -1,23 +1,13 @@
|
|
|
1
|
-
## Phase Logging
|
|
2
|
-
|
|
3
|
-
At the **start** of every phase (before any action) and the **end** of every phase (after all actions), run the command below. Substitute:
|
|
4
|
-
- `PHASE` with the phase identifier **and the initiative or wave slug** from `$ARGUMENTS` — e.g., `Phase PI1 (my-initiative)`, `Phase PW3 (wave-auth)`, `Phase PE2 (wave-checkout)`
|
|
5
|
-
- `EVENT` with `start` or `end`
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
bash -c 'D=$(date +%Y-%m-%d); T=$(date +%H:%M:%S); K=$(compressmcp --status 2>/dev/null | grep -oE "[0-9]+K/[0-9]+K" | head -1 || echo "-"); mkdir -p ~/.claude/mdd; printf "| %s | mdd-plan | PHASE | EVENT | %s | %s |\n" "$D" "$T" "$K" >> ~/.claude/mdd/log.md' 2>/dev/null || true
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Log file: `~/.claude/mdd/log.md`
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
1
|
## PLAN-INITIATIVE MODE — `/mdd plan-initiative`
|
|
16
2
|
|
|
17
3
|
Triggered when arguments start with `plan-initiative`. Creates a new initiative doc.
|
|
18
4
|
|
|
19
5
|
### Phase PI0 — Branch Guard
|
|
20
6
|
|
|
7
|
+
```bash
|
|
8
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PI0" start "$PLAN_TARGET"
|
|
9
|
+
```
|
|
10
|
+
|
|
21
11
|
Run before any file creation:
|
|
22
12
|
|
|
23
13
|
```bash
|
|
@@ -30,8 +20,16 @@ DIRTY=$(git status --porcelain)
|
|
|
30
20
|
- **On a feature branch** → working dirty is fine. Proceed — initiative planning docs belong on whatever branch is current.
|
|
31
21
|
- **Never proceed on main.** Hard block.
|
|
32
22
|
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PI0" end "$PLAN_TARGET"
|
|
26
|
+
```
|
|
33
27
|
### Phase PI1 — Mode choice
|
|
34
28
|
|
|
29
|
+
```bash
|
|
30
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PI1" start "$PLAN_TARGET"
|
|
31
|
+
```
|
|
32
|
+
|
|
35
33
|
Ask the user:
|
|
36
34
|
```
|
|
37
35
|
How do you want to create this initiative?
|
|
@@ -52,8 +50,16 @@ How do you want to create this initiative?
|
|
|
52
50
|
|
|
53
51
|
**If (a) Guide me:** proceed to Phase PI2.
|
|
54
52
|
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PI1" end "$PLAN_TARGET"
|
|
56
|
+
```
|
|
55
57
|
### Phase PI2 — Questions
|
|
56
58
|
|
|
59
|
+
```bash
|
|
60
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PI2" start "$PLAN_TARGET"
|
|
61
|
+
```
|
|
62
|
+
|
|
57
63
|
Ask all questions in a single interaction:
|
|
58
64
|
1. "What is the title of this initiative?"
|
|
59
65
|
2. "Describe it — what does it deliver and why does it exist?"
|
|
@@ -61,8 +67,16 @@ Ask all questions in a single interaction:
|
|
|
61
67
|
4. For each wave: "Wave N — name and one-sentence demo-state (what can the user DO when this wave is done?)"
|
|
62
68
|
5. "What's still undecided that could affect architecture?" → these become open product questions (unchecked `- [ ]` items)
|
|
63
69
|
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PI2" end "$PLAN_TARGET"
|
|
73
|
+
```
|
|
64
74
|
### Phase PI3 — Write initiative doc
|
|
65
75
|
|
|
76
|
+
```bash
|
|
77
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PI3" start "$PLAN_TARGET"
|
|
78
|
+
```
|
|
79
|
+
|
|
66
80
|
**Slug format:** lowercase, hyphens, no special characters. "Auth System" → `auth-system`.
|
|
67
81
|
|
|
68
82
|
**Collision check:** same as template mode above.
|
|
@@ -99,8 +113,16 @@ Compute and write `hash:` field after writing (hash of file content excluding th
|
|
|
99
113
|
|
|
100
114
|
Rebuild `.mdd/.startup.md`.
|
|
101
115
|
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PI3" end "$PLAN_TARGET"
|
|
119
|
+
```
|
|
102
120
|
### Phase PI4 — Chain to plan-wave
|
|
103
121
|
|
|
122
|
+
```bash
|
|
123
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PI4" start "$PLAN_TARGET"
|
|
124
|
+
```
|
|
125
|
+
|
|
104
126
|
Show the created doc to the user. Ask:
|
|
105
127
|
*"Want to plan Wave 1 now? (yes / no — I'll run /mdd plan-wave <slug>-wave-1 later)"*
|
|
106
128
|
|
|
@@ -108,12 +130,24 @@ If yes → run Phase PW1 inline for `<slug>-wave-1`.
|
|
|
108
130
|
|
|
109
131
|
---
|
|
110
132
|
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PI4" end "$PLAN_TARGET"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "-" "complete" "$PLAN_TARGET"
|
|
140
|
+
```
|
|
111
141
|
## PLAN-WAVE MODE — `/mdd plan-wave <wave-slug>`
|
|
112
142
|
|
|
113
143
|
Triggered when arguments start with `plan-wave`. Takes a wave slug (e.g. `auth-system-wave-2`), resolves the parent initiative from it.
|
|
114
144
|
|
|
115
145
|
### Phase PW1 — Load and validate
|
|
116
146
|
|
|
147
|
+
```bash
|
|
148
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PW1" start "$PLAN_TARGET"
|
|
149
|
+
```
|
|
150
|
+
|
|
117
151
|
**Step 0 — Branch guard:**
|
|
118
152
|
|
|
119
153
|
```bash
|
|
@@ -134,24 +168,48 @@ DIRTY=$(git status --porcelain)
|
|
|
134
168
|
6. **Depends-on gate:** read existing wave docs for this initiative. If the new wave's `depends_on` wave exists and is not `complete` → hard stop.
|
|
135
169
|
7. Surface context summary to user: initiative title, overview, wave count, which waves are done.
|
|
136
170
|
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PW1" end "$PLAN_TARGET"
|
|
174
|
+
```
|
|
137
175
|
### Phase PW2 — Mode choice
|
|
138
176
|
|
|
177
|
+
```bash
|
|
178
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PW2" start "$PLAN_TARGET"
|
|
179
|
+
```
|
|
180
|
+
|
|
139
181
|
Same as PI1: ask "(a) Guide me / (b) Template".
|
|
140
182
|
|
|
141
183
|
**If (b) Template:** create `waves/<wave-slug>.md` with blank template, tell user to fill it out and run `/mdd plan-sync` then `/mdd plan-execute <wave-slug>`.
|
|
142
184
|
|
|
143
185
|
**If (a) Guide me:** proceed to Phase PW3.
|
|
144
186
|
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PW2" end "$PLAN_TARGET"
|
|
190
|
+
```
|
|
145
191
|
### Phase PW3 — Questions
|
|
146
192
|
|
|
193
|
+
```bash
|
|
194
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PW3" start "$PLAN_TARGET"
|
|
195
|
+
```
|
|
196
|
+
|
|
147
197
|
Ask in a single interaction:
|
|
148
198
|
1. "Here's the demo-state from the initiative: [X]. Does this need sharpening for this wave?"
|
|
149
199
|
2. "List the features needed to reach that demo-state — name + one-line description each."
|
|
150
200
|
3. "Do any features depend on other features within this wave?"
|
|
151
201
|
4. "Any open research questions before building? Or none?"
|
|
152
202
|
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PW3" end "$PLAN_TARGET"
|
|
206
|
+
```
|
|
153
207
|
### Phase PW4 — Write wave doc
|
|
154
208
|
|
|
209
|
+
```bash
|
|
210
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PW4" start "$PLAN_TARGET"
|
|
211
|
+
```
|
|
212
|
+
|
|
155
213
|
Create `waves/<wave-slug>.md`:
|
|
156
214
|
|
|
157
215
|
```markdown
|
|
@@ -187,19 +245,39 @@ Compute and write `hash:` field. Update the Waves table in `initiatives/<slug>.m
|
|
|
187
245
|
|
|
188
246
|
Rebuild `.mdd/.startup.md`.
|
|
189
247
|
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PW4" end "$PLAN_TARGET"
|
|
251
|
+
```
|
|
190
252
|
### Phase PW5 — Chain
|
|
191
253
|
|
|
254
|
+
```bash
|
|
255
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PW5" start "$PLAN_TARGET"
|
|
256
|
+
```
|
|
257
|
+
|
|
192
258
|
Ask: *"Want to plan Wave N+1 now? (yes / no)"*
|
|
193
259
|
If yes → run Phase PW1 inline for the next wave slug.
|
|
194
260
|
|
|
195
261
|
---
|
|
196
262
|
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PW5" end "$PLAN_TARGET"
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "-" "complete" "$PLAN_TARGET"
|
|
270
|
+
```
|
|
197
271
|
## PLAN-EXECUTE MODE — `/mdd plan-execute <wave-slug>`
|
|
198
272
|
|
|
199
273
|
Triggered when arguments start with `plan-execute`. Runs the full MDD build flow for each feature in the wave.
|
|
200
274
|
|
|
201
275
|
### Phase PE1 — Load and validate
|
|
202
276
|
|
|
277
|
+
```bash
|
|
278
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PE1" start "$PLAN_TARGET"
|
|
279
|
+
```
|
|
280
|
+
|
|
203
281
|
**Step 0 — Branch guard (runs before everything else):**
|
|
204
282
|
|
|
205
283
|
```bash
|
|
@@ -245,8 +323,16 @@ DIRTY=$(git status --porcelain)
|
|
|
245
323
|
- **Discard:** delete the `wave-<wave-slug>/` folder, proceed to PE2 normally.
|
|
246
324
|
- If no stale job exists: proceed to PE2 normally.
|
|
247
325
|
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PE1" end "$PLAN_TARGET"
|
|
329
|
+
```
|
|
248
330
|
### Phase PE2 — Interaction mode + Job Setup
|
|
249
331
|
|
|
332
|
+
```bash
|
|
333
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PE2" start "$PLAN_TARGET"
|
|
334
|
+
```
|
|
335
|
+
|
|
250
336
|
Ask:
|
|
251
337
|
```
|
|
252
338
|
How do you want to run this wave?
|
|
@@ -282,8 +368,16 @@ Create `.mdd/jobs/wave-<wave-slug>/MANIFEST.md`:
|
|
|
282
368
|
|
|
283
369
|
List every feature from the wave's Features table in order. Features already marked `complete` in the wave doc get `[x]` from the start.
|
|
284
370
|
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PE2" end "$PLAN_TARGET"
|
|
374
|
+
```
|
|
285
375
|
### Phase PE3 — Execute features
|
|
286
376
|
|
|
377
|
+
```bash
|
|
378
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PE3" start "$PLAN_TARGET"
|
|
379
|
+
```
|
|
380
|
+
|
|
287
381
|
For each feature in the wave's feature table, in dependency order, skipping `complete` features:
|
|
288
382
|
|
|
289
383
|
1. Tell user: *"Starting Feature N: <feature-slug>"*
|
|
@@ -310,8 +404,16 @@ For each feature in the wave's feature table, in dependency order, skipping `com
|
|
|
310
404
|
|
|
311
405
|
**Resume behaviour:** if re-run on a partially complete wave, stale job detection in PE1 handles resume. MANIFEST is the authoritative progress record — it is always written before and after each feature so an interrupted session can pick up at the exact right point.
|
|
312
406
|
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PE3" end "$PLAN_TARGET"
|
|
410
|
+
```
|
|
313
411
|
### Phase PE4 — Wave completion
|
|
314
412
|
|
|
413
|
+
```bash
|
|
414
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PE4" start "$PLAN_TARGET"
|
|
415
|
+
```
|
|
416
|
+
|
|
315
417
|
When all features are `complete`:
|
|
316
418
|
1. Update `MANIFEST.md` — set `# Status: COMPLETE` in the header.
|
|
317
419
|
2. Show the demo-state: *"Wave complete. Demo-state: '<demo-state>'. Have you verified this?"*
|
|
@@ -343,12 +445,24 @@ Read all `.mdd/docs/*.md` (excluding `archive/`) — frontmatter only (id, title
|
|
|
343
445
|
|
|
344
446
|
---
|
|
345
447
|
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PE4" end "$PLAN_TARGET"
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "-" "complete" "$PLAN_TARGET"
|
|
455
|
+
```
|
|
346
456
|
## PLAN-SYNC MODE — `/mdd plan-sync`
|
|
347
457
|
|
|
348
458
|
Triggered when arguments start with `plan-sync`. Detects manual edits to initiative/wave files via hash comparison and reconciles them.
|
|
349
459
|
|
|
350
460
|
### Phase PS1 — Scan all files
|
|
351
461
|
|
|
462
|
+
```bash
|
|
463
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PS1" start "$PLAN_TARGET"
|
|
464
|
+
```
|
|
465
|
+
|
|
352
466
|
Read every file in `.mdd/initiatives/` and `.mdd/waves/` (including `archive/`). For each, compute the hash of the file content (excluding the `hash:` line). Compare against stored `hash:` field.
|
|
353
467
|
|
|
354
468
|
Build a change table:
|
|
@@ -359,8 +473,16 @@ auth-system-wave-1.md | def456 | abc999 | YES
|
|
|
359
473
|
billing-module.md | (empty) | xyz111 | YES (new — no hash yet)
|
|
360
474
|
```
|
|
361
475
|
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PS1" end "$PLAN_TARGET"
|
|
479
|
+
```
|
|
362
480
|
### Phase PS2 — Present changes + confirm
|
|
363
481
|
|
|
482
|
+
```bash
|
|
483
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PS2" start "$PLAN_TARGET"
|
|
484
|
+
```
|
|
485
|
+
|
|
364
486
|
Show the full change table. Tell the user what will happen:
|
|
365
487
|
|
|
366
488
|
- **Initiative changed:** version incremented, hash updated, completed waves with old `initiative_version` flagged for review
|
|
@@ -369,8 +491,16 @@ Show the full change table. Tell the user what will happen:
|
|
|
369
491
|
|
|
370
492
|
Ask: *"Apply these updates? (yes / review each / cancel)"*
|
|
371
493
|
|
|
494
|
+
|
|
495
|
+
```bash
|
|
496
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PS2" end "$PLAN_TARGET"
|
|
497
|
+
```
|
|
372
498
|
### Phase PS3 — Apply
|
|
373
499
|
|
|
500
|
+
```bash
|
|
501
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PS3" start "$PLAN_TARGET"
|
|
502
|
+
```
|
|
503
|
+
|
|
374
504
|
For each changed file, in initiative-first order:
|
|
375
505
|
|
|
376
506
|
**Initiative changed:**
|
|
@@ -403,19 +533,39 @@ Report:
|
|
|
403
533
|
|
|
404
534
|
---
|
|
405
535
|
|
|
536
|
+
|
|
537
|
+
```bash
|
|
538
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PS3" end "$PLAN_TARGET"
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
```bash
|
|
542
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "-" "complete" "$PLAN_TARGET"
|
|
543
|
+
```
|
|
406
544
|
## PLAN-REMOVE-FEATURE MODE — `/mdd plan-remove-feature <wave-slug> <feature-slug>`
|
|
407
545
|
|
|
408
546
|
Triggered when arguments start with `plan-remove-feature`.
|
|
409
547
|
|
|
410
548
|
### Phase PRF1 — Load and validate
|
|
411
549
|
|
|
550
|
+
```bash
|
|
551
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PRF1" start "$PLAN_TARGET"
|
|
552
|
+
```
|
|
553
|
+
|
|
412
554
|
1. Parse `<wave-slug>` and `<feature-slug>` from arguments.
|
|
413
555
|
2. Read wave doc — hard stop *"Wave does not exist"* if not found.
|
|
414
556
|
3. Find the feature row — hard stop *"Feature `<slug>` does not exist in wave `<wave-slug>`"* if not found.
|
|
415
557
|
4. **Dependency guard:** check if any other feature in the wave lists `<feature-slug>` in its `Depends on` column. If so → hard stop: *"`<other-feature>` depends on `<feature-slug>`. Remove or reassign that dependency first."*
|
|
416
558
|
|
|
559
|
+
|
|
560
|
+
```bash
|
|
561
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PRF1" end "$PLAN_TARGET"
|
|
562
|
+
```
|
|
417
563
|
### Phase PRF2 — Confirm and remove
|
|
418
564
|
|
|
565
|
+
```bash
|
|
566
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PRF2" start "$PLAN_TARGET"
|
|
567
|
+
```
|
|
568
|
+
|
|
419
569
|
Show summary:
|
|
420
570
|
```
|
|
421
571
|
Remove feature from wave?
|
|
@@ -441,17 +591,37 @@ Rebuild `.mdd/.startup.md`.
|
|
|
441
591
|
|
|
442
592
|
---
|
|
443
593
|
|
|
594
|
+
|
|
595
|
+
```bash
|
|
596
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PRF2" end "$PLAN_TARGET"
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
```bash
|
|
600
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "-" "complete" "$PLAN_TARGET"
|
|
601
|
+
```
|
|
444
602
|
## PLAN-CANCEL-INITIATIVE MODE — `/mdd plan-cancel-initiative <slug>`
|
|
445
603
|
|
|
446
604
|
Triggered when arguments start with `plan-cancel-initiative`.
|
|
447
605
|
|
|
448
606
|
### Phase PCI1 — Load
|
|
449
607
|
|
|
608
|
+
```bash
|
|
609
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PCI1" start "$PLAN_TARGET"
|
|
610
|
+
```
|
|
611
|
+
|
|
450
612
|
1. Parse `<slug>` — hard stop *"Initiative does not exist"* if `initiatives/<slug>.md` not found.
|
|
451
613
|
2. Read initiative doc. Count: waves, wave statuses, associated feature docs (those with `initiative: <slug>` frontmatter).
|
|
452
614
|
|
|
615
|
+
|
|
616
|
+
```bash
|
|
617
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PCI1" end "$PLAN_TARGET"
|
|
618
|
+
```
|
|
453
619
|
### Phase PCI2 — Confirm
|
|
454
620
|
|
|
621
|
+
```bash
|
|
622
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PCI2" start "$PLAN_TARGET"
|
|
623
|
+
```
|
|
624
|
+
|
|
455
625
|
Show summary:
|
|
456
626
|
```
|
|
457
627
|
Cancel initiative: Auth System
|
|
@@ -465,8 +635,16 @@ Cancel this initiative? (yes/no)
|
|
|
465
635
|
|
|
466
636
|
If yes:
|
|
467
637
|
|
|
638
|
+
|
|
639
|
+
```bash
|
|
640
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PCI2" end "$PLAN_TARGET"
|
|
641
|
+
```
|
|
468
642
|
### Phase PCI3 — Cancel
|
|
469
643
|
|
|
644
|
+
```bash
|
|
645
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PCI3" start "$PLAN_TARGET"
|
|
646
|
+
```
|
|
647
|
+
|
|
470
648
|
1. Set `status: cancelled` in initiative frontmatter. Recompute hash.
|
|
471
649
|
2. Ask: *"Archive wave docs? (yes/no)"* — if yes, move all wave files to `.mdd/waves/archive/`
|
|
472
650
|
3. Ask: *"Flag feature docs with a warning? (yes/no)"* — if yes, add to each associated feature doc's `known_issues`: `"Initiative <slug> was cancelled — review whether this feature is still needed."`
|
|
@@ -483,3 +661,12 @@ Report:
|
|
|
483
661
|
```
|
|
484
662
|
|
|
485
663
|
---
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
```bash
|
|
667
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "Phase PCI3" end "$PLAN_TARGET"
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
```bash
|
|
671
|
+
bash ~/.claude/hooks/mdd-log-phase.sh "mdd-plan" "-" "complete" "$PLAN_TARGET"
|
|
672
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## MDD Rules — Express
|
|
2
|
+
|
|
3
|
+
Rules loaded when `stack.frameworks` includes `express`. Applied additively to audit criteria and build checklists.
|
|
4
|
+
|
|
5
|
+
### Audit Criteria
|
|
6
|
+
|
|
7
|
+
#### P2 — Error Handler 5xx Leakage
|
|
8
|
+
- Express (or equivalent HTTP framework) error handler forwards raw `Error.message` to the client without differentiating expected errors (4xx) from unexpected errors (5xx). For responses with status >= 500, always return a generic message (`'Internal server error'`) — never the raw exception message. Exposing Prisma connection errors, stack traces, or internal paths to clients is a P2 finding.
|
|
9
|
+
|
|
10
|
+
#### P2 — Open Redirect
|
|
11
|
+
- `res.redirect()` called with a user-supplied path (from `req.query`, `req.body`, `req.params`) without allowlist validation. Any redirect target that an attacker can control is a P2 finding.
|
|
12
|
+
|
|
13
|
+
#### P2 — Prototype Pollution via Body/Query Merge
|
|
14
|
+
- `req.query` or `req.body` merged into a plain object using spread, `Object.assign`, or similar without sanitisation. Use `structuredClone()` or a safe merge utility. Reference: CVE-2024-29041 class.
|
|
15
|
+
|
|
16
|
+
#### P2 — Middleware Order
|
|
17
|
+
- Authentication or authorisation middleware registered after route handlers it is meant to protect — P2. Middleware order in Express is execution order; a route registered before `app.use(authMiddleware)` is unprotected.
|
|
18
|
+
|
|
19
|
+
### Build Checklist (Phase 6)
|
|
20
|
+
|
|
21
|
+
- **Error handler shape:** Error handler must differentiate 4xx vs 5xx. Never forward `err.message` for status >= 500.
|
|
22
|
+
- **Redirect validation:** Any `res.redirect()` that uses request-supplied data must validate against an explicit allowlist before redirecting.
|
|
23
|
+
- **Middleware order:** Register global middleware (auth, rate limiting, body parsing) before route handlers, not after.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## MDD Rules — JWT
|
|
2
|
+
|
|
3
|
+
Rules loaded when `stack.auth` includes `jwt`. Applied additively to audit criteria and build checklists.
|
|
4
|
+
|
|
5
|
+
### Audit Criteria
|
|
6
|
+
|
|
7
|
+
#### P2 — decode() Instead of verify()
|
|
8
|
+
- `jwt.decode()` used instead of `jwt.verify()` to process an incoming token — P2. `decode()` skips signature verification entirely. Any token, including a forged one, will appear valid. Always use `jwt.verify()` with the secret/public key.
|
|
9
|
+
|
|
10
|
+
#### P2 — Unsafe Type Assertion After verify()
|
|
11
|
+
- `jwt.verify()` result cast with a type assertion (`as JwtPayload`) without runtime narrowing on required fields — P2. `verify()` returns `string | JwtPayload`; casting directly sets fields to `undefined as string` when the payload shape doesn't match. Required fields (`sub`, `email`, `id`, etc.) must be checked with `typeof field === 'string'` before use.
|
|
12
|
+
|
|
13
|
+
#### P2 — Empty-String Secret Fallback
|
|
14
|
+
- JWT signing or verification called with an empty-string fallback for the secret (e.g. `process.env.JWT_SECRET || ''`) — P2. A server that signs tokens with `''` is equivalent to having no secret. Secret must be validated at startup.
|
|
15
|
+
|
|
16
|
+
#### P3 — Missing Token Expiry Check
|
|
17
|
+
- Token payload used without checking `exp` field when the library does not enforce it automatically — P3. Always pass `{ ignoreExpiration: false }` explicitly or verify the library's default behaviour.
|
|
18
|
+
|
|
19
|
+
### Build Checklist (Phase 6)
|
|
20
|
+
|
|
21
|
+
- **Always use verify(), never decode():** `jwt.decode()` is for inspecting token structure only — never for authentication.
|
|
22
|
+
- **Runtime narrowing after verify():** After `jwt.verify()`, check that all required payload fields exist and are the expected type before using them.
|
|
23
|
+
- **Secret at startup:** Add JWT secret(s) to `required_env` in the feature doc and add a startup validation block.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## MDD Rules — Prisma
|
|
2
|
+
|
|
3
|
+
Rules loaded when `stack.orm` includes `prisma`. Applied additively to audit criteria and build checklists.
|
|
4
|
+
|
|
5
|
+
### Audit Criteria
|
|
6
|
+
|
|
7
|
+
#### P2 — Raw Query Without Parameterisation
|
|
8
|
+
- `prisma.$queryRaw` or `prisma.$executeRaw` used with string interpolation (`` `SELECT ... WHERE id = ${userId}` ``) instead of tagged template literals or `Prisma.sql` — P2. Raw string interpolation bypasses Prisma's parameterisation and is vulnerable to SQL injection.
|
|
9
|
+
|
|
10
|
+
#### P2 — Missing Transaction for Multi-Step Writes
|
|
11
|
+
- Multiple `prisma.model.create/update/delete` calls in a single handler without wrapping in `prisma.$transaction()` — P2 when the operations must be atomic (e.g. deducting balance and creating a record).
|
|
12
|
+
|
|
13
|
+
#### P3 — PrismaClient Instantiated Per Request
|
|
14
|
+
- `new PrismaClient()` called inside a request handler or per-import in multiple files — P3. A single shared client instance should be created once (e.g. `src/lib/prisma.ts`) and imported everywhere. Multiple instances exhaust the connection pool.
|
|
15
|
+
|
|
16
|
+
#### P3 — Unhandled PrismaClientKnownRequestError
|
|
17
|
+
- Prisma operations in request handlers without catching `PrismaClientKnownRequestError` — P3. Uncaught Prisma errors surface as 500s with internal schema details in the default Express error handler.
|
|
18
|
+
|
|
19
|
+
### Build Checklist (Phase 6)
|
|
20
|
+
|
|
21
|
+
- **Single shared client:** Create `src/lib/prisma.ts` exporting one `PrismaClient` instance. Import it everywhere — never instantiate in handlers.
|
|
22
|
+
- **Transactions for atomic operations:** Any handler that writes to multiple tables must use `prisma.$transaction()`.
|
|
23
|
+
- **Catch Prisma errors:** Wrap Prisma calls in try/catch and handle `PrismaClientKnownRequestError` with appropriate HTTP responses.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
## MDD Rules — TypeScript
|
|
2
|
+
|
|
3
|
+
Rules loaded when `stack.language` includes `typescript`. Applied additively to audit criteria and build checklists.
|
|
4
|
+
|
|
5
|
+
### Audit Criteria
|
|
6
|
+
|
|
7
|
+
#### P2 — Switch Exhaustiveness
|
|
8
|
+
- Switch statement on a string-union type or operation enum has no `default:` case — P2.
|
|
9
|
+
- Switch `default:` returns any value (`''`, `undefined`, a node, etc.) instead of throwing with `satisfies never` — P2. Correct pattern: `default: throw new Error(\`unhandled: \${x satisfies never}\`)`.
|
|
10
|
+
|
|
11
|
+
#### P2 — Type Assertion Safety
|
|
12
|
+
- Type assertion (`as T`) used on the result of an external decode operation (`jwt.verify`, `JSON.parse`, schema validation output) without runtime narrowing on required fields — P2. Required fields must be checked with `typeof field === 'string'` (or equivalent) before use.
|
|
13
|
+
|
|
14
|
+
#### P2 — Environment Startup Validation
|
|
15
|
+
- Required environment variable accessed with an empty-string or falsy fallback (e.g. `process.env.SECRET || ''`) instead of throwing at startup when absent — P2. A server that boots silently with a missing secret is a latent vulnerability.
|
|
16
|
+
|
|
17
|
+
#### P3 — Pattern Coverage in File
|
|
18
|
+
- When a finding of type X is found in a file, grep the entire file for all other instances of the same pattern before marking the finding complete. A single finding does not imply the rest of the file is clean — P3 if additional instances are present and unflagged.
|
|
19
|
+
|
|
20
|
+
### Build Checklist (Phase 6)
|
|
21
|
+
|
|
22
|
+
- **Env validation block:** If `required_env` is non-empty in the feature doc, add a startup validation block to the server entry point before any route registration:
|
|
23
|
+
```typescript
|
|
24
|
+
const REQUIRED_ENV = ['SECRET_KEY', 'DB_URL'];
|
|
25
|
+
for (const key of REQUIRED_ENV) {
|
|
26
|
+
if (!process.env[key]) throw new Error(`Missing required env var: ${key}`);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
- **Switch exhaustiveness:** Every switch on a string-union or enum type must have a `default: throw new Error(\`unhandled: \${x satisfies never}\`)` branch.
|
|
30
|
+
- **Shared utilities:** If `depends_on` is non-empty, scan the dependency's source files for shared infrastructure (error types, DB clients, utility functions). If the new feature would duplicate them, extract to a shared module before implementing.
|