@adhd/backlog 0.1.9 → 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.
Files changed (60) hide show
  1. package/CHANGELOG.md +80 -47
  2. package/README.md +332 -81
  3. package/api.d.ts +146 -0
  4. package/cli.d.ts +45 -18
  5. package/env.d.ts +23 -3
  6. package/envelope.d.ts +163 -0
  7. package/index.d.ts +11 -10
  8. package/index.js +531 -173
  9. package/index.mjs +30039 -15879
  10. package/install-skill.d.ts +23 -0
  11. package/package.json +50 -15
  12. package/query/card.d.ts +31 -0
  13. package/query/get.d.ts +11 -0
  14. package/query/index.d.ts +67 -0
  15. package/query/markdown.d.ts +11 -0
  16. package/query/query.d.ts +131 -0
  17. package/query/resolve.d.ts +123 -0
  18. package/query/types.d.ts +450 -0
  19. package/query/views/registry.d.ts +43 -0
  20. package/query/views/semantic.d.ts +101 -0
  21. package/query/views/stats.d.ts +109 -0
  22. package/search-shortcut.d.ts +79 -0
  23. package/serve.d.ts +18 -0
  24. package/server.d.ts +139 -4
  25. package/skill/SKILL.md +619 -138
  26. package/store/graph-backlog-store.d.ts +80 -17
  27. package/store/immediate-retry.d.ts +24 -13
  28. package/store/type-policy.d.ts +4 -0
  29. package/store/vocabulary-guard.d.ts +52 -0
  30. package/write/audit.d.ts +36 -0
  31. package/write/bootstrap.d.ts +123 -0
  32. package/write/catalog.d.ts +351 -0
  33. package/write/claim-lease.d.ts +21 -0
  34. package/write/claim.d.ts +80 -0
  35. package/write/create-issue.d.ts +250 -0
  36. package/write/delete.d.ts +39 -0
  37. package/write/embed-drain.d.ts +68 -0
  38. package/write/embedding-observer.d.ts +80 -0
  39. package/write/errors.d.ts +303 -0
  40. package/write/issue-status.d.ts +10 -0
  41. package/write/move.d.ts +70 -0
  42. package/write/relate.d.ts +52 -0
  43. package/write/transition.d.ts +60 -0
  44. package/write/tx.d.ts +344 -0
  45. package/write/update.d.ts +81 -0
  46. package/client.d.ts +0 -174
  47. package/markdown.d.ts +0 -75
  48. package/migration-admin.d.ts +0 -26
  49. package/model.d.ts +0 -437
  50. package/store/audit-log.d.ts +0 -16
  51. package/store/claim.d.ts +0 -24
  52. package/store/crud.d.ts +0 -62
  53. package/store/ids.d.ts +0 -24
  54. package/store/lifecycle.d.ts +0 -36
  55. package/store/mapping.d.ts +0 -101
  56. package/store/mutate-metadata.d.ts +0 -8
  57. package/store/query.d.ts +0 -68
  58. package/store/repo-migration.d.ts +0 -51
  59. package/store/serve-lock.d.ts +0 -42
  60. package/store/structure.d.ts +0 -66
@@ -0,0 +1,450 @@
1
+ /**
2
+ * types.ts — the read/query-layer's own shared types (SPEC.md §5, §5a, §6.1, §6.5).
3
+ *
4
+ * These are the TYPESCRIPT shapes for the `get`/`query` verbs (§6.3.1, §6.5)
5
+ * and the §3a registry read views. They intentionally mirror SPEC.md's own
6
+ * `IIssue*`/`IProject*` interfaces field-for-field — this file is the single
7
+ * place those interfaces are declared for the read layer; `get.ts`/`query.ts`/
8
+ * `registry.ts` all import from here rather than re-declaring their own
9
+ * copies, and the six sibling write verbs (`update`/`transition`/`claim`/
10
+ * `relate`/`move`/`delete`) should import {@link IIssueCard} /
11
+ * {@link IIssueField} from here too, rather than re-declaring a third copy
12
+ * alongside `create-issue.ts`'s own `IIssueCard` (see this module's own
13
+ * top-level doc comment in `index.ts` for the reconciliation note).
14
+ */
15
+ /** Identity is the global `uid` (SPEC.md §6.1) — a single scalar, never a composite key. */
16
+ export type IssueUid = string;
17
+ /**
18
+ * The closed field vocabulary a `get`/`query` caller may request (SPEC.md
19
+ * §6.5). `assertKnownFields` (this module's {@link assertKnownIssueFields})
20
+ * rejects any name outside this union with a `BacklogValidationError` naming
21
+ * it — never a silent drop.
22
+ *
23
+ * `plain` fields are cheap: each is either a column on the `issue` node
24
+ * itself or a scalar living in `issue.meta.metadata` (`assignee`, `closedAt`,
25
+ * `gitContext` — SPEC.md §6.5's own reclassification of `closedAt` as
26
+ * `plain`, since it is read in the SAME single-row fetch as `assignee`, not a
27
+ * second query; `gitContext` is the item-level disclosure-contract git
28
+ * context, a sibling of `assignee` in the same metadata blob).
29
+ *
30
+ * `pseudo` fields are opt-in only: each costs a genuine extra read (an edge
31
+ * traversal to another node kind, or only exists on a `searchRanked`
32
+ * response) and is therefore NEVER included in the default card.
33
+ */
34
+ export type IIssuePlainField = 'uid' | 'title' | 'kind' | 'status' | 'priority' | 'project' | 'component' | 'createdAt' | 'updatedAt' | 'assignee' | 'author' | 'closedAt' | 'gitContext';
35
+ export type IIssuePseudoField = 'body' | 'citations' | 'notes' | 'auditTrail' | 'blockers' | 'related' | '_score' | '_vector';
36
+ export type IIssueField = IIssuePlainField | IIssuePseudoField;
37
+ export declare const ISSUE_PLAIN_FIELDS: readonly IIssuePlainField[];
38
+ export declare const ISSUE_PSEUDO_FIELDS: readonly IIssuePseudoField[];
39
+ export declare function isKnownIssueField(name: string): name is IIssueField;
40
+ /** SPEC.md §6.5: "Default (`fields` omitted): the exact five-field terse card established by `DEFAULT_CARD_FIELDS`." */
41
+ export declare const DEFAULT_ISSUE_CARD_FIELDS: readonly IIssuePlainField[];
42
+ /** A citation, projected for read (mirrors the write layer's `ICitationInput` shape plus the server-computed `sha`). */
43
+ export interface IIssueCitation {
44
+ uid: string;
45
+ file: string;
46
+ lines?: string;
47
+ /**
48
+ * Free-text prose for this citation (the write layer's `ICitationInput.context`).
49
+ * NOT the item's disclosure-contract git context — that is ITEM-level
50
+ * provenance and lives on {@link IIssueCard.gitContext}, a sibling of
51
+ * `assignee`, rendered once at the head of the `Citations:` block. This
52
+ * per-citation `context` is never rendered by `markdown.ts` and cannot carry
53
+ * the git context.
54
+ */
55
+ context?: string;
56
+ symbol?: string;
57
+ sha: string;
58
+ at: string;
59
+ }
60
+ export interface IIssueNote {
61
+ uid: string;
62
+ author: string;
63
+ text: string;
64
+ at: string;
65
+ }
66
+ export interface IIssueAuditEntry {
67
+ uid: string;
68
+ actor: string;
69
+ action: string;
70
+ from?: string;
71
+ to?: string;
72
+ note?: string;
73
+ sha: string;
74
+ at: string;
75
+ }
76
+ /** A minimal cross-reference to another issue — used by the `blockers`/`related` pseudo-fields. */
77
+ export interface IIssueRef {
78
+ uid: string;
79
+ title: string;
80
+ status: string;
81
+ }
82
+ /**
83
+ * The projected issue card (SPEC.md §6.5's `IIssueCard`). Every field is
84
+ * OPTIONAL here because the shape is fields-projected: a caller that asked
85
+ * for `['uid','title']` gets a card with only those two keys populated.
86
+ * `uid` is always present regardless of the requested `fields` — an issue
87
+ * card with no addressable identity is never a useful response.
88
+ */
89
+ export interface IIssueCard {
90
+ uid: string;
91
+ title?: string;
92
+ kind?: string;
93
+ status?: string;
94
+ priority?: string;
95
+ project?: string;
96
+ component?: string;
97
+ createdAt?: string;
98
+ updatedAt?: string;
99
+ assignee?: string;
100
+ author?: string;
101
+ closedAt?: string;
102
+ /**
103
+ * The item-level disclosure-contract git context (repo `AGENTS.md`'s
104
+ * "Cite what you read": the FIRST element of a `Citations:` block is
105
+ * `<active git context>`). A plain field — a sibling of `assignee` in
106
+ * `issue.meta.metadata`, read in the same single-row fetch. Rendered once at
107
+ * the head of the `Citations:` block by `markdown.ts`; never per-citation.
108
+ * Absent on every issue filed before the field existed, and on any issue
109
+ * whose `create`/`transition` supplied no `gitContext`.
110
+ */
111
+ gitContext?: string;
112
+ body?: string;
113
+ citations?: IIssueCitation[];
114
+ notes?: IIssueNote[];
115
+ auditTrail?: IIssueAuditEntry[];
116
+ blockers?: IIssueRef[];
117
+ related?: IIssueRef[];
118
+ _score?: number;
119
+ _vector?: number[];
120
+ }
121
+ /**
122
+ * `get`'s uid-addressed shape (SPEC.md §6.3.1/§6.5/AC-13) — the implementation
123
+ * layer's {@link getIssue} (`get.ts`) takes exactly this, never the mounted
124
+ * union below. Named `...ByUidInput` (rather than reusing the bare
125
+ * `IIssueGetInput` name) because the MOUNTED `get` verb (`api.ts`) also
126
+ * accepts the registry-detail shape (§3a/AC-11) — see {@link IIssueGetInput}.
127
+ */
128
+ export interface IIssueGetByUidInput {
129
+ uid: IssueUid;
130
+ fields?: readonly IIssueField[];
131
+ }
132
+ /** SPEC.md §6.5's `IIssueFilter`. */
133
+ export interface IIssueFilter {
134
+ /** uid or name (SPEC.md §6.1). */
135
+ project?: string;
136
+ /** uid or name, scoped within `project`. */
137
+ component?: string;
138
+ kind?: string | string[];
139
+ status?: string | string[] | 'open' | 'closed' | 'all';
140
+ priority?: string | string[];
141
+ assignee?: string;
142
+ claimedBy?: string;
143
+ /** Resolves via `authored_by` edge traversal — uid or name. */
144
+ author?: string;
145
+ /** FTS keyword, title+body — keyword-only, never hybrid. */
146
+ grep?: string;
147
+ /** Routes to `searchRanked` (§5a) — composes with `grep`, neither swallows the other. */
148
+ semantic?: string;
149
+ /** Item-anchored similarity/traversal seed (§6.1) — uid of the reference item. */
150
+ anchor?: string;
151
+ /**
152
+ * uid or name of the parent `issue` this one is filed under via the `part_of`
153
+ * edge (SPEC.md §1052/§1471: a plan is itself an `issue` row, not a
154
+ * dedicated node kind — attaching an item to it is
155
+ * `relate(childUid, planUid, 'part_of', 'add')`). Resolves candidate issues
156
+ * by the incoming `part_of` edge into the resolved plan node, exactly like
157
+ * the `project`/`component` edge-scoped filters.
158
+ */
159
+ plan?: string;
160
+ /** `component.meta.path` (repo-relative package path, SPEC.md §3) — exact match against every live `component` row, unioned across matches. Distinct from `component`, which takes a uid/name rather than a filesystem path. */
161
+ projectPath?: string;
162
+ closedAt?: {
163
+ since?: string;
164
+ until?: string;
165
+ };
166
+ createdAt?: {
167
+ since?: string;
168
+ until?: string;
169
+ };
170
+ updatedAt?: {
171
+ since?: string;
172
+ until?: string;
173
+ };
174
+ }
175
+ export type IIssueSort = 'priority' | 'updated' | 'created' | 'relevance' | 'textMatch';
176
+ export type IIssueSortDirection = 'asc' | 'desc';
177
+ /**
178
+ * `projects`/`components`/`locations` (SPEC.md §3a/§8 AC-9) are the registry
179
+ * LIST views — every live `project`/`component`/`location` row, optionally
180
+ * narrowed by `filter.project` (and, for `locations`, `filter.component`) —
181
+ * routed to `views/registry.ts`'s `listProjects`/`listComponents`/
182
+ * `listLocations`, distinct from the issue-search views above (§6.1:
183
+ * "conflating the two would be wrong").
184
+ */
185
+ export type IIssueView = 'list' | 'ready' | 'graph' | 'order' | 'stale' | 'similar' | 'overlap' | 'projects' | 'components' | 'locations';
186
+ export type IIssueQueryFormat = 'json' | 'markdown';
187
+ /** SPEC.md §5, §6.1's `axis` — the grouping dimension for `overlapUids` (§6.2). */
188
+ export type IOverlapAxis = 'file' | 'project' | 'component' | 'author';
189
+ export interface IIssueQueryInput {
190
+ /**
191
+ * The natural-language query — routed by `queryIssues` to `filter.semantic`
192
+ * when the vector space can return ranked results, or to `filter.grep`
193
+ * otherwise. Mutually exclusive with setting `filter.semantic` or
194
+ * `filter.grep` yourself; pick one or the other, never both.
195
+ */
196
+ text?: string;
197
+ filter?: IIssueFilter;
198
+ fields?: readonly IIssueField[];
199
+ sort?: IIssueSort;
200
+ direction?: IIssueSortDirection;
201
+ /** default 50, max 1000 (`MAX_QUERY_LIMIT`). */
202
+ limit?: number;
203
+ /** offset-based paging; mutually exclusive with `after`. */
204
+ offset?: number;
205
+ /** opaque keyset cursor from a prior page's `nextCursor`. */
206
+ after?: string;
207
+ view?: IIssueView;
208
+ /**
209
+ * default `'json'`. `'markdown'` is only supported for the four item-list
210
+ * views (`list`/`ready`/`stale`/`similar`) — see {@link IIssueMarkdownResult}
211
+ * — and rejects with `InvalidArgumentError('format', ...)` for any other
212
+ * view (`graph`/`order`/`overlap`/`projects`/`components`/`locations`),
213
+ * which return a shape DATA_MODEL.md §8's markdown projection has no rule
214
+ * for.
215
+ */
216
+ format?: IIssueQueryFormat;
217
+ /** `view:'overlap'` only (§6.2) — the axis to group `overlapUids` by. */
218
+ overlapAxis?: IOverlapAxis;
219
+ /** `view:'overlap'` only (§6.2) — the set of `uid`s to group by `overlapAxis`. */
220
+ overlapUids?: readonly string[];
221
+ /** `view:'stale'` only — minutes since `claimedAt`; falls back to `project_policy.claim_stale_after_min` (default 30) when omitted. */
222
+ staleAfterMin?: number;
223
+ }
224
+ export declare const MAX_QUERY_LIMIT = 1000;
225
+ export declare const DEFAULT_QUERY_LIMIT = 50;
226
+ export interface IIssuePage {
227
+ items: IIssueCard[];
228
+ /** Pass back as `after` for the next page; absent ⇒ no more pages. */
229
+ nextCursor?: string;
230
+ hasMore: boolean;
231
+ }
232
+ /** SPEC.md §5's `DependencyGraph`, using this system's edge vocabulary (§6.2: `blocks`, not `DEPENDS_ON`). */
233
+ export interface IDependencyGraph {
234
+ nodes: Array<{
235
+ uid: string;
236
+ title: string;
237
+ status: string;
238
+ }>;
239
+ edges: Array<{
240
+ from: string;
241
+ to: string;
242
+ rel: 'blocks' | 'relates_to' | 'part_of';
243
+ }>;
244
+ }
245
+ export type ITopoOrderResult = {
246
+ ok: true;
247
+ order: string[];
248
+ } | {
249
+ ok: false;
250
+ cycle: string[];
251
+ };
252
+ /** `view:'overlap'`'s output shape (§6.2) — one entry per distinct axis value shared by ≥1 of the input `uids`. */
253
+ export interface IOverlapGroup {
254
+ axisValue: string;
255
+ uids: string[];
256
+ }
257
+ /**
258
+ * `view:'list'`'s result — {@link IIssuePage} plus the discriminator.
259
+ *
260
+ * Declared as an interface rather than written inline as
261
+ * `({ view: 'list' } & IIssuePage)`. That distinction is load-bearing, not
262
+ * stylistic: the schema extractor that derives every mount's response shape
263
+ * cannot express a TypeScript intersection, and emitted a bare `{}` for that
264
+ * branch. The runtime encodes each response against the derived schema, so
265
+ * with `{}` in the union the list branch was projected onto a sibling member
266
+ * — and `hasMore` and `nextCursor` were silently dropped from every CLI, MCP
267
+ * and HTTP response, which made paging unreachable for every consumer while
268
+ * the in-process return value looked correct. An `extends` clause extracts
269
+ * into a complete object schema, so the wire shape matches the type.
270
+ */
271
+ export interface IIssueListResult extends IIssuePage {
272
+ view: 'list';
273
+ }
274
+ /**
275
+ * `format:'markdown'`'s result shape (SPEC.md §6.5/§6.6, DATA_MODEL.md §8) —
276
+ * returned instead of the matching JSON-shaped member above for any of the
277
+ * four item-list views (`list`/`ready`/`stale`/`similar`; the only views whose
278
+ * result is an `IIssueCard[]` DATA_MODEL.md §8's markdown projection can
279
+ * render). `markdown` is the SAME page a `format:'json'` call would have
280
+ * returned, re-serialized (`query/markdown.ts`'s `renderIssueCardsMarkdown`)
281
+ * — never a second query path.
282
+ */
283
+ export interface IIssueMarkdownResult {
284
+ view: 'list' | 'ready' | 'stale' | 'similar';
285
+ format: 'markdown';
286
+ markdown: string;
287
+ }
288
+ /** The one discriminated result shape `query` (§6.3, §5) returns — the `view` field selects which of the following members is populated. */
289
+ export type IIssueQueryResult = IIssueListResult | {
290
+ view: 'ready';
291
+ items: IIssueCard[];
292
+ } | {
293
+ view: 'graph';
294
+ graph: IDependencyGraph;
295
+ } | {
296
+ view: 'order';
297
+ order: ITopoOrderResult;
298
+ } | {
299
+ view: 'stale';
300
+ items: IIssueCard[];
301
+ } | {
302
+ view: 'similar';
303
+ items: IIssueCard[];
304
+ } | {
305
+ view: 'overlap';
306
+ groups: IOverlapGroup[];
307
+ } | {
308
+ view: 'projects';
309
+ items: IProjectSummary[];
310
+ } | {
311
+ view: 'components';
312
+ items: IComponentSummary[];
313
+ } | {
314
+ view: 'locations';
315
+ items: ILocationSummary[];
316
+ } | IIssueMarkdownResult;
317
+ export interface IProjectSummary {
318
+ uid: string;
319
+ name: string;
320
+ path?: string;
321
+ repoUrl?: string;
322
+ monorepo?: boolean;
323
+ description?: string;
324
+ }
325
+ export interface IComponentSummary {
326
+ uid: string;
327
+ name: string;
328
+ projectUid: string;
329
+ path?: string;
330
+ description?: string;
331
+ }
332
+ export type ILocationType = 'path' | 'url' | 'tool';
333
+ export interface ILocationSummary {
334
+ uid: string;
335
+ locType: ILocationType;
336
+ value: string;
337
+ componentUid: string;
338
+ }
339
+ export interface IProjectDetail extends IProjectSummary {
340
+ components: Array<{
341
+ name: string;
342
+ path?: string;
343
+ }>;
344
+ locations: Array<{
345
+ locType: ILocationType;
346
+ value: string;
347
+ }>;
348
+ }
349
+ export interface IComponentDetail extends IComponentSummary {
350
+ project: {
351
+ name: string;
352
+ path?: string;
353
+ repoUrl?: string;
354
+ };
355
+ locations: Array<{
356
+ locType: ILocationType;
357
+ value: string;
358
+ }>;
359
+ }
360
+ export interface ILocationDetail extends ILocationSummary {
361
+ component: {
362
+ name: string;
363
+ path?: string;
364
+ };
365
+ project: {
366
+ name: string;
367
+ path?: string;
368
+ repoUrl?: string;
369
+ };
370
+ }
371
+ export interface IRegistryQueryFilter {
372
+ project?: string;
373
+ component?: string;
374
+ }
375
+ export interface ILookupResult {
376
+ project: {
377
+ uid: string;
378
+ name: string;
379
+ path?: string;
380
+ repoUrl?: string;
381
+ };
382
+ component?: {
383
+ uid: string;
384
+ name: string;
385
+ path?: string;
386
+ };
387
+ location?: {
388
+ uid: string;
389
+ locType: ILocationType;
390
+ value: string;
391
+ };
392
+ /** Present when only a partial (project-level, or path-prefix) match was found — never a silent null (§3a). */
393
+ hint?: string;
394
+ }
395
+ /**
396
+ * `get`'s registry-detail shape (SPEC.md §3a/§8 AC-11) — `get --input
397
+ * '{"registry":"project"|"component"|"location","name":...}'`, routed to
398
+ * `views/registry.ts`'s `getRegistryDetail`. `filter` is honoured ONLY for
399
+ * `registry:'component'` (SPEC.md §6.1's project-scoping rule — `name` alone
400
+ * is ambiguous across projects, e.g. every project's reserved `(root)`
401
+ * component shares the same name, §8 AC-23) and is ignored for `project`/
402
+ * `location` (a location has no independent name at all — it is always
403
+ * resolved by uid, §3a).
404
+ */
405
+ export interface IIssueGetRegistryInput {
406
+ registry: 'project' | 'component' | 'location';
407
+ name: string;
408
+ filter?: IRegistryQueryFilter;
409
+ }
410
+ /**
411
+ * The MOUNTED `get` verb's input (`api.ts`) — either the uid-addressed issue
412
+ * card ({@link IIssueGetByUidInput}, SPEC.md §6.3.1/AC-13, UNCHANGED) or the
413
+ * registry-detail lookup ({@link IIssueGetRegistryInput}, §3a/AC-11). The two
414
+ * shapes are structurally disjoint (`uid` vs. `registry`+`name`), which is
415
+ * what lets `api.ts`'s `get` narrow on `'registry' in input` and is also what
416
+ * keeps apigen's undiscriminated-union structural encoder
417
+ * (`pickUnionBranch`/`scoreUnionBranch`, `@adhd/apigen-base-logical`) from
418
+ * ever conflating the two: each branch's OWN required-key set immediately
419
+ * disqualifies the other (a uid-input has no `registry`/`name`; a
420
+ * registry-input has no `uid`).
421
+ */
422
+ export type IIssueGetInput = IIssueGetByUidInput | IIssueGetRegistryInput;
423
+ /**
424
+ * The MOUNTED `get` verb's result — the plain issue card ({@link IIssueCard},
425
+ * exactly the five-field default per AC-13 when `uid` was given) or one of
426
+ * the three registry detail shapes (§3a/AC-11). Same structural-disjointness
427
+ * reasoning as {@link IIssueGetInput} above: `IIssueCard` requires only
428
+ * `uid`, while every registry detail type requires several fields NONE of
429
+ * the others (nor `IIssueCard`) declare (`IProjectDetail`:
430
+ * `components`+`locations`; `IComponentDetail`: `projectUid`+`project`;
431
+ * `ILocationDetail`: `locType`+`value`+`componentUid`+`component`+`project`)
432
+ * — a missing required key disqualifies a branch outright in
433
+ * `scoreUnionBranch`, so the four branches can never tie.
434
+ *
435
+ * NOTE (BUG-APIGEN-CORE-CLIENT-BARE-NAME-COLLISION-001, now fixed at the
436
+ * source): putting `query/types.ts`'s `IIssueCard` at a top-level
437
+ * operation-return position here (for the first time) exposed a real
438
+ * apigen-core-client extraction defect — `write/create-issue.ts` ALSO
439
+ * exported an interface bare-named `IIssueCard` (differently shaped:
440
+ * `title`/`kind`/`status`/`project`/`component`/`createdAt` required there,
441
+ * vs. only `uid` here), and the extractor's declaration resolution collided
442
+ * on the bare name across the whole extracted program, non-deterministically
443
+ * substituting the wrong shape into `query`'s `items` schema (confirmed
444
+ * empirically: `dist/index.js` resolved it to the wrong, stricter shape;
445
+ * `dist/index.mjs`, built from the identical source in the same pass, failed
446
+ * to resolve it at all). Fixed by renaming `write/create-issue.ts`'s
447
+ * interface to `ICreateIssueCard` — see that file's doc comment for the full
448
+ * repro — so no workaround is needed here; this type is plain `IIssueCard`.
449
+ */
450
+ export type IIssueGetResult = IIssueCard | IProjectDetail | IComponentDetail | ILocationDetail;
@@ -0,0 +1,43 @@
1
+ import { IComponentDetail, IComponentSummary, ILocationDetail, ILocationSummary, ILookupResult, IProjectDetail, IProjectSummary, IRegistryQueryFilter } from '../types.js';
2
+ import { GraphBackend } from '@adhd/sox-graph-store';
3
+
4
+ /** `query --input '{"view":"projects"}'` (§3a) — every live `project` row, optionally narrowed by `filter.project` (name/uid substring is NOT supported here; pass the exact ref — this is a listing view, not a search). */
5
+ export declare function listProjects(graph: GraphBackend, filter?: IRegistryQueryFilter): Promise<IProjectSummary[]>;
6
+ /** `query --input '{"view":"components"}'` (§3a) — every live `component` row, optionally narrowed by `filter.project`. */
7
+ export declare function listComponents(graph: GraphBackend, filter?: IRegistryQueryFilter): Promise<IComponentSummary[]>;
8
+ /** `query --input '{"view":"locations"}'` (§3a) — every live `location` row, optionally narrowed by `filter.component` (scoped within `filter.project` when both are given). */
9
+ export declare function listLocations(graph: GraphBackend, filter?: IRegistryQueryFilter): Promise<ILocationSummary[]>;
10
+ /**
11
+ * `get --input '{"registry":"project"|"component"|"location", "name":...}'`
12
+ * (§3a) — expanded detail. `name` accepts a uid or a name (project/component)
13
+ * per SPEC.md §6.1's shape-disambiguation rule; a location has no independent
14
+ * `name`, so it is looked up by uid only. `filter.project` scopes the
15
+ * `component` case (SPEC.md §6.1/§8 AC-23: a bare component NAME is
16
+ * ambiguous across projects — every project's reserved `(root)` component
17
+ * shares the same name — so `filter.project` disambiguates exactly like
18
+ * `listComponents`' own `filter.project`); omitted, `component` resolves
19
+ * against the first live component matching `name` in ANY project, same as
20
+ * before this parameter existed.
21
+ */
22
+ export declare function getRegistryDetail(graph: GraphBackend, input: {
23
+ registry: 'project';
24
+ name: string;
25
+ }): Promise<IProjectDetail>;
26
+ export declare function getRegistryDetail(graph: GraphBackend, input: {
27
+ registry: 'component';
28
+ name: string;
29
+ filter?: IRegistryQueryFilter;
30
+ }): Promise<IComponentDetail>;
31
+ export declare function getRegistryDetail(graph: GraphBackend, input: {
32
+ registry: 'location';
33
+ name: string;
34
+ }): Promise<ILocationDetail>;
35
+ /**
36
+ * `query --input '{"view":"lookup", "lookup": "<tool|file|url>"}'` (§3a) —
37
+ * classify → match → walk `location → component → project`. Never a silent
38
+ * null: an unresolved query throws `CatalogNotFoundError('location', q)`
39
+ * rather than returning an empty/undefined result, since "no match" is a
40
+ * distinct, actionable outcome from "found, but only a hint" (the `hint`
41
+ * field below).
42
+ */
43
+ export declare function lookup(graph: GraphBackend, q: string): Promise<ILookupResult>;
@@ -0,0 +1,101 @@
1
+ import { IQueryStoreHandle } from '../query.js';
2
+ import { IIssueCard, IIssueFilter, IIssueQueryInput } from '../types.js';
3
+ import { SearchResult } from '@adhd/sox-hybrid-search';
4
+ import { GraphBackend } from '@adhd/sox-graph-store';
5
+
6
+ /**
7
+ * FEAT-022's rescore knob (SPEC.md §5a: `rescore:[{kind:'temporal',decay}]`)
8
+ * — SPEC.md names the shape but states no value anywhere, and neither
9
+ * `IIssueFilter` nor `IIssueQueryInput` (the frozen filter/cursor contract)
10
+ * exposes a caller-supplied one. Rather than fabricate a number, this mirrors
11
+ * the ALREADY-established sibling convention `sox-hybrid-search` itself is
12
+ * pinned to for its OTHER FEAT-022 constant: `RRF_K = 60` is documented
13
+ * in that package's own source as "Matches memory-core's RRF_K so a
14
+ * 3-channel cutover produces the same rank magnitudes" — i.e. this package
15
+ * is deliberately kept rank-compatible with the sibling `memory-core` system
16
+ * FEAT-022 originated in. That system's own documented temporal-rescore
17
+ * convention is "recency×importance rerank (0.995/hour decay)"
18
+ * (`docs/sox/CAPABILITY-CATALOG.md:336`, this monorepo). `temporalRescore`'s
19
+ * formula is `exp(-decay * ageHours)`, so a 0.995-per-hour retention factor
20
+ * is `decay = -ln(0.995) ≈ 0.0050125`. Citing the same precedent this
21
+ * package's own `RRF_K` already cites, not inventing a fresh figure — but
22
+ * genuinely a decision this module makes, not a value SPEC.md itself states;
23
+ * flagged here rather than silently asserted as spec-given.
24
+ */
25
+ export declare const DEFAULT_TEMPORAL_DECAY_PER_HOUR = 0.0050125;
26
+ /**
27
+ * Resolves every edge-scoped/metadata/date dimension of `filter` (everything
28
+ * EXCEPT `grep`/`semantic`/`anchor`, which the caller consumes separately) to
29
+ * a concrete candidate issue-id set — the "resolve any node filter to
30
+ * concrete ids via the graph first" this module's top doc comment names.
31
+ *
32
+ * Returns:
33
+ * - `undefined` — no filter dimension this function handles was given at
34
+ * all; the caller may use the cheap `{kind:'issue'}` fast path (`kind` IS
35
+ * one of `buildFilterClause`'s recognized keys, so `searchRanked` applies
36
+ * it correctly on its own) rather than paying for an unbounded
37
+ * `queryNodes` scan just to re-derive "every live issue."
38
+ * - a `Set` (possibly empty) — at least one dimension was given; an empty
39
+ * `Set` means the filter, taken as a whole, matches zero issues. The
40
+ * caller MUST treat this as "return `[]`", NEVER pass it through as
41
+ * `ids: []` (see this file's top doc comment for why that would silently
42
+ * do the OPPOSITE — an unfiltered scan).
43
+ */
44
+ export declare function resolveSimilarFilterIds(graph: GraphBackend, filter: IIssueFilter | undefined): Promise<Set<number> | undefined>;
45
+ export interface IRelevanceRankOptions {
46
+ /** FTS query text — fused via the `'text'` RRF signal. */
47
+ text?: string;
48
+ /** Embedding query vector — fused via the `'vec'` RRF signal. */
49
+ vec: Float32Array;
50
+ /**
51
+ * A pre-resolved candidate id set (see {@link resolveSimilarFilterIds}).
52
+ * `undefined` ⇒ no restriction beyond `kind:'issue'`. A DEFINED-BUT-EMPTY
53
+ * set short-circuits to `[]` WITHOUT calling `searchRanked` at all — this
54
+ * guard exists at THIS layer too (not only in {@link querySimilarView}),
55
+ * so a future direct caller of this exported primitive gets the same
56
+ * protection against the empty-`ids`-means-unfiltered hazard by
57
+ * construction, not by caller discipline.
58
+ */
59
+ candidateIds?: Set<number>;
60
+ limit: number;
61
+ /** Overrides {@link DEFAULT_TEMPORAL_DECAY_PER_HOUR} — for tests and any future caller that wants a different recency curve. */
62
+ decayPerHour?: number;
63
+ }
64
+ /**
65
+ * The one `searchRanked` call shape SPEC.md §5a specifies for `view:'similar'`
66
+ * / `sort:'relevance'` / `sort:'textMatch'` / `_score`: fused text+vec (RRF)
67
+ * plus a temporal-recency rescore. Exported standalone (not only reachable
68
+ * via {@link querySimilarView}) so a future `view:'list'` ranked-sort
69
+ * integration can call the EXACT same primitive `view:'similar'` uses,
70
+ * rather than reimplementing a second ranking path — that reuse is the
71
+ * "relevance ordering" half of this module's brief, distinct from
72
+ * `view:'similar'` itself.
73
+ */
74
+ export declare function rankByFusedRelevance(handle: IQueryStoreHandle, opts: IRelevanceRankOptions): Promise<SearchResult[]>;
75
+ /**
76
+ * `view:'similar'` (SPEC.md §5a) — `filter.anchor` (item-anchored) or
77
+ * `filter.semantic` (free text), fused text+vec ranked via
78
+ * {@link rankByFusedRelevance}, projected to `IIssueCard`s with `_score`
79
+ * populated when requested (`card.ts`'s existing
80
+ * `IAssembleIssueCardOptions.score` seam — SPEC.md §5a's `_score` exposure).
81
+ *
82
+ * The anchor issue is EXCLUDED from its own results (RAG-SPEC.md §3.2's
83
+ * carried-forward "the query item itself is excluded" — SPEC.md §5a does not
84
+ * restate this explicitly, a genuine spec gap this implementation fills per
85
+ * that precedent rather than silently, since "similar to X" trivially
86
+ * self-matching X at rank 1 is a real usability defect, not a feature).
87
+ * Exclusion is done POST-search (fetch one extra candidate, drop the anchor,
88
+ * slice to `limit`) rather than by enumerating the whole issue table to
89
+ * subtract one id up front — far cheaper for the common "no other filter"
90
+ * case, and still exactly correct.
91
+ *
92
+ * Errors: `IssueNotFoundError(anchor)` (SPEC.md §6.1's general `uid`-
93
+ * addressing convention — `anchor` is `uid`-typed per §6.1's own statement
94
+ * that it is "the SAME `uid`-typed field" as every other addressing field;
95
+ * reused directly from `resolve.ts`'s `resolveIssueByUid` rather than
96
+ * re-derived, which also means this gets the SPEC-correct error class for
97
+ * free). `InvalidArgumentError('semantic', ...)` (no search backend configured).
98
+ * `InvalidArgumentError('filter', ...)` (neither `anchor` nor `semantic`
99
+ * given). `BacklogValidationError('limit', ...)` (out-of-range `limit`).
100
+ */
101
+ export declare function querySimilarView(handle: IQueryStoreHandle, input: IIssueQueryInput): Promise<IIssueCard[]>;