@holmes-lab/holmes-kit 0.18.0 → 0.19.2

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 (81) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/README.md +3 -0
  3. package/dist/.build-id +1 -1
  4. package/dist/holmes/cli/agents.d.ts +22 -0
  5. package/dist/holmes/cli/agents.js +76 -1
  6. package/dist/holmes/cli/approve.js +6 -1
  7. package/dist/holmes/cli/doctor.d.ts +36 -1
  8. package/dist/holmes/cli/doctor.js +182 -35
  9. package/dist/holmes/cli/index.js +7 -1
  10. package/dist/holmes/cli/init.js +12 -0
  11. package/dist/holmes/cli/native-deps.d.ts +65 -0
  12. package/dist/holmes/cli/native-deps.js +131 -0
  13. package/dist/holmes/cli/release-docs.d.ts +27 -0
  14. package/dist/holmes/cli/release-docs.js +68 -0
  15. package/dist/holmes/cpg/arch-observe.d.ts +15 -0
  16. package/dist/holmes/cpg/arch-observe.js +19 -0
  17. package/dist/holmes/cpg/cpg-scanner.d.ts +10 -36
  18. package/dist/holmes/cpg/cpg-scanner.js +27 -3
  19. package/dist/holmes/cpg/cycle-detect.d.ts +87 -0
  20. package/dist/holmes/cpg/cycle-detect.js +251 -0
  21. package/dist/holmes/cpg/cycle-observation.d.ts +65 -0
  22. package/dist/holmes/cpg/cycle-observation.js +146 -0
  23. package/dist/holmes/cpg/scan-cache.d.ts +1 -1
  24. package/dist/holmes/cpg/scanned-file.d.ts +36 -0
  25. package/dist/holmes/cpg/scanned-file.js +2 -0
  26. package/dist/holmes/governance/approval-queue.d.ts +23 -4
  27. package/dist/holmes/governance/approval-queue.js +44 -6
  28. package/dist/holmes/governance/constitution.d.ts +20 -0
  29. package/dist/holmes/governance/constitution.js +17 -0
  30. package/dist/holmes/governance/ledger-store.d.ts +9 -0
  31. package/dist/holmes/governance/ledger-store.js +47 -0
  32. package/dist/holmes/governance/provenance-chain.d.ts +16 -1
  33. package/dist/holmes/governance/provenance-chain.js +5 -3
  34. package/dist/holmes/hooks/pre-tool-use.js +3 -1
  35. package/dist/holmes/hooks/stop.d.ts +29 -0
  36. package/dist/holmes/hooks/stop.js +119 -3
  37. package/dist/holmes/mcp/defuse-bound.d.ts +1 -0
  38. package/dist/holmes/mcp/defuse-bound.js +8 -0
  39. package/dist/holmes/mcp/handlers.d.ts +7 -0
  40. package/dist/holmes/mcp/handlers.js +132 -6
  41. package/dist/holmes/mcp/history-admission.d.ts +15 -0
  42. package/dist/holmes/mcp/history-admission.js +37 -0
  43. package/dist/holmes/mcp/maintenance-analyze.d.ts +45 -0
  44. package/dist/holmes/mcp/maintenance-analyze.js +117 -9
  45. package/dist/holmes/mcp/maintenance-evidence.d.ts +41 -0
  46. package/dist/holmes/mcp/maintenance-evidence.js +71 -4
  47. package/dist/holmes/project/install-scripts-policy.d.ts +76 -0
  48. package/dist/holmes/project/install-scripts-policy.js +131 -0
  49. package/dist/holmes/project/npx-bin.d.ts +6 -0
  50. package/dist/holmes/project/npx-bin.js +10 -0
  51. package/dist/holmes/review/evaluation-metrics.d.ts +6 -0
  52. package/dist/holmes/review/evaluation-metrics.js +18 -1
  53. package/dist/holmes/review/failed-test-names.d.ts +19 -0
  54. package/dist/holmes/review/failed-test-names.js +43 -0
  55. package/dist/holmes/review/paired-power.d.ts +14 -0
  56. package/dist/holmes/review/paired-power.js +57 -0
  57. package/dist/holmes/review/replay-corpus.d.ts +11 -0
  58. package/dist/holmes/review/replay-corpus.js +34 -0
  59. package/dist/holmes/review/run-replay.d.ts +23 -0
  60. package/dist/holmes/review/run-replay.js +90 -4
  61. package/dist/holmes/review/symbol-truth.d.ts +14 -0
  62. package/dist/holmes/review/symbol-truth.js +23 -0
  63. package/dist/holmes/review/test-runner.d.ts +27 -0
  64. package/dist/holmes/review/test-runner.js +59 -3
  65. package/dist/holmes/rtm/defuse-symbols.d.ts +17 -0
  66. package/dist/holmes/rtm/defuse-symbols.js +91 -0
  67. package/dist/holmes/rtm/incremental.js +5 -0
  68. package/dist/holmes/rtm/rtm-builder.d.ts +8 -0
  69. package/dist/holmes/rtm/rtm-builder.js +32 -5
  70. package/dist/holmes/rtm/rtm-graph.d.ts +11 -0
  71. package/dist/holmes/rtm/rtm-graph.js +13 -0
  72. package/dist/holmes/spec/legacy-fields.d.ts +2 -0
  73. package/dist/holmes/spec/legacy-fields.js +9 -0
  74. package/dist/holmes/spec/legacy-format.d.ts +1 -1
  75. package/dist/holmes/spec/legacy-format.js +4 -1
  76. package/dist/holmes/spec/spec-parser.js +5 -3
  77. package/docs/install-guide.md +54 -5
  78. package/package.json +4 -1
  79. package/playbooks/author-slice/PLAYBOOK.md +14 -0
  80. package/playbooks/publish/PLAYBOOK.md +32 -0
  81. package/playbooks/tdd-slice/PLAYBOOK.md +14 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,155 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
  <!-- @implements A-SPEC-209 -->
8
+ ## [0.19.2] - 2026-09-10
9
+
10
+ Wiring diagnosis no longer depends on where you run it. No API change.
11
+
12
+ ### Fixed
13
+ - **`doctor` gave opposite verdicts for the same wiring depending on the directory it was run
14
+ from** (REQ-581). A relative executable path in a wiring file is resolved by Node against
15
+ `process.cwd()`, and a relative path is the CONVENTION here — `.mcp.json` has always carried
16
+ `bin/holmes-mcp.js`. Measured: the same project under the same `--target` read
17
+ `PASS codex wiring` from the repository root and `FAIL — points at a missing file` from `/tmp`,
18
+ with the fix line telling the operator to re-init a wiring that was never wrong. All four
19
+ affected checks (`mcp wiring spawn`, `codex wiring`, and the `wiringHandshakeChecks` that cover
20
+ all three harnesses) now resolve against the TARGET project, and the handshake launches the
21
+ server FROM that project — the way a harness actually starts it. Absolute wirings (antigravity's)
22
+ are returned untouched, and a path that is genuinely missing still fails. The message keeps the
23
+ string exactly as written in the wiring file: what an operator must fix is that string, not our
24
+ arithmetic.
25
+ - **The same wiring could read PASS from one check and FAIL from its sibling.** Measured on a
26
+ third-party tarball install: `claude wiring handshake` passed while `mcp wiring spawn` failed on
27
+ the identical `npx` command, because only one of the two spawn paths had been moved. Both now
28
+ launch from the target.
29
+
30
+ ### Verified on a third-party install (macOS, npm 11.17.0, Node 26.5.1)
31
+ - Tarball installed into a clean project: `better-sqlite3` binding present, `:memory:` query ok.
32
+ - With install scripts blocked (`--ignore-scripts`, the npm 12 condition): the CLI still starts,
33
+ `init` still wires, the MCP server still answers — and `doctor` names the cause and prints a
34
+ recovery command that works verbatim (`npm rebuild better-sqlite3 --foreground-scripts`).
35
+ - Diagnosed from an unrelated working directory with the consumer's own CLI: claude / codex /
36
+ antigravity handshakes 3/3 PASS, `target wiring — 3 holmes hook(s) resolve to this install`.
37
+
38
+ ### Corrected from 0.19.1's notes
39
+ - 0.19.1 recorded that `codexMcpBlock` "writes Windows backslash paths into `config.toml`
40
+ unescaped". Re-measured: it does not. `tomlStr` escapes `\` correctly and the write→read
41
+ round-trip returns `C:\Users\name\proj\bin\holmes-mcp.js` byte-identical (pure functions, so
42
+ the result is platform-independent). The `doctor-wiring-handshake` failure seen on a Windows
43
+ checkout is therefore unexplained by escaping — and is a candidate instance of the cwd defect
44
+ fixed above, which is a HYPOTHESIS for the next Windows run, not a conclusion.
45
+
46
+ ## [0.19.1] - 2026-09-10
47
+
48
+ Windows install hardening, measured on Windows 11 / npm 12.0.1 / Node 24.19.0. No API change.
49
+
50
+ ### Fixed
51
+ - **npm 12 skipped `better-sqlite3`'s install script and nothing said so** (REQ-579). npm ≥ 12
52
+ (and npm 11.19+) blocks dependency install scripts unless the root `package.json` approves
53
+ them; `npm ci` then succeeds with no `better_sqlite3.node`, and every SQLite-backed feature
54
+ fails on first use. `package.json` now declares `allowScripts: { "better-sqlite3@12.11.1": true }`
55
+ — the ONE dependency that needs its script, pinned to the lockfile version so a bump forces a
56
+ fresh review (a test goes red until the pin is updated). The 8 tree-sitter packages are
57
+ deliberately not approved: measured, they load from their shipped `prebuilds/` with the script
58
+ blocked. Older npm 11 ignores the field.
59
+ - **doctor misdiagnosed every missing SQLite binary as an ABI mismatch** (REQ-580). The
60
+ `better-sqlite3` check now judges from evidence — binary present or not, the load error's
61
+ wording, the npm major, the governing `allowScripts`, the install layout (repository / project
62
+ dependency / global / npx), and on Windows the toolchain and a spaced path — and names the
63
+ cause: `scripts-blocked`, `abi-mismatch`, `build-failed`, or `unknown` with the raw error. A
64
+ prebuilt-download failure is stated as unobservable, never asserted. The fix line is the exact
65
+ command for that layout; for a global install it targets the dependency
66
+ (`npm rebuild -g better-sqlite3 --foreground-scripts --allow-scripts=better-sqlite3`), because
67
+ `rebuild -g @holmes-lab/holmes-kit` dies `EEXIST` re-linking the bin under npm 12 (measured).
68
+ - **doctor's grammar check only resolved packages.** It now parses a source with all 8 grammars
69
+ (typescript, tsx, python, c-sharp, java, go, rust, cpp) in a fresh child process — in-process
70
+ parsing is unreliable after another module registry has loaded tree-sitter's native addon.
71
+ - **Windows recovery commands are runnable.** npm commands are emitted as `npm.cmd` on win32
72
+ (`npmBin`, the twin of `npxBin`), and when a `Restricted`/`AllSigned` PowerShell execution policy
73
+ is observed the verdict says the `.ps1` shims are blocked and the `.cmd` ones are not.
74
+
75
+ ### Docs
76
+ - `docs/install-guide.md`: npm 12 section, per-layout recovery table, PowerShell policy note,
77
+ build-failed evidence. `docs/windows-test-plan-2026-09.md`: items E/F/G updated with the
78
+ 2026-09-09/10 measurements and the known Windows-only baseline failures.
79
+
80
+ ### Known, not fixed (recorded honestly)
81
+ - macOS/Linux were not exercised on hardware in this release; their behaviour is covered by
82
+ platform-injected unit tests and the three-harness parity suite only.
83
+ - `codexMcpBlock` writes Windows backslash paths into `config.toml` unescaped
84
+ (`doctor-wiring-handshake` fails on a Windows checkout); doctor's `global prefix` check reports a
85
+ protected `C:\Program Files\nodejs` as writable. Both are pre-existing and tracked as follow-ups.
86
+ **(The escaping half of this was re-measured in 0.19.2 and does not reproduce — see that entry.)**
87
+
88
+ ## [0.19.0] - 2026-09-08
89
+
90
+ ### Added
91
+ - **Cycle governance for the code graph** (REQ-574). The spec graph has been required to be acyclic
92
+ by constitution (ART-2) for a long time; the code graph never was, in the same repository. Now it
93
+ is governed at three moments, because each can do a different thing:
94
+ - **Guidance, before design** — the authoring playbooks carry "share types through a third module,
95
+ do not create cycles" (pinned by test, inherited by the installed skills). At approval time the
96
+ graph cannot see the cycle a design *would* create — import edges come from scanning real source
97
+ — so words are the only thing that works there.
98
+ - **Advisory, at design time** — `approval_status`'s `graphPreview` gains `cycles`: the cycles the
99
+ files you declared are **already** in, with each edge classified as `type-erasable` (TypeScript
100
+ deletes it — not a runtime cycle), `lazy-require` (a workaround someone already paid for) or
101
+ `eager-value`. The note says in words that this is not a prediction.
102
+ - **Ratchet, after the code exists** — the Stop hook's constitution gains ART-2's sibling on the
103
+ code graph. Ships in `track` (records, never blocks); `strict` promotion waits on the observation
104
+ ledger. Not absolute zero but a ratchet, and the escape is a **named exception**, never a
105
+ threshold — a project with legacy cycles must still be able to adopt the harness.
106
+ - **Architecture observation beside the design-time advisory** (REQ-574 S4): `graphPreview` gains
107
+ `architecture` — lines, symbols, longest function, fan-in and fan-out for each declared file.
108
+ Numbers only: no grade, no threshold, no participation in any verdict (a test pins the absence of
109
+ such a field, because one would grow into the gate the measurement does not support).
110
+ - **A symbol axis in the replay benchmark** (REQ-573 S3-instrument): commit-touched symbols are
111
+ derived from the parent-side changed lines, so a mechanism aimed at function-level selection can
112
+ finally be judged.
113
+ - **Paired-power instrument** (REQ-573 S5): `editSetF1` and `pairedPower` report σ and the
114
+ minimum detectable effect at 80% power, so a verdict can state whether the difference it rests on
115
+ was large enough to detect.
116
+ - **The publish procedure reaches the external doc surfaces** (REQ-575): after a successful release,
117
+ the repo README is synchronised to GitHub and the org profile README is **inspected** for
118
+ claim-vs-reality drift (never auto-corrected — positioning wording is a person's call). The target
119
+ is derived from `package.json`'s `repository`, never hardcoded, because this playbook installs
120
+ into consuming projects.
121
+
122
+ ### Changed
123
+ - **Candidate admission narrowed to files that could be the answer** (REQ-573 S1). The commit-prose
124
+ channel admitted whatever git history named, so ledger JSONL, build baselines and CHANGELOG took
125
+ emission slots: measured over 12 real requests, **76 of 120 candidate slots (63.3%)** went to files
126
+ that cannot be the answer, one of them at the head of the list. History-derived candidates are now
127
+ restricted to files that could be source, and vendored trees are demoted the way the lexical path
128
+ already demotes them. Measured after: **0%**.
129
+ - **`maintenance_analyze` candidates carry def-use-ranked symbols** (REQ-573 S3): inside a file the
130
+ search already found, the functions the request's terms reach through data flow come first.
131
+ Measured on this repository: symbol Top-10 recall **0.087 → 0.287**, precision **0.038 → 0.098**,
132
+ file-level axes unchanged, request latency +10.3%. On two other corpora it moved nothing — the
133
+ gain is conditional on the file layer already being right, and it is reported as conditional.
134
+ - **Ambiguous call targets are preserved instead of dropped** (REQ-573 S4): a call whose name is
135
+ defined in several places now emits candidate edges on a new `calls_ambiguous` relation
136
+ (22.6% of resolved edges were being discarded). Every certainty-requiring reader filters on
137
+ `rel='calls'`, so gates and advisories are unaffected. **The PPR weight for it stays 0**: the
138
+ weighted arm was measured on two corpora and rejected (one unchanged, one monotonically worse).
139
+
140
+ ### Fixed
141
+ - **Three import cycles in this repository, and the workarounds they forced** (REQ-574 S2). All
142
+ three had the same cure — move the shared thing to a module neither side owns — and two lazy
143
+ `require()` calls existed only to dodge them. Their absence is now the evidence the cycles are gone.
144
+ - **The benchmark measured a pipeline that does not ship** (REQ-573 S3-instrument): the replay
145
+ narrowed the commit-prose boost on its own, a filter the product never had, which is why a defect
146
+ worth 63.3% of the product's emitted slots read as six-decimal no movement. Removing it moved four
147
+ pins (recorded before/after in the commit); the product did not change.
148
+
149
+ ### Notes
150
+ - `RRF_K` was swept over {5, 10, 20, 60} on two corpora and **kept at 60**: no value improved both
151
+ corpora on every metric. Worth recording is that the sweep on the *unrepaired* benchmark said
152
+ k=10 dominated — fixing the instrument reversed the adoption decision.
153
+ - Judgement axis, measured: harness+judgement 0.2946 vs grep+judgement 0.2867 in edit-set F1, with
154
+ σ 0.1113 and a minimum detectable effect of 0.0551. The observed 0.0080 is a seventh of what this
155
+ design could detect, so the advantage is **not distinguishable from zero** at n=32.
156
+
8
157
  ## [0.18.0] - 2026-09-08
9
158
 
10
159
  Decisions become governed. A consuming project's operational decisions had been leaking into agent
package/README.md CHANGED
@@ -16,6 +16,9 @@
16
16
 
17
17
  ### 🛡️ Currently Supported Features (Production Features)
18
18
 
19
+ - 🔁 **Import cycles are governed, at the three moments that can each do something** *(new in 0.19.0)*: the spec graph has been required to be acyclic since early on; the code graph now is too. **Guidance** reaches the agent before it designs (the authoring playbooks say "share types through a third module", pinned by test). **A design-time advisory** — `approval_status`'s `graphPreview` gains `cycles` — names the cycles your declared files are *already* in, with each edge classified as `type-erasable` (TypeScript deletes it, so it is not a runtime cycle), `lazy-require` (a workaround someone already paid for) or `eager-value`; the note says in words that this is **not** a prediction, because at approval time the code that would create a cycle does not exist yet. **A ratchet** in the Stop hook's constitution catches new cycles after the code is written — shipped in `track` (records, never blocks), and the escape is a **named exception**, never a threshold, so a project carrying legacy cycles can still adopt the harness. This repository went from three cycles to zero, and the two lazy `require()` workarounds they forced are gone.
20
+ - 📐 **Size and fan-in, shown but never judged** *(new in 0.19.0)*: the same design-time advisory carries `architecture` — lines, symbols, longest function, fan-in and fan-out for each declared file. Numbers only: no grade, no threshold, no participation in any verdict. A test pins the *absence* of a severity field, because one would grow into the gate the evidence does not support (the size/defect correlation in the literature is largely "there is more code"). Practitioners still reject inspecting more than five things, so the number is worth seeing — the person decides.
21
+ - 🎯 **Candidates you could actually act on** *(new in 0.19.0)*: the commit-history channel used to admit whatever git named, so ledger files and build baselines took emission slots — measured, **63.3% of candidate slots** went to files that cannot be the answer, one at the head of the list. Now history-derived candidates must be able to be source, vendored trees are demoted, and inside a file the search already found, def-use ranking puts the functions the request reaches through data flow first (symbol Top-10 recall 0.087 → 0.287 on this repository; unchanged on two others, and reported as conditional rather than general).
19
22
  - 🧭 **The graph speaks BEFORE you commit to a scope** *(new in 0.18.0)*: the read-only `approval_status` now also answers `graphPreview` — `impact` (files that call INTO your declared Files-to-Touch from outside it, each anchor carrying its spec's intent sentence) and `density` (anchor-dense files inside the scope) — computed by the **same functions the sealing advisory uses**, so the preview can never disagree with the seal. Read the impact, then widen the declaration, narrow the design, or leave it knowingly; the authoring playbooks carry the step (pinned by test) and it stays a discipline, not a gate. Root-cause work gets the other half: `maintenance_analyze` candidates ride with `decisionContext` — the ADRs constraining that file and each decision's own sentence — which is the order a person diagnoses in (what broke, then why it was left this way). Both are information only: value tests pin that no ranking, score or gate reads them.
20
23
  - 📜 **ADR as a first-class governed document** *(new in 0.18.0)*: decisions stop leaking into agent memory outside the gate (a measured incident on a consuming project drove this). `spec_create(type: "ADR")` scaffolds a root decision document (Context / Decision / Consequences / Alternatives, `decided`/`decider`) under the store's full authoring governance — validate, `spec_approve` seal, ledger, tamper-block — with its **own number space** (your existing `ADR-0001` just works) and a **hitl-only seal** (autonomy never self-approves a decision). Store ADRs join the existing decision surface with zero new edge kinds: `ADR-XXXX` citations in specs/code become `constrained_by` edges, `supersedes` chains link, the graph's SPEC:ADR node carries the Decision line as its intent summary, and legacy `.ax/decisions/` entries coexist (store wins on id collision). A migration guide ships at `docs/adr-migration.md`.
21
24
 
package/dist/.build-id CHANGED
@@ -1 +1 @@
1
- cc24589-mtrjegw0
1
+ e63e589-mtuemhhb
@@ -62,6 +62,28 @@ export declare function antigravityHookCommand(scriptPath: string): string;
62
62
  /** win32-only, best-effort: a spaced path becomes its 8.3 short name (no spaces); anything else — or
63
63
  * any lookup failure — returns the input unchanged. */
64
64
  export declare function shortPathIfSpaced(p: string): string;
65
+ export declare const MANAGED_BEGIN = "<!-- holmes-kit:managed:begin -->";
66
+ export declare const MANAGED_END = "<!-- holmes-kit:managed:end -->";
67
+ /**
68
+ * @implements A-SPEC-576.2
69
+ * The regeneration that keeps what it did not write.
70
+ *
71
+ * Measured cause: `init` rewrote AGENTS.md from the template, deleting the ADR-018 parity rule a
72
+ * commit had added the day before — and said nothing, so the loss was found weeks later by grep.
73
+ * Generated text now lives inside a MANAGED BLOCK; everything outside it belongs to the person
74
+ * whose repository this is.
75
+ *
76
+ * PURE, and it takes the whole file rather than a path: the caller reads `before` during init's
77
+ * COMPUTE phase, so a dry-run predicts the same bytes the real run writes (A-SPEC-190 §9).
78
+ *
79
+ * `preserved` is the report, not a courtesy. A run that moved something and returned an empty list
80
+ * would be the same silent loss in a new costume.
81
+ */
82
+ export declare function mergeAgentsMd(existing: string | null, generated: string): {
83
+ content: string;
84
+ preserved: string[];
85
+ note: string | null;
86
+ };
65
87
  /**
66
88
  * 이 하네스에 써야 할 파일들. **쓰지는 않는다** — 무엇을 쓸지만 말한다.
67
89
  *
@@ -33,9 +33,10 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.CODEX_MARKETPLACE = exports.CODEX_PLUGIN_DIR = exports.HARNESS_ENFORCES = exports.AGENTS = void 0;
36
+ exports.MANAGED_END = exports.MANAGED_BEGIN = exports.CODEX_MARKETPLACE = exports.CODEX_PLUGIN_DIR = exports.HARNESS_ENFORCES = exports.AGENTS = void 0;
37
37
  exports.antigravityHookCommand = antigravityHookCommand;
38
38
  exports.shortPathIfSpaced = shortPathIfSpaced;
39
+ exports.mergeAgentsMd = mergeAgentsMd;
39
40
  exports.agentFiles = agentFiles;
40
41
  exports.agentLinks = agentLinks;
41
42
  // @implements A-SPEC-442
@@ -197,6 +198,80 @@ ${enforced
197
198
  ? `Gates are **enforced**. Tool calls that write un-anchored code or target unapproved specifications will be denied. Unresolved critical findings block completion.`
198
199
  : `This harness does not have measured **hook enforcement points**. Holmes-Kit provides tools and guidance here without active gate blocking.`}
199
200
  `;
201
+ exports.MANAGED_BEGIN = '<!-- holmes-kit:managed:begin -->';
202
+ exports.MANAGED_END = '<!-- holmes-kit:managed:end -->';
203
+ const PRESERVED_NOTE = '<!-- kept from this file by holmes-kit init — holmes-kit does not write below this line -->';
204
+ /**
205
+ * @implements A-SPEC-576.2
206
+ * The regeneration that keeps what it did not write.
207
+ *
208
+ * Measured cause: `init` rewrote AGENTS.md from the template, deleting the ADR-018 parity rule a
209
+ * commit had added the day before — and said nothing, so the loss was found weeks later by grep.
210
+ * Generated text now lives inside a MANAGED BLOCK; everything outside it belongs to the person
211
+ * whose repository this is.
212
+ *
213
+ * PURE, and it takes the whole file rather than a path: the caller reads `before` during init's
214
+ * COMPUTE phase, so a dry-run predicts the same bytes the real run writes (A-SPEC-190 §9).
215
+ *
216
+ * `preserved` is the report, not a courtesy. A run that moved something and returned an empty list
217
+ * would be the same silent loss in a new costume.
218
+ */
219
+ function mergeAgentsMd(existing, generated) {
220
+ const block = exports.MANAGED_BEGIN + '\n' + (generated.endsWith('\n') ? generated : generated + '\n') + exports.MANAGED_END + '\n';
221
+ if (existing === null || existing === '')
222
+ return { content: block, preserved: [], note: null };
223
+ const b = existing.indexOf(exports.MANAGED_BEGIN);
224
+ const e = existing.indexOf(exports.MANAGED_END);
225
+ if (b !== -1 && e > b) {
226
+ // The ordinary case once a file has been through here: swap the block, and do not touch a
227
+ // single byte on either side of it.
228
+ const body = generated.endsWith('\n') ? generated : generated + '\n';
229
+ return {
230
+ content: existing.slice(0, b) + exports.MANAGED_BEGIN + '\n' + body + exports.MANAGED_END + existing.slice(e + exports.MANAGED_END.length),
231
+ preserved: [],
232
+ note: null,
233
+ };
234
+ }
235
+ // LEGACY: a file written before the markers existed — every AGENTS.md in the field today. Its
236
+ // lines are compared to the generated ones, and whatever the generator never wrote is carried
237
+ // below the block. Line terminators are kept as read, so a CRLF checkout stays CRLF.
238
+ const known = new Set(generated.split(/\r?\n/).map((l) => l.trim()).filter((l) => l !== ''));
239
+ const lines = existing.split(/(?<=\n)/);
240
+ const runs = [];
241
+ let run = [];
242
+ let pendingBlanks = [];
243
+ for (const line of lines) {
244
+ const t = line.trim();
245
+ if (t === '') {
246
+ if (run.length > 0)
247
+ pendingBlanks.push(line);
248
+ continue;
249
+ }
250
+ if (known.has(t)) {
251
+ run = closeRun(runs, run);
252
+ pendingBlanks = [];
253
+ continue;
254
+ }
255
+ // A blank line INSIDE a run is part of it; blanks trailing a run are not.
256
+ run.push(...pendingBlanks, line);
257
+ pendingBlanks = [];
258
+ }
259
+ closeRun(runs, run);
260
+ if (runs.length === 0)
261
+ return { content: block, preserved: [], note: null };
262
+ const preserved = runs.map((r) => (r.find((l) => l.trim() !== '') ?? '').trim());
263
+ return {
264
+ content: block + '\n' + PRESERVED_NOTE + '\n\n' + runs.map((r) => r.join('')).join('\n'),
265
+ preserved,
266
+ note: `kept ${runs.length} section(s) this generator did not write`,
267
+ };
268
+ }
269
+ /** Close the current run into `runs` (dropping an empty one) and hand back a fresh one. */
270
+ function closeRun(runs, run) {
271
+ if (run.length > 0)
272
+ runs.push(run);
273
+ return [];
274
+ }
200
275
  /**
201
276
  * 이 하네스에 써야 할 파일들. **쓰지는 않는다** — 무엇을 쓸지만 말한다.
202
277
  *
@@ -78,7 +78,12 @@ const DEFAULT_TTL_MINUTES = 30;
78
78
  const MAX_TTL_MINUTES = 7 * 24 * 60;
79
79
  const DEFAULT_RATIONALE = 'approved via holmes-kit approve';
80
80
  const findPending = (root, id) => {
81
- const state = (0, approval_queue_1.readQueue)(root);
81
+ // @implements A-SPEC-576.1 RESOLUTION reads everything; the human's LIST reads decisions only.
82
+ // The inbox filter hides gate refusals from the screen, but `approve <id>` must still find one:
83
+ // the fallback below only holds entries written after the REQ-563 split, so anything refused
84
+ // before it lives in the queue alone. Filtering here would cut the out-of-band approval path the
85
+ // deny message itself promises.
86
+ const state = (0, approval_queue_1.readQueue)(root, { includeAllKinds: true });
82
87
  const pending = state.pending.find((p) => p.id === id);
83
88
  if (pending)
84
89
  return { entry: pending, state };
@@ -1,3 +1,4 @@
1
+ import { NativeEvidence } from './native-deps';
1
2
  import { type SemanticTier } from '../semantic/tier';
2
3
  /**
3
4
  * `holmes-kit doctor` — install diagnostics.
@@ -82,6 +83,28 @@ export declare function prefixVerdict(input: PrefixProbe): {
82
83
  detail: string;
83
84
  fix?: string;
84
85
  };
86
+ /**
87
+ * @implements A-SPEC-580
88
+ * Parse a trivial source with every grammar, in a child node whose module resolution starts at
89
+ * `packageRoot`. `parsed` counts typescript and tsx separately (8 for the 7 grammar packages).
90
+ */
91
+ export declare function grammarProbe(packageRoot: string): {
92
+ parsed: number;
93
+ failed: string[];
94
+ error?: string;
95
+ };
96
+ /**
97
+ * @implements A-SPEC-580
98
+ * Everything observable about the better-sqlite3 install, gathered for `nativeVerdict`. Each probe
99
+ * is independent and optional: a spawn that fails leaves its field `undefined` (not observed), and
100
+ * the verdict says so rather than treating silence as evidence.
101
+ *
102
+ * Which package.json holds the approval policy depends on the layout: this checkout's own for a
103
+ * repository run, the PROJECT's (three levels up from node_modules/@holmes-lab/holmes-kit) for a
104
+ * local dependency. A global or npx install has no project package.json that npm consults, so its
105
+ * coverage is `uncovered` by construction and the remedy is the per-command `--allow-scripts`.
106
+ */
107
+ export declare function gatherNativeEvidence(packageRoot: string): NativeEvidence;
85
108
  /** The parent environment minus the variables that legitimately change a gate decision. Pure. */
86
109
  export declare function probeEnv(parent: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
87
110
  /** Seam for the gate probe's child process. Default is a real `spawnSync`; tests inject a fake. */
@@ -94,6 +117,18 @@ export interface ProbeRunner {
94
117
  stderr?: string;
95
118
  };
96
119
  }
120
+ /**
121
+ * Where a wiring's executable actually is, judged from the PROJECT rather than from the caller.
122
+ *
123
+ * Measured 2026-09-10: the same `.codex/config.toml` under the same `--target` read PASS from the
124
+ * repository root and FAIL from `/tmp`, because a relative arg is resolved by Node against
125
+ * `process.cwd()`. A relative arg is the CONVENTION here — `.mcp.json` has always carried
126
+ * `bin/holmes-mcp.js` — so the verdict was decided by where the person diagnosing stood.
127
+ *
128
+ * An absolute arg is returned untouched: agy's wiring is absolute and must not move. An empty arg
129
+ * stays empty — resolving it would conjure the target directory itself into a "file that exists".
130
+ */
131
+ export declare function resolveWiringPath(target: string, arg: string): string;
97
132
  export declare function runDoctor(packageRoot: string, target?: string, opts?: DoctorOptions, extraChecks?: Check[]): Promise<Check[]>;
98
133
  /**
99
134
  * Drive a real MCP stdio handshake: initialize -> initialized -> tools/list, with a timeout.
@@ -101,7 +136,7 @@ export declare function runDoctor(packageRoot: string, target?: string, opts?: D
101
136
  * and closes stdin makes the server exit before answering (a false FAIL this check produced on its
102
137
  * very first run against a healthy server).
103
138
  */
104
- export declare function wiringSpawnCheck(command: string, args: string[], timeoutMs?: number, platform?: NodeJS.Platform): Promise<Check>;
139
+ export declare function wiringSpawnCheck(command: string, args: string[], timeoutMs?: number, platform?: NodeJS.Platform, cwd?: string): Promise<Check>;
105
140
  /**
106
141
  * Push-gate presence, diagnosed only where it applies: a repo that opted into governance (.ax)
107
142
  * AND has git. Absence or a hook without our signature is a WARN carrying the install command —