superdev-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/.claude-plugin/marketplace.json +33 -0
  2. package/.claude-plugin/plugin.json +21 -0
  3. package/.codex-plugin/plugin.json +27 -0
  4. package/CODE_OF_CONDUCT.md +109 -0
  5. package/CONTRIBUTING.md +205 -0
  6. package/LICENSE +202 -0
  7. package/NOTICE +11 -0
  8. package/README.md +1051 -0
  9. package/SECURITY.md +135 -0
  10. package/THIRD-PARTY-NOTICES.md +253 -0
  11. package/hooks/hooks.json +61 -0
  12. package/hooks/run.mjs +88 -0
  13. package/package.json +65 -0
  14. package/references/confidentiality.md +48 -0
  15. package/references/evidence-and-risk.md +73 -0
  16. package/references/operating-model.md +105 -0
  17. package/references/platform-capabilities.md +51 -0
  18. package/references/project-record.md +91 -0
  19. package/references/provider-contracts.md +102 -0
  20. package/scripts/doctor/doctor.mjs +539 -0
  21. package/scripts/privacy/scan-history.mjs +163 -0
  22. package/scripts/privacy/scan.mjs +368 -0
  23. package/scripts/validate/README.md +94 -0
  24. package/scripts/validate/common.mjs +149 -0
  25. package/scripts/validate/data-model.mjs +225 -0
  26. package/scripts/validate/dependencies.mjs +69 -0
  27. package/scripts/validate/docs-templates.mjs +99 -0
  28. package/scripts/validate/footprint.mjs +78 -0
  29. package/scripts/validate/imports.mjs +61 -0
  30. package/scripts/validate/manifests.mjs +173 -0
  31. package/scripts/validate/markdown.mjs +123 -0
  32. package/scripts/validate/migrations.mjs +187 -0
  33. package/scripts/validate/no-tests.mjs +136 -0
  34. package/scripts/validate/privacy.mjs +115 -0
  35. package/scripts/validate/record-links.mjs +127 -0
  36. package/scripts/validate/skill-commands.mjs +190 -0
  37. package/scripts/validate/skills.mjs +112 -0
  38. package/scripts/validate/specification.mjs +88 -0
  39. package/scripts/validate/style.mjs +78 -0
  40. package/scripts/validate/validate-all.mjs +175 -0
  41. package/skills/debug/SKILL.md +68 -0
  42. package/skills/decision/SKILL.md +99 -0
  43. package/skills/docs/SKILL.md +86 -0
  44. package/skills/docs/assets/fragments/api/events.md +14 -0
  45. package/skills/docs/assets/fragments/api/graphql.md +14 -0
  46. package/skills/docs/assets/fragments/api/local-only.md +13 -0
  47. package/skills/docs/assets/fragments/api/rest.md +13 -0
  48. package/skills/docs/assets/fragments/api/rpc.md +13 -0
  49. package/skills/docs/assets/fragments/async/event-bus.md +13 -0
  50. package/skills/docs/assets/fragments/async/none.md +11 -0
  51. package/skills/docs/assets/fragments/async/platform-jobs.md +13 -0
  52. package/skills/docs/assets/fragments/async/queue.md +13 -0
  53. package/skills/docs/assets/fragments/async/scheduler.md +13 -0
  54. package/skills/docs/assets/fragments/auth/detected-provider.md +15 -0
  55. package/skills/docs/assets/fragments/auth/neutral-base.md +14 -0
  56. package/skills/docs/assets/fragments/data/document.md +13 -0
  57. package/skills/docs/assets/fragments/data/external-saas.md +13 -0
  58. package/skills/docs/assets/fragments/data/key-value.md +13 -0
  59. package/skills/docs/assets/fragments/data/none.md +12 -0
  60. package/skills/docs/assets/fragments/data/sql-orm.md +13 -0
  61. package/skills/docs/assets/fragments/data/sql-plain.md +13 -0
  62. package/skills/docs/assets/fragments/env/conventional.md +13 -0
  63. package/skills/docs/assets/fragments/env/envx.md +13 -0
  64. package/skills/docs/assets/fragments/env/managed-platform.md +13 -0
  65. package/skills/docs/assets/fragments/env/none.md +11 -0
  66. package/skills/docs/assets/fragments/ui/api-only.md +13 -0
  67. package/skills/docs/assets/fragments/ui/cli.md +14 -0
  68. package/skills/docs/assets/fragments/ui/desktop.md +14 -0
  69. package/skills/docs/assets/fragments/ui/mobile.md +14 -0
  70. package/skills/docs/assets/fragments/ui/web.md +14 -0
  71. package/skills/docs/assets/templates/adr.md +62 -0
  72. package/skills/docs/assets/templates/api.md +30 -0
  73. package/skills/docs/assets/templates/architecture.md +52 -0
  74. package/skills/docs/assets/templates/change-impact-drift-report.md +29 -0
  75. package/skills/docs/assets/templates/compliance.md +29 -0
  76. package/skills/docs/assets/templates/data-schema.md +43 -0
  77. package/skills/docs/assets/templates/feature.md +43 -0
  78. package/skills/docs/assets/templates/foundations.md +55 -0
  79. package/skills/docs/assets/templates/jobs-webhooks.md +36 -0
  80. package/skills/docs/assets/templates/module-inventory.md +19 -0
  81. package/skills/docs/assets/templates/module.md +50 -0
  82. package/skills/docs/assets/templates/nfr.md +22 -0
  83. package/skills/docs/assets/templates/observability.md +28 -0
  84. package/skills/docs/assets/templates/pages-ui-actions.md +47 -0
  85. package/skills/docs/assets/templates/project-summary.md +44 -0
  86. package/skills/docs/assets/templates/roles-permissions.md +26 -0
  87. package/skills/docs/assets/templates/test-plan.md +23 -0
  88. package/skills/docs/assets/templates/workflow-state-machine.md +43 -0
  89. package/skills/docs/references/adr-authoring.md +24 -0
  90. package/skills/docs/references/apis-and-data.md +23 -0
  91. package/skills/docs/references/capability-fragments.md +25 -0
  92. package/skills/docs/references/change-tracking.md +62 -0
  93. package/skills/docs/references/diagrams.md +31 -0
  94. package/skills/docs/references/discovery.md +59 -0
  95. package/skills/docs/references/edge-cases.md +45 -0
  96. package/skills/docs/references/foundations-modules.md +18 -0
  97. package/skills/docs/references/ingestion.md +52 -0
  98. package/skills/docs/references/initialize-adopt.md +25 -0
  99. package/skills/docs/references/module-decomposition.md +38 -0
  100. package/skills/docs/references/profiles.md +60 -0
  101. package/skills/docs/references/quality-attributes.md +27 -0
  102. package/skills/docs/references/reverse-engineer.md +21 -0
  103. package/skills/docs/references/spec-depths.md +31 -0
  104. package/skills/docs/references/summarize.md +18 -0
  105. package/skills/docs/references/surfaces-and-actions.md +24 -0
  106. package/skills/docs/references/validation.md +47 -0
  107. package/skills/docs/references/workflows-and-jobs.md +25 -0
  108. package/skills/docs/scripts/ingest.mjs +984 -0
  109. package/skills/docs/scripts/profile-detect.mjs +299 -0
  110. package/skills/docs/scripts/screen.mjs +96 -0
  111. package/skills/docs/scripts/template-lint.mjs +143 -0
  112. package/skills/docs/scripts/validate-docs.mjs +363 -0
  113. package/skills/doctor/SKILL.md +167 -0
  114. package/skills/feature/SKILL.md +132 -0
  115. package/skills/init/SKILL.md +137 -0
  116. package/skills/project/SKILL.md +261 -0
  117. package/skills/project/references/commands.md +213 -0
  118. package/skills/resume/SKILL.md +79 -0
  119. package/skills/review/SKILL.md +91 -0
  120. package/skills/status/SKILL.md +85 -0
  121. package/skills/task/SKILL.md +183 -0
  122. package/src/cli/product-map.mjs +468 -0
  123. package/src/cli/render.mjs +544 -0
  124. package/src/cli.mjs +2774 -0
  125. package/src/cloud/crypto.mjs +118 -0
  126. package/src/cloud/merge.mjs +186 -0
  127. package/src/cloud/policy.mjs +115 -0
  128. package/src/cloud/sync.mjs +512 -0
  129. package/src/cloud/transport.mjs +116 -0
  130. package/src/db/connect.mjs +189 -0
  131. package/src/db/maintenance.mjs +419 -0
  132. package/src/db/migrate.mjs +188 -0
  133. package/src/db/migrations/001_initial.sql +1024 -0
  134. package/src/db/migrations/002_docs_coverage.sql +126 -0
  135. package/src/db/migrations/003_memory_retrieval_and_integrity.sql +168 -0
  136. package/src/db/migrations/004_task_categories.sql +59 -0
  137. package/src/db/migrations/005_executable_evidence.sql +32 -0
  138. package/src/db/migrations/006_module_and_feature_boundaries.sql +27 -0
  139. package/src/db/migrations/007_drop_redundant_module_users.sql +13 -0
  140. package/src/db/migrations/008_changes_assumptions_test_plans_api_services.sql +166 -0
  141. package/src/db/migrations/009_retired_documents.sql +54 -0
  142. package/src/db/migrations/010_test_plan_evidence.sql +20 -0
  143. package/src/db/migrations/011_criterion_waiver.sql +13 -0
  144. package/src/db/migrations/012_synchronization.sql +56 -0
  145. package/src/db/store.mjs +327 -0
  146. package/src/decisions/record.mjs +167 -0
  147. package/src/docs/proposals.mjs +871 -0
  148. package/src/docs/render.mjs +1424 -0
  149. package/src/docs/templates.mjs +1281 -0
  150. package/src/features/acceptance.mjs +315 -0
  151. package/src/features/specify.mjs +248 -0
  152. package/src/init/discovery.mjs +901 -0
  153. package/src/init/index.mjs +784 -0
  154. package/src/init/questions.mjs +562 -0
  155. package/src/memory/benchmark.mjs +171 -0
  156. package/src/memory/capture.mjs +135 -0
  157. package/src/memory/consolidate.mjs +255 -0
  158. package/src/memory/index.mjs +810 -0
  159. package/src/model/ids.mjs +185 -0
  160. package/src/model/screening.mjs +150 -0
  161. package/src/model/toolkit.mjs +258 -0
  162. package/src/model/vocabulary.mjs +190 -0
  163. package/src/product/assumptions.mjs +120 -0
  164. package/src/product/changes.mjs +180 -0
  165. package/src/product/test-plans.mjs +183 -0
  166. package/src/progress/index.mjs +1364 -0
  167. package/src/runtime/harness.mjs +264 -0
  168. package/src/runtime/hooks.mjs +1021 -0
  169. package/src/runtime/identity.mjs +305 -0
  170. package/src/runtime/resume.mjs +343 -0
  171. package/src/runtime/session.mjs +679 -0
  172. package/src/runtime/version.mjs +315 -0
  173. package/src/service/assets/control-center.html +206 -0
  174. package/src/service/assets/control-center.manifest.json +7 -0
  175. package/src/service/manage.mjs +542 -0
  176. package/src/service/mutations.mjs +860 -0
  177. package/src/service/read-model.mjs +1557 -0
  178. package/src/service/server.mjs +783 -0
  179. package/src/tasks/categories.mjs +154 -0
  180. package/src/tasks/derive.mjs +977 -0
  181. package/src/tasks/lifecycle.mjs +830 -0
  182. package/src/verify/index.mjs +236 -0
@@ -0,0 +1,1024 @@
1
+ -- Superdev normalized schema, version 1.
2
+ --
3
+ -- Typed tables with real foreign keys. There is deliberately no generic node
4
+ -- table with a JSON field bag: the store has to be able to enforce what a
5
+ -- feature or a task actually requires. See ADR-0015.
6
+ --
7
+ -- Engine notes that shaped this file (measured, see ADR-0013):
8
+ -- - triggers with RAISE(ABORT, ...) are enforced, so the task mapping
9
+ -- constraint lives here rather than in application code
10
+ -- - WITH RECURSIVE is unavailable, so hierarchy walks are iterative in
11
+ -- application code and no view depends on recursion
12
+ -- - CHECK constraints are enforced
13
+
14
+ -- ---------------------------------------------------------------- 10.1 project
15
+
16
+ CREATE TABLE projects (
17
+ id TEXT PRIMARY KEY,
18
+ name TEXT NOT NULL,
19
+ slug TEXT NOT NULL UNIQUE,
20
+ statement TEXT,
21
+ problem TEXT,
22
+ status TEXT NOT NULL DEFAULT 'draft',
23
+ working_mode TEXT NOT NULL DEFAULT 'guided',
24
+ docs_profile TEXT NOT NULL DEFAULT 'talks-v1',
25
+ local_authority INTEGER NOT NULL DEFAULT 1,
26
+ created_at TEXT NOT NULL,
27
+ updated_at TEXT NOT NULL,
28
+ version INTEGER NOT NULL DEFAULT 1
29
+ );
30
+
31
+ CREATE TABLE source_material (
32
+ id TEXT PRIMARY KEY,
33
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
34
+ path_or_label TEXT NOT NULL,
35
+ content_hash TEXT NOT NULL,
36
+ source_type TEXT NOT NULL,
37
+ supplied_by TEXT,
38
+ received_at TEXT NOT NULL,
39
+ screening_status TEXT NOT NULL DEFAULT 'pending'
40
+ CHECK (screening_status IN ('pending','clean','redacted','rejected')),
41
+ redaction_summary TEXT,
42
+ immutable_revision TEXT NOT NULL
43
+ );
44
+ CREATE INDEX source_material_project ON source_material(project_id);
45
+
46
+ CREATE TABLE discovery_items (
47
+ id TEXT PRIMARY KEY,
48
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
49
+ source_id TEXT REFERENCES source_material(id) ON DELETE SET NULL,
50
+ kind TEXT NOT NULL CHECK (kind IN (
51
+ 'problem','user','goal_candidate','feature_candidate',
52
+ 'constraint','assumption','risk','unknown','exclusion','capability')),
53
+ statement TEXT NOT NULL,
54
+ epistemic_status TEXT NOT NULL DEFAULT 'inferred'
55
+ CHECK (epistemic_status IN ('confirmed','inferred','assumed','unknown','contradicted','declined')),
56
+ provenance TEXT,
57
+ status TEXT NOT NULL DEFAULT 'proposed'
58
+ CHECK (status IN ('proposed','accepted','rejected','converted','superseded')),
59
+ converted_type TEXT,
60
+ converted_id TEXT,
61
+ position_x REAL,
62
+ position_y REAL,
63
+ accepted_at TEXT,
64
+ created_at TEXT NOT NULL,
65
+ version INTEGER NOT NULL DEFAULT 1
66
+ );
67
+ CREATE INDEX discovery_items_project ON discovery_items(project_id, kind, status);
68
+
69
+ CREATE TABLE discovery_links (
70
+ from_id TEXT NOT NULL REFERENCES discovery_items(id) ON DELETE CASCADE,
71
+ to_id TEXT NOT NULL REFERENCES discovery_items(id) ON DELETE CASCADE,
72
+ relationship TEXT NOT NULL,
73
+ PRIMARY KEY (from_id, to_id, relationship)
74
+ );
75
+
76
+ CREATE TABLE questions (
77
+ id TEXT PRIMARY KEY,
78
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
79
+ scope_type TEXT NOT NULL,
80
+ scope_id TEXT,
81
+ question TEXT NOT NULL,
82
+ why_it_matters TEXT NOT NULL,
83
+ recommendation TEXT,
84
+ alternatives_json TEXT NOT NULL DEFAULT '[]',
85
+ status TEXT NOT NULL DEFAULT 'open'
86
+ CHECK (status IN ('open','answered','deferred','withdrawn')),
87
+ answer TEXT,
88
+ answered_by TEXT,
89
+ answered_at TEXT,
90
+ deferral_reason TEXT,
91
+ created_at TEXT NOT NULL,
92
+ version INTEGER NOT NULL DEFAULT 1
93
+ );
94
+ CREATE INDEX questions_project ON questions(project_id, status);
95
+
96
+ -- ------------------------------------------------------- 10.2 product definition
97
+
98
+ CREATE TABLE goals (
99
+ id TEXT PRIMARY KEY,
100
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
101
+ name TEXT NOT NULL,
102
+ description TEXT,
103
+ why_it_matters TEXT,
104
+ status TEXT NOT NULL DEFAULT 'draft',
105
+ priority TEXT,
106
+ sequence INTEGER NOT NULL DEFAULT 0,
107
+ created_at TEXT NOT NULL,
108
+ updated_at TEXT NOT NULL,
109
+ version INTEGER NOT NULL DEFAULT 1
110
+ );
111
+ CREATE INDEX goals_project ON goals(project_id, status);
112
+
113
+ CREATE TABLE goal_success_criteria (
114
+ id TEXT PRIMARY KEY,
115
+ goal_id TEXT NOT NULL REFERENCES goals(id) ON DELETE CASCADE,
116
+ criterion TEXT NOT NULL,
117
+ measurement_method TEXT,
118
+ target TEXT,
119
+ status TEXT NOT NULL DEFAULT 'unmet'
120
+ CHECK (status IN ('unmet','met','unmeasured','not_applicable')),
121
+ sequence INTEGER NOT NULL DEFAULT 0
122
+ );
123
+ CREATE INDEX goal_success_criteria_goal ON goal_success_criteria(goal_id);
124
+
125
+ CREATE TABLE milestones (
126
+ id TEXT PRIMARY KEY,
127
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
128
+ name TEXT NOT NULL,
129
+ outcome TEXT,
130
+ entry_conditions_json TEXT NOT NULL DEFAULT '[]',
131
+ exit_conditions_json TEXT NOT NULL DEFAULT '[]',
132
+ target_date TEXT,
133
+ status TEXT NOT NULL DEFAULT 'draft',
134
+ sequence INTEGER NOT NULL DEFAULT 0,
135
+ created_at TEXT NOT NULL,
136
+ updated_at TEXT NOT NULL,
137
+ version INTEGER NOT NULL DEFAULT 1
138
+ );
139
+ CREATE INDEX milestones_project ON milestones(project_id, sequence);
140
+
141
+ CREATE TABLE modules (
142
+ id TEXT PRIMARY KEY,
143
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
144
+ name TEXT NOT NULL,
145
+ slug TEXT NOT NULL,
146
+ purpose TEXT,
147
+ primary_users_json TEXT NOT NULL DEFAULT '[]',
148
+ owns_json TEXT NOT NULL DEFAULT '[]',
149
+ consumes_json TEXT NOT NULL DEFAULT '[]',
150
+ status TEXT NOT NULL DEFAULT 'planned'
151
+ CHECK (status IN ('planned','in_progress','implemented','deprecated')),
152
+ sequence INTEGER NOT NULL DEFAULT 0,
153
+ created_at TEXT NOT NULL,
154
+ updated_at TEXT NOT NULL,
155
+ version INTEGER NOT NULL DEFAULT 1,
156
+ UNIQUE (project_id, slug)
157
+ );
158
+
159
+ CREATE TABLE features (
160
+ id TEXT PRIMARY KEY,
161
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
162
+ module_id TEXT NOT NULL REFERENCES modules(id) ON DELETE RESTRICT,
163
+ milestone_id TEXT REFERENCES milestones(id) ON DELETE SET NULL,
164
+ name TEXT NOT NULL,
165
+ slug TEXT NOT NULL,
166
+ purpose TEXT,
167
+ user_statement TEXT,
168
+ user_value TEXT,
169
+ spec_depth TEXT NOT NULL DEFAULT 'standard'
170
+ CHECK (spec_depth IN ('microspec','standard','full')),
171
+ risk_level TEXT NOT NULL DEFAULT 'R1'
172
+ CHECK (risk_level IN ('R0','R1','R2','R3','R4')),
173
+ scope_in_json TEXT NOT NULL DEFAULT '[]',
174
+ scope_out_json TEXT NOT NULL DEFAULT '[]',
175
+ status TEXT NOT NULL DEFAULT 'draft',
176
+ priority TEXT,
177
+ what_works_now TEXT,
178
+ what_remains TEXT,
179
+ next_action TEXT,
180
+ accepted_at TEXT,
181
+ created_at TEXT NOT NULL,
182
+ updated_at TEXT NOT NULL,
183
+ version INTEGER NOT NULL DEFAULT 1,
184
+ UNIQUE (project_id, slug)
185
+ );
186
+ CREATE INDEX features_module ON features(module_id, status);
187
+ CREATE INDEX features_milestone ON features(milestone_id);
188
+
189
+ -- The primary flow, normalized rather than a JSON column, so it is queryable
190
+ -- and a workflow step can point at the flow step it realizes.
191
+ CREATE TABLE feature_flows (
192
+ id TEXT PRIMARY KEY,
193
+ feature_id TEXT NOT NULL REFERENCES features(id) ON DELETE CASCADE,
194
+ sequence INTEGER NOT NULL,
195
+ step TEXT NOT NULL,
196
+ UNIQUE (feature_id, sequence)
197
+ );
198
+
199
+ CREATE TABLE feature_goals (
200
+ feature_id TEXT NOT NULL REFERENCES features(id) ON DELETE CASCADE,
201
+ goal_id TEXT NOT NULL REFERENCES goals(id) ON DELETE CASCADE,
202
+ PRIMARY KEY (feature_id, goal_id)
203
+ );
204
+
205
+ CREATE TABLE feature_acceptance_criteria (
206
+ id TEXT PRIMARY KEY,
207
+ feature_id TEXT NOT NULL REFERENCES features(id) ON DELETE CASCADE,
208
+ criterion TEXT NOT NULL,
209
+ verification_method TEXT,
210
+ status TEXT NOT NULL DEFAULT 'unmet'
211
+ CHECK (status IN ('unmet','met','waived')),
212
+ evidence_id TEXT,
213
+ sequence INTEGER NOT NULL DEFAULT 0
214
+ );
215
+ CREATE INDEX feature_acceptance_criteria_feature ON feature_acceptance_criteria(feature_id);
216
+
217
+ CREATE TABLE feature_edge_cases (
218
+ id TEXT PRIMARY KEY,
219
+ feature_id TEXT NOT NULL REFERENCES features(id) ON DELETE CASCADE,
220
+ category TEXT NOT NULL,
221
+ applicability TEXT NOT NULL DEFAULT 'applicable'
222
+ CHECK (applicability IN ('applicable','not_applicable')),
223
+ behavior TEXT,
224
+ reason_not_applicable TEXT,
225
+ -- A category marked not applicable without a reason is the exact silent gap
226
+ -- the Docs methodology calls a defect, so the store refuses it.
227
+ CHECK (applicability = 'applicable' OR reason_not_applicable IS NOT NULL),
228
+ UNIQUE (feature_id, category)
229
+ );
230
+
231
+ -- --------------------------------------------------- 10.3 workflows and state
232
+
233
+ CREATE TABLE workflows (
234
+ id TEXT PRIMARY KEY,
235
+ feature_id TEXT NOT NULL REFERENCES features(id) ON DELETE CASCADE,
236
+ name TEXT NOT NULL,
237
+ purpose TEXT,
238
+ trigger TEXT,
239
+ preconditions_json TEXT NOT NULL DEFAULT '[]',
240
+ completion_criteria TEXT,
241
+ observability TEXT,
242
+ status TEXT NOT NULL DEFAULT 'draft',
243
+ created_at TEXT NOT NULL,
244
+ updated_at TEXT NOT NULL,
245
+ version INTEGER NOT NULL DEFAULT 1
246
+ );
247
+ CREATE INDEX workflows_feature ON workflows(feature_id);
248
+
249
+ CREATE TABLE workflow_actors (
250
+ id TEXT PRIMARY KEY,
251
+ workflow_id TEXT NOT NULL REFERENCES workflows(id) ON DELETE CASCADE,
252
+ actor TEXT NOT NULL,
253
+ actor_type TEXT NOT NULL DEFAULT 'person'
254
+ CHECK (actor_type IN ('person','system','job','external')),
255
+ sequence INTEGER NOT NULL DEFAULT 0,
256
+ UNIQUE (workflow_id, actor)
257
+ );
258
+
259
+ CREATE TABLE workflow_steps (
260
+ id TEXT PRIMARY KEY,
261
+ workflow_id TEXT NOT NULL REFERENCES workflows(id) ON DELETE CASCADE,
262
+ sequence INTEGER NOT NULL,
263
+ owner_type TEXT NOT NULL DEFAULT 'person'
264
+ CHECK (owner_type IN ('person','system','job','external')),
265
+ owner_ref TEXT,
266
+ action TEXT NOT NULL,
267
+ input_contract TEXT,
268
+ expected_result TEXT,
269
+ failure_behavior TEXT,
270
+ status TEXT NOT NULL DEFAULT 'draft',
271
+ created_at TEXT NOT NULL,
272
+ updated_at TEXT NOT NULL,
273
+ version INTEGER NOT NULL DEFAULT 1,
274
+ UNIQUE (workflow_id, sequence)
275
+ );
276
+
277
+ CREATE TABLE workflow_branches (
278
+ id TEXT PRIMARY KEY,
279
+ workflow_id TEXT NOT NULL REFERENCES workflows(id) ON DELETE CASCADE,
280
+ from_step_id TEXT NOT NULL REFERENCES workflow_steps(id) ON DELETE CASCADE,
281
+ condition TEXT NOT NULL,
282
+ to_step_id TEXT REFERENCES workflow_steps(id) ON DELETE SET NULL,
283
+ branch_type TEXT NOT NULL DEFAULT 'alternate'
284
+ CHECK (branch_type IN ('alternate','failure','retry','compensate','abort')),
285
+ failure_policy TEXT
286
+ );
287
+ CREATE INDEX workflow_branches_workflow ON workflow_branches(workflow_id);
288
+
289
+ CREATE TABLE state_machines (
290
+ id TEXT PRIMARY KEY,
291
+ module_id TEXT REFERENCES modules(id) ON DELETE CASCADE,
292
+ feature_id TEXT REFERENCES features(id) ON DELETE CASCADE,
293
+ entity_name TEXT NOT NULL,
294
+ initial_state TEXT,
295
+ status TEXT NOT NULL DEFAULT 'draft',
296
+ created_at TEXT NOT NULL,
297
+ version INTEGER NOT NULL DEFAULT 1,
298
+ CHECK (module_id IS NOT NULL OR feature_id IS NOT NULL)
299
+ );
300
+
301
+ CREATE TABLE states (
302
+ id TEXT PRIMARY KEY,
303
+ state_machine_id TEXT NOT NULL REFERENCES state_machines(id) ON DELETE CASCADE,
304
+ name TEXT NOT NULL,
305
+ meaning TEXT,
306
+ terminal INTEGER NOT NULL DEFAULT 0,
307
+ permitted_actions_json TEXT NOT NULL DEFAULT '[]',
308
+ sequence INTEGER NOT NULL DEFAULT 0,
309
+ UNIQUE (state_machine_id, name)
310
+ );
311
+
312
+ CREATE TABLE state_transitions (
313
+ id TEXT PRIMARY KEY,
314
+ state_machine_id TEXT NOT NULL REFERENCES state_machines(id) ON DELETE CASCADE,
315
+ from_state_id TEXT NOT NULL REFERENCES states(id) ON DELETE CASCADE,
316
+ event TEXT NOT NULL,
317
+ guard TEXT,
318
+ to_state_id TEXT NOT NULL REFERENCES states(id) ON DELETE CASCADE,
319
+ actor TEXT,
320
+ timeout_rule TEXT,
321
+ enforcement_point TEXT
322
+ );
323
+ CREATE INDEX state_transitions_machine ON state_transitions(state_machine_id);
324
+
325
+ -- ------------------------------------------------------------------- 10.4 UI
326
+
327
+ CREATE TABLE surfaces (
328
+ id TEXT PRIMARY KEY,
329
+ feature_id TEXT REFERENCES features(id) ON DELETE CASCADE,
330
+ module_id TEXT NOT NULL REFERENCES modules(id) ON DELETE CASCADE,
331
+ name TEXT NOT NULL,
332
+ surface_type TEXT NOT NULL DEFAULT 'page',
333
+ route TEXT,
334
+ purpose TEXT,
335
+ primary_role TEXT,
336
+ components_json TEXT NOT NULL DEFAULT '[]',
337
+ entities_shown_json TEXT NOT NULL DEFAULT '[]',
338
+ responsive_behavior TEXT,
339
+ accessibility_notes TEXT,
340
+ status TEXT NOT NULL DEFAULT 'draft',
341
+ created_at TEXT NOT NULL,
342
+ updated_at TEXT NOT NULL,
343
+ version INTEGER NOT NULL DEFAULT 1
344
+ );
345
+ CREATE INDEX surfaces_module ON surfaces(module_id);
346
+ CREATE INDEX surfaces_feature ON surfaces(feature_id);
347
+
348
+ CREATE TABLE surface_states (
349
+ id TEXT PRIMARY KEY,
350
+ surface_id TEXT NOT NULL REFERENCES surfaces(id) ON DELETE CASCADE,
351
+ state_type TEXT NOT NULL CHECK (state_type IN
352
+ ('loading','empty','success','error','disabled','offline','stale','unavailable')),
353
+ behavior TEXT,
354
+ copy TEXT,
355
+ UNIQUE (surface_id, state_type)
356
+ );
357
+
358
+ CREATE TABLE ui_actions (
359
+ id TEXT PRIMARY KEY,
360
+ surface_id TEXT NOT NULL REFERENCES surfaces(id) ON DELETE CASCADE,
361
+ name TEXT NOT NULL,
362
+ label TEXT,
363
+ trigger TEXT,
364
+ role TEXT,
365
+ permission TEXT,
366
+ enforcement_point TEXT,
367
+ precondition TEXT,
368
+ effect TEXT,
369
+ input_contract TEXT,
370
+ validation TEXT,
371
+ side_effects_json TEXT NOT NULL DEFAULT '[]',
372
+ confirmation TEXT,
373
+ loading_behavior TEXT,
374
+ disabled_behavior TEXT,
375
+ success_behavior TEXT,
376
+ error_behavior TEXT,
377
+ keyboard TEXT,
378
+ accessible_name TEXT,
379
+ focus_behavior TEXT,
380
+ status TEXT NOT NULL DEFAULT 'draft',
381
+ created_at TEXT NOT NULL,
382
+ updated_at TEXT NOT NULL,
383
+ version INTEGER NOT NULL DEFAULT 1
384
+ );
385
+ CREATE INDEX ui_actions_surface ON ui_actions(surface_id);
386
+
387
+ -- --------------------------------------------- 10.5 API, data and integrations
388
+
389
+ CREATE TABLE api_operations (
390
+ id TEXT PRIMARY KEY,
391
+ feature_id TEXT REFERENCES features(id) ON DELETE CASCADE,
392
+ module_id TEXT NOT NULL REFERENCES modules(id) ON DELETE CASCADE,
393
+ name TEXT NOT NULL,
394
+ style TEXT NOT NULL DEFAULT 'rest'
395
+ CHECK (style IN ('rest','graphql','rpc','events','local-only')),
396
+ method_or_procedure TEXT,
397
+ path_or_topic TEXT,
398
+ purpose TEXT,
399
+ auth_requirement TEXT,
400
+ permission TEXT,
401
+ enforcement_point TEXT,
402
+ request_contract_json TEXT NOT NULL DEFAULT '{}',
403
+ response_contract_json TEXT NOT NULL DEFAULT '{}',
404
+ error_contract_json TEXT NOT NULL DEFAULT '[]',
405
+ idempotency TEXT,
406
+ limits TEXT,
407
+ side_effects_json TEXT NOT NULL DEFAULT '[]',
408
+ versioning TEXT,
409
+ implementation_path TEXT,
410
+ status TEXT NOT NULL DEFAULT 'draft',
411
+ created_at TEXT NOT NULL,
412
+ updated_at TEXT NOT NULL,
413
+ version INTEGER NOT NULL DEFAULT 1
414
+ );
415
+ CREATE INDEX api_operations_module ON api_operations(module_id);
416
+ CREATE INDEX api_operations_feature ON api_operations(feature_id);
417
+
418
+ CREATE TABLE data_entities (
419
+ id TEXT PRIMARY KEY,
420
+ module_id TEXT NOT NULL REFERENCES modules(id) ON DELETE CASCADE,
421
+ feature_id TEXT REFERENCES features(id) ON DELETE SET NULL,
422
+ name TEXT NOT NULL,
423
+ purpose TEXT,
424
+ store TEXT,
425
+ schema_source TEXT,
426
+ sensitivity_class TEXT NOT NULL DEFAULT 'none'
427
+ CHECK (sensitivity_class IN ('none','personal','financial','secret','regulated')),
428
+ retention_rule TEXT,
429
+ deletion_semantics TEXT,
430
+ status TEXT NOT NULL DEFAULT 'draft',
431
+ position_x REAL,
432
+ position_y REAL,
433
+ created_at TEXT NOT NULL,
434
+ updated_at TEXT NOT NULL,
435
+ version INTEGER NOT NULL DEFAULT 1
436
+ );
437
+ CREATE INDEX data_entities_module ON data_entities(module_id);
438
+
439
+ CREATE TABLE data_fields (
440
+ id TEXT PRIMARY KEY,
441
+ entity_id TEXT NOT NULL REFERENCES data_entities(id) ON DELETE CASCADE,
442
+ name TEXT NOT NULL,
443
+ type TEXT NOT NULL,
444
+ nullable INTEGER NOT NULL DEFAULT 1,
445
+ default_value TEXT,
446
+ constraints_json TEXT NOT NULL DEFAULT '[]',
447
+ sensitivity_class TEXT NOT NULL DEFAULT 'none'
448
+ CHECK (sensitivity_class IN ('none','personal','financial','secret','regulated')),
449
+ sequence INTEGER NOT NULL DEFAULT 0,
450
+ UNIQUE (entity_id, name)
451
+ );
452
+
453
+ CREATE TABLE data_relationships (
454
+ id TEXT PRIMARY KEY,
455
+ from_entity_id TEXT NOT NULL REFERENCES data_entities(id) ON DELETE CASCADE,
456
+ to_entity_id TEXT NOT NULL REFERENCES data_entities(id) ON DELETE CASCADE,
457
+ name TEXT NOT NULL,
458
+ cardinality TEXT NOT NULL DEFAULT '1:n'
459
+ CHECK (cardinality IN ('1:1','1:n','n:1','n:m')),
460
+ on_delete TEXT CHECK (on_delete IN ('cascade','restrict','set_null','no_action')),
461
+ ownership_note TEXT
462
+ );
463
+ CREATE INDEX data_relationships_from ON data_relationships(from_entity_id);
464
+ CREATE INDEX data_relationships_to ON data_relationships(to_entity_id);
465
+
466
+ CREATE TABLE schema_migrations (
467
+ id TEXT PRIMARY KEY,
468
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
469
+ feature_id TEXT REFERENCES features(id) ON DELETE SET NULL,
470
+ name TEXT NOT NULL,
471
+ sequence INTEGER NOT NULL,
472
+ forward_plan TEXT,
473
+ rollback_plan TEXT,
474
+ compatibility_notes TEXT,
475
+ status TEXT NOT NULL DEFAULT 'planned'
476
+ CHECK (status IN ('planned','applied','rolled_back','superseded')),
477
+ applied_at TEXT,
478
+ version INTEGER NOT NULL DEFAULT 1
479
+ );
480
+
481
+ CREATE TABLE schema_migration_entities (
482
+ migration_id TEXT NOT NULL REFERENCES schema_migrations(id) ON DELETE CASCADE,
483
+ entity_id TEXT NOT NULL REFERENCES data_entities(id) ON DELETE CASCADE,
484
+ PRIMARY KEY (migration_id, entity_id)
485
+ );
486
+
487
+ CREATE TABLE integrations (
488
+ id TEXT PRIMARY KEY,
489
+ feature_id TEXT REFERENCES features(id) ON DELETE CASCADE,
490
+ module_id TEXT NOT NULL REFERENCES modules(id) ON DELETE CASCADE,
491
+ name TEXT NOT NULL,
492
+ purpose TEXT,
493
+ provider TEXT,
494
+ environments_json TEXT NOT NULL DEFAULT '[]',
495
+ auth_approach TEXT,
496
+ configuration_status TEXT NOT NULL DEFAULT 'unconfigured'
497
+ CHECK (configuration_status IN ('unconfigured','partial','configured')),
498
+ contract_refs_json TEXT NOT NULL DEFAULT '[]',
499
+ failure_behavior TEXT,
500
+ verification_status TEXT NOT NULL DEFAULT 'unverified'
501
+ CHECK (verification_status IN ('unverified','verified','failing')),
502
+ status TEXT NOT NULL DEFAULT 'draft',
503
+ created_at TEXT NOT NULL,
504
+ updated_at TEXT NOT NULL,
505
+ version INTEGER NOT NULL DEFAULT 1
506
+ );
507
+ CREATE INDEX integrations_module ON integrations(module_id);
508
+
509
+ CREATE TABLE jobs (
510
+ id TEXT PRIMARY KEY,
511
+ feature_id TEXT REFERENCES features(id) ON DELETE CASCADE,
512
+ module_id TEXT NOT NULL REFERENCES modules(id) ON DELETE CASCADE,
513
+ name TEXT NOT NULL,
514
+ trigger TEXT,
515
+ input_contract_json TEXT NOT NULL DEFAULT '{}',
516
+ idempotency TEXT,
517
+ retry_policy TEXT,
518
+ failure_destination TEXT,
519
+ timeout TEXT,
520
+ concurrency TEXT,
521
+ observability TEXT,
522
+ delivery_guarantee TEXT CHECK (delivery_guarantee IN
523
+ ('at_least_once','at_most_once','exactly_once','undeclared')),
524
+ status TEXT NOT NULL DEFAULT 'draft',
525
+ created_at TEXT NOT NULL,
526
+ updated_at TEXT NOT NULL,
527
+ version INTEGER NOT NULL DEFAULT 1
528
+ );
529
+
530
+ CREATE TABLE webhooks (
531
+ id TEXT PRIMARY KEY,
532
+ feature_id TEXT REFERENCES features(id) ON DELETE CASCADE,
533
+ module_id TEXT NOT NULL REFERENCES modules(id) ON DELETE CASCADE,
534
+ name TEXT NOT NULL,
535
+ direction TEXT NOT NULL CHECK (direction IN ('incoming','outgoing')),
536
+ endpoint_or_registration TEXT,
537
+ identity_verification TEXT,
538
+ replay_protection TEXT,
539
+ ordering_behavior TEXT,
540
+ retry_behavior TEXT,
541
+ signing TEXT,
542
+ payload_version TEXT,
543
+ failure_visibility TEXT,
544
+ status TEXT NOT NULL DEFAULT 'draft',
545
+ created_at TEXT NOT NULL,
546
+ updated_at TEXT NOT NULL,
547
+ version INTEGER NOT NULL DEFAULT 1
548
+ );
549
+
550
+ -- ------------------------------------------------- 10.6 quality and governance
551
+
552
+ CREATE TABLE roles (
553
+ id TEXT PRIMARY KEY,
554
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
555
+ name TEXT NOT NULL,
556
+ description TEXT,
557
+ sequence INTEGER NOT NULL DEFAULT 0,
558
+ UNIQUE (project_id, name)
559
+ );
560
+
561
+ CREATE TABLE permissions (
562
+ id TEXT PRIMARY KEY,
563
+ role_id TEXT NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
564
+ scope_type TEXT NOT NULL,
565
+ scope_id TEXT,
566
+ capability TEXT NOT NULL,
567
+ access_level TEXT NOT NULL DEFAULT 'none'
568
+ CHECK (access_level IN ('none','read','own','write','full','redacted')),
569
+ enforcement_point TEXT
570
+ );
571
+ CREATE INDEX permissions_role ON permissions(role_id);
572
+
573
+ CREATE TABLE non_functional_requirements (
574
+ id TEXT PRIMARY KEY,
575
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
576
+ module_id TEXT REFERENCES modules(id) ON DELETE CASCADE,
577
+ feature_id TEXT REFERENCES features(id) ON DELETE CASCADE,
578
+ category TEXT NOT NULL,
579
+ requirement TEXT NOT NULL,
580
+ target TEXT,
581
+ target_source TEXT,
582
+ measurement_method TEXT,
583
+ status TEXT NOT NULL DEFAULT 'unmeasured'
584
+ CHECK (status IN ('met','unmet','unmeasured','not_applicable')),
585
+ created_at TEXT NOT NULL,
586
+ version INTEGER NOT NULL DEFAULT 1
587
+ );
588
+ CREATE INDEX nfr_project ON non_functional_requirements(project_id);
589
+
590
+ CREATE TABLE decisions (
591
+ id TEXT PRIMARY KEY,
592
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
593
+ title TEXT NOT NULL,
594
+ status TEXT NOT NULL DEFAULT 'proposed'
595
+ CHECK (status IN ('proposed','accepted','rejected','deprecated',
596
+ 'partially_superseded','superseded','time_boxed','revisit_required')),
597
+ scope_type TEXT,
598
+ scope_id TEXT,
599
+ context TEXT,
600
+ evidence_json TEXT NOT NULL DEFAULT '[]',
601
+ criteria_json TEXT NOT NULL DEFAULT '[]',
602
+ options_json TEXT NOT NULL DEFAULT '[]',
603
+ decision TEXT,
604
+ observable_rationale TEXT,
605
+ consequences_json TEXT NOT NULL DEFAULT '{}',
606
+ risks_json TEXT NOT NULL DEFAULT '[]',
607
+ enforcement_json TEXT NOT NULL DEFAULT '[]',
608
+ verification TEXT,
609
+ revisit_triggers_json TEXT NOT NULL DEFAULT '[]',
610
+ supersedes_id TEXT REFERENCES decisions(id) ON DELETE SET NULL,
611
+ superseded_by_id TEXT REFERENCES decisions(id) ON DELETE SET NULL,
612
+ body_hash TEXT,
613
+ accepted_by TEXT,
614
+ accepted_at TEXT,
615
+ created_at TEXT NOT NULL,
616
+ updated_at TEXT NOT NULL,
617
+ version INTEGER NOT NULL DEFAULT 1
618
+ );
619
+ CREATE INDEX decisions_project ON decisions(project_id, status);
620
+
621
+ CREATE TABLE decision_transitions (
622
+ id TEXT PRIMARY KEY,
623
+ decision_id TEXT NOT NULL REFERENCES decisions(id) ON DELETE CASCADE,
624
+ from_status TEXT,
625
+ to_status TEXT NOT NULL,
626
+ scope_delta TEXT,
627
+ actor_id TEXT,
628
+ reason TEXT,
629
+ created_at TEXT NOT NULL,
630
+ sequence INTEGER NOT NULL,
631
+ immutable_hash TEXT NOT NULL,
632
+ UNIQUE (decision_id, sequence)
633
+ );
634
+
635
+ CREATE TABLE documents (
636
+ id TEXT PRIMARY KEY,
637
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
638
+ kind TEXT NOT NULL,
639
+ scope_type TEXT NOT NULL,
640
+ scope_id TEXT,
641
+ path TEXT NOT NULL,
642
+ template TEXT,
643
+ database_revision INTEGER NOT NULL DEFAULT 0,
644
+ generated_body TEXT,
645
+ generated_hash TEXT,
646
+ manual_hash TEXT,
647
+ sync_status TEXT NOT NULL DEFAULT 'generated'
648
+ CHECK (sync_status IN ('generated','manual_edit_pending','accepted','rejected','missing')),
649
+ generated_at TEXT,
650
+ UNIQUE (project_id, path)
651
+ );
652
+ CREATE INDEX documents_sync ON documents(project_id, sync_status);
653
+
654
+ -- Capability readiness, brief section 8.2. Without a home for this,
655
+ -- "silent gaps are invalid" cannot be enforced or displayed.
656
+ CREATE TABLE capability_areas (
657
+ id TEXT PRIMARY KEY,
658
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
659
+ area TEXT NOT NULL,
660
+ state TEXT NOT NULL DEFAULT 'awaiting_decision'
661
+ CHECK (state IN ('specified','awaiting_decision','not_applicable','deferred')),
662
+ reason TEXT,
663
+ owner TEXT,
664
+ revisit_trigger TEXT,
665
+ consequence TEXT,
666
+ question_id TEXT REFERENCES questions(id) ON DELETE SET NULL,
667
+ sequence INTEGER NOT NULL DEFAULT 0,
668
+ updated_at TEXT NOT NULL,
669
+ version INTEGER NOT NULL DEFAULT 1,
670
+ CHECK (state = 'specified' OR reason IS NOT NULL),
671
+ UNIQUE (project_id, area)
672
+ );
673
+
674
+ -- The twenty-step module loop, brief section 9.3.
675
+ CREATE TABLE module_completeness (
676
+ id TEXT PRIMARY KEY,
677
+ module_id TEXT NOT NULL REFERENCES modules(id) ON DELETE CASCADE,
678
+ step INTEGER NOT NULL CHECK (step BETWEEN 1 AND 20),
679
+ step_name TEXT NOT NULL,
680
+ state TEXT NOT NULL DEFAULT 'open'
681
+ CHECK (state IN ('open','filled','not_applicable')),
682
+ summary TEXT,
683
+ reason_not_applicable TEXT,
684
+ updated_at TEXT NOT NULL,
685
+ CHECK (state <> 'not_applicable' OR reason_not_applicable IS NOT NULL),
686
+ UNIQUE (module_id, step)
687
+ );
688
+
689
+ -- ------------------------------------------------------ 10.7 tasks and execution
690
+
691
+ CREATE TABLE task_categories (
692
+ id TEXT PRIMARY KEY,
693
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
694
+ name TEXT NOT NULL,
695
+ color_token TEXT,
696
+ system INTEGER NOT NULL DEFAULT 0,
697
+ active INTEGER NOT NULL DEFAULT 1,
698
+ sequence INTEGER NOT NULL DEFAULT 0,
699
+ UNIQUE (project_id, name)
700
+ );
701
+
702
+ CREATE TABLE tasks (
703
+ id TEXT PRIMARY KEY,
704
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
705
+ -- A task with no feature is invalid. Enforced by the column, not by a caller.
706
+ feature_id TEXT NOT NULL REFERENCES features(id) ON DELETE RESTRICT,
707
+ parent_task_id TEXT REFERENCES tasks(id) ON DELETE CASCADE,
708
+ category_id TEXT REFERENCES task_categories(id) ON DELETE SET NULL,
709
+ name TEXT NOT NULL,
710
+ description TEXT,
711
+ expected_outcome TEXT,
712
+ why_needed TEXT,
713
+ completion_criteria_json TEXT NOT NULL DEFAULT '[]',
714
+ verification_requirements_json TEXT NOT NULL DEFAULT '[]',
715
+ affected_boundaries_json TEXT NOT NULL DEFAULT '[]',
716
+ priority TEXT NOT NULL DEFAULT 'normal',
717
+ risk TEXT,
718
+ status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN
719
+ ('draft','ready','in_progress','in_review','verifying',
720
+ 'blocked','paused','complete','cancelled','superseded')),
721
+ enabling INTEGER NOT NULL DEFAULT 0,
722
+ enabled_feature_id TEXT REFERENCES features(id) ON DELETE SET NULL,
723
+ enabling_rationale TEXT,
724
+ estimate TEXT,
725
+ due_at TEXT,
726
+ started_at TEXT,
727
+ completed_at TEXT,
728
+ cancelled_at TEXT,
729
+ block_reason TEXT,
730
+ sequence INTEGER NOT NULL DEFAULT 0,
731
+ created_at TEXT NOT NULL,
732
+ updated_at TEXT NOT NULL,
733
+ version INTEGER NOT NULL DEFAULT 1
734
+ );
735
+ CREATE INDEX tasks_feature ON tasks(feature_id, status);
736
+ CREATE INDEX tasks_parent ON tasks(parent_task_id);
737
+ CREATE INDEX tasks_project_status ON tasks(project_id, status);
738
+
739
+ CREATE TABLE task_contract_links (
740
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
741
+ target_type TEXT NOT NULL CHECK (target_type IN
742
+ ('workflow_step','ui_action','api_operation','data_entity','schema_migration',
743
+ 'integration','job','webhook','nfr','document','decision','acceptance_criterion',
744
+ 'surface','state_transition','permission')),
745
+ target_id TEXT NOT NULL,
746
+ relationship TEXT NOT NULL DEFAULT 'implements',
747
+ PRIMARY KEY (task_id, target_type, target_id)
748
+ );
749
+
750
+ CREATE TABLE task_dependencies (
751
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
752
+ depends_on_task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
753
+ dependency_type TEXT NOT NULL DEFAULT 'blocks'
754
+ CHECK (dependency_type IN ('blocks','informs','shares_surface')),
755
+ PRIMARY KEY (task_id, depends_on_task_id),
756
+ CHECK (task_id <> depends_on_task_id)
757
+ );
758
+
759
+ -- ------------------------------------ 10.8 developers, agents, branches, sessions
760
+
761
+ CREATE TABLE developers (
762
+ id TEXT PRIMARY KEY,
763
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
764
+ display_name TEXT NOT NULL,
765
+ external_identity TEXT,
766
+ status TEXT NOT NULL DEFAULT 'active'
767
+ CHECK (status IN ('active','inactive')),
768
+ created_at TEXT NOT NULL,
769
+ UNIQUE (project_id, display_name)
770
+ );
771
+
772
+ CREATE TABLE agents (
773
+ id TEXT PRIMARY KEY,
774
+ developer_id TEXT NOT NULL REFERENCES developers(id) ON DELETE CASCADE,
775
+ harness TEXT NOT NULL,
776
+ model_label TEXT,
777
+ session_external_id TEXT,
778
+ status TEXT NOT NULL DEFAULT 'active'
779
+ CHECK (status IN ('active','idle','ended')),
780
+ last_seen_at TEXT
781
+ );
782
+ CREATE INDEX agents_developer ON agents(developer_id);
783
+
784
+ CREATE TABLE branches (
785
+ id TEXT PRIMARY KEY,
786
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
787
+ name TEXT NOT NULL,
788
+ worktree_fingerprint TEXT,
789
+ head_revision TEXT,
790
+ dirty INTEGER NOT NULL DEFAULT 0,
791
+ status TEXT NOT NULL DEFAULT 'active'
792
+ CHECK (status IN ('active','merged','abandoned')),
793
+ last_seen_at TEXT,
794
+ UNIQUE (project_id, name, worktree_fingerprint)
795
+ );
796
+
797
+ CREATE TABLE work_sessions (
798
+ id TEXT PRIMARY KEY,
799
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
800
+ developer_id TEXT REFERENCES developers(id) ON DELETE SET NULL,
801
+ agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
802
+ branch_id TEXT REFERENCES branches(id) ON DELETE SET NULL,
803
+ active_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
804
+ objective TEXT,
805
+ started_at TEXT NOT NULL,
806
+ last_activity_at TEXT,
807
+ ended_at TEXT,
808
+ outcome TEXT,
809
+ handoff TEXT,
810
+ next_action TEXT,
811
+ status TEXT NOT NULL DEFAULT 'active'
812
+ CHECK (status IN ('active','compacted','handed_off','ended'))
813
+ );
814
+ CREATE INDEX work_sessions_project ON work_sessions(project_id, status);
815
+
816
+ CREATE TABLE task_assignments (
817
+ id TEXT PRIMARY KEY,
818
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
819
+ developer_id TEXT REFERENCES developers(id) ON DELETE SET NULL,
820
+ agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
821
+ branch_id TEXT REFERENCES branches(id) ON DELETE SET NULL,
822
+ session_id TEXT REFERENCES work_sessions(id) ON DELETE SET NULL,
823
+ assigned_at TEXT NOT NULL,
824
+ released_at TEXT,
825
+ active INTEGER NOT NULL DEFAULT 1
826
+ );
827
+ CREATE INDEX task_assignments_task ON task_assignments(task_id, active);
828
+ -- At most one active assignment per task. A partial unique index is the only
829
+ -- thing that makes a double claim impossible rather than merely unlikely.
830
+ CREATE UNIQUE INDEX task_assignments_one_active
831
+ ON task_assignments(task_id) WHERE active = 1;
832
+
833
+ CREATE TABLE activity_events (
834
+ id TEXT PRIMARY KEY,
835
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
836
+ session_id TEXT REFERENCES work_sessions(id) ON DELETE SET NULL,
837
+ actor_id TEXT,
838
+ actor_label TEXT,
839
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
840
+ feature_id TEXT REFERENCES features(id) ON DELETE SET NULL,
841
+ event_type TEXT NOT NULL,
842
+ summary TEXT NOT NULL,
843
+ metadata_json TEXT NOT NULL DEFAULT '{}',
844
+ created_at TEXT NOT NULL,
845
+ sequence INTEGER NOT NULL,
846
+ immutable_hash TEXT NOT NULL,
847
+ UNIQUE (project_id, sequence)
848
+ );
849
+ CREATE INDEX activity_events_task ON activity_events(task_id);
850
+ CREATE INDEX activity_events_created ON activity_events(project_id, created_at);
851
+
852
+ CREATE TABLE verification_evidence (
853
+ id TEXT PRIMARY KEY,
854
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
855
+ task_id TEXT REFERENCES tasks(id) ON DELETE CASCADE,
856
+ feature_id TEXT REFERENCES features(id) ON DELETE CASCADE,
857
+ acceptance_criterion_id TEXT REFERENCES feature_acceptance_criteria(id) ON DELETE SET NULL,
858
+ evidence_type TEXT NOT NULL,
859
+ summary TEXT NOT NULL,
860
+ reference TEXT,
861
+ result TEXT NOT NULL DEFAULT 'pass'
862
+ CHECK (result IN ('pass','fail','inconclusive')),
863
+ content_hash TEXT,
864
+ recorded_by TEXT,
865
+ recorded_at TEXT NOT NULL,
866
+ stale_at TEXT,
867
+ status TEXT NOT NULL DEFAULT 'current'
868
+ CHECK (status IN ('current','stale','superseded'))
869
+ );
870
+ CREATE INDEX verification_evidence_task ON verification_evidence(task_id);
871
+ CREATE INDEX verification_evidence_feature ON verification_evidence(feature_id);
872
+
873
+ -- One append-only history for every record that has a lifecycle. This is an
874
+ -- audit table keyed by record type, not a generic entity store.
875
+ CREATE TABLE status_history (
876
+ id TEXT PRIMARY KEY,
877
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
878
+ record_type TEXT NOT NULL,
879
+ record_id TEXT NOT NULL,
880
+ from_status TEXT,
881
+ to_status TEXT NOT NULL,
882
+ actor_label TEXT,
883
+ note TEXT,
884
+ created_at TEXT NOT NULL,
885
+ sequence INTEGER NOT NULL
886
+ );
887
+ CREATE INDEX status_history_record ON status_history(record_type, record_id, sequence);
888
+
889
+ -- ------------------------------------------------- 10.9 local memory and sync
890
+
891
+ CREATE TABLE memory_entries (
892
+ id TEXT PRIMARY KEY,
893
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
894
+ session_id TEXT REFERENCES work_sessions(id) ON DELETE SET NULL,
895
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
896
+ feature_id TEXT REFERENCES features(id) ON DELETE SET NULL,
897
+ kind TEXT NOT NULL CHECK (kind IN
898
+ ('outcome','decision','learned_fact','unresolved_question',
899
+ 'blocker','handoff','summary')),
900
+ title TEXT NOT NULL,
901
+ content TEXT NOT NULL,
902
+ source_ref TEXT,
903
+ epistemic_status TEXT NOT NULL DEFAULT 'inferred'
904
+ CHECK (epistemic_status IN ('confirmed','inferred','assumed','unknown','contradicted')),
905
+ embedding BLOB,
906
+ created_at TEXT NOT NULL,
907
+ superseded_by TEXT REFERENCES memory_entries(id) ON DELETE SET NULL,
908
+ version INTEGER NOT NULL DEFAULT 1
909
+ );
910
+ CREATE INDEX memory_entries_project ON memory_entries(project_id, kind, created_at);
911
+
912
+ CREATE TABLE memory_links (
913
+ memory_id TEXT NOT NULL REFERENCES memory_entries(id) ON DELETE CASCADE,
914
+ target_type TEXT NOT NULL,
915
+ target_id TEXT NOT NULL,
916
+ relationship TEXT NOT NULL DEFAULT 'about',
917
+ PRIMARY KEY (memory_id, target_type, target_id)
918
+ );
919
+
920
+ CREATE TABLE sync_peers (
921
+ id TEXT PRIMARY KEY,
922
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
923
+ peer_type TEXT NOT NULL,
924
+ remote_project_id TEXT,
925
+ local_priority INTEGER NOT NULL DEFAULT 1,
926
+ last_pull_cursor TEXT,
927
+ last_push_cursor TEXT,
928
+ last_synced_at TEXT,
929
+ status TEXT NOT NULL DEFAULT 'disconnected'
930
+ CHECK (status IN ('disconnected','connected','error'))
931
+ );
932
+
933
+ CREATE TABLE sync_conflicts (
934
+ id TEXT PRIMARY KEY,
935
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
936
+ record_type TEXT NOT NULL,
937
+ record_id TEXT NOT NULL,
938
+ local_version INTEGER,
939
+ remote_version INTEGER,
940
+ base_version INTEGER,
941
+ local_value_json TEXT,
942
+ remote_value_json TEXT,
943
+ resolution TEXT CHECK (resolution IN ('local','remote','merged')),
944
+ resolved_by TEXT,
945
+ resolved_at TEXT,
946
+ detected_at TEXT NOT NULL,
947
+ status TEXT NOT NULL DEFAULT 'open'
948
+ CHECK (status IN ('open','resolved'))
949
+ );
950
+ CREATE INDEX sync_conflicts_open ON sync_conflicts(project_id, status);
951
+
952
+ -- Saved canvas positions for the blueprint. Layout is a view concern, but it
953
+ -- has to survive a reload, so it lives with the data it decorates.
954
+ CREATE TABLE layout_positions (
955
+ id TEXT PRIMARY KEY,
956
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
957
+ canvas TEXT NOT NULL,
958
+ record_type TEXT NOT NULL,
959
+ record_id TEXT NOT NULL,
960
+ x REAL NOT NULL,
961
+ y REAL NOT NULL,
962
+ updated_at TEXT NOT NULL,
963
+ UNIQUE (project_id, canvas, record_type, record_id)
964
+ );
965
+
966
+ -- ------------------------------------------------------------------- triggers
967
+
968
+ -- The task mapping constraint. A draft may be incomplete; nothing may leave
969
+ -- draft without either a contract link or a named feature it unblocks.
970
+ CREATE TRIGGER tasks_require_contract_on_ready
971
+ BEFORE UPDATE OF status ON tasks
972
+ WHEN NEW.status <> 'draft'
973
+ AND NEW.status <> 'cancelled'
974
+ AND NEW.enabling = 0
975
+ AND (SELECT count(*) FROM task_contract_links WHERE task_id = NEW.id) = 0
976
+ BEGIN
977
+ SELECT RAISE(ABORT, 'E_TASK_WITHOUT_CONTRACT');
978
+ END;
979
+
980
+ CREATE TRIGGER tasks_enabling_needs_target
981
+ BEFORE UPDATE OF status ON tasks
982
+ WHEN NEW.status <> 'draft'
983
+ AND NEW.status <> 'cancelled'
984
+ AND NEW.enabling = 1
985
+ AND (NEW.enabled_feature_id IS NULL OR NEW.enabling_rationale IS NULL)
986
+ BEGIN
987
+ SELECT RAISE(ABORT, 'E_ENABLING_WITHOUT_TARGET');
988
+ END;
989
+
990
+ CREATE TRIGGER tasks_enabling_needs_target_insert
991
+ BEFORE INSERT ON tasks
992
+ WHEN NEW.status <> 'draft'
993
+ AND NEW.enabling = 1
994
+ AND (NEW.enabled_feature_id IS NULL OR NEW.enabling_rationale IS NULL)
995
+ BEGIN
996
+ SELECT RAISE(ABORT, 'E_ENABLING_WITHOUT_TARGET');
997
+ END;
998
+
999
+ -- A parent cannot complete over open children.
1000
+ CREATE TRIGGER tasks_no_complete_over_open_children
1001
+ BEFORE UPDATE OF status ON tasks
1002
+ WHEN NEW.status = 'complete'
1003
+ AND (SELECT count(*) FROM tasks c
1004
+ WHERE c.parent_task_id = NEW.id
1005
+ AND c.status NOT IN ('complete','cancelled','superseded')) > 0
1006
+ BEGIN
1007
+ SELECT RAISE(ABORT, 'E_OPEN_SUBTASKS');
1008
+ END;
1009
+
1010
+ -- Append-only history. These tables are written once and never revised.
1011
+ CREATE TRIGGER activity_events_no_update BEFORE UPDATE ON activity_events
1012
+ BEGIN SELECT RAISE(ABORT, 'E_APPEND_ONLY'); END;
1013
+ CREATE TRIGGER activity_events_no_delete BEFORE DELETE ON activity_events
1014
+ BEGIN SELECT RAISE(ABORT, 'E_APPEND_ONLY'); END;
1015
+
1016
+ CREATE TRIGGER decision_transitions_no_update BEFORE UPDATE ON decision_transitions
1017
+ BEGIN SELECT RAISE(ABORT, 'E_APPEND_ONLY'); END;
1018
+ CREATE TRIGGER decision_transitions_no_delete BEFORE DELETE ON decision_transitions
1019
+ BEGIN SELECT RAISE(ABORT, 'E_APPEND_ONLY'); END;
1020
+
1021
+ CREATE TRIGGER status_history_no_update BEFORE UPDATE ON status_history
1022
+ BEGIN SELECT RAISE(ABORT, 'E_APPEND_ONLY'); END;
1023
+ CREATE TRIGGER status_history_no_delete BEFORE DELETE ON status_history
1024
+ BEGIN SELECT RAISE(ABORT, 'E_APPEND_ONLY'); END;