@inerrata-corporation/errata 2.0.2-dev.211 → 2.0.2-dev.220
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/errata.mjs +105 -12
- package/package.json +1 -1
package/errata.mjs
CHANGED
|
@@ -21852,7 +21852,7 @@ var init_client = __esm({
|
|
|
21852
21852
|
init_oauth();
|
|
21853
21853
|
init_src();
|
|
21854
21854
|
asWireCount = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? Math.floor(n) : null;
|
|
21855
|
-
INGEST_NODE_CHUNK =
|
|
21855
|
+
INGEST_NODE_CHUNK = 8;
|
|
21856
21856
|
CloudClient = class {
|
|
21857
21857
|
baseUrl;
|
|
21858
21858
|
apiKey;
|
|
@@ -22015,7 +22015,7 @@ var init_client = __esm({
|
|
|
22015
22015
|
async ingest(batch) {
|
|
22016
22016
|
const runId = randomUUID();
|
|
22017
22017
|
if (batch.nodes.length <= INGEST_NODE_CHUNK && batch.edges.length <= MAX_EDGES_PER_PAYLOAD) {
|
|
22018
|
-
const result = await this.
|
|
22018
|
+
const result = await this.ingestWithSplit(batch, batch.nodes, batch.edges);
|
|
22019
22019
|
return { ...summarizeIngestResult(result), result };
|
|
22020
22020
|
}
|
|
22021
22021
|
const merged = { runId, nodes: [], edges: [] };
|
|
@@ -22028,7 +22028,7 @@ var init_client = __esm({
|
|
|
22028
22028
|
const shipped = new Set(inChunk);
|
|
22029
22029
|
pendingEdges = pendingEdges.filter((e) => !shipped.has(e));
|
|
22030
22030
|
}
|
|
22031
|
-
const r = await this.
|
|
22031
|
+
const r = await this.ingestWithSplit(batch, nodeChunk, inChunk);
|
|
22032
22032
|
merged.nodes.push(...r.nodes);
|
|
22033
22033
|
merged.edges.push(...r.edges);
|
|
22034
22034
|
for (const rn of r.nodes) {
|
|
@@ -22055,6 +22055,38 @@ var init_client = __esm({
|
|
|
22055
22055
|
async ingestWire(payload) {
|
|
22056
22056
|
return this.json("POST", "/v2/ingest", payload);
|
|
22057
22057
|
}
|
|
22058
|
+
/**
|
|
22059
|
+
* Ship a node chunk, and on an EDGE TIMEOUT split it and retry the halves.
|
|
22060
|
+
*
|
|
22061
|
+
* `INGEST_NODE_CHUNK` is a guess about how long the server takes per node, and
|
|
22062
|
+
* that number moves with the size of the graph — so any fixed value eventually
|
|
22063
|
+
* drifts into the proxy's timeout and the lane dies silently (the 2026-07-31
|
|
22064
|
+
* three-day instances-lane outage). This makes the client self-correcting
|
|
22065
|
+
* instead: a chunk that times out is halved and retried, down to a single node,
|
|
22066
|
+
* so the drain degrades to slower rather than to stopped.
|
|
22067
|
+
*
|
|
22068
|
+
* ONLY on timeout-shaped failures (502/504/408, or a transport abort). A 4xx is
|
|
22069
|
+
* a verdict about the payload — splitting it would just re-send a rejected batch
|
|
22070
|
+
* N more times — and a 409/413 has its own handling upstream.
|
|
22071
|
+
*/
|
|
22072
|
+
async ingestWithSplit(batch, nodes, edges) {
|
|
22073
|
+
try {
|
|
22074
|
+
return await this.ingestWire(toWirePayload({ ...batch, nodes, edges }, randomUUID()));
|
|
22075
|
+
} catch (err2) {
|
|
22076
|
+
const status = err2 instanceof CloudError ? err2.status : 0;
|
|
22077
|
+
const timedOut = status === 502 || status === 504 || status === 408 || status === 0;
|
|
22078
|
+
if (!timedOut || nodes.length <= 1) throw err2;
|
|
22079
|
+
const mid = Math.ceil(nodes.length / 2);
|
|
22080
|
+
const a = await this.ingestWithSplit(batch, nodes.slice(0, mid), edges);
|
|
22081
|
+
const b = await this.ingestWithSplit(batch, nodes.slice(mid), []);
|
|
22082
|
+
return {
|
|
22083
|
+
runId: a.runId,
|
|
22084
|
+
nodes: [...a.nodes, ...b.nodes],
|
|
22085
|
+
edges: [...a.edges, ...b.edges],
|
|
22086
|
+
...a.patternReconciliation || b.patternReconciliation ? { patternReconciliation: { ...a.patternReconciliation, ...b.patternReconciliation } } : {}
|
|
22087
|
+
};
|
|
22088
|
+
}
|
|
22089
|
+
}
|
|
22058
22090
|
/**
|
|
22059
22091
|
* SDK v2 graph-native write. It remembers a typed diagnostic spine through
|
|
22060
22092
|
* `/v2/ingest` instead of the legacy forum question/answer routes:
|
|
@@ -52342,7 +52374,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
52342
52374
|
}
|
|
52343
52375
|
|
|
52344
52376
|
// src/engine.ts
|
|
52345
|
-
var DAEMON_VERSION = true ? "2.0.2-dev.
|
|
52377
|
+
var DAEMON_VERSION = true ? "2.0.2-dev.220" : "2.0.0-alpha.0";
|
|
52346
52378
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
52347
52379
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
52348
52380
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -54003,6 +54035,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
54003
54035
|
for (const n of store.findNodesByLabel(label)) {
|
|
54004
54036
|
if (n.attrs["source"] === "cloud") continue;
|
|
54005
54037
|
if (ignored(n.description) || ignored(String(n.attrs["name"] ?? ""))) continue;
|
|
54038
|
+
if (typeof n.attrs["contributedAtSeq"] === "number") continue;
|
|
54006
54039
|
nodes.push({
|
|
54007
54040
|
...n,
|
|
54008
54041
|
// Language wire id = the shared `languageCanonicalId` (warming-spine
|
|
@@ -54046,7 +54079,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
54046
54079
|
nodes,
|
|
54047
54080
|
edges
|
|
54048
54081
|
};
|
|
54049
|
-
return { ...base, payloadDigest: digest(base) };
|
|
54082
|
+
return { ...base, payloadDigest: digest(base), localIds: [...seen] };
|
|
54050
54083
|
}
|
|
54051
54084
|
|
|
54052
54085
|
// src/lockfile-auto.ts
|
|
@@ -54901,14 +54934,35 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54901
54934
|
let boundaryFlushing = false;
|
|
54902
54935
|
let boundaryFlushStartedAt = 0;
|
|
54903
54936
|
let boundaryFlushStage = "";
|
|
54937
|
+
let boundaryFlushUnits = 0;
|
|
54938
|
+
let boundaryFlushLastAdvanceAt = 0;
|
|
54939
|
+
let boundaryFlushBatches = 0;
|
|
54904
54940
|
const beginFlush = (stage) => {
|
|
54905
54941
|
boundaryFlushing = true;
|
|
54906
54942
|
boundaryFlushStartedAt = Date.now();
|
|
54943
|
+
boundaryFlushLastAdvanceAt = Date.now();
|
|
54944
|
+
boundaryFlushUnits = 0;
|
|
54945
|
+
boundaryFlushBatches = 0;
|
|
54907
54946
|
boundaryFlushStage = stage;
|
|
54908
54947
|
};
|
|
54909
54948
|
const flushStage = (stage) => {
|
|
54910
54949
|
boundaryFlushStage = stage;
|
|
54911
54950
|
};
|
|
54951
|
+
const noteFlushProgress = (units) => {
|
|
54952
|
+
boundaryFlushBatches++;
|
|
54953
|
+
if (units > 0) {
|
|
54954
|
+
boundaryFlushUnits += units;
|
|
54955
|
+
boundaryFlushLastAdvanceAt = Date.now();
|
|
54956
|
+
}
|
|
54957
|
+
};
|
|
54958
|
+
const flushSnapshot = () => ({
|
|
54959
|
+
running: boundaryFlushing,
|
|
54960
|
+
stage: boundaryFlushStage,
|
|
54961
|
+
ageMs: boundaryFlushStartedAt ? Date.now() - boundaryFlushStartedAt : 0,
|
|
54962
|
+
units: boundaryFlushUnits,
|
|
54963
|
+
batches: boundaryFlushBatches,
|
|
54964
|
+
stalledMs: boundaryFlushLastAdvanceAt ? Date.now() - boundaryFlushLastAdvanceAt : 0
|
|
54965
|
+
});
|
|
54912
54966
|
const endFlush = () => {
|
|
54913
54967
|
boundaryFlushing = false;
|
|
54914
54968
|
boundaryFlushStage = "";
|
|
@@ -55489,8 +55543,10 @@ async function startMultiDaemon(opts = {}) {
|
|
|
55489
55543
|
const errors = [];
|
|
55490
55544
|
const lane = async (name2, projectName, run3) => {
|
|
55491
55545
|
try {
|
|
55492
|
-
await run3();
|
|
55546
|
+
const accepted = await run3();
|
|
55547
|
+
noteFlushProgress(typeof accepted === "number" ? accepted : 0);
|
|
55493
55548
|
} catch (err2) {
|
|
55549
|
+
noteFlushProgress(0);
|
|
55494
55550
|
const msg = `[${projectName}] ${name2}: ${err2 instanceof Error ? err2.message : String(err2)}`;
|
|
55495
55551
|
errors.push(msg);
|
|
55496
55552
|
console.error(`[sync\u2192cloud] ${msg}`);
|
|
@@ -55506,6 +55562,13 @@ async function startMultiDaemon(opts = {}) {
|
|
|
55506
55562
|
await lane("context", projectName, async () => {
|
|
55507
55563
|
const res = await client.ingest(context);
|
|
55508
55564
|
uploaded += res.accepted;
|
|
55565
|
+
const seq = store.currentIngestSeq();
|
|
55566
|
+
for (const localId of context.localIds) {
|
|
55567
|
+
const local = store.getNode(localId);
|
|
55568
|
+
if (!local) continue;
|
|
55569
|
+
store.updateNode(localId, { attrs: { ...local.attrs, contributedAtSeq: seq } });
|
|
55570
|
+
}
|
|
55571
|
+
return res.accepted;
|
|
55509
55572
|
});
|
|
55510
55573
|
}
|
|
55511
55574
|
let lexicon;
|
|
@@ -55562,6 +55625,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
55562
55625
|
attrs: { ...local.attrs, semanticEdgesContributedDigest: dg }
|
|
55563
55626
|
});
|
|
55564
55627
|
}
|
|
55628
|
+
return res.accepted;
|
|
55565
55629
|
});
|
|
55566
55630
|
}
|
|
55567
55631
|
}
|
|
@@ -55671,11 +55735,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
55671
55735
|
}
|
|
55672
55736
|
};
|
|
55673
55737
|
app.post("/api/sync", async (c) => {
|
|
55674
|
-
if (boundaryFlushing)
|
|
55675
|
-
return c.json(
|
|
55676
|
-
{ skipped: "in-flight", stage: boundaryFlushStage, ageMs: Date.now() - boundaryFlushStartedAt },
|
|
55677
|
-
409
|
|
55678
|
-
);
|
|
55738
|
+
if (boundaryFlushing) return c.json({ skipped: "in-flight", ...flushSnapshot() }, 409);
|
|
55679
55739
|
beginFlush("principles");
|
|
55680
55740
|
try {
|
|
55681
55741
|
const principles = await daemon.syncPrinciplesPublic();
|
|
@@ -55692,6 +55752,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
55692
55752
|
endFlush();
|
|
55693
55753
|
}
|
|
55694
55754
|
});
|
|
55755
|
+
app.get("/api/sync", (c) => c.json(flushSnapshot()));
|
|
55695
55756
|
app.post("/api/tick", async (c) => {
|
|
55696
55757
|
try {
|
|
55697
55758
|
return c.json({ reports: Object.fromEntries(await daemon.tickAll()) });
|
|
@@ -56847,6 +56908,28 @@ async function cmdStatus() {
|
|
|
56847
56908
|
}
|
|
56848
56909
|
console.log(` graph db: ${existsSync25(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
|
|
56849
56910
|
console.log(` event log: ${existsSync25(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
|
|
56911
|
+
if (existsSync25(paths.castalia)) {
|
|
56912
|
+
try {
|
|
56913
|
+
const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
|
|
56914
|
+
const store = openGraphStore2({ path: paths.castalia });
|
|
56915
|
+
try {
|
|
56916
|
+
let pending = 0;
|
|
56917
|
+
let total = 0;
|
|
56918
|
+
for (const label of ["Problem", "Solution", "RootCause"]) {
|
|
56919
|
+
for (const n of store.findNodesByLabel(label)) {
|
|
56920
|
+
if (n.attrs["source"] === "cloud") continue;
|
|
56921
|
+
total++;
|
|
56922
|
+
const c = n.attrs["contributedAtSeq"];
|
|
56923
|
+
if (typeof c !== "number" || (n.lastReinforcedAtSeq ?? 0) > c) pending++;
|
|
56924
|
+
}
|
|
56925
|
+
}
|
|
56926
|
+
console.log(` contribute: ${pending} pending of ${total} instance node(s)`);
|
|
56927
|
+
} finally {
|
|
56928
|
+
store.close();
|
|
56929
|
+
}
|
|
56930
|
+
} catch {
|
|
56931
|
+
}
|
|
56932
|
+
}
|
|
56850
56933
|
const lockPath = globalDaemonLock();
|
|
56851
56934
|
const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
|
|
56852
56935
|
console.log(
|
|
@@ -58520,9 +58603,19 @@ async function cmdSync(arg) {
|
|
|
58520
58603
|
const ageMs = typeof body2?.ageMs === "number" ? body2.ageMs : 0;
|
|
58521
58604
|
const dur = ageMs >= 6e4 ? `${Math.floor(ageMs / 6e4)}m` : `${Math.max(1, Math.round(ageMs / 1e3))}s`;
|
|
58522
58605
|
const stage = body2?.stage ? ` \u2014 stage: ${body2.stage}` : "";
|
|
58606
|
+
const units = typeof body2?.units === "number" ? body2.units : 0;
|
|
58607
|
+
const batches = typeof body2?.batches === "number" ? body2.batches : 0;
|
|
58608
|
+
const stalledMs = typeof body2?.stalledMs === "number" ? body2.stalledMs : 0;
|
|
58609
|
+
const moved = batches > 0 ? ` \xB7 ${batches} batch(es), ${units} accepted` : "";
|
|
58523
58610
|
console.log(
|
|
58524
|
-
ageMs > 0 ? `sync already running: the daemon has been draining for ${dur}${stage}. 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."
|
|
58611
|
+
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."
|
|
58525
58612
|
);
|
|
58613
|
+
if (stalledMs > 12e4 && batches > 0) {
|
|
58614
|
+
const stalledMin = Math.floor(stalledMs / 6e4);
|
|
58615
|
+
console.log(
|
|
58616
|
+
` \u26A0 STALLED: ${batches} batch(es) shipped but nothing accepted for ${stalledMin}m \u2014 the lane is looping, not draining. Check \`errata status\` for the backlog depth and daemon.log for the failing lane.`
|
|
58617
|
+
);
|
|
58618
|
+
}
|
|
58526
58619
|
if (ageMs > 30 * 6e4) {
|
|
58527
58620
|
console.log(
|
|
58528
58621
|
" \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"
|