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.
- package/.claude/settings.local.example.json +20 -0
- package/.claude-plugin/marketplace.json +16 -0
- package/.claude-plugin/plugin.json +18 -0
- package/.env.shapeup.example +14 -0
- package/AGENTS.md +133 -0
- package/LICENSE +21 -0
- package/README.md +362 -0
- package/SECURITY.md +72 -0
- package/bin/init.mjs +329 -0
- package/commands/build.md +14 -0
- package/commands/eval.md +15 -0
- package/commands/hammer.md +12 -0
- package/commands/orient.md +11 -0
- package/commands/qa.md +12 -0
- package/commands/retro.md +13 -0
- package/commands/scopes.md +14 -0
- package/commands/shape.md +12 -0
- package/commands/ship.md +53 -0
- package/commands/wire.md +11 -0
- package/hooks/anti-rationalization.mjs +244 -0
- package/hooks/compact-snapshot.mjs +47 -0
- package/hooks/gate-deadline.mjs +151 -0
- package/hooks/gate-intake.mjs +110 -0
- package/hooks/gate-l2.mjs +161 -0
- package/hooks/gate-zerowork.mjs +264 -0
- package/hooks/hooks.json +118 -0
- package/hooks/lib/decision.mjs +183 -0
- package/hooks/safety-spine.mjs +296 -0
- package/hooks/sandbox-guard.mjs +172 -0
- package/hooks/session-rehydrate.mjs +109 -0
- package/hooks/slop-cleaner.mjs +176 -0
- package/oracles/_shared.mjs +46 -0
- package/oracles/http-oracle.mjs +155 -0
- package/oracles/index.mjs +36 -0
- package/oracles/process-oracle.mjs +146 -0
- package/oracles/snapshot-oracle.mjs +119 -0
- package/oracles/test-oracle.mjs +138 -0
- package/package.json +49 -0
- package/skills/advisor-protocol/SKILL.md +171 -0
- package/skills/ba-pitch-analyzer/SKILL.md +175 -0
- package/skills/ba-pitch-analyzer/assets/templates/_index.tmpl.md +84 -0
- package/skills/ba-pitch-analyzer/assets/templates/api-feasibility.tmpl.md +65 -0
- package/skills/ba-pitch-analyzer/assets/templates/assess-report.tmpl.md +127 -0
- package/skills/ba-pitch-analyzer/assets/templates/contracts/be-service.contract.tmpl.md +62 -0
- package/skills/ba-pitch-analyzer/assets/templates/contracts/offline-storage.contract.tmpl.md +92 -0
- package/skills/ba-pitch-analyzer/assets/templates/contracts/third-party-api.contract.tmpl.md +66 -0
- package/skills/ba-pitch-analyzer/assets/templates/cross-context/context-map.tmpl.md +64 -0
- package/skills/ba-pitch-analyzer/assets/templates/cross-context/event-choreography.tmpl.md +77 -0
- package/skills/ba-pitch-analyzer/assets/templates/cross-context/migration-plan.tmpl.md +104 -0
- package/skills/ba-pitch-analyzer/assets/templates/cross-context/team-handoff.tmpl.md +79 -0
- package/skills/ba-pitch-analyzer/assets/templates/domain-model.tmpl.md +79 -0
- package/skills/ba-pitch-analyzer/assets/templates/feedback.tmpl.md +87 -0
- package/skills/ba-pitch-analyzer/assets/templates/integration.tmpl.md +64 -0
- package/skills/ba-pitch-analyzer/assets/templates/run-state.tmpl.md +77 -0
- package/skills/ba-pitch-analyzer/assets/templates/scope-summary.tmpl.md +105 -0
- package/skills/ba-pitch-analyzer/assets/templates/synthesis.tmpl.md +215 -0
- package/skills/ba-pitch-analyzer/assets/templates/task-board.tmpl.md +35 -0
- package/skills/ba-pitch-analyzer/assets/templates/task-spike.tmpl.md +86 -0
- package/skills/ba-pitch-analyzer/assets/templates/task.tmpl.md +84 -0
- package/skills/ba-pitch-analyzer/assets/templates/usecase.tmpl.md +105 -0
- package/skills/ba-pitch-analyzer/assets/templates/ux-behavior.tmpl.md +65 -0
- package/skills/ba-pitch-analyzer/references/contract-patterns.md +152 -0
- package/skills/ba-pitch-analyzer/references/ddd-patterns.md +173 -0
- package/skills/ba-pitch-analyzer/references/doc-schemas.md +275 -0
- package/skills/ba-pitch-analyzer/references/integration-analysis.md +152 -0
- package/skills/ba-pitch-analyzer/references/task-generation.md +623 -0
- package/skills/ba-pitch-analyzer/references/test-surface.md +102 -0
- package/skills/ba-pitch-analyzer/references/ux-behavior-patterns.md +269 -0
- package/skills/ba-pitch-analyzer/scripts/board-derive.mjs +241 -0
- package/skills/ba-pitch-analyzer/scripts/spec-lint.mjs +239 -0
- package/skills/coach/SKILL.md +202 -0
- package/skills/orient/SKILL.md +262 -0
- package/skills/qa-edge-hunter/SKILL.md +369 -0
- package/skills/scope-architect/SKILL.md +111 -0
- package/skills/scope-hammer/SKILL.md +186 -0
- package/skills/shapeup/SKILL.md +403 -0
- package/skills/shapeup/resources/breadboard-reflection.md +88 -0
- package/skills/shapeup/resources/breadboarding.md +334 -0
- package/skills/shapeup/resources/context-compaction.md +240 -0
- package/skills/shapeup/resources/framing-doc.md +92 -0
- package/skills/shapeup/resources/kickoff-doc.md +95 -0
- package/skills/shapeup/resources/shaping.md +194 -0
- package/skills/shapeup/resources/spike.md +86 -0
- package/skills/solution-architect/SKILL.md +129 -0
- package/skills/spec-evaluator/README.md +93 -0
- package/skills/spec-evaluator/SKILL.md +213 -0
- package/skills/spec-evaluator/references/anti-leniency.md +50 -0
- package/skills/spec-evaluator/references/dimension-contract.md +126 -0
- package/skills/spec-evaluator/references/dimensions/_registry.md +40 -0
- package/skills/spec-evaluator/references/dimensions/completeness.md +102 -0
- package/skills/spec-evaluator/references/dimensions/integration.md +129 -0
- package/skills/spec-evaluator/references/dimensions/performance.md +48 -0
- package/skills/spec-evaluator/references/dimensions/security.md +60 -0
- package/skills/spec-evaluator/references/dimensions/spec-conformance.md +134 -0
- package/skills/spec-evaluator/references/dimensions/tdd-surface.md +110 -0
- package/skills/spec-evaluator/references/dimensions/test-surface-conformance.md +100 -0
- package/skills/spec-evaluator/references/probing.md +177 -0
- package/skills/spec-evaluator/references/report-schema.md +101 -0
- package/skills/spec-evaluator/references/verdict-ledger.md +92 -0
- package/skills/spec-evaluator/scripts/verdict-ledger.mjs +166 -0
- package/skills/task-executor/SKILL.md +194 -0
- package/skills/tech-lead/README.md +71 -0
- package/skills/tech-lead/SKILL.md +459 -0
- package/skills/tech-lead/references/delegation.md +254 -0
- package/skills/tech-lead/references/gates.md +379 -0
- package/skills/tech-lead/references/invocation.md +45 -0
- package/skills/tech-lead/references/ledger-schema.md +214 -0
- package/skills/tech-lead/references/round-protocol.md +184 -0
- package/skills/tech-lead/references/state-model.md +66 -0
- package/skills/tech-lead/references/tiny-lane.md +52 -0
- package/skills/tech-lead/schemas/domain.schema.json +2294 -0
- package/skills/tech-lead/schemas/gate-answers.schema.json +92 -0
- package/skills/tech-lead/schemas/work-order.schema.json +21 -0
- package/skills/tech-lead/schemas/work-result.schema.json +40 -0
- package/skills/tech-lead/scripts/aegis-digest.mjs +124 -0
- package/skills/tech-lead/scripts/budget-check.mjs +156 -0
- package/skills/tech-lead/scripts/compile-order.mjs +518 -0
- package/skills/tech-lead/scripts/fit-check.mjs +196 -0
- package/skills/tech-lead/scripts/gate-answers.mjs +338 -0
- package/skills/tech-lead/scripts/ingest-result.mjs +270 -0
- package/skills/tech-lead/scripts/init-run.mjs +326 -0
- package/skills/tech-lead/scripts/lib/argv.mjs +224 -0
- package/skills/tech-lead/scripts/lib/contract-md.mjs +481 -0
- package/skills/tech-lead/scripts/lib/is-main.mjs +82 -0
- package/skills/tech-lead/scripts/lib/paths.mjs +278 -0
- package/skills/tech-lead/scripts/lib/ratchet-tree.mjs +112 -0
- package/skills/tech-lead/scripts/run-snapshot.mjs +269 -0
- package/skills/tech-lead/scripts/ship-report.mjs +275 -0
- package/skills/tech-lead/scripts/stats.mjs +423 -0
- package/skills/tech-lead/scripts/t0-verify.mjs +470 -0
- package/skills/tech-lead/scripts/trace-lint.mjs +367 -0
- package/skills/tech-lead/scripts/validate-envelope.mjs +283 -0
- package/skills/translator/README.md +66 -0
- package/skills/translator/SKILL.md +258 -0
- package/skills/translator/references/preservation-rules.md +102 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Contract Patterns — Repository Boundary Contracts
|
|
2
|
+
|
|
3
|
+
Reference for Phase 2b. Read before writing any `.contract.md` file.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Why Contracts Exist
|
|
8
|
+
|
|
9
|
+
Repository interfaces in `domain-model.md` define *what* a repository does.
|
|
10
|
+
Contract files define *exactly what shape* goes in and comes out.
|
|
11
|
+
|
|
12
|
+
A developer must be able to read a contract and answer:
|
|
13
|
+
- What exact fields do I send?
|
|
14
|
+
- What fields come back, and what are their types and invariants?
|
|
15
|
+
- What errors can occur, and how do I handle each one?
|
|
16
|
+
|
|
17
|
+
If any of those questions require opening another document, the contract is incomplete.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Determining Source Type
|
|
22
|
+
|
|
23
|
+
Read the pitch and domain model. For each repository interface, ask:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Is the data store internal to the monorepo?
|
|
27
|
+
├── YES and it's a database/file/local cache → offline-storage
|
|
28
|
+
└── YES and it's another service in the system → be-service
|
|
29
|
+
|
|
30
|
+
Is the data store an external vendor?
|
|
31
|
+
└── YES → third-party-api
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Use the corresponding template from `assets/templates/contracts/`.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Source Type: `be-service`
|
|
39
|
+
|
|
40
|
+
Use when: repository calls an internal HTTP service (same org, different service/package).
|
|
41
|
+
|
|
42
|
+
**Key contract fields:**
|
|
43
|
+
- HTTP method + path (exact, including path params)
|
|
44
|
+
- Auth header type — never leave this implicit
|
|
45
|
+
- Request body table with `Source` column tracing each field to a UC input or env var
|
|
46
|
+
- Response table per HTTP status code (200, 201, etc.)
|
|
47
|
+
- Error table with HTTP status, error code, UX action
|
|
48
|
+
|
|
49
|
+
**Contract is `status: confirmed` immediately** — Dev can verify from service code.
|
|
50
|
+
|
|
51
|
+
**Source column values:**
|
|
52
|
+
```
|
|
53
|
+
UC-[Name].input.[fieldName] ← from a use case input interface
|
|
54
|
+
env.[ENV_VAR_NAME] ← from environment config
|
|
55
|
+
session.[claim] ← from JWT/auth context (e.g. session.userId)
|
|
56
|
+
domain.[Aggregate].[field] ← derived from domain model state
|
|
57
|
+
computed:[formula] ← calculated by application layer before calling repo
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Source Type: `third-party-api`
|
|
63
|
+
|
|
64
|
+
Use when: repository calls an external vendor API or SDK (Framer, Stripe, Twilio, etc.).
|
|
65
|
+
|
|
66
|
+
**Key contract fields:**
|
|
67
|
+
- Same as be-service PLUS:
|
|
68
|
+
- `feasibility_ref: API-NN` — links to api-feasibility.md investigation block
|
|
69
|
+
- `spike_task` — links to the SPIKE task that will confirm this contract
|
|
70
|
+
- `status: speculative` — MUST remain speculative until SPIKE is done
|
|
71
|
+
- `⚠️ SPECULATIVE CONTRACT` header — visible warning to all readers
|
|
72
|
+
|
|
73
|
+
**⏳ TBD annotation format:**
|
|
74
|
+
```markdown
|
|
75
|
+
| pageId | string | ⏳ TBD — field name unverified | publishPage() |
|
|
76
|
+
```
|
|
77
|
+
Every `⏳ TBD` field must reference what needs to be confirmed.
|
|
78
|
+
|
|
79
|
+
**Transition to confirmed:**
|
|
80
|
+
When SPIKE task is complete, Dev:
|
|
81
|
+
1. Replaces all `⏳ TBD` entries with confirmed values + source URL
|
|
82
|
+
2. Changes `status: speculative` → `status: confirmed`
|
|
83
|
+
3. Removes `⚠️ SPECULATIVE CONTRACT` header
|
|
84
|
+
4. Fills `Post-SPIKE Update Log` table
|
|
85
|
+
5. Implementation tasks blocking annotation can now be removed
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Source Type: `offline-storage`
|
|
90
|
+
|
|
91
|
+
Use when: repository reads/writes to local device storage (SQLite, AsyncStorage, MMKV,
|
|
92
|
+
file system, or in-memory cache).
|
|
93
|
+
|
|
94
|
+
**Key contract fields:**
|
|
95
|
+
- Engine name (SQLite via Drizzle, AsyncStorage, MMKV, etc.)
|
|
96
|
+
- Table/key schema with column types, constraints, and migration version
|
|
97
|
+
- Migration version must match `domain-model.md` schema version — they stay in sync
|
|
98
|
+
- Conflict strategy — what happens on concurrent writes
|
|
99
|
+
- Null behavior for read methods — must be explicit (null vs empty array vs throw)
|
|
100
|
+
|
|
101
|
+
**Contract is `status: confirmed` immediately** — schema is defined in the spec.
|
|
102
|
+
|
|
103
|
+
**Null behavior rule:**
|
|
104
|
+
```
|
|
105
|
+
findById → returns null (never throws) when not found
|
|
106
|
+
findAll → returns [] (never null) when empty
|
|
107
|
+
write → throws StorageError (never returns null) on failure
|
|
108
|
+
```
|
|
109
|
+
These invariants must appear in the contract Error Cases table.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Contract Registry (`contracts/_index.md`)
|
|
114
|
+
|
|
115
|
+
Every contracts folder must have an `_index.md` registry:
|
|
116
|
+
|
|
117
|
+
```markdown
|
|
118
|
+
# Contract Registry — [feature-slug]
|
|
119
|
+
|
|
120
|
+
| Repository | Source Type | Service / Engine | Status | SPIKE Task |
|
|
121
|
+
|-----------|------------|-----------------|--------|------------|
|
|
122
|
+
| [[FramerPageRepository]] | third-party-api | Framer REST API | ⚠️ speculative | [[TASK-001]] |
|
|
123
|
+
| [[PageCacheRepository]] | offline-storage | SQLite/Drizzle | ✅ confirmed | — |
|
|
124
|
+
| [[UserSessionRepository]] | be-service | apps/api /auth | ✅ confirmed | — |
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This registry is the entry point for the spec-lint structure checks.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Contract → Task Traceability
|
|
132
|
+
|
|
133
|
+
Every implementation task that uses a repository MUST:
|
|
134
|
+
|
|
135
|
+
1. Reference the contract in the Context section:
|
|
136
|
+
```markdown
|
|
137
|
+
## Context
|
|
138
|
+
Implement FramerPageRepository per [[contracts/framer-page.contract.md]].
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
2. Include these three AC lines under Baseline:
|
|
142
|
+
```markdown
|
|
143
|
+
- [ ] Request shape matches [[contracts/framer-page.contract.md#Request]] table
|
|
144
|
+
- [ ] Response mapping matches [[contracts/framer-page.contract.md#Response]] table
|
|
145
|
+
- [ ] All error codes in contract Error Cases table are handled
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
3. If contract is still `speculative` at task-generation time:
|
|
149
|
+
```markdown
|
|
150
|
+
> ⏳ BLOCKED by [[TASK-001-spike-framer-feasibility]]
|
|
151
|
+
> Unblock condition: [[contracts/framer-page.contract.md]] has no remaining ⏳ TBD fields
|
|
152
|
+
```
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# DDD Patterns — Domain Modeling Guide
|
|
2
|
+
|
|
3
|
+
Reference for Phase 2 of the BA Pitch Analyzer. Read this before writing `domain-model.md`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Bounded Context Decision
|
|
8
|
+
|
|
9
|
+
Ask these questions from the pitch:
|
|
10
|
+
|
|
11
|
+
1. **Who owns the data lifecycle?** The context that creates AND deletes an entity owns it.
|
|
12
|
+
2. **What crosses a context boundary?** Cross-context = domain event or anti-corruption layer, never direct DB join.
|
|
13
|
+
3. **Does this pitch create a new context or extend existing?** Extending = add to existing `domain-model.md`. New = create new bounded context folder.
|
|
14
|
+
|
|
15
|
+
### Common Bounded Contexts (reference for monorepo)
|
|
16
|
+
|
|
17
|
+
| Context | Owns | Does NOT own |
|
|
18
|
+
|---------|------|-------------|
|
|
19
|
+
| `ordering` | Order, LineItem, Cart | Product catalog, Stock levels |
|
|
20
|
+
| `inventory` | StockItem, Warehouse | Order details |
|
|
21
|
+
| `identity` | User, Session, Role | Profile preferences |
|
|
22
|
+
| `payment` | Payment, PaymentAttempt | Order totals |
|
|
23
|
+
| `notification` | Message, Channel, Template | Business triggers |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Aggregate Design Rules
|
|
28
|
+
|
|
29
|
+
### Identifying Aggregate Roots
|
|
30
|
+
|
|
31
|
+
An entity is an Aggregate Root if:
|
|
32
|
+
- Other entities can't exist without it (LineItem can't exist without Order)
|
|
33
|
+
- It enforces invariants across child entities
|
|
34
|
+
- It's the entry point for all external writes
|
|
35
|
+
|
|
36
|
+
### Invariant Checklist
|
|
37
|
+
|
|
38
|
+
For every aggregate, document at least one invariant:
|
|
39
|
+
- `Order.totalAmount` must equal sum of `LineItem.subtotals`
|
|
40
|
+
- `Order` can only transition: `draft → placed → paid | failed`
|
|
41
|
+
- `PaymentAttempt` count must not exceed 3 per `Payment`
|
|
42
|
+
|
|
43
|
+
### Aggregate Size Heuristic
|
|
44
|
+
|
|
45
|
+
- **Too large:** aggregate has more than 3-4 entity types → split
|
|
46
|
+
- **Too small:** every operation needs to load 3 aggregates → merge
|
|
47
|
+
- **Right size:** one transaction boundary, one invariant set
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Value Object Patterns
|
|
52
|
+
|
|
53
|
+
Value Objects have no identity — equality is by value, not ID.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// ✅ Value Object — no ID field
|
|
57
|
+
class Money {
|
|
58
|
+
constructor(
|
|
59
|
+
readonly amount: number, // integer cents/VND — no float
|
|
60
|
+
readonly currency: 'VND' | 'USD'
|
|
61
|
+
) {
|
|
62
|
+
if (amount < 0) throw new DomainError('Money cannot be negative')
|
|
63
|
+
}
|
|
64
|
+
equals(other: Money): boolean {
|
|
65
|
+
return this.amount === other.amount && this.currency === other.currency
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ✅ Value Object — wraps primitive with validation
|
|
70
|
+
class OrderId {
|
|
71
|
+
constructor(readonly value: string) {
|
|
72
|
+
if (!isUUID(value)) throw new DomainError('Invalid OrderId')
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
When to use VO vs Entity:
|
|
78
|
+
- Has identity that persists across mutations → Entity
|
|
79
|
+
- Replaced entirely when changed → Value Object
|
|
80
|
+
- Examples of VOs: Money, Address, Email, PhoneNumber, DateRange, Coordinates
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Domain Event Patterns
|
|
85
|
+
|
|
86
|
+
### Naming Convention
|
|
87
|
+
`[AggregateRoot][PastTense]` — e.g., `OrderPlaced`, `PaymentConfirmed`, `UserRegistered`
|
|
88
|
+
|
|
89
|
+
### Event Payload Design
|
|
90
|
+
```typescript
|
|
91
|
+
// Include: what happened, when, who, minimal context for consumers
|
|
92
|
+
interface OrderPlaced {
|
|
93
|
+
eventId: string // UUID
|
|
94
|
+
occurredAt: Date
|
|
95
|
+
orderId: string
|
|
96
|
+
customerId: string
|
|
97
|
+
totalAmount: number
|
|
98
|
+
lineItemCount: number
|
|
99
|
+
// Do NOT include: full line items, customer PII, computed fields
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Event → Consumer Mapping Table Format
|
|
104
|
+
```
|
|
105
|
+
| Event | Emitted By | Consumed By | Purpose |
|
|
106
|
+
|-------|------------|-------------|---------|
|
|
107
|
+
| OrderPlaced | ordering ctx | inventory ctx | Reserve stock |
|
|
108
|
+
| OrderPlaced | ordering ctx | notification ctx | Send confirmation |
|
|
109
|
+
| PaymentConfirmed | payment ctx | ordering ctx | Transition order to paid |
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Repository Interface Rules
|
|
115
|
+
|
|
116
|
+
1. **Return domain objects, not DTOs** — `findById` returns `Order`, not `OrderRow`
|
|
117
|
+
2. **No query logic in interface** — business queries get named methods
|
|
118
|
+
3. **Async always** — all methods return `Promise<T>`
|
|
119
|
+
4. **Soft-delete pattern** — if entities are soft-deleted, interface reflects it
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// ✅ Well-designed repository interface
|
|
123
|
+
interface OrderRepository {
|
|
124
|
+
findById(id: OrderId): Promise<Order | null>
|
|
125
|
+
findByCustomer(customerId: string, limit: number): Promise<Order[]>
|
|
126
|
+
findPendingPayment(): Promise<Order[]> // named business query
|
|
127
|
+
save(order: Order): Promise<void> // insert or update
|
|
128
|
+
// NO: findAll(), count(), rawQuery()
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Anti-Patterns to Flag in Pitch Analysis
|
|
135
|
+
|
|
136
|
+
When reading a pitch, look for these and document them as **Rabbit Holes**:
|
|
137
|
+
|
|
138
|
+
| Anti-Pattern | Symptom | Better Approach |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| Shared DB table | "Both services read the orders table" | Domain events + projection |
|
|
141
|
+
| Aggregate too large | "Order contains everything incl. shipping" | Split shipping to own aggregate |
|
|
142
|
+
| Anemic domain | "Service layer contains all business logic" | Move invariants into aggregate |
|
|
143
|
+
| Primitive obsession | `userId: string` everywhere | Typed `UserId` value object |
|
|
144
|
+
| Missing domain event | State change with no downstream consumer | Add event even if no consumer yet |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Template: Aggregate Section in domain-model.md
|
|
149
|
+
|
|
150
|
+
```markdown
|
|
151
|
+
## Aggregate: [Name]
|
|
152
|
+
|
|
153
|
+
**Aggregate Root:** `[EntityName]`
|
|
154
|
+
**Invariants:**
|
|
155
|
+
- [invariant 1]
|
|
156
|
+
- [invariant 2]
|
|
157
|
+
|
|
158
|
+
\`\`\`
|
|
159
|
+
[EntityName] (Aggregate Root)
|
|
160
|
+
├── id: [EntityName]Id (VO)
|
|
161
|
+
├── status: [EntityName]Status ([values])
|
|
162
|
+
├── [field]: [Type]
|
|
163
|
+
└── [childEntity]: [ChildEntity][] (Entity, owned)
|
|
164
|
+
├── id: [ChildEntity]Id
|
|
165
|
+
└── [field]: [Type]
|
|
166
|
+
\`\`\`
|
|
167
|
+
|
|
168
|
+
**State Transitions:**
|
|
169
|
+
\`\`\`
|
|
170
|
+
draft ──place()──► placed ──confirm()──► confirmed
|
|
171
|
+
└───fail()────► failed
|
|
172
|
+
\`\`\`
|
|
173
|
+
```
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Document Schemas — Frontmatter Taxonomy
|
|
2
|
+
|
|
3
|
+
Shared vocabulary used across all document types. Every field listed here uses the same
|
|
4
|
+
values so wikilinks and tag queries work consistently across the spec tree.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Shared Taxonomy Fields
|
|
9
|
+
|
|
10
|
+
```yaml
|
|
11
|
+
# TYPE — document classification
|
|
12
|
+
type: pitch | domain-model | ux-spec | usecase | integration | task
|
|
13
|
+
| assess-report | run-state | context-map | event-choreography
|
|
14
|
+
| migration-plan | team-handoff | synthesis
|
|
15
|
+
|
|
16
|
+
# FEATURE — namespace for the entire spec tree (kebab-case)
|
|
17
|
+
feature: checkout-vnpay
|
|
18
|
+
|
|
19
|
+
# LENS — architectural perspective used when generating this document
|
|
20
|
+
# Determines which phases ran and which documents are authoritative
|
|
21
|
+
lens: lite | standard | cross-context
|
|
22
|
+
|
|
23
|
+
# BOUNDED CONTEXT — DDD context that owns this feature
|
|
24
|
+
bounded_context: ordering | inventory | identity | notification | payment
|
|
25
|
+
|
|
26
|
+
# STATUS — lifecycle state
|
|
27
|
+
status: draft | ready | in-progress | blocked | done
|
|
28
|
+
|
|
29
|
+
# ENTITIES — domain entities referenced or defined
|
|
30
|
+
entities: [Order, LineItem, Payment]
|
|
31
|
+
|
|
32
|
+
# REPOSITORIES — repository interfaces referenced or defined
|
|
33
|
+
repositories: [OrderRepository, PaymentRepository]
|
|
34
|
+
|
|
35
|
+
# USECASES — use case IDs referenced (e.g. UC-CreateOrder)
|
|
36
|
+
usecases: [UC-CreateOrder, UC-InitiatePayment]
|
|
37
|
+
|
|
38
|
+
# TAGS — free-form, for cross-cutting search
|
|
39
|
+
tags: [payment, vnpay, checkout, mobile]
|
|
40
|
+
|
|
41
|
+
# DEPENDS_ON — wikilinks to docs this doc depends on
|
|
42
|
+
depends_on: ["[[_index]]", "[[domain-model]]"]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Lens-Aware Document Authority
|
|
48
|
+
|
|
49
|
+
Each lens has a **central document** — the most detailed and authoritative spec.
|
|
50
|
+
Other documents exist but are less exhaustive.
|
|
51
|
+
|
|
52
|
+
| Lens | Central Document | Secondary | Skipped |
|
|
53
|
+
|------|-----------------|-----------|---------|
|
|
54
|
+
| `lite` | `ux-behavior.md` | domain-model, usecases, tasks | contracts/, integration.md, synthesis S-02/S-03 |
|
|
55
|
+
| `standard` | `contracts/` | domain-model, ux-behavior, usecases, integration, synthesis full | — |
|
|
56
|
+
| `cross-context` | `_cross-context/` | all standard docs per context | — |
|
|
57
|
+
|
|
58
|
+
When upgrading `lite → standard`:
|
|
59
|
+
- `ux-behavior.md` stays authoritative for screen specs
|
|
60
|
+
- Use cases are **reconciled** (API boundary sub-section added, not overwritten)
|
|
61
|
+
- `contracts/` generated from reconciled UC API sub-sections
|
|
62
|
+
- Reconciled files reported in the WorkResult's `deviations[]`
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Schema Per Document Type
|
|
67
|
+
|
|
68
|
+
### `_index.md` — Pitch Digest
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
---
|
|
72
|
+
type: pitch
|
|
73
|
+
feature: [slug]
|
|
74
|
+
appetite: "6 weeks" # Shape Up time box
|
|
75
|
+
status: draft | ready
|
|
76
|
+
bounded_context: [context]
|
|
77
|
+
entities: []
|
|
78
|
+
tags: []
|
|
79
|
+
---
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Required sections: Problem, Appetite, Boundaries, Breadboarding, Rabbit Holes, Document Map
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### `domain-model.md` — DDD Model
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
---
|
|
90
|
+
type: domain-model
|
|
91
|
+
feature: [slug]
|
|
92
|
+
bounded_context: [context]
|
|
93
|
+
entities: []
|
|
94
|
+
value_objects: []
|
|
95
|
+
domain_events: []
|
|
96
|
+
repositories: []
|
|
97
|
+
tags: [ddd]
|
|
98
|
+
depends_on: ["[[_index]]"]
|
|
99
|
+
status: draft | ready
|
|
100
|
+
---
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Required sections: Bounded Context, Aggregates (one subsection each), Value Objects table,
|
|
104
|
+
Domain Events table, Repository Interfaces (TypeScript interface blocks)
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### `ux-behavior.md` — UX Spec
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
---
|
|
112
|
+
type: ux-spec
|
|
113
|
+
feature: [slug]
|
|
114
|
+
entities: []
|
|
115
|
+
usecases: []
|
|
116
|
+
screens: [] # list of screen/view names
|
|
117
|
+
figma_link: [Figma Project URL] # Optional: Link to the overall Figma file
|
|
118
|
+
tags: [ux]
|
|
119
|
+
depends_on: ["[[domain-model]]"]
|
|
120
|
+
status: draft | ready
|
|
121
|
+
---
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Required sections: Screen Flow (ASCII diagram), one section per Screen with:
|
|
125
|
+
- **Figma Frame**: [Figma Frame Node ID / URL] (Mandatory if Figma mocks exist)
|
|
126
|
+
- **Visual & Layout Specs**: Flex/Grid structure, spacing, alignment rules, and desktop/mobile responsiveness
|
|
127
|
+
- **Design Tokens**: Specific CSS variables or Tailwind classes used for background, borders, fonts, and actions
|
|
128
|
+
- **States table**, **Behavior Rules list**, **Error States table**
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### `usecases/UC-[Name].md` — Use Case
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
---
|
|
137
|
+
type: usecase
|
|
138
|
+
feature: [slug]
|
|
139
|
+
id: UC-[Name] # PascalCase, must be unique in feature
|
|
140
|
+
bounded_context: [context]
|
|
141
|
+
actor: Customer | Admin | System
|
|
142
|
+
entities: []
|
|
143
|
+
repositories: []
|
|
144
|
+
domain_events_emitted: []
|
|
145
|
+
tags: []
|
|
146
|
+
depends_on: ["[[domain-model]]", "[[ux-behavior]]"]
|
|
147
|
+
status: draft | ready
|
|
148
|
+
---
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
> **No `related_tasks` (retired in v3.3).** UC→task linkage is one-directional by design:
|
|
152
|
+
> tasks carry `use_case_refs` (LOCAL, per-machine board); reverse lookup is always computed
|
|
153
|
+
> live (synthesis S-01, audit coverage) — never stored on the committed UC. Rule: **never
|
|
154
|
+
> declare a bidirectional field across the committed/local boundary** — task IDs renumber
|
|
155
|
+
> per machine (generate-board bootstrap), so a stored back-link is wrong on every machine
|
|
156
|
+
> but one. Tolerate (ignore) the field on pre-v3.3 specs; do not emit or update it.
|
|
157
|
+
|
|
158
|
+
Required sections: Summary (1 sentence), Preconditions, Input (TS interface), Steps
|
|
159
|
+
(numbered application layer), Output (TS interface), Error Cases table, Integration Points
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### `usecases/_index.md` — Use Case Index
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
---
|
|
167
|
+
type: usecase-index
|
|
168
|
+
feature: [slug]
|
|
169
|
+
tags: []
|
|
170
|
+
---
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Required: table of all UCs with columns: ID | Title | Actor | Status | Depends On
|
|
174
|
+
Plus: dependency diagram (ASCII or mermaid)
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
### `integration.md` — System Impact Map
|
|
179
|
+
|
|
180
|
+
```yaml
|
|
181
|
+
---
|
|
182
|
+
type: integration
|
|
183
|
+
feature: [slug]
|
|
184
|
+
affected_services: [] # internal services, external APIs
|
|
185
|
+
domain_events_consumed: []
|
|
186
|
+
domain_events_produced: []
|
|
187
|
+
tags: [integration]
|
|
188
|
+
depends_on: ["[[domain-model]]", "[[usecases/_index]]"]
|
|
189
|
+
status: draft | ready
|
|
190
|
+
---
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Required sections: Impact Summary, one subsection per affected system with:
|
|
194
|
+
Data Flow, Trigger, Risk, Mitigation
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### `tasks/TASK-NNN-slug.md` — Task
|
|
199
|
+
|
|
200
|
+
> **Locality (v3.2):** written under the LOCAL gitignored root
|
|
201
|
+
> (`.shapeup/<slug>/tasks/`), not `spec_folder`. Every other schema on this page is a
|
|
202
|
+
> SHARED, committed document. Tier direction: the task file must fully anchor INTO the
|
|
203
|
+
> committed spec (`use_case_refs`, `linked_docs`, the Context wikilink — LOCAL→SHARED);
|
|
204
|
+
> no SHARED doc ever wikilinks `[[tasks/...]]` back (spec-lint TIER-DIRECTION red).
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
---
|
|
208
|
+
type: task
|
|
209
|
+
feature: [slug]
|
|
210
|
+
id: TASK-[NNN] # zero-padded 3 digits: TASK-001
|
|
211
|
+
title: "[imperative verb phrase]"
|
|
212
|
+
lens: lite | standard # inherited from run-state
|
|
213
|
+
package: apps/api | apps/web | packages/shared | apps/mobile
|
|
214
|
+
status: ready | in-progress | blocked | done
|
|
215
|
+
priority: [integer] # 1 = highest
|
|
216
|
+
depends_on: [] # other TASK IDs: [TASK-001, TASK-002] — the AUTHORITATIVE edge
|
|
217
|
+
unlocks: [] # DERIVED — inverse of the board's depends_on graph, recomputed
|
|
218
|
+
# on every board write; never hand-authored (v3.3)
|
|
219
|
+
use_case_refs: [] # UC IDs this task implements: [UC-CreateOrder]
|
|
220
|
+
entities: []
|
|
221
|
+
repositories: []
|
|
222
|
+
linked_docs: [] # [[usecase]], [[domain-model#section]]
|
|
223
|
+
estimated_hours: [number]
|
|
224
|
+
tags: []
|
|
225
|
+
---
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Required sections: Context (2-3 sentences with wikilink to spec), Acceptance Criteria
|
|
229
|
+
(checkboxes, all verifiable by command), Implementation Notes (optional hints),
|
|
230
|
+
Non-go (what is explicitly NOT in scope for this task)
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
### `tasks/_index.md` — Task Board
|
|
235
|
+
|
|
236
|
+
```yaml
|
|
237
|
+
---
|
|
238
|
+
type: task-board
|
|
239
|
+
feature: [slug]
|
|
240
|
+
tags: []
|
|
241
|
+
---
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Required: table with columns: ID | Title | Package | Status | Priority | Depends On | Est.Hours
|
|
245
|
+
Sorted by priority ascending. Status uses emoji: ⬜ ready | 🔄 in-progress | 🚫 blocked | ✅ done
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Wikilink Conventions
|
|
250
|
+
|
|
251
|
+
```markdown
|
|
252
|
+
[[_index]] # pitch digest
|
|
253
|
+
[[domain-model]] # full domain model doc
|
|
254
|
+
[[domain-model#Aggregate-Order]] # specific section
|
|
255
|
+
[[ux-behavior#CheckoutScreen]] # specific screen
|
|
256
|
+
[[usecases/UC-CreateOrder]] # specific use case
|
|
257
|
+
[[usecases/_index]] # use case index
|
|
258
|
+
[[integration]] # integration map
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Always use wikilinks (double brackets), never relative paths like `../domain-model.md`.
|
|
262
|
+
|
|
263
|
+
**Tier-direction rule.** Persisted links flow **LOCAL → SHARED only**:
|
|
264
|
+
|
|
265
|
+
- A LOCAL task file must **fully anchor** into the committed spec — `use_case_refs`
|
|
266
|
+
(single-anchor rule), `linked_docs`, and the Context wikilink. spec-lint flags a task
|
|
267
|
+
with an empty or unresolvable anchor as a red `UC-ANCHOR` finding.
|
|
268
|
+
- A SHARED spec doc links only its committed siblings (the list above) and **never**
|
|
269
|
+
`[[tasks/...]]`: task ids are machine-local (boards regenerate and renumber) and
|
|
270
|
+
`.shapeup/` is gitignored, so a committed task link dangles on every fresh clone.
|
|
271
|
+
spec-lint flags it as a red `TIER-DIRECTION` finding. Coverage views (synthesis
|
|
272
|
+
traceability) record derived counts/status, not task ids.
|
|
273
|
+
- `[[tasks/...]]` wikilinks are valid only inside LOCAL documents (task files, the board,
|
|
274
|
+
EVAL reports), where they resolve against the LOCAL root (`.shapeup/<slug>/`);
|
|
275
|
+
every wikilink in a SHARED doc stays `spec_folder`-relative.
|