@inerrata-corporation/errata 2.0.2-dev.125 → 2.0.2-dev.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/consolidate-worker.mjs +22 -2
- package/errata.mjs +1454 -443
- package/package.json +1 -1
- package/pass-worker.mjs +48 -13
package/errata.mjs
CHANGED
|
@@ -183,7 +183,14 @@ var init_castalia = __esm({
|
|
|
183
183
|
"SOLVED_BY",
|
|
184
184
|
"MITIGATES",
|
|
185
185
|
"REPORTED_FAILURE",
|
|
186
|
-
"CONTRADICTS"
|
|
186
|
+
"CONTRADICTS",
|
|
187
|
+
// Motif twin-faces (KN-twin-faces): failure-face motif → remedy-face motif
|
|
188
|
+
// across layers (AntiPattern/Weakness ↔ Technique/Pattern). Same
|
|
189
|
+
// problem→fix direction as FIXED_BY/SOLVED_BY. Minted by the nightly LLM
|
|
190
|
+
// twin-face pass — these are the near-identical cross-layer pairs the
|
|
191
|
+
// polarity gate (finding 5) correctly refuses to FUSE; the link carries
|
|
192
|
+
// what fusion can't.
|
|
193
|
+
"REMEDIED_BY"
|
|
187
194
|
];
|
|
188
195
|
CONCEPTUAL_EDGES = [
|
|
189
196
|
"INSTANCE_OF",
|
|
@@ -313,6 +320,10 @@ var init_castalia = __esm({
|
|
|
313
320
|
CAUSED_BY: 3,
|
|
314
321
|
FIXED_BY: 3,
|
|
315
322
|
SOLVED_BY: 3,
|
|
323
|
+
// Twin-face link (failure motif → remedy motif, KN-twin-faces) — causal-grade
|
|
324
|
+
// but a notch below witnessed FIXED_BY/SOLVED_BY: the pairing is an LLM
|
|
325
|
+
// judgment over descriptions, not an agent-witnessed resolution.
|
|
326
|
+
REMEDIED_BY: 2.5,
|
|
316
327
|
MANIFESTS_AS: 2,
|
|
317
328
|
ESCALATES_TO: 1.5,
|
|
318
329
|
AFFECTS: 1.2,
|
|
@@ -15605,6 +15616,15 @@ var init_wire = __esm({
|
|
|
15605
15616
|
/** Canonical human-readable description (no raw paths — daemon scrubs; server rechecks). */
|
|
15606
15617
|
description: external_exports.string().min(1).max(4e3),
|
|
15607
15618
|
attrs: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
|
|
15619
|
+
/** One-way origin key of the SESSION that minted this node (`ws_…`, a
|
|
15620
|
+
* truncated digest — never the raw session id). Stamped onto the created
|
|
15621
|
+
* node as `authoringSession`, which is the independence unit for the
|
|
15622
|
+
* evidence channels: the session that authored a claim may not corroborate
|
|
15623
|
+
* or refute it, while a DIFFERENT session on the same checkout may (alyssa,
|
|
15624
|
+
* 2026-07-31 — the workspace key made every witness on a single-checkout
|
|
15625
|
+
* deployment a self-corroboration). Optional + additive: absent leaves the
|
|
15626
|
+
* gate fail-open for that node, exactly today's behaviour. */
|
|
15627
|
+
originSession: external_exports.string().min(3).max(64).optional(),
|
|
15608
15628
|
extractionSource: external_exports.enum(INGEST_EXTRACTION_SOURCES),
|
|
15609
15629
|
validationSource: external_exports.enum(VALIDATION_SOURCES).optional(),
|
|
15610
15630
|
/** Org-membrane (M2): the daemon's anchor tag — it owns the lockfile, so it
|
|
@@ -15720,6 +15740,9 @@ function projectSymbolId(projectSalt, projectId, relPath, qname, kind) {
|
|
|
15720
15740
|
const path2 = normalizeSymbolPath(relPath);
|
|
15721
15741
|
return `sym_${createHmac("sha256", projectSalt).update(`${projectId}:${path2}:${qname}:${kind}`).digest("hex").slice(0, 24)}`;
|
|
15722
15742
|
}
|
|
15743
|
+
function isMembraneSaltedId(id) {
|
|
15744
|
+
return typeof id === "string" && /^(orgn_|teamn_|projn_)/.test(id);
|
|
15745
|
+
}
|
|
15723
15746
|
var init_project_symbol = __esm({
|
|
15724
15747
|
"../../packages/shared/src/project-symbol.ts"() {
|
|
15725
15748
|
"use strict";
|
|
@@ -15876,7 +15899,7 @@ var init_canonicalize = __esm({
|
|
|
15876
15899
|
|
|
15877
15900
|
// ../../packages/shared/src/nlp/triage-canon.ts
|
|
15878
15901
|
function canonicalizeToken(t) {
|
|
15879
|
-
const base = t.trim().toLowerCase().replace(/
|
|
15902
|
+
const base = t.trim().toLowerCase().replace(/(?!^)@.*$|\s.*$/, "");
|
|
15880
15903
|
if (!base) return "";
|
|
15881
15904
|
return resolveCanonicalId(base) ?? base;
|
|
15882
15905
|
}
|
|
@@ -15969,6 +15992,7 @@ __export(src_exports, {
|
|
|
15969
15992
|
isCodeLabel: () => isCodeLabel,
|
|
15970
15993
|
isContextLabel: () => isContextLabel,
|
|
15971
15994
|
isInferredSource: () => isInferredSource,
|
|
15995
|
+
isMembraneSaltedId: () => isMembraneSaltedId,
|
|
15972
15996
|
isProjectOnlyEdgeType: () => isProjectOnlyEdgeType,
|
|
15973
15997
|
isProjectOnlyNodeLabel: () => isProjectOnlyNodeLabel,
|
|
15974
15998
|
isQuarantinedExtractionSource: () => isQuarantinedExtractionSource,
|
|
@@ -16264,6 +16288,9 @@ var init_profile = __esm({
|
|
|
16264
16288
|
});
|
|
16265
16289
|
|
|
16266
16290
|
// ../../packages/local-shared/src/daemon-wire.ts
|
|
16291
|
+
function formatDisposition(d) {
|
|
16292
|
+
return `${d.recorded} recorded \xB7 ${d.unmatched} unmatched (queued) \xB7 ${d.duplicate} dup \xB7 ${d.selfGated} self`;
|
|
16293
|
+
}
|
|
16267
16294
|
var init_daemon_wire = __esm({
|
|
16268
16295
|
"../../packages/local-shared/src/daemon-wire.ts"() {
|
|
16269
16296
|
"use strict";
|
|
@@ -18168,6 +18195,9 @@ var init_problem_package_link = __esm({
|
|
|
18168
18195
|
});
|
|
18169
18196
|
|
|
18170
18197
|
// ../../packages/local-graph/src/design-problem.ts
|
|
18198
|
+
function isConstraintProblem(node2) {
|
|
18199
|
+
return node2.attrs["kind"] === "constraint";
|
|
18200
|
+
}
|
|
18171
18201
|
function isPlaceholderStatement(statement) {
|
|
18172
18202
|
const s = statement.trim();
|
|
18173
18203
|
if (s.length < 8) return true;
|
|
@@ -18417,7 +18447,7 @@ function tokenJaccard(a, b) {
|
|
|
18417
18447
|
for (const t of sa) if (sb.has(t)) inter++;
|
|
18418
18448
|
return inter / (sa.size + sb.size - inter);
|
|
18419
18449
|
}
|
|
18420
|
-
function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts) {
|
|
18450
|
+
function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts, kind = "problem") {
|
|
18421
18451
|
const stmt = statement.trim();
|
|
18422
18452
|
if (!ANCHORABLE_CODE.test(relPath)) return null;
|
|
18423
18453
|
const file2 = resolveFileNode(store, relPath, workspaceId2);
|
|
@@ -18428,6 +18458,8 @@ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, sou
|
|
|
18428
18458
|
const cand = store.getNode(e.from);
|
|
18429
18459
|
if (!cand || cand.label !== "Problem" || cand.attrs["resolvedAt"]) continue;
|
|
18430
18460
|
if (cand.id === selfId) continue;
|
|
18461
|
+
const candKind = cand.attrs["kind"] === "constraint" ? "constraint" : "problem";
|
|
18462
|
+
if (candKind !== (kind === "constraint" ? "constraint" : "problem")) continue;
|
|
18431
18463
|
const score2 = tokenJaccard(stmt, cand.description);
|
|
18432
18464
|
if (score2 >= SAME_ANCHOR_DEDUP_JACCARD && (!best || score2 > best.score)) best = { node: cand, score: score2 };
|
|
18433
18465
|
}
|
|
@@ -18462,6 +18494,7 @@ function priorsForFile(store, relPath) {
|
|
|
18462
18494
|
}
|
|
18463
18495
|
if (!file2) return null;
|
|
18464
18496
|
const openProblems = [];
|
|
18497
|
+
const constraints = [];
|
|
18465
18498
|
const seenProblem = /* @__PURE__ */ new Set();
|
|
18466
18499
|
const related = /* @__PURE__ */ new Map();
|
|
18467
18500
|
for (const e of store.inEdges(file2.id, ["ANCHORED_AT"])) {
|
|
@@ -18470,14 +18503,14 @@ function priorsForFile(store, relPath) {
|
|
|
18470
18503
|
if (n.label === "Problem") {
|
|
18471
18504
|
if (!n.attrs["resolvedAt"] && !seenProblem.has(n.id)) {
|
|
18472
18505
|
seenProblem.add(n.id);
|
|
18473
|
-
openProblems.push(n);
|
|
18506
|
+
(isConstraintProblem(n) ? constraints : openProblems).push(n);
|
|
18474
18507
|
}
|
|
18475
18508
|
} else if (CITABLE_PRIOR_LABELS.has(n.label)) {
|
|
18476
18509
|
related.set(n.id, n);
|
|
18477
18510
|
}
|
|
18478
18511
|
}
|
|
18479
18512
|
const solutionsByProblem = /* @__PURE__ */ new Map();
|
|
18480
|
-
for (const p of openProblems) {
|
|
18513
|
+
for (const p of [...openProblems, ...constraints]) {
|
|
18481
18514
|
for (const e of store.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
|
|
18482
18515
|
const n = store.getNode(e.to);
|
|
18483
18516
|
if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
|
|
@@ -18488,9 +18521,11 @@ function priorsForFile(store, relPath) {
|
|
|
18488
18521
|
}
|
|
18489
18522
|
}
|
|
18490
18523
|
}
|
|
18491
|
-
if (openProblems.length === 0 && related.size === 0) return null;
|
|
18492
|
-
|
|
18493
|
-
|
|
18524
|
+
if (openProblems.length === 0 && constraints.length === 0 && related.size === 0) return null;
|
|
18525
|
+
const recentFirst = (a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0);
|
|
18526
|
+
openProblems.sort(recentFirst);
|
|
18527
|
+
constraints.sort(recentFirst);
|
|
18528
|
+
return { file: file2, openProblems, constraints, related: [...related.values()], solutionsByProblem };
|
|
18494
18529
|
}
|
|
18495
18530
|
function anchorProblemToDiff(store, problemId, changedPaths, workspaceId2, t, opts = {}) {
|
|
18496
18531
|
return anchorNodeToDiff(store, problemId, "Problem", changedPaths, workspaceId2, t, opts);
|
|
@@ -18561,7 +18596,8 @@ function resolveDesignProblemById(store, id, fixNote, t) {
|
|
|
18561
18596
|
}
|
|
18562
18597
|
function closeDesignProblem(store, problemId, fixNote, t) {
|
|
18563
18598
|
const p = store.getNode(problemId);
|
|
18564
|
-
if (!p || p.label !== "Problem"
|
|
18599
|
+
if (!p || p.label !== "Problem") return false;
|
|
18600
|
+
if (p.attrs["resolvedAs"]) return false;
|
|
18565
18601
|
if (fixNote?.trim() && store.outEdges(problemId, ["SOLVED_BY"]).length === 0) {
|
|
18566
18602
|
const solId = `dfix_${digest({ problemId, fix: fixNote })}`.slice(0, 56);
|
|
18567
18603
|
store.mergeNode(
|
|
@@ -18569,6 +18605,7 @@ function closeDesignProblem(store, problemId, fixNote, t) {
|
|
|
18569
18605
|
);
|
|
18570
18606
|
mergeEdge(store, problemId, solId, "SOLVED_BY", t);
|
|
18571
18607
|
}
|
|
18608
|
+
if (p.attrs["resolvedAt"]) return false;
|
|
18572
18609
|
store.updateNode(problemId, {
|
|
18573
18610
|
attrs: { ...p.attrs, provisional: false, resolvedAt: t },
|
|
18574
18611
|
lastUpdatedAt: t
|
|
@@ -18609,6 +18646,7 @@ function resolveDesignProblems(store, t) {
|
|
|
18609
18646
|
for (const p of store.findNodesByLabel("Problem")) {
|
|
18610
18647
|
if (!p.id.startsWith("dprob_")) continue;
|
|
18611
18648
|
if (p.attrs["resolvedAt"]) continue;
|
|
18649
|
+
if (isConstraintProblem(p)) continue;
|
|
18612
18650
|
let symName = "";
|
|
18613
18651
|
let symRelPath;
|
|
18614
18652
|
let edited = false;
|
|
@@ -18626,7 +18664,7 @@ function resolveDesignProblems(store, t) {
|
|
|
18626
18664
|
if (store.outEdges(p.id, ["SOLVED_BY"]).length === 0) {
|
|
18627
18665
|
const solId = `dfix_${digest({ problemId: p.id, edit: t })}`.slice(0, 56);
|
|
18628
18666
|
store.mergeNode(
|
|
18629
|
-
buildNode(solId, "Solution",
|
|
18667
|
+
buildNode(solId, "Solution", `${AUTO_MINT_PREFIX}${symName}`, t, {
|
|
18630
18668
|
source: "convo",
|
|
18631
18669
|
provisional: false,
|
|
18632
18670
|
// AC-resolution-attrs: the resolving symbol/file as STRUCTURED data, not
|
|
@@ -18647,7 +18685,7 @@ function resolveDesignProblems(store, t) {
|
|
|
18647
18685
|
}
|
|
18648
18686
|
return resolved;
|
|
18649
18687
|
}
|
|
18650
|
-
var DESIGN_PROMOTE_AT, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, ANCHORABLE_CODE, MAX_ANCHOR_FILES;
|
|
18688
|
+
var DESIGN_PROMOTE_AT, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, ANCHORABLE_CODE, MAX_ANCHOR_FILES, AUTO_MINT_PREFIX;
|
|
18651
18689
|
var init_design_problem = __esm({
|
|
18652
18690
|
"../../packages/local-graph/src/design-problem.ts"() {
|
|
18653
18691
|
"use strict";
|
|
@@ -18689,6 +18727,7 @@ var init_design_problem = __esm({
|
|
|
18689
18727
|
]);
|
|
18690
18728
|
ANCHORABLE_CODE = /\.(ts|tsx|js|mjs|cjs|py|go|rs|java|rb|c|cpp|h)$/;
|
|
18691
18729
|
MAX_ANCHOR_FILES = 5;
|
|
18730
|
+
AUTO_MINT_PREFIX = "addressed by an edit to ";
|
|
18692
18731
|
}
|
|
18693
18732
|
});
|
|
18694
18733
|
|
|
@@ -18753,13 +18792,11 @@ function backfillLegacyAnchors(store, ts) {
|
|
|
18753
18792
|
}
|
|
18754
18793
|
return report;
|
|
18755
18794
|
}
|
|
18756
|
-
var AUTO_MINT_PREFIX;
|
|
18757
18795
|
var init_anchor_backfill = __esm({
|
|
18758
18796
|
"../../packages/local-graph/src/anchor-backfill.ts"() {
|
|
18759
18797
|
"use strict";
|
|
18760
18798
|
init_justification();
|
|
18761
18799
|
init_design_problem();
|
|
18762
|
-
AUTO_MINT_PREFIX = "addressed by an edit to ";
|
|
18763
18800
|
}
|
|
18764
18801
|
});
|
|
18765
18802
|
|
|
@@ -20520,6 +20557,7 @@ function mergeDuplicateProblems(store, opts) {
|
|
|
20520
20557
|
if (consumed.has(b.id)) continue;
|
|
20521
20558
|
const tb = tokens.get(b.id);
|
|
20522
20559
|
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
20560
|
+
if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
|
|
20523
20561
|
if (overlap(ta, tb) >= minOverlap) {
|
|
20524
20562
|
cluster.push(b);
|
|
20525
20563
|
consumed.add(b.id);
|
|
@@ -20576,6 +20614,7 @@ var init_problem_dedup = __esm({
|
|
|
20576
20614
|
"use strict";
|
|
20577
20615
|
init_src();
|
|
20578
20616
|
init_src();
|
|
20617
|
+
init_design_problem();
|
|
20579
20618
|
REDIRECT_EDGES = [
|
|
20580
20619
|
"CAUSED_BY",
|
|
20581
20620
|
"SOLVED_BY",
|
|
@@ -20782,6 +20821,7 @@ var init_principle_sync = __esm({
|
|
|
20782
20821
|
// ../../packages/local-graph/src/index.ts
|
|
20783
20822
|
var src_exports2 = {};
|
|
20784
20823
|
__export(src_exports2, {
|
|
20824
|
+
AUTO_MINT_PREFIX: () => AUTO_MINT_PREFIX,
|
|
20785
20825
|
CAUSAL_FAMILY: () => CAUSAL_FAMILY,
|
|
20786
20826
|
CITABLE_PRIOR_LABELS: () => CITABLE_PRIOR_LABELS,
|
|
20787
20827
|
CODE_REACH_EDGES: () => CODE_REACH_EDGES,
|
|
@@ -20833,6 +20873,7 @@ __export(src_exports2, {
|
|
|
20833
20873
|
induceAbstractions: () => induceAbstractions,
|
|
20834
20874
|
induceTriage: () => induceTriage,
|
|
20835
20875
|
ingestDesignProblem: () => ingestDesignProblem,
|
|
20876
|
+
isConstraintProblem: () => isConstraintProblem,
|
|
20836
20877
|
isPlaceholderStatement: () => isPlaceholderStatement,
|
|
20837
20878
|
linkProblemToLanguages: () => linkProblemToLanguages,
|
|
20838
20879
|
linkProblemToPackages: () => linkProblemToPackages,
|
|
@@ -20918,12 +20959,14 @@ function isPassiveInjectable(node2) {
|
|
|
20918
20959
|
}
|
|
20919
20960
|
function buildSnapshot(opts) {
|
|
20920
20961
|
const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
|
|
20921
|
-
const
|
|
20962
|
+
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
20963
|
+
const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
|
|
20922
20964
|
allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
|
|
20923
20965
|
const recent = allProblems.slice(0, 8).map((p) => ({
|
|
20924
20966
|
node: p,
|
|
20925
20967
|
anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
|
|
20926
20968
|
}));
|
|
20969
|
+
const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
|
|
20927
20970
|
const recentResolved = problems.filter((p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null).sort((a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)).slice(0, 4).map((p) => {
|
|
20928
20971
|
const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
|
|
20929
20972
|
const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
|
|
@@ -20951,10 +20994,13 @@ function buildSnapshot(opts) {
|
|
|
20951
20994
|
episodeId: episodeId2,
|
|
20952
20995
|
problems: problems2
|
|
20953
20996
|
}));
|
|
20954
|
-
const
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
|
|
20997
|
+
const revisitSeen = /* @__PURE__ */ new Set();
|
|
20998
|
+
const needsRevisit = listNeedsRevisit(opts.store, (opts.now ?? /* @__PURE__ */ new Date()).getTime()).filter((r) => {
|
|
20999
|
+
const key = `${r.label}:${r.description.trim().toLowerCase()}`;
|
|
21000
|
+
if (revisitSeen.has(key)) return false;
|
|
21001
|
+
revisitSeen.add(key);
|
|
21002
|
+
return true;
|
|
21003
|
+
}).slice(0, 5);
|
|
20958
21004
|
const norm = (x) => x.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
20959
21005
|
const pkgBase = (x) => {
|
|
20960
21006
|
const at = x.lastIndexOf("@");
|
|
@@ -20990,6 +21036,7 @@ function buildSnapshot(opts) {
|
|
|
20990
21036
|
profileContext,
|
|
20991
21037
|
recentProblems: recent,
|
|
20992
21038
|
recentResolved,
|
|
21039
|
+
recentConstraints,
|
|
20993
21040
|
...causalNudge ? { causalNudge } : {},
|
|
20994
21041
|
...domainNudge ? { domainNudge } : {},
|
|
20995
21042
|
motifs,
|
|
@@ -21042,6 +21089,7 @@ function sliceForFile(store, relPath) {
|
|
|
21042
21089
|
const fp = priorsForFile(store, relPath);
|
|
21043
21090
|
const priors = fp ? {
|
|
21044
21091
|
openProblems: fp.openProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
|
|
21092
|
+
constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
|
|
21045
21093
|
related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
|
|
21046
21094
|
solutionsByProblem: Object.fromEntries(
|
|
21047
21095
|
fp.openProblems.slice(0, 3).map((p) => [
|
|
@@ -21136,7 +21184,7 @@ function renderSnapshot(s) {
|
|
|
21136
21184
|
}
|
|
21137
21185
|
const tagOf = (n) => s.edgeElicitation ? ` \`[${priorHandle(n)}]\`` : "";
|
|
21138
21186
|
lines.push("### Recently observed problems in this workspace");
|
|
21139
|
-
if (s.recentProblems.length === 0 && s.recentResolved.length === 0) {
|
|
21187
|
+
if (s.recentProblems.length === 0 && s.recentResolved.length === 0 && s.recentConstraints.length === 0) {
|
|
21140
21188
|
lines.push("- _none yet \u2014 errata is still building its model_");
|
|
21141
21189
|
} else {
|
|
21142
21190
|
if (s.recentProblems.length > 0) {
|
|
@@ -21158,6 +21206,12 @@ function renderSnapshot(s) {
|
|
|
21158
21206
|
);
|
|
21159
21207
|
}
|
|
21160
21208
|
}
|
|
21209
|
+
if (s.recentConstraints.length > 0) {
|
|
21210
|
+
lines.push("_Design tensions \u2014 constraints this work is shaped around, not defects to fix:_");
|
|
21211
|
+
for (const c of s.recentConstraints) {
|
|
21212
|
+
lines.push(`- \u2696 **${c.description}** \u2014 \`${c.id}\`${tagOf(c)}`);
|
|
21213
|
+
}
|
|
21214
|
+
}
|
|
21161
21215
|
if (s.recentResolved.length > 0) {
|
|
21162
21216
|
lines.push("_Recently resolved \u2014 solved here; jumping-off points, not live defects:_");
|
|
21163
21217
|
for (const r of s.recentResolved) {
|
|
@@ -21225,6 +21279,14 @@ function renderSnapshot(s) {
|
|
|
21225
21279
|
}
|
|
21226
21280
|
}
|
|
21227
21281
|
}
|
|
21282
|
+
if (w.priors?.constraints.length) {
|
|
21283
|
+
lines.push(
|
|
21284
|
+
"- **Design tensions here** \u2014 standing constraints this code is shaped around. They are NOT open work and a change does not resolve one, so there is no `(fix:)` token; cite one you worked within with `([id])`:"
|
|
21285
|
+
);
|
|
21286
|
+
for (const c of w.priors.constraints) {
|
|
21287
|
+
lines.push(` - ${c.description} \u2192 \`([${c.id}])\``);
|
|
21288
|
+
}
|
|
21289
|
+
}
|
|
21228
21290
|
if (w.priors?.related.length) {
|
|
21229
21291
|
lines.push("- **Related priors** (cite with `([id])` where you lean on one):");
|
|
21230
21292
|
for (const n of w.priors.related) {
|
|
@@ -21262,6 +21324,9 @@ function dropLowestUnit(s) {
|
|
|
21262
21324
|
case "pendingEnrichment":
|
|
21263
21325
|
if (s.pendingEnrichment.length) return s.pendingEnrichment.pop(), true;
|
|
21264
21326
|
break;
|
|
21327
|
+
case "recentConstraints":
|
|
21328
|
+
if (s.recentConstraints.length) return s.recentConstraints.pop(), true;
|
|
21329
|
+
break;
|
|
21265
21330
|
case "recentProblems":
|
|
21266
21331
|
if (s.recentProblems.length) return s.recentProblems.pop(), true;
|
|
21267
21332
|
break;
|
|
@@ -21333,8 +21398,19 @@ ${RECALL_FIRST_BODY}`;
|
|
|
21333
21398
|
// budget they drop before anything open/actionable.
|
|
21334
21399
|
"recentResolved",
|
|
21335
21400
|
"pendingEnrichment",
|
|
21336
|
-
|
|
21337
|
-
|
|
21401
|
+
// A standing design tension outranks an enrichment nudge (it prevents a wrong
|
|
21402
|
+
// decision) but yields to a live defect (which is actionable now).
|
|
21403
|
+
"recentConstraints",
|
|
21404
|
+
// `needsRevisit` used to sit LAST — the most protected band in the block. That
|
|
21405
|
+
// inverted the block's whole purpose once the link-elicitation instruction (4,942
|
|
21406
|
+
// chars, 55% of the budget, non-evictable) started competing for room: the
|
|
21407
|
+
// budgeter drained all 8 Problems and all 4 Solutions and kept nine stale
|
|
21408
|
+
// auto-close notices, so the agent was handed an instruction to cite `[handles]`
|
|
21409
|
+
// with zero problem handles left to cite. A revisit flag is a maintenance nudge
|
|
21410
|
+
// about something ALREADY closed; a live prior is the substrate every link verb
|
|
21411
|
+
// needs. Problems outrank it now.
|
|
21412
|
+
"needsRevisit",
|
|
21413
|
+
"recentProblems"
|
|
21338
21414
|
];
|
|
21339
21415
|
DEFAULT_AGENT_CONTEXT_BUDGET = 9e3;
|
|
21340
21416
|
}
|
|
@@ -21567,6 +21643,7 @@ var init_oauth = __esm({
|
|
|
21567
21643
|
|
|
21568
21644
|
// ../../packages/cloud-client/src/client.ts
|
|
21569
21645
|
import { randomUUID } from "node:crypto";
|
|
21646
|
+
import { gzipSync } from "node:zlib";
|
|
21570
21647
|
function wirePerContext(value) {
|
|
21571
21648
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
21572
21649
|
const out2 = {};
|
|
@@ -21764,6 +21841,11 @@ function normalizeSolution(input) {
|
|
|
21764
21841
|
validationSource: input.validationSource
|
|
21765
21842
|
};
|
|
21766
21843
|
}
|
|
21844
|
+
function isIntermediaryRejection(err2) {
|
|
21845
|
+
if (!(err2 instanceof CloudError)) return false;
|
|
21846
|
+
if (err2.status < 400 || err2.status >= 500) return false;
|
|
21847
|
+
return /<!DOCTYPE html|<html/i.test(err2.message);
|
|
21848
|
+
}
|
|
21767
21849
|
function chunkArray(items, size) {
|
|
21768
21850
|
if (items.length <= size) return items.length ? [[...items]] : [];
|
|
21769
21851
|
const out2 = [];
|
|
@@ -21781,14 +21863,15 @@ function provenanceHeaders(provenance) {
|
|
|
21781
21863
|
...provenance.agentModel ? { "x-inerrata-agent-model": provenance.agentModel } : {}
|
|
21782
21864
|
};
|
|
21783
21865
|
}
|
|
21784
|
-
var asWireCount, INGEST_NODE_CHUNK, CloudClient, CloudError;
|
|
21866
|
+
var asWireCount, INGEST_NODE_CHUNK, COMPRESS_MIN_BYTES, CloudClient, CloudError;
|
|
21785
21867
|
var init_client = __esm({
|
|
21786
21868
|
"../../packages/cloud-client/src/client.ts"() {
|
|
21787
21869
|
"use strict";
|
|
21788
21870
|
init_oauth();
|
|
21789
21871
|
init_src();
|
|
21790
21872
|
asWireCount = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? Math.floor(n) : null;
|
|
21791
|
-
INGEST_NODE_CHUNK =
|
|
21873
|
+
INGEST_NODE_CHUNK = 8;
|
|
21874
|
+
COMPRESS_MIN_BYTES = 4096;
|
|
21792
21875
|
CloudClient = class {
|
|
21793
21876
|
baseUrl;
|
|
21794
21877
|
apiKey;
|
|
@@ -21803,6 +21886,8 @@ var init_client = __esm({
|
|
|
21803
21886
|
daemonVersion;
|
|
21804
21887
|
daemonChannel;
|
|
21805
21888
|
provenance;
|
|
21889
|
+
/** Cleared for the process once a server proves it cannot inflate (see `json`). */
|
|
21890
|
+
compressRequests;
|
|
21806
21891
|
constructor(opts) {
|
|
21807
21892
|
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
21808
21893
|
if (opts.apiKey !== void 0) this.apiKey = opts.apiKey;
|
|
@@ -21816,6 +21901,7 @@ var init_client = __esm({
|
|
|
21816
21901
|
this.timeoutMs = opts.timeoutMs ?? 15e3;
|
|
21817
21902
|
this.daemonVersion = opts.daemonVersion;
|
|
21818
21903
|
this.daemonChannel = opts.daemonChannel;
|
|
21904
|
+
this.compressRequests = opts.compressRequests ?? true;
|
|
21819
21905
|
this.provenance = opts.provenance ?? {
|
|
21820
21906
|
clientProduct: "inerrata_cloud_client",
|
|
21821
21907
|
clientKind: "sdk",
|
|
@@ -21948,11 +22034,32 @@ var init_client = __esm({
|
|
|
21948
22034
|
* drain (endpoint-label validation is deferred to the service when an endpoint
|
|
21949
22035
|
* isn't in-payload), so the split never orphans an edge. Sub-results concat into
|
|
21950
22036
|
* one. (fix: an un-chunked >200-node backlog 413'd forever and never drained.) */
|
|
21951
|
-
async ingest(batch) {
|
|
22037
|
+
async ingest(batch, opts = {}) {
|
|
21952
22038
|
const runId = randomUUID();
|
|
22039
|
+
const startedAt = Date.now();
|
|
22040
|
+
const totalChunks = Math.max(1, Math.ceil(batch.nodes.length / INGEST_NODE_CHUNK)) + Math.ceil(Math.max(0, batch.edges.length - MAX_EDGES_PER_PAYLOAD) / MAX_EDGES_PER_PAYLOAD);
|
|
22041
|
+
let chunkIndex = 0;
|
|
22042
|
+
const blocked = [];
|
|
22043
|
+
const emit = (nodeIds, accepted) => {
|
|
22044
|
+
chunkIndex++;
|
|
22045
|
+
const refused = blocked.splice(0, blocked.length);
|
|
22046
|
+
const refusedSet = new Set(refused);
|
|
22047
|
+
opts.onChunk?.({
|
|
22048
|
+
chunkIndex,
|
|
22049
|
+
totalChunks,
|
|
22050
|
+
// Watermark only what actually landed — a blocked node never reached the
|
|
22051
|
+
// door, and marking it contributed would lose it permanently and silently.
|
|
22052
|
+
nodeIds: refusedSet.size ? nodeIds.filter((id) => !refusedSet.has(id)) : nodeIds,
|
|
22053
|
+
accepted,
|
|
22054
|
+
elapsedMs: Date.now() - startedAt,
|
|
22055
|
+
...refused.length ? { blockedNodeIds: refused } : {}
|
|
22056
|
+
});
|
|
22057
|
+
};
|
|
21953
22058
|
if (batch.nodes.length <= INGEST_NODE_CHUNK && batch.edges.length <= MAX_EDGES_PER_PAYLOAD) {
|
|
21954
|
-
const result = await this.
|
|
21955
|
-
|
|
22059
|
+
const result = await this.ingestWithSplit(batch, batch.nodes, batch.edges, blocked);
|
|
22060
|
+
const summary = summarizeIngestResult(result);
|
|
22061
|
+
emit(batch.nodes.map((n) => n.id), summary.accepted);
|
|
22062
|
+
return { ...summary, result };
|
|
21956
22063
|
}
|
|
21957
22064
|
const merged = { runId, nodes: [], edges: [] };
|
|
21958
22065
|
let pendingEdges = [...batch.edges];
|
|
@@ -21964,11 +22071,14 @@ var init_client = __esm({
|
|
|
21964
22071
|
const shipped = new Set(inChunk);
|
|
21965
22072
|
pendingEdges = pendingEdges.filter((e) => !shipped.has(e));
|
|
21966
22073
|
}
|
|
21967
|
-
const r = await this.
|
|
22074
|
+
const r = await this.ingestWithSplit(batch, nodeChunk, inChunk, blocked);
|
|
22075
|
+
emit(nodeChunk.map((n) => n.id), summarizeIngestResult(r).accepted);
|
|
21968
22076
|
merged.nodes.push(...r.nodes);
|
|
21969
22077
|
merged.edges.push(...r.edges);
|
|
21970
22078
|
for (const rn of r.nodes) {
|
|
21971
|
-
if (rn.nodeId && rn.nodeId !== rn.canonicalId
|
|
22079
|
+
if (rn.nodeId && rn.nodeId !== rn.canonicalId && isMembraneSaltedId(rn.nodeId)) {
|
|
22080
|
+
echoedCloudId.set(rn.canonicalId, rn.nodeId);
|
|
22081
|
+
}
|
|
21972
22082
|
}
|
|
21973
22083
|
if (r.patternReconciliation) {
|
|
21974
22084
|
merged.patternReconciliation = { ...merged.patternReconciliation, ...r.patternReconciliation };
|
|
@@ -21981,6 +22091,7 @@ var init_client = __esm({
|
|
|
21981
22091
|
to: echoedCloudId.get(e.to) ?? e.to
|
|
21982
22092
|
}));
|
|
21983
22093
|
const r = await this.ingestWire(toWirePayload({ ...batch, nodes: [], edges: rewritten }, randomUUID()));
|
|
22094
|
+
emit([], summarizeIngestResult(r).accepted);
|
|
21984
22095
|
merged.edges.push(...r.edges);
|
|
21985
22096
|
}
|
|
21986
22097
|
return { ...summarizeIngestResult(merged), result: merged };
|
|
@@ -21989,6 +22100,43 @@ var init_client = __esm({
|
|
|
21989
22100
|
async ingestWire(payload) {
|
|
21990
22101
|
return this.json("POST", "/v2/ingest", payload);
|
|
21991
22102
|
}
|
|
22103
|
+
/**
|
|
22104
|
+
* Ship a node chunk, and on an EDGE TIMEOUT split it and retry the halves.
|
|
22105
|
+
*
|
|
22106
|
+
* `INGEST_NODE_CHUNK` is a guess about how long the server takes per node, and
|
|
22107
|
+
* that number moves with the size of the graph — so any fixed value eventually
|
|
22108
|
+
* drifts into the proxy's timeout and the lane dies silently (the 2026-07-31
|
|
22109
|
+
* three-day instances-lane outage). This makes the client self-correcting
|
|
22110
|
+
* instead: a chunk that times out is halved and retried, down to a single node,
|
|
22111
|
+
* so the drain degrades to slower rather than to stopped.
|
|
22112
|
+
*
|
|
22113
|
+
* ONLY on timeout-shaped failures (502/504/408, or a transport abort). A 4xx is
|
|
22114
|
+
* a verdict about the payload — splitting it would just re-send a rejected batch
|
|
22115
|
+
* N more times — and a 409/413 has its own handling upstream.
|
|
22116
|
+
*/
|
|
22117
|
+
async ingestWithSplit(batch, nodes, edges, blocked) {
|
|
22118
|
+
try {
|
|
22119
|
+
return await this.ingestWire(toWirePayload({ ...batch, nodes, edges }, randomUUID()));
|
|
22120
|
+
} catch (err2) {
|
|
22121
|
+
const status = err2 instanceof CloudError ? err2.status : 0;
|
|
22122
|
+
const timedOut = status === 502 || status === 504 || status === 408 || status === 0;
|
|
22123
|
+
const refusedAtEdge = blocked !== void 0 && isIntermediaryRejection(err2);
|
|
22124
|
+
if (refusedAtEdge && nodes.length === 1 && nodes[0]) {
|
|
22125
|
+
blocked.push(nodes[0].id);
|
|
22126
|
+
return { runId: randomUUID(), nodes: [], edges: [] };
|
|
22127
|
+
}
|
|
22128
|
+
if (!timedOut && !refusedAtEdge || nodes.length <= 1) throw err2;
|
|
22129
|
+
const mid = Math.ceil(nodes.length / 2);
|
|
22130
|
+
const a = await this.ingestWithSplit(batch, nodes.slice(0, mid), edges, blocked);
|
|
22131
|
+
const b = await this.ingestWithSplit(batch, nodes.slice(mid), [], blocked);
|
|
22132
|
+
return {
|
|
22133
|
+
runId: a.runId,
|
|
22134
|
+
nodes: [...a.nodes, ...b.nodes],
|
|
22135
|
+
edges: [...a.edges, ...b.edges],
|
|
22136
|
+
...a.patternReconciliation || b.patternReconciliation ? { patternReconciliation: { ...a.patternReconciliation, ...b.patternReconciliation } } : {}
|
|
22137
|
+
};
|
|
22138
|
+
}
|
|
22139
|
+
}
|
|
21992
22140
|
/**
|
|
21993
22141
|
* SDK v2 graph-native write. It remembers a typed diagnostic spine through
|
|
21994
22142
|
* `/v2/ingest` instead of the legacy forum question/answer routes:
|
|
@@ -22190,10 +22338,13 @@ var init_client = __esm({
|
|
|
22190
22338
|
}
|
|
22191
22339
|
/** Pull cloud-induced skills + the session-bootstrap prime payload. `seed` is the
|
|
22192
22340
|
* canonical ids of the agent's recent problems — the cloud ranks the skills whose
|
|
22193
|
-
* motifs those problems match first (the rest are baseline).
|
|
22194
|
-
|
|
22341
|
+
* motifs those problems match first (the rest are baseline). `techSeed`
|
|
22342
|
+
* (CL-tech-bridge) is the workspace's spine tech ids (`lang:<slug>`, purls) —
|
|
22343
|
+
* a secondary relevance signal that also covers the zero-problem cold start. */
|
|
22344
|
+
async getSkills(context = "daemon skill sync", seed, techSeed) {
|
|
22195
22345
|
const qs = new URLSearchParams({ context });
|
|
22196
22346
|
if (seed && seed.length > 0) qs.set("seed", seed.join(","));
|
|
22347
|
+
if (techSeed && techSeed.length > 0) qs.set("techSeed", techSeed.join(","));
|
|
22197
22348
|
if (this.daemonVersion) qs.set("daemonVersion", this.daemonVersion);
|
|
22198
22349
|
if (this.daemonChannel) qs.set("daemonChannel", this.daemonChannel);
|
|
22199
22350
|
return this.json("GET", `/v2/skills?${qs.toString()}`);
|
|
@@ -22279,15 +22430,43 @@ var init_client = __esm({
|
|
|
22279
22430
|
const token = opts?.bearerToken ?? await this.authToken();
|
|
22280
22431
|
if (token) headers["authorization"] = `Bearer ${token}`;
|
|
22281
22432
|
}
|
|
22433
|
+
let payload;
|
|
22434
|
+
if (body2 !== void 0) {
|
|
22435
|
+
const raw2 = JSON.stringify(body2);
|
|
22436
|
+
if (this.compressRequests && Buffer.byteLength(raw2) >= COMPRESS_MIN_BYTES) {
|
|
22437
|
+
payload = gzipSync(Buffer.from(raw2));
|
|
22438
|
+
headers["content-encoding"] = "gzip";
|
|
22439
|
+
} else {
|
|
22440
|
+
payload = raw2;
|
|
22441
|
+
}
|
|
22442
|
+
}
|
|
22282
22443
|
const ac = new AbortController();
|
|
22283
22444
|
const tid = setTimeout(() => ac.abort(), this.timeoutMs);
|
|
22284
22445
|
try {
|
|
22285
22446
|
const res = await this.fetchFn(url2, {
|
|
22286
22447
|
method,
|
|
22287
22448
|
headers,
|
|
22288
|
-
body:
|
|
22449
|
+
body: payload,
|
|
22289
22450
|
signal: ac.signal
|
|
22290
22451
|
});
|
|
22452
|
+
if (res.status === 400 && headers["content-encoding"] === "gzip") {
|
|
22453
|
+
delete headers["content-encoding"];
|
|
22454
|
+
const retry = await this.fetchFn(url2, {
|
|
22455
|
+
method,
|
|
22456
|
+
headers,
|
|
22457
|
+
body: JSON.stringify(body2),
|
|
22458
|
+
signal: ac.signal
|
|
22459
|
+
});
|
|
22460
|
+
if (!retry.ok) {
|
|
22461
|
+
const text = await retry.text();
|
|
22462
|
+
throw new CloudError(
|
|
22463
|
+
`${method} ${path2} failed: HTTP ${retry.status} ${text.slice(0, 200)}`,
|
|
22464
|
+
retry.status
|
|
22465
|
+
);
|
|
22466
|
+
}
|
|
22467
|
+
this.compressRequests = false;
|
|
22468
|
+
return await retry.json();
|
|
22469
|
+
}
|
|
22291
22470
|
if (!res.ok) {
|
|
22292
22471
|
const text = await res.text();
|
|
22293
22472
|
throw new CloudError(
|
|
@@ -25201,15 +25380,17 @@ function triageBullet(ref) {
|
|
|
25201
25380
|
}
|
|
25202
25381
|
function linkBullet(ref) {
|
|
25203
25382
|
return [
|
|
25204
|
-
|
|
25383
|
+
// Lead-in states the trigger without the old "resembles prior knowledge"
|
|
25384
|
+
// conditional (which gated the whole bullet on recognising a prior). Kept to ONE
|
|
25385
|
+
// line on purpose: Cycle 13 measured this instruction at 55% of the 9,000-char
|
|
25386
|
+
// passive-context budget, and every char here is paid for by evicting a prior the
|
|
25387
|
+
// agent could have cited — so unproven framing is a net loss, however good it reads.
|
|
25388
|
+
" \u2022 you flagged a problem \u2014 LINK it, ESPECIALLY when the finding feels novel to here:",
|
|
25205
25389
|
` \xB7 (instance:[${ref}],[another-prior],\u2026) \u2014 the priors this problem is an instance of.`,
|
|
25206
25390
|
" Aim for ~3 DIFFERENT relevant priors; one link is weak, three triangulate it.",
|
|
25207
25391
|
` \xB7 ${TAG_EXAMPLE.pattern()} \u2014 name the general shape it instantiates (e.g. (pattern: unbounded`,
|
|
25208
25392
|
" queue growth under backpressure)) \u2014 this works with NO code anchor, and two agents naming",
|
|
25209
25393
|
" the same pattern converge on one node. Cite a shown Pattern by handle: (pattern:[handle]).",
|
|
25210
|
-
` \xB7 ${TAG_EXAMPLE.domain()} \u2014 name the ABSTRACT AREA it's about (e.g. (domain: Observability),`,
|
|
25211
|
-
" (domain: Community Detection)). An abstract problem with no code anchor NEEDS this or it's",
|
|
25212
|
-
" an invisible island: the Domain is the topic other problems in the area cluster on. Title Case.",
|
|
25213
25394
|
` \xB7 ${TAG_EXAMPLE.package()} \u2014 the PUBLIC PACKAGE the problem is about, even when this`,
|
|
25214
25395
|
" workspace doesn't depend on it (e.g. (package: chokidar), (package: pypi/requests)) \u2014 a",
|
|
25215
25396
|
" public-registry anchor lets the knowledge cross to the collective; internal names stay private.",
|
|
@@ -25233,9 +25414,9 @@ function attemptBullet() {
|
|
|
25233
25414
|
].join("\n");
|
|
25234
25415
|
}
|
|
25235
25416
|
function buildAgentInstruction(opts = {}) {
|
|
25236
|
-
const signals = opts.signals ?? ["prior", "problem", "fix", "constraint", "triage", "attempt", "link"];
|
|
25417
|
+
const signals = opts.signals ?? ["prior", "problem", "domain", "fix", "constraint", "triage", "attempt", "link"];
|
|
25237
25418
|
const ref = opts.referent ?? "its-handle";
|
|
25238
|
-
const token = (s) => s === "problem" ? TAG_EXAMPLE.problem() : s === "constraint" ? TAG_EXAMPLE.constraint() : s === "attempt" ? TAG_EXAMPLE.tried() : s === "link" ? TAG_EXAMPLE.pattern() : TAG_EXAMPLE[s](ref);
|
|
25419
|
+
const token = (s) => s === "problem" ? TAG_EXAMPLE.problem() : s === "constraint" ? TAG_EXAMPLE.constraint() : s === "attempt" ? TAG_EXAMPLE.tried() : s === "domain" ? TAG_EXAMPLE.domain() : s === "link" ? TAG_EXAMPLE.pattern() : TAG_EXAMPLE[s](ref);
|
|
25239
25420
|
const head2 = `We tag the priors we show you with a short handle like [${opts.handleExample ?? "chokidar-glob"}]. This is a capture protocol, not optional notes: what you DON'T tag is silently lost \u2014 the tag is the ONLY thing recorded. Whenever one of these is true, wrap it inline in your prose (no fences, no extra calls). Tag every one you state; we filter downstream, so don't self-censor or batch:`;
|
|
25240
25421
|
const tail = "Mid-turn text (between tool calls) can be dropped by the harness \u2014 if a tag above appeared only mid-turn, RESTATE it in your final message of the turn; the final message always survives.";
|
|
25241
25422
|
return [
|
|
@@ -25301,6 +25482,22 @@ var init_agent_signals = __esm({
|
|
|
25301
25482
|
constraint: "you made a design decision because requirements conflict or something's constrained \u2014 tag the tension, ESPECIALLY when your fix makes it 'only look' contradictory (a clean solution still hides a real trap the next agent needs)",
|
|
25302
25483
|
triage: "you diagnosed a bug \u2014 SPLIT the symptom (what breaks \u2192 [!\u2026]) from the cause (the mechanism you'd change \u2192 (cause:\u2026)); if the line says WHY it breaks, it's a cause, not a problem",
|
|
25303
25484
|
attempt: "you're attempting an approach, or an attempt didn't pan out \u2014 (tried: \u2026) / (failed: \u2026); a failed attempt rules out a path for the next agent",
|
|
25485
|
+
// DOMAIN — promoted to a FIRST-CLASS signal (was nested inside linkBullet, whose
|
|
25486
|
+
// lead-in gates on "a problem you flagged resembles prior knowledge"). Naming the
|
|
25487
|
+
// area a problem is about is unconditional and has nothing to do with recognizing
|
|
25488
|
+
// a prior, so that gate silently suppressed it: field census 2026-07-30 across 387
|
|
25489
|
+
// sessions — `domain` in 10/194 tag-emitting sessions (5%) against `problem` in
|
|
25490
|
+
// 124/194 (64%), and bimodal (the 10 that do emit average ~7 each), i.e. the
|
|
25491
|
+
// syntax is fine and the trigger was never reached.
|
|
25492
|
+
//
|
|
25493
|
+
// The gloss carries the anti-performance framing Cycles 9–11 proved is the actual
|
|
25494
|
+
// lever (the tag alone scored 0 = control; naming the bias and inverting it is
|
|
25495
|
+
// what lifted capture). The bias here is the mirror of the constraint one: an
|
|
25496
|
+
// agent that has just named a problem *precisely* feels the precision IS the
|
|
25497
|
+
// contribution, so stating the general area reads as vague restatement — exactly
|
|
25498
|
+
// when it matters most, because precise wording is what makes a problem
|
|
25499
|
+
// unfindable to anyone who doesn't already share it.
|
|
25500
|
+
domain: "you flagged a problem that isn't about one specific file \u2014 name the AREA it's about. The sharper your wording, the LESS anyone else will search for it; an abstract problem with no area is an invisible island. Title Case",
|
|
25304
25501
|
link: "the problem you flagged is an instance of priors/abstractions you can name \u2014 (instance:[h1],[h2],\u2026) / (pattern: \u2026) / (aids:[h]); rendered whole via linkBullet"
|
|
25305
25502
|
};
|
|
25306
25503
|
}
|
|
@@ -25903,6 +26100,33 @@ function federationHint(status) {
|
|
|
25903
26100
|
return void 0;
|
|
25904
26101
|
}
|
|
25905
26102
|
}
|
|
26103
|
+
function hasCloudNodeShape(id) {
|
|
26104
|
+
return CANONICAL_KNOWLEDGE_ID.test(id) || UUID_ID.test(id) || id.startsWith("pkg:");
|
|
26105
|
+
}
|
|
26106
|
+
async function collectiveDrilldown(cloud, seedId, limit) {
|
|
26107
|
+
try {
|
|
26108
|
+
const res = await cloud.search({ stack: [], domains: [], kinds: [], seed: [seedId], limit });
|
|
26109
|
+
if (res.nodes.length === 0) return null;
|
|
26110
|
+
return {
|
|
26111
|
+
seed: seedId,
|
|
26112
|
+
nodes: res.nodes.map((n) => ({
|
|
26113
|
+
id: n.id,
|
|
26114
|
+
label: n.label,
|
|
26115
|
+
name: n.description,
|
|
26116
|
+
score: Number((n.extractionConfidence ?? 0.5).toFixed(4)),
|
|
26117
|
+
hops: 1,
|
|
26118
|
+
provenance: "collective"
|
|
26119
|
+
})),
|
|
26120
|
+
edges: res.edges.map((e) => ({ from: e.from, to: e.to, type: e.type }))
|
|
26121
|
+
};
|
|
26122
|
+
} catch (err2) {
|
|
26123
|
+
console.error(
|
|
26124
|
+
"[errata] collectiveDrilldown: cloud fallback failed \u2014",
|
|
26125
|
+
err2 instanceof Error ? err2.message : err2
|
|
26126
|
+
);
|
|
26127
|
+
return null;
|
|
26128
|
+
}
|
|
26129
|
+
}
|
|
25906
26130
|
function formatDualNodes(merged) {
|
|
25907
26131
|
return merged.nodes.map((n) => ({
|
|
25908
26132
|
id: n.id,
|
|
@@ -25922,7 +26146,7 @@ function formatDualNodes(merged) {
|
|
|
25922
26146
|
} : {}
|
|
25923
26147
|
}));
|
|
25924
26148
|
}
|
|
25925
|
-
var BRIDGE_LABELS;
|
|
26149
|
+
var BRIDGE_LABELS, CANONICAL_KNOWLEDGE_ID, UUID_ID;
|
|
25926
26150
|
var init_dual_augment = __esm({
|
|
25927
26151
|
"src/dual-augment.ts"() {
|
|
25928
26152
|
"use strict";
|
|
@@ -25930,6 +26154,8 @@ var init_dual_augment = __esm({
|
|
|
25930
26154
|
init_generalize_graph();
|
|
25931
26155
|
init_dual_burst();
|
|
25932
26156
|
BRIDGE_LABELS = new Set(SEMANTIC_NODE_LABELS);
|
|
26157
|
+
CANONICAL_KNOWLEDGE_ID = /^[a-z][a-z0-9]*_[0-9a-f]{8,}$/;
|
|
26158
|
+
UUID_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
25933
26159
|
}
|
|
25934
26160
|
});
|
|
25935
26161
|
|
|
@@ -26424,6 +26650,7 @@ function mergeProblemsByEmbedding(store, opts) {
|
|
|
26424
26650
|
if (b.id === a.id || consumed.has(b.id)) continue;
|
|
26425
26651
|
if (b.embedding.length !== a.embedding.length) continue;
|
|
26426
26652
|
if ((a.attrs["embeddingVersion"] ?? "") !== (b.attrs["embeddingVersion"] ?? "")) continue;
|
|
26653
|
+
if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
|
|
26427
26654
|
if (cosine(a.embedding, b.embedding) >= minCosine) {
|
|
26428
26655
|
cluster.push(b);
|
|
26429
26656
|
consumed.add(b.id);
|
|
@@ -36845,6 +37072,12 @@ function cosine3(a, b) {
|
|
|
36845
37072
|
for (let i2 = 0; i2 < a.length; i2++) dot += a[i2] * b[i2];
|
|
36846
37073
|
return dot;
|
|
36847
37074
|
}
|
|
37075
|
+
async function collectiveSeedFallback(args2, ctx, defaultLimit) {
|
|
37076
|
+
const seedId = typeof args2["nodeId"] === "string" ? args2["nodeId"] : null;
|
|
37077
|
+
if (!seedId || !ctx.cloud || !hasCloudNodeShape(seedId)) return null;
|
|
37078
|
+
const limit = Math.max(1, Math.min(100, Number(args2["limit"] ?? defaultLimit)));
|
|
37079
|
+
return collectiveDrilldown(ctx.cloud, seedId, limit);
|
|
37080
|
+
}
|
|
36848
37081
|
function resolveSymbol2(store, args2) {
|
|
36849
37082
|
const id = args2["nodeId"];
|
|
36850
37083
|
if (typeof id === "string" && store.getNode(id)) return id;
|
|
@@ -37195,7 +37428,7 @@ var init_mcp = __esm({
|
|
|
37195
37428
|
},
|
|
37196
37429
|
{
|
|
37197
37430
|
name: "errata.neighbors",
|
|
37198
|
-
description: "Return direct in/out edges of a node. Pass either a nodeId or a qname (e.g. ClassName.method) \u2014 resolved like the other nav verbs. Use after errata.search or errata.locate to walk the graph.",
|
|
37431
|
+
description: "Return direct in/out edges of a node. Pass either a nodeId or a qname (e.g. ClassName.method) \u2014 resolved like the other nav verbs. Use after errata.search or errata.locate to walk the graph. A cloud id from search's collective blend (e.g. dprob_\u2026) that isn't in the local graph falls back to its collective neighborhood, tagged provenance:collective.",
|
|
37199
37432
|
inputSchema: {
|
|
37200
37433
|
type: "object",
|
|
37201
37434
|
properties: {
|
|
@@ -37204,9 +37437,27 @@ var init_mcp = __esm({
|
|
|
37204
37437
|
limit: { type: "number", description: "Max edges per direction (default 30)" }
|
|
37205
37438
|
}
|
|
37206
37439
|
},
|
|
37207
|
-
handler: (args2, store) => {
|
|
37440
|
+
handler: async (args2, store, ctx) => {
|
|
37208
37441
|
const id = resolveSymbol2(store, args2);
|
|
37209
|
-
if (!id)
|
|
37442
|
+
if (!id) {
|
|
37443
|
+
const cn = await collectiveSeedFallback(args2, ctx, 30);
|
|
37444
|
+
if (cn) {
|
|
37445
|
+
return {
|
|
37446
|
+
found: true,
|
|
37447
|
+
node: { id: cn.seed, provenance: "collective" },
|
|
37448
|
+
collectiveReachable: true,
|
|
37449
|
+
collectiveStatus: "cloud-seed",
|
|
37450
|
+
// Seed-touching edge types aren't on the wire (the server's seed
|
|
37451
|
+
// burst excludes the seed itself, and edges to nodes outside the
|
|
37452
|
+
// result set are dropped), so the collective fallback reports the
|
|
37453
|
+
// 1-hop neighborhood + its internal topology instead of the local
|
|
37454
|
+
// contract's typed out/in lists.
|
|
37455
|
+
nearby: cn.nodes,
|
|
37456
|
+
edges: cn.edges
|
|
37457
|
+
};
|
|
37458
|
+
}
|
|
37459
|
+
return unresolved(args2);
|
|
37460
|
+
}
|
|
37210
37461
|
const limit = Math.max(1, Math.min(200, Number(args2["limit"] ?? 30)));
|
|
37211
37462
|
const node2 = store.getNode(id);
|
|
37212
37463
|
if (!node2) return { found: false, reason: "not-found" };
|
|
@@ -37253,32 +37504,17 @@ var init_mcp = __esm({
|
|
|
37253
37504
|
handler: async (args2, store, ctx) => {
|
|
37254
37505
|
const id = resolveSymbol2(store, args2);
|
|
37255
37506
|
if (!id) {
|
|
37256
|
-
const
|
|
37257
|
-
if (
|
|
37258
|
-
|
|
37259
|
-
|
|
37260
|
-
|
|
37261
|
-
|
|
37262
|
-
|
|
37263
|
-
|
|
37264
|
-
|
|
37265
|
-
|
|
37266
|
-
|
|
37267
|
-
seed: seedId,
|
|
37268
|
-
seedProvenance: "collective",
|
|
37269
|
-
collectiveReachable: true,
|
|
37270
|
-
collectiveStatus: "cloud-seed",
|
|
37271
|
-
count: res.nodes.length,
|
|
37272
|
-
results: res.nodes.map((n) => ({
|
|
37273
|
-
id: n.id,
|
|
37274
|
-
label: n.label,
|
|
37275
|
-
name: n.description,
|
|
37276
|
-
score: Number((n.extractionConfidence ?? 0.5).toFixed(4)),
|
|
37277
|
-
hops: 1,
|
|
37278
|
-
provenance: "collective"
|
|
37279
|
-
}))
|
|
37280
|
-
};
|
|
37281
|
-
}
|
|
37507
|
+
const cn = await collectiveSeedFallback(args2, ctx, 20);
|
|
37508
|
+
if (cn) {
|
|
37509
|
+
return {
|
|
37510
|
+
seed: cn.seed,
|
|
37511
|
+
seedProvenance: "collective",
|
|
37512
|
+
collectiveReachable: true,
|
|
37513
|
+
collectiveStatus: "cloud-seed",
|
|
37514
|
+
count: cn.nodes.length,
|
|
37515
|
+
results: cn.nodes,
|
|
37516
|
+
edges: cn.edges
|
|
37517
|
+
};
|
|
37282
37518
|
}
|
|
37283
37519
|
return unresolved(args2);
|
|
37284
37520
|
}
|
|
@@ -37435,7 +37671,7 @@ var init_mcp = __esm({
|
|
|
37435
37671
|
},
|
|
37436
37672
|
{
|
|
37437
37673
|
name: "errata.why",
|
|
37438
|
-
description: "Causal/resolution neighborhood along the causal edge families (CAUSED_BY, MANIFESTS_AS, FIXED_BY, SOLVED_BY, \u2026), walked in both directions. For a Problem this surfaces its root causes AND solutions; for a Solution, the problems it resolves. Merges the machine-wide shared store's triage layer (cause chains from inline `<-` diagnoses land there, keyed by the same content-derived problem id). Ranked by accumulated conductance. Pass nodeId or qname. Returns {seedId, nodes:[{id,label,description,hops,edgeType,relevance}]}.",
|
|
37674
|
+
description: "Causal/resolution neighborhood along the causal edge families (CAUSED_BY, MANIFESTS_AS, FIXED_BY, SOLVED_BY, \u2026), walked in both directions. For a Problem this surfaces its root causes AND solutions; for a Solution, the problems it resolves. Merges the machine-wide shared store's triage layer (cause chains from inline `<-` diagnoses land there, keyed by the same content-derived problem id). Ranked by accumulated conductance. Pass nodeId or qname. Returns {seedId, nodes:[{id,label,description,hops,edgeType,relevance}]}. A cloud id from search's collective blend (e.g. dprob_\u2026) that isn't in the local graph falls back to its collective causal neighborhood, tagged provenance:collective.",
|
|
37439
37675
|
inputSchema: {
|
|
37440
37676
|
type: "object",
|
|
37441
37677
|
properties: {
|
|
@@ -37445,9 +37681,30 @@ var init_mcp = __esm({
|
|
|
37445
37681
|
limit: { type: "number", description: "Default 30" }
|
|
37446
37682
|
}
|
|
37447
37683
|
},
|
|
37448
|
-
handler: (args2, store) => {
|
|
37684
|
+
handler: async (args2, store, ctx) => {
|
|
37449
37685
|
const id = resolveSymbol2(store, args2);
|
|
37450
|
-
if (!id)
|
|
37686
|
+
if (!id) {
|
|
37687
|
+
const cn = await collectiveSeedFallback(args2, ctx, 30);
|
|
37688
|
+
if (cn) {
|
|
37689
|
+
return {
|
|
37690
|
+
found: true,
|
|
37691
|
+
seedId: cn.seed,
|
|
37692
|
+
seedProvenance: "collective",
|
|
37693
|
+
collectiveReachable: true,
|
|
37694
|
+
collectiveStatus: "cloud-seed",
|
|
37695
|
+
nodes: cn.nodes.map((n) => ({
|
|
37696
|
+
id: n.id,
|
|
37697
|
+
label: n.label,
|
|
37698
|
+
description: n.name,
|
|
37699
|
+
hops: n.hops,
|
|
37700
|
+
relevance: n.score,
|
|
37701
|
+
provenance: n.provenance
|
|
37702
|
+
})),
|
|
37703
|
+
edges: cn.edges
|
|
37704
|
+
};
|
|
37705
|
+
}
|
|
37706
|
+
return unresolved(args2);
|
|
37707
|
+
}
|
|
37451
37708
|
const maxHops = Math.max(1, Math.min(8, Number(args2["maxHops"] ?? 4)));
|
|
37452
37709
|
const limit = Math.max(1, Math.min(100, Number(args2["limit"] ?? 30)));
|
|
37453
37710
|
const local = causalChain(store, { seedId: id, direction: "both", maxHops, limit });
|
|
@@ -37630,11 +37887,11 @@ var init_mcp = __esm({
|
|
|
37630
37887
|
},
|
|
37631
37888
|
{
|
|
37632
37889
|
name: "errata.problems",
|
|
37633
|
-
description: "List the workspace's Problem backlog \u2014 the triage view that search/why/impact can't give because they need a seed. status: 'open' (default) | 'resolved' (a real fix) | 'retracted' (false alarms \u2014 false_positive/dissolution) | 'all'. Also returns a `retracted` breakdown so the false-positive rate stays visible.",
|
|
37890
|
+
description: "List the workspace's Problem backlog \u2014 the triage view that search/why/impact can't give because they need a seed. status: 'open' (default) | 'resolved' (a real fix) | 'retracted' (false alarms \u2014 false_positive/dissolution) | 'constraint' (design TENSIONS captured via `(constraint: \u2026)` \u2014 standing context, not open work, so they're excluded from 'open') | 'all'. Also returns a `retracted` breakdown so the false-positive rate stays visible.",
|
|
37634
37891
|
inputSchema: {
|
|
37635
37892
|
type: "object",
|
|
37636
37893
|
properties: {
|
|
37637
|
-
status: { type: "string", description: "open | resolved | retracted | all (default open)" },
|
|
37894
|
+
status: { type: "string", description: "open | resolved | retracted | constraint | all (default open)" },
|
|
37638
37895
|
limit: { type: "number" }
|
|
37639
37896
|
}
|
|
37640
37897
|
},
|
|
@@ -37644,7 +37901,7 @@ var init_mcp = __esm({
|
|
|
37644
37901
|
const RETRACTIONS = /* @__PURE__ */ new Set(["false_positive", "invalid", "duplicate"]);
|
|
37645
37902
|
const rows = store.findAllVersionsByLabel("Problem").map((p) => {
|
|
37646
37903
|
const ra = p.attrs["resolvedAs"];
|
|
37647
|
-
const status = ra && RETRACTIONS.has(ra) ? ra : p.attrs["resolvedAt"] != null ? "resolved" : "open";
|
|
37904
|
+
const status = ra && RETRACTIONS.has(ra) ? ra : p.attrs["resolvedAt"] != null ? "resolved" : isConstraintProblem(p) ? "constraint" : "open";
|
|
37648
37905
|
const anchorEdge = store.outEdges(p.id, ["ANCHORED_AT"])[0];
|
|
37649
37906
|
const anchor = anchorEdge ? store.getNode(anchorEdge.to)?.description : void 0;
|
|
37650
37907
|
return {
|
|
@@ -37652,17 +37909,21 @@ var init_mcp = __esm({
|
|
|
37652
37909
|
problem: p.description,
|
|
37653
37910
|
status,
|
|
37654
37911
|
createdAt: p.createdAt,
|
|
37912
|
+
// Surfaced even when `status` is resolved/retracted, so a caller can tell
|
|
37913
|
+
// an explicitly-discharged TENSION from a fixed defect.
|
|
37914
|
+
...isConstraintProblem(p) ? { kind: "constraint" } : {},
|
|
37655
37915
|
...p.attrs["provisional"] ? { provisional: true } : {},
|
|
37656
37916
|
...p.attrs["resolvedReason"] ? { reason: String(p.attrs["resolvedReason"]) } : {},
|
|
37657
37917
|
...anchor ? { anchor } : {}
|
|
37658
37918
|
};
|
|
37659
37919
|
});
|
|
37660
|
-
const inScope = (s) => want === "all" ? true : want === "resolved" ? s === "resolved" : want === "retracted" ? RETRACTIONS.has(s) : s === "open";
|
|
37920
|
+
const inScope = (s) => want === "all" ? true : want === "resolved" ? s === "resolved" : want === "retracted" ? RETRACTIONS.has(s) : want === "constraint" ? s === "constraint" : s === "open";
|
|
37661
37921
|
const items = rows.filter((r) => inScope(r.status)).sort((a, b) => b.createdAt - a.createdAt).slice(0, limit);
|
|
37662
37922
|
const countBy = (s) => rows.filter((r) => r.status === s).length;
|
|
37663
37923
|
return {
|
|
37664
37924
|
open: countBy("open"),
|
|
37665
37925
|
resolved: countBy("resolved"),
|
|
37926
|
+
constraints: countBy("constraint"),
|
|
37666
37927
|
retracted: {
|
|
37667
37928
|
falsePositive: countBy("false_positive"),
|
|
37668
37929
|
dissolution: countBy("invalid"),
|
|
@@ -37751,7 +38012,8 @@ var init_mcp = __esm({
|
|
|
37751
38012
|
inputSchema: { type: "object", properties: {} },
|
|
37752
38013
|
handler: (_args, store) => {
|
|
37753
38014
|
const problems = store.findNodesByLabel("Problem");
|
|
37754
|
-
const open2 = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
38015
|
+
const open2 = problems.filter((p) => p.attrs["resolvedAt"] == null && !isConstraintProblem(p));
|
|
38016
|
+
const constraints = problems.filter((p) => p.attrs["resolvedAt"] == null && isConstraintProblem(p));
|
|
37755
38017
|
const noFix = open2.filter((p) => store.outEdges(p.id, ["SOLVED_BY"]).length === 0);
|
|
37756
38018
|
const allProblems = store.findAllVersionsByLabel("Problem");
|
|
37757
38019
|
const ungroundedDerived = store.findNodesByLabel("Claim").filter((c) => c.attrs["crystallized"] === "derived" && Number(c.attrs["groundedSupport"] ?? 0) === 0);
|
|
@@ -37761,6 +38023,7 @@ var init_mcp = __esm({
|
|
|
37761
38023
|
strandedSymbols: findStrandedSymbols(store).length,
|
|
37762
38024
|
openProblems: open2.length,
|
|
37763
38025
|
openProblemsWithoutFix: noFix.length,
|
|
38026
|
+
designConstraints: constraints.length,
|
|
37764
38027
|
retractedFalsePositive: allProblems.filter((p) => p.attrs["resolvedAs"] === "false_positive").length,
|
|
37765
38028
|
retractedDissolution: allProblems.filter((p) => p.attrs["resolvedAs"] === "invalid").length,
|
|
37766
38029
|
ungroundedDerivedClaims: ungroundedDerived.length,
|
|
@@ -38332,7 +38595,7 @@ function formatProblemsMd(r) {
|
|
|
38332
38595
|
const lines = [head("problems")];
|
|
38333
38596
|
const ret = r.retracted ?? { falsePositive: 0, dissolution: 0, duplicate: 0 };
|
|
38334
38597
|
lines.push(
|
|
38335
|
-
`open ${r.open} \xB7 resolved ${r.resolved} \xB7 retracted: ${ret.falsePositive} fp / ${ret.dissolution} dissolution / ${ret.duplicate} dup`
|
|
38598
|
+
`open ${r.open} \xB7 resolved ${r.resolved}` + (r.constraints ? ` \xB7 constraints ${r.constraints}` : "") + ` \xB7 retracted: ${ret.falsePositive} fp / ${ret.dissolution} dissolution / ${ret.duplicate} dup`
|
|
38336
38599
|
);
|
|
38337
38600
|
lines.push(`showing ${r.count}`);
|
|
38338
38601
|
lines.push("");
|
|
@@ -46070,10 +46333,13 @@ function recallForFile(store, relPath) {
|
|
|
46070
46333
|
recallLine(p, `${TAG_EXAMPLE.fix(p.id)} if you resolved it \xB7 ${TAG_EXAMPLE.prior(p.id)} to cite${causeCue}`)
|
|
46071
46334
|
);
|
|
46072
46335
|
}
|
|
46336
|
+
for (const c of priors.constraints.slice(0, 2)) {
|
|
46337
|
+
lines.push(recallLine(c, `design tension \u2014 hold it, don't "fix" it \xB7 ${TAG_EXAMPLE.prior(c.id)} to cite`));
|
|
46338
|
+
}
|
|
46073
46339
|
for (const n of priors.related.slice(0, 4)) {
|
|
46074
46340
|
lines.push(recallLine(n, `cite with ${TAG_EXAMPLE.prior(n.id)}`));
|
|
46075
46341
|
}
|
|
46076
|
-
const total = priors.openProblems.length + priors.related.length;
|
|
46342
|
+
const total = priors.openProblems.length + priors.constraints.length + priors.related.length;
|
|
46077
46343
|
return `errata \u2014 ${total} prior(s) recorded on this file (act only if relevant):
|
|
46078
46344
|
${lines.join("\n")}
|
|
46079
46345
|
` + buildFileRecallInstruction();
|
|
@@ -47045,12 +47311,12 @@ var init_report_render = __esm({
|
|
|
47045
47311
|
|
|
47046
47312
|
// src/cli.ts
|
|
47047
47313
|
init_src5();
|
|
47048
|
-
import { closeSync as closeSync2, existsSync as
|
|
47049
|
-
import { join as
|
|
47314
|
+
import { closeSync as closeSync2, existsSync as existsSync25, openSync as openSync2, readFileSync as readFileSync24, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
|
|
47315
|
+
import { join as join28 } from "node:path";
|
|
47050
47316
|
import { spawn as spawn3 } from "node:child_process";
|
|
47051
47317
|
|
|
47052
47318
|
// src/daemon.ts
|
|
47053
|
-
import { existsSync as
|
|
47319
|
+
import { existsSync as existsSync20, writeFileSync as writeFileSync17 } from "node:fs";
|
|
47054
47320
|
|
|
47055
47321
|
// ../../node_modules/.pnpm/@hono+node-server@1.19.11_hono@4.12.8/node_modules/@hono/node-server/dist/index.mjs
|
|
47056
47322
|
import { createServer as createServerHTTP } from "http";
|
|
@@ -47630,8 +47896,8 @@ init_config();
|
|
|
47630
47896
|
|
|
47631
47897
|
// src/engine.ts
|
|
47632
47898
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
47633
|
-
import { existsSync as
|
|
47634
|
-
import { join as
|
|
47899
|
+
import { existsSync as existsSync19, statSync as statSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync9, renameSync as renameSync3, readFileSync as readFileSync19, writeFileSync as writeFileSync16 } from "node:fs";
|
|
47900
|
+
import { join as join24, relative as relative6, sep as sep4 } from "node:path";
|
|
47635
47901
|
|
|
47636
47902
|
// ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
|
|
47637
47903
|
import { stat as statcb } from "fs";
|
|
@@ -48362,9 +48628,9 @@ var NodeFsHandler = class {
|
|
|
48362
48628
|
if (this.fsw.closed) {
|
|
48363
48629
|
return;
|
|
48364
48630
|
}
|
|
48365
|
-
const
|
|
48631
|
+
const dirname11 = sysPath.dirname(file2);
|
|
48366
48632
|
const basename5 = sysPath.basename(file2);
|
|
48367
|
-
const parent = this.fsw._getWatchedDir(
|
|
48633
|
+
const parent = this.fsw._getWatchedDir(dirname11);
|
|
48368
48634
|
let prevStats = stats;
|
|
48369
48635
|
if (parent.has(basename5))
|
|
48370
48636
|
return;
|
|
@@ -48391,7 +48657,7 @@ var NodeFsHandler = class {
|
|
|
48391
48657
|
prevStats = newStats2;
|
|
48392
48658
|
}
|
|
48393
48659
|
} catch (error48) {
|
|
48394
|
-
this.fsw._remove(
|
|
48660
|
+
this.fsw._remove(dirname11, basename5);
|
|
48395
48661
|
}
|
|
48396
48662
|
} else if (parent.has(basename5)) {
|
|
48397
48663
|
const at = newStats.atimeMs;
|
|
@@ -49496,6 +49762,39 @@ init_review2();
|
|
|
49496
49762
|
import { closeSync, existsSync as existsSync12, fstatSync, openSync, readdirSync as readdirSync5, readSync, statSync as statSync3 } from "node:fs";
|
|
49497
49763
|
import { basename as basename3, dirname as dirname8, join as join15 } from "node:path";
|
|
49498
49764
|
import { homedir as homedir3 } from "node:os";
|
|
49765
|
+
function readFrom(path2, fromByte) {
|
|
49766
|
+
let fd;
|
|
49767
|
+
try {
|
|
49768
|
+
fd = openSync(path2, "r");
|
|
49769
|
+
} catch {
|
|
49770
|
+
return { text: "", nextOffset: fromByte };
|
|
49771
|
+
}
|
|
49772
|
+
try {
|
|
49773
|
+
const size = fstatSync(fd).size;
|
|
49774
|
+
const start2 = fromByte > 0 && fromByte <= size ? fromByte : 0;
|
|
49775
|
+
const len = size - start2;
|
|
49776
|
+
if (len <= 0) return { text: "", nextOffset: size };
|
|
49777
|
+
const buf = Buffer.allocUnsafe(len);
|
|
49778
|
+
readSync(fd, buf, 0, len, start2);
|
|
49779
|
+
return { text: buf.toString("utf8"), nextOffset: size };
|
|
49780
|
+
} catch {
|
|
49781
|
+
return { text: "", nextOffset: fromByte };
|
|
49782
|
+
} finally {
|
|
49783
|
+
closeSync(fd);
|
|
49784
|
+
}
|
|
49785
|
+
}
|
|
49786
|
+
function transcriptSize(path2) {
|
|
49787
|
+
try {
|
|
49788
|
+
const fd = openSync(path2, "r");
|
|
49789
|
+
try {
|
|
49790
|
+
return fstatSync(fd).size;
|
|
49791
|
+
} finally {
|
|
49792
|
+
closeSync(fd);
|
|
49793
|
+
}
|
|
49794
|
+
} catch {
|
|
49795
|
+
return 0;
|
|
49796
|
+
}
|
|
49797
|
+
}
|
|
49499
49798
|
function readTail(path2, maxBytes) {
|
|
49500
49799
|
let fd;
|
|
49501
49800
|
try {
|
|
@@ -49573,7 +49872,13 @@ function isUserTurnBoundary(obj) {
|
|
|
49573
49872
|
return blocks.some((b) => b["type"] === "text");
|
|
49574
49873
|
}
|
|
49575
49874
|
function readAssistantTurns(transcriptPath, includeThinking = true, maxBytes = 2e6) {
|
|
49576
|
-
|
|
49875
|
+
return parseAssistantTurns(readTail(transcriptPath, maxBytes), includeThinking);
|
|
49876
|
+
}
|
|
49877
|
+
function readAssistantTurnsFrom(transcriptPath, fromByte, includeThinking = true) {
|
|
49878
|
+
const { text, nextOffset } = readFrom(transcriptPath, fromByte);
|
|
49879
|
+
return { turns: parseAssistantTurns(text, includeThinking), nextOffset };
|
|
49880
|
+
}
|
|
49881
|
+
function parseAssistantTurns(raw2, includeThinking) {
|
|
49577
49882
|
if (!raw2) return [];
|
|
49578
49883
|
const turns = [];
|
|
49579
49884
|
const lines = raw2.split(/\r?\n/);
|
|
@@ -49696,10 +50001,13 @@ init_src4();
|
|
|
49696
50001
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
49697
50002
|
var NEG = /n['']t|\b(?:not|never|no|none|neither|nor|unrelated|irrelevant|would|might|could|if)\b/i;
|
|
49698
50003
|
var CITE_GROUP_RE = /\(((?:[^()\n]|\([^()\n]*\)){1,200})\)/g;
|
|
49699
|
-
var HANDLE_RE = /\[([a-z0-9][\w-]{0,
|
|
50004
|
+
var HANDLE_RE = /\[([a-z0-9][\w-]{0,80})\]|((?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})/gi;
|
|
49700
50005
|
var FIX_PREFIX = /^\s*fix:(?:#([a-z][\w-]{0,63}))?\s*/i;
|
|
49701
50006
|
var CONSTRAINT_RE = /\(\s*constraint:\s*([^()\n]{8,}?)\s*\)/gi;
|
|
49702
50007
|
var CAUSE_PREFIX = /^\s*cause:(?:#([a-z][\w-]{0,63}))?\s*/i;
|
|
50008
|
+
var NEST2_UNIT = String.raw`(?:[^()\n]|\((?:[^()\n]|\([^()\n]*\))*\))`;
|
|
50009
|
+
var FIX_LONG_RE = new RegExp(String.raw`\(\s*fix:(?:#([a-z][\w-]{0,63}))?\s*(${NEST2_UNIT}{12,}?)\s*\)`, "gi");
|
|
50010
|
+
var CAUSE_LONG_RE = new RegExp(String.raw`\(\s*cause:(?:#([a-z][\w-]{0,63}))?\s*(${NEST2_UNIT}{8,}?)\s*\)`, "gi");
|
|
49703
50011
|
var CAUSE_ARROW = /\s+(?:<-|←)\s+/;
|
|
49704
50012
|
var ATTEMPT_RE = /\(\s*(tried|failed):(?:#([a-z][\w-]{0,63}))?\s*((?:[^()\n]|\([^()\n]*\)){8,}?)\s*\)/gi;
|
|
49705
50013
|
var AIDS_PREFIX = /^\s*aids:(?:#([a-z][\w-]{0,63}))?\s*/i;
|
|
@@ -49712,7 +50020,7 @@ var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{3,}?)\s*\)/gi;
|
|
|
49712
50020
|
var PACKAGE_RE = /\(\s*package:\s*([^()\n]{2,}?)\s*\)/gi;
|
|
49713
50021
|
var COMPONENT_RE = /\(\s*component:\s*([^()\n]{2,}?)\s*\)/gi;
|
|
49714
50022
|
var CAUSE_TEXT_MIN = 8;
|
|
49715
|
-
var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*([^\]\n]{8,}?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
|
|
50023
|
+
var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*((?:\[[^\]\n]*\]|[^\]\n]){8,}?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
|
|
49716
50024
|
var CONSTRAINT_MIN = 8;
|
|
49717
50025
|
function clauseBefore(sentence, idx) {
|
|
49718
50026
|
const win = sentence.slice(Math.max(0, idx - 80), idx);
|
|
@@ -49727,6 +50035,17 @@ function fixRationaleBefore(sentence, idx) {
|
|
|
49727
50035
|
const clause = m ? win.slice(m.index + 1) : win;
|
|
49728
50036
|
return clause.replace(/\s+/g, " ").trim();
|
|
49729
50037
|
}
|
|
50038
|
+
function fixRationaleAfter(sentence, endIdx) {
|
|
50039
|
+
const win = sentence.slice(endIdx, endIdx + FIX_RATIONALE_WINDOW).replace(new RegExp(CITE_GROUP_RE.source, "g"), " ").replace(new RegExp(FLAG_RE.source, "g"), " ");
|
|
50040
|
+
const stripped = win.replace(/^[\s—:–,-]+/, "");
|
|
50041
|
+
const m = /^[^.;!?—]*/.exec(stripped);
|
|
50042
|
+
return (m ? m[0] : stripped).replace(/\s+/g, " ").trim();
|
|
50043
|
+
}
|
|
50044
|
+
function deriveFixRationale(sentence, startIdx, endIdx) {
|
|
50045
|
+
const before = fixRationaleBefore(sentence, startIdx);
|
|
50046
|
+
if (before.length >= FIX_RATIONALE_MIN) return before;
|
|
50047
|
+
return fixRationaleAfter(sentence, endIdx);
|
|
50048
|
+
}
|
|
49730
50049
|
function parseInlineTags(text) {
|
|
49731
50050
|
const out2 = [];
|
|
49732
50051
|
const deFenced = text.replace(/```[\s\S]*?```/g, " ");
|
|
@@ -49740,9 +50059,11 @@ function parseInlineTags(text) {
|
|
|
49740
50059
|
(m) => /\[[!?]|\((?:fix|cause|constraint|tried|failed):|\(\[/.test(m) ? " " : m
|
|
49741
50060
|
);
|
|
49742
50061
|
const sfield = sentence.replace(/\s+/g, " ").trim().slice(0, 160);
|
|
50062
|
+
const citeMatched = /* @__PURE__ */ new Set();
|
|
49743
50063
|
CITE_GROUP_RE.lastIndex = 0;
|
|
49744
50064
|
let g;
|
|
49745
50065
|
while ((g = CITE_GROUP_RE.exec(sentence)) !== null) {
|
|
50066
|
+
citeMatched.add(g.index);
|
|
49746
50067
|
let content = g[1];
|
|
49747
50068
|
if (/^\s*pattern:/i.test(content)) continue;
|
|
49748
50069
|
const fixM = FIX_PREFIX.exec(content);
|
|
@@ -49796,7 +50117,7 @@ function parseInlineTags(text) {
|
|
|
49796
50117
|
if (!isFix && NEG.test(clauseBefore(sentence, g.index))) continue;
|
|
49797
50118
|
let fixRationale;
|
|
49798
50119
|
if (isFix) {
|
|
49799
|
-
const r =
|
|
50120
|
+
const r = deriveFixRationale(sentence, g.index, g.index + g[0].length);
|
|
49800
50121
|
if (r.length >= FIX_RATIONALE_MIN) fixRationale = r;
|
|
49801
50122
|
}
|
|
49802
50123
|
HANDLE_RE.lastIndex = 0;
|
|
@@ -49821,6 +50142,55 @@ function parseInlineTags(text) {
|
|
|
49821
50142
|
out2.push({ kind: "fix", fixRationale: note, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
|
|
49822
50143
|
}
|
|
49823
50144
|
}
|
|
50145
|
+
for (const pass of [
|
|
50146
|
+
{ re: FIX_LONG_RE, isFix: true },
|
|
50147
|
+
{ re: CAUSE_LONG_RE, isFix: false }
|
|
50148
|
+
]) {
|
|
50149
|
+
pass.re.lastIndex = 0;
|
|
50150
|
+
let lm;
|
|
50151
|
+
while ((lm = pass.re.exec(sentence)) !== null) {
|
|
50152
|
+
if (citeMatched.has(lm.index)) continue;
|
|
50153
|
+
const verbThread = lm[1] ?? void 0;
|
|
50154
|
+
const content = lm[2];
|
|
50155
|
+
HANDLE_RE.lastIndex = 0;
|
|
50156
|
+
const h = HANDLE_RE.exec(content);
|
|
50157
|
+
const handle2 = h ? h[1] ?? h[2] : void 0;
|
|
50158
|
+
if (!pass.isFix) {
|
|
50159
|
+
if (handle2) {
|
|
50160
|
+
out2.push({ kind: "triage", handle: handle2, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
|
|
50161
|
+
} else {
|
|
50162
|
+
const causeText = content.replace(/\s+/g, " ").trim();
|
|
50163
|
+
if (causeText.length >= CAUSE_TEXT_MIN)
|
|
50164
|
+
out2.push({ kind: "triage", causeText, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
|
|
50165
|
+
}
|
|
50166
|
+
continue;
|
|
50167
|
+
}
|
|
50168
|
+
if (handle2) {
|
|
50169
|
+
const inGroup = content.replace(new RegExp(HANDLE_RE.source, "gi"), " ").replace(/\s+/g, " ").trim();
|
|
50170
|
+
const r = inGroup.length >= FIX_RATIONALE_MIN ? inGroup : deriveFixRationale(sentence, lm.index, lm.index + lm[0].length);
|
|
50171
|
+
let emitted = false;
|
|
50172
|
+
HANDLE_RE.lastIndex = 0;
|
|
50173
|
+
let hh;
|
|
50174
|
+
while ((hh = HANDLE_RE.exec(content)) !== null) {
|
|
50175
|
+
const hcap = hh[1] ?? hh[2];
|
|
50176
|
+
if (hcap) {
|
|
50177
|
+
emitted = true;
|
|
50178
|
+
out2.push({
|
|
50179
|
+
kind: "fix",
|
|
50180
|
+
handle: hcap,
|
|
50181
|
+
sentence: sfield,
|
|
50182
|
+
...r.length >= FIX_RATIONALE_MIN ? { fixRationale: r } : {},
|
|
50183
|
+
...verbThread ? { threadId: verbThread } : {}
|
|
50184
|
+
});
|
|
50185
|
+
}
|
|
50186
|
+
}
|
|
50187
|
+
if (emitted) continue;
|
|
50188
|
+
}
|
|
50189
|
+
const note = content.replace(/\s+/g, " ").trim();
|
|
50190
|
+
if (note.length >= FIX_RATIONALE_MIN)
|
|
50191
|
+
out2.push({ kind: "fix", fixRationale: note, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
|
|
50192
|
+
}
|
|
50193
|
+
}
|
|
49824
50194
|
FLAG_RE.lastIndex = 0;
|
|
49825
50195
|
let f;
|
|
49826
50196
|
while ((f = FLAG_RE.exec(sentence)) !== null) {
|
|
@@ -50058,7 +50428,7 @@ function harvestInlineTags(store, text, opts) {
|
|
|
50058
50428
|
const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
|
|
50059
50429
|
const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
|
|
50060
50430
|
const tags = parseInlineTags(text);
|
|
50061
|
-
const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo"
|
|
50431
|
+
const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId })).sort((a, b) => a.seq - b.seq);
|
|
50062
50432
|
const bindSymptom = (seq, threadId) => {
|
|
50063
50433
|
if (threadId) {
|
|
50064
50434
|
const hit = symptomSeqs.find((s) => s.threadId === threadId);
|
|
@@ -50088,7 +50458,7 @@ function harvestInlineTags(store, text, opts) {
|
|
|
50088
50458
|
...tag.threadId ? { threadId: tag.threadId } : {}
|
|
50089
50459
|
});
|
|
50090
50460
|
} else if (tag.kind === "constraint") {
|
|
50091
|
-
plan.problems.push({ statement: tag.statement, kind: "
|
|
50461
|
+
plan.problems.push({ statement: tag.statement, kind: "constraint" });
|
|
50092
50462
|
} else if (tag.kind === "fix") {
|
|
50093
50463
|
if (tag.handle) {
|
|
50094
50464
|
const problemId = resolveHandle(store, tag.handle, opts.handleMap);
|
|
@@ -50249,7 +50619,15 @@ function harvestInlineTags(store, text, opts) {
|
|
|
50249
50619
|
}
|
|
50250
50620
|
return plan;
|
|
50251
50621
|
}
|
|
50252
|
-
var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set([
|
|
50622
|
+
var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set([
|
|
50623
|
+
"Problem",
|
|
50624
|
+
"Solution",
|
|
50625
|
+
"RootCause",
|
|
50626
|
+
"Pattern",
|
|
50627
|
+
"AntiPattern",
|
|
50628
|
+
"Technique",
|
|
50629
|
+
"Weakness"
|
|
50630
|
+
]);
|
|
50253
50631
|
function stampWitnessOrigin(items, origin) {
|
|
50254
50632
|
if (!origin) return [...items];
|
|
50255
50633
|
const o = digest({ p: origin }).slice(0, 12);
|
|
@@ -50300,7 +50678,10 @@ function parseRollupJson(text) {
|
|
|
50300
50678
|
...str("cause") ? { cause: str("cause") } : {},
|
|
50301
50679
|
...str("fix") ? { fix: str("fix") } : {},
|
|
50302
50680
|
...str("anchor") ? { anchor: str("anchor") } : {},
|
|
50303
|
-
|
|
50681
|
+
// Preserve `constraint` (GH-constraint-kind) — collapsing it to `problem`
|
|
50682
|
+
// here would re-arm the auto-close and the inferred fix-binding on a design
|
|
50683
|
+
// tension that arrived through the rollup path instead of the inline tag.
|
|
50684
|
+
kind: o["kind"] === "todo" ? "todo" : o["kind"] === "constraint" ? "constraint" : "problem"
|
|
50304
50685
|
});
|
|
50305
50686
|
}
|
|
50306
50687
|
return flags2.slice(0, 12);
|
|
@@ -50338,6 +50719,174 @@ ${conversation}` }]
|
|
|
50338
50719
|
};
|
|
50339
50720
|
}
|
|
50340
50721
|
|
|
50722
|
+
// src/constraint-backfill.ts
|
|
50723
|
+
init_src4();
|
|
50724
|
+
init_src2();
|
|
50725
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11, readdirSync as readdirSync6, writeFileSync as writeFileSync11 } from "node:fs";
|
|
50726
|
+
import { join as join16 } from "node:path";
|
|
50727
|
+
import { homedir as homedir4 } from "node:os";
|
|
50728
|
+
var BACKFILL_VERSION = 1;
|
|
50729
|
+
var EMPTY = {
|
|
50730
|
+
skipped: true,
|
|
50731
|
+
stamped: 0,
|
|
50732
|
+
detached: 0,
|
|
50733
|
+
reopened: 0,
|
|
50734
|
+
witnessed: 0,
|
|
50735
|
+
cloudTwins: [],
|
|
50736
|
+
reminted: 0,
|
|
50737
|
+
recoverable: 0
|
|
50738
|
+
};
|
|
50739
|
+
function markerPath(configDir) {
|
|
50740
|
+
return join16(configDir, "constraint-backfill.json");
|
|
50741
|
+
}
|
|
50742
|
+
function alreadyDone(configDir) {
|
|
50743
|
+
const p = markerPath(configDir);
|
|
50744
|
+
if (!existsSync13(p)) return false;
|
|
50745
|
+
try {
|
|
50746
|
+
const raw2 = JSON.parse(readFileSync11(p, "utf8"));
|
|
50747
|
+
return raw2?.version === BACKFILL_VERSION;
|
|
50748
|
+
} catch {
|
|
50749
|
+
return false;
|
|
50750
|
+
}
|
|
50751
|
+
}
|
|
50752
|
+
function replay(root) {
|
|
50753
|
+
const statements = /* @__PURE__ */ new Set();
|
|
50754
|
+
const citedByFix = /* @__PURE__ */ new Set();
|
|
50755
|
+
const dir = claudeProjectDir(root, homedir4());
|
|
50756
|
+
if (!existsSync13(dir)) return { statements, citedByFix };
|
|
50757
|
+
let names;
|
|
50758
|
+
try {
|
|
50759
|
+
names = readdirSync6(dir).filter((n) => n.endsWith(".jsonl"));
|
|
50760
|
+
} catch {
|
|
50761
|
+
return { statements, citedByFix };
|
|
50762
|
+
}
|
|
50763
|
+
for (const f of names) {
|
|
50764
|
+
let lines;
|
|
50765
|
+
try {
|
|
50766
|
+
lines = readFileSync11(join16(dir, f), "utf8").split("\n");
|
|
50767
|
+
} catch {
|
|
50768
|
+
continue;
|
|
50769
|
+
}
|
|
50770
|
+
for (const line of lines) {
|
|
50771
|
+
if (!line.trim()) continue;
|
|
50772
|
+
let rec;
|
|
50773
|
+
try {
|
|
50774
|
+
rec = JSON.parse(line);
|
|
50775
|
+
} catch {
|
|
50776
|
+
continue;
|
|
50777
|
+
}
|
|
50778
|
+
if (rec.type !== "assistant" || !Array.isArray(rec.message?.content)) continue;
|
|
50779
|
+
for (const b of rec.message.content) {
|
|
50780
|
+
if (b?.type !== "text" || typeof b.text !== "string") continue;
|
|
50781
|
+
for (const tag of parseInlineTags(b.text)) {
|
|
50782
|
+
if (tag.kind === "constraint" && tag.statement) statements.add(tag.statement);
|
|
50783
|
+
if (tag.kind === "fix") {
|
|
50784
|
+
const t = tag;
|
|
50785
|
+
if (t.handle) citedByFix.add(t.handle);
|
|
50786
|
+
if (t.threadId) citedByFix.add(t.threadId);
|
|
50787
|
+
}
|
|
50788
|
+
}
|
|
50789
|
+
}
|
|
50790
|
+
}
|
|
50791
|
+
}
|
|
50792
|
+
return { statements, citedByFix };
|
|
50793
|
+
}
|
|
50794
|
+
function backfillConstraintKind(store, opts) {
|
|
50795
|
+
if (!opts.force && !opts.dryRun && alreadyDone(opts.configDir)) return EMPTY;
|
|
50796
|
+
const { statements, citedByFix } = replay(opts.root);
|
|
50797
|
+
const report = {
|
|
50798
|
+
skipped: false,
|
|
50799
|
+
stamped: 0,
|
|
50800
|
+
detached: 0,
|
|
50801
|
+
reopened: 0,
|
|
50802
|
+
witnessed: 0,
|
|
50803
|
+
cloudTwins: [],
|
|
50804
|
+
reminted: 0,
|
|
50805
|
+
recoverable: 0
|
|
50806
|
+
};
|
|
50807
|
+
const missing = [];
|
|
50808
|
+
const seen = /* @__PURE__ */ new Set();
|
|
50809
|
+
const work = [];
|
|
50810
|
+
for (const statement of statements) {
|
|
50811
|
+
const id = designProblemId(statement);
|
|
50812
|
+
if (seen.has(id)) continue;
|
|
50813
|
+
seen.add(id);
|
|
50814
|
+
const node2 = store.getNode(id);
|
|
50815
|
+
if (!node2 || node2.label !== "Problem") {
|
|
50816
|
+
missing.push(statement);
|
|
50817
|
+
continue;
|
|
50818
|
+
}
|
|
50819
|
+
const isWitnessed = citedByFix.has(priorHandle(node2)) || citedByFix.has(id) || [...citedByFix].some((c) => c && id.startsWith(c));
|
|
50820
|
+
const fabricated = [];
|
|
50821
|
+
for (const e of store.outEdges(id, ["SOLVED_BY"])) {
|
|
50822
|
+
const sol = store.getNode(e.to);
|
|
50823
|
+
if (!sol) continue;
|
|
50824
|
+
const auto = sol.description.startsWith(AUTO_MINT_PREFIX);
|
|
50825
|
+
if (!auto && isWitnessed) report.witnessed++;
|
|
50826
|
+
else fabricated.push(sol.id);
|
|
50827
|
+
}
|
|
50828
|
+
const stamp = node2.attrs["kind"] !== "constraint";
|
|
50829
|
+
if (stamp) report.stamped++;
|
|
50830
|
+
if (fabricated.length > 0) {
|
|
50831
|
+
report.detached += fabricated.length;
|
|
50832
|
+
report.reopened++;
|
|
50833
|
+
}
|
|
50834
|
+
const cloudNodeId = node2.attrs["cloudNodeId"];
|
|
50835
|
+
if (typeof cloudNodeId === "string" && (stamp || fabricated.length > 0)) {
|
|
50836
|
+
report.cloudTwins.push(cloudNodeId);
|
|
50837
|
+
}
|
|
50838
|
+
if (stamp || fabricated.length > 0) work.push({ id, fabricated, stamp });
|
|
50839
|
+
}
|
|
50840
|
+
report.recoverable = opts.remint ? 0 : missing.length;
|
|
50841
|
+
if (opts.dryRun) {
|
|
50842
|
+
report.reminted = opts.remint ? missing.length : 0;
|
|
50843
|
+
return report;
|
|
50844
|
+
}
|
|
50845
|
+
if (opts.remint && missing.length > 0) {
|
|
50846
|
+
store.transaction(() => {
|
|
50847
|
+
for (const statement of missing) {
|
|
50848
|
+
try {
|
|
50849
|
+
const r = ingestDesignProblem(
|
|
50850
|
+
store,
|
|
50851
|
+
{ problem: statement, kind: "constraint" },
|
|
50852
|
+
{ workspaceId: "", source: `constraint-remint:v${BACKFILL_VERSION}`, ts: opts.now }
|
|
50853
|
+
);
|
|
50854
|
+
if (r.created) report.reminted++;
|
|
50855
|
+
} catch {
|
|
50856
|
+
}
|
|
50857
|
+
}
|
|
50858
|
+
});
|
|
50859
|
+
}
|
|
50860
|
+
store.transaction(() => {
|
|
50861
|
+
for (const w of work) {
|
|
50862
|
+
const node2 = store.getNode(w.id);
|
|
50863
|
+
if (!node2) continue;
|
|
50864
|
+
const attrs = { ...node2.attrs, kind: "constraint" };
|
|
50865
|
+
if (w.fabricated.length > 0) {
|
|
50866
|
+
delete attrs["resolvedAt"];
|
|
50867
|
+
delete attrs["resolvedReason"];
|
|
50868
|
+
}
|
|
50869
|
+
store.updateNode(w.id, { attrs, lastUpdatedAt: opts.now });
|
|
50870
|
+
for (const solId of w.fabricated) {
|
|
50871
|
+
for (const e of store.outEdges(w.id, ["SOLVED_BY"])) {
|
|
50872
|
+
if (e.to === solId) store.closeEdge(e.id, opts.now);
|
|
50873
|
+
}
|
|
50874
|
+
const sol = store.getNode(solId);
|
|
50875
|
+
if (sol?.description.startsWith(AUTO_MINT_PREFIX)) store.closeNode(solId, opts.now);
|
|
50876
|
+
}
|
|
50877
|
+
}
|
|
50878
|
+
});
|
|
50879
|
+
try {
|
|
50880
|
+
writeFileSync11(
|
|
50881
|
+
markerPath(opts.configDir),
|
|
50882
|
+
JSON.stringify({ version: BACKFILL_VERSION, at: opts.now, ...report, cloudTwins: report.cloudTwins.length }, null, 2),
|
|
50883
|
+
"utf8"
|
|
50884
|
+
);
|
|
50885
|
+
} catch {
|
|
50886
|
+
}
|
|
50887
|
+
return report;
|
|
50888
|
+
}
|
|
50889
|
+
|
|
50341
50890
|
// src/engine.ts
|
|
50342
50891
|
init_symbol_summaries();
|
|
50343
50892
|
init_reconcile();
|
|
@@ -50536,11 +51085,11 @@ init_outbox();
|
|
|
50536
51085
|
init_src8();
|
|
50537
51086
|
init_src();
|
|
50538
51087
|
init_src2();
|
|
50539
|
-
import { readFileSync as
|
|
50540
|
-
import { join as
|
|
51088
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
51089
|
+
import { join as join17 } from "node:path";
|
|
50541
51090
|
function loadClaimIgnorePatterns(workspaceRoot) {
|
|
50542
51091
|
try {
|
|
50543
|
-
return
|
|
51092
|
+
return readFileSync12(join17(workspaceRoot, ".errataignore"), "utf8").split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#")).map((l) => l.toLowerCase());
|
|
50544
51093
|
} catch {
|
|
50545
51094
|
return [];
|
|
50546
51095
|
}
|
|
@@ -50672,14 +51221,16 @@ function generalizePrincipleForSync(principle, level = 2) {
|
|
|
50672
51221
|
delete node2.logicalId;
|
|
50673
51222
|
return node2;
|
|
50674
51223
|
}
|
|
50675
|
-
function buildPrincipleIngest(sharedStore, daemonVersion, ignorePatterns = [], level = 2) {
|
|
51224
|
+
function buildPrincipleIngest(sharedStore, daemonVersion, ignorePatterns = [], level = 2, opts = {}) {
|
|
51225
|
+
const lang = opts.primaryLanguage?.trim().toLowerCase();
|
|
51226
|
+
const claim = lang ? { anchorVisibility: "public", anchor: `lang:${lang}` } : {};
|
|
50676
51227
|
const selected = sharedStore.findNodesByLabel("Claim").filter((n) => isSyncablePrinciple(n, ignorePatterns));
|
|
50677
51228
|
for (const n of selected) {
|
|
50678
51229
|
if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) !== ABSTRACTION_LEVEL.PRINCIPLE || n.attrs["provisional"] !== false) {
|
|
50679
51230
|
throw new Error(`buildPrincipleIngest: refusing to sync non-canonical-principle ${n.id} \u2014 C7 invariant violated`);
|
|
50680
51231
|
}
|
|
50681
51232
|
}
|
|
50682
|
-
const nodes = selected.map((n) => generalizePrincipleForSync(n, level));
|
|
51233
|
+
const nodes = selected.map((n) => ({ ...generalizePrincipleForSync(n, level), ...claim }));
|
|
50683
51234
|
if (nodes.length === 0) return null;
|
|
50684
51235
|
const base = {
|
|
50685
51236
|
daemonVersion,
|
|
@@ -50732,11 +51283,17 @@ function isSyncableRoute(edge2, shared, triage, ignorePatterns = []) {
|
|
|
50732
51283
|
].join(" ").toLowerCase();
|
|
50733
51284
|
return !ignorePatterns.some((p) => blob.includes(p));
|
|
50734
51285
|
}
|
|
50735
|
-
|
|
51286
|
+
var isScopedBucket = (bucket) => bucket.startsWith("stack:@");
|
|
51287
|
+
function filterScopedBucketKeys(rec, includeScopedBuckets) {
|
|
51288
|
+
if (includeScopedBuckets) return rec;
|
|
51289
|
+
return Object.fromEntries(Object.entries(rec).filter(([k]) => !isScopedBucket(k)));
|
|
51290
|
+
}
|
|
51291
|
+
function generalizeRouteForSync(edge2, level, includeScopedBuckets = false) {
|
|
50736
51292
|
const disc = edge2.attrs["discriminator"];
|
|
50737
51293
|
const local = edge2.attrs["perContext"] ?? {};
|
|
50738
51294
|
const perContext = {};
|
|
50739
51295
|
for (const [b, c] of Object.entries(local)) {
|
|
51296
|
+
if (!includeScopedBuckets && isScopedBucket(b)) continue;
|
|
50740
51297
|
const independent = c.independent ?? c.contributors?.length;
|
|
50741
51298
|
const inferredIndependent = c.inferredIndependent ?? c.inferredContributors?.length;
|
|
50742
51299
|
perContext[b] = {
|
|
@@ -50757,13 +51314,52 @@ function generalizeRouteForSync(edge2, level) {
|
|
|
50757
51314
|
navFailures: 0
|
|
50758
51315
|
};
|
|
50759
51316
|
}
|
|
51317
|
+
function buildBucketTokenAnchors(languages, packages = []) {
|
|
51318
|
+
const anchors = {};
|
|
51319
|
+
const langTokens = /* @__PURE__ */ new Set();
|
|
51320
|
+
for (const l of languages) {
|
|
51321
|
+
const t = canonicalizeToken(String(l));
|
|
51322
|
+
if (!t || t.includes(":") && !t.startsWith("lang:")) continue;
|
|
51323
|
+
langTokens.add(t);
|
|
51324
|
+
anchors[t] = t.startsWith("lang:") ? t : `lang:${t}`;
|
|
51325
|
+
}
|
|
51326
|
+
const versionless = (purl) => purl.replace(/@[^/@]+$/, "");
|
|
51327
|
+
const conflicted = /* @__PURE__ */ new Set();
|
|
51328
|
+
for (const p of packages) {
|
|
51329
|
+
const t = canonicalizeToken(p.name);
|
|
51330
|
+
if (!t || !p.purl || langTokens.has(t) || conflicted.has(t)) continue;
|
|
51331
|
+
const existing = anchors[t];
|
|
51332
|
+
if (existing === void 0) {
|
|
51333
|
+
anchors[t] = p.purl;
|
|
51334
|
+
} else if (versionless(existing) !== versionless(p.purl)) {
|
|
51335
|
+
delete anchors[t];
|
|
51336
|
+
conflicted.add(t);
|
|
51337
|
+
}
|
|
51338
|
+
}
|
|
51339
|
+
return anchors;
|
|
51340
|
+
}
|
|
51341
|
+
function witnessedAnchor(routes, tokenAnchors) {
|
|
51342
|
+
const weight = /* @__PURE__ */ new Map();
|
|
51343
|
+
for (const e of routes) {
|
|
51344
|
+
const perContext = e.attrs["perContext"] ?? {};
|
|
51345
|
+
for (const [bucket, c] of Object.entries(perContext)) {
|
|
51346
|
+
if (!bucket.startsWith("stack:") || !c || !(c.confirmed > 0)) continue;
|
|
51347
|
+
const anchor = tokenAnchors[bucket.slice("stack:".length)];
|
|
51348
|
+
if (anchor) weight.set(anchor, (weight.get(anchor) ?? 0) + c.confirmed);
|
|
51349
|
+
}
|
|
51350
|
+
}
|
|
51351
|
+
return [...weight.entries()].sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1))[0]?.[0];
|
|
51352
|
+
}
|
|
50760
51353
|
function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2, opts = {}) {
|
|
50761
51354
|
const lang = opts.primaryLanguage?.trim().toLowerCase();
|
|
50762
|
-
const
|
|
51355
|
+
const fallbackAnchor = lang ? `lang:${lang}` : void 0;
|
|
51356
|
+
const tokenAnchors = opts.tokenAnchors ?? {};
|
|
51357
|
+
const includeScopedBuckets = opts.includeScopedBuckets === true;
|
|
51358
|
+
const claimOf = (anchor) => anchor ? { anchorVisibility: "public", anchor } : {};
|
|
50763
51359
|
const nodes = [];
|
|
50764
51360
|
const edges = [];
|
|
50765
51361
|
const seenIds = /* @__PURE__ */ new Set();
|
|
50766
|
-
const shareEndpoint = (id) => {
|
|
51362
|
+
const shareEndpoint = (id, anchor) => {
|
|
50767
51363
|
if (seenIds.has(id)) return;
|
|
50768
51364
|
const n = shared.getNode(id);
|
|
50769
51365
|
if (!n) return;
|
|
@@ -50773,12 +51369,13 @@ function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2
|
|
|
50773
51369
|
description: generalize(n.description, { level }).text,
|
|
50774
51370
|
embedding: [],
|
|
50775
51371
|
attrs: { scope: {} },
|
|
50776
|
-
...
|
|
51372
|
+
...claimOf(anchor ?? fallbackAnchor)
|
|
50777
51373
|
});
|
|
50778
51374
|
};
|
|
50779
51375
|
for (const tri of shared.findNodesByLabel("Triage")) {
|
|
50780
51376
|
const routes = shared.outEdges(tri.id, ["CONFIRMS", "INDICATES", "ROUTES_TO"]).filter((e) => isSyncableRoute(e, shared, tri, ignorePatterns));
|
|
50781
51377
|
if (routes.length === 0) continue;
|
|
51378
|
+
const triAnchor = witnessedAnchor(routes, tokenAnchors);
|
|
50782
51379
|
if (!seenIds.has(tri.id)) {
|
|
50783
51380
|
seenIds.add(tri.id);
|
|
50784
51381
|
const statement = String(tri.attrs["statement"] ?? "");
|
|
@@ -50788,21 +51385,26 @@ function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2
|
|
|
50788
51385
|
embedding: [],
|
|
50789
51386
|
attrs: {
|
|
50790
51387
|
...statement ? { statement: generalize(statement, { level }).text } : {},
|
|
50791
|
-
...tri.attrs["perContextSeen"] ? {
|
|
51388
|
+
...tri.attrs["perContextSeen"] ? {
|
|
51389
|
+
perContextSeen: filterScopedBucketKeys(
|
|
51390
|
+
tri.attrs["perContextSeen"],
|
|
51391
|
+
includeScopedBuckets
|
|
51392
|
+
)
|
|
51393
|
+
} : {}
|
|
50792
51394
|
},
|
|
50793
|
-
...
|
|
51395
|
+
...claimOf(triAnchor ?? fallbackAnchor)
|
|
50794
51396
|
});
|
|
50795
51397
|
}
|
|
50796
51398
|
for (const tb of shared.inEdges(tri.id, ["TRIAGED_BY"])) {
|
|
50797
|
-
shareEndpoint(tb.from);
|
|
51399
|
+
shareEndpoint(tb.from, triAnchor);
|
|
50798
51400
|
edges.push({ ...tb, navSuccesses: 0, navFailures: 0 });
|
|
50799
51401
|
}
|
|
50800
51402
|
for (const e of routes) {
|
|
50801
51403
|
if (!(e.type === "CONFIRMS" || e.type === "ROUTES_TO" && e.attrs["provisional"] === false)) {
|
|
50802
51404
|
throw new Error(`buildTriageIngest: refusing to sync non-router route ${e.id} \u2014 C7 invariant violated`);
|
|
50803
51405
|
}
|
|
50804
|
-
shareEndpoint(e.to);
|
|
50805
|
-
edges.push(generalizeRouteForSync(e, level));
|
|
51406
|
+
shareEndpoint(e.to, witnessedAnchor([e], tokenAnchors));
|
|
51407
|
+
edges.push(generalizeRouteForSync(e, level, includeScopedBuckets));
|
|
50806
51408
|
}
|
|
50807
51409
|
}
|
|
50808
51410
|
if (edges.length === 0) return null;
|
|
@@ -50848,22 +51450,22 @@ async function pullCloudTriage(shared, cloud, profile, limit = 50) {
|
|
|
50848
51450
|
}
|
|
50849
51451
|
|
|
50850
51452
|
// src/git-sensor.ts
|
|
50851
|
-
import { existsSync as
|
|
50852
|
-
import { join as
|
|
51453
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13, watch as fsWatch } from "node:fs";
|
|
51454
|
+
import { join as join18 } from "node:path";
|
|
50853
51455
|
function readFirstLine(path2) {
|
|
50854
51456
|
try {
|
|
50855
|
-
return
|
|
51457
|
+
return readFileSync13(path2, "utf8").split(/\r?\n/, 1)[0].trim();
|
|
50856
51458
|
} catch {
|
|
50857
51459
|
return null;
|
|
50858
51460
|
}
|
|
50859
51461
|
}
|
|
50860
51462
|
function readGitRefState(gitDir) {
|
|
50861
|
-
const head2 = readFirstLine(
|
|
51463
|
+
const head2 = readFirstLine(join18(gitDir, "HEAD"));
|
|
50862
51464
|
const m = head2 ? /^ref:\s*refs\/heads\/(.+)$/.exec(head2) : null;
|
|
50863
51465
|
const branch = m ? m[1] : null;
|
|
50864
51466
|
let sha2 = null;
|
|
50865
51467
|
if (branch) {
|
|
50866
|
-
sha2 = readFirstLine(
|
|
51468
|
+
sha2 = readFirstLine(join18(gitDir, "refs", "heads", branch));
|
|
50867
51469
|
if (!sha2) sha2 = shaFromPackedRefs(gitDir, `refs/heads/${branch}`);
|
|
50868
51470
|
} else if (head2 && /^[0-9a-f]{7,40}$/i.test(head2)) {
|
|
50869
51471
|
sha2 = head2;
|
|
@@ -50871,13 +51473,13 @@ function readGitRefState(gitDir) {
|
|
|
50871
51473
|
return {
|
|
50872
51474
|
branch,
|
|
50873
51475
|
sha: sha2,
|
|
50874
|
-
mergeHeadExists:
|
|
50875
|
-
origHeadExists:
|
|
51476
|
+
mergeHeadExists: existsSync14(join18(gitDir, "MERGE_HEAD")),
|
|
51477
|
+
origHeadExists: existsSync14(join18(gitDir, "ORIG_HEAD"))
|
|
50876
51478
|
};
|
|
50877
51479
|
}
|
|
50878
51480
|
function shaFromPackedRefs(gitDir, ref) {
|
|
50879
51481
|
try {
|
|
50880
|
-
for (const line of
|
|
51482
|
+
for (const line of readFileSync13(join18(gitDir, "packed-refs"), "utf8").split(/\r?\n/)) {
|
|
50881
51483
|
const [sha2, name2] = line.split(/\s+/);
|
|
50882
51484
|
if (name2 === ref && sha2) return sha2;
|
|
50883
51485
|
}
|
|
@@ -50911,7 +51513,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
|
|
|
50911
51513
|
const settle = () => {
|
|
50912
51514
|
if (timer) clearTimeout(timer);
|
|
50913
51515
|
timer = setTimeout(() => {
|
|
50914
|
-
if (
|
|
51516
|
+
if (existsSync14(join18(gitDir, "index.lock"))) {
|
|
50915
51517
|
settle();
|
|
50916
51518
|
return;
|
|
50917
51519
|
}
|
|
@@ -50922,7 +51524,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
|
|
|
50922
51524
|
}, debounceMs);
|
|
50923
51525
|
};
|
|
50924
51526
|
for (const sub of ["HEAD", "logs/HEAD", "MERGE_HEAD", "ORIG_HEAD"]) {
|
|
50925
|
-
const p =
|
|
51527
|
+
const p = join18(gitDir, sub);
|
|
50926
51528
|
try {
|
|
50927
51529
|
watchers.push(fsWatch(p, settle));
|
|
50928
51530
|
} catch {
|
|
@@ -51147,21 +51749,21 @@ var TelemetryRecorder = class {
|
|
|
51147
51749
|
|
|
51148
51750
|
// src/skills.ts
|
|
51149
51751
|
import {
|
|
51150
|
-
existsSync as
|
|
51752
|
+
existsSync as existsSync15,
|
|
51151
51753
|
mkdirSync as mkdirSync6,
|
|
51152
|
-
readFileSync as
|
|
51153
|
-
readdirSync as
|
|
51754
|
+
readFileSync as readFileSync14,
|
|
51755
|
+
readdirSync as readdirSync7,
|
|
51154
51756
|
unlinkSync as unlinkSync2,
|
|
51155
|
-
writeFileSync as
|
|
51757
|
+
writeFileSync as writeFileSync12
|
|
51156
51758
|
} from "node:fs";
|
|
51157
|
-
import { basename as basename4, join as
|
|
51759
|
+
import { basename as basename4, join as join19 } from "node:path";
|
|
51158
51760
|
function skillFileName(id) {
|
|
51159
51761
|
return `${id.replace(/[^A-Za-z0-9_.-]/g, "_")}.md`;
|
|
51160
51762
|
}
|
|
51161
51763
|
function readSkillManifest(manifestPath) {
|
|
51162
|
-
if (!
|
|
51764
|
+
if (!existsSync15(manifestPath)) return [];
|
|
51163
51765
|
try {
|
|
51164
|
-
const parsed = JSON.parse(
|
|
51766
|
+
const parsed = JSON.parse(readFileSync14(manifestPath, "utf8"));
|
|
51165
51767
|
return (parsed.skills ?? []).map((s) => ({
|
|
51166
51768
|
title: s.title ?? "",
|
|
51167
51769
|
layer: s.layer ?? "technique",
|
|
@@ -51172,12 +51774,12 @@ function readSkillManifest(manifestPath) {
|
|
|
51172
51774
|
return [];
|
|
51173
51775
|
}
|
|
51174
51776
|
}
|
|
51175
|
-
async function syncSkills(paths, client, seed, pins = []) {
|
|
51176
|
-
const res = await client.getSkills(void 0, seed);
|
|
51777
|
+
async function syncSkills(paths, client, seed, pins = [], techSeed) {
|
|
51778
|
+
const res = await client.getSkills(void 0, seed, techSeed);
|
|
51177
51779
|
const staleDaemon = res.staleDaemon && res.latestDaemonVersion ? { latest: res.latestDaemonVersion } : void 0;
|
|
51178
51780
|
mkdirSync6(paths.skillsDir, { recursive: true });
|
|
51179
51781
|
if (res.skills.length === 0 && pins.length === 0) {
|
|
51180
|
-
const existing =
|
|
51782
|
+
const existing = readdirSync7(paths.skillsDir).filter((f) => f.endsWith(".md"));
|
|
51181
51783
|
if (existing.length > 0) return { written: 0, pruned: 0, ...staleDaemon ? { staleDaemon } : {} };
|
|
51182
51784
|
}
|
|
51183
51785
|
const rows = [];
|
|
@@ -51185,7 +51787,7 @@ async function syncSkills(paths, client, seed, pins = []) {
|
|
|
51185
51787
|
for (const s of res.skills) {
|
|
51186
51788
|
const fileName = skillFileName(s.id);
|
|
51187
51789
|
keep.add(fileName);
|
|
51188
|
-
|
|
51790
|
+
writeFileSync12(join19(paths.skillsDir, fileName), s.markdown, "utf8");
|
|
51189
51791
|
rows.push({
|
|
51190
51792
|
id: s.id,
|
|
51191
51793
|
title: s.title,
|
|
@@ -51198,7 +51800,7 @@ async function syncSkills(paths, client, seed, pins = []) {
|
|
|
51198
51800
|
const fileName = skillFileName(p.id);
|
|
51199
51801
|
if (keep.has(fileName)) continue;
|
|
51200
51802
|
keep.add(fileName);
|
|
51201
|
-
|
|
51803
|
+
writeFileSync12(join19(paths.skillsDir, fileName), p.markdown, "utf8");
|
|
51202
51804
|
rows.push({
|
|
51203
51805
|
id: p.id,
|
|
51204
51806
|
title: p.title,
|
|
@@ -51208,17 +51810,17 @@ async function syncSkills(paths, client, seed, pins = []) {
|
|
|
51208
51810
|
});
|
|
51209
51811
|
}
|
|
51210
51812
|
let pruned = 0;
|
|
51211
|
-
for (const f of
|
|
51813
|
+
for (const f of readdirSync7(paths.skillsDir)) {
|
|
51212
51814
|
if (!f.endsWith(".md")) continue;
|
|
51213
51815
|
if (keep.has(basename4(f))) continue;
|
|
51214
51816
|
try {
|
|
51215
|
-
unlinkSync2(
|
|
51817
|
+
unlinkSync2(join19(paths.skillsDir, f));
|
|
51216
51818
|
pruned++;
|
|
51217
51819
|
} catch {
|
|
51218
51820
|
}
|
|
51219
51821
|
}
|
|
51220
51822
|
rows.sort((a, b) => a.id.localeCompare(b.id));
|
|
51221
|
-
|
|
51823
|
+
writeFileSync12(
|
|
51222
51824
|
paths.skillsManifest,
|
|
51223
51825
|
JSON.stringify({ generatedAt: Date.now(), skills: rows }, null, 2),
|
|
51224
51826
|
"utf8"
|
|
@@ -51227,24 +51829,25 @@ async function syncSkills(paths, client, seed, pins = []) {
|
|
|
51227
51829
|
}
|
|
51228
51830
|
|
|
51229
51831
|
// src/agent-skills.ts
|
|
51832
|
+
init_src2();
|
|
51230
51833
|
import {
|
|
51231
51834
|
cpSync,
|
|
51232
|
-
existsSync as
|
|
51835
|
+
existsSync as existsSync16,
|
|
51233
51836
|
lstatSync,
|
|
51234
51837
|
mkdirSync as mkdirSync7,
|
|
51235
|
-
readFileSync as
|
|
51236
|
-
readdirSync as
|
|
51838
|
+
readFileSync as readFileSync15,
|
|
51839
|
+
readdirSync as readdirSync8,
|
|
51237
51840
|
rmSync as rmSync2,
|
|
51238
51841
|
symlinkSync,
|
|
51239
|
-
writeFileSync as
|
|
51842
|
+
writeFileSync as writeFileSync13
|
|
51240
51843
|
} from "node:fs";
|
|
51241
|
-
import { join as
|
|
51844
|
+
import { join as join20 } from "node:path";
|
|
51242
51845
|
var SKILL_NS = "errata-";
|
|
51243
51846
|
var HARNESS_SKILL_DIRS = [
|
|
51244
|
-
{ configDir: ".claude", skillsDir:
|
|
51847
|
+
{ configDir: ".claude", skillsDir: join20(".claude", "skills") },
|
|
51245
51848
|
// Cursor adopted the standard; its exact project dir is still moving — kept
|
|
51246
51849
|
// best-effort and gated on `.cursor/` presence so we never create it blind.
|
|
51247
|
-
{ configDir: ".cursor", skillsDir:
|
|
51850
|
+
{ configDir: ".cursor", skillsDir: join20(".cursor", "skills") }
|
|
51248
51851
|
];
|
|
51249
51852
|
function skillSlug(title, id) {
|
|
51250
51853
|
const base = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "skill";
|
|
@@ -51259,22 +51862,42 @@ function deriveDescription(title, layer, body2) {
|
|
|
51259
51862
|
const when = ` Apply when working on ${layer === "antipattern" ? "code that risks this pitfall" : "problems in this area"}.`;
|
|
51260
51863
|
return `${lead}${lead.endsWith(".") ? "" : "."}${when}`.slice(0, 1024);
|
|
51261
51864
|
}
|
|
51262
|
-
function renderSkillMd(name2, description, body2) {
|
|
51865
|
+
function renderSkillMd(name2, description, body2, citeHandle) {
|
|
51866
|
+
const footer = citeHandle ? `
|
|
51867
|
+
|
|
51868
|
+
---
|
|
51869
|
+
When this skill actually helps you, witness it inline in your reply: \`([${citeHandle}])\` \u2014 the citation is recorded and feeds this skill's reliability, so it keeps being served. Untagged help is invisible.
|
|
51870
|
+
` : "\n";
|
|
51263
51871
|
return `---
|
|
51264
51872
|
name: ${name2}
|
|
51265
51873
|
description: ${yamlScalar(description)}
|
|
51266
51874
|
---
|
|
51267
51875
|
|
|
51268
|
-
${body2.trimEnd()}
|
|
51269
|
-
|
|
51876
|
+
${body2.trimEnd()}${footer}`;
|
|
51877
|
+
}
|
|
51878
|
+
var LAYER_TO_LABEL = {
|
|
51879
|
+
pattern: "Pattern",
|
|
51880
|
+
antipattern: "AntiPattern",
|
|
51881
|
+
technique: "Technique",
|
|
51882
|
+
weakness: "Weakness"
|
|
51883
|
+
};
|
|
51884
|
+
function skillHandleNodes(skills) {
|
|
51885
|
+
return skills.map((s) => ({
|
|
51886
|
+
id: s.id,
|
|
51887
|
+
label: LAYER_TO_LABEL[s.layer.toLowerCase()] ?? "Pattern",
|
|
51888
|
+
description: s.title
|
|
51889
|
+
}));
|
|
51890
|
+
}
|
|
51891
|
+
function skillCiteHandle(s) {
|
|
51892
|
+
return priorHandle({ id: s.id, description: s.title });
|
|
51270
51893
|
}
|
|
51271
51894
|
function reconcileNamespaced(dir, keep) {
|
|
51272
|
-
if (!
|
|
51895
|
+
if (!existsSync16(dir)) return 0;
|
|
51273
51896
|
let pruned = 0;
|
|
51274
|
-
for (const name2 of
|
|
51897
|
+
for (const name2 of readdirSync8(dir)) {
|
|
51275
51898
|
if (!name2.startsWith(SKILL_NS) || keep.has(name2)) continue;
|
|
51276
51899
|
try {
|
|
51277
|
-
rmSync2(
|
|
51900
|
+
rmSync2(join20(dir, name2), { recursive: true, force: true });
|
|
51278
51901
|
pruned++;
|
|
51279
51902
|
} catch {
|
|
51280
51903
|
}
|
|
@@ -51283,7 +51906,7 @@ function reconcileNamespaced(dir, keep) {
|
|
|
51283
51906
|
}
|
|
51284
51907
|
function linkOrCopy(linkPath, target) {
|
|
51285
51908
|
try {
|
|
51286
|
-
if (
|
|
51909
|
+
if (existsSync16(linkPath) || safeLstat(linkPath)) rmSync2(linkPath, { recursive: true, force: true });
|
|
51287
51910
|
} catch {
|
|
51288
51911
|
}
|
|
51289
51912
|
try {
|
|
@@ -51304,7 +51927,7 @@ function safeLstat(p) {
|
|
|
51304
51927
|
}
|
|
51305
51928
|
}
|
|
51306
51929
|
function emitAndProjectSkills(root, skills) {
|
|
51307
|
-
const agentsSkillsDir =
|
|
51930
|
+
const agentsSkillsDir = join20(root, ".agents", "skills");
|
|
51308
51931
|
mkdirSync7(agentsSkillsDir, { recursive: true });
|
|
51309
51932
|
const slugs = [];
|
|
51310
51933
|
const keep = /* @__PURE__ */ new Set();
|
|
@@ -51312,7 +51935,7 @@ function emitAndProjectSkills(root, skills) {
|
|
|
51312
51935
|
for (const s of skills) {
|
|
51313
51936
|
let body2;
|
|
51314
51937
|
try {
|
|
51315
|
-
body2 =
|
|
51938
|
+
body2 = readFileSync15(s.bodyPath, "utf8");
|
|
51316
51939
|
} catch {
|
|
51317
51940
|
continue;
|
|
51318
51941
|
}
|
|
@@ -51321,10 +51944,10 @@ function emitAndProjectSkills(root, skills) {
|
|
|
51321
51944
|
keep.add(slug2);
|
|
51322
51945
|
slugs.push(slug2);
|
|
51323
51946
|
const description = deriveDescription(s.title, s.layer, body2);
|
|
51324
|
-
mkdirSync7(
|
|
51325
|
-
|
|
51326
|
-
|
|
51327
|
-
renderSkillMd(slug2, description, body2),
|
|
51947
|
+
mkdirSync7(join20(agentsSkillsDir, slug2), { recursive: true });
|
|
51948
|
+
writeFileSync13(
|
|
51949
|
+
join20(agentsSkillsDir, slug2, "SKILL.md"),
|
|
51950
|
+
renderSkillMd(slug2, description, body2, skillCiteHandle(s)),
|
|
51328
51951
|
"utf8"
|
|
51329
51952
|
);
|
|
51330
51953
|
emitted++;
|
|
@@ -51332,11 +51955,11 @@ function emitAndProjectSkills(root, skills) {
|
|
|
51332
51955
|
reconcileNamespaced(agentsSkillsDir, keep);
|
|
51333
51956
|
let projected = 0;
|
|
51334
51957
|
for (const h of HARNESS_SKILL_DIRS) {
|
|
51335
|
-
if (!
|
|
51336
|
-
const dir =
|
|
51958
|
+
if (!existsSync16(join20(root, h.configDir))) continue;
|
|
51959
|
+
const dir = join20(root, h.skillsDir);
|
|
51337
51960
|
mkdirSync7(dir, { recursive: true });
|
|
51338
51961
|
for (const slug2 of slugs) {
|
|
51339
|
-
linkOrCopy(
|
|
51962
|
+
linkOrCopy(join20(dir, slug2), join20(agentsSkillsDir, slug2));
|
|
51340
51963
|
projected++;
|
|
51341
51964
|
}
|
|
51342
51965
|
reconcileNamespaced(dir, keep);
|
|
@@ -51345,15 +51968,15 @@ function emitAndProjectSkills(root, skills) {
|
|
|
51345
51968
|
return { slugs, emitted, projected };
|
|
51346
51969
|
}
|
|
51347
51970
|
function emitInputsFromManifest(erretaDir, manifestPath) {
|
|
51348
|
-
if (!
|
|
51971
|
+
if (!existsSync16(manifestPath)) return [];
|
|
51349
51972
|
try {
|
|
51350
|
-
const parsed = JSON.parse(
|
|
51973
|
+
const parsed = JSON.parse(readFileSync15(manifestPath, "utf8"));
|
|
51351
51974
|
return (parsed.skills ?? []).filter((s) => Boolean(s.id && s.file)).map((s) => ({
|
|
51352
51975
|
id: s.id,
|
|
51353
51976
|
title: s.title ?? s.id,
|
|
51354
51977
|
layer: s.layer ?? "technique",
|
|
51355
51978
|
confidence: s.confidence ?? 0,
|
|
51356
|
-
bodyPath:
|
|
51979
|
+
bodyPath: join20(erretaDir, s.file)
|
|
51357
51980
|
}));
|
|
51358
51981
|
} catch {
|
|
51359
51982
|
return [];
|
|
@@ -51367,17 +51990,17 @@ var GITIGNORE_LINES = [
|
|
|
51367
51990
|
".cursor/skills/errata-*/"
|
|
51368
51991
|
];
|
|
51369
51992
|
function ensureSkillGitignore(root) {
|
|
51370
|
-
const path2 =
|
|
51993
|
+
const path2 = join20(root, ".gitignore");
|
|
51371
51994
|
let current = "";
|
|
51372
51995
|
try {
|
|
51373
|
-
current =
|
|
51996
|
+
current = existsSync16(path2) ? readFileSync15(path2, "utf8") : "";
|
|
51374
51997
|
} catch {
|
|
51375
51998
|
return;
|
|
51376
51999
|
}
|
|
51377
52000
|
if (current.includes(GITIGNORE_MARK)) return;
|
|
51378
52001
|
const prefix = current.length > 0 && !current.endsWith("\n") ? "\n" : "";
|
|
51379
52002
|
try {
|
|
51380
|
-
|
|
52003
|
+
writeFileSync13(path2, `${current}${prefix}
|
|
51381
52004
|
${GITIGNORE_LINES.join("\n")}
|
|
51382
52005
|
`, "utf8");
|
|
51383
52006
|
} catch {
|
|
@@ -51435,126 +52058,23 @@ function createToolRunState() {
|
|
|
51435
52058
|
// src/engine.ts
|
|
51436
52059
|
init_paths();
|
|
51437
52060
|
|
|
51438
|
-
// src/causal.ts
|
|
51439
|
-
var SUPPRESSORS = [
|
|
51440
|
-
{ kind: "ts-nocheck", re: /@ts-nocheck\b/g },
|
|
51441
|
-
{ kind: "ts-ignore", re: /@ts-(?:ignore|expect-error)\b/g },
|
|
51442
|
-
{ kind: "eslint-disable", re: /eslint-disable(?:-next-line|-line)?\b/g },
|
|
51443
|
-
{ kind: "py-ignore", re: /#\s*(?:noqa|type:\s*ignore|pyright:\s*ignore|mypy:\s*ignore|nosec|pragma:\s*no\s*cover)\b/gi },
|
|
51444
|
-
{ kind: "test-skip", re: /\bx(?:it|describe|test)\s*\(|\.(?:skip|todo)\s*\(|@(?:pytest\.mark\.skip|unittest\.skip)|\bxfail\b|\bt\.Skip\s*\(/g },
|
|
51445
|
-
{ kind: "lint-allow", re: /\/\/\s*nolint\b|#\[allow\(|istanbul\s+ignore|c8\s+ignore/g },
|
|
51446
|
-
{ kind: "config-loosen", re: /"strict"\s*:\s*false|"skipLibCheck"\s*:\s*true|ignore_errors\s*=\s*True/gi }
|
|
51447
|
-
];
|
|
51448
|
-
function detectSuppression(oldStr, newStr) {
|
|
51449
|
-
if (!newStr) return null;
|
|
51450
|
-
const old = oldStr ?? "";
|
|
51451
|
-
for (const { kind, re } of SUPPRESSORS) {
|
|
51452
|
-
const inNew = (newStr.match(re) ?? []).length;
|
|
51453
|
-
const inOld = (old.match(re) ?? []).length;
|
|
51454
|
-
if (inNew > inOld) return kind;
|
|
51455
|
-
}
|
|
51456
|
-
return null;
|
|
51457
|
-
}
|
|
51458
|
-
var DECL_RE = /\b(?:function|class|const|let|var|def|interface|type|enum)\s+([A-Za-z_$][\w$]*)/g;
|
|
51459
|
-
function deriveIntentHints(tool, oldStr, newStr) {
|
|
51460
|
-
const hints = /* @__PURE__ */ new Set();
|
|
51461
|
-
if (tool === "Write" && (oldStr == null || oldStr === "")) hints.add("add");
|
|
51462
|
-
if (detectSuppression(oldStr, newStr)) hints.add("suppress");
|
|
51463
|
-
if (oldStr != null && newStr != null) {
|
|
51464
|
-
const oldNames = new Set([...oldStr.matchAll(DECL_RE)].map((m) => m[1]));
|
|
51465
|
-
const newNames = new Set([...newStr.matchAll(DECL_RE)].map((m) => m[1]));
|
|
51466
|
-
const removed = [...oldNames].filter((n) => !newNames.has(n));
|
|
51467
|
-
const added = [...newNames].filter((n) => !oldNames.has(n));
|
|
51468
|
-
if (removed.length > 0 && added.length > 0) hints.add("rename");
|
|
51469
|
-
else if (added.length > 0) hints.add("add");
|
|
51470
|
-
else if (removed.length > 0) hints.add("delete");
|
|
51471
|
-
}
|
|
51472
|
-
return [...hints];
|
|
51473
|
-
}
|
|
51474
|
-
function deriveDeclaredRenames(oldStr, newStr) {
|
|
51475
|
-
if (oldStr == null || newStr == null) return [];
|
|
51476
|
-
const oldNames = [...oldStr.matchAll(DECL_RE)].map((m) => m[1]);
|
|
51477
|
-
const newNames = [...newStr.matchAll(DECL_RE)].map((m) => m[1]);
|
|
51478
|
-
const oldSet = new Set(oldNames);
|
|
51479
|
-
const newSet = new Set(newNames);
|
|
51480
|
-
const removed = [...new Set(oldNames.filter((n) => !newSet.has(n)))];
|
|
51481
|
-
const added = [...new Set(newNames.filter((n) => !oldSet.has(n)))];
|
|
51482
|
-
if (removed.length === 1 && added.length === 1) {
|
|
51483
|
-
return [{ from: removed[0], to: added[0] }];
|
|
51484
|
-
}
|
|
51485
|
-
return [];
|
|
51486
|
-
}
|
|
51487
|
-
var CausalBuffer = class {
|
|
51488
|
-
constructor(windowMs = 5e3, maxAgeMs = 3e4) {
|
|
51489
|
-
this.windowMs = windowMs;
|
|
51490
|
-
this.maxAgeMs = maxAgeMs;
|
|
51491
|
-
}
|
|
51492
|
-
events = [];
|
|
51493
|
-
record(e) {
|
|
51494
|
-
this.events.push(e);
|
|
51495
|
-
this.prune(e.ts);
|
|
51496
|
-
}
|
|
51497
|
-
prune(now) {
|
|
51498
|
-
const cutoff = now - this.maxAgeMs;
|
|
51499
|
-
if (this.events.length > 0 && this.events[0].ts < cutoff) {
|
|
51500
|
-
this.events = this.events.filter((e) => e.ts >= cutoff);
|
|
51501
|
-
}
|
|
51502
|
-
}
|
|
51503
|
-
/**
|
|
51504
|
-
* Correlate a reindex flush (the changed relPaths at instant `t`) to a causal
|
|
51505
|
-
* context, if any buffered tool event touched one of those paths within the
|
|
51506
|
-
* window. The most recent match wins for session/tool; hints are unioned.
|
|
51507
|
-
*/
|
|
51508
|
-
correlate(relPaths, t) {
|
|
51509
|
-
const set2 = new Set(relPaths);
|
|
51510
|
-
const matches = this.events.filter(
|
|
51511
|
-
(e) => set2.has(e.relPath) && Math.abs(e.ts - t) <= this.windowMs
|
|
51512
|
-
);
|
|
51513
|
-
if (matches.length === 0) return null;
|
|
51514
|
-
matches.sort((a, b) => b.ts - a.ts);
|
|
51515
|
-
const top = matches[0];
|
|
51516
|
-
const hints = /* @__PURE__ */ new Set();
|
|
51517
|
-
const renames = [];
|
|
51518
|
-
const seen = /* @__PURE__ */ new Set();
|
|
51519
|
-
for (const m of matches) {
|
|
51520
|
-
for (const h of m.intentHints) hints.add(h);
|
|
51521
|
-
for (const r of m.declaredRenames) {
|
|
51522
|
-
const key = `${r.from}->${r.to}`;
|
|
51523
|
-
if (!seen.has(key)) {
|
|
51524
|
-
seen.add(key);
|
|
51525
|
-
renames.push(r);
|
|
51526
|
-
}
|
|
51527
|
-
}
|
|
51528
|
-
}
|
|
51529
|
-
return {
|
|
51530
|
-
sessionId: top.sessionId,
|
|
51531
|
-
tool: top.tool,
|
|
51532
|
-
intentHints: [...hints],
|
|
51533
|
-
declaredRenames: renames
|
|
51534
|
-
};
|
|
51535
|
-
}
|
|
51536
|
-
size() {
|
|
51537
|
-
return this.events.length;
|
|
51538
|
-
}
|
|
51539
|
-
};
|
|
51540
|
-
|
|
51541
52061
|
// src/profile.ts
|
|
51542
52062
|
init_src2();
|
|
51543
52063
|
init_paths();
|
|
51544
|
-
import { existsSync as
|
|
52064
|
+
import { existsSync as existsSync18, readFileSync as readFileSync17, writeFileSync as writeFileSync14 } from "node:fs";
|
|
51545
52065
|
import { createHash as createHash12 } from "node:crypto";
|
|
51546
|
-
import { join as
|
|
52066
|
+
import { join as join22 } from "node:path";
|
|
51547
52067
|
|
|
51548
52068
|
// src/git-remote.ts
|
|
51549
52069
|
init_src();
|
|
51550
|
-
import { existsSync as
|
|
51551
|
-
import { isAbsolute as isAbsolute3, join as
|
|
52070
|
+
import { existsSync as existsSync17, readFileSync as readFileSync16, statSync as statSync4 } from "node:fs";
|
|
52071
|
+
import { isAbsolute as isAbsolute3, join as join21, resolve as resolve5 } from "node:path";
|
|
51552
52072
|
function resolveGitDir(root) {
|
|
51553
|
-
const dotGit =
|
|
52073
|
+
const dotGit = join21(root, ".git");
|
|
51554
52074
|
try {
|
|
51555
52075
|
const st = statSync4(dotGit);
|
|
51556
52076
|
if (st.isDirectory()) return dotGit;
|
|
51557
|
-
const m = /^gitdir:\s*(.+?)\s*$/m.exec(
|
|
52077
|
+
const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync16(dotGit, "utf8"));
|
|
51558
52078
|
if (!m) return null;
|
|
51559
52079
|
const dir = m[1];
|
|
51560
52080
|
return isAbsolute3(dir) ? dir : resolve5(root, dir);
|
|
@@ -51563,22 +52083,22 @@ function resolveGitDir(root) {
|
|
|
51563
52083
|
}
|
|
51564
52084
|
}
|
|
51565
52085
|
function gitConfigPath(gitDir) {
|
|
51566
|
-
const commondirFile =
|
|
51567
|
-
if (
|
|
51568
|
-
const common =
|
|
52086
|
+
const commondirFile = join21(gitDir, "commondir");
|
|
52087
|
+
if (existsSync17(commondirFile)) {
|
|
52088
|
+
const common = readFileSync16(commondirFile, "utf8").trim();
|
|
51569
52089
|
const commonDir = isAbsolute3(common) ? common : resolve5(gitDir, common);
|
|
51570
|
-
return
|
|
52090
|
+
return join21(commonDir, "config");
|
|
51571
52091
|
}
|
|
51572
|
-
return
|
|
52092
|
+
return join21(gitDir, "config");
|
|
51573
52093
|
}
|
|
51574
52094
|
function readRemotes(root) {
|
|
51575
52095
|
const gitDir = resolveGitDir(root);
|
|
51576
52096
|
if (!gitDir) return [];
|
|
51577
52097
|
const cfgPath = gitConfigPath(gitDir);
|
|
51578
|
-
if (!
|
|
52098
|
+
if (!existsSync17(cfgPath)) return [];
|
|
51579
52099
|
let txt;
|
|
51580
52100
|
try {
|
|
51581
|
-
txt =
|
|
52101
|
+
txt = readFileSync16(cfgPath, "utf8");
|
|
51582
52102
|
} catch {
|
|
51583
52103
|
return [];
|
|
51584
52104
|
}
|
|
@@ -51601,6 +52121,10 @@ function detectRepoLocator(root, remote) {
|
|
|
51601
52121
|
function workspaceId(root) {
|
|
51602
52122
|
return "wp_" + createHash12("sha256").update(root).digest("hex").slice(0, 12);
|
|
51603
52123
|
}
|
|
52124
|
+
function sessionOriginKey(sessionId) {
|
|
52125
|
+
if (!sessionId) return void 0;
|
|
52126
|
+
return "ws_" + createHash12("sha256").update(sessionId).digest("hex").slice(0, 12);
|
|
52127
|
+
}
|
|
51604
52128
|
function refreshRepoLocator(root, profile) {
|
|
51605
52129
|
const detected = detectRepoLocator(root, profile.repoRemote);
|
|
51606
52130
|
if (!detected || detected === profile.repoLocator) return false;
|
|
@@ -51610,13 +52134,13 @@ function refreshRepoLocator(root, profile) {
|
|
|
51610
52134
|
}
|
|
51611
52135
|
function loadProfile(root) {
|
|
51612
52136
|
const p = workspacePaths(root);
|
|
51613
|
-
if (!
|
|
51614
|
-
return JSON.parse(
|
|
52137
|
+
if (!existsSync18(p.workspaceJson)) return null;
|
|
52138
|
+
return JSON.parse(readFileSync17(p.workspaceJson, "utf8"));
|
|
51615
52139
|
}
|
|
51616
52140
|
function saveProfile(root, profile) {
|
|
51617
52141
|
const p = workspacePaths(root);
|
|
51618
52142
|
ensureDir(p.configDir);
|
|
51619
|
-
|
|
52143
|
+
writeFileSync14(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
|
|
51620
52144
|
}
|
|
51621
52145
|
function autodetectProfile(root) {
|
|
51622
52146
|
const id = workspaceId(root);
|
|
@@ -51624,10 +52148,10 @@ function autodetectProfile(root) {
|
|
|
51624
52148
|
const p = emptyProfile(id, name2);
|
|
51625
52149
|
const locator = detectRepoLocator(root);
|
|
51626
52150
|
if (locator) p.repoLocator = locator;
|
|
51627
|
-
const pkgPath =
|
|
51628
|
-
if (
|
|
52151
|
+
const pkgPath = join22(root, "package.json");
|
|
52152
|
+
if (existsSync18(pkgPath)) {
|
|
51629
52153
|
try {
|
|
51630
|
-
const pkg = JSON.parse(
|
|
52154
|
+
const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
|
|
51631
52155
|
p.languages.push("typescript", "javascript");
|
|
51632
52156
|
const nodeVer = pkg.engines?.node ?? "node";
|
|
51633
52157
|
p.stack.push(`node@${nodeVer}`);
|
|
@@ -51648,10 +52172,10 @@ function autodetectProfile(root) {
|
|
|
51648
52172
|
} catch {
|
|
51649
52173
|
}
|
|
51650
52174
|
}
|
|
51651
|
-
const pyproject =
|
|
51652
|
-
if (
|
|
52175
|
+
const pyproject = join22(root, "pyproject.toml");
|
|
52176
|
+
if (existsSync18(pyproject)) {
|
|
51653
52177
|
try {
|
|
51654
|
-
const txt =
|
|
52178
|
+
const txt = readFileSync17(pyproject, "utf8");
|
|
51655
52179
|
const py = /python\s*=\s*"([^"]+)"/.exec(txt)?.[1];
|
|
51656
52180
|
p.languages.push("python");
|
|
51657
52181
|
p.stack.push(`python@${py ?? "3"}`);
|
|
@@ -51662,16 +52186,16 @@ function autodetectProfile(root) {
|
|
|
51662
52186
|
} catch {
|
|
51663
52187
|
}
|
|
51664
52188
|
}
|
|
51665
|
-
const reqs =
|
|
51666
|
-
if (
|
|
52189
|
+
const reqs = join22(root, "requirements.txt");
|
|
52190
|
+
if (existsSync18(reqs)) {
|
|
51667
52191
|
if (!p.languages.includes("python")) p.languages.push("python");
|
|
51668
52192
|
if (!p.stack.includes("python@3")) p.stack.push("python@3");
|
|
51669
52193
|
}
|
|
51670
|
-
if (
|
|
52194
|
+
if (existsSync18(join22(root, "go.mod"))) {
|
|
51671
52195
|
p.languages.push("go");
|
|
51672
52196
|
p.stack.push("go");
|
|
51673
52197
|
}
|
|
51674
|
-
if (
|
|
52198
|
+
if (existsSync18(join22(root, "Cargo.toml"))) {
|
|
51675
52199
|
p.languages.push("rust");
|
|
51676
52200
|
p.stack.push("rust");
|
|
51677
52201
|
}
|
|
@@ -51680,6 +52204,160 @@ function autodetectProfile(root) {
|
|
|
51680
52204
|
return p;
|
|
51681
52205
|
}
|
|
51682
52206
|
|
|
52207
|
+
// src/witness-queue.ts
|
|
52208
|
+
import { readFileSync as readFileSync18, renameSync as renameSync2, writeFileSync as writeFileSync15 } from "node:fs";
|
|
52209
|
+
import { dirname as dirname9, join as join23 } from "node:path";
|
|
52210
|
+
var WITNESS_QUEUE_CAP = 500;
|
|
52211
|
+
var WITNESS_TTL_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
52212
|
+
var WITNESS_MAX_ATTEMPTS = 5;
|
|
52213
|
+
function witnessQueuePath(workspaceConfigDir) {
|
|
52214
|
+
return join23(workspaceConfigDir, "witness-queue.json");
|
|
52215
|
+
}
|
|
52216
|
+
function loadWitnessQueue(path2) {
|
|
52217
|
+
try {
|
|
52218
|
+
const raw2 = JSON.parse(readFileSync18(path2, "utf8"));
|
|
52219
|
+
if (!Array.isArray(raw2)) return [];
|
|
52220
|
+
return raw2.filter(
|
|
52221
|
+
(w) => !!w && typeof w === "object" && typeof w.nodeId === "string" && typeof w.witnessKey === "string"
|
|
52222
|
+
);
|
|
52223
|
+
} catch {
|
|
52224
|
+
return [];
|
|
52225
|
+
}
|
|
52226
|
+
}
|
|
52227
|
+
function saveWitnessQueue(path2, queue) {
|
|
52228
|
+
try {
|
|
52229
|
+
const tmp = join23(dirname9(path2), `.${Date.now()}.witness-queue.tmp`);
|
|
52230
|
+
writeFileSync15(tmp, JSON.stringify(queue), "utf8");
|
|
52231
|
+
renameSync2(tmp, path2);
|
|
52232
|
+
} catch {
|
|
52233
|
+
}
|
|
52234
|
+
}
|
|
52235
|
+
function pruneWitnessQueue(queue, now) {
|
|
52236
|
+
const live = queue.filter(
|
|
52237
|
+
(w) => now - w.ts < WITNESS_TTL_MS && w.attempts < WITNESS_MAX_ATTEMPTS
|
|
52238
|
+
);
|
|
52239
|
+
return live.length > WITNESS_QUEUE_CAP ? live.slice(live.length - WITNESS_QUEUE_CAP) : live;
|
|
52240
|
+
}
|
|
52241
|
+
function enqueueWitnesses(queue, fresh, now) {
|
|
52242
|
+
const byKey = new Map(queue.map((w) => [`${w.channel}:${w.witnessKey}`, w]));
|
|
52243
|
+
for (const f of fresh) {
|
|
52244
|
+
const k = `${f.channel}:${f.witnessKey}`;
|
|
52245
|
+
const existing = byKey.get(k);
|
|
52246
|
+
if (existing) existing.attempts += 1;
|
|
52247
|
+
else byKey.set(k, { ...f, ts: now, attempts: 1 });
|
|
52248
|
+
}
|
|
52249
|
+
return pruneWitnessQueue([...byKey.values()], now);
|
|
52250
|
+
}
|
|
52251
|
+
function pendingFor(queue, channel) {
|
|
52252
|
+
return queue.filter((w) => w.channel === channel);
|
|
52253
|
+
}
|
|
52254
|
+
function retireWitnesses(queue, channel, settledKeys) {
|
|
52255
|
+
return queue.filter((w) => !(w.channel === channel && settledKeys.has(w.witnessKey)));
|
|
52256
|
+
}
|
|
52257
|
+
|
|
52258
|
+
// src/causal.ts
|
|
52259
|
+
var SUPPRESSORS = [
|
|
52260
|
+
{ kind: "ts-nocheck", re: /@ts-nocheck\b/g },
|
|
52261
|
+
{ kind: "ts-ignore", re: /@ts-(?:ignore|expect-error)\b/g },
|
|
52262
|
+
{ kind: "eslint-disable", re: /eslint-disable(?:-next-line|-line)?\b/g },
|
|
52263
|
+
{ kind: "py-ignore", re: /#\s*(?:noqa|type:\s*ignore|pyright:\s*ignore|mypy:\s*ignore|nosec|pragma:\s*no\s*cover)\b/gi },
|
|
52264
|
+
{ kind: "test-skip", re: /\bx(?:it|describe|test)\s*\(|\.(?:skip|todo)\s*\(|@(?:pytest\.mark\.skip|unittest\.skip)|\bxfail\b|\bt\.Skip\s*\(/g },
|
|
52265
|
+
{ kind: "lint-allow", re: /\/\/\s*nolint\b|#\[allow\(|istanbul\s+ignore|c8\s+ignore/g },
|
|
52266
|
+
{ kind: "config-loosen", re: /"strict"\s*:\s*false|"skipLibCheck"\s*:\s*true|ignore_errors\s*=\s*True/gi }
|
|
52267
|
+
];
|
|
52268
|
+
function detectSuppression(oldStr, newStr) {
|
|
52269
|
+
if (!newStr) return null;
|
|
52270
|
+
const old = oldStr ?? "";
|
|
52271
|
+
for (const { kind, re } of SUPPRESSORS) {
|
|
52272
|
+
const inNew = (newStr.match(re) ?? []).length;
|
|
52273
|
+
const inOld = (old.match(re) ?? []).length;
|
|
52274
|
+
if (inNew > inOld) return kind;
|
|
52275
|
+
}
|
|
52276
|
+
return null;
|
|
52277
|
+
}
|
|
52278
|
+
var DECL_RE = /\b(?:function|class|const|let|var|def|interface|type|enum)\s+([A-Za-z_$][\w$]*)/g;
|
|
52279
|
+
function deriveIntentHints(tool, oldStr, newStr) {
|
|
52280
|
+
const hints = /* @__PURE__ */ new Set();
|
|
52281
|
+
if (tool === "Write" && (oldStr == null || oldStr === "")) hints.add("add");
|
|
52282
|
+
if (detectSuppression(oldStr, newStr)) hints.add("suppress");
|
|
52283
|
+
if (oldStr != null && newStr != null) {
|
|
52284
|
+
const oldNames = new Set([...oldStr.matchAll(DECL_RE)].map((m) => m[1]));
|
|
52285
|
+
const newNames = new Set([...newStr.matchAll(DECL_RE)].map((m) => m[1]));
|
|
52286
|
+
const removed = [...oldNames].filter((n) => !newNames.has(n));
|
|
52287
|
+
const added = [...newNames].filter((n) => !oldNames.has(n));
|
|
52288
|
+
if (removed.length > 0 && added.length > 0) hints.add("rename");
|
|
52289
|
+
else if (added.length > 0) hints.add("add");
|
|
52290
|
+
else if (removed.length > 0) hints.add("delete");
|
|
52291
|
+
}
|
|
52292
|
+
return [...hints];
|
|
52293
|
+
}
|
|
52294
|
+
function deriveDeclaredRenames(oldStr, newStr) {
|
|
52295
|
+
if (oldStr == null || newStr == null) return [];
|
|
52296
|
+
const oldNames = [...oldStr.matchAll(DECL_RE)].map((m) => m[1]);
|
|
52297
|
+
const newNames = [...newStr.matchAll(DECL_RE)].map((m) => m[1]);
|
|
52298
|
+
const oldSet = new Set(oldNames);
|
|
52299
|
+
const newSet = new Set(newNames);
|
|
52300
|
+
const removed = [...new Set(oldNames.filter((n) => !newSet.has(n)))];
|
|
52301
|
+
const added = [...new Set(newNames.filter((n) => !oldSet.has(n)))];
|
|
52302
|
+
if (removed.length === 1 && added.length === 1) {
|
|
52303
|
+
return [{ from: removed[0], to: added[0] }];
|
|
52304
|
+
}
|
|
52305
|
+
return [];
|
|
52306
|
+
}
|
|
52307
|
+
var CausalBuffer = class {
|
|
52308
|
+
constructor(windowMs = 5e3, maxAgeMs = 3e4) {
|
|
52309
|
+
this.windowMs = windowMs;
|
|
52310
|
+
this.maxAgeMs = maxAgeMs;
|
|
52311
|
+
}
|
|
52312
|
+
events = [];
|
|
52313
|
+
record(e) {
|
|
52314
|
+
this.events.push(e);
|
|
52315
|
+
this.prune(e.ts);
|
|
52316
|
+
}
|
|
52317
|
+
prune(now) {
|
|
52318
|
+
const cutoff = now - this.maxAgeMs;
|
|
52319
|
+
if (this.events.length > 0 && this.events[0].ts < cutoff) {
|
|
52320
|
+
this.events = this.events.filter((e) => e.ts >= cutoff);
|
|
52321
|
+
}
|
|
52322
|
+
}
|
|
52323
|
+
/**
|
|
52324
|
+
* Correlate a reindex flush (the changed relPaths at instant `t`) to a causal
|
|
52325
|
+
* context, if any buffered tool event touched one of those paths within the
|
|
52326
|
+
* window. The most recent match wins for session/tool; hints are unioned.
|
|
52327
|
+
*/
|
|
52328
|
+
correlate(relPaths, t) {
|
|
52329
|
+
const set2 = new Set(relPaths);
|
|
52330
|
+
const matches = this.events.filter(
|
|
52331
|
+
(e) => set2.has(e.relPath) && Math.abs(e.ts - t) <= this.windowMs
|
|
52332
|
+
);
|
|
52333
|
+
if (matches.length === 0) return null;
|
|
52334
|
+
matches.sort((a, b) => b.ts - a.ts);
|
|
52335
|
+
const top = matches[0];
|
|
52336
|
+
const hints = /* @__PURE__ */ new Set();
|
|
52337
|
+
const renames = [];
|
|
52338
|
+
const seen = /* @__PURE__ */ new Set();
|
|
52339
|
+
for (const m of matches) {
|
|
52340
|
+
for (const h of m.intentHints) hints.add(h);
|
|
52341
|
+
for (const r of m.declaredRenames) {
|
|
52342
|
+
const key = `${r.from}->${r.to}`;
|
|
52343
|
+
if (!seen.has(key)) {
|
|
52344
|
+
seen.add(key);
|
|
52345
|
+
renames.push(r);
|
|
52346
|
+
}
|
|
52347
|
+
}
|
|
52348
|
+
}
|
|
52349
|
+
return {
|
|
52350
|
+
sessionId: top.sessionId,
|
|
52351
|
+
tool: top.tool,
|
|
52352
|
+
intentHints: [...hints],
|
|
52353
|
+
declaredRenames: renames
|
|
52354
|
+
};
|
|
52355
|
+
}
|
|
52356
|
+
size() {
|
|
52357
|
+
return this.events.length;
|
|
52358
|
+
}
|
|
52359
|
+
};
|
|
52360
|
+
|
|
51683
52361
|
// src/engine.ts
|
|
51684
52362
|
init_config();
|
|
51685
52363
|
init_cloud_auth();
|
|
@@ -51829,7 +52507,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
51829
52507
|
}
|
|
51830
52508
|
|
|
51831
52509
|
// src/engine.ts
|
|
51832
|
-
var DAEMON_VERSION = true ? "2.0.2-dev.
|
|
52510
|
+
var DAEMON_VERSION = true ? "2.0.2-dev.133" : "2.0.0-alpha.0";
|
|
51833
52511
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
51834
52512
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
51835
52513
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -51839,8 +52517,8 @@ var TURN_REPLAY_LOOKBACK_MS = 7 * 24 * 60 * 6e4;
|
|
|
51839
52517
|
function appendIdentityAudit(path2, record2, line) {
|
|
51840
52518
|
if (!record2.accepted && record2.score <= 0) return;
|
|
51841
52519
|
try {
|
|
51842
|
-
if (
|
|
51843
|
-
|
|
52520
|
+
if (existsSync19(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
|
|
52521
|
+
renameSync3(path2, `${path2}.1`);
|
|
51844
52522
|
}
|
|
51845
52523
|
appendFileSync2(path2, line);
|
|
51846
52524
|
} catch {
|
|
@@ -51849,14 +52527,33 @@ function appendIdentityAudit(path2, record2, line) {
|
|
|
51849
52527
|
var yieldToLoop = () => new Promise((r) => setImmediate(r));
|
|
51850
52528
|
function loadTurnCursors(path2) {
|
|
51851
52529
|
try {
|
|
51852
|
-
|
|
52530
|
+
const raw2 = JSON.parse(readFileSync19(path2, "utf8"));
|
|
52531
|
+
return new Map(
|
|
52532
|
+
Object.entries(raw2).map(([k, v]) => [k, typeof v === "string" ? v : String(v?.uuid ?? "")])
|
|
52533
|
+
);
|
|
52534
|
+
} catch {
|
|
52535
|
+
return /* @__PURE__ */ new Map();
|
|
52536
|
+
}
|
|
52537
|
+
}
|
|
52538
|
+
function loadTurnOffsets(path2) {
|
|
52539
|
+
try {
|
|
52540
|
+
const raw2 = JSON.parse(readFileSync19(path2, "utf8"));
|
|
52541
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
52542
|
+
for (const [k, v] of Object.entries(raw2)) {
|
|
52543
|
+
const off = typeof v === "object" && v !== null ? v.offset : void 0;
|
|
52544
|
+
if (typeof off === "number" && off >= 0) out2.set(k, off);
|
|
52545
|
+
}
|
|
52546
|
+
return out2;
|
|
51853
52547
|
} catch {
|
|
51854
52548
|
return /* @__PURE__ */ new Map();
|
|
51855
52549
|
}
|
|
51856
52550
|
}
|
|
51857
|
-
function saveTurnCursors(path2, cursors) {
|
|
52551
|
+
function saveTurnCursors(path2, cursors, offsets) {
|
|
51858
52552
|
try {
|
|
51859
|
-
|
|
52553
|
+
const merged = {};
|
|
52554
|
+
for (const [k, uuid3] of cursors) merged[k] = { uuid: uuid3, offset: offsets.get(k) ?? 0 };
|
|
52555
|
+
for (const [k, offset] of offsets) if (!merged[k]) merged[k] = { uuid: "", offset };
|
|
52556
|
+
writeFileSync16(path2, JSON.stringify(merged), "utf8");
|
|
51860
52557
|
} catch {
|
|
51861
52558
|
}
|
|
51862
52559
|
}
|
|
@@ -51878,7 +52575,7 @@ function gitSourceWatchTargets(root) {
|
|
|
51878
52575
|
["-C", root, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "-z"],
|
|
51879
52576
|
{ encoding: "utf8", maxBuffer: 256 * 1024 * 1024, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
|
|
51880
52577
|
);
|
|
51881
|
-
ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(
|
|
52578
|
+
ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join24(root, d) + sep4));
|
|
51882
52579
|
} catch {
|
|
51883
52580
|
}
|
|
51884
52581
|
const hasIgnoredChild = (dir) => ignoredDirs.some((ig) => ig.startsWith(dir + "/"));
|
|
@@ -51890,19 +52587,19 @@ function gitSourceWatchTargets(root) {
|
|
|
51890
52587
|
if (!f.startsWith(prefix)) continue;
|
|
51891
52588
|
const rest2 = f.slice(prefix.length);
|
|
51892
52589
|
if (rest2.includes("/")) children.add(dir === "" ? rest2.slice(0, rest2.indexOf("/")) : dir + "/" + rest2.slice(0, rest2.indexOf("/")));
|
|
51893
|
-
else targets.add(
|
|
52590
|
+
else targets.add(join24(root, f));
|
|
51894
52591
|
}
|
|
51895
52592
|
for (const c of children) {
|
|
51896
|
-
if (IGNORED_PATH.test(
|
|
52593
|
+
if (IGNORED_PATH.test(join24(root, c) + sep4)) continue;
|
|
51897
52594
|
if (hasIgnoredChild(c)) addUnder(c);
|
|
51898
|
-
else targets.add(
|
|
52595
|
+
else targets.add(join24(root, c));
|
|
51899
52596
|
}
|
|
51900
52597
|
};
|
|
51901
52598
|
addUnder("");
|
|
51902
52599
|
if (targets.size > 0) return [...targets];
|
|
51903
52600
|
} catch {
|
|
51904
52601
|
}
|
|
51905
|
-
return
|
|
52602
|
+
return readdirSync9(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !IGNORED_PATH.test(join24(root, String(e.name)) + sep4)).map((e) => join24(root, String(e.name)));
|
|
51906
52603
|
}
|
|
51907
52604
|
function createWorkspaceEngine(opts) {
|
|
51908
52605
|
const paths = workspacePaths(opts.workspaceRoot);
|
|
@@ -51917,6 +52614,10 @@ function createWorkspaceEngine(opts) {
|
|
|
51917
52614
|
refreshRepoLocator(opts.workspaceRoot, profile);
|
|
51918
52615
|
}
|
|
51919
52616
|
const store = openGraphStore({ path: paths.castalia });
|
|
52617
|
+
let witnessQueue = pruneWitnessQueue(
|
|
52618
|
+
loadWitnessQueue(witnessQueuePath(paths.configDir)),
|
|
52619
|
+
Date.now()
|
|
52620
|
+
);
|
|
51920
52621
|
const log = openEventLog({ path: paths.eventLog });
|
|
51921
52622
|
const bundled = import.meta.url.endsWith(".mjs");
|
|
51922
52623
|
const passWorker = bundled && paths.castalia !== ":memory:" ? new PassWorker({
|
|
@@ -52056,7 +52757,7 @@ function createWorkspaceEngine(opts) {
|
|
|
52056
52757
|
const srcPaths = diff.changedPaths.filter((p) => /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(p));
|
|
52057
52758
|
let episodeId2;
|
|
52058
52759
|
if (srcPaths.length > 0) {
|
|
52059
|
-
const abs = srcPaths.map((p) =>
|
|
52760
|
+
const abs = srcPaths.map((p) => join24(opts.workspaceRoot, p));
|
|
52060
52761
|
try {
|
|
52061
52762
|
const r = await runReindexPass(
|
|
52062
52763
|
`git-reindex:${profile.name} (${abs.length} files)`,
|
|
@@ -52092,8 +52793,8 @@ function createWorkspaceEngine(opts) {
|
|
|
52092
52793
|
`[errata] git: ${ev.kind} ${ev.newSha.slice(0, 7)} by ${meta3.authorName} \u2014 ${diff.changedPaths.length} file(s), ${diff.renames.length} rename(s)`
|
|
52093
52794
|
);
|
|
52094
52795
|
};
|
|
52095
|
-
const gitDir =
|
|
52096
|
-
if (
|
|
52796
|
+
const gitDir = join24(opts.workspaceRoot, ".git");
|
|
52797
|
+
if (existsSync19(gitDir)) {
|
|
52097
52798
|
stopGit = startGitSensor(gitDir, (ev) => {
|
|
52098
52799
|
void handleGitEvent(ev).catch((err2) => {
|
|
52099
52800
|
console.warn("[errata] git event handler failed:", err2);
|
|
@@ -52163,10 +52864,10 @@ function createWorkspaceEngine(opts) {
|
|
|
52163
52864
|
});
|
|
52164
52865
|
doneRender?.();
|
|
52165
52866
|
writeContextFile(opts.workspaceRoot, body2);
|
|
52166
|
-
const target =
|
|
52867
|
+
const target = join24(opts.workspaceRoot, "AGENTS.md");
|
|
52167
52868
|
writeManagedBlock(target, { body: AGENTS_POINTER_BODY, stable: true, force: true });
|
|
52168
52869
|
if (elicit) {
|
|
52169
|
-
writePrimingHandles(
|
|
52870
|
+
writePrimingHandles(join24(paths.configDir, "priming-handles.json"), [
|
|
52170
52871
|
...snapshot.recentProblems.map((r) => r.node),
|
|
52171
52872
|
// Resolved-band handles: the ✓ problem AND its Solution are citable
|
|
52172
52873
|
// (a fix tag on an already-resolved problem no-ops idempotently; the
|
|
@@ -52257,6 +52958,21 @@ function createWorkspaceEngine(opts) {
|
|
|
52257
52958
|
} catch (err2) {
|
|
52258
52959
|
console.warn("[errata] anchor backfill failed:", err2);
|
|
52259
52960
|
}
|
|
52961
|
+
try {
|
|
52962
|
+
const c = backfillConstraintKind(store, {
|
|
52963
|
+
root: opts.workspaceRoot,
|
|
52964
|
+
configDir: paths.configDir,
|
|
52965
|
+
now: Date.now()
|
|
52966
|
+
});
|
|
52967
|
+
if (!c.skipped && (c.stamped > 0 || c.detached > 0)) {
|
|
52968
|
+
console.log(
|
|
52969
|
+
`[errata] constraint backfill: ${c.stamped} design tension(s) marked` + (c.detached > 0 ? `; ${c.detached} fabricated resolution(s) detached, ${c.reopened} reopened` : "") + (c.witnessed > 0 ? `; ${c.witnessed} cited discharge(s) kept` : "") + (c.cloudTwins.length > 0 ? ` \u2014 ${c.cloudTwins.length} already contributed; run scripts/repair-cloud-constraints.ts to repair the cloud twins` : "")
|
|
52970
|
+
);
|
|
52971
|
+
refreshContextNow();
|
|
52972
|
+
}
|
|
52973
|
+
} catch (err2) {
|
|
52974
|
+
console.warn("[errata] constraint backfill failed:", err2);
|
|
52975
|
+
}
|
|
52260
52976
|
const report = runNightlyPipeline(store);
|
|
52261
52977
|
try {
|
|
52262
52978
|
const m = mergeDuplicateProblems(store, { ts: Date.now() });
|
|
@@ -52356,8 +53072,9 @@ function createWorkspaceEngine(opts) {
|
|
|
52356
53072
|
resultSummary: { exitCode: e.exitCode ?? 1, errorTokens: e.errorTokens }
|
|
52357
53073
|
});
|
|
52358
53074
|
};
|
|
52359
|
-
const turnCursorPath =
|
|
53075
|
+
const turnCursorPath = join24(paths.configDir, "turn-cursors.json");
|
|
52360
53076
|
const lastTurnUuid = loadTurnCursors(turnCursorPath);
|
|
53077
|
+
const turnOffset = loadTurnOffsets(turnCursorPath);
|
|
52361
53078
|
const sessionLastProblem = /* @__PURE__ */ new Map();
|
|
52362
53079
|
const sessionThreads = /* @__PURE__ */ new Map();
|
|
52363
53080
|
const harvestTexts = async (sessionId, items) => {
|
|
@@ -52379,7 +53096,7 @@ function createWorkspaceEngine(opts) {
|
|
|
52379
53096
|
const t = Date.now();
|
|
52380
53097
|
let processedTurns = 0;
|
|
52381
53098
|
const elicit = isEdgeElicitationEnabled();
|
|
52382
|
-
const handleMap = elicit ? readPrimingHandles(
|
|
53099
|
+
const handleMap = elicit ? readPrimingHandles(join24(paths.configDir, "priming-handles.json")) : {};
|
|
52383
53100
|
const wsRoot = (opts.workspaceRoot ?? "").replace(/\\/g, "/");
|
|
52384
53101
|
const toRel = (abs) => {
|
|
52385
53102
|
const p = abs.replace(/\\/g, "/");
|
|
@@ -52421,7 +53138,7 @@ function createWorkspaceEngine(opts) {
|
|
|
52421
53138
|
ts: t
|
|
52422
53139
|
});
|
|
52423
53140
|
if (r.created || r.corroborated) minted++;
|
|
52424
|
-
sessionLastProblem.set(sessionId, designProblemId(flag.problem));
|
|
53141
|
+
if (flag.kind !== "constraint") sessionLastProblem.set(sessionId, designProblemId(flag.problem));
|
|
52425
53142
|
if (r.created || r.corroborated) {
|
|
52426
53143
|
try {
|
|
52427
53144
|
if (editedTurnFile) {
|
|
@@ -52501,11 +53218,13 @@ function createWorkspaceEngine(opts) {
|
|
|
52501
53218
|
const dedupPath = anchorPath ?? hintPath;
|
|
52502
53219
|
if (dedupPath) {
|
|
52503
53220
|
try {
|
|
52504
|
-
const dupId = reinforceSameAnchorProblem(store, dedupPath, profile.id, p.statement, sessionId, t);
|
|
53221
|
+
const dupId = reinforceSameAnchorProblem(store, dedupPath, profile.id, p.statement, sessionId, t, p.kind);
|
|
52505
53222
|
if (dupId) {
|
|
52506
53223
|
minted++;
|
|
52507
|
-
|
|
52508
|
-
|
|
53224
|
+
if (p.kind !== "constraint") {
|
|
53225
|
+
sessionLastProblem.set(sessionId, dupId);
|
|
53226
|
+
inScopeProblemId = dupId;
|
|
53227
|
+
}
|
|
52509
53228
|
if (p.threadId) threads.set(p.threadId, dupId);
|
|
52510
53229
|
continue;
|
|
52511
53230
|
}
|
|
@@ -52519,8 +53238,10 @@ function createWorkspaceEngine(opts) {
|
|
|
52519
53238
|
});
|
|
52520
53239
|
if (r.created || r.corroborated) {
|
|
52521
53240
|
minted++;
|
|
52522
|
-
|
|
52523
|
-
|
|
53241
|
+
if (p.kind !== "constraint") {
|
|
53242
|
+
sessionLastProblem.set(sessionId, designProblemId(p.statement));
|
|
53243
|
+
inScopeProblemId = designProblemId(p.statement);
|
|
53244
|
+
}
|
|
52524
53245
|
if (p.threadId) threads.set(p.threadId, designProblemId(p.statement));
|
|
52525
53246
|
try {
|
|
52526
53247
|
if (anchorPath) {
|
|
@@ -52717,18 +53438,83 @@ function createWorkspaceEngine(opts) {
|
|
|
52717
53438
|
mintCiteEdge(solId, targetId, sl.relation, 0.4, { solutionStructure: true, hypothesis: true });
|
|
52718
53439
|
}
|
|
52719
53440
|
}
|
|
52720
|
-
|
|
52721
|
-
|
|
52722
|
-
|
|
52723
|
-
|
|
52724
|
-
|
|
53441
|
+
const citingSession = sessionOriginKey(sessionId);
|
|
53442
|
+
const mintedHere = (nodeId) => store.getNode(nodeId)?.attrs["sources"]?.[0] === sessionId;
|
|
53443
|
+
const sendWitnesses = async (channel, fresh, send) => {
|
|
53444
|
+
const queued = pendingFor(witnessQueue, channel);
|
|
53445
|
+
if (fresh.length === 0 && queued.length === 0) return;
|
|
53446
|
+
const groups = /* @__PURE__ */ new Map();
|
|
53447
|
+
for (const q of queued) {
|
|
53448
|
+
const g = groups.get(q.sessionKey) ?? [];
|
|
53449
|
+
g.push({ nodeId: q.nodeId, witnessKey: q.witnessKey });
|
|
53450
|
+
groups.set(q.sessionKey, g);
|
|
53451
|
+
}
|
|
53452
|
+
const freshGroup = groups.get(citingSession) ?? [];
|
|
53453
|
+
for (const f of fresh) {
|
|
53454
|
+
if (freshGroup.some((x) => x.witnessKey === f.witnessKey)) continue;
|
|
53455
|
+
freshGroup.push(f);
|
|
53456
|
+
}
|
|
53457
|
+
groups.set(citingSession, freshGroup);
|
|
53458
|
+
const settled = /* @__PURE__ */ new Set();
|
|
53459
|
+
const stillUnmatched = [];
|
|
53460
|
+
let recorded = 0, unmatched = 0, duplicate = 0, selfGated = 0;
|
|
53461
|
+
for (const [session, items2] of groups) {
|
|
53462
|
+
if (items2.length === 0) continue;
|
|
53463
|
+
try {
|
|
53464
|
+
const raw2 = await send(items2, session);
|
|
53465
|
+
const d = {
|
|
53466
|
+
recorded: raw2?.recorded ?? 0,
|
|
53467
|
+
unmatched: raw2?.unmatched ?? 0,
|
|
53468
|
+
duplicate: raw2?.duplicate ?? 0,
|
|
53469
|
+
selfGated: raw2?.selfGated ?? 0,
|
|
53470
|
+
unmatchedIds: raw2?.unmatchedIds ?? []
|
|
53471
|
+
};
|
|
53472
|
+
recorded += d.recorded;
|
|
53473
|
+
unmatched += d.unmatched;
|
|
53474
|
+
duplicate += d.duplicate;
|
|
53475
|
+
selfGated += d.selfGated;
|
|
53476
|
+
const missing = new Set(d.unmatchedIds);
|
|
53477
|
+
for (const it of items2) {
|
|
53478
|
+
if (missing.has(it.nodeId)) stillUnmatched.push({ channel, ...it, sessionKey: session });
|
|
53479
|
+
else settled.add(it.witnessKey);
|
|
53480
|
+
}
|
|
53481
|
+
} catch (err2) {
|
|
53482
|
+
console.warn(`[errata] ${channel} transport failed (witnesses kept for retry):`, err2 instanceof Error ? err2.message : err2);
|
|
53483
|
+
for (const it of items2) stillUnmatched.push({ channel, ...it, sessionKey: session });
|
|
53484
|
+
}
|
|
53485
|
+
}
|
|
53486
|
+
witnessQueue = retireWitnesses(witnessQueue, channel, settled);
|
|
53487
|
+
witnessQueue = enqueueWitnesses(witnessQueue, stillUnmatched, Date.now());
|
|
53488
|
+
saveWitnessQueue(witnessQueuePath(paths.configDir), witnessQueue);
|
|
53489
|
+
console.log(
|
|
53490
|
+
`[errata] ${channel}: ${formatDisposition({ recorded, unmatched, duplicate, selfGated })}` + (queued.length > 0 ? ` (incl. ${queued.length} replayed)` : "")
|
|
53491
|
+
);
|
|
53492
|
+
};
|
|
53493
|
+
if (typeof cloud.reportContradictions === "function") {
|
|
53494
|
+
await sendWitnesses(
|
|
53495
|
+
"contradict",
|
|
53496
|
+
plan.refutes,
|
|
53497
|
+
(items2, session) => cloud.reportContradictions({
|
|
53498
|
+
daemonVersion: DAEMON_VERSION,
|
|
53499
|
+
projectId: profile.id,
|
|
53500
|
+
...session ? { sessionId: session } : {},
|
|
53501
|
+
items: stampWitnessOrigin(items2, profile.id)
|
|
53502
|
+
})
|
|
52725
53503
|
);
|
|
52726
53504
|
}
|
|
52727
|
-
if (
|
|
52728
|
-
|
|
52729
|
-
|
|
52730
|
-
|
|
52731
|
-
|
|
53505
|
+
if (typeof cloud.reportCorroborations === "function") {
|
|
53506
|
+
const emit = plan.corroborations.filter((c) => !mintedHere(c.nodeId));
|
|
53507
|
+
const skipped = plan.corroborations.length - emit.length;
|
|
53508
|
+
if (skipped > 0) console.log(`[errata] corroborate: ${skipped} skipped (this session authored the node)`);
|
|
53509
|
+
await sendWitnesses(
|
|
53510
|
+
"corroborate",
|
|
53511
|
+
emit,
|
|
53512
|
+
(items2, session) => cloud.reportCorroborations({
|
|
53513
|
+
daemonVersion: DAEMON_VERSION,
|
|
53514
|
+
projectId: profile.id,
|
|
53515
|
+
...session ? { sessionId: session } : {},
|
|
53516
|
+
items: stampWitnessOrigin(items2, profile.id)
|
|
53517
|
+
})
|
|
52732
53518
|
);
|
|
52733
53519
|
}
|
|
52734
53520
|
} catch (err2) {
|
|
@@ -52757,17 +53543,28 @@ function createWorkspaceEngine(opts) {
|
|
|
52757
53543
|
}
|
|
52758
53544
|
};
|
|
52759
53545
|
const harvestTurns = async (sessionId, transcriptPath) => {
|
|
53546
|
+
const known = turnOffset.get(sessionId);
|
|
52760
53547
|
let turns;
|
|
53548
|
+
let nextOffset;
|
|
52761
53549
|
try {
|
|
52762
|
-
|
|
53550
|
+
if (known === void 0) {
|
|
53551
|
+
turns = readAssistantTurns(transcriptPath);
|
|
53552
|
+
nextOffset = transcriptSize(transcriptPath);
|
|
53553
|
+
} else {
|
|
53554
|
+
({ turns, nextOffset } = readAssistantTurnsFrom(transcriptPath, known));
|
|
53555
|
+
}
|
|
52763
53556
|
} catch {
|
|
52764
53557
|
return;
|
|
52765
53558
|
}
|
|
52766
53559
|
const fresh = turnsSince(turns, lastTurnUuid.get(sessionId));
|
|
52767
|
-
|
|
53560
|
+
turnOffset.set(sessionId, nextOffset);
|
|
53561
|
+
if (fresh.length === 0) {
|
|
53562
|
+
saveTurnCursors(turnCursorPath, lastTurnUuid, turnOffset);
|
|
53563
|
+
return;
|
|
53564
|
+
}
|
|
52768
53565
|
lastTurnUuid.set(sessionId, turns[turns.length - 1].uuid);
|
|
52769
53566
|
await harvestTexts(sessionId, fresh);
|
|
52770
|
-
saveTurnCursors(turnCursorPath, lastTurnUuid);
|
|
53567
|
+
saveTurnCursors(turnCursorPath, lastTurnUuid, turnOffset);
|
|
52771
53568
|
};
|
|
52772
53569
|
const seenMessageIds = /* @__PURE__ */ new Set();
|
|
52773
53570
|
const onMessage = (e) => {
|
|
@@ -52893,9 +53690,24 @@ function createWorkspaceEngine(opts) {
|
|
|
52893
53690
|
return typeof cloudId === "string" && cloudId !== p.id ? [p.id, cloudId] : [p.id];
|
|
52894
53691
|
});
|
|
52895
53692
|
const pins = profile?.projectId ? await cloud.getSkillPins(profile.projectId).then((r) => r.pins).catch(() => []) : [];
|
|
52896
|
-
const
|
|
53693
|
+
const anchorMap = buildBucketTokenAnchors(
|
|
53694
|
+
profile.languages ?? [],
|
|
53695
|
+
loadConfig().consent.contributePackages ? store.findNodesByLabel("Package").map((p) => ({
|
|
53696
|
+
name: String(p.attrs["name"] ?? ""),
|
|
53697
|
+
purl: String(p.attrs["purl"] ?? p.id)
|
|
53698
|
+
})) : []
|
|
53699
|
+
);
|
|
53700
|
+
const stackTokens = new Set((profile.stack ?? []).map((t) => canonicalizeToken(String(t))).filter(Boolean));
|
|
53701
|
+
const techSeed = [
|
|
53702
|
+
...new Set(
|
|
53703
|
+
Object.entries(anchorMap).filter(([tok, a]) => a.startsWith("lang:") || stackTokens.has(tok)).map(([, a]) => a)
|
|
53704
|
+
)
|
|
53705
|
+
].slice(0, 24);
|
|
53706
|
+
const result = await syncSkills(paths, cloud, skillSeed, pins, techSeed);
|
|
52897
53707
|
try {
|
|
52898
|
-
|
|
53708
|
+
const inputs = emitInputsFromManifest(paths.configDir, paths.skillsManifest);
|
|
53709
|
+
emitAndProjectSkills(opts.workspaceRoot, inputs);
|
|
53710
|
+
writePrimingHandles(join24(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
|
|
52899
53711
|
} catch (err2) {
|
|
52900
53712
|
console.warn("[skills] agent-skills projection failed (non-fatal):", err2 instanceof Error ? err2.message : err2);
|
|
52901
53713
|
}
|
|
@@ -53082,7 +53894,7 @@ function createWorkspaceEngine(opts) {
|
|
|
53082
53894
|
console.log(
|
|
53083
53895
|
"[errata] sync skipped \u2014 cloud sync consent is off (enable with `errata consent sync on`)"
|
|
53084
53896
|
);
|
|
53085
|
-
const pending =
|
|
53897
|
+
const pending = existsSync19(paths.outbox) ? readdirSync9(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
|
|
53086
53898
|
return { uploaded: 0, failed: 0, remaining: pending };
|
|
53087
53899
|
}
|
|
53088
53900
|
try {
|
|
@@ -53169,7 +53981,7 @@ async function startDaemon(opts) {
|
|
|
53169
53981
|
reviewUrl: () => webUiUrl + "/review"
|
|
53170
53982
|
});
|
|
53171
53983
|
const writeLockFile = (url2) => {
|
|
53172
|
-
|
|
53984
|
+
writeFileSync17(
|
|
53173
53985
|
engine.paths.daemonLock,
|
|
53174
53986
|
JSON.stringify({ pid: process.pid, webUiUrl: url2, startedAt: Date.now() }),
|
|
53175
53987
|
"utf8"
|
|
@@ -53212,7 +54024,7 @@ async function startDaemon(opts) {
|
|
|
53212
54024
|
);
|
|
53213
54025
|
await engine.stop();
|
|
53214
54026
|
try {
|
|
53215
|
-
if (
|
|
54027
|
+
if (existsSync20(engine.paths.daemonLock)) {
|
|
53216
54028
|
}
|
|
53217
54029
|
} catch {
|
|
53218
54030
|
}
|
|
@@ -53229,16 +54041,16 @@ async function listenServer(fetchFn, port) {
|
|
|
53229
54041
|
|
|
53230
54042
|
// src/registry.ts
|
|
53231
54043
|
init_paths();
|
|
53232
|
-
import { existsSync as
|
|
53233
|
-
import { join as
|
|
54044
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20, writeFileSync as writeFileSync18 } from "node:fs";
|
|
54045
|
+
import { join as join25 } from "node:path";
|
|
53234
54046
|
function registryPath() {
|
|
53235
|
-
return process.env["ERRATA_REGISTRY_PATH"] ??
|
|
54047
|
+
return process.env["ERRATA_REGISTRY_PATH"] ?? join25(globalDir(), "workspaces.json");
|
|
53236
54048
|
}
|
|
53237
54049
|
function read() {
|
|
53238
54050
|
const p = registryPath();
|
|
53239
|
-
if (!
|
|
54051
|
+
if (!existsSync21(p)) return { version: 1, workspaces: {} };
|
|
53240
54052
|
try {
|
|
53241
|
-
const parsed = JSON.parse(
|
|
54053
|
+
const parsed = JSON.parse(readFileSync20(p, "utf8"));
|
|
53242
54054
|
return { version: 1, workspaces: parsed.workspaces ?? {} };
|
|
53243
54055
|
} catch {
|
|
53244
54056
|
return { version: 1, workspaces: {} };
|
|
@@ -53246,7 +54058,7 @@ function read() {
|
|
|
53246
54058
|
}
|
|
53247
54059
|
function write(reg) {
|
|
53248
54060
|
ensureDir(globalDir());
|
|
53249
|
-
|
|
54061
|
+
writeFileSync18(registryPath(), JSON.stringify(reg, null, 2), "utf8");
|
|
53250
54062
|
}
|
|
53251
54063
|
function registerWorkspace(profile, root, now = Date.now()) {
|
|
53252
54064
|
const reg = read();
|
|
@@ -53263,7 +54075,7 @@ function pruneMissingWorkspaces() {
|
|
|
53263
54075
|
const reg = read();
|
|
53264
54076
|
const removed = [];
|
|
53265
54077
|
for (const [id, entry] of Object.entries(reg.workspaces)) {
|
|
53266
|
-
if (!
|
|
54078
|
+
if (!existsSync21(entry.path)) {
|
|
53267
54079
|
removed.push(entry);
|
|
53268
54080
|
delete reg.workspaces[id];
|
|
53269
54081
|
}
|
|
@@ -53272,13 +54084,13 @@ function pruneMissingWorkspaces() {
|
|
|
53272
54084
|
return removed;
|
|
53273
54085
|
}
|
|
53274
54086
|
function workspaceStatus(entry) {
|
|
53275
|
-
const missing = !
|
|
54087
|
+
const missing = !existsSync21(entry.path);
|
|
53276
54088
|
const lockPath = workspacePaths(entry.path).daemonLock;
|
|
53277
54089
|
let running = false;
|
|
53278
54090
|
let webUiUrl = null;
|
|
53279
|
-
if (
|
|
54091
|
+
if (existsSync21(lockPath)) {
|
|
53280
54092
|
try {
|
|
53281
|
-
const lock = JSON.parse(
|
|
54093
|
+
const lock = JSON.parse(readFileSync20(lockPath, "utf8"));
|
|
53282
54094
|
if (lock.pid && lock.webUiUrl && pidAlive(lock.pid)) {
|
|
53283
54095
|
running = true;
|
|
53284
54096
|
webUiUrl = lock.webUiUrl;
|
|
@@ -53306,7 +54118,7 @@ function pidAlive(pid) {
|
|
|
53306
54118
|
// src/multi.ts
|
|
53307
54119
|
init_dist();
|
|
53308
54120
|
init_src4();
|
|
53309
|
-
import { readFileSync as
|
|
54121
|
+
import { readFileSync as readFileSync23, unlinkSync as unlinkSync3, writeFileSync as writeFileSync19 } from "node:fs";
|
|
53310
54122
|
|
|
53311
54123
|
// src/principle-sync.ts
|
|
53312
54124
|
init_src4();
|
|
@@ -53334,8 +54146,8 @@ init_reconcile();
|
|
|
53334
54146
|
|
|
53335
54147
|
// src/lockfile-auto.ts
|
|
53336
54148
|
init_src();
|
|
53337
|
-
import { existsSync as
|
|
53338
|
-
import { join as
|
|
54149
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
|
|
54150
|
+
import { join as join26 } from "node:path";
|
|
53339
54151
|
|
|
53340
54152
|
// src/package-index.ts
|
|
53341
54153
|
init_src();
|
|
@@ -53421,10 +54233,12 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53421
54233
|
const ignored = (s) => ignorePatterns.length > 0 && ignorePatterns.some((p) => s.toLowerCase().includes(p));
|
|
53422
54234
|
const nodes = [];
|
|
53423
54235
|
const seen = /* @__PURE__ */ new Set();
|
|
54236
|
+
const localIdByWireId = {};
|
|
53424
54237
|
for (const label of labels) {
|
|
53425
54238
|
for (const n of store.findNodesByLabel(label)) {
|
|
53426
54239
|
if (n.attrs["source"] === "cloud") continue;
|
|
53427
54240
|
if (ignored(n.description) || ignored(String(n.attrs["name"] ?? ""))) continue;
|
|
54241
|
+
if (typeof n.attrs["contributedAtSeq"] === "number") continue;
|
|
53428
54242
|
nodes.push({
|
|
53429
54243
|
...n,
|
|
53430
54244
|
// Language wire id = the shared `languageCanonicalId` (warming-spine
|
|
@@ -53449,6 +54263,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53449
54263
|
anchorVisibility: "public"
|
|
53450
54264
|
});
|
|
53451
54265
|
seen.add(n.id);
|
|
54266
|
+
localIdByWireId[nodes[nodes.length - 1].id] = n.id;
|
|
53452
54267
|
}
|
|
53453
54268
|
}
|
|
53454
54269
|
if (nodes.length === 0) return null;
|
|
@@ -53468,7 +54283,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53468
54283
|
nodes,
|
|
53469
54284
|
edges
|
|
53470
54285
|
};
|
|
53471
|
-
return { ...base, payloadDigest: digest(base) };
|
|
54286
|
+
return { ...base, payloadDigest: digest(base), localIds: [...seen], localIdByWireId };
|
|
53472
54287
|
}
|
|
53473
54288
|
|
|
53474
54289
|
// src/lockfile-auto.ts
|
|
@@ -53481,11 +54296,11 @@ function runLockfilePass(opts) {
|
|
|
53481
54296
|
{ file: "package-lock.json", parse: parsePackageLockJson }
|
|
53482
54297
|
];
|
|
53483
54298
|
for (const c of candidates) {
|
|
53484
|
-
const p =
|
|
53485
|
-
if (!
|
|
54299
|
+
const p = join26(opts.root, c.file);
|
|
54300
|
+
if (!existsSync22(p)) continue;
|
|
53486
54301
|
let sbom;
|
|
53487
54302
|
try {
|
|
53488
|
-
sbom = c.parse(
|
|
54303
|
+
sbom = c.parse(readFileSync21(p, "utf8"));
|
|
53489
54304
|
} catch {
|
|
53490
54305
|
continue;
|
|
53491
54306
|
}
|
|
@@ -53500,6 +54315,7 @@ function runLockfilePass(opts) {
|
|
|
53500
54315
|
// src/instance-ingest.ts
|
|
53501
54316
|
init_src();
|
|
53502
54317
|
init_src2();
|
|
54318
|
+
init_src4();
|
|
53503
54319
|
init_src8();
|
|
53504
54320
|
init_src();
|
|
53505
54321
|
|
|
@@ -53550,6 +54366,10 @@ function stripCodebaseScope(scope) {
|
|
|
53550
54366
|
delete s["codebase"];
|
|
53551
54367
|
return s;
|
|
53552
54368
|
}
|
|
54369
|
+
function contributedWireId(n) {
|
|
54370
|
+
const cloudId = n.attrs["cloudNodeId"];
|
|
54371
|
+
return isMembraneSaltedId(cloudId) ? cloudId : n.id;
|
|
54372
|
+
}
|
|
53553
54373
|
function wireContextId(n) {
|
|
53554
54374
|
if (n.label === "Language" && n.attrs["source"] !== "cloud") {
|
|
53555
54375
|
const name2 = String(n.attrs["name"] ?? "").trim() || n.description.trim() || n.id.replace(/^lang:/, "");
|
|
@@ -53590,10 +54410,15 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53590
54410
|
const level = opts.level ?? 1;
|
|
53591
54411
|
const noveltyOpts = opts.dedupCosine != null ? { dedupCosine: opts.dedupCosine } : {};
|
|
53592
54412
|
const ignored = (text) => ignorePatterns.length > 0 && ignorePatterns.some((p) => text.toLowerCase().includes(p));
|
|
54413
|
+
const originSessionOf = (n) => {
|
|
54414
|
+
const key = sessionOriginKey(n.attrs["sources"]?.[0]);
|
|
54415
|
+
return key ? { originSession: key } : {};
|
|
54416
|
+
};
|
|
53593
54417
|
const shareable = (n) => ({
|
|
53594
54418
|
...n,
|
|
53595
54419
|
description: generalize(n.description, { level }).text,
|
|
53596
54420
|
embedding: [],
|
|
54421
|
+
...originSessionOf(n),
|
|
53597
54422
|
attrs: {
|
|
53598
54423
|
scope: stripCodebaseScope(n.attrs["scope"]),
|
|
53599
54424
|
...n.attrs["kind"] ? { kind: n.attrs["kind"] } : {},
|
|
@@ -53604,6 +54429,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53604
54429
|
const seen = /* @__PURE__ */ new Set();
|
|
53605
54430
|
const shippedById = /* @__PURE__ */ new Map();
|
|
53606
54431
|
const anchorSources = /* @__PURE__ */ new Set();
|
|
54432
|
+
const semanticEndpoints = /* @__PURE__ */ new Map();
|
|
53607
54433
|
const includedByLabel = /* @__PURE__ */ new Map();
|
|
53608
54434
|
for (const label of INSTANCE_LABELS) {
|
|
53609
54435
|
const ref = [];
|
|
@@ -53616,8 +54442,16 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53616
54442
|
const contributedAtSeq = n.attrs["contributedAtSeq"];
|
|
53617
54443
|
if (typeof contributedAtSeq === "number" && (n.lastReinforcedAtSeq ?? 0) <= contributedAtSeq) {
|
|
53618
54444
|
anchorSources.add(n.id);
|
|
54445
|
+
semanticEndpoints.set(n.id, contributedWireId(n));
|
|
53619
54446
|
continue;
|
|
53620
54447
|
}
|
|
54448
|
+
if (label === "Solution" && n.description.startsWith(AUTO_MINT_PREFIX)) {
|
|
54449
|
+
const problemShippable = store.inEdges(n.id, ["SOLVED_BY"]).some((e) => {
|
|
54450
|
+
const p = store.getNode(e.from);
|
|
54451
|
+
return p?.label === "Problem" && (seen.has(p.id) || typeof p.attrs["contributedAtSeq"] === "number");
|
|
54452
|
+
});
|
|
54453
|
+
if (!problemShippable) continue;
|
|
54454
|
+
}
|
|
53621
54455
|
if (!noveltyAgainst(n, ref, noveltyOpts).ready) continue;
|
|
53622
54456
|
ref.push(n);
|
|
53623
54457
|
const wireNode = shareable(n);
|
|
@@ -53625,6 +54459,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53625
54459
|
shippedById.set(n.id, wireNode);
|
|
53626
54460
|
seen.add(n.id);
|
|
53627
54461
|
anchorSources.add(n.id);
|
|
54462
|
+
semanticEndpoints.set(n.id, n.id);
|
|
53628
54463
|
}
|
|
53629
54464
|
}
|
|
53630
54465
|
for (const n of nodes) {
|
|
@@ -53665,8 +54500,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53665
54500
|
const sourceNode = store.getNode(sourceId);
|
|
53666
54501
|
if (sourceNode?.attrs["anchorsContributedDigest"] === dg) continue;
|
|
53667
54502
|
anchorDigests[sourceId] = dg;
|
|
53668
|
-
const
|
|
53669
|
-
const wireFrom = typeof skippedCloudId === "string" && skippedCloudId.length > 0 ? skippedCloudId : sourceId;
|
|
54503
|
+
const wireFrom = seen.has(sourceId) || !sourceNode ? sourceId : contributedWireId(sourceNode);
|
|
53670
54504
|
for (const { e, t, wireId } of targets) {
|
|
53671
54505
|
anchorEdges.push({ ...e, from: wireFrom, to: wireId, attrs: {} });
|
|
53672
54506
|
if (t.attrs["source"] === "cloud" || contextSeen.has(wireId)) continue;
|
|
@@ -53683,14 +54517,28 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53683
54517
|
}
|
|
53684
54518
|
}
|
|
53685
54519
|
}
|
|
54520
|
+
for (const [localId, shipped] of shippedById) {
|
|
54521
|
+
const local = store.getNode(localId);
|
|
54522
|
+
if (!local || local.label !== "Solution" || !local.description.startsWith(AUTO_MINT_PREFIX)) continue;
|
|
54523
|
+
const problem = store.inEdges(localId, ["SOLVED_BY"]).map((e) => store.getNode(e.from)).find((p) => p?.label === "Problem");
|
|
54524
|
+
if (!problem) continue;
|
|
54525
|
+
const inBatch = shippedById.get(problem.id);
|
|
54526
|
+
if (inBatch) {
|
|
54527
|
+
if (inBatch.anchorVisibility) shipped.anchorVisibility = inBatch.anchorVisibility;
|
|
54528
|
+
if (inBatch.anchor != null) shipped.anchor = inBatch.anchor;
|
|
54529
|
+
else delete shipped.anchor;
|
|
54530
|
+
} else if (isMembraneSaltedId(problem.attrs["cloudNodeId"])) {
|
|
54531
|
+
shipped.anchorVisibility = "private";
|
|
54532
|
+
delete shipped.anchor;
|
|
54533
|
+
}
|
|
54534
|
+
}
|
|
53686
54535
|
const project = opts.project;
|
|
53687
54536
|
if (project) {
|
|
53688
54537
|
const now = Date.now();
|
|
53689
54538
|
for (const sourceId of anchorSources) {
|
|
53690
54539
|
const source = store.getNode(sourceId);
|
|
53691
54540
|
if (!source) continue;
|
|
53692
|
-
const
|
|
53693
|
-
const wireFrom = typeof skippedCloudId === "string" && skippedCloudId.length > 0 ? skippedCloudId : sourceId;
|
|
54541
|
+
const wireFrom = seen.has(sourceId) ? sourceId : contributedWireId(source);
|
|
53694
54542
|
for (const e of store.outEdges(sourceId, ["ANCHORED_AT"])) {
|
|
53695
54543
|
const target = store.getNode(e.to);
|
|
53696
54544
|
if (!target) continue;
|
|
@@ -53729,7 +54577,17 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53729
54577
|
}
|
|
53730
54578
|
}
|
|
53731
54579
|
}
|
|
53732
|
-
|
|
54580
|
+
const edges = [];
|
|
54581
|
+
const semanticEdgeDigests = {};
|
|
54582
|
+
for (const [sourceId, wireFrom] of semanticEndpoints) {
|
|
54583
|
+
const outs = store.outEdges(sourceId, [...INSTANCE_EDGES]).map((e) => ({ e, wireTo: semanticEndpoints.get(e.to) })).filter((x) => x.wireTo != null);
|
|
54584
|
+
if (outs.length === 0) continue;
|
|
54585
|
+
const dg = digest(outs.map(({ e, wireTo }) => `${e.type}>${wireTo}`).sort());
|
|
54586
|
+
if (store.getNode(sourceId)?.attrs["semanticEdgesContributedDigest"] === dg) continue;
|
|
54587
|
+
semanticEdgeDigests[sourceId] = dg;
|
|
54588
|
+
for (const { e, wireTo } of outs) edges.push({ ...e, from: wireFrom, to: wireTo, attrs: {} });
|
|
54589
|
+
}
|
|
54590
|
+
if (nodes.length === 0 && anchorEdges.length === 0 && edges.length === 0) return null;
|
|
53733
54591
|
let symbolSummaries;
|
|
53734
54592
|
if (opts.lexicon && opts.lexicon.size > 0 && nodes.length > 0) {
|
|
53735
54593
|
const lexicon = opts.lexicon;
|
|
@@ -53751,12 +54609,6 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53751
54609
|
}
|
|
53752
54610
|
if (count > 0) symbolSummaries = sidecar;
|
|
53753
54611
|
}
|
|
53754
|
-
const edges = [];
|
|
53755
|
-
for (const id of seen) {
|
|
53756
|
-
for (const e of store.outEdges(id, [...INSTANCE_EDGES])) {
|
|
53757
|
-
if (seen.has(e.to)) edges.push({ ...e, attrs: {} });
|
|
53758
|
-
}
|
|
53759
|
-
}
|
|
53760
54612
|
edges.push(...anchorEdges);
|
|
53761
54613
|
const base = {
|
|
53762
54614
|
daemonVersion,
|
|
@@ -53775,7 +54627,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53775
54627
|
// payloadDigest — byte-identical to the pre-sidecar batch (backward compat).
|
|
53776
54628
|
...symbolSummaries ? { symbolSummaries } : {}
|
|
53777
54629
|
};
|
|
53778
|
-
return { ...base, payloadDigest: digest(base), anchorDigests };
|
|
54630
|
+
return { ...base, payloadDigest: digest(base), anchorDigests, semanticEdgeDigests };
|
|
53779
54631
|
}
|
|
53780
54632
|
|
|
53781
54633
|
// src/backfill-edges.ts
|
|
@@ -53896,7 +54748,7 @@ var ConsolidateWorker = class {
|
|
|
53896
54748
|
init_paths();
|
|
53897
54749
|
|
|
53898
54750
|
// src/lock.ts
|
|
53899
|
-
import { existsSync as
|
|
54751
|
+
import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
|
|
53900
54752
|
function isProcessAlive(pid) {
|
|
53901
54753
|
if (!pid || pid <= 0) return false;
|
|
53902
54754
|
try {
|
|
@@ -53907,9 +54759,9 @@ function isProcessAlive(pid) {
|
|
|
53907
54759
|
}
|
|
53908
54760
|
}
|
|
53909
54761
|
function readDaemonLock(lockPath) {
|
|
53910
|
-
if (!
|
|
54762
|
+
if (!existsSync23(lockPath)) return null;
|
|
53911
54763
|
try {
|
|
53912
|
-
const lock = JSON.parse(
|
|
54764
|
+
const lock = JSON.parse(readFileSync22(lockPath, "utf8"));
|
|
53913
54765
|
return typeof lock.pid === "number" ? lock : null;
|
|
53914
54766
|
} catch {
|
|
53915
54767
|
return null;
|
|
@@ -54151,13 +55003,13 @@ async function reanchorProject(opts) {
|
|
|
54151
55003
|
}
|
|
54152
55004
|
|
|
54153
55005
|
// src/adopt.ts
|
|
54154
|
-
import { existsSync as
|
|
54155
|
-
import { dirname as
|
|
55006
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
55007
|
+
import { dirname as dirname10, join as join27 } from "node:path";
|
|
54156
55008
|
function findGitRoot(absPath) {
|
|
54157
55009
|
let dir = absPath;
|
|
54158
55010
|
for (let depth = 0; depth < 64; depth++) {
|
|
54159
|
-
if (
|
|
54160
|
-
const parent =
|
|
55011
|
+
if (existsSync24(join27(dir, ".git"))) return dir;
|
|
55012
|
+
const parent = dirname10(dir);
|
|
54161
55013
|
if (parent === dir) return null;
|
|
54162
55014
|
dir = parent;
|
|
54163
55015
|
}
|
|
@@ -54265,6 +55117,18 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54265
55117
|
startLoopLagMonitor();
|
|
54266
55118
|
let baseUrl = "";
|
|
54267
55119
|
const records = [];
|
|
55120
|
+
const machineDominantLanguage = () => {
|
|
55121
|
+
const langCounts = /* @__PURE__ */ new Map();
|
|
55122
|
+
for (const r of records) {
|
|
55123
|
+
for (const l of r.engine.profile.languages ?? []) {
|
|
55124
|
+
const k = String(l).trim().toLowerCase();
|
|
55125
|
+
if (k) langCounts.set(k, (langCounts.get(k) ?? 0) + 1);
|
|
55126
|
+
}
|
|
55127
|
+
}
|
|
55128
|
+
return [...langCounts.entries()].sort(
|
|
55129
|
+
(a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1)
|
|
55130
|
+
)[0]?.[0];
|
|
55131
|
+
};
|
|
54268
55132
|
const updatePoller = opts.updateCheck ? startUpdatePoller({
|
|
54269
55133
|
channel: loadConfig().updateChannel,
|
|
54270
55134
|
onChange: (pending) => {
|
|
@@ -54277,6 +55141,47 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54277
55141
|
let serverPending = null;
|
|
54278
55142
|
const boundaryListeners = [];
|
|
54279
55143
|
let boundaryFlushing = false;
|
|
55144
|
+
let boundaryFlushStartedAt = 0;
|
|
55145
|
+
let boundaryFlushStage = "";
|
|
55146
|
+
let boundaryFlushUnits = 0;
|
|
55147
|
+
let boundaryFlushLastAdvanceAt = 0;
|
|
55148
|
+
let boundaryFlushBatches = 0;
|
|
55149
|
+
const beginFlush = (stage) => {
|
|
55150
|
+
boundaryFlushing = true;
|
|
55151
|
+
boundaryFlushStartedAt = Date.now();
|
|
55152
|
+
boundaryFlushLastAdvanceAt = Date.now();
|
|
55153
|
+
boundaryFlushUnits = 0;
|
|
55154
|
+
boundaryFlushBatches = 0;
|
|
55155
|
+
boundaryFlushItems = 0;
|
|
55156
|
+
boundaryFlushDetail = "";
|
|
55157
|
+
boundaryFlushStage = stage;
|
|
55158
|
+
};
|
|
55159
|
+
const flushStage = (stage) => {
|
|
55160
|
+
boundaryFlushStage = stage;
|
|
55161
|
+
};
|
|
55162
|
+
let boundaryFlushItems = 0;
|
|
55163
|
+
let boundaryFlushDetail = "";
|
|
55164
|
+
const noteFlushProgress = (units, items = 0, detail = "") => {
|
|
55165
|
+
boundaryFlushBatches++;
|
|
55166
|
+
boundaryFlushUnits += Math.max(0, units);
|
|
55167
|
+
boundaryFlushItems += Math.max(0, items);
|
|
55168
|
+
if (detail) boundaryFlushDetail = detail;
|
|
55169
|
+
if (units > 0 || items > 0) boundaryFlushLastAdvanceAt = Date.now();
|
|
55170
|
+
};
|
|
55171
|
+
const flushSnapshot = () => ({
|
|
55172
|
+
running: boundaryFlushing,
|
|
55173
|
+
stage: boundaryFlushStage,
|
|
55174
|
+
detail: boundaryFlushDetail,
|
|
55175
|
+
ageMs: boundaryFlushStartedAt ? Date.now() - boundaryFlushStartedAt : 0,
|
|
55176
|
+
units: boundaryFlushUnits,
|
|
55177
|
+
items: boundaryFlushItems,
|
|
55178
|
+
chunks: boundaryFlushBatches,
|
|
55179
|
+
stalledMs: boundaryFlushLastAdvanceAt ? Date.now() - boundaryFlushLastAdvanceAt : 0
|
|
55180
|
+
});
|
|
55181
|
+
const endFlush = () => {
|
|
55182
|
+
boundaryFlushing = false;
|
|
55183
|
+
boundaryFlushStage = "";
|
|
55184
|
+
};
|
|
54280
55185
|
const fireBoundary = () => {
|
|
54281
55186
|
for (const l of boundaryListeners) {
|
|
54282
55187
|
try {
|
|
@@ -54352,7 +55257,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54352
55257
|
void ambientLinkAll();
|
|
54353
55258
|
app.route(`/ws/${rec.id}`, rec.webApp);
|
|
54354
55259
|
try {
|
|
54355
|
-
|
|
55260
|
+
writeFileSync19(
|
|
54356
55261
|
rec.engine.paths.daemonLock,
|
|
54357
55262
|
JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${rec.id}`, startedAt: Date.now() }),
|
|
54358
55263
|
"utf8"
|
|
@@ -54541,7 +55446,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54541
55446
|
baseUrl = `http://127.0.0.1:${port}`;
|
|
54542
55447
|
try {
|
|
54543
55448
|
ensureDir(globalDir());
|
|
54544
|
-
|
|
55449
|
+
writeFileSync19(
|
|
54545
55450
|
lockPath,
|
|
54546
55451
|
JSON.stringify({ pid: process.pid, webUiUrl: baseUrl, startedAt: Date.now() }),
|
|
54547
55452
|
"utf8"
|
|
@@ -54550,7 +55455,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54550
55455
|
}
|
|
54551
55456
|
for (const r of records) {
|
|
54552
55457
|
try {
|
|
54553
|
-
|
|
55458
|
+
writeFileSync19(
|
|
54554
55459
|
r.engine.paths.daemonLock,
|
|
54555
55460
|
JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${r.id}`, startedAt: Date.now() }),
|
|
54556
55461
|
"utf8"
|
|
@@ -54813,26 +55718,32 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54813
55718
|
async syncPrinciplesPublic() {
|
|
54814
55719
|
if (!loadConfig().consent.sync) return { uploaded: 0, skipped: "consent-off" };
|
|
54815
55720
|
const ignore = loadClaimIgnorePatterns(globalDir());
|
|
54816
|
-
const
|
|
55721
|
+
const primaryLanguage = machineDominantLanguage();
|
|
55722
|
+
const payload = buildPrincipleIngest(sharedStore, DAEMON_VERSION, ignore, 2, {
|
|
55723
|
+
...primaryLanguage ? { primaryLanguage } : {}
|
|
55724
|
+
});
|
|
54817
55725
|
if (!payload) return { uploaded: 0 };
|
|
54818
55726
|
const res = await cloudNow().ingest(payload);
|
|
54819
55727
|
return { uploaded: res.accepted };
|
|
54820
55728
|
},
|
|
54821
55729
|
async syncTriagePublic() {
|
|
54822
|
-
|
|
55730
|
+
const cfg2 = loadConfig();
|
|
55731
|
+
if (!cfg2.consent.sync) return { uploaded: 0, skipped: "consent-off" };
|
|
54823
55732
|
const ignore = loadClaimIgnorePatterns(globalDir());
|
|
54824
|
-
const
|
|
54825
|
-
|
|
54826
|
-
|
|
54827
|
-
|
|
54828
|
-
|
|
54829
|
-
|
|
54830
|
-
|
|
54831
|
-
|
|
54832
|
-
|
|
54833
|
-
)[0]?.[0];
|
|
55733
|
+
const primaryLanguage = machineDominantLanguage();
|
|
55734
|
+
const languages = records.flatMap((r) => r.engine.profile.languages ?? []);
|
|
55735
|
+
const packages = cfg2.consent.contributePackages ? records.flatMap(
|
|
55736
|
+
(r) => r.engine.store.findNodesByLabel("Package").map((p) => ({
|
|
55737
|
+
name: String(p.attrs["name"] ?? ""),
|
|
55738
|
+
purl: String(p.attrs["purl"] ?? p.id)
|
|
55739
|
+
}))
|
|
55740
|
+
) : [];
|
|
55741
|
+
const tokenAnchors = buildBucketTokenAnchors(languages, packages);
|
|
54834
55742
|
const payload = buildTriageIngest(sharedStore, DAEMON_VERSION, ignore, 2, {
|
|
54835
|
-
...primaryLanguage ? { primaryLanguage } : {}
|
|
55743
|
+
...primaryLanguage ? { primaryLanguage } : {},
|
|
55744
|
+
tokenAnchors,
|
|
55745
|
+
// Scoped bucket keys carry package names — same consent as the purls.
|
|
55746
|
+
includeScopedBuckets: cfg2.consent.contributePackages
|
|
54836
55747
|
});
|
|
54837
55748
|
if (!payload) return { uploaded: 0 };
|
|
54838
55749
|
const res = await cloudNow().ingest(payload);
|
|
@@ -54862,8 +55773,25 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54862
55773
|
});
|
|
54863
55774
|
if (context) {
|
|
54864
55775
|
await lane("context", projectName, async () => {
|
|
54865
|
-
const res = await client.ingest(context
|
|
55776
|
+
const res = await client.ingest(context, {
|
|
55777
|
+
onChunk: (p) => {
|
|
55778
|
+
const seq = store.currentIngestSeq();
|
|
55779
|
+
for (const wireId of p.nodeIds) {
|
|
55780
|
+
const localId = context.localIdByWireId[wireId] ?? wireId;
|
|
55781
|
+
const local = store.getNode(localId);
|
|
55782
|
+
if (!local) continue;
|
|
55783
|
+
store.updateNode(localId, { attrs: { ...local.attrs, contributedAtSeq: seq } });
|
|
55784
|
+
}
|
|
55785
|
+
if (p.blockedNodeIds?.length) {
|
|
55786
|
+
console.log(
|
|
55787
|
+
`[sync\u2192cloud] [${projectName}] context: ${p.blockedNodeIds.length} node(s) refused by an edge/CDN filter on content \u2014 left pending, will retry`
|
|
55788
|
+
);
|
|
55789
|
+
}
|
|
55790
|
+
noteFlushProgress(p.accepted, p.nodeIds.length, `context ${p.chunkIndex}/${p.totalChunks}`);
|
|
55791
|
+
}
|
|
55792
|
+
});
|
|
54866
55793
|
uploaded += res.accepted;
|
|
55794
|
+
return res.accepted;
|
|
54867
55795
|
});
|
|
54868
55796
|
}
|
|
54869
55797
|
let lexicon;
|
|
@@ -54888,7 +55816,18 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54888
55816
|
if (instances) {
|
|
54889
55817
|
if (project) instances.projectId = project.projectId;
|
|
54890
55818
|
await lane("instances", projectName, async () => {
|
|
54891
|
-
const
|
|
55819
|
+
const blocked = /* @__PURE__ */ new Set();
|
|
55820
|
+
const res = await client.ingest(instances, {
|
|
55821
|
+
onChunk: (p) => {
|
|
55822
|
+
for (const id of p.blockedNodeIds ?? []) blocked.add(id);
|
|
55823
|
+
noteFlushProgress(p.accepted, p.nodeIds.length, `instances ${p.chunkIndex}/${p.totalChunks}`);
|
|
55824
|
+
}
|
|
55825
|
+
});
|
|
55826
|
+
if (blocked.size > 0) {
|
|
55827
|
+
console.log(
|
|
55828
|
+
`[sync\u2192cloud] [${projectName}] instances: ${blocked.size} node(s) refused by an edge/CDN filter on content \u2014 left pending, will retry. First: ${store.getNode([...blocked][0])?.description.slice(0, 120) ?? [...blocked][0]}`
|
|
55829
|
+
);
|
|
55830
|
+
}
|
|
54892
55831
|
uploaded += res.accepted;
|
|
54893
55832
|
const seq = store.currentIngestSeq();
|
|
54894
55833
|
const cloudIdByLocal = new Map(
|
|
@@ -54897,6 +55836,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54897
55836
|
for (const n of instances.nodes) {
|
|
54898
55837
|
const local = store.getNode(n.id);
|
|
54899
55838
|
if (!local || !["Problem", "Solution", "RootCause"].includes(local.label)) continue;
|
|
55839
|
+
if (blocked.has(n.id)) continue;
|
|
54900
55840
|
const cloudNodeId = cloudIdByLocal.get(n.id);
|
|
54901
55841
|
store.updateNode(n.id, {
|
|
54902
55842
|
attrs: {
|
|
@@ -54913,6 +55853,14 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54913
55853
|
attrs: { ...local.attrs, anchorsContributedDigest: dg }
|
|
54914
55854
|
});
|
|
54915
55855
|
}
|
|
55856
|
+
for (const [localId, dg] of Object.entries(instances.semanticEdgeDigests)) {
|
|
55857
|
+
const local = store.getNode(localId);
|
|
55858
|
+
if (!local) continue;
|
|
55859
|
+
store.updateNode(localId, {
|
|
55860
|
+
attrs: { ...local.attrs, semanticEdgesContributedDigest: dg }
|
|
55861
|
+
});
|
|
55862
|
+
}
|
|
55863
|
+
return res.accepted;
|
|
54916
55864
|
});
|
|
54917
55865
|
}
|
|
54918
55866
|
}
|
|
@@ -54981,12 +55929,14 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54981
55929
|
},
|
|
54982
55930
|
async sessionBoundaryFlush() {
|
|
54983
55931
|
if (boundaryFlushing) return { uploaded: 0, written: 0, pruned: 0, skipped: "in-flight" };
|
|
54984
|
-
|
|
55932
|
+
beginFlush("embed");
|
|
54985
55933
|
try {
|
|
54986
55934
|
for (const r of records) {
|
|
54987
55935
|
await r.engine.embedSettled();
|
|
54988
55936
|
}
|
|
55937
|
+
flushStage("instances");
|
|
54989
55938
|
const inst = await daemon.syncInstancesPublic();
|
|
55939
|
+
flushStage("skills");
|
|
54990
55940
|
const skills = await daemon.syncSkillsAll();
|
|
54991
55941
|
return {
|
|
54992
55942
|
uploaded: inst.uploaded,
|
|
@@ -54995,7 +55945,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54995
55945
|
...inst.errors ? { errors: inst.errors } : {}
|
|
54996
55946
|
};
|
|
54997
55947
|
} finally {
|
|
54998
|
-
|
|
55948
|
+
endFlush();
|
|
54999
55949
|
}
|
|
55000
55950
|
},
|
|
55001
55951
|
onSessionBoundary(cb) {
|
|
@@ -55003,7 +55953,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
55003
55953
|
},
|
|
55004
55954
|
async stop() {
|
|
55005
55955
|
try {
|
|
55006
|
-
const cur =
|
|
55956
|
+
const cur = readFileSync23(lockPath, "utf8");
|
|
55007
55957
|
if (JSON.parse(cur).pid === process.pid) unlinkSync3(lockPath);
|
|
55008
55958
|
} catch {
|
|
55009
55959
|
}
|
|
@@ -55020,20 +55970,24 @@ async function startMultiDaemon(opts = {}) {
|
|
|
55020
55970
|
}
|
|
55021
55971
|
};
|
|
55022
55972
|
app.post("/api/sync", async (c) => {
|
|
55023
|
-
if (boundaryFlushing) return c.json({ skipped: "in-flight" }, 409);
|
|
55024
|
-
|
|
55973
|
+
if (boundaryFlushing) return c.json({ skipped: "in-flight", ...flushSnapshot() }, 409);
|
|
55974
|
+
beginFlush("principles");
|
|
55025
55975
|
try {
|
|
55026
55976
|
const principles = await daemon.syncPrinciplesPublic();
|
|
55977
|
+
flushStage("triage");
|
|
55027
55978
|
const triage = await daemon.syncTriagePublic();
|
|
55979
|
+
flushStage("instances");
|
|
55028
55980
|
const instances = await daemon.syncInstancesPublic();
|
|
55981
|
+
flushStage("edge-backfill");
|
|
55029
55982
|
const edgeBackfill = await daemon.backfillCausalEdges();
|
|
55030
55983
|
return c.json({ principles, triage, instances, edgeBackfill });
|
|
55031
55984
|
} catch (err2) {
|
|
55032
55985
|
return c.json({ error: err2 instanceof Error ? err2.message : String(err2) }, 500);
|
|
55033
55986
|
} finally {
|
|
55034
|
-
|
|
55987
|
+
endFlush();
|
|
55035
55988
|
}
|
|
55036
55989
|
});
|
|
55990
|
+
app.get("/api/sync", (c) => c.json(flushSnapshot()));
|
|
55037
55991
|
app.post("/api/tick", async (c) => {
|
|
55038
55992
|
try {
|
|
55039
55993
|
return c.json({ reports: Object.fromEntries(await daemon.tickAll()) });
|
|
@@ -56016,21 +56970,21 @@ async function cmdInit() {
|
|
|
56016
56970
|
if (!skipHooks) {
|
|
56017
56971
|
console.log("");
|
|
56018
56972
|
console.log("installing harness hooks...");
|
|
56019
|
-
const { existsSync:
|
|
56020
|
-
const { join:
|
|
56973
|
+
const { existsSync: existsSync26 } = await import("node:fs");
|
|
56974
|
+
const { join: join29 } = await import("node:path");
|
|
56021
56975
|
try {
|
|
56022
56976
|
await installClaudeHooks(port);
|
|
56023
56977
|
} catch (err2) {
|
|
56024
56978
|
console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
56025
56979
|
}
|
|
56026
|
-
if (
|
|
56980
|
+
if (existsSync26(join29(ROOT, ".cursor"))) {
|
|
56027
56981
|
try {
|
|
56028
56982
|
await installCursorMcpConfig();
|
|
56029
56983
|
} catch (err2) {
|
|
56030
56984
|
console.warn(` \u26A0\uFE0F Cursor MCP config failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
56031
56985
|
}
|
|
56032
56986
|
}
|
|
56033
|
-
if (
|
|
56987
|
+
if (existsSync26(join29(ROOT, ".codex"))) {
|
|
56034
56988
|
try {
|
|
56035
56989
|
await installCodexHooks(port);
|
|
56036
56990
|
} catch (err2) {
|
|
@@ -56187,8 +57141,30 @@ async function cmdStatus() {
|
|
|
56187
57141
|
console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
|
|
56188
57142
|
console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
|
|
56189
57143
|
}
|
|
56190
|
-
console.log(` graph db: ${
|
|
56191
|
-
console.log(` event log: ${
|
|
57144
|
+
console.log(` graph db: ${existsSync25(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
|
|
57145
|
+
console.log(` event log: ${existsSync25(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
|
|
57146
|
+
if (existsSync25(paths.castalia)) {
|
|
57147
|
+
try {
|
|
57148
|
+
const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
|
|
57149
|
+
const store = openGraphStore2({ path: paths.castalia });
|
|
57150
|
+
try {
|
|
57151
|
+
let pending = 0;
|
|
57152
|
+
let total = 0;
|
|
57153
|
+
for (const label of ["Problem", "Solution", "RootCause"]) {
|
|
57154
|
+
for (const n of store.findNodesByLabel(label)) {
|
|
57155
|
+
if (n.attrs["source"] === "cloud") continue;
|
|
57156
|
+
total++;
|
|
57157
|
+
const c = n.attrs["contributedAtSeq"];
|
|
57158
|
+
if (typeof c !== "number" || (n.lastReinforcedAtSeq ?? 0) > c) pending++;
|
|
57159
|
+
}
|
|
57160
|
+
}
|
|
57161
|
+
console.log(` contribute: ${pending} pending of ${total} instance node(s)`);
|
|
57162
|
+
} finally {
|
|
57163
|
+
store.close();
|
|
57164
|
+
}
|
|
57165
|
+
} catch {
|
|
57166
|
+
}
|
|
57167
|
+
}
|
|
56192
57168
|
const lockPath = globalDaemonLock();
|
|
56193
57169
|
const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
|
|
56194
57170
|
console.log(
|
|
@@ -56818,11 +57794,11 @@ function cmdInstallationProfile(args2) {
|
|
|
56818
57794
|
}
|
|
56819
57795
|
async function cmdReview() {
|
|
56820
57796
|
const paths = workspacePaths(ROOT);
|
|
56821
|
-
if (!
|
|
57797
|
+
if (!existsSync25(paths.reviewQueue)) {
|
|
56822
57798
|
console.log("(review queue empty)");
|
|
56823
57799
|
return;
|
|
56824
57800
|
}
|
|
56825
|
-
const queue = JSON.parse(
|
|
57801
|
+
const queue = JSON.parse(readFileSync24(paths.reviewQueue, "utf8"));
|
|
56826
57802
|
if (queue.length === 0) {
|
|
56827
57803
|
console.log("(review queue empty)");
|
|
56828
57804
|
return;
|
|
@@ -57241,8 +58217,8 @@ async function cmdSimilar(args2) {
|
|
|
57241
58217
|
const limitIdx = args2.indexOf("--limit");
|
|
57242
58218
|
const limit = limitIdx >= 0 && args2[limitIdx + 1] ? Number(args2[limitIdx + 1]) : 5;
|
|
57243
58219
|
const { runTool: runTool2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
|
|
57244
|
-
await withStore((store) => {
|
|
57245
|
-
const r = runTool2("errata.similar", { nodeId: seed, qname: seed, limit }, store);
|
|
58220
|
+
await withStore(async (store) => {
|
|
58221
|
+
const r = await runTool2("errata.similar", { nodeId: seed, qname: seed, limit }, store);
|
|
57246
58222
|
if (!r.found) {
|
|
57247
58223
|
console.log("seed not found");
|
|
57248
58224
|
return;
|
|
@@ -57493,7 +58469,7 @@ async function gatherRepo(store, ws) {
|
|
|
57493
58469
|
};
|
|
57494
58470
|
}
|
|
57495
58471
|
async function gatherReportData(generatedAt) {
|
|
57496
|
-
const { existsSync:
|
|
58472
|
+
const { existsSync: existsSync26 } = await import("node:fs");
|
|
57497
58473
|
const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
|
|
57498
58474
|
const cfg = loadConfig();
|
|
57499
58475
|
const outbound = cfg.consent.sync ? "auto" : "off";
|
|
@@ -57501,7 +58477,7 @@ async function gatherReportData(generatedAt) {
|
|
|
57501
58477
|
for (const ws of listWorkspaces()) {
|
|
57502
58478
|
if (ws.missing) continue;
|
|
57503
58479
|
const dbPath = workspacePaths(ws.path).castalia;
|
|
57504
|
-
if (!
|
|
58480
|
+
if (!existsSync26(dbPath)) continue;
|
|
57505
58481
|
let store = null;
|
|
57506
58482
|
try {
|
|
57507
58483
|
store = openGraphStore2({ path: dbPath });
|
|
@@ -57532,7 +58508,7 @@ async function gatherReportData(generatedAt) {
|
|
|
57532
58508
|
};
|
|
57533
58509
|
}
|
|
57534
58510
|
async function cmdReport(args2) {
|
|
57535
|
-
const { mkdirSync: mkdirSync8, writeFileSync:
|
|
58511
|
+
const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync20 } = await import("node:fs");
|
|
57536
58512
|
const { renderReport: renderReport2 } = await Promise.resolve().then(() => (init_report_render(), report_render_exports));
|
|
57537
58513
|
const includeFutureVerbs = args2.includes("--future-verbs");
|
|
57538
58514
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -57545,8 +58521,8 @@ async function cmdReport(args2) {
|
|
|
57545
58521
|
const outDir = workspacePaths(ROOT).configDir;
|
|
57546
58522
|
mkdirSync8(outDir, { recursive: true });
|
|
57547
58523
|
const files = renderReport2(data, { includeFutureVerbs });
|
|
57548
|
-
for (const f of files)
|
|
57549
|
-
const indexPath =
|
|
58524
|
+
for (const f of files) writeFileSync20(join28(outDir, f.name), f.html, "utf8");
|
|
58525
|
+
const indexPath = join28(outDir, "report.html");
|
|
57550
58526
|
console.log(`report \u2192 ${indexPath}`);
|
|
57551
58527
|
console.log(
|
|
57552
58528
|
` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
|
|
@@ -57598,7 +58574,7 @@ function spawnDaemonDetached() {
|
|
|
57598
58574
|
try {
|
|
57599
58575
|
const logPath = daemonLogPath();
|
|
57600
58576
|
try {
|
|
57601
|
-
if (statSync6(logPath).size > 5 * 1024 * 1024)
|
|
58577
|
+
if (statSync6(logPath).size > 5 * 1024 * 1024) renameSync4(logPath, `${logPath}.1`);
|
|
57602
58578
|
} catch {
|
|
57603
58579
|
}
|
|
57604
58580
|
out2 = openSync2(logPath, "a");
|
|
@@ -57664,15 +58640,15 @@ function hookRelayCommand(port, path2) {
|
|
|
57664
58640
|
return process.platform === "win32" ? `cmd //c "curl -s --connect-timeout 1 --max-time 2 -X POST -H \\"Content-Type: application/json\\" --data-binary @- ${url2} 2>NUL || echo {}"` : `curl -s --connect-timeout 1 --max-time 2 -X POST -H 'Content-Type: application/json' --data-binary @- ${url2} 2>/dev/null || echo '{}'`;
|
|
57665
58641
|
}
|
|
57666
58642
|
async function installClaudeHooks(port) {
|
|
57667
|
-
const { mkdirSync: mkdirSync8, existsSync:
|
|
57668
|
-
const { join:
|
|
57669
|
-
const dir =
|
|
57670
|
-
if (!
|
|
57671
|
-
const file2 =
|
|
58643
|
+
const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
|
|
58644
|
+
const { join: join29 } = await import("node:path");
|
|
58645
|
+
const dir = join29(ROOT, ".claude");
|
|
58646
|
+
if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
|
|
58647
|
+
const file2 = join29(dir, "settings.json");
|
|
57672
58648
|
let settings = {};
|
|
57673
|
-
if (
|
|
58649
|
+
if (existsSync26(file2)) {
|
|
57674
58650
|
try {
|
|
57675
|
-
settings = JSON.parse(
|
|
58651
|
+
settings = JSON.parse(readFileSync25(file2, "utf8"));
|
|
57676
58652
|
} catch {
|
|
57677
58653
|
console.error(`refusing to overwrite invalid JSON at ${file2}`);
|
|
57678
58654
|
process.exit(2);
|
|
@@ -57718,10 +58694,10 @@ async function installClaudeHooks(port) {
|
|
|
57718
58694
|
dropErrata(list);
|
|
57719
58695
|
list.push({ hooks: [{ type: "command", command: injectCmd }] });
|
|
57720
58696
|
}
|
|
57721
|
-
|
|
58697
|
+
writeFileSync20(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
57722
58698
|
console.log(`installed Claude Code hooks \u2192 ${file2}`);
|
|
57723
58699
|
await installClaudeMcpConfig();
|
|
57724
|
-
const claudeMd =
|
|
58700
|
+
const claudeMd = join29(ROOT, "CLAUDE.md");
|
|
57725
58701
|
const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
|
|
57726
58702
|
if (recall.kind === "collision") {
|
|
57727
58703
|
console.warn(
|
|
@@ -57733,15 +58709,15 @@ async function installClaudeHooks(port) {
|
|
|
57733
58709
|
console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
|
|
57734
58710
|
}
|
|
57735
58711
|
async function installClaudeMcpConfig() {
|
|
57736
|
-
const { mkdirSync: mkdirSync8, existsSync:
|
|
57737
|
-
const { join:
|
|
57738
|
-
const file2 =
|
|
57739
|
-
const dir =
|
|
57740
|
-
if (!
|
|
58712
|
+
const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
|
|
58713
|
+
const { join: join29, dirname: dirname11 } = await import("node:path");
|
|
58714
|
+
const file2 = join29(ROOT, ".mcp.json");
|
|
58715
|
+
const dir = dirname11(file2);
|
|
58716
|
+
if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
|
|
57741
58717
|
let cfg = {};
|
|
57742
|
-
if (
|
|
58718
|
+
if (existsSync26(file2)) {
|
|
57743
58719
|
try {
|
|
57744
|
-
cfg = JSON.parse(
|
|
58720
|
+
cfg = JSON.parse(readFileSync25(file2, "utf8"));
|
|
57745
58721
|
} catch {
|
|
57746
58722
|
console.error(`refusing to overwrite invalid JSON at ${file2}`);
|
|
57747
58723
|
process.exit(2);
|
|
@@ -57749,21 +58725,21 @@ async function installClaudeMcpConfig() {
|
|
|
57749
58725
|
}
|
|
57750
58726
|
cfg.mcpServers ??= {};
|
|
57751
58727
|
cfg.mcpServers["errata"] = errataMcpInvocation();
|
|
57752
|
-
|
|
58728
|
+
writeFileSync20(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
57753
58729
|
console.log(`installed Claude Code MCP server config \u2192 ${file2}`);
|
|
57754
58730
|
console.log(` Claude Code will spawn \`errata mcp\` on workspace open.`);
|
|
57755
58731
|
console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
|
|
57756
58732
|
}
|
|
57757
58733
|
async function installCursorMcpConfig() {
|
|
57758
|
-
const { mkdirSync: mkdirSync8, existsSync:
|
|
57759
|
-
const { join:
|
|
57760
|
-
const dir =
|
|
57761
|
-
if (!
|
|
57762
|
-
const file2 =
|
|
58734
|
+
const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
|
|
58735
|
+
const { join: join29 } = await import("node:path");
|
|
58736
|
+
const dir = join29(ROOT, ".cursor");
|
|
58737
|
+
if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
|
|
58738
|
+
const file2 = join29(dir, "mcp.json");
|
|
57763
58739
|
let cfg = {};
|
|
57764
|
-
if (
|
|
58740
|
+
if (existsSync26(file2)) {
|
|
57765
58741
|
try {
|
|
57766
|
-
cfg = JSON.parse(
|
|
58742
|
+
cfg = JSON.parse(readFileSync25(file2, "utf8"));
|
|
57767
58743
|
} catch {
|
|
57768
58744
|
console.error(`refusing to overwrite invalid JSON at ${file2}`);
|
|
57769
58745
|
process.exit(2);
|
|
@@ -57771,7 +58747,7 @@ async function installCursorMcpConfig() {
|
|
|
57771
58747
|
}
|
|
57772
58748
|
cfg.mcpServers ??= {};
|
|
57773
58749
|
cfg.mcpServers["errata"] = errataMcpInvocation();
|
|
57774
|
-
|
|
58750
|
+
writeFileSync20(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
57775
58751
|
console.log(`installed Cursor MCP server config \u2192 ${file2}`);
|
|
57776
58752
|
console.log(` Cursor will spawn \`errata mcp\` on workspace open.`);
|
|
57777
58753
|
console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses`);
|
|
@@ -57779,16 +58755,16 @@ async function installCursorMcpConfig() {
|
|
|
57779
58755
|
console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
|
|
57780
58756
|
}
|
|
57781
58757
|
async function installCodexHooks(port) {
|
|
57782
|
-
const { mkdirSync: mkdirSync8, existsSync:
|
|
57783
|
-
const { join:
|
|
57784
|
-
const dir =
|
|
57785
|
-
if (!
|
|
57786
|
-
const file2 =
|
|
58758
|
+
const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
|
|
58759
|
+
const { join: join29 } = await import("node:path");
|
|
58760
|
+
const dir = join29(ROOT, ".codex");
|
|
58761
|
+
if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
|
|
58762
|
+
const file2 = join29(dir, "config.toml");
|
|
57787
58763
|
const BEGIN = `# >>> errata hooks (errata-managed)`;
|
|
57788
58764
|
const END = `# <<< errata hooks`;
|
|
57789
58765
|
let existing = "";
|
|
57790
|
-
if (
|
|
57791
|
-
existing =
|
|
58766
|
+
if (existsSync26(file2)) {
|
|
58767
|
+
existing = readFileSync25(file2, "utf8");
|
|
57792
58768
|
const beginIdx = existing.indexOf(BEGIN);
|
|
57793
58769
|
const endIdx = existing.indexOf(END);
|
|
57794
58770
|
if (beginIdx >= 0 && endIdx > beginIdx) {
|
|
@@ -57817,7 +58793,7 @@ ${END}
|
|
|
57817
58793
|
const final = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
|
|
57818
58794
|
|
|
57819
58795
|
${block}` : existing + (existing.endsWith("\n\n") ? "" : "\n") + block;
|
|
57820
|
-
|
|
58796
|
+
writeFileSync20(file2, final, "utf8");
|
|
57821
58797
|
console.log(`installed Codex hooks \u2192 ${file2}`);
|
|
57822
58798
|
console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
|
|
57823
58799
|
console.log("");
|
|
@@ -57858,7 +58834,30 @@ async function cmdSync(arg) {
|
|
|
57858
58834
|
signal: AbortSignal.timeout(15 * 6e4)
|
|
57859
58835
|
});
|
|
57860
58836
|
if (res.status === 409) {
|
|
57861
|
-
|
|
58837
|
+
const body2 = await res.json().catch(() => null);
|
|
58838
|
+
const ageMs = typeof body2?.ageMs === "number" ? body2.ageMs : 0;
|
|
58839
|
+
const dur = ageMs >= 6e4 ? `${Math.floor(ageMs / 6e4)}m` : `${Math.max(1, Math.round(ageMs / 1e3))}s`;
|
|
58840
|
+
const stage = body2?.stage ? ` \u2014 stage: ${body2.stage}` : "";
|
|
58841
|
+
const units = typeof body2?.units === "number" ? body2.units : 0;
|
|
58842
|
+
const items = typeof body2?.items === "number" ? body2.items : 0;
|
|
58843
|
+
const chunks = typeof body2?.chunks === "number" ? body2.chunks : 0;
|
|
58844
|
+
const stalledMs = typeof body2?.stalledMs === "number" ? body2.stalledMs : 0;
|
|
58845
|
+
const where = body2?.detail ? ` [${body2.detail}]` : "";
|
|
58846
|
+
const moved = chunks > 0 ? ` \xB7 ${chunks} chunk(s), ${items} shipped, ${units} accepted${where}` : "";
|
|
58847
|
+
console.log(
|
|
58848
|
+
ageMs > 0 ? `sync already running: the daemon has been draining for ${dur}${stage}${moved}. It will finish on its own; nothing new was started.` : "sync already running: the daemon is mid-drain \u2014 it will finish on its own. Nothing new was started."
|
|
58849
|
+
);
|
|
58850
|
+
if (stalledMs > 12e4 && chunks > 0) {
|
|
58851
|
+
const stalledMin = Math.floor(stalledMs / 6e4);
|
|
58852
|
+
console.log(
|
|
58853
|
+
` \u26A0 STALLED: ${chunks} chunk(s) shipped but nothing has moved for ${stalledMin}m \u2014 the lane is looping, not draining. Check \`errata status\` for the backlog depth and daemon.log for the failing lane.`
|
|
58854
|
+
);
|
|
58855
|
+
}
|
|
58856
|
+
if (ageMs > 30 * 6e4) {
|
|
58857
|
+
console.log(
|
|
58858
|
+
" \u26A0 this pass has run past 30 min \u2014 if daemon.log shows no upload progress it may be stranded (a sleep/resume can do this). Restart with: errata stop && errata start"
|
|
58859
|
+
);
|
|
58860
|
+
}
|
|
57862
58861
|
return;
|
|
57863
58862
|
}
|
|
57864
58863
|
if (!res.ok) {
|
|
@@ -57934,7 +58933,19 @@ async function cmdConsent(args2) {
|
|
|
57934
58933
|
console.log(" note: run `errata login` to actually upload (no cloud credential yet).");
|
|
57935
58934
|
}
|
|
57936
58935
|
}
|
|
58936
|
+
function installLogTimestamps() {
|
|
58937
|
+
const ISO_AT_START = /^\[?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
|
|
58938
|
+
for (const level of ["log", "warn", "error"]) {
|
|
58939
|
+
const original = console[level].bind(console);
|
|
58940
|
+
console[level] = (...args2) => {
|
|
58941
|
+
const first = args2[0];
|
|
58942
|
+
if (typeof first === "string" && ISO_AT_START.test(first)) return original(...args2);
|
|
58943
|
+
original(`[${(/* @__PURE__ */ new Date()).toISOString()}]`, ...args2);
|
|
58944
|
+
};
|
|
58945
|
+
}
|
|
58946
|
+
}
|
|
57937
58947
|
async function cmdDash(args2) {
|
|
58948
|
+
installLogTimestamps();
|
|
57938
58949
|
const portIdx = args2.indexOf("--port");
|
|
57939
58950
|
const port = portIdx >= 0 && args2[portIdx + 1] ? Number(args2[portIdx + 1]) : 7891;
|
|
57940
58951
|
const reindexOnStart = !args2.includes("--no-reindex");
|
|
@@ -58096,7 +59107,7 @@ async function cmdDash(args2) {
|
|
|
58096
59107
|
await yieldToLoop2();
|
|
58097
59108
|
try {
|
|
58098
59109
|
const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
|
|
58099
|
-
const res = bleedRules(
|
|
59110
|
+
const res = bleedRules(join28(r.root, ".claude", "rules"), items);
|
|
58100
59111
|
if (res.written || res.pruned) {
|
|
58101
59112
|
console.log(
|
|
58102
59113
|
`[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")
|