@inerrata-corporation/errata 2.0.0-dev.79 → 2.0.0-dev.82
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/consolidate-worker.mjs +129 -0
- package/errata.mjs +198 -133
- package/package.json +1 -1
- package/pass-worker.mjs +129 -0
package/consolidate-worker.mjs
CHANGED
|
@@ -15110,9 +15110,90 @@ var init_zod = __esm({
|
|
|
15110
15110
|
});
|
|
15111
15111
|
|
|
15112
15112
|
// ../../packages/shared/src/edge-rules.ts
|
|
15113
|
+
function isValidEdge(from, type, to) {
|
|
15114
|
+
const rule = EDGE_RULES[type];
|
|
15115
|
+
if (!rule) return { ok: true };
|
|
15116
|
+
if (from && rule.from && !rule.from.includes(from)) {
|
|
15117
|
+
return {
|
|
15118
|
+
ok: false,
|
|
15119
|
+
reason: `${type} cannot originate from ${from} (allowed: ${rule.from.join(", ")})`
|
|
15120
|
+
};
|
|
15121
|
+
}
|
|
15122
|
+
if (to && rule.to && !rule.to.includes(to)) {
|
|
15123
|
+
return {
|
|
15124
|
+
ok: false,
|
|
15125
|
+
reason: `${type} cannot target ${to} (allowed: ${rule.to.join(", ")})`
|
|
15126
|
+
};
|
|
15127
|
+
}
|
|
15128
|
+
return { ok: true };
|
|
15129
|
+
}
|
|
15130
|
+
var EDGE_RULES;
|
|
15113
15131
|
var init_edge_rules = __esm({
|
|
15114
15132
|
"../../packages/shared/src/edge-rules.ts"() {
|
|
15115
15133
|
"use strict";
|
|
15134
|
+
EDGE_RULES = {
|
|
15135
|
+
// ── Project anchors (PJ-anchors · PLAN_PROJECT_STORE §6c) ──
|
|
15136
|
+
// ANCHORED_AT is the ONE bridge edge admitted to the cloud, and it exists for
|
|
15137
|
+
// exactly one purpose: point a piece of knowledge at an OPAQUE project `Symbol`
|
|
15138
|
+
// so two teammates' observations about the same function converge on one node.
|
|
15139
|
+
// Constrain it to that purpose — an unconstrained ANCHORED_AT (its state before
|
|
15140
|
+
// this rule, since a missing entry means "anything goes") would let a batch
|
|
15141
|
+
// anchor anything at anything, e.g. at a public `Package`, quietly re-creating
|
|
15142
|
+
// the code↔collective bridge the membrane exists to prevent.
|
|
15143
|
+
ANCHORED_AT: {
|
|
15144
|
+
from: ["Problem", "Solution", "RootCause", "Claim"],
|
|
15145
|
+
to: ["Symbol"]
|
|
15146
|
+
},
|
|
15147
|
+
// ── Security spine (v1 taxonomy.ts SECURITY_FORWARD_ONLY commentary) ──
|
|
15148
|
+
TARGETS: { from: ["Exploit"], to: ["Vulnerability"] },
|
|
15149
|
+
ENABLES: { from: ["AntiPattern"], to: ["Vulnerability"] },
|
|
15150
|
+
MITIGATES: { from: ["Solution"], to: ["Vulnerability"] },
|
|
15151
|
+
PRESENTS: { from: ["Solution"], to: ["Problem", "Vulnerability"] },
|
|
15152
|
+
// SOLVED_BY sources: Vulnerability (v1 comment) + Problem/RootCause
|
|
15153
|
+
// (upstream design-problem.ts + triage.ts mint both).
|
|
15154
|
+
SOLVED_BY: { from: ["Vulnerability", "Problem", "RootCause"], to: ["Solution"] },
|
|
15155
|
+
// FIXED_BY sources mirror SOLVED_BY's (same ailment→fix semantics; live-graph
|
|
15156
|
+
// census 2026-07-14: 512 Problem + 13 RootCause sources, zero others besides
|
|
15157
|
+
// 3 REVERSED Solution→Problem edges the missing `from` let through — the
|
|
15158
|
+
// "Problem solved by Problem" splash bug. A reversed resolution edge is NOT
|
|
15159
|
+
// mechanically flippable (half the sample were PoC-not-fix claims), so the
|
|
15160
|
+
// door rejects rather than repairs; a correct re-emission lands via
|
|
15161
|
+
// recognition-at-the-door.
|
|
15162
|
+
FIXED_BY: { from: ["Vulnerability", "Problem", "RootCause"], to: ["Solution"] },
|
|
15163
|
+
// ── Triage family (upstream castalia.ts: Problem —TRIAGED_BY→ Triage —{INDICATES|CONFIRMS}→ cause) ──
|
|
15164
|
+
// Two-edge differential model (T3-rename): INDICATES = provisional candidate,
|
|
15165
|
+
// CONFIRMS = promoted router (a discriminator exists). Same endpoint matrix.
|
|
15166
|
+
// ROUTES_TO is the deprecated pre-split alias, kept readable during migration.
|
|
15167
|
+
TRIAGED_BY: { from: ["Problem"], to: ["Triage"] },
|
|
15168
|
+
CONFIRMS: { from: ["Triage"], to: ["RootCause", "Problem"] },
|
|
15169
|
+
INDICATES: { from: ["Triage"], to: ["RootCause", "Problem"] },
|
|
15170
|
+
ROUTES_TO: { from: ["Triage"], to: ["RootCause", "Problem"] },
|
|
15171
|
+
// ── Context grounding (v1 taxonomy.ts comments) ──
|
|
15172
|
+
WRITTEN_IN: { from: ["Component", "Package"], to: ["Language"] },
|
|
15173
|
+
OCCURS_IN: { to: ["Language"] },
|
|
15174
|
+
OCCURS_ON: { to: ["OperatingSystem"] },
|
|
15175
|
+
RUNS_ON: { to: ["OperatingSystem"] },
|
|
15176
|
+
OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
|
|
15177
|
+
INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
|
|
15178
|
+
// ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
|
|
15179
|
+
IS_A: { from: ["Weakness"], to: ["Weakness"] },
|
|
15180
|
+
// ── Conceptual (v1 taxonomy.ts: instance → motif/pattern reference) ──
|
|
15181
|
+
INSTANCE_OF: { to: ["Pattern", "Weakness", "Technique"] },
|
|
15182
|
+
IMPLEMENTS: { from: ["Solution", "Language", "Component"], to: ["Pattern", "Technique"] },
|
|
15183
|
+
MATCHES: { to: ["Pattern"] },
|
|
15184
|
+
// ── Artifact evidence (v1 taxonomy.ts artifact rows) ──
|
|
15185
|
+
PRODUCED: { from: ["Report"], to: ["Artifact"] },
|
|
15186
|
+
MANIFESTED_IN: { from: ["Problem"], to: ["Artifact"] },
|
|
15187
|
+
REVEALED_BY: { from: ["RootCause"], to: ["Artifact"] },
|
|
15188
|
+
// ── Belief / abstraction gradients (upstream castalia.ts) ──
|
|
15189
|
+
CORROBORATES: { from: ["Claim"], to: ["Claim"] },
|
|
15190
|
+
GENERALIZES: { from: ["Claim"] },
|
|
15191
|
+
DERIVED_FROM: { from: ["Claim"] },
|
|
15192
|
+
// ── Decomposition (castalia.ts: a Problem fans out into independent root
|
|
15193
|
+
// problems; §5.1 also attaches SPLIT_INTO to Claim when one conflates two).
|
|
15194
|
+
// `to` left unconstrained — the replacement set's labels vary by case. ──
|
|
15195
|
+
SPLIT_INTO: { from: ["Problem", "Claim"] }
|
|
15196
|
+
};
|
|
15116
15197
|
}
|
|
15117
15198
|
});
|
|
15118
15199
|
|
|
@@ -15274,6 +15355,13 @@ var init_project_symbol = __esm({
|
|
|
15274
15355
|
}
|
|
15275
15356
|
});
|
|
15276
15357
|
|
|
15358
|
+
// ../../packages/shared/src/lockfile.ts
|
|
15359
|
+
var init_lockfile = __esm({
|
|
15360
|
+
"../../packages/shared/src/lockfile.ts"() {
|
|
15361
|
+
"use strict";
|
|
15362
|
+
}
|
|
15363
|
+
});
|
|
15364
|
+
|
|
15277
15365
|
// ../../packages/shared/src/nlp/canonicalize.ts
|
|
15278
15366
|
var init_canonicalize = __esm({
|
|
15279
15367
|
"../../packages/shared/src/nlp/canonicalize.ts"() {
|
|
@@ -15310,6 +15398,7 @@ var init_src = __esm({
|
|
|
15310
15398
|
init_symbol_intent();
|
|
15311
15399
|
init_repo_locator();
|
|
15312
15400
|
init_project_symbol();
|
|
15401
|
+
init_lockfile();
|
|
15313
15402
|
init_hash();
|
|
15314
15403
|
init_error_signature();
|
|
15315
15404
|
init_canonicalize();
|
|
@@ -15623,6 +15712,15 @@ import { parentPort } from "node:worker_threads";
|
|
|
15623
15712
|
|
|
15624
15713
|
// ../../packages/local-graph/src/store.ts
|
|
15625
15714
|
init_src2();
|
|
15715
|
+
init_src();
|
|
15716
|
+
var LOCAL_RULE_OVERRIDES = {
|
|
15717
|
+
ANCHORED_AT: {
|
|
15718
|
+
from: EDGE_RULES["ANCHORED_AT"]?.from,
|
|
15719
|
+
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
15720
|
+
},
|
|
15721
|
+
REVEALED_BY: null,
|
|
15722
|
+
PRODUCED: null
|
|
15723
|
+
};
|
|
15626
15724
|
var SCHEMA_VERSION = 5;
|
|
15627
15725
|
var SCHEMA_SQL = `
|
|
15628
15726
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -15772,6 +15870,10 @@ var SqliteGraphStore = class {
|
|
|
15772
15870
|
* every node/edge upsert — code-index and capture alike — so the consolidation
|
|
15773
15871
|
* trigger can read "how much changed" without caring what changed it. */
|
|
15774
15872
|
mutations = 0;
|
|
15873
|
+
/** Edges refused by the local ontology gate (EDGE_RULES) since open — a
|
|
15874
|
+
* visible counter so a misbehaving extractor shows up in stats instead of
|
|
15875
|
+
* silently thinning the graph. */
|
|
15876
|
+
rejectedEdgeCount = 0;
|
|
15775
15877
|
/** Persistent monotonic ingest-sequence high-water (KN-decay-momentum). Loaded
|
|
15776
15878
|
* from store_meta on open, advanced once per knowledge ingest event via
|
|
15777
15879
|
* nextIngestSeq(). Distinct from `mutations` (in-memory, per-session, bumped by
|
|
@@ -16081,6 +16183,12 @@ var SqliteGraphStore = class {
|
|
|
16081
16183
|
this.stmts.setCommunity.run({ id, v: community });
|
|
16082
16184
|
}
|
|
16083
16185
|
mergeEdge(edge) {
|
|
16186
|
+
const violation = this.edgeRuleViolation(edge);
|
|
16187
|
+
if (violation) {
|
|
16188
|
+
this.rejectedEdgeCount++;
|
|
16189
|
+
console.warn(`[local-graph] rejected edge ${edge.from}-[:${edge.type}]->${edge.to}: ${violation}`);
|
|
16190
|
+
return;
|
|
16191
|
+
}
|
|
16084
16192
|
this.mutations++;
|
|
16085
16193
|
this.stmts.insertEdge.run({
|
|
16086
16194
|
id: edge.id,
|
|
@@ -16098,6 +16206,27 @@ var SqliteGraphStore = class {
|
|
|
16098
16206
|
attrs_json: JSON.stringify(edge.attrs ?? {})
|
|
16099
16207
|
});
|
|
16100
16208
|
}
|
|
16209
|
+
/** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
|
|
16210
|
+
* overlay consulted first. Returns the reason string on a documented-
|
|
16211
|
+
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
16212
|
+
* next to the insert itself. */
|
|
16213
|
+
edgeRuleViolation(edge) {
|
|
16214
|
+
const fromLabel = this.getNode(edge.from)?.label;
|
|
16215
|
+
const toLabel = this.getNode(edge.to)?.label;
|
|
16216
|
+
if (edge.type in LOCAL_RULE_OVERRIDES) {
|
|
16217
|
+
const rule = LOCAL_RULE_OVERRIDES[edge.type];
|
|
16218
|
+
if (!rule) return null;
|
|
16219
|
+
if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
|
|
16220
|
+
return `${edge.type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
|
|
16221
|
+
}
|
|
16222
|
+
if (toLabel && rule.to && !rule.to.includes(toLabel)) {
|
|
16223
|
+
return `${edge.type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
16224
|
+
}
|
|
16225
|
+
return null;
|
|
16226
|
+
}
|
|
16227
|
+
const verdict = isValidEdge(fromLabel, edge.type, toLabel);
|
|
16228
|
+
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
16229
|
+
}
|
|
16101
16230
|
updateEdge(id, patch) {
|
|
16102
16231
|
this.stmts.updateEdge.run({
|
|
16103
16232
|
id,
|
package/errata.mjs
CHANGED
|
@@ -15656,6 +15656,144 @@ var init_project_symbol = __esm({
|
|
|
15656
15656
|
}
|
|
15657
15657
|
});
|
|
15658
15658
|
|
|
15659
|
+
// ../../packages/shared/src/lockfile.ts
|
|
15660
|
+
function npmPurl(name2, version2) {
|
|
15661
|
+
return `pkg:npm/${name2}@${version2}`;
|
|
15662
|
+
}
|
|
15663
|
+
function component(name2, version2) {
|
|
15664
|
+
return { purl: npmPurl(name2, version2), name: name2, version: version2, ecosystem: "npm" };
|
|
15665
|
+
}
|
|
15666
|
+
function parsePackageLockJson(jsonText) {
|
|
15667
|
+
let doc;
|
|
15668
|
+
try {
|
|
15669
|
+
doc = JSON.parse(jsonText);
|
|
15670
|
+
} catch {
|
|
15671
|
+
return { components: [], dependencies: [] };
|
|
15672
|
+
}
|
|
15673
|
+
const packages = doc.packages ?? {};
|
|
15674
|
+
const components = [];
|
|
15675
|
+
const hoisted = /* @__PURE__ */ new Map();
|
|
15676
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
15677
|
+
for (const [path2, meta3] of Object.entries(packages)) {
|
|
15678
|
+
if (path2 === "" || meta3.link === true) continue;
|
|
15679
|
+
const ix = path2.lastIndexOf("node_modules/");
|
|
15680
|
+
if (ix === -1) continue;
|
|
15681
|
+
const name2 = path2.slice(ix + "node_modules/".length);
|
|
15682
|
+
const version2 = typeof meta3.version === "string" ? meta3.version : "";
|
|
15683
|
+
if (!name2 || !version2) continue;
|
|
15684
|
+
components.push(component(name2, version2));
|
|
15685
|
+
byPath.set(path2, { name: name2, version: version2 });
|
|
15686
|
+
const depth = path2.split("node_modules/").length;
|
|
15687
|
+
const prev = hoisted.get(name2);
|
|
15688
|
+
if (!prev || depth < prev.depth) hoisted.set(name2, { version: version2, depth });
|
|
15689
|
+
}
|
|
15690
|
+
const dependencies = [];
|
|
15691
|
+
for (const [path2, meta3] of Object.entries(packages)) {
|
|
15692
|
+
const self = byPath.get(path2);
|
|
15693
|
+
if (!self) continue;
|
|
15694
|
+
const deps = meta3.dependencies;
|
|
15695
|
+
if (!deps || typeof deps !== "object") continue;
|
|
15696
|
+
const dependsOn = [];
|
|
15697
|
+
for (const depName of Object.keys(deps)) {
|
|
15698
|
+
const target = hoisted.get(depName);
|
|
15699
|
+
if (target) dependsOn.push(npmPurl(depName, target.version));
|
|
15700
|
+
}
|
|
15701
|
+
if (dependsOn.length > 0)
|
|
15702
|
+
dependencies.push({ ref: npmPurl(self.name, self.version), dependsOn });
|
|
15703
|
+
}
|
|
15704
|
+
return { components, dependencies: dedupeRefs(dependencies) };
|
|
15705
|
+
}
|
|
15706
|
+
function splitPnpmKey(raw2) {
|
|
15707
|
+
let k = raw2.trim().replace(/^['"]|['"]$/g, "");
|
|
15708
|
+
if (k.startsWith("/")) k = k.slice(1);
|
|
15709
|
+
const paren = k.indexOf("(");
|
|
15710
|
+
if (paren !== -1) k = k.slice(0, paren);
|
|
15711
|
+
if (k.startsWith("link:") || k.startsWith("file:") || k.includes("://")) return null;
|
|
15712
|
+
const at = k.lastIndexOf("@");
|
|
15713
|
+
if (at <= 0) return null;
|
|
15714
|
+
const name2 = k.slice(0, at);
|
|
15715
|
+
const version2 = k.slice(at + 1);
|
|
15716
|
+
if (!name2 || !version2) return null;
|
|
15717
|
+
return { name: name2, version: version2 };
|
|
15718
|
+
}
|
|
15719
|
+
function parsePnpmLock(text) {
|
|
15720
|
+
const lines = text.split(/\r?\n/);
|
|
15721
|
+
const components = [];
|
|
15722
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15723
|
+
const dependencies = [];
|
|
15724
|
+
let section = null;
|
|
15725
|
+
let currentPkg = null;
|
|
15726
|
+
let inDepsBlock = false;
|
|
15727
|
+
let currentDeps = [];
|
|
15728
|
+
const flushDeps = () => {
|
|
15729
|
+
if (currentPkg && currentDeps.length > 0) {
|
|
15730
|
+
dependencies.push({
|
|
15731
|
+
ref: npmPurl(currentPkg.name, currentPkg.version),
|
|
15732
|
+
dependsOn: currentDeps.slice()
|
|
15733
|
+
});
|
|
15734
|
+
}
|
|
15735
|
+
currentDeps = [];
|
|
15736
|
+
};
|
|
15737
|
+
for (const line of lines) {
|
|
15738
|
+
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
15739
|
+
const indent = line.length - line.trimStart().length;
|
|
15740
|
+
if (indent === 0) {
|
|
15741
|
+
flushDeps();
|
|
15742
|
+
currentPkg = null;
|
|
15743
|
+
inDepsBlock = false;
|
|
15744
|
+
section = line === "packages:" ? "packages" : line === "snapshots:" ? "snapshots" : null;
|
|
15745
|
+
continue;
|
|
15746
|
+
}
|
|
15747
|
+
if (!section) continue;
|
|
15748
|
+
if (indent === 2 && line.trimEnd().endsWith(":")) {
|
|
15749
|
+
flushDeps();
|
|
15750
|
+
inDepsBlock = false;
|
|
15751
|
+
const key = line.trim().slice(0, -1);
|
|
15752
|
+
currentPkg = splitPnpmKey(key);
|
|
15753
|
+
if (currentPkg && section === "packages") {
|
|
15754
|
+
const purl = npmPurl(currentPkg.name, currentPkg.version);
|
|
15755
|
+
if (!seen.has(purl)) {
|
|
15756
|
+
seen.add(purl);
|
|
15757
|
+
components.push(component(currentPkg.name, currentPkg.version));
|
|
15758
|
+
}
|
|
15759
|
+
}
|
|
15760
|
+
continue;
|
|
15761
|
+
}
|
|
15762
|
+
if (!currentPkg) continue;
|
|
15763
|
+
if (indent === 4) {
|
|
15764
|
+
inDepsBlock = line.trim() === "dependencies:";
|
|
15765
|
+
continue;
|
|
15766
|
+
}
|
|
15767
|
+
if (inDepsBlock && indent === 6) {
|
|
15768
|
+
const t = line.trim();
|
|
15769
|
+
const at = t.indexOf(": ");
|
|
15770
|
+
if (at === -1) continue;
|
|
15771
|
+
const depName = t.slice(0, at).replace(/^['"]|['"]$/g, "");
|
|
15772
|
+
let depVersion = t.slice(at + 2).trim().replace(/^['"]|['"]$/g, "");
|
|
15773
|
+
const paren = depVersion.indexOf("(");
|
|
15774
|
+
if (paren !== -1) depVersion = depVersion.slice(0, paren);
|
|
15775
|
+
if (depName && depVersion && !depVersion.startsWith("link:"))
|
|
15776
|
+
currentDeps.push(npmPurl(depName, depVersion));
|
|
15777
|
+
}
|
|
15778
|
+
}
|
|
15779
|
+
flushDeps();
|
|
15780
|
+
return { components, dependencies: dedupeRefs(dependencies) };
|
|
15781
|
+
}
|
|
15782
|
+
function dedupeRefs(deps) {
|
|
15783
|
+
const byRef = /* @__PURE__ */ new Map();
|
|
15784
|
+
for (const d of deps) {
|
|
15785
|
+
const set2 = byRef.get(d.ref) ?? /* @__PURE__ */ new Set();
|
|
15786
|
+
for (const t of d.dependsOn) set2.add(t);
|
|
15787
|
+
byRef.set(d.ref, set2);
|
|
15788
|
+
}
|
|
15789
|
+
return [...byRef.entries()].map(([ref, set2]) => ({ ref, dependsOn: [...set2] }));
|
|
15790
|
+
}
|
|
15791
|
+
var init_lockfile = __esm({
|
|
15792
|
+
"../../packages/shared/src/lockfile.ts"() {
|
|
15793
|
+
"use strict";
|
|
15794
|
+
}
|
|
15795
|
+
});
|
|
15796
|
+
|
|
15659
15797
|
// ../../packages/shared/src/nlp/canonicalize.ts
|
|
15660
15798
|
function canonicalize(value) {
|
|
15661
15799
|
return value.normalize("NFC").trim().toLowerCase().replace(/\s+/g, " ");
|
|
@@ -15774,9 +15912,12 @@ __export(src_exports, {
|
|
|
15774
15912
|
normalizePredicate: () => normalizePredicate,
|
|
15775
15913
|
normalizeRepoLocator: () => normalizeRepoLocator,
|
|
15776
15914
|
normalizeSymbolPath: () => normalizeSymbolPath,
|
|
15915
|
+
npmPurl: () => npmPurl,
|
|
15777
15916
|
packageCanonicalId: () => packageCanonicalId,
|
|
15778
15917
|
pagerankEdgeTypes: () => pagerankEdgeTypes,
|
|
15918
|
+
parsePackageLockJson: () => parsePackageLockJson,
|
|
15779
15919
|
parsePackageRef: () => parsePackageRef,
|
|
15920
|
+
parsePnpmLock: () => parsePnpmLock,
|
|
15780
15921
|
prefilterEntities: () => prefilterEntities,
|
|
15781
15922
|
problemIdFromError: () => problemIdFromError,
|
|
15782
15923
|
projectSymbolId: () => projectSymbolId,
|
|
@@ -15785,6 +15926,7 @@ __export(src_exports, {
|
|
|
15785
15926
|
resolveCanonicalId: () => resolveCanonicalId,
|
|
15786
15927
|
sha256: () => sha256,
|
|
15787
15928
|
sha256Short: () => sha256Short,
|
|
15929
|
+
splitPnpmKey: () => splitPnpmKey,
|
|
15788
15930
|
substituteSymbolSummaries: () => substituteSymbolSummaries,
|
|
15789
15931
|
summarizeIngestResult: () => summarizeIngestResult,
|
|
15790
15932
|
summaryRejectionReason: () => summaryRejectionReason,
|
|
@@ -15802,6 +15944,7 @@ var init_src = __esm({
|
|
|
15802
15944
|
init_symbol_intent();
|
|
15803
15945
|
init_repo_locator();
|
|
15804
15946
|
init_project_symbol();
|
|
15947
|
+
init_lockfile();
|
|
15805
15948
|
init_hash();
|
|
15806
15949
|
init_error_signature();
|
|
15807
15950
|
init_canonicalize();
|
|
@@ -16188,11 +16331,20 @@ function rowToEdge(r) {
|
|
|
16188
16331
|
function openGraphStore(opts) {
|
|
16189
16332
|
return new SqliteGraphStore(opts);
|
|
16190
16333
|
}
|
|
16191
|
-
var SCHEMA_VERSION, SCHEMA_SQL, TEMPORAL_INDEX_SQL, SqliteGraphStore;
|
|
16334
|
+
var LOCAL_RULE_OVERRIDES, SCHEMA_VERSION, SCHEMA_SQL, TEMPORAL_INDEX_SQL, SqliteGraphStore;
|
|
16192
16335
|
var init_store = __esm({
|
|
16193
16336
|
"../../packages/local-graph/src/store.ts"() {
|
|
16194
16337
|
"use strict";
|
|
16195
16338
|
init_src2();
|
|
16339
|
+
init_src();
|
|
16340
|
+
LOCAL_RULE_OVERRIDES = {
|
|
16341
|
+
ANCHORED_AT: {
|
|
16342
|
+
from: EDGE_RULES["ANCHORED_AT"]?.from,
|
|
16343
|
+
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
16344
|
+
},
|
|
16345
|
+
REVEALED_BY: null,
|
|
16346
|
+
PRODUCED: null
|
|
16347
|
+
};
|
|
16196
16348
|
SCHEMA_VERSION = 5;
|
|
16197
16349
|
SCHEMA_SQL = `
|
|
16198
16350
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -16285,6 +16437,10 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
|
|
|
16285
16437
|
* every node/edge upsert — code-index and capture alike — so the consolidation
|
|
16286
16438
|
* trigger can read "how much changed" without caring what changed it. */
|
|
16287
16439
|
mutations = 0;
|
|
16440
|
+
/** Edges refused by the local ontology gate (EDGE_RULES) since open — a
|
|
16441
|
+
* visible counter so a misbehaving extractor shows up in stats instead of
|
|
16442
|
+
* silently thinning the graph. */
|
|
16443
|
+
rejectedEdgeCount = 0;
|
|
16288
16444
|
/** Persistent monotonic ingest-sequence high-water (KN-decay-momentum). Loaded
|
|
16289
16445
|
* from store_meta on open, advanced once per knowledge ingest event via
|
|
16290
16446
|
* nextIngestSeq(). Distinct from `mutations` (in-memory, per-session, bumped by
|
|
@@ -16594,6 +16750,12 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
|
|
|
16594
16750
|
this.stmts.setCommunity.run({ id, v: community });
|
|
16595
16751
|
}
|
|
16596
16752
|
mergeEdge(edge2) {
|
|
16753
|
+
const violation = this.edgeRuleViolation(edge2);
|
|
16754
|
+
if (violation) {
|
|
16755
|
+
this.rejectedEdgeCount++;
|
|
16756
|
+
console.warn(`[local-graph] rejected edge ${edge2.from}-[:${edge2.type}]->${edge2.to}: ${violation}`);
|
|
16757
|
+
return;
|
|
16758
|
+
}
|
|
16597
16759
|
this.mutations++;
|
|
16598
16760
|
this.stmts.insertEdge.run({
|
|
16599
16761
|
id: edge2.id,
|
|
@@ -16611,6 +16773,27 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
|
|
|
16611
16773
|
attrs_json: JSON.stringify(edge2.attrs ?? {})
|
|
16612
16774
|
});
|
|
16613
16775
|
}
|
|
16776
|
+
/** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
|
|
16777
|
+
* overlay consulted first. Returns the reason string on a documented-
|
|
16778
|
+
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
16779
|
+
* next to the insert itself. */
|
|
16780
|
+
edgeRuleViolation(edge2) {
|
|
16781
|
+
const fromLabel = this.getNode(edge2.from)?.label;
|
|
16782
|
+
const toLabel = this.getNode(edge2.to)?.label;
|
|
16783
|
+
if (edge2.type in LOCAL_RULE_OVERRIDES) {
|
|
16784
|
+
const rule = LOCAL_RULE_OVERRIDES[edge2.type];
|
|
16785
|
+
if (!rule) return null;
|
|
16786
|
+
if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
|
|
16787
|
+
return `${edge2.type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
|
|
16788
|
+
}
|
|
16789
|
+
if (toLabel && rule.to && !rule.to.includes(toLabel)) {
|
|
16790
|
+
return `${edge2.type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
16791
|
+
}
|
|
16792
|
+
return null;
|
|
16793
|
+
}
|
|
16794
|
+
const verdict = isValidEdge(fromLabel, edge2.type, toLabel);
|
|
16795
|
+
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
16796
|
+
}
|
|
16614
16797
|
updateEdge(id, patch) {
|
|
16615
16798
|
this.stmts.updateEdge.run({
|
|
16616
16799
|
id,
|
|
@@ -37675,6 +37858,17 @@ async function flushOutbox(paths, client, store) {
|
|
|
37675
37858
|
if (res.accepted > 0) uploaded++;
|
|
37676
37859
|
if (res.rejected > 0) {
|
|
37677
37860
|
failed++;
|
|
37861
|
+
const rejections = [
|
|
37862
|
+
...(res.result?.nodes ?? []).filter((n) => n.decision === "rejected"),
|
|
37863
|
+
...(res.result?.edges ?? []).filter((e) => e.decision === "rejected")
|
|
37864
|
+
];
|
|
37865
|
+
for (const r of rejections.slice(0, 5)) {
|
|
37866
|
+
const what = "type" in r ? `${r.from}-[:${r.type}]->${r.to}` : r.canonicalId ?? "node";
|
|
37867
|
+
console.warn(`[errata] outbox: cloud rejected ${what}: ${r.reason ?? "no reason given"}`);
|
|
37868
|
+
}
|
|
37869
|
+
if (rejections.length > 5) {
|
|
37870
|
+
console.warn(`[errata] outbox: \u2026and ${rejections.length - 5} more rejections in this batch`);
|
|
37871
|
+
}
|
|
37678
37872
|
quarantine(abs, `semantic rejection (${res.rejected} item${res.rejected === 1 ? "" : "s"})`);
|
|
37679
37873
|
continue;
|
|
37680
37874
|
}
|
|
@@ -43328,7 +43522,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
43328
43522
|
}
|
|
43329
43523
|
|
|
43330
43524
|
// src/engine.ts
|
|
43331
|
-
var DAEMON_VERSION = true ? "2.0.0-dev.
|
|
43525
|
+
var DAEMON_VERSION = true ? "2.0.0-dev.82" : "2.0.0-alpha.0";
|
|
43332
43526
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
43333
43527
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
43334
43528
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -44772,6 +44966,7 @@ async function syncPrinciples(store, cloud, opts) {
|
|
|
44772
44966
|
init_reconcile();
|
|
44773
44967
|
|
|
44774
44968
|
// src/lockfile-auto.ts
|
|
44969
|
+
init_src();
|
|
44775
44970
|
import { existsSync as existsSync20, readFileSync as readFileSync18 } from "node:fs";
|
|
44776
44971
|
import { join as join24 } from "node:path";
|
|
44777
44972
|
|
|
@@ -44898,137 +45093,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
44898
45093
|
}
|
|
44899
45094
|
|
|
44900
45095
|
// src/lockfile-auto.ts
|
|
44901
|
-
|
|
44902
|
-
return `pkg:npm/${name2}@${version2}`;
|
|
44903
|
-
}
|
|
44904
|
-
function component(name2, version2) {
|
|
44905
|
-
return { purl: npmPurl(name2, version2), name: name2, version: version2, ecosystem: "npm" };
|
|
44906
|
-
}
|
|
44907
|
-
function parsePackageLockJson(jsonText) {
|
|
44908
|
-
let doc;
|
|
44909
|
-
try {
|
|
44910
|
-
doc = JSON.parse(jsonText);
|
|
44911
|
-
} catch {
|
|
44912
|
-
return { components: [], dependencies: [] };
|
|
44913
|
-
}
|
|
44914
|
-
const packages = doc.packages ?? {};
|
|
44915
|
-
const components = [];
|
|
44916
|
-
const hoisted = /* @__PURE__ */ new Map();
|
|
44917
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
44918
|
-
for (const [path2, meta3] of Object.entries(packages)) {
|
|
44919
|
-
if (path2 === "" || meta3.link === true) continue;
|
|
44920
|
-
const ix = path2.lastIndexOf("node_modules/");
|
|
44921
|
-
if (ix === -1) continue;
|
|
44922
|
-
const name2 = path2.slice(ix + "node_modules/".length);
|
|
44923
|
-
const version2 = typeof meta3.version === "string" ? meta3.version : "";
|
|
44924
|
-
if (!name2 || !version2) continue;
|
|
44925
|
-
components.push(component(name2, version2));
|
|
44926
|
-
byPath.set(path2, { name: name2, version: version2 });
|
|
44927
|
-
const depth = path2.split("node_modules/").length;
|
|
44928
|
-
const prev = hoisted.get(name2);
|
|
44929
|
-
if (!prev || depth < prev.depth) hoisted.set(name2, { version: version2, depth });
|
|
44930
|
-
}
|
|
44931
|
-
const dependencies = [];
|
|
44932
|
-
for (const [path2, meta3] of Object.entries(packages)) {
|
|
44933
|
-
const self = byPath.get(path2);
|
|
44934
|
-
if (!self) continue;
|
|
44935
|
-
const deps = meta3.dependencies;
|
|
44936
|
-
if (!deps || typeof deps !== "object") continue;
|
|
44937
|
-
const dependsOn = [];
|
|
44938
|
-
for (const depName of Object.keys(deps)) {
|
|
44939
|
-
const target = hoisted.get(depName);
|
|
44940
|
-
if (target) dependsOn.push(npmPurl(depName, target.version));
|
|
44941
|
-
}
|
|
44942
|
-
if (dependsOn.length > 0)
|
|
44943
|
-
dependencies.push({ ref: npmPurl(self.name, self.version), dependsOn });
|
|
44944
|
-
}
|
|
44945
|
-
return { components, dependencies: dedupeRefs(dependencies) };
|
|
44946
|
-
}
|
|
44947
|
-
function splitPnpmKey(raw2) {
|
|
44948
|
-
let k = raw2.trim().replace(/^['"]|['"]$/g, "");
|
|
44949
|
-
if (k.startsWith("/")) k = k.slice(1);
|
|
44950
|
-
const paren = k.indexOf("(");
|
|
44951
|
-
if (paren !== -1) k = k.slice(0, paren);
|
|
44952
|
-
if (k.startsWith("link:") || k.startsWith("file:") || k.includes("://")) return null;
|
|
44953
|
-
const at = k.lastIndexOf("@");
|
|
44954
|
-
if (at <= 0) return null;
|
|
44955
|
-
const name2 = k.slice(0, at);
|
|
44956
|
-
const version2 = k.slice(at + 1);
|
|
44957
|
-
if (!name2 || !version2) return null;
|
|
44958
|
-
return { name: name2, version: version2 };
|
|
44959
|
-
}
|
|
44960
|
-
function parsePnpmLock(text) {
|
|
44961
|
-
const lines = text.split(/\r?\n/);
|
|
44962
|
-
const components = [];
|
|
44963
|
-
const seen = /* @__PURE__ */ new Set();
|
|
44964
|
-
const dependencies = [];
|
|
44965
|
-
let section = null;
|
|
44966
|
-
let currentPkg = null;
|
|
44967
|
-
let inDepsBlock = false;
|
|
44968
|
-
let currentDeps = [];
|
|
44969
|
-
const flushDeps = () => {
|
|
44970
|
-
if (currentPkg && currentDeps.length > 0) {
|
|
44971
|
-
dependencies.push({
|
|
44972
|
-
ref: npmPurl(currentPkg.name, currentPkg.version),
|
|
44973
|
-
dependsOn: currentDeps.slice()
|
|
44974
|
-
});
|
|
44975
|
-
}
|
|
44976
|
-
currentDeps = [];
|
|
44977
|
-
};
|
|
44978
|
-
for (const line of lines) {
|
|
44979
|
-
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
44980
|
-
const indent = line.length - line.trimStart().length;
|
|
44981
|
-
if (indent === 0) {
|
|
44982
|
-
flushDeps();
|
|
44983
|
-
currentPkg = null;
|
|
44984
|
-
inDepsBlock = false;
|
|
44985
|
-
section = line === "packages:" ? "packages" : line === "snapshots:" ? "snapshots" : null;
|
|
44986
|
-
continue;
|
|
44987
|
-
}
|
|
44988
|
-
if (!section) continue;
|
|
44989
|
-
if (indent === 2 && line.trimEnd().endsWith(":")) {
|
|
44990
|
-
flushDeps();
|
|
44991
|
-
inDepsBlock = false;
|
|
44992
|
-
const key = line.trim().slice(0, -1);
|
|
44993
|
-
currentPkg = splitPnpmKey(key);
|
|
44994
|
-
if (currentPkg && section === "packages") {
|
|
44995
|
-
const purl = npmPurl(currentPkg.name, currentPkg.version);
|
|
44996
|
-
if (!seen.has(purl)) {
|
|
44997
|
-
seen.add(purl);
|
|
44998
|
-
components.push(component(currentPkg.name, currentPkg.version));
|
|
44999
|
-
}
|
|
45000
|
-
}
|
|
45001
|
-
continue;
|
|
45002
|
-
}
|
|
45003
|
-
if (!currentPkg) continue;
|
|
45004
|
-
if (indent === 4) {
|
|
45005
|
-
inDepsBlock = line.trim() === "dependencies:";
|
|
45006
|
-
continue;
|
|
45007
|
-
}
|
|
45008
|
-
if (inDepsBlock && indent === 6) {
|
|
45009
|
-
const t = line.trim();
|
|
45010
|
-
const at = t.indexOf(": ");
|
|
45011
|
-
if (at === -1) continue;
|
|
45012
|
-
const depName = t.slice(0, at).replace(/^['"]|['"]$/g, "");
|
|
45013
|
-
let depVersion = t.slice(at + 2).trim().replace(/^['"]|['"]$/g, "");
|
|
45014
|
-
const paren = depVersion.indexOf("(");
|
|
45015
|
-
if (paren !== -1) depVersion = depVersion.slice(0, paren);
|
|
45016
|
-
if (depName && depVersion && !depVersion.startsWith("link:"))
|
|
45017
|
-
currentDeps.push(npmPurl(depName, depVersion));
|
|
45018
|
-
}
|
|
45019
|
-
}
|
|
45020
|
-
flushDeps();
|
|
45021
|
-
return { components, dependencies: dedupeRefs(dependencies) };
|
|
45022
|
-
}
|
|
45023
|
-
function dedupeRefs(deps) {
|
|
45024
|
-
const byRef = /* @__PURE__ */ new Map();
|
|
45025
|
-
for (const d of deps) {
|
|
45026
|
-
const set2 = byRef.get(d.ref) ?? /* @__PURE__ */ new Set();
|
|
45027
|
-
for (const t of d.dependsOn) set2.add(t);
|
|
45028
|
-
byRef.set(d.ref, set2);
|
|
45029
|
-
}
|
|
45030
|
-
return [...byRef.entries()].map(([ref, set2]) => ({ ref, dependsOn: [...set2] }));
|
|
45031
|
-
}
|
|
45096
|
+
init_src();
|
|
45032
45097
|
function runLockfilePass(opts) {
|
|
45033
45098
|
const ts = Date.now();
|
|
45034
45099
|
const languages = mintLanguageNodes(opts.store, opts.languages, opts.workspaceId, ts);
|
package/package.json
CHANGED
package/pass-worker.mjs
CHANGED
|
@@ -14965,9 +14965,90 @@ var init_zod = __esm({
|
|
|
14965
14965
|
});
|
|
14966
14966
|
|
|
14967
14967
|
// ../../packages/shared/src/edge-rules.ts
|
|
14968
|
+
function isValidEdge(from, type, to) {
|
|
14969
|
+
const rule = EDGE_RULES[type];
|
|
14970
|
+
if (!rule) return { ok: true };
|
|
14971
|
+
if (from && rule.from && !rule.from.includes(from)) {
|
|
14972
|
+
return {
|
|
14973
|
+
ok: false,
|
|
14974
|
+
reason: `${type} cannot originate from ${from} (allowed: ${rule.from.join(", ")})`
|
|
14975
|
+
};
|
|
14976
|
+
}
|
|
14977
|
+
if (to && rule.to && !rule.to.includes(to)) {
|
|
14978
|
+
return {
|
|
14979
|
+
ok: false,
|
|
14980
|
+
reason: `${type} cannot target ${to} (allowed: ${rule.to.join(", ")})`
|
|
14981
|
+
};
|
|
14982
|
+
}
|
|
14983
|
+
return { ok: true };
|
|
14984
|
+
}
|
|
14985
|
+
var EDGE_RULES;
|
|
14968
14986
|
var init_edge_rules = __esm({
|
|
14969
14987
|
"../../packages/shared/src/edge-rules.ts"() {
|
|
14970
14988
|
"use strict";
|
|
14989
|
+
EDGE_RULES = {
|
|
14990
|
+
// ── Project anchors (PJ-anchors · PLAN_PROJECT_STORE §6c) ──
|
|
14991
|
+
// ANCHORED_AT is the ONE bridge edge admitted to the cloud, and it exists for
|
|
14992
|
+
// exactly one purpose: point a piece of knowledge at an OPAQUE project `Symbol`
|
|
14993
|
+
// so two teammates' observations about the same function converge on one node.
|
|
14994
|
+
// Constrain it to that purpose — an unconstrained ANCHORED_AT (its state before
|
|
14995
|
+
// this rule, since a missing entry means "anything goes") would let a batch
|
|
14996
|
+
// anchor anything at anything, e.g. at a public `Package`, quietly re-creating
|
|
14997
|
+
// the code↔collective bridge the membrane exists to prevent.
|
|
14998
|
+
ANCHORED_AT: {
|
|
14999
|
+
from: ["Problem", "Solution", "RootCause", "Claim"],
|
|
15000
|
+
to: ["Symbol"]
|
|
15001
|
+
},
|
|
15002
|
+
// ── Security spine (v1 taxonomy.ts SECURITY_FORWARD_ONLY commentary) ──
|
|
15003
|
+
TARGETS: { from: ["Exploit"], to: ["Vulnerability"] },
|
|
15004
|
+
ENABLES: { from: ["AntiPattern"], to: ["Vulnerability"] },
|
|
15005
|
+
MITIGATES: { from: ["Solution"], to: ["Vulnerability"] },
|
|
15006
|
+
PRESENTS: { from: ["Solution"], to: ["Problem", "Vulnerability"] },
|
|
15007
|
+
// SOLVED_BY sources: Vulnerability (v1 comment) + Problem/RootCause
|
|
15008
|
+
// (upstream design-problem.ts + triage.ts mint both).
|
|
15009
|
+
SOLVED_BY: { from: ["Vulnerability", "Problem", "RootCause"], to: ["Solution"] },
|
|
15010
|
+
// FIXED_BY sources mirror SOLVED_BY's (same ailment→fix semantics; live-graph
|
|
15011
|
+
// census 2026-07-14: 512 Problem + 13 RootCause sources, zero others besides
|
|
15012
|
+
// 3 REVERSED Solution→Problem edges the missing `from` let through — the
|
|
15013
|
+
// "Problem solved by Problem" splash bug. A reversed resolution edge is NOT
|
|
15014
|
+
// mechanically flippable (half the sample were PoC-not-fix claims), so the
|
|
15015
|
+
// door rejects rather than repairs; a correct re-emission lands via
|
|
15016
|
+
// recognition-at-the-door.
|
|
15017
|
+
FIXED_BY: { from: ["Vulnerability", "Problem", "RootCause"], to: ["Solution"] },
|
|
15018
|
+
// ── Triage family (upstream castalia.ts: Problem —TRIAGED_BY→ Triage —{INDICATES|CONFIRMS}→ cause) ──
|
|
15019
|
+
// Two-edge differential model (T3-rename): INDICATES = provisional candidate,
|
|
15020
|
+
// CONFIRMS = promoted router (a discriminator exists). Same endpoint matrix.
|
|
15021
|
+
// ROUTES_TO is the deprecated pre-split alias, kept readable during migration.
|
|
15022
|
+
TRIAGED_BY: { from: ["Problem"], to: ["Triage"] },
|
|
15023
|
+
CONFIRMS: { from: ["Triage"], to: ["RootCause", "Problem"] },
|
|
15024
|
+
INDICATES: { from: ["Triage"], to: ["RootCause", "Problem"] },
|
|
15025
|
+
ROUTES_TO: { from: ["Triage"], to: ["RootCause", "Problem"] },
|
|
15026
|
+
// ── Context grounding (v1 taxonomy.ts comments) ──
|
|
15027
|
+
WRITTEN_IN: { from: ["Component", "Package"], to: ["Language"] },
|
|
15028
|
+
OCCURS_IN: { to: ["Language"] },
|
|
15029
|
+
OCCURS_ON: { to: ["OperatingSystem"] },
|
|
15030
|
+
RUNS_ON: { to: ["OperatingSystem"] },
|
|
15031
|
+
OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
|
|
15032
|
+
INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
|
|
15033
|
+
// ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
|
|
15034
|
+
IS_A: { from: ["Weakness"], to: ["Weakness"] },
|
|
15035
|
+
// ── Conceptual (v1 taxonomy.ts: instance → motif/pattern reference) ──
|
|
15036
|
+
INSTANCE_OF: { to: ["Pattern", "Weakness", "Technique"] },
|
|
15037
|
+
IMPLEMENTS: { from: ["Solution", "Language", "Component"], to: ["Pattern", "Technique"] },
|
|
15038
|
+
MATCHES: { to: ["Pattern"] },
|
|
15039
|
+
// ── Artifact evidence (v1 taxonomy.ts artifact rows) ──
|
|
15040
|
+
PRODUCED: { from: ["Report"], to: ["Artifact"] },
|
|
15041
|
+
MANIFESTED_IN: { from: ["Problem"], to: ["Artifact"] },
|
|
15042
|
+
REVEALED_BY: { from: ["RootCause"], to: ["Artifact"] },
|
|
15043
|
+
// ── Belief / abstraction gradients (upstream castalia.ts) ──
|
|
15044
|
+
CORROBORATES: { from: ["Claim"], to: ["Claim"] },
|
|
15045
|
+
GENERALIZES: { from: ["Claim"] },
|
|
15046
|
+
DERIVED_FROM: { from: ["Claim"] },
|
|
15047
|
+
// ── Decomposition (castalia.ts: a Problem fans out into independent root
|
|
15048
|
+
// problems; §5.1 also attaches SPLIT_INTO to Claim when one conflates two).
|
|
15049
|
+
// `to` left unconstrained — the replacement set's labels vary by case. ──
|
|
15050
|
+
SPLIT_INTO: { from: ["Problem", "Claim"] }
|
|
15051
|
+
};
|
|
14971
15052
|
}
|
|
14972
15053
|
});
|
|
14973
15054
|
|
|
@@ -15129,6 +15210,13 @@ var init_project_symbol = __esm({
|
|
|
15129
15210
|
}
|
|
15130
15211
|
});
|
|
15131
15212
|
|
|
15213
|
+
// ../../packages/shared/src/lockfile.ts
|
|
15214
|
+
var init_lockfile = __esm({
|
|
15215
|
+
"../../packages/shared/src/lockfile.ts"() {
|
|
15216
|
+
"use strict";
|
|
15217
|
+
}
|
|
15218
|
+
});
|
|
15219
|
+
|
|
15132
15220
|
// ../../packages/shared/src/nlp/canonicalize.ts
|
|
15133
15221
|
var init_canonicalize = __esm({
|
|
15134
15222
|
"../../packages/shared/src/nlp/canonicalize.ts"() {
|
|
@@ -15155,6 +15243,7 @@ var init_src = __esm({
|
|
|
15155
15243
|
init_symbol_intent();
|
|
15156
15244
|
init_repo_locator();
|
|
15157
15245
|
init_project_symbol();
|
|
15246
|
+
init_lockfile();
|
|
15158
15247
|
init_hash();
|
|
15159
15248
|
init_error_signature();
|
|
15160
15249
|
init_canonicalize();
|
|
@@ -23930,6 +24019,15 @@ import { parentPort, workerData } from "node:worker_threads";
|
|
|
23930
24019
|
|
|
23931
24020
|
// ../../packages/local-graph/src/store.ts
|
|
23932
24021
|
init_src2();
|
|
24022
|
+
init_src();
|
|
24023
|
+
var LOCAL_RULE_OVERRIDES = {
|
|
24024
|
+
ANCHORED_AT: {
|
|
24025
|
+
from: EDGE_RULES["ANCHORED_AT"]?.from,
|
|
24026
|
+
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
24027
|
+
},
|
|
24028
|
+
REVEALED_BY: null,
|
|
24029
|
+
PRODUCED: null
|
|
24030
|
+
};
|
|
23933
24031
|
var SCHEMA_VERSION = 5;
|
|
23934
24032
|
var SCHEMA_SQL = `
|
|
23935
24033
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -24079,6 +24177,10 @@ var SqliteGraphStore = class {
|
|
|
24079
24177
|
* every node/edge upsert — code-index and capture alike — so the consolidation
|
|
24080
24178
|
* trigger can read "how much changed" without caring what changed it. */
|
|
24081
24179
|
mutations = 0;
|
|
24180
|
+
/** Edges refused by the local ontology gate (EDGE_RULES) since open — a
|
|
24181
|
+
* visible counter so a misbehaving extractor shows up in stats instead of
|
|
24182
|
+
* silently thinning the graph. */
|
|
24183
|
+
rejectedEdgeCount = 0;
|
|
24082
24184
|
/** Persistent monotonic ingest-sequence high-water (KN-decay-momentum). Loaded
|
|
24083
24185
|
* from store_meta on open, advanced once per knowledge ingest event via
|
|
24084
24186
|
* nextIngestSeq(). Distinct from `mutations` (in-memory, per-session, bumped by
|
|
@@ -24388,6 +24490,12 @@ var SqliteGraphStore = class {
|
|
|
24388
24490
|
this.stmts.setCommunity.run({ id, v: community });
|
|
24389
24491
|
}
|
|
24390
24492
|
mergeEdge(edge) {
|
|
24493
|
+
const violation = this.edgeRuleViolation(edge);
|
|
24494
|
+
if (violation) {
|
|
24495
|
+
this.rejectedEdgeCount++;
|
|
24496
|
+
console.warn(`[local-graph] rejected edge ${edge.from}-[:${edge.type}]->${edge.to}: ${violation}`);
|
|
24497
|
+
return;
|
|
24498
|
+
}
|
|
24391
24499
|
this.mutations++;
|
|
24392
24500
|
this.stmts.insertEdge.run({
|
|
24393
24501
|
id: edge.id,
|
|
@@ -24405,6 +24513,27 @@ var SqliteGraphStore = class {
|
|
|
24405
24513
|
attrs_json: JSON.stringify(edge.attrs ?? {})
|
|
24406
24514
|
});
|
|
24407
24515
|
}
|
|
24516
|
+
/** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
|
|
24517
|
+
* overlay consulted first. Returns the reason string on a documented-
|
|
24518
|
+
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
24519
|
+
* next to the insert itself. */
|
|
24520
|
+
edgeRuleViolation(edge) {
|
|
24521
|
+
const fromLabel = this.getNode(edge.from)?.label;
|
|
24522
|
+
const toLabel = this.getNode(edge.to)?.label;
|
|
24523
|
+
if (edge.type in LOCAL_RULE_OVERRIDES) {
|
|
24524
|
+
const rule = LOCAL_RULE_OVERRIDES[edge.type];
|
|
24525
|
+
if (!rule) return null;
|
|
24526
|
+
if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
|
|
24527
|
+
return `${edge.type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
|
|
24528
|
+
}
|
|
24529
|
+
if (toLabel && rule.to && !rule.to.includes(toLabel)) {
|
|
24530
|
+
return `${edge.type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
24531
|
+
}
|
|
24532
|
+
return null;
|
|
24533
|
+
}
|
|
24534
|
+
const verdict = isValidEdge(fromLabel, edge.type, toLabel);
|
|
24535
|
+
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
24536
|
+
}
|
|
24408
24537
|
updateEdge(id, patch) {
|
|
24409
24538
|
this.stmts.updateEdge.run({
|
|
24410
24539
|
id,
|