@adhd/backlog 1.0.5 → 1.0.6

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 (58) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +194 -39
  3. package/api.d.ts +87 -0
  4. package/api.ir.json +1 -1
  5. package/citation.d.ts +176 -0
  6. package/envelope.d.ts +35 -1
  7. package/index.d.ts +23 -3
  8. package/index.js +106 -58
  9. package/index.mjs +11627 -7068
  10. package/ir-artifact.d.ts +7 -3
  11. package/lifecycle.d.ts +49 -0
  12. package/package.json +5 -5
  13. package/query/canonical.d.ts +16 -0
  14. package/query/card.d.ts +116 -4
  15. package/query/get.d.ts +8 -1
  16. package/query/index.d.ts +1 -0
  17. package/query/query.d.ts +20 -13
  18. package/query/redirect.d.ts +30 -0
  19. package/query/resolve.d.ts +48 -0
  20. package/query/similar-clusters.d.ts +8 -0
  21. package/query/similarity-signals.d.ts +47 -0
  22. package/query/spec-staleness.d.ts +21 -0
  23. package/query/types.d.ts +249 -34
  24. package/query/verdict-core.d.ts +21 -0
  25. package/query/verdict.d.ts +22 -0
  26. package/query/views/catalog.d.ts +47 -0
  27. package/query/views/registry.d.ts +27 -7
  28. package/query/views/report.d.ts +49 -0
  29. package/query/views/semantic.d.ts +28 -5
  30. package/query/views/stats.d.ts +38 -2
  31. package/readiness.d.ts +29 -0
  32. package/retry-policy.d.ts +44 -0
  33. package/serve.d.ts +1 -1
  34. package/server.d.ts +71 -3
  35. package/service-config.d.ts +109 -0
  36. package/service-errors.d.ts +51 -0
  37. package/skill/SKILL.md +688 -81
  38. package/store/catalog-invariant-guard.d.ts +4 -4
  39. package/vocabulary.d.ts +48 -0
  40. package/write/anchor-check.d.ts +139 -0
  41. package/write/attestation.d.ts +64 -0
  42. package/write/catalog-merge.d.ts +15 -8
  43. package/write/catalog.d.ts +63 -0
  44. package/write/citation-path.d.ts +31 -0
  45. package/write/citation.d.ts +157 -0
  46. package/write/create-issue.d.ts +143 -29
  47. package/write/errors.d.ts +159 -0
  48. package/write/gate.d.ts +67 -0
  49. package/write/merge-project.d.ts +54 -0
  50. package/write/obligation.d.ts +133 -0
  51. package/write/relate.d.ts +1 -1
  52. package/write/revision.d.ts +27 -0
  53. package/write/similarity-scan.d.ts +84 -0
  54. package/write/spec-revision.d.ts +131 -0
  55. package/write/spec-revision.reconcile.d.ts +48 -0
  56. package/write/transition.d.ts +13 -2
  57. package/write/tx.d.ts +21 -1
  58. package/write/update.d.ts +23 -1
package/query/types.d.ts CHANGED
@@ -1,17 +1,8 @@
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
- */
1
+ import { CatalogKindName, ICatalogTerm, ICatalogView } from './views/catalog.js';
2
+ import { SpecFreshness } from './spec-staleness.js';
3
+ import { ICitationRecord } from '../citation.js';
4
+ import { IObligationAppliesTo, IObligationOverride, IObligationSeverity, IPredicate } from '../write/obligation.js';
5
+
15
6
  /** Identity is the global `uid` (SPEC.md §6.1) — a single scalar, never a composite key. */
16
7
  export type IssueUid = string;
17
8
  /**
@@ -32,31 +23,25 @@ export type IssueUid = string;
32
23
  * response) and is therefore NEVER included in the default card.
33
24
  */
34
25
  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';
26
+ export type IIssuePseudoField = 'body' | 'citations' | 'notes' | 'auditTrail' | 'blockers' | 'related' | '_score' | '_vector' | 'blocksOut' | 'dependents' | 'partOf' | 'obligations' | 'verdict' | 'spec' | 'similar';
36
27
  export type IIssueField = IIssuePlainField | IIssuePseudoField;
37
28
  export declare const ISSUE_PLAIN_FIELDS: readonly IIssuePlainField[];
38
29
  export declare const ISSUE_PSEUDO_FIELDS: readonly IIssuePseudoField[];
39
30
  export declare function isKnownIssueField(name: string): name is IIssueField;
40
31
  /** SPEC.md §6.5: "Default (`fields` omitted): the exact five-field terse card established by `DEFAULT_CARD_FIELDS`." */
41
32
  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
- }
33
+ /**
34
+ * A citation, projected for read — the ONE citation contract
35
+ * (`../citation.ts`'s `ICitationRecord`: `ICitation` plus the server-computed
36
+ * `uid`/`sha`/`at` and the persisted `target_type`) with no re-declaration
37
+ * here. `context` is the per-citation free-text prose (the contract's
38
+ * `context`, persisted on the node's `content` column); it is NOT the item's
39
+ * disclosure-contract git context, which is ITEM-level provenance on
40
+ * {@link IIssueCard.gitContext} and rendered once at the head of the block.
41
+ * `revision`, when present, names the git revision the target was resolved
42
+ * against (a revision-pinned citation).
43
+ */
44
+ export type IIssueCitation = ICitationRecord;
60
45
  export interface IIssueNote {
61
46
  uid: string;
62
47
  author: string;
@@ -78,6 +63,91 @@ export interface IIssueRef {
78
63
  uid: string;
79
64
  title: string;
80
65
  status: string;
66
+ /**
67
+ * Which live relation produced this ref (C2 — structural legibility).
68
+ * ADDITIVE — existing consumers reading `uid`/`title`/`status` are
69
+ * unaffected. `related` now surfaces every live relation type: `relates_to`
70
+ * and `part_of` in both directions, and `blocks` (outbound, tagged
71
+ * `blocks`) / incoming (tagged `blocked_by`). `similar_to` (C9's reviewed
72
+ * similarity link) and the reserved `duplicate_of` are declared here for
73
+ * the same ref shape's future producers.
74
+ */
75
+ rel?: 'relates_to' | 'part_of' | 'blocks' | 'blocked_by' | 'similar_to' | 'duplicate_of';
76
+ }
77
+ /**
78
+ * A declared obligation, projected for read (C4). This is the STORED shape,
79
+ * never an evaluation: `requirement` is the predicate as written, and nothing
80
+ * on the read path resolves it (that is C5's gate / C6's verdict). `on_fail` is
81
+ * returned verbatim so a caller can see the declared severity.
82
+ *
83
+ * The predicate/applies-to/override types are imported `import type`-only from
84
+ * `write/obligation.ts` — the grammar is declared in exactly one place, and a
85
+ * type-only import introduces no runtime dependency from `query/**` onto
86
+ * `write/**` (the two are already coupled on `write/errors.js`).
87
+ */
88
+ export interface IObligationView {
89
+ uid: string;
90
+ applies_to: IObligationAppliesTo;
91
+ requirement: IPredicate;
92
+ on_fail: IObligationSeverity;
93
+ override?: IObligationOverride;
94
+ }
95
+ /**
96
+ * Provenance of an `IIssueCard._score` (DESIGN §2 Invariant 5: "a `score_kind`
97
+ * provenance tag on every derived score"). A rank-derived score (`rrf`) is
98
+ * ORDINAL — never to be labelled or read as a similarity/confidence; `bm25`
99
+ * is likewise corpus-relative. A consumer that sees a bare `_score` with no
100
+ * `_score_kind` cannot know which regime produced it, which is exactly the
101
+ * conflation this tag prevents.
102
+ */
103
+ export type IScoreKind = 'rrf' | 'bm25' | 'cosine' | 'rank' | 'priority';
104
+ /**
105
+ * The closed CONDITION type vocabulary. A `Condition` names one thing that can
106
+ * make an item non-actionable (or warn about it); `status` says whether that
107
+ * named condition HOLDS (`{type:'Blocked',status:True}` ⇒ the item IS blocked).
108
+ */
109
+ export type IConditionType = 'Blocked' | 'Obligation' | 'Evidence' | 'Claim' | 'Reference' | 'Budget';
110
+ /** Whether the NAMED condition holds. `Unknown` is a first-class "could not determine", distinct from `False`. */
111
+ export type IConditionStatus = 'True' | 'False' | 'Unknown';
112
+ /** `block` flips actionability false; `warn` is reported but never blocks (systemd Condition-vs-Assert split). */
113
+ export type IConditionSeverity = 'block' | 'warn';
114
+ /**
115
+ * The governed reason-code core (DESIGN §2 Primitive 4) plus the
116
+ * `<domain>/<Code>` extension namespace. `Unknown` is the honest "the check
117
+ * was not run / could not be decided".
118
+ */
119
+ export type IVerdictReasonCode = 'BlockedBy' | 'MissingObligation' | 'EvidenceUnverified' | 'EvidenceStale' | 'ClaimStale' | 'ReferenceUnresolved' | 'Unknown' | `${string}/${string}`;
120
+ /**
121
+ * TRI-STATE (DESIGN §2 Primitive 4) — NEVER a bare boolean. `'unknown'` is
122
+ * never a green light: a caller that handles only booleans must be told, and
123
+ * the list path must report `'unknown'` rather than `true` where it cannot
124
+ * afford the rung. `false` iff a `block`-severity condition is `True`;
125
+ * `'unknown'` iff a `block`-severity condition is `Unknown` (and none is
126
+ * `True`); otherwise `true`.
127
+ */
128
+ export type IActionable = boolean | 'unknown';
129
+ /** One named condition of a {@link IVerdict}. */
130
+ export interface ICondition {
131
+ type: IConditionType;
132
+ /** Whether the NAMED condition HOLDS: `True` on a `Blocked` condition = IS blocked. */
133
+ status: IConditionStatus;
134
+ severity: IConditionSeverity;
135
+ code: IVerdictReasonCode;
136
+ message?: string;
137
+ /** The thing to fix (blocker uid / attestation uid / obligation uid). */
138
+ subject?: string;
139
+ }
140
+ /**
141
+ * The verdict — derived on read, never stored (`revision` is the node's own
142
+ * current content revision, so a caller can detect staleness; there is no
143
+ * materialized status field anywhere).
144
+ */
145
+ export interface IVerdict {
146
+ actionable: IActionable;
147
+ evaluated_at: string;
148
+ revision: number;
149
+ /** Ordered block-severity first, then warn. */
150
+ conditions: ICondition[];
81
151
  }
82
152
  /**
83
153
  * The projected issue card (SPEC.md §6.5's `IIssueCard`). Every field is
@@ -116,7 +186,52 @@ export interface IIssueCard {
116
186
  blockers?: IIssueRef[];
117
187
  related?: IIssueRef[];
118
188
  _score?: number;
189
+ /** Provenance tag for `_score` (DESIGN §2 Invariant 5). Present iff `_score` is; a rank-derived score is ordinal, never similarity/confidence. */
190
+ _score_kind?: IScoreKind;
119
191
  _vector?: number[];
192
+ /** Outbound `blocks` — issues that cannot start until this one is terminal. */
193
+ blocksOut?: IIssueRef[];
194
+ /**
195
+ * Transitive dependent count: how many nodes reach THIS node via `blocks`
196
+ * (inclusive of direct dependents). Deterministic, scope-bounded (bounded to
197
+ * a page's candidate id set on a list read; unbounded but cycle-safe on a
198
+ * single `get`).
199
+ */
200
+ dependents?: number;
201
+ /** The `part_of` parent, if any (`n:1` — at most one). */
202
+ partOf?: IIssueRef | null;
203
+ /** The declared obligations on this item, in `has_obligation` edge order. */
204
+ obligations?: IObligationView[];
205
+ /** The derived actionability verdict + its typed reasons. */
206
+ verdict?: IVerdict;
207
+ /**
208
+ * C10 — the work item's current spec revision pointer. The card carries the
209
+ * revision uid + its `'sha256:<hex>'` token (the token a reader holds and
210
+ * compares) and the sequence — NEVER the revision body. Absent when the item
211
+ * has no spec, or when the `spec` pseudo field was not requested.
212
+ */
213
+ spec?: IIssueSpecProjection;
214
+ /**
215
+ * C9 — reviewed `similar_to` links touching this item, both directions
216
+ * (`ISimilarLinks`). This is the ADVISORY similarity relation, populated
217
+ * only when explicitly requested via `fields:['similar']`; it never includes
218
+ * the reserved `duplicate_of` same-judgement relation.
219
+ */
220
+ similar?: ISimilarLinks;
221
+ }
222
+ /**
223
+ * C10 — the spec pointer projected onto an issue card (DESIGN §12). Additive:
224
+ * a consumer can hold `spec_revision_token` and later hand it to `spec-check`
225
+ * to learn whether it has gone stale.
226
+ */
227
+ export interface IIssueSpecProjection {
228
+ /** The current revision uid (the pointer's target). */
229
+ spec_revision: string;
230
+ /** `'sha256:<hex>'` — the token a reader holds and compares. */
231
+ spec_revision_token: string;
232
+ revision_seq: number;
233
+ /** Present only when the caller supplied a token to compare against. */
234
+ freshness?: SpecFreshness;
120
235
  }
121
236
  /**
122
237
  * `get`'s uid-addressed shape (SPEC.md §6.3.1/§6.5/AC-13) — the implementation
@@ -128,6 +243,17 @@ export interface IIssueCard {
128
243
  export interface IIssueGetByUidInput {
129
244
  uid: IssueUid;
130
245
  fields?: readonly IIssueField[];
246
+ /** Bounds the `auditTrail`/`citations`/`related`/`blockers` pseudo fields to the LAST N rows (preserving each resolver's own order — the audit trail's oldest-first order is sliced to its tail). */
247
+ lastN?: number;
248
+ /** Cursor for the bounded sub-collection (opaque; from the previous page's last returned uid). */
249
+ after?: string;
250
+ /**
251
+ * C6 — the highest verdict ladder rung this `get` may run. Default 3 (the
252
+ * single-item default); a caller may raise it to 5 for a full anchor
253
+ * re-resolve. The list views never accept this and always derive at rung 2
254
+ * (DESIGN §2 Primitive 4, "Bounded derivation").
255
+ */
256
+ deriveThrough?: 1 | 2 | 3 | 4 | 5;
131
257
  }
132
258
  /** SPEC.md §6.5's `IIssueFilter`. */
133
259
  export interface IIssueFilter {
@@ -171,6 +297,14 @@ export interface IIssueFilter {
171
297
  since?: string;
172
298
  until?: string;
173
299
  };
300
+ /**
301
+ * C9 — uid of X: return items linked `similar_to` X (i.e. the SOURCE side of
302
+ * an outgoing `similar_to` edge whose target is X). Renamed from the removed
303
+ * `duplicateOf`; `similar_to` is the reviewed similarity relation.
304
+ */
305
+ similarTo?: string;
306
+ /** C9 — true: return items with ≥1 incoming `similar_to` link (the DST side). Renamed from the removed `hasDuplicates`. */
307
+ hasSimilar?: boolean;
174
308
  }
175
309
  export type IIssueSort = 'priority' | 'updated' | 'created' | 'relevance' | 'textMatch';
176
310
  export type IIssueSortDirection = 'asc' | 'desc';
@@ -182,7 +316,7 @@ export type IIssueSortDirection = 'asc' | 'desc';
182
316
  * `listLocations`, distinct from the issue-search views above (§6.1:
183
317
  * "conflating the two would be wrong").
184
318
  */
185
- export type IIssueView = 'list' | 'ready' | 'graph' | 'order' | 'stale' | 'similar' | 'overlap' | 'projects' | 'components' | 'locations';
319
+ export type IIssueView = 'list' | 'ready' | 'graph' | 'order' | 'stale' | 'similar' | 'overlap' | 'projects' | 'components' | 'locations' | 'kinds' | 'catalogs';
186
320
  export type IIssueQueryFormat = 'json' | 'markdown';
187
321
  /** SPEC.md §5, §6.1's `axis` — the grouping dimension for `overlapUids` (§6.2). */
188
322
  export type IOverlapAxis = 'file' | 'project' | 'component' | 'author';
@@ -220,6 +354,8 @@ export interface IIssueQueryInput {
220
354
  overlapUids?: readonly string[];
221
355
  /** `view:'stale'` only — minutes since `claimedAt`; falls back to `project_policy.claim_stale_after_min` (default 30) when omitted. */
222
356
  staleAfterMin?: number;
357
+ /** C8 — `view:'catalogs'` only: narrow to one vocabulary catalog. Omitted ⇒ every catalog's terms (the same set `view:'kinds'` exposes). */
358
+ catalog?: CatalogKindName;
223
359
  }
224
360
  export declare const MAX_QUERY_LIMIT = 1000;
225
361
  export declare const DEFAULT_QUERY_LIMIT = 50;
@@ -301,6 +437,7 @@ export type IIssueQueryResult = IIssueListResult | {
301
437
  } | {
302
438
  view: 'similar';
303
439
  items: IIssueCard[];
440
+ clusters?: ISimilarViewClusterBlock;
304
441
  } | {
305
442
  view: 'overlap';
306
443
  groups: IOverlapGroup[];
@@ -313,6 +450,12 @@ export type IIssueQueryResult = IIssueListResult | {
313
450
  } | {
314
451
  view: 'locations';
315
452
  items: ILocationSummary[];
453
+ } | {
454
+ view: 'kinds';
455
+ catalogs: ICatalogView;
456
+ } | {
457
+ view: 'catalogs';
458
+ terms: ICatalogTerm[];
316
459
  } | IIssueMarkdownResult;
317
460
  export interface IProjectSummary {
318
461
  uid: string;
@@ -391,6 +534,28 @@ export interface ILookupResult {
391
534
  };
392
535
  /** Present when only a partial (project-level, or path-prefix) match was found — never a silent null (§3a). */
393
536
  hint?: string;
537
+ /**
538
+ * C1 — a resolved-by-token hint. When `lookup` recognises `q` as a uid/uid
539
+ * prefix or a unique issue title, the registry shape does not apply; instead
540
+ * the caller is told which verb to re-issue (`get`) and with which uid, so a
541
+ * transport can forward exactly one canonical request rather than guessing.
542
+ */
543
+ redirect?: {
544
+ verb: 'get' | 'query';
545
+ uid?: string;
546
+ query?: string;
547
+ };
548
+ /**
549
+ * C1 — the candidate set behind an ambiguity this read CHOSE to surface
550
+ * rather than fail on. (A `lookup` whose title grep matches ≥2 issues still
551
+ * throws `AmbiguousReferenceError`; this field exists for callers that
552
+ * surface candidates alongside a chosen primary.)
553
+ */
554
+ candidates?: Array<{
555
+ uid: string;
556
+ kind: string;
557
+ name: string;
558
+ }>;
394
559
  }
395
560
  /**
396
561
  * `get`'s registry-detail shape (SPEC.md §3a/§8 AC-11) — `get --input
@@ -448,3 +613,53 @@ export type IIssueGetInput = IIssueGetByUidInput | IIssueGetRegistryInput;
448
613
  * repro — so no workaround is needed here; this type is plain `IIssueCard`.
449
614
  */
450
615
  export type IIssueGetResult = IIssueCard | IProjectDetail | IComponentDetail | ILocationDetail;
616
+ /** C9 — one reviewed `similar_to` neighbour of a card, in the compact cross-reference shape. */
617
+ export interface ISimilarRef {
618
+ uid: string;
619
+ title: string;
620
+ status: string;
621
+ projectUid?: string;
622
+ repoUrl?: string;
623
+ }
624
+ /** C9 — a card's reviewed `similar_to` links, both directions (`n:m`, so both are sets). */
625
+ export interface ISimilarLinks {
626
+ /** Items THIS one is linked `similar_to` (outgoing, n:m). */
627
+ similarTo: ISimilarRef[];
628
+ /** Items linked `similar_to` THIS one (incoming, n:m). */
629
+ similarFrom: ISimilarRef[];
630
+ }
631
+ /** C9 — one member of an advisory or linked similarity cluster. */
632
+ export interface ISimilarMember {
633
+ uid: string;
634
+ title: string;
635
+ projectUid: string;
636
+ projectName: string;
637
+ componentUid?: string;
638
+ repoUrl?: string;
639
+ /** Cosine to the cluster seed (undefined for linked-only members). */
640
+ score?: number;
641
+ /** Which independent signals fired, e.g. `['cosine','title-tokens']`. */
642
+ signals?: string[];
643
+ /** A `similar_to` target when linked. */
644
+ linkedTo?: string;
645
+ }
646
+ /** C9 — a single-linkage similarity cluster: `seedUid` is the top-scoring member that anchored it. */
647
+ export interface ISimilarCluster {
648
+ seedUid: string;
649
+ members: ISimilarMember[];
650
+ /** true iff live `similar_to` edges exist among members. */
651
+ linked: boolean;
652
+ }
653
+ /**
654
+ * C9 — the additive cluster block `view:'similar'` gains when a scope filter
655
+ * (`project`/`component`) is present. `candidate` is the advisory,
656
+ * write-nothing scan output; `linked` is built from existing live `similar_to`
657
+ * edges only. Without the embedding substrate `candidate` is `[]` (the
658
+ * documented degraded mode) while `linked` still returns.
659
+ */
660
+ export interface ISimilarViewClusterBlock {
661
+ candidate: ISimilarCluster[];
662
+ linked: ISimilarCluster[];
663
+ scanned: number;
664
+ computedAt: string;
665
+ }
@@ -0,0 +1,21 @@
1
+ import { IActionable, ICondition } from './types.js';
2
+
3
+ /**
4
+ * The ONE actionability rule (DESIGN §2 Primitive 4).
5
+ *
6
+ * - `false` iff at least one `block`-severity condition is `True` (the item IS
7
+ * blocked — a definite answer beats an uncertain one);
8
+ * - `'unknown'` iff any `block`-severity condition is `Unknown` and none is
9
+ * `True` (the check could not be run / decided — NEVER a green light);
10
+ * - otherwise `true`.
11
+ *
12
+ * `'Unknown'` is never treated as `false` OR as `true`. `warn`-severity
13
+ * conditions never affect the result.
14
+ */
15
+ export declare function computeActionable(conditions: readonly ICondition[]): IActionable;
16
+ /**
17
+ * Stable ordering: all `block` before all `warn`; within a band, by `code`
18
+ * then `subject` (both lexical, `undefined` subject sorts first). Pure and
19
+ * total — two callers ordering the same set always agree.
20
+ */
21
+ export declare function orderConditions(conditions: readonly ICondition[]): ICondition[];
@@ -0,0 +1,22 @@
1
+ import { IVerdict } from './types.js';
2
+ import { GraphBackend, NodeRecord } from '@adhd/sox-graph-store';
3
+
4
+ /** Highest ladder rung `deriveVerdict` may run. */
5
+ export type IVerdictRung = 1 | 2 | 3 | 4 | 5;
6
+ export interface IDeriveVerdictOptions {
7
+ /**
8
+ * Highest ladder rung this call is allowed to run. Default 2 (the list
9
+ * bound). 5 = full re-resolve. A budget stop yields an `Unknown` condition.
10
+ */
11
+ maxRung?: IVerdictRung;
12
+ /** The instant used for staleness maths; defaults to nowISO(). */
13
+ at?: string;
14
+ /** Staleness threshold (minutes); defaults to 30 (project_policy default). */
15
+ claimStaleAfterMin?: number;
16
+ /** Test instrumentation: invoked with each rung actually evaluated. */
17
+ onRung?: (rung: number) => void;
18
+ }
19
+ /**
20
+ * Derive the verdict for a live issue (DESIGN §2 Primitive 4). NEVER writes.
21
+ */
22
+ export declare function deriveVerdict(graph: GraphBackend, issue: NodeRecord, opts?: IDeriveVerdictOptions): Promise<IVerdict>;
@@ -0,0 +1,47 @@
1
+ import { GraphBackend } from '@adhd/sox-graph-store';
2
+
3
+ /**
4
+ * The catalogs this view exposes. `kind`/`status`/`priority` are store-backed
5
+ * (open vocabularies minted by the write layer); the rest are fixed in-code
6
+ * vocabularies projected from their validating source.
7
+ */
8
+ export type CatalogKindName = 'kind' | 'status' | 'priority' | 'relation' | 'field' | 'error_code' | 'location_type' | 'verb';
9
+ /** The in-code source a term was generated from. Named so the catalog is provably generated, not hand-maintained. */
10
+ export type CatalogSource = 'store' | 'edge_kind_table' | 'reserved_terminal_status_names' | 'issue_field_union' | 'error_code_union' | 'valid_location_types' | 'mounted_verb_surface';
11
+ export type CatalogLifecycle = 'active' | 'deprecated';
12
+ export interface ICatalogTerm {
13
+ /** The term's canonical name. */
14
+ name: string;
15
+ /** Live row uid when a store row backs it; absent for in-code source terms. */
16
+ uid?: string;
17
+ /** Which vocabulary this term belongs to. */
18
+ catalog: CatalogKindName;
19
+ /** The validating source this term was generated from at call time. */
20
+ source: CatalogSource;
21
+ lifecycle: CatalogLifecycle;
22
+ /** Present when deprecated: the term to use instead. */
23
+ replacedBy?: string;
24
+ /** For `kind`/`status`/`priority`: the live callers count, read at call time. */
25
+ usageCount?: number;
26
+ }
27
+ export interface ICatalogView {
28
+ catalogs: CatalogKindName[];
29
+ terms: ICatalogTerm[];
30
+ /** True when any term is a case-fold collision of another within its catalog (the invariant the read layer depends on). */
31
+ hasCaseCollisions: boolean;
32
+ }
33
+ /**
34
+ * ONE read view over every vocabulary (AC1). Every non-`store` term is derived
35
+ * from its in-code source at call time — never a hand-written list — and every
36
+ * `store` term is a live row with its caller count.
37
+ *
38
+ * Read-only: it never opens a transaction and never writes, so it is safe
39
+ * against any store, including the live one.
40
+ */
41
+ export declare function catalogView(graph: GraphBackend): Promise<ICatalogView>;
42
+ /**
43
+ * The terms of ONE catalog (AC1's narrowed sibling). Derived from the SAME
44
+ * {@link catalogView} — there is no second generation path, so `kinds` and
45
+ * `catalogs` can never disagree about a catalog's contents.
46
+ */
47
+ export declare function catalogFor(graph: GraphBackend, catalog: CatalogKindName): Promise<ICatalogTerm[]>;
@@ -33,11 +33,31 @@ export declare function getRegistryDetail(graph: GraphBackend, input: {
33
33
  name: string;
34
34
  }): Promise<ILocationDetail>;
35
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).
36
+ * The optional kind HINT `lookup` accepts (C1, §3a). `q` is the token to
37
+ * resolve; `kind` narrows which routing branch runs, so a caller that already
38
+ * knows it is holding an issue uid or a project name can suppress the other
39
+ * branches instead of relying on the default precedence.
42
40
  */
43
- export declare function lookup(graph: GraphBackend, q: string): Promise<ILookupResult>;
41
+ export interface ILookupInput {
42
+ q: string;
43
+ /** optional: `'location' | 'project' | 'component' | 'issue'` */
44
+ kind?: string;
45
+ }
46
+ /**
47
+ * `query --input '{"view":"lookup", "lookup": "<tool|file|url|uid|title|project>"}'`
48
+ * (§3a, C1 AC4) — resolve ANY token a consumer holds to exactly one canonical
49
+ * answer, or fail loudly naming what was searched.
50
+ *
51
+ * Routing order (C1 spec, fixed):
52
+ * 1. a uid or uid PREFIX → a `redirect` to `get` (the registry shape does not
53
+ * apply to a node reached by identity);
54
+ * 2. an issue TITLE → exactly one hit redirects to `get`; ≥2 is
55
+ * `AmbiguousReferenceError`; zero falls through;
56
+ * 3. the existing location classify→match→walk;
57
+ * 4. a project `name`/`repoUrl` match;
58
+ * then `CatalogNotFoundError('location', q)` — `lookup` is never a silent null.
59
+ *
60
+ * Existing location-only consumers keep working: their exact-path query misses
61
+ * the uid/title/project branches and is served by step 3 unchanged.
62
+ */
63
+ export declare function lookup(graph: GraphBackend, input: string | ILookupInput): Promise<ILookupResult>;
@@ -0,0 +1,49 @@
1
+ import { IQueryStoreHandle } from '../query.js';
2
+ import { IIssueFilter } from '../types.js';
3
+
4
+ /**
5
+ * `report`'s filter — the four dimensions `priorityMatrix` composes with
6
+ * (`project`/`component`/`kind`/`status`). `status` is the coarse
7
+ * `open`/`closed`/`all` selector (never a name list): `report`'s whole point
8
+ * is a scope-named aggregate, and `statusScope` echoes exactly what was
9
+ * counted. An omitted `status` defaults to `'open'`, matching `priorityMatrix`.
10
+ */
11
+ export interface IReportInput {
12
+ filter?: {
13
+ project?: string;
14
+ component?: string;
15
+ kind?: string | string[];
16
+ status?: 'open' | 'closed' | 'all';
17
+ };
18
+ }
19
+ export interface IReportResult {
20
+ /** Counts by `kind` name (live, in-scope rows only), name-ascending. */
21
+ byKind: Array<{
22
+ kind: string;
23
+ count: number;
24
+ }>;
25
+ /** Counts by `priority` name + rank (composed from `priorityMatrix`). */
26
+ byPriority: Array<{
27
+ priority: string;
28
+ rank?: number;
29
+ count: number;
30
+ }>;
31
+ /** Status histogram: status name, its terminal flag, and the count. */
32
+ byStatus: Array<{
33
+ status: string;
34
+ terminal: boolean;
35
+ count: number;
36
+ }>;
37
+ /** Average age in days of OPEN (non-terminal) in-scope items, measured from `tCreated` at report time. */
38
+ avgAgeDays: number;
39
+ /** Names what was counted — `'open'` when the filter omitted `status`. */
40
+ statusScope: NonNullable<IIssueFilter['status']>;
41
+ /** The instant every time-relative number above was computed against. */
42
+ computedAt: string;
43
+ }
44
+ /**
45
+ * SPEC.md §5's grouped rollup (DESIGN §5 AC7) — every count computed from the
46
+ * store in this call, none stored. See this file's header for the
47
+ * composition rule.
48
+ */
49
+ export declare function report(handle: IQueryStoreHandle, input: IReportInput): Promise<IReportResult>;
@@ -72,23 +72,46 @@ export interface IRelevanceRankOptions {
72
72
  * `view:'similar'` itself.
73
73
  */
74
74
  export declare function rankByFusedRelevance(handle: IQueryStoreHandle, opts: IRelevanceRankOptions): Promise<SearchResult[]>;
75
+ /**
76
+ * `view:'similar'`'s result when the caller also needs completeness truth
77
+ * (`has_more`/`limit`) — see {@link querySimilarViewWithMeta}. `hasMore` is
78
+ * derived by fetching one row beyond the page, never fabricated.
79
+ */
80
+ export interface ISimilarViewResult {
81
+ items: IIssueCard[];
82
+ hasMore: boolean;
83
+ /** The caller-facing page size actually applied. */
84
+ limit: number;
85
+ }
86
+ /**
87
+ * `view:'similar'` (SPEC.md §5a) — the plain `IIssueCard[]` every existing
88
+ * caller (and `semantic.spec.ts`) already reads. Thin wrapper over
89
+ * {@link querySimilarViewWithMeta}, which also yields the completeness flag
90
+ * the C7 envelope attaches.
91
+ */
92
+ export declare function querySimilarView(handle: IQueryStoreHandle, input: IIssueQueryInput): Promise<IIssueCard[]>;
75
93
  /**
76
94
  * `view:'similar'` (SPEC.md §5a) — `filter.anchor` (item-anchored) or
77
95
  * `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).
96
+ * {@link rankByFusedRelevance}, projected to `IIssueCard`s with `_score`/`_score_kind`
97
+ * populated when requested (`card.ts`'s `IAssembleIssueCardOptions` seam — SPEC.md
98
+ * §5a's `_score` exposure; `_score_kind:'rrf'` per DESIGN §2 Invariant 5).
81
99
  *
82
100
  * The anchor issue is EXCLUDED from its own results (RAG-SPEC.md §3.2's
83
101
  * carried-forward "the query item itself is excluded" — SPEC.md §5a does not
84
102
  * restate this explicitly, a genuine spec gap this implementation fills per
85
103
  * that precedent rather than silently, since "similar to X" trivially
86
104
  * 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,
105
+ * Exclusion is done POST-search (fetch beyond the page, drop the anchor,
88
106
  * slice to `limit`) rather than by enumerating the whole issue table to
89
107
  * subtract one id up front — far cheaper for the common "no other filter"
90
108
  * case, and still exactly correct.
91
109
  *
110
+ * **Completeness (`hasMore`):** the fetch is one row beyond the page — plus
111
+ * one MORE when anchored, since the anchor consumes a slot when it appears in
112
+ * the window — so `hasMore` is genuinely "a live row exists beyond this page",
113
+ * not an inference from `items.length === limit`.
114
+ *
92
115
  * Errors: `IssueNotFoundError(anchor)` (SPEC.md §6.1's general `uid`-
93
116
  * addressing convention — `anchor` is `uid`-typed per §6.1's own statement
94
117
  * that it is "the SAME `uid`-typed field" as every other addressing field;
@@ -98,4 +121,4 @@ export declare function rankByFusedRelevance(handle: IQueryStoreHandle, opts: IR
98
121
  * `InvalidArgumentError('filter', ...)` (neither `anchor` nor `semantic`
99
122
  * given). `BacklogValidationError('limit', ...)` (out-of-range `limit`).
100
123
  */
101
- export declare function querySimilarView(handle: IQueryStoreHandle, input: IIssueQueryInput): Promise<IIssueCard[]>;
124
+ export declare function querySimilarViewWithMeta(handle: IQueryStoreHandle, input: IIssueQueryInput): Promise<ISimilarViewResult>;