squadrant 0.28.0 → 0.28.1

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.
@@ -9494,10 +9494,34 @@ function expandSource(kbRoot, entry) {
9494
9494
  const re = globToRegExp(abs.split(path16.sep).join("/"));
9495
9495
  return walkFiles(root).filter((f) => re.test(f.split(path16.sep).join("/"))).sort();
9496
9496
  }
9497
- function sourceCovers(kbRoot, entry, ref) {
9498
- const abs = path16.resolve(kbRoot, expandHome(entry.path));
9499
- const file = fileForRef(kbRoot, ref);
9500
- return /[*?]/.test(entry.path) ? globToRegExp(abs.split(path16.sep).join("/")).test(file.split(path16.sep).join("/")) : abs === file;
9497
+ function refCandidates(kbRoot, sources, ref) {
9498
+ const asIs = path16.resolve(kbRoot, expandHome(ref));
9499
+ if (path16.isAbsolute(expandHome(ref)))
9500
+ return [asIs];
9501
+ const bases = /* @__PURE__ */ new Set([kbRoot]);
9502
+ for (const s of sources) {
9503
+ let d = path16.resolve(kbRoot, expandHome(s.path.split(/[*?[{]/)[0]));
9504
+ for (let i = 0; i < 6 && d !== path16.dirname(d); i++) {
9505
+ d = path16.dirname(d);
9506
+ bases.add(d);
9507
+ }
9508
+ }
9509
+ return [.../* @__PURE__ */ new Set([asIs, ...[...bases].map((b) => path16.join(b, ref))])];
9510
+ }
9511
+ function refPath(kbRoot, sources, ref) {
9512
+ const cands = refCandidates(kbRoot, sources, ref);
9513
+ return cands.find(isFile) ?? cands.find((f) => sources.some((e) => entryCovers(kbRoot, e, f))) ?? cands[0];
9514
+ }
9515
+ function refCanon(kbRoot, sources) {
9516
+ const memo = /* @__PURE__ */ new Map();
9517
+ return (ref) => {
9518
+ let c = memo.get(ref);
9519
+ if (c === void 0) {
9520
+ c = refFor(kbRoot, refPath(kbRoot, sources, ref));
9521
+ memo.set(ref, c);
9522
+ }
9523
+ return c;
9524
+ };
9501
9525
  }
9502
9526
  function detectChange(kbRoot, file) {
9503
9527
  const ref = refFor(kbRoot, file);
@@ -9595,7 +9619,7 @@ async function planIngest(kbRoot, opts) {
9595
9619
  }
9596
9620
  return plan;
9597
9621
  }
9598
- var execFileAsync, CONVERTED_DIR, DEFAULT_MAX_SECTIONS_PER_PASS, CHARS_PER_TOKEN, PASS_THROUGH, SECTION_TARGET_CHARS, markitdownConverter, expandHome, fileForRef, cacheKey, convertedFile, stampFile, isGatedRef, sha256;
9622
+ var execFileAsync, CONVERTED_DIR, DEFAULT_MAX_SECTIONS_PER_PASS, CHARS_PER_TOKEN, PASS_THROUGH, SECTION_TARGET_CHARS, markitdownConverter, expandHome, cacheKey, convertedFile, stampFile, isFile, resolveSourceRef, entryCovers, sourceCovers, isGatedRef, sha256;
9599
9623
  var init_ingest = __esm({
9600
9624
  "packages/core/dist/knowledge/ingest.js"() {
9601
9625
  execFileAsync = promisify2(execFile2);
@@ -9609,11 +9633,17 @@ var init_ingest = __esm({
9609
9633
  return stdout;
9610
9634
  };
9611
9635
  expandHome = (p) => p === "~" || p.startsWith("~/") ? path16.join(os7.homedir(), p.slice(1)) : p;
9612
- fileForRef = (kbRoot, ref) => path16.resolve(kbRoot, expandHome(ref));
9613
9636
  cacheKey = (ref) => `${ref.replace(/[^A-Za-z0-9._-]+/g, "_")}.${crypto3.createHash("sha1").update(ref).digest("hex").slice(0, 6)}`;
9614
9637
  convertedFile = (kbRoot, ref) => path16.join(kbRoot, CONVERTED_DIR, `${cacheKey(ref)}.md`);
9615
9638
  stampFile = (kbRoot, ref) => path16.join(kbRoot, CONVERTED_DIR, `${cacheKey(ref)}.sha`);
9616
- isGatedRef = (kbRoot, sources, ref) => sources.some((e) => e.review === "required" && sourceCovers(kbRoot, e, ref));
9639
+ isFile = (f) => fs15.existsSync(f) && fs15.statSync(f).isFile();
9640
+ resolveSourceRef = (kbRoot, sources, ref) => refCandidates(kbRoot, sources, ref).find(isFile);
9641
+ entryCovers = (kbRoot, entry, file) => {
9642
+ const abs = path16.resolve(kbRoot, expandHome(entry.path));
9643
+ return /[*?]/.test(entry.path) ? globToRegExp(abs.split(path16.sep).join("/")).test(file.split(path16.sep).join("/")) : abs === file;
9644
+ };
9645
+ sourceCovers = (kbRoot, sources, entry, ref) => entryCovers(kbRoot, entry, refPath(kbRoot, sources, ref));
9646
+ isGatedRef = (kbRoot, sources, ref) => sources.some((e) => e.review === "required" && sourceCovers(kbRoot, sources, e, ref));
9617
9647
  sha256 = (file) => crypto3.createHash("sha256").update(fs15.readFileSync(file)).digest("hex");
9618
9648
  }
9619
9649
  });
@@ -9696,7 +9726,6 @@ var init_verify = __esm({
9696
9726
 
9697
9727
  // packages/core/dist/knowledge/store.js
9698
9728
  import fs16 from "fs";
9699
- import os8 from "os";
9700
9729
  import path17 from "path";
9701
9730
  import matter2 from "gray-matter";
9702
9731
  function duplicateIdProblem(id, ignored, used) {
@@ -9757,18 +9786,6 @@ function loadRulesDir(dir, layer, opts = {}) {
9757
9786
  }
9758
9787
  return { rules, errors };
9759
9788
  }
9760
- function resolveSourceRef(ref, kbRoot, sources) {
9761
- const bases = /* @__PURE__ */ new Set([kbRoot]);
9762
- for (const s of sources) {
9763
- let d = path17.resolve(kbRoot, expandHome2(s.path.split(/[*?[{]/)[0]));
9764
- for (let i = 0; i < 6 && d !== path17.dirname(d); i++) {
9765
- d = path17.dirname(d);
9766
- bases.add(d);
9767
- }
9768
- }
9769
- const cands = [path17.resolve(kbRoot, expandHome2(ref)), ...[...bases].map((b) => path17.join(b, ref))];
9770
- return cands.find((c) => fs16.existsSync(c) && fs16.statSync(c).isFile());
9771
- }
9772
9789
  function validateKb(opts) {
9773
9790
  const { rules, errors } = loadRulesDir(opts.rulesDir, "kb", { includeProposed: true });
9774
9791
  const checks = [];
@@ -9788,17 +9805,18 @@ function validateKb(opts) {
9788
9805
  else
9789
9806
  checks.push({ level: "ok", message: "no duplicate ids" });
9790
9807
  const cache = /* @__PURE__ */ new Map();
9808
+ const canon = refCanon(opts.kbRoot, opts.sources);
9791
9809
  let verified = 0, unresolved = 0, notIngested = 0;
9792
9810
  for (const r of rules) {
9793
9811
  r.sources.forEach((src, i) => {
9794
- const file = resolveSourceRef(src.ref, opts.kbRoot, opts.sources);
9812
+ const file = resolveSourceRef(opts.kbRoot, opts.sources, src.ref);
9795
9813
  if (!file) {
9796
9814
  unresolved++;
9797
9815
  return;
9798
9816
  }
9799
9817
  const key = `${src.ref}\0${file}`;
9800
9818
  if (!cache.has(key)) {
9801
- const conv = readState2(opts.kbRoot, src.ref) ? convertedFile(opts.kbRoot, src.ref) : null;
9819
+ const conv = readState2(opts.kbRoot, canon(src.ref)) ? convertedFile(opts.kbRoot, canon(src.ref)) : null;
9802
9820
  const cached = !!conv && fs16.existsSync(conv);
9803
9821
  cache.set(key, {
9804
9822
  hay: normalizedViews(fs16.readFileSync(cached ? conv : file, "utf8")),
@@ -9810,7 +9828,7 @@ function validateKb(opts) {
9810
9828
  if (span) {
9811
9829
  verified++;
9812
9830
  const active = r.status === "active" && !path17.relative(opts.rulesDir, r.file).split(path17.sep).includes(PROPOSED_DIR);
9813
- const err = active ? (opts.errata ?? []).find((e) => e.ref === src.ref && ((o) => !!o && spansOverlap(o, span))(findQuote(hay, e.quote))) : void 0;
9831
+ const err = active ? (opts.errata ?? []).find((e) => canon(e.ref) === canon(src.ref) && ((o) => !!o && spansOverlap(o, span))(findQuote(hay, e.quote))) : void 0;
9814
9832
  if (err)
9815
9833
  checks.push({ level: "warn", message: `${r.id}: grounded in erratum ${err.id} (${err.reason}); re-check or retire it (${r.file})` });
9816
9834
  } else if (convertible)
@@ -9833,7 +9851,7 @@ function validateKb(opts) {
9833
9851
  }
9834
9852
  return { checks, ok: !checks.some((c) => c.level === "fail") };
9835
9853
  }
9836
- var NO_JS_FRONTMATTER, expandHome2;
9854
+ var NO_JS_FRONTMATTER;
9837
9855
  var init_store2 = __esm({
9838
9856
  "packages/core/dist/knowledge/store.js"() {
9839
9857
  init_dist();
@@ -9845,7 +9863,6 @@ var init_store2 = __esm({
9845
9863
  throw new Error("only YAML front matter is allowed");
9846
9864
  } } }
9847
9865
  };
9848
- expandHome2 = (p) => p === "~" || p.startsWith("~/") ? path17.join(os8.homedir(), p.slice(1)) : p;
9849
9866
  }
9850
9867
  });
9851
9868
 
@@ -13068,7 +13085,7 @@ function removeSource(kbRoot, rulesDir, entryPath, opts = {}) {
13068
13085
  const res = { dryRun: !!opts.dryRun, entryRemoved: true, stripped: [], stale: [] };
13069
13086
  const writes = [];
13070
13087
  for (const rule of loadRulesDir(rulesDir, "kb").rules) {
13071
- const kept = rule.sources.filter((s) => !sourceCovers(kbRoot, entry, s.ref));
13088
+ const kept = rule.sources.filter((s) => !sourceCovers(kbRoot, sources, entry, s.ref));
13072
13089
  if (kept.length === rule.sources.length)
13073
13090
  continue;
13074
13091
  res.stripped.push(rule.id);
@@ -13189,7 +13206,7 @@ var init_match = __esm({
13189
13206
 
13190
13207
  // packages/core/dist/knowledge/audit.js
13191
13208
  import fs20 from "fs";
13192
- import os9 from "os";
13209
+ import os8 from "os";
13193
13210
  import path21 from "path";
13194
13211
  function appendAudit(events, sink = {}) {
13195
13212
  if (!events.length)
@@ -13219,8 +13236,8 @@ var AUDIT_QUERY_CHARS, defaultAuditDir, auditMachineId;
13219
13236
  var init_audit = __esm({
13220
13237
  "packages/core/dist/knowledge/audit.js"() {
13221
13238
  AUDIT_QUERY_CHARS = 200;
13222
- defaultAuditDir = () => process.env.SQUADRANT_AUDIT_DIR || path21.join(os9.homedir(), ".local", "state", "squadrant", "audit");
13223
- auditMachineId = () => os9.hostname().replace(/[^A-Za-z0-9_-]/g, "_") || "host";
13239
+ defaultAuditDir = () => process.env.SQUADRANT_AUDIT_DIR || path21.join(os8.homedir(), ".local", "state", "squadrant", "audit");
13240
+ auditMachineId = () => os8.hostname().replace(/[^A-Za-z0-9_-]/g, "_") || "host";
13224
13241
  }
13225
13242
  });
13226
13243
 
@@ -13646,18 +13663,20 @@ function loadErrata(kbRoot) {
13646
13663
  });
13647
13664
  return { errata, errors };
13648
13665
  }
13649
- function orphanedErrata(kbRoot, errata, refs) {
13650
- const only = refs ? new Set(refs) : void 0;
13666
+ function orphanedErrata(kbRoot, sources, errata, refs) {
13667
+ const canon = refCanon(kbRoot, sources);
13668
+ const only = refs ? new Set([...refs].map(canon)) : void 0;
13651
13669
  const hays = /* @__PURE__ */ new Map();
13652
13670
  const out = [];
13653
13671
  for (const e of errata) {
13654
- if (only && !only.has(e.ref))
13672
+ const ref = canon(e.ref);
13673
+ if (only && !only.has(ref))
13655
13674
  continue;
13656
- if (!hays.has(e.ref)) {
13657
- const conv = readState2(kbRoot, e.ref) ? convertedFile(kbRoot, e.ref) : null;
13658
- hays.set(e.ref, conv && fs23.existsSync(conv) ? normalizedViews(fs23.readFileSync(conv, "utf8")) : null);
13675
+ if (!hays.has(ref)) {
13676
+ const conv = readState2(kbRoot, ref) ? convertedFile(kbRoot, ref) : null;
13677
+ hays.set(ref, conv && fs23.existsSync(conv) ? normalizedViews(fs23.readFileSync(conv, "utf8")) : null);
13659
13678
  }
13660
- const hay = hays.get(e.ref);
13679
+ const hay = hays.get(ref);
13661
13680
  if (hay && !findQuote(hay, e.quote))
13662
13681
  out.push({ id: e.id, ref: e.ref });
13663
13682
  }
@@ -13674,9 +13693,31 @@ var init_errata = __esm({
13674
13693
 
13675
13694
  // packages/core/dist/knowledge/anchor.js
13676
13695
  import fs24 from "fs";
13696
+ import { execFileSync as execFileSync7 } from "child_process";
13677
13697
  import path25 from "path";
13678
- function* repoFiles(root) {
13698
+ function gitFiles(root) {
13699
+ try {
13700
+ const out = execFileSync7("git", ["ls-files", "-co", "--exclude-standard", "-z"], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], maxBuffer: 64 * 1024 * 1024 });
13701
+ return out.split("\0").filter(Boolean).sort();
13702
+ } catch {
13703
+ return null;
13704
+ }
13705
+ }
13706
+ function* repoFiles(root, maxFiles) {
13679
13707
  let count = 0;
13708
+ const listed = gitFiles(root);
13709
+ if (listed) {
13710
+ for (const rel of listed) {
13711
+ if (!CODE_EXT.has(path25.extname(rel)) || rel.split("/").slice(0, -1).some((d) => SKIP_DIRS.has(d)))
13712
+ continue;
13713
+ if (++count > maxFiles) {
13714
+ console.warn(`anchor: ${root} has more than ${maxFiles} code files; anchors cover only the first ${maxFiles}`);
13715
+ return;
13716
+ }
13717
+ yield path25.join(root, rel);
13718
+ }
13719
+ return;
13720
+ }
13680
13721
  const stack = [root];
13681
13722
  while (stack.length) {
13682
13723
  const dir = stack.pop();
@@ -13691,48 +13732,60 @@ function* repoFiles(root) {
13691
13732
  if (!SKIP_DIRS.has(ent.name))
13692
13733
  stack.push(path25.join(dir, ent.name));
13693
13734
  } else if (ent.isFile() && CODE_EXT.has(path25.extname(ent.name))) {
13694
- if (++count > MAX_FILES)
13735
+ if (++count > maxFiles) {
13736
+ console.warn(`anchor: ${root} has more than ${maxFiles} code files; anchors cover only the first ${maxFiles}`);
13695
13737
  return;
13738
+ }
13696
13739
  yield path25.join(dir, ent.name);
13697
13740
  }
13698
13741
  }
13699
13742
  }
13700
13743
  }
13701
- function computeAnchor(repoRoot, triggers) {
13702
- const terms2 = anchorTerms(triggers);
13703
- if (!terms2.length || !fs24.existsSync(repoRoot))
13704
- return null;
13705
- const hits = [];
13706
- for (const file of repoFiles(repoRoot)) {
13707
- let text;
13744
+ function scanRepo(root, maxFiles) {
13745
+ const out = [];
13746
+ for (const file of repoFiles(root, maxFiles)) {
13708
13747
  try {
13709
13748
  if (fs24.statSync(file).size > MAX_FILE_BYTES)
13710
13749
  continue;
13711
- text = fs24.readFileSync(file, "utf8");
13750
+ const text = fs24.readFileSync(file, "utf8");
13751
+ const rel = path25.relative(root, file).split(path25.sep).join("/");
13752
+ out.push({ rel, text, hay: `${path25.relative(root, file)}
13753
+ ${text}`.toLowerCase() });
13712
13754
  } catch {
13713
13755
  continue;
13714
13756
  }
13715
- const hay = `${path25.relative(repoRoot, file)}
13716
- ${text}`.toLowerCase();
13757
+ }
13758
+ return out;
13759
+ }
13760
+ function computeAnchor(repoRoot, triggers, cache, maxFiles = MAX_FILES) {
13761
+ const terms2 = anchorTerms(triggers);
13762
+ if (!terms2.length || !fs24.existsSync(repoRoot))
13763
+ return null;
13764
+ let files = cache?.get(repoRoot);
13765
+ if (!files) {
13766
+ files = scanRepo(repoRoot, maxFiles);
13767
+ cache?.set(repoRoot, files);
13768
+ }
13769
+ const hits = [];
13770
+ for (const { rel, text, hay } of files) {
13717
13771
  const score = terms2.filter((t) => hay.includes(t)).length;
13718
13772
  if (!score)
13719
13773
  continue;
13720
- const symbols2 = [...text.matchAll(SYMBOL_RE)].map((m) => m[1]).filter((s) => terms2.some((t) => s.toLowerCase().includes(t)));
13721
- hits.push({ rel: path25.relative(repoRoot, file).split(path25.sep).join("/"), score, symbols: symbols2 });
13774
+ hits.push({ rel, score, text });
13722
13775
  }
13723
13776
  if (!hits.length)
13724
13777
  return null;
13725
13778
  hits.sort((a, b) => b.score - a.score || (a.rel < b.rel ? -1 : 1));
13726
13779
  const top = hits.slice(0, MAX_PATHS);
13727
- const symbols = [...new Set(top.flatMap((h) => h.symbols))].sort().slice(0, MAX_SYMBOLS);
13780
+ const symbols = [...new Set(top.flatMap((h) => [...h.text.matchAll(SYMBOL_RE)].map((m) => m[1]).filter((s) => terms2.some((t) => s.toLowerCase().includes(t)))))].sort().slice(0, MAX_SYMBOLS);
13728
13781
  return { paths: top.map((h) => h.rel).sort(), ...symbols.length ? { symbols } : {} };
13729
13782
  }
13730
13783
  var SKIP_DIRS, CODE_EXT, MAX_FILES, MAX_FILE_BYTES, MAX_PATHS, MAX_SYMBOLS, SYMBOL_RE, anchorTerms;
13731
13784
  var init_anchor = __esm({
13732
13785
  "packages/core/dist/knowledge/anchor.js"() {
13733
- SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", ".next", ".worktrees", ".planning", ".converted"]);
13786
+ SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", ".next", ".worktrees", ".planning", ".converted", ".venv", "venv", "__pycache__"]);
13734
13787
  CODE_EXT = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs", ".java", ".kt", ".swift", ".rb", ".php", ".cs", ".sol", ".sql", ".md"]);
13735
- MAX_FILES = 5e3;
13788
+ MAX_FILES = 2e4;
13736
13789
  MAX_FILE_BYTES = 256 * 1024;
13737
13790
  MAX_PATHS = 5;
13738
13791
  MAX_SYMBOLS = 10;
@@ -13776,8 +13829,8 @@ function validateCandidate(c) {
13776
13829
  }
13777
13830
  return p;
13778
13831
  }
13779
- function upsertSource(fm, src) {
13780
- const i = fm.sources.findIndex((s) => s.ref === src.ref);
13832
+ function upsertSource(fm, src, canon) {
13833
+ const i = fm.sources.findIndex((s) => canon(s.ref) === canon(src.ref));
13781
13834
  if (i < 0) {
13782
13835
  fm.sources.push(src);
13783
13836
  return true;
@@ -13801,7 +13854,8 @@ function applyCandidates(input, opts) {
13801
13854
  const norm = /* @__PURE__ */ new Map();
13802
13855
  const totals = /* @__PURE__ */ new Map();
13803
13856
  const touched = /* @__PURE__ */ new Set();
13804
- const complete = new Set(input.complete ?? []);
13857
+ const canon = refCanon(kbRoot, opts.sources ?? []);
13858
+ const complete = new Set((input.complete ?? []).map(canon));
13805
13859
  for (const cand of input.candidates) {
13806
13860
  const problems = validateCandidate(cand);
13807
13861
  if (problems.length) {
@@ -13829,7 +13883,7 @@ function applyCandidates(input, opts) {
13829
13883
  ev2("item.rejected", cand.id, { reason: "ungrounded" });
13830
13884
  continue;
13831
13885
  }
13832
- const hit = (opts.errata ?? []).find((e) => e.ref === cand.source && ((o) => !!o && spansOverlap(o, offset))(findQuote(hay, e.quote)));
13886
+ const hit = (opts.errata ?? []).find((e) => canon(e.ref) === canon(cand.source) && ((o) => !!o && spansOverlap(o, offset))(findQuote(hay, e.quote)));
13833
13887
  if (hit) {
13834
13888
  tot.total--;
13835
13889
  res.rejected.push({ id: cand.id, source: cand.source, reason: `errata:${hit.id}` });
@@ -13839,7 +13893,7 @@ function applyCandidates(input, opts) {
13839
13893
  let id = cand.id;
13840
13894
  for (const [wid, w] of work) {
13841
13895
  const span = (s) => s.sha === st.sha ? s.offset : findQuote(hay, s.quote) ?? s.offset;
13842
- if (w.fm.sources.some((s) => s.ref === cand.source && s.offset && ((o) => !!o && spansOverlap(o, offset))(span(s)))) {
13896
+ if (w.fm.sources.some((s) => canon(s.ref) === canon(cand.source) && s.offset && ((o) => !!o && spansOverlap(o, offset))(span(s)))) {
13843
13897
  id = wid;
13844
13898
  break;
13845
13899
  }
@@ -13854,7 +13908,7 @@ function applyCandidates(input, opts) {
13854
13908
 
13855
13909
  ${cand.rationale.trim()}` : "");
13856
13910
  if (target && contradicted.length === 0 && sameMeaning(target, cand)) {
13857
- let changed = upsertSource(target.fm, src);
13911
+ let changed = upsertSource(target.fm, src, canon);
13858
13912
  if (target.fm.status === "stale") {
13859
13913
  target.fm.status = "active";
13860
13914
  changed = true;
@@ -13905,7 +13959,7 @@ ${cand.rationale.trim()}` : "");
13905
13959
  if (!other.startsWith(`${id}@`) || !other.endsWith(".md") || path26.join(dir, other) === file)
13906
13960
  continue;
13907
13961
  const old = matter4(fs25.readFileSync(path26.join(dir, other), "utf8"));
13908
- if (!old.data.sources?.some((s) => s.ref === cand.source))
13962
+ if (!old.data.sources?.some((s) => canon(s.ref) === canon(cand.source)))
13909
13963
  continue;
13910
13964
  fs25.writeFileSync(path26.join(dir, other.replace(/\.md$/, ".superseded")), matter4.stringify(old.content, { ...old.data, supersededBy: path26.basename(file) }));
13911
13965
  fs25.rmSync(path26.join(dir, other));
@@ -13919,18 +13973,19 @@ ${cand.rationale.trim()}` : "");
13919
13973
  for (const [id, w] of work) {
13920
13974
  if (touched.has(id) || w.fm.status !== "active" || w.isNew)
13921
13975
  continue;
13922
- if (w.fm.sources.length && w.fm.sources.every((s) => complete.has(s.ref))) {
13976
+ if (w.fm.sources.length && w.fm.sources.every((s) => complete.has(canon(s.ref)))) {
13923
13977
  w.fm.status = "stale";
13924
13978
  w.dirty = true;
13925
13979
  res.stale.push(id);
13926
13980
  }
13927
13981
  }
13982
+ const anchorCache = /* @__PURE__ */ new Map();
13928
13983
  for (const w of work.values()) {
13929
13984
  if (!w.dirty || !opts.projects?.length)
13930
13985
  continue;
13931
13986
  const anchors = { ...w.fm.anchors ?? {} };
13932
13987
  for (const p of opts.projects) {
13933
- const a = computeAnchor(p.root, w.fm.triggers);
13988
+ const a = computeAnchor(p.root, w.fm.triggers, anchorCache);
13934
13989
  if (a)
13935
13990
  anchors[p.name] = a;
13936
13991
  }
@@ -13945,7 +14000,7 @@ ${cand.rationale.trim()}` : "");
13945
14000
  if (!fs25.existsSync(w.file) || fs25.readFileSync(w.file, "utf8") !== text)
13946
14001
  fs25.writeFileSync(w.file, text);
13947
14002
  }
13948
- res.orphanedErrata = orphanedErrata(kbRoot, opts.errata ?? [], totals.keys());
14003
+ res.orphanedErrata = orphanedErrata(kbRoot, opts.sources ?? [], opts.errata ?? [], totals.keys());
13949
14004
  for (const [ref, t] of totals)
13950
14005
  if (needsDocling(t.dropped, t.total))
13951
14006
  res.doclingFlags.push({ ref, ...t });
@@ -14144,7 +14199,7 @@ var init_schedule = __esm({
14144
14199
 
14145
14200
  // packages/core/dist/knowledge/review.js
14146
14201
  import crypto4 from "crypto";
14147
- import { execFileSync as execFileSync7 } from "child_process";
14202
+ import { execFileSync as execFileSync8 } from "child_process";
14148
14203
  import fs27 from "fs";
14149
14204
  import path28 from "path";
14150
14205
  import matter5 from "gray-matter";
@@ -14174,11 +14229,7 @@ function priorityOfRef(c, ref) {
14174
14229
  if (ref.startsWith("agent:"))
14175
14230
  return "agent";
14176
14231
  const root = kbDir(c.cfg, c.kb);
14177
- for (const e of c.sources)
14178
- for (const f of expandSource(root, e))
14179
- if (refFor(root, f) === ref)
14180
- return e.priority;
14181
- return "project";
14232
+ return c.sources.find((e) => sourceCovers(root, c.sources, e, ref))?.priority ?? "project";
14182
14233
  }
14183
14234
  function priorityOfRule(c, r) {
14184
14235
  return r.sources.map((s) => priorityOfRef(c, s.ref)).sort((a, b) => PRIORITY_RANK[b] - PRIORITY_RANK[a])[0] ?? "project";
@@ -14215,7 +14266,7 @@ function reindex(c) {
14215
14266
  function anchorsFor(c, fm) {
14216
14267
  const anchors = { ...fm.anchors ?? {} };
14217
14268
  for (const p of c.projects ?? []) {
14218
- const a = computeAnchor(p.root, fm.triggers);
14269
+ const a = computeAnchor(p.root, fm.triggers, c.anchorCache);
14219
14270
  if (a)
14220
14271
  anchors[p.name] = a;
14221
14272
  }
@@ -14258,8 +14309,9 @@ function mergeInto(c, p, targetId, live, events) {
14258
14309
  if (!t)
14259
14310
  return false;
14260
14311
  const tf = readFm(t.file);
14312
+ const canon = refCanon(kbDir(c.cfg, c.kb), c.sources);
14261
14313
  for (const s of p.rule.sources)
14262
- if (!tf.fm.sources.some((x) => x.ref === s.ref))
14314
+ if (!tf.fm.sources.some((x) => canon(x.ref) === canon(s.ref)))
14263
14315
  tf.fm.sources.push(s);
14264
14316
  fs27.writeFileSync(t.file, fileText(tf.fm, tf.body));
14265
14317
  fs27.rmSync(p.rule.file);
@@ -14272,7 +14324,7 @@ function evaluateCodeConflict(c, rule, cc) {
14272
14324
  const root = proj?.root;
14273
14325
  const code = root ? g.lastCommit(root, cc.files) : null;
14274
14326
  const kbRoot = kbDir(c.cfg, c.kb);
14275
- const docDates = rule.sources.map((s) => g.docDate(fileForRef(kbRoot, s.ref))).filter((d2) => !!d2).sort();
14327
+ const docDates = rule.sources.map((s) => g.docDate(refPath(kbRoot, c.sources, s.ref))).filter((d2) => !!d2).sort();
14276
14328
  const docDate = docDates[docDates.length - 1];
14277
14329
  const docRev = docRevOf(rule);
14278
14330
  const evidence = { docRev, codeRev: code?.rev ?? "unknown", files: cc.files, project: cc.project };
@@ -14294,6 +14346,7 @@ function evaluateCodeConflict(c, rule, cc) {
14294
14346
  };
14295
14347
  }
14296
14348
  function applyDecisions(file, c) {
14349
+ c = { ...c, anchorCache: /* @__PURE__ */ new Map() };
14297
14350
  const now = nowOf(c);
14298
14351
  const kbRoot = kbDir(c.cfg, c.kb);
14299
14352
  const threshold = reconcileConfig(c.cfg, c.kb).autoApproveConfidence;
@@ -14447,8 +14500,9 @@ function applyPair(pd, c, res, threshold, events, kbRoot, now) {
14447
14500
  return;
14448
14501
  }
14449
14502
  const kf = readFm(keep.file);
14503
+ const canon = refCanon(kbRoot, c.sources);
14450
14504
  for (const s of drop.sources)
14451
- if (!kf.fm.sources.some((x) => x.ref === s.ref))
14505
+ if (!kf.fm.sources.some((x) => canon(x.ref) === canon(s.ref)))
14452
14506
  kf.fm.sources.push(s);
14453
14507
  kf.fm.supersedes = [.../* @__PURE__ */ new Set([...kf.fm.supersedes ?? [], drop.id])];
14454
14508
  fs27.writeFileSync(keep.file, fileText(kf.fm, kf.body));
@@ -14594,7 +14648,7 @@ var init_review = __esm({
14594
14648
  escalationsFile = (kbRoot) => path28.join(kbRoot, "escalations.json");
14595
14649
  verdictsFile = (kbRoot) => path28.join(kbRoot, "verdicts.jsonl");
14596
14650
  needsYouCount = (kbRoot) => readEscalations(kbRoot).filter((e) => e.needsYou).length;
14597
- git = (root, args) => execFileSync7("git", ["-C", root, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
14651
+ git = (root, args) => execFileSync8("git", ["-C", root, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
14598
14652
  realGit = {
14599
14653
  lastCommit(root, files) {
14600
14654
  try {
@@ -15033,7 +15087,7 @@ function renderReport(cfg, kb, o) {
15033
15087
  if (info.length)
15034
15088
  L.push("", `## For information (${info.length})`, "", ...info.map((e) => `- **${e.key}**: ${e.explanation}`));
15035
15089
  const { errata, errors: errataErrors } = loadErrata(kbDir(cfg, kb));
15036
- const orphans = orphanedErrata(kbDir(cfg, kb), errata);
15090
+ const orphans = orphanedErrata(kbDir(cfg, kb), loadSources(kbDir(cfg, kb)).sources, errata);
15037
15091
  if (orphans.length || errataErrors.length) {
15038
15092
  L.push("", `## Errata to re-check (${orphans.length + errataErrors.length})`, "", ...orphans.map((e) => `- **${e.id}** (${e.ref}): quote no longer found in the source; the raw file changed. Re-check it in errata.yaml.`), ...errataErrors.map((e) => `- errata.yaml: ${e}`));
15039
15093
  }
@@ -15055,6 +15109,7 @@ var init_report = __esm({
15055
15109
  init_errata();
15056
15110
  init_layers();
15057
15111
  init_paths();
15112
+ init_sources();
15058
15113
  init_review();
15059
15114
  init_scores();
15060
15115
  }
@@ -15390,7 +15445,6 @@ __export(dist_exports, {
15390
15445
  extractToolPayload: () => extractToolPayload,
15391
15446
  extractUserIntentFromTranscript: () => extractUserIntentFromTranscript,
15392
15447
  fallsBackToPane: () => fallsBackToPane,
15393
- fileForRef: () => fileForRef,
15394
15448
  findCrewPane: () => findCrewPane,
15395
15449
  findLegacyKbs: () => findLegacyKbs,
15396
15450
  findLegacyOverlays: () => findLegacyOverlays,
@@ -15532,7 +15586,10 @@ __export(dist_exports, {
15532
15586
  recoverStall: () => recoverStall,
15533
15587
  reduce: () => reduce,
15534
15588
  reduceLifecycle: () => reduceLifecycle,
15589
+ refCandidates: () => refCandidates,
15590
+ refCanon: () => refCanon,
15535
15591
  refFor: () => refFor,
15592
+ refPath: () => refPath,
15536
15593
  releaseDaemonLock: () => releaseDaemonLock,
15537
15594
  removeRunningMarker: () => removeRunningMarker,
15538
15595
  removeSource: () => removeSource,
@@ -15565,6 +15622,7 @@ __export(dist_exports, {
15565
15622
  resolveSetupGroup: () => resolveSetupGroup,
15566
15623
  resolveSetupToken: () => resolveSetupToken,
15567
15624
  resolveSetupUserId: () => resolveSetupUserId,
15625
+ resolveSourceRef: () => resolveSourceRef,
15568
15626
  restartDaemonIfRunning: () => restartDaemonIfRunning,
15569
15627
  rotateIfNeeded: () => rotateIfNeeded,
15570
15628
  ruleAuditScope: () => ruleAuditScope,
@@ -15832,31 +15890,31 @@ import path32 from "path";
15832
15890
  // packages/agents/dist/projection/cursor.js
15833
15891
  import { mkdir, readFile, writeFile } from "fs/promises";
15834
15892
  import path33 from "path";
15835
- import os10 from "os";
15893
+ import os9 from "os";
15836
15894
 
15837
15895
  // packages/agents/dist/projection/codex.js
15838
15896
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
15839
15897
  import path34 from "path";
15840
- import os11 from "os";
15898
+ import os10 from "os";
15841
15899
 
15842
15900
  // packages/agents/dist/projection/gemini.js
15843
15901
  import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
15844
15902
  import path35 from "path";
15845
- import os12 from "os";
15903
+ import os11 from "os";
15846
15904
 
15847
15905
  // packages/agents/dist/projection/opencode.js
15848
15906
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
15849
15907
  import path36 from "path";
15850
- import os13 from "os";
15908
+ import os12 from "os";
15851
15909
 
15852
15910
  // packages/agents/dist/projection/opencode-skills.js
15853
15911
  import fs33 from "fs";
15854
- import os14 from "os";
15912
+ import os13 from "os";
15855
15913
  import path37 from "path";
15856
15914
  var OPENCODE_SKILL_MARKER = ".squadrant-managed";
15857
15915
  var MARKER_BODY = "squadrant\n";
15858
15916
  function defaultOpencodeSkillsRoot(env = process.env) {
15859
- const configHome = env.XDG_CONFIG_HOME || path37.join(os14.homedir(), ".config");
15917
+ const configHome = env.XDG_CONFIG_HOME || path37.join(os13.homedir(), ".config");
15860
15918
  return path37.join(configHome, "opencode", "skills");
15861
15919
  }
15862
15920
  var SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
@@ -15944,7 +16002,7 @@ function syncShippedOpencodeSkills(opts) {
15944
16002
  // packages/agents/dist/projection/claude.js
15945
16003
  import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
15946
16004
  import path38 from "path";
15947
- import os15 from "os";
16005
+ import os14 from "os";
15948
16006
 
15949
16007
  // packages/agents/dist/codex/app-server-client.js
15950
16008
  import { EventEmitter } from "events";
@@ -17289,7 +17347,7 @@ var ClaudeReceiptListener = class {
17289
17347
 
17290
17348
  // packages/agents/dist/claude/api-key-approval.js
17291
17349
  import fs34 from "fs";
17292
- import os16 from "os";
17350
+ import os15 from "os";
17293
17351
  import path39 from "path";
17294
17352
 
17295
17353
  // packages/agents/dist/opencode/http-channel.js
@@ -17460,7 +17518,7 @@ function createOpencodeFactAdapter(deps) {
17460
17518
 
17461
17519
  // packages/agents/dist/rules-hooks/install.js
17462
17520
  import fs35 from "fs";
17463
- import os17 from "os";
17521
+ import os16 from "os";
17464
17522
  import path40 from "path";
17465
17523
 
17466
17524
  // packages/workspaces/dist/runtimes/cmux.js
@@ -17469,7 +17527,7 @@ init_dist();
17469
17527
  init_dist();
17470
17528
  init_dist2();
17471
17529
  init_dist2();
17472
- import { execFile as execFile3, execFileSync as execFileSync8 } from "child_process";
17530
+ import { execFile as execFile3, execFileSync as execFileSync9 } from "child_process";
17473
17531
  var CMUX_TIMEOUT = 15e3;
17474
17532
  var CmuxTimeoutError = class extends Error {
17475
17533
  constructor(cmd) {
@@ -18143,7 +18201,7 @@ var RuntimeRegistry = class {
18143
18201
 
18144
18202
  // packages/workspaces/dist/notifiers/cmux.js
18145
18203
  init_dist();
18146
- import { execFile as execFileCb, execFileSync as execFileSync9 } from "child_process";
18204
+ import { execFile as execFileCb, execFileSync as execFileSync10 } from "child_process";
18147
18205
  import { promisify as promisify3 } from "util";
18148
18206
  var execFile4 = promisify3(execFileCb);
18149
18207
  function createCmuxNotifier(_scope) {
@@ -18151,7 +18209,7 @@ function createCmuxNotifier(_scope) {
18151
18209
  name: "cmux",
18152
18210
  async probe() {
18153
18211
  try {
18154
- execFileSync9(resolveCmuxBin(), ["capabilities"], { encoding: "utf-8", stdio: "pipe", timeout: CMUX_TIMEOUT });
18212
+ execFileSync10(resolveCmuxBin(), ["capabilities"], { encoding: "utf-8", stdio: "pipe", timeout: CMUX_TIMEOUT });
18155
18213
  return { installed: true, reachable: true };
18156
18214
  } catch (err) {
18157
18215
  const code = err.code;
@@ -18361,9 +18419,9 @@ import { homedir as homedir11 } from "os";
18361
18419
  // packages/workspaces/dist/cmux-daemon/store-fingerprint.js
18362
18420
  init_dist();
18363
18421
  init_dist2();
18364
- import { execFileSync as execFileSync10 } from "child_process";
18422
+ import { execFileSync as execFileSync11 } from "child_process";
18365
18423
  function defaultExecPs(pid) {
18366
- return execFileSync10("ps", ["-o", "command=", "-p", String(pid)], {
18424
+ return execFileSync11("ps", ["-o", "command=", "-p", String(pid)], {
18367
18425
  encoding: "utf-8",
18368
18426
  stdio: ["ignore", "pipe", "ignore"]
18369
18427
  });