@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.
- package/CHANGELOG.md +53 -0
- package/README.md +194 -39
- package/api.d.ts +87 -0
- package/api.ir.json +1 -1
- package/citation.d.ts +176 -0
- package/envelope.d.ts +35 -1
- package/index.d.ts +23 -3
- package/index.js +106 -58
- package/index.mjs +11627 -7068
- package/ir-artifact.d.ts +7 -3
- package/lifecycle.d.ts +49 -0
- package/package.json +5 -5
- package/query/canonical.d.ts +16 -0
- package/query/card.d.ts +116 -4
- package/query/get.d.ts +8 -1
- package/query/index.d.ts +1 -0
- package/query/query.d.ts +20 -13
- package/query/redirect.d.ts +30 -0
- package/query/resolve.d.ts +48 -0
- package/query/similar-clusters.d.ts +8 -0
- package/query/similarity-signals.d.ts +47 -0
- package/query/spec-staleness.d.ts +21 -0
- package/query/types.d.ts +249 -34
- package/query/verdict-core.d.ts +21 -0
- package/query/verdict.d.ts +22 -0
- package/query/views/catalog.d.ts +47 -0
- package/query/views/registry.d.ts +27 -7
- package/query/views/report.d.ts +49 -0
- package/query/views/semantic.d.ts +28 -5
- package/query/views/stats.d.ts +38 -2
- package/readiness.d.ts +29 -0
- package/retry-policy.d.ts +44 -0
- package/serve.d.ts +1 -1
- package/server.d.ts +71 -3
- package/service-config.d.ts +109 -0
- package/service-errors.d.ts +51 -0
- package/skill/SKILL.md +688 -81
- package/store/catalog-invariant-guard.d.ts +4 -4
- package/vocabulary.d.ts +48 -0
- package/write/anchor-check.d.ts +139 -0
- package/write/attestation.d.ts +64 -0
- package/write/catalog-merge.d.ts +15 -8
- package/write/catalog.d.ts +63 -0
- package/write/citation-path.d.ts +31 -0
- package/write/citation.d.ts +157 -0
- package/write/create-issue.d.ts +143 -29
- package/write/errors.d.ts +159 -0
- package/write/gate.d.ts +67 -0
- package/write/merge-project.d.ts +54 -0
- package/write/obligation.d.ts +133 -0
- package/write/relate.d.ts +1 -1
- package/write/revision.d.ts +27 -0
- package/write/similarity-scan.d.ts +84 -0
- package/write/spec-revision.d.ts +131 -0
- package/write/spec-revision.reconcile.d.ts +48 -0
- package/write/transition.d.ts +13 -2
- package/write/tx.d.ts +21 -1
- package/write/update.d.ts +23 -1
package/ir-artifact.d.ts
CHANGED
|
@@ -61,9 +61,13 @@ export declare function bakedIrArtifactPath(distDir: string): string;
|
|
|
61
61
|
export declare function readBakedIrArtifact(distDir: string): Operation[] | undefined;
|
|
62
62
|
/**
|
|
63
63
|
* Writes the baked IR artifact to `outFile`, recording the provenance the
|
|
64
|
-
* reader re-validates against: the source `.d.ts` path
|
|
65
|
-
*
|
|
66
|
-
*
|
|
64
|
+
* reader re-validates against: the source `.d.ts`'s DIST-RELATIVE path
|
|
65
|
+
* (`api.d.ts`; audit, machine-independent), its sha256 and byte length, plus a
|
|
66
|
+
* sha256 per EVERY `*.d.ts` under the artifact's `dist/`
|
|
67
|
+
* (`artifactSource.deps`). Emits NO timestamp and NO absolute path, so two
|
|
68
|
+
* builds of identical source produce byte-identical artifacts — reproducible
|
|
69
|
+
* output that `version`'s no-bump fast path depends on (`normalizedHash` over
|
|
70
|
+
* `dist/`). Uses the plugin's shared `atomicWriteJson`, so the
|
|
67
71
|
* artifact is published atomically and durably exactly like a runtime-cache
|
|
68
72
|
* entry — a build killed mid-write can never leave a half-written artifact
|
|
69
73
|
* behind.
|
package/lifecycle.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lifecycle.ts — the `starting / live / ready` trichotomy (D-A, Segment B).
|
|
3
|
+
*
|
|
4
|
+
* Exactly the three states DESIGN §2 D1 names. Failure/stop are transitions,
|
|
5
|
+
* never states:
|
|
6
|
+
*
|
|
7
|
+
* - `starting`: env resolved, store opening, the mount being composed (the
|
|
8
|
+
* ~14 s cold-start window), transports not yet accepting.
|
|
9
|
+
* - `live`: the transport accepts connections (stdio connected / socket bound).
|
|
10
|
+
* - `ready`: the serving-path probe (`readiness.ts`) has passed.
|
|
11
|
+
*
|
|
12
|
+
* A LIVENESS failure means RESTART; a READINESS failure is REPORT ONLY, state
|
|
13
|
+
* unchanged, NO restart. {@link IServiceLifecycle.fail} records a failure
|
|
14
|
+
* WITHOUT changing the state and returns the action the supervisor must take.
|
|
15
|
+
*/
|
|
16
|
+
export type IServiceState = 'starting' | 'live' | 'ready';
|
|
17
|
+
export interface IServiceFailure {
|
|
18
|
+
kind: 'liveness' | 'readiness';
|
|
19
|
+
code: string;
|
|
20
|
+
message: string;
|
|
21
|
+
subject?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface IServiceReport {
|
|
24
|
+
state: IServiceState;
|
|
25
|
+
/** ISO timestamp the current state was entered. */
|
|
26
|
+
since: string;
|
|
27
|
+
/** Present on a failure. A liveness failure means RESTART; a readiness
|
|
28
|
+
* failure means REPORT ONLY, no restart. */
|
|
29
|
+
failure?: IServiceFailure;
|
|
30
|
+
lastTickAt?: string;
|
|
31
|
+
degraded: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface IServiceLifecycle {
|
|
34
|
+
state(): IServiceState;
|
|
35
|
+
report(): IServiceReport;
|
|
36
|
+
/** starting → live (transport accepting). */
|
|
37
|
+
markLive(): void;
|
|
38
|
+
/** live → ready (the serving-path probe passed). Idempotent. */
|
|
39
|
+
markReady(): void;
|
|
40
|
+
/** Record a failure WITHOUT changing the state; returns the supervisor action. */
|
|
41
|
+
fail(f: IServiceFailure): {
|
|
42
|
+
action: 'restart' | 'report';
|
|
43
|
+
};
|
|
44
|
+
whenReady(): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
export interface ILifecycleDeps {
|
|
47
|
+
now?: () => number;
|
|
48
|
+
}
|
|
49
|
+
export declare function createLifecycle(deps?: ILifecycleDeps): IServiceLifecycle;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhd/backlog",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"bin": {
|
|
5
5
|
"adhd-backlog": "index.js"
|
|
6
6
|
},
|
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
"@adhd/apigen-engine-naming": "^0.2.5",
|
|
11
11
|
"@adhd/apigen-plugin-api-fastify": "^0.2.6",
|
|
12
12
|
"@adhd/apigen-plugin-batch": "^0.2.5",
|
|
13
|
-
"@adhd/apigen-plugin-cli-output": "^0.2.
|
|
13
|
+
"@adhd/apigen-plugin-cli-output": "^0.2.7",
|
|
14
14
|
"@adhd/apigen-plugin-ir-cache": "^0.1.3",
|
|
15
15
|
"@adhd/apigen-plugin-mcp": "^0.3.1",
|
|
16
16
|
"@adhd/apigen-plugin-openapi": "^0.2.5",
|
|
17
17
|
"@adhd/environment": "^0.1.8",
|
|
18
18
|
"@adhd/environment-base-spec": "^0.1.3",
|
|
19
19
|
"@adhd/environment-builder": "^0.1.7",
|
|
20
|
-
"@adhd/sox-graph-store": "^0.
|
|
21
|
-
"@adhd/sox-hybrid-search": "^0.
|
|
20
|
+
"@adhd/sox-graph-store": "^0.12.0",
|
|
21
|
+
"@adhd/sox-hybrid-search": "^0.6.0",
|
|
22
22
|
"@adhd/sox-semantic": "^0.1.8",
|
|
23
|
-
"@adhd/sox-store-adapter": "^0.
|
|
23
|
+
"@adhd/sox-store-adapter": "^0.11.0",
|
|
24
24
|
"@adhd/sox-telemetry": "^0.3.2",
|
|
25
25
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
26
26
|
"ajv": "^8.20.0",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AdapterTransaction } from '@adhd/sox-store-adapter';
|
|
2
|
+
import { GraphBackend } from '@adhd/sox-graph-store';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolve `uid` to the canonical head of its reserved `duplicate_of` chain.
|
|
6
|
+
* Read-only; returns the input uid when it resolves to nothing or is unlinked.
|
|
7
|
+
* Never throws on a linked uid.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveCanonicalIssue(graph: GraphBackend, uid: string): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Tx-scoped {@link resolveCanonicalIssue} for the reserved same-judgement
|
|
12
|
+
* write path: the same chain walk (duplicate_of → one-hop redirect →
|
|
13
|
+
* `SUPERSEDES` head), hand-composed against the transaction handle rather than
|
|
14
|
+
* a bare `GraphBackend` (ADR-0002 — one concept, one algorithm).
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveIssueCanonicalTx(tx: AdapterTransaction, uid: string): Promise<string>;
|
package/query/card.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IVerdictRung } from './verdict.js';
|
|
2
|
+
import { IIssueAuditEntry, IIssueCard, IIssueField, IIssueRef, IObligationView, ISimilarLinks, IScoreKind } from './types.js';
|
|
2
3
|
import { EdgeRecord, GraphBackend, NodeRecord } from '@adhd/sox-graph-store';
|
|
3
4
|
|
|
4
5
|
/** SPEC.md §6.5's `assertKnownFields` — unknown name → `BacklogValidationError` naming it, never a silent drop. */
|
|
@@ -14,18 +15,129 @@ export declare function resolveAuditTrail(graph: GraphBackend, issueId: number,
|
|
|
14
15
|
* actually blocking it right now," never the full historical blocker set.
|
|
15
16
|
*/
|
|
16
17
|
export declare function resolveBlockers(graph: GraphBackend, issueId: number): Promise<IIssueRef[]>;
|
|
17
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* `related` — every live relation type that touches this issue, deduplicated
|
|
20
|
+
* by node and tagged with the relation that produced it (C2 — structural
|
|
21
|
+
* legibility; SPEC.md §5/§6.5, DESIGN §5 AC2):
|
|
22
|
+
*
|
|
23
|
+
* - `relates_to` (both directions — an `n:m` symmetric-in-practice rel, §3),
|
|
24
|
+
* tagged `relates_to`;
|
|
25
|
+
* - `part_of` (both directions: the parent when this item is the child, the
|
|
26
|
+
* children when it is the parent), tagged `part_of`;
|
|
27
|
+
* - `blocks` outbound (this item blocks the target), tagged `blocks`; and
|
|
28
|
+
* `blocks` incoming (the source blocks this item), tagged `blocked_by`.
|
|
29
|
+
*
|
|
30
|
+
* `blockers` (the non-terminal INCOMING subset) is deliberately NOT removed —
|
|
31
|
+
* it stays as the "what's actually blocking me right now" view; `related` is
|
|
32
|
+
* the structural superset. Widening this result is additive ROWS, never a
|
|
33
|
+
* signature change: a consumer reading `uid`/`title`/`status` is unaffected.
|
|
34
|
+
* Dedup is by node id, first-seen wins, in the deterministic order above.
|
|
35
|
+
*/
|
|
18
36
|
export declare function resolveRelated(graph: GraphBackend, issueId: number, outgoing?: EdgeRecord[]): Promise<IIssueRef[]>;
|
|
37
|
+
/**
|
|
38
|
+
* C9 — the reviewed `similar_to` links touching one issue, BOTH directions
|
|
39
|
+
* (`n:m`). Outgoing edges (`similar_to` from this issue) become `similarTo`;
|
|
40
|
+
* incoming edges (into it) become `similarFrom`. Each neighbour is projected to
|
|
41
|
+
* the compact `ISimilarRef` shape (uid/title/status + owning project/repo).
|
|
42
|
+
*
|
|
43
|
+
* ADVISORY relation only — the reserved `duplicate_of` same-judgement relation
|
|
44
|
+
* is deliberately never read here.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveSimilar(graph: GraphBackend, issueId: number, outgoing?: EdgeRecord[]): Promise<ISimilarLinks>;
|
|
47
|
+
/**
|
|
48
|
+
* Outbound `blocks` (C2) — the issues THIS one blocks, live targets only.
|
|
49
|
+
* Mirrors {@link resolveBlockers}'s shape but for the other direction: that
|
|
50
|
+
* function returns the non-terminal INCOMING blockers ("what stops me"),
|
|
51
|
+
* this one returns everything this item unblocks ("what I am stopping").
|
|
52
|
+
*/
|
|
53
|
+
export declare function resolveBlocksOut(graph: GraphBackend, issueId: number, outgoing?: EdgeRecord[]): Promise<IIssueRef[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Transitive outbound dependent count (C2, AC3): how many nodes reach
|
|
56
|
+
* `issueId` via `blocks` (inclusive of direct dependents — the nodes this one
|
|
57
|
+
* blocks, directly or through a chain). A cycle-safe BFS over the outgoing
|
|
58
|
+
* `blocks` edges: a `visited` set counts each node once and terminates a cycle
|
|
59
|
+
* (the reachability set is finite even when the edge set is cyclic).
|
|
60
|
+
*
|
|
61
|
+
* `scope`, when given, restricts the walk to node ids in the set — a list page
|
|
62
|
+
* passes its own candidate id set so an N-item page costs N bounded walks, not
|
|
63
|
+
* N whole-graph walks. On a single `get`, `scope` is omitted and the walk is
|
|
64
|
+
* unbounded but still cycle-safe.
|
|
65
|
+
*/
|
|
66
|
+
export declare function resolveDependents(graph: GraphBackend, issueId: number, scope?: ReadonlySet<number>): Promise<number>;
|
|
67
|
+
/**
|
|
68
|
+
* The single `part_of` parent of this item, or `null` (C2, AC3). `part_of` is
|
|
69
|
+
* declared `n:1` (one parent per item, DATA_MODEL.md §3 / `EDGE_KIND_TABLE`),
|
|
70
|
+
* so at most one live outgoing `part_of` edge can exist; the multiplicity gate
|
|
71
|
+
* (`checkMultiplicityTx`) enforces that at write time.
|
|
72
|
+
*/
|
|
73
|
+
export declare function resolvePartOf(graph: GraphBackend, issueId: number, outgoing?: EdgeRecord[]): Promise<IIssueRef | null>;
|
|
74
|
+
/**
|
|
75
|
+
* `obligations` (C4) — the declared, typed requirements on this issue, read
|
|
76
|
+
* from its live `has_obligation` targets. PURE PROJECTION: each node's stored
|
|
77
|
+
* `metadata` maps straight onto an {@link IObligationView}; the predicate is
|
|
78
|
+
* NEVER evaluated here (evaluation belongs to the C5 gate at transition time
|
|
79
|
+
* and the C6 verdict on read). One batched `getNodesByIds`; the returned order
|
|
80
|
+
* follows the edge order, and an edge whose target no longer resolves (a
|
|
81
|
+
* concurrently-invalidated row) is simply skipped rather than yielding a
|
|
82
|
+
* partial view.
|
|
83
|
+
*/
|
|
84
|
+
export declare function resolveObligations(graph: GraphBackend, issueId: number, outgoing?: EdgeRecord[]): Promise<IObligationView[]>;
|
|
19
85
|
export interface IAssembleIssueCardOptions {
|
|
20
86
|
/** Score from a `searchRanked`/`searchNodes` response — populates `_score` when requested (§5a). */
|
|
21
87
|
score?: number;
|
|
88
|
+
/** Provenance of `score` — populates `_score_kind` alongside `_score` (DESIGN §2 Invariant 5). */
|
|
89
|
+
scoreKind?: IScoreKind;
|
|
90
|
+
/**
|
|
91
|
+
* Bounds the sub-collection pseudo fields (`auditTrail`/`citations`/
|
|
92
|
+
* `related`/`blockers`) — `lastN` takes the LAST N rows, `after` is an
|
|
93
|
+
* opaque cursor naming a uid to resume strictly after. Applied HERE (at the
|
|
94
|
+
* card-assembly call site), never inside `resolveAuditTrail` itself: the
|
|
95
|
+
* `openCurve` view (`views/stats.ts`) calls an independent batched audit
|
|
96
|
+
* reader that needs the FULL trail and must not be bounded.
|
|
97
|
+
*/
|
|
98
|
+
bounds?: {
|
|
99
|
+
lastN?: number;
|
|
100
|
+
after?: string;
|
|
101
|
+
};
|
|
22
102
|
/** Pre-fetched outgoing edges (`getEdges({src: issue.id})`) — pass when the caller already has them (e.g. a batch `query` page) to avoid a redundant round trip. */
|
|
23
103
|
outgoingEdges?: EdgeRecord[];
|
|
104
|
+
/**
|
|
105
|
+
* The candidate id set a `dependents` walk is bounded to (C2). A list page
|
|
106
|
+
* passes its own page id set so an N-item page costs N bounded walks; a
|
|
107
|
+
* single `get` omits it, leaving the walk unbounded (but cycle-safe). Only
|
|
108
|
+
* consulted when `fields` requests `dependents`.
|
|
109
|
+
*/
|
|
110
|
+
scope?: ReadonlySet<number>;
|
|
111
|
+
/**
|
|
112
|
+
* C6 — the highest verdict ladder rung this card may derive. Default 2 (the
|
|
113
|
+
* list bound); a single-item `get` raises it to 3+ via `deriveThrough`.
|
|
114
|
+
* Only consulted when `fields` requests `verdict`.
|
|
115
|
+
*/
|
|
116
|
+
verdictRung?: IVerdictRung;
|
|
117
|
+
/** C6 test instrumentation — threaded to `deriveVerdict`'s `onRung`. */
|
|
118
|
+
onVerdictRung?: (rung: number) => void;
|
|
119
|
+
/**
|
|
120
|
+
* C10 — a token the caller holds; when supplied AND the `spec` field was
|
|
121
|
+
* requested, `card.spec.freshness` reports whether that token is still
|
|
122
|
+
* current for this item's spec revision.
|
|
123
|
+
*/
|
|
124
|
+
specToken?: string;
|
|
24
125
|
}
|
|
25
126
|
/**
|
|
26
127
|
* Project `issue` onto the requested `fields` (default: the five-field terse
|
|
27
128
|
* card, SPEC.md §6.5). `uid` is always populated regardless of `fields`.
|
|
28
129
|
*/
|
|
29
130
|
export declare function assembleIssueCard(graph: GraphBackend, issue: NodeRecord, fields: readonly IIssueField[], opts?: IAssembleIssueCardOptions): Promise<IIssueCard>;
|
|
30
|
-
/** Batch-assemble cards for a page of issue nodes, sharing one `getNodesByIds`-batched catalog/placement resolution pass where possible. Falls back to per-issue assembly (still batches internally) — a cross-issue batch would require a different, `getEdges`-list API this file's dependency (`GraphBackend`) does not expose.
|
|
31
|
-
|
|
131
|
+
/** Batch-assemble cards for a page of issue nodes, sharing one `getNodesByIds`-batched catalog/placement resolution pass where possible. Falls back to per-issue assembly (still batches internally) — a cross-issue batch would require a different, `getEdges`-list API this file's dependency (`GraphBackend`) does not expose.
|
|
132
|
+
*
|
|
133
|
+
* The page's own node ids are handed to each card as the `dependents` walk's
|
|
134
|
+
* `scope` (C2): a list read bounds each transitive-dependent count to the page
|
|
135
|
+
* it is rendering, so an N-item page costs N bounded walks rather than N walks
|
|
136
|
+
* over the whole `blocks` graph. A single `get` (which calls
|
|
137
|
+
* {@link assembleIssueCard} directly, not this function) leaves the walk
|
|
138
|
+
* unbounded but cycle-safe. `scope` is inert unless `fields` requests
|
|
139
|
+
* `dependents`. */
|
|
140
|
+
export declare function assembleIssueCards(graph: GraphBackend, issues: NodeRecord[], fields: readonly IIssueField[], scoreByUid?: ReadonlyMap<string, number>, scoreKindByUid?: ReadonlyMap<string, IScoreKind>, verdictOpts?: {
|
|
141
|
+
rung?: IVerdictRung;
|
|
142
|
+
onRung?: (rung: number) => void;
|
|
143
|
+
}): Promise<IIssueCard[]>;
|
package/query/get.d.ts
CHANGED
|
@@ -5,7 +5,14 @@ import { GraphBackend } from '@adhd/sox-graph-store';
|
|
|
5
5
|
* Fetch one issue by `uid`, projected to the requested `fields` (default:
|
|
6
6
|
* the same five-field card `query` defaults to, SPEC.md §6.3.1/§6.5/AC-13).
|
|
7
7
|
*
|
|
8
|
+
* `lastN`/`after` bound the sub-collection pseudo fields (`auditTrail`/
|
|
9
|
+
* `citations`/`related`/`blockers`) — `lastN` keeps the LAST N rows in each
|
|
10
|
+
* resolver's own order (the audit trail's oldest-first order is sliced to its
|
|
11
|
+
* newest tail), `after` resumes strictly after a uid. Both are opt-in; absent
|
|
12
|
+
* means the unbounded behavior every existing caller already had.
|
|
13
|
+
*
|
|
8
14
|
* Errors: `IssueNotFoundError(uid)` (no live node carries `uid`),
|
|
9
|
-
* `BacklogValidationError('fields', ...)` (an unknown field name
|
|
15
|
+
* `BacklogValidationError('fields'|'lastN', ...)` (an unknown field name, or
|
|
16
|
+
* a non-positive/non-integer `lastN`).
|
|
10
17
|
*/
|
|
11
18
|
export declare function getIssue(graph: GraphBackend, input: IIssueGetByUidInput): Promise<IIssueCard>;
|
package/query/index.d.ts
CHANGED
package/query/query.d.ts
CHANGED
|
@@ -62,6 +62,13 @@ export interface IQueryStoreHandle {
|
|
|
62
62
|
* same way, so neither ordinary path is gated.)
|
|
63
63
|
*/
|
|
64
64
|
readonly assertCatalogInvariants?: () => Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* C6 test instrumentation — invoked with each verdict ladder rung the list
|
|
67
|
+
* path actually evaluates. Never consulted by production callers; wired only
|
|
68
|
+
* so the N-item list bound (AC6) can be MEASURED, not asserted. The list path
|
|
69
|
+
* must never evaluate beyond rungs 1–2.
|
|
70
|
+
*/
|
|
71
|
+
readonly onVerdictRung?: (rung: number) => void;
|
|
65
72
|
}
|
|
66
73
|
/**
|
|
67
74
|
* Whether a `query` verb's own input touches the semantic channel at all —
|
|
@@ -109,19 +116,19 @@ export declare function resolveTextInput(handle: IQueryStoreHandle, input: IIssu
|
|
|
109
116
|
export interface IQueryIssuesOutcome {
|
|
110
117
|
result: IIssueQueryResult;
|
|
111
118
|
/**
|
|
112
|
-
* Present
|
|
113
|
-
* filtered
|
|
114
|
-
* (`envelope.ts`'s {@link IQueryEnvelopeMeta}).
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* `
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
119
|
+
* Present for the four ITEM-LIST views (`list`/`ready`/`stale`/`similar`) —
|
|
120
|
+
* the only views whose result is a filtered row set with an honestly
|
|
121
|
+
* reportable completeness flag (`envelope.ts`'s {@link IQueryEnvelopeMeta}).
|
|
122
|
+
*
|
|
123
|
+
* `list` reports an exact pre-limit `total`; the other three derive `has_more`
|
|
124
|
+
* by fetching one row beyond the page and, when more exist, report `total`
|
|
125
|
+
* with `total_relation:'gte'` — an honest lower bound, never a fabricated
|
|
126
|
+
* exact number (DESIGN §2 Invariant 5, §7 condition 2). `graph`/`order`/
|
|
127
|
+
* `overlap` deliberately carry NO `meta`: they are a graph, a topological
|
|
128
|
+
* order, and an axis grouping — none a filtered row set with a "how many
|
|
129
|
+
* matched" count. Adding a `meta` to any of those would mean inventing a
|
|
130
|
+
* number this module cannot stand behind (see `meta-wire.e2e.ts`, whose
|
|
131
|
+
* `view:'graph'`-has-no-`meta` assertion is load-bearing).
|
|
125
132
|
*/
|
|
126
133
|
meta?: IQueryEnvelopeMeta;
|
|
127
134
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { GraphBackend, NodeRecord } from '@adhd/sox-graph-store';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The stored shape of a soft-retired row's redirect. `toUid` is the canonical
|
|
5
|
+
* live node the retired row points at; `reason` is the free-text explanation
|
|
6
|
+
* recorded on the retired node's audit trail (and, when set, its
|
|
7
|
+
* `meta.retiredReason`). Kept as a named contract so the merge writer and any
|
|
8
|
+
* future reader share one shape rather than re-deriving it from `meta`.
|
|
9
|
+
*/
|
|
10
|
+
export interface IRedirect {
|
|
11
|
+
/** The canonical uid this retired row points at. */
|
|
12
|
+
toUid: string;
|
|
13
|
+
/** Why the row was retired — recorded on the retired node's audit + meta. */
|
|
14
|
+
reason: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* If `record` is a soft-retired row carrying `meta.redirectTo`, return the
|
|
18
|
+
* canonical live node it points at (one hop only; a redirect→redirect chain
|
|
19
|
+
* longer than one hop throws). Otherwise `null`.
|
|
20
|
+
*
|
|
21
|
+
* `record` may itself be invalidated (`t_invalid` set) — that is the normal
|
|
22
|
+
* case, since retiring a row is what gives it a redirect. The TARGET, by
|
|
23
|
+
* contrast, must be a live node: a redirect into a tombstone is a broken
|
|
24
|
+
* invariant, not an unresolved name, so it throws rather than degrading to
|
|
25
|
+
* `null` (which a caller would misread as "this token leads nowhere").
|
|
26
|
+
*
|
|
27
|
+
* @throws InvalidArgumentError when the redirect target is missing/retired, or
|
|
28
|
+
* when the target is itself a redirect (a chain longer than one hop).
|
|
29
|
+
*/
|
|
30
|
+
export declare function followRedirect(graph: GraphBackend, record: NodeRecord): Promise<NodeRecord | null>;
|
package/query/resolve.d.ts
CHANGED
|
@@ -44,6 +44,54 @@ export declare function tryResolveUidPrefix(graph: GraphBackend, ref: string, op
|
|
|
44
44
|
* {@link StaleSupersedeError} pointing at the chain head.
|
|
45
45
|
*/
|
|
46
46
|
export declare function resolveIssueByUid(graph: GraphBackend, uid: string): Promise<NodeRecord>;
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a token to the LOGICAL node it names TODAY: follow a one-hop merge
|
|
49
|
+
* redirect (C1's soft-retired project/component rows), then walk the
|
|
50
|
+
* `SUPERSEDES` chain to its head. NEVER throws on a superseded uid — that is
|
|
51
|
+
* the whole point of the split from {@link resolveIssueByUid}, which throws
|
|
52
|
+
* `StaleSupersedeError` because its callers (`get`, `card` blockers, `stats`)
|
|
53
|
+
* want to be told a reference is stale, not silently forwarded.
|
|
54
|
+
*
|
|
55
|
+
* This is the non-throwing resolver C3's attestation `subject.id` uses: an
|
|
56
|
+
* attestation written against a uid that a later body-edit superseded must
|
|
57
|
+
* still name the SAME logical item, and a caller holding a stale citation
|
|
58
|
+
* wants today's issue, not the next-oldest corpse.
|
|
59
|
+
*
|
|
60
|
+
* Delegates the chain walk to {@link currentUidOf} rather than re-implementing
|
|
61
|
+
* it (ADR-0002 — one concept, one implementation). Returns the head
|
|
62
|
+
* `NodeRecord`; when the chain dead-ends with no live successor it returns the
|
|
63
|
+
* node it started from, which is the most useful answer available.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveLogicalIssue(graph: GraphBackend, ref: string): Promise<NodeRecord>;
|
|
66
|
+
/**
|
|
67
|
+
* Normalise any issue uid (possibly superseded) to its SUPERSEDES-chain HEAD
|
|
68
|
+
* uid. Returns the input unchanged for an already-live uid.
|
|
69
|
+
*
|
|
70
|
+
* ONE concept, ONE implementation: this delegates to {@link resolveLogicalIssue}
|
|
71
|
+
* (which returns the head `NodeRecord`) — it is NOT a second chain walk
|
|
72
|
+
* (ADR-0002). Used by C3's attestation read path so an attestation filed
|
|
73
|
+
* against a uid a later body edit superseded still names the SAME logical item.
|
|
74
|
+
*/
|
|
75
|
+
export declare function resolveLogicalIssueId(graph: GraphBackend, uid: string): Promise<string>;
|
|
76
|
+
/**
|
|
77
|
+
* Walk the `SUPERSEDES` chain forward from a superseded node to the uid the
|
|
78
|
+
* issue lives under NOW.
|
|
79
|
+
*
|
|
80
|
+
* `update`'s body path mints a new node and writes `SUPERSEDES` new→old, so
|
|
81
|
+
* the successor of a node is the `src` of the edge whose `dst` is that node —
|
|
82
|
+
* the reverse of the direction the edge reads. Repeated edits build a chain,
|
|
83
|
+
* and a uid cited before several edits is several hops back, so this follows
|
|
84
|
+
* the chain to its head rather than stopping at the first hop: a caller
|
|
85
|
+
* holding a stale citation wants today's issue, not the next-oldest corpse.
|
|
86
|
+
*
|
|
87
|
+
* Defensive, because this runs on an error path that must not itself throw:
|
|
88
|
+
* a cycle (impossible by construction, since each edit mints a fresh node,
|
|
89
|
+
* but not enforced by a constraint) is bounded by `seen`; a missing or
|
|
90
|
+
* invalidated successor ends the walk and yields the last good uid, and a
|
|
91
|
+
* chain that dead-ends immediately yields `undefined` — which
|
|
92
|
+
* {@link StaleSupersedeError} documents as "not known here".
|
|
93
|
+
*/
|
|
94
|
+
export declare function currentUidOf(graph: GraphBackend, superseded: NodeRecord): Promise<string | undefined>;
|
|
47
95
|
/**
|
|
48
96
|
* Resolve `ref` (uid or name, disambiguated by shape — SPEC.md §6.1) against
|
|
49
97
|
* `expectedKind`, on the READ path: an unresolved NAME is never an error here
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IIssueQueryInput, ISimilarViewClusterBlock } from './types.js';
|
|
2
|
+
import { ISimilarityScanHandle } from '../write/similarity-scan.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Build the additive `view:'similar'` cluster block, or `undefined` when the
|
|
6
|
+
* query is not scope-filtered. Never writes.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildSimilarClusterBlock(handle: ISimilarityScanHandle, input: IIssueQueryInput): Promise<ISimilarViewClusterBlock | undefined>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* similarity-signals.ts — the PURE multi-signal comparator the C9
|
|
3
|
+
* cross-project similarity guard is built on (C9 spec, `similarity-signals.ts`
|
|
4
|
+
* row). No store access, no I/O: two independent signals over plain values, so
|
|
5
|
+
* the guard is unit-testable in isolation and the scan (`write/similarity-scan.ts`)
|
|
6
|
+
* stays a thin composition of this plus the raw vector channel.
|
|
7
|
+
*
|
|
8
|
+
* Why a SECOND signal at all (AC7): cosine alone is never sufficient
|
|
9
|
+
* cross-project. Two boilerplate filings from unrelated repos can sit at high
|
|
10
|
+
* cosine while sharing no real intent — the title-token Jaccard and the
|
|
11
|
+
* structural (citation / component-path) overlap are the independent evidence
|
|
12
|
+
* that keeps such a pair advisory-invisible.
|
|
13
|
+
*/
|
|
14
|
+
/** The plain, store-free view of one item a signal comparison needs. */
|
|
15
|
+
export interface ISignalItem {
|
|
16
|
+
title: string;
|
|
17
|
+
body?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Tokens drawn from the item's citations — `file`/`symbol`/`errorText`/
|
|
20
|
+
* `blastRadius`. Callers pass whatever citation strings they have; this
|
|
21
|
+
* module tokenizes them.
|
|
22
|
+
*/
|
|
23
|
+
citationTokens?: readonly string[];
|
|
24
|
+
/** The owning component's `meta.path` (repo-relative), when known. */
|
|
25
|
+
componentPath?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Lowercase, split on every non-alphanumeric run, and drop stop words — the
|
|
29
|
+
* token stream {@link titleTokenOverlap} and {@link sharedStructuralSignal}
|
|
30
|
+
* both compare over, so the two signals can never disagree on what a "token"
|
|
31
|
+
* is.
|
|
32
|
+
*/
|
|
33
|
+
export declare function normalizeTokens(text: string): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Jaccard overlap of two title token SETS: `|A∩B| / |A∪B|`, in `[0,1]`.
|
|
36
|
+
* Two empty (or all-stop-word) titles have no overlap to speak of and score
|
|
37
|
+
* `0`, never `1` — "nothing in common" must not read as "identical".
|
|
38
|
+
*/
|
|
39
|
+
export declare function titleTokenOverlap(a: string, b: string): number;
|
|
40
|
+
/**
|
|
41
|
+
* The second independent signal for a cross-project candidate: does the pair
|
|
42
|
+
* share a citation token (`file`/`symbol`/`errorText`/`blastRadius`) OR do
|
|
43
|
+
* their component paths nest (one is a path-prefix of the other — e.g.
|
|
44
|
+
* `packages/agent` and `packages/agent/agent-engine-compiler`)? Either is
|
|
45
|
+
* structural evidence of a real shared locus, independent of the cosine.
|
|
46
|
+
*/
|
|
47
|
+
export declare function sharedStructuralSignal(a: ISignalItem, b: ISignalItem): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { GraphBackend } from '@adhd/sox-graph-store';
|
|
2
|
+
|
|
3
|
+
export type SpecFreshness = 'fresh' | 'stale' | 'unknown';
|
|
4
|
+
export interface ISpecCheckInput {
|
|
5
|
+
uid: string;
|
|
6
|
+
token?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ISpecCheckOutcome {
|
|
9
|
+
current_revision: string;
|
|
10
|
+
/** `'sha256:<hex>'` — always this encoding. */
|
|
11
|
+
current_token: string;
|
|
12
|
+
state: SpecFreshness;
|
|
13
|
+
method: 'token' | 'content_hash' | 'ancestry' | 'none';
|
|
14
|
+
reason?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Compare the caller's `token` against the ticket's current revision token and
|
|
18
|
+
* report the freshness ladder's verdict. See the module header for the rules.
|
|
19
|
+
* `uid` is the ticket (any uid on its `SUPERSEDES` chain is resolved forward).
|
|
20
|
+
*/
|
|
21
|
+
export declare function checkSpecStaleness(graph: GraphBackend, input: ISpecCheckInput): Promise<ISpecCheckOutcome>;
|