@adhd/backlog 1.0.4 → 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 (59) hide show
  1. package/CHANGELOG.md +69 -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 +36 -2
  7. package/index.d.ts +23 -3
  8. package/index.js +105 -57
  9. package/index.mjs +11450 -6758
  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 +32 -22
  18. package/query/redirect.d.ts +30 -0
  19. package/query/resolve.d.ts +75 -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 +13 -7
  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 +72 -2
  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 +196 -1
  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 +44 -2
  58. package/write/uid-prefix.d.ts +78 -0
  59. package/write/update.d.ts +23 -1
@@ -1,5 +1,6 @@
1
1
  import { IQueryStoreHandle } from '../query.js';
2
2
  import { IIssueFilter } from '../types.js';
3
+ import { GraphBackend, NodeRecord } from '@adhd/sox-graph-store';
3
4
 
4
5
  export interface IPriorityMatrixRow {
5
6
  priority: string;
@@ -20,11 +21,39 @@ export interface IPriorityMatrixInput {
20
21
  /** `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
22
  filter?: IIssueFilter;
22
23
  }
24
+ /**
25
+ * An `IIssueFilter`'s placement/kind/status dimensions resolved to the
26
+ * concrete set of LIVE, non-superseded issue nodes they match — the exact
27
+ * scoping `priorityMatrix` counts over. See
28
+ * {@link resolveScopedLiveIssueNodes}.
29
+ */
30
+ export interface IScopedLiveIssues {
31
+ nodes: NodeRecord[];
32
+ statusScope: NonNullable<IIssueFilter['status']>;
33
+ }
34
+ /**
35
+ * Resolve an `IIssueFilter`'s placement/kind/status dimensions to the concrete
36
+ * set of LIVE, non-superseded issue NODES they match — the exact scoping
37
+ * `priorityMatrix` (and, via it, `report`) counts over. Exported so `report`
38
+ * composes this ONE implementation rather than re-deriving it (ADR-0002).
39
+ *
40
+ * `nodes: []` (never `undefined`) when a placement dimension resolved to an
41
+ * empty set or the status scope matched nothing, so callers branch on one
42
+ * shape. `statusScope` always names the scope applied — `'open'` when the
43
+ * filter omitted `status`, mirroring `priorityMatrix`'s BUG-023 default.
44
+ */
45
+ export declare function resolveScopedLiveIssueNodes(graph: GraphBackend, filter: IIssueFilter | undefined): Promise<IScopedLiveIssues>;
23
46
  /** SPEC.md §5's status-aware priority matrix (BUG-023). */
24
47
  export declare function priorityMatrix(handle: IQueryStoreHandle, input?: IPriorityMatrixInput): Promise<IPriorityMatrixResult>;
25
48
  export interface IPartOfRollupInput {
26
49
  /** The root issue's `uid` (§6.1). Throws `IssueNotFoundError` if it does not resolve to a live issue. */
27
50
  uid: string;
51
+ /** Count-only mode: return the counts, omit the `childrenOpenUids` uid list entirely. */
52
+ countOnly?: boolean;
53
+ /** Max uids in `childrenOpenUids` when not count-only (default 50, max 1000). */
54
+ limit?: number;
55
+ /** Opaque cursor for the paged uid list — pass the previous page's `nextCursor`. */
56
+ after?: string;
28
57
  }
29
58
  export interface IPartOfRollupResult {
30
59
  uid: string;
@@ -32,9 +61,16 @@ export interface IPartOfRollupResult {
32
61
  childrenTotal: number;
33
62
  childrenOpen: number;
34
63
  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[];
64
+ /** `uid`s of the still-open descendants — the actionable half, mirroring the `IIssueRef`-shaped convention `card.ts`'s `blockers`/`related` already use. Absent in count-only mode, or present and bounded by `limit`. */
65
+ childrenOpenUids?: readonly string[];
66
+ /** Pass back as `after` for the next page; absent ⇒ no further open-descendant uids. */
67
+ nextCursor?: string;
68
+ /** True iff more open-descendant uids exist beyond this page. */
69
+ hasMore?: boolean;
37
70
  }
71
+ /** Default/maximum uid-list page size for {@link partOfRollup}'s non-count-only mode. */
72
+ export declare const DEFAULT_PART_OF_ROLLUP_LIMIT = 50;
73
+ export declare const MAX_PART_OF_ROLLUP_LIMIT = 1000;
38
74
  /**
39
75
  * SPEC.md §5's `part_of` + derived two-axis rollup (FEAT-005), realized as
40
76
  * ONE axis here — children-open/closed over the FULL transitive subtree
package/readiness.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { IServiceFailure } from './lifecycle.js';
2
+
3
+ export interface IReadinessTimer {
4
+ setTimeout(fn: () => void, ms: number): unknown;
5
+ clearTimeout(handle: unknown): void;
6
+ }
7
+ /** The minimal composed-server surface the probe needs. */
8
+ export interface IReadinessHandle {
9
+ pkg: {
10
+ fns: Record<string, (...args: unknown[]) => unknown>;
11
+ createClient: () => Promise<unknown>;
12
+ };
13
+ operations: readonly {
14
+ id: string;
15
+ }[];
16
+ store: unknown;
17
+ }
18
+ export interface IReadinessProbeOpts {
19
+ timeoutMs: number;
20
+ /** Injectable one-shot timer so a test uses a virtual clock, never sleep. */
21
+ timer?: IReadinessTimer;
22
+ /** Test seam: the exact serving-path call to drive. */
23
+ invoke?: () => Promise<unknown>;
24
+ }
25
+ export interface IReadinessResult {
26
+ ready: boolean;
27
+ failure?: IServiceFailure;
28
+ }
29
+ export declare function probeReadiness(handle: IReadinessHandle, opts: IReadinessProbeOpts): Promise<IReadinessResult>;
@@ -0,0 +1,44 @@
1
+ export interface IResiliencePolicy {
2
+ maxAttempts: number;
3
+ baseMs: number;
4
+ maxMs: number;
5
+ budgetMs: number;
6
+ breakerFailureThreshold: number;
7
+ breakerResetMs: number;
8
+ }
9
+ /** Full jitter: `rand() * min(maxMs, baseMs * 2**attempt)`. */
10
+ export declare function fullJitterDelay(attempt: number, p: Pick<IResiliencePolicy, 'baseMs' | 'maxMs'>, rand: () => number): number;
11
+ export type IBreakerState = 'closed' | 'open' | 'half-open';
12
+ export declare class CircuitBreaker {
13
+ private failures;
14
+ private openedAt;
15
+ private probing;
16
+ private readonly failureThreshold;
17
+ private readonly resetMs;
18
+ private readonly now;
19
+ constructor(p: {
20
+ failureThreshold: number;
21
+ resetMs: number;
22
+ now?: () => number;
23
+ });
24
+ state(): IBreakerState;
25
+ /** open → refuse (no attempt). closed/half-open → allow one. */
26
+ tryPass(): boolean;
27
+ /** → closed. */
28
+ onSuccess(): void;
29
+ /** → open after threshold (or immediately on a failed half-open probe). */
30
+ onFailure(): void;
31
+ }
32
+ export interface IResilienceDeps {
33
+ graceMs: number;
34
+ rand: () => number;
35
+ now: () => number;
36
+ sleep: (ms: number) => Promise<void>;
37
+ }
38
+ /**
39
+ * Wraps the connect/handshake of ONE call. Applies `graceMs` to the FIRST
40
+ * attempt's deadline (the cold-start window) BEFORE any retry, then retries
41
+ * with full jitter inside a total `budgetMs`; refuses immediately when the
42
+ * breaker is open. On budget exhaustion / open breaker → {@link ServiceNotReadyError}.
43
+ */
44
+ export declare function withResilience<T>(fn: (attempt: number, deadlineMs: number) => Promise<T>, p: IResiliencePolicy, breaker: CircuitBreaker, deps: IResilienceDeps): Promise<T>;
package/serve.d.ts CHANGED
@@ -23,7 +23,7 @@ export interface RunServeCommandOpts {
23
23
  * (never a bare `Error`) so `runServeCommand` can catch it and hand it to
24
24
  * `failUsage` instead of letting it reach the bin guard's stack-trace path.
25
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";
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: from ADHD_BACKLOG_SERVICE_TRANSPORT\n / the service.* config, else mcp)\n --port <N> HTTP listen port (default: from ADHD_BACKLOG_SERVICE_PORT\n / the service.* config, else 3300; ignored for mcp-only)\n --host <name> HTTP listen host (default: from ADHD_BACKLOG_SERVICE_HOST\n / the service.* config, else 127.0.0.1; ignored for mcp-only)\n --ready-file <path> Write the service report to <path> once the server is\n READY (the serving path answered), atomically, and\n again on every state change.\n --probe Run the serving-path readiness probe once, print the\n report as JSON, and exit 0 (ready) / 1 (not ready).\n\nExamples:\n backlog serve\n backlog serve --transport http --port 3300\n backlog serve --transport both --host 0.0.0.0\n backlog serve --transport mcp --ready-file /run/backlog/ready.json\n backlog serve --probe\n";
27
27
  /** Runs until the process receives SIGTERM/SIGINT (the normal way a host
28
28
  * process manager — or `.mcp.json`'s own stdio transport lifecycle — stops
29
29
  * a long-lived MCP/HTTP server), then resolves cleanly. */
package/server.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { IServiceReport } from './lifecycle.js';
2
+ import { BACKLOG_VERBS } from './vocabulary.js';
1
3
  import { BacklogCtx } from './api.js';
2
4
  import { HttpVerb } from '@adhd/apigen-engine-naming';
3
5
  import { composeSchemas, Operation, Plugin, Logger, OutputPlugin, RunInput } from '@adhd/apigen-core-client';
@@ -83,7 +85,7 @@ export declare const BACKLOG_HOST_COMMANDS: readonly string[];
83
85
  *
84
86
  * Order is the SPEC.md §4/§6 declaration order, not alphabetical; compare as sets.
85
87
  */
86
- export declare const BACKLOG_VERBS: readonly string[];
88
+ export { BACKLOG_VERBS };
87
89
  /**
88
90
  * One mounted operation, projected to all four transports backlog serves.
89
91
  *
@@ -149,8 +151,43 @@ export declare function describeMountedSurface(operations: readonly Operation[])
149
151
  * shipped surface automatically.
150
152
  */
151
153
  export declare function resolveExpectedMcpToolNames(operations: readonly Operation[], usePlugins?: readonly Plugin[]): string[];
154
+ /**
155
+ * The full advertised surface, one entry per pinned data verb, each projecting
156
+ * to the identical `id`/`mcpTool`/`cliCommand` a real mounted operation has.
157
+ *
158
+ * Derived from {@link BACKLOG_VERBS} (the single pinned list) so it can never
159
+ * drift from what `server.verbs.spec.ts` checks; {@link assertSurfaceIsReal}
160
+ * proves each entry is actually present in the built package.
161
+ */
162
+ export declare function describeBacklogSurface(): IMountedOperationSurface[];
163
+ /**
164
+ * Assert that every advertised verb is a REAL mounted operation in the built
165
+ * package — the AC3 mechanism, and the fix for the `enrich` class of defect
166
+ * (an agent spec that advertises a verb this tool does not implement).
167
+ *
168
+ * Reads the baked operation descriptors (`dist/api.ir.json`, the SAME artifact
169
+ * every real mount composes from) and compares its projected surface against
170
+ * {@link describeBacklogSurface} **both ways**: a verb advertised but absent
171
+ * from the build throws (naming it), and an operation mounted but absent from
172
+ * the pinned list also throws (a split brain). The artifact reader is
173
+ * synchronous and content-hash validated; a missing/stale artifact is a hard
174
+ * failure, never a silent pass (an unverifiable surface is not a real one).
175
+ *
176
+ * Library-only / NEVER mounted (`index.ts` re-exports it): it opens no store.
177
+ */
178
+ export declare function assertSurfaceIsReal(): void;
152
179
  export interface StartOpts {
153
- transport: 'http' | 'mcp' | 'both';
180
+ /**
181
+ * OPTIONAL on purpose (D-A apply-fix): an omitted transport must NOT
182
+ * pre-empt the resolved `service.*` cascade. `resolveServiceConfig`'s
183
+ * precedence is explicit opts > `ADHD_BACKLOG_SERVICE_TRANSPORT` > layer
184
+ * files > the `'mcp'` code default, and it only consults `opts.transport`
185
+ * when it is actually present — so `serve.ts` must pass `undefined` (not a
186
+ * materialised `'mcp'`) when the operator did not type `--transport`.
187
+ * `createBacklogServer` mounts from the RESOLVED `serviceConfig.transport`,
188
+ * never from this raw field.
189
+ */
190
+ transport?: 'http' | 'mcp' | 'both';
154
191
  port?: number;
155
192
  host?: string;
156
193
  scope?: Scope;
@@ -205,9 +242,40 @@ export declare function buildBacklogApigenPackage(ctx: BacklogCtx | (() => Backl
205
242
  surface: IMountedOperationSurface[];
206
243
  operations: Operation[];
207
244
  }>;
245
+ /**
246
+ * The long-lived server handle (D-A, Segment D). Additive: existing callers
247
+ * keep using {@link startBacklogServer} (unchanged `Promise<void>` signature).
248
+ */
249
+ export interface IBacklogServerHandle {
250
+ report(): IServiceReport;
251
+ whenReady(): Promise<void>;
252
+ /** Resolves when the server has fully closed (store released). Additive. */
253
+ whenClosed(): Promise<void>;
254
+ /** Abort the transports and release the store. Idempotent. */
255
+ close(): Promise<void>;
256
+ }
208
257
  /**
209
258
  * Opens (or reuses) the backlog store + env, mounts every `client.ts` export
210
259
  * live via `@adhd/apigen-plugin-api-fastify` and/or `@adhd/apigen-plugin-mcp`
211
- * — no code generation.
260
+ * — no code generation — and returns a lifecycle handle.
261
+ *
262
+ * D-A additions: resolves + validates `service.*` config, runs the load-time
263
+ * artifact drift check, and drives the `starting → live → ready` lifecycle
264
+ * with a serving-path readiness probe. `startBacklogServer` (below) is the
265
+ * unchanged-signature wrapper over this function.
266
+ */
267
+ export declare function createBacklogServer(opts: StartOpts & {
268
+ onStateChange?: (r: IServiceReport) => void;
269
+ /**
270
+ * `--probe` mode: compose the serving path but do NOT mount a network
271
+ * transport. The readiness probe drives the SAME composed invoker the
272
+ * tools use, so a live socket is not required — and skipping the mount
273
+ * avoids a one-shot probe process being kept alive by a listening socket.
274
+ */
275
+ probeOnly?: boolean;
276
+ }): Promise<IBacklogServerHandle>;
277
+ /**
278
+ * UNCHANGED public signature (`Promise<void>`, resolves on close) — a thin
279
+ * wrapper over {@link createBacklogServer} for existing callers.
212
280
  */
213
281
  export declare function startBacklogServer(opts: StartOpts): Promise<void>;
@@ -0,0 +1,109 @@
1
+ import { StartOpts } from './server.js';
2
+ import { BacklogConfig } from './env.js';
3
+ import { Environment } from '@adhd/environment';
4
+ import { Scope } from '@adhd/environment-base-spec';
5
+
6
+ export type IServiceTransport = 'mcp' | 'http' | 'both';
7
+ export interface IArtifactIdentity {
8
+ kind: 'path' | 'package';
9
+ /** kind:'path' — sha256 hex of the resolved entry file. */
10
+ sha256?: string;
11
+ /** kind:'package' — the published name + version to match. */
12
+ name?: string;
13
+ version?: string;
14
+ }
15
+ export interface IServiceConfig {
16
+ transport: IServiceTransport;
17
+ port: number;
18
+ host: string;
19
+ scope: Scope;
20
+ /** MUST be absolute (or the literal ':memory:' test path). */
21
+ dbPath: string;
22
+ busyTimeoutMs: number;
23
+ server: {
24
+ /** Absolute path, or a PATH-resolvable bin (`npx`/`node`). `''` = unset. */
25
+ command: string;
26
+ identity: IArtifactIdentity;
27
+ };
28
+ readiness: {
29
+ timeoutMs: number;
30
+ };
31
+ connect: {
32
+ /** Pre-connect deadline extension — the cold-start grace window. */
33
+ graceMs: number;
34
+ /** Total wall-clock budget across all attempts (bounded, never unbounded). */
35
+ budgetMs: number;
36
+ maxAttempts: number;
37
+ baseMs: number;
38
+ maxMs: number;
39
+ breakerFailureThreshold: number;
40
+ breakerResetMs: number;
41
+ };
42
+ }
43
+ /**
44
+ * The schema owner for the `service.*` subtree — the allow-list unknown keys
45
+ * are checked against. The keys are FULL dot-paths; `env.ts`'s spec declares
46
+ * exactly this set and a unit test asserts the two cannot drift.
47
+ *
48
+ * D-A apply-fix (2026-09-27): four keys that were RESOLVED but consumed by
49
+ * nothing were REMOVED rather than shipped as silent no-ops (the finding's
50
+ * "wire it, or stop declaring it" rule):
51
+ * - `service.namespace` — directly contradicts `env.ts`'s own contract that
52
+ * namespace selection is explicit-parameter-only
53
+ * (`BuildBacklogEnvOptions.namespace`), never resolved from an env var or
54
+ * config file; a key that can never be honored only misleads.
55
+ * - `service.serverArgs` — this process IS the server; nothing ever LAUNCHES
56
+ * `server.command` (its sole consumer is the load-time artifact-drift
57
+ * check, which ignores args), so args had no role.
58
+ * - `service.readinessIntervalMs` / `service.readinessMaxMissedTicks` — they
59
+ * describe a serving-loop watchdog TICK, but no such tick exists here and
60
+ * a same-process watchdog cannot observe its own frozen event loop;
61
+ * genuinely a later (external-supervisor) slice.
62
+ * The remaining keys are all APPLIED: transport/port/host mount the server,
63
+ * `serverCommand` drives `assertServerArtifact`, `connect*` drives
64
+ * `withResilience` around the serving-path readiness probe, and
65
+ * `readinessTimeoutMs` caps one probe.
66
+ */
67
+ export declare const SERVICE_CONFIG_KEYS: readonly string[];
68
+ /**
69
+ * True when `candidate` is an executable bin reachable on `PATH`. An absolute
70
+ * path is NOT automatically accepted here — the drift check owns absolute
71
+ * existence; this helper only answers "can the shell find this bare name".
72
+ */
73
+ export declare function isPathResolvableBin(candidate: string): boolean;
74
+ /**
75
+ * Resolves, validates and freezes the `service.*` configuration for one
76
+ * process. Throws {@link UnknownConfigKeyError} on an unknown key in ANY
77
+ * layer file and {@link NonAbsolutePathError} on a relative path field.
78
+ */
79
+ export declare function resolveServiceConfig(opts: StartOpts, env: Environment<BacklogConfig>): IServiceConfig;
80
+ export interface IMcpServerEntry {
81
+ type?: string;
82
+ command?: string | string[];
83
+ args?: string[];
84
+ env?: Record<string, string>;
85
+ url?: string;
86
+ }
87
+ /**
88
+ * Validates ONE host MCP server entry the tool writes or reads. Unknown keys
89
+ * are a HARD ERROR (the recorded `environment` vs `env` silent no-op is the
90
+ * exact failure): a key outside the entry's own schema
91
+ * (`type/command/args/env/url`) throws {@link UnknownMcpConfigKeyError}; a
92
+ * relative `command` path that is not a PATH-resolvable bin throws
93
+ * {@link NonAbsolutePathError}.
94
+ */
95
+ export declare function assertMcpEntryValid(entry: unknown, hostPath: string): asserts entry is IMcpServerEntry;
96
+ /**
97
+ * Load-time drift check. Resolves `server.command` to an absolute path (or a
98
+ * PATH-resolvable bin recorded with its resolved path), asserts it EXISTS, is
99
+ * EXECUTABLE, and that its content identity matches `server.identity`:
100
+ *
101
+ * - `kind:'path'` → sha256(resolved file) === identity.sha256
102
+ * - `kind:'package'` → the resolved package's package.json version === identity.version
103
+ *
104
+ * On missing/drift → {@link ArtifactDriftError} naming the resolved absolute
105
+ * path — it refuses to start rather than launch a phantom. A PURE read: it
106
+ * mutates nothing. A `command` that is unset (`''`) is a no-op, so a process
107
+ * that is not configured to launch an external artifact is never blocked.
108
+ */
109
+ export declare function assertServerArtifact(server: IServiceConfig['server']): void;
@@ -0,0 +1,51 @@
1
+ import { BacklogWriteError } from './write/errors.js';
2
+
3
+ /** An unknown key under the `service.*` config subtree in a layer file. */
4
+ export declare class UnknownConfigKeyError extends BacklogWriteError {
5
+ readonly code: "E_VALIDATION";
6
+ readonly retryable = false;
7
+ /** The offending key, WITHOUT the `service.` prefix (e.g. `bogusKey`). */
8
+ readonly key: string;
9
+ /** The absolute layer file the key was found in. */
10
+ readonly file: string;
11
+ constructor(key: string, file: string);
12
+ }
13
+ /** A path-valued config field that is neither absolute nor a PATH bin. */
14
+ export declare class NonAbsolutePathError extends BacklogWriteError {
15
+ readonly code: "E_VALIDATION";
16
+ readonly retryable = false;
17
+ readonly field: string;
18
+ readonly value: string;
19
+ constructor(field: string, value: string);
20
+ }
21
+ /** An unknown key inside a host MCP server entry (the `environment` vs `env`
22
+ * silent no-op is the exact failure this prevents). */
23
+ export declare class UnknownMcpConfigKeyError extends BacklogWriteError {
24
+ readonly code: "E_VALIDATION";
25
+ readonly retryable = false;
26
+ /** The host config file the entry lives in. */
27
+ readonly entry: string;
28
+ readonly key: string;
29
+ /** Closest allowed key, when one is close enough to suggest. */
30
+ readonly suggestion?: string;
31
+ constructor(entry: string, key: string, suggestion?: string);
32
+ }
33
+ /** The configured server artifact is missing, non-executable, or its content
34
+ * identity does not match what was installed. */
35
+ export declare class ArtifactDriftError extends BacklogWriteError {
36
+ readonly code: "E_VALIDATION";
37
+ readonly retryable = false;
38
+ readonly expected: string;
39
+ readonly actual: string;
40
+ readonly resolvedPath: string;
41
+ constructor(expected: string, actual: string, resolvedPath: string);
42
+ }
43
+ /** A readiness/liveness failure surfaced by the resilience layer (a bounded
44
+ * budget was exhausted, or the breaker refused the attempt). */
45
+ export declare class ServiceNotReadyError extends BacklogWriteError {
46
+ readonly code: "E_VALIDATION";
47
+ readonly retryable = true;
48
+ readonly state: string;
49
+ readonly failure?: string;
50
+ constructor(state: string, failure?: string);
51
+ }