@ctxr/skill-llm-wiki 1.0.2 → 1.1.0

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.
@@ -10,7 +10,7 @@
10
10
  // circuits the whole ladder.
11
11
  //
12
12
  // Three quality modes, selected via --quality-mode or the
13
- // LLM_WIKI_QUALITY_MODE env var:
13
+ // LLM_WIKI_QUALITY_MODE env var (see resolveQualityMode):
14
14
  //
15
15
  // tiered-fast (default):
16
16
  // Tier 0 → Tier 1 → Tier 2, the full ladder. Mid-band Tier 0
@@ -21,9 +21,16 @@
21
21
  // obvious decisions) but anything in the Tier 0 mid-band goes
22
22
  // straight to Tier 2, skipping Tier 1.
23
23
  //
24
- // tier0-only:
25
- // Tier 0 decisions only. Mid-band becomes an explicit
26
- // "undecidable" marker that the caller must resolve manually.
24
+ // deterministic:
25
+ // Tier 0 + Tier 1 ladder, but the ladder terminates at Tier 1:
26
+ // mid-band Tier 0 escalates to Tier 1 (as in tiered-fast), but
27
+ // mid-band Tier 1 is resolved by a deterministic threshold
28
+ // (`TIER1_DETERMINISTIC_THRESHOLD`) instead of escalating to
29
+ // Tier 2. No LLM/sub-agent is ever consulted — every decision
30
+ // is produced from TF-IDF + MiniLM cosine alone, so repeated
31
+ // runs on the same inputs are byte-reproducible. This is the
32
+ // mode the clustering pipeline pairs with algorithmic HAC +
33
+ // auto-slug to produce deterministic wiki builds end-to-end.
27
34
  //
28
35
  // Tier 2 escalation contract: the skill's CLI runs under Node with
29
36
  // no access to Claude Code's `Agent` tool, so it cannot spawn
@@ -64,11 +71,27 @@ import {
64
71
  export const QUALITY_MODES = Object.freeze([
65
72
  "tiered-fast",
66
73
  "claude-first",
67
- "tier0-only",
74
+ "deterministic",
68
75
  ]);
69
76
 
70
77
  export const DEFAULT_QUALITY_MODE = "tiered-fast";
71
78
 
79
+ // Deterministic-mode split point for resolving mid-band Tier 1
80
+ // similarities. Derived as the midpoint of the Tier 1 mid-band so
81
+ // future tuning of the decisive-same / decisive-different thresholds
82
+ // propagates here without a separate code-change — no drift between
83
+ // "where the ladder says 'escalate'" and "where deterministic mode
84
+ // says 'same vs different'". Any pair whose Tier 1 cosine sits
85
+ // strictly above this is routed to "same"; anything at-or-below is
86
+ // routed to "different". In this mode there is no mid-band
87
+ // "undecidable" / pending-Tier-2 outcome — Tier 1 always produces a
88
+ // concrete branch without an LLM in the loop. (Note: Tier 0 can still
89
+ // produce an "undecidable" result on insufficient-text inputs — two
90
+ // empty frontmatters — independent of quality mode; that predates
91
+ // deterministic mode and is by design.)
92
+ export const TIER1_DETERMINISTIC_THRESHOLD =
93
+ (TIER1_DECISIVE_SAME + TIER1_DECISIVE_DIFFERENT) / 2;
94
+
72
95
  export function resolveQualityMode(flags = {}) {
73
96
  const fromFlag = flags.quality_mode;
74
97
  const fromEnv = process.env.LLM_WIKI_QUALITY_MODE;
@@ -244,18 +267,6 @@ export async function decide(
244
267
  }
245
268
 
246
269
  // Mid-band Tier 0 → escalate. Behaviour depends on quality mode.
247
- if (qualityMode === "tier0-only") {
248
- const result = {
249
- tier: 0,
250
- similarity: t0.similarity,
251
- decision: "undecidable",
252
- confidence_band: t0.confidence_band,
253
- reason: "tier0-only quality mode — mid-band left unresolved",
254
- };
255
- finaliseDecision(result, { a, b, hashA, hashB, wikiRoot, opId, operator, writeLog, writeCache });
256
- return result;
257
- }
258
-
259
270
  if (qualityMode === "claude-first") {
260
271
  // Skip Tier 1 entirely, go straight to Tier 2.
261
272
  return await escalateToTier2(
@@ -308,7 +319,20 @@ export async function decide(
308
319
  finaliseDecision(result, { a, b, hashA, hashB, wikiRoot, opId, operator, writeLog, writeCache });
309
320
  return result;
310
321
  }
311
- // Mid-band Tier 1 Tier 2.
322
+ // Mid-band Tier 1. Branch on quality mode: deterministic resolves
323
+ // algorithmically, tiered-fast escalates to Tier 2.
324
+ if (qualityMode === "deterministic") {
325
+ const decision = sim > TIER1_DETERMINISTIC_THRESHOLD ? "same" : "different";
326
+ const result = {
327
+ tier: 1,
328
+ similarity: sim,
329
+ decision,
330
+ confidence_band: "deterministic-mid-band",
331
+ reason: `deterministic mode: sim ${sim.toFixed(3)} ${decision === "same" ? ">" : "≤"} ${TIER1_DETERMINISTIC_THRESHOLD}`,
332
+ };
333
+ finaliseDecision(result, { a, b, hashA, hashB, wikiRoot, opId, operator, writeLog, writeCache });
334
+ return result;
335
+ }
312
336
  return await escalateToTier2(
313
337
  a, b, hashA, hashB, wikiRoot, opId, operator,
314
338
  sim, "tier1 mid-band", writeLog, writeCache,
@@ -133,6 +133,28 @@ export function validateWiki(wikiRoot) {
133
133
  }
134
134
  }
135
135
  }
136
+
137
+ // LEAF-AT-WIKI-ROOT — the wiki root must hold only `index.md`
138
+ // plus subdirectories. Any `.md` file at the wiki root other
139
+ // than `index.md` itself violates the invariant, regardless of
140
+ // what its frontmatter `type:` claims. Keying off path rather
141
+ // than `data.type` catches the edge case of a hand-authored
142
+ // `foo.md` at root declared as `type: index` — it's still a
143
+ // loose root file the navigational model forbids. The rule is
144
+ // navigational: Claude reading `<root>/index.md` and following
145
+ // its `entries[]` should reach every leaf via a
146
+ // semantically-named category; loose root files bypass that
147
+ // mental model and bloat the top-level index.
148
+ const absDir = dirname(e.absolute);
149
+ const absName = basename(e.absolute);
150
+ if (absDir === wikiRoot && absName !== "index.md") {
151
+ push(
152
+ "error",
153
+ "LEAF-AT-WIKI-ROOT",
154
+ e.absolute,
155
+ `non-index markdown file at wiki root — must live in a subcategory (run 'fix' to contain)`,
156
+ );
157
+ }
136
158
  }
137
159
 
138
160
  return findings;