@inerrata-corporation/errata 2.0.0-dev.78 → 2.0.0-dev.80
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 +121 -0
- package/errata.mjs +61 -3
- package/package.json +1 -1
- package/pass-worker.mjs +121 -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
|
|
|
@@ -15623,6 +15704,15 @@ import { parentPort } from "node:worker_threads";
|
|
|
15623
15704
|
|
|
15624
15705
|
// ../../packages/local-graph/src/store.ts
|
|
15625
15706
|
init_src2();
|
|
15707
|
+
init_src();
|
|
15708
|
+
var LOCAL_RULE_OVERRIDES = {
|
|
15709
|
+
ANCHORED_AT: {
|
|
15710
|
+
from: EDGE_RULES["ANCHORED_AT"]?.from,
|
|
15711
|
+
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
15712
|
+
},
|
|
15713
|
+
REVEALED_BY: null,
|
|
15714
|
+
PRODUCED: null
|
|
15715
|
+
};
|
|
15626
15716
|
var SCHEMA_VERSION = 5;
|
|
15627
15717
|
var SCHEMA_SQL = `
|
|
15628
15718
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -15772,6 +15862,10 @@ var SqliteGraphStore = class {
|
|
|
15772
15862
|
* every node/edge upsert — code-index and capture alike — so the consolidation
|
|
15773
15863
|
* trigger can read "how much changed" without caring what changed it. */
|
|
15774
15864
|
mutations = 0;
|
|
15865
|
+
/** Edges refused by the local ontology gate (EDGE_RULES) since open — a
|
|
15866
|
+
* visible counter so a misbehaving extractor shows up in stats instead of
|
|
15867
|
+
* silently thinning the graph. */
|
|
15868
|
+
rejectedEdgeCount = 0;
|
|
15775
15869
|
/** Persistent monotonic ingest-sequence high-water (KN-decay-momentum). Loaded
|
|
15776
15870
|
* from store_meta on open, advanced once per knowledge ingest event via
|
|
15777
15871
|
* nextIngestSeq(). Distinct from `mutations` (in-memory, per-session, bumped by
|
|
@@ -16081,6 +16175,12 @@ var SqliteGraphStore = class {
|
|
|
16081
16175
|
this.stmts.setCommunity.run({ id, v: community });
|
|
16082
16176
|
}
|
|
16083
16177
|
mergeEdge(edge) {
|
|
16178
|
+
const violation = this.edgeRuleViolation(edge);
|
|
16179
|
+
if (violation) {
|
|
16180
|
+
this.rejectedEdgeCount++;
|
|
16181
|
+
console.warn(`[local-graph] rejected edge ${edge.from}-[:${edge.type}]->${edge.to}: ${violation}`);
|
|
16182
|
+
return;
|
|
16183
|
+
}
|
|
16084
16184
|
this.mutations++;
|
|
16085
16185
|
this.stmts.insertEdge.run({
|
|
16086
16186
|
id: edge.id,
|
|
@@ -16098,6 +16198,27 @@ var SqliteGraphStore = class {
|
|
|
16098
16198
|
attrs_json: JSON.stringify(edge.attrs ?? {})
|
|
16099
16199
|
});
|
|
16100
16200
|
}
|
|
16201
|
+
/** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
|
|
16202
|
+
* overlay consulted first. Returns the reason string on a documented-
|
|
16203
|
+
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
16204
|
+
* next to the insert itself. */
|
|
16205
|
+
edgeRuleViolation(edge) {
|
|
16206
|
+
const fromLabel = this.getNode(edge.from)?.label;
|
|
16207
|
+
const toLabel = this.getNode(edge.to)?.label;
|
|
16208
|
+
if (edge.type in LOCAL_RULE_OVERRIDES) {
|
|
16209
|
+
const rule = LOCAL_RULE_OVERRIDES[edge.type];
|
|
16210
|
+
if (!rule) return null;
|
|
16211
|
+
if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
|
|
16212
|
+
return `${edge.type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
|
|
16213
|
+
}
|
|
16214
|
+
if (toLabel && rule.to && !rule.to.includes(toLabel)) {
|
|
16215
|
+
return `${edge.type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
16216
|
+
}
|
|
16217
|
+
return null;
|
|
16218
|
+
}
|
|
16219
|
+
const verdict = isValidEdge(fromLabel, edge.type, toLabel);
|
|
16220
|
+
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
16221
|
+
}
|
|
16101
16222
|
updateEdge(id, patch) {
|
|
16102
16223
|
this.stmts.updateEdge.run({
|
|
16103
16224
|
id,
|
package/errata.mjs
CHANGED
|
@@ -15290,7 +15290,14 @@ var init_edge_rules = __esm({
|
|
|
15290
15290
|
// SOLVED_BY sources: Vulnerability (v1 comment) + Problem/RootCause
|
|
15291
15291
|
// (upstream design-problem.ts + triage.ts mint both).
|
|
15292
15292
|
SOLVED_BY: { from: ["Vulnerability", "Problem", "RootCause"], to: ["Solution"] },
|
|
15293
|
-
FIXED_BY
|
|
15293
|
+
// FIXED_BY sources mirror SOLVED_BY's (same ailment→fix semantics; live-graph
|
|
15294
|
+
// census 2026-07-14: 512 Problem + 13 RootCause sources, zero others besides
|
|
15295
|
+
// 3 REVERSED Solution→Problem edges the missing `from` let through — the
|
|
15296
|
+
// "Problem solved by Problem" splash bug. A reversed resolution edge is NOT
|
|
15297
|
+
// mechanically flippable (half the sample were PoC-not-fix claims), so the
|
|
15298
|
+
// door rejects rather than repairs; a correct re-emission lands via
|
|
15299
|
+
// recognition-at-the-door.
|
|
15300
|
+
FIXED_BY: { from: ["Vulnerability", "Problem", "RootCause"], to: ["Solution"] },
|
|
15294
15301
|
// ── Triage family (upstream castalia.ts: Problem —TRIAGED_BY→ Triage —{INDICATES|CONFIRMS}→ cause) ──
|
|
15295
15302
|
// Two-edge differential model (T3-rename): INDICATES = provisional candidate,
|
|
15296
15303
|
// CONFIRMS = promoted router (a discriminator exists). Same endpoint matrix.
|
|
@@ -16181,11 +16188,20 @@ function rowToEdge(r) {
|
|
|
16181
16188
|
function openGraphStore(opts) {
|
|
16182
16189
|
return new SqliteGraphStore(opts);
|
|
16183
16190
|
}
|
|
16184
|
-
var SCHEMA_VERSION, SCHEMA_SQL, TEMPORAL_INDEX_SQL, SqliteGraphStore;
|
|
16191
|
+
var LOCAL_RULE_OVERRIDES, SCHEMA_VERSION, SCHEMA_SQL, TEMPORAL_INDEX_SQL, SqliteGraphStore;
|
|
16185
16192
|
var init_store = __esm({
|
|
16186
16193
|
"../../packages/local-graph/src/store.ts"() {
|
|
16187
16194
|
"use strict";
|
|
16188
16195
|
init_src2();
|
|
16196
|
+
init_src();
|
|
16197
|
+
LOCAL_RULE_OVERRIDES = {
|
|
16198
|
+
ANCHORED_AT: {
|
|
16199
|
+
from: EDGE_RULES["ANCHORED_AT"]?.from,
|
|
16200
|
+
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
16201
|
+
},
|
|
16202
|
+
REVEALED_BY: null,
|
|
16203
|
+
PRODUCED: null
|
|
16204
|
+
};
|
|
16189
16205
|
SCHEMA_VERSION = 5;
|
|
16190
16206
|
SCHEMA_SQL = `
|
|
16191
16207
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -16278,6 +16294,10 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
|
|
|
16278
16294
|
* every node/edge upsert — code-index and capture alike — so the consolidation
|
|
16279
16295
|
* trigger can read "how much changed" without caring what changed it. */
|
|
16280
16296
|
mutations = 0;
|
|
16297
|
+
/** Edges refused by the local ontology gate (EDGE_RULES) since open — a
|
|
16298
|
+
* visible counter so a misbehaving extractor shows up in stats instead of
|
|
16299
|
+
* silently thinning the graph. */
|
|
16300
|
+
rejectedEdgeCount = 0;
|
|
16281
16301
|
/** Persistent monotonic ingest-sequence high-water (KN-decay-momentum). Loaded
|
|
16282
16302
|
* from store_meta on open, advanced once per knowledge ingest event via
|
|
16283
16303
|
* nextIngestSeq(). Distinct from `mutations` (in-memory, per-session, bumped by
|
|
@@ -16587,6 +16607,12 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
|
|
|
16587
16607
|
this.stmts.setCommunity.run({ id, v: community });
|
|
16588
16608
|
}
|
|
16589
16609
|
mergeEdge(edge2) {
|
|
16610
|
+
const violation = this.edgeRuleViolation(edge2);
|
|
16611
|
+
if (violation) {
|
|
16612
|
+
this.rejectedEdgeCount++;
|
|
16613
|
+
console.warn(`[local-graph] rejected edge ${edge2.from}-[:${edge2.type}]->${edge2.to}: ${violation}`);
|
|
16614
|
+
return;
|
|
16615
|
+
}
|
|
16590
16616
|
this.mutations++;
|
|
16591
16617
|
this.stmts.insertEdge.run({
|
|
16592
16618
|
id: edge2.id,
|
|
@@ -16604,6 +16630,27 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
|
|
|
16604
16630
|
attrs_json: JSON.stringify(edge2.attrs ?? {})
|
|
16605
16631
|
});
|
|
16606
16632
|
}
|
|
16633
|
+
/** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
|
|
16634
|
+
* overlay consulted first. Returns the reason string on a documented-
|
|
16635
|
+
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
16636
|
+
* next to the insert itself. */
|
|
16637
|
+
edgeRuleViolation(edge2) {
|
|
16638
|
+
const fromLabel = this.getNode(edge2.from)?.label;
|
|
16639
|
+
const toLabel = this.getNode(edge2.to)?.label;
|
|
16640
|
+
if (edge2.type in LOCAL_RULE_OVERRIDES) {
|
|
16641
|
+
const rule = LOCAL_RULE_OVERRIDES[edge2.type];
|
|
16642
|
+
if (!rule) return null;
|
|
16643
|
+
if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
|
|
16644
|
+
return `${edge2.type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
|
|
16645
|
+
}
|
|
16646
|
+
if (toLabel && rule.to && !rule.to.includes(toLabel)) {
|
|
16647
|
+
return `${edge2.type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
16648
|
+
}
|
|
16649
|
+
return null;
|
|
16650
|
+
}
|
|
16651
|
+
const verdict = isValidEdge(fromLabel, edge2.type, toLabel);
|
|
16652
|
+
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
16653
|
+
}
|
|
16607
16654
|
updateEdge(id, patch) {
|
|
16608
16655
|
this.stmts.updateEdge.run({
|
|
16609
16656
|
id,
|
|
@@ -37668,6 +37715,17 @@ async function flushOutbox(paths, client, store) {
|
|
|
37668
37715
|
if (res.accepted > 0) uploaded++;
|
|
37669
37716
|
if (res.rejected > 0) {
|
|
37670
37717
|
failed++;
|
|
37718
|
+
const rejections = [
|
|
37719
|
+
...(res.result?.nodes ?? []).filter((n) => n.decision === "rejected"),
|
|
37720
|
+
...(res.result?.edges ?? []).filter((e) => e.decision === "rejected")
|
|
37721
|
+
];
|
|
37722
|
+
for (const r of rejections.slice(0, 5)) {
|
|
37723
|
+
const what = "type" in r ? `${r.from}-[:${r.type}]->${r.to}` : r.canonicalId ?? "node";
|
|
37724
|
+
console.warn(`[errata] outbox: cloud rejected ${what}: ${r.reason ?? "no reason given"}`);
|
|
37725
|
+
}
|
|
37726
|
+
if (rejections.length > 5) {
|
|
37727
|
+
console.warn(`[errata] outbox: \u2026and ${rejections.length - 5} more rejections in this batch`);
|
|
37728
|
+
}
|
|
37671
37729
|
quarantine(abs, `semantic rejection (${res.rejected} item${res.rejected === 1 ? "" : "s"})`);
|
|
37672
37730
|
continue;
|
|
37673
37731
|
}
|
|
@@ -43321,7 +43379,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
43321
43379
|
}
|
|
43322
43380
|
|
|
43323
43381
|
// src/engine.ts
|
|
43324
|
-
var DAEMON_VERSION = true ? "2.0.0-dev.
|
|
43382
|
+
var DAEMON_VERSION = true ? "2.0.0-dev.80" : "2.0.0-alpha.0";
|
|
43325
43383
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
43326
43384
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
43327
43385
|
var GIT_OP_MUTE_MS = 4e3;
|
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
|
|
|
@@ -23930,6 +24011,15 @@ import { parentPort, workerData } from "node:worker_threads";
|
|
|
23930
24011
|
|
|
23931
24012
|
// ../../packages/local-graph/src/store.ts
|
|
23932
24013
|
init_src2();
|
|
24014
|
+
init_src();
|
|
24015
|
+
var LOCAL_RULE_OVERRIDES = {
|
|
24016
|
+
ANCHORED_AT: {
|
|
24017
|
+
from: EDGE_RULES["ANCHORED_AT"]?.from,
|
|
24018
|
+
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
24019
|
+
},
|
|
24020
|
+
REVEALED_BY: null,
|
|
24021
|
+
PRODUCED: null
|
|
24022
|
+
};
|
|
23933
24023
|
var SCHEMA_VERSION = 5;
|
|
23934
24024
|
var SCHEMA_SQL = `
|
|
23935
24025
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -24079,6 +24169,10 @@ var SqliteGraphStore = class {
|
|
|
24079
24169
|
* every node/edge upsert — code-index and capture alike — so the consolidation
|
|
24080
24170
|
* trigger can read "how much changed" without caring what changed it. */
|
|
24081
24171
|
mutations = 0;
|
|
24172
|
+
/** Edges refused by the local ontology gate (EDGE_RULES) since open — a
|
|
24173
|
+
* visible counter so a misbehaving extractor shows up in stats instead of
|
|
24174
|
+
* silently thinning the graph. */
|
|
24175
|
+
rejectedEdgeCount = 0;
|
|
24082
24176
|
/** Persistent monotonic ingest-sequence high-water (KN-decay-momentum). Loaded
|
|
24083
24177
|
* from store_meta on open, advanced once per knowledge ingest event via
|
|
24084
24178
|
* nextIngestSeq(). Distinct from `mutations` (in-memory, per-session, bumped by
|
|
@@ -24388,6 +24482,12 @@ var SqliteGraphStore = class {
|
|
|
24388
24482
|
this.stmts.setCommunity.run({ id, v: community });
|
|
24389
24483
|
}
|
|
24390
24484
|
mergeEdge(edge) {
|
|
24485
|
+
const violation = this.edgeRuleViolation(edge);
|
|
24486
|
+
if (violation) {
|
|
24487
|
+
this.rejectedEdgeCount++;
|
|
24488
|
+
console.warn(`[local-graph] rejected edge ${edge.from}-[:${edge.type}]->${edge.to}: ${violation}`);
|
|
24489
|
+
return;
|
|
24490
|
+
}
|
|
24391
24491
|
this.mutations++;
|
|
24392
24492
|
this.stmts.insertEdge.run({
|
|
24393
24493
|
id: edge.id,
|
|
@@ -24405,6 +24505,27 @@ var SqliteGraphStore = class {
|
|
|
24405
24505
|
attrs_json: JSON.stringify(edge.attrs ?? {})
|
|
24406
24506
|
});
|
|
24407
24507
|
}
|
|
24508
|
+
/** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
|
|
24509
|
+
* overlay consulted first. Returns the reason string on a documented-
|
|
24510
|
+
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
24511
|
+
* next to the insert itself. */
|
|
24512
|
+
edgeRuleViolation(edge) {
|
|
24513
|
+
const fromLabel = this.getNode(edge.from)?.label;
|
|
24514
|
+
const toLabel = this.getNode(edge.to)?.label;
|
|
24515
|
+
if (edge.type in LOCAL_RULE_OVERRIDES) {
|
|
24516
|
+
const rule = LOCAL_RULE_OVERRIDES[edge.type];
|
|
24517
|
+
if (!rule) return null;
|
|
24518
|
+
if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
|
|
24519
|
+
return `${edge.type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
|
|
24520
|
+
}
|
|
24521
|
+
if (toLabel && rule.to && !rule.to.includes(toLabel)) {
|
|
24522
|
+
return `${edge.type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
24523
|
+
}
|
|
24524
|
+
return null;
|
|
24525
|
+
}
|
|
24526
|
+
const verdict = isValidEdge(fromLabel, edge.type, toLabel);
|
|
24527
|
+
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
24528
|
+
}
|
|
24408
24529
|
updateEdge(id, patch) {
|
|
24409
24530
|
this.stmts.updateEdge.run({
|
|
24410
24531
|
id,
|