shapeup-sdlc 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/.claude/settings.local.example.json +20 -0
  2. package/.claude-plugin/marketplace.json +16 -0
  3. package/.claude-plugin/plugin.json +18 -0
  4. package/.env.shapeup.example +14 -0
  5. package/AGENTS.md +133 -0
  6. package/LICENSE +21 -0
  7. package/README.md +362 -0
  8. package/SECURITY.md +72 -0
  9. package/bin/init.mjs +329 -0
  10. package/commands/build.md +14 -0
  11. package/commands/eval.md +15 -0
  12. package/commands/hammer.md +12 -0
  13. package/commands/orient.md +11 -0
  14. package/commands/qa.md +12 -0
  15. package/commands/retro.md +13 -0
  16. package/commands/scopes.md +14 -0
  17. package/commands/shape.md +12 -0
  18. package/commands/ship.md +53 -0
  19. package/commands/wire.md +11 -0
  20. package/hooks/anti-rationalization.mjs +244 -0
  21. package/hooks/compact-snapshot.mjs +47 -0
  22. package/hooks/gate-deadline.mjs +151 -0
  23. package/hooks/gate-intake.mjs +110 -0
  24. package/hooks/gate-l2.mjs +161 -0
  25. package/hooks/gate-zerowork.mjs +264 -0
  26. package/hooks/hooks.json +118 -0
  27. package/hooks/lib/decision.mjs +183 -0
  28. package/hooks/safety-spine.mjs +296 -0
  29. package/hooks/sandbox-guard.mjs +172 -0
  30. package/hooks/session-rehydrate.mjs +109 -0
  31. package/hooks/slop-cleaner.mjs +176 -0
  32. package/oracles/_shared.mjs +46 -0
  33. package/oracles/http-oracle.mjs +155 -0
  34. package/oracles/index.mjs +36 -0
  35. package/oracles/process-oracle.mjs +146 -0
  36. package/oracles/snapshot-oracle.mjs +119 -0
  37. package/oracles/test-oracle.mjs +138 -0
  38. package/package.json +49 -0
  39. package/skills/advisor-protocol/SKILL.md +171 -0
  40. package/skills/ba-pitch-analyzer/SKILL.md +175 -0
  41. package/skills/ba-pitch-analyzer/assets/templates/_index.tmpl.md +84 -0
  42. package/skills/ba-pitch-analyzer/assets/templates/api-feasibility.tmpl.md +65 -0
  43. package/skills/ba-pitch-analyzer/assets/templates/assess-report.tmpl.md +127 -0
  44. package/skills/ba-pitch-analyzer/assets/templates/contracts/be-service.contract.tmpl.md +62 -0
  45. package/skills/ba-pitch-analyzer/assets/templates/contracts/offline-storage.contract.tmpl.md +92 -0
  46. package/skills/ba-pitch-analyzer/assets/templates/contracts/third-party-api.contract.tmpl.md +66 -0
  47. package/skills/ba-pitch-analyzer/assets/templates/cross-context/context-map.tmpl.md +64 -0
  48. package/skills/ba-pitch-analyzer/assets/templates/cross-context/event-choreography.tmpl.md +77 -0
  49. package/skills/ba-pitch-analyzer/assets/templates/cross-context/migration-plan.tmpl.md +104 -0
  50. package/skills/ba-pitch-analyzer/assets/templates/cross-context/team-handoff.tmpl.md +79 -0
  51. package/skills/ba-pitch-analyzer/assets/templates/domain-model.tmpl.md +79 -0
  52. package/skills/ba-pitch-analyzer/assets/templates/feedback.tmpl.md +87 -0
  53. package/skills/ba-pitch-analyzer/assets/templates/integration.tmpl.md +64 -0
  54. package/skills/ba-pitch-analyzer/assets/templates/run-state.tmpl.md +77 -0
  55. package/skills/ba-pitch-analyzer/assets/templates/scope-summary.tmpl.md +105 -0
  56. package/skills/ba-pitch-analyzer/assets/templates/synthesis.tmpl.md +215 -0
  57. package/skills/ba-pitch-analyzer/assets/templates/task-board.tmpl.md +35 -0
  58. package/skills/ba-pitch-analyzer/assets/templates/task-spike.tmpl.md +86 -0
  59. package/skills/ba-pitch-analyzer/assets/templates/task.tmpl.md +84 -0
  60. package/skills/ba-pitch-analyzer/assets/templates/usecase.tmpl.md +105 -0
  61. package/skills/ba-pitch-analyzer/assets/templates/ux-behavior.tmpl.md +65 -0
  62. package/skills/ba-pitch-analyzer/references/contract-patterns.md +152 -0
  63. package/skills/ba-pitch-analyzer/references/ddd-patterns.md +173 -0
  64. package/skills/ba-pitch-analyzer/references/doc-schemas.md +275 -0
  65. package/skills/ba-pitch-analyzer/references/integration-analysis.md +152 -0
  66. package/skills/ba-pitch-analyzer/references/task-generation.md +623 -0
  67. package/skills/ba-pitch-analyzer/references/test-surface.md +102 -0
  68. package/skills/ba-pitch-analyzer/references/ux-behavior-patterns.md +269 -0
  69. package/skills/ba-pitch-analyzer/scripts/board-derive.mjs +241 -0
  70. package/skills/ba-pitch-analyzer/scripts/spec-lint.mjs +239 -0
  71. package/skills/coach/SKILL.md +202 -0
  72. package/skills/orient/SKILL.md +262 -0
  73. package/skills/qa-edge-hunter/SKILL.md +369 -0
  74. package/skills/scope-architect/SKILL.md +111 -0
  75. package/skills/scope-hammer/SKILL.md +186 -0
  76. package/skills/shapeup/SKILL.md +403 -0
  77. package/skills/shapeup/resources/breadboard-reflection.md +88 -0
  78. package/skills/shapeup/resources/breadboarding.md +334 -0
  79. package/skills/shapeup/resources/context-compaction.md +240 -0
  80. package/skills/shapeup/resources/framing-doc.md +92 -0
  81. package/skills/shapeup/resources/kickoff-doc.md +95 -0
  82. package/skills/shapeup/resources/shaping.md +194 -0
  83. package/skills/shapeup/resources/spike.md +86 -0
  84. package/skills/solution-architect/SKILL.md +129 -0
  85. package/skills/spec-evaluator/README.md +93 -0
  86. package/skills/spec-evaluator/SKILL.md +213 -0
  87. package/skills/spec-evaluator/references/anti-leniency.md +50 -0
  88. package/skills/spec-evaluator/references/dimension-contract.md +126 -0
  89. package/skills/spec-evaluator/references/dimensions/_registry.md +40 -0
  90. package/skills/spec-evaluator/references/dimensions/completeness.md +102 -0
  91. package/skills/spec-evaluator/references/dimensions/integration.md +129 -0
  92. package/skills/spec-evaluator/references/dimensions/performance.md +48 -0
  93. package/skills/spec-evaluator/references/dimensions/security.md +60 -0
  94. package/skills/spec-evaluator/references/dimensions/spec-conformance.md +134 -0
  95. package/skills/spec-evaluator/references/dimensions/tdd-surface.md +110 -0
  96. package/skills/spec-evaluator/references/dimensions/test-surface-conformance.md +100 -0
  97. package/skills/spec-evaluator/references/probing.md +177 -0
  98. package/skills/spec-evaluator/references/report-schema.md +101 -0
  99. package/skills/spec-evaluator/references/verdict-ledger.md +92 -0
  100. package/skills/spec-evaluator/scripts/verdict-ledger.mjs +166 -0
  101. package/skills/task-executor/SKILL.md +194 -0
  102. package/skills/tech-lead/README.md +71 -0
  103. package/skills/tech-lead/SKILL.md +459 -0
  104. package/skills/tech-lead/references/delegation.md +254 -0
  105. package/skills/tech-lead/references/gates.md +379 -0
  106. package/skills/tech-lead/references/invocation.md +45 -0
  107. package/skills/tech-lead/references/ledger-schema.md +214 -0
  108. package/skills/tech-lead/references/round-protocol.md +184 -0
  109. package/skills/tech-lead/references/state-model.md +66 -0
  110. package/skills/tech-lead/references/tiny-lane.md +52 -0
  111. package/skills/tech-lead/schemas/domain.schema.json +2294 -0
  112. package/skills/tech-lead/schemas/gate-answers.schema.json +92 -0
  113. package/skills/tech-lead/schemas/work-order.schema.json +21 -0
  114. package/skills/tech-lead/schemas/work-result.schema.json +40 -0
  115. package/skills/tech-lead/scripts/aegis-digest.mjs +124 -0
  116. package/skills/tech-lead/scripts/budget-check.mjs +156 -0
  117. package/skills/tech-lead/scripts/compile-order.mjs +518 -0
  118. package/skills/tech-lead/scripts/fit-check.mjs +196 -0
  119. package/skills/tech-lead/scripts/gate-answers.mjs +338 -0
  120. package/skills/tech-lead/scripts/ingest-result.mjs +270 -0
  121. package/skills/tech-lead/scripts/init-run.mjs +326 -0
  122. package/skills/tech-lead/scripts/lib/argv.mjs +224 -0
  123. package/skills/tech-lead/scripts/lib/contract-md.mjs +481 -0
  124. package/skills/tech-lead/scripts/lib/is-main.mjs +82 -0
  125. package/skills/tech-lead/scripts/lib/paths.mjs +278 -0
  126. package/skills/tech-lead/scripts/lib/ratchet-tree.mjs +112 -0
  127. package/skills/tech-lead/scripts/run-snapshot.mjs +269 -0
  128. package/skills/tech-lead/scripts/ship-report.mjs +275 -0
  129. package/skills/tech-lead/scripts/stats.mjs +423 -0
  130. package/skills/tech-lead/scripts/t0-verify.mjs +470 -0
  131. package/skills/tech-lead/scripts/trace-lint.mjs +367 -0
  132. package/skills/tech-lead/scripts/validate-envelope.mjs +283 -0
  133. package/skills/translator/README.md +66 -0
  134. package/skills/translator/SKILL.md +258 -0
  135. package/skills/translator/references/preservation-rules.md +102 -0
@@ -0,0 +1,623 @@
1
+ # Task Generation Rules
2
+
3
+ Reference for Phase 6. Read before writing any `TASK-NNN` files.
4
+
5
+ ---
6
+
7
+ ## Core Principle
8
+
9
+ A task is the smallest unit of work that:
10
+ 1. Can be verified independently (run a command → pass/fail)
11
+ 2. Touches one package or one concern
12
+ 3. Can be committed as a standalone change
13
+
14
+ If a task can't be verified by running `pnpm test` or `pnpm typecheck`, it's too vague.
15
+ If a task touches 3 packages, it needs to be split.
16
+
17
+ ---
18
+
19
+ ## Task Types
20
+
21
+ | Type | Purpose | Output | AC Style |
22
+ |------|---------|--------|----------|
23
+ | `FEAT` | New capability | Shippable code | Commands + observable outcomes |
24
+ | `FIX` | Bug correction | Patched code | Regression test passes |
25
+ | `CHORE` | Non-functional work | Config / tooling change | Command exits 0 |
26
+ | `MIGRATION` | DB schema change | Migration file | Migration runs + rolls back |
27
+ | `DOCS` | Documentation only | Markdown files | File exists + content check |
28
+ | `SPIKE` | Feasibility investigation | **Decision document** | All questions answered with citation |
29
+
30
+ **SPIKE is not a FEAT.** It produces knowledge, not code. Time-boxed, not estimated.
31
+
32
+ ---
33
+
34
+ ## SPIKE Task Rules
35
+
36
+ A SPIKE task MUST be generated whenever Phase 1b detected unverified third-party capabilities.
37
+
38
+ ```yaml
39
+ ---
40
+ id: TASK-[NNN]
41
+ type: SPIKE
42
+ slug: spike-[api-name]-feasibility
43
+ time_box_hours: 4 # hard cap — if not answerable in 4h, escalate to PO
44
+ api_ref: API-[NN] # references api-feasibility.md investigation block
45
+ blocks: # REQUIRED — every task that cannot start until SPIKE done
46
+ - TASK-[NNN]
47
+ - TASK-[NNN]
48
+ status: todo
49
+ priority: 1 # always priority 1 — nothing can proceed without it
50
+ package: research # not a code package — signals non-implementation task
51
+ estimated_hours: ~ # leave blank — SPIKE uses time_box_hours instead
52
+ linked_docs:
53
+ - "[[api-feasibility#API-NN]]"
54
+ ---
55
+ ```
56
+
57
+ **Definition of Done for every SPIKE:**
58
+ ```markdown
59
+ ## Definition of Done
60
+ - [ ] All questions in [[api-feasibility#API-NN]] answered with direct source URL citation
61
+ - [ ] Contract file [[contracts/[repo].contract.md]] updated — no remaining ⏳ TBD fields
62
+ - [ ] If capability confirmed: tasks in `blocks` list updated to remove ⏳ BLOCKED annotation
63
+ - [ ] If capability NOT confirmed: PO notified with fallback scope options from api-feasibility.md
64
+ ```
65
+
66
+ **Verification method section (mandatory for SPIKE):**
67
+ ```markdown
68
+ ## Verification Method
69
+ 1. Read [Official Docs URL] — section: [specific section name]
70
+ 2. If not documented: search [Community/GitHub/Discord] for [specific query]
71
+ 3. If ambiguous: write minimal curl/SDK test against sandbox environment
72
+ 4. Record finding in api-feasibility.md API-NN block before closing task
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Contract-First Rule
78
+
79
+ Before generating any implementation task that touches a repository:
80
+
81
+ ```
82
+ Step 1: Verify contracts/[repo].contract.md exists
83
+ → If missing: generate TASK-NNN-[repo]-contract-stub BEFORE the implementation task
84
+
85
+ Step 2: Check contract source type
86
+ → be-service or offline-storage: proceed normally
87
+ → third-party-api AND contract has ⏳ TBD fields:
88
+ Add ⏳ BLOCKED annotation to implementation task
89
+ Add implementation task ID to SPIKE blocks: list
90
+
91
+ Step 3: Implementation task Context section MUST include:
92
+ "Implement [RepoName] per [[contracts/[repo].contract.md]]"
93
+
94
+ Step 4: Implementation task AC MUST include these three lines:
95
+ - [ ] Request shape matches [[contracts/[repo].contract.md#Request]] table
96
+ - [ ] Response mapping matches [[contracts/[repo].contract.md#Response]] table
97
+ - [ ] All error codes in contract Error Cases table are handled
98
+ ```
99
+
100
+ ---
101
+
102
+ ## Mandatory Ordering Rules
103
+
104
+ Tasks MUST follow this sequence within a feature. Never invert layers:
105
+
106
+ ```
107
+ Layer 1: Shared types & DB schema (packages/shared, migrations)
108
+ Layer 2: Domain model & repository (packages/shared or apps/api/domain)
109
+ Layer 3: Application / use cases (apps/api/usecases or apps/api/services)
110
+ Layer 4: Infrastructure / HTTP (apps/api/routes, apps/api/controllers)
111
+ Layer 5: Frontend integration (apps/web/features/...)
112
+ Layer 6: End-to-end tests (e2e/, integration tests)
113
+ ```
114
+
115
+ Every task at Layer N depends on all relevant tasks at Layer N-1.
116
+
117
+ ---
118
+
119
+ ## Lens-Aware Task Layer Breakdown
120
+
121
+ The layer structure differs by lens. Read the order's `payload.lens` for the active lens
122
+ before generating tasks.
123
+
124
+ ### LITE — Mobile-first layer breakdown
125
+
126
+ ```
127
+ Layer 1: Local schema / types (packages/shared — entity shapes only, no repo interfaces)
128
+ Layer 2: Local state management (apps/mobile — Zustand/Redux store, offline cache)
129
+ Layer 3: API call stubs (apps/mobile — typed fetch wrappers, no contract defined yet)
130
+ Layer 4: UI components (apps/mobile — screens, navigation, platform variants)
131
+ Layer 5: Integration smoke tests (apps/mobile — happy path only, mocked API responses)
132
+ ```
133
+
134
+ `use_case_refs` in LITE tasks maps to: *"User does X in screen Y"* — mobile perspective.
135
+ TypeScript Input/Output interfaces in UC files are **optional** in LITE (no API contract).
136
+ Tasks do NOT reference `contracts/` — use `[[ux-behavior#ScreenName]]` instead.
137
+
138
+ ### STANDARD — App ↔ API layer breakdown
139
+
140
+ ```
141
+ Layer 1: Shared schema / DB types (packages/shared — full entity + VO types)
142
+ Layer 2: Domain model (packages/shared or apps/api/domain)
143
+ Layer 3: Repository contracts (contracts/ — one per repo interface)
144
+ Layer 4: Application / use cases (apps/api/usecases — API perspective)
145
+ Layer 5: HTTP layer (apps/api/routes, controllers)
146
+ Layer 6: Mobile integration (apps/mobile — consumes contracts, replaces stubs)
147
+ Layer 7: End-to-end tests (e2e/ — contract + integration verified)
148
+ ```
149
+
150
+ `use_case_refs` in STANDARD tasks maps to: *"App calls endpoint X → API does Y"*.
151
+ Every task at Layer 4+ MUST reference `[[contracts/[repo].contract.md]]`.
152
+
153
+ ### LITE → STANDARD Reconciliation Pass
154
+
155
+ When upgrading, use cases with mobile perspective need an **API Contract sub-section added**:
156
+
157
+ ```markdown
158
+ ## API Contract ← ADD THIS to existing UC files during reconciliation
159
+ > Added during STANDARD upgrade — original mobile steps preserved above.
160
+
161
+ POST /[resource]
162
+ Request → [[contracts/[repo].contract.md#Request]]
163
+ Response → [[contracts/[repo].contract.md#Response]]
164
+ Errors → [[contracts/[repo].contract.md#Error-Cases]]
165
+ ```
166
+
167
+ Do NOT overwrite existing Steps section — append API Contract sub-section only.
168
+ Report reconciled files in the WorkResult's `deviations[]` with note `reconciled-by-upgrade`.
169
+
170
+ ---
171
+
172
+ ## Task Decomposition Decision Tree
173
+
174
+ ```
175
+ Is the work in this task > 8 hours?
176
+ ├── YES → Split into smaller tasks
177
+ └── NO → Continue
178
+
179
+ Does the task touch more than one package?
180
+ ├── YES → Split by package
181
+ └── NO → Continue
182
+
183
+ Does the task have two independent concerns?
184
+ (e.g., "create schema AND implement service")
185
+ ├── YES → Split by concern
186
+ └── NO → Task is appropriately scoped
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Acceptance Criteria Rules
192
+
193
+ Every criterion must be checkable by a command or observable action:
194
+
195
+ ```markdown
196
+ # ✅ Good — verifiable
197
+ - [ ] `pnpm --filter shared typecheck` exits 0
198
+ - [ ] `pnpm --filter api test src/domain/order.test.ts` passes
199
+ - [ ] `GET /api/orders/:id` returns 200 with `{ id, status, totalAmount }`
200
+ - [ ] Migration file exists at `packages/shared/drizzle/migrations/XXXXXX_add_orders.sql`
201
+ - [ ] Type `Order` is exported from `packages/shared/index.ts`
202
+
203
+ # ❌ Bad — not verifiable
204
+ - [ ] The order aggregate is well-designed
205
+ - [ ] Code follows DDD principles
206
+ - [ ] Service is implemented correctly
207
+ ```
208
+
209
+ ---
210
+
211
+ ## AC Trigger Matrix
212
+
213
+ Phase 6 applies this matrix to determine which AC sub-sections are REQUIRED
214
+ for each task. SKILL self-checks during generation — do not skip.
215
+
216
+ | Trigger Signal | ac_inverse | ac_empty_state | ac_boundary |
217
+ |----------------|:----------:|:--------------:|:-----------:|
218
+ | `layer: ui` + keyword `show/hide/display/visible/render` | ✓ | | |
219
+ | `task.type: FIX` + `layer: ui` — unconditional, no keyword required | ✓ | | |
220
+ | `layer: repository` or `layer: api` + keyword `fetch/load/list` | | ✓ | |
221
+ | `layer: ui` + description references data prop (`user.X`, `list.length`, `item?.field`) | | ✓ | |
222
+ | Any layer + numeric value, `size`, `limit`, `max`, `min` in description | | | ✓ |
223
+
224
+ **Invariant-backed regression (unconditional):**
225
+ A task generated to verify a UC `[INV-NN]` (its Context wikilinks `[[usecases/UC-Name#Invariants]]`)
226
+ MUST carry a regression-style `## Acceptance Criteria` whose every item is verifiable by command —
227
+ e.g. load a pre-change fixture → assert no throw, assert count preserved, assert no stray data.
228
+ A vague AC like "old board still works" FAILS L3-04 and the executor will gate-reject it.
229
+ This is the cost of absorbing the invariant into AC at *generation* time so the executor needs
230
+ zero awareness of where the invariant lives (Steps vs Invariants) — it just reads the task file.
231
+
232
+ **Exemptions — AC sub-sections are OPTIONAL (no L3 penalty if absent):**
233
+ ```
234
+ task.type IN [INFRA, CONFIG, MIGRATION, DOCS]
235
+ ```
236
+
237
+ **Dependency-blocked AC:**
238
+ If a required AC sub-section cannot be written because a dependency task is
239
+ not yet implemented (e.g., `ac_empty_state` requires a repository that doesn't
240
+ exist yet), annotate as follows instead of leaving the slot empty:
241
+ ```markdown
242
+ - [ ] ⏳ BLOCKED: Empty state test pending [[TASK-NNN]] (repository not yet available)
243
+ ```
244
+ Blocked ACs are captured in `scope-summary.md` as blockers — NOT penalized in L3.
245
+
246
+ ---
247
+
248
+ ## BDD Scenario Rules
249
+
250
+ `### 🧪 BDD Scenarios` is REQUIRED in `## Acceptance Criteria` when:
251
+ - `task.type: FEAT` AND (task has a user-actor OR task crosses a service/API/repository boundary)
252
+
253
+ **Exemptions:** CHORE · DOCS · MIGRATION · INFRA · SPIKE
254
+
255
+ **Format:**
256
+ ```
257
+ **Scenario: [Plain-English name]**
258
+ Given [precondition — who/what is in what state]
259
+ When [actor performs specific action]
260
+ Then [expected observable outcome]
261
+ ```
262
+
263
+ Rules:
264
+ - Minimum 1 scenario (happy path). Maximum 3 per task — fewer, more signal-rich.
265
+ - Each scenario must map to ≥1 item in `### ✅ Baseline` or `### 🔁 Inverse Conditions`.
266
+ - Scenarios describe behavior, not implementation — no class names or method calls.
267
+ - Never invent behavior not traceable to UC steps, domain model, or ux-behavior.
268
+
269
+ ---
270
+
271
+ ## Integration Flow Rules
272
+
273
+ `### 🔗 Integration Flow` is REQUIRED when the task crosses ≥1 service boundary:
274
+ - Layer 4+ tasks (HTTP controllers and above in STANDARD)
275
+ - Any task referencing an external service or third-party API
276
+ - Frontend tasks that call an API endpoint
277
+
278
+ **Format:**
279
+ ```
280
+ **[Source layer] → [Target layer/service]**
281
+ Given [upstream caller/actor is in [state]]
282
+ When [action triggers at [layer — e.g. POST /api/resource or Repository.save()]]
283
+ Then [downstream side effect — DB row, event, response shape]
284
+ And [caller receives — HTTP status + response body shape]
285
+ ```
286
+
287
+ Rules:
288
+ - Name layers and surfaces explicitly: `POST /api/boards → BoardRepository.save() → DB: boards`
289
+ - Cover only the call chain this task owns — not the whole system.
290
+ - If a SPIKE is outstanding for this boundary: `⏳ UNVERIFIED — pending [[TASK-NNN-spike]]`
291
+ - Omit section (and note as blocked) if the integration design is not yet resolved.
292
+
293
+ ---
294
+
295
+ ## Non-Go Section (Mandatory)
296
+
297
+ Every task must explicitly state what is NOT in scope:
298
+
299
+ ```markdown
300
+ ## Non-Go (not in this task)
301
+ - Repository implementation → TASK-002
302
+ - API endpoints → TASK-004
303
+ - Frontend components → TASK-007
304
+ - Error handling for edge case X → TASK-009
305
+ ```
306
+
307
+ This prevents Claude Code from over-implementing and breaking task boundaries.
308
+
309
+ ---
310
+
311
+ ## Context Section Format
312
+
313
+ The Context section gives Claude Code the minimum information to implement:
314
+
315
+ ```markdown
316
+ ## Context
317
+ Implement the `Order` aggregate as defined in [[domain-model#Aggregate-Order]].
318
+ The aggregate root must enforce the invariant: totalAmount = sum of lineItem subtotals.
319
+ See [[usecases/UC-CreateOrder#Input]] for the data shape entering the system.
320
+ Existing `User` entity is in `packages/shared/src/domain/user.ts` — reference for style.
321
+ ```
322
+
323
+ Rules:
324
+ - Always wikilink to the source spec (not re-describe it)
325
+ - Point to existing code as style reference
326
+ - Mention constraints that aren't in the spec doc
327
+
328
+ ---
329
+
330
+ ## Implementation Notes Format
331
+
332
+ Optional hints — only include when there's a non-obvious implementation decision:
333
+
334
+ ```markdown
335
+ ## Implementation Notes
336
+ - Use `integer` for all monetary amounts (VND, no decimal). Never `float`.
337
+ - Drizzle enum: `pgEnum('order_status', ['draft', 'placed', 'paid', 'failed'])`
338
+ - Export both the table schema AND the inferred type:
339
+ `export type Order = typeof orders.$inferSelect`
340
+ - Do NOT use Drizzle relations yet — that comes in TASK-003
341
+ ```
342
+
343
+ ---
344
+
345
+ ## Task Numbering and Naming
346
+
347
+ ```
348
+ TASK-001-[slug].md
349
+ TASK-002-[slug].md
350
+ ...
351
+ TASK-010-[slug].md ← zero-pad to 3 digits for sort order
352
+ ```
353
+
354
+ Slug rules:
355
+ - Verb-noun format: `domain-schema`, `order-repository`, `create-order-usecase`
356
+ - No package name in slug (it's in frontmatter)
357
+ - Max 4 words
358
+
359
+ ---
360
+
361
+ ## Link-Field Integrity (v3.3)
362
+
363
+ **`depends_on` is the single authoritative edge; `unlocks` is derived, never hand-authored.**
364
+ On EVERY board write — analyze, generate-board, reconcile — recompute (via
365
+ `board-derive.mjs --write`, never by hand) the `unlocks` field of every task on the board as the
366
+ exact inverse of the full board's `depends_on` graph, then write it. Adding one task that
367
+ declares `depends_on: [TASK-007]` therefore rewrites `TASK-007.unlocks` in the same pass.
368
+ "Write both sides when you remember" allows drift; a derived field cannot drift
369
+ (island-escape shipped 10 asymmetric edges under the old rule). Audit L3-06 fails the board
370
+ on any asymmetric edge.
371
+
372
+ The same discipline has a boundary: it applies only when both sides live in the SAME root
373
+ (both task files are LOCAL). Never store a link from a committed doc back to a LOCAL task —
374
+ that is why UC `related_tasks` was retired (see doc-schemas.md, Use Case).
375
+
376
+ ---
377
+
378
+ ## Task Board (`tasks/_index.md`) Format
379
+
380
+ > Written to the LOCAL gitignored root `.shapeup/<slug>/tasks/_index.md`, not
381
+ > `spec_folder` — see SKILL.md Phase 6 "Locality" note (v3.2).
382
+
383
+ ```markdown
384
+ # Task Board: [Feature Name]
385
+
386
+ | ID | Title | Package | Status | Priority | Depends On | Est. |
387
+ |----|-------|---------|--------|----------|------------|------|
388
+ | [[TASK-001\|TASK-001]] | Domain schema | shared | ⬜ ready | 1 | — | 4h |
389
+ | [[TASK-002\|TASK-002]] | Order repository | api | ⬜ ready | 2 | TASK-001 | 3h |
390
+ | [[TASK-003\|TASK-003]] | CreateOrder use case | api | ⬜ ready | 3 | TASK-002 | 4h |
391
+ ```
392
+
393
+ Status emoji: ⬜ ready · 🔄 in-progress · 🚫 blocked · ✅ done
394
+
395
+ ---
396
+
397
+ ## Common Task Patterns for Monorepo
398
+
399
+ ### Pattern: New Shared Schema
400
+
401
+ ```
402
+ TASK-NNN: [Entity] schema + migration
403
+ Package: packages/shared
404
+ Acceptance:
405
+ - Schema file created
406
+ - Migration generated with drizzle-kit
407
+ - Types exported from index.ts
408
+ - pnpm --filter shared typecheck passes
409
+ ```
410
+
411
+ ### Pattern: Repository Implementation
412
+
413
+ ```
414
+ TASK-NNN: [Entity]Repository implementation
415
+ Package: apps/api (or packages/shared if pure domain)
416
+ Acceptance — Baseline:
417
+ - Class implements interface from domain-model.md
418
+ - `pnpm --filter api test [file]` passes
419
+ Acceptance — Empty State (REQUIRED — layer=repository):
420
+ - findById returns null (not throws) when ID not found
421
+ - findAll returns [] (not null) when table is empty
422
+ - All methods handle DB connection error → throw RepositoryError, not raw DB error
423
+ Depends on: schema task
424
+ ```
425
+
426
+ ### Pattern: Use Case Implementation
427
+
428
+ ```
429
+ TASK-NNN: [UseCaseName] use case
430
+ Package: apps/api
431
+ Acceptance — Baseline:
432
+ - `pnpm --filter api test [file]` passes
433
+ - Input validated: missing required field → throws UseCaseError with code E_XXX
434
+ - Output shape matches TypeScript interface in [[usecases/UC-Name#Output]]
435
+ Acceptance — Inverse:
436
+ - Unauthenticated caller → throws AuthError (not proceeds)
437
+ - Caller without [ROLE] permission → throws ForbiddenError
438
+ Acceptance — Empty State:
439
+ - ⏳ BLOCKED: integration test pending [[TASK-NNN]] (repository not yet available)
440
+ Depends on: repository task
441
+ ```
442
+
443
+ ### Pattern: HTTP Endpoint
444
+
445
+ ```
446
+ TASK-NNN: [METHOD] /[path] endpoint
447
+ Package: apps/api
448
+ Acceptance:
449
+ - Route registered in router
450
+ - Request/response types match use case IO
451
+ - 401 when unauthenticated
452
+ - Integration test passes
453
+ Depends on: use case task
454
+ ```
455
+
456
+ ### Pattern: Frontend Feature
457
+
458
+ ```
459
+ TASK-NNN: [ScreenName] component
460
+ Package: apps/web
461
+ Acceptance — Baseline:
462
+ - `pnpm --filter web typecheck` passes
463
+ - Implements all states from [[ux-behavior#ScreenName]]
464
+ - All RULE-XX from ux-behavior implemented
465
+ - All error codes from error catalog handled
466
+ Acceptance — Inverse (REQUIRED — layer=ui):
467
+ - [Primary CTA] does NOT render in loading state
468
+ - [Primary CTA] does NOT render when user lacks permission [ROLE]
469
+ Acceptance — Empty State (REQUIRED — data-driven UI):
470
+ - EmptyState renders when API returns []
471
+ - No crash when `[primaryDataProp]` is null on first render
472
+ Acceptance — Boundary:
473
+ - [Input field] rejects input > [N] characters with inline error
474
+ ```
475
+
476
+ ### Pattern: Integration Test
477
+
478
+ ```
479
+ TASK-NNN: Integration test — [use-case or feature name]
480
+ Package: apps/api/test | apps/web/e2e | e2e/
481
+ Layer: integration
482
+ Depends on: all implementation tasks for this feature (HTTP endpoint task minimum)
483
+ Estimated: 2–4h
484
+
485
+ Acceptance — Baseline:
486
+ - `pnpm --filter [pkg] test:integration [file]` passes
487
+ - [METHOD] /api/[resource] with valid auth → [expected status] + expected body shape
488
+ - DB round-trip confirmed: persisted record matches submitted payload
489
+ - Unauthenticated request → 401
490
+
491
+ Acceptance — BDD Scenarios (REQUIRED — FEAT + cross-boundary):
492
+
493
+ Scenario: Happy-path round-trip
494
+ Given an authenticated user with [role]
495
+ When [METHOD] /api/[resource] is called with valid payload
496
+ Then [expected status] response with {id, [fields]}
497
+ And DB row exists in [table] matching the submitted payload
498
+
499
+ Scenario: Auth rejection
500
+ Given an unauthenticated caller
501
+ When [METHOD] /api/[resource] is called
502
+ Then 401 Unauthorized with no side effects in DB
503
+
504
+ Acceptance — Integration Flow (REQUIRED — cross-service):
505
+
506
+ POST /api/[resource] → [UseCase].execute() → [Repository].save() → DB: [table]
507
+ Given authenticated request arrives at [Controller]
508
+ When use case validates + persists via repository
509
+ Then DB row committed; response body matches [[contracts/[repo].contract.md#Response]]
510
+ And domain event [EventName] published (if applicable)
511
+
512
+ Non-Go:
513
+ - Performance / load testing → separate task
514
+ - UI E2E / Playwright flows → separate TASK-NNN
515
+ ```
516
+
517
+ ---
518
+
519
+ ## Discovered-Task Reconciliation (operation: reconcile)
520
+
521
+ Used by the reconcile operation over the discovered ledger at a round boundary. This is a pure
522
+ reducer: read existing state + the ledger delta, emit new state. The pitch and the
523
+ upfront DDD layer are FROZEN — this mode never re-runs Phase 1–5.
524
+
525
+ **Single anchor, no branch.** Every reconciled task anchors `use_case_refs` to exactly one
526
+ trust source: a UC. There is no second coverage trust (no `scope`/`invariant` frontmatter on
527
+ tasks). Scope (Basecamp sense) maps onto a UC; an invariant lives *inside* the UC it came from.
528
+
529
+ **Reconcile loop:**
530
+ ```
531
+ 1. Verify the ledger belongs to this spec. A discovered ledger rarely carries a pitch_hash
532
+ (its frontmatter names the raw materials, e.g. source: shaping.md + breadboard.md).
533
+ So match on identity that DOES exist:
534
+ a. ledger.feature == run-state.feature (REQUIRED — mismatch → STOP)
535
+ b. if ledger carries pitch_hash, it must == run-state.pitch_hash
536
+ Neither resolvable → STOP: "ledger does not match this spec (different feature slug)."
537
+ 2. READ-ONLY for the frozen zone: domain-model, usecases/ (Steps), ux-behavior, contracts/.
538
+ 3. Parse ledger scopes → map each to its owning UC (S1→UC-…, S2→UC-…, S3→UC-…).
539
+ A ledger scope with no matching UC, and that introduces a NEW actor/action,
540
+ is NOT a discovered task — it is a shaping miss. STOP and escalate to PO.
541
+ (Spawning a new UC mid-cycle = silent re-shaping = anti-Shape-Up.)
542
+ 4. Parse discovered items under each scope's "Discovered" section:
543
+ [+] Keep → new task to generate
544
+ ~ / Cut → append a row to synthesis "Hammered Out (Cut)" — NO file
545
+ [ ] already has a file → skip
546
+ 5. For a Keep item that asserts a new invariant on its scope's UC:
547
+ APPEND an [INV-NN] line to that UC's ## Invariants section (append-only,
548
+ never touch Steps/Input/Output). Log the UC path in run-state.human_edited_files.
549
+ Generate the regression task with command-verifiable AC (see AC Trigger Matrix).
550
+ 6. Number new tasks by reading max id in tasks/_index.md and incrementing.
551
+ NEVER renumber existing tasks — that would break every depends_on wikilink the
552
+ executor has already resolved.
553
+ 7. Recompute `unlocks` across the WHOLE board from the depends_on inverse (Link-Field
554
+ Integrity above). This is the one write this mode makes to existing task files, and it
555
+ touches only that frontmatter field — a new task's depends_on MUST patch its
556
+ dependencies' unlocks in this same pass, or the edge ships asymmetric.
557
+ 8. Drift check (flag, never fix): for each scope with a T0 verdict artifact
558
+ (t0/<scope>/t0/verdicts/*.json, round-ledger Hill table), compare the scope's mechanical
559
+ state against its tasks' local status/AC checkboxes. A FINISHED/T0-green scope whose
560
+ tasks still read `status: ready` (or vice versa) gets a ⚠️ drift marker on those rows in
561
+ the regenerated tasks/_index.md + one line in the gate output. Join on SCOPE, never on
562
+ task id — ids are per-machine and renumber on bootstrap. Status authority stays with
563
+ task-executor / the tech lead; this mode only surfaces the disagreement.
564
+ 9. Regenerate ONLY the derived files: tasks/_index.md, scope-summary.md, synthesis.md
565
+ (+ the unlocks frontmatter recompute from step 7).
566
+ 10. Run board-derive.mjs (Appetite Guard arithmetic, below); report overflow as a discovery.
567
+ ```
568
+
569
+ **Appetite Guard (forcing function, not a report):**
570
+ ```
571
+ appetite_hours = parse from pitch frontmatter (e.g. ~3 weeks → hours)
572
+ keep_hours = Σ estimated_hours over tasks with status ≠ cut
573
+ IF keep_hours > appetite_hours:
574
+ ⏸ print HAMMER prompt — surface the overflow + candidate cuts (must→nice),
575
+ then WAIT. Never auto-resolve. Options offered:
576
+ cut TASK-NNN | shrink the new task's scope | expand appetite (needs PO re-bet)
577
+ ```
578
+ This is scope hammering at the gate boundary — the overflow is surfaced, never silently absorbed.
579
+
580
+ ---
581
+
582
+ ## Scope Contracts
583
+
584
+ **Naming note:** the discovery ledger's "scope" sections (above) predate the formal Scope
585
+ Contract artifact below and name the same thing — a `discovery/ledger.md` scope heading MUST
586
+ match an existing `scope_id` from a `scopes/<scope-id>.md` contract, or (a Keep item
587
+ introducing new flow) become the seed for a new one via a scope-architect remap order. One concept, two touch points.
588
+
589
+ **Import/flow slicing (PA1 countermeasure).** Build a lightweight import graph over the task
590
+ board's touched files: for each file a task writes, note what it imports and what imports it
591
+ (grep for `import .* from ['"](\.\.?/[^'"]+)`-class patterns is sufficient — a full AST parser
592
+ is an optimization, not a prerequisite, per design spec R6). Two files are in the same flow if
593
+ they sit on one call chain: a UI component → the API route it calls → the use case that route
594
+ invokes → the repository that use case depends on. Group tasks by flow, not by which top-level
595
+ directory their file lives in. A scope whose `allowed_file_substrate` is entirely `apps/web/**`
596
+ or entirely `apps/api/**` with no cross-layer flow is the PA1 failure mode — re-slice it.
597
+
598
+ **Scope contract schema** (written to `scopes/<scope-id>.md`, one file per scope):
599
+ ```json
600
+ {
601
+ "scope_id": "cart-creation",
602
+ "topology_type": "ICEBERG",
603
+ "business_goal": "Shopper can create a cart and see it persist",
604
+ "allowed_file_substrate": ["apps/web/cart/*.tsx", "apps/api/cart/*.ts", "packages/domain/cart/*.ts"],
605
+ "shared_substrate": ["packages/shared/http.ts"],
606
+ "discovered_tasks_pool": [],
607
+ "e2e_verification_fixtures": ["fixtures/cart-create.spec.ts"],
608
+ "affordance_manifest": {
609
+ "interactive_elements": [{ "test_id": "add-to-cart-btn", "role": "button" }],
610
+ "required_states": ["idle", "loading", "success", "error", "empty"]
611
+ },
612
+ "hill_phase": "UPHILL_UNKNOWN",
613
+ "superseded_by": null
614
+ }
615
+ ```
616
+ `hill_phase` is always written `UPHILL_UNKNOWN` at generation time — it is derived later from
617
+ mechanical T0/T1/seesaw facts, never declared by `ba` (design spec DD-10). `superseded_by` stays
618
+ `null` until a scope-architect split-scope order retires this contract in favor of its replacements.
619
+
620
+ **PA2 size lint:** a scope whose `allowed_file_substrate` glob set resolves to more than ~15
621
+ files gets a ⚠️ from spec-lint PA2 (hard-cap configurable via pitch frontmatter `scope_size_cap`, default
622
+ 15). A CHOWDER scope is exempt by design (it is the deliberate strays bucket) but still gets
623
+ flagged if it grows past 2x the cap — that usually means real flows are hiding in it unsliced.