@inerrata-corporation/errata 2.0.2-dev.362 → 2.0.2-dev.437

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/errata.mjs CHANGED
@@ -18036,6 +18036,207 @@ var init_aggregate = __esm({
18036
18036
  }
18037
18037
  });
18038
18038
 
18039
+ // ../../packages/embedding/src/model.ts
18040
+ import { mkdirSync as mkdirSync3, existsSync as existsSync4 } from "node:fs";
18041
+ import { homedir } from "node:os";
18042
+ import { dirname as dirname3, join as join3 } from "node:path";
18043
+ import { createRequire } from "node:module";
18044
+ function semanticFloorFor(version2) {
18045
+ if (version2 === EMBEDDING_VERSION || version2 === MODEL_EMBEDDING_VERSION) return 0.25;
18046
+ return 0.5;
18047
+ }
18048
+ function noteHashFallback() {
18049
+ if (warnedHashFallback) return;
18050
+ if (process.env["ERRATA_EMBED_HASH_ONLY"] === "1") return;
18051
+ warnedHashFallback = true;
18052
+ console.warn(
18053
+ `[errata] embedding model unavailable (${lastError?.message ?? "unknown"}) \u2014 falling back to ${EMBEDDING_VERSION} hash vectors; semantic ranking runs at reduced fidelity`
18054
+ );
18055
+ }
18056
+ function getCacheDir() {
18057
+ return process.env["ERRATA_MODEL_CACHE"] ?? join3(homedir(), ".errata", "models");
18058
+ }
18059
+ async function loadTransformers() {
18060
+ try {
18061
+ return await import("@huggingface/transformers");
18062
+ } catch (err2) {
18063
+ void err2;
18064
+ }
18065
+ try {
18066
+ const seaResourceBase = join3(
18067
+ // execPath dir is where errata.exe lives; resources/ rides alongside.
18068
+ dirname3(process.execPath),
18069
+ "resources",
18070
+ "_resolve.js"
18071
+ );
18072
+ const resourceRequire = createRequire(seaResourceBase);
18073
+ return resourceRequire("@huggingface/transformers");
18074
+ } catch (err2) {
18075
+ lastError = err2 instanceof Error ? err2 : new Error(String(err2));
18076
+ return null;
18077
+ }
18078
+ }
18079
+ async function loadPipeline() {
18080
+ const cacheDir = getCacheDir();
18081
+ if (!existsSync4(cacheDir)) mkdirSync3(cacheDir, { recursive: true });
18082
+ try {
18083
+ const tx = await loadTransformers();
18084
+ if (!tx) {
18085
+ throw lastError ?? new Error("transformers package unavailable");
18086
+ }
18087
+ tx.env.cacheDir = cacheDir;
18088
+ tx.env.useFSCache = true;
18089
+ tx.env.allowLocalModels = true;
18090
+ tx.env.allowRemoteModels = process.env["ERRATA_OFFLINE"] !== "1";
18091
+ const pipe2 = await tx.pipeline("feature-extraction", MODEL_NAME, {
18092
+ // Quantized weights cut size 4x; quality drop is negligible for
18093
+ // short-text similarity. Override via env if you want full
18094
+ // precision.
18095
+ dtype: process.env["ERRATA_MODEL_DTYPE"] ?? "q8"
18096
+ });
18097
+ return pipe2;
18098
+ } catch (err2) {
18099
+ lastError = err2 instanceof Error ? err2 : new Error(String(err2));
18100
+ return null;
18101
+ }
18102
+ }
18103
+ async function ensureModelLoaded() {
18104
+ if (!pipelinePromise) {
18105
+ pipelinePromise = loadPipeline();
18106
+ }
18107
+ return pipelinePromise;
18108
+ }
18109
+ async function embedTextWithModel(text) {
18110
+ const pipe2 = await ensureModelLoaded();
18111
+ if (!pipe2) {
18112
+ throw new Error(
18113
+ `embedding model could not be loaded: ${lastError?.message ?? "unknown"}`
18114
+ );
18115
+ }
18116
+ if (!text) return new Array(MODEL_EMBEDDING_DIM).fill(0);
18117
+ const out2 = await pipe2(text, { pooling: "mean", normalize: true });
18118
+ return Array.from(out2.data);
18119
+ }
18120
+ async function embedBatchWithModelOrHash(texts) {
18121
+ if (texts.length === 0) return [];
18122
+ if (process.env["ERRATA_EMBED_HASH_ONLY"] === "1") {
18123
+ return texts.map((t) => {
18124
+ const v = embed(t);
18125
+ return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
18126
+ });
18127
+ }
18128
+ try {
18129
+ const pipe2 = await ensureModelLoaded();
18130
+ if (pipe2) {
18131
+ const out2 = await pipe2(texts, {
18132
+ pooling: "mean",
18133
+ normalize: true
18134
+ });
18135
+ const dim = out2.dims[out2.dims.length - 1] ?? MODEL_EMBEDDING_DIM;
18136
+ const total = texts.length;
18137
+ const results = [];
18138
+ for (let i2 = 0; i2 < total; i2++) {
18139
+ const v = Array.from(out2.data.subarray(i2 * dim, (i2 + 1) * dim));
18140
+ results.push({ vector: v, version: MODEL_EMBEDDING_VERSION, dim });
18141
+ }
18142
+ return results;
18143
+ }
18144
+ } catch {
18145
+ }
18146
+ noteHashFallback();
18147
+ return texts.map((t) => {
18148
+ const v = embed(t);
18149
+ return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
18150
+ });
18151
+ }
18152
+ async function embedTextWithModelOrHash(text) {
18153
+ if (process.env["ERRATA_EMBED_HASH_ONLY"] === "1") {
18154
+ const v = embed(text);
18155
+ return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
18156
+ }
18157
+ try {
18158
+ const v = await embedTextWithModel(text);
18159
+ return { vector: v, version: MODEL_EMBEDDING_VERSION, dim: v.length };
18160
+ } catch {
18161
+ noteHashFallback();
18162
+ const v = embed(text);
18163
+ return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
18164
+ }
18165
+ }
18166
+ var MODEL_NAME, MODEL_EMBEDDING_DIM, MODEL_EMBEDDING_VERSION, pipelinePromise, lastError, warnedHashFallback;
18167
+ var init_model = __esm({
18168
+ "../../packages/embedding/src/model.ts"() {
18169
+ "use strict";
18170
+ init_src3();
18171
+ MODEL_NAME = "Xenova/all-MiniLM-L6-v2";
18172
+ MODEL_EMBEDDING_DIM = 384;
18173
+ MODEL_EMBEDDING_VERSION = "minilm-l6-v2";
18174
+ pipelinePromise = null;
18175
+ lastError = null;
18176
+ warnedHashFallback = false;
18177
+ }
18178
+ });
18179
+
18180
+ // ../../packages/embedding/src/index.ts
18181
+ function djb2(s) {
18182
+ let h = 5381;
18183
+ for (let i2 = 0; i2 < s.length; i2++) {
18184
+ h = (h << 5) + h + s.charCodeAt(i2) >>> 0;
18185
+ }
18186
+ return h;
18187
+ }
18188
+ function djb2Signed(s) {
18189
+ let h = 0;
18190
+ for (let i2 = 0; i2 < s.length; i2++) {
18191
+ h = h * 31 + s.charCodeAt(i2) | 0;
18192
+ }
18193
+ return h;
18194
+ }
18195
+ function tokenize(text) {
18196
+ return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 0 && t.length <= 40);
18197
+ }
18198
+ function* charTrigrams(text) {
18199
+ const s = text.toLowerCase();
18200
+ for (let i2 = 0; i2 <= s.length - 3; i2++) {
18201
+ yield s.slice(i2, i2 + 3);
18202
+ }
18203
+ }
18204
+ function embed(text) {
18205
+ const vec = new Array(EMBEDDING_DIM).fill(0);
18206
+ if (!text) return vec;
18207
+ for (const tok of tokenize(text)) {
18208
+ const idx = djb2(tok) % EMBEDDING_DIM;
18209
+ const sign = djb2Signed(tok) & 1 ? 1 : -1;
18210
+ vec[idx] = vec[idx] + sign;
18211
+ }
18212
+ for (const tri of charTrigrams(text)) {
18213
+ const idx = djb2("3:" + tri) % EMBEDDING_DIM;
18214
+ const sign = djb2Signed("3:" + tri) & 1 ? 1 : -1;
18215
+ vec[idx] = vec[idx] + 0.5 * sign;
18216
+ }
18217
+ let normSq = 0;
18218
+ for (const x of vec) normSq += x * x;
18219
+ if (normSq <= 0) return vec;
18220
+ const inv = 1 / Math.sqrt(normSq);
18221
+ for (let i2 = 0; i2 < vec.length; i2++) vec[i2] = vec[i2] * inv;
18222
+ return vec;
18223
+ }
18224
+ function cosine(a, b) {
18225
+ if (a.length !== b.length || a.length === 0) return 0;
18226
+ let dot = 0;
18227
+ for (let i2 = 0; i2 < a.length; i2++) dot += a[i2] * b[i2];
18228
+ return dot;
18229
+ }
18230
+ var EMBEDDING_DIM, EMBEDDING_VERSION;
18231
+ var init_src3 = __esm({
18232
+ "../../packages/embedding/src/index.ts"() {
18233
+ "use strict";
18234
+ init_model();
18235
+ EMBEDDING_DIM = 256;
18236
+ EMBEDDING_VERSION = "hash-v1";
18237
+ }
18238
+ });
18239
+
18039
18240
  // ../../packages/local-graph/src/problem-package-link.ts
18040
18241
  function buildPackageIndex(store) {
18041
18242
  const idx = /* @__PURE__ */ new Map();
@@ -18515,14 +18716,51 @@ function ingestDesignProblem(store, flag, opts) {
18515
18716
  const linkedPackages = linkProblemToPackages(store, problemId, linkText, opts.ts).linked;
18516
18717
  return { problemId, created, corroborated, rootCauseId, solutionId, anchored, linkedPackages };
18517
18718
  }
18719
+ function canonicalizePatternText(name2) {
18720
+ const stripped = name2.replace(/^\s*pattern(?:\s*[×x]\s*\d+)?\s*(?:\(exemplar\))?\s*[:—–-]\s*/i, "").replace(/\s+/g, " ").trim();
18721
+ return stripped.length > 0 ? stripped : name2.replace(/\s+/g, " ").trim();
18722
+ }
18518
18723
  function mintPatternNode(store, name2, ts) {
18519
- const display = name2.replace(/\s+/g, " ").trim();
18724
+ const display = canonicalizePatternText(name2);
18520
18725
  const id = `pat_${digest({ pattern: display.toLowerCase() })}`.slice(0, 56);
18521
- if (!store.getNode(id)) {
18522
- store.mergeNode(
18523
- buildNode(id, "Pattern", display, ts, { source: "convo", provisional: true })
18524
- );
18525
- }
18726
+ const exact = store.getNode(id);
18727
+ const reinforce = (node2, variant) => {
18728
+ const aliases = Array.isArray(node2.attrs["aliasTexts"]) ? [...node2.attrs["aliasTexts"]] : [];
18729
+ if (variant && variant !== node2.description && !aliases.includes(variant) && aliases.length < PATTERN_ALIAS_CAP) {
18730
+ aliases.push(variant);
18731
+ }
18732
+ store.updateNode(node2.id, {
18733
+ attrs: {
18734
+ ...node2.attrs,
18735
+ observedCount: (Number(node2.attrs["observedCount"]) || 1) + 1,
18736
+ ...aliases.length > 0 ? { aliasTexts: aliases } : {}
18737
+ },
18738
+ lastUpdatedAt: ts
18739
+ });
18740
+ return node2.id;
18741
+ };
18742
+ if (exact) return reinforce(exact);
18743
+ const emb = embed(display);
18744
+ let best = null;
18745
+ let bestCos = 0;
18746
+ for (const p of store.findNodesByLabel("Pattern")) {
18747
+ const e = p.embedding;
18748
+ if (!e || e.length !== EMBEDDING_DIM) continue;
18749
+ let d = 0;
18750
+ for (let i2 = 0; i2 < EMBEDDING_DIM; i2++) d += e[i2] * emb[i2];
18751
+ if (d > bestCos) {
18752
+ bestCos = d;
18753
+ best = p;
18754
+ }
18755
+ }
18756
+ if (best && bestCos >= PATTERN_DEDUP_COSINE) return reinforce(best, display);
18757
+ store.mergeNode({
18758
+ ...buildNode(id, "Pattern", display, ts, { source: "convo", provisional: true, observedCount: 1 }),
18759
+ // Embedded inline (sync hash) so the node participates in the gate
18760
+ // immediately — buildNode's empty embedding would leave every new pattern
18761
+ // invisible to dedup until the nightly embed pass.
18762
+ embedding: emb
18763
+ });
18526
18764
  return id;
18527
18765
  }
18528
18766
  function titleCaseDomain(name2) {
@@ -18895,15 +19133,18 @@ function markFixCandidates(store, t) {
18895
19133
  }
18896
19134
  return resolved;
18897
19135
  }
18898
- var DESIGN_PROMOTE_AT, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, MAX_ANCHOR_FILES, AUTO_MINT_PREFIX;
19136
+ var DESIGN_PROMOTE_AT, PATTERN_DEDUP_COSINE, PATTERN_ALIAS_CAP, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, MAX_ANCHOR_FILES, AUTO_MINT_PREFIX;
18899
19137
  var init_design_problem = __esm({
18900
19138
  "../../packages/local-graph/src/design-problem.ts"() {
18901
19139
  "use strict";
18902
19140
  init_src();
18903
19141
  init_src();
19142
+ init_src3();
18904
19143
  init_src2();
18905
19144
  init_problem_package_link();
18906
19145
  DESIGN_PROMOTE_AT = 1;
19146
+ PATTERN_DEDUP_COSINE = 0.85;
19147
+ PATTERN_ALIAS_CAP = 5;
18907
19148
  DEDUP_STOPWORDS = /* @__PURE__ */ new Set([
18908
19149
  "the",
18909
19150
  "and",
@@ -19177,7 +19418,55 @@ function percolate(l1, l2, opts) {
19177
19418
  result.touched = [...touched];
19178
19419
  return result;
19179
19420
  }
19180
- var PERCOLATING_LABELS, PERCOLATING_EDGES, PERCOLATING_DRIFT_EDGES;
19421
+ function migrateProjectAlias(l2, opts) {
19422
+ const { from, to } = opts;
19423
+ let nodesRewritten = 0;
19424
+ let edgesRewritten = 0;
19425
+ if (from === to) return { nodesRewritten, edgesRewritten };
19426
+ const rewrite = (seen) => {
19427
+ if (!seen.includes(from)) return null;
19428
+ const out2 = [];
19429
+ for (const p of seen.map((p2) => p2 === from ? to : p2)) if (!out2.includes(p)) out2.push(p);
19430
+ return out2;
19431
+ };
19432
+ const nodeUpdates = [];
19433
+ const edgeUpdates = [];
19434
+ const seenEdges = /* @__PURE__ */ new Set();
19435
+ for (const label of PROVENANCE_LABELS) {
19436
+ for (const n of l2.findNodesByLabel(label)) {
19437
+ const next = rewrite(observedProjects(n));
19438
+ if (next) nodeUpdates.push({ id: n.id, attrs: { ...n.attrs, observedInProjects: next } });
19439
+ for (const e of l2.outEdges(n.id, [...PERCOLATING_DRIFT_EDGES])) {
19440
+ if (seenEdges.has(e.id)) continue;
19441
+ seenEdges.add(e.id);
19442
+ const eSeen = Array.isArray(e.attrs["observedInProjects"]) ? e.attrs["observedInProjects"] : [];
19443
+ const eNext = rewrite(eSeen);
19444
+ if (eNext) edgeUpdates.push({ id: e.id, attrs: { ...e.attrs, observedInProjects: eNext } });
19445
+ }
19446
+ }
19447
+ }
19448
+ const CHUNK = 200;
19449
+ for (let i2 = 0; i2 < nodeUpdates.length; i2 += CHUNK) {
19450
+ const slice = nodeUpdates.slice(i2, i2 + CHUNK);
19451
+ l2.transaction(() => {
19452
+ for (const u of slice) {
19453
+ l2.updateNode(u.id, { attrs: u.attrs, lastUpdatedAt: opts.ts });
19454
+ nodesRewritten++;
19455
+ }
19456
+ });
19457
+ }
19458
+ for (let i2 = 0; i2 < edgeUpdates.length; i2 += CHUNK) {
19459
+ const slice = edgeUpdates.slice(i2, i2 + CHUNK);
19460
+ l2.transaction(() => {
19461
+ for (const u of slice) {
19462
+ l2.updateEdge(u.id, { attrs: u.attrs, lastSeenAt: opts.ts });
19463
+ edgesRewritten++;
19464
+ }
19465
+ });
19466
+ }
19467
+ return { nodesRewritten, edgesRewritten };
19468
+ }
19469
+ var PERCOLATING_LABELS, PERCOLATING_EDGES, PERCOLATING_DRIFT_EDGES, PROVENANCE_LABELS;
19181
19470
  var init_percolate = __esm({
19182
19471
  "../../packages/local-graph/src/percolate.ts"() {
19183
19472
  "use strict";
@@ -19185,6 +19474,7 @@ var init_percolate = __esm({
19185
19474
  PERCOLATING_LABELS = ["Claim", "Problem", "Solution"];
19186
19475
  PERCOLATING_EDGES = ["CAUSED_BY", "SOLVED_BY", "CONTRADICTS"];
19187
19476
  PERCOLATING_DRIFT_EDGES = ["CONTINUES", "REVEALED_BY", "SUPERSEDED_BY", "SPLIT_INTO"];
19477
+ PROVENANCE_LABELS = ["Claim", "Problem", "Solution", "Triage", "RootCause"];
19188
19478
  }
19189
19479
  });
19190
19480
 
@@ -19912,7 +20202,7 @@ var init_bko = __esm({
19912
20202
  });
19913
20203
 
19914
20204
  // ../../packages/math/src/index.ts
19915
- var init_src3 = __esm({
20205
+ var init_src4 = __esm({
19916
20206
  "../../packages/math/src/index.ts"() {
19917
20207
  "use strict";
19918
20208
  init_constants();
@@ -19937,463 +20227,6 @@ var init_src3 = __esm({
19937
20227
  }
19938
20228
  });
19939
20229
 
19940
- // ../../packages/local-graph/src/abstraction.ts
19941
- function sharedScope(l2, memberIds) {
19942
- const shared = (field) => {
19943
- const vals = /* @__PURE__ */ new Set();
19944
- for (const id of memberIds) {
19945
- const s = l2.getNode(id)?.attrs["scope"] ?? {};
19946
- if (typeof s[field] === "string") vals.add(s[field]);
19947
- }
19948
- return vals.size === 1 ? [...vals][0] : void 0;
19949
- };
19950
- const lang = shared("lang");
19951
- const versionRange = shared("versionRange");
19952
- return { ...lang ? { lang } : {}, ...versionRange ? { versionRange } : {} };
19953
- }
19954
- function buildCandidate(id, ts, members, contexts, originMachine, scope) {
19955
- return {
19956
- id,
19957
- label: "Claim",
19958
- description: "",
19959
- // empty — the harness distills the prose in P3
19960
- extractionConfidence: 0.4,
19961
- extractionSource: "agent-observed",
19962
- embedding: [],
19963
- cumulativeSurprise: 0,
19964
- peakSurprise: 0,
19965
- cumulativeHits: 1,
19966
- lastUpdatedAt: ts,
19967
- createdAt: ts,
19968
- memoryTier: "short-term",
19969
- pageRank: 0,
19970
- isLandmark: false,
19971
- community: null,
19972
- stability: "unstable",
19973
- attrs: {
19974
- abstractionLevel: ABSTRACTION_LEVEL.PRINCIPLE,
19975
- kind: "abstraction-candidate",
19976
- provisional: true,
19977
- pendingDistillation: true,
19978
- // membership is the unit of truth — the harness validates its fence against
19979
- // this set (P3 / C3) and may only phrase, never alter, it.
19980
- members,
19981
- memberContexts: contexts,
19982
- distinctContexts: contexts.length,
19983
- // standard Claim envelope so it crystallizes/syncs unchanged once distilled
19984
- truthKind: "contextual",
19985
- // Derived from members (P1): a single-language cluster becomes a lang-scoped
19986
- // principle; a cross-language one stays universal (`{}`).
19987
- scope,
19988
- groundedSupport: 0,
19989
- sources: [],
19990
- crystallized: "hypothesis",
19991
- confidence: 0.4,
19992
- ...originMachine ? { originMachine } : {}
19993
- }
19994
- };
19995
- }
19996
- function mergeGeneralizes(store, principleId, memberId, ts) {
19997
- store.mergeEdge({
19998
- id: `edge_${digest({ from: principleId, type: "GENERALIZES", to: memberId })}`.slice(0, 24),
19999
- from: principleId,
20000
- to: memberId,
20001
- type: "GENERALIZES",
20002
- confidence: 0.4,
20003
- extractionSource: "agent-observed",
20004
- createdAt: ts,
20005
- lastSeenAt: ts,
20006
- navSuccesses: 0,
20007
- navFailures: 0,
20008
- attrs: { provisional: true }
20009
- });
20010
- }
20011
- function induceAbstractions(l2, opts) {
20012
- const K = opts.minClusterSize ?? DEFAULT_MIN_CLUSTER_SIZE;
20013
- const minCtx = opts.minDistinctContexts ?? DEFAULT_MIN_DISTINCT_CONTEXTS;
20014
- const report = {
20015
- communities: 0,
20016
- candidatesMinted: 0,
20017
- candidatesExisting: 0,
20018
- skippedSmall: 0,
20019
- skippedSingleContext: 0,
20020
- generalizesEdges: 0
20021
- };
20022
- const ids = /* @__PURE__ */ new Set();
20023
- for (const label of PERCOLATING_LABELS) {
20024
- for (const n of l2.findNodesByLabel(label)) {
20025
- if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) >= ABSTRACTION_LEVEL.PRINCIPLE) {
20026
- continue;
20027
- }
20028
- if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
20029
- ids.add(n.id);
20030
- }
20031
- }
20032
- if (ids.size === 0) return report;
20033
- const adj = /* @__PURE__ */ new Map();
20034
- const protect = [];
20035
- const add = (a, b, w) => {
20036
- const l = adj.get(a) ?? [];
20037
- l.push({ to: b, weight: w });
20038
- adj.set(a, l);
20039
- };
20040
- for (const id of ids) {
20041
- for (const e of l2.outEdges(id, [...PERCOLATING_EDGES])) {
20042
- if (!ids.has(e.to)) continue;
20043
- const conf = e.confidence > 0 ? e.confidence : 0.5;
20044
- const w = Math.max(EDGE_WEIGHT[e.type] ?? 1, 0.1) * conf;
20045
- add(id, e.to, w);
20046
- add(e.to, id, w);
20047
- if (isCausalProtected(e.type)) protect.push([id, e.to]);
20048
- }
20049
- }
20050
- const comm = detectCommunitiesLeiden({ nodeIds: [...ids], adj }, { protect });
20051
- report.communities = comm.count;
20052
- const members = /* @__PURE__ */ new Map();
20053
- for (const [id, c] of comm.community) {
20054
- const l = members.get(c) ?? [];
20055
- l.push(id);
20056
- members.set(c, l);
20057
- }
20058
- l2.transaction(() => {
20059
- for (const mem of members.values()) {
20060
- if (opts.touched && !mem.some((id) => opts.touched.has(id))) continue;
20061
- if (mem.length < K) {
20062
- report.skippedSmall++;
20063
- continue;
20064
- }
20065
- if (mem.some((id) => l2.inEdges(id, ["GENERALIZES"]).length > 0)) {
20066
- report.candidatesExisting++;
20067
- continue;
20068
- }
20069
- const contexts = /* @__PURE__ */ new Set();
20070
- for (const id of mem) {
20071
- const n = l2.getNode(id);
20072
- const obs = n?.attrs["observedInProjects"];
20073
- if (Array.isArray(obs)) {
20074
- for (const ws of obs) contexts.add(opts.contextOf(ws) ?? `project:${ws}`);
20075
- }
20076
- }
20077
- if (contexts.size < minCtx) {
20078
- report.skippedSingleContext++;
20079
- continue;
20080
- }
20081
- const sorted = [...mem].sort();
20082
- const principleId = `princ_${digest({ members: sorted })}`.slice(0, 56);
20083
- l2.mergeNode(
20084
- buildCandidate(principleId, opts.ts, sorted, [...contexts].sort(), opts.originMachine, sharedScope(l2, sorted))
20085
- );
20086
- for (const id of sorted) {
20087
- mergeGeneralizes(l2, principleId, id, opts.ts);
20088
- report.generalizesEdges++;
20089
- }
20090
- report.candidatesMinted++;
20091
- }
20092
- });
20093
- return report;
20094
- }
20095
- function pendingAbstractions(l2) {
20096
- const out2 = [];
20097
- for (const n of l2.findNodesByLabel("Claim")) {
20098
- if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) < ABSTRACTION_LEVEL.PRINCIPLE) continue;
20099
- if (n.attrs["provisional"] !== true) continue;
20100
- const memberIds = Array.isArray(n.attrs["members"]) ? n.attrs["members"] : [];
20101
- const members = memberIds.map((id) => {
20102
- const m = l2.getNode(id);
20103
- return m ? { id: m.id, label: m.label, description: m.description } : { id, label: "?", description: "(member missing)" };
20104
- });
20105
- out2.push({
20106
- candidate: n.id,
20107
- distinctContexts: Number(n.attrs["distinctContexts"] ?? 0),
20108
- members
20109
- });
20110
- }
20111
- return out2;
20112
- }
20113
- function parseAbstractionFences(text) {
20114
- const out2 = [];
20115
- const block = /```errata-abstraction[^\n]*\n([\s\S]*?)```/g;
20116
- let m;
20117
- while ((m = block.exec(text)) !== null) {
20118
- const fields = {};
20119
- for (const line of m[1].split(/\r?\n/)) {
20120
- const kv = /^\s*(candidate|principle|covers)\s*:\s*(.+?)\s*$/i.exec(line);
20121
- if (kv) fields[kv[1].toLowerCase()] = kv[2].trim();
20122
- }
20123
- if (!fields["candidate"] || !fields["principle"] || !fields["covers"]) continue;
20124
- const covers = fields["covers"].split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
20125
- if (covers.length === 0) continue;
20126
- out2.push({ candidate: fields["candidate"], principle: fields["principle"], covers });
20127
- }
20128
- return out2;
20129
- }
20130
- function applyAbstractionFence(l2, fence, ts) {
20131
- const node2 = l2.getNode(fence.candidate);
20132
- if (!node2 || node2.label !== "Claim" || Number(node2.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) < ABSTRACTION_LEVEL.PRINCIPLE) {
20133
- return { applied: false, principleId: fence.candidate, reason: "no such abstraction candidate" };
20134
- }
20135
- if (node2.attrs["provisional"] !== true) {
20136
- return { applied: false, principleId: fence.candidate, reason: "candidate already distilled" };
20137
- }
20138
- const members = Array.isArray(node2.attrs["members"]) ? node2.attrs["members"] : [];
20139
- const want = new Set(members);
20140
- const got = new Set(fence.covers);
20141
- const missing = members.filter((id) => !got.has(id));
20142
- const invented = fence.covers.filter((id) => !want.has(id));
20143
- if (missing.length > 0 || invented.length > 0) {
20144
- return {
20145
- applied: false,
20146
- principleId: fence.candidate,
20147
- reason: `coverage mismatch \u2014 ${missing.length} member(s) uncovered, ${invented.length} non-member(s) invented`
20148
- };
20149
- }
20150
- const principle = fence.principle.trim();
20151
- if (!principle) return { applied: false, principleId: fence.candidate, reason: "empty principle" };
20152
- const { revisit: _r, revisitReason: _rr, revisitSinceTs: _rs, ...rest2 } = node2.attrs;
20153
- void _r;
20154
- void _rr;
20155
- void _rs;
20156
- l2.updateNode(fence.candidate, {
20157
- description: principle,
20158
- attrs: { ...rest2, provisional: false, pendingDistillation: false, distilledAt: ts },
20159
- lastUpdatedAt: ts
20160
- });
20161
- return { applied: true, principleId: fence.candidate };
20162
- }
20163
- function revisitContradictedPrinciples(l2, ts) {
20164
- const report = { flagged: 0 };
20165
- l2.transaction(() => {
20166
- for (const n of l2.findNodesByLabel("Claim")) {
20167
- if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) < ABSTRACTION_LEVEL.PRINCIPLE) continue;
20168
- if (n.attrs["revisit"] === true) continue;
20169
- const members = Array.isArray(n.attrs["members"]) ? n.attrs["members"] : [];
20170
- const memberSet = new Set(members);
20171
- let reason = "";
20172
- for (const id of members) {
20173
- const m = l2.getNode(id);
20174
- if (!m) {
20175
- reason = `member ${id} was removed`;
20176
- break;
20177
- }
20178
- if (m.attrs["revisit"] === true) {
20179
- reason = `member "${m.description}" needs revisit`;
20180
- break;
20181
- }
20182
- const contradictors = [
20183
- ...l2.outEdges(id, ["CONTRADICTS"]).map((e) => e.to),
20184
- ...l2.inEdges(id, ["CONTRADICTS"]).map((e) => e.from)
20185
- ];
20186
- if (contradictors.some((other) => !memberSet.has(other))) {
20187
- reason = `member "${m.description}" is now contradicted by external evidence`;
20188
- break;
20189
- }
20190
- }
20191
- if (!reason) continue;
20192
- l2.updateNode(n.id, {
20193
- attrs: {
20194
- ...n.attrs,
20195
- revisit: true,
20196
- revisitReason: reason,
20197
- revisitSinceTs: ts,
20198
- provisional: true,
20199
- // re-queue for re-distillation (P3 re-name)
20200
- pendingDistillation: true
20201
- },
20202
- lastUpdatedAt: ts
20203
- });
20204
- report.flagged++;
20205
- }
20206
- });
20207
- return report;
20208
- }
20209
- function harvestAbstractionFences(l2, text, ts) {
20210
- let applied = 0;
20211
- let rejected = 0;
20212
- for (const fence of parseAbstractionFences(text)) {
20213
- if (applyAbstractionFence(l2, fence, ts).applied) applied++;
20214
- else rejected++;
20215
- }
20216
- return { applied, rejected };
20217
- }
20218
- var DEFAULT_MIN_CLUSTER_SIZE, DEFAULT_MIN_DISTINCT_CONTEXTS;
20219
- var init_abstraction = __esm({
20220
- "../../packages/local-graph/src/abstraction.ts"() {
20221
- "use strict";
20222
- init_src();
20223
- init_src2();
20224
- init_src3();
20225
- init_percolate();
20226
- DEFAULT_MIN_CLUSTER_SIZE = 3;
20227
- DEFAULT_MIN_DISTINCT_CONTEXTS = 2;
20228
- }
20229
- });
20230
-
20231
- // ../../packages/local-graph/src/community.ts
20232
- function detectLocalCommunities(store, opts = {}) {
20233
- const minSize = opts.minCommunitySize ?? DEFAULT_MIN_COMMUNITY_SIZE;
20234
- const report = { candidates: 0, communities: 0, joint: 0, assigned: 0 };
20235
- const ids = /* @__PURE__ */ new Set();
20236
- for (const label of JOINT_COMMUNITY_LABELS) {
20237
- for (const n of store.findNodesByLabel(label)) {
20238
- if (label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
20239
- ids.add(n.id);
20240
- }
20241
- }
20242
- report.candidates = ids.size;
20243
- if (ids.size === 0) return report;
20244
- const adj = /* @__PURE__ */ new Map();
20245
- const protect = [];
20246
- const add = (a, b, w) => {
20247
- const l = adj.get(a) ?? [];
20248
- l.push({ to: b, weight: w });
20249
- adj.set(a, l);
20250
- };
20251
- for (const id of ids) {
20252
- for (const e of store.outEdges(id, [...JOINT_COMMUNITY_EDGES])) {
20253
- if (!ids.has(e.to)) continue;
20254
- const conf = e.confidence > 0 ? e.confidence : 0.5;
20255
- const w = Math.max(EDGE_WEIGHT[e.type] ?? 1, 0.1) * conf;
20256
- add(id, e.to, w);
20257
- add(e.to, id, w);
20258
- if (isCausalProtected(e.type)) protect.push([id, e.to]);
20259
- }
20260
- }
20261
- const comm = detectCommunitiesLeiden({ nodeIds: [...ids], adj }, { protect });
20262
- const members = /* @__PURE__ */ new Map();
20263
- for (const [id, c] of comm.community) {
20264
- const l = members.get(c) ?? [];
20265
- l.push(id);
20266
- members.set(c, l);
20267
- }
20268
- store.transaction(() => {
20269
- for (const [cid, mem] of members) {
20270
- const qualifies = mem.length >= minSize;
20271
- let hasLocal = false;
20272
- let hasCloud = false;
20273
- for (const id of mem) {
20274
- const n = store.getNode(id);
20275
- if (!n) continue;
20276
- const pulled = n.attrs["source"] === "cloud";
20277
- if (pulled) hasCloud = true;
20278
- else hasLocal = true;
20279
- const nextAttrs = { ...n.attrs };
20280
- if (qualifies) nextAttrs["localCommunity"] = cid;
20281
- else delete nextAttrs["localCommunity"];
20282
- store.updateNode(id, { attrs: nextAttrs });
20283
- if (!pulled) store.setCommunity(id, qualifies ? cid : null);
20284
- }
20285
- if (qualifies) {
20286
- report.communities++;
20287
- report.assigned += mem.length;
20288
- if (hasLocal && hasCloud) report.joint++;
20289
- }
20290
- }
20291
- });
20292
- return report;
20293
- }
20294
- function communitySeeds(store, opts = {}) {
20295
- const maxCommunities = opts.maxCommunities ?? 4;
20296
- const maxSeeds = opts.maxSeeds ?? 32;
20297
- const byCommunity = /* @__PURE__ */ new Map();
20298
- for (const label of JOINT_COMMUNITY_LABELS) {
20299
- for (const n of store.findNodesByLabel(label)) {
20300
- const cid = n.attrs["localCommunity"];
20301
- if (typeof cid !== "string") continue;
20302
- const cloudId = n.attrs["cloudNodeId"];
20303
- const l = byCommunity.get(cid) ?? [];
20304
- l.push({
20305
- id: n.id,
20306
- pulled: n.attrs["source"] === "cloud",
20307
- cloudId: typeof cloudId === "string" && cloudId !== n.id ? cloudId : null,
20308
- ts: n.lastUpdatedAt
20309
- });
20310
- byCommunity.set(cid, l);
20311
- }
20312
- }
20313
- if (byCommunity.size === 0) return [];
20314
- const ranked = [...byCommunity.entries()].map(([cid, mem]) => ({ cid, mem, fresh: Math.max(...mem.map((m) => m.ts)) })).sort((a, b) => b.fresh - a.fresh).slice(0, maxCommunities);
20315
- const seeds = [];
20316
- const seen = /* @__PURE__ */ new Set();
20317
- const push = (id) => {
20318
- if (seeds.length >= maxSeeds || seen.has(id)) return;
20319
- seen.add(id);
20320
- seeds.push(id);
20321
- };
20322
- for (const { mem } of ranked) {
20323
- const ordered = [...mem].sort(
20324
- (a, b) => a.pulled === b.pulled ? b.ts - a.ts : a.pulled ? -1 : 1
20325
- );
20326
- for (const m of ordered) {
20327
- push(m.id);
20328
- if (m.cloudId) push(m.cloudId);
20329
- }
20330
- }
20331
- return seeds;
20332
- }
20333
- function communityInductionRequests(store, opts = {}) {
20334
- const maxCommunities = opts.maxCommunities ?? 2;
20335
- const byCommunity = /* @__PURE__ */ new Map();
20336
- for (const label of JOINT_COMMUNITY_LABELS) {
20337
- for (const n of store.findNodesByLabel(label)) {
20338
- const cid = n.attrs["localCommunity"];
20339
- if (typeof cid !== "string") continue;
20340
- const cloudId = n.attrs["cloudNodeId"];
20341
- const l = byCommunity.get(cid) ?? [];
20342
- l.push({
20343
- id: n.id,
20344
- pulled: n.attrs["source"] === "cloud",
20345
- cloudId: typeof cloudId === "string" && cloudId !== n.id ? cloudId : null,
20346
- ts: n.lastUpdatedAt,
20347
- description: n.description
20348
- });
20349
- byCommunity.set(cid, l);
20350
- }
20351
- }
20352
- if (byCommunity.size === 0) return [];
20353
- return [...byCommunity.entries()].map(([cid, mem]) => ({ cid, mem, fresh: Math.max(...mem.map((m) => m.ts)) })).sort((a, b) => b.fresh - a.fresh).map(({ cid, mem }) => {
20354
- const ids = /* @__PURE__ */ new Set();
20355
- for (const m of mem) {
20356
- ids.add(m.id);
20357
- if (m.cloudId) ids.add(m.cloudId);
20358
- }
20359
- const freshestLocal = [...mem].filter((m) => !m.pulled).sort((a, b) => b.ts - a.ts)[0];
20360
- return {
20361
- communityId: cid,
20362
- members: [...ids].slice(0, 64),
20363
- context: (freshestLocal?.description ?? "recurring workspace problem cluster").slice(0, 200)
20364
- };
20365
- }).filter((r) => r.members.length >= MIN_INDUCTION_MEMBERS).slice(0, maxCommunities);
20366
- }
20367
- var JOINT_COMMUNITY_LABELS, JOINT_COMMUNITY_EDGES, DEFAULT_MIN_COMMUNITY_SIZE, MIN_INDUCTION_MEMBERS;
20368
- var init_community2 = __esm({
20369
- "../../packages/local-graph/src/community.ts"() {
20370
- "use strict";
20371
- init_src2();
20372
- init_src3();
20373
- JOINT_COMMUNITY_LABELS = [
20374
- "Problem",
20375
- "Solution",
20376
- "RootCause",
20377
- "Pattern"
20378
- ];
20379
- JOINT_COMMUNITY_EDGES = [
20380
- "CAUSED_BY",
20381
- "SOLVED_BY",
20382
- "FIXED_BY",
20383
- "CONTRADICTS",
20384
- "INSTANCE_OF",
20385
- "MATCHES",
20386
- "IMPLEMENTS",
20387
- "TRIAGED_BY",
20388
- "INDICATES",
20389
- "CONFIRMS",
20390
- "RELATES_TO"
20391
- ];
20392
- DEFAULT_MIN_COMMUNITY_SIZE = 2;
20393
- MIN_INDUCTION_MEMBERS = 3;
20394
- }
20395
- });
20396
-
20397
20230
  // ../../packages/local-graph/src/triage.ts
20398
20231
  function semNode(id, label, description, ts, attrs) {
20399
20232
  return {
@@ -20431,6 +20264,10 @@ function semEdge(id, from, to, type, ts, attrs) {
20431
20264
  attrs
20432
20265
  };
20433
20266
  }
20267
+ function isUnfilledPlaceholder(value) {
20268
+ const v = value.trim();
20269
+ return v.startsWith("<") && v.endsWith(">");
20270
+ }
20434
20271
  function recordTriageObservation(l2, obs, ts) {
20435
20272
  const statement = obs.presentingStatement.trim();
20436
20273
  const presentingId = obs.presentingId?.trim() || identityId({ kind: "DesignProblem", statement });
@@ -20539,6 +20376,33 @@ function recordCauseChainLink(l2, link, ts, attrs = {}) {
20539
20376
  });
20540
20377
  return created;
20541
20378
  }
20379
+ function mintWorkspaceCausalFact(ws, fact, ts) {
20380
+ if (fact.causeId === fact.presentingId) return false;
20381
+ const problem = ws.getNode(fact.presentingId);
20382
+ if (!problem || problem.label !== "Problem") return false;
20383
+ if (problem.attrs["source"] === "cloud") return false;
20384
+ const existingCause = ws.getNode(fact.causeId);
20385
+ if (existingCause && existingCause.label !== "RootCause") return false;
20386
+ const description = fact.causeDescription?.trim();
20387
+ if (!existingCause && !description) return false;
20388
+ let created = false;
20389
+ ws.transaction(() => {
20390
+ if (!ws.getNode(fact.causeId)) {
20391
+ ws.mergeNode(
20392
+ semNode(fact.causeId, "RootCause", description, ts, {
20393
+ scope: {},
20394
+ ...fact.sessionId ? { sources: [fact.sessionId] } : {}
20395
+ })
20396
+ );
20397
+ }
20398
+ const edgeId2 = `edge_cb_${fact.presentingId}_${fact.causeId}`;
20399
+ if (!ws.getEdge(edgeId2)) {
20400
+ ws.mergeEdge(semEdge(edgeId2, fact.presentingId, fact.causeId, "CAUSED_BY", ts, { witnessed: true }));
20401
+ created = true;
20402
+ }
20403
+ });
20404
+ return created;
20405
+ }
20542
20406
  function recordMisreadPrior(shared, problem, context, contributor, ts) {
20543
20407
  if (problem.attrs["resolvedAs"] !== PROBLEM_RESOLUTION.FALSE_POSITIVE) return false;
20544
20408
  if (Number(problem.attrs["corroborations"] ?? 0) < 1) return false;
@@ -20739,6 +20603,7 @@ function markDiscriminatorAsked(l2, routeIds, ts) {
20739
20603
  function promoteRouteWithDiscriminator(l2, routeId, discriminator, ts, source) {
20740
20604
  const test = discriminator.trim();
20741
20605
  if (!test) return false;
20606
+ if (isUnfilledPlaceholder(test)) return false;
20742
20607
  const existing = l2.getEdge(routeId);
20743
20608
  if (!existing || !ROUTE_EDGE_TYPES.includes(existing.type)) return false;
20744
20609
  const attrs = {
@@ -20884,7 +20749,7 @@ var init_triage2 = __esm({
20884
20749
  init_src();
20885
20750
  init_src();
20886
20751
  init_src();
20887
- init_src3();
20752
+ init_src4();
20888
20753
  init_src2();
20889
20754
  COOCCUR_BONUS = 0.25;
20890
20755
  routeEdgeId = (triageId, causeId) => `edge_rt_${triageId}_${causeId}`;
@@ -20896,6 +20761,467 @@ var init_triage2 = __esm({
20896
20761
  }
20897
20762
  });
20898
20763
 
20764
+ // ../../packages/local-graph/src/abstraction.ts
20765
+ function sharedScope(l2, memberIds) {
20766
+ const shared = (field) => {
20767
+ const vals = /* @__PURE__ */ new Set();
20768
+ for (const id of memberIds) {
20769
+ const s = l2.getNode(id)?.attrs["scope"] ?? {};
20770
+ if (typeof s[field] === "string") vals.add(s[field]);
20771
+ }
20772
+ return vals.size === 1 ? [...vals][0] : void 0;
20773
+ };
20774
+ const lang = shared("lang");
20775
+ const versionRange = shared("versionRange");
20776
+ return { ...lang ? { lang } : {}, ...versionRange ? { versionRange } : {} };
20777
+ }
20778
+ function buildCandidate(id, ts, members, contexts, originMachine, scope) {
20779
+ return {
20780
+ id,
20781
+ label: "Claim",
20782
+ description: "",
20783
+ // empty — the harness distills the prose in P3
20784
+ extractionConfidence: 0.4,
20785
+ extractionSource: "agent-observed",
20786
+ embedding: [],
20787
+ cumulativeSurprise: 0,
20788
+ peakSurprise: 0,
20789
+ cumulativeHits: 1,
20790
+ lastUpdatedAt: ts,
20791
+ createdAt: ts,
20792
+ memoryTier: "short-term",
20793
+ pageRank: 0,
20794
+ isLandmark: false,
20795
+ community: null,
20796
+ stability: "unstable",
20797
+ attrs: {
20798
+ abstractionLevel: ABSTRACTION_LEVEL.PRINCIPLE,
20799
+ kind: "abstraction-candidate",
20800
+ provisional: true,
20801
+ pendingDistillation: true,
20802
+ // membership is the unit of truth — the harness validates its fence against
20803
+ // this set (P3 / C3) and may only phrase, never alter, it.
20804
+ members,
20805
+ memberContexts: contexts,
20806
+ distinctContexts: contexts.length,
20807
+ // standard Claim envelope so it crystallizes/syncs unchanged once distilled
20808
+ truthKind: "contextual",
20809
+ // Derived from members (P1): a single-language cluster becomes a lang-scoped
20810
+ // principle; a cross-language one stays universal (`{}`).
20811
+ scope,
20812
+ groundedSupport: 0,
20813
+ sources: [],
20814
+ crystallized: "hypothesis",
20815
+ confidence: 0.4,
20816
+ ...originMachine ? { originMachine } : {}
20817
+ }
20818
+ };
20819
+ }
20820
+ function mergeGeneralizes(store, principleId, memberId, ts) {
20821
+ store.mergeEdge({
20822
+ id: `edge_${digest({ from: principleId, type: "GENERALIZES", to: memberId })}`.slice(0, 24),
20823
+ from: principleId,
20824
+ to: memberId,
20825
+ type: "GENERALIZES",
20826
+ confidence: 0.4,
20827
+ extractionSource: "agent-observed",
20828
+ createdAt: ts,
20829
+ lastSeenAt: ts,
20830
+ navSuccesses: 0,
20831
+ navFailures: 0,
20832
+ attrs: { provisional: true }
20833
+ });
20834
+ }
20835
+ function induceAbstractions(l2, opts) {
20836
+ const K = opts.minClusterSize ?? DEFAULT_MIN_CLUSTER_SIZE;
20837
+ const minCtx = opts.minDistinctContexts ?? DEFAULT_MIN_DISTINCT_CONTEXTS;
20838
+ const report = {
20839
+ communities: 0,
20840
+ candidatesMinted: 0,
20841
+ candidatesExisting: 0,
20842
+ skippedSmall: 0,
20843
+ skippedSingleContext: 0,
20844
+ generalizesEdges: 0
20845
+ };
20846
+ const ids = /* @__PURE__ */ new Set();
20847
+ for (const label of PERCOLATING_LABELS) {
20848
+ for (const n of l2.findNodesByLabel(label)) {
20849
+ if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) >= ABSTRACTION_LEVEL.PRINCIPLE) {
20850
+ continue;
20851
+ }
20852
+ if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
20853
+ ids.add(n.id);
20854
+ }
20855
+ }
20856
+ if (ids.size === 0) return report;
20857
+ const adj = /* @__PURE__ */ new Map();
20858
+ const protect = [];
20859
+ const add = (a, b, w) => {
20860
+ const l = adj.get(a) ?? [];
20861
+ l.push({ to: b, weight: w });
20862
+ adj.set(a, l);
20863
+ };
20864
+ for (const id of ids) {
20865
+ for (const e of l2.outEdges(id, [...PERCOLATING_EDGES])) {
20866
+ if (!ids.has(e.to)) continue;
20867
+ const conf = e.confidence > 0 ? e.confidence : 0.5;
20868
+ const w = Math.max(EDGE_WEIGHT[e.type] ?? 1, 0.1) * conf;
20869
+ add(id, e.to, w);
20870
+ add(e.to, id, w);
20871
+ if (isCausalProtected(e.type)) protect.push([id, e.to]);
20872
+ }
20873
+ }
20874
+ const comm = detectCommunitiesLeiden({ nodeIds: [...ids], adj }, { protect });
20875
+ report.communities = comm.count;
20876
+ const members = /* @__PURE__ */ new Map();
20877
+ for (const [id, c] of comm.community) {
20878
+ const l = members.get(c) ?? [];
20879
+ l.push(id);
20880
+ members.set(c, l);
20881
+ }
20882
+ l2.transaction(() => {
20883
+ for (const mem of members.values()) {
20884
+ if (opts.touched && !mem.some((id) => opts.touched.has(id))) continue;
20885
+ if (mem.length < K) {
20886
+ report.skippedSmall++;
20887
+ continue;
20888
+ }
20889
+ if (mem.some((id) => l2.inEdges(id, ["GENERALIZES"]).length > 0)) {
20890
+ report.candidatesExisting++;
20891
+ continue;
20892
+ }
20893
+ const contexts = /* @__PURE__ */ new Set();
20894
+ for (const id of mem) {
20895
+ const n = l2.getNode(id);
20896
+ const obs = n?.attrs["observedInProjects"];
20897
+ if (Array.isArray(obs)) {
20898
+ for (const ws of obs) contexts.add(opts.contextOf(ws) ?? `project:${ws}`);
20899
+ }
20900
+ }
20901
+ if (contexts.size < minCtx) {
20902
+ report.skippedSingleContext++;
20903
+ continue;
20904
+ }
20905
+ const sorted = [...mem].sort();
20906
+ const principleId = `princ_${digest({ members: sorted })}`.slice(0, 56);
20907
+ l2.mergeNode(
20908
+ buildCandidate(principleId, opts.ts, sorted, [...contexts].sort(), opts.originMachine, sharedScope(l2, sorted))
20909
+ );
20910
+ for (const id of sorted) {
20911
+ mergeGeneralizes(l2, principleId, id, opts.ts);
20912
+ report.generalizesEdges++;
20913
+ }
20914
+ report.candidatesMinted++;
20915
+ }
20916
+ });
20917
+ return report;
20918
+ }
20919
+ function pendingAbstractions(l2) {
20920
+ const out2 = [];
20921
+ for (const n of l2.findNodesByLabel("Claim")) {
20922
+ if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) < ABSTRACTION_LEVEL.PRINCIPLE) continue;
20923
+ if (n.attrs["provisional"] !== true) continue;
20924
+ const memberIds = Array.isArray(n.attrs["members"]) ? n.attrs["members"] : [];
20925
+ const members = memberIds.map((id) => {
20926
+ const m = l2.getNode(id);
20927
+ return m ? { id: m.id, label: m.label, description: m.description } : { id, label: "?", description: "(member missing)" };
20928
+ });
20929
+ out2.push({
20930
+ candidate: n.id,
20931
+ distinctContexts: Number(n.attrs["distinctContexts"] ?? 0),
20932
+ members
20933
+ });
20934
+ }
20935
+ return out2;
20936
+ }
20937
+ function parseAbstractionFences(text) {
20938
+ const out2 = [];
20939
+ const block = /```errata-abstraction[^\n]*\n([\s\S]*?)```/g;
20940
+ let m;
20941
+ while ((m = block.exec(text)) !== null) {
20942
+ const fields = {};
20943
+ for (const line of m[1].split(/\r?\n/)) {
20944
+ const kv = /^\s*(candidate|principle|covers)\s*:\s*(.+?)\s*$/i.exec(line);
20945
+ if (kv) fields[kv[1].toLowerCase()] = kv[2].trim();
20946
+ }
20947
+ if (!fields["candidate"] || !fields["principle"] || !fields["covers"]) continue;
20948
+ const covers = fields["covers"].split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
20949
+ if (covers.length === 0) continue;
20950
+ out2.push({ candidate: fields["candidate"], principle: fields["principle"], covers });
20951
+ }
20952
+ return out2;
20953
+ }
20954
+ function applyAbstractionFence(l2, fence, ts) {
20955
+ const node2 = l2.getNode(fence.candidate);
20956
+ if (!node2 || node2.label !== "Claim" || Number(node2.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) < ABSTRACTION_LEVEL.PRINCIPLE) {
20957
+ return { applied: false, principleId: fence.candidate, reason: "no such abstraction candidate" };
20958
+ }
20959
+ if (node2.attrs["provisional"] !== true) {
20960
+ return { applied: false, principleId: fence.candidate, reason: "candidate already distilled" };
20961
+ }
20962
+ const members = Array.isArray(node2.attrs["members"]) ? node2.attrs["members"] : [];
20963
+ const want = new Set(members);
20964
+ const got = new Set(fence.covers);
20965
+ const missing = members.filter((id) => !got.has(id));
20966
+ const invented = fence.covers.filter((id) => !want.has(id));
20967
+ if (missing.length > 0 || invented.length > 0) {
20968
+ return {
20969
+ applied: false,
20970
+ principleId: fence.candidate,
20971
+ reason: `coverage mismatch \u2014 ${missing.length} member(s) uncovered, ${invented.length} non-member(s) invented`
20972
+ };
20973
+ }
20974
+ const principle = fence.principle.trim();
20975
+ if (!principle) return { applied: false, principleId: fence.candidate, reason: "empty principle" };
20976
+ if (isUnfilledPlaceholder(principle)) {
20977
+ return { applied: false, principleId: fence.candidate, reason: "unfilled template placeholder" };
20978
+ }
20979
+ const { revisit: _r, revisitReason: _rr, revisitSinceTs: _rs, ...rest2 } = node2.attrs;
20980
+ void _r;
20981
+ void _rr;
20982
+ void _rs;
20983
+ l2.updateNode(fence.candidate, {
20984
+ description: principle,
20985
+ attrs: { ...rest2, provisional: false, pendingDistillation: false, distilledAt: ts },
20986
+ lastUpdatedAt: ts
20987
+ });
20988
+ return { applied: true, principleId: fence.candidate };
20989
+ }
20990
+ function revisitContradictedPrinciples(l2, ts) {
20991
+ const report = { flagged: 0 };
20992
+ l2.transaction(() => {
20993
+ for (const n of l2.findNodesByLabel("Claim")) {
20994
+ if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) < ABSTRACTION_LEVEL.PRINCIPLE) continue;
20995
+ if (n.attrs["revisit"] === true) continue;
20996
+ const members = Array.isArray(n.attrs["members"]) ? n.attrs["members"] : [];
20997
+ const memberSet = new Set(members);
20998
+ let reason = "";
20999
+ for (const id of members) {
21000
+ const m = l2.getNode(id);
21001
+ if (!m) {
21002
+ reason = `member ${id} was removed`;
21003
+ break;
21004
+ }
21005
+ if (m.attrs["revisit"] === true) {
21006
+ reason = `member "${m.description}" needs revisit`;
21007
+ break;
21008
+ }
21009
+ const contradictors = [
21010
+ ...l2.outEdges(id, ["CONTRADICTS"]).map((e) => e.to),
21011
+ ...l2.inEdges(id, ["CONTRADICTS"]).map((e) => e.from)
21012
+ ];
21013
+ if (contradictors.some((other) => !memberSet.has(other))) {
21014
+ reason = `member "${m.description}" is now contradicted by external evidence`;
21015
+ break;
21016
+ }
21017
+ }
21018
+ if (!reason) continue;
21019
+ l2.updateNode(n.id, {
21020
+ attrs: {
21021
+ ...n.attrs,
21022
+ revisit: true,
21023
+ revisitReason: reason,
21024
+ revisitSinceTs: ts,
21025
+ provisional: true,
21026
+ // re-queue for re-distillation (P3 re-name)
21027
+ pendingDistillation: true
21028
+ },
21029
+ lastUpdatedAt: ts
21030
+ });
21031
+ report.flagged++;
21032
+ }
21033
+ });
21034
+ return report;
21035
+ }
21036
+ function harvestAbstractionFences(l2, text, ts) {
21037
+ let applied = 0;
21038
+ let rejected = 0;
21039
+ for (const fence of parseAbstractionFences(text)) {
21040
+ if (applyAbstractionFence(l2, fence, ts).applied) applied++;
21041
+ else rejected++;
21042
+ }
21043
+ return { applied, rejected };
21044
+ }
21045
+ var DEFAULT_MIN_CLUSTER_SIZE, DEFAULT_MIN_DISTINCT_CONTEXTS;
21046
+ var init_abstraction = __esm({
21047
+ "../../packages/local-graph/src/abstraction.ts"() {
21048
+ "use strict";
21049
+ init_src();
21050
+ init_src2();
21051
+ init_src4();
21052
+ init_percolate();
21053
+ init_triage2();
21054
+ DEFAULT_MIN_CLUSTER_SIZE = 3;
21055
+ DEFAULT_MIN_DISTINCT_CONTEXTS = 2;
21056
+ }
21057
+ });
21058
+
21059
+ // ../../packages/local-graph/src/community.ts
21060
+ function detectLocalCommunities(store, opts = {}) {
21061
+ const minSize = opts.minCommunitySize ?? DEFAULT_MIN_COMMUNITY_SIZE;
21062
+ const report = { candidates: 0, communities: 0, joint: 0, assigned: 0 };
21063
+ const ids = /* @__PURE__ */ new Set();
21064
+ for (const label of JOINT_COMMUNITY_LABELS) {
21065
+ for (const n of store.findNodesByLabel(label)) {
21066
+ if (label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
21067
+ ids.add(n.id);
21068
+ }
21069
+ }
21070
+ report.candidates = ids.size;
21071
+ if (ids.size === 0) return report;
21072
+ const adj = /* @__PURE__ */ new Map();
21073
+ const protect = [];
21074
+ const add = (a, b, w) => {
21075
+ const l = adj.get(a) ?? [];
21076
+ l.push({ to: b, weight: w });
21077
+ adj.set(a, l);
21078
+ };
21079
+ for (const id of ids) {
21080
+ for (const e of store.outEdges(id, [...JOINT_COMMUNITY_EDGES])) {
21081
+ if (!ids.has(e.to)) continue;
21082
+ const conf = e.confidence > 0 ? e.confidence : 0.5;
21083
+ const w = Math.max(EDGE_WEIGHT[e.type] ?? 1, 0.1) * conf;
21084
+ add(id, e.to, w);
21085
+ add(e.to, id, w);
21086
+ if (isCausalProtected(e.type)) protect.push([id, e.to]);
21087
+ }
21088
+ }
21089
+ const comm = detectCommunitiesLeiden({ nodeIds: [...ids], adj }, { protect });
21090
+ const members = /* @__PURE__ */ new Map();
21091
+ for (const [id, c] of comm.community) {
21092
+ const l = members.get(c) ?? [];
21093
+ l.push(id);
21094
+ members.set(c, l);
21095
+ }
21096
+ store.transaction(() => {
21097
+ for (const [cid, mem] of members) {
21098
+ const qualifies = mem.length >= minSize;
21099
+ let hasLocal = false;
21100
+ let hasCloud = false;
21101
+ for (const id of mem) {
21102
+ const n = store.getNode(id);
21103
+ if (!n) continue;
21104
+ const pulled = n.attrs["source"] === "cloud";
21105
+ if (pulled) hasCloud = true;
21106
+ else hasLocal = true;
21107
+ const nextAttrs = { ...n.attrs };
21108
+ if (qualifies) nextAttrs["localCommunity"] = cid;
21109
+ else delete nextAttrs["localCommunity"];
21110
+ store.updateNode(id, { attrs: nextAttrs });
21111
+ if (!pulled) store.setCommunity(id, qualifies ? cid : null);
21112
+ }
21113
+ if (qualifies) {
21114
+ report.communities++;
21115
+ report.assigned += mem.length;
21116
+ if (hasLocal && hasCloud) report.joint++;
21117
+ }
21118
+ }
21119
+ });
21120
+ return report;
21121
+ }
21122
+ function communitySeeds(store, opts = {}) {
21123
+ const maxCommunities = opts.maxCommunities ?? 4;
21124
+ const maxSeeds = opts.maxSeeds ?? 32;
21125
+ const byCommunity = /* @__PURE__ */ new Map();
21126
+ for (const label of JOINT_COMMUNITY_LABELS) {
21127
+ for (const n of store.findNodesByLabel(label)) {
21128
+ const cid = n.attrs["localCommunity"];
21129
+ if (typeof cid !== "string") continue;
21130
+ const cloudId = n.attrs["cloudNodeId"];
21131
+ const l = byCommunity.get(cid) ?? [];
21132
+ l.push({
21133
+ id: n.id,
21134
+ pulled: n.attrs["source"] === "cloud",
21135
+ cloudId: typeof cloudId === "string" && cloudId !== n.id ? cloudId : null,
21136
+ ts: n.lastUpdatedAt
21137
+ });
21138
+ byCommunity.set(cid, l);
21139
+ }
21140
+ }
21141
+ if (byCommunity.size === 0) return [];
21142
+ const ranked = [...byCommunity.entries()].map(([cid, mem]) => ({ cid, mem, fresh: Math.max(...mem.map((m) => m.ts)) })).sort((a, b) => b.fresh - a.fresh).slice(0, maxCommunities);
21143
+ const seeds = [];
21144
+ const seen = /* @__PURE__ */ new Set();
21145
+ const push = (id) => {
21146
+ if (seeds.length >= maxSeeds || seen.has(id)) return;
21147
+ seen.add(id);
21148
+ seeds.push(id);
21149
+ };
21150
+ for (const { mem } of ranked) {
21151
+ const ordered = [...mem].sort(
21152
+ (a, b) => a.pulled === b.pulled ? b.ts - a.ts : a.pulled ? -1 : 1
21153
+ );
21154
+ for (const m of ordered) {
21155
+ push(m.id);
21156
+ if (m.cloudId) push(m.cloudId);
21157
+ }
21158
+ }
21159
+ return seeds;
21160
+ }
21161
+ function communityInductionRequests(store, opts = {}) {
21162
+ const maxCommunities = opts.maxCommunities ?? 2;
21163
+ const byCommunity = /* @__PURE__ */ new Map();
21164
+ for (const label of JOINT_COMMUNITY_LABELS) {
21165
+ for (const n of store.findNodesByLabel(label)) {
21166
+ const cid = n.attrs["localCommunity"];
21167
+ if (typeof cid !== "string") continue;
21168
+ const cloudId = n.attrs["cloudNodeId"];
21169
+ const l = byCommunity.get(cid) ?? [];
21170
+ l.push({
21171
+ id: n.id,
21172
+ pulled: n.attrs["source"] === "cloud",
21173
+ cloudId: typeof cloudId === "string" && cloudId !== n.id ? cloudId : null,
21174
+ ts: n.lastUpdatedAt,
21175
+ description: n.description
21176
+ });
21177
+ byCommunity.set(cid, l);
21178
+ }
21179
+ }
21180
+ if (byCommunity.size === 0) return [];
21181
+ return [...byCommunity.entries()].map(([cid, mem]) => ({ cid, mem, fresh: Math.max(...mem.map((m) => m.ts)) })).sort((a, b) => b.fresh - a.fresh).map(({ cid, mem }) => {
21182
+ const ids = /* @__PURE__ */ new Set();
21183
+ for (const m of mem) {
21184
+ ids.add(m.id);
21185
+ if (m.cloudId) ids.add(m.cloudId);
21186
+ }
21187
+ const freshestLocal = [...mem].filter((m) => !m.pulled).sort((a, b) => b.ts - a.ts)[0];
21188
+ return {
21189
+ communityId: cid,
21190
+ members: [...ids].slice(0, 64),
21191
+ context: (freshestLocal?.description ?? "recurring workspace problem cluster").slice(0, 200)
21192
+ };
21193
+ }).filter((r) => r.members.length >= MIN_INDUCTION_MEMBERS).slice(0, maxCommunities);
21194
+ }
21195
+ var JOINT_COMMUNITY_LABELS, JOINT_COMMUNITY_EDGES, DEFAULT_MIN_COMMUNITY_SIZE, MIN_INDUCTION_MEMBERS;
21196
+ var init_community2 = __esm({
21197
+ "../../packages/local-graph/src/community.ts"() {
21198
+ "use strict";
21199
+ init_src2();
21200
+ init_src4();
21201
+ JOINT_COMMUNITY_LABELS = [
21202
+ "Problem",
21203
+ "Solution",
21204
+ "RootCause",
21205
+ "Pattern"
21206
+ ];
21207
+ JOINT_COMMUNITY_EDGES = [
21208
+ "CAUSED_BY",
21209
+ "SOLVED_BY",
21210
+ "FIXED_BY",
21211
+ "CONTRADICTS",
21212
+ "INSTANCE_OF",
21213
+ "MATCHES",
21214
+ "IMPLEMENTS",
21215
+ "TRIAGED_BY",
21216
+ "INDICATES",
21217
+ "CONFIRMS",
21218
+ "RELATES_TO"
21219
+ ];
21220
+ DEFAULT_MIN_COMMUNITY_SIZE = 2;
21221
+ MIN_INDUCTION_MEMBERS = 3;
21222
+ }
21223
+ });
21224
+
20899
21225
  // ../../packages/local-graph/src/problem-dedup.ts
20900
21226
  function corroborations(n) {
20901
21227
  return Number(n.attrs["corroborations"] ?? 0);
@@ -21197,6 +21523,7 @@ __export(src_exports2, {
21197
21523
  JOINT_COMMUNITY_EDGES: () => JOINT_COMMUNITY_EDGES,
21198
21524
  JOINT_COMMUNITY_LABELS: () => JOINT_COMMUNITY_LABELS,
21199
21525
  MAX_ANCHOR_FILES: () => MAX_ANCHOR_FILES,
21526
+ PATTERN_DEDUP_COSINE: () => PATTERN_DEDUP_COSINE,
21200
21527
  PERCOLATING_DRIFT_EDGES: () => PERCOLATING_DRIFT_EDGES,
21201
21528
  PERCOLATING_EDGES: () => PERCOLATING_EDGES,
21202
21529
  PERCOLATING_LABELS: () => PERCOLATING_LABELS,
@@ -21216,6 +21543,7 @@ __export(src_exports2, {
21216
21543
  buildPackageIndex: () => buildPackageIndex,
21217
21544
  buildPrincipleSync: () => buildPrincipleSync,
21218
21545
  buildSymbolIndex: () => buildSymbolIndex,
21546
+ canonicalizePatternText: () => canonicalizePatternText,
21219
21547
  causalChain: () => causalChain,
21220
21548
  claimId: () => claimId,
21221
21549
  clearRevisit: () => clearRevisit,
@@ -21243,6 +21571,7 @@ __export(src_exports2, {
21243
21571
  ingestDesignProblem: () => ingestDesignProblem,
21244
21572
  isConstraintProblem: () => isConstraintProblem,
21245
21573
  isPlaceholderStatement: () => isPlaceholderStatement,
21574
+ isUnfilledPlaceholder: () => isUnfilledPlaceholder,
21246
21575
  linkProblemToLanguages: () => linkProblemToLanguages,
21247
21576
  linkProblemToPackages: () => linkProblemToPackages,
21248
21577
  linkProblemToSymbols: () => linkProblemToSymbols,
@@ -21256,10 +21585,12 @@ __export(src_exports2, {
21256
21585
  matchSymbolsInText: () => matchSymbolsInText,
21257
21586
  mergeCloudCounts: () => mergeCloudCounts,
21258
21587
  mergeDuplicateProblems: () => mergeDuplicateProblems,
21588
+ migrateProjectAlias: () => migrateProjectAlias,
21259
21589
  mintCitedPackageNode: () => mintCitedPackageNode,
21260
21590
  mintComponentNode: () => mintComponentNode,
21261
21591
  mintDomainNode: () => mintDomainNode,
21262
21592
  mintPatternNode: () => mintPatternNode,
21593
+ mintWorkspaceCausalFact: () => mintWorkspaceCausalFact,
21263
21594
  openGraphStore: () => openGraphStore,
21264
21595
  osNodeId: () => osNodeId,
21265
21596
  parseAbstractionFences: () => parseAbstractionFences,
@@ -21298,7 +21629,7 @@ __export(src_exports2, {
21298
21629
  triageOf: () => triageOf,
21299
21630
  walk: () => walk
21300
21631
  });
21301
- var init_src4 = __esm({
21632
+ var init_src5 = __esm({
21302
21633
  "../../packages/local-graph/src/index.ts"() {
21303
21634
  "use strict";
21304
21635
  init_store();
@@ -21698,10 +22029,32 @@ function renderSnapshot(s) {
21698
22029
  lines.push("");
21699
22030
  }
21700
22031
  lines.push("### Open review queue");
21701
- if (s.reviewCount === 0) {
22032
+ const reviewItems = s.reviewItems ?? [];
22033
+ if (s.reviewCount === 0 && reviewItems.length === 0) {
21702
22034
  lines.push("- (none)");
21703
22035
  } else {
21704
- lines.push(`- ${s.reviewCount} items awaiting your review at ${s.reviewUiUrl}`);
22036
+ if (s.reviewCount > 0) lines.push(`- ${s.reviewCount} items awaiting your review at ${s.reviewUiUrl}`);
22037
+ for (const item of reviewItems) {
22038
+ if (item.kind === "discriminator") {
22039
+ lines.push(
22040
+ `- **A recurring diagnosis is one cheap check from promoting** \u2014 "${item.presenting}" usually turns out to be: ${item.cause}. If (and only if) you KNOW a cheap ante-hoc check that confirms it, answer in your reply:`
22041
+ );
22042
+ lines.push(" ```errata-triage");
22043
+ lines.push(` route: ${item.route}`);
22044
+ lines.push(" test: <the cheap check>");
22045
+ lines.push(" ```");
22046
+ } else {
22047
+ lines.push(
22048
+ `- **Name the principle this cluster of your beliefs generalizes** \u2014 it ships to the collective only once named, and the prose must cover ALL members in one sentence:`
22049
+ );
22050
+ for (const m of item.members) lines.push(` - ${m.description}`);
22051
+ lines.push(" ```errata-abstraction");
22052
+ lines.push(` candidate: ${item.candidate}`);
22053
+ lines.push(" principle: <one sentence covering all members>");
22054
+ lines.push(` covers: ${item.members.map((m) => m.id).join(", ")}`);
22055
+ lines.push(" ```");
22056
+ }
22057
+ }
21705
22058
  }
21706
22059
  return lines.join("\n");
21707
22060
  }
@@ -21711,6 +22064,12 @@ function dropLowestUnit(s) {
21711
22064
  case "skills":
21712
22065
  if (s.skills.length) return s.skills.pop(), true;
21713
22066
  break;
22067
+ case "reviewItemsOverFloor":
22068
+ if (s.reviewItems && s.reviewItems.length > REVIEW_ITEM_FLOOR) return s.reviewItems.pop(), true;
22069
+ break;
22070
+ case "reviewItems":
22071
+ if (s.reviewItems && s.reviewItems.length) return s.reviewItems.pop(), true;
22072
+ break;
21714
22073
  case "motifsOverFloor":
21715
22074
  if (s.motifs.length > MOTIF_FLOOR) return s.motifs.pop(), true;
21716
22075
  break;
@@ -21765,6 +22124,7 @@ function assembleAgentContext(opts) {
21765
22124
  });
21766
22125
  if (opts.edgeElicitation) snapshot.edgeElicitation = opts.edgeElicitation;
21767
22126
  if (opts.pendingUpdate) snapshot.pendingUpdate = opts.pendingUpdate;
22127
+ if (opts.reviewItems?.length) snapshot.reviewItems = [...opts.reviewItems];
21768
22128
  if (opts.remote && opts.remote.length > 0) {
21769
22129
  const seen = /* @__PURE__ */ new Set();
21770
22130
  const remote = opts.remote.filter((n) => {
@@ -21787,11 +22147,11 @@ _${dropped} lower-priority item${dropped === 1 ? "" : "s"} omitted to fit the pa
21787
22147
  }
21788
22148
  return { body: body2, snapshot, dropped };
21789
22149
  }
21790
- var RECALL_FIRST_HEADER, RECALL_FIRST_BODY, RECALL_FIRST_BLOCK, SEARCH_IMPERATIVE_HEADER, SEARCH_IMPERATIVE_BODY, EVICTION_ORDER, MOTIF_FLOOR, REMOTE_FLOOR, PROBLEM_FLOOR, DEFAULT_AGENT_CONTEXT_BUDGET;
22150
+ var RECALL_FIRST_HEADER, RECALL_FIRST_BODY, RECALL_FIRST_BLOCK, SEARCH_IMPERATIVE_HEADER, SEARCH_IMPERATIVE_BODY, EVICTION_ORDER, MOTIF_FLOOR, REMOTE_FLOOR, PROBLEM_FLOOR, REVIEW_ITEM_FLOOR, DEFAULT_AGENT_CONTEXT_BUDGET;
21791
22151
  var init_render = __esm({
21792
22152
  "../../packages/context-writer/src/render.ts"() {
21793
22153
  "use strict";
21794
- init_src4();
22154
+ init_src5();
21795
22155
  init_src2();
21796
22156
  RECALL_FIRST_HEADER = "## errata \u2014 recall before you work";
21797
22157
  RECALL_FIRST_BODY = "Before investigating any bug or error, your FIRST tool call must be `mcp__errata__errata_search` with a few words describing the problem. This machine's knowledge graph records previously solved problems \u2014 the answer may already be recorded. Read what it returns before touching any files.";
@@ -21802,6 +22162,9 @@ ${RECALL_FIRST_BODY}`;
21802
22162
  SEARCH_IMPERATIVE_BODY = "Everything below is a budgeted, top-of-head slice of a much larger graph. Any prior id below is a live burst seed: `errata.burst` it (or read `.errata/g/burst/<id>`) to pull its wider neighborhood \u2014 causes, fixes, siblings. When a prior is adjacent-but-not-quite, or none fit, that gap is exactly when to search deeper before solving cold: a stuck search is itself a signal that routes help to you.";
21803
22163
  EVICTION_ORDER = [
21804
22164
  "skills",
22165
+ // Review items beyond the floor drop immediately after skills — one naming
22166
+ // ask per render is the design (the caller rotates), extras are padding.
22167
+ "reviewItemsOverFloor",
21805
22168
  // Collective floors (EE-evidence-live): motifs/remote trim to a FLOOR here and
21806
22169
  // fully drain only near the very end. The unfloored order zeroed them on every
21807
22170
  // render (~44 units dropped per block, live 8-01), which starved §5.4 at the
@@ -21837,11 +22200,22 @@ ${RECALL_FIRST_BODY}`;
21837
22200
  "recentProblemsOverFloor",
21838
22201
  "motifs",
21839
22202
  "remote",
22203
+ // The floored review item outlives even the terminal motif/remote stages ON
22204
+ // PURPOSE: the block's deficit reaches those stages on EVERY render (measured
22205
+ // — non-evictable instruction prose leaves ~2k discretionary, and live
22206
+ // renders land at exactly the problem floor with motifs/remote at zero), so
22207
+ // any earlier slot is structurally invisible — the same starvation that
22208
+ // killed the pull surfaces this band replaces. Cost: one compact ask ≈ one
22209
+ // remote prior. That trade is deliberate: the ask IS a witness elicitation
22210
+ // (a discriminator/principle is §5.4-grade evidence nothing else produces),
22211
+ // and it still yields to the agent's own live problems.
22212
+ "reviewItems",
21840
22213
  "recentProblems"
21841
22214
  ];
21842
22215
  MOTIF_FLOOR = 2;
21843
22216
  REMOTE_FLOOR = 3;
21844
22217
  PROBLEM_FLOOR = 4;
22218
+ REVIEW_ITEM_FLOOR = 1;
21845
22219
  DEFAULT_AGENT_CONTEXT_BUDGET = 9e3;
21846
22220
  }
21847
22221
  });
@@ -21855,8 +22229,8 @@ var init_bleed_memory = __esm({
21855
22229
  });
21856
22230
 
21857
22231
  // ../../packages/context-writer/src/bleed-rules.ts
21858
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
21859
- import { join as join3 } from "node:path";
22232
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
22233
+ import { join as join4 } from "node:path";
21860
22234
  function formatRuleFile(item) {
21861
22235
  const frontmatter = item.paths && item.paths.length > 0 ? `---
21862
22236
  paths:
@@ -21869,21 +22243,21 @@ ${item.body.trim()}
21869
22243
  `;
21870
22244
  }
21871
22245
  function bleedRules(rulesDir, items) {
21872
- if (!existsSync4(rulesDir)) mkdirSync3(rulesDir, { recursive: true });
22246
+ if (!existsSync5(rulesDir)) mkdirSync4(rulesDir, { recursive: true });
21873
22247
  const wanted = new Map(items.map((i2) => [fileFor(i2.slug), i2]));
21874
22248
  let written = 0;
21875
22249
  let created = 0;
21876
22250
  for (const [file2, item] of wanted) {
21877
- const path2 = join3(rulesDir, file2);
21878
- if (!existsSync4(path2)) created++;
22251
+ const path2 = join4(rulesDir, file2);
22252
+ if (!existsSync5(path2)) created++;
21879
22253
  writeFileSync4(path2, formatRuleFile(item), "utf8");
21880
22254
  written++;
21881
22255
  }
21882
22256
  let pruned = 0;
21883
22257
  for (const f of readdirSync(rulesDir)) {
21884
22258
  if (!f.startsWith(PREFIX) || !f.endsWith(".md") || wanted.has(f)) continue;
21885
- if (readFileSync3(join3(rulesDir, f), "utf8").includes(MARKER)) {
21886
- rmSync(join3(rulesDir, f));
22259
+ if (readFileSync3(join4(rulesDir, f), "utf8").includes(MARKER)) {
22260
+ rmSync(join4(rulesDir, f));
21887
22261
  pruned++;
21888
22262
  }
21889
22263
  }
@@ -21966,7 +22340,7 @@ var init_select_durable_memory = __esm({
21966
22340
  });
21967
22341
 
21968
22342
  // ../../packages/context-writer/src/index.ts
21969
- var init_src5 = __esm({
22343
+ var init_src6 = __esm({
21970
22344
  "../../packages/context-writer/src/index.ts"() {
21971
22345
  "use strict";
21972
22346
  init_agents_md();
@@ -22921,7 +23295,7 @@ var init_pkce = __esm({
22921
23295
  });
22922
23296
 
22923
23297
  // ../../packages/cloud-client/src/index.ts
22924
- var init_src6 = __esm({
23298
+ var init_src7 = __esm({
22925
23299
  "../../packages/cloud-client/src/index.ts"() {
22926
23300
  "use strict";
22927
23301
  init_client();
@@ -22949,14 +23323,14 @@ function envPaths(name2, { suffix = "nodejs" } = {}) {
22949
23323
  }
22950
23324
  return linux(name2);
22951
23325
  }
22952
- var homedir, tmpdir, env, macos, windows, linux;
23326
+ var homedir2, tmpdir, env, macos, windows, linux;
22953
23327
  var init_env_paths = __esm({
22954
23328
  "../../node_modules/.pnpm/env-paths@3.0.0/node_modules/env-paths/index.js"() {
22955
- homedir = os.homedir();
23329
+ homedir2 = os.homedir();
22956
23330
  tmpdir = os.tmpdir();
22957
23331
  ({ env } = process3);
22958
23332
  macos = (name2) => {
22959
- const library = path.join(homedir, "Library");
23333
+ const library = path.join(homedir2, "Library");
22960
23334
  return {
22961
23335
  data: path.join(library, "Application Support", name2),
22962
23336
  config: path.join(library, "Preferences", name2),
@@ -22966,8 +23340,8 @@ var init_env_paths = __esm({
22966
23340
  };
22967
23341
  };
22968
23342
  windows = (name2) => {
22969
- const appData = env.APPDATA || path.join(homedir, "AppData", "Roaming");
22970
- const localAppData = env.LOCALAPPDATA || path.join(homedir, "AppData", "Local");
23343
+ const appData = env.APPDATA || path.join(homedir2, "AppData", "Roaming");
23344
+ const localAppData = env.LOCALAPPDATA || path.join(homedir2, "AppData", "Local");
22971
23345
  return {
22972
23346
  // Data/config/cache/log are invented by me as Windows isn't opinionated about this
22973
23347
  data: path.join(localAppData, name2, "Data"),
@@ -22978,13 +23352,13 @@ var init_env_paths = __esm({
22978
23352
  };
22979
23353
  };
22980
23354
  linux = (name2) => {
22981
- const username = path.basename(homedir);
23355
+ const username = path.basename(homedir2);
22982
23356
  return {
22983
- data: path.join(env.XDG_DATA_HOME || path.join(homedir, ".local", "share"), name2),
22984
- config: path.join(env.XDG_CONFIG_HOME || path.join(homedir, ".config"), name2),
22985
- cache: path.join(env.XDG_CACHE_HOME || path.join(homedir, ".cache"), name2),
23357
+ data: path.join(env.XDG_DATA_HOME || path.join(homedir2, ".local", "share"), name2),
23358
+ config: path.join(env.XDG_CONFIG_HOME || path.join(homedir2, ".config"), name2),
23359
+ cache: path.join(env.XDG_CACHE_HOME || path.join(homedir2, ".cache"), name2),
22986
23360
  // https://wiki.debian.org/XDGBaseDirectorySpecification#state
22987
- log: path.join(env.XDG_STATE_HOME || path.join(homedir, ".local", "state"), name2),
23361
+ log: path.join(env.XDG_STATE_HOME || path.join(homedir2, ".local", "state"), name2),
22988
23362
  temp: path.join(tmpdir, username, name2)
22989
23363
  };
22990
23364
  };
@@ -23004,34 +23378,34 @@ __export(paths_exports, {
23004
23378
  workspaceDir: () => workspaceDir,
23005
23379
  workspacePaths: () => workspacePaths
23006
23380
  });
23007
- import { mkdirSync as mkdirSync4 } from "node:fs";
23008
- import { dirname as dirname3, join as join4 } from "node:path";
23381
+ import { mkdirSync as mkdirSync5 } from "node:fs";
23382
+ import { dirname as dirname4, join as join5 } from "node:path";
23009
23383
  function globalDir() {
23010
23384
  const p = envPaths("errata", { suffix: "" });
23011
23385
  return p.config;
23012
23386
  }
23013
23387
  function globalConfigPath() {
23014
- return process.env["ERRATA_CONFIG_PATH"] ?? join4(globalDir(), "config.json");
23388
+ return process.env["ERRATA_CONFIG_PATH"] ?? join5(globalDir(), "config.json");
23015
23389
  }
23016
23390
  function daemonLogPath() {
23017
- mkdirSync4(globalDir(), { recursive: true });
23018
- return join4(globalDir(), "daemon.log");
23391
+ mkdirSync5(globalDir(), { recursive: true });
23392
+ return join5(globalDir(), "daemon.log");
23019
23393
  }
23020
23394
  function globalDaemonLock() {
23021
- return process.env["ERRATA_DAEMON_LOCK"] ?? join4(globalDir(), "daemon.lock");
23395
+ return process.env["ERRATA_DAEMON_LOCK"] ?? join5(globalDir(), "daemon.lock");
23022
23396
  }
23023
23397
  function sharedStorePath() {
23024
- return process.env["ERRATA_SHARED_DB"] ?? join4(globalDir(), "shared", "graph.db");
23398
+ return process.env["ERRATA_SHARED_DB"] ?? join5(globalDir(), "shared", "graph.db");
23025
23399
  }
23026
23400
  function workspaceDir(workspaceRoot) {
23027
- return join4(workspaceRoot, ".errata");
23401
+ return join5(workspaceRoot, ".errata");
23028
23402
  }
23029
23403
  function ensureDir(p) {
23030
- mkdirSync4(p, { recursive: true });
23404
+ mkdirSync5(p, { recursive: true });
23031
23405
  return p;
23032
23406
  }
23033
23407
  function ensureParent(p) {
23034
- mkdirSync4(dirname3(p), { recursive: true });
23408
+ mkdirSync5(dirname4(p), { recursive: true });
23035
23409
  return p;
23036
23410
  }
23037
23411
  function workspacePaths(root) {
@@ -23039,15 +23413,15 @@ function workspacePaths(root) {
23039
23413
  return {
23040
23414
  root,
23041
23415
  configDir: dir,
23042
- workspaceJson: join4(dir, "workspace.json"),
23043
- eventLog: join4(dir, "eventlog.sqlite"),
23044
- castalia: join4(dir, "castalia.db"),
23045
- reviewQueue: join4(dir, "review-queue.json"),
23046
- outbox: join4(dir, "outbox"),
23047
- daemonLock: join4(dir, "daemon.lock"),
23048
- identityAudit: join4(dir, "identity-audit.log"),
23049
- skillsDir: join4(dir, "skills"),
23050
- skillsManifest: join4(dir, "skills.json")
23416
+ workspaceJson: join5(dir, "workspace.json"),
23417
+ eventLog: join5(dir, "eventlog.sqlite"),
23418
+ castalia: join5(dir, "castalia.db"),
23419
+ reviewQueue: join5(dir, "review-queue.json"),
23420
+ outbox: join5(dir, "outbox"),
23421
+ daemonLock: join5(dir, "daemon.lock"),
23422
+ identityAudit: join5(dir, "identity-audit.log"),
23423
+ skillsDir: join5(dir, "skills"),
23424
+ skillsManifest: join5(dir, "skills.json")
23051
23425
  };
23052
23426
  }
23053
23427
  var init_paths = __esm({
@@ -23058,7 +23432,7 @@ var init_paths = __esm({
23058
23432
  });
23059
23433
 
23060
23434
  // src/config.ts
23061
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
23435
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
23062
23436
  import { randomUUID as randomUUID2 } from "node:crypto";
23063
23437
  function defaultConfig() {
23064
23438
  return {
@@ -23086,7 +23460,7 @@ function defaultConfig() {
23086
23460
  }
23087
23461
  function loadConfig() {
23088
23462
  const p = globalConfigPath();
23089
- if (!existsSync5(p)) {
23463
+ if (!existsSync6(p)) {
23090
23464
  ensureDir(globalDir());
23091
23465
  const cfg = defaultConfig();
23092
23466
  saveConfig({ ...cfg, cloudUrl: DEFAULT_CLOUD_URL });
@@ -23547,7 +23921,7 @@ function hasCloudCredential(cfg) {
23547
23921
  var init_cloud_auth = __esm({
23548
23922
  "src/cloud-auth.ts"() {
23549
23923
  "use strict";
23550
- init_src6();
23924
+ init_src7();
23551
23925
  init_config();
23552
23926
  init_cloud_endpoint_policy();
23553
23927
  }
@@ -25892,9 +26266,9 @@ var init_agent_signals = __esm({
25892
26266
  });
25893
26267
 
25894
26268
  // src/review.ts
25895
- import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "node:fs";
26269
+ import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "node:fs";
25896
26270
  function loadReviewQueue(paths) {
25897
- if (!existsSync6(paths.reviewQueue)) return [];
26271
+ if (!existsSync7(paths.reviewQueue)) return [];
25898
26272
  try {
25899
26273
  return JSON.parse(readFileSync5(paths.reviewQueue, "utf8"));
25900
26274
  } catch {
@@ -26189,7 +26563,7 @@ __export(src_exports3, {
26189
26563
  recheck: () => recheck,
26190
26564
  scrub: () => scrub
26191
26565
  });
26192
- var init_src7 = __esm({
26566
+ var init_src8 = __esm({
26193
26567
  "../../packages/canonical/src/index.ts"() {
26194
26568
  "use strict";
26195
26569
  init_scrub();
@@ -26215,7 +26589,7 @@ var TOKEN_RULES;
26215
26589
  var init_generalize = __esm({
26216
26590
  "../../packages/privacy-edge/src/generalize.ts"() {
26217
26591
  "use strict";
26218
- init_src7();
26592
+ init_src8();
26219
26593
  init_date_shapes();
26220
26594
  init_ontology();
26221
26595
  TOKEN_RULES = [
@@ -26233,7 +26607,7 @@ var init_generalize = __esm({
26233
26607
  });
26234
26608
 
26235
26609
  // ../../packages/privacy-edge/src/index.ts
26236
- var init_src8 = __esm({
26610
+ var init_src9 = __esm({
26237
26611
  "../../packages/privacy-edge/src/index.ts"() {
26238
26612
  "use strict";
26239
26613
  init_ontology();
@@ -26296,7 +26670,7 @@ var SYMBOL_LABELS2, KIND_PHRASE, RE_RESERVED2, escapeRe4, TOKEN_RE3;
26296
26670
  var init_generalize_graph = __esm({
26297
26671
  "src/generalize-graph.ts"() {
26298
26672
  "use strict";
26299
- init_src8();
26673
+ init_src9();
26300
26674
  init_src();
26301
26675
  SYMBOL_LABELS2 = [
26302
26676
  ["Function", "function"],
@@ -26549,207 +26923,6 @@ var init_dual_augment = __esm({
26549
26923
  }
26550
26924
  });
26551
26925
 
26552
- // ../../packages/embedding/src/model.ts
26553
- import { mkdirSync as mkdirSync5, existsSync as existsSync7 } from "node:fs";
26554
- import { homedir as homedir2 } from "node:os";
26555
- import { dirname as dirname4, join as join5 } from "node:path";
26556
- import { createRequire } from "node:module";
26557
- function semanticFloorFor(version2) {
26558
- if (version2 === EMBEDDING_VERSION || version2 === MODEL_EMBEDDING_VERSION) return 0.25;
26559
- return 0.5;
26560
- }
26561
- function noteHashFallback() {
26562
- if (warnedHashFallback) return;
26563
- if (process.env["ERRATA_EMBED_HASH_ONLY"] === "1") return;
26564
- warnedHashFallback = true;
26565
- console.warn(
26566
- `[errata] embedding model unavailable (${lastError?.message ?? "unknown"}) \u2014 falling back to ${EMBEDDING_VERSION} hash vectors; semantic ranking runs at reduced fidelity`
26567
- );
26568
- }
26569
- function getCacheDir() {
26570
- return process.env["ERRATA_MODEL_CACHE"] ?? join5(homedir2(), ".errata", "models");
26571
- }
26572
- async function loadTransformers() {
26573
- try {
26574
- return await import("@huggingface/transformers");
26575
- } catch (err2) {
26576
- void err2;
26577
- }
26578
- try {
26579
- const seaResourceBase = join5(
26580
- // execPath dir is where errata.exe lives; resources/ rides alongside.
26581
- dirname4(process.execPath),
26582
- "resources",
26583
- "_resolve.js"
26584
- );
26585
- const resourceRequire = createRequire(seaResourceBase);
26586
- return resourceRequire("@huggingface/transformers");
26587
- } catch (err2) {
26588
- lastError = err2 instanceof Error ? err2 : new Error(String(err2));
26589
- return null;
26590
- }
26591
- }
26592
- async function loadPipeline() {
26593
- const cacheDir = getCacheDir();
26594
- if (!existsSync7(cacheDir)) mkdirSync5(cacheDir, { recursive: true });
26595
- try {
26596
- const tx = await loadTransformers();
26597
- if (!tx) {
26598
- throw lastError ?? new Error("transformers package unavailable");
26599
- }
26600
- tx.env.cacheDir = cacheDir;
26601
- tx.env.useFSCache = true;
26602
- tx.env.allowLocalModels = true;
26603
- tx.env.allowRemoteModels = process.env["ERRATA_OFFLINE"] !== "1";
26604
- const pipe2 = await tx.pipeline("feature-extraction", MODEL_NAME, {
26605
- // Quantized weights cut size 4x; quality drop is negligible for
26606
- // short-text similarity. Override via env if you want full
26607
- // precision.
26608
- dtype: process.env["ERRATA_MODEL_DTYPE"] ?? "q8"
26609
- });
26610
- return pipe2;
26611
- } catch (err2) {
26612
- lastError = err2 instanceof Error ? err2 : new Error(String(err2));
26613
- return null;
26614
- }
26615
- }
26616
- async function ensureModelLoaded() {
26617
- if (!pipelinePromise) {
26618
- pipelinePromise = loadPipeline();
26619
- }
26620
- return pipelinePromise;
26621
- }
26622
- async function embedTextWithModel(text) {
26623
- const pipe2 = await ensureModelLoaded();
26624
- if (!pipe2) {
26625
- throw new Error(
26626
- `embedding model could not be loaded: ${lastError?.message ?? "unknown"}`
26627
- );
26628
- }
26629
- if (!text) return new Array(MODEL_EMBEDDING_DIM).fill(0);
26630
- const out2 = await pipe2(text, { pooling: "mean", normalize: true });
26631
- return Array.from(out2.data);
26632
- }
26633
- async function embedBatchWithModelOrHash(texts) {
26634
- if (texts.length === 0) return [];
26635
- if (process.env["ERRATA_EMBED_HASH_ONLY"] === "1") {
26636
- return texts.map((t) => {
26637
- const v = embed(t);
26638
- return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
26639
- });
26640
- }
26641
- try {
26642
- const pipe2 = await ensureModelLoaded();
26643
- if (pipe2) {
26644
- const out2 = await pipe2(texts, {
26645
- pooling: "mean",
26646
- normalize: true
26647
- });
26648
- const dim = out2.dims[out2.dims.length - 1] ?? MODEL_EMBEDDING_DIM;
26649
- const total = texts.length;
26650
- const results = [];
26651
- for (let i2 = 0; i2 < total; i2++) {
26652
- const v = Array.from(out2.data.subarray(i2 * dim, (i2 + 1) * dim));
26653
- results.push({ vector: v, version: MODEL_EMBEDDING_VERSION, dim });
26654
- }
26655
- return results;
26656
- }
26657
- } catch {
26658
- }
26659
- noteHashFallback();
26660
- return texts.map((t) => {
26661
- const v = embed(t);
26662
- return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
26663
- });
26664
- }
26665
- async function embedTextWithModelOrHash(text) {
26666
- if (process.env["ERRATA_EMBED_HASH_ONLY"] === "1") {
26667
- const v = embed(text);
26668
- return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
26669
- }
26670
- try {
26671
- const v = await embedTextWithModel(text);
26672
- return { vector: v, version: MODEL_EMBEDDING_VERSION, dim: v.length };
26673
- } catch {
26674
- noteHashFallback();
26675
- const v = embed(text);
26676
- return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
26677
- }
26678
- }
26679
- var MODEL_NAME, MODEL_EMBEDDING_DIM, MODEL_EMBEDDING_VERSION, pipelinePromise, lastError, warnedHashFallback;
26680
- var init_model = __esm({
26681
- "../../packages/embedding/src/model.ts"() {
26682
- "use strict";
26683
- init_src9();
26684
- MODEL_NAME = "Xenova/all-MiniLM-L6-v2";
26685
- MODEL_EMBEDDING_DIM = 384;
26686
- MODEL_EMBEDDING_VERSION = "minilm-l6-v2";
26687
- pipelinePromise = null;
26688
- lastError = null;
26689
- warnedHashFallback = false;
26690
- }
26691
- });
26692
-
26693
- // ../../packages/embedding/src/index.ts
26694
- function djb2(s) {
26695
- let h = 5381;
26696
- for (let i2 = 0; i2 < s.length; i2++) {
26697
- h = (h << 5) + h + s.charCodeAt(i2) >>> 0;
26698
- }
26699
- return h;
26700
- }
26701
- function djb2Signed(s) {
26702
- let h = 0;
26703
- for (let i2 = 0; i2 < s.length; i2++) {
26704
- h = h * 31 + s.charCodeAt(i2) | 0;
26705
- }
26706
- return h;
26707
- }
26708
- function tokenize(text) {
26709
- return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 0 && t.length <= 40);
26710
- }
26711
- function* charTrigrams(text) {
26712
- const s = text.toLowerCase();
26713
- for (let i2 = 0; i2 <= s.length - 3; i2++) {
26714
- yield s.slice(i2, i2 + 3);
26715
- }
26716
- }
26717
- function embed(text) {
26718
- const vec = new Array(EMBEDDING_DIM).fill(0);
26719
- if (!text) return vec;
26720
- for (const tok of tokenize(text)) {
26721
- const idx = djb2(tok) % EMBEDDING_DIM;
26722
- const sign = djb2Signed(tok) & 1 ? 1 : -1;
26723
- vec[idx] = vec[idx] + sign;
26724
- }
26725
- for (const tri of charTrigrams(text)) {
26726
- const idx = djb2("3:" + tri) % EMBEDDING_DIM;
26727
- const sign = djb2Signed("3:" + tri) & 1 ? 1 : -1;
26728
- vec[idx] = vec[idx] + 0.5 * sign;
26729
- }
26730
- let normSq = 0;
26731
- for (const x of vec) normSq += x * x;
26732
- if (normSq <= 0) return vec;
26733
- const inv = 1 / Math.sqrt(normSq);
26734
- for (let i2 = 0; i2 < vec.length; i2++) vec[i2] = vec[i2] * inv;
26735
- return vec;
26736
- }
26737
- function cosine(a, b) {
26738
- if (a.length !== b.length || a.length === 0) return 0;
26739
- let dot = 0;
26740
- for (let i2 = 0; i2 < a.length; i2++) dot += a[i2] * b[i2];
26741
- return dot;
26742
- }
26743
- var EMBEDDING_DIM, EMBEDDING_VERSION;
26744
- var init_src9 = __esm({
26745
- "../../packages/embedding/src/index.ts"() {
26746
- "use strict";
26747
- init_model();
26748
- EMBEDDING_DIM = 256;
26749
- EMBEDDING_VERSION = "hash-v1";
26750
- }
26751
- });
26752
-
26753
26926
  // ../../packages/generalizer/src/review-detector.ts
26754
26927
  function evaluateForReview(node2, prevTier) {
26755
26928
  if (node2.cumulativeSurprise >= REVIEW_SURPRISE_THRESHOLD) {
@@ -26766,7 +26939,7 @@ function evaluateForReview(node2, prevTier) {
26766
26939
  var init_review_detector = __esm({
26767
26940
  "../../packages/generalizer/src/review-detector.ts"() {
26768
26941
  "use strict";
26769
- init_src3();
26942
+ init_src4();
26770
26943
  }
26771
26944
  });
26772
26945
 
@@ -26789,7 +26962,7 @@ function nemoriDecide(input) {
26789
26962
  var init_nemori_gate = __esm({
26790
26963
  "../../packages/generalizer/src/nemori-gate.ts"() {
26791
26964
  "use strict";
26792
- init_src3();
26965
+ init_src4();
26793
26966
  }
26794
26967
  });
26795
26968
 
@@ -27822,11 +27995,11 @@ var init_generalizer = __esm({
27822
27995
  "../../packages/generalizer/src/generalizer.ts"() {
27823
27996
  "use strict";
27824
27997
  init_src2();
27998
+ init_src4();
27825
27999
  init_src3();
27826
- init_src9();
27827
28000
  init_src();
27828
28001
  init_src();
27829
- init_src4();
28002
+ init_src5();
27830
28003
  init_review_detector();
27831
28004
  init_nemori_gate();
27832
28005
  init_diagnostic_matcher();
@@ -27951,7 +28124,7 @@ var init_motifs = __esm({
27951
28124
  "../../packages/generalizer/src/motifs.ts"() {
27952
28125
  "use strict";
27953
28126
  init_src2();
27954
- init_src3();
28127
+ init_src4();
27955
28128
  init_src();
27956
28129
  MEMBER_LABELS = ["Problem", "RootCause", "Solution"];
27957
28130
  }
@@ -28088,8 +28261,8 @@ var init_nightly = __esm({
28088
28261
  "../../packages/generalizer/src/nightly.ts"() {
28089
28262
  "use strict";
28090
28263
  init_src2();
28091
- init_src3();
28092
28264
  init_src4();
28265
+ init_src5();
28093
28266
  init_motifs();
28094
28267
  }
28095
28268
  });
@@ -28211,7 +28384,7 @@ var CODE_LABELS, SEMANTIC_LABELS, BATCH_SIZE;
28211
28384
  var init_embed_code = __esm({
28212
28385
  "../../packages/generalizer/src/embed-code.ts"() {
28213
28386
  "use strict";
28214
- init_src9();
28387
+ init_src3();
28215
28388
  CODE_LABELS = ["Function", "Method", "Class", "Const"];
28216
28389
  SEMANTIC_LABELS = ["Problem", "RootCause", "Solution", "Pattern", "Claim"];
28217
28390
  BATCH_SIZE = 32;
@@ -28318,8 +28491,8 @@ function localBurst(store, seedId, opts = {}) {
28318
28491
  var init_relevance_rank = __esm({
28319
28492
  "../../packages/generalizer/src/relevance-rank.ts"() {
28320
28493
  "use strict";
28321
- init_src9();
28322
28494
  init_src3();
28495
+ init_src4();
28323
28496
  }
28324
28497
  });
28325
28498
 
@@ -37792,7 +37965,7 @@ var init_mcp = __esm({
37792
37965
  init_cloud_auth();
37793
37966
  init_config();
37794
37967
  init_dual_augment();
37795
- init_src4();
37968
+ init_src5();
37796
37969
  init_src2();
37797
37970
  init_src10();
37798
37971
  init_dual_burst();
@@ -46748,7 +46921,7 @@ var init_tool_index = __esm({
46748
46921
  "use strict";
46749
46922
  init_src();
46750
46923
  init_src();
46751
- init_src4();
46924
+ init_src5();
46752
46925
  init_tldr_tools_generated();
46753
46926
  PUBLIC_TOOLS = new Set(TLDR_TOOL_NAMES);
46754
46927
  PLUMBING = /* @__PURE__ */ new Set([
@@ -47489,8 +47662,8 @@ var init_webui = __esm({
47489
47662
  "src/webui.ts"() {
47490
47663
  "use strict";
47491
47664
  init_dist();
47665
+ init_src6();
47492
47666
  init_src5();
47493
- init_src4();
47494
47667
  init_agent_signals();
47495
47668
  init_review2();
47496
47669
  init_vfile();
@@ -47961,7 +48134,7 @@ var init_report_render = __esm({
47961
48134
  });
47962
48135
 
47963
48136
  // src/cli.ts
47964
- init_src5();
48137
+ init_src6();
47965
48138
  import { closeSync as closeSync2, existsSync as existsSync27, openSync as openSync2, readFileSync as readFileSync26, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
47966
48139
  import { join as join30 } from "node:path";
47967
48140
  import { spawn as spawn3 } from "node:child_process";
@@ -50244,7 +50417,7 @@ var esm_default = { watch, FSWatcher };
50244
50417
  // src/engine.ts
50245
50418
  init_src();
50246
50419
  init_src2();
50247
- init_src4();
50420
+ init_src5();
50248
50421
 
50249
50422
  // ../../packages/eventlog/src/store.ts
50250
50423
  init_src2();
@@ -50405,7 +50578,7 @@ function openEventLog(opts) {
50405
50578
 
50406
50579
  // src/engine.ts
50407
50580
  init_src11();
50408
- init_src5();
50581
+ init_src6();
50409
50582
  init_src10();
50410
50583
  init_review2();
50411
50584
 
@@ -50648,7 +50821,7 @@ init_src();
50648
50821
  init_src2();
50649
50822
  init_agent_signals();
50650
50823
  init_tool_index();
50651
- init_src4();
50824
+ init_src5();
50652
50825
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
50653
50826
  var NEG = /n['']t|\b(?:not|never|no|none|neither|nor|unrelated|irrelevant|would|might|could|if)\b/i;
50654
50827
  var CITE_GROUP_RE = /\(((?:[^()\n]|\([^()\n]*\)){1,200})\)/g;
@@ -51392,7 +51565,7 @@ ${conversation}` }]
51392
51565
  }
51393
51566
 
51394
51567
  // src/constraint-backfill.ts
51395
- init_src4();
51568
+ init_src5();
51396
51569
  init_src2();
51397
51570
  import { existsSync as existsSync13, readFileSync as readFileSync11, readdirSync as readdirSync6, writeFileSync as writeFileSync11 } from "node:fs";
51398
51571
  import { join as join16 } from "node:path";
@@ -51561,7 +51734,7 @@ function backfillConstraintKind(store, opts) {
51561
51734
 
51562
51735
  // src/edge-repair.ts
51563
51736
  init_src();
51564
- init_src4();
51737
+ init_src5();
51565
51738
  import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "node:fs";
51566
51739
  import { join as join17 } from "node:path";
51567
51740
  function citeEdgeId(from, type, to) {
@@ -51838,7 +52011,7 @@ var PassWorker = class {
51838
52011
  init_outbox();
51839
52012
 
51840
52013
  // src/claim-sync.ts
51841
- init_src8();
52014
+ init_src9();
51842
52015
  init_src();
51843
52016
  init_src2();
51844
52017
  import { readFileSync as readFileSync13 } from "node:fs";
@@ -52884,6 +53057,12 @@ function sessionOriginKey(sessionId) {
52884
53057
  function refreshRepoLocator(root, profile) {
52885
53058
  const detected = detectRepoLocator(root, profile.repoRemote);
52886
53059
  if (!detected || detected === profile.repoLocator) return false;
53060
+ if (profile.repoLocator) {
53061
+ const aliases = new Set(profile.repoLocatorAliases ?? []);
53062
+ aliases.add(profile.repoLocator);
53063
+ aliases.delete(detected);
53064
+ profile.repoLocatorAliases = [...aliases];
53065
+ }
52887
53066
  profile.repoLocator = detected;
52888
53067
  saveProfile(root, profile);
52889
53068
  return true;
@@ -53274,7 +53453,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
53274
53453
  }
53275
53454
 
53276
53455
  // src/engine.ts
53277
- var DAEMON_VERSION = true ? "2.0.2-dev.362" : "2.0.0-alpha.0";
53456
+ var DAEMON_VERSION = true ? "2.0.2-dev.437" : "2.0.0-alpha.0";
53278
53457
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
53279
53458
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
53280
53459
  var GIT_OP_MUTE_MS = 4e3;
@@ -53610,6 +53789,27 @@ function createWorkspaceEngine(opts) {
53610
53789
  }
53611
53790
  }
53612
53791
  };
53792
+ const clip = (s, n) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
53793
+ const pickReviewItems = () => {
53794
+ const shared = opts.sharedStore;
53795
+ if (!shared) return [];
53796
+ try {
53797
+ if ((/* @__PURE__ */ new Date()).getHours() % 2 === 0) {
53798
+ const small = pendingAbstractions(shared).filter((a) => a.members.length >= 2 && a.members.length <= 5).sort((a, b) => a.members.length - b.members.length)[0];
53799
+ if (small) {
53800
+ return [{
53801
+ kind: "abstraction",
53802
+ candidate: small.candidate,
53803
+ members: small.members.map((m) => ({ id: m.id, description: clip(m.description, 110) }))
53804
+ }];
53805
+ }
53806
+ }
53807
+ const [d] = pendingDiscriminators(shared, { limit: 1 });
53808
+ return d ? [{ kind: "discriminator", route: d.route, presenting: clip(d.presenting, 140), cause: clip(d.cause, 140) }] : [];
53809
+ } catch {
53810
+ return [];
53811
+ }
53812
+ };
53613
53813
  const refreshContextOnce = async () => {
53614
53814
  const elicit = isEdgeElicitationEnabled();
53615
53815
  const snapOpts = {
@@ -53623,13 +53823,15 @@ function createWorkspaceEngine(opts) {
53623
53823
  const prebuilt = passWorker ? await passWorker.snapshot(snapOpts).catch(() => null) : null;
53624
53824
  const doneRender = prebuilt ? null : markPass(`context-render-inline:${profile.name}`);
53625
53825
  const pendingUpdate = opts.pendingUpdate?.() ?? null;
53826
+ const reviewItems = pickReviewItems();
53626
53827
  const { body: body2, snapshot } = assembleAgentContext({
53627
53828
  store,
53628
53829
  ...snapOpts,
53629
53830
  remote: remotePriors,
53630
53831
  ...elicit ? { edgeElicitation: { instruction: PRIOR_TAG_INSTRUCTION } } : {},
53631
53832
  ...prebuilt ? { snapshot: prebuilt } : {},
53632
- ...pendingUpdate ? { pendingUpdate } : {}
53833
+ ...pendingUpdate ? { pendingUpdate } : {},
53834
+ ...reviewItems.length ? { reviewItems } : {}
53633
53835
  });
53634
53836
  doneRender?.();
53635
53837
  writeContextFile(opts.workspaceRoot, body2);
@@ -54112,6 +54314,22 @@ function createWorkspaceEngine(opts) {
54112
54314
  } catch (err2) {
54113
54315
  console.warn("[errata] inline triage route failed:", err2);
54114
54316
  }
54317
+ if (evidence !== "inferred") {
54318
+ try {
54319
+ mintWorkspaceCausalFact(
54320
+ store,
54321
+ {
54322
+ presentingId,
54323
+ causeId: tr.causeId,
54324
+ ...tr.causeDescription ? { causeDescription: tr.causeDescription } : {},
54325
+ sessionId
54326
+ },
54327
+ t
54328
+ );
54329
+ } catch (err2) {
54330
+ console.warn("[errata] workspace causal-fact mirror failed:", err2);
54331
+ }
54332
+ }
54115
54333
  }
54116
54334
  }
54117
54335
  if (opts.sharedStore) {
@@ -54125,6 +54343,13 @@ function createWorkspaceEngine(opts) {
54125
54343
  }
54126
54344
  }
54127
54345
  }
54346
+ for (const link of plan.causalLinks) {
54347
+ try {
54348
+ recordCauseChainLink(store, link, t, { contributor: sessionId, sources: [sessionId] });
54349
+ } catch (err2) {
54350
+ console.warn("[errata] workspace causal chain mirror failed:", err2);
54351
+ }
54352
+ }
54128
54353
  for (const at of plan.attempts) {
54129
54354
  const pid = at.problemId ?? (at.threadId ? threads.get(at.threadId) : void 0) ?? (at.boundStatement ? designProblemId(at.boundStatement) : void 0);
54130
54355
  const node2 = pid ? store.getNode(pid) : void 0;
@@ -54939,11 +55164,11 @@ function pidAlive(pid) {
54939
55164
 
54940
55165
  // src/multi.ts
54941
55166
  init_dist();
54942
- init_src4();
55167
+ init_src5();
54943
55168
  import { readFileSync as readFileSync25, unlinkSync as unlinkSync3, writeFileSync as writeFileSync21 } from "node:fs";
54944
55169
 
54945
55170
  // src/principle-sync.ts
54946
- init_src4();
55171
+ init_src5();
54947
55172
  async function syncPrinciples(store, cloud, opts) {
54948
55173
  const mine = buildPrincipleSync(store, opts.machine);
54949
55174
  let pushed = 0;
@@ -55137,12 +55362,12 @@ function runLockfilePass(opts) {
55137
55362
  // src/instance-ingest.ts
55138
55363
  init_src();
55139
55364
  init_src2();
55140
- init_src4();
55141
- init_src8();
55365
+ init_src5();
55366
+ init_src9();
55142
55367
  init_src();
55143
55368
 
55144
55369
  // src/contribution-ready.ts
55145
- init_src3();
55370
+ init_src4();
55146
55371
  function cosine4(a, b) {
55147
55372
  if (a.length === 0 || a.length !== b.length) return 0;
55148
55373
  let dot = 0;
@@ -56463,6 +56688,37 @@ async function startMultiDaemon(opts = {}) {
56463
56688
  async percolateAll() {
56464
56689
  const ts = Date.now();
56465
56690
  const out2 = /* @__PURE__ */ new Map();
56691
+ const repoBasename = (locator) => (locator.split("/").filter(Boolean).pop() ?? locator).toLowerCase();
56692
+ for (const r of records) {
56693
+ const profile = r.engine.profile;
56694
+ const aliases = profile.repoLocatorAliases ?? [];
56695
+ const locator = profile.repoLocator;
56696
+ if (!locator || aliases.length === 0) continue;
56697
+ try {
56698
+ for (const alias of aliases) {
56699
+ if (repoBasename(alias) !== repoBasename(locator)) {
56700
+ console.log(
56701
+ `[errata] project alias NOT auto-migrated for ${r.entry.name} (${alias} \u2192 ${locator}: repo name differs \u2014 a fork/repoint, not a rename). Run scripts/migrate-project-alias.ts if this really is the same repo.`
56702
+ );
56703
+ continue;
56704
+ }
56705
+ const m = migrateProjectAlias(sharedStore, { from: alias, to: locator, ts });
56706
+ if (m.nodesRewritten || m.edgesRewritten) {
56707
+ console.log(
56708
+ `[errata] project alias migrated for ${r.entry.name}: ${alias} \u2192 ${locator} (${m.nodesRewritten} nodes, ${m.edgesRewritten} edges)`
56709
+ );
56710
+ }
56711
+ }
56712
+ delete profile.repoLocatorAliases;
56713
+ const onDisk = loadProfile(r.root);
56714
+ if (onDisk) {
56715
+ delete onDisk.repoLocatorAliases;
56716
+ saveProfile(r.root, onDisk);
56717
+ }
56718
+ } catch (err2) {
56719
+ console.warn(`[errata] project-alias migration failed for ${r.entry.name} (kept for next pass): ${err2 instanceof Error ? err2.message : err2}`);
56720
+ }
56721
+ }
56466
56722
  let inlineRecords = records;
56467
56723
  if (consolidateWorker) {
56468
56724
  const fileBacked = records.filter((r) => r.engine.paths.castalia !== ":memory:");
@@ -56866,9 +57122,9 @@ async function startMultiDaemon(opts = {}) {
56866
57122
  }
56867
57123
 
56868
57124
  // src/cli.ts
56869
- init_src4();
57125
+ init_src5();
56870
57126
  init_config();
56871
- init_src6();
57127
+ init_src7();
56872
57128
  init_cloud_auth();
56873
57129
 
56874
57130
  // src/agent-switch.ts
@@ -56981,7 +57237,7 @@ function errText(err2) {
56981
57237
  init_cloud_endpoint_policy();
56982
57238
 
56983
57239
  // src/oauth-login.ts
56984
- init_src6();
57240
+ init_src7();
56985
57241
  init_cloud_auth();
56986
57242
  import { createServer } from "node:http";
56987
57243
  var DEFAULT_OAUTH_SCOPE = "openid profile graph:read graph:write mcp:tools";
@@ -57206,7 +57462,7 @@ async function loginOAuthLoopback(opts) {
57206
57462
  }
57207
57463
 
57208
57464
  // src/device-login.ts
57209
- init_src6();
57465
+ init_src7();
57210
57466
  var DEFAULT_SCOPE = "openid profile graph:read graph:write mcp:tools";
57211
57467
  var BRIDGE_REDIRECT_URI = "http://127.0.0.1:1/device-bridge-callback";
57212
57468
  var strip2 = (u) => u.replace(/\/+$/, "");
@@ -58012,7 +58268,7 @@ async function cmdStatus() {
58012
58268
  console.log(` event log: ${existsSync27(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
58013
58269
  if (existsSync27(paths.castalia)) {
58014
58270
  try {
58015
- const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
58271
+ const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
58016
58272
  const store = openGraphStore2({ path: paths.castalia });
58017
58273
  try {
58018
58274
  let pending = 0;
@@ -58760,7 +59016,7 @@ async function cmdLocate(relPath) {
58760
59016
  process.exit(2);
58761
59017
  }
58762
59018
  await ensureProfile();
58763
- const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
59019
+ const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
58764
59020
  const { workspacePaths: workspacePaths2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
58765
59021
  const paths = workspacePaths2(ROOT);
58766
59022
  const store = openGraphStore2({ path: paths.castalia });
@@ -58810,7 +59066,7 @@ async function cmdNeighbors(args2) {
58810
59066
  const depthIdx = args2.indexOf("--depth");
58811
59067
  const depth = depthIdx >= 0 && args2[depthIdx + 1] ? Math.max(1, Math.min(5, Number(args2[depthIdx + 1]))) : 2;
58812
59068
  await ensureProfile();
58813
- const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
59069
+ const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
58814
59070
  const { workspacePaths: workspacePaths2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
58815
59071
  const paths = workspacePaths2(ROOT);
58816
59072
  const store = openGraphStore2({ path: paths.castalia });
@@ -58858,7 +59114,7 @@ async function cmdNeighbors(args2) {
58858
59114
  }
58859
59115
  async function cmdTodos() {
58860
59116
  await ensureProfile();
58861
- const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
59117
+ const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
58862
59118
  const { workspacePaths: workspacePaths2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
58863
59119
  const paths = workspacePaths2(ROOT);
58864
59120
  const store = openGraphStore2({ path: paths.castalia });
@@ -58897,7 +59153,7 @@ async function cmdTodos() {
58897
59153
  async function cmdBackfillAnchors() {
58898
59154
  const profile = loadProfile(ROOT);
58899
59155
  await ensureProfile();
58900
- const { anchorSolutionToDiff: anchorSolutionToDiff2, anchorProblemToDiff: anchorProblemToDiff2, deriveContextFromAnchors: deriveContextFromAnchors2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
59156
+ const { anchorSolutionToDiff: anchorSolutionToDiff2, anchorProblemToDiff: anchorProblemToDiff2, deriveContextFromAnchors: deriveContextFromAnchors2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
58901
59157
  const { bindCanonicalNeighbors: bindCanonicalNeighbors2 } = await Promise.resolve().then(() => (init_src10(), src_exports4));
58902
59158
  const { digest: digest2 } = await Promise.resolve().then(() => (init_src(), src_exports));
58903
59159
  await withStore(async (store) => {
@@ -58973,7 +59229,7 @@ async function cmdBackfillAnchors() {
58973
59229
  }
58974
59230
  async function withStore(fn) {
58975
59231
  await ensureProfile();
58976
- const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
59232
+ const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
58977
59233
  const { workspacePaths: workspacePaths2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
58978
59234
  const paths = workspacePaths2(ROOT);
58979
59235
  const store = openGraphStore2({ path: paths.castalia });
@@ -59367,7 +59623,7 @@ async function gatherRepo(store, ws) {
59367
59623
  }
59368
59624
  async function gatherReportData(generatedAt) {
59369
59625
  const { existsSync: existsSync28 } = await import("node:fs");
59370
- const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
59626
+ const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
59371
59627
  const cfg = loadConfig();
59372
59628
  const outbound = cfg.consent.sync ? "auto" : "off";
59373
59629
  const repos = [];
@@ -60167,7 +60423,7 @@ async function cmdFeedback(args2) {
60167
60423
  console.error('usage: errata feedback [up|down] "<message>"');
60168
60424
  process.exit(2);
60169
60425
  }
60170
- const { scrub: scrub2 } = await Promise.resolve().then(() => (init_src7(), src_exports3));
60426
+ const { scrub: scrub2 } = await Promise.resolve().then(() => (init_src8(), src_exports3));
60171
60427
  const scrubbed = scrub2(message).text;
60172
60428
  const c = authedCloudClient(cfg);
60173
60429
  try {