@adhd/backlog 0.1.2 → 0.1.3

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/model.d.ts CHANGED
@@ -90,6 +90,41 @@ export declare class DependencyCycleError extends Error {
90
90
  readonly cycle: string[];
91
91
  constructor(cycle: string[]);
92
92
  }
93
+ /**
94
+ * BUG-BACKLOG-HUMANID-COLLISION-001 (fix #1 — write-time guard):
95
+ * `createItemNode` rejects a `family` that is missing/empty/whitespace-only
96
+ * UNLESS `idOverride` is also given (SPEC.md §5.1's `CreateItemInput.family`
97
+ * contract: "required unless idOverride given"). Thrown BEFORE
98
+ * `allocateHumanIdAndInsert`/`computeNextHumanId` ever run, so a caller that
99
+ * omits `family` (previously silently coerced to the literal string
100
+ * `"undefined"` by `computeNextHumanId`'s template literal, producing
101
+ * `humanId: "undefined-001"` and colliding with every other item that hit
102
+ * the same bug) now fails loudly instead of minting a collision. This is
103
+ * defense in depth: it must hold regardless of whether an upstream caller's
104
+ * own input-schema validation (e.g. apigen-core-client's extracted
105
+ * `CreateItemInput` schema, BUG-APIGEN-CORE-CLIENT-001) enforces `family` as
106
+ * required — the store's own write path must never trust the caller alone.
107
+ */
108
+ export declare class InvalidArgumentError extends Error {
109
+ readonly argument: string;
110
+ constructor(argument: string, message: string);
111
+ }
112
+ /**
113
+ * BUG-BACKLOG-HUMANID-COLLISION-001 (fix #2 — read-time guard): every
114
+ * `(repo, humanId)`-keyed lookup used to silently resolve to "whichever
115
+ * live node is found first" when more than one live node shared the same
116
+ * key (the exact shape of the pre-existing `"undefined-001"` collisions,
117
+ * and the root cause of a real mis-transition this session — see the
118
+ * backlog item's body). Any lookup that finds >1 live match now throws this
119
+ * instead of guessing, listing every colliding `nodeId` so a caller can
120
+ * disambiguate (there is no tool-level nodeId-addressed path yet — the
121
+ * caller must go through the store's own repair primitives, e.g.
122
+ * `renameHumanId`, to resolve the collision).
123
+ */
124
+ export declare class AmbiguousHumanIdError extends Error {
125
+ readonly nodeIds: number[];
126
+ constructor(repo: string, humanId: string, nodeIds: number[]);
127
+ }
93
128
  export interface DedupeScanInput {
94
129
  symbol?: string;
95
130
  path?: string;
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@adhd/backlog",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "bin": {
5
5
  "backlog": "index.js"
6
6
  },
7
7
  "dependencies": {
8
8
  "@adhd/sox-graph-store": "^0.3.0",
9
9
  "better-sqlite3": "^12.10.0",
10
- "@adhd/environment": "^0.1.2",
10
+ "@adhd/environment": "^0.1.3",
11
11
  "@adhd/environment-base-spec": "^0.1.0",
12
- "@adhd/apigen-core-client": "^0.2.1",
13
- "@adhd/apigen-plugin-api-fastify": "^0.2.1",
12
+ "@adhd/apigen-core-client": "^0.2.2",
13
+ "@adhd/apigen-plugin-api-fastify": "^0.2.2",
14
14
  "@adhd/apigen-plugin-openapi": "^0.2.1",
15
- "@adhd/apigen-plugin-mcp": "^0.2.1",
16
- "@adhd/apigen-plugin-batch": "^0.2.0",
17
- "@adhd/apigen-plugin-cli-output": "^0.2.1",
15
+ "@adhd/apigen-plugin-mcp": "^0.2.2",
16
+ "@adhd/apigen-plugin-batch": "^0.2.1",
17
+ "@adhd/apigen-plugin-cli-output": "^0.2.2",
18
18
  "@adhd/apigen-engine-naming": "^0.2.1",
19
19
  "yaml": "1.10.3",
20
20
  "pino": "10.3.1",
package/store/query.d.ts CHANGED
@@ -6,6 +6,20 @@ import { NodeRecord } from '@adhd/sox-graph-store';
6
6
  /** Raw NodeRecord query — used internally where the full node (not just the mapped BacklogItem) is needed. */
7
7
  export declare function queryItemNodes(store: GraphBacklogStore, filter?: BacklogFilter): NodeRecord[];
8
8
  export declare function listItems(store: GraphBacklogStore, filter?: BacklogFilter): BacklogItem[];
9
+ /**
10
+ * BUG-BACKLOG-HUMANID-COLLISION-001 fix #2: this is THE shared `(repo,
11
+ * humanId) -> NodeRecord` lookup every store module funnels through
12
+ * (`crud.ts`/`lifecycle.ts`/`structure.ts`/`client.ts`'s `requireItem*`
13
+ * helpers all call this, directly or via `buildNotFoundError`'s sibling
14
+ * miss path). It used to silently resolve to "whichever live node happens
15
+ * to match `name`, else whichever is first" when more than one live node
16
+ * shared the same `(repo, humanId)` key — the exact shape of the
17
+ * pre-existing `"undefined-001"` collisions, and the root cause of a real
18
+ * mis-transition (see the backlog item body: a `resolveItem` call intended
19
+ * for one node silently landed on a different, unrelated one). Any lookup
20
+ * that finds >1 live match now throws `AmbiguousHumanIdError` instead of
21
+ * guessing.
22
+ */
9
23
  export declare function findItemNode(store: GraphBacklogStore, repo: string, humanId: string): NodeRecord | null;
10
24
  /**
11
25
  * Finds every LIVE node carrying this `humanId`, across ALL repos (no
@@ -33,3 +33,32 @@ export declare function mergeItemsNode(store: GraphBacklogStore, repo: string, k
33
33
  export declare function setPriorityNode(store: GraphBacklogStore, repo: string, humanId: string, priority: Priority): BacklogItem;
34
34
  export declare function attachToPlanNode(store: GraphBacklogStore, repo: string, humanId: string, planSlug: string): void;
35
35
  export declare function assignItemNode(store: GraphBacklogStore, repo: string, humanId: string, to: string, by: string): BacklogItem;
36
+ /**
37
+ * BUG-BACKLOG-HUMANID-COLLISION-001 fix #3 (repair primitive): re-ids a
38
+ * single, `nodeId`-scoped live backlog item to a new `humanId` within the
39
+ * same `repo`. `nodeId`-scoped (not `(repo, oldHumanId)`-keyed) so this is
40
+ * unambiguous EVEN under the exact collision it exists to repair — every
41
+ * other `(repo, humanId)`-keyed lookup in this file would throw
42
+ * `AmbiguousHumanIdError` on a colliding key (fix #2, `findItemNode`), so a
43
+ * repair tool needs a way in that doesn't go through that same lookup.
44
+ *
45
+ * There is no tool-level rename/re-id operation exposed anywhere in this
46
+ * store today (the backlog item's fix direction #5 explicitly calls this
47
+ * gap out) — this is that primitive, added as part of this fix, kept
48
+ * store-internal (not wired to `client.ts`/the MCP surface) since it is a
49
+ * narrow one-off repair tool, not a general-purpose end-user operation.
50
+ *
51
+ * Guards:
52
+ * - the node at `nodeId` must be live and its CURRENT `metadata.humanId`
53
+ * must equal `oldHumanId` (sanity check — refuses to rename the wrong
54
+ * node out from under a caller who mis-copied a nodeId).
55
+ * - `newHumanId` must not already resolve to a DIFFERENT live node in this
56
+ * `repo` (refuses to rename INTO a fresh collision).
57
+ * - re-derives `kind`/`family` from `newHumanId`, rebuilds `tags` (swapping
58
+ * the old kind/family tags for the new ones, preserving every other
59
+ * user tag) and the node `name`/`content`/`content_hash` (which both bake
60
+ * in `repo::humanId` — DESIGN.md §2.2, mapping.ts's `buildNodeName`/
61
+ * `buildNodeContent`) so the renamed node is indistinguishable from one
62
+ * that was always minted under `newHumanId`.
63
+ */
64
+ export declare function renameHumanIdNode(store: GraphBacklogStore, repo: string, nodeId: number, oldHumanId: string, newHumanId: string): BacklogItem;