@inerrata-corporation/errata 2.0.22 → 2.0.23-dev.4682
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/THIRD-PARTY-NOTICES.md +1080 -0
- package/consolidate-worker.mjs +1868 -159
- package/errata.mjs +6935 -2403
- package/generalize-worker.mjs +1698 -97
- package/package.json +3 -2
- package/pass-worker.mjs +2307 -361
- package/resources/context-plugin/.codex-plugin/plugin.json +1 -1
- package/resources/context-plugin/SHA256SUMS +3 -3
- package/resources/context-plugin/THIRD-PARTY-NOTICES.md +5 -7
- package/resources/context-plugin/dist/mcp-server.mjs +545 -148
package/consolidate-worker.mjs
CHANGED
|
@@ -792,9 +792,13 @@ function predicateIsTheEntity(text, entityId, entityCount) {
|
|
|
792
792
|
}
|
|
793
793
|
function canonicalClaimId(text, scope) {
|
|
794
794
|
const s = claimStructure(text, scope);
|
|
795
|
-
return digest(
|
|
796
|
-
|
|
797
|
-
|
|
795
|
+
return s.raw !== void 0 ? conceptDigest(s.raw, s.scope, s.stance) : digest({ entities: s.entities, predicate: s.predicate, stance: s.stance, scope: s.scope });
|
|
796
|
+
}
|
|
797
|
+
function conceptClaimId(text, scope) {
|
|
798
|
+
return conceptDigest(conceptBag(text), scope ?? {}, detectStance(text));
|
|
799
|
+
}
|
|
800
|
+
function conceptDigest(raw, scope, stance) {
|
|
801
|
+
return digest({ raw, scope, stance });
|
|
798
802
|
}
|
|
799
803
|
var init_claim_id = __esm({
|
|
800
804
|
"../../packages/shared/src/nlp/claim-id.ts"() {
|
|
@@ -822,14 +826,14 @@ function identityId(input) {
|
|
|
822
826
|
statement: canonicalClaimId(input.statement.trim())
|
|
823
827
|
});
|
|
824
828
|
case "DesignProblem":
|
|
825
|
-
return `${DESIGN_PROBLEM_PREFIX}${
|
|
829
|
+
return `${DESIGN_PROBLEM_PREFIX}${conceptClaimId(input.statement.trim())}`.slice(
|
|
826
830
|
0,
|
|
827
831
|
DESIGN_PROBLEM_ID_LENGTH
|
|
828
832
|
);
|
|
829
833
|
case "DiagnosticProblem":
|
|
830
834
|
return `${DIAGNOSTIC_PROBLEM_PREFIX}${digest({ key: input.key }).slice(0, DIAGNOSTIC_HASH_LENGTH)}`;
|
|
831
835
|
case "Triage":
|
|
832
|
-
return `${TRIAGE_PREFIX}${
|
|
836
|
+
return `${TRIAGE_PREFIX}${conceptClaimId(input.statement.trim())}`.slice(
|
|
833
837
|
0,
|
|
834
838
|
TRIAGE_ID_LENGTH
|
|
835
839
|
);
|
|
@@ -1114,10 +1118,10 @@ function mergeDefs(...defs) {
|
|
|
1114
1118
|
function cloneDef(schema) {
|
|
1115
1119
|
return mergeDefs(schema._zod.def);
|
|
1116
1120
|
}
|
|
1117
|
-
function getElementAtPath(obj,
|
|
1118
|
-
if (!
|
|
1121
|
+
function getElementAtPath(obj, path2) {
|
|
1122
|
+
if (!path2)
|
|
1119
1123
|
return obj;
|
|
1120
|
-
return
|
|
1124
|
+
return path2.reduce((acc, key) => acc?.[key], obj);
|
|
1121
1125
|
}
|
|
1122
1126
|
function promiseAllObject(promisesObj) {
|
|
1123
1127
|
const keys = Object.keys(promisesObj);
|
|
@@ -1429,11 +1433,11 @@ function aborted(x, startIndex = 0) {
|
|
|
1429
1433
|
}
|
|
1430
1434
|
return false;
|
|
1431
1435
|
}
|
|
1432
|
-
function prefixIssues(
|
|
1436
|
+
function prefixIssues(path2, issues) {
|
|
1433
1437
|
return issues.map((iss) => {
|
|
1434
1438
|
var _a2;
|
|
1435
1439
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
1436
|
-
iss.path.unshift(
|
|
1440
|
+
iss.path.unshift(path2);
|
|
1437
1441
|
return iss;
|
|
1438
1442
|
});
|
|
1439
1443
|
}
|
|
@@ -1675,7 +1679,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
|
|
|
1675
1679
|
}
|
|
1676
1680
|
function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
1677
1681
|
const result = { errors: [] };
|
|
1678
|
-
const processError = (error49,
|
|
1682
|
+
const processError = (error49, path2 = []) => {
|
|
1679
1683
|
var _a2, _b;
|
|
1680
1684
|
for (const issue2 of error49.issues) {
|
|
1681
1685
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -1685,7 +1689,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
1685
1689
|
} else if (issue2.code === "invalid_element") {
|
|
1686
1690
|
processError({ issues: issue2.issues }, issue2.path);
|
|
1687
1691
|
} else {
|
|
1688
|
-
const fullpath = [...
|
|
1692
|
+
const fullpath = [...path2, ...issue2.path];
|
|
1689
1693
|
if (fullpath.length === 0) {
|
|
1690
1694
|
result.errors.push(mapper(issue2));
|
|
1691
1695
|
continue;
|
|
@@ -1717,8 +1721,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
1717
1721
|
}
|
|
1718
1722
|
function toDotPath(_path) {
|
|
1719
1723
|
const segs = [];
|
|
1720
|
-
const
|
|
1721
|
-
for (const seg of
|
|
1724
|
+
const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
1725
|
+
for (const seg of path2) {
|
|
1722
1726
|
if (typeof seg === "number")
|
|
1723
1727
|
segs.push(`[${seg}]`);
|
|
1724
1728
|
else if (typeof seg === "symbol")
|
|
@@ -14412,13 +14416,13 @@ function resolveRef(ref, ctx) {
|
|
|
14412
14416
|
if (!ref.startsWith("#")) {
|
|
14413
14417
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
14414
14418
|
}
|
|
14415
|
-
const
|
|
14416
|
-
if (
|
|
14419
|
+
const path2 = ref.slice(1).split("/").filter(Boolean);
|
|
14420
|
+
if (path2.length === 0) {
|
|
14417
14421
|
return ctx.rootSchema;
|
|
14418
14422
|
}
|
|
14419
14423
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
14420
|
-
if (
|
|
14421
|
-
const key =
|
|
14424
|
+
if (path2[0] === defsKey) {
|
|
14425
|
+
const key = path2[1];
|
|
14422
14426
|
if (!key || !ctx.defs[key]) {
|
|
14423
14427
|
throw new Error(`Reference not found: ${ref}`);
|
|
14424
14428
|
}
|
|
@@ -15737,8 +15741,8 @@ function normalizeSymbolPath(relPath) {
|
|
|
15737
15741
|
return relPath.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\.\//, "").replace(/^\//, "");
|
|
15738
15742
|
}
|
|
15739
15743
|
function projectSymbolId(projectSalt, projectId, relPath, qname, kind) {
|
|
15740
|
-
const
|
|
15741
|
-
return `sym_${createHmac("sha256", projectSalt).update(`${projectId}:${
|
|
15744
|
+
const path2 = normalizeSymbolPath(relPath);
|
|
15745
|
+
return `sym_${createHmac("sha256", projectSalt).update(`${projectId}:${path2}:${qname}:${kind}`).digest("hex").slice(0, 24)}`;
|
|
15742
15746
|
}
|
|
15743
15747
|
var init_project_symbol = __esm({
|
|
15744
15748
|
"../../packages/shared/src/project-symbol.ts"() {
|
|
@@ -15774,7 +15778,7 @@ var init_history_read = __esm({
|
|
|
15774
15778
|
"../../packages/shared/src/history-read.ts"() {
|
|
15775
15779
|
"use strict";
|
|
15776
15780
|
init_history_dependencies();
|
|
15777
|
-
CLOUD_ATTR_DUPLICATE_KEYS = ["attrsJson", "embedding"];
|
|
15781
|
+
CLOUD_ATTR_DUPLICATE_KEYS = ["attrsJson", "embedding", "structEmbedding", "structEmbeddedAt"];
|
|
15778
15782
|
}
|
|
15779
15783
|
});
|
|
15780
15784
|
|
|
@@ -15786,7 +15790,7 @@ var init_history_temporal = __esm({
|
|
|
15786
15790
|
});
|
|
15787
15791
|
|
|
15788
15792
|
// ../../packages/shared/src/history-revision.ts
|
|
15789
|
-
var Sha, HistoryRevisionRequestSchema;
|
|
15793
|
+
var Sha, HistoryRevisionRequestSchema, HistoryCarryRequestSchema, HISTORY_REVIEW_BUDGET, HISTORY_REVIEW_POLICY;
|
|
15790
15794
|
var init_history_revision = __esm({
|
|
15791
15795
|
"../../packages/shared/src/history-revision.ts"() {
|
|
15792
15796
|
"use strict";
|
|
@@ -15806,6 +15810,17 @@ var init_history_revision = __esm({
|
|
|
15806
15810
|
dependencyContext: HistoryDependencyContextSchema.optional()
|
|
15807
15811
|
})).max(50)
|
|
15808
15812
|
});
|
|
15813
|
+
HistoryCarryRequestSchema = external_exports.strictObject({
|
|
15814
|
+
fromCommit: Sha,
|
|
15815
|
+
atCommit: Sha,
|
|
15816
|
+
parents: external_exports.array(external_exports.strictObject({ commit: Sha, parents: external_exports.array(Sha).max(32) })).max(8192),
|
|
15817
|
+
/** Every symbol in a file the range changed, both sides; `null` = too many to name, treat all as changed. */
|
|
15818
|
+
changedSymbols: external_exports.array(external_exports.string().regex(/^sym_[a-f0-9]{8,64}$/)).max(5e3).nullable(),
|
|
15819
|
+
/** Manifests the range changed; `null` = a lockfile (or too many manifests) changed, treat all as changed. */
|
|
15820
|
+
changedManifests: external_exports.array(external_exports.string().regex(/^hmanifest_[a-f0-9]{32}$/)).max(512).nullable()
|
|
15821
|
+
});
|
|
15822
|
+
HISTORY_REVIEW_BUDGET = 48e3;
|
|
15823
|
+
HISTORY_REVIEW_POLICY = `reviewer-view-${HISTORY_REVIEW_BUDGET}`;
|
|
15809
15824
|
}
|
|
15810
15825
|
});
|
|
15811
15826
|
|
|
@@ -15995,13 +16010,13 @@ function loadNodeSqlite() {
|
|
|
15995
16010
|
process.emitWarning = original;
|
|
15996
16011
|
}
|
|
15997
16012
|
}
|
|
15998
|
-
function openDatabase(
|
|
15999
|
-
const db = opts ? new DatabaseSync(
|
|
16013
|
+
function openDatabase(path2, opts) {
|
|
16014
|
+
const db = opts ? new DatabaseSync(path2, opts) : new DatabaseSync(path2);
|
|
16000
16015
|
try {
|
|
16001
16016
|
db.exec("PRAGMA foreign_keys = ON");
|
|
16002
16017
|
} catch {
|
|
16003
16018
|
}
|
|
16004
|
-
if (
|
|
16019
|
+
if (path2 !== ":memory:" && !opts?.readOnly) {
|
|
16005
16020
|
try {
|
|
16006
16021
|
db.exec("PRAGMA journal_mode = WAL");
|
|
16007
16022
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
@@ -16010,20 +16025,20 @@ function openDatabase(path, opts) {
|
|
|
16010
16025
|
} catch {
|
|
16011
16026
|
}
|
|
16012
16027
|
}
|
|
16013
|
-
const wrapStmt = (
|
|
16028
|
+
const wrapStmt = (stmt2) => ({
|
|
16014
16029
|
run: (...params) => {
|
|
16015
|
-
const r =
|
|
16030
|
+
const r = stmt2.run(...coerceParams(params));
|
|
16016
16031
|
return {
|
|
16017
16032
|
changes: Number(r.changes),
|
|
16018
16033
|
lastInsertRowid: Number(r.lastInsertRowid)
|
|
16019
16034
|
};
|
|
16020
16035
|
},
|
|
16021
16036
|
get: (...params) => {
|
|
16022
|
-
const r =
|
|
16037
|
+
const r = stmt2.get(...coerceParams(params));
|
|
16023
16038
|
return r;
|
|
16024
16039
|
},
|
|
16025
16040
|
all: (...params) => {
|
|
16026
|
-
const r =
|
|
16041
|
+
const r = stmt2.all(...coerceParams(params));
|
|
16027
16042
|
return r;
|
|
16028
16043
|
}
|
|
16029
16044
|
});
|
|
@@ -16084,6 +16099,548 @@ var init_sqlite_adapter = __esm({
|
|
|
16084
16099
|
}
|
|
16085
16100
|
});
|
|
16086
16101
|
|
|
16102
|
+
// ../../packages/local-shared/src/tag-grammar.ts
|
|
16103
|
+
function tagGrammarCopy() {
|
|
16104
|
+
const copy = [];
|
|
16105
|
+
for (const ref of ["its-handle", "its-id", "h", "id"]) {
|
|
16106
|
+
for (const build of Object.values(TAG_EXAMPLES)) copy.push(build(ref));
|
|
16107
|
+
}
|
|
16108
|
+
for (const v of TAG_VERBS) {
|
|
16109
|
+
copy.push(v.hint);
|
|
16110
|
+
if (v.taughtBy) copy.push(v.taughtBy);
|
|
16111
|
+
for (const q of v.qualifiers ?? []) copy.push(q.note);
|
|
16112
|
+
for (const l of v.lowering) {
|
|
16113
|
+
if (l.note) copy.push(l.note);
|
|
16114
|
+
if (l.knownViolation) copy.push(l.knownViolation.finding);
|
|
16115
|
+
}
|
|
16116
|
+
}
|
|
16117
|
+
for (const f of TAG_FENCES) copy.push(f.hint);
|
|
16118
|
+
return copy;
|
|
16119
|
+
}
|
|
16120
|
+
function normalizeTemplateBody(body) {
|
|
16121
|
+
return body.replace(/…|\.{3}/g, " ").replace(/\s+/g, " ").trim().replace(/^[\s,;:—–-]+|[\s,;:.—–-]+$/g, "").toLowerCase();
|
|
16122
|
+
}
|
|
16123
|
+
function deriveTagTemplates(copy) {
|
|
16124
|
+
const bodies = /* @__PURE__ */ new Set();
|
|
16125
|
+
const handles = new Set([...TAG_PLACEHOLDER_HANDLES].map((h) => h.toLowerCase()));
|
|
16126
|
+
for (const text of copy) {
|
|
16127
|
+
for (const m of text.matchAll(BRACKET_TOKEN)) {
|
|
16128
|
+
const token = m[1].trim().toLowerCase();
|
|
16129
|
+
if (!/^[!?]/.test(token) && !REAL_HANDLE_SHAPE.test(token)) handles.add(token);
|
|
16130
|
+
}
|
|
16131
|
+
}
|
|
16132
|
+
for (const text of copy) {
|
|
16133
|
+
for (const m of text.matchAll(TEMPLATE_VERB_GROUP)) {
|
|
16134
|
+
const body = normalizeTemplateBody(m[2]);
|
|
16135
|
+
bodies.add(body);
|
|
16136
|
+
const unthreaded = normalizeTemplateBody(body.replace(/^#[a-z][\w-]*/i, ""));
|
|
16137
|
+
bodies.add(unthreaded);
|
|
16138
|
+
const rest = normalizeTemplateBody(unthreaded.replace(BRACKET_TOKEN, (t, h) => handles.has(h.trim().toLowerCase()) ? " " : t).replace(/,/g, " "));
|
|
16139
|
+
bodies.add(rest);
|
|
16140
|
+
}
|
|
16141
|
+
}
|
|
16142
|
+
bodies.delete("");
|
|
16143
|
+
return { bodies, handles };
|
|
16144
|
+
}
|
|
16145
|
+
var TAG_CANONICAL_ID_PREFIXES, TAG_HANDLE_SOURCE, TAG_THREAD_SOURCE, TAG_PLACEHOLDER_HANDLES, TAG_EXAMPLES, P, S, RC, CITE_LOWERING, INSTANCE_LOWERING, TRIAGE_TO_RC, TAG_VERBS, TAG_FENCES, TAG_VERB_BY_NAME, TAG_PAREN_VERBS, TAG_GRAMMAR_OPENERS, TAG_QUOTED_PAREN_SOURCE, TAG_QUOTED_RE, TAG_QUOTED_PAREN_RE, TEMPLATE_VERB_GROUP, BRACKET_TOKEN, REAL_HANDLE_SHAPE, TAG_TEMPLATES, VERB_WORDS_SOURCE, JOINED_VERB, LEADING_VERB, ANY_VERB_WORD, QUOTED_TAG_OPENER;
|
|
16146
|
+
var init_tag_grammar = __esm({
|
|
16147
|
+
"../../packages/local-shared/src/tag-grammar.ts"() {
|
|
16148
|
+
"use strict";
|
|
16149
|
+
TAG_CANONICAL_ID_PREFIXES = ["pat", "sol", "drc", "dcause", "dfix", "dprob", "dvuln", "prob", "claim", "err"];
|
|
16150
|
+
TAG_HANDLE_SOURCE = String.raw`\[([a-z0-9][\w-]{0,80})\]|((?:${TAG_CANONICAL_ID_PREFIXES.join("|")})_[0-9a-f]{6,})`;
|
|
16151
|
+
TAG_THREAD_SOURCE = String.raw`#([a-z][\w-]{0,63})`;
|
|
16152
|
+
TAG_PLACEHOLDER_HANDLES = /* @__PURE__ */ new Set([
|
|
16153
|
+
"handle",
|
|
16154
|
+
"its-handle",
|
|
16155
|
+
"another",
|
|
16156
|
+
"a-third",
|
|
16157
|
+
"h",
|
|
16158
|
+
"h1",
|
|
16159
|
+
"h2",
|
|
16160
|
+
"id",
|
|
16161
|
+
"its-id",
|
|
16162
|
+
"a",
|
|
16163
|
+
"b",
|
|
16164
|
+
"\u2026",
|
|
16165
|
+
"..."
|
|
16166
|
+
]);
|
|
16167
|
+
TAG_EXAMPLES = {
|
|
16168
|
+
prior: (ref) => `([${ref}])`,
|
|
16169
|
+
problem: () => `[!one line on what's wrong]`,
|
|
16170
|
+
vulnerability: () => `[!vuln: who can do what; observed basis @ path:line]`,
|
|
16171
|
+
suspectedVulnerability: () => `[?vuln: consequence; missing check]`,
|
|
16172
|
+
vulnerabilityThread: () => `[!#v vuln: finding here]`,
|
|
16173
|
+
fix: (ref) => `(fix:[${ref}])`,
|
|
16174
|
+
// CONSTRAINT — the native capture path for a DIFFUSE problem (a constraint/tension/
|
|
16175
|
+
// conflict that shaped a decision). Free text, attaches to the decision sentence the
|
|
16176
|
+
// agent is already writing. Full rationale in the constraint note below + prior-tags.ts.
|
|
16177
|
+
constraint: () => `(constraint: the limitation that forced it)`,
|
|
16178
|
+
// TRIAGE — routes the problem/symptom in scope to its cause. `(cause:[handle])`
|
|
16179
|
+
// links to a cited RootCause/Solution node; `(cause: free text)` mints a RootCause
|
|
16180
|
+
// from the text. Harvested in prior-tags.ts → engine.ts (see the triage note below).
|
|
16181
|
+
triage: (ref) => `(cause:[${ref}])`,
|
|
16182
|
+
// Unlike a bare cause citation, this handle names the Problem being explained.
|
|
16183
|
+
triageExplain: (ref) => `(cause:[${ref}] the mechanism)`,
|
|
16184
|
+
// The MINT form — free-text cause that mints a RootCause. Prime-time surfacing
|
|
16185
|
+
// (recallForFile) pastes this next to an open Problem that has no cause yet, so the
|
|
16186
|
+
// diagnostic moment is cued when the agent is looking at the problem. Kept here so
|
|
16187
|
+
// the grammar can't drift from parseInlineTags' `(cause: …)` acceptance.
|
|
16188
|
+
triageMint: () => `(cause: why it happens)`,
|
|
16189
|
+
// THREAD forms — the witness-declared symptom↔cause/fix binding. A `#id` on the
|
|
16190
|
+
// flag names the diagnosis thread; the same `#id` on a later verb binds to it
|
|
16191
|
+
// explicitly (across sentences AND turns), replacing proximity guessing. Adjacent
|
|
16192
|
+
// tags never need one — the id is for diagnoses that unfold over distance.
|
|
16193
|
+
problemThread: () => `[!#slug one line on what's wrong]`,
|
|
16194
|
+
causeThread: () => `(cause:#slug the mechanism)`,
|
|
16195
|
+
fixThread: (thread = "slug") => `(fix:#${thread} what you changed)`,
|
|
16196
|
+
// ATTEMPT arc — what's being tried and what didn't work. A failed attempt is a
|
|
16197
|
+
// witnessed NEGATIVE result: it saves the next agent the same dead end.
|
|
16198
|
+
tried: () => `(tried: the approach you're attempting)`,
|
|
16199
|
+
failed: () => `(failed: what didn't work and the dead end it rules out)`,
|
|
16200
|
+
// `(failed:[h] …)` on a shown SOLUTION — the refute-by-failure form the context render teaches.
|
|
16201
|
+
failedCite: (ref) => `(failed:[${ref}] what came back)`,
|
|
16202
|
+
// LINK verbs (AC epic) — bind the problem in scope to CANONICAL priors and
|
|
16203
|
+
// abstractions. Multi-target by design: single links are weak; ~3 links to 3
|
|
16204
|
+
// DIFFERENT relevant priors is the target shape (see linkBullet).
|
|
16205
|
+
instance: (ref) => `(instance:[${ref}],[another],[a-third])`,
|
|
16206
|
+
pattern: () => `(pattern: name the general shape)`,
|
|
16207
|
+
// DOMAIN — the abstract area a problem is about; the concept layer an
|
|
16208
|
+
// anchor-less problem clusters on. Mints/resolves a Domain by name.
|
|
16209
|
+
domain: () => `(domain: The Area)`,
|
|
16210
|
+
// PACKAGE/COMPONENT — agent-named public anchors (OM-agent-anchors): the
|
|
16211
|
+
// package a problem is ABOUT (even when not a dependency) and the
|
|
16212
|
+
// framework/product-level unit it concerns. Public spine anchors → the
|
|
16213
|
+
// knowledge can cross to the collective.
|
|
16214
|
+
package: () => `(package: the-package-name)`,
|
|
16215
|
+
component: () => `(component: The Component)`,
|
|
16216
|
+
aids: (ref) => `(aids:[${ref}])`,
|
|
16217
|
+
// SOLUTION-STRUCTURE (SS-structure) — taught in the link bullet; Solution→Solution.
|
|
16218
|
+
buildsOn: (ref) => `(builds-on:[${ref}])`,
|
|
16219
|
+
supersedes: (ref) => `(supersedes:[${ref}])`,
|
|
16220
|
+
alternative: (ref) => `(alternative:[${ref}])`,
|
|
16221
|
+
// VERDICT (VS-verdict-tags) — a judgment on a SHOWN prior, distinct from leaning on it.
|
|
16222
|
+
// `confirm` rides the corroborate channel (it held); `refute` the contradict channel
|
|
16223
|
+
// (it's wrong / didn't hold) and REQUIRES a reason. This is the inline producer the
|
|
16224
|
+
// evidence channels lacked — refute sat dead (contradictionPressure 0) until now, while
|
|
16225
|
+
// lean-on corroboration lived, because only corroboration had an inline path. Grammar
|
|
16226
|
+
// single-sourced with parseInlineTags' CONFIRM_PREFIX/REFUTE_PREFIX.
|
|
16227
|
+
confirm: (ref) => `(confirm:[${ref}])`,
|
|
16228
|
+
refute: (ref) => `(refute:[${ref}] one line why)`,
|
|
16229
|
+
// RETRACT — "not a real problem": the errata-resolved block with a disposition
|
|
16230
|
+
// (parseDesignResolutions, turn.ts). No inline form exists; the block is the grammar.
|
|
16231
|
+
retract: (id) => ["```errata-resolved", `id: ${id}`, "disposition: false_positive", "reason: why it was never a real problem", "```"].join("\n")
|
|
16232
|
+
};
|
|
16233
|
+
P = "Problem";
|
|
16234
|
+
S = "Solution";
|
|
16235
|
+
RC = "RootCause";
|
|
16236
|
+
CITE_LOWERING = [
|
|
16237
|
+
{ target: RC, writes: [{ from: P, edge: "CAUSED_BY", to: RC }] },
|
|
16238
|
+
{ target: S, writes: [{ from: P, edge: "SOLVED_BY", to: S }] },
|
|
16239
|
+
{ target: "Pattern", writes: [{ from: P, edge: "INSTANCE_OF", to: "Pattern" }, { from: "Pattern", edge: "ANCHORED_AT", to: "File" }], note: "cite-anchor promotion after 2 sessions", decision: "d", knownViolation: { edge: "ANCHORED_AT", finding: "motif cite-anchor promotion writes a refused ANCHORED_AT" } },
|
|
16240
|
+
{ target: "Package", writes: [{ from: P, edge: "DEPENDS_ON", to: "Package" }] },
|
|
16241
|
+
{ target: "Language", writes: [{ from: P, edge: "OCCURS_IN", to: "Language" }] },
|
|
16242
|
+
{ target: "Tool", writes: [{ from: P, edge: "CONCERNS", to: "Tool" }] },
|
|
16243
|
+
{ target: "Component", writes: [{ from: P, edge: "CONCERNS", to: "Component" }] },
|
|
16244
|
+
{ target: P, writes: [{ from: P, edge: "RELATES_TO", to: P }, { from: P, edge: "ANCHORED_AT", to: "File" }], note: "corroboration witness + cite-anchor promotion" },
|
|
16245
|
+
{ target: "AntiPattern", writes: [{ from: P, edge: "RELATES_TO", to: "AntiPattern" }, { from: "AntiPattern", edge: "ANCHORED_AT", to: "File" }], decision: "e", knownViolation: { edge: "ANCHORED_AT", finding: "motif cite-anchor promotion writes a refused ANCHORED_AT" } },
|
|
16246
|
+
{ target: "Technique", writes: [{ from: P, edge: "RELATES_TO", to: "Technique" }, { from: "Technique", edge: "ANCHORED_AT", to: "File" }], decision: "e", knownViolation: { edge: "ANCHORED_AT", finding: "motif cite-anchor promotion writes a refused ANCHORED_AT" } },
|
|
16247
|
+
{ target: "Weakness", writes: [{ from: P, edge: "RELATES_TO", to: "Weakness" }], decision: "e" },
|
|
16248
|
+
{ target: "Domain", writes: [{ from: P, edge: "RELATES_TO", to: "Domain" }], decision: "e" },
|
|
16249
|
+
{ target: "Claim", writes: [{ from: P, edge: "RELATES_TO", to: "Claim" }, { from: "Claim", edge: "ANCHORED_AT", to: "File" }] }
|
|
16250
|
+
];
|
|
16251
|
+
INSTANCE_LOWERING = [
|
|
16252
|
+
{ target: "Pattern", writes: [{ from: P, edge: "INSTANCE_OF", to: "Pattern" }] },
|
|
16253
|
+
{ target: P, writes: [{ from: P, edge: "RELATES_TO", to: P }] },
|
|
16254
|
+
{ target: S, writes: [{ from: P, edge: "SOLVED_BY", to: S }], note: "an instance cite on a Solution claims the problem is solved by it", decision: "a" },
|
|
16255
|
+
{ target: RC, writes: [{ from: P, edge: "CAUSED_BY", to: RC }] },
|
|
16256
|
+
{ target: "AntiPattern", writes: [{ from: P, edge: "RELATES_TO", to: "AntiPattern" }], decision: "e" },
|
|
16257
|
+
{ target: "Technique", writes: [{ from: P, edge: "RELATES_TO", to: "Technique" }], decision: "e" },
|
|
16258
|
+
{ target: "Weakness", writes: [{ from: P, edge: "RELATES_TO", to: "Weakness" }], decision: "e" },
|
|
16259
|
+
{ target: "Domain", writes: [{ from: P, edge: "RELATES_TO", to: "Domain" }], decision: "e" },
|
|
16260
|
+
{ target: "Package", writes: [{ from: P, edge: "DEPENDS_ON", to: "Package" }] },
|
|
16261
|
+
{ target: "Component", writes: [{ from: P, edge: "CONCERNS", to: "Component" }] }
|
|
16262
|
+
];
|
|
16263
|
+
TRIAGE_TO_RC = [
|
|
16264
|
+
{ from: P, edge: "TRIAGED_BY", to: "Triage" },
|
|
16265
|
+
{ from: "Triage", edge: "INDICATES", to: RC },
|
|
16266
|
+
{ from: P, edge: "CAUSED_BY", to: RC }
|
|
16267
|
+
];
|
|
16268
|
+
TAG_VERBS = [
|
|
16269
|
+
{
|
|
16270
|
+
verb: "cite",
|
|
16271
|
+
form: "citation",
|
|
16272
|
+
opener: "(",
|
|
16273
|
+
kinds: ["prior"],
|
|
16274
|
+
args: "handles",
|
|
16275
|
+
thread: false,
|
|
16276
|
+
handleRequired: true,
|
|
16277
|
+
lowering: CITE_LOWERING,
|
|
16278
|
+
examples: ["prior"],
|
|
16279
|
+
hint: "cite a shown prior you leaned on: ([its-handle]) \u2014 handles only, no prose inside the parens",
|
|
16280
|
+
taught: "taught"
|
|
16281
|
+
},
|
|
16282
|
+
{
|
|
16283
|
+
verb: "!",
|
|
16284
|
+
form: "flag",
|
|
16285
|
+
opener: "[!",
|
|
16286
|
+
kinds: ["problem", "triage", "vulnerability", "fix", "prior"],
|
|
16287
|
+
args: "flag-body",
|
|
16288
|
+
thread: true,
|
|
16289
|
+
minText: 8,
|
|
16290
|
+
qualifiers: [
|
|
16291
|
+
{ name: "vuln", taught: true, note: "[!vuln: \u2026] \u2192 Vulnerability (asserted)" },
|
|
16292
|
+
{ name: "fixed", taught: false, note: "[!fixed: X] \u2192 read as a self-found fix" },
|
|
16293
|
+
{ name: "resolved", taught: false, note: "[!resolved: X] \u2192 read as a self-found fix" }
|
|
16294
|
+
],
|
|
16295
|
+
lowering: [
|
|
16296
|
+
{ target: P, writes: [], note: "mints a Problem; `@ path[:line]` anchors it (ANCHORED_AT to code)" },
|
|
16297
|
+
{ target: RC, writes: [...TRIAGE_TO_RC, { from: RC, edge: "CAUSED_BY", to: RC }], note: "`<-` segments: first \u2192 triage route, later \u2192 RootCause chain" },
|
|
16298
|
+
{ target: "Vulnerability", writes: [], note: "[!vuln: \u2026]; `<-` causes stay on the finding (vulnerabilityCauses)" }
|
|
16299
|
+
],
|
|
16300
|
+
examples: ["problem", "problemThread", "vulnerability", "vulnerabilityThread"],
|
|
16301
|
+
hint: "flag a problem on one line: [!what's wrong @ path:42]; fold causes with <- ; a fix goes in (fix: \u2026), not [!fixed: \u2026]",
|
|
16302
|
+
taught: "taught"
|
|
16303
|
+
},
|
|
16304
|
+
{
|
|
16305
|
+
verb: "?",
|
|
16306
|
+
form: "flag",
|
|
16307
|
+
opener: "[?",
|
|
16308
|
+
kinds: ["todo", "vulnerability"],
|
|
16309
|
+
args: "flag-body",
|
|
16310
|
+
thread: true,
|
|
16311
|
+
minText: 8,
|
|
16312
|
+
qualifiers: [{ name: "vuln", taught: true, note: "[?vuln: \u2026] \u2192 Vulnerability (suspected)" }],
|
|
16313
|
+
lowering: [{ target: P, writes: [], note: "mints a todo Problem; a fix can close it today", decision: "g" }],
|
|
16314
|
+
examples: ["suspectedVulnerability"],
|
|
16315
|
+
hint: "[?\u2026] marks an open question / todo; [?vuln: consequence; missing check] a suspected vulnerability",
|
|
16316
|
+
taught: "untaught",
|
|
16317
|
+
taughtBy: "only the [?vuln:] form is taught; the bare [?todo] form is not",
|
|
16318
|
+
decisions: ["g"]
|
|
16319
|
+
},
|
|
16320
|
+
{
|
|
16321
|
+
verb: "fix",
|
|
16322
|
+
form: "verb",
|
|
16323
|
+
opener: "(fix:",
|
|
16324
|
+
kinds: ["fix"],
|
|
16325
|
+
args: "handles-text",
|
|
16326
|
+
thread: true,
|
|
16327
|
+
minText: 12,
|
|
16328
|
+
lowering: [
|
|
16329
|
+
{ target: P, writes: [{ from: P, edge: "SOLVED_BY", to: S }], note: "closes the Problem; a note \u226512 chars mints the Solution" },
|
|
16330
|
+
{ target: "Vulnerability", writes: [{ from: "Vulnerability", edge: "SOLVED_BY", to: S }] },
|
|
16331
|
+
{ target: RC, writes: [], note: "dropped: 'fix tag went nowhere' (not a Problem); no tag writes FIXED_BY", decision: "f" }
|
|
16332
|
+
],
|
|
16333
|
+
examples: ["fix", "fixThread"],
|
|
16334
|
+
hint: "one fix per tag: (fix:[its-handle]) or (fix:#thread what you changed) \u2014 put the cause in its own (cause: \u2026)",
|
|
16335
|
+
taught: "taught",
|
|
16336
|
+
decisions: ["f"]
|
|
16337
|
+
},
|
|
16338
|
+
{
|
|
16339
|
+
verb: "cause",
|
|
16340
|
+
form: "verb",
|
|
16341
|
+
opener: "(cause:",
|
|
16342
|
+
kinds: ["triage"],
|
|
16343
|
+
args: "handles-text",
|
|
16344
|
+
thread: true,
|
|
16345
|
+
minText: 8,
|
|
16346
|
+
lowering: [
|
|
16347
|
+
{ target: RC, writes: TRIAGE_TO_RC },
|
|
16348
|
+
{ target: P, writes: [{ from: P, edge: "TRIAGED_BY", to: "Triage" }, { from: "Triage", edge: "INDICATES", to: RC }, { from: P, edge: "CAUSED_BY", to: RC }], note: "(cause:[problem] mechanism) \u2014 the cited Problem is the symptom; the mechanism mints the RootCause" },
|
|
16349
|
+
{ target: S, writes: [{ from: P, edge: "TRIAGED_BY", to: "Triage" }, { from: "Triage", edge: "INDICATES", to: S }], decision: "b", knownViolation: { edge: "INDICATES", finding: "(cause:[solution]) writes a refused INDICATES \u2192 Solution" } }
|
|
16350
|
+
],
|
|
16351
|
+
examples: ["triage", "triageExplain", "triageMint", "causeThread"],
|
|
16352
|
+
hint: "(cause: the mechanism you'd change) or (cause:[root-cause-handle]); bind a distant symptom with (cause:#thread \u2026)",
|
|
16353
|
+
taught: "taught",
|
|
16354
|
+
decisions: ["b"]
|
|
16355
|
+
},
|
|
16356
|
+
{
|
|
16357
|
+
verb: "constraint",
|
|
16358
|
+
form: "verb",
|
|
16359
|
+
opener: "(constraint:",
|
|
16360
|
+
kinds: ["constraint"],
|
|
16361
|
+
args: "text",
|
|
16362
|
+
thread: false,
|
|
16363
|
+
minText: 8,
|
|
16364
|
+
textRequired: true,
|
|
16365
|
+
lowering: [{ target: P, writes: [], note: "mints a constraint-kind Problem" }],
|
|
16366
|
+
examples: ["constraint"],
|
|
16367
|
+
hint: "(constraint: the limitation that forced the decision)",
|
|
16368
|
+
taught: "taught"
|
|
16369
|
+
},
|
|
16370
|
+
{
|
|
16371
|
+
verb: "tried",
|
|
16372
|
+
form: "verb",
|
|
16373
|
+
opener: "(tried:",
|
|
16374
|
+
kinds: ["attempt"],
|
|
16375
|
+
args: "text",
|
|
16376
|
+
thread: true,
|
|
16377
|
+
minText: 8,
|
|
16378
|
+
textRequired: true,
|
|
16379
|
+
lowering: [{ target: P, writes: [], note: "journals an attempt on the bound problem" }],
|
|
16380
|
+
examples: ["tried"],
|
|
16381
|
+
hint: "(tried: the approach) \u2014 bind with (tried:#thread \u2026)",
|
|
16382
|
+
taught: "taught"
|
|
16383
|
+
},
|
|
16384
|
+
{
|
|
16385
|
+
verb: "failed",
|
|
16386
|
+
form: "verb",
|
|
16387
|
+
opener: "(failed:",
|
|
16388
|
+
kinds: ["failure"],
|
|
16389
|
+
args: "handles-text",
|
|
16390
|
+
thread: true,
|
|
16391
|
+
minText: 8,
|
|
16392
|
+
textRequired: true,
|
|
16393
|
+
lowering: [
|
|
16394
|
+
{ target: P, writes: [], note: "journals a failed attempt on the bound problem" },
|
|
16395
|
+
{ target: S, writes: [], note: "(failed:[h]) refutes the cited prior (contradictionPressure); skips the label gate today", decision: "j" }
|
|
16396
|
+
],
|
|
16397
|
+
examples: ["failed", "failedCite"],
|
|
16398
|
+
hint: "(failed: what didn't work); (failed:[solution] what came back) refutes a shown fix",
|
|
16399
|
+
taught: "taught",
|
|
16400
|
+
decisions: ["j"]
|
|
16401
|
+
},
|
|
16402
|
+
{
|
|
16403
|
+
verb: "instance",
|
|
16404
|
+
form: "verb",
|
|
16405
|
+
opener: "(instance:",
|
|
16406
|
+
kinds: ["instance"],
|
|
16407
|
+
args: "handles",
|
|
16408
|
+
thread: false,
|
|
16409
|
+
handleRequired: true,
|
|
16410
|
+
lowering: INSTANCE_LOWERING,
|
|
16411
|
+
examples: ["instance"],
|
|
16412
|
+
hint: "(instance:[h1],[h2],[h3]) \u2014 ~3 DIFFERENT shown priors this problem instantiates; handles only",
|
|
16413
|
+
taught: "taught",
|
|
16414
|
+
decisions: ["a", "e"]
|
|
16415
|
+
},
|
|
16416
|
+
{
|
|
16417
|
+
verb: "pattern",
|
|
16418
|
+
form: "verb",
|
|
16419
|
+
opener: "(pattern:",
|
|
16420
|
+
kinds: ["pattern"],
|
|
16421
|
+
args: "handle-or-text",
|
|
16422
|
+
thread: false,
|
|
16423
|
+
minText: 3,
|
|
16424
|
+
lowering: [{ target: "Pattern", writes: [{ from: P, edge: "INSTANCE_OF", to: "Pattern" }], note: "a handle must be a Pattern; free text resolves-or-mints one" }],
|
|
16425
|
+
examples: ["pattern"],
|
|
16426
|
+
hint: "(pattern: name the general shape) or (pattern:[shown-pattern])",
|
|
16427
|
+
taught: "taught"
|
|
16428
|
+
},
|
|
16429
|
+
{
|
|
16430
|
+
verb: "domain",
|
|
16431
|
+
form: "verb",
|
|
16432
|
+
opener: "(domain:",
|
|
16433
|
+
kinds: ["domain"],
|
|
16434
|
+
args: "text",
|
|
16435
|
+
thread: false,
|
|
16436
|
+
minText: 2,
|
|
16437
|
+
textRequired: true,
|
|
16438
|
+
lowering: [{ target: "Domain", writes: [{ from: P, edge: "PERTAIN_TO", to: "Domain" }], note: '(domain:#t X) mints a Domain named "#t X" today', decision: "k" }],
|
|
16439
|
+
examples: ["domain"],
|
|
16440
|
+
hint: "(domain: The Area) \u2014 Title Case, no thread",
|
|
16441
|
+
taught: "taught",
|
|
16442
|
+
decisions: ["k"]
|
|
16443
|
+
},
|
|
16444
|
+
{
|
|
16445
|
+
verb: "package",
|
|
16446
|
+
form: "verb",
|
|
16447
|
+
opener: "(package:",
|
|
16448
|
+
kinds: ["package"],
|
|
16449
|
+
args: "text",
|
|
16450
|
+
thread: false,
|
|
16451
|
+
minText: 2,
|
|
16452
|
+
textRequired: true,
|
|
16453
|
+
lowering: [{ target: "Package", writes: [{ from: P, edge: "DEPENDS_ON", to: "Package" }], note: "means 'concerns', written as DEPENDS_ON", decision: "c" }],
|
|
16454
|
+
examples: ["package"],
|
|
16455
|
+
hint: "(package: the-package-name) \u2014 the public package it's about",
|
|
16456
|
+
taught: "taught",
|
|
16457
|
+
decisions: ["c"]
|
|
16458
|
+
},
|
|
16459
|
+
{
|
|
16460
|
+
verb: "component",
|
|
16461
|
+
form: "verb",
|
|
16462
|
+
opener: "(component:",
|
|
16463
|
+
kinds: ["component"],
|
|
16464
|
+
args: "text",
|
|
16465
|
+
thread: false,
|
|
16466
|
+
minText: 2,
|
|
16467
|
+
textRequired: true,
|
|
16468
|
+
lowering: [{ target: "Component", writes: [{ from: P, edge: "CONCERNS", to: "Component" }] }],
|
|
16469
|
+
examples: ["component"],
|
|
16470
|
+
hint: "(component: The Component) \u2014 the framework/product unit",
|
|
16471
|
+
taught: "taught"
|
|
16472
|
+
},
|
|
16473
|
+
{
|
|
16474
|
+
verb: "aids",
|
|
16475
|
+
form: "verb",
|
|
16476
|
+
opener: "(aids:",
|
|
16477
|
+
kinds: ["transfer"],
|
|
16478
|
+
args: "handles",
|
|
16479
|
+
thread: true,
|
|
16480
|
+
handleRequired: true,
|
|
16481
|
+
lowering: [{ target: P, writes: [{ from: S, edge: "MAY_RESOLVE", to: P }], note: "source = the Solution in scope; non-Problem targets are skipped" }],
|
|
16482
|
+
examples: ["aids"],
|
|
16483
|
+
hint: "(aids:[problem-handle],\u2026) \u2014 other shown problems your fix might also resolve",
|
|
16484
|
+
taught: "taught"
|
|
16485
|
+
},
|
|
16486
|
+
{
|
|
16487
|
+
verb: "builds-on",
|
|
16488
|
+
form: "verb",
|
|
16489
|
+
opener: "(builds-on:",
|
|
16490
|
+
kinds: ["builds-on"],
|
|
16491
|
+
args: "handles",
|
|
16492
|
+
thread: true,
|
|
16493
|
+
handleRequired: true,
|
|
16494
|
+
lowering: [{ target: S, writes: [{ from: S, edge: "BUILDS_ON", to: S }] }],
|
|
16495
|
+
examples: ["buildsOn"],
|
|
16496
|
+
hint: "(builds-on:[shown-solution]) \u2014 your fix layers on it",
|
|
16497
|
+
taught: "taught",
|
|
16498
|
+
taughtBy: "linkBullet instruction text"
|
|
16499
|
+
},
|
|
16500
|
+
{
|
|
16501
|
+
verb: "supersedes",
|
|
16502
|
+
form: "verb",
|
|
16503
|
+
opener: "(supersedes:",
|
|
16504
|
+
kinds: ["supersedes"],
|
|
16505
|
+
args: "handles",
|
|
16506
|
+
thread: true,
|
|
16507
|
+
handleRequired: true,
|
|
16508
|
+
lowering: [{ target: S, writes: [{ from: S, edge: "SUPERSEDES", to: S }] }],
|
|
16509
|
+
examples: ["supersedes"],
|
|
16510
|
+
hint: "(supersedes:[shown-solution]) \u2014 your fix replaces it",
|
|
16511
|
+
taught: "taught",
|
|
16512
|
+
taughtBy: "linkBullet instruction text"
|
|
16513
|
+
},
|
|
16514
|
+
{
|
|
16515
|
+
verb: "alternative",
|
|
16516
|
+
form: "verb",
|
|
16517
|
+
opener: "(alternative:",
|
|
16518
|
+
kinds: ["alternative"],
|
|
16519
|
+
args: "handles",
|
|
16520
|
+
thread: true,
|
|
16521
|
+
handleRequired: true,
|
|
16522
|
+
lowering: [{ target: S, writes: [{ from: S, edge: "ALTERNATIVE_TO", to: S }] }],
|
|
16523
|
+
examples: ["alternative"],
|
|
16524
|
+
hint: "(alternative:[shown-solution]) \u2014 a different valid approach",
|
|
16525
|
+
taught: "taught",
|
|
16526
|
+
taughtBy: "linkBullet instruction text"
|
|
16527
|
+
},
|
|
16528
|
+
{
|
|
16529
|
+
verb: "confirm",
|
|
16530
|
+
form: "verb",
|
|
16531
|
+
opener: "(confirm:",
|
|
16532
|
+
kinds: ["confirm"],
|
|
16533
|
+
args: "handles",
|
|
16534
|
+
thread: false,
|
|
16535
|
+
handleRequired: true,
|
|
16536
|
+
lowering: [{ target: S, writes: [], note: "corroboration witness (corroborationCount), no edge" }],
|
|
16537
|
+
examples: ["confirm"],
|
|
16538
|
+
hint: "(confirm:[shown-prior]) \u2014 it held",
|
|
16539
|
+
taught: "taught",
|
|
16540
|
+
taughtBy: "verdictBullet + context render",
|
|
16541
|
+
decisions: ["j"]
|
|
16542
|
+
},
|
|
16543
|
+
{
|
|
16544
|
+
verb: "refute",
|
|
16545
|
+
form: "verb",
|
|
16546
|
+
opener: "(refute:",
|
|
16547
|
+
kinds: ["refute"],
|
|
16548
|
+
args: "handles-text",
|
|
16549
|
+
thread: false,
|
|
16550
|
+
minText: 4,
|
|
16551
|
+
handleRequired: true,
|
|
16552
|
+
textRequired: true,
|
|
16553
|
+
lowering: [{ target: S, writes: [], note: "contradiction witness (contradictionPressure), no edge; reason stays local" }],
|
|
16554
|
+
examples: ["refute"],
|
|
16555
|
+
hint: "(refute:[shown-prior] one line why) \u2014 the reason is required",
|
|
16556
|
+
taught: "taught",
|
|
16557
|
+
taughtBy: "verdictBullet + context render",
|
|
16558
|
+
decisions: ["j"]
|
|
16559
|
+
}
|
|
16560
|
+
];
|
|
16561
|
+
TAG_FENCES = [
|
|
16562
|
+
{
|
|
16563
|
+
fence: "errata-problem",
|
|
16564
|
+
fields: ["problem", "cause", "fix", "anchor", "kind"],
|
|
16565
|
+
required: [["problem"]],
|
|
16566
|
+
taught: "untaught",
|
|
16567
|
+
parser: "apps/daemon/src/turn.ts parseDesignFlags",
|
|
16568
|
+
decisions: ["h"],
|
|
16569
|
+
hint: "use an inline [!\u2026] flag instead (errata-problem cause ids never meet inline ids)"
|
|
16570
|
+
},
|
|
16571
|
+
{
|
|
16572
|
+
fence: "errata-resolved",
|
|
16573
|
+
fields: ["id", "problem", "fix", "disposition", "reason"],
|
|
16574
|
+
required: [["id", "problem"]],
|
|
16575
|
+
taught: "taught",
|
|
16576
|
+
taughtBy: "retract form only (TAG_EXAMPLE.retract); the fix form is untaught",
|
|
16577
|
+
parser: "apps/daemon/src/turn.ts parseDesignResolutions",
|
|
16578
|
+
hint: "```errata-resolved with id: and disposition: false_positive | invalid, reason: \u2026"
|
|
16579
|
+
},
|
|
16580
|
+
{
|
|
16581
|
+
fence: "errata-triage",
|
|
16582
|
+
fields: ["presenting", "cause", "test", "fix", "drift", "route"],
|
|
16583
|
+
required: [["presenting", "route"], ["cause", "test"]],
|
|
16584
|
+
taught: "taught",
|
|
16585
|
+
taughtBy: "MCP errata_triage tool copy",
|
|
16586
|
+
parser: "packages/local-graph/src/triage.ts parseTriageFences",
|
|
16587
|
+
hint: "```errata-triage with presenting: and cause: (or route: + test: to answer one)"
|
|
16588
|
+
},
|
|
16589
|
+
{
|
|
16590
|
+
fence: "errata-abstraction",
|
|
16591
|
+
fields: ["candidate", "principle", "covers"],
|
|
16592
|
+
required: [["candidate"], ["principle"], ["covers"]],
|
|
16593
|
+
taught: "taught",
|
|
16594
|
+
taughtBy: "MCP errata_pending_abstractions tool copy",
|
|
16595
|
+
parser: "packages/local-graph/src/abstraction.ts parseAbstractionFences",
|
|
16596
|
+
hint: "```errata-abstraction with candidate:, principle:, covers: (all required)"
|
|
16597
|
+
},
|
|
16598
|
+
{
|
|
16599
|
+
fence: "errata-intent",
|
|
16600
|
+
fields: ["intent"],
|
|
16601
|
+
required: [["intent"]],
|
|
16602
|
+
taught: "taught",
|
|
16603
|
+
parser: "packages/local-graph/src/intent.ts parseIntentFences",
|
|
16604
|
+
hint: "```errata-intent with intent: what you're about to do"
|
|
16605
|
+
},
|
|
16606
|
+
{
|
|
16607
|
+
fence: "errata-share",
|
|
16608
|
+
fields: ["priors"],
|
|
16609
|
+
required: [["priors"]],
|
|
16610
|
+
taught: "taught",
|
|
16611
|
+
taughtBy: "subagent hand-off copy",
|
|
16612
|
+
parser: "packages/local-graph/src/intent.ts parseShareFence",
|
|
16613
|
+
hint: "```errata-share with priors: all | [h1], [h2]"
|
|
16614
|
+
}
|
|
16615
|
+
];
|
|
16616
|
+
TAG_VERB_BY_NAME = new Map(
|
|
16617
|
+
TAG_VERBS.filter((v) => v.form === "verb").map((v) => [v.verb, v])
|
|
16618
|
+
);
|
|
16619
|
+
TAG_PAREN_VERBS = [...TAG_VERB_BY_NAME.keys()].sort((a, b) => b.length - a.length);
|
|
16620
|
+
TAG_GRAMMAR_OPENERS = [
|
|
16621
|
+
...new Set(TAG_VERBS.filter((v) => v.form !== "citation").map((v) => v.opener))
|
|
16622
|
+
];
|
|
16623
|
+
TAG_QUOTED_PAREN_SOURCE = String.raw`\(\s*(?:${TAG_PAREN_VERBS.map((v) => v.replace(/-/g, "\\-")).join("|")})\s*:|\(\[`;
|
|
16624
|
+
TAG_QUOTED_RE = new RegExp(String.raw`\[[!?]|` + TAG_QUOTED_PAREN_SOURCE, "i");
|
|
16625
|
+
TAG_QUOTED_PAREN_RE = new RegExp(TAG_QUOTED_PAREN_SOURCE, "i");
|
|
16626
|
+
TEMPLATE_VERB_GROUP = new RegExp(
|
|
16627
|
+
String.raw`\(\s*(${TAG_PAREN_VERBS.map((v) => v.replace(/-/g, "\\-")).join("|")})\s*:((?:[^()\n]|\([^()\n]*\))*)\)`,
|
|
16628
|
+
"gi"
|
|
16629
|
+
);
|
|
16630
|
+
BRACKET_TOKEN = /\[([^\]\n]{1,80})\]/g;
|
|
16631
|
+
REAL_HANDLE_SHAPE = new RegExp(
|
|
16632
|
+
String.raw`^(?:[a-z0-9]+(?:-[a-z0-9]+)*-(?=[0-9a-f]{0,3}\d)[0-9a-f]{4}|(?:${TAG_CANONICAL_ID_PREFIXES.join("|")})_[0-9a-f]{6,})$`,
|
|
16633
|
+
"i"
|
|
16634
|
+
);
|
|
16635
|
+
TAG_TEMPLATES = deriveTagTemplates(tagGrammarCopy());
|
|
16636
|
+
VERB_WORDS_SOURCE = TAG_PAREN_VERBS.map((v) => v.replace(/-/g, "\\-")).join("|");
|
|
16637
|
+
JOINED_VERB = new RegExp(String.raw`[,;]\s*(?=(?:${VERB_WORDS_SOURCE})\s*:)`, "i");
|
|
16638
|
+
LEADING_VERB = new RegExp(String.raw`^\s*(${VERB_WORDS_SOURCE})\s*:`, "i");
|
|
16639
|
+
ANY_VERB_WORD = new RegExp(String.raw`\b(?:${VERB_WORDS_SOURCE})\s*:`, "gi");
|
|
16640
|
+
QUOTED_TAG_OPENER = new RegExp(String.raw`\(\s*(?:(${VERB_WORDS_SOURCE})\s*:|(?=\[))`, "gi");
|
|
16641
|
+
}
|
|
16642
|
+
});
|
|
16643
|
+
|
|
16087
16644
|
// ../../packages/local-shared/src/index.ts
|
|
16088
16645
|
var init_src2 = __esm({
|
|
16089
16646
|
"../../packages/local-shared/src/index.ts"() {
|
|
@@ -16097,6 +16654,7 @@ var init_src2 = __esm({
|
|
|
16097
16654
|
init_review();
|
|
16098
16655
|
init_anchorable();
|
|
16099
16656
|
init_sqlite_adapter();
|
|
16657
|
+
init_tag_grammar();
|
|
16100
16658
|
}
|
|
16101
16659
|
});
|
|
16102
16660
|
|
|
@@ -16267,6 +16825,48 @@ import { parentPort } from "node:worker_threads";
|
|
|
16267
16825
|
init_src2();
|
|
16268
16826
|
init_src();
|
|
16269
16827
|
|
|
16828
|
+
// ../../packages/local-graph/src/wm-calibration.ts
|
|
16829
|
+
var WM_CALIBRATION_KEYS = [
|
|
16830
|
+
"shownCount",
|
|
16831
|
+
"evictedCount",
|
|
16832
|
+
"reinforceShownCount",
|
|
16833
|
+
"reinforceRecentCount",
|
|
16834
|
+
"reinforceEvictedCount",
|
|
16835
|
+
"reinforceUnshownCount",
|
|
16836
|
+
"reinforceUnshownQueryOnlyCount"
|
|
16837
|
+
];
|
|
16838
|
+
var WM_COUNTED_PREDICATE = WM_CALIBRATION_KEYS.map((k) => `json_type(attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
|
|
16839
|
+
var wmCounterSql = (k) => `CASE WHEN json_type(attrs_json, '$.${k}') IN ('integer', 'real') THEN json_extract(attrs_json, '$.${k}') END`;
|
|
16840
|
+
|
|
16841
|
+
// ../../packages/local-graph/src/exposure-attrs.ts
|
|
16842
|
+
var EXPOSURE_ATTRS = [
|
|
16843
|
+
"shownCount",
|
|
16844
|
+
"evictedCount",
|
|
16845
|
+
"firstShownAt",
|
|
16846
|
+
"firstEvictedAt",
|
|
16847
|
+
"firstShownAtSeq",
|
|
16848
|
+
"lastShownAtSeq",
|
|
16849
|
+
"lastEvictedAtSeq",
|
|
16850
|
+
"shownSinceTouch",
|
|
16851
|
+
"shownSinceTouchAt",
|
|
16852
|
+
"exposureLog",
|
|
16853
|
+
"exposureLogStart",
|
|
16854
|
+
"exposureLogSince",
|
|
16855
|
+
"exposureFirst",
|
|
16856
|
+
"exposureTrimmed"
|
|
16857
|
+
];
|
|
16858
|
+
var EXPOSURE_SCOPE = "exposure";
|
|
16859
|
+
var EXPOSURE_KEYS = new Set(EXPOSURE_ATTRS);
|
|
16860
|
+
var isExposureAttr = (key) => EXPOSURE_KEYS.has(key);
|
|
16861
|
+
function pickExposure(attrs) {
|
|
16862
|
+
const out = {};
|
|
16863
|
+
for (const k of EXPOSURE_ATTRS) if (attrs[k] !== void 0) out[k] = attrs[k];
|
|
16864
|
+
return out;
|
|
16865
|
+
}
|
|
16866
|
+
function exposureDocSql(key) {
|
|
16867
|
+
return `coalesce((SELECT x.attrs_json FROM node_exposure x WHERE x.id = nodes.id AND json_type(x.attrs_json, '$.${key}') IS NOT NULL), attrs_json)`;
|
|
16868
|
+
}
|
|
16869
|
+
|
|
16270
16870
|
// ../../packages/local-graph/src/history-cache-floor.ts
|
|
16271
16871
|
var HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
|
|
16272
16872
|
function installHistoryCacheFloor(db) {
|
|
@@ -16307,7 +16907,7 @@ function installHistoryCacheFloor(db) {
|
|
|
16307
16907
|
OR EXISTS(SELECT 1 FROM history_cache_withdrawals w WHERE w.id = nodes.id))
|
|
16308
16908
|
AND (embedding_blob IS NOT NULL OR embedding_json <> '[]' OR is_landmark <> 0
|
|
16309
16909
|
OR description <> ${description} OR attrs_json <> ${attrs});`;
|
|
16310
|
-
const internal = `key IN ('orgId','teamId','groupId','projectId','aliasTexts','aliasCanonicalIds')
|
|
16910
|
+
const internal = `key IN ('orgId','teamId','groupId','projectId','aliasTexts','aliasCanonicalIds','residueAudienceVerified')
|
|
16311
16911
|
OR key GLOB 'mergedFrom*' OR key GLOB '*WitnessKeys' OR key GLOB '*Sessions'
|
|
16312
16912
|
OR key GLOB 'authoringSession*' OR key GLOB 'contributor*' OR key GLOB 'witnessKeys*'`;
|
|
16313
16913
|
const stripMetadata = (scope) => `UPDATE nodes SET attrs_json=(
|
|
@@ -16443,7 +17043,9 @@ function installEditPlaceholderRetirement(db) {
|
|
|
16443
17043
|
|
|
16444
17044
|
// ../../packages/local-graph/src/cloud-attr-compaction.ts
|
|
16445
17045
|
init_src2();
|
|
16446
|
-
var CLOUD_ATTR_COMPACTION_KEY = "cloud-attr-compaction-
|
|
17046
|
+
var CLOUD_ATTR_COMPACTION_KEY = "cloud-attr-compaction-2";
|
|
17047
|
+
var V1_KEY = "cloud-attr-compaction-1";
|
|
17048
|
+
var STRUCT_LABELS = "('Problem', 'Solution', 'RootCause')";
|
|
16447
17049
|
var DUPES = CLOUD_ATTR_DUPLICATE_KEYS.map((k) => `'${k}'`).join(",");
|
|
16448
17050
|
function compacted(row) {
|
|
16449
17051
|
const packed = `json_extract(${row}.attrs_json, '$.attrsJson')`;
|
|
@@ -16462,14 +17064,17 @@ function dirty(row) {
|
|
|
16462
17064
|
return `json_extract(${row}.attrs_json, '$.source') = 'cloud' AND (${has})`;
|
|
16463
17065
|
}
|
|
16464
17066
|
function installCloudAttrCompaction(db) {
|
|
16465
|
-
|
|
17067
|
+
const marked = (key) => Boolean(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key));
|
|
17068
|
+
if (marked(CLOUD_ATTR_COMPACTION_KEY)) return;
|
|
17069
|
+
const scope = marked(V1_KEY) ? ` AND label IN ${STRUCT_LABELS}` : "";
|
|
16466
17070
|
db.transaction(() => {
|
|
16467
17071
|
for (const operation of ["INSERT", "UPDATE"]) {
|
|
16468
|
-
db.exec(`
|
|
17072
|
+
db.exec(`DROP TRIGGER IF EXISTS cloud_attr_compact_${operation.toLowerCase()}`);
|
|
17073
|
+
db.exec(`CREATE TRIGGER cloud_attr_compact_${operation.toLowerCase()}
|
|
16469
17074
|
AFTER ${operation} ON nodes WHEN ${dirty("NEW")}
|
|
16470
17075
|
BEGIN UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE id = NEW.id; END;`);
|
|
16471
17076
|
}
|
|
16472
|
-
db.exec(`UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE ${dirty("nodes")}`);
|
|
17077
|
+
db.exec(`UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE ${dirty("nodes")}${scope}`);
|
|
16473
17078
|
db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(CLOUD_ATTR_COMPACTION_KEY, "installed");
|
|
16474
17079
|
});
|
|
16475
17080
|
}
|
|
@@ -16508,6 +17113,313 @@ function installHistoryAttrSlim(db) {
|
|
|
16508
17113
|
db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(HISTORY_ATTR_SLIM_KEY, "done");
|
|
16509
17114
|
}
|
|
16510
17115
|
|
|
17116
|
+
// ../../packages/local-graph/src/text-index.ts
|
|
17117
|
+
var TEXT_INDEX_KEY = "text-index-trigram-2";
|
|
17118
|
+
var TEXT_INDEX_MIN_TERM = 3;
|
|
17119
|
+
function installTextIndex(db) {
|
|
17120
|
+
if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)) return;
|
|
17121
|
+
try {
|
|
17122
|
+
db.exec(`CREATE VIRTUAL TABLE temp.fts5_probe USING fts5(x, tokenize='trigram'); DROP TABLE temp.fts5_probe`);
|
|
17123
|
+
} catch {
|
|
17124
|
+
db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(TEXT_INDEX_KEY, "unavailable");
|
|
17125
|
+
return;
|
|
17126
|
+
}
|
|
17127
|
+
db.transaction(() => {
|
|
17128
|
+
db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS nodes_text
|
|
17129
|
+
USING fts5(description, content='nodes', content_rowid='rowid', tokenize='trigram')`);
|
|
17130
|
+
db.exec(`DROP TRIGGER IF EXISTS nodes_text_update_out; DROP TRIGGER IF EXISTS nodes_text_update_in;
|
|
17131
|
+
DROP TRIGGER IF EXISTS nodes_text_update`);
|
|
17132
|
+
db.exec(`CREATE TRIGGER IF NOT EXISTS nodes_text_insert AFTER INSERT ON nodes WHEN NEW.valid_to IS NULL
|
|
17133
|
+
BEGIN INSERT INTO nodes_text(rowid, description) VALUES (NEW.rowid, NEW.description); END;`);
|
|
17134
|
+
db.exec(`CREATE TRIGGER nodes_text_update AFTER UPDATE OF description, valid_to ON nodes
|
|
17135
|
+
WHEN OLD.description IS NOT NEW.description OR OLD.valid_to IS NOT NEW.valid_to
|
|
17136
|
+
BEGIN
|
|
17137
|
+
INSERT INTO nodes_text(nodes_text, rowid, description)
|
|
17138
|
+
SELECT 'delete', OLD.rowid, OLD.description WHERE OLD.valid_to IS NULL;
|
|
17139
|
+
INSERT INTO nodes_text(rowid, description)
|
|
17140
|
+
SELECT NEW.rowid, NEW.description WHERE NEW.valid_to IS NULL;
|
|
17141
|
+
END;`);
|
|
17142
|
+
db.exec(`CREATE TRIGGER IF NOT EXISTS nodes_text_delete AFTER DELETE ON nodes WHEN OLD.valid_to IS NULL
|
|
17143
|
+
BEGIN INSERT INTO nodes_text(nodes_text, rowid, description) VALUES ('delete', OLD.rowid, OLD.description); END;`);
|
|
17144
|
+
db.exec(`INSERT INTO nodes_text(nodes_text) VALUES ('delete-all')`);
|
|
17145
|
+
db.exec(`INSERT INTO nodes_text(rowid, description) SELECT rowid, description FROM nodes WHERE valid_to IS NULL`);
|
|
17146
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key,value) VALUES (?,?)").run("text-index-trigram-1", "installed");
|
|
17147
|
+
db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(TEXT_INDEX_KEY, "installed");
|
|
17148
|
+
});
|
|
17149
|
+
}
|
|
17150
|
+
function textIndexReady(db) {
|
|
17151
|
+
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)?.["value"] === "installed";
|
|
17152
|
+
}
|
|
17153
|
+
|
|
17154
|
+
// ../../packages/local-graph/src/change-feed.ts
|
|
17155
|
+
var CHANGE_FEED_KEY = "change-feed";
|
|
17156
|
+
var ANY_CHANGE = "*";
|
|
17157
|
+
var nodeScope = (label) => `node:${label}`;
|
|
17158
|
+
var CHANGE_LOG_KEEP = 25e4;
|
|
17159
|
+
var PRUNED_TO_KEY = "change-log-pruned-to";
|
|
17160
|
+
function installChangeFeed(db) {
|
|
17161
|
+
if (changeFeedInstalled(db)) return;
|
|
17162
|
+
db.transaction(() => {
|
|
17163
|
+
db.exec("CREATE TABLE IF NOT EXISTS change_seq (scope TEXT PRIMARY KEY, seq INTEGER NOT NULL) WITHOUT ROWID");
|
|
17164
|
+
db.exec(`CREATE TABLE IF NOT EXISTS change_log (change_seq INTEGER PRIMARY KEY, kind TEXT NOT NULL, id TEXT NOT NULL,
|
|
17165
|
+
label TEXT, from_id TEXT, to_id TEXT)`);
|
|
17166
|
+
for (const name of ["nodes", "edges"].flatMap((t) => ["insert", "update", "delete"].map((op) => `change_feed_${t}_${op}`))) {
|
|
17167
|
+
db.exec(`DROP TRIGGER IF EXISTS ${name}`);
|
|
17168
|
+
}
|
|
17169
|
+
const changed = (table) => columns(db, table).map((c) => `OLD."${c}" IS NOT NEW."${c}"`).join(" OR ");
|
|
17170
|
+
const strip = (row) => `json_remove(${row}.attrs_json, ${EXPOSURE_ATTRS.map((a) => `'$.${a}'`).join(", ")})`;
|
|
17171
|
+
const exposureOnly = `(${columns(db, "nodes").filter((c) => c !== "attrs_json").map((c) => `OLD."${c}" IS NEW."${c}"`).join(" AND ")}
|
|
17172
|
+
AND CASE WHEN json_valid(OLD.attrs_json) AND json_valid(NEW.attrs_json) THEN ${strip("OLD")} IS ${strip("NEW")} ELSE 0 END)`;
|
|
17173
|
+
const bump = (...scopes) => `INSERT INTO change_seq(scope, seq) VALUES ${scopes.map((s) => `(${s}, 1)`).join(", ")}
|
|
17174
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;`;
|
|
17175
|
+
const seq = `(SELECT seq FROM change_seq WHERE scope = '*')`;
|
|
17176
|
+
const logNode = (row) => `INSERT INTO change_log(change_seq, kind, id, label) VALUES (${seq}, 'node', ${row}.id, ${row}.label);`;
|
|
17177
|
+
const logEdge = (row) => `INSERT INTO change_log(change_seq, kind, id, label, from_id, to_id) VALUES (${seq}, 'edge', ${row}.id, ${row}.type, ${row}.from_id, ${row}.to_id);`;
|
|
17178
|
+
const node = (row) => `'node:' || ${row}.label`;
|
|
17179
|
+
const edge = (row) => `'edge:' || ${row}.type`;
|
|
17180
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_insert AFTER INSERT ON nodes BEGIN ${bump(`'*'`, node("NEW"))} ${logNode("NEW")} END;`);
|
|
17181
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_update AFTER UPDATE ON nodes WHEN ${changed("nodes")} BEGIN
|
|
17182
|
+
${bump(`'*'`)}
|
|
17183
|
+
INSERT INTO change_seq(scope, seq) SELECT CASE WHEN ${exposureOnly} THEN '${EXPOSURE_SCOPE}' ELSE ${node("NEW")} END, 1 WHERE 1
|
|
17184
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
17185
|
+
INSERT INTO change_seq(scope, seq) SELECT ${node("OLD")}, 1 WHERE OLD.label IS NOT NEW.label
|
|
17186
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
17187
|
+
${logNode("NEW")}
|
|
17188
|
+
END;`);
|
|
17189
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_delete AFTER DELETE ON nodes BEGIN ${bump(`'*'`, node("OLD"))} ${logNode("OLD")} END;`);
|
|
17190
|
+
db.exec(`CREATE TRIGGER change_feed_edges_insert AFTER INSERT ON edges BEGIN ${bump(`'*'`, edge("NEW"))} ${logEdge("NEW")} END;`);
|
|
17191
|
+
db.exec(`CREATE TRIGGER change_feed_edges_update AFTER UPDATE ON edges WHEN ${changed("edges")} BEGIN
|
|
17192
|
+
${bump(`'*'`, edge("NEW"))}
|
|
17193
|
+
INSERT INTO change_seq(scope, seq) SELECT ${edge("OLD")}, 1 WHERE OLD.type IS NOT NEW.type
|
|
17194
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
17195
|
+
${logEdge("NEW")}
|
|
17196
|
+
END;`);
|
|
17197
|
+
db.exec(`CREATE TRIGGER change_feed_edges_delete AFTER DELETE ON edges BEGIN ${bump(`'*'`, edge("OLD"))} ${logEdge("OLD")} END;`);
|
|
17198
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(PRUNED_TO_KEY, String(readChangeSeq(db)));
|
|
17199
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(CHANGE_FEED_KEY, columnSignature(db));
|
|
17200
|
+
});
|
|
17201
|
+
}
|
|
17202
|
+
function readChangeSeq(db, scopes = [ANY_CHANGE]) {
|
|
17203
|
+
if (!scopes.length) return 0;
|
|
17204
|
+
const row = db.prepare(`SELECT coalesce(sum(seq), 0) AS n FROM change_seq WHERE scope IN (${scopes.map(() => "?").join(",")})`).get(...scopes);
|
|
17205
|
+
return Number(row?.n ?? 0);
|
|
17206
|
+
}
|
|
17207
|
+
function changeLogCovers(db, sinceSeq) {
|
|
17208
|
+
const prunedTo = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? NaN);
|
|
17209
|
+
return Number.isFinite(prunedTo) && sinceSeq >= prunedTo;
|
|
17210
|
+
}
|
|
17211
|
+
function readChangedNodeIds(db, sinceSeq, labels) {
|
|
17212
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
17213
|
+
if (!labels.length) return [];
|
|
17214
|
+
const rows = db.prepare(`SELECT DISTINCT id FROM change_log WHERE change_seq > ? AND kind = 'node' AND label IN (${labels.map(() => "?").join(",")})`).all(sinceSeq, ...labels);
|
|
17215
|
+
return rows.map((r) => r.id);
|
|
17216
|
+
}
|
|
17217
|
+
function readChangedAmong(db, sinceSeq, ids) {
|
|
17218
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
17219
|
+
const out = [];
|
|
17220
|
+
for (let i = 0; i < ids.length; i += 500) {
|
|
17221
|
+
const chunk = ids.slice(i, i + 500);
|
|
17222
|
+
const rows = db.prepare(`SELECT DISTINCT id FROM change_log WHERE change_seq > ? AND kind = 'node' AND id IN (${chunk.map(() => "?").join(",")})`).all(sinceSeq, ...chunk);
|
|
17223
|
+
for (const r of rows) out.push(r.id);
|
|
17224
|
+
}
|
|
17225
|
+
return out;
|
|
17226
|
+
}
|
|
17227
|
+
function readLineageChangedFiles(db, sinceSeq) {
|
|
17228
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
17229
|
+
const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
|
|
17230
|
+
return rows.map((r) => r.id);
|
|
17231
|
+
}
|
|
17232
|
+
var LINEAGE_CHANGED_FILES_SQL = `
|
|
17233
|
+
SELECT id FROM change_log WHERE change_seq > :s AND kind = 'node' AND label = 'File'
|
|
17234
|
+
UNION SELECT from_id FROM change_log WHERE change_seq > :s AND kind = 'edge' AND label IN ('DEFINES','CONTAINS')
|
|
17235
|
+
UNION SELECT e.from_id FROM (SELECT DISTINCT id FROM change_log WHERE change_seq > :s AND kind = 'node') c
|
|
17236
|
+
CROSS JOIN edges e ON e.to_id = c.id AND e.type IN ('DEFINES','CONTAINS')
|
|
17237
|
+
`;
|
|
17238
|
+
function pruneChangeLog(db) {
|
|
17239
|
+
const max = Number(db.prepare("SELECT coalesce(max(change_seq), 0) AS n FROM change_log").get()?.["n"] ?? 0);
|
|
17240
|
+
const cut = max - CHANGE_LOG_KEEP;
|
|
17241
|
+
if (cut <= 0) return 0;
|
|
17242
|
+
let removed = 0;
|
|
17243
|
+
db.transaction(() => {
|
|
17244
|
+
removed = Number(db.prepare("DELETE FROM change_log WHERE change_seq <= ?").run(cut).changes);
|
|
17245
|
+
const prev = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? 0);
|
|
17246
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(PRUNED_TO_KEY, String(Math.max(prev, cut)));
|
|
17247
|
+
});
|
|
17248
|
+
return removed;
|
|
17249
|
+
}
|
|
17250
|
+
function changeFeedInstalled(db) {
|
|
17251
|
+
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(CHANGE_FEED_KEY)?.["value"] === columnSignature(db);
|
|
17252
|
+
}
|
|
17253
|
+
function columns(db, table) {
|
|
17254
|
+
return db.prepare(`SELECT name FROM pragma_table_info('${table}') ORDER BY cid`).all().map((r) => r.name);
|
|
17255
|
+
}
|
|
17256
|
+
function columnSignature(db) {
|
|
17257
|
+
return `5|${columns(db, "nodes").join(",")}|${columns(db, "edges").join(",")}|${EXPOSURE_ATTRS.join(",")}`;
|
|
17258
|
+
}
|
|
17259
|
+
|
|
17260
|
+
// ../../packages/local-graph/src/lineage-integrity.ts
|
|
17261
|
+
var statements = /* @__PURE__ */ new WeakMap();
|
|
17262
|
+
function stmt(db, sql) {
|
|
17263
|
+
let byDb = statements.get(db);
|
|
17264
|
+
if (!byDb) statements.set(db, byDb = /* @__PURE__ */ new Map());
|
|
17265
|
+
let s = byDb.get(sql);
|
|
17266
|
+
if (!s) byDb.set(sql, s = db.prepare(sql));
|
|
17267
|
+
return s;
|
|
17268
|
+
}
|
|
17269
|
+
var HISTORY_EDGE_TYPES = [
|
|
17270
|
+
"SUPERSEDED_BY",
|
|
17271
|
+
"CONTINUES",
|
|
17272
|
+
"FORKED_FROM",
|
|
17273
|
+
"REGRESSION_OF",
|
|
17274
|
+
"SPLIT_INTO",
|
|
17275
|
+
"REVEALED_BY"
|
|
17276
|
+
];
|
|
17277
|
+
var LINEAGE_INTEGRITY_KEY = "lineage-integrity-v1";
|
|
17278
|
+
var HISTORY_SQL = HISTORY_EDGE_TYPES.map((t) => `'${t}'`).join(",");
|
|
17279
|
+
var NOT_HISTORY = `type NOT IN (${HISTORY_SQL})`;
|
|
17280
|
+
var NOT_HISTORICAL_ROW = `json_type(attrs_json, '$.historical') IS NOT 'true'`;
|
|
17281
|
+
var RETAINS_EDGES = (n) => `(${n}.label = 'Problem'
|
|
17282
|
+
AND json_type(${n}.attrs_json, '$.resolvedAt') IS NOT NULL
|
|
17283
|
+
AND COALESCE(json_extract(${n}.attrs_json, '$.resolvedAs'), '') NOT IN ('duplicate', 'false_positive'))`;
|
|
17284
|
+
var PRESENT = (n) => `(${n}.valid_to IS NULL OR ${RETAINS_EDGES(n)})`;
|
|
17285
|
+
var STRANDED_EDGE_PREDICATE = `e.valid_to IS NULL AND e.${NOT_HISTORY}
|
|
17286
|
+
AND (NOT EXISTS (SELECT 1 FROM nodes f WHERE f.id = e.from_id AND ${PRESENT("f")})
|
|
17287
|
+
OR NOT EXISTS (SELECT 1 FROM nodes t WHERE t.id = e.to_id AND ${PRESENT("t")}))`;
|
|
17288
|
+
function countStrandedEdges(db) {
|
|
17289
|
+
const rows = db.prepare(`SELECT e.type AS type, count(*) AS n FROM edges e WHERE ${STRANDED_EDGE_PREDICATE} GROUP BY e.type ORDER BY n DESC`).all();
|
|
17290
|
+
const byType = {};
|
|
17291
|
+
let total = 0;
|
|
17292
|
+
for (const r of rows) {
|
|
17293
|
+
byType[r.type] = Number(r.n);
|
|
17294
|
+
total += Number(r.n);
|
|
17295
|
+
}
|
|
17296
|
+
return { total, byType };
|
|
17297
|
+
}
|
|
17298
|
+
function countLiveHeadConflicts(db) {
|
|
17299
|
+
const row = db.prepare(
|
|
17300
|
+
`SELECT count(*) AS n FROM (
|
|
17301
|
+
SELECT COALESCE(logical_id, id) AS lid FROM nodes
|
|
17302
|
+
WHERE valid_to IS NULL AND ${NOT_HISTORICAL_ROW}
|
|
17303
|
+
GROUP BY lid HAVING count(*) > 1)`
|
|
17304
|
+
).get();
|
|
17305
|
+
return Number(row?.n ?? 0);
|
|
17306
|
+
}
|
|
17307
|
+
function closeIncidentEdges(db, id, t) {
|
|
17308
|
+
let closed = 0;
|
|
17309
|
+
for (const side of ["from_id", "to_id"]) {
|
|
17310
|
+
const r = stmt(db, `UPDATE edges SET valid_to = MAX(valid_from, ?) WHERE ${side} = ? AND valid_to IS NULL AND ${NOT_HISTORY}`).run(t, id);
|
|
17311
|
+
closed += Number(r.changes);
|
|
17312
|
+
}
|
|
17313
|
+
return closed;
|
|
17314
|
+
}
|
|
17315
|
+
function reopenCascadeClosedEdges(db, id, closedAt) {
|
|
17316
|
+
let reopened = 0;
|
|
17317
|
+
for (const [side, other] of [["from_id", "to_id"], ["to_id", "from_id"]]) {
|
|
17318
|
+
const r = stmt(
|
|
17319
|
+
db,
|
|
17320
|
+
`UPDATE edges SET valid_to = NULL
|
|
17321
|
+
WHERE ${side} = ? AND valid_to = ? AND ${NOT_HISTORY}
|
|
17322
|
+
AND EXISTS (SELECT 1 FROM nodes o WHERE o.id = edges.${other} AND o.valid_to IS NULL)`
|
|
17323
|
+
).run(id, closedAt);
|
|
17324
|
+
reopened += Number(r.changes);
|
|
17325
|
+
}
|
|
17326
|
+
return reopened;
|
|
17327
|
+
}
|
|
17328
|
+
function collapseLiveHeads(db, logicalIds, t) {
|
|
17329
|
+
const headsOf = db.prepare(
|
|
17330
|
+
`SELECT id, version, valid_from FROM (
|
|
17331
|
+
SELECT id, version, valid_from, attrs_json FROM nodes WHERE logical_id = ?1 AND valid_to IS NULL
|
|
17332
|
+
UNION
|
|
17333
|
+
SELECT id, version, valid_from, attrs_json FROM nodes WHERE id = ?1 AND logical_id IS NULL AND valid_to IS NULL
|
|
17334
|
+
) WHERE ${NOT_HISTORICAL_ROW}`
|
|
17335
|
+
);
|
|
17336
|
+
const continuesInto = db.prepare(
|
|
17337
|
+
`SELECT to_id FROM edges WHERE from_id = ? AND type = 'CONTINUES' AND valid_to IS NULL`
|
|
17338
|
+
);
|
|
17339
|
+
const rekeyHead = db.prepare(`UPDATE nodes SET logical_id = ?1 WHERE id = ?1`);
|
|
17340
|
+
const rekeyCopies = db.prepare(
|
|
17341
|
+
`UPDATE nodes SET logical_id = ?1 WHERE logical_id = ?2 AND substr(id, 1, length(?1) + 2) = ?1 || '@v'`
|
|
17342
|
+
);
|
|
17343
|
+
const lineageMembers = db.prepare(
|
|
17344
|
+
`SELECT id FROM nodes WHERE logical_id = ?1 UNION SELECT id FROM nodes WHERE id = ?1`
|
|
17345
|
+
);
|
|
17346
|
+
const closeContinues = db.prepare(
|
|
17347
|
+
`UPDATE edges SET valid_to = MAX(valid_from, ?) WHERE from_id = ? AND to_id = ? AND type = 'CONTINUES' AND valid_to IS NULL`
|
|
17348
|
+
);
|
|
17349
|
+
let split = 0;
|
|
17350
|
+
const queue = [...new Set(logicalIds)];
|
|
17351
|
+
for (let i = 0; i < queue.length; i++) {
|
|
17352
|
+
const lid = queue[i];
|
|
17353
|
+
const heads = headsOf.all(lid);
|
|
17354
|
+
if (heads.length < 2) continue;
|
|
17355
|
+
const members = new Set(lineageMembers.all(lid).map((r) => r.id));
|
|
17356
|
+
const splitOff = (h) => {
|
|
17357
|
+
for (const { to_id } of continuesInto.all(h.id)) {
|
|
17358
|
+
if (members.has(to_id)) closeContinues.run(t, h.id, to_id);
|
|
17359
|
+
}
|
|
17360
|
+
rekeyHead.run(h.id);
|
|
17361
|
+
rekeyCopies.run(h.id, lid);
|
|
17362
|
+
queue.push(h.id);
|
|
17363
|
+
split++;
|
|
17364
|
+
};
|
|
17365
|
+
const kept = new Set(heads.map((h) => h.id));
|
|
17366
|
+
for (const h of heads) {
|
|
17367
|
+
if (h.id === lid) continue;
|
|
17368
|
+
const refuted = continuesInto.all(h.id).some(
|
|
17369
|
+
(e) => e.to_id !== h.id && kept.has(e.to_id)
|
|
17370
|
+
);
|
|
17371
|
+
if (refuted && kept.size > 1) {
|
|
17372
|
+
splitOff(h);
|
|
17373
|
+
kept.delete(h.id);
|
|
17374
|
+
}
|
|
17375
|
+
}
|
|
17376
|
+
let remaining = heads.filter((h) => kept.has(h.id));
|
|
17377
|
+
if (remaining.length < 2) continue;
|
|
17378
|
+
remaining = [...remaining].sort(
|
|
17379
|
+
(a, b) => Number(b.id === lid) - Number(a.id === lid) || Number(b.version) - Number(a.version) || Number(b.valid_from) - Number(a.valid_from) || (a.id < b.id ? -1 : 1)
|
|
17380
|
+
);
|
|
17381
|
+
for (const h of remaining.slice(1)) splitOff(h);
|
|
17382
|
+
}
|
|
17383
|
+
return split;
|
|
17384
|
+
}
|
|
17385
|
+
function repairLineageIntegrity(db, now) {
|
|
17386
|
+
const historicalRowsClosed = Number(
|
|
17387
|
+
db.prepare(
|
|
17388
|
+
`UPDATE nodes SET valid_to = MAX(valid_from, COALESCE(
|
|
17389
|
+
(SELECT s.valid_from FROM edges s WHERE s.id = 'edge_superseded_' || nodes.id), ?))
|
|
17390
|
+
WHERE valid_to IS NULL AND json_type(attrs_json, '$.historical') = 'true'`
|
|
17391
|
+
).run(now).changes
|
|
17392
|
+
);
|
|
17393
|
+
const conflicted = db.prepare(
|
|
17394
|
+
`SELECT COALESCE(logical_id, id) AS lid FROM nodes
|
|
17395
|
+
WHERE valid_to IS NULL AND ${NOT_HISTORICAL_ROW}
|
|
17396
|
+
GROUP BY lid HAVING count(*) > 1`
|
|
17397
|
+
).all().map((r) => r.lid);
|
|
17398
|
+
const headsSplit = collapseLiveHeads(db, conflicted, now);
|
|
17399
|
+
const strandedEdgesClosed = Number(
|
|
17400
|
+
db.prepare(
|
|
17401
|
+
`UPDATE edges SET valid_to = MAX(valid_from, COALESCE(
|
|
17402
|
+
(SELECT min(v) FROM (
|
|
17403
|
+
SELECT f.valid_to AS v FROM nodes f WHERE f.id = edges.from_id AND NOT ${PRESENT("f")}
|
|
17404
|
+
UNION ALL
|
|
17405
|
+
SELECT t.valid_to FROM nodes t WHERE t.id = edges.to_id AND NOT ${PRESENT("t")})), ?))
|
|
17406
|
+
WHERE id IN (SELECT e.id FROM edges e WHERE ${STRANDED_EDGE_PREDICATE})`
|
|
17407
|
+
).run(now).changes
|
|
17408
|
+
);
|
|
17409
|
+
return { historicalRowsClosed, headsSplit, strandedEdgesClosed };
|
|
17410
|
+
}
|
|
17411
|
+
function installLineageIntegrityRepair(db, now = Date.now()) {
|
|
17412
|
+
if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(LINEAGE_INTEGRITY_KEY)) return null;
|
|
17413
|
+
return db.transaction(() => {
|
|
17414
|
+
const report = repairLineageIntegrity(db, now);
|
|
17415
|
+
db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, ?)").run(
|
|
17416
|
+
LINEAGE_INTEGRITY_KEY,
|
|
17417
|
+
JSON.stringify({ at: now, ...report })
|
|
17418
|
+
);
|
|
17419
|
+
return report;
|
|
17420
|
+
});
|
|
17421
|
+
}
|
|
17422
|
+
|
|
16511
17423
|
// ../../packages/math/src/constants.ts
|
|
16512
17424
|
var NEMORI_DEDUP_COSINE = 0.88;
|
|
16513
17425
|
|
|
@@ -16695,6 +17607,177 @@ function detectCommunitiesLeiden(input, opts = {}) {
|
|
|
16695
17607
|
var DEMAND_HOT = Math.log1p(10);
|
|
16696
17608
|
var DEMAND_WARM = Math.log1p(5);
|
|
16697
17609
|
|
|
17610
|
+
// ../../packages/local-graph/src/node-select.ts
|
|
17611
|
+
var SELECT_INDEXES = [
|
|
17612
|
+
"nodes_live_local_community",
|
|
17613
|
+
"nodes_live_fold_candidate",
|
|
17614
|
+
"nodes_live_resolved",
|
|
17615
|
+
"nodes_live_fix_candidate",
|
|
17616
|
+
"nodes_live_anchor_hint"
|
|
17617
|
+
];
|
|
17618
|
+
var EMBEDDING_DIM_SQL = "(CASE WHEN length(embedding_blob) >= 4 THEN length(embedding_blob) / 4 ELSE COALESCE(json_array_length(embedding_json), 0) END)";
|
|
17619
|
+
var KEY = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
17620
|
+
var COLUMNS = /* @__PURE__ */ new Set([
|
|
17621
|
+
"created_at",
|
|
17622
|
+
"last_updated_at",
|
|
17623
|
+
"page_rank",
|
|
17624
|
+
"created_at_seq",
|
|
17625
|
+
"last_reinforced_at_seq",
|
|
17626
|
+
"cumulative_hits",
|
|
17627
|
+
"extraction_confidence"
|
|
17628
|
+
]);
|
|
17629
|
+
function path(key) {
|
|
17630
|
+
if (!KEY.test(key)) throw new Error(`selectNodes: bad attr key ${key}`);
|
|
17631
|
+
return `'$.${key}'`;
|
|
17632
|
+
}
|
|
17633
|
+
function selectIndex(i) {
|
|
17634
|
+
if (!SELECT_INDEXES.includes(i)) throw new Error(`selectNodes: bad index ${i}`);
|
|
17635
|
+
return i;
|
|
17636
|
+
}
|
|
17637
|
+
function column(c) {
|
|
17638
|
+
if (!COLUMNS.has(c)) throw new Error(`selectNodes: bad column ${c}`);
|
|
17639
|
+
return c;
|
|
17640
|
+
}
|
|
17641
|
+
function compileNodeWhere(q) {
|
|
17642
|
+
if (q.labels.length === 0) throw new Error("selectNodes: no labels");
|
|
17643
|
+
const params = [...q.labels];
|
|
17644
|
+
const byId = (q.where ?? []).some((w) => "idIn" in w || "idPrefix" in w && w.idPrefix.length > 0);
|
|
17645
|
+
const unindexed = byId ? "+" : "";
|
|
17646
|
+
const conds = [`${unindexed}label IN (${q.labels.map(() => "?").join(", ")})`];
|
|
17647
|
+
if (!q.includeClosed) conds.push(`${unindexed}valid_to IS NULL`);
|
|
17648
|
+
for (const w of q.where ?? []) {
|
|
17649
|
+
if ("embedding" in w) {
|
|
17650
|
+
conds.push(`${EMBEDDING_DIM_SQL} ${w.embedding === "present" ? ">" : "="} 0`);
|
|
17651
|
+
} else if ("idPrefix" in w) {
|
|
17652
|
+
conds.push("substr(id, 1, ?) = ?");
|
|
17653
|
+
params.push(w.idPrefix.length, w.idPrefix);
|
|
17654
|
+
if (w.idPrefix.length > 0) {
|
|
17655
|
+
const last = w.idPrefix.charCodeAt(w.idPrefix.length - 1);
|
|
17656
|
+
conds.push("id >= ? AND id < ?");
|
|
17657
|
+
params.push(w.idPrefix, w.idPrefix.slice(0, -1) + String.fromCharCode(last + 1));
|
|
17658
|
+
}
|
|
17659
|
+
} else if ("idIn" in w) {
|
|
17660
|
+
if (w.idIn.length === 0) conds.push("0");
|
|
17661
|
+
else {
|
|
17662
|
+
conds.push(`id IN (${w.idIn.map(() => "?").join(", ")})`);
|
|
17663
|
+
params.push(...w.idIn);
|
|
17664
|
+
}
|
|
17665
|
+
} else if ("descriptionIn" in w) {
|
|
17666
|
+
if (w.descriptionIn.length === 0) conds.push("0");
|
|
17667
|
+
else {
|
|
17668
|
+
conds.push(`description IN (${w.descriptionIn.map(() => "?").join(", ")})`);
|
|
17669
|
+
params.push(...w.descriptionIn);
|
|
17670
|
+
}
|
|
17671
|
+
} else if ("column" in w) {
|
|
17672
|
+
const c = column(w.column);
|
|
17673
|
+
for (const [op, sym] of [["gte", ">="], ["gt", ">"], ["lte", "<="], ["lt", "<"]]) {
|
|
17674
|
+
const v = w[op];
|
|
17675
|
+
if (v !== void 0) {
|
|
17676
|
+
conds.push(`${c} ${sym} ?`);
|
|
17677
|
+
params.push(v);
|
|
17678
|
+
}
|
|
17679
|
+
}
|
|
17680
|
+
} else {
|
|
17681
|
+
const p = path(w.attr);
|
|
17682
|
+
if ("absent" in w) conds.push(`json_type(attrs_json, ${p}) IS NULL`);
|
|
17683
|
+
else if ("present" in w) conds.push(`json_type(attrs_json, ${p}) IS NOT NULL`);
|
|
17684
|
+
else if ("nullish" in w) conds.push(`json_extract(attrs_json, ${p}) IS NULL`);
|
|
17685
|
+
else if ("notNullish" in w) conds.push(`json_extract(attrs_json, ${p}) IS NOT NULL`);
|
|
17686
|
+
else if ("eq" in w || "ne" in w) {
|
|
17687
|
+
const v = "eq" in w ? w.eq : w.ne;
|
|
17688
|
+
const neg = "ne" in w;
|
|
17689
|
+
if (typeof v === "boolean") {
|
|
17690
|
+
conds.push(`json_type(attrs_json, ${p}) ${neg ? "IS NOT" : "="} ?`);
|
|
17691
|
+
params.push(v ? "true" : "false");
|
|
17692
|
+
} else {
|
|
17693
|
+
conds.push(neg ? `json_extract(attrs_json, ${p}) IS NOT ?` : `json_extract(attrs_json, ${p}) = ?`);
|
|
17694
|
+
params.push(v);
|
|
17695
|
+
}
|
|
17696
|
+
}
|
|
17697
|
+
}
|
|
17698
|
+
}
|
|
17699
|
+
return { where: conds.join(" AND "), params };
|
|
17700
|
+
}
|
|
17701
|
+
function attrValueSql(p, doc = "attrs_json") {
|
|
17702
|
+
return `CASE json_type(${doc}, ${p}) WHEN 'integer' THEN CAST(json_extract(${doc}, ${p}) AS REAL) ELSE json_extract(${doc}, ${p}) END`;
|
|
17703
|
+
}
|
|
17704
|
+
function attrDocSql(key) {
|
|
17705
|
+
return isExposureAttr(key) ? exposureDocSql(key) : "attrs_json";
|
|
17706
|
+
}
|
|
17707
|
+
function nodeSelectFrom(q) {
|
|
17708
|
+
if (q.indexedBy) return `nodes INDEXED BY ${selectIndex(q.indexedBy)}`;
|
|
17709
|
+
const where = q.where ?? [];
|
|
17710
|
+
if (!q.includeClosed && where.some((w) => "descriptionIn" in w && w.descriptionIn.length > 0) && !where.some((w) => "idIn" in w)) {
|
|
17711
|
+
return "nodes INDEXED BY nodes_live_description";
|
|
17712
|
+
}
|
|
17713
|
+
return "nodes";
|
|
17714
|
+
}
|
|
17715
|
+
function compileNodeSelect(q) {
|
|
17716
|
+
const { where, params } = compileNodeWhere(q);
|
|
17717
|
+
const attrs = q.attrs ?? [];
|
|
17718
|
+
const attrCols = attrs.map((k, i) => `, ${attrValueSql(path(k), attrDocSql(k))} AS a${i}, json_type(${attrDocSql(k)}, ${path(k)}) AS t${i}`).join("");
|
|
17719
|
+
let order;
|
|
17720
|
+
if (q.orderBy && "column" in q.orderBy) {
|
|
17721
|
+
order = `${column(q.orderBy.column)} ${q.orderBy.desc ? "DESC" : "ASC"}, rowid`;
|
|
17722
|
+
} else if (q.orderBy) {
|
|
17723
|
+
const p = path(q.orderBy.attr);
|
|
17724
|
+
order = `json_extract(attrs_json, ${p}) IS NULL, json_extract(attrs_json, ${p}) ${q.orderBy.desc ? "DESC" : "ASC"}, rowid`;
|
|
17725
|
+
} else if (q.labels.length > 1) {
|
|
17726
|
+
order = `CASE label ${q.labels.map((_, i) => `WHEN ? THEN ${i}`).join(" ")} END, rowid`;
|
|
17727
|
+
} else {
|
|
17728
|
+
order = "rowid";
|
|
17729
|
+
}
|
|
17730
|
+
const orderParams = !q.orderBy && q.labels.length > 1 ? [...q.labels] : [];
|
|
17731
|
+
const limit = q.limit !== void 0 ? ` LIMIT ${Math.max(0, Math.floor(q.limit))}` : "";
|
|
17732
|
+
const sql = `SELECT id, label, description, created_at, last_updated_at, page_rank, cumulative_hits, extraction_confidence, extraction_source, community, created_at_seq, last_reinforced_at_seq, valid_to, logical_id, version, ${EMBEDDING_DIM_SQL} AS embedding_dim${q.withEmbedding ? ", embedding_blob, embedding_json" : ""}${attrCols} FROM ${nodeSelectFrom(q)} WHERE ${where} ORDER BY ${order}${limit}`;
|
|
17733
|
+
return { sql, params: [...params, ...orderParams] };
|
|
17734
|
+
}
|
|
17735
|
+
function decodeAttr(value, type) {
|
|
17736
|
+
switch (type) {
|
|
17737
|
+
case null:
|
|
17738
|
+
case void 0:
|
|
17739
|
+
return void 0;
|
|
17740
|
+
case "true":
|
|
17741
|
+
return true;
|
|
17742
|
+
case "false":
|
|
17743
|
+
return false;
|
|
17744
|
+
case "null":
|
|
17745
|
+
return null;
|
|
17746
|
+
case "object":
|
|
17747
|
+
case "array":
|
|
17748
|
+
return typeof value === "string" ? JSON.parse(value) : value;
|
|
17749
|
+
default:
|
|
17750
|
+
return value;
|
|
17751
|
+
}
|
|
17752
|
+
}
|
|
17753
|
+
function decodeSelectedRow(r, attrs, decodeVector) {
|
|
17754
|
+
const out = {};
|
|
17755
|
+
attrs.forEach((k, i) => {
|
|
17756
|
+
const v = decodeAttr(r[`a${i}`], r[`t${i}`]);
|
|
17757
|
+
if (v !== void 0) out[k] = v;
|
|
17758
|
+
});
|
|
17759
|
+
return {
|
|
17760
|
+
id: r["id"],
|
|
17761
|
+
label: r["label"],
|
|
17762
|
+
description: r["description"] ?? "",
|
|
17763
|
+
createdAt: r["created_at"],
|
|
17764
|
+
lastUpdatedAt: r["last_updated_at"],
|
|
17765
|
+
pageRank: r["page_rank"],
|
|
17766
|
+
cumulativeHits: r["cumulative_hits"],
|
|
17767
|
+
extractionConfidence: r["extraction_confidence"],
|
|
17768
|
+
extractionSource: r["extraction_source"],
|
|
17769
|
+
community: r["community"] ?? null,
|
|
17770
|
+
...r["created_at_seq"] != null ? { createdAtSeq: r["created_at_seq"] } : {},
|
|
17771
|
+
...r["last_reinforced_at_seq"] != null ? { lastReinforcedAtSeq: r["last_reinforced_at_seq"] } : {},
|
|
17772
|
+
validTo: r["valid_to"] ?? null,
|
|
17773
|
+
logicalId: r["logical_id"] ?? r["id"],
|
|
17774
|
+
version: Number(r["version"] ?? 1),
|
|
17775
|
+
embeddingDim: Number(r["embedding_dim"] ?? 0),
|
|
17776
|
+
...decodeVector ? { embedding: decodeVector(r["embedding_blob"], r["embedding_json"] ?? "[]") } : {},
|
|
17777
|
+
attrs: out
|
|
17778
|
+
};
|
|
17779
|
+
}
|
|
17780
|
+
|
|
16698
17781
|
// ../../packages/local-graph/src/store.ts
|
|
16699
17782
|
var PAGE_RANK_SCALE_KEY = "pageRankScale.p95.v1";
|
|
16700
17783
|
var TEXT_SEARCH_COLUMNS = `id, label, description, page_rank,
|
|
@@ -16749,6 +17832,7 @@ function localEdgeViolation(fromLabel, type, toLabel) {
|
|
|
16749
17832
|
}
|
|
16750
17833
|
var SCHEMA_VERSION = 6;
|
|
16751
17834
|
var SESSION_REPAIR_KEY = "session-evidence-v1";
|
|
17835
|
+
var EXPOSURE_SIDE_KEY = "exposure-side-rows-1";
|
|
16752
17836
|
var SESSION_EVIDENCE_PREDICATE = SESSION_EVIDENCE_FIELDS.map((field) => `instr(attrs_json, '${JSON.stringify(field)}') > 0`).join(" OR ");
|
|
16753
17837
|
var SCHEMA_SQL = `
|
|
16754
17838
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -16812,8 +17896,13 @@ CREATE TABLE IF NOT EXISTS nodes (
|
|
|
16812
17896
|
);
|
|
16813
17897
|
|
|
16814
17898
|
CREATE INDEX IF NOT EXISTS nodes_label ON nodes(label);
|
|
16815
|
-
|
|
16816
|
-
|
|
17899
|
+
-- Dropped: no query plans through them (every prepared statement across the
|
|
17900
|
+
-- local-graph, indexer, context-writer, generalizer and daemon suites was
|
|
17901
|
+
-- planned with and without them, 2026-10-21), and each write that touches
|
|
17902
|
+
-- memory_tier / page_rank (every node upsert, every rescore) paid to maintain
|
|
17903
|
+
-- them. nodes_live_page_rank serves the live page-rank orderings.
|
|
17904
|
+
DROP INDEX IF EXISTS nodes_memory_tier;
|
|
17905
|
+
DROP INDEX IF EXISTS nodes_page_rank;
|
|
16817
17906
|
|
|
16818
17907
|
CREATE TABLE IF NOT EXISTS edges (
|
|
16819
17908
|
id TEXT PRIMARY KEY,
|
|
@@ -16835,7 +17924,10 @@ CREATE TABLE IF NOT EXISTS edges (
|
|
|
16835
17924
|
FOREIGN KEY (to_id) REFERENCES nodes(id) ON DELETE CASCADE
|
|
16836
17925
|
);
|
|
16837
17926
|
|
|
16838
|
-
|
|
17927
|
+
-- Dropped: a prefix of edges_triple (from_id, type, to_id), which every
|
|
17928
|
+
-- from-side lookup and foreign-key check already plans through; it cost 47 MB
|
|
17929
|
+
-- and an index write on every edge write.
|
|
17930
|
+
DROP INDEX IF EXISTS edges_from;
|
|
16839
17931
|
CREATE INDEX IF NOT EXISTS edges_to ON edges(to_id, type);
|
|
16840
17932
|
CREATE INDEX IF NOT EXISTS edges_type ON edges(type);
|
|
16841
17933
|
CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
|
|
@@ -16843,8 +17935,41 @@ CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
|
|
|
16843
17935
|
var TEMPORAL_INDEX_SQL = `
|
|
16844
17936
|
CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
|
|
16845
17937
|
CREATE INDEX IF NOT EXISTS nodes_live_count ON nodes(valid_to);
|
|
16846
|
-
|
|
17938
|
+
-- Problems by recency (the only label read newest-first). Over every live row,
|
|
17939
|
+
-- each write that stamps last_updated_at moved a code symbol's entry from a
|
|
17940
|
+
-- scattered leaf: ~19 KB per position-only symbol rewrite on a 1.1 GB store.
|
|
17941
|
+
-- label stays the leading column so label = 'Problem' plans without a sort.
|
|
17942
|
+
DROP INDEX IF EXISTS nodes_live_recent;
|
|
17943
|
+
CREATE INDEX IF NOT EXISTS nodes_live_problem_recent ON nodes(label, last_updated_at DESC) WHERE valid_to IS NULL AND label = 'Problem';
|
|
17944
|
+
-- "Anything created since the watermark?" guards (the fold, regression and
|
|
17945
|
+
-- canonical-bind sweeps run one per session boundary). Without it the planner
|
|
17946
|
+
-- walks nodes_live and reads every live row of the label to test created_at:
|
|
17947
|
+
-- 56 MB per guard on a 1.1 GB store, to learn there was nothing to do. valid_to
|
|
17948
|
+
-- is a key column so the planner prefers this over nodes_live (one more term).
|
|
17949
|
+
CREATE INDEX IF NOT EXISTS nodes_live_created ON nodes(label, valid_to, created_at) WHERE valid_to IS NULL;
|
|
17950
|
+
-- Community members (the skills pull reads them twice per session boundary):
|
|
17951
|
+
-- ~15% of semantic nodes carry a localCommunity, and without this every live
|
|
17952
|
+
-- semantic row was read to find them (168 MB \u2192 18 MB per read).
|
|
17953
|
+
CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to, json_extract(attrs_json, '$.localCommunity')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.localCommunity') IS NOT NULL;
|
|
17954
|
+
-- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
|
|
17955
|
+
-- Problems; without it every live Problem was read to find them (56 MB).
|
|
17956
|
+
CREATE INDEX IF NOT EXISTS nodes_live_fold_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.foldCandidate') IS NOT NULL;
|
|
17957
|
+
-- Render impressions, one row per node id (exposure-attrs.ts): a render bumps
|
|
17958
|
+
-- ~40 of these, which in the node row rewrote each whole row and its indexes.
|
|
17959
|
+
CREATE TABLE IF NOT EXISTS node_exposure (
|
|
17960
|
+
id TEXT PRIMARY KEY,
|
|
17961
|
+
attrs_json TEXT NOT NULL DEFAULT '{}'
|
|
17962
|
+
) WITHOUT ROWID;
|
|
17963
|
+
-- The semantic-maintenance sweeps' own filters (closed, asked, hinted): each read
|
|
17964
|
+
-- every Problem row (~30 MB on a 1.1 GB store) for the few hundred that match,
|
|
17965
|
+
-- several times a run, and a run follows most writes (2026-10-20).
|
|
17966
|
+
CREATE INDEX IF NOT EXISTS nodes_live_resolved ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.resolvedAt') IS NOT NULL;
|
|
17967
|
+
CREATE INDEX IF NOT EXISTS nodes_live_fix_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.fixCandidateAt') IS NOT NULL;
|
|
17968
|
+
CREATE INDEX IF NOT EXISTS nodes_live_anchor_hint ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.anchorHint') IS NOT NULL;
|
|
16847
17969
|
CREATE INDEX IF NOT EXISTS nodes_session_evidence ON nodes(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
17970
|
+
-- Rows carrying a calibration counter, any version: the logical nodes wm
|
|
17971
|
+
-- calibration can count (~3k), found without reading every citable row.
|
|
17972
|
+
CREATE INDEX IF NOT EXISTS nodes_wm_counted ON nodes(logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map(wmCounterSql).join(", ")}) WHERE ${WM_COUNTED_PREDICATE};
|
|
16848
17973
|
CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
16849
17974
|
-- Covering + partial index for labelFingerprint over the instance labels: the
|
|
16850
17975
|
-- live-push probe runs it per workspace on a timer, and each hit used to read
|
|
@@ -16858,31 +17983,70 @@ CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_up
|
|
|
16858
17983
|
CREATE INDEX IF NOT EXISTS nodes_live_page_rank ON nodes(valid_to, page_rank);
|
|
16859
17984
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
16860
17985
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
17986
|
+
-- Newest live edges first, so a "recent N matching edges" read stops early instead
|
|
17987
|
+
-- of matching and sorting every live edge (the hourly liveness check: 163 MB \u2192
|
|
17988
|
+
-- 69 MB). Named by recentEdgeAttrCoverage; the planner keeps edges_live otherwise.
|
|
17989
|
+
CREATE INDEX IF NOT EXISTS edges_live_created ON edges(created_at) WHERE valid_to IS NULL;
|
|
17990
|
+
-- Live prior-tag edges by recency: the liveness corroboration check reads the
|
|
17991
|
+
-- newest 100 of ~300, and walking all live edges newest-first to find them read
|
|
17992
|
+
-- 69 MB. Its predicate is the one recentEdgeAttrCoverage uses for that marker.
|
|
17993
|
+
CREATE INDEX IF NOT EXISTS edges_live_prior_tag ON edges(created_at) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.priorTag') = 1;
|
|
16861
17994
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
16862
17995
|
-- incremental reindex can fetch just the changed files' symbols instead of
|
|
16863
17996
|
-- scanning every versioned node. Partial: only live rows are ever queried.
|
|
16864
17997
|
CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.relPath')) WHERE valid_to IS NULL;
|
|
17998
|
+
-- Symbols by qname and doc comments by the symbol they attach to: the drain's
|
|
17999
|
+
-- symbol lexicon resolves the names in the text it ships one at a time, instead
|
|
18000
|
+
-- of reading every Function/Method/Class/Interface/Const and Comment row on each
|
|
18001
|
+
-- live push (~300 MB of a 1.1 GB store per push, 2026-10-20).
|
|
18002
|
+
CREATE INDEX IF NOT EXISTS nodes_live_qname ON nodes(json_extract(attrs_json, '$.qname')) WHERE valid_to IS NULL;
|
|
18003
|
+
CREATE INDEX IF NOT EXISTS nodes_live_attached_to ON nodes(json_extract(attrs_json, '$.attachedTo')) WHERE valid_to IS NULL AND label = 'Comment';
|
|
18004
|
+
-- The attrs the cloud drain gates every instance row on, so it reads them from
|
|
18005
|
+
-- the index alone: pulled cloud rows are most of these labels (7,105 of 7,708
|
|
18006
|
+
-- Solutions on the live store) at ~10 KB of attrs each, and five full scans for
|
|
18007
|
+
-- two attrs read ~220 MB per push to find the few local rows that can ship.
|
|
18008
|
+
-- created_at leads so the index order is findNodeFieldsByLabel's.
|
|
18009
|
+
CREATE INDEX IF NOT EXISTS nodes_live_instance_gate ON nodes(label, created_at, json_extract(attrs_json, '$.source'), json_extract(attrs_json, '$.privateFinding'), json_extract(attrs_json, '$.resolvedAs'), id) WHERE valid_to IS NULL AND label IN ('Problem', 'Vulnerability', 'Solution', 'RootCause');
|
|
18010
|
+
-- Closed rows by relPath, for reviveReobserved (every incremental reindex): the
|
|
18011
|
+
-- live-only index above cannot serve it, so it scanned the whole table (~275 MB
|
|
18012
|
+
-- of a 1.1 GB store per reindex, 2026-10-20).
|
|
18013
|
+
CREATE INDEX IF NOT EXISTS nodes_closed_relpath ON nodes(json_extract(attrs_json, '$.relPath'), last_updated_at) WHERE valid_to IS NOT NULL;
|
|
16865
18014
|
-- Live rows by name, for looking up the few symbols a statement mentions
|
|
16866
18015
|
-- instead of hydrating every code symbol per minted problem.
|
|
16867
18016
|
CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) WHERE valid_to IS NULL;
|
|
18017
|
+
-- The six single-attr lookups below are partial on the attr being present: each
|
|
18018
|
+
-- is read only by equality (or IN), which implies it. Keyed on NULL for the ~95%
|
|
18019
|
+
-- of rows that lack the attr, every write to such a row's attrs rewrote its NULL
|
|
18020
|
+
-- entry: ~25 KB per position-only symbol rewrite on a 1.1 GB store (2026-10-21).
|
|
18021
|
+
DROP INDEX IF EXISTS nodes_anchor_hint_path;
|
|
18022
|
+
DROP INDEX IF EXISTS nodes_command_signature;
|
|
18023
|
+
DROP INDEX IF EXISTS nodes_diag_command;
|
|
18024
|
+
DROP INDEX IF EXISTS nodes_lsp_rel_path;
|
|
18025
|
+
DROP INDEX IF EXISTS nodes_cloud_node_id;
|
|
18026
|
+
DROP INDEX IF EXISTS nodes_enrichment_pending;
|
|
16868
18027
|
-- Problems by the file their anchor hint names: the harvest upgrades hints for
|
|
16869
18028
|
-- every edited file of every turn, and a scan parses every Problem's attrs.
|
|
16870
|
-
CREATE INDEX IF NOT EXISTS
|
|
18029
|
+
CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path_v2 ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.anchorHint.path') IS NOT NULL;
|
|
16871
18030
|
-- Problems by the command / checker / file that opened them. The generalizer
|
|
16872
18031
|
-- looked these up per shell or tool EVENT by hydrating every Problem: ~12s of
|
|
16873
18032
|
-- every minute of daemon main-thread time on a 3.4k-Problem store (2026-10-05).
|
|
16874
|
-
CREATE INDEX IF NOT EXISTS
|
|
16875
|
-
CREATE INDEX IF NOT EXISTS
|
|
16876
|
-
CREATE INDEX IF NOT EXISTS
|
|
18033
|
+
CREATE INDEX IF NOT EXISTS nodes_command_signature_v2 ON nodes(json_extract(attrs_json, '$.commandSignature')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.commandSignature') IS NOT NULL;
|
|
18034
|
+
CREATE INDEX IF NOT EXISTS nodes_diag_command_v2 ON nodes(json_extract(attrs_json, '$.diagCommand')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.diagCommand') IS NOT NULL;
|
|
18035
|
+
CREATE INDEX IF NOT EXISTS nodes_lsp_rel_path_v2 ON nodes(json_extract(attrs_json, '$.lspRelPath')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.lspRelPath') IS NOT NULL;
|
|
16877
18036
|
-- Local nodes by the cloud twin their upload receipt recorded. Every burst /
|
|
16878
18037
|
-- search fusion folds the Network copy of the caller's own knowledge back onto
|
|
16879
18038
|
-- the local node; unindexed, the reverse lookup was a ~300ms attrs scan per call.
|
|
16880
|
-
CREATE INDEX IF NOT EXISTS
|
|
18039
|
+
CREATE INDEX IF NOT EXISTS nodes_cloud_node_id_v2 ON nodes(json_extract(attrs_json, '$.cloudNodeId')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.cloudNodeId') IS NOT NULL;
|
|
16881
18040
|
-- Nodes still waiting on a root-cause ask. The MCP server checks this on EVERY
|
|
16882
18041
|
-- tool response (the enrichment piggyback), so it must not parse every
|
|
16883
|
-
-- Solution's and Problem's attrs per call.
|
|
18042
|
+
-- Solution's and Problem's attrs per call. Any version: the Problem pull reads
|
|
16884
18043
|
-- closed versions too.
|
|
16885
|
-
CREATE INDEX IF NOT EXISTS
|
|
18044
|
+
CREATE INDEX IF NOT EXISTS nodes_enrichment_pending_v2 ON nodes(json_extract(attrs_json, '$.enrichmentPending')) WHERE json_extract(attrs_json, '$.enrichmentPending') IS NOT NULL;
|
|
18045
|
+
-- Revisit flags, any label, any version: listNeedsRevisit's as-of read runs on
|
|
18046
|
+
-- every context render and otherwise json-checks every row (10.3 s on a 324k-row
|
|
18047
|
+
-- store, to return zero flags). The predicate must stay textually identical to
|
|
18048
|
+
-- nodesAsOfWhereTrue's or the planner will not use it.
|
|
18049
|
+
CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_type(attrs_json, '$.revisit') = 'true';
|
|
16886
18050
|
`;
|
|
16887
18051
|
function encodeEmbedding(emb) {
|
|
16888
18052
|
if (!emb || emb.length === 0) return null;
|
|
@@ -16943,12 +18107,39 @@ function rowToEdge(r) {
|
|
|
16943
18107
|
}
|
|
16944
18108
|
var FINGERPRINT_INDEXED_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
|
|
16945
18109
|
var NODE_LABELS_CHUNK = 256;
|
|
18110
|
+
var NODE_UPSERT_COLUMNS = [
|
|
18111
|
+
"label",
|
|
18112
|
+
"description",
|
|
18113
|
+
"extraction_confidence",
|
|
18114
|
+
"embedding_json",
|
|
18115
|
+
"embedding_blob",
|
|
18116
|
+
"cumulative_surprise",
|
|
18117
|
+
"peak_surprise",
|
|
18118
|
+
"cumulative_hits",
|
|
18119
|
+
"last_updated_at",
|
|
18120
|
+
"memory_tier",
|
|
18121
|
+
"page_rank",
|
|
18122
|
+
"is_landmark",
|
|
18123
|
+
"community",
|
|
18124
|
+
"stability",
|
|
18125
|
+
"last_reinforced_at_seq",
|
|
18126
|
+
"attrs_json"
|
|
18127
|
+
];
|
|
18128
|
+
var EDGE_UPSERT_COLUMNS = ["confidence", "last_seen_at", "nav_successes", "nav_failures", "valid_to", "attrs_json"];
|
|
18129
|
+
function sameSqlValue(a, b) {
|
|
18130
|
+
if (a === b || a == null && b == null) return true;
|
|
18131
|
+
if (a instanceof Uint8Array && b instanceof Uint8Array) return Buffer.compare(a, b) === 0;
|
|
18132
|
+
return false;
|
|
18133
|
+
}
|
|
18134
|
+
var PRIOR_TAG_MARKER = '"priorTag":true';
|
|
16946
18135
|
var SqliteGraphStore = class {
|
|
16947
18136
|
db;
|
|
16948
18137
|
// Typed edge lookups build their SQL from the number of types asked for, and
|
|
16949
18138
|
// used to prepare it on every call: ~600ms of a 2.5s drain build on a live
|
|
16950
18139
|
// 225k-node store was statement compilation alone. Keyed by the SQL text.
|
|
16951
18140
|
typedEdgeStmts = /* @__PURE__ */ new Map();
|
|
18141
|
+
/** Narrow merge updates, keyed by table and the columns they set. */
|
|
18142
|
+
changedColumnStmts = /* @__PURE__ */ new Map();
|
|
16952
18143
|
stmts;
|
|
16953
18144
|
/** Unified graph-mutation counter (see GraphStore.mutationCount). Bumped on
|
|
16954
18145
|
* every node/edge upsert — code-index and capture alike — so the consolidation
|
|
@@ -16967,6 +18158,8 @@ var SqliteGraphStore = class {
|
|
|
16967
18158
|
initialPageRankScale;
|
|
16968
18159
|
constructor(opts) {
|
|
16969
18160
|
this.db = openDatabase(opts.path);
|
|
18161
|
+
this.db.exec("PRAGMA cache_size = -32768");
|
|
18162
|
+
this.db.exec("PRAGMA mmap_size = 2147483648");
|
|
16970
18163
|
this.db.exec(SCHEMA_SQL);
|
|
16971
18164
|
const versionRow = this.db.prepare("SELECT version FROM schema_version LIMIT 1").get();
|
|
16972
18165
|
if (!versionRow) {
|
|
@@ -16983,12 +18176,15 @@ var SqliteGraphStore = class {
|
|
|
16983
18176
|
installEditPlaceholderRetirement(this.db);
|
|
16984
18177
|
installCloudAttrCompaction(this.db);
|
|
16985
18178
|
installHistoryAttrSlim(this.db);
|
|
18179
|
+
installTextIndex(this.db);
|
|
16986
18180
|
if (!this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(SESSION_REPAIR_KEY)) {
|
|
16987
18181
|
this.db.transaction(() => {
|
|
16988
18182
|
this.repairSessionAliases();
|
|
16989
18183
|
this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, 'done')").run(SESSION_REPAIR_KEY);
|
|
16990
18184
|
});
|
|
16991
18185
|
}
|
|
18186
|
+
installLineageIntegrityRepair(this.db);
|
|
18187
|
+
installChangeFeed(this.db);
|
|
16992
18188
|
this.stmts = {
|
|
16993
18189
|
insertNode: this.db.prepare(`
|
|
16994
18190
|
INSERT INTO nodes (
|
|
@@ -17027,7 +18223,19 @@ var SqliteGraphStore = class {
|
|
|
17027
18223
|
last_reinforced_at_seq = excluded.last_reinforced_at_seq,
|
|
17028
18224
|
attrs_json = excluded.attrs_json
|
|
17029
18225
|
`),
|
|
17030
|
-
|
|
18226
|
+
nodeUpsertTarget: this.db.prepare(`SELECT rowid AS rid, ${NODE_UPSERT_COLUMNS.join(", ")} FROM nodes WHERE id = ?`),
|
|
18227
|
+
edgeUpsertTarget: this.db.prepare(`SELECT rowid AS rid, ${EDGE_UPSERT_COLUMNS.join(", ")} FROM edges WHERE from_id = ? AND type = ? AND to_id = ?`),
|
|
18228
|
+
getNode: this.db.prepare("SELECT nodes.*, (SELECT x.attrs_json FROM node_exposure x WHERE x.id = nodes.id) AS exposure_json FROM nodes WHERE id = ?"),
|
|
18229
|
+
exposureOf: this.db.prepare("SELECT attrs_json FROM node_exposure WHERE id = ?"),
|
|
18230
|
+
writeExposure: this.db.prepare("INSERT INTO node_exposure(id, attrs_json) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET attrs_json = excluded.attrs_json"),
|
|
18231
|
+
lastUpdatedOf: this.db.prepare("SELECT last_updated_at AS t, attrs_json FROM nodes WHERE id = ?"),
|
|
18232
|
+
labelOf: this.db.prepare("SELECT label FROM nodes WHERE id = ?"),
|
|
18233
|
+
nodeContent: this.db.prepare(
|
|
18234
|
+
"SELECT label, description, extraction_confidence, extraction_source, stability, valid_to, attrs_json FROM nodes WHERE id = ?"
|
|
18235
|
+
),
|
|
18236
|
+
edgeContent: this.db.prepare(
|
|
18237
|
+
"SELECT confidence, valid_to, attrs_json FROM edges WHERE from_id = ? AND type = ? AND to_id = ?"
|
|
18238
|
+
),
|
|
17031
18239
|
// Single-column fast paths for the nightly pass — writing 100k pageRank
|
|
17032
18240
|
// values through the 18-column COALESCE updateNode took ~30s; these are ~10x
|
|
17033
18241
|
// cheaper (one bound column, no COALESCE evaluation per row).
|
|
@@ -17184,6 +18392,7 @@ var SqliteGraphStore = class {
|
|
|
17184
18392
|
"INSERT INTO store_meta (key, value) VALUES ('ingest_seq', :v) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
17185
18393
|
)
|
|
17186
18394
|
};
|
|
18395
|
+
this.moveExposureToSideRows();
|
|
17187
18396
|
const seqRow = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
17188
18397
|
this.ingestSeq = seqRow ? Number(seqRow.value) : 0;
|
|
17189
18398
|
}
|
|
@@ -17199,6 +18408,93 @@ var SqliteGraphStore = class {
|
|
|
17199
18408
|
AND (t.id IS NULL OR t.valid_to IS NOT NULL)
|
|
17200
18409
|
`).all();
|
|
17201
18410
|
}
|
|
18411
|
+
strandedSymbolsForFiles(fileIds) {
|
|
18412
|
+
const out = [];
|
|
18413
|
+
for (let i = 0; i < fileIds.length; i += 500) {
|
|
18414
|
+
const slice = fileIds.slice(i, i + 500);
|
|
18415
|
+
if (slice.length === 0) continue;
|
|
18416
|
+
out.push(...this.db.prepare(`
|
|
18417
|
+
SELECT f.id AS fileId, e.to_id AS symbolId
|
|
18418
|
+
FROM nodes f CROSS JOIN edges e ON e.from_id = f.id
|
|
18419
|
+
LEFT JOIN nodes t ON t.id = e.to_id
|
|
18420
|
+
WHERE f.id IN (${slice.map(() => "?").join(",")}) AND f.label = 'File' AND f.valid_to IS NULL AND e.valid_to IS NULL
|
|
18421
|
+
AND e.type IN ('DEFINES', 'CONTAINS')
|
|
18422
|
+
AND (t.id IS NULL OR t.valid_to IS NOT NULL)
|
|
18423
|
+
`).all(...slice));
|
|
18424
|
+
}
|
|
18425
|
+
return out;
|
|
18426
|
+
}
|
|
18427
|
+
firstLiveByNameOrQname(labels, name) {
|
|
18428
|
+
if (labels.length === 0) return null;
|
|
18429
|
+
const inLabels = labels.map(() => "?").join(",");
|
|
18430
|
+
const probe = (index, match) => this.db.prepare(
|
|
18431
|
+
`SELECT rowid AS rid, created_at AS at, label, description, json_extract(attrs_json, '$.qname') AS qname FROM nodes INDEXED BY ${index}
|
|
18432
|
+
WHERE ${match} = ? AND label IN (${inLabels}) AND valid_to IS NULL`
|
|
18433
|
+
).all(name, ...labels);
|
|
18434
|
+
const rank = (r) => [labels.indexOf(r.label), r.at, r.rid];
|
|
18435
|
+
const before = (a, b) => a[0] < b[0] || a[0] === b[0] && (a[1] < b[1] || a[1] === b[1] && a[2] < b[2]);
|
|
18436
|
+
let best;
|
|
18437
|
+
for (const r of [...probe("nodes_live_description", "description"), ...probe("nodes_live_qname", "json_extract(attrs_json, '$.qname')")]) {
|
|
18438
|
+
if (!best || before(rank(r), rank(best))) best = r;
|
|
18439
|
+
}
|
|
18440
|
+
return best ? { label: best.label, description: best.description, qname: typeof best.qname === "string" ? best.qname : null } : null;
|
|
18441
|
+
}
|
|
18442
|
+
instanceGateRows(label) {
|
|
18443
|
+
return this.db.prepare(
|
|
18444
|
+
`SELECT id, json_extract(attrs_json, '$.source') AS source, json_extract(attrs_json, '$.privateFinding') AS privateFinding,
|
|
18445
|
+
json_extract(attrs_json, '$.resolvedAs') AS resolvedAs
|
|
18446
|
+
FROM nodes INDEXED BY nodes_live_instance_gate
|
|
18447
|
+
WHERE label IN ('Problem', 'Vulnerability', 'Solution', 'RootCause') AND label = ? AND valid_to IS NULL
|
|
18448
|
+
ORDER BY label, created_at, rowid`
|
|
18449
|
+
).all(label);
|
|
18450
|
+
}
|
|
18451
|
+
wmCalibrationRows(labels) {
|
|
18452
|
+
const counted = this.db.prepare(
|
|
18453
|
+
`SELECT rowid AS rid, logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map((k, i) => `${wmCounterSql(k)} AS c${i}`).join(", ")}
|
|
18454
|
+
FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`
|
|
18455
|
+
).all();
|
|
18456
|
+
const exposed = this.db.prepare(
|
|
18457
|
+
`SELECT n.rowid AS rid, n.logical_id, n.version, n.valid_to, n.label, x.attrs_json AS exposure
|
|
18458
|
+
FROM node_exposure x JOIN nodes n ON n.id = x.id`
|
|
18459
|
+
).all();
|
|
18460
|
+
const others = this.db.prepare(
|
|
18461
|
+
`SELECT rowid AS rid, logical_id, version, valid_to, label FROM nodes WHERE rowid IN (
|
|
18462
|
+
SELECT n.rowid FROM nodes AS n INDEXED BY nodes_logical
|
|
18463
|
+
WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
|
|
18464
|
+
UNION SELECT n2.logical_id FROM node_exposure x2 JOIN nodes n2 ON n2.id = x2.id)
|
|
18465
|
+
EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
|
|
18466
|
+
EXCEPT SELECT n3.rowid FROM node_exposure x3 JOIN nodes n3 ON n3.id = x3.id)`
|
|
18467
|
+
).all();
|
|
18468
|
+
const byRid = /* @__PURE__ */ new Map();
|
|
18469
|
+
for (const r of [...counted, ...others]) {
|
|
18470
|
+
const attrs = {};
|
|
18471
|
+
WM_CALIBRATION_KEYS.forEach((k, i) => {
|
|
18472
|
+
const v = r[`c${i}`];
|
|
18473
|
+
if (typeof v === "number") attrs[k] = v;
|
|
18474
|
+
});
|
|
18475
|
+
byRid.set(r.rid, { row: r, attrs });
|
|
18476
|
+
}
|
|
18477
|
+
for (const r of exposed) {
|
|
18478
|
+
const side = JSON.parse(r.exposure);
|
|
18479
|
+
const entry = byRid.get(r.rid) ?? { row: r, attrs: {} };
|
|
18480
|
+
for (const k of WM_CALIBRATION_KEYS) if (typeof side[k] === "number" && Number.isFinite(side[k])) entry.attrs[k] = side[k];
|
|
18481
|
+
byRid.set(r.rid, entry);
|
|
18482
|
+
}
|
|
18483
|
+
const order = new Map(labels.map((l, i) => [l, i]));
|
|
18484
|
+
return [...byRid.values()].filter(({ row }) => order.has(row.label)).sort((a, b) => order.get(a.row.label) - order.get(b.row.label) || a.row.rid - b.row.rid).map(({ row, attrs }) => ({ logicalId: row.logical_id, version: Number(row.version), validTo: row.valid_to ?? null, attrs }));
|
|
18485
|
+
}
|
|
18486
|
+
liveCommentsAttachedTo(name) {
|
|
18487
|
+
return this.db.prepare(
|
|
18488
|
+
`SELECT json_extract(attrs_json, '$.kind') AS kind, json_extract(attrs_json, '$.text') AS text, description FROM nodes INDEXED BY nodes_live_attached_to
|
|
18489
|
+
WHERE label = 'Comment' AND valid_to IS NULL AND json_extract(attrs_json, '$.attachedTo') = ? ORDER BY created_at, rowid`
|
|
18490
|
+
).all(name);
|
|
18491
|
+
}
|
|
18492
|
+
strandedEdges() {
|
|
18493
|
+
return countStrandedEdges(this.db);
|
|
18494
|
+
}
|
|
18495
|
+
liveHeadConflicts() {
|
|
18496
|
+
return countLiveHeadConflicts(this.db);
|
|
18497
|
+
}
|
|
17202
18498
|
fileIndexStates(ids) {
|
|
17203
18499
|
if (ids.length === 0) return [];
|
|
17204
18500
|
if (ids.length > 500) throw new RangeError("fileIndexStates accepts at most 500 ids per read");
|
|
@@ -17297,7 +18593,7 @@ var SqliteGraphStore = class {
|
|
|
17297
18593
|
}
|
|
17298
18594
|
mergeNode(node) {
|
|
17299
18595
|
this.mutations++;
|
|
17300
|
-
|
|
18596
|
+
const values = {
|
|
17301
18597
|
id: node.id,
|
|
17302
18598
|
label: node.label,
|
|
17303
18599
|
description: node.description,
|
|
@@ -17323,8 +18619,37 @@ var SqliteGraphStore = class {
|
|
|
17323
18619
|
logical_id: node.logicalId ?? node.id,
|
|
17324
18620
|
created_at_seq: node.createdAtSeq ?? null,
|
|
17325
18621
|
last_reinforced_at_seq: node.lastReinforcedAtSeq ?? null,
|
|
17326
|
-
attrs_json:
|
|
17327
|
-
}
|
|
18622
|
+
attrs_json: this.nodeAttrsJson(node.id, node.attrs ?? {})
|
|
18623
|
+
};
|
|
18624
|
+
const current = this.stmts.nodeUpsertTarget.get(node.id);
|
|
18625
|
+
if (current) this.writeChangedColumns("nodes", NODE_UPSERT_COLUMNS, current, values);
|
|
18626
|
+
else this.stmts.insertNode.run(values);
|
|
18627
|
+
}
|
|
18628
|
+
/** What the upsert's ON CONFLICT would write over `current`, setting only the
|
|
18629
|
+
* columns whose value moves (NODE_UPSERT_COLUMNS says why). */
|
|
18630
|
+
writeChangedColumns(table, columns2, current, next) {
|
|
18631
|
+
const changed = columns2.filter((c) => !sameSqlValue(current[c], next[c]));
|
|
18632
|
+
if (changed.length === 0) return;
|
|
18633
|
+
const key = `${table}:${changed.join(",")}`;
|
|
18634
|
+
let stmt2 = this.changedColumnStmts.get(key);
|
|
18635
|
+
if (!stmt2) {
|
|
18636
|
+
stmt2 = this.db.prepare(`UPDATE ${table} SET ${changed.map((c) => `${c} = :${c}`).join(", ")} WHERE rowid = :rid`);
|
|
18637
|
+
this.changedColumnStmts.set(key, stmt2);
|
|
18638
|
+
}
|
|
18639
|
+
stmt2.run({ rid: current["rid"], ...Object.fromEntries(changed.map((c) => [c, next[c]])) });
|
|
18640
|
+
}
|
|
18641
|
+
/** mergeNode, unless the live row already holds this content. Content is what
|
|
18642
|
+
* the columns a merge rewrites from the producer (label, description, attrs,
|
|
18643
|
+
* confidence, stability);
|
|
18644
|
+
* rank, community, landmark, surprise, hits, tier and the vector belong to
|
|
18645
|
+
* the passes that compute them, and a skipped write leaves them as they are
|
|
18646
|
+
* where a merge would have reset them to the producer's defaults. Returns
|
|
18647
|
+
* whether it wrote. */
|
|
18648
|
+
mergeNodeIfChanged(node) {
|
|
18649
|
+
const row = this.stmts.nodeContent.get(node.id);
|
|
18650
|
+
if (row && row.valid_to === null && row.label === node.label && row.description === node.description && row.extraction_confidence === node.extractionConfidence && row.stability === node.stability && !EXPOSURE_ATTRS.some((k) => node.attrs?.[k] !== void 0) && row.attrs_json === JSON.stringify(node.attrs ?? {})) return false;
|
|
18651
|
+
this.mergeNode(node);
|
|
18652
|
+
return true;
|
|
17328
18653
|
}
|
|
17329
18654
|
updateNode(id, patch) {
|
|
17330
18655
|
const hasEmbedding = patch.embedding !== void 0 && patch.embedding.length > 0;
|
|
@@ -17349,7 +18674,37 @@ var SqliteGraphStore = class {
|
|
|
17349
18674
|
logical_id: patch.logicalId ?? null,
|
|
17350
18675
|
created_at_seq: patch.createdAtSeq ?? null,
|
|
17351
18676
|
last_reinforced_at_seq: patch.lastReinforcedAtSeq ?? null,
|
|
17352
|
-
attrs_json: patch.attrs ?
|
|
18677
|
+
attrs_json: patch.attrs ? this.nodeAttrsJson(id, patch.attrs) : null
|
|
18678
|
+
});
|
|
18679
|
+
}
|
|
18680
|
+
/**
|
|
18681
|
+
* The node-row attrs of a write, after handing any exposure keys to the side
|
|
18682
|
+
* row (exposure-attrs.ts). `getNode` returns exposure merged into attrs, so a
|
|
18683
|
+
* read-modify-write carries it; kept in the row it would re-bloat the row and
|
|
18684
|
+
* drift from the side row. Unchanged exposure costs no write.
|
|
18685
|
+
*/
|
|
18686
|
+
nodeAttrsJson(id, attrs) {
|
|
18687
|
+
if (!EXPOSURE_ATTRS.some((k) => attrs[k] !== void 0)) return JSON.stringify(attrs);
|
|
18688
|
+
const current = this.stmts.exposureOf.get(id);
|
|
18689
|
+
const next = JSON.stringify({ ...current ? JSON.parse(current.attrs_json) : {}, ...pickExposure(attrs) });
|
|
18690
|
+
if (current?.attrs_json !== next) this.stmts.writeExposure.run(id, next);
|
|
18691
|
+
const rest = { ...attrs };
|
|
18692
|
+
for (const k of EXPOSURE_ATTRS) delete rest[k];
|
|
18693
|
+
return JSON.stringify(rest);
|
|
18694
|
+
}
|
|
18695
|
+
/** One-time: exposure keys written to node rows before the side table existed
|
|
18696
|
+
* move to it. Only rows carrying a counter can carry exposure, and those are
|
|
18697
|
+
* what nodes_wm_counted holds, so this reads the index, not the table. */
|
|
18698
|
+
moveExposureToSideRows() {
|
|
18699
|
+
if (this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(EXPOSURE_SIDE_KEY)) return;
|
|
18700
|
+
const rows = this.db.prepare(`SELECT id, attrs_json FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`).all();
|
|
18701
|
+
const move = this.db.prepare("UPDATE nodes SET attrs_json = ? WHERE id = ?");
|
|
18702
|
+
this.db.transaction(() => {
|
|
18703
|
+
for (const r of rows) {
|
|
18704
|
+
const stripped = this.nodeAttrsJson(r.id, JSON.parse(r.attrs_json));
|
|
18705
|
+
if (stripped !== r.attrs_json) move.run(stripped, r.id);
|
|
18706
|
+
}
|
|
18707
|
+
this.db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, 'done')").run(EXPOSURE_SIDE_KEY);
|
|
17353
18708
|
});
|
|
17354
18709
|
}
|
|
17355
18710
|
/** Fast single-column writes for the nightly pass (avoid the 18-column COALESCE
|
|
@@ -17360,26 +18715,30 @@ var SqliteGraphStore = class {
|
|
|
17360
18715
|
setLandmark(id, isLandmark) {
|
|
17361
18716
|
this.stmts.setLandmark.run({ id, v: isLandmark ? 1 : 0 });
|
|
17362
18717
|
}
|
|
17363
|
-
setLocalCommunity(id, community,
|
|
17364
|
-
this.stmts.setLocalCommunity.run({ id, v: community, col:
|
|
18718
|
+
setLocalCommunity(id, community, column2) {
|
|
18719
|
+
this.stmts.setLocalCommunity.run({ id, v: community, col: column2 ? 1 : 0 });
|
|
17365
18720
|
}
|
|
17366
18721
|
fieldStmts = /* @__PURE__ */ new Map();
|
|
17367
18722
|
findNodeFieldsByLabel(label, attrKeys) {
|
|
17368
18723
|
for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeFieldsByLabel: bad attr key ${k}`);
|
|
17369
18724
|
const cacheKey = attrKeys.join(",");
|
|
17370
|
-
let
|
|
17371
|
-
if (!
|
|
17372
|
-
const cols = attrKeys.map((k, i) => `,
|
|
17373
|
-
|
|
17374
|
-
|
|
18725
|
+
let stmt2 = this.fieldStmts.get(cacheKey);
|
|
18726
|
+
if (!stmt2) {
|
|
18727
|
+
const cols = attrKeys.map((k, i) => `, ${attrValueSql(`'$.${k}'`)} AS a${i}, json_type(attrs_json, '$.${k}') AS t${i}`).join("");
|
|
18728
|
+
stmt2 = this.db.prepare(
|
|
18729
|
+
// Ordered, not left to the planner: readers take "first row wins" (the
|
|
18730
|
+
// symbol lexicon's name winner and doc pick), and the order otherwise
|
|
18731
|
+
// followed whichever partial index the planner chose. nodes_live_created
|
|
18732
|
+
// serves this order without a sort.
|
|
18733
|
+
`SELECT id, description, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL ORDER BY created_at, rowid`
|
|
17375
18734
|
);
|
|
17376
|
-
this.fieldStmts.set(cacheKey,
|
|
18735
|
+
this.fieldStmts.set(cacheKey, stmt2);
|
|
17377
18736
|
}
|
|
17378
|
-
return
|
|
18737
|
+
return stmt2.all(label).map((r) => {
|
|
17379
18738
|
const attrs = {};
|
|
17380
18739
|
attrKeys.forEach((k, i) => {
|
|
17381
|
-
const v = r[`a${i}`];
|
|
17382
|
-
if (v !==
|
|
18740
|
+
const v = decodeAttr(r[`a${i}`], r[`t${i}`]);
|
|
18741
|
+
if (v !== void 0) attrs[k] = v;
|
|
17383
18742
|
});
|
|
17384
18743
|
return {
|
|
17385
18744
|
id: r["id"],
|
|
@@ -17390,6 +18749,15 @@ var SqliteGraphStore = class {
|
|
|
17390
18749
|
};
|
|
17391
18750
|
});
|
|
17392
18751
|
}
|
|
18752
|
+
selectNodes(q) {
|
|
18753
|
+
const { sql, params } = compileNodeSelect(q);
|
|
18754
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
18755
|
+
return rows.map((r) => decodeSelectedRow(r, q.attrs ?? [], q.withEmbedding ? decodeEmbedding : void 0));
|
|
18756
|
+
}
|
|
18757
|
+
countNodes(q) {
|
|
18758
|
+
const { where, params } = compileNodeWhere(q);
|
|
18759
|
+
return Number(this.db.prepare(`SELECT COUNT(*) AS n FROM ${nodeSelectFrom(q)} WHERE ${where}`).get(...params).n);
|
|
18760
|
+
}
|
|
17393
18761
|
setCommunity(id, community) {
|
|
17394
18762
|
this.stmts.setCommunity.run({ id, v: community });
|
|
17395
18763
|
}
|
|
@@ -17402,7 +18770,7 @@ var SqliteGraphStore = class {
|
|
|
17402
18770
|
return;
|
|
17403
18771
|
}
|
|
17404
18772
|
this.mutations++;
|
|
17405
|
-
|
|
18773
|
+
const values = {
|
|
17406
18774
|
id: edge.id,
|
|
17407
18775
|
from_id: edge.from,
|
|
17408
18776
|
to_id: edge.to,
|
|
@@ -17416,8 +18784,33 @@ var SqliteGraphStore = class {
|
|
|
17416
18784
|
valid_from: edge.validFrom ?? edge.createdAt,
|
|
17417
18785
|
valid_to: edge.validTo ?? null,
|
|
17418
18786
|
attrs_json: JSON.stringify(edge.attrs ?? {})
|
|
18787
|
+
};
|
|
18788
|
+
const current = this.stmts.edgeUpsertTarget.get(edge.from, edge.type, edge.to);
|
|
18789
|
+
if (!current) {
|
|
18790
|
+
this.stmts.insertEdge.run(values);
|
|
18791
|
+
return;
|
|
18792
|
+
}
|
|
18793
|
+
this.writeChangedColumns("edges", EDGE_UPSERT_COLUMNS, current, {
|
|
18794
|
+
...values,
|
|
18795
|
+
confidence: Math.max(current["confidence"], edge.confidence),
|
|
18796
|
+
valid_to: null
|
|
17419
18797
|
});
|
|
17420
18798
|
}
|
|
18799
|
+
/** mergeEdge, unless the live edge already holds this content: same attrs, and
|
|
18800
|
+
* a confidence the upsert's MAX would keep. Its navigation posteriors and
|
|
18801
|
+
* last-seen stamp stay as they are where a merge would have reset them. The
|
|
18802
|
+
* ontology gate runs first either way, so refusals are recorded as before.
|
|
18803
|
+
* Returns whether it wrote (or refused). */
|
|
18804
|
+
mergeEdgeIfChanged(edge) {
|
|
18805
|
+
if (!this.edgeRuleViolation(edge)) {
|
|
18806
|
+
const row = this.stmts.edgeContent.get(edge.from, edge.type, edge.to);
|
|
18807
|
+
if (row && row.valid_to === null && row.confidence >= edge.confidence && row.attrs_json === JSON.stringify(edge.attrs ?? {})) {
|
|
18808
|
+
return false;
|
|
18809
|
+
}
|
|
18810
|
+
}
|
|
18811
|
+
this.mergeEdge(edge);
|
|
18812
|
+
return true;
|
|
18813
|
+
}
|
|
17421
18814
|
/** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
|
|
17422
18815
|
* overlay consulted first. Returns the reason string on a documented-
|
|
17423
18816
|
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
@@ -17462,11 +18855,13 @@ var SqliteGraphStore = class {
|
|
|
17462
18855
|
}
|
|
17463
18856
|
}
|
|
17464
18857
|
edgeRuleViolation(edge) {
|
|
17465
|
-
return localEdgeViolation(
|
|
17466
|
-
|
|
17467
|
-
|
|
17468
|
-
|
|
17469
|
-
|
|
18858
|
+
return localEdgeViolation(this.labelOf(edge.from), edge.type, this.labelOf(edge.to));
|
|
18859
|
+
}
|
|
18860
|
+
/** An endpoint's label, any version, as `getNode(id)?.label` reads it. Every
|
|
18861
|
+
* edge write checks both endpoints, and hydrating them (attrs + vector) to
|
|
18862
|
+
* read one leading column was most of an incremental reindex's reads. */
|
|
18863
|
+
labelOf(id) {
|
|
18864
|
+
return this.stmts.labelOf.get(id)?.label;
|
|
17470
18865
|
}
|
|
17471
18866
|
updateEdge(id, patch) {
|
|
17472
18867
|
this.stmts.updateEdge.run({
|
|
@@ -17482,18 +18877,38 @@ var SqliteGraphStore = class {
|
|
|
17482
18877
|
}
|
|
17483
18878
|
getNode(id) {
|
|
17484
18879
|
const row = this.stmts.getNode.get(id);
|
|
17485
|
-
|
|
18880
|
+
if (!row) return null;
|
|
18881
|
+
const node = rowToNode(row);
|
|
18882
|
+
if (row.exposure_json) Object.assign(node.attrs, JSON.parse(row.exposure_json));
|
|
18883
|
+
return node;
|
|
18884
|
+
}
|
|
18885
|
+
/** A node's render exposure: the side row's keys over the node row's own
|
|
18886
|
+
* (exposure-attrs.ts), plus the node's `lastUpdatedAt` (the "shown since
|
|
18887
|
+
* touch" counter is relative to it) — without reading the node's attrs. */
|
|
18888
|
+
exposureOf(id) {
|
|
18889
|
+
const row = this.stmts.lastUpdatedOf.get(id);
|
|
18890
|
+
if (!row) return null;
|
|
18891
|
+
const side = this.stmts.exposureOf.get(id);
|
|
18892
|
+
return {
|
|
18893
|
+
attrs: { ...pickExposure(JSON.parse(row.attrs_json)), ...side ? JSON.parse(side.attrs_json) : {} },
|
|
18894
|
+
lastUpdatedAt: row.t
|
|
18895
|
+
};
|
|
18896
|
+
}
|
|
18897
|
+
/** Replace a node's exposure (only EXPOSURE_ATTRS keys belong here). Touches
|
|
18898
|
+
* neither the node row nor its indexes, nor the change feed. */
|
|
18899
|
+
writeExposure(id, attrs) {
|
|
18900
|
+
this.stmts.writeExposure.run(id, JSON.stringify(attrs));
|
|
17486
18901
|
}
|
|
17487
18902
|
nodeLabels(ids) {
|
|
17488
18903
|
const width = NODE_LABELS_CHUNK;
|
|
17489
|
-
const
|
|
18904
|
+
const stmt2 = this.typedEdgeStmt(
|
|
17490
18905
|
`SELECT id, label FROM nodes WHERE id IN (${Array.from({ length: width }, () => "?").join(",")})`
|
|
17491
18906
|
);
|
|
17492
18907
|
const labels = /* @__PURE__ */ new Map();
|
|
17493
18908
|
for (let i = 0; i < ids.length; i += width) {
|
|
17494
18909
|
const chunk = ids.slice(i, i + width);
|
|
17495
18910
|
while (chunk.length < width) chunk.push(null);
|
|
17496
|
-
for (const row of
|
|
18911
|
+
for (const row of stmt2.all(...chunk)) labels.set(row.id, row.label);
|
|
17497
18912
|
}
|
|
17498
18913
|
return labels;
|
|
17499
18914
|
}
|
|
@@ -17510,7 +18925,21 @@ var SqliteGraphStore = class {
|
|
|
17510
18925
|
* per project per request; the materializing form held the daemon's event
|
|
17511
18926
|
* loop for seconds at scale. */
|
|
17512
18927
|
countNodesByLabel(label) {
|
|
17513
|
-
return Number(this.stmts.countByLabel.get(label).n);
|
|
18928
|
+
return this.countAt(`label:${label}`, [nodeScope(label)], () => Number(this.stmts.countByLabel.get(label).n));
|
|
18929
|
+
}
|
|
18930
|
+
/** Counts asked for on every status poll (/health, `errata status`, the
|
|
18931
|
+
* dashboards): a live-node COUNT(*) reads the whole liveness index, ~3 MB per
|
|
18932
|
+
* call on a 1.1 GB store, to recount a set that moves only on a write. Each is
|
|
18933
|
+
* kept with the change-feed reading of the rows it counts — triggers bump it on
|
|
18934
|
+
* every write from any connection, so a hit is exact, not a TTL guess. */
|
|
18935
|
+
counts = /* @__PURE__ */ new Map();
|
|
18936
|
+
countAt(key, scopes, count) {
|
|
18937
|
+
const seq = readChangeSeq(this.db, scopes);
|
|
18938
|
+
const hit = this.counts.get(key);
|
|
18939
|
+
if (hit && hit.seq === seq) return hit.n;
|
|
18940
|
+
const n = count();
|
|
18941
|
+
this.counts.set(key, { seq, n });
|
|
18942
|
+
return n;
|
|
17514
18943
|
}
|
|
17515
18944
|
/** Live rows for a label with attrs and creation stamps only — for scans that
|
|
17516
18945
|
* read attrs and never the vector. `findNodesByLabel` decodes every embedding
|
|
@@ -17519,16 +18948,39 @@ var SqliteGraphStore = class {
|
|
|
17519
18948
|
findNodeStampsByLabel(label, attrKey) {
|
|
17520
18949
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
|
|
17521
18950
|
const rows = this.db.prepare(
|
|
17522
|
-
`SELECT id, created_at, created_at_seq, json_type(
|
|
18951
|
+
`SELECT id, created_at, created_at_seq, json_type(${attrDocSql(attrKey)}, '$.${attrKey}') IS NOT NULL AS has,
|
|
18952
|
+
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
17523
18953
|
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
17524
18954
|
).all(label);
|
|
17525
18955
|
return rows.map((r) => ({
|
|
17526
18956
|
id: r.id,
|
|
17527
18957
|
createdAt: r.created_at,
|
|
17528
18958
|
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
17529
|
-
has: r.has === 1
|
|
18959
|
+
has: r.has === 1,
|
|
18960
|
+
cloud: r.cloud === 1
|
|
17530
18961
|
}));
|
|
17531
18962
|
}
|
|
18963
|
+
findNodeStampsByLabelAttrs(label, attrKeys) {
|
|
18964
|
+
for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeStampsByLabelAttrs: bad attr key ${k}`);
|
|
18965
|
+
const keys = [...new Set(attrKeys)];
|
|
18966
|
+
const rows = this.db.prepare(
|
|
18967
|
+
`SELECT id, created_at, created_at_seq,
|
|
18968
|
+
${keys.map((k, i) => `json_type(${attrDocSql(k)}, '$.${k}') IS NOT NULL AS h${i}`).join(", ")}${keys.length ? "," : ""}
|
|
18969
|
+
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
18970
|
+
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
18971
|
+
).all(label);
|
|
18972
|
+
const out = new Map(keys.map((k) => [k, []]));
|
|
18973
|
+
for (const r of rows) {
|
|
18974
|
+
keys.forEach((k, i) => out.get(k).push({
|
|
18975
|
+
id: r.id,
|
|
18976
|
+
createdAt: r.created_at,
|
|
18977
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
18978
|
+
has: r[`h${i}`] === 1,
|
|
18979
|
+
cloud: r.cloud === 1
|
|
18980
|
+
}));
|
|
18981
|
+
}
|
|
18982
|
+
return out;
|
|
18983
|
+
}
|
|
17532
18984
|
findNodeAttrsByLabel(label) {
|
|
17533
18985
|
const rows = this.stmts.attrsByLabel.all(label);
|
|
17534
18986
|
return rows.map((r) => ({
|
|
@@ -17556,8 +19008,8 @@ var SqliteGraphStore = class {
|
|
|
17556
19008
|
after = rows[rows.length - 1].rid;
|
|
17557
19009
|
}
|
|
17558
19010
|
}
|
|
17559
|
-
|
|
17560
|
-
const rows = this.db.prepare("SELECT id FROM nodes WHERE label =
|
|
19011
|
+
recentProblemIds(limit) {
|
|
19012
|
+
const rows = this.db.prepare("SELECT id FROM nodes WHERE label = 'Problem' AND valid_to IS NULL ORDER BY last_updated_at DESC LIMIT ?").all(Math.max(0, limit));
|
|
17561
19013
|
return rows.map((r) => r.id);
|
|
17562
19014
|
}
|
|
17563
19015
|
recentOpenProblemCandidates(opts) {
|
|
@@ -17637,6 +19089,20 @@ var SqliteGraphStore = class {
|
|
|
17637
19089
|
}
|
|
17638
19090
|
searchTextCandidates(terms) {
|
|
17639
19091
|
if (terms.length === 0) return [];
|
|
19092
|
+
const indexed = terms.filter((t) => t.length >= TEXT_INDEX_MIN_TERM);
|
|
19093
|
+
if (indexed.length > 0 && textIndexReady(this.db)) {
|
|
19094
|
+
const short = terms.filter((t) => t.length < TEXT_INDEX_MIN_TERM).map((t) => t.toLowerCase());
|
|
19095
|
+
const rows2 = this.db.prepare(`WITH hits AS MATERIALIZED (
|
|
19096
|
+
SELECT t.rowid AS rid, COUNT(*) AS matched FROM json_each(:terms) q, nodes_text t
|
|
19097
|
+
WHERE t.description LIKE '%' || q.value || '%' GROUP BY t.rowid
|
|
19098
|
+
) SELECT ${TEXT_SEARCH_COLUMNS}, hits.matched FROM hits JOIN nodes ON nodes.rowid = hits.rid
|
|
19099
|
+
WHERE nodes.valid_to IS NULL ORDER BY page_rank DESC, last_updated_at DESC`).all({ terms: JSON.stringify(indexed) });
|
|
19100
|
+
return rows2.map((row) => {
|
|
19101
|
+
const text = row.description.toLowerCase();
|
|
19102
|
+
const extra = short.filter((t) => text.includes(t)).length;
|
|
19103
|
+
return { node: textSearchNode(row), matched: row.matched + extra };
|
|
19104
|
+
});
|
|
19105
|
+
}
|
|
17640
19106
|
const rows = this.db.prepare(`WITH matches AS MATERIALIZED (
|
|
17641
19107
|
SELECT n.id, COUNT(*) AS matched FROM nodes n INDEXED BY nodes_live_description, json_each(:terms) t
|
|
17642
19108
|
WHERE n.valid_to IS NULL AND n.description LIKE '%' || t.value || '%'
|
|
@@ -17645,6 +19111,18 @@ var SqliteGraphStore = class {
|
|
|
17645
19111
|
JOIN matches USING (id) ORDER BY page_rank DESC, last_updated_at DESC`).all({ terms: JSON.stringify(terms) });
|
|
17646
19112
|
return rows.map((row) => ({ node: textSearchNode(row), matched: row.matched }));
|
|
17647
19113
|
}
|
|
19114
|
+
commentAnchors(commentIds) {
|
|
19115
|
+
const out = /* @__PURE__ */ new Map();
|
|
19116
|
+
if (commentIds.length === 0) return out;
|
|
19117
|
+
const rows = this.db.prepare(`WITH a AS MATERIALIZED (
|
|
19118
|
+
SELECT q.value AS cid, COALESCE(
|
|
19119
|
+
(SELECT to_id FROM edges WHERE from_id = q.value AND type IN ('EXPLAINS', 'ANNOTATES') AND valid_to IS NULL LIMIT 1),
|
|
19120
|
+
(SELECT from_id FROM edges WHERE to_id = q.value AND type = 'DOCUMENTED_BY' AND valid_to IS NULL LIMIT 1)
|
|
19121
|
+
) AS aid FROM json_each(:ids) q
|
|
19122
|
+
) SELECT a.cid AS cid, ${TEXT_SEARCH_COLUMNS} FROM a JOIN nodes ON nodes.id = a.aid AND nodes.valid_to IS NULL`).all({ ids: JSON.stringify(commentIds) });
|
|
19123
|
+
for (const row of rows) out.set(row.cid, textSearchNode(row));
|
|
19124
|
+
return out;
|
|
19125
|
+
}
|
|
17648
19126
|
getTextSearchNode(id) {
|
|
17649
19127
|
const row = this.db.prepare(`SELECT ${TEXT_SEARCH_COLUMNS} FROM nodes WHERE id = ? AND valid_to IS NULL`).get(id);
|
|
17650
19128
|
return row ? textSearchNode(row) : null;
|
|
@@ -17664,12 +19142,12 @@ var SqliteGraphStore = class {
|
|
|
17664
19142
|
return scale;
|
|
17665
19143
|
}
|
|
17666
19144
|
typedEdgeStmt(sql) {
|
|
17667
|
-
let
|
|
17668
|
-
if (!
|
|
17669
|
-
|
|
17670
|
-
this.typedEdgeStmts.set(sql,
|
|
19145
|
+
let stmt2 = this.typedEdgeStmts.get(sql);
|
|
19146
|
+
if (!stmt2) {
|
|
19147
|
+
stmt2 = this.db.prepare(sql);
|
|
19148
|
+
this.typedEdgeStmts.set(sql, stmt2);
|
|
17671
19149
|
}
|
|
17672
|
-
return
|
|
19150
|
+
return stmt2;
|
|
17673
19151
|
}
|
|
17674
19152
|
outEdges(nodeId, types) {
|
|
17675
19153
|
if (!types || types.length === 0) {
|
|
@@ -17728,21 +19206,54 @@ var SqliteGraphStore = class {
|
|
|
17728
19206
|
return out;
|
|
17729
19207
|
}
|
|
17730
19208
|
nodeCount() {
|
|
17731
|
-
return Number(this.stmts.nodeCount.get().n);
|
|
19209
|
+
return this.countAt("nodes", [ANY_CHANGE], () => Number(this.stmts.nodeCount.get().n));
|
|
17732
19210
|
}
|
|
17733
19211
|
mutationCount() {
|
|
17734
19212
|
return this.mutations;
|
|
17735
19213
|
}
|
|
19214
|
+
changeStamp() {
|
|
19215
|
+
const { data_version } = this.db.pragma("data_version");
|
|
19216
|
+
const { n } = this.db.prepare("SELECT total_changes() AS n").get();
|
|
19217
|
+
return `${data_version}:${n}`;
|
|
19218
|
+
}
|
|
19219
|
+
changeSeq(scopes) {
|
|
19220
|
+
return readChangeSeq(this.db, scopes);
|
|
19221
|
+
}
|
|
19222
|
+
lineageChangedFiles(sinceSeq) {
|
|
19223
|
+
return readLineageChangedFiles(this.db, sinceSeq);
|
|
19224
|
+
}
|
|
19225
|
+
changedNodeIds(sinceSeq, labels) {
|
|
19226
|
+
return readChangedNodeIds(this.db, sinceSeq, labels);
|
|
19227
|
+
}
|
|
19228
|
+
changedAmong(sinceSeq, ids) {
|
|
19229
|
+
return readChangedAmong(this.db, sinceSeq, ids);
|
|
19230
|
+
}
|
|
19231
|
+
pruneChangeLog() {
|
|
19232
|
+
return pruneChangeLog(this.db);
|
|
19233
|
+
}
|
|
19234
|
+
fileRowStates(ids) {
|
|
19235
|
+
if (ids.length === 0) return [];
|
|
19236
|
+
if (ids.length > 500) throw new RangeError("fileRowStates accepts at most 500 ids per read");
|
|
19237
|
+
return this.db.prepare(`SELECT id, last_updated_at AS lastUpdatedAt, valid_to AS validTo FROM nodes WHERE id IN (${ids.map(() => "?").join(",")})`).all(...ids);
|
|
19238
|
+
}
|
|
19239
|
+
// Read and advanced in SQL, never from a per-connection copy: pass workers and
|
|
19240
|
+
// MCP processes open their own connections and advance the same row, so a
|
|
19241
|
+
// copy loaded at open went stale, and `copy + 1` wrote a duplicate or a
|
|
19242
|
+
// backwards seq from whichever connection had fallen behind.
|
|
17736
19243
|
currentIngestSeq() {
|
|
19244
|
+
const row = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
19245
|
+
this.ingestSeq = row ? Number(row.value) : 0;
|
|
17737
19246
|
return this.ingestSeq;
|
|
17738
19247
|
}
|
|
17739
19248
|
nextIngestSeq() {
|
|
17740
|
-
this.
|
|
17741
|
-
|
|
19249
|
+
const row = this.db.prepare(
|
|
19250
|
+
"INSERT INTO store_meta (key, value) VALUES ('ingest_seq', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(value AS INTEGER) + 1 RETURNING value"
|
|
19251
|
+
).get();
|
|
19252
|
+
this.ingestSeq = Number(row.value);
|
|
17742
19253
|
return this.ingestSeq;
|
|
17743
19254
|
}
|
|
17744
19255
|
edgeCount() {
|
|
17745
|
-
return Number(this.stmts.edgeCount.get().n);
|
|
19256
|
+
return this.countAt("edges", [ANY_CHANGE], () => Number(this.stmts.edgeCount.get().n));
|
|
17746
19257
|
}
|
|
17747
19258
|
// Lightweight whole-graph scans — select only the columns the caller needs so a
|
|
17748
19259
|
// 100k-node pass skips the per-row attrs JSON.parse + embedding decode rowToNode
|
|
@@ -17870,28 +19381,63 @@ var SqliteGraphStore = class {
|
|
|
17870
19381
|
}
|
|
17871
19382
|
}
|
|
17872
19383
|
reviveReobserved(relPaths, workspaceId, since) {
|
|
17873
|
-
|
|
19384
|
+
const revived = [];
|
|
17874
19385
|
const CHUNK = 400;
|
|
17875
19386
|
for (let i = 0; i < relPaths.length; i += CHUNK) {
|
|
17876
19387
|
const slice = relPaths.slice(i, i + CHUNK);
|
|
17877
19388
|
if (slice.length === 0) continue;
|
|
17878
|
-
const
|
|
17879
|
-
`
|
|
19389
|
+
const rows = this.db.prepare(
|
|
19390
|
+
`SELECT id, valid_to AS closedAt, COALESCE(logical_id, id) AS lid FROM nodes
|
|
17880
19391
|
WHERE valid_to IS NOT NULL
|
|
17881
19392
|
AND last_updated_at >= ?
|
|
17882
19393
|
AND json_extract(attrs_json, '$.workspaceId') = ?
|
|
17883
|
-
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})
|
|
17884
|
-
|
|
17885
|
-
|
|
19394
|
+
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})
|
|
19395
|
+
AND json_type(attrs_json, '$.historical') IS NOT 'true'`
|
|
19396
|
+
).all(since, workspaceId, ...slice);
|
|
19397
|
+
revived.push(...rows);
|
|
17886
19398
|
}
|
|
17887
|
-
if (revived
|
|
17888
|
-
|
|
19399
|
+
if (revived.length === 0) return 0;
|
|
19400
|
+
const reopen = this.db.prepare("UPDATE nodes SET valid_to = NULL WHERE id = ? AND valid_to IS NOT NULL");
|
|
19401
|
+
for (const r of revived) reopen.run(r.id);
|
|
19402
|
+
for (const r of revived) reopenCascadeClosedEdges(this.db, r.id, Number(r.closedAt));
|
|
19403
|
+
collapseLiveHeads(this.db, revived.map((r) => r.lid), since);
|
|
19404
|
+
this.mutations++;
|
|
19405
|
+
return revived.length;
|
|
19406
|
+
}
|
|
19407
|
+
incomingEdgeCoverage(label, edgeTypes, cloudOnly) {
|
|
19408
|
+
if (edgeTypes.length === 0) return { count: 0, total: 0 };
|
|
19409
|
+
const r = this.db.prepare(
|
|
19410
|
+
`SELECT COUNT(*) AS total,
|
|
19411
|
+
COALESCE(SUM(CASE WHEN EXISTS (
|
|
19412
|
+
SELECT 1 FROM edges e WHERE e.to_id = n.id AND e.valid_to IS NULL
|
|
19413
|
+
AND e.type IN (${edgeTypes.map(() => "?").join(",")})
|
|
19414
|
+
) THEN 1 ELSE 0 END), 0) AS count
|
|
19415
|
+
FROM nodes n
|
|
19416
|
+
WHERE n.label = ? AND n.valid_to IS NULL
|
|
19417
|
+
AND (? = 0 OR json_extract(n.attrs_json, '$.source') = 'cloud')`
|
|
19418
|
+
).get(...edgeTypes, label, cloudOnly ? 1 : 0);
|
|
19419
|
+
return { count: Number(r.count), total: Number(r.total) };
|
|
19420
|
+
}
|
|
19421
|
+
liveEdgeTargets(edgeTypes) {
|
|
19422
|
+
if (edgeTypes.length === 0) return [];
|
|
19423
|
+
const rows = this.db.prepare(`SELECT DISTINCT to_id FROM edges INDEXED BY edges_type WHERE type IN (${edgeTypes.map(() => "?").join(",")}) AND valid_to IS NULL`).all(...edgeTypes);
|
|
19424
|
+
return rows.map((r) => r.to_id);
|
|
17889
19425
|
}
|
|
17890
19426
|
recentEdgeAttrCoverage(marker, attr, limit) {
|
|
19427
|
+
if (marker === PRIOR_TAG_MARKER) {
|
|
19428
|
+
const r2 = this.db.prepare(
|
|
19429
|
+
`SELECT COUNT(*) AS total,
|
|
19430
|
+
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
19431
|
+
FROM (SELECT attrs_json FROM edges INDEXED BY edges_live_prior_tag
|
|
19432
|
+
WHERE valid_to IS NULL AND json_extract(attrs_json, '$.priorTag') = 1
|
|
19433
|
+
ORDER BY created_at DESC LIMIT ?)`
|
|
19434
|
+
).get(attr, limit);
|
|
19435
|
+
return { count: Number(r2.count), total: Number(r2.total) };
|
|
19436
|
+
}
|
|
17891
19437
|
const r = this.db.prepare(
|
|
17892
19438
|
`SELECT COUNT(*) AS total,
|
|
17893
19439
|
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
17894
|
-
FROM (SELECT attrs_json FROM edges
|
|
19440
|
+
FROM (SELECT attrs_json FROM edges INDEXED BY edges_live_created
|
|
17895
19441
|
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
17896
19442
|
ORDER BY created_at DESC LIMIT ?)`
|
|
17897
19443
|
).get(attr, `%${marker}%`, limit);
|
|
@@ -17904,7 +19450,7 @@ var SqliteGraphStore = class {
|
|
|
17904
19450
|
const slice = cloudIds.slice(i, i + CHUNK);
|
|
17905
19451
|
if (slice.length === 0) continue;
|
|
17906
19452
|
const rows = this.db.prepare(
|
|
17907
|
-
`SELECT id, json_extract(attrs_json, '$.cloudNodeId') AS cloud FROM nodes INDEXED BY
|
|
19453
|
+
`SELECT id, json_extract(attrs_json, '$.cloudNodeId') AS cloud FROM nodes INDEXED BY nodes_cloud_node_id_v2 WHERE json_extract(attrs_json, '$.cloudNodeId') IN (${slice.map(() => "?").join(",")}) AND valid_to IS NULL ORDER BY id`
|
|
17908
19454
|
).all(...slice);
|
|
17909
19455
|
for (const r of rows) out.set(r.cloud, [...out.get(r.cloud) ?? [], r.id]);
|
|
17910
19456
|
}
|
|
@@ -18017,8 +19563,9 @@ var SqliteGraphStore = class {
|
|
|
18017
19563
|
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
18018
19564
|
return frozenId;
|
|
18019
19565
|
}
|
|
18020
|
-
closeNode(id, t) {
|
|
18021
|
-
this.stmts.closeNode.run({ id, t });
|
|
19566
|
+
closeNode(id, t, opts = {}) {
|
|
19567
|
+
const r = this.stmts.closeNode.run({ id, t });
|
|
19568
|
+
if (Number(r.changes) > 0 && !opts.retainEdges) closeIncidentEdges(this.db, id, t);
|
|
18022
19569
|
}
|
|
18023
19570
|
closeEdge(id, t) {
|
|
18024
19571
|
this.stmts.closeEdge.run({ id, t });
|
|
@@ -18038,9 +19585,11 @@ var SqliteGraphStore = class {
|
|
|
18038
19585
|
* so it must be a plain identifier. */
|
|
18039
19586
|
nodesAsOfWhereTrue(t, attr) {
|
|
18040
19587
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attr)) throw new Error(`nodesAsOfWhereTrue: bad attr ${JSON.stringify(attr)}`);
|
|
19588
|
+
const indexed = attr === "revisit" ? " INDEXED BY nodes_revisit_true" : "";
|
|
18041
19589
|
const rows = this.db.prepare(
|
|
18042
|
-
`SELECT * FROM nodes WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
18043
|
-
AND json_type(attrs_json, '$.${attr}') = 'true'
|
|
19590
|
+
`SELECT * FROM nodes${indexed} WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
19591
|
+
AND json_type(attrs_json, '$.${attr}') = 'true'
|
|
19592
|
+
ORDER BY rowid`
|
|
18044
19593
|
).all({ t });
|
|
18045
19594
|
return rows.map(rowToNode);
|
|
18046
19595
|
}
|
|
@@ -18100,6 +19649,7 @@ init_src();
|
|
|
18100
19649
|
// ../../packages/local-graph/src/design-problem.ts
|
|
18101
19650
|
init_src();
|
|
18102
19651
|
var RECURRENCE_DEBOUNCE_MS = 60 * 60 * 1e3;
|
|
19652
|
+
var EXPOSURE_LOG_KEYS = ["exposureLog", "exposureLogSince", "exposureLogStart", "exposureTrimmed", "exposureFirst"];
|
|
18103
19653
|
|
|
18104
19654
|
// ../../packages/local-graph/src/intent.ts
|
|
18105
19655
|
init_src();
|
|
@@ -18160,7 +19710,7 @@ function percolate(l1, l2, opts) {
|
|
|
18160
19710
|
validFrom: opts.ts,
|
|
18161
19711
|
validTo: null,
|
|
18162
19712
|
attrs: {
|
|
18163
|
-
...n.attrs,
|
|
19713
|
+
...withoutExposureLog(n.attrs),
|
|
18164
19714
|
abstractionLevel: ABSTRACTION_LEVEL.CONCRETE,
|
|
18165
19715
|
observedInProjects: [ws]
|
|
18166
19716
|
}
|
|
@@ -18219,6 +19769,11 @@ function percolate(l1, l2, opts) {
|
|
|
18219
19769
|
result.touched = [...touched];
|
|
18220
19770
|
return result;
|
|
18221
19771
|
}
|
|
19772
|
+
function withoutExposureLog(attrs) {
|
|
19773
|
+
const out = { ...attrs };
|
|
19774
|
+
for (const k of EXPOSURE_LOG_KEYS) delete out[k];
|
|
19775
|
+
return out;
|
|
19776
|
+
}
|
|
18222
19777
|
|
|
18223
19778
|
// ../../packages/local-graph/src/abstraction.ts
|
|
18224
19779
|
init_src();
|
|
@@ -18370,32 +19925,35 @@ function projectsOf(attrs) {
|
|
|
18370
19925
|
function induceTriage(l2, opts) {
|
|
18371
19926
|
const report = { episodes: 0, observations: 0, skippedRefinement: 0 };
|
|
18372
19927
|
const episodes = [];
|
|
18373
|
-
|
|
18374
|
-
|
|
18375
|
-
|
|
18376
|
-
|
|
18377
|
-
|
|
18378
|
-
|
|
18379
|
-
|
|
18380
|
-
|
|
18381
|
-
|
|
18382
|
-
|
|
18383
|
-
|
|
18384
|
-
|
|
18385
|
-
|
|
18386
|
-
|
|
18387
|
-
|
|
18388
|
-
|
|
18389
|
-
|
|
18390
|
-
|
|
18391
|
-
|
|
18392
|
-
|
|
18393
|
-
|
|
18394
|
-
|
|
18395
|
-
|
|
18396
|
-
|
|
18397
|
-
|
|
18398
|
-
|
|
19928
|
+
const sources = l2.selectNodes({
|
|
19929
|
+
labels: ["Problem", "Solution", "Claim"],
|
|
19930
|
+
attrs: ["resolvedAs", INDUCTED_MARK, "observedInProjects"]
|
|
19931
|
+
});
|
|
19932
|
+
for (const n of sources) {
|
|
19933
|
+
const label = n.label;
|
|
19934
|
+
if (label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) {
|
|
19935
|
+
continue;
|
|
19936
|
+
}
|
|
19937
|
+
if (label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.INVALID && n.attrs[INDUCTED_MARK] !== true) {
|
|
19938
|
+
episodes.push({
|
|
19939
|
+
assumedId: n.id,
|
|
19940
|
+
actualId: `dcause_dissolve_${n.id}`.slice(0, 56),
|
|
19941
|
+
driftKind: DRIFT_KIND.DISSOLUTION,
|
|
19942
|
+
projects: projectsOf(n.attrs),
|
|
19943
|
+
dissolution: true,
|
|
19944
|
+
markNode: n.id
|
|
19945
|
+
});
|
|
19946
|
+
}
|
|
19947
|
+
for (const e of l2.outEdges(n.id)) {
|
|
19948
|
+
if (e.attrs["reframe"] !== true || e.attrs[INDUCTED_MARK] === true) continue;
|
|
19949
|
+
episodes.push({
|
|
19950
|
+
assumedId: String(e.attrs["assumed"] ?? e.from),
|
|
19951
|
+
actualId: String(e.attrs["actual"] ?? e.to),
|
|
19952
|
+
driftKind: String(e.attrs["driftKind"] ?? ""),
|
|
19953
|
+
projects: projectsOf(e.attrs),
|
|
19954
|
+
dissolution: false,
|
|
19955
|
+
markEdge: e.id
|
|
19956
|
+
});
|
|
18399
19957
|
}
|
|
18400
19958
|
}
|
|
18401
19959
|
for (const ep of episodes) {
|
|
@@ -18446,7 +20004,7 @@ function revisitStaleRoutes(l2, ts, opts = {}) {
|
|
|
18446
20004
|
const minNavFailures = opts.minNavFailures ?? 3;
|
|
18447
20005
|
const report = { flagged: 0, demoted: 0 };
|
|
18448
20006
|
l2.transaction(() => {
|
|
18449
|
-
for (const tri of l2.
|
|
20007
|
+
for (const tri of l2.selectNodes({ labels: ["Triage"] })) {
|
|
18450
20008
|
for (const e of l2.outEdges(tri.id, [...ROUTE_EDGE_TYPES])) {
|
|
18451
20009
|
if (e.attrs["revisit"] === true) continue;
|
|
18452
20010
|
let reason = "";
|
|
@@ -18552,6 +20110,7 @@ function mergeGeneralizes(store, principleId, memberId, ts) {
|
|
|
18552
20110
|
attrs: { provisional: true }
|
|
18553
20111
|
});
|
|
18554
20112
|
}
|
|
20113
|
+
var CONCRETE_MEMBER_KEYS = ["retiredReason", "abstractionLevel", "resolvedAs", "rationale"];
|
|
18555
20114
|
function induceAbstractions(l2, opts) {
|
|
18556
20115
|
const K = opts.minClusterSize ?? DEFAULT_MIN_CLUSTER_SIZE;
|
|
18557
20116
|
const minCtx = opts.minDistinctContexts ?? DEFAULT_MIN_DISTINCT_CONTEXTS;
|
|
@@ -18564,15 +20123,13 @@ function induceAbstractions(l2, opts) {
|
|
|
18564
20123
|
generalizesEdges: 0
|
|
18565
20124
|
};
|
|
18566
20125
|
const ids = /* @__PURE__ */ new Set();
|
|
18567
|
-
for (const
|
|
18568
|
-
|
|
18569
|
-
|
|
18570
|
-
|
|
18571
|
-
continue;
|
|
18572
|
-
}
|
|
18573
|
-
if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
|
|
18574
|
-
ids.add(n.id);
|
|
20126
|
+
for (const n of l2.selectNodes({ labels: PERCOLATING_LABELS, attrs: CONCRETE_MEMBER_KEYS })) {
|
|
20127
|
+
if (isEditPlaceholderSolution(n) || n.attrs["retiredReason"] || n.validTo != null) continue;
|
|
20128
|
+
if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) >= ABSTRACTION_LEVEL.PRINCIPLE) {
|
|
20129
|
+
continue;
|
|
18575
20130
|
}
|
|
20131
|
+
if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
|
|
20132
|
+
ids.add(n.id);
|
|
18576
20133
|
}
|
|
18577
20134
|
if (ids.size === 0) return report;
|
|
18578
20135
|
const adj = /* @__PURE__ */ new Map();
|
|
@@ -18640,7 +20197,7 @@ function induceAbstractions(l2, opts) {
|
|
|
18640
20197
|
function revisitContradictedPrinciples(l2, ts) {
|
|
18641
20198
|
const report = { flagged: 0 };
|
|
18642
20199
|
l2.transaction(() => {
|
|
18643
|
-
for (const n of l2.
|
|
20200
|
+
for (const n of l2.selectNodes({ labels: ["Claim"], attrs: ["abstractionLevel", "revisit", "members"] })) {
|
|
18644
20201
|
if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) < ABSTRACTION_LEVEL.PRINCIPLE) continue;
|
|
18645
20202
|
if (n.attrs["revisit"] === true) continue;
|
|
18646
20203
|
const members = Array.isArray(n.attrs["members"]) ? n.attrs["members"] : [];
|
|
@@ -18666,9 +20223,11 @@ function revisitContradictedPrinciples(l2, ts) {
|
|
|
18666
20223
|
}
|
|
18667
20224
|
}
|
|
18668
20225
|
if (!reason) continue;
|
|
20226
|
+
const principle = l2.getNode(n.id);
|
|
20227
|
+
if (!principle) continue;
|
|
18669
20228
|
l2.updateNode(n.id, {
|
|
18670
20229
|
attrs: {
|
|
18671
|
-
...
|
|
20230
|
+
...principle.attrs,
|
|
18672
20231
|
revisit: true,
|
|
18673
20232
|
revisitReason: reason,
|
|
18674
20233
|
revisitSinceTs: ts,
|
|
@@ -18701,9 +20260,26 @@ init_src3();
|
|
|
18701
20260
|
function findStrandedSymbols(store) {
|
|
18702
20261
|
return store.strandedSymbols();
|
|
18703
20262
|
}
|
|
20263
|
+
var FILE_DECLARATION_LABELS = [
|
|
20264
|
+
"Function",
|
|
20265
|
+
"Class",
|
|
20266
|
+
"Method",
|
|
20267
|
+
"Module",
|
|
20268
|
+
"Interface",
|
|
20269
|
+
"Const",
|
|
20270
|
+
"Static",
|
|
20271
|
+
"Property",
|
|
20272
|
+
"TypeAlias",
|
|
20273
|
+
"Namespace",
|
|
20274
|
+
"Enum",
|
|
20275
|
+
"Annotation",
|
|
20276
|
+
"Comment"
|
|
20277
|
+
];
|
|
20278
|
+
var DECLARATION_LABEL_SET = new Set(FILE_DECLARATION_LABELS);
|
|
18704
20279
|
|
|
18705
20280
|
// src/package-index.ts
|
|
18706
20281
|
init_src();
|
|
20282
|
+
var CONTEXT_GATE_ATTRS = ["source", "name", "contributedAtSeq"];
|
|
18707
20283
|
function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [], opts = {}) {
|
|
18708
20284
|
const labels = opts.includePackages === false ? ["Language"] : ["Package", "Language"];
|
|
18709
20285
|
const ignored = (s) => ignorePatterns.length > 0 && ignorePatterns.some((p) => s.toLowerCase().includes(p));
|
|
@@ -18711,10 +20287,12 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
18711
20287
|
const seen = /* @__PURE__ */ new Set();
|
|
18712
20288
|
const localIdByWireId = {};
|
|
18713
20289
|
for (const label of labels) {
|
|
18714
|
-
for (const
|
|
18715
|
-
if (
|
|
18716
|
-
if (ignored(
|
|
18717
|
-
if (typeof
|
|
20290
|
+
for (const row of store.selectNodes({ labels: [label], attrs: CONTEXT_GATE_ATTRS })) {
|
|
20291
|
+
if (row.attrs["source"] === "cloud") continue;
|
|
20292
|
+
if (ignored(row.description) || ignored(String(row.attrs["name"] ?? ""))) continue;
|
|
20293
|
+
if (typeof row.attrs["contributedAtSeq"] === "number") continue;
|
|
20294
|
+
const n = store.getNode(row.id);
|
|
20295
|
+
if (!n) continue;
|
|
18718
20296
|
nodes.push({
|
|
18719
20297
|
...n,
|
|
18720
20298
|
// Language wire id = the shared `languageCanonicalId` (warming-spine
|
|
@@ -18743,10 +20321,14 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
18743
20321
|
}
|
|
18744
20322
|
}
|
|
18745
20323
|
if (nodes.length === 0) return null;
|
|
20324
|
+
const contributed = (id) => {
|
|
20325
|
+
const n = store.getNode(id);
|
|
20326
|
+
return !!n && (n.label === "Package" || n.label === "Language") && n.attrs["source"] !== "cloud" && typeof n.attrs["contributedAtSeq"] === "number" && !ignored(n.description) && !ignored(String(n.attrs["name"] ?? ""));
|
|
20327
|
+
};
|
|
18746
20328
|
const edges = [];
|
|
18747
20329
|
for (const id of seen) {
|
|
18748
20330
|
for (const e of store.outEdges(id, ["DEPENDS_ON"])) {
|
|
18749
|
-
if (seen.has(e.to)) edges.push({ ...e, attrs: {} });
|
|
20331
|
+
if (seen.has(e.to) || contributed(e.to)) edges.push({ ...e, attrs: {} });
|
|
18750
20332
|
}
|
|
18751
20333
|
}
|
|
18752
20334
|
const base = {
|
|
@@ -19147,7 +20729,7 @@ function publicSymbolIndex(store, workspaceRoot, deps = {}) {
|
|
|
19147
20729
|
});
|
|
19148
20730
|
const internal = internalPackageNames(workspaceRoot);
|
|
19149
20731
|
const bareModules = /* @__PURE__ */ new Set();
|
|
19150
|
-
for (const mod of store.
|
|
20732
|
+
for (const mod of store.selectNodes({ labels: ["Module"] })) {
|
|
19151
20733
|
if (isBareExternal(mod.description, internal)) bareModules.add(mod.id);
|
|
19152
20734
|
}
|
|
19153
20735
|
const files = /* @__PURE__ */ new Set();
|
|
@@ -19541,7 +21123,14 @@ function summaryFromDoc(doc, kind) {
|
|
|
19541
21123
|
}
|
|
19542
21124
|
if (verb !== null) return null;
|
|
19543
21125
|
const noun = clipped.replace(/^(?:the|a|an)\s+/i, "");
|
|
19544
|
-
|
|
21126
|
+
const head = noun.split(/[\s-]/, 1)[0].replace(/[.,;:!?]+$/, "");
|
|
21127
|
+
return tidy(`${lead(kind)} for ${isOrdinaryCapital(head) ? noun.charAt(0).toLowerCase() + noun.slice(1) : noun}`);
|
|
21128
|
+
}
|
|
21129
|
+
var CONTRACTION_BASES = /* @__PURE__ */ new Set(["it", "that", "this", "there", "here", "what", "who", "where", "when", "why", "how", "let", "he", "she"]);
|
|
21130
|
+
function isOrdinaryCapital(word) {
|
|
21131
|
+
const contraction = /^([A-Za-z]+)['’]s$/.exec(word);
|
|
21132
|
+
if (contraction) return CONTRACTION_BASES.has(contraction[1].toLowerCase()) && /^[A-Z][a-z]*$/.test(contraction[1]);
|
|
21133
|
+
return /^[A-Z][a-z]+$/.test(word);
|
|
19545
21134
|
}
|
|
19546
21135
|
var VERB_PHRASE_KINDS = /* @__PURE__ */ new Set(["function", "method"]);
|
|
19547
21136
|
function summaryFromName(name, kind) {
|
|
@@ -19651,6 +21240,62 @@ function buildSymbolLexicon(store) {
|
|
|
19651
21240
|
}
|
|
19652
21241
|
return lex;
|
|
19653
21242
|
}
|
|
21243
|
+
function lazySymbolLexicon(store) {
|
|
21244
|
+
if (!store.firstLiveByNameOrQname || !store.liveCommentsAttachedTo) return null;
|
|
21245
|
+
const labels = SYMBOL_LABELS.map(([label]) => label);
|
|
21246
|
+
const kindOf = new Map(SYMBOL_LABELS.map(([label, kind]) => [label, kind]));
|
|
21247
|
+
const members = /* @__PURE__ */ new Map();
|
|
21248
|
+
const docs = /* @__PURE__ */ new Map();
|
|
21249
|
+
const entries = /* @__PURE__ */ new Map();
|
|
21250
|
+
const member = (name) => {
|
|
21251
|
+
if (!members.has(name)) {
|
|
21252
|
+
const hit = isDistinctiveIdentifier(name) ? store.firstLiveByNameOrQname(labels, name) : null;
|
|
21253
|
+
members.set(name, hit ? { kind: kindOf.get(hit.label), description: hit.description, qname: hit.qname } : null);
|
|
21254
|
+
}
|
|
21255
|
+
return members.get(name);
|
|
21256
|
+
};
|
|
21257
|
+
const docFor = (name) => {
|
|
21258
|
+
if (!docs.has(name)) {
|
|
21259
|
+
let found;
|
|
21260
|
+
for (const c of store.liveCommentsAttachedTo(name)) {
|
|
21261
|
+
if (typeof c.kind !== "string" || !DOC_COMMENT_KINDS.has(c.kind)) continue;
|
|
21262
|
+
const text = typeof c.text === "string" ? c.text : c.description;
|
|
21263
|
+
if (!text?.trim()) continue;
|
|
21264
|
+
found = text.trim().slice(0, MAX_DOC_CHARS);
|
|
21265
|
+
break;
|
|
21266
|
+
}
|
|
21267
|
+
docs.set(name, found);
|
|
21268
|
+
}
|
|
21269
|
+
return docs.get(name);
|
|
21270
|
+
};
|
|
21271
|
+
const isKnown = (tok) => member(tok) !== null;
|
|
21272
|
+
let anySymbol;
|
|
21273
|
+
return {
|
|
21274
|
+
has: isKnown,
|
|
21275
|
+
get(name) {
|
|
21276
|
+
if (!entries.has(name)) {
|
|
21277
|
+
const m = member(name);
|
|
21278
|
+
let entry = null;
|
|
21279
|
+
if (m) {
|
|
21280
|
+
entry = { kind: m.kind };
|
|
21281
|
+
const doc = (m.description ? docFor(m.description) : void 0) ?? (m.qname ? docFor(m.qname) : void 0);
|
|
21282
|
+
for (const phrase of summaryCandidates(name, m.kind, doc)) {
|
|
21283
|
+
if (summaryRejectionReason(phrase, isKnown) === null) {
|
|
21284
|
+
entry.summary = phrase;
|
|
21285
|
+
break;
|
|
21286
|
+
}
|
|
21287
|
+
}
|
|
21288
|
+
}
|
|
21289
|
+
entries.set(name, entry);
|
|
21290
|
+
}
|
|
21291
|
+
return entries.get(name) ?? void 0;
|
|
21292
|
+
},
|
|
21293
|
+
get size() {
|
|
21294
|
+
anySymbol ??= labels.some((label) => store.countNodesByLabel(label) > 0);
|
|
21295
|
+
return anySymbol ? 1 : 0;
|
|
21296
|
+
}
|
|
21297
|
+
};
|
|
21298
|
+
}
|
|
19654
21299
|
var LEXICON_SYMBOL_LABELS = ["Function", "Method", "Class", "Interface", "Const", "Comment"];
|
|
19655
21300
|
function codeGraphKey(store) {
|
|
19656
21301
|
const files = store.labelFingerprint?.(["File"]);
|
|
@@ -19662,7 +21307,7 @@ function lexiconsFor(store) {
|
|
|
19662
21307
|
const key = codeGraphKey(store);
|
|
19663
21308
|
const hit = key !== null ? lexiconCache.get(store) : void 0;
|
|
19664
21309
|
if (hit && hit.key === key) return hit;
|
|
19665
|
-
const built = { symbols: buildSymbolLexicon(store), files: buildFileLexicon(store) };
|
|
21310
|
+
const built = { symbols: lazySymbolLexicon(store) ?? buildSymbolLexicon(store), files: buildFileLexicon(store) };
|
|
19666
21311
|
if (key !== null) lexiconCache.set(store, { key, ...built });
|
|
19667
21312
|
return built;
|
|
19668
21313
|
}
|
|
@@ -19901,6 +21546,38 @@ function stripCodebaseScope(scope) {
|
|
|
19901
21546
|
delete s["codebase"];
|
|
19902
21547
|
return s;
|
|
19903
21548
|
}
|
|
21549
|
+
function instanceGateRows(store, label) {
|
|
21550
|
+
return store.instanceGateRows?.(label) ?? store.findNodeFieldsByLabel(label, ["source", "privateFinding", "resolvedAs"]).map((r) => ({
|
|
21551
|
+
id: r.id,
|
|
21552
|
+
source: r.attrs["source"],
|
|
21553
|
+
privateFinding: r.attrs["privateFinding"],
|
|
21554
|
+
resolvedAs: r.attrs["resolvedAs"]
|
|
21555
|
+
}));
|
|
21556
|
+
}
|
|
21557
|
+
var GATE_VIEW_ATTRS = [
|
|
21558
|
+
"privateFinding",
|
|
21559
|
+
"source",
|
|
21560
|
+
"sources",
|
|
21561
|
+
"resolvedAs",
|
|
21562
|
+
"instanceReceipts",
|
|
21563
|
+
"cloudWithdrawn",
|
|
21564
|
+
"withdrawnAtCorrectionSeq",
|
|
21565
|
+
"contributedAtSeq",
|
|
21566
|
+
"cloudNodeId"
|
|
21567
|
+
];
|
|
21568
|
+
var GATE_VIEW_CHUNK = 500;
|
|
21569
|
+
var gateView = /* @__PURE__ */ new WeakSet();
|
|
21570
|
+
function gateViews(store, label, ids) {
|
|
21571
|
+
const byId = /* @__PURE__ */ new Map();
|
|
21572
|
+
for (let i = 0; i < ids.length; i += GATE_VIEW_CHUNK) {
|
|
21573
|
+
for (const r of store.selectNodes({ labels: [label], attrs: GATE_VIEW_ATTRS, where: [{ idIn: ids.slice(i, i + GATE_VIEW_CHUNK) }] })) {
|
|
21574
|
+
const view = { ...r, embedding: [] };
|
|
21575
|
+
gateView.add(view);
|
|
21576
|
+
byId.set(r.id, view);
|
|
21577
|
+
}
|
|
21578
|
+
}
|
|
21579
|
+
return ids.flatMap((id) => byId.get(id) ?? []);
|
|
21580
|
+
}
|
|
19904
21581
|
function contributedWireId(n, destinationKey) {
|
|
19905
21582
|
const cloudId = instanceReceipt(n, destinationKey)["cloudNodeId"];
|
|
19906
21583
|
return typeof cloudId === "string" && cloudId ? cloudId : n.id;
|
|
@@ -20054,10 +21731,12 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
20054
21731
|
sidecarNoSummary: 0,
|
|
20055
21732
|
fileRefsSubstituted: 0,
|
|
20056
21733
|
fileRefLexiconMisses: 0,
|
|
20057
|
-
collapsedEdges: 0
|
|
21734
|
+
collapsedEdges: 0,
|
|
21735
|
+
edgesHeldParked: 0,
|
|
21736
|
+
edgesHeldDropped: 0
|
|
20058
21737
|
};
|
|
20059
21738
|
const sharedCauses = /* @__PURE__ */ new Map();
|
|
20060
|
-
const causalQueue = store
|
|
21739
|
+
const causalQueue = instanceGateRows(store, "Problem").filter((r) => r.source !== "cloud" && r.resolvedAs !== PROBLEM_RESOLUTION.FALSE_POSITIVE).map((r) => r.id).filter((id) => ignorePatterns.length === 0 || !ignored(store.getNode(id)?.description ?? ""));
|
|
20061
21740
|
const causalSeen = new Set(causalQueue);
|
|
20062
21741
|
for (let i = 0; i < causalQueue.length; i++) {
|
|
20063
21742
|
const id = causalQueue[i];
|
|
@@ -20073,21 +21752,21 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
20073
21752
|
for (const label of INSTANCE_LABELS) {
|
|
20074
21753
|
const ref = [];
|
|
20075
21754
|
includedByLabel.set(label, ref);
|
|
20076
|
-
const
|
|
20077
|
-
for (const row of store
|
|
20078
|
-
if (row.
|
|
20079
|
-
|
|
20080
|
-
if (n) local.push(n);
|
|
21755
|
+
const localIds = [];
|
|
21756
|
+
for (const row of instanceGateRows(store, label)) {
|
|
21757
|
+
if (row.source !== "cloud") {
|
|
21758
|
+
localIds.push(row.id);
|
|
20081
21759
|
continue;
|
|
20082
21760
|
}
|
|
20083
21761
|
if (seen.has(row.id) || walked.has(row.id)) continue;
|
|
20084
21762
|
walked.add(row.id);
|
|
20085
21763
|
composition.candidates++;
|
|
20086
|
-
if ((row.
|
|
21764
|
+
if ((row.privateFinding || label === "Vulnerability") && !opts.project) composition.privateTargetMissing++;
|
|
20087
21765
|
else composition.cloudSourced++;
|
|
20088
21766
|
}
|
|
21767
|
+
const local = gateViews(store, label, localIds);
|
|
20089
21768
|
const population = label === "RootCause" && opts.sharedStore ? [...local, ...sharedCauses.values()] : local;
|
|
20090
|
-
for (
|
|
21769
|
+
for (let n of population) {
|
|
20091
21770
|
if (seen.has(n.id)) continue;
|
|
20092
21771
|
if (walked.has(n.id)) continue;
|
|
20093
21772
|
walked.add(n.id);
|
|
@@ -20123,6 +21802,11 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
20123
21802
|
semanticEndpoints.set(n.id, contributedWireId(n, opts.destinationKey));
|
|
20124
21803
|
continue;
|
|
20125
21804
|
}
|
|
21805
|
+
if (gateView.has(n)) {
|
|
21806
|
+
const full = store.getNode(n.id);
|
|
21807
|
+
if (!full) continue;
|
|
21808
|
+
n = full;
|
|
21809
|
+
}
|
|
20126
21810
|
if (isEditPlaceholderSolution(n)) {
|
|
20127
21811
|
composition.autoMint++;
|
|
20128
21812
|
continue;
|
|
@@ -20365,6 +22049,30 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
20365
22049
|
if (count > 0) symbolSummaries = sidecar;
|
|
20366
22050
|
}
|
|
20367
22051
|
edges.push(...anchorEdges);
|
|
22052
|
+
if (opts.edgeHolds && Object.keys(opts.edgeHolds).length > 0) {
|
|
22053
|
+
const holds = opts.edgeHolds;
|
|
22054
|
+
const shipsHere = (wireId) => (endpointSources[wireId] ?? [wireId]).some((localId) => shippedById.has(localId));
|
|
22055
|
+
const kept = [];
|
|
22056
|
+
for (const e of edges) {
|
|
22057
|
+
const hold = holds[e.id];
|
|
22058
|
+
if (!hold || hold === "held" && (shipsHere(e.from) || shipsHere(e.to))) {
|
|
22059
|
+
kept.push(e);
|
|
22060
|
+
continue;
|
|
22061
|
+
}
|
|
22062
|
+
if (hold === "held") {
|
|
22063
|
+
composition.edgesHeldParked++;
|
|
22064
|
+
for (const localId of endpointSources[e.from] ?? [e.from]) {
|
|
22065
|
+
delete anchorDigests[localId];
|
|
22066
|
+
delete semanticEdgeDigests[localId];
|
|
22067
|
+
delete projectAnchorDigests[localId];
|
|
22068
|
+
}
|
|
22069
|
+
} else {
|
|
22070
|
+
composition.edgesHeldDropped++;
|
|
22071
|
+
}
|
|
22072
|
+
}
|
|
22073
|
+
edges.length = 0;
|
|
22074
|
+
edges.push(...kept);
|
|
22075
|
+
}
|
|
20368
22076
|
const bindingEndpoints = /* @__PURE__ */ new Set();
|
|
20369
22077
|
for (const e of edges) {
|
|
20370
22078
|
if (e.type === "CAUSED_BY" || e.type === "SOLVED_BY") {
|
|
@@ -20446,7 +22154,8 @@ function buildDrainInstances(store, sharedStore, input) {
|
|
|
20446
22154
|
...lexicon ? { lexicon } : {},
|
|
20447
22155
|
...fileLexicon ? { fileLexicon } : {},
|
|
20448
22156
|
...input.project ? { project: input.project } : {},
|
|
20449
|
-
...input.quarantinedSessions?.length ? { quarantinedSessions: input.quarantinedSessions } : {}
|
|
22157
|
+
...input.quarantinedSessions?.length ? { quarantinedSessions: input.quarantinedSessions } : {},
|
|
22158
|
+
...input.edgeHolds ? { edgeHolds: input.edgeHolds } : {}
|
|
20450
22159
|
});
|
|
20451
22160
|
return {
|
|
20452
22161
|
batch,
|
|
@@ -20457,11 +22166,11 @@ function buildDrainInstances(store, sharedStore, input) {
|
|
|
20457
22166
|
|
|
20458
22167
|
// src/consolidate-worker.ts
|
|
20459
22168
|
var stores = /* @__PURE__ */ new Map();
|
|
20460
|
-
function storeAt(
|
|
20461
|
-
let s = stores.get(
|
|
22169
|
+
function storeAt(path2) {
|
|
22170
|
+
let s = stores.get(path2);
|
|
20462
22171
|
if (!s) {
|
|
20463
|
-
s = openGraphStore({ path });
|
|
20464
|
-
stores.set(
|
|
22172
|
+
s = openGraphStore({ path: path2 });
|
|
22173
|
+
stores.set(path2, s);
|
|
20465
22174
|
}
|
|
20466
22175
|
return s;
|
|
20467
22176
|
}
|
|
@@ -20501,7 +22210,7 @@ function runPass(msg) {
|
|
|
20501
22210
|
return revisitContradictedPrinciples(shared, msg.ts ?? Date.now());
|
|
20502
22211
|
case "strandedCount": {
|
|
20503
22212
|
if (!msg.castaliaPath) throw new Error("strandedCount: castaliaPath required");
|
|
20504
|
-
return findStrandedSymbols(storeAt(msg.castaliaPath))
|
|
22213
|
+
return findStrandedSymbols(storeAt(msg.castaliaPath));
|
|
20505
22214
|
}
|
|
20506
22215
|
case "drainContext": {
|
|
20507
22216
|
if (!msg.castaliaPath) throw new Error("drainContext: castaliaPath required");
|