@adhd/backlog 0.1.9 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +128 -47
  2. package/README.md +369 -81
  3. package/api.d.ts +196 -0
  4. package/api.ir.json +1 -0
  5. package/cli.d.ts +45 -18
  6. package/env.d.ts +23 -3
  7. package/envelope.d.ts +163 -0
  8. package/extract-live.d.ts +56 -0
  9. package/index.d.ts +11 -10
  10. package/index.js +255 -197
  11. package/index.mjs +11348 -19043
  12. package/install-skill.d.ts +23 -0
  13. package/ir-artifact.d.ts +86 -0
  14. package/package.json +51 -15
  15. package/query/card.d.ts +31 -0
  16. package/query/get.d.ts +11 -0
  17. package/query/index.d.ts +67 -0
  18. package/query/markdown.d.ts +11 -0
  19. package/query/query.d.ts +131 -0
  20. package/query/resolve.d.ts +123 -0
  21. package/query/types.d.ts +450 -0
  22. package/query/views/registry.d.ts +43 -0
  23. package/query/views/semantic.d.ts +101 -0
  24. package/query/views/stats.d.ts +109 -0
  25. package/search-shortcut.d.ts +79 -0
  26. package/serve.d.ts +18 -0
  27. package/server.d.ts +139 -4
  28. package/skill/SKILL.md +632 -138
  29. package/store/graph-backlog-store.d.ts +80 -17
  30. package/store/immediate-retry.d.ts +24 -13
  31. package/store/type-policy.d.ts +4 -0
  32. package/store/vocabulary-guard.d.ts +52 -0
  33. package/write/audit.d.ts +36 -0
  34. package/write/bootstrap.d.ts +161 -0
  35. package/write/catalog.d.ts +369 -0
  36. package/write/citation-path.d.ts +133 -0
  37. package/write/claim-lease.d.ts +21 -0
  38. package/write/claim.d.ts +80 -0
  39. package/write/create-issue.d.ts +253 -0
  40. package/write/delete.d.ts +39 -0
  41. package/write/embed-drain.d.ts +68 -0
  42. package/write/embedding-config.d.ts +81 -0
  43. package/write/embedding-observer.d.ts +80 -0
  44. package/write/errors.d.ts +365 -0
  45. package/write/issue-status.d.ts +10 -0
  46. package/write/move.d.ts +70 -0
  47. package/write/relate.d.ts +52 -0
  48. package/write/transition.d.ts +64 -0
  49. package/write/tx.d.ts +344 -0
  50. package/write/update.d.ts +81 -0
  51. package/client.d.ts +0 -174
  52. package/markdown.d.ts +0 -75
  53. package/migration-admin.d.ts +0 -26
  54. package/model.d.ts +0 -437
  55. package/store/audit-log.d.ts +0 -16
  56. package/store/claim.d.ts +0 -24
  57. package/store/crud.d.ts +0 -62
  58. package/store/ids.d.ts +0 -24
  59. package/store/lifecycle.d.ts +0 -36
  60. package/store/mapping.d.ts +0 -101
  61. package/store/mutate-metadata.d.ts +0 -8
  62. package/store/query.d.ts +0 -68
  63. package/store/repo-migration.d.ts +0 -51
  64. package/store/serve-lock.d.ts +0 -42
  65. package/store/structure.d.ts +0 -66
@@ -0,0 +1,109 @@
1
+ import { IQueryStoreHandle } from '../query.js';
2
+ import { IIssueFilter } from '../types.js';
3
+
4
+ export interface IPriorityMatrixRow {
5
+ priority: string;
6
+ priorityUid: string;
7
+ /** `priority.meta.metadata.rank` (`nextPriorityRankTx`, §2) — lower is more urgent. Absent only for a malformed pre-existing row with no numeric rank; the count itself is still real. */
8
+ rank?: number;
9
+ count: number;
10
+ }
11
+ export interface IPriorityMatrixResult {
12
+ /** Sorted by `rank` ascending (a missing rank sorts last), mirroring `query.ts`'s own `sortByPriorityRank` convention. */
13
+ rows: IPriorityMatrixRow[];
14
+ /** In-scope issues carrying no live `has_priority` edge at all — never silently folded into a phantom priority row, never silently dropped from the total. */
15
+ unassigned: number;
16
+ /** The status scope actually applied. `'open'` when `filter?.status` was omitted (BUG-023's fix) — echoed here so a caller can never mistake a default-scoped result for an all-status one. */
17
+ statusScope: NonNullable<IIssueFilter['status']>;
18
+ }
19
+ export interface IPriorityMatrixInput {
20
+ /** `project`/`component`/`kind`/`status` only (§6.5 rule 3's edge-scoped dimensions, restricted to what a priority breakdown composes with) — any other key throws. */
21
+ filter?: IIssueFilter;
22
+ }
23
+ /** SPEC.md §5's status-aware priority matrix (BUG-023). */
24
+ export declare function priorityMatrix(handle: IQueryStoreHandle, input?: IPriorityMatrixInput): Promise<IPriorityMatrixResult>;
25
+ export interface IPartOfRollupInput {
26
+ /** The root issue's `uid` (§6.1). Throws `IssueNotFoundError` if it does not resolve to a live issue. */
27
+ uid: string;
28
+ }
29
+ export interface IPartOfRollupResult {
30
+ uid: string;
31
+ /** Every TRANSITIVE descendant via `part_of` (issue → issue, `n:1`, §3) — not just direct children. Counted exactly once each, regardless of chain depth. */
32
+ childrenTotal: number;
33
+ childrenOpen: number;
34
+ childrenClosed: number;
35
+ /** `uid`s of the still-open descendants — the actionable half, mirroring the `IIssueRef`-shaped convention `card.ts`'s `blockers`/`related` already use. */
36
+ childrenOpenUids: readonly string[];
37
+ }
38
+ /**
39
+ * SPEC.md §5's `part_of` + derived two-axis rollup (FEAT-005), realized as
40
+ * ONE axis here — children-open/closed over the FULL transitive subtree
41
+ * (the second axis, "does THIS item carry its own closing evidence," is a
42
+ * `get`/card-assembly concern over the root's own citations, out of scope
43
+ * for a rollup over its CHILDREN).
44
+ *
45
+ * Uses `getSubgraph(rootId, {rel:'part_of', direction:'in', depth:-1})` —
46
+ * the unbounded walk (verified: `getNeighbors`'s `depth` is a row BUDGET,
47
+ * not a depth bound, per `@adhd/sox-graph-store`'s own dist comment; only
48
+ * `getSubgraph`'s `-1` sentinel gives a genuine unbounded multi-level walk)
49
+ * with its own internal visited set, so a grandchild (or deeper) reached
50
+ * through exactly one path is collected into the SAME node set as a direct
51
+ * child — counted once, never re-added by also summing each child's own
52
+ * subtree size on top of a direct-children count. `getSubgraph` does NOT
53
+ * filter node liveness (documented on the library's own `getSubgraphIterative`:
54
+ * "an invalidated node behind a live edge IS part of the subgraph") — a
55
+ * soft-deleted descendant is explicitly filtered back out below, since a
56
+ * deleted issue is neither open work nor closed work, it is gone.
57
+ */
58
+ export declare function partOfRollup(handle: IQueryStoreHandle, input: IPartOfRollupInput): Promise<IPartOfRollupResult>;
59
+ export interface IOpenCurveInput {
60
+ /** `project`/`component`/`kind` only — `status` is rejected (`InvalidArgumentError`): this view computes openness itself, per sampled instant, so a caller-given status filter would silently conflict with that computation rather than compose with it. */
61
+ filter?: IIssueFilter;
62
+ /** ISO-8601 instants to sample, in the given order. Must be non-empty; each is parsed via `Date.parse` and normalized to a full ISO string for lexical comparison against stored `at` timestamps — an unparsable entry throws by name rather than silently producing `NaN`-derived output. */
63
+ at: readonly string[];
64
+ }
65
+ export interface IOpenCurvePoint {
66
+ at: string;
67
+ /** In-scope issues that EXISTED at this instant (`NodeFilter.validAt`: `t_created <= at` and `t_invalid` is null or after `at`) — exact, never reconstructed. */
68
+ existed: number;
69
+ /** Of `existed`, the count whose RECONSTRUCTED status was non-terminal at this instant (never the issue's CURRENT status) — see {@link reconstructStatusAt}. */
70
+ open: number;
71
+ /** `existed - open`. */
72
+ closed: number;
73
+ }
74
+ export interface IOpenCurveResult {
75
+ points: readonly IOpenCurvePoint[];
76
+ }
77
+ /**
78
+ * SPEC.md §5's `validAt`-driven cumulative-open curve.
79
+ *
80
+ * **Cost is bounded by the sampled-instant count and the size of the
81
+ * `has_status`/`audits` relations, never by (instants × existing-issue
82
+ * count).** The obvious shape — for each sampled instant, fetch the issues
83
+ * that existed then, per issue, fetch its current-status edge, its
84
+ * current-status node, and its full audit trail — is up to four sequential
85
+ * round trips PER ISSUE PER INSTANT. Ten sampled instants over a 5,000-issue
86
+ * store cost on the order of 200,000 serialized queries under that shape,
87
+ * even though nothing about "how open was the store on these ten dates"
88
+ * scales with store size on its own — it should scale with the number of
89
+ * dates the caller asked about. Worse, an issue's CURRENT status and full
90
+ * audit trail don't depend on `at` at all (`reconstructStatusAt` only reads
91
+ * them; it never asks the backend anything itself), so the naive per-instant
92
+ * loop was refetching the exact same unchanging per-issue data once per
93
+ * instant for nothing.
94
+ *
95
+ * Instead, both relations are fetched ONCE, before the instant loop, and
96
+ * grouped in memory — the same shape `query.ts`'s `queryReady` already uses
97
+ * for `blocks`/`has_status`: `getEdges({rel:'has_status'})` and
98
+ * `getEdges({rel:'audits'})` each take the WHOLE relation in a single call
99
+ * (there is no batch-by-many-`src` primitive — `getEdges` accepts only one
100
+ * `src`/`dst` at a time, which is exactly why fetching the whole relation
101
+ * once is the right move), plus one `getNodesByIds` each to resolve the
102
+ * status/audit-entry nodes those edges point at — see
103
+ * {@link resolveCurrentStatusAndAuditTrails}. That's four queries total for
104
+ * this cost, however many issues or instants are involved; the only
105
+ * per-instant cost left is the one `queryNodes({validAt})` call SPEC.md's own
106
+ * existence check requires. Total round trips: `4 + M` for `M` sampled
107
+ * instants — independent of N, the number of issues in the store.
108
+ */
109
+ export declare function openCurve(handle: IQueryStoreHandle, input: IOpenCurveInput): Promise<IOpenCurveResult>;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * search-shortcut.ts — `backlog search "<text>" [flags]`, a pure ARGV
3
+ * TRANSLATION onto the mounted `query` operation.
4
+ *
5
+ * ## Why a translation and not a seventh operation
6
+ *
7
+ * The apigen mount-surface spec §3 fixes the apigen mount surface at exactly six verbs
8
+ * (`get, query, create, update, relate, admin`) — `index.ts`'s own comment
9
+ * above those exports says "these, and only these". Adding a `search` export
10
+ * to `client.ts` would widen that surface for every transport (CLI, HTTP,
11
+ * MCP) to buy CLI ergonomics, which is the wrong trade. So `search` never
12
+ * becomes an operation: `runBacklogCli` rewrites its argv into
13
+ * `['query', '--input', '<json>']` and hands it to the SAME
14
+ * `@adhd/apigen-plugin-cli-output` dispatch every other command goes
15
+ * through. Store lifecycle, signal cleanup, `exitCodeForEnvelope` mapping and
16
+ * output shape are therefore reused unchanged rather than reimplemented —
17
+ * this module is a pure function over `string[]` and opens nothing.
18
+ *
19
+ * ## Why the positional compiles to `text`, not `filter.semantic`
20
+ *
21
+ * `IIssueQueryInput.text` is already specified as "the natural-language
22
+ * query; also the CLI positional form" (§2.1b) — this shortcut is the CLI
23
+ * form that field was written for. `resolveTextInput` (query/query.ts, called
24
+ * once from `queryIssues`) routes the whole string into `filter.semantic`
25
+ * when the vector space is readable and into `filter.grep` when it is NOT
26
+ * (RAG-SPEC §3.1 / BUG-045), and picks `sort: "relevance"` or `"textMatch"`
27
+ * to match. Compiling the positional straight to `filter.semantic` instead
28
+ * would hard-fail on an unconfigured or unbackfilled store, throwing away a
29
+ * working keyword answer — and would ALSO need this module to hardcode a
30
+ * `sort` default that `resolveTextInput` already derives correctly. Neither
31
+ * divergence is worth owning here.
32
+ *
33
+ * For the same reason there is no `fields` default: the `text` path's own
34
+ * projection is already the compact `uid/kind/title/status/priority`
35
+ * list (`DEFAULT_ISSUE_CARD_FIELDS`, query/types.ts), so a default invented
36
+ * here could only make it worse. `--fields` overrides it (add `_score` to
37
+ * see the ranking scores).
38
+ *
39
+ * ## Error parity
40
+ *
41
+ * The flags below are hand-parsed, not schema-derived, so every rejection
42
+ * mirrors `@adhd/apigen-plugin-cli-output`'s `parseArgs`/validate-Layer
43
+ * wording byte-for-byte: `Unknown option: --X. Available: …` (a REAL list, never a placebo),
44
+ * `Missing value for --X`, `Unexpected positional argument: "X"`. The caller
45
+ * prints them as `{"code":"invalid_argument","message":…}` on stderr with
46
+ * `process.exitCode = 2` (`CLI_EXIT_CODE['invalid_argument']`).
47
+ *
48
+ * DEBT-BACKLOG-001 (the backlog CLI's bespoke argv parsing diverging from the
49
+ * apigen-mounted surface) is enlarged by this file, deliberately and with the
50
+ * parity discipline above; see that item.
51
+ */
52
+ /**
53
+ * The complete, REAL flag vocabulary — the `Available:` list an unknown-option
54
+ * rejection prints. Derived from the four arrays above rather than
55
+ * hand-maintained, so a flag can never be accepted but unlisted (or listed
56
+ * but unaccepted).
57
+ */
58
+ export declare const SEARCH_FLAGS: readonly string[];
59
+ export declare const SEARCH_HELP: string;
60
+ /** A rendered `--help`, a rejection, or the rewritten argv to dispatch. */
61
+ export type SearchShortcutOutcome = {
62
+ kind: 'help';
63
+ text: string;
64
+ } | {
65
+ kind: 'error';
66
+ message: string;
67
+ } | {
68
+ kind: 'argv';
69
+ argv: string[];
70
+ };
71
+ /**
72
+ * Translates `search`'s tokens (everything AFTER the `search` word) into the
73
+ * `['query', '--input', '<json>']` argv the apigen command table dispatches.
74
+ *
75
+ * Pure: reads no config, opens no store, touches no globals — so the caller
76
+ * can reject a bad invocation before any of `runBacklogCli`'s store lifecycle
77
+ * has started, and so every branch here is unit-testable on its own.
78
+ */
79
+ export declare function buildSearchArgv(rest: readonly string[]): SearchShortcutOutcome;
package/serve.d.ts CHANGED
@@ -5,7 +5,25 @@ export interface RunServeCommandOpts {
5
5
  /** Test-only override — see `buildBacklogEnv`'s `BuildBacklogEnvOptions`. */
6
6
  adhdRoot?: string;
7
7
  cwd?: string;
8
+ /** Explicit-parameter-first namespace override — see
9
+ * `BuildBacklogEnvOptions.namespace`'s doc comment. `--namespace sandbox
10
+ * serve` (cli.ts) sets this to `'sandbox'`, along with minting a fresh
11
+ * `adhdRoot` and writing D8's sandbox `config.yaml`. */
12
+ namespace?: string;
8
13
  }
14
+ /**
15
+ * BUG-033: `backlog serve --help` used to throw a plain `Error` from
16
+ * `parseArgs` (unknown-argument), which `runServeCommand` neither caught nor
17
+ * short-circuited — the error propagated all the way to `index.ts`'s
18
+ * bin-entry guard, which prints `err.stack` unconditionally, so a completely
19
+ * ordinary "show me the help" request rendered as ten frames of minified
20
+ * dist. Mirrors `install.ts`'s established `--help` + `BacklogUsageError` +
21
+ * `failUsage` pattern exactly: `--help`/`-h` anywhere in argv short-circuits
22
+ * BEFORE parsing, and every argument-parsing failure is a `BacklogUsageError`
23
+ * (never a bare `Error`) so `runServeCommand` can catch it and hand it to
24
+ * `failUsage` instead of letting it reach the bin guard's stack-trace path.
25
+ */
26
+ export declare const SERVE_HELP_TEXT = "backlog serve [--transport mcp|http|both] [--port N] [--host H]\n\nStarts the long-lived backlog server (MCP and/or HTTP), matching one of the\nprocess's own configured transports to the way an agent host or a script\nexpects to reach it.\n\n --transport <name> mcp | http | both (default: mcp)\n --port <N> HTTP listen port (default: 3300; ignored for mcp-only)\n --host <name> HTTP listen host (default: 127.0.0.1; ignored for mcp-only)\n\nExamples:\n backlog serve\n backlog serve --transport http --port 3300\n backlog serve --transport both --host 0.0.0.0\n";
9
27
  /** Runs until the process receives SIGTERM/SIGINT (the normal way a host
10
28
  * process manager — or `.mcp.json`'s own stdio transport lifecycle — stops
11
29
  * a long-lived MCP/HTTP server), then resolves cleanly. */
package/server.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { BacklogCtx } from './client.js';
2
- import { composeSchemas, Operation, Logger, OutputPlugin, RunInput } from '@adhd/apigen-core-client';
1
+ import { BacklogCtx } from './api.js';
2
+ import { HttpVerb } from '@adhd/apigen-engine-naming';
3
+ import { composeSchemas, Operation, Plugin, Logger, OutputPlugin, RunInput } from '@adhd/apigen-core-client';
3
4
  import { Scope } from '@adhd/environment-base-spec';
4
5
 
5
6
  /**
@@ -37,6 +38,117 @@ export declare function requireRun(plugin: OutputPlugin): (input: RunInput) => P
37
38
  * `undefined` and the real pino default logger is used, unchanged.
38
39
  */
39
40
  export declare function testSilentLogger(): Logger | undefined;
41
+ /**
42
+ * SPEC.md §6.6's "host-command carve-out" — the commands that are
43
+ * deliberately NOT part of the mounted data surface, pinned as a value so the
44
+ * refusal is enforceable rather than prose.
45
+ *
46
+ * `install`/`install-skill` are pure filesystem/config operations that must
47
+ * never open the store (cli.ts:243-256 special-cases them BEFORE the apigen
48
+ * command table is ever built — that is what closes
49
+ * DEBT-BACKLOG-CLI-EAGER-STORE-OPEN-001), and `serve` is a long-lived
50
+ * listener launcher with a completely different lifecycle (cli.ts:262-265).
51
+ * Neither is a data op, so neither may ever appear as an apigen operation on
52
+ * ANY of the four mounts.
53
+ *
54
+ * `assertHostCarveOut` turns §6.6's negative assertion ("`install`/`serve` are
55
+ * NOT among the mounted verbs") into a mount-time invariant: a future refactor that
56
+ * accidentally exports a host command from the mounted client module fails at
57
+ * `buildBacklogApigenPackage()` — in every transport at once — instead of
58
+ * silently shipping a `backlog_serve` MCP tool that would open a second
59
+ * concurrent writer against the store via a completely different lifecycle
60
+ * path than the one `startBacklogServer` itself expects.
61
+ */
62
+ export declare const BACKLOG_HOST_COMMANDS: readonly string[];
63
+ /**
64
+ * SPEC.md §6.7 — the data verbs the whole surface consolidates onto:
65
+ * the nine issue verbs plus `lookup`, §3a's registry CRUD verbs, and the
66
+ * three §5 stats/rollup reads (`priority-matrix`/`part-of-rollup`/
67
+ * `open-curve`), mounted as `backlog_<verb>`. Pinned here, next to the
68
+ * carve-out it is the complement of, because it is the ONE list four separate
69
+ * surfaces are
70
+ * checked against: the three apigen mounts derive their names from the
71
+ * operation descriptors via `describeMountedSurface`, and `cli.ts`'s argv
72
+ * parser — which is deliberately NOT an apigen mount, because apigen's
73
+ * `parseArgs` cannot express the §2.1b positional form, projects `string[]`
74
+ * as a JSON-valued flag where §7.3 wants comma-separated, and only sets
75
+ * `process.exitCode` on a thrown `ApiError` (so an `ok:false` envelope would
76
+ * exit 0, contradicting `BACKLOG_EXIT_CODE`) — has to be checked against this list rather than
77
+ * derived from the mount.
78
+ *
79
+ * That asymmetry is exactly how a split brain starts, and this repo already
80
+ * has one open as BUG-BACKLOG-MCP-CLI-SPLIT-BRAIN-001. `server.verbs.spec.ts`
81
+ * asserts BOTH sides against this constant so a verb added to one surface and
82
+ * forgotten on the other fails a test instead of shipping.
83
+ *
84
+ * Order is the SPEC.md §4/§6 declaration order, not alphabetical; compare as sets.
85
+ */
86
+ export declare const BACKLOG_VERBS: readonly string[];
87
+ /**
88
+ * One mounted operation, projected to all four transports backlog serves.
89
+ *
90
+ * Every field here is computed by `@adhd/apigen-engine-naming`'s `project()`
91
+ * — the SAME function `apigen-plugin-api-fastify` (`run.ts:214-215` via
92
+ * `routeFor`), `apigen-plugin-mcp` (tool registration), `apigen-plugin-cli-output`
93
+ * (command table) and `@adhd/apigen-codegen-openapi`'s `toOpenApi`
94
+ * (`to-openapi.ts:131` → `paths[route]`) each call independently. That shared
95
+ * projector is *why* the four surfaces agree: there is one operation
96
+ * descriptor list and one naming function, never a per-transport definition
97
+ * and never a hand-maintained OpenAPI document.
98
+ */
99
+ export interface IMountedOperationSurface {
100
+ /** Canonical apigen operation id (e.g. `backlog/get-item`). */
101
+ id: string;
102
+ /** CLI command as a human types it, e.g. `backlog get-item`. */
103
+ cliCommand: string;
104
+ /** CLI command segments, e.g. `['backlog','get-item']`. */
105
+ cliPath: string[];
106
+ /** MCP tool name as a host loads it, e.g. `backlog_get_item`. */
107
+ mcpTool: string;
108
+ /** HTTP verb the fastify mount registers. */
109
+ httpVerb: HttpVerb;
110
+ /** HTTP route the fastify mount registers AND the OpenAPI `paths` key. */
111
+ httpRoute: string;
112
+ }
113
+ /**
114
+ * Projects the extracted operation descriptors onto the four transports
115
+ * backlog mounts, returning the single expected surface.
116
+ *
117
+ * This is the mechanical statement of SPEC.md §6.7: *one* operation
118
+ * definition serves CLI, MCP, REST and OpenAPI. A test (or an operator) can
119
+ * call this once and compare it against what each live transport actually
120
+ * advertises; a divergence means some transport grew its own definition,
121
+ * which is precisely what §6.7 forbids.
122
+ *
123
+ * Only `kind: 'action'` operations are mounted — the same filter
124
+ * `buildBacklogApigenPackage` applies when it builds `generated.schemas`, so
125
+ * this never claims a surface the package does not actually compose.
126
+ *
127
+ * @param operations the descriptor list returned by `buildBacklogApigenPackage`
128
+ * @returns one entry per mounted operation, sorted by canonical id for stable
129
+ * comparison
130
+ */
131
+ export declare function describeMountedSurface(operations: readonly Operation[]): IMountedOperationSurface[];
132
+ /**
133
+ * Derives the FULL set of MCP tool names the real `serve --transport mcp`
134
+ * process advertises — every mounted `client.ts` verb (via
135
+ * `describeMountedSurface`'s `mcpTool`, itself `project(op).mcp.name`) PLUS
136
+ * every tool a mount plugin (currently just `apigen-plugin-batch`'s
137
+ * `batch_action`) contributes, using the exact same `project(op).mcp.name`
138
+ * derivation `cli.ts`'s `resolveMountNamespaces` uses for the CLI's own
139
+ * top-level segments.
140
+ *
141
+ * Exists so a test can assert against a LIVE-derived tool set instead of a
142
+ * hardcoded literal array/count — a hardcoded `['backlog_get', …]` (or a bare
143
+ * `.length === 7`) silently stops meaning anything the moment a `client.ts`
144
+ * export is added, renamed, or removed, and would then either falsely fail
145
+ * (a legitimate, intended surface change) or — worse — falsely pass (typo'd
146
+ * to match the wrong new count) with no signal that the assertion itself
147
+ * needs updating. Deriving it here, from the same `operations`/`usePlugins`
148
+ * every transport is actually built from, means the assertion tracks the
149
+ * shipped surface automatically.
150
+ */
151
+ export declare function resolveExpectedMcpToolNames(operations: readonly Operation[], usePlugins?: readonly Plugin[]): string[];
40
152
  export interface StartOpts {
41
153
  transport: 'http' | 'mcp' | 'both';
42
154
  port?: number;
@@ -46,6 +158,9 @@ export interface StartOpts {
46
158
  adhdRoot?: string;
47
159
  cwd?: string;
48
160
  signal: AbortSignal;
161
+ /** Explicit-parameter-first namespace override — see
162
+ * `BuildBacklogEnvOptions.namespace`'s doc comment. */
163
+ namespace?: string;
49
164
  }
50
165
  /**
51
166
  * Builds the composed, apigen-ready package descriptor for `client.ts`'s
@@ -56,18 +171,38 @@ export interface StartOpts {
56
171
  * when a dispatched command actually reaches the real function, which never
57
172
  * happens for `--help`/no-args/an unknown command. This is what closes
58
173
  * DEBT-BACKLOG-CLI-EAGER-STORE-OPEN-001: `operations`/`schemas` below are
59
- * computed purely from the built `client.d.ts` (via `extractClientOperations`)
174
+ * computed purely from the built `api.d.ts` (via `extractApiOperations`)
60
175
  * and never touch `ctx` at all, so a lazy caller can defer opening the real
61
176
  * backing store until a command that actually needs it is dispatched.
177
+ *
178
+ * @param opts.adhdRoot/instanceId BUG-BACKLOG-SANDBOX-IRCACHE-001 — forwarded
179
+ * verbatim to `extractApiOperations`/the IR-cache plugin, so a caller
180
+ * already isolating its real store via `adhdRoot` (`--namespace sandbox`, or any
181
+ * other test-isolation caller of `buildBacklogEnv`) gets the extract-stage
182
+ * IR cache isolated the SAME way, instead of it silently falling through
183
+ * to the real machine `HOME`. Optional and additive — every existing call
184
+ * site that omits it keeps its prior (real-`HOME`, shared-cache) behavior
185
+ * exactly.
62
186
  */
63
- export declare function buildBacklogApigenPackage(ctx: BacklogCtx | (() => BacklogCtx | Promise<BacklogCtx>)): Promise<{
187
+ export declare function buildBacklogApigenPackage(ctx: BacklogCtx | (() => BacklogCtx | Promise<BacklogCtx>), opts?: {
188
+ adhdRoot?: string;
189
+ instanceId?: string;
190
+ }): Promise<{
64
191
  pkg: {
65
192
  id: string;
193
+ version: string;
66
194
  schemas: ReturnType<typeof composeSchemas>;
67
195
  importPath: string;
68
196
  fns: Record<string, (...args: unknown[]) => unknown>;
69
197
  createClient: () => Promise<BacklogCtx>;
70
198
  };
199
+ /**
200
+ * §6.7: the four-transport projection of `operations`, computed once here
201
+ * so CLI, MCP, REST and OpenAPI are provably reading ONE definition. Callers
202
+ * that need to know "what is mounted" must read this rather than
203
+ * re-deriving names per transport.
204
+ */
205
+ surface: IMountedOperationSurface[];
71
206
  operations: Operation[];
72
207
  }>;
73
208
  /**