@lifeaitools/rdc-skills 0.24.38 → 0.24.41

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 (196) hide show
  1. package/.claude/settings.json +15 -15
  2. package/.claude-plugin/marketplace.json +21 -21
  3. package/.claude-plugin/plugin.json +1371 -1371
  4. package/.github/workflows/publish.yml +34 -34
  5. package/.github/workflows/self-test.yml +58 -58
  6. package/CHANGELOG.md +310 -310
  7. package/LICENSE +21 -21
  8. package/MANIFEST.md +221 -221
  9. package/README.md +376 -375
  10. package/README.sandbox.md +3 -3
  11. package/RELEASE.md +42 -0
  12. package/assets/watcher/viewer.html +164 -164
  13. package/bin/rdc-skills-mcp.mjs +316 -316
  14. package/commands/build.md +183 -183
  15. package/commands/collab.md +180 -180
  16. package/commands/deploy.md +152 -152
  17. package/commands/design.md +31 -31
  18. package/commands/edit.md +28 -28
  19. package/commands/fixit.md +124 -124
  20. package/commands/handoff.md +173 -173
  21. package/commands/help.md +95 -95
  22. package/commands/overnight.md +220 -220
  23. package/commands/plan.md +158 -158
  24. package/commands/preplan.md +131 -131
  25. package/commands/prototype.md +145 -145
  26. package/commands/release.md +49 -49
  27. package/commands/report.md +99 -99
  28. package/commands/review.md +120 -120
  29. package/commands/self-test.md +113 -113
  30. package/commands/status.md +86 -86
  31. package/commands/watch.md +98 -98
  32. package/commands/workitems.md +137 -137
  33. package/git-sha.json +1 -1
  34. package/guides/agent-bootstrap.md +295 -295
  35. package/guides/agents/backend.md +104 -104
  36. package/guides/agents/content.md +94 -94
  37. package/guides/agents/cs2.md +56 -56
  38. package/guides/agents/data.md +87 -87
  39. package/guides/agents/design.md +77 -77
  40. package/guides/agents/frontend.md +92 -92
  41. package/guides/agents/infrastructure.md +81 -81
  42. package/guides/agents/setup.md +281 -281
  43. package/guides/agents/verify.md +151 -151
  44. package/guides/agents/viz.md +106 -106
  45. package/guides/backend.md +146 -146
  46. package/guides/content.md +147 -147
  47. package/guides/cs2.md +190 -190
  48. package/guides/data.md +123 -123
  49. package/guides/design.md +116 -116
  50. package/guides/engineering-behavior.md +43 -43
  51. package/guides/escalation-protocol.md +125 -125
  52. package/guides/frontend.md +151 -151
  53. package/guides/history-md-spec.md +297 -297
  54. package/guides/infrastructure.md +179 -179
  55. package/guides/lessons-learned-spec.md +151 -145
  56. package/guides/output-contract.md +108 -108
  57. package/guides/publish-md-spec.md +289 -289
  58. package/guides/rdc-skills-startup.md +30 -30
  59. package/guides/verify.md +11 -11
  60. package/hooks/check-cwd.js +31 -31
  61. package/hooks/check-rdc-environment.js +164 -164
  62. package/hooks/check-services.js +6 -6
  63. package/hooks/check-stale-work-items.js +19 -19
  64. package/hooks/foreground-process-gate.js +128 -128
  65. package/hooks/gate-watchdog-selfcheck.js +257 -257
  66. package/hooks/hook-logger.js +25 -25
  67. package/hooks/lib/run-evidence-gate.mjs +241 -241
  68. package/hooks/no-stop-open-epics.js +127 -127
  69. package/hooks/post-tool-batch-gate.js +203 -203
  70. package/hooks/post-work-check.js +21 -21
  71. package/hooks/postcompact-log.js +13 -13
  72. package/hooks/precompact-log.js +13 -13
  73. package/hooks/rate-limit-retry.js +46 -46
  74. package/hooks/rdc-invocation-marker.js +157 -157
  75. package/hooks/rdc-output-contract-gate.js +94 -94
  76. package/hooks/require-work-item-on-commit.js +294 -294
  77. package/hooks/restart-brief.js +19 -19
  78. package/hooks/run-hidden-hook.ps1 +47 -47
  79. package/hooks/task-completed-gate.js +274 -274
  80. package/hooks/work-item-exit-gate.js +944 -944
  81. package/lib/catalog.mjs +236 -236
  82. package/lib/cloud-rewrite.mjs +155 -155
  83. package/package.json +57 -56
  84. package/rules/work-items-rpc.md +520 -520
  85. package/scaffold/templates/HISTORY.md.template +39 -39
  86. package/scaffold/templates/PUBLISH.md.template +21 -21
  87. package/scaffold/templates/brochure-studio-default.html +70 -70
  88. package/scripts/acceptance.mjs +502 -502
  89. package/scripts/fixtures/guides/bad-guide.md +15 -15
  90. package/scripts/fixtures/guides-clean/good-guide.md +16 -16
  91. package/scripts/install-rdc-skills.js +1289 -1289
  92. package/scripts/install.ps1 +202 -202
  93. package/scripts/install.sh +132 -132
  94. package/scripts/lib/assertions.mjs +287 -287
  95. package/scripts/lib/manifest-schema.mjs +754 -754
  96. package/scripts/lib/runner.mjs +465 -465
  97. package/scripts/lib/sandbox.mjs +435 -435
  98. package/scripts/prepack.mjs +32 -32
  99. package/scripts/rdc-brochure.mjs +482 -464
  100. package/scripts/rdc-design-cli.mjs +134 -134
  101. package/scripts/rebuild-mcp.mjs +107 -107
  102. package/scripts/self-test.mjs +1460 -1460
  103. package/scripts/stamp-git-sha.mjs +29 -29
  104. package/scripts/test-guide-validator.mjs +196 -196
  105. package/scripts/test-rdc-hooks.mjs +145 -145
  106. package/scripts/uninstall.ps1 +77 -77
  107. package/scripts/uninstall.sh +69 -69
  108. package/scripts/update.ps1 +43 -43
  109. package/scripts/update.sh +43 -43
  110. package/scripts/validate-place-histories.js +461 -461
  111. package/scripts/validate-publish-manifests.js +424 -424
  112. package/scripts/watch-init.mjs +100 -100
  113. package/skills/brochure/SKILL.md +107 -107
  114. package/skills/build/SKILL.md +563 -563
  115. package/skills/channel-formatter/SKILL.md +533 -533
  116. package/skills/co-develop/SKILL.md +196 -196
  117. package/skills/collab/SKILL.md +239 -239
  118. package/skills/convert/SKILL.md +140 -140
  119. package/skills/deploy/SKILL.md +541 -541
  120. package/skills/design/SKILL.md +211 -211
  121. package/skills/design/reference/ownership.md +16 -16
  122. package/skills/design/reference/rampa.md +92 -92
  123. package/skills/design/reference/studio-model.md +153 -153
  124. package/skills/edit/SKILL.md +98 -98
  125. package/skills/fixit/SKILL.md +165 -165
  126. package/skills/fs-mcp/SKILL.md +148 -148
  127. package/skills/handoff/SKILL.md +236 -200
  128. package/skills/help/SKILL.md +143 -143
  129. package/skills/housekeeping/SKILL.md +219 -160
  130. package/skills/lifeai-brochure-author/SKILL.md +340 -340
  131. package/skills/overnight/SKILL.md +251 -251
  132. package/skills/plan/SKILL.md +345 -345
  133. package/skills/preplan/SKILL.md +90 -90
  134. package/skills/prototype/SKILL.md +150 -150
  135. package/skills/rdc-brochurify/SKILL.md +245 -245
  136. package/skills/rdc-extract-verifier-rules/SKILL.md +191 -191
  137. package/skills/release/SKILL.md +140 -140
  138. package/skills/report/SKILL.md +100 -100
  139. package/skills/review/SKILL.md +152 -152
  140. package/skills/rpms-filemap/SKILL.cloud.md +111 -111
  141. package/skills/rpms-filemap/SKILL.md +111 -111
  142. package/skills/self-test/SKILL.md +132 -132
  143. package/skills/status/SKILL.md +99 -99
  144. package/skills/terminal-config/SKILL.md +62 -62
  145. package/skills/tests/MATRIX.md +54 -54
  146. package/skills/tests/README.md +47 -47
  147. package/skills/tests/rdc-brochure.test.json +34 -34
  148. package/skills/tests/rdc-build.test.json +36 -36
  149. package/skills/tests/rdc-channel-formatter.test.json +45 -45
  150. package/skills/tests/rdc-co-develop.test.json +29 -29
  151. package/skills/tests/rdc-collab.test.json +29 -29
  152. package/skills/tests/rdc-convert.test.json +35 -35
  153. package/skills/tests/rdc-deploy.test.json +30 -30
  154. package/skills/tests/rdc-design.test.json +27 -27
  155. package/skills/tests/rdc-edit.test.json +29 -29
  156. package/skills/tests/rdc-fixit.test.json +36 -36
  157. package/skills/tests/rdc-fs-mcp.test.json +36 -36
  158. package/skills/tests/rdc-handoff.test.json +28 -28
  159. package/skills/tests/rdc-help.test.json +29 -29
  160. package/skills/tests/rdc-housekeeping.test.json +32 -28
  161. package/skills/tests/rdc-lifeai-brochure-author.test.json +35 -35
  162. package/skills/tests/rdc-overnight.test.json +37 -37
  163. package/skills/tests/rdc-plan.test.json +27 -27
  164. package/skills/tests/rdc-preplan.test.json +31 -31
  165. package/skills/tests/rdc-prototype.test.json +28 -28
  166. package/skills/tests/rdc-rdc-brochurify.test.json +23 -23
  167. package/skills/tests/rdc-rdc-extract-verifier-rules.test.json +34 -34
  168. package/skills/tests/rdc-release.test.json +29 -29
  169. package/skills/tests/rdc-report.test.json +28 -28
  170. package/skills/tests/rdc-review.test.json +29 -29
  171. package/skills/tests/rdc-rpms-filemap.test.json +28 -28
  172. package/skills/tests/rdc-self-test.test.json +24 -24
  173. package/skills/tests/rdc-status.test.json +29 -29
  174. package/skills/tests/rdc-terminal-config.test.json +29 -29
  175. package/skills/tests/rdc-watch.test.json +24 -24
  176. package/skills/tests/rdc-workitems.test.json +27 -27
  177. package/skills/watch/SKILL.md +97 -97
  178. package/skills/workitems/SKILL.md +151 -151
  179. package/tests/acceptance.test.mjs +59 -59
  180. package/tests/channel-formatter.contract.test.mjs +251 -251
  181. package/tests/curl-surface.test.mjs +289 -289
  182. package/tests/harness-gates.test.mjs +325 -325
  183. package/tests/help-surface.test.mjs +61 -61
  184. package/tests/housekeeping-lessons-triage.test.mjs +49 -0
  185. package/tests/install-rdc-skills.test.mjs +49 -49
  186. package/tests/lessons-pipeline-contract.test.mjs +26 -0
  187. package/tests/manifest-contract-fields.test.mjs +78 -78
  188. package/tests/mcp.test.mjs +271 -271
  189. package/tests/rdc-brochure.test.mjs +125 -0
  190. package/tests/release-contract.test.mjs +16 -0
  191. package/tests/require-work-item-on-commit.test.mjs +162 -162
  192. package/tests/run-evidence-gate.test.mjs +82 -82
  193. package/tests/skill-test-matrix.test.mjs +66 -66
  194. package/tests/validate-skills.js +27 -27
  195. package/tests/work-item-exit-gate-l2.test.mjs +368 -368
  196. package/tests/work-item-exit-gate-l3.test.mjs +197 -197
@@ -1,520 +1,520 @@
1
- # Work Items RPC Reference
2
-
3
- > Complete reference for work_items table operations via Supabase RPC functions.
4
- > Use these functions for ALL CRUD operations. Never write raw INSERT/UPDATE/SELECT against work_items.
5
-
6
- ---
7
-
8
- ## Core Rule
9
-
10
- **Always use RPC functions.** They handle defaults, check constraints, timestamps, sorting, and return structured JSONB.
11
-
12
- Exception: Ad-hoc analytics queries (e.g., `SELECT count(*)`) are acceptable as raw SQL.
13
-
14
- ---
15
-
16
- ## Valid Enum Values
17
-
18
- | Column | Allowed Values |
19
- |--------|---|
20
- | `status` | `todo` · `in_progress` · `blocked` · `review` · `done` · `archived` |
21
- | `priority` | `urgent` · `high` · `normal` · `low` |
22
- | `item_type` | `epic` · `task` · `subtask` · `bug` · `spike` |
23
-
24
- ---
25
-
26
- ## RPC Functions
27
-
28
- ### 1. get_open_epics — List open epics
29
-
30
- Returns all non-done/archived epics sorted by priority, then created_at.
31
-
32
- **Supabase SQL:**
33
- ```sql
34
- SELECT get_open_epics();
35
- SELECT get_open_epics('urgent');
36
- SELECT get_open_epics(p_label_filter := 'feature-x');
37
- SELECT get_open_epics('high', 'backend');
38
- ```
39
-
40
- **Supabase JS Client:**
41
- ```ts
42
- const { data } = await supabase.rpc('get_open_epics');
43
- const { data } = await supabase.rpc('get_open_epics', {
44
- p_priority_filter: 'urgent'
45
- });
46
- const { data } = await supabase.rpc('get_open_epics', {
47
- p_label_filter: 'cs2'
48
- });
49
- ```
50
-
51
- **Parameters:**
52
-
53
- | Name | Type | Required | Notes |
54
- |------|------|----------|-------|
55
- | `p_priority_filter` | text | no | NULL = all priorities. One of: `urgent`, `high`, `normal`, `low` |
56
- | `p_label_filter` | text | no | NULL = all labels. Uses array containment (`@>`) for matching |
57
-
58
- **Returns:**
59
- - JSONB array of epic objects (sorted by priority then created_at)
60
- - Empty `[]` if none found
61
- - Each epic has: `id`, `title`, `status`, `priority`, `item_type`, `labels`, `created_at`, `updated_at`
62
-
63
- ---
64
-
65
- ### 2. insert_work_item — Create a work item
66
-
67
- Create a new task, epic, subtask, bug, or spike.
68
-
69
- **Supabase SQL:**
70
- ```sql
71
- SELECT insert_work_item(
72
- p_title := 'Build user authentication',
73
- p_description := 'Implement OAuth2 flow...',
74
- p_status := 'todo',
75
- p_priority := 'high',
76
- p_item_type := 'task',
77
- p_parent_id := 'epic-uuid'::uuid,
78
- p_labels := ARRAY['backend', 'auth'],
79
- p_source := 'agent'
80
- );
81
- ```
82
-
83
- **Supabase JS Client:**
84
- ```ts
85
- const { data } = await supabase.rpc('insert_work_item', {
86
- p_title: 'Build user authentication',
87
- p_description: 'Implement OAuth2 flow...',
88
- p_status: 'todo',
89
- p_priority: 'high',
90
- p_item_type: 'task',
91
- p_parent_id: epicId,
92
- p_labels: ['backend', 'auth'],
93
- p_source: 'agent'
94
- });
95
- ```
96
-
97
- **All Parameters:**
98
-
99
- | Name | Type | Default | Notes |
100
- |------|------|---------|-------|
101
- | `p_title` | text | *required* | Work item name (max 255 chars) |
102
- | `p_description` | text | NULL | Long-form description |
103
- | `p_status` | text | `'todo'` | One of: `todo`, `in_progress`, `blocked`, `review`, `done`, `archived` |
104
- | `p_priority` | text | `'normal'` | One of: `urgent`, `high`, `normal`, `low` |
105
- | `p_item_type` | text | `'task'` | One of: `epic`, `task`, `subtask`, `bug`, `spike` |
106
- | `p_parent_id` | uuid | NULL | Parent epic UUID for hierarchy (for subtasks) |
107
- | `p_company_id` | uuid | NULL | Foreign key to companies table |
108
- | `p_customer_id` | uuid | NULL | Foreign key to customers table |
109
- | `p_campaign_id` | uuid | NULL | Foreign key to campaigns table |
110
- | `p_contact_id` | uuid | NULL | Foreign key to contacts table |
111
- | `p_project_id` | uuid | NULL | Project identifier (project-specific) |
112
- | `p_assignee` | text | NULL | Assigned person name or email |
113
- | `p_assigned_to` | text | NULL | Same as assignee (alternate field) |
114
- | `p_due_date` | date | NULL | Due date for the task |
115
- | `p_tags` | text[] | `'{}'` | Array of tags |
116
- | `p_labels` | text[] | `'{}'` | Array of labels (used for filtering) |
117
- | `p_linked_table` | text | NULL | Name of external table to link (if any) |
118
- | `p_linked_id` | uuid | NULL | ID in linked_table |
119
- | `p_created_by` | text | NULL | Who created this item |
120
- | `p_estimated_hours` | numeric | NULL | Time estimate in hours |
121
- | `p_notes` | jsonb | `'[]'` | Array of note objects `[{ text, author, timestamp }]` |
122
- | `p_session_id` | text | NULL | Session UUID (for audit trail) |
123
- | `p_package` | text | NULL | Package/module name (e.g., '@regen/ui') |
124
- | `p_source` | text | `'manual'` | How was this created: `manual`, `agent`, `api`, `import` |
125
- | `p_checklist` | jsonb | `NULL` | Checklist items. If NULL and parent has `definition_of_done`, auto-inherited with all `checked: false` |
126
- | `p_definition_of_done` | jsonb | `NULL` | DoD template (epics only). Child tasks inherit this as their checklist at insert time |
127
-
128
- **Returns:**
129
- - Full inserted work_item row as JSONB
130
- - Includes auto-generated `id`, `created_at`, `updated_at`
131
-
132
- ---
133
-
134
- ### 3. update_work_item_status — Change status
135
-
136
- Transition a work item's status. Auto-manages `completed_at`.
137
-
138
- **Hard gate:** implementation agents do not set non-epic work directly to `done`.
139
- They submit a report and move the item to `review`. A validator closes it as
140
- `done` only after fresh verification.
141
-
142
- **Supabase SQL:**
143
- ```sql
144
- SELECT update_work_item_status(
145
- 'work-item-uuid'::uuid,
146
- 'in_progress'
147
- );
148
-
149
- SELECT update_work_item_status(
150
- 'work-item-uuid'::uuid,
151
- 'review',
152
- '["Implementation complete; ready for validator"]'::jsonb,
153
- 'agent-session-id',
154
- 'agent'
155
- );
156
-
157
- SELECT update_work_item_status(
158
- 'work-item-uuid'::uuid,
159
- 'done',
160
- '["Validator verified implementation report, CodeFlow post, and checklist evidence"]'::jsonb,
161
- 'validator-session-id',
162
- 'validator'
163
- );
164
- ```
165
-
166
- **Supabase JS Client:**
167
- ```ts
168
- const { data } = await supabase.rpc('update_work_item_status', {
169
- p_id: itemId,
170
- p_status: 'in_progress'
171
- });
172
-
173
- const { data } = await supabase.rpc('update_work_item_status', {
174
- p_id: itemId,
175
- p_status: 'review',
176
- p_notes_append: ['Implementation complete; ready for validator'],
177
- p_actor_session_id: agentSessionId,
178
- p_actor_role: 'agent'
179
- });
180
-
181
- const { data } = await supabase.rpc('update_work_item_status', {
182
- p_id: itemId,
183
- p_status: 'done',
184
- p_notes_append: ['Validator verified implementation report, CodeFlow post, and checklist evidence'],
185
- p_actor_session_id: validatorSessionId,
186
- p_actor_role: 'validator'
187
- });
188
- ```
189
-
190
- **Parameters:**
191
-
192
- | Name | Type | Required | Notes |
193
- |------|------|----------|-------|
194
- | `p_id` | uuid | yes | work_item ID |
195
- | `p_status` | text | yes | One of: `todo`, `in_progress`, `blocked`, `review`, `done`, `archived` |
196
- | `p_notes_append` | jsonb | no | Array of strings appended to existing notes |
197
- | `p_actor_session_id` | text | review/done | Current agent or validator session ID |
198
- | `p_actor_role` | text | review/done | `agent` for `review`, `validator` for `done` |
199
-
200
- **Behavior:**
201
- - Setting `done` auto-sets `completed_at = now()`
202
- - Setting non-epic work to `review` raises EXCEPTION unless `implementation_report.codeflow_post` exists and caller role is `agent`
203
- - Setting non-epic work to `done` raises EXCEPTION unless current status is `review`, caller role is `validator`, `implementation_report.codeflow_post` exists, every required checklist item is checked, and required checklist evidence was ticked by the originating agent session
204
- - Setting `todo`, `in_progress`, `blocked`, or `review` clears `completed_at`
205
- - `updated_at` always refreshed to current timestamp
206
- - Raises exception if ID not found
207
-
208
- **Returns:**
209
- - Full updated work_item row as JSONB
210
-
211
- ---
212
-
213
- ### 4. update_checklist_item — Tick off a checklist item
214
-
215
- **Supabase SQL:**
216
- ```sql
217
- SELECT update_checklist_item(
218
- 'work-item-uuid'::uuid,
219
- 'tsc-clean', -- string id of the checklist item
220
- true, -- checked state
221
- 'agent-session-id',
222
- 'agent',
223
- 'rpc'
224
- );
225
- ```
226
-
227
- **Parameters:**
228
-
229
- | Name | Type | Required | Notes |
230
- |------|------|----------|-------|
231
- | `p_work_item_id` | uuid | yes | the work item ID |
232
- | `p_item_id` | text | yes | the `id` field of the checklist item |
233
- | `p_checked` | boolean | yes | `true` = checked, `false` = unchecked |
234
- | `p_actor_session_id` | text | yes for checked=true | originating implementation agent session ID |
235
- | `p_actor_role` | text | yes for checked=true | must be `agent` for completion evidence ticks |
236
- | `p_event_source` | text | no | source label, default `rpc` |
237
-
238
- Call this as each checklist item is completed — not all at once at the end.
239
- The tick is written to `work_item_checklist_events`; supervisor or validator
240
- re-ticks are rejected by the exit gate.
241
-
242
- **Returns:** updated checklist JSONB array.
243
-
244
- ---
245
-
246
- ### 5. submit_implementation_report — File agent completion narrative
247
-
248
- MUST be called BEFORE `update_work_item_status(..., 'review', ...)`.
249
-
250
- **Supabase SQL:**
251
- ```sql
252
- SELECT submit_implementation_report(
253
- 'work-item-uuid'::uuid,
254
- '{
255
- "tldr": "One-sentence summary",
256
- "assumptions": [],
257
- "deviations": [],
258
- "uncertainty": [],
259
- "detail": "Full narrative",
260
- "flags": [],
261
- "codeflow_post": {
262
- "agent_session_id": "agent-session-id",
263
- "summary": "What changed and why",
264
- "files_changed": ["path/to/file"],
265
- "verification": ["command or evidence"],
266
- "commit": "optional commit hash"
267
- }
268
- }'::jsonb
269
- );
270
- ```
271
-
272
- **Parameters:**
273
-
274
- | Name | Type | Required | Notes |
275
- |------|------|----------|-------|
276
- | `p_work_item_id` | uuid | yes | the work item ID |
277
- | `p_report` | jsonb | yes | implementation report (shape below) |
278
-
279
- **Returns:** `{ "flags_count": N, "deviations_count": N, "has_deviations": bool }` — signal only.
280
-
281
- **`implementation_report` shape:**
282
- ```json
283
- {
284
- "tldr": "string",
285
- "assumptions": ["string", "..."],
286
- "deviations": ["string — deviation from plan", "..."],
287
- "uncertainty": ["string — anything unclear", "..."],
288
- "detail": "string — full narrative",
289
- "flags": ["string — supervisor attention needed", "..."],
290
- "codeflow_post": {
291
- "agent_session_id": "string — originating implementation session",
292
- "summary": "string — agent-authored CodeFlow post",
293
- "files_changed": ["string", "..."],
294
- "verification": ["string", "..."],
295
- "commit": "string — optional"
296
- }
297
- }
298
- ```
299
-
300
- If `has_deviations` is true or `flags_count > 0`, supervisor pulls full report from DB.
301
-
302
- ---
303
-
304
- ### 6. get_work_items_by_epic — Fetch epic + children
305
-
306
- Get an epic and all its child tasks/subtasks, sorted by priority.
307
-
308
- **Supabase SQL:**
309
- ```sql
310
- SELECT get_work_items_by_epic('epic-uuid'::uuid);
311
- SELECT get_work_items_by_epic('epic-uuid'::uuid, 'todo');
312
- ```
313
-
314
- **Supabase JS Client:**
315
- ```ts
316
- const { data } = await supabase.rpc('get_work_items_by_epic', {
317
- p_epic_id: epicId
318
- });
319
-
320
- const { data } = await supabase.rpc('get_work_items_by_epic', {
321
- p_epic_id: epicId,
322
- p_status_filter: 'todo'
323
- });
324
- ```
325
-
326
- **Parameters:**
327
-
328
- | Name | Type | Required | Notes |
329
- |------|------|----------|-------|
330
- | `p_epic_id` | uuid | yes | The epic's work_item ID |
331
- | `p_status_filter` | text | no | NULL = all statuses. One of: `todo`, `in_progress`, `blocked`, `review`, `done`, `archived` |
332
-
333
- **Returns:**
334
- - JSONB array with parent epic first, then children
335
- - Sorted by: priority (urgent > high > normal > low), then created_at
336
- - Each item has same structure as returned from insert_work_item
337
-
338
- ---
339
-
340
- ### 7. bump_epic_version — Version with history
341
-
342
- Snapshot an epic's current state and assign a version number.
343
-
344
- **Supabase SQL:**
345
- ```sql
346
- SELECT bump_epic_version(
347
- 'epic-uuid'::uuid,
348
- '0.2.0',
349
- 'Added async task execution',
350
- 'agent'
351
- );
352
- ```
353
-
354
- **Supabase JS Client:**
355
- ```ts
356
- const { data } = await supabase.rpc('bump_epic_version', {
357
- p_epic_id: epicId,
358
- p_version: '0.2.0',
359
- p_change_summary: 'Added async task execution',
360
- p_changed_by: 'agent'
361
- });
362
- ```
363
-
364
- **Parameters:**
365
-
366
- | Name | Type | Required | Notes |
367
- |------|------|----------|-------|
368
- | `p_epic_id` | uuid | yes | Must be item_type = `'epic'` |
369
- | `p_version` | text | yes | Semantic version string (e.g., `'0.1.0'`, `'1.0.0'`) |
370
- | `p_change_summary` | text | no | What changed in this version |
371
- | `p_changed_by` | text | no | Who bumped the version |
372
-
373
- **Behavior:**
374
- - Snapshots the epic's current state into work_item_versions table
375
- - Updates the epic's `version` column
376
- - Raises exception if item is not an epic (item_type != 'epic')
377
-
378
- **Returns:**
379
- - JSONB object: `{ epic_id, version, version_record_id, change_summary, changed_by, bumped_at }`
380
-
381
- ---
382
-
383
- ## Common Patterns
384
-
385
- ### Create an Epic + Child Tasks
386
-
387
- ```ts
388
- // 1. Create epic
389
- const { data: epic } = await supabase.rpc('insert_work_item', {
390
- p_title: 'Implement feature X',
391
- p_item_type: 'epic',
392
- p_status: 'todo',
393
- p_priority: 'high',
394
- p_source: 'agent'
395
- });
396
- const epicId = epic[0].id;
397
-
398
- // 2. Create child tasks
399
- for (const task of tasks) {
400
- await supabase.rpc('insert_work_item', {
401
- p_title: task.title,
402
- p_item_type: 'task',
403
- p_priority: task.priority,
404
- p_parent_id: epicId,
405
- p_source: 'agent'
406
- });
407
- }
408
- ```
409
-
410
- ### Fetch Epic Tree, Filter by Status, Update Each
411
-
412
- ```ts
413
- const { data: tree } = await supabase.rpc('get_work_items_by_epic', {
414
- p_epic_id: epicId,
415
- p_status_filter: 'todo'
416
- });
417
-
418
- for (const item of tree) {
419
- if (item.item_type === 'epic') continue; // skip parent
420
-
421
- await supabase.rpc('update_work_item_status', {
422
- p_id: item.id,
423
- p_status: 'in_progress'
424
- });
425
- }
426
- ```
427
-
428
- ### Mark Epic Done with Notes
429
-
430
- ```ts
431
- const { data } = await supabase.rpc('update_work_item_status', {
432
- p_id: epicId,
433
- p_status: 'done',
434
- p_notes_append: [
435
- 'All child tasks completed',
436
- 'Code reviewed and merged to main',
437
- 'Deployed to production'
438
- ]
439
- });
440
- ```
441
-
442
- ### List All Open Work, Filter by Priority
443
-
444
- ```ts
445
- const { data: epics } = await supabase.rpc('get_open_epics', {
446
- p_priority_filter: 'urgent'
447
- });
448
-
449
- for (const epic of epics) {
450
- const { data: items } = await supabase.rpc('get_work_items_by_epic', {
451
- p_epic_id: epic.id
452
- });
453
- // process items...
454
- }
455
- ```
456
-
457
- ---
458
-
459
- ## Supabase Project Reference
460
-
461
- In production, you'll need:
462
-
463
- **Environment variables (in .env.local):**
464
- ```bash
465
- NEXT_PUBLIC_SUPABASE_URL=https://<project-ref>.supabase.co
466
- NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key>
467
- SUPABASE_SERVICE_ROLE_KEY=<service-role-key> # Server-side only
468
- ```
469
-
470
- Replace `<project-ref>` with your actual Supabase project reference (e.g., `uvojezuorjgqzmhhgluu`).
471
-
472
- **Project-specific data (from docs/guides/agent-bootstrap.md):**
473
- - SUPABASE_PROJECT_ID (if required by your project)
474
- - Any custom schema names (if work_items lives in non-public schema)
475
- - Related table names (companies, customers, contacts, campaigns, etc.)
476
-
477
- ---
478
-
479
- ## Error Handling
480
-
481
- ### RPC Not Found
482
- If `get_open_epics` returns a 404, the function doesn't exist on your Supabase instance.
483
- Run migration to add the function, or contact your DBA.
484
-
485
- ### Invalid UUID
486
- ```ts
487
- // Wrong — string passed directly
488
- p_id: "item-id"
489
-
490
- // Correct — cast to uuid
491
- p_id: itemId
492
- ```
493
-
494
- ### Status Not in Enum
495
- Check `p_status` against allowed values. Will raise a constraint error if invalid.
496
-
497
- ### Item Not Found
498
- `update_work_item_status` and `bump_epic_version` will raise an exception if ID doesn't exist.
499
- Always catch or check first:
500
- ```ts
501
- const { data: items } = await supabase.rpc('get_work_items_by_epic', { p_epic_id: id });
502
- if (!items || items.length === 0) {
503
- // Epic not found
504
- }
505
- ```
506
-
507
- ---
508
-
509
- ## Analytics (Raw SQL)
510
-
511
- Direct SELECT is acceptable for read-only analytics:
512
-
513
- ```sql
514
- SELECT status, count(*) as cnt FROM work_items GROUP BY status;
515
- SELECT priority, count(*) FROM work_items WHERE labels @> ARRAY['urgent'] GROUP BY priority;
516
- SELECT avg(EXTRACT(EPOCH FROM (completed_at - created_at))/3600)::numeric(10,2) as avg_hours
517
- FROM work_items WHERE status = 'done' AND item_type = 'task';
518
- ```
519
-
520
- For INSERT/UPDATE/DELETE: always use the RPC functions above.
1
+ # Work Items RPC Reference
2
+
3
+ > Complete reference for work_items table operations via Supabase RPC functions.
4
+ > Use these functions for ALL CRUD operations. Never write raw INSERT/UPDATE/SELECT against work_items.
5
+
6
+ ---
7
+
8
+ ## Core Rule
9
+
10
+ **Always use RPC functions.** They handle defaults, check constraints, timestamps, sorting, and return structured JSONB.
11
+
12
+ Exception: Ad-hoc analytics queries (e.g., `SELECT count(*)`) are acceptable as raw SQL.
13
+
14
+ ---
15
+
16
+ ## Valid Enum Values
17
+
18
+ | Column | Allowed Values |
19
+ |--------|---|
20
+ | `status` | `todo` · `in_progress` · `blocked` · `review` · `done` · `archived` |
21
+ | `priority` | `urgent` · `high` · `normal` · `low` |
22
+ | `item_type` | `epic` · `task` · `subtask` · `bug` · `spike` |
23
+
24
+ ---
25
+
26
+ ## RPC Functions
27
+
28
+ ### 1. get_open_epics — List open epics
29
+
30
+ Returns all non-done/archived epics sorted by priority, then created_at.
31
+
32
+ **Supabase SQL:**
33
+ ```sql
34
+ SELECT get_open_epics();
35
+ SELECT get_open_epics('urgent');
36
+ SELECT get_open_epics(p_label_filter := 'feature-x');
37
+ SELECT get_open_epics('high', 'backend');
38
+ ```
39
+
40
+ **Supabase JS Client:**
41
+ ```ts
42
+ const { data } = await supabase.rpc('get_open_epics');
43
+ const { data } = await supabase.rpc('get_open_epics', {
44
+ p_priority_filter: 'urgent'
45
+ });
46
+ const { data } = await supabase.rpc('get_open_epics', {
47
+ p_label_filter: 'cs2'
48
+ });
49
+ ```
50
+
51
+ **Parameters:**
52
+
53
+ | Name | Type | Required | Notes |
54
+ |------|------|----------|-------|
55
+ | `p_priority_filter` | text | no | NULL = all priorities. One of: `urgent`, `high`, `normal`, `low` |
56
+ | `p_label_filter` | text | no | NULL = all labels. Uses array containment (`@>`) for matching |
57
+
58
+ **Returns:**
59
+ - JSONB array of epic objects (sorted by priority then created_at)
60
+ - Empty `[]` if none found
61
+ - Each epic has: `id`, `title`, `status`, `priority`, `item_type`, `labels`, `created_at`, `updated_at`
62
+
63
+ ---
64
+
65
+ ### 2. insert_work_item — Create a work item
66
+
67
+ Create a new task, epic, subtask, bug, or spike.
68
+
69
+ **Supabase SQL:**
70
+ ```sql
71
+ SELECT insert_work_item(
72
+ p_title := 'Build user authentication',
73
+ p_description := 'Implement OAuth2 flow...',
74
+ p_status := 'todo',
75
+ p_priority := 'high',
76
+ p_item_type := 'task',
77
+ p_parent_id := 'epic-uuid'::uuid,
78
+ p_labels := ARRAY['backend', 'auth'],
79
+ p_source := 'agent'
80
+ );
81
+ ```
82
+
83
+ **Supabase JS Client:**
84
+ ```ts
85
+ const { data } = await supabase.rpc('insert_work_item', {
86
+ p_title: 'Build user authentication',
87
+ p_description: 'Implement OAuth2 flow...',
88
+ p_status: 'todo',
89
+ p_priority: 'high',
90
+ p_item_type: 'task',
91
+ p_parent_id: epicId,
92
+ p_labels: ['backend', 'auth'],
93
+ p_source: 'agent'
94
+ });
95
+ ```
96
+
97
+ **All Parameters:**
98
+
99
+ | Name | Type | Default | Notes |
100
+ |------|------|---------|-------|
101
+ | `p_title` | text | *required* | Work item name (max 255 chars) |
102
+ | `p_description` | text | NULL | Long-form description |
103
+ | `p_status` | text | `'todo'` | One of: `todo`, `in_progress`, `blocked`, `review`, `done`, `archived` |
104
+ | `p_priority` | text | `'normal'` | One of: `urgent`, `high`, `normal`, `low` |
105
+ | `p_item_type` | text | `'task'` | One of: `epic`, `task`, `subtask`, `bug`, `spike` |
106
+ | `p_parent_id` | uuid | NULL | Parent epic UUID for hierarchy (for subtasks) |
107
+ | `p_company_id` | uuid | NULL | Foreign key to companies table |
108
+ | `p_customer_id` | uuid | NULL | Foreign key to customers table |
109
+ | `p_campaign_id` | uuid | NULL | Foreign key to campaigns table |
110
+ | `p_contact_id` | uuid | NULL | Foreign key to contacts table |
111
+ | `p_project_id` | uuid | NULL | Project identifier (project-specific) |
112
+ | `p_assignee` | text | NULL | Assigned person name or email |
113
+ | `p_assigned_to` | text | NULL | Same as assignee (alternate field) |
114
+ | `p_due_date` | date | NULL | Due date for the task |
115
+ | `p_tags` | text[] | `'{}'` | Array of tags |
116
+ | `p_labels` | text[] | `'{}'` | Array of labels (used for filtering) |
117
+ | `p_linked_table` | text | NULL | Name of external table to link (if any) |
118
+ | `p_linked_id` | uuid | NULL | ID in linked_table |
119
+ | `p_created_by` | text | NULL | Who created this item |
120
+ | `p_estimated_hours` | numeric | NULL | Time estimate in hours |
121
+ | `p_notes` | jsonb | `'[]'` | Array of note objects `[{ text, author, timestamp }]` |
122
+ | `p_session_id` | text | NULL | Session UUID (for audit trail) |
123
+ | `p_package` | text | NULL | Package/module name (e.g., '@regen/ui') |
124
+ | `p_source` | text | `'manual'` | How was this created: `manual`, `agent`, `api`, `import` |
125
+ | `p_checklist` | jsonb | `NULL` | Checklist items. If NULL and parent has `definition_of_done`, auto-inherited with all `checked: false` |
126
+ | `p_definition_of_done` | jsonb | `NULL` | DoD template (epics only). Child tasks inherit this as their checklist at insert time |
127
+
128
+ **Returns:**
129
+ - Full inserted work_item row as JSONB
130
+ - Includes auto-generated `id`, `created_at`, `updated_at`
131
+
132
+ ---
133
+
134
+ ### 3. update_work_item_status — Change status
135
+
136
+ Transition a work item's status. Auto-manages `completed_at`.
137
+
138
+ **Hard gate:** implementation agents do not set non-epic work directly to `done`.
139
+ They submit a report and move the item to `review`. A validator closes it as
140
+ `done` only after fresh verification.
141
+
142
+ **Supabase SQL:**
143
+ ```sql
144
+ SELECT update_work_item_status(
145
+ 'work-item-uuid'::uuid,
146
+ 'in_progress'
147
+ );
148
+
149
+ SELECT update_work_item_status(
150
+ 'work-item-uuid'::uuid,
151
+ 'review',
152
+ '["Implementation complete; ready for validator"]'::jsonb,
153
+ 'agent-session-id',
154
+ 'agent'
155
+ );
156
+
157
+ SELECT update_work_item_status(
158
+ 'work-item-uuid'::uuid,
159
+ 'done',
160
+ '["Validator verified implementation report, CodeFlow post, and checklist evidence"]'::jsonb,
161
+ 'validator-session-id',
162
+ 'validator'
163
+ );
164
+ ```
165
+
166
+ **Supabase JS Client:**
167
+ ```ts
168
+ const { data } = await supabase.rpc('update_work_item_status', {
169
+ p_id: itemId,
170
+ p_status: 'in_progress'
171
+ });
172
+
173
+ const { data } = await supabase.rpc('update_work_item_status', {
174
+ p_id: itemId,
175
+ p_status: 'review',
176
+ p_notes_append: ['Implementation complete; ready for validator'],
177
+ p_actor_session_id: agentSessionId,
178
+ p_actor_role: 'agent'
179
+ });
180
+
181
+ const { data } = await supabase.rpc('update_work_item_status', {
182
+ p_id: itemId,
183
+ p_status: 'done',
184
+ p_notes_append: ['Validator verified implementation report, CodeFlow post, and checklist evidence'],
185
+ p_actor_session_id: validatorSessionId,
186
+ p_actor_role: 'validator'
187
+ });
188
+ ```
189
+
190
+ **Parameters:**
191
+
192
+ | Name | Type | Required | Notes |
193
+ |------|------|----------|-------|
194
+ | `p_id` | uuid | yes | work_item ID |
195
+ | `p_status` | text | yes | One of: `todo`, `in_progress`, `blocked`, `review`, `done`, `archived` |
196
+ | `p_notes_append` | jsonb | no | Array of strings appended to existing notes |
197
+ | `p_actor_session_id` | text | review/done | Current agent or validator session ID |
198
+ | `p_actor_role` | text | review/done | `agent` for `review`, `validator` for `done` |
199
+
200
+ **Behavior:**
201
+ - Setting `done` auto-sets `completed_at = now()`
202
+ - Setting non-epic work to `review` raises EXCEPTION unless `implementation_report.codeflow_post` exists and caller role is `agent`
203
+ - Setting non-epic work to `done` raises EXCEPTION unless current status is `review`, caller role is `validator`, `implementation_report.codeflow_post` exists, every required checklist item is checked, and required checklist evidence was ticked by the originating agent session
204
+ - Setting `todo`, `in_progress`, `blocked`, or `review` clears `completed_at`
205
+ - `updated_at` always refreshed to current timestamp
206
+ - Raises exception if ID not found
207
+
208
+ **Returns:**
209
+ - Full updated work_item row as JSONB
210
+
211
+ ---
212
+
213
+ ### 4. update_checklist_item — Tick off a checklist item
214
+
215
+ **Supabase SQL:**
216
+ ```sql
217
+ SELECT update_checklist_item(
218
+ 'work-item-uuid'::uuid,
219
+ 'tsc-clean', -- string id of the checklist item
220
+ true, -- checked state
221
+ 'agent-session-id',
222
+ 'agent',
223
+ 'rpc'
224
+ );
225
+ ```
226
+
227
+ **Parameters:**
228
+
229
+ | Name | Type | Required | Notes |
230
+ |------|------|----------|-------|
231
+ | `p_work_item_id` | uuid | yes | the work item ID |
232
+ | `p_item_id` | text | yes | the `id` field of the checklist item |
233
+ | `p_checked` | boolean | yes | `true` = checked, `false` = unchecked |
234
+ | `p_actor_session_id` | text | yes for checked=true | originating implementation agent session ID |
235
+ | `p_actor_role` | text | yes for checked=true | must be `agent` for completion evidence ticks |
236
+ | `p_event_source` | text | no | source label, default `rpc` |
237
+
238
+ Call this as each checklist item is completed — not all at once at the end.
239
+ The tick is written to `work_item_checklist_events`; supervisor or validator
240
+ re-ticks are rejected by the exit gate.
241
+
242
+ **Returns:** updated checklist JSONB array.
243
+
244
+ ---
245
+
246
+ ### 5. submit_implementation_report — File agent completion narrative
247
+
248
+ MUST be called BEFORE `update_work_item_status(..., 'review', ...)`.
249
+
250
+ **Supabase SQL:**
251
+ ```sql
252
+ SELECT submit_implementation_report(
253
+ 'work-item-uuid'::uuid,
254
+ '{
255
+ "tldr": "One-sentence summary",
256
+ "assumptions": [],
257
+ "deviations": [],
258
+ "uncertainty": [],
259
+ "detail": "Full narrative",
260
+ "flags": [],
261
+ "codeflow_post": {
262
+ "agent_session_id": "agent-session-id",
263
+ "summary": "What changed and why",
264
+ "files_changed": ["path/to/file"],
265
+ "verification": ["command or evidence"],
266
+ "commit": "optional commit hash"
267
+ }
268
+ }'::jsonb
269
+ );
270
+ ```
271
+
272
+ **Parameters:**
273
+
274
+ | Name | Type | Required | Notes |
275
+ |------|------|----------|-------|
276
+ | `p_work_item_id` | uuid | yes | the work item ID |
277
+ | `p_report` | jsonb | yes | implementation report (shape below) |
278
+
279
+ **Returns:** `{ "flags_count": N, "deviations_count": N, "has_deviations": bool }` — signal only.
280
+
281
+ **`implementation_report` shape:**
282
+ ```json
283
+ {
284
+ "tldr": "string",
285
+ "assumptions": ["string", "..."],
286
+ "deviations": ["string — deviation from plan", "..."],
287
+ "uncertainty": ["string — anything unclear", "..."],
288
+ "detail": "string — full narrative",
289
+ "flags": ["string — supervisor attention needed", "..."],
290
+ "codeflow_post": {
291
+ "agent_session_id": "string — originating implementation session",
292
+ "summary": "string — agent-authored CodeFlow post",
293
+ "files_changed": ["string", "..."],
294
+ "verification": ["string", "..."],
295
+ "commit": "string — optional"
296
+ }
297
+ }
298
+ ```
299
+
300
+ If `has_deviations` is true or `flags_count > 0`, supervisor pulls full report from DB.
301
+
302
+ ---
303
+
304
+ ### 6. get_work_items_by_epic — Fetch epic + children
305
+
306
+ Get an epic and all its child tasks/subtasks, sorted by priority.
307
+
308
+ **Supabase SQL:**
309
+ ```sql
310
+ SELECT get_work_items_by_epic('epic-uuid'::uuid);
311
+ SELECT get_work_items_by_epic('epic-uuid'::uuid, 'todo');
312
+ ```
313
+
314
+ **Supabase JS Client:**
315
+ ```ts
316
+ const { data } = await supabase.rpc('get_work_items_by_epic', {
317
+ p_epic_id: epicId
318
+ });
319
+
320
+ const { data } = await supabase.rpc('get_work_items_by_epic', {
321
+ p_epic_id: epicId,
322
+ p_status_filter: 'todo'
323
+ });
324
+ ```
325
+
326
+ **Parameters:**
327
+
328
+ | Name | Type | Required | Notes |
329
+ |------|------|----------|-------|
330
+ | `p_epic_id` | uuid | yes | The epic's work_item ID |
331
+ | `p_status_filter` | text | no | NULL = all statuses. One of: `todo`, `in_progress`, `blocked`, `review`, `done`, `archived` |
332
+
333
+ **Returns:**
334
+ - JSONB array with parent epic first, then children
335
+ - Sorted by: priority (urgent > high > normal > low), then created_at
336
+ - Each item has same structure as returned from insert_work_item
337
+
338
+ ---
339
+
340
+ ### 7. bump_epic_version — Version with history
341
+
342
+ Snapshot an epic's current state and assign a version number.
343
+
344
+ **Supabase SQL:**
345
+ ```sql
346
+ SELECT bump_epic_version(
347
+ 'epic-uuid'::uuid,
348
+ '0.2.0',
349
+ 'Added async task execution',
350
+ 'agent'
351
+ );
352
+ ```
353
+
354
+ **Supabase JS Client:**
355
+ ```ts
356
+ const { data } = await supabase.rpc('bump_epic_version', {
357
+ p_epic_id: epicId,
358
+ p_version: '0.2.0',
359
+ p_change_summary: 'Added async task execution',
360
+ p_changed_by: 'agent'
361
+ });
362
+ ```
363
+
364
+ **Parameters:**
365
+
366
+ | Name | Type | Required | Notes |
367
+ |------|------|----------|-------|
368
+ | `p_epic_id` | uuid | yes | Must be item_type = `'epic'` |
369
+ | `p_version` | text | yes | Semantic version string (e.g., `'0.1.0'`, `'1.0.0'`) |
370
+ | `p_change_summary` | text | no | What changed in this version |
371
+ | `p_changed_by` | text | no | Who bumped the version |
372
+
373
+ **Behavior:**
374
+ - Snapshots the epic's current state into work_item_versions table
375
+ - Updates the epic's `version` column
376
+ - Raises exception if item is not an epic (item_type != 'epic')
377
+
378
+ **Returns:**
379
+ - JSONB object: `{ epic_id, version, version_record_id, change_summary, changed_by, bumped_at }`
380
+
381
+ ---
382
+
383
+ ## Common Patterns
384
+
385
+ ### Create an Epic + Child Tasks
386
+
387
+ ```ts
388
+ // 1. Create epic
389
+ const { data: epic } = await supabase.rpc('insert_work_item', {
390
+ p_title: 'Implement feature X',
391
+ p_item_type: 'epic',
392
+ p_status: 'todo',
393
+ p_priority: 'high',
394
+ p_source: 'agent'
395
+ });
396
+ const epicId = epic[0].id;
397
+
398
+ // 2. Create child tasks
399
+ for (const task of tasks) {
400
+ await supabase.rpc('insert_work_item', {
401
+ p_title: task.title,
402
+ p_item_type: 'task',
403
+ p_priority: task.priority,
404
+ p_parent_id: epicId,
405
+ p_source: 'agent'
406
+ });
407
+ }
408
+ ```
409
+
410
+ ### Fetch Epic Tree, Filter by Status, Update Each
411
+
412
+ ```ts
413
+ const { data: tree } = await supabase.rpc('get_work_items_by_epic', {
414
+ p_epic_id: epicId,
415
+ p_status_filter: 'todo'
416
+ });
417
+
418
+ for (const item of tree) {
419
+ if (item.item_type === 'epic') continue; // skip parent
420
+
421
+ await supabase.rpc('update_work_item_status', {
422
+ p_id: item.id,
423
+ p_status: 'in_progress'
424
+ });
425
+ }
426
+ ```
427
+
428
+ ### Mark Epic Done with Notes
429
+
430
+ ```ts
431
+ const { data } = await supabase.rpc('update_work_item_status', {
432
+ p_id: epicId,
433
+ p_status: 'done',
434
+ p_notes_append: [
435
+ 'All child tasks completed',
436
+ 'Code reviewed and merged to main',
437
+ 'Deployed to production'
438
+ ]
439
+ });
440
+ ```
441
+
442
+ ### List All Open Work, Filter by Priority
443
+
444
+ ```ts
445
+ const { data: epics } = await supabase.rpc('get_open_epics', {
446
+ p_priority_filter: 'urgent'
447
+ });
448
+
449
+ for (const epic of epics) {
450
+ const { data: items } = await supabase.rpc('get_work_items_by_epic', {
451
+ p_epic_id: epic.id
452
+ });
453
+ // process items...
454
+ }
455
+ ```
456
+
457
+ ---
458
+
459
+ ## Supabase Project Reference
460
+
461
+ In production, you'll need:
462
+
463
+ **Environment variables (in .env.local):**
464
+ ```bash
465
+ NEXT_PUBLIC_SUPABASE_URL=https://<project-ref>.supabase.co
466
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key>
467
+ SUPABASE_SERVICE_ROLE_KEY=<service-role-key> # Server-side only
468
+ ```
469
+
470
+ Replace `<project-ref>` with your actual Supabase project reference (e.g., `uvojezuorjgqzmhhgluu`).
471
+
472
+ **Project-specific data (from docs/guides/agent-bootstrap.md):**
473
+ - SUPABASE_PROJECT_ID (if required by your project)
474
+ - Any custom schema names (if work_items lives in non-public schema)
475
+ - Related table names (companies, customers, contacts, campaigns, etc.)
476
+
477
+ ---
478
+
479
+ ## Error Handling
480
+
481
+ ### RPC Not Found
482
+ If `get_open_epics` returns a 404, the function doesn't exist on your Supabase instance.
483
+ Run migration to add the function, or contact your DBA.
484
+
485
+ ### Invalid UUID
486
+ ```ts
487
+ // Wrong — string passed directly
488
+ p_id: "item-id"
489
+
490
+ // Correct — cast to uuid
491
+ p_id: itemId
492
+ ```
493
+
494
+ ### Status Not in Enum
495
+ Check `p_status` against allowed values. Will raise a constraint error if invalid.
496
+
497
+ ### Item Not Found
498
+ `update_work_item_status` and `bump_epic_version` will raise an exception if ID doesn't exist.
499
+ Always catch or check first:
500
+ ```ts
501
+ const { data: items } = await supabase.rpc('get_work_items_by_epic', { p_epic_id: id });
502
+ if (!items || items.length === 0) {
503
+ // Epic not found
504
+ }
505
+ ```
506
+
507
+ ---
508
+
509
+ ## Analytics (Raw SQL)
510
+
511
+ Direct SELECT is acceptable for read-only analytics:
512
+
513
+ ```sql
514
+ SELECT status, count(*) as cnt FROM work_items GROUP BY status;
515
+ SELECT priority, count(*) FROM work_items WHERE labels @> ARRAY['urgent'] GROUP BY priority;
516
+ SELECT avg(EXTRACT(EPOCH FROM (completed_at - created_at))/3600)::numeric(10,2) as avg_hours
517
+ FROM work_items WHERE status = 'done' AND item_type = 'task';
518
+ ```
519
+
520
+ For INSERT/UPDATE/DELETE: always use the RPC functions above.