@adhd/backlog 0.1.9 → 1.0.1

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 (65) hide show
  1. package/CHANGELOG.md +128 -47
  2. package/README.md +369 -81
  3. package/api.d.ts +196 -0
  4. package/api.ir.json +1 -0
  5. package/cli.d.ts +45 -18
  6. package/env.d.ts +23 -3
  7. package/envelope.d.ts +163 -0
  8. package/extract-live.d.ts +56 -0
  9. package/index.d.ts +11 -10
  10. package/index.js +255 -197
  11. package/index.mjs +11348 -19043
  12. package/install-skill.d.ts +23 -0
  13. package/ir-artifact.d.ts +86 -0
  14. package/package.json +51 -15
  15. package/query/card.d.ts +31 -0
  16. package/query/get.d.ts +11 -0
  17. package/query/index.d.ts +67 -0
  18. package/query/markdown.d.ts +11 -0
  19. package/query/query.d.ts +131 -0
  20. package/query/resolve.d.ts +123 -0
  21. package/query/types.d.ts +450 -0
  22. package/query/views/registry.d.ts +43 -0
  23. package/query/views/semantic.d.ts +101 -0
  24. package/query/views/stats.d.ts +109 -0
  25. package/search-shortcut.d.ts +79 -0
  26. package/serve.d.ts +18 -0
  27. package/server.d.ts +139 -4
  28. package/skill/SKILL.md +632 -138
  29. package/store/graph-backlog-store.d.ts +80 -17
  30. package/store/immediate-retry.d.ts +24 -13
  31. package/store/type-policy.d.ts +4 -0
  32. package/store/vocabulary-guard.d.ts +52 -0
  33. package/write/audit.d.ts +36 -0
  34. package/write/bootstrap.d.ts +161 -0
  35. package/write/catalog.d.ts +369 -0
  36. package/write/citation-path.d.ts +133 -0
  37. package/write/claim-lease.d.ts +21 -0
  38. package/write/claim.d.ts +80 -0
  39. package/write/create-issue.d.ts +253 -0
  40. package/write/delete.d.ts +39 -0
  41. package/write/embed-drain.d.ts +68 -0
  42. package/write/embedding-config.d.ts +81 -0
  43. package/write/embedding-observer.d.ts +80 -0
  44. package/write/errors.d.ts +365 -0
  45. package/write/issue-status.d.ts +10 -0
  46. package/write/move.d.ts +70 -0
  47. package/write/relate.d.ts +52 -0
  48. package/write/transition.d.ts +64 -0
  49. package/write/tx.d.ts +344 -0
  50. package/write/update.d.ts +81 -0
  51. package/client.d.ts +0 -174
  52. package/markdown.d.ts +0 -75
  53. package/migration-admin.d.ts +0 -26
  54. package/model.d.ts +0 -437
  55. package/store/audit-log.d.ts +0 -16
  56. package/store/claim.d.ts +0 -24
  57. package/store/crud.d.ts +0 -62
  58. package/store/ids.d.ts +0 -24
  59. package/store/lifecycle.d.ts +0 -36
  60. package/store/mapping.d.ts +0 -101
  61. package/store/mutate-metadata.d.ts +0 -8
  62. package/store/query.d.ts +0 -68
  63. package/store/repo-migration.d.ts +0 -51
  64. package/store/serve-lock.d.ts +0 -42
  65. package/store/structure.d.ts +0 -66
package/README.md CHANGED
@@ -1,103 +1,391 @@
1
1
  # @adhd/backlog
2
2
 
3
- A structured, queryable, multi-agent-safe **graph store** for backlog items (bugs,
4
- debt, features, investigations, plans) — a replacement for ad-hoc `BACKLOG.md`
5
- editing that stays compatible with the existing markdown convention this repo
6
- already uses.
3
+ A structured, queryable, concurrent-safe backlog for bugs, debt, features, and
4
+ investigations — served from one graph store over a CLI, an MCP server, an HTTP
5
+ API, and an in-process client.
7
6
 
8
- Built on `@adhd/sox-graph-store` (bi-temporal nodes/edges over SQLite) and mounted
9
- live via `@adhd/apigen-core-client` (no code generation — `extract()` →
10
- `composeSchemas()` → `plugin.run()`).
7
+ ## Why
11
8
 
12
- See `SPEC.md` (functional spec: personas, data model, status vocabulary,
13
- operation surface) and `DESIGN.md` (technical design: graph mapping, claim
14
- protocol, env/apigen wiring) in this package for the full contract.
9
+ The problem this solves: a plain `BACKLOG.md` file has no way to represent
10
+ "someone is already working on this," no way to enforce that a closed item
11
+ cites the fix that closed it, no structured way to ask "what's open in this
12
+ component," and no safe way for two agents to edit it at once. `@adhd/backlog`
13
+ replaces that file with a real store: issues are addressed by a stable `uid`,
14
+ every mutation is audited, claims are leases with staleness detection, and
15
+ queries are structured filters instead of `grep`.
16
+
17
+ ## Install
15
18
 
16
19
  ```bash
17
- pnpm add @adhd/backlog
20
+ pnpm add -g @adhd/backlog
21
+ adhd-backlog --help
18
22
  ```
19
23
 
20
- ## Usage
24
+ This installs the `adhd-backlog` CLI binary. The same package also runs as an
25
+ MCP server, an HTTP API, and an importable client library — see
26
+ [Transports](#transports).
21
27
 
22
- ```ts
23
- import { createItem, listItems, claimItem, transitionStatus } from '@adhd/backlog';
24
- import { buildBacklogEnv } from '@adhd/backlog';
25
- import { openGraphBacklogStore } from '@adhd/backlog';
28
+ ## Quickstart
29
+
30
+ Issues live under a **project** (and, optionally, a **component**), both
31
+ resolved by name or `uid`. File the project first — `create` never mints one.
32
+ File every issue with the correct project **and component**: a component-less
33
+ item lands on the project's reserved `(root)` component and is invisible to
34
+ component-scoped queries. The agent-facing filing rules and the real misfiling
35
+ hazards live in [`skill/SKILL.md` §4](skill/SKILL.md).
36
+
37
+ ```bash
38
+ adhd-backlog upsert-project --input '{
39
+ "name": "demo-project",
40
+ "path": "/tmp/demo",
41
+ "by": "agent:worker-1"
42
+ }'
43
+ ```
26
44
 
27
- const env = buildBacklogEnv();
28
- env.ensureDirs();
29
- const store = openGraphBacklogStore(env.files.db);
30
- const ctx = { store, env };
45
+ ```json
46
+ { "ok": true, "data": { "uid": "49ec673b-…", "created": true, "project": { "uid": "49ec673b-…", "name": "demo-project", "path": "/tmp/demo" } } }
47
+ ```
31
48
 
32
- const { item } = await createItem(ctx, {
33
- family: 'BUG-EXAMPLE',
34
- title: 'Example bug',
35
- body: 'Something is broken.',
36
- repo: 'PseudoSky/adhd',
37
- });
49
+ File an issue against it:
38
50
 
39
- await claimItem(ctx, item.repo, item.humanId, 'implementer:abc123');
40
- await transitionStatus(ctx, item.repo, item.humanId, 'FIXED', {
41
- by: 'implementer:abc123',
42
- citations: [{ file: 'entrypoint/backlog/src/client.ts' }],
43
- });
51
+ ```bash
52
+ adhd-backlog create --input '{
53
+ "title": "Query pagination drops the last page under offset paging",
54
+ "body": "offset+limit near the end of a result set silently returns fewer rows than `total` implies.",
55
+ "project": "demo-project",
56
+ "priority": "HIGH",
57
+ "gitContext": "feat/backlog-hard-replacement @ 4bf902fc",
58
+ "by": "agent:worker-1"
59
+ }'
60
+ ```
44
61
 
45
- const open = await listItems(ctx, { repo: item.repo, status: 'open' });
62
+ ```json
63
+ { "ok": true, "data": { "created": true, "uid": "b3b2da0e-…", "item": { "uid": "b3b2da0e-…", "title": "Query pagination drops the last page under offset paging", "kind": "issue", "status": "open", "priority": "HIGH", "project": "49ec673b-…", "component": "65c4e373-…", "createdAt": "2026-09-24T00:14:38.802Z", "author": "agent:worker-1", "gitContext": "feat/backlog-hard-replacement @ 4bf902fc" } } }
46
64
  ```
47
65
 
48
- ## Running as a live server (no codegen)
66
+ > `project` and `component` in the result are `uid`s, not names. `gitContext`
67
+ > is described under [Citations & git context](#citations--git-context).
49
68
 
50
- ```ts
51
- import { startBacklogServer } from '@adhd/backlog';
69
+ Query for it:
70
+
71
+ ```bash
72
+ adhd-backlog query --input '{"filter":{"project":"demo-project","status":"open"}}'
73
+ ```
74
+
75
+ ```json
76
+ { "ok": true, "data": { "view": "list", "items": [{ "uid": "b3b2da0e-…", "title": "Query pagination drops the last page under offset paging", "kind": "issue", "status": "open", "priority": "HIGH" }], "hasMore": false }, "meta": { "total": 1, "returned": 1, "limit": 50 } }
77
+ ```
78
+
79
+ Move it forward — a `transition` records the status change in the issue's audit
80
+ trail. A non-empty `note` is required by default
81
+ (`project_policy.transition_requires_note`); a terminal status additionally
82
+ requires verifiable `citations` **only if** the project's policy turns citation
83
+ enforcement on (`citationRequired`, default `false`):
84
+
85
+ ```bash
86
+ adhd-backlog transition --input '{
87
+ "uid": "b3b2da0e-…",
88
+ "toStatus": "in-progress",
89
+ "note": "reproduced with limit:10, offset:95 against a 100-row set",
90
+ "by": "agent:worker-1"
91
+ }'
92
+ ```
93
+
94
+ ```json
95
+ { "ok": true, "data": { "uid": "b3b2da0e-…", "fromStatus": "open", "toStatus": "in-progress", "transitionUid": "9bcb9844-…" } }
96
+ ```
97
+
98
+ A failed call returns the same envelope with `ok:false` instead of throwing:
99
+
100
+ ```json
101
+ { "ok": false, "error": { "code": "item_not_found", "message": "No live issue found for uid \"does-not-exist\"", "details": { "retryable": false } } }
102
+ ```
103
+
104
+ Every output above was captured from the built binary
105
+ (`entrypoint/backlog/dist/index.js`); `uid`s are truncated with `…`. See
106
+ [Which build these docs describe](#which-build-these-docs-describe).
107
+
108
+ ## Key features
109
+
110
+ ### 1. One shared graph store, concurrent-safe by design
111
+
112
+ The store is not a file you edit — it is a graph database that many processes
113
+ read and write at once. There is no single-writer lock; concurrency is bounded
114
+ by real transactions and leases. File the same item from two agents and one
115
+ wins cleanly, with correct audit rows. (SPEC.md §3/§4; `src/write/tx.ts`.)
116
+
117
+ ### 2. Leased claims for multi-agent work
52
118
 
53
- const abort = new AbortController();
54
- await startBacklogServer({ transport: 'both', port: 3400, signal: abort.signal });
119
+ `claim` is a lease, not a flag: it is idempotent for the same claimant
120
+ (`renew`), self-healing when stale, and it protects the item — a competing
121
+ `transition` by another agent is refused with `conflict` until the lease is
122
+ released or goes stale.
123
+
124
+ ```bash
125
+ adhd-backlog claim --input '{"uid":"b3b2da0e-…","by":"agent:worker-1","action":"claim"}'
126
+ ```
127
+
128
+ ```json
129
+ { "ok": true, "data": { "uid": "b3b2da0e-…", "status": "claimed", "claimedBy": "agent:worker-1", "claimedAt": "2026-09-24T00:14:39.978Z" } }
55
130
  ```
56
131
 
57
- - `POST /backlog/client-d/create-item`, `GET /backlog/client-d/get-item`, ... —
58
- every `client.ts` export, mounted live via `@adhd/apigen-plugin-api-fastify`.
59
- (The `client-d` route segment is not a typo — see `DESIGN.md` §7's CLI
60
- section / `SPEC.md` §7's transport table for why it's there.)
61
- - Every export is also available as an MCP tool (e.g. `backlog_client_d_get_item`)
62
- via `@adhd/apigen-plugin-mcp` (stdio transport by default).
132
+ ### 3. Structured, verifiable citations
133
+
134
+ A citation is `{ file, lines?, context?, symbol? }`, not hand-typed markdown.
135
+ When a project enforces citations, a terminal transition is rejected with
136
+ `precondition_failed` unless every cited file resolves inside the project's own
137
+ registered path. Name a `symbol` and the store shells out to
138
+ `gitnexus impact <symbol>` at write time to stamp a best-effort `blastRadius`
139
+ on the citation.
140
+
141
+ ### 4. A registry that answers "where does this live?"
63
142
 
64
- ## CLI (`backlog`, live apigen mount — no codegen)
143
+ `lookup` resolves a tool name, file path, or URL to its owning
144
+ project/component — before you reach for `find`. `query` also lists the
145
+ registry directly:
65
146
 
66
147
  ```bash
67
- pnpm add -g @adhd/backlog # installs the `backlog` bin
68
- backlog --help # live-derived command listing
69
- backlog create-item --input '{"family":"BUG-EXAMPLE","title":"t","body":"b","repo":"org/repo"}'
70
- backlog get-item --repo org/repo --human-id BUG-EXAMPLE-001
71
- backlog list-items --filter '{"status":"OPEN"}'
72
- ```
73
-
74
- - Same architecture as the HTTP/MCP transports above — `entrypoint/backlog/src/cli.ts`'s
75
- `runBacklogCli()` reuses `buildBacklogApigenPackage()` and hands it straight
76
- to `@adhd/apigen-plugin-cli-output`'s `run()`. No `apigen generate`, no
77
- bespoke argument parsing — routing, flag parsing, validation, dispatch, and
78
- exit codes all come from that plugin.
79
- - **You type `backlog <command>`, never the internal `client-d` segment.**
80
- `runBacklogCli` derives the real command-table prefix from the live
81
- `operations` list at runtime (`resolveCommandPrefix`) and prepends it before
82
- dispatch — so `backlog get-item …` resolves even though the plugin's real
83
- command table is keyed `backlog client-d get-item` internally (same
84
- `client-d` artifact as the HTTP/MCP routes above; the CLI is the one
85
- transport that hides it from the caller).
86
- - Flags are the schema's domain params, kebab-cased (`humanId` → `--human-id`);
87
- object/array-typed params take a JSON string (`--input '{...}'`,
88
- `--filter '{...}'`).
89
- - Exit codes follow `@adhd/apigen-base-errors`'s `CLI_EXIT_CODE` table: `0`
90
- success, `2` invalid argument (bad/unknown flag, failed validation), `4`
91
- unknown command, etc. Result is printed as JSON to stdout; errors as JSON to
92
- stderr.
93
- - Honors the same `ADHD_BACKLOG_SCOPE`/`ADHD_ENV_SCOPE` scope env vars as the
94
- library API (see Scope below) — there is no separate CLI-only config.
95
- - `runBacklogCli(argv?, opts?)` is also exported for in-process programmatic
96
- use (e.g. a test harness), symmetric with `startBacklogServer`.
97
-
98
- ## Scope
99
-
100
- Resolved via `@adhd/environment` (see `env.ts`): `global` (default —
101
- `~/.adhd/backlog/<namespace>/data/backlog.db`, spans every repo on the
102
- machine), `project` (`<projectRoot>/.adhd/backlog/<namespace>/data/backlog.db`,
103
- one repo), or `system`. See `SPEC.md` §3 for the full resolution order.
148
+ adhd-backlog get --input '{"registry":"project","name":"demo-project"}'
149
+ ```
150
+
151
+ ```json
152
+ { "ok": true, "data": { "uid": "49ec673b-…", "name": "demo-project", "path": "/tmp/demo", "components": [{ "name": "(root)" }], "locations": [] } }
153
+ ```
154
+
155
+ ### 5. Optional semantic search, optional Markdown rendering
156
+
157
+ With embeddings off (the default), keyword filtering (`filter.grep`),
158
+ exact/registry lookup, and every write verb work fully. Turn them on and
159
+ `query`'s natural-language `text` routes to semantic ranking; ask for a semantic
160
+ read with no backend and you get a precise `rag_not_configured` error rather
161
+ than a silent empty result. `query --input '{"format":"markdown", …}'` renders
162
+ every item-list view as Markdown.
163
+
164
+ ### 6. N-way fan-out with `batch action`
165
+
166
+ Run one operation over many items instead of N one-at-a-time calls. `operation`
167
+ is the mounted id (`backlog/create`, …); each item wraps its payload under
168
+ `input`; `mode` (`parallel`/`serial`/`chained`), `onItemError`, `concurrency`,
169
+ and `itemTimeoutMs` govern the run.
170
+
171
+ ```bash
172
+ adhd-backlog batch action --input '{
173
+ "operation": "backlog/get",
174
+ "items": [{"input": {"uid": "does-not-exist"}}]
175
+ }'
176
+ ```
177
+
178
+ ### 7. Supersede-safe history
179
+
180
+ A `body` edit does not mutate in place — it mints a successor issue and carries
181
+ the previous node's edges (claims, citations, notes, transitions, relations)
182
+ forward to it. The old `uid` remains queryable history: addressing it returns
183
+ `conflict` and names the live successor, so a stale uid can never silently
184
+ resolve to the wrong record.
185
+
186
+ ```json
187
+ { "ok": false, "error": { "code": "conflict", "message": "Issue \"63c2f57e-…\" was superseded by a body edit and is no longer the live issue; it now lives under \"e3b32183-…\"", "details": { "retryable": false } } }
188
+ ```
189
+
190
+ ## Command surface
191
+
192
+ Every verb but `embedding-status` takes a single `--input` flag carrying one JSON
193
+ object; there are no per-field flags. Nineteen operations (the 18 verbs plus
194
+ `batch`):
195
+
196
+ | Verb | CLI | MCP tool |
197
+ | ----------------- | ------------------------------- | -------------------------- |
198
+ | `get` | `adhd-backlog get` | `backlog_get` |
199
+ | `query` | `adhd-backlog query` | `backlog_query` |
200
+ | `priorityMatrix` | `adhd-backlog priority-matrix` | `backlog_priority_matrix` |
201
+ | `partOfRollup` | `adhd-backlog part-of-rollup` | `backlog_part_of_rollup` |
202
+ | `openCurve` | `adhd-backlog open-curve` | `backlog_open_curve` |
203
+ | `embeddingStatus` | `adhd-backlog embedding-status` | `backlog_embedding_status` |
204
+ | `lookup` | `adhd-backlog lookup` | `backlog_lookup` |
205
+ | `create` | `adhd-backlog create` | `backlog_create` |
206
+ | `update` | `adhd-backlog update` | `backlog_update` |
207
+ | `transition` | `adhd-backlog transition` | `backlog_transition` |
208
+ | `claim` | `adhd-backlog claim` | `backlog_claim` |
209
+ | `relate` | `adhd-backlog relate` | `backlog_relate` |
210
+ | `move` | `adhd-backlog move` | `backlog_move` |
211
+ | `delete` | `adhd-backlog delete` | `backlog_delete` |
212
+ | `upsertProject` | `adhd-backlog upsert-project` | `backlog_upsert_project` |
213
+ | `upsertComponent` | `adhd-backlog upsert-component` | `backlog_upsert_component` |
214
+ | `upsertLocation` | `adhd-backlog upsert-location` | `backlog_upsert_location` |
215
+ | `rmLocation` | `adhd-backlog rm-location` | `backlog_rm_location` |
216
+ | `batch` | `adhd-backlog batch action` | `batch_action` |
217
+
218
+ `--help` prints these as `backlog <verb>`; the CLI also accepts the bare
219
+ `adhd-backlog <verb>` form shown above, and accepts the explicit namespace
220
+ prefix at any position. `get`/`query`/`lookup`/`embedding-status` are reads.
221
+ `create`/`update`/`transition`/`claim`/`relate`/`move`/`delete` mutate one
222
+ issue. The four `upsert*`/`rmLocation` verbs manage the **registry** —
223
+ projects, components, and locations. `batch action` fans any one of them out
224
+ over many items.
225
+
226
+ ## Transports
227
+
228
+ `adhd-backlog` mounts one set of operation descriptors onto every host — there
229
+ is no per-transport reimplementation, so a change to a verb's behavior is
230
+ simultaneously true everywhere:
231
+
232
+ - **CLI** — `adhd-backlog <verb> --input '<json>'`
233
+ - **MCP** — tools named `backlog_<verb>` (e.g. `backlog_create`, `backlog_query`)
234
+ - **HTTP** — a REST API generated from the same descriptors, with an OpenAPI document
235
+ - **In-process** — the package's exported client library, for embedding the store in a Node process
236
+
237
+ ## Envelope
238
+
239
+ Every verb call returns one of two shapes, on every transport:
240
+
241
+ ```ts
242
+ { ok: true, data: T, warnings?: string[], meta?: { total, returned, limit?, offset?, truncated? } }
243
+ { ok: false, error: { code, message, details? }, warnings?: string[] }
244
+ ```
245
+
246
+ `meta` is present on list-shaped reads (`query`) and its `total` is the true
247
+ match count _before_ `limit`/`offset` are applied; a truncated page sets
248
+ `data.hasMore: true` and `data.nextCursor`, so a short result never silently
249
+ looks complete.
250
+
251
+ ### Error codes
252
+
253
+ | Code | Meaning | CLI exit code |
254
+ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
255
+ | `not_found` | A referenced catalog entry (project/component/kind/status/priority) doesn't exist | 4 |
256
+ | `item_not_found` | The addressed issue doesn't exist | 1 |
257
+ | `invalid_argument` | Malformed flag or parameter shape | 2 |
258
+ | `validation` | Schema rejection — unknown filter key, unknown projection field, over-limit | 2 |
259
+ | `store_busy` | Store contention (a lease or a write conflict); `details.retryable` and `details.retryAfterMs` indicate whether/how to retry | 1 |
260
+ | `rag_not_configured` | A semantic/similarity read was requested but no embedding backend is configured, or the vector space is empty | 1 |
261
+ | `conflict` | Someone else holds the claim, a single-valued relation is already taken, or a supersede raced | 1 |
262
+ | `precondition_failed` | A gate refused the write — a terminal transition missing its required citation/note, or a citation that couldn't be verified against a known project path | 1 |
263
+ | `internal` | Unclassified server-side failure | 1 |
264
+
265
+ Success always exits 0. `item_not_found` and `internal` are deliberately
266
+ distinct codes even though they share exit code 1 — a caller distinguishes
267
+ "this uid doesn't exist" from "something broke" by `error.code`, not by exit
268
+ code alone.
269
+
270
+ ### Citations & git context
271
+
272
+ A citation is `{ file, lines?, context?, symbol? }`. Pass `citations` on
273
+ `create`, or on the `transition` that moves an issue into a terminal status
274
+ when the project's policy requires it. `file` must name a file, not a
275
+ directory: a directory target is rejected as a non-retryable `validation`
276
+ error.
277
+
278
+ The item-level **`gitContext`** is separate from a citation's own `context`:
279
+ it records the repo disclosure contract's `<active git context>` — the first
280
+ element of a `Citations:` block (`Citations: [<active git context>, …]`).
281
+ Pass it on `create`, or on a `transition` to update it. It is stored on the
282
+ issue itself, and a `format:'markdown'` query renders it once at the head of
283
+ the item's `Citations:` block. Omit it and nothing is stored.
284
+
285
+ ### Which build these docs describe
286
+
287
+ These docs describe `entrypoint/backlog/dist/index.js` built from revision
288
+ `9df2a5c7`, whose `create`/`transition` inputs include `gitContext`. A
289
+ **globally installed** `adhd-backlog` may be an older build (it is whatever was
290
+ last published/installed); on such a build `gitContext` is not in the schema
291
+ and is rejected with `invalid_argument`, and the stats/rollup ops
292
+ (`priority-matrix` / `part-of-rollup` / `open-curve`) are absent. Run
293
+ `adhd-backlog --help` and compare the `backlog create` / `backlog
294
+ priority-matrix` lines against this page if a documented field or verb is
295
+ refused.
296
+
297
+ ## Build & startup
298
+
299
+ `nx build backlog` emits TWO things into `dist/`: the compiled
300
+ `index.js` + `api.d.ts`, and `api.ir.json` — the extracted operation
301
+ descriptors for `api.d.ts`, produced by the build's own hidden `ir-artifact`
302
+ subcommand:
303
+
304
+ ```bash
305
+ node dist/index.js ir-artifact --out dist/api.ir.json
306
+ ```
307
+
308
+ Startup (the CLI, an MCP `initialize`, `--help`) reads `api.ir.json` and
309
+ derives the mounted surface from it **without loading the type extractor**, so
310
+ a cold start is fast without any warm cache — the fix for a startup path that
311
+ previously paid a multi-second synchronous extraction on every invocation.
312
+
313
+ `api.ir.json` is trusted only while it still matches the built declarations
314
+ beside it: the artifact records content hashes of the WHOLE `dist/**.d.ts`
315
+ surface it was extracted from (not just `api.d.ts` — extraction resolves types
316
+ through `api.d.ts`'s sibling imports), and startup re-hashes that surface
317
+ before using it. A missing, corrupt, or stale artifact (any `.d.ts` changed
318
+ since the bake) is refused, and startup falls back to a live extraction through
319
+ the extract-stage IR cache, which persists the result for the next run.
320
+
321
+ `ir-artifact` is a build step, not a user command — it is absent from `--help`
322
+ and store-free (it never opens the graph store and never writes under
323
+ `~/.adhd`).
324
+
325
+ | Setting | Env var | Default | Notes |
326
+ | ---------------- | -------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
327
+ | IR cache enabled | `APIGEN_IR_CACHE_ENABLED` | `1` | Governs ONLY the **runtime fallback** cache. It does **not** bypass the baked `api.ir.json` artifact — that artifact is the startup path, not a cache |
328
+ | IR cache file | `APIGEN_IR_CACHE_FILE` | a machine-global path under `~/.adhd` | Where a fallback extraction's result is cached |
329
+
330
+ ## Library API
331
+
332
+ Beyond the CLI/MCP/HTTP surface, the package exports its query layer
333
+ (`src/query/index.ts`), including three rollup/stats read views that are **not**
334
+ members of `query.view`:
335
+
336
+ - `priorityMatrix(handle, { filter? })` — a status-aware per-priority count
337
+ breakdown (defaults to open work; `filter.status` may be `'open'`, `'closed'`,
338
+ `'all'`, or a status name).
339
+ - `partOfRollup(handle, { uid })` — transitive `part_of` descendants of an
340
+ issue, counted once each regardless of chain depth.
341
+ - `openCurve(handle, { filter?, at })` — per-sampled-instant counts of issues
342
+ that existed and how many were open, reconstructed from the audit trail.
343
+
344
+ Reach them by importing `@adhd/backlog` in-process. The same three are also
345
+ mounted as operations — `priority-matrix` / `part-of-rollup` / `open-curve` on
346
+ the CLI (`backlog_priority_matrix` / `backlog_part_of_rollup` /
347
+ `backlog_open_curve` on MCP); see the command surface above.
348
+
349
+ ## Configuration
350
+
351
+ Configuration cascades over `@adhd/environment`, prefixed `ADHD_BACKLOG_`
352
+ (later layers override earlier: built-in default → config file → environment
353
+ variable). The store defaults to a single shared **global** scope — one backlog
354
+ spanning every project on the machine, not one per repository — so an agent
355
+ working across repos sees the same graph everywhere unless it explicitly opts
356
+ into a narrower scope.
357
+
358
+ | Setting | Env var | Default | Notes |
359
+ | ------------------ | ----------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
360
+ | Database path | `ADHD_BACKLOG_DATABASE_PATH` | resolved under the scope root | Where the graph store's data file lives |
361
+ | Write busy timeout | `ADHD_BACKLOG_DATABASE_BUSY_TIMEOUT_MS` | `5000` | How long a write waits on a contended lock before giving up |
362
+ | Log level | `ADHD_BACKLOG_LOG_LEVEL` | `info` | `trace`\|`debug`\|`info`\|`warn`\|`error`\|`fatal`\|`silent` |
363
+ | Scope | `ADHD_BACKLOG_SCOPE` (falls back to `ADHD_ENV_SCOPE`) | `global` | Which store root to resolve against |
364
+ | Namespace | `--namespace <value>` CLI flag (no env var) | `production` | Which path segment under the scope root to resolve against (`<root>/backlog/<namespace>/…`) — one of `production`\|`test`\|`sandbox`. `--namespace sandbox` ALSO mints a fresh throwaway root and writes a real `config.yaml` there with `embedding.enabled: false`, so a sandboxed invocation is isolated along two independent axes plus a deliberate config, never an accident of an empty directory |
365
+ | Semantic search | `ADHD_BACKLOG_EMBEDDING_ENABLED` | `false` | See below |
366
+ | Embedding provider | `ADHD_BACKLOG_EMBEDDING_PROVIDER` | `fastembed` | Only consulted when embedding is enabled |
367
+ | Embedding model | `ADHD_BACKLOG_EMBEDDING_MODEL` | `bge-base-en-v1.5` (768-dim) | Only consulted when embedding is enabled |
368
+
369
+ **Embedding (semantic search) is entirely optional.** With it left at its
370
+ default (`false`), `@adhd/backlog` works fully — every verb, keyword filtering
371
+ (`filter.grep`), and exact/registry lookup all function with no embedding
372
+ backend at all. Turning `filter.semantic`, `filter.anchor`, `view:"similar"`,
373
+ `sort:"relevance"`, or `fields:["_vector"]` on without an embedding backend
374
+ configured doesn't crash anything — it returns a `rag_not_configured` error
375
+ that says exactly that, so a caller can tell "not available" apart from "no
376
+ matches." Enabling embedding requires two optional dependencies to be
377
+ installed alongside the package and a store backend that supports native
378
+ vector storage; if either is missing, backlog logs the reason and leaves
379
+ semantic search unconfigured rather than failing to start.
380
+
381
+ ## Further docs
382
+
383
+ - [`skill/SKILL.md`](skill/SKILL.md) — the full agent-facing command surface, worked examples, and filing rules.
384
+ - [`SPEC.md`](SPEC.md) — the functional specification: operation surface, status vocabulary, acceptance criteria.
385
+ - [`DATA_MODEL.md`](DATA_MODEL.md) — the node/edge model this package reads and writes.
386
+ - [`CHANGELOG.md`](CHANGELOG.md) — version history.
387
+
388
+ ## Contributing
389
+
390
+ See [`CONTRIBUTING.md`](CONTRIBUTING.md). This package is MIT-licensed under
391
+ [`LICENSE`](LICENSE); security policy: [`SECURITY.md`](../../SECURITY.md).