@tailor-platform/erp-kit 0.1.0 → 0.1.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 (102) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +77 -50
  4. package/dist/cli.js +693 -553
  5. package/package.json +4 -2
  6. package/schemas/module/command.yml +1 -0
  7. package/schemas/module/model.yml +9 -0
  8. package/schemas/module/query.yml +53 -0
  9. package/skills/1-module-docs/SKILL.md +4 -0
  10. package/{rules/module-development → skills/1-module-docs/references}/structure.md +2 -7
  11. package/skills/2-module-feature-breakdown/SKILL.md +6 -0
  12. package/{rules/module-development → skills/2-module-feature-breakdown/references}/commands.md +0 -6
  13. package/{rules/module-development → skills/2-module-feature-breakdown/references}/models.md +0 -5
  14. package/skills/2-module-feature-breakdown/references/structure.md +22 -0
  15. package/skills/3-module-doc-review/SKILL.md +6 -0
  16. package/skills/3-module-doc-review/references/commands.md +54 -0
  17. package/skills/3-module-doc-review/references/models.md +29 -0
  18. package/{rules/module-development → skills/3-module-doc-review/references}/testing.md +0 -6
  19. package/skills/4-module-tdd-implementation/SKILL.md +24 -6
  20. package/skills/4-module-tdd-implementation/references/commands.md +45 -0
  21. package/{rules/sdk-best-practices → skills/4-module-tdd-implementation/references}/db-relations.md +0 -5
  22. package/{rules/module-development → skills/4-module-tdd-implementation/references}/errors.md +0 -5
  23. package/{rules/module-development → skills/4-module-tdd-implementation/references}/exports.md +0 -5
  24. package/skills/4-module-tdd-implementation/references/models.md +30 -0
  25. package/skills/4-module-tdd-implementation/references/structure.md +22 -0
  26. package/skills/4-module-tdd-implementation/references/testing.md +37 -0
  27. package/skills/5-module-implementation-review/SKILL.md +8 -0
  28. package/skills/5-module-implementation-review/references/commands.md +45 -0
  29. package/skills/5-module-implementation-review/references/errors.md +7 -0
  30. package/skills/5-module-implementation-review/references/exports.md +8 -0
  31. package/skills/5-module-implementation-review/references/models.md +30 -0
  32. package/skills/5-module-implementation-review/references/testing.md +29 -0
  33. package/skills/app-compose-1-requirement-analysis/SKILL.md +4 -0
  34. package/{rules/app-compose → skills/app-compose-1-requirement-analysis/references}/structure.md +0 -5
  35. package/skills/app-compose-2-requirements-breakdown/SKILL.md +7 -0
  36. package/{rules/app-compose/frontend → skills/app-compose-2-requirements-breakdown/references}/screen-detailview.md +0 -6
  37. package/{rules/app-compose/frontend → skills/app-compose-2-requirements-breakdown/references}/screen-form.md +0 -6
  38. package/{rules/app-compose/frontend → skills/app-compose-2-requirements-breakdown/references}/screen-listview.md +0 -6
  39. package/skills/app-compose-2-requirements-breakdown/references/structure.md +27 -0
  40. package/skills/app-compose-3-doc-review/SKILL.md +4 -0
  41. package/skills/app-compose-3-doc-review/references/structure.md +27 -0
  42. package/skills/app-compose-4-design-mock/SKILL.md +8 -0
  43. package/{rules/app-compose/frontend → skills/app-compose-4-design-mock/references}/component.md +0 -5
  44. package/skills/app-compose-4-design-mock/references/screen-detailview.md +106 -0
  45. package/skills/app-compose-4-design-mock/references/screen-form.md +139 -0
  46. package/skills/app-compose-4-design-mock/references/screen-listview.md +153 -0
  47. package/skills/app-compose-4-design-mock/references/structure.md +27 -0
  48. package/skills/app-compose-5-design-mock-review/SKILL.md +7 -0
  49. package/skills/app-compose-5-design-mock-review/references/component.md +50 -0
  50. package/skills/app-compose-5-design-mock-review/references/screen-detailview.md +106 -0
  51. package/skills/app-compose-5-design-mock-review/references/screen-form.md +139 -0
  52. package/skills/app-compose-5-design-mock-review/references/screen-listview.md +153 -0
  53. package/skills/app-compose-6-implementation-spec/SKILL.md +5 -0
  54. package/{rules/app-compose/backend → skills/app-compose-6-implementation-spec/references}/auth.md +0 -6
  55. package/skills/app-compose-6-implementation-spec/references/structure.md +27 -0
  56. package/src/cli.ts +8 -90
  57. package/src/commands/app/index.ts +74 -0
  58. package/src/commands/check.test.ts +2 -1
  59. package/src/commands/check.ts +1 -0
  60. package/src/commands/init.test.ts +30 -19
  61. package/src/commands/init.ts +76 -43
  62. package/src/commands/module/index.ts +85 -0
  63. package/src/commands/module/list.test.ts +62 -0
  64. package/src/commands/module/list.ts +64 -0
  65. package/src/commands/scaffold.test.ts +5 -0
  66. package/src/commands/scaffold.ts +2 -1
  67. package/src/commands/sync-check.test.ts +28 -0
  68. package/src/commands/sync-check.ts +6 -0
  69. package/src/integration.test.ts +6 -8
  70. package/src/module.ts +4 -3
  71. package/src/modules/primitives/docs/models/Currency.md +4 -0
  72. package/src/modules/primitives/docs/models/ExchangeRate.md +4 -1
  73. package/src/modules/primitives/docs/models/Unit.md +4 -1
  74. package/src/modules/primitives/docs/models/UoMCategory.md +2 -0
  75. package/src/modules/primitives/index.ts +2 -2
  76. package/src/modules/primitives/module.ts +5 -3
  77. package/src/modules/primitives/permissions.ts +0 -2
  78. package/src/modules/primitives/{command → query}/convertAmount.test.ts +2 -19
  79. package/src/modules/primitives/query/convertAmount.ts +122 -0
  80. package/src/modules/primitives/{command → query}/convertQuantity.test.ts +2 -13
  81. package/src/modules/primitives/{command → query}/convertQuantity.ts +4 -6
  82. package/src/modules/shared/defineQuery.test.ts +28 -0
  83. package/src/modules/shared/defineQuery.ts +16 -0
  84. package/src/modules/shared/internal.ts +2 -1
  85. package/src/modules/shared/types.ts +8 -0
  86. package/src/modules/user-management/docs/models/AuditEvent.md +2 -0
  87. package/src/modules/user-management/docs/models/Permission.md +2 -0
  88. package/src/modules/user-management/docs/models/Role.md +2 -0
  89. package/src/modules/user-management/docs/models/RolePermission.md +2 -0
  90. package/src/modules/user-management/docs/models/User.md +2 -0
  91. package/src/modules/user-management/docs/models/UserRole.md +2 -0
  92. package/src/schemas.ts +1 -0
  93. package/rules/app-compose/frontend/auth.md +0 -55
  94. package/rules/app-compose/frontend/page.md +0 -86
  95. package/rules/module-development/cross-module-type-injection.md +0 -28
  96. package/rules/module-development/dependency-modules.md +0 -24
  97. package/rules/module-development/executors.md +0 -67
  98. package/rules/module-development/sync-vs-async-operations.md +0 -83
  99. package/rules/sdk-best-practices/sdk-docs.md +0 -14
  100. package/src/modules/primitives/command/convertAmount.ts +0 -126
  101. /package/src/modules/primitives/docs/{commands → queries}/ConvertAmount.md +0 -0
  102. /package/src/modules/primitives/docs/{commands → queries}/ConvertQuantity.md +0 -0
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "@tailor-platform/erp-kit",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Opinionated ERP toolkit for building business applications on Tailor Platform",
5
+ "license": "MIT",
5
6
  "bin": {
6
7
  "erp-kit": "./dist/cli.js"
7
8
  },
8
9
  "files": [
10
+ "CHANGELOG.md",
9
11
  "dist",
10
- "rules",
12
+ "LICENSE",
11
13
  "schemas",
12
14
  "skills",
13
15
  "src"
@@ -35,6 +35,7 @@ structure:
35
35
  # Error handling - REQUIRED
36
36
  # How errors are handled
37
37
  - heading: "## Error Scenarios"
38
+ description: "Use UPPER_SNAKE_CASE error codes to identify each scenario.\ne.g. `- **ITEM_NOT_FOUND**: Specified item ID does not exist`"
38
39
  lists:
39
40
  - min: 0
40
41
  type: unordered
@@ -37,6 +37,15 @@ structure:
37
37
  type: unordered
38
38
  min_items: 0
39
39
 
40
+ - heading: "### Query Definitions"
41
+ description: |
42
+ Definitions of queries this domain model supports.
43
+ Should match with the queries defined in modules/**/docs/queries/*.md and link to them.
44
+ lists:
45
+ - min: 0
46
+ type: unordered
47
+ min_items: 0
48
+
40
49
  - heading: "### Models"
41
50
  description: |
42
51
  Actual database models necessary for the domain model.
@@ -0,0 +1,53 @@
1
+ # yaml-language-server: $schema=https://raw.githubusercontent.com/jackchuka/mdschema/main/schema.json
2
+
3
+ structure:
4
+ - heading:
5
+ expr: "filename == heading"
6
+ children:
7
+ # What this query does - REQUIRED
8
+ - heading: "## Overview"
9
+
10
+ # Business rules - REQUIRED
11
+ # Rules, constraints, and validation logic
12
+ - heading: "## Business Rules"
13
+ allow_additional: true
14
+ lists:
15
+ - min: 0
16
+ type: unordered
17
+ min_items: 1
18
+
19
+ # How it works - REQUIRED
20
+ # Step-by-step flow or workflow
21
+ - heading: "## Process Flow"
22
+ code_blocks:
23
+ - lang: mermaid
24
+ min: 1
25
+
26
+ # External dependencies - OPTIONAL
27
+ # Queries from other modules that this query depends on
28
+ - heading: "## External Dependencies"
29
+ description: "e.g. `- [inventory::getStockLevel](../../inventory/queries/getStockLevel.md) - Get current stock level`"
30
+ lists:
31
+ - min: 0
32
+ type: unordered
33
+ min_items: 1
34
+
35
+ # Error handling - REQUIRED
36
+ # How errors are handled
37
+ - heading: "## Error Scenarios"
38
+ description: "Use UPPER_SNAKE_CASE error codes to identify each scenario.\ne.g. `- **ITEM_NOT_FOUND**: Specified item ID does not exist`"
39
+ lists:
40
+ - min: 0
41
+ type: unordered
42
+ min_items: 1
43
+
44
+ # Link validation
45
+ links:
46
+ validate_internal: true
47
+ validate_files: true
48
+
49
+ # Heading rules
50
+ heading_rules:
51
+ no_skip_levels: true
52
+ unique: true
53
+ max_depth: 4
@@ -105,3 +105,7 @@ Always run before completing:
105
105
  ```bash
106
106
  pnpm run module:doc:check
107
107
  ```
108
+
109
+ ## References
110
+
111
+ - [Module structure](references/structure.md)
@@ -1,15 +1,10 @@
1
- ---
2
- paths:
3
- - "modules/*/src/"
4
- ---
5
-
6
1
  # Module Directory Structure
7
2
 
8
3
  ```
9
4
  src/
10
5
  ├── db/ # Database models (one file per model)
11
6
  ├── executor/ # Async executors (record triggers, job functions)
12
- ├── function/ # Domain functions + tests (*.test.ts co-located)
7
+ ├── command/ # Domain commands + tests (*.test.ts co-located)
13
8
  ├── lib/ # Internal shared code (errors.ts, types.ts)
14
9
  ├── testing/ # Test fixtures and helpers
15
10
  ├── generated/ # Auto-generated code (do not edit)
@@ -21,7 +16,7 @@ src/
21
16
 
22
17
  - `db/`: Only documentable model definitions, no helpers
23
18
  - `executor/`: Async executors as factory functions (see executors.md)
24
- - `function/`: Domain functions + co-located tests, no utilities
19
+ - `command/`: Domain commands + co-located tests, no utilities
25
20
  - `lib/`: Internal errors and types (not documented)
26
21
  - `testing/`: Fixtures for tests only
27
22
  - Run `pnpm generate` after modifying `db/` models
@@ -64,3 +64,9 @@ Always run before completing:
64
64
  ```bash
65
65
  pnpm run module:doc:check
66
66
  ```
67
+
68
+ ## References
69
+
70
+ - [Module structure](references/structure.md)
71
+ - [Model patterns](references/models.md)
72
+ - [Command patterns](references/commands.md)
@@ -1,9 +1,3 @@
1
- ---
2
- paths:
3
- - "modules/*/src/command/*.ts"
4
- - "!modules/*/src/command/*.test.ts"
5
- ---
6
-
7
1
  # Command Implementation
8
2
 
9
3
  ## defineCommand Pattern
@@ -1,8 +1,3 @@
1
- ---
2
- paths:
3
- - "modules/*/src/db/*.ts"
4
- ---
5
-
6
1
  # Database Models
7
2
 
8
3
  ## Factory Function Pattern
@@ -0,0 +1,22 @@
1
+ # Module Directory Structure
2
+
3
+ ```
4
+ src/
5
+ ├── db/ # Database models (one file per model)
6
+ ├── executor/ # Async executors (record triggers, job functions)
7
+ ├── command/ # Domain commands + tests (*.test.ts co-located)
8
+ ├── lib/ # Internal shared code (errors.ts, types.ts)
9
+ ├── testing/ # Test fixtures and helpers
10
+ ├── generated/ # Auto-generated code (do not edit)
11
+ ├── index.ts # Public exports
12
+ └── module.ts # Module definition
13
+ ```
14
+
15
+ ## Rules
16
+
17
+ - `db/`: Only documentable model definitions, no helpers
18
+ - `executor/`: Async executors as factory functions (see executors.md)
19
+ - `command/`: Domain commands + co-located tests, no utilities
20
+ - `lib/`: Internal errors and types (not documented)
21
+ - `testing/`: Fixtures for tests only
22
+ - Run `pnpm generate` after modifying `db/` models
@@ -228,3 +228,9 @@ For each command doc:
228
228
  | "Deactivate X" | deactivateX |
229
229
  | "Assign X to Y" | assignXToY |
230
230
  | "Remove X from Y" | removeXFromY |
231
+
232
+ ## References
233
+
234
+ - [Model patterns](references/models.md)
235
+ - [Command patterns](references/commands.md)
236
+ - [Testing patterns](references/testing.md)
@@ -0,0 +1,54 @@
1
+ ---
2
+ paths:
3
+ - "packages/erp-kit/src/modules/*/command/*.ts"
4
+ - "!packages/erp-kit/src/modules/*/command/*.test.ts"
5
+ ---
6
+
7
+ # Command Implementation
8
+
9
+ ## defineCommand Pattern
10
+
11
+ Commands that don't need custom fields use `defineCommand` directly:
12
+
13
+ ```typescript
14
+ export const myCommand = defineCommand(permissions.myCommand, async (db: DB, input: Input) => { ... });
15
+ ```
16
+
17
+ ## Factory Function Pattern (custom fields)
18
+
19
+ Commands that insert into a table with user-extensible fields use a `makeCreateX<CF>()` factory:
20
+
21
+ - Export only `makeCreateX` — no default instance
22
+ - Generic `CF extends Record<string, unknown>` for custom fields
23
+ - Input type: `CreateXInput & CF`
24
+ - Destructure known fields, rest-spread custom fields into `.values()` before known fields
25
+ - Cast custom fields: `...(customFields as Record<string, unknown>)`
26
+ - `module.ts` wires with `makeCreateX<FieldsToInsertable<F>>()`
27
+
28
+ ## Implementation Considerations
29
+
30
+ - **Validation**: Check referenced entities exist before operating
31
+ - **Idempotency**: For assign/revoke, return existing instead of throwing
32
+ - **Return format**: Wrap in object `{ entity }` not just `entity`
33
+
34
+ ## Conventions
35
+
36
+ - Input types: exported interfaces (`export interface MyFunctionInput`)
37
+ - Use `.executeTakeFirst()` for single results
38
+ - Include JSDoc: `/** Function: name \n Description */`
39
+
40
+ ## State Transitions
41
+
42
+ For commands that transition between statuses, accept `from?: string[]` with a default:
43
+
44
+ ```typescript
45
+ from?: string[]; // Default: ["ACTIVE"]
46
+
47
+ const validFromStatuses = input.from ?? ["ACTIVE"];
48
+ if (!validFromStatuses.includes(user.status)) {
49
+ throw new InvalidStatusTransitionError(user.status, targetStatus);
50
+ }
51
+ ```
52
+
53
+ - Default `from` contains the base valid source status
54
+ - Parent modules can override to allow transitions from additional statuses
@@ -0,0 +1,29 @@
1
+ # Database Models
2
+
3
+ ## Factory Function Pattern
4
+
5
+ Each model is a `createXType<const F>(params)` factory:
6
+
7
+ - Params interface generic: `F extends Record<string, TailorAnyDBField>`
8
+ - `fields?: F` — optional custom fields from parent modules
9
+ - Spread as `...(params.fields ?? {}) as F` to preserve type information
10
+ - Include `...db.fields.timestamps()`
11
+ - Use `.description()` for field docs
12
+ - Apply permissions at model level
13
+ - Export a default instance: `export const x = createXType({})`
14
+
15
+ ## Stateful Model Enums
16
+
17
+ Status enums are tied to the module's state machine. Base module defines core statuses; parent callers can only **extend, not replace**.
18
+
19
+ ```typescript
20
+ // Good: extension only
21
+ const BASE_STATUSES = ["PENDING", "ACTIVE", "INACTIVE"] as const;
22
+ const statuses = [...BASE_STATUSES, ...(params.additionalStatuses ?? [])];
23
+
24
+ // Bad: allows replacement
25
+ const statuses = params.statuses ?? ["PENDING", "ACTIVE", "INACTIVE"];
26
+ ```
27
+
28
+ - Name parameter `additionalX` to signal extension-only
29
+ - Parent modules handle their additional status transitions
@@ -1,9 +1,3 @@
1
- ---
2
- paths:
3
- - "modules/*/src/command/*.test.ts"
4
- - "modules/*/src/testing/*.ts"
5
- ---
6
-
7
1
  # Testing Patterns
8
2
 
9
3
  ## Test Coverage Goal
@@ -24,27 +24,35 @@ MODELS → ERRORS → FIXTURES → TESTS → IMPLEMENT → EXPORT → VERIFY
24
24
 
25
25
  ### 1. Database Models (`src/db/`)
26
26
 
27
- See: `.agents/rules/module-development/models.md`
27
+ **Read [models rules](references/models.md) and [db-relations rules](references/db-relations.md) before writing any model.**
28
+
29
+ Key patterns: factory function, `db.fields.timestamps()`, `.description()` on fields, `.relation()` for foreign keys.
28
30
 
29
31
  ### 2. Error Classes (`src/lib/errors.ts`)
30
32
 
31
- See: `.agents/rules/module-development/errors.md`
33
+ **Read [errors rules](references/errors.md) before defining errors.**
34
+
35
+ Key patterns: `name` as const = class name, `code` as const = SCREAMING_SNAKE_CASE.
32
36
 
33
37
  ### 3. Test Fixtures (`src/testing/fixtures.ts`)
34
38
 
35
- See: `.agents/rules/module-development/testing.md`
39
+ **Read [testing rules](references/testing.md) before writing fixtures or tests.**
36
40
 
37
41
  ### 4. Write Tests First (`src/command/*.test.ts`)
38
42
 
39
- See: `.agents/rules/module-development/testing.md`
43
+ Same reference: [testing rules](references/testing.md).
44
+
45
+ Key patterns: `createMockDb<DB>()`, fixed IDs `"entity-1"`, cover all doc branches.
40
46
 
41
47
  ### 5. Implement Commands (`src/command/*.ts`)
42
48
 
43
- See: `.agents/rules/module-development/commands.md`
49
+ **Read [commands rules](references/commands.md) before implementing.**
50
+
51
+ Key patterns: dual function (`_fn` internal + `fn<T>` public), validate → query → mutate.
44
52
 
45
53
  ### 6. Export (`src/index.ts`)
46
54
 
47
- See: `.agents/rules/module-development/exports.md`
55
+ **Read [exports rules](references/exports.md) for export order.**
48
56
 
49
57
  ### 7. Verify
50
58
 
@@ -54,3 +62,13 @@ pnpm lint # Check code style
54
62
  pnpm typecheck # Verify TypeScript types
55
63
  pnpm test # Run all tests
56
64
  ```
65
+
66
+ ## References
67
+
68
+ - [Module structure](references/structure.md)
69
+ - [Model patterns](references/models.md)
70
+ - [Database relations](references/db-relations.md)
71
+ - [Command patterns](references/commands.md)
72
+ - [Error patterns](references/errors.md)
73
+ - [Testing patterns](references/testing.md)
74
+ - [Export order](references/exports.md)
@@ -0,0 +1,45 @@
1
+ # Command Implementation
2
+
3
+ ## Dual Function Pattern
4
+
5
+ Two functions per operation: internal `_fn` and public `fn`.
6
+
7
+ **Internal function (`_myFunction`):**
8
+
9
+ - Takes concrete `DB` type from `generated/kysely-tailordb`
10
+ - Return type uses `Schema` from `lib/types`: `Promise<{ entity: Entity<Schema> }>`
11
+ - Contains actual implementation
12
+
13
+ **Public function (`myFunction`):**
14
+
15
+ - Generic signature: `<T extends { Entity: object }>(db: Kysely<T>, ...)`
16
+ - Return type uses generic `T`: `Promise<{ entity: Entity<T> }>`
17
+ - Delegates to internal with type casting: `_fn(db as unknown as DB, ...) as unknown as Result`
18
+
19
+ ## Implementation Considerations
20
+
21
+ - **Validation**: Check referenced entities exist before operating
22
+ - **Idempotency**: For assign/revoke, return existing instead of throwing
23
+ - **Return format**: Wrap in object `{ entity }` not just `entity`
24
+
25
+ ## Conventions
26
+
27
+ - Input types: exported interfaces (`export interface MyFunctionInput`)
28
+ - Use `.executeTakeFirst()` for single results
29
+ - Include JSDoc: `/** Function: name \n Description */`
30
+
31
+ ## State Transitions
32
+
33
+ For commands that transition between statuses, accept `from?: string[]` with a default:
34
+
35
+ ```typescript
36
+ from?: string[]; // Default: ["ACTIVE"]
37
+
38
+ const validFromStatuses = input.from ?? ["ACTIVE"];
39
+ if (!validFromStatuses.includes(user.status)) {
40
+ throw new InvalidStatusTransitionError(user.status, targetStatus);
41
+ }
42
+ ```
43
+
44
+ - Default `from` contains the base valid source status
45
+ - Parent modules can override to allow transitions from additional statuses
@@ -1,8 +1,3 @@
1
- ---
2
- paths:
3
- - "modules/*/src/db/*.ts"
4
- ---
5
-
6
1
  # Database Relations
7
2
 
8
3
  ## Relation Pattern
@@ -1,8 +1,3 @@
1
- ---
2
- paths:
3
- - "modules/*/src/lib/errors.ts"
4
- ---
5
-
6
1
  # Error Classes
7
2
 
8
3
  Naming convention:
@@ -1,8 +1,3 @@
1
- ---
2
- paths:
3
- - "modules/*/src/index.ts"
4
- ---
5
-
6
1
  # Module Exports
7
2
 
8
3
  Export order:
@@ -0,0 +1,30 @@
1
+ # Database Models
2
+
3
+ ## Factory Function Pattern
4
+
5
+ ```typescript
6
+ export function createEntityType(params: {
7
+ fields?: Record<string, unknown>;
8
+ additionalStatuses?: string[];
9
+ });
10
+ ```
11
+
12
+ - Include `...db.fields.timestamps()`
13
+ - Use `.description()` for field docs
14
+ - Apply permissions at model level
15
+
16
+ ## Stateful Model Enums
17
+
18
+ Status enums are tied to the module's state machine. Base module defines core statuses; parent callers can only **extend, not replace**.
19
+
20
+ ```typescript
21
+ // Good: extension only
22
+ const BASE_STATUSES = ["PENDING", "ACTIVE", "INACTIVE"] as const;
23
+ const statuses = [...BASE_STATUSES, ...(params.additionalStatuses ?? [])];
24
+
25
+ // Bad: allows replacement
26
+ const statuses = params.statuses ?? ["PENDING", "ACTIVE", "INACTIVE"];
27
+ ```
28
+
29
+ - Name parameter `additionalX` to signal extension-only
30
+ - Parent modules handle their additional status transitions
@@ -0,0 +1,22 @@
1
+ # Module Directory Structure
2
+
3
+ ```
4
+ src/
5
+ ├── db/ # Database models (one file per model)
6
+ ├── executor/ # Async executors (record triggers, job functions)
7
+ ├── command/ # Domain commands + tests (*.test.ts co-located)
8
+ ├── lib/ # Internal shared code (errors.ts, types.ts)
9
+ ├── testing/ # Test fixtures and helpers
10
+ ├── generated/ # Auto-generated code (do not edit)
11
+ ├── index.ts # Public exports
12
+ └── module.ts # Module definition
13
+ ```
14
+
15
+ ## Rules
16
+
17
+ - `db/`: Only documentable model definitions, no helpers
18
+ - `executor/`: Async executors as factory functions (see executors.md)
19
+ - `command/`: Domain commands + co-located tests, no utilities
20
+ - `lib/`: Internal errors and types (not documented)
21
+ - `testing/`: Fixtures for tests only
22
+ - Run `pnpm generate` after modifying `db/` models
@@ -0,0 +1,37 @@
1
+ # Testing Patterns
2
+
3
+ ## Test Coverage Goal
4
+
5
+ Tests should cover all paths in the corresponding `docs/commands/*.md`:
6
+
7
+ - **Process Flow**: Each branch in the mermaid flowchart = one test case
8
+ - **Error Scenarios**: Each error code listed = one test case
9
+ - **Idempotent paths**: If flowchart shows "Already exists? → Return existing"
10
+
11
+ ## Mock Database
12
+
13
+ ```typescript
14
+ const { db, spies } = createMockDb<DB>();
15
+
16
+ // Single return
17
+ spies.select.mockReturnValue(entity);
18
+
19
+ // Sequential returns (in query execution order)
20
+ spies.select.mockReturnValueOnce(first).mockReturnValueOnce(second);
21
+ ```
22
+
23
+ ## Custom Fields Passthrough
24
+
25
+ For `makeCreateX` factory commands, add one test verifying custom fields reach `.values()`:
26
+
27
+ - Instantiate with concrete type: `makeCreateX<{ myField: string }>()`
28
+ - Assert with `spies.values`: `expect(spies.values).toHaveBeenNthCalledWith(1, expect.objectContaining({ myField: "value" }))`
29
+ - Use `toHaveBeenNthCalledWith(n, ...)` when multiple inserts occur (e.g., audit events)
30
+
31
+ ## Fixtures (`src/testing/fixtures.ts`)
32
+
33
+ - Import `Schema` from `lib/types` (not `Namespace` from generated code)
34
+ - Pattern: `export const baseEntity = { ... } as const satisfies Entity<Schema>`
35
+ - Fixed IDs for traceability: `"entity-1"`
36
+ - Consistent timestamp: `new Date("2024-01-01T00:00:00.000Z")`
37
+ - `updatedAt: null` for base fixtures
@@ -398,3 +398,11 @@ describe("myCommand", () => {
398
398
  it("throws NOT_FOUND when missing"); // Check: error scenario
399
399
  });
400
400
  ```
401
+
402
+ ## References
403
+
404
+ - [Model patterns](references/models.md)
405
+ - [Command patterns](references/commands.md)
406
+ - [Error patterns](references/errors.md)
407
+ - [Testing patterns](references/testing.md)
408
+ - [Export order](references/exports.md)
@@ -0,0 +1,45 @@
1
+ # Command Implementation
2
+
3
+ ## Dual Function Pattern
4
+
5
+ Two functions per operation: internal `_fn` and public `fn`.
6
+
7
+ **Internal function (`_myFunction`):**
8
+
9
+ - Takes concrete `DB` type from `generated/kysely-tailordb`
10
+ - Return type uses `Schema` from `lib/types`: `Promise<{ entity: Entity<Schema> }>`
11
+ - Contains actual implementation
12
+
13
+ **Public function (`myFunction`):**
14
+
15
+ - Generic signature: `<T extends { Entity: object }>(db: Kysely<T>, ...)`
16
+ - Return type uses generic `T`: `Promise<{ entity: Entity<T> }>`
17
+ - Delegates to internal with type casting: `_fn(db as unknown as DB, ...) as unknown as Result`
18
+
19
+ ## Implementation Considerations
20
+
21
+ - **Validation**: Check referenced entities exist before operating
22
+ - **Idempotency**: For assign/revoke, return existing instead of throwing
23
+ - **Return format**: Wrap in object `{ entity }` not just `entity`
24
+
25
+ ## Conventions
26
+
27
+ - Input types: exported interfaces (`export interface MyFunctionInput`)
28
+ - Use `.executeTakeFirst()` for single results
29
+ - Include JSDoc: `/** Function: name \n Description */`
30
+
31
+ ## State Transitions
32
+
33
+ For commands that transition between statuses, accept `from?: string[]` with a default:
34
+
35
+ ```typescript
36
+ from?: string[]; // Default: ["ACTIVE"]
37
+
38
+ const validFromStatuses = input.from ?? ["ACTIVE"];
39
+ if (!validFromStatuses.includes(user.status)) {
40
+ throw new InvalidStatusTransitionError(user.status, targetStatus);
41
+ }
42
+ ```
43
+
44
+ - Default `from` contains the base valid source status
45
+ - Parent modules can override to allow transitions from additional statuses
@@ -0,0 +1,7 @@
1
+ # Error Classes
2
+
3
+ Naming convention:
4
+
5
+ - `name`: Class name exactly, `as const`
6
+ - `code`: SCREAMING_SNAKE_CASE, `as const`
7
+ - Constructor includes context (IDs, values)
@@ -0,0 +1,8 @@
1
+ # Module Exports
2
+
3
+ Export order:
4
+
5
+ 1. `defineModule` from module.ts
6
+ 2. Generated types (enum values + types separately)
7
+ 3. Error classes (for typed catch blocks)
8
+ 4. Domain functions with input types
@@ -0,0 +1,30 @@
1
+ # Database Models
2
+
3
+ ## Factory Function Pattern
4
+
5
+ ```typescript
6
+ export function createEntityType(params: {
7
+ fields?: Record<string, unknown>;
8
+ additionalStatuses?: string[];
9
+ });
10
+ ```
11
+
12
+ - Include `...db.fields.timestamps()`
13
+ - Use `.description()` for field docs
14
+ - Apply permissions at model level
15
+
16
+ ## Stateful Model Enums
17
+
18
+ Status enums are tied to the module's state machine. Base module defines core statuses; parent callers can only **extend, not replace**.
19
+
20
+ ```typescript
21
+ // Good: extension only
22
+ const BASE_STATUSES = ["PENDING", "ACTIVE", "INACTIVE"] as const;
23
+ const statuses = [...BASE_STATUSES, ...(params.additionalStatuses ?? [])];
24
+
25
+ // Bad: allows replacement
26
+ const statuses = params.statuses ?? ["PENDING", "ACTIVE", "INACTIVE"];
27
+ ```
28
+
29
+ - Name parameter `additionalX` to signal extension-only
30
+ - Parent modules handle their additional status transitions
@@ -0,0 +1,29 @@
1
+ # Testing Patterns
2
+
3
+ ## Test Coverage Goal
4
+
5
+ Tests should cover all paths in the corresponding `docs/commands/*.md`:
6
+
7
+ - **Process Flow**: Each branch in the mermaid flowchart = one test case
8
+ - **Error Scenarios**: Each error code listed = one test case
9
+ - **Idempotent paths**: If flowchart shows "Already exists? → Return existing"
10
+
11
+ ## Mock Database
12
+
13
+ ```typescript
14
+ const { db, spies } = createMockDb<DB>();
15
+
16
+ // Single return
17
+ spies.select.mockReturnValue(entity);
18
+
19
+ // Sequential returns (in query execution order)
20
+ spies.select.mockReturnValueOnce(first).mockReturnValueOnce(second);
21
+ ```
22
+
23
+ ## Fixtures (`src/testing/fixtures.ts`)
24
+
25
+ - Import `Schema` from `lib/types` (not `Namespace` from generated code)
26
+ - Pattern: `export const baseEntity = { ... } as const satisfies Entity<Schema>`
27
+ - Fixed IDs for traceability: `"entity-1"`
28
+ - Consistent timestamp: `new Date("2024-01-01T00:00:00.000Z")`
29
+ - `updatedAt: null` for base fixtures
@@ -83,3 +83,7 @@ pnpm run app:doc:check
83
83
  ## Next Step
84
84
 
85
85
  After completing Tier 1-2, use `/app-compose-2-requirements-breakdown` to create Tier 3 documentation.
86
+
87
+ ## References
88
+
89
+ - [Application structure](references/structure.md)