@adhd/backlog 0.1.1 → 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/CHANGELOG.md +30 -0
- package/index.js +143 -118
- package/index.mjs +5907 -5625
- package/install-skill.d.ts +15 -0
- package/install.d.ts +59 -0
- package/model.d.ts +35 -0
- package/package.json +10 -7
- package/skill/SKILL.md +148 -0
- package/store/query.d.ts +14 -0
- package/store/structure.d.ts +29 -0
package/install-skill.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export type SkillHost = 'claude' | 'codex' | 'opencode';
|
|
2
2
|
export type SkillScope = 'user' | 'project';
|
|
3
|
+
export declare const ALL_HOSTS: readonly SkillHost[];
|
|
4
|
+
/** Exported so `install.ts`'s codex MCP-registration path (which needs the
|
|
5
|
+
* SAME `~/.codex` (or `$CODEX_HOME`) root as the skill installer, but the
|
|
6
|
+
* file `config.toml` rather than a `skills/` subdirectory) never
|
|
7
|
+
* re-derives — and risks desyncing from — this resolution rule. */
|
|
8
|
+
export declare function resolveCodexHomeDir(home: string, homeOverride?: string): string;
|
|
9
|
+
export declare function hostSkillsDir(host: SkillHost, scope: SkillScope, cwd: string, homeOverride?: string): string;
|
|
3
10
|
export interface InstallSkillResult {
|
|
4
11
|
host: SkillHost;
|
|
5
12
|
scope: SkillScope;
|
|
@@ -20,6 +27,14 @@ export interface InstallSkillResult {
|
|
|
20
27
|
* always resolves the genuine machine home directory for `--scope user`.
|
|
21
28
|
*/
|
|
22
29
|
export declare function installSkill(argv: string[], cwd?: string, homeOverride?: string): InstallSkillResult[];
|
|
30
|
+
/**
|
|
31
|
+
* The actual filesystem work behind `installSkill` — pulled out so
|
|
32
|
+
* `install.ts`'s richer `backlog install` parser (which additionally
|
|
33
|
+
* accepts `--skill-only`/`--mcp-only`) can drive it directly with an
|
|
34
|
+
* already-parsed `{ hosts, scope }` instead of re-parsing (and risking a
|
|
35
|
+
* DIFFERENT `--host`/`--scope` grammar from) a raw argv a second time.
|
|
36
|
+
*/
|
|
37
|
+
export declare function installSkillToHosts(hosts: SkillHost[], scope: SkillScope, cwd: string, homeOverride?: string): InstallSkillResult[];
|
|
23
38
|
/** CLI entry — parses argv, runs the install, prints the same
|
|
24
39
|
* `console.log(JSON.stringify(...))` shape every other CLI command uses
|
|
25
40
|
* (BUG-APIGEN-015 parity), so scripting `backlog install-skill` composes
|
package/install.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { InstallSkillResult, SkillHost, SkillScope } from './install-skill.js';
|
|
2
|
+
|
|
3
|
+
export type McpHost = SkillHost;
|
|
4
|
+
export type McpScope = SkillScope;
|
|
5
|
+
/** The one, portable invocation every host's registration entry points at —
|
|
6
|
+
* `npx -y @adhd/backlog@latest serve --transport mcp` — so a written config
|
|
7
|
+
* works on any machine with `npx` and network access, never tied to this
|
|
8
|
+
* monorepo's own local `dist/index.js` path (that local-path form is what
|
|
9
|
+
* THIS repo's own checked-in `.mcp.json` uses for its own dev loop, which is
|
|
10
|
+
* deliberately NOT what a published `install` command should write for a
|
|
11
|
+
* third-party consumer). */
|
|
12
|
+
export declare const BACKLOG_MCP_NPX_ARGS: readonly string[];
|
|
13
|
+
export type McpInstallStatus = 'written' | 'manual';
|
|
14
|
+
export interface McpInstallResult {
|
|
15
|
+
host: McpHost;
|
|
16
|
+
scope: McpScope;
|
|
17
|
+
configPath: string;
|
|
18
|
+
status: McpInstallStatus;
|
|
19
|
+
note?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Replaces (or appends) exactly one `[tableHeader]` table in a TOML
|
|
23
|
+
* document's text, leaving every other line — including every OTHER
|
|
24
|
+
* table — byte-for-byte untouched. Scoped deliberately narrow (see this
|
|
25
|
+
* file's header doc): it finds the line matching `[tableHeader]` exactly,
|
|
26
|
+
* then treats everything up to (but not including) the next line that
|
|
27
|
+
* starts a new table (`[...`) — or end of file — as "this table's body",
|
|
28
|
+
* and swaps that whole span for `newBodyLines`. If the table doesn't exist
|
|
29
|
+
* yet, the new table is appended at the end (separated by a single blank
|
|
30
|
+
* line from any existing content).
|
|
31
|
+
*
|
|
32
|
+
* This is intentionally NOT a general TOML parser/editor — it only
|
|
33
|
+
* recognizes bracketed table headers on their own line, which is exactly
|
|
34
|
+
* (and only) the shape this file ever writes or looks for.
|
|
35
|
+
*/
|
|
36
|
+
export declare function upsertTomlTable(text: string, tableHeader: string, newBodyLines: readonly string[]): string;
|
|
37
|
+
export declare const INSTALL_HELP_TEXT = "backlog install [--host claude|opencode|codex|all] [--scope user|project] [--skill-only | --mcp-only]\n\nInstalls the backlog skill AND registers the backlog MCP server into an agent\nhost's config, in one step.\n\n --host <name> claude | opencode | codex | all (default: all)\n --scope <name> user | project (default: user)\n --skill-only only install the skill \u2014 never touch any MCP config\n --mcp-only only register the MCP server \u2014 never touch any skill dir\n\nExamples:\n backlog install\n backlog install --host claude --scope project\n backlog install --mcp-only --host opencode\n\nBack-compat: `backlog install-skill` remains available and behaves exactly\nlike `backlog install --skill-only` (original --host/--scope grammar only).\n";
|
|
38
|
+
export interface InstallResult {
|
|
39
|
+
skill: InstallSkillResult[];
|
|
40
|
+
mcp: McpInstallResult[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Installs the backlog skill and/or registers the `backlog` MCP server into
|
|
44
|
+
* the requested host config(s), per `--host`/`--scope`/`--skill-only`/
|
|
45
|
+
* `--mcp-only`. Both halves are idempotent and non-destructive: re-running
|
|
46
|
+
* overwrites only the `backlog` skill files / the `backlog` MCP entry,
|
|
47
|
+
* never touching anything else already present.
|
|
48
|
+
*
|
|
49
|
+
* `homeOverride` is a TEST-ISOLATION ESCAPE HATCH ONLY, mirroring
|
|
50
|
+
* `installSkill`'s own parameter — never passed by `runInstallCommand`/the
|
|
51
|
+
* real CLI.
|
|
52
|
+
*/
|
|
53
|
+
export declare function install(argv: string[], cwd?: string, homeOverride?: string): InstallResult;
|
|
54
|
+
/** CLI entry for `backlog install` — same `console.log(JSON.stringify(...))`
|
|
55
|
+
* output convention as `install-skill`, plus a short human-readable summary
|
|
56
|
+
* of what was written where (this command's own explicit DoD requirement),
|
|
57
|
+
* printed to stderr so it never disturbs the machine-readable stdout JSON
|
|
58
|
+
* a script might parse. */
|
|
59
|
+
export declare function runInstallCommand(argv: string[]): Promise<void>;
|
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,25 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhd/backlog",
|
|
3
|
-
"version": "0.1.
|
|
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.
|
|
10
|
+
"@adhd/environment": "^0.1.3",
|
|
11
11
|
"@adhd/environment-base-spec": "^0.1.0",
|
|
12
|
-
"@adhd/apigen-core-client": "^0.2.
|
|
13
|
-
"@adhd/apigen-plugin-api-fastify": "^0.2.
|
|
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.
|
|
16
|
-
"@adhd/apigen-plugin-batch": "^0.2.
|
|
17
|
-
"@adhd/apigen-plugin-cli-output": "^0.2.
|
|
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",
|
|
21
21
|
"pino-pretty": "13.1.3"
|
|
22
22
|
},
|
|
23
|
+
"assets": [
|
|
24
|
+
"skill"
|
|
25
|
+
],
|
|
23
26
|
"main": "./index.js",
|
|
24
27
|
"module": "./index.mjs",
|
|
25
28
|
"typings": "./index.d.ts",
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backlog-usage
|
|
3
|
+
description: "Use whenever filing, claiming, transitioning, or resolving a backlog item (bug/debt/feature/investigation) in ANY repo on this machine — via the `backlog` CLI/MCP, never by hand-editing a BACKLOG.md file. Also use to check current migration status before assuming markdown vs. the tool is authoritative. Examples: \"log this bug\", \"file a debt item for the flaky test\", \"claim BUG-042\", \"what's still open in this repo\", \"is BACKLOG.md still the source of truth here\"."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `@adhd/backlog` usage
|
|
7
|
+
|
|
8
|
+
`@adhd/backlog` is a graph-backed, multi-agent, cross-repo backlog tool. It is
|
|
9
|
+
migrating every repo on this machine off hand-edited `BACKLOG.md` files onto
|
|
10
|
+
itself as the source of truth, with `BACKLOG.md` demoted to a generated,
|
|
11
|
+
git-visible *projection* of the graph. This skill is the ONLY place the
|
|
12
|
+
command surface, protocol, and migration-state mechanism are documented —
|
|
13
|
+
the global `CLAUDE.md`/`AGENTS.md` carry just one pointer line to this file.
|
|
14
|
+
|
|
15
|
+
## 1. Check migration status FIRST, every time
|
|
16
|
+
|
|
17
|
+
Never trust a hardcoded phase number in this document — it goes stale the
|
|
18
|
+
moment a phase advances. Before deciding whether `BACKLOG.md` or the tool is
|
|
19
|
+
authoritative for the CURRENT repo, run:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
backlog migration-status
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
It reports `{ phase, description, toolIsAuthoritative }`. While
|
|
26
|
+
`toolIsAuthoritative` is `false` (phases `not-started`/`phase-1`/`phase-2`),
|
|
27
|
+
`BACKLOG.md` (root, per-plan, per-package) is still the real source of truth
|
|
28
|
+
in this repo — read/file there by hand as usual, but still prefer `backlog
|
|
29
|
+
list-items`/`backlog create-item` for querying and filing so items are
|
|
30
|
+
visible cross-repo and get FTS/symbol dedup for free. Once `toolIsAuthoritative`
|
|
31
|
+
is `true` (`phase-3` and later), **never hand-edit a `BACKLOG.md` file** —
|
|
32
|
+
every one is a generated projection at that point, and a hand-edit will be
|
|
33
|
+
silently overwritten (or, once the parity gate is blocking, rejected in CI).
|
|
34
|
+
|
|
35
|
+
## 2. Command surface — 36 ops, one CLI convention
|
|
36
|
+
|
|
37
|
+
Every op takes `ctx` as an implicit first argument (never pass it — the CLI/
|
|
38
|
+
MCP host supplies it). The CLI's own parameter-naming convention (verified
|
|
39
|
+
against real spawned-binary tests, `cli.spec.ts`):
|
|
40
|
+
|
|
41
|
+
- **Scalar parameters** get individual kebab-case flags, named after the
|
|
42
|
+
parameter itself: `getItem(ctx, repo, humanId)` → `backlog get-item --repo
|
|
43
|
+
<repo> --human-id <id>`.
|
|
44
|
+
- **Object-shaped parameters** get a single JSON-blob flag, named after the
|
|
45
|
+
parameter's own name, kebab-cased: `createItem(ctx, input)` → `backlog
|
|
46
|
+
create-item --input '<json>'`.
|
|
47
|
+
- The bin name is never part of `argv` — a bare `create-item …`/`get-item …`
|
|
48
|
+
resolves without any manual namespace prefix.
|
|
49
|
+
- Failures use real process exit codes (never `0` on error — unknown command
|
|
50
|
+
→ exit `4`, bad flag → exit `2`); errors are JSON on the last stderr line.
|
|
51
|
+
- Every op is also available as an MCP tool, named `backlog_client_d_<snake_case_op>`
|
|
52
|
+
(e.g. `backlog_client_d_create_item`), once `.mcp.json` wires the server —
|
|
53
|
+
same parameters, same semantics.
|
|
54
|
+
|
|
55
|
+
| `client.ts` export | CLI form |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `createItem(ctx, input)` | `backlog create-item --input '<CreateItemInput json>'` |
|
|
58
|
+
| `getItem(ctx, repo, humanId)` | `backlog get-item --repo <repo> --human-id <id>` |
|
|
59
|
+
| `updateItem(ctx, repo, humanId, patch)` | `backlog update-item --repo <repo> --human-id <id> --patch '<UpdateItemInput json>'` |
|
|
60
|
+
| `listItems(ctx, filter?)` | `backlog list-items --filter '<BacklogFilter json>'` |
|
|
61
|
+
| `softDeleteItem(ctx, repo, humanId, reason)` | `backlog soft-delete-item --repo <repo> --human-id <id> --reason <text>` |
|
|
62
|
+
| `stats(ctx, scope?)` | `backlog stats --scope '<StatsScope json>'` |
|
|
63
|
+
| `spotlight(ctx, scope?, limit?)` | `backlog spotlight --scope '<json>' --limit <n>` |
|
|
64
|
+
| `readyItems(ctx, scope?)` | `backlog ready-items --scope '<json>'` |
|
|
65
|
+
| `blockers(ctx, repo, humanId)` | `backlog blockers --repo <repo> --human-id <id>` |
|
|
66
|
+
| `dependencyGraph(ctx, scope?)` | `backlog dependency-graph --scope '<json>'` |
|
|
67
|
+
| `topoOrder(ctx, scope?)` | `backlog topo-order --scope '<json>'` |
|
|
68
|
+
| `staleClaims(ctx, maxAgeMin, scope?)` | `backlog stale-claims --max-age-min <n> --scope '<json>'` |
|
|
69
|
+
| `claimItem(ctx, repo, humanId, by, opts?)` | `backlog claim-item --repo <repo> --human-id <id> --by <identity> --opts '<ClaimOpts json>'` |
|
|
70
|
+
| `renewClaim(ctx, repo, humanId, by)` | `backlog renew-claim --repo <repo> --human-id <id> --by <identity>` |
|
|
71
|
+
| `releaseClaim(ctx, repo, humanId, by, opts?)` | `backlog release-claim --repo <repo> --human-id <id> --by <identity> --opts '<json>'` |
|
|
72
|
+
| `assignItem(ctx, repo, humanId, to, by)` | `backlog assign-item --repo <repo> --human-id <id> --to <identity> --by <identity>` |
|
|
73
|
+
| `startWork(ctx, repo, humanId, by)` | `backlog start-work --repo <repo> --human-id <id> --by <identity>` |
|
|
74
|
+
| `transitionStatus(ctx, repo, humanId, status, opts)` | `backlog transition-status --repo <repo> --human-id <id> --status <STATUS> --opts '<TransitionOpts json>'` |
|
|
75
|
+
| `addCitation(ctx, repo, humanId, citation)` | `backlog add-citation --repo <repo> --human-id <id> --citation '<Citation json>'` |
|
|
76
|
+
| `appendNote(ctx, repo, humanId, by, text)` | `backlog append-note --repo <repo> --human-id <id> --by <identity> --text <note>` |
|
|
77
|
+
| `resolveItem(ctx, repo, humanId, status, opts)` | `backlog resolve-item --repo <repo> --human-id <id> --status <STATUS> --opts '<json>'` |
|
|
78
|
+
| `archiveResolved(ctx, scope, opts?)` | `backlog archive-resolved --scope '<StatsScope json>' --opts '<ArchiveOpts json>'` |
|
|
79
|
+
| `addDependency(ctx, repo, humanId, dependsOnHumanId)` | `backlog add-dependency --repo <repo> --human-id <id> --depends-on-human-id <id2>` |
|
|
80
|
+
| `removeDependency(ctx, repo, humanId, dependsOnHumanId)` | `backlog remove-dependency --repo <repo> --human-id <id> --depends-on-human-id <id2>` |
|
|
81
|
+
| `linkRelated(ctx, repo, humanIdA, humanIdB)` | `backlog link-related --repo <repo> --human-id-a <id1> --human-id-b <id2>` |
|
|
82
|
+
| `supersedeItem(ctx, repo, oldHumanId, newInput, reason)` | `backlog supersede-item --repo <repo> --old-human-id <id> --new-input '<CreateItemInput json>' --reason <text>` |
|
|
83
|
+
| `splitItem(ctx, repo, parentHumanId, children)` | `backlog split-item --repo <repo> --parent-human-id <id> --children '<CreateItemInput[] json>'` |
|
|
84
|
+
| `mergeItems(ctx, repo, keepHumanId, dropHumanId, reason)` | `backlog merge-items --repo <repo> --keep-human-id <id1> --drop-human-id <id2> --reason <text>` |
|
|
85
|
+
| `setPriority(ctx, repo, humanId, priority)` | `backlog set-priority --repo <repo> --human-id <id> --priority <PRIORITY>` |
|
|
86
|
+
| `attachToPlan(ctx, repo, humanId, planSlug)` | `backlog attach-to-plan --repo <repo> --human-id <id> --plan-slug <slug>` |
|
|
87
|
+
| `importFromMarkdown(ctx, input)` | `backlog import-from-markdown --input '<ImportMarkdownInput json>'` |
|
|
88
|
+
| `renderToMarkdown(ctx, filter?)` | `backlog render-to-markdown --filter '<BacklogFilter json>'` |
|
|
89
|
+
| `exportJson(ctx, filter?)` | `backlog export-json --filter '<json>'` |
|
|
90
|
+
| `auditTrail(ctx, repo, humanId)` | `backlog audit-trail --repo <repo> --human-id <id>` |
|
|
91
|
+
| `migrationStatus(ctx)` | `backlog migration-status` |
|
|
92
|
+
| `setMigrationPhase(ctx, phase)` | `backlog set-migration-phase --phase <PHASE>` (admin-only — only whoever just verified a phase's DoD should call this) |
|
|
93
|
+
|
|
94
|
+
Every `<repo>` value is this machine's stable git-remote-derived slug (e.g.
|
|
95
|
+
`PseudoSky/adhd`) — never a bare directory name. A worktree agent
|
|
96
|
+
(`<repo>/.worktrees/*`) resolves to the SAME main-repo `repo` slug, not a
|
|
97
|
+
phantom per-worktree repo.
|
|
98
|
+
|
|
99
|
+
## 3. Claim/renew/release protocol (multi-agent use)
|
|
100
|
+
|
|
101
|
+
- Identity is always `${agentName}:${instanceId}` — NEVER a bare role literal
|
|
102
|
+
like `"agent"`. Two concurrent agents both claiming as `"implementer"`
|
|
103
|
+
defeats the CAS protocol entirely.
|
|
104
|
+
- `claimItem` is idempotent for the SAME claimant — always `renewed`, no
|
|
105
|
+
contention check.
|
|
106
|
+
- A long-running task must call `renewClaim` periodically (default
|
|
107
|
+
staleness: 30 minutes).
|
|
108
|
+
- Every exit path (done/error/abandoned) calls `releaseClaim`
|
|
109
|
+
unconditionally — it is a no-op on an already-unclaimed item, never an
|
|
110
|
+
error. Never leave an item claimed after you stop working on it.
|
|
111
|
+
|
|
112
|
+
## 4. Citations — via tool calls, never hand-typed markdown
|
|
113
|
+
|
|
114
|
+
The `Citation` type (`{ file, lines?, context? }`) is the structured form of
|
|
115
|
+
one bracketed citation entry in the old hand-edited convention. Instead of
|
|
116
|
+
typing a `Citations: [...]` line by hand:
|
|
117
|
+
|
|
118
|
+
- Call `transitionStatus`/`resolveItem` with `{ citations: [...] }` when
|
|
119
|
+
moving into any terminal-done/terminal-workaround status — this is
|
|
120
|
+
REQUIRED and enforced (a status transition without citations there is
|
|
121
|
+
rejected).
|
|
122
|
+
- Call `addCitation` to attach evidence without a status change.
|
|
123
|
+
|
|
124
|
+
## 5. Dedupe before filing — via the tool, not eyeballing
|
|
125
|
+
|
|
126
|
+
`createItem` runs a dedupe scan (FTS over title+body, plus exact
|
|
127
|
+
symbol/path/errorText metadata match) BEFORE writing, and returns
|
|
128
|
+
`duplicateCandidates` alongside `created: false` when a likely match exists.
|
|
129
|
+
**Always inspect `duplicateCandidates` first.** Only pass `force: true` after
|
|
130
|
+
confirming the candidates are genuinely a distinct issue — never as a way to
|
|
131
|
+
skip reading them.
|
|
132
|
+
|
|
133
|
+
## 6. Filing a new item — worked example
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
backlog create-item --input '{
|
|
137
|
+
"family": "BUG-MYAREA",
|
|
138
|
+
"title": "Short, specific summary",
|
|
139
|
+
"body": "Full description, root cause if known, evidence.",
|
|
140
|
+
"repo": "PseudoSky/adhd",
|
|
141
|
+
"projectPath": "packages/domain/my-package",
|
|
142
|
+
"priority": "HIGH"
|
|
143
|
+
}'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
If the response has `created: false` with non-empty `duplicateCandidates`,
|
|
147
|
+
read them before deciding whether to `force: true` or update the existing
|
|
148
|
+
item instead (`updateItem`/`appendNote`).
|
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
|
package/store/structure.d.ts
CHANGED
|
@@ -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;
|