sdg-agents 1.0.5
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/LICENSE +15 -0
- package/README.md +161 -0
- package/package.json +88 -0
- package/src/assets/dev-guides/agent-deep-flow.md +139 -0
- package/src/assets/dev-guides/prompt-tracks/00-lite-mode/01-project-scope-and-mvp.md +45 -0
- package/src/assets/dev-guides/prompt-tracks/00-lite-mode/02-stack-and-data-model.md +56 -0
- package/src/assets/dev-guides/prompt-tracks/00-lite-mode/03-external-integrations.md +44 -0
- package/src/assets/dev-guides/prompt-tracks/00-lite-mode/04-launch-checklist.md +26 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/01-project-vision.md +77 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/02-problem-definition.md +63 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/03-success-metrics.md +48 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/05-engineering-culture-and-silos.md +41 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/06-foundation-approval.md +57 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/01-tech-stack.md +62 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/02-local-environment.md +50 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/03-version-control-and-tracking.md +65 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/04-hello-world-validation.md +37 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/05-setup-approval.md +61 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/01-folder-structure.md +49 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/02-design-patterns.md +59 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/03-apis-and-communication.md +55 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/04-security-and-access.md +63 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/05-security-audit.md +64 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/06-design-system-ux.md +72 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/07-ai-strategy.md +72 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/08-observability.md +65 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/09-technical-documentation.md +64 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/10-infrastructure-and-costs.md +40 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/11-data-privacy-compliance.md +41 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/12-performance-and-scale.md +46 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/13-disaster-recovery.md +39 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/14-architecture-approval.md +81 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/03-delivery/01-ci-cd-pipeline.md +49 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/03-delivery/02-quality-standards.md +46 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/03-delivery/03-delivery-approval.md +58 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/00-feature-template.md +30 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/01-context-and-scope.md +46 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/02-business-rules.md +39 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/03-architecture-and-design.md +50 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/04-testing-strategy.md +48 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/05-implementation-plan.md +45 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/06-feature-approval.md +46 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/05-evolution/01-rfcs-and-changes.md +63 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/05-evolution/02-incident-postmortems.md +64 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/05-evolution/03-adr-template.md +66 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/00-foundation/01-legacy-vision.md +73 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/00-foundation/02-foundation-approval.md +53 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/01-tech-debt-inventory.md +55 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/02-security-audit.md +63 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/03-regression-baseline.md +49 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/04-analysis-approval.md +65 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/01-modernization-approach.md +60 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/02-versioning-and-coexistence.md +57 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/03-new-demands-triage.md +41 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/04-strategy-approval.md +56 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/03-refactor/01-cleanup-backlog.md +45 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/03-refactor/02-refactor-approval.md +53 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/04-migration/01-migration-roadmap.md +47 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/04-migration/02-data-sync-strategy.md +56 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/04-migration/03-migration-approval.md +55 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/05-sunset/01-decommission-plan.md +47 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/05-sunset/02-sunset-approval.md +60 -0
- package/src/assets/dev-guides/prompt-tracks/README.md +144 -0
- package/src/assets/dev-guides/software-development-lifecycle-sdlc.md +147 -0
- package/src/assets/dev-guides/spec-driven-dev-guide.md +81 -0
- package/src/assets/dev-guides/ui-prompt-guide.md +181 -0
- package/src/assets/img/sdg-agents-icon-dark.svg +55 -0
- package/src/assets/img/sdg-agents-icon-light.svg +55 -0
- package/src/assets/img/sdg-agents-menu-v1.png +0 -0
- package/src/assets/img/sdg-icon.svg +110 -0
- package/src/assets/instructions/commands/sdg-docs.md +69 -0
- package/src/assets/instructions/commands/sdg-feat.md +45 -0
- package/src/assets/instructions/commands/sdg-fix.md +41 -0
- package/src/assets/instructions/commands/sdg-land.md +128 -0
- package/src/assets/instructions/competencies/backend.md +353 -0
- package/src/assets/instructions/competencies/frontend.md +439 -0
- package/src/assets/instructions/core/agent-roles.md +104 -0
- package/src/assets/instructions/core/api-design.md +65 -0
- package/src/assets/instructions/core/ci-cd.md +144 -0
- package/src/assets/instructions/core/cloud.md +63 -0
- package/src/assets/instructions/core/code-style.md +144 -0
- package/src/assets/instructions/core/containers.md +115 -0
- package/src/assets/instructions/core/data-access.md +119 -0
- package/src/assets/instructions/core/engineering-standards.md +502 -0
- package/src/assets/instructions/core/naming.md +136 -0
- package/src/assets/instructions/core/observability.md +73 -0
- package/src/assets/instructions/core/security-pipeline.md +209 -0
- package/src/assets/instructions/core/security.md +45 -0
- package/src/assets/instructions/core/sql-style.md +138 -0
- package/src/assets/instructions/core/staff-dna.md +72 -0
- package/src/assets/instructions/core/testing-principles.md +212 -0
- package/src/assets/instructions/core/ui/architecture.md +171 -0
- package/src/assets/instructions/core/ui/design-thinking.md +319 -0
- package/src/assets/instructions/core/ui/presets.md +200 -0
- package/src/assets/instructions/core/ui/standards.md +144 -0
- package/src/assets/instructions/core/writing-soul.md +82 -0
- package/src/assets/instructions/flavors/legacy/principles.md +64 -0
- package/src/assets/instructions/flavors/lite/principles.md +39 -0
- package/src/assets/instructions/flavors/mvc/principles.md +124 -0
- package/src/assets/instructions/flavors/vertical-slice/principles.md +178 -0
- package/src/assets/instructions/idioms/csharp/patterns.md +367 -0
- package/src/assets/instructions/idioms/flutter/patterns.md +97 -0
- package/src/assets/instructions/idioms/go/patterns.md +193 -0
- package/src/assets/instructions/idioms/java/patterns.md +233 -0
- package/src/assets/instructions/idioms/javascript/patterns.md +223 -0
- package/src/assets/instructions/idioms/kotlin/patterns.md +94 -0
- package/src/assets/instructions/idioms/python/patterns.md +185 -0
- package/src/assets/instructions/idioms/rust/patterns.md +189 -0
- package/src/assets/instructions/idioms/scripts/patterns.md +81 -0
- package/src/assets/instructions/idioms/sql/patterns.md +109 -0
- package/src/assets/instructions/idioms/swift/patterns.md +97 -0
- package/src/assets/instructions/idioms/typescript/patterns.md +304 -0
- package/src/assets/instructions/idioms/vbnet/patterns.md +96 -0
- package/src/assets/instructions/idioms/vbnet-legacy/patterns.md +104 -0
- package/src/assets/instructions/templates/workflow.md +244 -0
- package/src/assets/instructions/workflows/governance.md +162 -0
- package/src/engine/bin/add-idiom.mjs +186 -0
- package/src/engine/bin/index.mjs +226 -0
- package/src/engine/config/stack-display.mjs +75 -0
- package/src/engine/config/stack-versions.mjs +76 -0
- package/src/engine/lib/cli-parser.mjs +80 -0
- package/src/engine/lib/display-utils.mjs +20 -0
- package/src/engine/lib/fs-utils.mjs +99 -0
- package/src/engine/lib/instruction-assembler.mjs +487 -0
- package/src/engine/lib/manifest-utils.mjs +128 -0
- package/src/engine/lib/prompt-utils.mjs +137 -0
- package/src/engine/lib/result-utils.mjs +14 -0
- package/src/engine/lib/ruleset-injector.mjs +183 -0
- package/src/engine/lib/ui-utils.mjs +216 -0
- package/src/engine/lib/wizard.mjs +472 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Land Cycle — Project Inception
|
|
2
|
+
|
|
3
|
+
We are landing on a project: $ARGUMENTS. This command runs once, at the very beginning — before the first `feat:` — to turn a raw vision into a grounded, sequenced backlog.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What this cycle produces
|
|
8
|
+
|
|
9
|
+
| Output | Where |
|
|
10
|
+
| :--------------------------------------------------- | :----------------------- |
|
|
11
|
+
| `## Vision` section added to `context.md` | `.ai-backlog/context.md` |
|
|
12
|
+
| Ordered epics decomposed into `feat:` tasks | `.ai-backlog/tasks.md` |
|
|
13
|
+
| STOP — explicit approval required before any `feat:` | — |
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Phase: VISION
|
|
18
|
+
|
|
19
|
+
Parse the input prompt. Extract only what was explicitly stated — no assumptions, no embellishments.
|
|
20
|
+
|
|
21
|
+
Identify:
|
|
22
|
+
|
|
23
|
+
- **What** is being built (the product or system)
|
|
24
|
+
- **For whom** (the target user or operator)
|
|
25
|
+
- **The core problem** it solves (one sentence)
|
|
26
|
+
- **Signal** — greenfield (no existing code) or legacy (existing project)
|
|
27
|
+
|
|
28
|
+
If the prompt is too vague to extract these three points, ask one clarifying question before proceeding.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Phase: SURVEY (legacy only — skip if greenfield)
|
|
33
|
+
|
|
34
|
+
Read the project silently. Do not announce findings yet.
|
|
35
|
+
|
|
36
|
+
- Read `package.json`, `README.md`, entry points, top-level folder structure
|
|
37
|
+
- Run `git log --oneline -10` to understand recent activity
|
|
38
|
+
- Identify: stack, main architectural pattern, biggest pain points visible from code
|
|
39
|
+
|
|
40
|
+
If the project has no code at all, treat it as greenfield.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Phase: SCOPE
|
|
45
|
+
|
|
46
|
+
Define the MVP boundary. This is the most important step.
|
|
47
|
+
|
|
48
|
+
**Rules:**
|
|
49
|
+
|
|
50
|
+
- Constrain the vision to what is realistic for the first meaningful release
|
|
51
|
+
- Explicitly list what is **out of scope** (name it — vague exclusions don't count)
|
|
52
|
+
- For legacy projects: diagnosis always comes before new features
|
|
53
|
+
- Maximum **7 epics** — if you have more, merge or defer. A 20-item backlog is a liability, not a plan
|
|
54
|
+
|
|
55
|
+
Present the scope boundary before generating the backlog. If the vision is clearly overambitious, say so directly and propose a realistic MVP cut.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Phase: BACKLOG
|
|
60
|
+
|
|
61
|
+
Generate two outputs:
|
|
62
|
+
|
|
63
|
+
### 1. Update `context.md`
|
|
64
|
+
|
|
65
|
+
Add a `## Vision` section. **Never overwrite existing content** — only add or append.
|
|
66
|
+
|
|
67
|
+
```md
|
|
68
|
+
## Vision
|
|
69
|
+
|
|
70
|
+
**Product:** [what is being built]
|
|
71
|
+
**User:** [for whom]
|
|
72
|
+
**Problem:** [core problem in one sentence]
|
|
73
|
+
**MVP Scope:** [what is in for v1]
|
|
74
|
+
**Out of scope:** [what is explicitly deferred]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
If `context.md` does not exist, create it using the bootstrap template from the Working Protocol before adding `## Vision`.
|
|
78
|
+
|
|
79
|
+
### 2. Write `tasks.md` backlog
|
|
80
|
+
|
|
81
|
+
Structure the backlog as ordered epics, each decomposed into concrete `feat:` tasks. Every task must be actionable on its own.
|
|
82
|
+
|
|
83
|
+
**Ordering rules (non-negotiable):**
|
|
84
|
+
|
|
85
|
+
1. Foundation first — scaffolding, config, CI
|
|
86
|
+
2. Data/domain layer — models, repositories, core logic
|
|
87
|
+
3. Integration layer — APIs, external services, adapters
|
|
88
|
+
4. Application layer — use cases, orchestration
|
|
89
|
+
5. Delivery layer — UI, CLI, endpoints
|
|
90
|
+
6. Hardening — auth, error handling, observability
|
|
91
|
+
7. Polish — docs, migration, release
|
|
92
|
+
|
|
93
|
+
**For legacy projects:** add a diagnosis epic at position 0 — read, map, and document before changing anything.
|
|
94
|
+
|
|
95
|
+
**Format:**
|
|
96
|
+
|
|
97
|
+
```md
|
|
98
|
+
## Backlog
|
|
99
|
+
|
|
100
|
+
### Epic 1 — [Name]
|
|
101
|
+
|
|
102
|
+
- [TODO] feat: [atomic task]
|
|
103
|
+
- [TODO] feat: [atomic task]
|
|
104
|
+
|
|
105
|
+
### Epic 2 — [Name]
|
|
106
|
+
|
|
107
|
+
- [TODO] feat: [atomic task]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Move the first task of Epic 1 to `## Active` as `[IN_PROGRESS]` only after the developer approves.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Phase: STOP
|
|
115
|
+
|
|
116
|
+
Present a summary of what was produced:
|
|
117
|
+
|
|
118
|
+
- The vision statement (3 lines)
|
|
119
|
+
- The scope boundary (in / out)
|
|
120
|
+
- The full epic list with task count per epic
|
|
121
|
+
|
|
122
|
+
Then stop completely. Do not begin any `feat:` — do not write any code. Wait for explicit developer approval.
|
|
123
|
+
|
|
124
|
+
**Hard Rule:** The Land Cycle ends here. The developer decides what comes next.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
> Read `.ai/instructions/core/agent-roles.md` for the multi-agent handoff protocol (Planning + Fast roles).
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# Backend Execution System
|
|
2
|
+
|
|
3
|
+
<ruleset name="BackendSkill">
|
|
4
|
+
|
|
5
|
+
> [!IMPORTANT]
|
|
6
|
+
> **Backend for Frontend (BFF) is the default architectural pattern.**
|
|
7
|
+
> Every endpoint exists to serve a specific frontend need — not to expose a generic data layer.
|
|
8
|
+
> Developer Experience (DX) is the primary quality metric: predictable contracts, clear error codes, zero surprises for the frontend consumer.
|
|
9
|
+
|
|
10
|
+
## Rule: Standard Response Envelope
|
|
11
|
+
|
|
12
|
+
<rule name="ResponseEnvelope">
|
|
13
|
+
|
|
14
|
+
All responses use a consistent envelope. Never return raw entities or ad-hoc response shapes.
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
// Success — single resource
|
|
18
|
+
{ success: true, error: null, data: T }
|
|
19
|
+
|
|
20
|
+
// Success — with BFF action directive inside meta
|
|
21
|
+
{ success: true, error: null, meta: { action: "SHOW_TOAST" }, data: T }
|
|
22
|
+
|
|
23
|
+
// Success — paginated collection with action
|
|
24
|
+
{ success: true, error: null, meta: { action: "SHOW_TOAST", nextCursor: string | null, hasMore: boolean }, data: T[] }
|
|
25
|
+
|
|
26
|
+
// Failure
|
|
27
|
+
{ success: false, error: { code: string, message: string }, data: null }
|
|
28
|
+
|
|
29
|
+
// Failure — with BFF action directive inside meta
|
|
30
|
+
{ success: false, error: { code: "UNAUTHORIZED", message: "Session expired" }, meta: { action: "REDIRECT_TO_LOGIN" }, data: null }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The `success` boolean is the discriminator — consumers branch on it, never on HTTP status alone. `error` is always present: `null` on success, `{ code, message }` on failure. `data` is always last — it is the payload; envelope headers (`success`, `error`, `meta`) are read first. `action` lives inside `meta` — it is one of several possible directives the server can send alongside pagination or observability context.
|
|
34
|
+
|
|
35
|
+
**Pagination shape:**
|
|
36
|
+
|
|
37
|
+
- **Default (cursor-based):** Use `nextCursor` + `hasMore` for all collection endpoints. Cursor is stable under concurrent inserts and performs consistently at any depth.
|
|
38
|
+
- **Offset-based (exception only):** Use `page` + `total` only for admin/backoffice UIs where deep pagination is explicit and dataset size is bounded. Document the exception in the contract.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// Cursor-based (default)
|
|
42
|
+
GET /api/orders?cursor=eyJpZCI6MTAwfQ&limit=20
|
|
43
|
+
→ { success: true, error: null, meta: { nextCursor: "eyJpZCI6MTIwfQ", hasMore: true }, data: Order[] }
|
|
44
|
+
|
|
45
|
+
// Offset-based (exception)
|
|
46
|
+
GET /api/admin/users?page=3&limit=50
|
|
47
|
+
→ { success: true, error: null, meta: { page: 3, pageSize: 50, total: 240 }, data: User[] }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### Meta — Context, Not Content
|
|
51
|
+
|
|
52
|
+
`meta` is optional and always lean. It carries technical context that helps consumers paginate, trace, or monitor — never business data.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
type PaginationCursor = {
|
|
56
|
+
nextCursor: string | null; // null when no more pages
|
|
57
|
+
hasMore: boolean;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
type PaginationOffset = {
|
|
61
|
+
page: number;
|
|
62
|
+
pageSize: number; // items per page — equivalent to "take"
|
|
63
|
+
total: number; // total record count for UI pagination controls
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
type Meta = {
|
|
67
|
+
action?: string; // BFF directive — guides client behavior (REDIRECT_TO_LOGIN, SHOW_TOAST, SILENT_RETRY, REFRESH_TOKEN)
|
|
68
|
+
traceId?: string; // distributed trace ID — correlates request across services
|
|
69
|
+
requestId?: string; // per-request unique ID — correlates logs within this service
|
|
70
|
+
timestamp?: string; // ISO 8601 — when the response was generated
|
|
71
|
+
duration?: number; // response time in ms — useful for client-side monitoring
|
|
72
|
+
pagination?: PaginationCursor | PaginationOffset;
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**When to include each field:**
|
|
77
|
+
|
|
78
|
+
| Field | Include when |
|
|
79
|
+
| :----------- | :------------------------------------------------------------ |
|
|
80
|
+
| `action` | Server must guide client behavior — BFF only |
|
|
81
|
+
| `traceId` | Distributed system — correlates calls across services |
|
|
82
|
+
| `requestId` | Always on non-trivial endpoints — correlates server-side logs |
|
|
83
|
+
| `timestamp` | Caching-sensitive responses or audit trails |
|
|
84
|
+
| `duration` | Performance-sensitive endpoints or internal tooling |
|
|
85
|
+
| `pagination` | Any collection endpoint — choose cursor or offset (not both) |
|
|
86
|
+
|
|
87
|
+
**Pagination strategy:**
|
|
88
|
+
|
|
89
|
+
- **Cursor** (`nextCursor` + `hasMore`) — default for all feeds, timelines, and large datasets. Stable under concurrent writes; no total count needed.
|
|
90
|
+
- **Offset** (`page` + `pageSize` + `total`) — exception for admin/backoffice UIs where the user navigates by page number and the dataset is bounded.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// Builder — spread only what is present, never send null/undefined fields
|
|
94
|
+
function buildMeta(fields: Partial<Meta>): Meta {
|
|
95
|
+
const meta = {
|
|
96
|
+
...(fields.action && { action: fields.action }),
|
|
97
|
+
...(fields.traceId && { traceId: fields.traceId }),
|
|
98
|
+
...(fields.requestId && { requestId: fields.requestId }),
|
|
99
|
+
...(fields.timestamp && { timestamp: fields.timestamp }),
|
|
100
|
+
...(fields.duration !== undefined && { duration: fields.duration }),
|
|
101
|
+
...(fields.pagination && { pagination: fields.pagination }),
|
|
102
|
+
};
|
|
103
|
+
return meta;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Rules:**
|
|
108
|
+
|
|
109
|
+
- `meta` is flat — `pagination` is the only valid nested object
|
|
110
|
+
- `action` lives in `meta`, not at envelope root — it is one directive among several context fields
|
|
111
|
+
- Forbidden in `meta`: business data, user objects, domain payloads, debug dumps
|
|
112
|
+
- Test: _"Does this field help debug, navigate, or guide the client without knowing the domain?"_ Yes → meta. No → `data` or drop it.
|
|
113
|
+
|
|
114
|
+
HTTP status codes map to explicit, documented error codes:
|
|
115
|
+
|
|
116
|
+
| Status | Code | When |
|
|
117
|
+
| :----- | :------------------------ | :-------------------------------------------------------- |
|
|
118
|
+
| `400` | `INVALID_INPUT` | Schema validation failed |
|
|
119
|
+
| `401` | `UNAUTHORIZED` | Missing or invalid credentials |
|
|
120
|
+
| `403` | `FORBIDDEN` | Authenticated but not permitted |
|
|
121
|
+
| `404` | `NOT_FOUND` | Resource does not exist |
|
|
122
|
+
| `409` | `CONFLICT` | Unique constraint violation (e.g. duplicate email) |
|
|
123
|
+
| `409` | `BUSINESS_RULE_VIOLATION` | Domain rule violated (e.g. cannot cancel a shipped order) |
|
|
124
|
+
| `422` | `UNPROCESSABLE` | Input valid but semantically incorrect |
|
|
125
|
+
| `500` | `INTERNAL_ERROR` | Unexpected server failure |
|
|
126
|
+
|
|
127
|
+
The frontend consumer must never need to parse free-form error messages or infer meaning from HTTP status alone.
|
|
128
|
+
</rule>
|
|
129
|
+
|
|
130
|
+
## Rule: Contract-First Implementation
|
|
131
|
+
|
|
132
|
+
<rule name="ContractFirst">
|
|
133
|
+
|
|
134
|
+
Define the contract before writing any code. The spec must establish:
|
|
135
|
+
|
|
136
|
+
```markdown
|
|
137
|
+
## Contract: <UseCaseName>
|
|
138
|
+
|
|
139
|
+
Input:
|
|
140
|
+
|
|
141
|
+
- field: type (constraints)
|
|
142
|
+
|
|
143
|
+
Output (200):
|
|
144
|
+
|
|
145
|
+
- field: type (from envelope: data.field)
|
|
146
|
+
|
|
147
|
+
Meta (optional):
|
|
148
|
+
|
|
149
|
+
- meta.action: "REDIRECT_TO_LOGIN" | "SHOW_TOAST" | "SILENT_RETRY" | "REFRESH_TOKEN" — BFF directive, only when server must guide client
|
|
150
|
+
- meta.pagination: cursor or offset — for collection endpoints
|
|
151
|
+
|
|
152
|
+
Errors:
|
|
153
|
+
|
|
154
|
+
- 400 INVALID_INPUT — when: [condition]
|
|
155
|
+
- 404 NOT_FOUND — when: [condition]
|
|
156
|
+
- 409 CONFLICT — when: [condition]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Without a defined contract in the spec, ask — do not invent structure.
|
|
160
|
+
</rule>
|
|
161
|
+
|
|
162
|
+
## Rule: Mandatory Execution Flow
|
|
163
|
+
|
|
164
|
+
<rule name="ExecutionFlow">
|
|
165
|
+
|
|
166
|
+
Every operation follows this sequence. Mixing layers is forbidden.
|
|
167
|
+
|
|
168
|
+
1. **Validate input** — guard clauses, fail fast before any I/O
|
|
169
|
+
2. **Load dependencies** — DB reads, service calls (no writes yet)
|
|
170
|
+
3. **Apply business rules** — invariants, policy checks
|
|
171
|
+
4. **Execute core logic** — the actual operation
|
|
172
|
+
5. **Persist state** — writes, events
|
|
173
|
+
6. **Return response** — map through Response DTO (OutputFilter), then wrap in the standard envelope
|
|
174
|
+
|
|
175
|
+
Business logic does not belong in repositories. DB calls do not happen after state mutation.
|
|
176
|
+
</rule>
|
|
177
|
+
|
|
178
|
+
## Rule: Use Case Structure
|
|
179
|
+
|
|
180
|
+
<rule name="UseCaseStructure">
|
|
181
|
+
|
|
182
|
+
Each operation lives in a dedicated Use Case:
|
|
183
|
+
|
|
184
|
+
- No business logic in controllers — controllers route and delegate only
|
|
185
|
+
- No anemic domain — entities enforce their own invariants
|
|
186
|
+
- Dependencies injected via interfaces (testable and swappable)
|
|
187
|
+
- Async all the way — no sync blocking on I/O paths
|
|
188
|
+
- Guard clauses over nested conditionals
|
|
189
|
+
|
|
190
|
+
The **Result Pattern** (`Result<T>`) is preferred when it meaningfully clarifies the happy/failure path distinction. In simple CRUD operations or contexts where idiomatic language error handling is clearer, do not force the pattern — DX matters more than dogma.
|
|
191
|
+
</rule>
|
|
192
|
+
|
|
193
|
+
## Rule: Typed Layer Results
|
|
194
|
+
|
|
195
|
+
<rule name="TypedLayerResults">
|
|
196
|
+
|
|
197
|
+
> [!NOTE]
|
|
198
|
+
> Every inter-layer return uses a named type. Anonymous object shapes are forbidden at layer boundaries.
|
|
199
|
+
|
|
200
|
+
Use a named type (interface, type alias, record, struct) for every value crossing a layer boundary — Use Case → Controller, Repository → Use Case, Service → Use Case. Never return raw anonymous objects.
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// ✅ Correct — named return type + Explaining Returns
|
|
204
|
+
interface CreateOrderResult {
|
|
205
|
+
orderId: string;
|
|
206
|
+
estimatedDelivery: Date;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function createOrder(input: CreateOrderInput): Promise<CreateOrderResult> {
|
|
210
|
+
// ...
|
|
211
|
+
const orderResult: CreateOrderResult = { orderId, estimatedDelivery };
|
|
212
|
+
return orderResult;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ❌ Wrong — anonymous shape leaks through the cascade; callers must guess the contract
|
|
216
|
+
async function createOrder(input) {
|
|
217
|
+
return { orderId, estimatedDelivery, internalRef, rawEntity }; // what's safe to use?
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Why it matters:**
|
|
222
|
+
|
|
223
|
+
- Callers know exactly what they receive — no guessing which fields are safe to use
|
|
224
|
+
- Refactoring a field name propagates through the type system, not through manual grep
|
|
225
|
+
- Each boundary becomes a readable contract, reinforcing **NarrativeCascade**
|
|
226
|
+
- OutputFilter at the API boundary is the final step of a chain that was already typed throughout
|
|
227
|
+
|
|
228
|
+
This is the internal analog of OutputFilter: the same contract discipline, applied to every layer — not just the API edge.
|
|
229
|
+
</rule>
|
|
230
|
+
|
|
231
|
+
## Rule: Thin Entry Point
|
|
232
|
+
|
|
233
|
+
<rule name="ThinEntryPoint">
|
|
234
|
+
|
|
235
|
+
The HTTP entry point (Controller in MVC, Handler/Endpoint in Vertical Slice, Minimal API delegate) must be a thin delegation — no business logic, no branching, no DB calls. Ideally one line.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// ✅ Correct — named input, Explaining Returns, delegates to Adapter
|
|
239
|
+
async function createOrderHandler(request: Request, response: Response) {
|
|
240
|
+
const input: CreateOrderInput = request.body;
|
|
241
|
+
const result = await createOrderUseCase.execute(input);
|
|
242
|
+
const { status, body } = toEnvelope(result);
|
|
243
|
+
response.status(status).json(body);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ❌ Wrong
|
|
247
|
+
async function createOrderHandler(req, res) {
|
|
248
|
+
// req/res — ambiguous abbreviations; use request/response
|
|
249
|
+
const existing = await db.orders.findByRef(req.body.ref); // DB call in entry point — belongs in repository
|
|
250
|
+
if (existing) return res.status(409).json({ error: 'Duplicate' }); // business rule here + wrong envelope shape
|
|
251
|
+
// ...
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### Adapter — Result\<T\> → HTTP Envelope
|
|
256
|
+
|
|
257
|
+
The entry point delegates through an Adapter. The Adapter is the **only** layer that knows both `Result<T>` (domain) and the HTTP envelope (infrastructure). Domain code never imports HTTP; the entry point never knows `Result<T>`.
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// adapter.ts — lives in infrastructure/, one per feature or shared
|
|
261
|
+
|
|
262
|
+
interface HttpEnvelope<T> {
|
|
263
|
+
status: number;
|
|
264
|
+
body: { success: boolean; error: { code: string; message: string } | null; data: T | null };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const statusByCode: Record<string, number> = {
|
|
268
|
+
NOT_FOUND: 404,
|
|
269
|
+
UNAUTHORIZED: 401,
|
|
270
|
+
FORBIDDEN: 403,
|
|
271
|
+
CONFLICT: 409,
|
|
272
|
+
VALIDATION_ERROR: 400,
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
function toEnvelope<T>(result: Result<T>): HttpEnvelope<T> {
|
|
276
|
+
if (result.isFailure) {
|
|
277
|
+
const status = statusByCode[result.error] ?? 400;
|
|
278
|
+
const body = {
|
|
279
|
+
success: false,
|
|
280
|
+
error: { code: result.error, message: result.error },
|
|
281
|
+
data: null,
|
|
282
|
+
};
|
|
283
|
+
const failureEnvelope = { status, body };
|
|
284
|
+
return failureEnvelope;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const body = { success: true, error: null, data: result.data };
|
|
288
|
+
const successEnvelope = { status: 200, body };
|
|
289
|
+
return successEnvelope;
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Rules:**
|
|
294
|
+
|
|
295
|
+
- `toEnvelope` receives `Result<T>` — never a raw entity or domain object
|
|
296
|
+
- HTTP status is derived from the error code — never hardcoded in the entry point
|
|
297
|
+
- Human-readable `message` can be resolved from a message map; defaults to the code itself
|
|
298
|
+
- `action` is added by the entry point or use case when the server must guide the client — not by `toEnvelope` generically
|
|
299
|
+
|
|
300
|
+
Naming varies by flavor — the rule is universal:
|
|
301
|
+
|
|
302
|
+
- **MVC**: Controller delegates to **Service** → `toEnvelope`
|
|
303
|
+
- **Vertical Slice**: Handler delegates to **UseCase** → `toEnvelope`
|
|
304
|
+
- **Minimal API (C#, Hono)**: Endpoint delegate → **Handler** → `toEnvelope`
|
|
305
|
+
|
|
306
|
+
If the entry point needs more than 4 lines, the logic belongs in the application layer.
|
|
307
|
+
</rule>
|
|
308
|
+
|
|
309
|
+
## Rule: OutputFilter (Response DTO)
|
|
310
|
+
|
|
311
|
+
<rule name="OutputFilter">
|
|
312
|
+
|
|
313
|
+
Every response must be serialized through a Response DTO before leaving the system. Never return raw entities.
|
|
314
|
+
|
|
315
|
+
- Map domain/DB entity → Response DTO at the boundary (Presenter in Vertical Slice, Service return type in MVC)
|
|
316
|
+
- The DTO defines exactly which fields are exposed — unlisted fields are never returned
|
|
317
|
+
- Sensitive fields (`passwordHash`, `internalId`, `createdBy`, PII) must never appear in any Response DTO
|
|
318
|
+
- No exceptions: even simple CRUD endpoints go through the DTO
|
|
319
|
+
|
|
320
|
+
This is the enforcement layer for data masking and BFF contract integrity.
|
|
321
|
+
</rule>
|
|
322
|
+
|
|
323
|
+
## Rule: BFF Endpoint Design
|
|
324
|
+
|
|
325
|
+
<rule name="BFFDesign">
|
|
326
|
+
|
|
327
|
+
> [!NOTE]
|
|
328
|
+
> Shape every endpoint for its UI consumer. Generic REST purity is secondary to frontend DX.
|
|
329
|
+
|
|
330
|
+
Endpoints are shaped for the UI consumer, not for generic REST purity:
|
|
331
|
+
|
|
332
|
+
- Response shapes match what the frontend needs directly — no N+1 client-side assembly
|
|
333
|
+
- Aggregation and transformation happen server-side (one call, complete data)
|
|
334
|
+
- Field names match the frontend's domain language, not the DB column names
|
|
335
|
+
- Pagination, filtering, and sorting parameters reflect the UI's actual usage patterns
|
|
336
|
+
|
|
337
|
+
Avoid exposing raw DB shapes through generic repositories. Transform at the use case boundary.
|
|
338
|
+
</rule>
|
|
339
|
+
|
|
340
|
+
## Rule: Infrastructure Insulation (Cache & Persistence)
|
|
341
|
+
|
|
342
|
+
<rule name="InfraInsulation">
|
|
343
|
+
|
|
344
|
+
Infrastructure concerns must never leak into the Domain Layer.
|
|
345
|
+
|
|
346
|
+
- **Cache as Decorator**: Caching logic must be implemented as a decorator or interceptor around the service/repository. Never add `if (cache.get(key))` inside a business use case.
|
|
347
|
+
- **Repository Pattern**: Persistence details (SQL, NoSQL, ORM) stay in the repository implementation. The use case only knows the interface.
|
|
348
|
+
- **Circuit Breakers**: External service calls must be wrapped in resilience patterns outside the business logic.
|
|
349
|
+
|
|
350
|
+
Anti-pattern: **Coupled Cache** — mixing business rules with TTL and cache key management.
|
|
351
|
+
</rule>
|
|
352
|
+
|
|
353
|
+
</ruleset>
|