ndomo 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 (247) hide show
  1. package/.bun-version +1 -0
  2. package/.dockerignore +79 -0
  3. package/.editorconfig +18 -0
  4. package/.env.example +19 -0
  5. package/.github/CODEOWNERS +8 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
  7. package/.github/ISSUE_TEMPLATE/config.yml +2 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.yml +34 -0
  9. package/.github/dependabot.yml +36 -0
  10. package/.github/pull_request_template.md +24 -0
  11. package/.github/release.yml +30 -0
  12. package/.github/workflows/gitleaks.yml +28 -0
  13. package/.github/workflows/release-please.yml +27 -0
  14. package/.github/workflows/smoke.yml +29 -0
  15. package/.husky/commit-msg +1 -0
  16. package/CHANGELOG.md +114 -0
  17. package/Dockerfile +32 -0
  18. package/README.es.md +174 -0
  19. package/README.md +187 -0
  20. package/agents/chronicler.md +98 -0
  21. package/agents/ci-smith.md +136 -0
  22. package/agents/craftsman.md +341 -0
  23. package/agents/deploy-smith.md +138 -0
  24. package/agents/foreman.md +377 -0
  25. package/agents/go-smith.md +164 -0
  26. package/agents/guild.md +188 -0
  27. package/agents/inspector.md +83 -0
  28. package/agents/js-smith.md +127 -0
  29. package/agents/ops-scout.md +173 -0
  30. package/agents/painter.md +200 -0
  31. package/agents/python-smith.md +120 -0
  32. package/agents/ranger.md +307 -0
  33. package/agents/release-smith.md +165 -0
  34. package/agents/rust-smith.md +159 -0
  35. package/agents/sage.md +178 -0
  36. package/agents/scout.md +144 -0
  37. package/agents/scribe.md +156 -0
  38. package/agents/smith.md +201 -0
  39. package/agents/vue-smith.md +155 -0
  40. package/agents/warden.md +216 -0
  41. package/agents/zig-smith.md +156 -0
  42. package/bin/ndomo-analyses.ts +4 -0
  43. package/bin/ndomo-status.ts +4 -0
  44. package/biome.json +57 -0
  45. package/bun.lock +514 -0
  46. package/commitlint.config.js +3 -0
  47. package/config/ndomo.config.json +258 -0
  48. package/config/ndomo.schema.json +166 -0
  49. package/docs/agents.md +375 -0
  50. package/docs/bugs/plan-create-orphan-fk.md +131 -0
  51. package/docs/bugs/task_create_batch-order-index-collision.md +158 -0
  52. package/docs/configuration.md +276 -0
  53. package/docs/database.md +364 -0
  54. package/docs/features/feature-flexible-builder-v1.md +724 -0
  55. package/docs/features/feature-flexible-builder-v2.md +882 -0
  56. package/docs/features/feature-flexible-builder.md +974 -0
  57. package/docs/http-server.md +244 -0
  58. package/docs/installation.md +259 -0
  59. package/docs/integrations.md +129 -0
  60. package/docs/operations/anti-pattern-sub-agent-verify-2026-06-21.md +32 -0
  61. package/docs/operations/audit-v1.md +417 -0
  62. package/docs/operations/audit-v2.md +197 -0
  63. package/docs/operations/audit-v3.md +306 -0
  64. package/docs/operations/db-optimize-foundations.md +123 -0
  65. package/docs/operations/verify-gate-architecture.md +82 -0
  66. package/docs/workflows.md +448 -0
  67. package/opencode.json +5 -0
  68. package/package.json +65 -0
  69. package/release-please-config.json +11 -0
  70. package/scripts/dev-bust-cache.sh +164 -0
  71. package/scripts/install.sh +688 -0
  72. package/scripts/smoke-e2e.ts +704 -0
  73. package/scripts/smoke-hot.ts +417 -0
  74. package/scripts/smoke-http.sh +228 -0
  75. package/scripts/smoke-v4.ts +256 -0
  76. package/scripts/smoke-v5.ts +397 -0
  77. package/scripts/smoke.sh +9 -0
  78. package/scripts/uninstall.sh +224 -0
  79. package/skills/api-security-best-practices/SKILL.md +915 -0
  80. package/skills/bash-scripting/SKILL.md +201 -0
  81. package/skills/bun/SKILL.md +313 -0
  82. package/skills/cavecrew/SKILL.md +82 -0
  83. package/skills/caveman/SKILL.md +74 -0
  84. package/skills/caveman-review/README.md +33 -0
  85. package/skills/caveman-review/SKILL.md +55 -0
  86. package/skills/find-skills/SKILL.md +142 -0
  87. package/skills/frontend-design/LICENSE.txt +177 -0
  88. package/skills/frontend-design/SKILL.md +55 -0
  89. package/skills/golang-patterns/SKILL.md +674 -0
  90. package/skills/golang-security/SKILL.md +185 -0
  91. package/skills/golang-security/evals/evals.json +595 -0
  92. package/skills/golang-security/references/architecture.md +268 -0
  93. package/skills/golang-security/references/checklist.md +80 -0
  94. package/skills/golang-security/references/cookies.md +200 -0
  95. package/skills/golang-security/references/cryptography.md +424 -0
  96. package/skills/golang-security/references/filesystem.md +285 -0
  97. package/skills/golang-security/references/injection.md +315 -0
  98. package/skills/golang-security/references/logging.md +163 -0
  99. package/skills/golang-security/references/memory-safety.md +241 -0
  100. package/skills/golang-security/references/network.md +253 -0
  101. package/skills/golang-security/references/secrets.md +189 -0
  102. package/skills/golang-security/references/third-party.md +159 -0
  103. package/skills/golang-security/references/threat-modeling.md +189 -0
  104. package/skills/golang-testing/SKILL.md +720 -0
  105. package/skills/grill-me/SKILL.md +7 -0
  106. package/skills/javascript-testing-patterns/SKILL.md +537 -0
  107. package/skills/javascript-testing-patterns/references/advanced-testing-patterns.md +513 -0
  108. package/skills/modern-javascript-patterns/SKILL.md +43 -0
  109. package/skills/modern-javascript-patterns/references/advanced-patterns.md +487 -0
  110. package/skills/modern-javascript-patterns/references/details.md +457 -0
  111. package/skills/python-anti-patterns/SKILL.md +349 -0
  112. package/skills/python-design-patterns/SKILL.md +85 -0
  113. package/skills/python-design-patterns/references/details.md +353 -0
  114. package/skills/python-error-handling/SKILL.md +193 -0
  115. package/skills/python-error-handling/references/details.md +171 -0
  116. package/skills/python-testing-patterns/SKILL.md +278 -0
  117. package/skills/python-testing-patterns/references/advanced-patterns.md +411 -0
  118. package/skills/python-testing-patterns/references/details.md +349 -0
  119. package/skills/rust-patterns/SKILL.md +500 -0
  120. package/skills/rust-testing/SKILL.md +501 -0
  121. package/skills/security-review/SKILL.md +504 -0
  122. package/skills/security-review/cloud-infrastructure-security.md +361 -0
  123. package/skills/vue-best-practices/SKILL.md +154 -0
  124. package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
  125. package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
  126. package/skills/vue-best-practices/references/component-async.md +97 -0
  127. package/skills/vue-best-practices/references/component-data-flow.md +307 -0
  128. package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
  129. package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
  130. package/skills/vue-best-practices/references/component-slots.md +216 -0
  131. package/skills/vue-best-practices/references/component-suspense.md +228 -0
  132. package/skills/vue-best-practices/references/component-teleport.md +108 -0
  133. package/skills/vue-best-practices/references/component-transition-group.md +128 -0
  134. package/skills/vue-best-practices/references/component-transition.md +125 -0
  135. package/skills/vue-best-practices/references/composables.md +290 -0
  136. package/skills/vue-best-practices/references/directives.md +162 -0
  137. package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
  138. package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
  139. package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
  140. package/skills/vue-best-practices/references/plugins.md +166 -0
  141. package/skills/vue-best-practices/references/reactivity.md +344 -0
  142. package/skills/vue-best-practices/references/render-functions.md +201 -0
  143. package/skills/vue-best-practices/references/sfc.md +310 -0
  144. package/skills/vue-best-practices/references/state-management.md +135 -0
  145. package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
  146. package/skills/vue-pinia-best-practices/SKILL.md +21 -0
  147. package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
  148. package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
  149. package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
  150. package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
  151. package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
  152. package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
  153. package/skills/zig-0.16/SKILL.md +840 -0
  154. package/skills/zig-0.16/scripts/check-zig-version.sh +21 -0
  155. package/src/cli/analyses.ts +280 -0
  156. package/src/cli/index.ts +108 -0
  157. package/src/cli/serve.ts +192 -0
  158. package/src/cli/smoke.ts +131 -0
  159. package/src/cli/status.test.ts +204 -0
  160. package/src/cli/status.ts +263 -0
  161. package/src/cli/vacuum.test.ts +82 -0
  162. package/src/cli/vacuum.ts +96 -0
  163. package/src/config/schema.test.ts +88 -0
  164. package/src/config/schema.ts +64 -0
  165. package/src/db/analyses-migration.test.ts +210 -0
  166. package/src/db/analyses.test.ts +466 -0
  167. package/src/db/analyses.ts +375 -0
  168. package/src/db/auto-checkpoint.ts +131 -0
  169. package/src/db/client.test.ts +129 -0
  170. package/src/db/client.ts +55 -0
  171. package/src/db/fts-escape.ts +20 -0
  172. package/src/db/incidents.test.ts +201 -0
  173. package/src/db/incidents.ts +93 -0
  174. package/src/db/index.ts +86 -0
  175. package/src/db/migrations-v13.test.ts +141 -0
  176. package/src/db/migrations-v8.test.ts +301 -0
  177. package/src/db/migrations.ts +147 -0
  178. package/src/db/plan-archive.test.ts +180 -0
  179. package/src/db/plan-archive.ts +274 -0
  180. package/src/db/plan-create.test.ts +276 -0
  181. package/src/db/plan-create.ts +78 -0
  182. package/src/db/plan-files.test.ts +289 -0
  183. package/src/db/plan-update-status.ts +287 -0
  184. package/src/db/plans.test.ts +490 -0
  185. package/src/db/plans.ts +534 -0
  186. package/src/db/resolve-project-dir.test.ts +143 -0
  187. package/src/db/resolve-project-dir.ts +75 -0
  188. package/src/db/rollbacks.test.ts +150 -0
  189. package/src/db/rollbacks.ts +67 -0
  190. package/src/db/schema.ts +907 -0
  191. package/src/db/sessions.test.ts +80 -0
  192. package/src/db/sessions.ts +135 -0
  193. package/src/db/shutdown.test.ts +147 -0
  194. package/src/db/shutdown.ts +45 -0
  195. package/src/db/tasks.test.ts +921 -0
  196. package/src/db/tasks.ts +747 -0
  197. package/src/db/types.ts +619 -0
  198. package/src/http/__tests__/auth.test.ts +196 -0
  199. package/src/http/__tests__/routes.test.ts +465 -0
  200. package/src/http/__tests__/sse.test.ts +317 -0
  201. package/src/http/auth.ts +72 -0
  202. package/src/http/middleware/cors.ts +53 -0
  203. package/src/http/middleware/security-headers.ts +21 -0
  204. package/src/http/routes/events.ts +112 -0
  205. package/src/http/routes/health.ts +51 -0
  206. package/src/http/routes/plans.ts +66 -0
  207. package/src/http/routes/sessions.ts +50 -0
  208. package/src/http/routes/tasks.ts +60 -0
  209. package/src/http/server.ts +95 -0
  210. package/src/http/sse.ts +116 -0
  211. package/src/index.ts +37 -0
  212. package/src/lib.ts +65 -0
  213. package/src/mem/scoped.ts +65 -0
  214. package/src/orchestrator/background.test.ts +268 -0
  215. package/src/orchestrator/background.ts +293 -0
  216. package/src/orchestrator/memory-hook.ts +182 -0
  217. package/src/orchestrator/reconciler.ts +123 -0
  218. package/src/orchestrator/scheduler.test.ts +300 -0
  219. package/src/orchestrator/scheduler.ts +243 -0
  220. package/src/plugin.test.ts +2574 -0
  221. package/src/plugin.ts +1690 -0
  222. package/src/sdk/client.ts +66 -0
  223. package/src/worktrees/manager.ts +236 -0
  224. package/src/worktrees/state.ts +87 -0
  225. package/tests/integration/ranger-flow.test.ts +257 -0
  226. package/tools/analysis_archive.ts +28 -0
  227. package/tools/analysis_create.ts +55 -0
  228. package/tools/analysis_get.ts +33 -0
  229. package/tools/analysis_link_plan.ts +44 -0
  230. package/tools/analysis_list.ts +48 -0
  231. package/tools/analysis_search.ts +36 -0
  232. package/tools/analysis_update.ts +44 -0
  233. package/tools/plan_approve.ts +31 -0
  234. package/tools/plan_create.ts +58 -0
  235. package/tools/plan_get.ts +40 -0
  236. package/tools/plan_list.ts +37 -0
  237. package/tools/plan_search.ts +34 -0
  238. package/tools/plan_update_status.ts +71 -0
  239. package/tools/session_checkpoint.ts +31 -0
  240. package/tools/session_end.ts +26 -0
  241. package/tools/session_start.ts +43 -0
  242. package/tools/task_create_batch.ts +70 -0
  243. package/tools/task_list.ts +35 -0
  244. package/tools/task_next_for_agent.ts +30 -0
  245. package/tools/task_search.ts +34 -0
  246. package/tools/task_update_status.ts +37 -0
  247. package/tsconfig.json +31 -0
@@ -0,0 +1,244 @@
1
+ # HTTP Server (Phase 1)
2
+
3
+ ## Overview
4
+
5
+ The optional **ndomo HTTP server** exposes the plugin's SQLite state (plans, tasks, sessions) as a read-only REST API and bridges the OpenCode SDK event stream as Server-Sent Events. Built on [Elysia](https://elysiajs.com/) with `@opencode-ai/sdk` for upstream connectivity.
6
+
7
+ **Phase 1 scope:** read-only REST endpoints + live SSE event relay. No HTTP writes to the DB, no WebSocket, no JWT. Phase 2 will introduce peer-spawning actions.
8
+
9
+ **Feature flag:** `NDOMO_HTTP_ENABLED=false` by default. The server does not bind a port unless explicitly enabled. CLI `--force` flag can override.
10
+
11
+ **Why it exists:** lets external clients (custom dashboards, scripts, browsers) observe ndomo's multi-agent activity without running inside OpenCode. Decouples consumer clients from OpenCode's session lifecycle.
12
+
13
+ ## Quickstart
14
+
15
+ ```bash
16
+ # 1. Configure (only OPENCODE_SERVER_PASSWORD is mandatory when auth enabled)
17
+ export NDOMO_HTTP_ENABLED=true
18
+ export NDOMO_HTTP_PORT=4097
19
+ export OPENCODE_SERVER_PASSWORD='pick-a-strong-passphrase'
20
+
21
+ # 2. Start the server from your project root (where .ndomo/state.db lives)
22
+ bun run src/cli/serve.ts
23
+
24
+ # 3. Liveness probe (no auth)
25
+ curl -fsS localhost:4097/health
26
+
27
+ # 4. Authenticated read
28
+ curl -fsS -u "user:$OPENCODE_SERVER_PASSWORD" localhost:4097/api/plans
29
+
30
+ # 5. Live SSE stream (Ctrl-C to disconnect)
31
+ curl -N -u "user:$OPENCODE_SERVER_PASSWORD" localhost:4097/api/events
32
+ ```
33
+
34
+ ## Configuration
35
+
36
+ All settings have sensible defaults; only `OPENCODE_SERVER_PASSWORD` is required when auth is enabled.
37
+
38
+ | Env var | Default | Purpose |
39
+ |---|---|---|
40
+ | `NDOMO_HTTP_ENABLED` | `false` | Master feature flag. Server binds port only when `true`. |
41
+ | `NDOMO_HTTP_PORT` | `4097` | TCP port. Avoids OpenCode default `4096`. |
42
+ | `NDOMO_HTTP_CORS_ORIGINS` | `*` | Comma-separated allowlist. `*` permits all origins (no credentials). |
43
+ | `NDOMO_HTTP_AUTH_REQUIRED` | `true` | HTTP Basic auth gate. Set `false` for local dev. |
44
+ | `OPENCODE_SERVER_PASSWORD` | (unset) | The HTTP Basic password. Server returns `503 auth_not_configured` if required + unset. |
45
+ | `OPENCODE_SERVER_URL` | `http://localhost:4096` | Upstream OpenCode server for SDK event relay. |
46
+
47
+ See [`.env.example`](.env.example) for full annotations.
48
+
49
+ ## CLI usage
50
+
51
+ ```
52
+ bun run src/cli/serve.ts [options]
53
+ ```
54
+
55
+ Or via the unified CLI:
56
+
57
+ ```
58
+ bun run src/cli/index.ts serve [options]
59
+ ```
60
+
61
+ | Flag | Default | Effect |
62
+ |---|---|---|
63
+ | `--port <n>` | config (4097) | Override port (1-65535). |
64
+ | `--no-auth` | auth required | Disable HTTP Basic auth check (still loads `HttpConfig`). |
65
+ | `--cors <origins>` | config (`*`) | Comma-separated CORS origins, e.g. `https://app.example.com,https://admin.example.com`. |
66
+ | `--force` | off | Start even when `NDOMO_HTTP_ENABLED` is not `true`. |
67
+ | `--help`, `-h` | — | Print help and exit. |
68
+
69
+ **Examples:**
70
+
71
+ ```bash
72
+ # Default config (auth enabled, CORS *)
73
+ bun run src/cli/serve.ts
74
+
75
+ # Local dev with auth off
76
+ bun run src/cli/serve.ts --no-auth --port 4098
77
+
78
+ # Production: explicit CORS allowlist
79
+ bun run src/cli/serve.ts --cors "https://app.example.com,https://admin.example.com"
80
+
81
+ # Bypass feature flag (for ad-hoc runs)
82
+ bun run src/cli/serve.ts --force --port 4099
83
+ ```
84
+
85
+ **Graceful shutdown:** `SIGINT` and `SIGTERM` close the listener and DB cleanly. Exit code `0` on clean shutdown, `1` on startup failure.
86
+
87
+ ## API reference
88
+
89
+ All `/api/*` endpoints require HTTP Basic auth (unless `--no-auth`). `/health` is always public.
90
+
91
+ | Method | Path | Auth | Query params | Response |
92
+ |---|---|---|---|---|
93
+ | `GET` | `/health` | no | — | `{ status, version, uptime, timestamp, dbHealthy }` |
94
+ | `GET` | `/api/plans` | yes | `status`, `sessionId`, `limit` (1-500) | `Plan[]` |
95
+ | `GET` | `/api/plans/search` | yes | `q` (required), `limit` (1-100) | `Plan[]` (FTS5) |
96
+ | `GET` | `/api/plans/:id` | yes | — | `Plan` or `404` |
97
+ | `GET` | `/api/tasks` | yes | `planId` (**required**), `status` | `Task[]` (422 if `planId` missing) |
98
+ | `GET` | `/api/tasks/search` | yes | `q` (required), `limit` (1-100) | `Task[]` (FTS5) |
99
+ | `GET` | `/api/tasks/:id` | yes | — | `Task` or `404` |
100
+ | `GET` | `/api/sessions` | yes | `planId`, `limit` (1-100) | `Session[]` |
101
+ | `GET` | `/api/sessions/active` | yes | — | `Session[]` (`endedAt === null`) |
102
+ | `GET` | `/api/sessions/:id` | yes | — | `Session` or `404` |
103
+ | `GET` | `/api/events` | yes | `types` (csv filter) | `text/event-stream` (SSE) |
104
+
105
+ ### Health response shape
106
+
107
+ ```json
108
+ {
109
+ "status": "ok",
110
+ "version": "0.1.0",
111
+ "uptime": 12345,
112
+ "timestamp": 1735689600000,
113
+ "dbHealthy": true
114
+ }
115
+ ```
116
+
117
+ `status` is `"ok"` when the DB responds to `SELECT 1`, otherwise `"degraded"`.
118
+
119
+ ### SSE response
120
+
121
+ `GET /api/events` returns `Content-Type: text/event-stream` with:
122
+
123
+ - A `hello` event on connect.
124
+ - Forwarded SDK events as `event: <type>` + `data: <json>` lines (default: all types).
125
+ - Optional filter: `?types=session.idle,session.error` (comma-separated type allowlist).
126
+ - A `: keepalive` comment every **30 seconds** to keep proxies from closing idle connections.
127
+ - The stream closes cleanly on client disconnect (abort signal) or SDK error.
128
+
129
+ If the SDK client is unreachable, the endpoint returns `503 sdk_unavailable` (no stream).
130
+
131
+ **Browser example:**
132
+
133
+ ```js
134
+ const es = new EventSource("/api/events", { withCredentials: true });
135
+ // NOTE: EventSource cannot set Authorization. Use a short-lived token via query string (Phase 3).
136
+ es.addEventListener("session.idle", (e) => console.log("session idle:", e.data));
137
+ es.addEventListener("error", (e) => console.error("SSE error:", e));
138
+ ```
139
+
140
+ For auth in the browser, Phase 3 will introduce short-lived SSE tokens via query string. Today, use HTTP Basic + a reverse proxy that injects `Authorization`, or call from server-side scripts only.
141
+
142
+ ## Security notes
143
+
144
+ **HTTP Basic is plaintext over the wire.** Always run behind TLS in production (reverse proxy with nginx/Caddy, or `--force` over a VPN/localhost). HTTP Basic sends `base64(user:password)` — not encrypted, only obfuscated.
145
+
146
+ **CORS:**
147
+ - `NDOMO_HTTP_CORS_ORIGINS=*` permits any origin to call `/api/*` but **does not** send `Access-Control-Allow-Credentials`. Browser credentialed requests (cookies, `withCredentials`) will be rejected.
148
+ - For a real frontend, set explicit origins: `NDOMO_HTTP_CORS_ORIGINS=https://app.example.com`. Credentials are then allowed.
149
+
150
+ **Security headers** applied to every response (from `SECURITY_HEADERS` in `src/config/schema.ts`):
151
+
152
+ | Header | Value |
153
+ |---|---|
154
+ | `X-Content-Type-Options` | `nosniff` |
155
+ | `X-Frame-Options` | `DENY` |
156
+ | `X-XSS-Protection` | `1; mode=block` |
157
+ | `Referrer-Policy` | `strict-origin-when-cross-origin` |
158
+ | `Content-Security-Policy` | `default-src 'none'; frame-ancestors 'none'` |
159
+ | `Permissions-Policy` | `interest-cohort=()` |
160
+ | `X-Powered-By` | `ndomo` |
161
+ | `Strict-Transport-Security` | `max-age=31536000; includeSubDomains` (when `NODE_ENV=production`) |
162
+
163
+ **Localhost-only recommendation:** the server has no rate limiting and no per-user audit trail. For multi-tenant exposure, put it behind a reverse proxy that enforces auth, rate limits, and request logging.
164
+
165
+ **Password strength:** `OPENCODE_SERVER_PASSWORD` is the only gate. Use a passphrase ≥ 24 chars, or wire in a secret manager. Rotate via env reload + process restart.
166
+
167
+ **503 when password missing:** if `auth.required=true` and `OPENCODE_SERVER_PASSWORD` is unset, `/api/*` returns `503 auth_not_configured` with `WWW-Authenticate: Basic realm="ndomo"`. The `/health` endpoint stays public.
168
+
169
+ **No CSRF:** HTTP Basic is CSRF-immune (browser auto-supplies credentials only for same-origin requests, and cross-origin requests need explicit `withCredentials: true` + matching `Allow-Credentials`). For a frontend served from a different origin, set explicit CORS origins (not `*`).
170
+
171
+ ## Troubleshooting
172
+
173
+ **Port already in use (`EADDRINUSE`).**
174
+ Another process bound the port. Check with `lsof -i :4097` or `ss -lntp | grep 4097`. Either kill the conflicting process or use `--port <free>`.
175
+
176
+ **`error: HTTP server is disabled (NDOMO_HTTP_ENABLED is not 'true').`**
177
+ Feature flag off. Either set `NDOMO_HTTP_ENABLED=true` or pass `--force` to the CLI.
178
+
179
+ **`503 auth_not_configured` on every request.**
180
+ `auth.required=true` but `OPENCODE_SERVER_PASSWORD` is empty. Export the env var and restart.
181
+
182
+ **CORS error in browser console: "No 'Access-Control-Allow-Origin' header".**
183
+ The request's `Origin` is not in `NDOMO_HTTP_CORS_ORIGINS` and `*` is not set. Either add the origin to the allowlist or set `*` for development (no credentials).
184
+
185
+ **SSE appears to hang / no events arrive.**
186
+ Two common causes:
187
+ 1. **nginx buffering.** SSE responses need `X-Accel-Buffering: no` (the server sets this header, but a proxy might strip it). Configure your proxy: `proxy_buffering off;` (nginx) or `flush_interval -1` (HAProxy).
188
+ 2. **OpenCode server unreachable.** The `/api/events` endpoint depends on `OPENCODE_SERVER_URL` (default `http://localhost:4096`). Verify with `curl -fsS localhost:4096/config`. If the OpenCode server is down, the endpoint returns `503 sdk_unavailable`.
189
+
190
+ **Connection drops after ~60s behind a load balancer.**
191
+ The server sends `: keepalive` every 30s. If you still see drops, the proxy is closing idle TCP connections faster than 30s. Either lower the keepalive in your proxy or configure TCP keepalive at the OS level.
192
+
193
+ **`422 validation_error: planId is required` from `/api/tasks`.**
194
+ `planId` is required (unlike `/api/sessions` which makes it optional). Pass `?planId=<uuid>`.
195
+
196
+ ## Architecture
197
+
198
+ ```
199
+ ┌──────────┐ ┌──────────────────────────┐ ┌─────────────────────┐
200
+ │ Browser │ HTTP+SSE│ ndomo Elysia server │ SDK │ OpenCode server │
201
+ │ / curl │────────▶│ :4097 (Phase 1) │───────▶│ :4096 │
202
+ │ / script │ basic │ ┌────────────────────┐ │ HTTP │ ┌───────────────┐ │
203
+ │ │◀────────│ │ securityHeaders │ │◀───────│ │ event.subscribe│ │
204
+ │ │ │ │ corsMiddleware │ │ SSE │ │ session.* │ │
205
+ │ │ │ │ httpBasicAuth │ │ │ │ ... │ │
206
+ │ │ │ │ /health (public) │ │ │ └───────────────┘ │
207
+ │ │ │ │ /api/plans (auth) │ │ └─────────────────────┘
208
+ │ │ │ │ /api/tasks (auth) │ │
209
+ │ │ │ │ /api/sessions │ │ ┌─────────────────────┐
210
+ │ │ │ │ /api/events (SSE) │──────────│ .ndomo/state.db │
211
+ │ │ │ └────────────────────┘ │ SQL │ (plans, tasks, ... )│
212
+ │ │ └──────────────────────────┘ └─────────────────────┘
213
+ ```
214
+
215
+ - **Inbound:** `Elysia` handles auth (Basic), CORS, security headers, then dispatches to REST handlers or SSE stream.
216
+ - **REST handlers** are thin adapters — they delegate to `src/db/*.ts` (SQLite) and return JSON. No business logic in routes.
217
+ - **SSE** opens an async iterator on `sdkClient.event.subscribe()`, writes events through `SseWriter`, and cleans up on abort.
218
+ - **Outbound to OpenCode:** `getSdkClient()` creates a singleton `createOpencodeClient` configured with `baseUrl: OPENCODE_SERVER_URL` and `directory: process.cwd()` (project scoping via `x-opencode-directory` header).
219
+
220
+ ## Files
221
+
222
+ | File | Purpose |
223
+ |---|---|
224
+ | `src/http/server.ts` | Elysia app builder + listen wrapper |
225
+ | `src/http/auth.ts` | HTTP Basic auth middleware (timing-safe compare, 503 on missing password) |
226
+ | `src/http/middleware/security-headers.ts` | OWASP baseline headers |
227
+ | `src/http/middleware/cors.ts` | CORS preflight + `Allow-Origin` logic |
228
+ | `src/http/routes/health.ts` | `GET /health` |
229
+ | `src/http/routes/plans.ts` | `/api/plans/*` |
230
+ | `src/http/routes/tasks.ts` | `/api/tasks/*` |
231
+ | `src/http/routes/sessions.ts` | `/api/sessions/*` |
232
+ | `src/http/routes/events.ts` | `/api/events` SSE relay |
233
+ | `src/http/sse.ts` | SSE format + writer + keepalive |
234
+ | `src/sdk/client.ts` | OpenCode SDK singleton (with health check) |
235
+ | `src/cli/serve.ts` | CLI entry point with flags |
236
+ | `src/config/schema.ts` | `loadHttpConfig()` + `SECURITY_HEADERS` |
237
+ | `src/http/__tests__/` | Unit + integration tests (528 tests total in repo) |
238
+ | `scripts/smoke-http.sh` | End-to-end smoke (5 curl assertions + headers) |
239
+
240
+ ## See also
241
+
242
+ - [`.env.example`](.env.example) — annotated env reference
243
+ - [`docs/database.md`](database.md) — schema for `Plan`, `Task`, `Session`
244
+ - [`README.md`](../README.md) — quickstart + features
@@ -0,0 +1,259 @@
1
+ # Installation Guide
2
+
3
+ ## Prerequisites
4
+
5
+ - **bun** >= 1.1.0 — [install bun](https://bun.sh)
6
+ - **OpenCode** installed and configured
7
+ - At least one **provider authenticated** in OpenCode (the agents will use provider models)
8
+
9
+ Verify prerequisites:
10
+
11
+ ```bash
12
+ bun --version # >= 1.1.0
13
+ opencode --version
14
+ opencode config list providers # should show at least one authenticated provider
15
+ ```
16
+
17
+ ## Install via curl/wget
18
+
19
+ The install script supports being piped directly from a URL, useful for quick setups, CI/CD pipelines, and ephemeral environments. When piped, the script detects it is running from stdin, clones the repository to `/tmp`, and re-executes itself.
20
+
21
+ ```bash
22
+ # Quick install (interactive, will prompt for provider)
23
+ curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash
24
+
25
+ # Non-interactive with provider preset
26
+ curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s -- --provider=opencode --no-provider-prompt
27
+
28
+ # With budget preset + DCP
29
+ curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s -- --preset=budget --with-dcp
30
+
31
+ # Install from a fork or dev branch
32
+ curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s -- \
33
+ --repo=https://github.com/myorg/ndomo-fork \
34
+ --branch=dev \
35
+ --provider=opencode
36
+ ```
37
+
38
+ The `--repo` and `--branch` flags are only relevant in piped mode; they are ignored when running from a local clone.
39
+
40
+ ## Install from Git Clone
41
+
42
+ ```bash
43
+ # 1. Clone the repository
44
+ git clone <repo-url> ndomo
45
+ cd ndomo
46
+
47
+ # 2. Install dependencies
48
+ bun install
49
+
50
+ # 3. Run the install script
51
+ ./scripts/install.sh
52
+ ```
53
+
54
+ The install script:
55
+ 1. Copies the configuration to `~/.config/opencode/ndomo.json`
56
+ 2. Registers the plugin with OpenCode
57
+ 3. Verifies all agent definitions in `agents/` are valid
58
+ 4. Links the bundled skills (bash-scripting, caveman, grill-me, and 20+ others from `skills/`) to the OpenCode skills directory
59
+ 5. Applies the active preset (`presets[PRESET]` from `ndomo.config.json`) to every agent's `model:` and `temperature:` frontmatter.
60
+ 6. Registers `ndomo` as a local `file:` dependency in `~/.config/opencode/package.json` and installs it to `~/.config/opencode/node_modules/ndomo/` via `bun install`. This is what allows OpenCode to resolve the plugin from `plugin: ["ndomo", ...]` and register its tools. See [OpenCode plugin docs](https://opencode.ai/docs/es/plugins/) and [custom tools docs](https://opencode.ai/docs/es/custom-tools/).
61
+ 7. Symlinks the bundled custom tools (14 DB access tools: `plan_*`, `task_*`, `session_*`) from `tools/` to `~/.config/opencode/tools/`. See [OpenCode custom tools docs](https://opencode.ai/docs/es/custom-tools/).
62
+
63
+ **Note:** The `reasoning_effort` field (optional, `low`|`medium`|`high`|`xhigh`) is supported for reasoning-capable models (DeepSeek, MiMo, OpenAI). Omit it for non-reasoning models.
64
+
65
+ ## Development Workflow
66
+
67
+ When developing ndomo (editing agents, skills, config, or source files), use these commands to apply changes:
68
+
69
+ | Command | When to use | What it does |
70
+ |---|---|---|
71
+ | `./scripts/install.sh` | First install, after cloning, or after changing agents/skills/config | Copies agents, skills, and config to `~/.config/opencode/`. Installs ndomo package. Always preferred for structural changes. |
72
+ | `bun run dev:bust` | After editing only `src/*.ts` source files (no agent/skill/config changes) | Quick cache bust: removes stale Bun transpiler cache entries referencing ndomo, bumps mtime on all `src/*.ts` files. Does not kill opencode. |
73
+ | `bun run dev:reset` | Same as `dev:bust` but when opencode is running and you need a clean restart | `dev:bust` + kills running opencode processes. Run this after editing source to ensure the next `opencode` picks up fresh code. |
74
+
75
+ ### Daily dev loop
76
+
77
+ 1. Edit source files in `src/`.
78
+ 2. Run `bun run dev:reset` to kill opencode + bust Bun cache + bump mtimes.
79
+ 3. Start opencode: `opencode`.
80
+ 4. Repeat.
81
+
82
+ ### Why symlinks cause stale cache
83
+
84
+ Bun caches transpiled TypeScript modules in `~/.bun/install/cache/` keyed by the **resolved path** of the module. When ndomo is installed via a symlink (e.g., `~/.config/opencode/node_modules/ndomo → /home/nico/ndomo`), the cache key uses the symlink path. Editing source files on the target filesystem does **not** change the symlink path, so Bun serves stale code from cache.
85
+
86
+ The `file:` dep strategy in `install_ndomo_package()` avoids this by creating a real copy in `node_modules` (no symlink → no stale cache). If you ever end up with a symlink install (from an older `install.sh` version or a manual `ln -s`), re-run `./scripts/install.sh` — it detects the symlink, removes it, and reinstalls as a real copy (`scripts/install.sh` lines 247–254).
87
+
88
+ ### Manual cache busting (advanced)
89
+
90
+ Run the cache bust script directly for more control:
91
+
92
+ ```bash
93
+ # Dry-run — inspect what would be removed
94
+ ./scripts/dev-bust-cache.sh
95
+
96
+ # Apply — remove stale cache entries + bump mtimes
97
+ ./scripts/dev-bust-cache.sh --apply
98
+
99
+ # Kill opencode first, then bust cache
100
+ ./scripts/dev-bust-cache.sh --apply --kill
101
+ ```
102
+
103
+ The script (`scripts/dev-bust-cache.sh`):
104
+ 1. Optionally kills running opencode processes (`--kill`)
105
+ 2. Removes Bun cache entries referencing "ndomo"
106
+ 3. Removes Bun cache entries referencing the ndomo source path (`$PROJECT_ROOT/src`)
107
+ 4. Touches all `src/*.ts` files to bump mtime (forces re-transpilation)
108
+
109
+ It is **idempotent** — safe to run multiple times. No-op if the cache is already clean.
110
+
111
+ ## Install Flags
112
+
113
+ | Flag | Description |
114
+ |---|---|
115
+ | `--provider=ID` | Override the provider prefix for all agents. The model ID is taken from the active preset; only the `provider/` segment of the `model:` field is swapped. Example: preset gives `opencode-go/minimax-m2.7`, `--provider=opencode` rewrites to `opencode/minimax-m2.7`. |
116
+ | `--no-provider-prompt` | Skip the interactive provider prompt. The preset is still applied; no provider prefix override is performed. |
117
+ | `--with-dcp` | Install and configure the DCP plugin (opencode-dynamic-context-pruning) as an optional peer dependency |
118
+ | `--preset=NAME` | Select preset from `config/ndomo.config.json::presets[NAME]`. The preset is the source of truth for agent models at install time. (default: `default`, options: `default`, `budget`) |
119
+ | `--repo=URL` | Override the repository URL (for piped installs from a fork or mirror). Ignored in local clones. |
120
+ | `--branch=NAME` | Override the repository branch (for piped installs from `dev`/`feature/*` branches). Ignored in local clones. |
121
+
122
+ **Environment variable:** `NDOMO_SKIP_PACKAGE_INSTALL=1` — skip the package installation step (`bun install` in `~/.config/opencode/`). Useful if you manage the OpenCode plugin directory manually or if the install step is causing conflicts.
123
+
124
+ Example with all flags:
125
+
126
+ ```bash
127
+ # Local clone with all flags
128
+ ./scripts/install.sh --with-dcp --preset=budget
129
+
130
+ # Piped install with provider, fork, and custom branch
131
+ curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s -- \
132
+ --repo=https://github.com/myorg/ndomo-fork \
133
+ --branch=dev \
134
+ --provider=opencode \
135
+ --no-provider-prompt
136
+ ```
137
+
138
+ ## Provider Override
139
+
140
+ When `install.sh` runs without `--provider` and without `--no-provider-prompt`, it shows the active preset and asks for confirmation. The active preset is the single source of truth for agent models; `--provider=ID` only changes the provider prefix.
141
+
142
+ TTY flow:
143
+
144
+ 1. The script prints a table of `(agent, preset model, current provider prefix)` derived from `config/ndomo.config.json`.
145
+ 2. It asks: `Apply preset '$PRESET' as configured? [Y/n/override]`
146
+ 3. `Y` (or Enter) applies the preset, no prefix override.
147
+ 4. `n` skips preset application (warn).
148
+ 5. `override` enters the interactive provider picker from models.dev and applies a prefix override to every agent's `model:` field. The model ID comes from the preset; only the `provider/` segment is swapped.
149
+
150
+ To skip the prompt and apply the preset silently:
151
+
152
+ ```bash
153
+ ./scripts/install.sh --no-provider-prompt
154
+ ```
155
+
156
+ To override the provider prefix non-interactively (e.g., use `opencode` instead of `opencode-go` for all agents):
157
+
158
+ ```bash
159
+ ./scripts/install.sh --provider=opencode
160
+ ```
161
+
162
+ The provider override works in piped mode:
163
+
164
+ ```bash
165
+ curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s -- --provider=opencode --no-provider-prompt
166
+ ```
167
+
168
+ ## Verify Installation
169
+
170
+ 1. Start OpenCode:
171
+
172
+ ```bash
173
+ opencode
174
+ ```
175
+
176
+ 2. Inside the OpenCode session, test agent communication:
177
+
178
+ ```
179
+ ping all agents
180
+ ```
181
+
182
+ Expected output: each of the 22 agents responds with a status confirmation.
183
+
184
+ 3. Check that the Foreman is the primary agent:
185
+
186
+ ```
187
+ /agent
188
+ ```
189
+
190
+ Should show `foreman` as the active agent.
191
+
192
+ 4. Verify the ndomo DB was created:
193
+
194
+ ```bash
195
+ ls -la .ndomo/state.db
196
+ ```
197
+
198
+ The plugin creates this SQLite database automatically on first load. It
199
+ stores plans, tasks, and sessions. See [docs/database.md](docs/database.md)
200
+ for details.
201
+
202
+ ## Uninstall
203
+
204
+ ```bash
205
+ ./scripts/uninstall.sh
206
+ ```
207
+
208
+ Removes:
209
+ - The config file at `~/.config/opencode/ndomo.json`
210
+ - Plugin registration from OpenCode
211
+ - Skill symlinks
212
+
213
+ **Flag:**
214
+
215
+ | Flag | Description |
216
+ |---|---|
217
+ | `--keep-data` | Remove plugin config but preserve memory files in `~/.ndomo/mem/` |
218
+
219
+ ## Troubleshooting
220
+
221
+ ### bun not found
222
+
223
+ ```
224
+ bun: command not found
225
+ ```
226
+
227
+ Install bun: `curl -fsSL https://bun.sh/install | bash`. Restart your shell after installation.
228
+
229
+ ### Provider not authenticated
230
+
231
+ ```
232
+ Error: No authenticated provider found
233
+ ```
234
+
235
+ Configure a provider in OpenCode: `opencode config set provider <provider-name>` and follow the authentication flow. At least one provider must be authenticated before ndomo agents can make API calls.
236
+
237
+ ### Agent not responding
238
+
239
+ If `ping all agents` shows no response from one or more agents:
240
+
241
+ 1. Verify the config file exists: `ls ~/.config/opencode/ndomo.json`
242
+ 2. Validate the config against the schema: `cat ~/.config/opencode/ndomo.json`
243
+ 3. Re-run the install script: `./scripts/install.sh`
244
+ 4. Check OpenCode logs for model routing errors — the agent's `model` field in the config must match a model available through your authenticated provider.
245
+
246
+ ### Permission denied on scripts
247
+
248
+ ```bash
249
+ chmod +x scripts/install.sh scripts/uninstall.sh
250
+ ```
251
+
252
+ ### Plugin not loading
253
+
254
+ If OpenCode doesn't detect ndomo as a plugin:
255
+
256
+ 1. Ensure `ndomo` is listed in `config/ndomo.config.json` under `plugins`
257
+ 2. Check that the package is installed: `ls ~/.config/opencode/node_modules/ndomo/` — if missing, re-run `./scripts/install.sh` or symlink manually: `ln -sfn $(pwd) ~/.config/opencode/node_modules/ndomo`
258
+ 3. Verify the plugin entry point (`src/index.ts`) compiles without errors: `bun run build`
259
+ 4. Check that the local node_modules were installed: `ls node_modules/ndomo` (or the symlink target)
@@ -0,0 +1,129 @@
1
+ # Integration Guide
2
+
3
+ ## opencode-mem (required)
4
+
5
+ opencode-mem is a persistent memory system for OpenCode. It provides a local vector database (SQLite + USearch) with semantic search across sessions.
6
+
7
+ **License:** MIT
8
+
9
+ ### What it is
10
+
11
+ opencode-mem stores and retrieves developer knowledge across sessions. ndomo uses it as the primary persistence layer — every agent stores and searches memories before planning or executing tasks.
12
+
13
+ ### How ndomo uses it
14
+
15
+ The foreman searches memory before every planning cycle:
16
+
17
+ 1. **Project search** — `memory({mode:"search", query, scope:"project"})` retrieves past decisions from the current project.
18
+ 2. **Cross-project search** — `memory({mode:"search", query, scope:"all-projects"})` retrieves knowledge from all projects.
19
+ 3. **Compressed storage** — before calling `memory({mode:"add"})`, ndomo compresses content using caveman regex patterns (0 LLM tokens).
20
+
21
+ ### Tool usage
22
+
23
+ | Mode | Call | Purpose |
24
+ |---|---|---|
25
+ | search | `memory({mode:"search", query, scope:"project"})` | Search current project memories |
26
+ | search | `memory({mode:"search", query, scope:"all-projects"})` | Search all project memories |
27
+ | add | `memory({mode:"add", content, topic})` | Store a new memory entry |
28
+ | add | `memory({mode:"add", content, topic, tags})` | Store with tags for filtering |
29
+
30
+ ### Web UI
31
+
32
+ opencode-mem includes a web UI at `http://localhost:4747` for browsing and managing memory entries.
33
+
34
+ ### Config
35
+
36
+ See [configuration.md](configuration.md#memory-config) for memory-specific settings (`storagePath`, `defaultScope`, `autoCaptureEnabled`, `cavemanCompress`).
37
+
38
+ ## DCP (optional)
39
+
40
+ Dynamic Context Pruning (`@tarquinen/opencode-dcp`) is an optional plugin that compresses conversation context by removing low-value tool outputs while preserving critical information.
41
+
42
+ **License:** AGPL-3.0
43
+
44
+ ### What it is
45
+
46
+ DCP monitors context token usage and, on request or automatically, prunes low-value content from the conversation window. This extends session life in long-running tasks.
47
+
48
+ ### How to install
49
+
50
+ ```bash
51
+ ./scripts/install.sh --with-dcp
52
+ ```
53
+
54
+ This installs `@tarquinen/opencode-dcp` as an optional peer dependency.
55
+
56
+ ### How ndomo uses it
57
+
58
+ The foreman monitors context size:
59
+
60
+ - **~50k tokens** (foreman `minContextLimit`) — suggests `/dcp-compress` to the user.
61
+ - **~100k tokens** (foreman `maxContextLimit`) — invokes `compress` tool automatically at a natural pause point.
62
+ - **If DCP not installed** — falls back to native OpenCode context compaction.
63
+
64
+ ### Context thresholds
65
+
66
+ Per-agent thresholds in `dcp_overrides` (only when DCP installed):
67
+
68
+ | Agent | minContextLimit | maxContextLimit |
69
+ |---|---|---|
70
+ | scout | 30,000 | 80,000 |
71
+ | scribe | 30,000 | 80,000 |
72
+ | foreman | 50,000 | 100,000 |
73
+ | sage | 50,000 | 100,000 |
74
+ | guild | 50,000 | 100,000 |
75
+ | inspector | 40,000 | 90,000 |
76
+
77
+ Agents without overrides use DCP defaults.
78
+
79
+ ### Protected tools
80
+
81
+ The `compress` tool is listed in `protectedTools` — it cannot be pruned from context or disabled by subagents. This ensures DCP can always function when needed.
82
+
83
+ ## Caveman + Memory
84
+
85
+ Memories are compressed before storage using regex-based caveman compression (`src/orchestrator/memory-hook.ts`).
86
+
87
+ ### Compression rules
88
+
89
+ - **Protected:** Fenced code blocks (`` ``` ``), URLs (http, https, git, ssh).
90
+ - **Removed:** Articles (a, an, the, el, la, los, las, un, una), filler words (just, really, basically, actually, simply, etc.), leading conjunctions (and, but, or, so, then, also), filler phrases ("in order to", "it is important to note that", etc.), excess whitespace.
91
+
92
+ ### Regex-only
93
+
94
+ All compression is regex-based — zero LLM tokens consumed for compression. The `COMPRESSION_PATTERNS` array in `memory-hook.ts` defines all patterns, applied sequentially.
95
+
96
+ ### Limitations
97
+
98
+ - **Non-English text:** Spanish articles (el, la, los, las, un, una, unos, unas) are included in the pattern set. Other languages are not explicitly handled — their articles and fillers may survive compression.
99
+ - **Bilingual content:** Mixed-language content is compressed with English + Spanish rules only. Additional languages may require new patterns in `COMPRESSION_PATTERNS`.
100
+ - **Preserved content:** Code blocks and URLs are always preserved verbatim, even if they contain patterns that would otherwise be stripped.
101
+
102
+ ## Troubleshooting
103
+
104
+ ### opencode-mem not found
105
+
106
+ ```
107
+ Error: Cannot find module 'opencode-mem'
108
+ ```
109
+
110
+ Ensure opencode-mem is installed. ndomo lists it as a dependency in `package.json` — `bun install` should install it automatically. If not: `bun add opencode-mem`.
111
+
112
+ ### Web UI not accessible
113
+
114
+ ```
115
+ curl http://localhost:4747
116
+ Connection refused
117
+ ```
118
+
119
+ Verify opencode-mem is running. Start it manually: `npx opencode-mem serve`. Default port is 4747.
120
+
121
+ ### DCP commands not available
122
+
123
+ If `/dcp-compress` doesn't work:
124
+
125
+ 1. Verify DCP is installed: check `~/.config/opencode/node_modules/@tarquinen/opencode-dcp` exists.
126
+ 2. Verify DCP is registered as an optional plugin in `config/ndomo.config.json`: `"optionalPlugins": ["@tarquinen/opencode-dcp"]`.
127
+ 3. Restart OpenCode after installing.
128
+
129
+ DCP is optional — ndomo functions without it, but long sessions may exhaust context without pruning.
@@ -0,0 +1,32 @@
1
+ # Anti-pattern: Sub-agents marking plans done without DB verification
2
+
3
+ **Date:** 2026-06-21
4
+ **Plan context:** a7e75f8b (foreman-verify-protocol-v1)
5
+ **Severity:** process — schema drift / audit gaps
6
+
7
+ ## Incidents
8
+
9
+ ### 1. ops-audit-v2
10
+ A sub-agent closed the plan as `completed` after reporting audit findings, but never ran an objective verification pass against the DB state. The completion status became self-certified rather than evidence-based.
11
+
12
+ - **Root cause:** no explicit verify step before `plan_update_status("completed")`.
13
+ - **Impact:** plan marked done while DB consistency checks were unverified; audit trail gap.
14
+
15
+ ### 2. craftsman-db-optimize-v1
16
+ Migration v12 was not auto-applied in the execution environment, yet the plan was marked `completed`. This is the second occurrence of a schema migration being skipped while the plan status advanced.
17
+
18
+ - **Root cause:** craftsman trusted implementation-side success (tests green, code merged) without checking `PRAGMA user_version`.
19
+ - **Impact:** schema drift between code expectations and runtime DB; runtime queries could hit missing views/indexes.
20
+
21
+ ## Pattern
22
+
23
+ Both cases share the same failure mode: the executing sub-agent acted as both implementer and certifier. It assumed "I wrote it / tests passed" meant "system state matches intent," bypassing objective verification of the actual DB and runtime artifacts.
24
+
25
+ This is a governance anti-pattern: the agent that performs the work cannot also be the sole authority that declares the work correct. Objective verification must come from an independent check of the produced state.
26
+
27
+ ## Mitigation
28
+
29
+ - Enforce the **foreman verify protocol** (see `agents/foreman.md`): no plan moves to `completed` until verify checks are recorded.
30
+ - Add a **gate agent** (inspector) as an independent post-plan verification step; see `verify-gate-architecture.md`.
31
+ - Craftsman must not self-certify completion; gate verdict is binary and write-once.
32
+ - For DB-touching plans, require `PRAGMA user_version` and a smoke query against the changed schema as gate checks.