@adhd/backlog 0.1.8 → 1.0.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.
- package/CHANGELOG.md +93 -44
- package/README.md +332 -81
- package/api.d.ts +146 -0
- package/cli.d.ts +45 -18
- package/env.d.ts +23 -3
- package/envelope.d.ts +163 -0
- package/index.d.ts +11 -10
- package/index.js +531 -173
- package/index.mjs +29814 -15647
- package/install-skill.d.ts +23 -0
- package/package.json +50 -15
- package/query/card.d.ts +31 -0
- package/query/get.d.ts +11 -0
- package/query/index.d.ts +67 -0
- package/query/markdown.d.ts +11 -0
- package/query/query.d.ts +131 -0
- package/query/resolve.d.ts +123 -0
- package/query/types.d.ts +450 -0
- package/query/views/registry.d.ts +43 -0
- package/query/views/semantic.d.ts +101 -0
- package/query/views/stats.d.ts +109 -0
- package/search-shortcut.d.ts +79 -0
- package/serve.d.ts +18 -0
- package/server.d.ts +139 -4
- package/skill/SKILL.md +619 -138
- package/store/graph-backlog-store.d.ts +80 -17
- package/store/immediate-retry.d.ts +24 -13
- package/store/type-policy.d.ts +4 -0
- package/store/vocabulary-guard.d.ts +52 -0
- package/version-info.d.ts +15 -0
- package/write/audit.d.ts +36 -0
- package/write/bootstrap.d.ts +123 -0
- package/write/catalog.d.ts +351 -0
- package/write/claim-lease.d.ts +21 -0
- package/write/claim.d.ts +80 -0
- package/write/create-issue.d.ts +250 -0
- package/write/delete.d.ts +39 -0
- package/write/embed-drain.d.ts +68 -0
- package/write/embedding-observer.d.ts +80 -0
- package/write/errors.d.ts +303 -0
- package/write/issue-status.d.ts +10 -0
- package/write/move.d.ts +70 -0
- package/write/relate.d.ts +52 -0
- package/write/transition.d.ts +60 -0
- package/write/tx.d.ts +344 -0
- package/write/update.d.ts +81 -0
- package/client.d.ts +0 -169
- package/markdown.d.ts +0 -75
- package/migration-admin.d.ts +0 -26
- package/model.d.ts +0 -437
- package/store/audit-log.d.ts +0 -16
- package/store/claim.d.ts +0 -24
- package/store/crud.d.ts +0 -62
- package/store/ids.d.ts +0 -24
- package/store/lifecycle.d.ts +0 -36
- package/store/mapping.d.ts +0 -101
- package/store/mutate-metadata.d.ts +0 -8
- package/store/query.d.ts +0 -68
- package/store/repo-migration.d.ts +0 -51
- package/store/serve-lock.d.ts +0 -42
- package/store/structure.d.ts +0 -66
package/README.md
CHANGED
|
@@ -1,103 +1,354 @@
|
|
|
1
1
|
# @adhd/backlog
|
|
2
2
|
|
|
3
|
-
A structured, queryable,
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
9
|
-
live via `@adhd/apigen-core-client` (no code generation — `extract()` →
|
|
10
|
-
`composeSchemas()` → `plugin.run()`).
|
|
7
|
+
## Why
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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).
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
```bash
|
|
38
|
+
adhd-backlog upsert-project --input '{
|
|
39
|
+
"name": "demo-project",
|
|
40
|
+
"path": "/tmp/demo",
|
|
41
|
+
"by": "agent:worker-1"
|
|
42
|
+
}'
|
|
43
|
+
```
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
body: 'Something is broken.',
|
|
36
|
-
repo: 'PseudoSky/adhd',
|
|
37
|
-
});
|
|
45
|
+
```json
|
|
46
|
+
{"ok":true,"data":{"uid":"49ec673b-…","created":true,"project":{"uid":"49ec673b-…","name":"demo-project","path":"/tmp/demo"}}}
|
|
47
|
+
```
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
await transitionStatus(ctx, item.repo, item.humanId, 'FIXED', {
|
|
41
|
-
by: 'implementer:abc123',
|
|
42
|
-
citations: [{ file: 'entrypoint/backlog/src/client.ts' }],
|
|
43
|
-
});
|
|
49
|
+
File an issue against it:
|
|
44
50
|
|
|
45
|
-
|
|
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
|
+
}'
|
|
46
60
|
```
|
|
47
61
|
|
|
48
|
-
|
|
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"}}}
|
|
64
|
+
```
|
|
49
65
|
|
|
50
|
-
|
|
51
|
-
|
|
66
|
+
> `project` and `component` in the result are `uid`s, not names. `gitContext`
|
|
67
|
+
> is described under [Citations & git context](#citations--git-context).
|
|
68
|
+
|
|
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
|
|
118
|
+
|
|
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"}}
|
|
130
|
+
```
|
|
131
|
+
|
|
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?"
|
|
142
|
+
|
|
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:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
adhd-backlog get --input '{"registry":"project","name":"demo-project"}'
|
|
149
|
+
```
|
|
52
150
|
|
|
53
|
-
|
|
54
|
-
|
|
151
|
+
```json
|
|
152
|
+
{"ok":true,"data":{"uid":"49ec673b-…","name":"demo-project","path":"/tmp/demo","components":[{"name":"(root)"}],"locations":[]}}
|
|
55
153
|
```
|
|
56
154
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
|
|
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`
|
|
63
165
|
|
|
64
|
-
|
|
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.
|
|
65
170
|
|
|
66
171
|
```bash
|
|
67
|
-
|
|
68
|
-
backlog
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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 takes a single `--input` flag carrying one JSON object; there are no
|
|
193
|
+
per-field flags. Eighteen operations (the 17 verbs plus `batch`):
|
|
194
|
+
|
|
195
|
+
| Verb | CLI | MCP tool |
|
|
196
|
+
| ----------------- | ------------------------------- | -------------------------- |
|
|
197
|
+
| `get` | `adhd-backlog get` | `backlog_get` |
|
|
198
|
+
| `query` | `adhd-backlog query` | `backlog_query` |
|
|
199
|
+
| `priorityMatrix` | `adhd-backlog priority-matrix` | `backlog_priority_matrix` |
|
|
200
|
+
| `partOfRollup` | `adhd-backlog part-of-rollup` | `backlog_part_of_rollup` |
|
|
201
|
+
| `openCurve` | `adhd-backlog open-curve` | `backlog_open_curve` |
|
|
202
|
+
| `lookup` | `adhd-backlog lookup` | `backlog_lookup` |
|
|
203
|
+
| `create` | `adhd-backlog create` | `backlog_create` |
|
|
204
|
+
| `update` | `adhd-backlog update` | `backlog_update` |
|
|
205
|
+
| `transition` | `adhd-backlog transition` | `backlog_transition` |
|
|
206
|
+
| `claim` | `adhd-backlog claim` | `backlog_claim` |
|
|
207
|
+
| `relate` | `adhd-backlog relate` | `backlog_relate` |
|
|
208
|
+
| `move` | `adhd-backlog move` | `backlog_move` |
|
|
209
|
+
| `delete` | `adhd-backlog delete` | `backlog_delete` |
|
|
210
|
+
| `upsertProject` | `adhd-backlog upsert-project` | `backlog_upsert_project` |
|
|
211
|
+
| `upsertComponent` | `adhd-backlog upsert-component` | `backlog_upsert_component` |
|
|
212
|
+
| `upsertLocation` | `adhd-backlog upsert-location` | `backlog_upsert_location` |
|
|
213
|
+
| `rmLocation` | `adhd-backlog rm-location` | `backlog_rm_location` |
|
|
214
|
+
| `batch` | `adhd-backlog batch action` | `batch_action` |
|
|
215
|
+
|
|
216
|
+
`--help` prints these as `backlog <verb>`; the CLI also accepts the bare
|
|
217
|
+
`adhd-backlog <verb>` form shown above, and accepts the explicit namespace
|
|
218
|
+
prefix at any position. `get`/`query`/`lookup` are reads.
|
|
219
|
+
`create`/`update`/`transition`/`claim`/`relate`/`move`/`delete` mutate one
|
|
220
|
+
issue. The four `upsert*`/`rmLocation` verbs manage the **registry** —
|
|
221
|
+
projects, components, and locations. `batch action` fans any one of them out
|
|
222
|
+
over many items.
|
|
223
|
+
|
|
224
|
+
## Transports
|
|
225
|
+
|
|
226
|
+
`adhd-backlog` mounts one set of operation descriptors onto every host — there
|
|
227
|
+
is no per-transport reimplementation, so a change to a verb's behavior is
|
|
228
|
+
simultaneously true everywhere:
|
|
229
|
+
|
|
230
|
+
- **CLI** — `adhd-backlog <verb> --input '<json>'`
|
|
231
|
+
- **MCP** — tools named `backlog_<verb>` (e.g. `backlog_create`, `backlog_query`)
|
|
232
|
+
- **HTTP** — a REST API generated from the same descriptors, with an OpenAPI document
|
|
233
|
+
- **In-process** — the package's exported client library, for embedding the store in a Node process
|
|
234
|
+
|
|
235
|
+
## Envelope
|
|
236
|
+
|
|
237
|
+
Every verb call returns one of two shapes, on every transport:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
{ ok: true, data: T, warnings?: string[], meta?: { total, returned, limit?, offset?, truncated? } }
|
|
241
|
+
{ ok: false, error: { code, message, details? }, warnings?: string[] }
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`meta` is present on list-shaped reads (`query`) and its `total` is the true
|
|
245
|
+
match count _before_ `limit`/`offset` are applied; a truncated page sets
|
|
246
|
+
`data.hasMore: true` and `data.nextCursor`, so a short result never silently
|
|
247
|
+
looks complete.
|
|
248
|
+
|
|
249
|
+
### Error codes
|
|
250
|
+
|
|
251
|
+
| Code | Meaning | CLI exit code |
|
|
252
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
|
253
|
+
| `not_found` | A referenced catalog entry (project/component/kind/status/priority) doesn't exist | 4 |
|
|
254
|
+
| `item_not_found` | The addressed issue doesn't exist | 1 |
|
|
255
|
+
| `invalid_argument` | Malformed flag or parameter shape | 2 |
|
|
256
|
+
| `validation` | Schema rejection — unknown filter key, unknown projection field, over-limit | 2 |
|
|
257
|
+
| `store_busy` | Store contention (a lease or a write conflict); `details.retryable` and `details.retryAfterMs` indicate whether/how to retry | 1 |
|
|
258
|
+
| `rag_not_configured` | A semantic/similarity read was requested but no embedding backend is configured, or the vector space is empty | 1 |
|
|
259
|
+
| `conflict` | Someone else holds the claim, a single-valued relation is already taken, or a supersede raced | 1 |
|
|
260
|
+
| `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 |
|
|
261
|
+
| `internal` | Unclassified server-side failure | 1 |
|
|
262
|
+
|
|
263
|
+
Success always exits 0. `item_not_found` and `internal` are deliberately
|
|
264
|
+
distinct codes even though they share exit code 1 — a caller distinguishes
|
|
265
|
+
"this uid doesn't exist" from "something broke" by `error.code`, not by exit
|
|
266
|
+
code alone.
|
|
267
|
+
|
|
268
|
+
### Citations & git context
|
|
269
|
+
|
|
270
|
+
A citation is `{ file, lines?, context?, symbol? }`. Pass `citations` on
|
|
271
|
+
`create`, or on the `transition` that moves an issue into a terminal status
|
|
272
|
+
when the project's policy requires it.
|
|
273
|
+
|
|
274
|
+
The item-level **`gitContext`** is separate from a citation's own `context`:
|
|
275
|
+
it records the repo disclosure contract's `<active git context>` — the first
|
|
276
|
+
element of a `Citations:` block (`Citations: [<active git context>, …]`).
|
|
277
|
+
Pass it on `create`, or on a `transition` to update it. It is stored on the
|
|
278
|
+
issue itself, and a `format:'markdown'` query renders it once at the head of
|
|
279
|
+
the item's `Citations:` block. Omit it and nothing is stored.
|
|
280
|
+
|
|
281
|
+
### Which build these docs describe
|
|
282
|
+
|
|
283
|
+
These docs describe `entrypoint/backlog/dist/index.js` built from revision
|
|
284
|
+
`9df2a5c7`, whose `create`/`transition` inputs include `gitContext`. A
|
|
285
|
+
**globally installed** `adhd-backlog` may be an older build (it is whatever was
|
|
286
|
+
last published/installed); on such a build `gitContext` is not in the schema
|
|
287
|
+
and is rejected with `invalid_argument`, and the stats/rollup ops
|
|
288
|
+
(`priority-matrix` / `part-of-rollup` / `open-curve`) are absent. Run
|
|
289
|
+
`adhd-backlog --help` and compare the `backlog create` / `backlog
|
|
290
|
+
priority-matrix` lines against this page if a documented field or verb is
|
|
291
|
+
refused.
|
|
292
|
+
|
|
293
|
+
## Library API
|
|
294
|
+
|
|
295
|
+
Beyond the CLI/MCP/HTTP surface, the package exports its query layer
|
|
296
|
+
(`src/query/index.ts`), including three rollup/stats read views that are **not**
|
|
297
|
+
members of `query.view`:
|
|
298
|
+
|
|
299
|
+
- `priorityMatrix(handle, { filter? })` — a status-aware per-priority count
|
|
300
|
+
breakdown (defaults to open work; `filter.status` may be `'open'`, `'closed'`,
|
|
301
|
+
`'all'`, or a status name).
|
|
302
|
+
- `partOfRollup(handle, { uid })` — transitive `part_of` descendants of an
|
|
303
|
+
issue, counted once each regardless of chain depth.
|
|
304
|
+
- `openCurve(handle, { filter?, at })` — per-sampled-instant counts of issues
|
|
305
|
+
that existed and how many were open, reconstructed from the audit trail.
|
|
306
|
+
|
|
307
|
+
Reach them by importing `@adhd/backlog` in-process. The same three are also
|
|
308
|
+
mounted as operations — `priority-matrix` / `part-of-rollup` / `open-curve` on
|
|
309
|
+
the CLI (`backlog_priority_matrix` / `backlog_part_of_rollup` /
|
|
310
|
+
`backlog_open_curve` on MCP); see the command surface above.
|
|
311
|
+
|
|
312
|
+
## Configuration
|
|
313
|
+
|
|
314
|
+
Configuration cascades over `@adhd/environment`, prefixed `ADHD_BACKLOG_`
|
|
315
|
+
(later layers override earlier: built-in default → config file → environment
|
|
316
|
+
variable). The store defaults to a single shared **global** scope — one backlog
|
|
317
|
+
spanning every project on the machine, not one per repository — so an agent
|
|
318
|
+
working across repos sees the same graph everywhere unless it explicitly opts
|
|
319
|
+
into a narrower scope.
|
|
320
|
+
|
|
321
|
+
| Setting | Env var | Default | Notes |
|
|
322
|
+
| ------------------ | ----------------------------------------------------- | -------------------------- | ------------------------------------------------------------ |
|
|
323
|
+
| Database path | `ADHD_BACKLOG_DATABASE_PATH` | resolved under the scope root | Where the graph store's data file lives |
|
|
324
|
+
| Write busy timeout | `ADHD_BACKLOG_DATABASE_BUSY_TIMEOUT_MS` | `5000` | How long a write waits on a contended lock before giving up |
|
|
325
|
+
| Log level | `ADHD_BACKLOG_LOG_LEVEL` | `info` | `trace`\|`debug`\|`info`\|`warn`\|`error`\|`fatal`\|`silent` |
|
|
326
|
+
| Scope | `ADHD_BACKLOG_SCOPE` (falls back to `ADHD_ENV_SCOPE`) | `global` | Which store root to resolve against |
|
|
327
|
+
| 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 |
|
|
328
|
+
| Semantic search | `ADHD_BACKLOG_EMBEDDING_ENABLED` | `false` | See below |
|
|
329
|
+
| Embedding provider | `ADHD_BACKLOG_EMBEDDING_PROVIDER` | `fastembed` | Only consulted when embedding is enabled |
|
|
330
|
+
| Embedding model | `ADHD_BACKLOG_EMBEDDING_MODEL` | `bge-base-en-v1.5` (768-dim) | Only consulted when embedding is enabled |
|
|
331
|
+
|
|
332
|
+
**Embedding (semantic search) is entirely optional.** With it left at its
|
|
333
|
+
default (`false`), `@adhd/backlog` works fully — every verb, keyword filtering
|
|
334
|
+
(`filter.grep`), and exact/registry lookup all function with no embedding
|
|
335
|
+
backend at all. Turning `filter.semantic`, `filter.anchor`, `view:"similar"`,
|
|
336
|
+
`sort:"relevance"`, or `fields:["_vector"]` on without an embedding backend
|
|
337
|
+
configured doesn't crash anything — it returns a `rag_not_configured` error
|
|
338
|
+
that says exactly that, so a caller can tell "not available" apart from "no
|
|
339
|
+
matches." Enabling embedding requires two optional dependencies to be
|
|
340
|
+
installed alongside the package and a store backend that supports native
|
|
341
|
+
vector storage; if either is missing, backlog logs the reason and leaves
|
|
342
|
+
semantic search unconfigured rather than failing to start.
|
|
343
|
+
|
|
344
|
+
## Further docs
|
|
345
|
+
|
|
346
|
+
- [`skill/SKILL.md`](skill/SKILL.md) — the full agent-facing command surface, worked examples, and filing rules.
|
|
347
|
+
- [`SPEC.md`](SPEC.md) — the functional specification: operation surface, status vocabulary, acceptance criteria.
|
|
348
|
+
- [`DATA_MODEL.md`](DATA_MODEL.md) — the node/edge model this package reads and writes.
|
|
349
|
+
- [`CHANGELOG.md`](CHANGELOG.md) — version history.
|
|
350
|
+
|
|
351
|
+
## Contributing
|
|
352
|
+
|
|
353
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md). This package is MIT-licensed under
|
|
354
|
+
[`LICENSE`](LICENSE); security policy: [`SECURITY.md`](../../SECURITY.md).
|
package/api.d.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { IUpsertProjectInput, IUpsertProjectOutcome, IUpsertComponentInput, IUpsertComponentOutcome, IUpsertLocationInput, IUpsertLocationOutcome, IRmLocationInput, IRmLocationOutcome } from './write/catalog.js';
|
|
2
|
+
import { IDeleteIssueInput, IDeleteIssueOutcome } from './write/delete.js';
|
|
3
|
+
import { IMoveIssueInput, IMoveIssueOutcome } from './write/move.js';
|
|
4
|
+
import { IRelateInput, IRelateOutcome } from './write/relate.js';
|
|
5
|
+
import { IClaimInput, IClaimOutcome } from './write/claim.js';
|
|
6
|
+
import { ITransitionInput, ITransitionOutcome } from './write/transition.js';
|
|
7
|
+
import { IUpdateIssueInput, IUpdateIssueOutcome } from './write/update.js';
|
|
8
|
+
import { ICreateIssueInput, ICreateIssueResult } from './write/create-issue.js';
|
|
9
|
+
import { IIssueGetInput, IIssueGetResult, IIssueQueryInput, IIssueQueryResult, ILookupResult } from './query/types.js';
|
|
10
|
+
import { IPriorityMatrixInput, IPriorityMatrixResult, IPartOfRollupInput, IPartOfRollupResult, IOpenCurveInput, IOpenCurveResult } from './query/views/stats.js';
|
|
11
|
+
import { IOutcomeEnvelope } from './envelope.js';
|
|
12
|
+
import { GraphBacklogStore } from './store/graph-backlog-store.js';
|
|
13
|
+
import { BacklogConfig } from './env.js';
|
|
14
|
+
import { Environment } from '@adhd/environment';
|
|
15
|
+
|
|
16
|
+
/** The one type apigen special-cases via the `ctx-name-only` invariant. */
|
|
17
|
+
export interface BacklogCtx {
|
|
18
|
+
store: GraphBacklogStore;
|
|
19
|
+
env: Environment<BacklogConfig>;
|
|
20
|
+
/**
|
|
21
|
+
* Test-isolation escape hatch ONLY — mirrors `BuildBacklogEnvOptions.adhdRoot`
|
|
22
|
+
* (the same value passed to `buildBacklogEnv({ adhdRoot })` when constructing
|
|
23
|
+
* `env`). NEVER set this in production code (`server.ts`/`cli.ts` never do).
|
|
24
|
+
*/
|
|
25
|
+
adhdRoot?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Fetch one issue by `uid`, projected to the requested `fields`.
|
|
29
|
+
*
|
|
30
|
+
* Defaults to the same five-field card `query` returns
|
|
31
|
+
* (`uid`, `kind`, `title`, `status`, `priority`).
|
|
32
|
+
*/
|
|
33
|
+
export declare function get(ctx: BacklogCtx, input: IIssueGetInput): Promise<IOutcomeEnvelope<IIssueGetResult>>;
|
|
34
|
+
/**
|
|
35
|
+
* Search, filter, group and paginate issues.
|
|
36
|
+
*
|
|
37
|
+
* Supports field projection, keyset and offset pagination, the `view`/`groupBy`
|
|
38
|
+
* aggregate axes, and `grep`/`semantic` text filters.
|
|
39
|
+
*/
|
|
40
|
+
export declare function query(ctx: BacklogCtx, input: IIssueQueryInput): Promise<IOutcomeEnvelope<IIssueQueryResult>>;
|
|
41
|
+
/**
|
|
42
|
+
* SPEC.md §5's status-aware priority matrix (BUG-023) — a per-priority
|
|
43
|
+
* breakdown of issue counts, scoped by `project`/`component`/`kind`/`status`.
|
|
44
|
+
*
|
|
45
|
+
* An omitted `input.filter.status` scopes to OPEN work (a deliberate
|
|
46
|
+
* divergence from `query`'s `list` default, where an omitted status means "no
|
|
47
|
+
* restriction"); the applied scope is echoed on `data.statusScope`, so a
|
|
48
|
+
* default-scoped result can never be mistaken for an all-status one. `{}` is a
|
|
49
|
+
* valid input.
|
|
50
|
+
*
|
|
51
|
+
* A read op, NOT a `query.view` member: it returns a matrix
|
|
52
|
+
* (`{rows, unassigned, statusScope}`) rather than a `{view, items}` list
|
|
53
|
+
* permutation, and mounting it as a view would force `IIssueQueryInput` to
|
|
54
|
+
* carry axes (`at`, an issue root) the other views must reject. Uses only
|
|
55
|
+
* `handle.graph` — hence `needsSemantic:false`/`probeSpace:false`, so this op
|
|
56
|
+
* never pays the cold semantic-backend bootstrap (`queryHandle`'s own doc
|
|
57
|
+
* comment).
|
|
58
|
+
*/
|
|
59
|
+
export declare function priorityMatrix(ctx: BacklogCtx, input: IPriorityMatrixInput): Promise<IOutcomeEnvelope<IPriorityMatrixResult>>;
|
|
60
|
+
/**
|
|
61
|
+
* SPEC.md §5's `part_of` hierarchy rollup (FEAT-005) — every TRANSITIVE
|
|
62
|
+
* descendant of the root issue `input.uid` via `part_of` (issue → issue,
|
|
63
|
+
* `n:1`), counted exactly once each regardless of chain depth, split into
|
|
64
|
+
* `childrenOpen`/`childrenClosed` (plus the open descendants' uids).
|
|
65
|
+
*
|
|
66
|
+
* A read op, NOT a `query.view` member: it is rooted at an issue (`uid`), a
|
|
67
|
+
* per-op input no list view carries. Uses only `handle.graph` — see
|
|
68
|
+
* {@link priorityMatrix}'s note on the `queryHandle` gates.
|
|
69
|
+
*/
|
|
70
|
+
export declare function partOfRollup(ctx: BacklogCtx, input: IPartOfRollupInput): Promise<IOutcomeEnvelope<IPartOfRollupResult>>;
|
|
71
|
+
/**
|
|
72
|
+
* SPEC.md §5's `validAt` cumulative-open curve — for each sampled ISO-8601
|
|
73
|
+
* instant in `input.at`, how many in-scope issues EXISTED then and, of those,
|
|
74
|
+
* how many were reconstructed as OPEN then (never the issue's current status;
|
|
75
|
+
* see `openCurve`'s own doc comment for the reconstruction rule).
|
|
76
|
+
*
|
|
77
|
+
* A read op, NOT a `query.view` member: it returns a time series
|
|
78
|
+
* (`{points}`) keyed by a caller-given instant list, an axis no list view has.
|
|
79
|
+
* Uses only `handle.graph` — see {@link priorityMatrix}'s note on the
|
|
80
|
+
* `queryHandle` gates.
|
|
81
|
+
*/
|
|
82
|
+
export declare function openCurve(ctx: BacklogCtx, input: IOpenCurveInput): Promise<IOutcomeEnvelope<IOpenCurveResult>>;
|
|
83
|
+
/**
|
|
84
|
+
* Resolve a free-text reference to a project, component or location in the
|
|
85
|
+
* registry.
|
|
86
|
+
*/
|
|
87
|
+
export declare function lookup(ctx: BacklogCtx, input: {
|
|
88
|
+
q: string;
|
|
89
|
+
}): Promise<IOutcomeEnvelope<ILookupResult>>;
|
|
90
|
+
/** File a new issue, minting its `uid` and linking it to a project component. */
|
|
91
|
+
export declare function create(ctx: BacklogCtx, input: ICreateIssueInput): Promise<IOutcomeEnvelope<ICreateIssueResult>>;
|
|
92
|
+
/**
|
|
93
|
+
* Edit an existing issue.
|
|
94
|
+
*
|
|
95
|
+
* A `body` change supersedes the issue, minting a fresh `uid`; every other
|
|
96
|
+
* change edits the existing node in place. `status` is not editable here —
|
|
97
|
+
* use `transition`.
|
|
98
|
+
*/
|
|
99
|
+
export declare function update(ctx: BacklogCtx, input: IUpdateIssueInput): Promise<IOutcomeEnvelope<IUpdateIssueOutcome>>;
|
|
100
|
+
/** Move an issue to a new status, recording the transition in its audit trail. */
|
|
101
|
+
export declare function transition(ctx: BacklogCtx, input: ITransitionInput): Promise<IOutcomeEnvelope<ITransitionOutcome>>;
|
|
102
|
+
/** Take, renew or release an exclusive working lease on an issue. */
|
|
103
|
+
export declare function claim(ctx: BacklogCtx, input: IClaimInput): Promise<IOutcomeEnvelope<IClaimOutcome>>;
|
|
104
|
+
/** Create or remove a typed relationship between two issues. */
|
|
105
|
+
export declare function relate(ctx: BacklogCtx, input: IRelateInput): Promise<IOutcomeEnvelope<IRelateOutcome>>;
|
|
106
|
+
/** Re-file an issue under a different project component. */
|
|
107
|
+
export declare function move(ctx: BacklogCtx, input: IMoveIssueInput): Promise<IOutcomeEnvelope<IMoveIssueOutcome>>;
|
|
108
|
+
/**
|
|
109
|
+
* Soft-delete an issue.
|
|
110
|
+
*
|
|
111
|
+
* The node is closed off bi-temporally, never physically removed — its audit
|
|
112
|
+
* trail and every edge pointing at it remain readable.
|
|
113
|
+
*/
|
|
114
|
+
declare function remove(ctx: BacklogCtx, input: IDeleteIssueInput): Promise<IOutcomeEnvelope<IDeleteIssueOutcome>>;
|
|
115
|
+
/**
|
|
116
|
+
* Create or update a project by `name`.
|
|
117
|
+
*
|
|
118
|
+
* On first creation, also mints the project's reserved default component
|
|
119
|
+
* `(root)`. A repeat call against an existing project is idempotent.
|
|
120
|
+
*/
|
|
121
|
+
export declare function upsertProject(ctx: BacklogCtx, input: IUpsertProjectInput): Promise<IOutcomeEnvelope<IUpsertProjectOutcome>>;
|
|
122
|
+
/** Create or update a component by `(project, name)`. */
|
|
123
|
+
export declare function upsertComponent(ctx: BacklogCtx, input: IUpsertComponentInput): Promise<IOutcomeEnvelope<IUpsertComponentOutcome>>;
|
|
124
|
+
/**
|
|
125
|
+
* Create or find a location by `(component, locType, value)`.
|
|
126
|
+
*
|
|
127
|
+
* A component referenced by bare name (rather than uid) requires `project`
|
|
128
|
+
* to disambiguate it.
|
|
129
|
+
*/
|
|
130
|
+
export declare function upsertLocation(ctx: BacklogCtx, input: IUpsertLocationInput): Promise<IOutcomeEnvelope<IUpsertLocationOutcome>>;
|
|
131
|
+
/** Soft-remove a location by `uid`. */
|
|
132
|
+
export declare function rmLocation(ctx: BacklogCtx, input: IRmLocationInput): Promise<IOutcomeEnvelope<IRmLocationOutcome>>;
|
|
133
|
+
/**
|
|
134
|
+
* SPEC §6.7 names this verb `delete` on every mount (`backlog_delete`,
|
|
135
|
+
* `backlog delete`, `DELETE /issue`). `export async function delete` is a
|
|
136
|
+
* syntax error — `delete` is a reserved word — but an export CLAUSE may alias
|
|
137
|
+
* to any IdentifierName, reserved words included, and apigen resolves the
|
|
138
|
+
* mounted name from `sf.getExportedDeclarations()` (ts-morph's own
|
|
139
|
+
* rename/re-export resolver, which extract.ts documents as covering "named
|
|
140
|
+
* exports — local, renamed, AND re-exported"). So the operation mounts as
|
|
141
|
+
* `delete` while the implementation keeps a legal identifier.
|
|
142
|
+
*
|
|
143
|
+
* Do NOT "simplify" this to `export async function remove`: that silently
|
|
144
|
+
* renames the tool to `backlog_remove` on all four transports.
|
|
145
|
+
*/
|
|
146
|
+
export { remove as delete };
|