@inerrata-corporation/errata 2.0.2-dev.1130 → 2.0.2-dev.1172
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 +416 -18
- package/package.json +1 -1
package/errata.mjs
CHANGED
|
@@ -22357,6 +22357,24 @@ var init_mechanism_liveness = __esm({
|
|
|
22357
22357
|
STALL_MIN_RUNS = 3;
|
|
22358
22358
|
STALL_MIN_SPAN_MS = 60 * 60 * 1e3;
|
|
22359
22359
|
MECHANISMS = [
|
|
22360
|
+
{
|
|
22361
|
+
id: "refute-local-stamp",
|
|
22362
|
+
what: "stamps a harvested refute witness onto the LOCAL node (refutedAt/refutations/reason)",
|
|
22363
|
+
// Stamped at harvest, so the capture pass carries the effect counter.
|
|
22364
|
+
pass: "capture",
|
|
22365
|
+
// Refutes are rare by design (a verdict, not a lean-on) — the bar is "the
|
|
22366
|
+
// field is written at all", the revisitAnsweredWhen precedent. RG-mark
|
|
22367
|
+
// ships the producer; RG-recall is the ranking consumer — until it lands,
|
|
22368
|
+
// THIS descriptor is the consumer, which is exactly the condition the
|
|
22369
|
+
// probe exists to keep visible rather than let rot silently.
|
|
22370
|
+
fed: {
|
|
22371
|
+
labels: ["Problem", "Solution", "RootCause", "Pattern"],
|
|
22372
|
+
attr: "refutedAt",
|
|
22373
|
+
minFraction: 0
|
|
22374
|
+
},
|
|
22375
|
+
effectCounter: "refutesStamped",
|
|
22376
|
+
note: "RG-mark; reason text is local-only (wire ships nodeId+witnessKey)"
|
|
22377
|
+
},
|
|
22360
22378
|
{
|
|
22361
22379
|
id: "fix-candidate-ask",
|
|
22362
22380
|
what: "asks an agent whether an edit to a problem's anchor fixed it",
|
|
@@ -22976,6 +22994,14 @@ function renderSnapshot(s) {
|
|
|
22976
22994
|
);
|
|
22977
22995
|
lines.push("");
|
|
22978
22996
|
}
|
|
22997
|
+
if (s.daemonDistress) {
|
|
22998
|
+
const mins = Math.max(1, Math.round((Date.now() - s.daemonDistress.sinceTs) / 6e4));
|
|
22999
|
+
lines.push("### \u{1F6A8} errata daemon distress");
|
|
23000
|
+
lines.push(
|
|
23001
|
+
`${s.daemonDistress.body} (ongoing ~${mins} min). TELL YOUR USER about this now \u2014 the daemon cannot fix everything itself, and this line may be its only witness. \`errata status\` has detail; a daemon restart (\`errata stop\` then \`errata start\`) is safe mid-session.`
|
|
23002
|
+
);
|
|
23003
|
+
lines.push("");
|
|
23004
|
+
}
|
|
22979
23005
|
if (s.needsRevisit.length > 0) {
|
|
22980
23006
|
lines.push("### \u26A0\uFE0F Needs revisit \u2014 a fact these rested on changed");
|
|
22981
23007
|
lines.push(
|
|
@@ -23288,6 +23314,7 @@ function assembleAgentContext(opts) {
|
|
|
23288
23314
|
});
|
|
23289
23315
|
if (opts.edgeElicitation) snapshot.edgeElicitation = opts.edgeElicitation;
|
|
23290
23316
|
if (opts.pendingUpdate) snapshot.pendingUpdate = opts.pendingUpdate;
|
|
23317
|
+
if (opts.daemonDistress) snapshot.daemonDistress = opts.daemonDistress;
|
|
23291
23318
|
if (opts.reviewItems?.length) snapshot.reviewItems = [...opts.reviewItems];
|
|
23292
23319
|
if (opts.statedIntent) snapshot.statedIntent = opts.statedIntent;
|
|
23293
23320
|
if (opts.remote && opts.remote.length > 0) {
|
|
@@ -24650,6 +24677,7 @@ function defaultConfig() {
|
|
|
24650
24677
|
// user opts into sync at all.
|
|
24651
24678
|
consent: { sync: false, telemetry: false, contributePackages: true },
|
|
24652
24679
|
notifications: true,
|
|
24680
|
+
opsNotices: false,
|
|
24653
24681
|
onboardedAt: null,
|
|
24654
24682
|
machineId: null,
|
|
24655
24683
|
updateChannel: "dev",
|
|
@@ -53870,6 +53898,43 @@ function typePriorEdge(sourceLabel, targetLabel2, sentence = "") {
|
|
|
53870
53898
|
}
|
|
53871
53899
|
return "RELATES_TO";
|
|
53872
53900
|
}
|
|
53901
|
+
function refuteReason(statement) {
|
|
53902
|
+
const s = (statement ?? "").trim();
|
|
53903
|
+
return s.length > 0 ? s.slice(0, 240) : void 0;
|
|
53904
|
+
}
|
|
53905
|
+
function wireWitnessItems(items) {
|
|
53906
|
+
return items.map(({ nodeId, witnessKey }) => ({ nodeId, witnessKey }));
|
|
53907
|
+
}
|
|
53908
|
+
var REFUTE_REASONS_CAP = 5;
|
|
53909
|
+
var REFUTE_WITNESS_KEYS_CAP = 16;
|
|
53910
|
+
function applyLocalRefutations(store, refutes, ts) {
|
|
53911
|
+
const out2 = { stamped: 0, duplicate: 0, unknownNode: 0 };
|
|
53912
|
+
for (const r of refutes) {
|
|
53913
|
+
const node2 = store.getNode(r.nodeId);
|
|
53914
|
+
if (!node2) {
|
|
53915
|
+
out2.unknownNode++;
|
|
53916
|
+
continue;
|
|
53917
|
+
}
|
|
53918
|
+
const keys = node2.attrs["refuteWitnessKeys"] ?? [];
|
|
53919
|
+
if (keys.includes(r.witnessKey)) {
|
|
53920
|
+
out2.duplicate++;
|
|
53921
|
+
continue;
|
|
53922
|
+
}
|
|
53923
|
+
const reasons = node2.attrs["refuteReasons"] ?? [];
|
|
53924
|
+
store.updateNode(r.nodeId, {
|
|
53925
|
+
attrs: {
|
|
53926
|
+
...node2.attrs,
|
|
53927
|
+
refutedAt: ts,
|
|
53928
|
+
refutations: (Number(node2.attrs["refutations"]) || 0) + 1,
|
|
53929
|
+
...r.reason ? { refuteReasons: [r.reason, ...reasons].slice(0, REFUTE_REASONS_CAP) } : {},
|
|
53930
|
+
refuteWitnessKeys: [...keys, r.witnessKey].slice(-REFUTE_WITNESS_KEYS_CAP)
|
|
53931
|
+
},
|
|
53932
|
+
lastUpdatedAt: ts
|
|
53933
|
+
});
|
|
53934
|
+
out2.stamped++;
|
|
53935
|
+
}
|
|
53936
|
+
return out2;
|
|
53937
|
+
}
|
|
53873
53938
|
var PRIOR_TAG_INSTRUCTION = buildAgentInstruction();
|
|
53874
53939
|
function isEdgeElicitationEnabled() {
|
|
53875
53940
|
return (process.env["EDGE_ELICITATION_ENABLED"] ?? "true").toLowerCase() !== "false";
|
|
@@ -54130,7 +54195,8 @@ function harvestInlineTags(store, text, opts) {
|
|
|
54130
54195
|
if (nodeId) {
|
|
54131
54196
|
const witnessKey = `refute:${nodeId}:${digest({ s: tag.statement ?? "" })}`.slice(0, 72);
|
|
54132
54197
|
if (!plan.refutes.some((r) => r.witnessKey === witnessKey)) {
|
|
54133
|
-
|
|
54198
|
+
const reason = refuteReason(tag.statement);
|
|
54199
|
+
plan.refutes.push({ nodeId, witnessKey, ...reason ? { reason } : {} });
|
|
54134
54200
|
}
|
|
54135
54201
|
}
|
|
54136
54202
|
}
|
|
@@ -54165,7 +54231,8 @@ function harvestInlineTags(store, text, opts) {
|
|
|
54165
54231
|
} else {
|
|
54166
54232
|
const witnessKey = `refute:${targetId}:${digest({ s: tag.statement ?? "" })}`.slice(0, 72);
|
|
54167
54233
|
if (!plan.refutes.some((r) => r.witnessKey === witnessKey)) {
|
|
54168
|
-
|
|
54234
|
+
const reason = refuteReason(tag.statement);
|
|
54235
|
+
plan.refutes.push({ nodeId: targetId, witnessKey, ...reason ? { reason } : {} });
|
|
54169
54236
|
}
|
|
54170
54237
|
}
|
|
54171
54238
|
}
|
|
@@ -56427,7 +56494,8 @@ var TITLES = {
|
|
|
56427
56494
|
"hotspot-problem": "Errata \xB7 high-impact problem",
|
|
56428
56495
|
"index-started": "Errata \xB7 indexing\u2026",
|
|
56429
56496
|
"index-completed": "Errata \xB7 index ready \u2713",
|
|
56430
|
-
"mechanism-stalled": "Errata \xB7 a mechanism is not working"
|
|
56497
|
+
"mechanism-stalled": "Errata \xB7 a mechanism is not working",
|
|
56498
|
+
"daemon-distress": "Errata \xB7 daemon health \u26A0"
|
|
56431
56499
|
};
|
|
56432
56500
|
var lastFired = /* @__PURE__ */ new Map();
|
|
56433
56501
|
var THROTTLE_MS = 3e4;
|
|
@@ -56443,6 +56511,14 @@ function notifyEvent(kind, body2, opts = {}) {
|
|
|
56443
56511
|
} catch {
|
|
56444
56512
|
}
|
|
56445
56513
|
}
|
|
56514
|
+
function notifyOpsEvent(kind, body2, opts = {}) {
|
|
56515
|
+
try {
|
|
56516
|
+
if (!loadConfig().opsNotices) return;
|
|
56517
|
+
} catch {
|
|
56518
|
+
return;
|
|
56519
|
+
}
|
|
56520
|
+
notifyEvent(kind, body2, opts);
|
|
56521
|
+
}
|
|
56446
56522
|
function notifyTick(delta) {
|
|
56447
56523
|
const { problemsResolved, reviewsTriggered } = delta;
|
|
56448
56524
|
if (problemsResolved > 0)
|
|
@@ -56532,11 +56608,30 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
56532
56608
|
}
|
|
56533
56609
|
|
|
56534
56610
|
// src/watch-census.ts
|
|
56535
|
-
var census = {
|
|
56611
|
+
var census = {
|
|
56612
|
+
dispatched: 0,
|
|
56613
|
+
emitted: 0,
|
|
56614
|
+
logged: 0,
|
|
56615
|
+
rejected: 0,
|
|
56616
|
+
since: Date.now(),
|
|
56617
|
+
breakerRecycles: 0,
|
|
56618
|
+
breakerQuarantined: [],
|
|
56619
|
+
topOffender: null
|
|
56620
|
+
};
|
|
56621
|
+
var QUARANTINE_LIST_CAP = 8;
|
|
56536
56622
|
var noteWatchDispatched = () => void census.dispatched++;
|
|
56537
56623
|
var noteWatchEmitted = () => void census.emitted++;
|
|
56538
56624
|
var noteWatchLogged = () => void census.logged++;
|
|
56539
56625
|
var noteWatchRejected = () => void census.rejected++;
|
|
56626
|
+
var noteBreakerRecycle = (_path) => void census.breakerRecycles++;
|
|
56627
|
+
var noteBreakerQuarantine = (path2) => {
|
|
56628
|
+
if (!census.breakerQuarantined.includes(path2) && census.breakerQuarantined.length < QUARANTINE_LIST_CAP) {
|
|
56629
|
+
census.breakerQuarantined.push(path2);
|
|
56630
|
+
}
|
|
56631
|
+
};
|
|
56632
|
+
var noteWindowOffender = (path2, count, windowMs) => {
|
|
56633
|
+
census.topOffender = { path: path2, count, windowMs, at: Date.now() };
|
|
56634
|
+
};
|
|
56540
56635
|
function watchCensus() {
|
|
56541
56636
|
return { ...census };
|
|
56542
56637
|
}
|
|
@@ -56544,9 +56639,14 @@ function watchCensusLine(c = watchCensus()) {
|
|
|
56544
56639
|
if (c.dispatched === 0 && c.emitted === 0) return null;
|
|
56545
56640
|
const mins = Math.max(1, Math.round((Date.now() - c.since) / 6e4));
|
|
56546
56641
|
const perMin = Math.round(c.dispatched / mins);
|
|
56547
|
-
|
|
56548
|
-
if (c.
|
|
56549
|
-
|
|
56642
|
+
let base = `${c.dispatched} OS event(s) \u2192 ${c.logged} logged (${perMin}/min, ${c.rejected} rejected here)`;
|
|
56643
|
+
if (c.breakerRecycles > 0 || c.breakerQuarantined.length > 0) {
|
|
56644
|
+
const q = c.breakerQuarantined.length > 0 ? `, ${c.breakerQuarantined.length} quarantined` : "";
|
|
56645
|
+
base += ` \xB7 breaker: ${c.breakerRecycles} recycled${q}`;
|
|
56646
|
+
}
|
|
56647
|
+
if (c.dispatched > 500 && c.dispatched / Math.max(c.logged, 1) > 1e4) {
|
|
56648
|
+
const top = c.topOffender ? ` \u2014 top: ${c.topOffender.path} (${c.topOffender.count}/${Math.round(c.topOffender.windowMs / 1e3)}s)` : "";
|
|
56649
|
+
return `${base} \u26A0 watcher is doing work with NO output \u2014 a watch target likely contains an ignored tree${top}`;
|
|
56550
56650
|
}
|
|
56551
56651
|
return base;
|
|
56552
56652
|
}
|
|
@@ -56588,6 +56688,11 @@ function createLivenessWatch(deps) {
|
|
|
56588
56688
|
watchEmitted: watch2.emitted,
|
|
56589
56689
|
watchLogged: watch2.logged,
|
|
56590
56690
|
watchRejected: watch2.rejected,
|
|
56691
|
+
// HZ-watch-breaker: a recycle count trending upward across hours is a
|
|
56692
|
+
// handle that keeps wedging — the restart-survivable trace the 8-16
|
|
56693
|
+
// phantom-rename storm never left.
|
|
56694
|
+
watchBreakerRecycles: watch2.breakerRecycles,
|
|
56695
|
+
watchBreakerQuarantined: watch2.breakerQuarantined.length,
|
|
56591
56696
|
// Concurrency and the longest single holder — the two readings that
|
|
56592
56697
|
// named the 6017s zombie pass. A pass legitimately running for hours
|
|
56593
56698
|
// and one wedged forever look identical without a trend.
|
|
@@ -56612,8 +56717,108 @@ function createLivenessWatch(deps) {
|
|
|
56612
56717
|
};
|
|
56613
56718
|
}
|
|
56614
56719
|
|
|
56720
|
+
// src/watch-breaker.ts
|
|
56721
|
+
function numEnv3(key, fallback) {
|
|
56722
|
+
const n = Number(process.env[key]);
|
|
56723
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
56724
|
+
}
|
|
56725
|
+
var BREAKER_WINDOW_MS = numEnv3("ERRATA_WATCH_BREAKER_WINDOW_MS", 1e4);
|
|
56726
|
+
var BREAKER_TRIP_SELF_EVENTS = numEnv3("ERRATA_WATCH_BREAKER_TRIP", 1e3);
|
|
56727
|
+
var BREAKER_MAX_RECYCLES = numEnv3("ERRATA_WATCH_BREAKER_RECYCLES", 2);
|
|
56728
|
+
var BREAKER_REWEDGE_TTL_MS = numEnv3("ERRATA_WATCH_BREAKER_TTL_MS", 6e5);
|
|
56729
|
+
var BREAKER_READD_DELAY_MS = numEnv3("ERRATA_WATCH_BREAKER_READD_MS", 250);
|
|
56730
|
+
var OFFENDER_FLOOR = 100;
|
|
56731
|
+
function normalizeWatchPath(p) {
|
|
56732
|
+
let s = p.startsWith("\\\\?\\") ? p.slice(4) : p;
|
|
56733
|
+
s = s.replace(/\\/g, "/");
|
|
56734
|
+
if (s.length > 1 && s.endsWith("/")) s = s.slice(0, -1);
|
|
56735
|
+
return process.platform === "win32" ? s.toLowerCase() : s;
|
|
56736
|
+
}
|
|
56737
|
+
function createWatchBreaker(deps) {
|
|
56738
|
+
const now = deps.now ?? Date.now;
|
|
56739
|
+
const say = deps.onAction ?? (() => {
|
|
56740
|
+
});
|
|
56741
|
+
const windows2 = /* @__PURE__ */ new Map();
|
|
56742
|
+
let windowStart = now();
|
|
56743
|
+
const recycleHistory = /* @__PURE__ */ new Map();
|
|
56744
|
+
const quarantined = /* @__PURE__ */ new Set();
|
|
56745
|
+
const pendingReadds = /* @__PURE__ */ new Set();
|
|
56746
|
+
const rotate = (t) => {
|
|
56747
|
+
let top = null;
|
|
56748
|
+
for (const [path2, w] of windows2) {
|
|
56749
|
+
if (w.dispatched >= OFFENDER_FLOOR && (!top || w.dispatched > top.count)) {
|
|
56750
|
+
top = { path: path2, count: w.dispatched };
|
|
56751
|
+
}
|
|
56752
|
+
}
|
|
56753
|
+
if (top) noteWindowOffender(top.path, top.count, BREAKER_WINDOW_MS);
|
|
56754
|
+
windows2.clear();
|
|
56755
|
+
windowStart = t;
|
|
56756
|
+
};
|
|
56757
|
+
const trip = (path2, t) => {
|
|
56758
|
+
const history = (recycleHistory.get(path2) ?? []).filter(
|
|
56759
|
+
(ts) => t - ts < BREAKER_REWEDGE_TTL_MS
|
|
56760
|
+
);
|
|
56761
|
+
if (history.length >= BREAKER_MAX_RECYCLES) {
|
|
56762
|
+
quarantined.add(path2);
|
|
56763
|
+
recycleHistory.delete(path2);
|
|
56764
|
+
try {
|
|
56765
|
+
deps.unwatch(path2);
|
|
56766
|
+
} catch {
|
|
56767
|
+
}
|
|
56768
|
+
noteBreakerQuarantine(path2);
|
|
56769
|
+
say(
|
|
56770
|
+
`QUARANTINED ${path2} \u2014 re-wedged after ${history.length} recycle(s); unwatched for this session (reconcile covers its edits)`
|
|
56771
|
+
);
|
|
56772
|
+
return;
|
|
56773
|
+
}
|
|
56774
|
+
history.push(t);
|
|
56775
|
+
recycleHistory.set(path2, history);
|
|
56776
|
+
try {
|
|
56777
|
+
deps.unwatch(path2);
|
|
56778
|
+
} catch {
|
|
56779
|
+
}
|
|
56780
|
+
const timer = setTimeout(() => {
|
|
56781
|
+
pendingReadds.delete(timer);
|
|
56782
|
+
try {
|
|
56783
|
+
deps.readd(path2);
|
|
56784
|
+
} catch {
|
|
56785
|
+
}
|
|
56786
|
+
}, BREAKER_READD_DELAY_MS);
|
|
56787
|
+
timer.unref?.();
|
|
56788
|
+
pendingReadds.add(timer);
|
|
56789
|
+
noteBreakerRecycle(path2);
|
|
56790
|
+
say(
|
|
56791
|
+
`recycled ${path2} \u2014 ${BREAKER_TRIP_SELF_EVENTS}+ phantom self-events in ${Math.round(BREAKER_WINDOW_MS / 1e3)}s; handle closed + re-created`
|
|
56792
|
+
);
|
|
56793
|
+
};
|
|
56794
|
+
return {
|
|
56795
|
+
onRaw(event, evPath, watchedPath) {
|
|
56796
|
+
if (typeof watchedPath !== "string" || watchedPath.length === 0) return;
|
|
56797
|
+
const t = now();
|
|
56798
|
+
if (t - windowStart >= BREAKER_WINDOW_MS) rotate(t);
|
|
56799
|
+
let w = windows2.get(watchedPath);
|
|
56800
|
+
if (!w) {
|
|
56801
|
+
w = { dispatched: 0, self: 0, tripped: false };
|
|
56802
|
+
windows2.set(watchedPath, w);
|
|
56803
|
+
}
|
|
56804
|
+
w.dispatched++;
|
|
56805
|
+
const self = evPath == null || evPath === "" || typeof evPath === "string" && normalizeWatchPath(evPath) === normalizeWatchPath(watchedPath);
|
|
56806
|
+
if (!self) return;
|
|
56807
|
+
w.self++;
|
|
56808
|
+
if (!w.tripped && !quarantined.has(watchedPath) && w.self >= BREAKER_TRIP_SELF_EVENTS) {
|
|
56809
|
+
w.tripped = true;
|
|
56810
|
+
trip(watchedPath, t);
|
|
56811
|
+
}
|
|
56812
|
+
},
|
|
56813
|
+
stop() {
|
|
56814
|
+
for (const timer of pendingReadds) clearTimeout(timer);
|
|
56815
|
+
pendingReadds.clear();
|
|
56816
|
+
}
|
|
56817
|
+
};
|
|
56818
|
+
}
|
|
56819
|
+
|
|
56615
56820
|
// src/engine.ts
|
|
56616
|
-
var DAEMON_VERSION = true ? "2.0.2-dev.
|
|
56821
|
+
var DAEMON_VERSION = true ? "2.0.2-dev.1172" : "2.0.0-alpha.0";
|
|
56617
56822
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
56618
56823
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
56619
56824
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -56745,6 +56950,7 @@ function createWorkspaceEngine(opts) {
|
|
|
56745
56950
|
const cloud = opts.cloud;
|
|
56746
56951
|
const telemetry = new TelemetryRecorder(profile.id, DAEMON_VERSION);
|
|
56747
56952
|
let watcher = null;
|
|
56953
|
+
let watchBreaker = null;
|
|
56748
56954
|
let lastActivityTs = Date.now();
|
|
56749
56955
|
let stopGit = null;
|
|
56750
56956
|
let flushTimer = null;
|
|
@@ -56757,7 +56963,23 @@ function createWorkspaceEngine(opts) {
|
|
|
56757
56963
|
ignoreInitial: true,
|
|
56758
56964
|
persistent: true
|
|
56759
56965
|
});
|
|
56760
|
-
|
|
56966
|
+
watchBreaker = createWatchBreaker({
|
|
56967
|
+
unwatch: (p) => void watcher?.unwatch(p),
|
|
56968
|
+
readd: (p) => void watcher?.add(p),
|
|
56969
|
+
// Toast AND log: the autostarted daemon's stdout is a discarded stream
|
|
56970
|
+
// (the 8-01 unobservability finding), so console alone would make every
|
|
56971
|
+
// production breaker action invisible — the exact gauge-with-no-alarm
|
|
56972
|
+
// shape this mechanism exists to end.
|
|
56973
|
+
onAction: (line) => {
|
|
56974
|
+
console.warn(`[errata] watch-breaker: ${line}`);
|
|
56975
|
+
notifyOpsEvent("daemon-distress", `watch-breaker: ${line}`, { key: `breaker:${profile.id}` });
|
|
56976
|
+
}
|
|
56977
|
+
});
|
|
56978
|
+
watcher.on("raw", (event, evPath, opts2) => {
|
|
56979
|
+
noteWatchDispatched();
|
|
56980
|
+
const watchedPath = opts2 && typeof opts2 === "object" && "watchedPath" in opts2 ? opts2.watchedPath : void 0;
|
|
56981
|
+
watchBreaker?.onRaw(event, evPath, watchedPath);
|
|
56982
|
+
});
|
|
56761
56983
|
const onFs = (kind) => (path2) => {
|
|
56762
56984
|
noteWatchEmitted();
|
|
56763
56985
|
if (IGNORED_PATH.test(path2) || IGNORED_NOISE.test(path2)) {
|
|
@@ -57052,6 +57274,7 @@ function createWorkspaceEngine(opts) {
|
|
|
57052
57274
|
}) : null;
|
|
57053
57275
|
const doneRender = prebuilt ? null : markPass(`context-render-inline:${profile.name}`);
|
|
57054
57276
|
const pendingUpdate = opts.pendingUpdate?.() ?? null;
|
|
57277
|
+
const daemonDistress = opts.daemonDistress?.() ?? null;
|
|
57055
57278
|
const reviewItems = pickReviewItems();
|
|
57056
57279
|
const liveIntent = intents.mostRecent(Date.now());
|
|
57057
57280
|
maybeRefreshIntentPriors(liveIntent ?? null);
|
|
@@ -57069,6 +57292,7 @@ function createWorkspaceEngine(opts) {
|
|
|
57069
57292
|
...elicit ? { edgeElicitation: { instruction: PRIOR_TAG_INSTRUCTION } } : {},
|
|
57070
57293
|
...prebuilt ? { snapshot: prebuilt } : {},
|
|
57071
57294
|
...pendingUpdate ? { pendingUpdate } : {},
|
|
57295
|
+
...daemonDistress ? { daemonDistress } : {},
|
|
57072
57296
|
...reviewItems.length ? { reviewItems } : {},
|
|
57073
57297
|
...liveIntent ? {
|
|
57074
57298
|
statedIntent: {
|
|
@@ -57429,6 +57653,7 @@ function createWorkspaceEngine(opts) {
|
|
|
57429
57653
|
let exposureEvicted = 0;
|
|
57430
57654
|
let exposureUnshown = 0;
|
|
57431
57655
|
let corroboratedEdges = 0;
|
|
57656
|
+
let refutesStamped = 0;
|
|
57432
57657
|
const dispositions = emptyTagDispositions();
|
|
57433
57658
|
let unresolvedHandles = 0;
|
|
57434
57659
|
let priorEdgesFlagOff = 0;
|
|
@@ -57570,6 +57795,7 @@ function createWorkspaceEngine(opts) {
|
|
|
57570
57795
|
});
|
|
57571
57796
|
priorEdges += plan.priorEdges;
|
|
57572
57797
|
corroboratedEdges += plan.corroboratedEdges;
|
|
57798
|
+
refutesStamped += applyLocalRefutations(store, plan.refutes, t).stamped;
|
|
57573
57799
|
addTagDispositions(dispositions, plan.dispositions);
|
|
57574
57800
|
unresolvedHandles += plan.unresolvedHandles.length;
|
|
57575
57801
|
priorEdgesFlagOff += plan.priorEdgesSuppressed.flagOff;
|
|
@@ -57945,7 +58171,7 @@ function createWorkspaceEngine(opts) {
|
|
|
57945
58171
|
if (typeof cloud.reportContradictions === "function") {
|
|
57946
58172
|
await sendWitnesses(
|
|
57947
58173
|
"contradict",
|
|
57948
|
-
plan.refutes,
|
|
58174
|
+
wireWitnessItems(plan.refutes),
|
|
57949
58175
|
(items2, session) => cloud.reportContradictions({
|
|
57950
58176
|
daemonVersion: DAEMON_VERSION,
|
|
57951
58177
|
projectId: profile.id,
|
|
@@ -58013,6 +58239,9 @@ function createWorkspaceEngine(opts) {
|
|
|
58013
58239
|
triaged,
|
|
58014
58240
|
priorEdges,
|
|
58015
58241
|
corroboratedEdges,
|
|
58242
|
+
// RG-mark: refute witnesses stamped onto local nodes (the local half of
|
|
58243
|
+
// the contradict channel; the wire half rides the witness ledger).
|
|
58244
|
+
refutesStamped,
|
|
58016
58245
|
// WM-labels calibration cells (see prior-tags exposure split).
|
|
58017
58246
|
exposureShown,
|
|
58018
58247
|
exposureEvicted,
|
|
@@ -58586,6 +58815,7 @@ function createWorkspaceEngine(opts) {
|
|
|
58586
58815
|
async stop() {
|
|
58587
58816
|
if (stopGit) stopGit();
|
|
58588
58817
|
if (passWorker) await passWorker.stop();
|
|
58818
|
+
if (watchBreaker) watchBreaker.stop();
|
|
58589
58819
|
if (watcher) await watcher.close();
|
|
58590
58820
|
if (flushTimer) clearTimeout(flushTimer);
|
|
58591
58821
|
for (const tmr of diagTimers.values()) clearTimeout(tmr);
|
|
@@ -59955,6 +60185,137 @@ function createRootAdopter(deps) {
|
|
|
59955
60185
|
};
|
|
59956
60186
|
}
|
|
59957
60187
|
|
|
60188
|
+
// src/health-sentinel.ts
|
|
60189
|
+
function numEnv4(key, fallback) {
|
|
60190
|
+
const n = Number(process.env[key]);
|
|
60191
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
60192
|
+
}
|
|
60193
|
+
var SENTINEL_TICK_MS = numEnv4("ERRATA_SENTINEL_TICK_MS", 3e4);
|
|
60194
|
+
var SENTINEL_WINDOW_TICKS = numEnv4("ERRATA_SENTINEL_WINDOW_TICKS", 6);
|
|
60195
|
+
var SENTINEL_BURN_CORES = numEnv4("ERRATA_SENTINEL_BURN_CORES", 1.2);
|
|
60196
|
+
var SENTINEL_STORM_RATE = numEnv4("ERRATA_SENTINEL_STORM_RATE", 1e3);
|
|
60197
|
+
var SENTINEL_STORM_TICKS = numEnv4("ERRATA_SENTINEL_STORM_TICKS", 4);
|
|
60198
|
+
var SENTINEL_RENOTIFY_MS = numEnv4("ERRATA_SENTINEL_RENOTIFY_MS", 18e5);
|
|
60199
|
+
var SENTINEL_CALM_TICKS = numEnv4("ERRATA_SENTINEL_CALM_TICKS", 2);
|
|
60200
|
+
var SENTINEL_TOAST_COOLDOWN_MS = numEnv4("ERRATA_SENTINEL_TOAST_COOLDOWN_MS", 6e5);
|
|
60201
|
+
function createHealthSentinel(deps = {}) {
|
|
60202
|
+
const now = deps.now ?? Date.now;
|
|
60203
|
+
const cpuUsage = deps.cpuUsage ?? (() => process.cpuUsage());
|
|
60204
|
+
const readCensus = deps.readCensus ?? (() => {
|
|
60205
|
+
const c = watchCensus();
|
|
60206
|
+
return {
|
|
60207
|
+
dispatched: c.dispatched,
|
|
60208
|
+
logged: c.logged,
|
|
60209
|
+
topOffender: c.topOffender ? { path: c.topOffender.path, count: c.topOffender.count } : null
|
|
60210
|
+
};
|
|
60211
|
+
});
|
|
60212
|
+
const activePasses = deps.activePasses ?? (() => passState().active.map((p) => p.name));
|
|
60213
|
+
const rssMb = deps.rssMb ?? (() => Math.round(process.memoryUsage.rss() / 1048576));
|
|
60214
|
+
const toast = deps.notify ?? ((body2, key) => notifyOpsEvent("daemon-distress", body2, { key }));
|
|
60215
|
+
let prevCpu = cpuUsage();
|
|
60216
|
+
let prevTs = now();
|
|
60217
|
+
let prevDispatched = null;
|
|
60218
|
+
let prevLogged = 0;
|
|
60219
|
+
const coreSamples = [];
|
|
60220
|
+
let stormTicks = 0;
|
|
60221
|
+
let calmTicks = 0;
|
|
60222
|
+
let distress = null;
|
|
60223
|
+
let lastNotifiedAt = 0;
|
|
60224
|
+
const lastToastByKind = /* @__PURE__ */ new Map();
|
|
60225
|
+
const attribution = () => {
|
|
60226
|
+
const parts2 = [];
|
|
60227
|
+
const passes = activePasses();
|
|
60228
|
+
if (passes.length > 0) parts2.push(`active pass: ${passes.join(", ")}`);
|
|
60229
|
+
const c = readCensus();
|
|
60230
|
+
if (c.topOffender) parts2.push(`hottest watch path: ${c.topOffender.path}`);
|
|
60231
|
+
parts2.push(`rss ${rssMb()}MB`);
|
|
60232
|
+
return parts2.join(" \xB7 ");
|
|
60233
|
+
};
|
|
60234
|
+
const maybeToast = (d, t) => {
|
|
60235
|
+
if (t - (lastToastByKind.get(d.kind) ?? 0) < SENTINEL_TOAST_COOLDOWN_MS) return;
|
|
60236
|
+
lastToastByKind.set(d.kind, t);
|
|
60237
|
+
toast(d.headline, d.kind);
|
|
60238
|
+
lastNotifiedAt = t;
|
|
60239
|
+
};
|
|
60240
|
+
const transition = (next) => {
|
|
60241
|
+
const t = now();
|
|
60242
|
+
if (next && !distress) {
|
|
60243
|
+
distress = next;
|
|
60244
|
+
deps.ledger?.("enter", next);
|
|
60245
|
+
maybeToast(next, t);
|
|
60246
|
+
deps.onChange?.(next);
|
|
60247
|
+
} else if (next && distress) {
|
|
60248
|
+
distress.body = next.body;
|
|
60249
|
+
distress.headline = next.headline;
|
|
60250
|
+
if (t - lastNotifiedAt >= SENTINEL_RENOTIFY_MS) maybeToast(distress, t);
|
|
60251
|
+
} else if (!next && distress) {
|
|
60252
|
+
deps.ledger?.("clear", distress);
|
|
60253
|
+
distress = null;
|
|
60254
|
+
deps.onChange?.(null);
|
|
60255
|
+
}
|
|
60256
|
+
};
|
|
60257
|
+
const evaluate = () => {
|
|
60258
|
+
const t = now();
|
|
60259
|
+
const wallMs = Math.max(1, t - prevTs);
|
|
60260
|
+
const cpu = cpuUsage();
|
|
60261
|
+
const cores = (cpu.user + cpu.system - prevCpu.user - prevCpu.system) / 1e3 / wallMs;
|
|
60262
|
+
prevCpu = cpu;
|
|
60263
|
+
prevTs = t;
|
|
60264
|
+
coreSamples.push(cores);
|
|
60265
|
+
if (coreSamples.length > SENTINEL_WINDOW_TICKS) coreSamples.shift();
|
|
60266
|
+
const c = readCensus();
|
|
60267
|
+
const dispatchRate = prevDispatched === null ? 0 : (c.dispatched - prevDispatched) / wallMs * 1e3;
|
|
60268
|
+
const survived = c.logged - prevLogged;
|
|
60269
|
+
prevDispatched = c.dispatched;
|
|
60270
|
+
prevLogged = c.logged;
|
|
60271
|
+
stormTicks = dispatchRate > SENTINEL_STORM_RATE && survived === 0 ? stormTicks + 1 : 0;
|
|
60272
|
+
let wanted = null;
|
|
60273
|
+
if (stormTicks >= SENTINEL_STORM_TICKS) {
|
|
60274
|
+
wanted = {
|
|
60275
|
+
kind: "watch-storm",
|
|
60276
|
+
headline: `errata's file watcher is stuck busy (~${Math.round(dispatchRate)} events/sec with nothing changing) \u2014 run \`errata status\` for detail`,
|
|
60277
|
+
body: `filesystem watcher is processing ~${Math.round(dispatchRate)} events/sec with NOTHING surviving \u2014 a wedged or ignored-tree watch is burning CPU for no signal \xB7 ${attribution()}`,
|
|
60278
|
+
sinceTs: distress?.kind === "watch-storm" ? distress.sinceTs : t
|
|
60279
|
+
};
|
|
60280
|
+
} else {
|
|
60281
|
+
const windowFull = coreSamples.length >= SENTINEL_WINDOW_TICKS;
|
|
60282
|
+
const avgCores = coreSamples.reduce((a, b) => a + b, 0) / Math.max(1, coreSamples.length);
|
|
60283
|
+
if (windowFull && avgCores >= SENTINEL_BURN_CORES && cores >= SENTINEL_BURN_CORES && activePasses().length === 0) {
|
|
60284
|
+
wanted = {
|
|
60285
|
+
kind: "cpu-burn",
|
|
60286
|
+
headline: `errata is using more CPU than expected (${avgCores.toFixed(1)} cores with no work running) \u2014 run \`errata status\` for detail`,
|
|
60287
|
+
body: `sustained CPU with no pass running \u2014 avg ${avgCores.toFixed(1)} cores \xB7 ${attribution()}`,
|
|
60288
|
+
sinceTs: distress?.kind === "cpu-burn" ? distress.sinceTs : t
|
|
60289
|
+
};
|
|
60290
|
+
}
|
|
60291
|
+
}
|
|
60292
|
+
if (wanted) {
|
|
60293
|
+
calmTicks = 0;
|
|
60294
|
+
transition(wanted);
|
|
60295
|
+
} else if (distress) {
|
|
60296
|
+
calmTicks++;
|
|
60297
|
+
if (calmTicks >= SENTINEL_CALM_TICKS) {
|
|
60298
|
+
calmTicks = 0;
|
|
60299
|
+
transition(null);
|
|
60300
|
+
}
|
|
60301
|
+
}
|
|
60302
|
+
};
|
|
60303
|
+
const interval = deps.scheduleTicks === false ? null : setInterval(() => {
|
|
60304
|
+
try {
|
|
60305
|
+
evaluate();
|
|
60306
|
+
} catch {
|
|
60307
|
+
}
|
|
60308
|
+
}, SENTINEL_TICK_MS);
|
|
60309
|
+
interval?.unref?.();
|
|
60310
|
+
return {
|
|
60311
|
+
current: () => distress ? { ...distress } : null,
|
|
60312
|
+
tick: evaluate,
|
|
60313
|
+
stop: () => {
|
|
60314
|
+
if (interval) clearInterval(interval);
|
|
60315
|
+
}
|
|
60316
|
+
};
|
|
60317
|
+
}
|
|
60318
|
+
|
|
59958
60319
|
// src/multi.ts
|
|
59959
60320
|
var projectSymbolSalts = /* @__PURE__ */ new Map();
|
|
59960
60321
|
async function resolveProjectSymbolSalt(client, projectId) {
|
|
@@ -60099,6 +60460,20 @@ async function startMultiDaemon(opts = {}) {
|
|
|
60099
60460
|
for (const r of records) r.engine.markContextDirty();
|
|
60100
60461
|
}
|
|
60101
60462
|
}) : null;
|
|
60463
|
+
const healthSentinel = createHealthSentinel({
|
|
60464
|
+
onChange: () => {
|
|
60465
|
+
for (const r of records) r.engine.markContextDirty();
|
|
60466
|
+
},
|
|
60467
|
+
ledger: (event, d) => {
|
|
60468
|
+
const dir = records[0]?.engine.paths.configDir;
|
|
60469
|
+
if (dir) {
|
|
60470
|
+
appendPassLedger(dir, "health-sentinel", 0, {
|
|
60471
|
+
[`distress:${event}:${d.kind}`]: 1,
|
|
60472
|
+
distressSinceTs: d.sinceTs
|
|
60473
|
+
});
|
|
60474
|
+
}
|
|
60475
|
+
}
|
|
60476
|
+
});
|
|
60102
60477
|
let serverPending = null;
|
|
60103
60478
|
const boundaryListeners = [];
|
|
60104
60479
|
let boundaryFlushing = false;
|
|
@@ -60193,6 +60568,10 @@ async function startMultiDaemon(opts = {}) {
|
|
|
60193
60568
|
reviewUrl: () => `${baseUrl}/ws/${id}/review`,
|
|
60194
60569
|
sharedStore,
|
|
60195
60570
|
pendingUpdate: () => serverPending ?? updatePoller?.current() ?? null,
|
|
60571
|
+
// Ops-gated (default OFF): the 🚨 notice reads as "errata broke my
|
|
60572
|
+
// machine" to a non-operator. Detection + ledger run regardless — only
|
|
60573
|
+
// this push surface (and the toast, gated in notifyOpsEvent) is scoped.
|
|
60574
|
+
daemonDistress: () => loadConfig().opsNotices ? healthSentinel.current() : null,
|
|
60196
60575
|
...opts.skipWatchers ? { skipWatchers: true } : {}
|
|
60197
60576
|
});
|
|
60198
60577
|
const webApp = buildWebUi({
|
|
@@ -60281,7 +60660,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
60281
60660
|
registerWorkspace(rec.engine.profile, rec.root);
|
|
60282
60661
|
} catch {
|
|
60283
60662
|
}
|
|
60284
|
-
void rec.engine.reindex({ skipEmbed: true }).catch(() => {
|
|
60663
|
+
void rec.engine.reindex({ skipEmbed: true, quiet: rec.engine.store.nodeCount() > 0 }).catch(() => {
|
|
60285
60664
|
});
|
|
60286
60665
|
console.log(`[errata] hot-registered ${rec.entry.name} (${rec.id}) \u2014 now watching live`);
|
|
60287
60666
|
return { attached: true, id: rec.id };
|
|
@@ -61199,6 +61578,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
61199
61578
|
(res) => server.close(res)
|
|
61200
61579
|
);
|
|
61201
61580
|
updatePoller?.stop();
|
|
61581
|
+
healthSentinel.stop();
|
|
61202
61582
|
for (const r of records) await r.engine.stop();
|
|
61203
61583
|
if (consolidateWorker) await consolidateWorker.stop();
|
|
61204
61584
|
try {
|
|
@@ -61954,7 +62334,7 @@ var DEFAULT_CONSOLIDATION_POLICY = {
|
|
|
61954
62334
|
quiescenceMs: 6e4
|
|
61955
62335
|
// sim preferred 60s over 90s (fresher, esp. heavy-code)
|
|
61956
62336
|
};
|
|
61957
|
-
function
|
|
62337
|
+
function numEnv5(env2, key, fallback) {
|
|
61958
62338
|
const raw2 = env2[key];
|
|
61959
62339
|
if (raw2 == null || raw2.trim() === "") return fallback;
|
|
61960
62340
|
const n = Number(raw2);
|
|
@@ -61962,12 +62342,12 @@ function numEnv3(env2, key, fallback) {
|
|
|
61962
62342
|
}
|
|
61963
62343
|
function consolidationPolicyFromEnv(env2 = process.env) {
|
|
61964
62344
|
return {
|
|
61965
|
-
baseFloorMs:
|
|
61966
|
-
dutyFactor:
|
|
61967
|
-
momentumThreshold:
|
|
61968
|
-
momentumRatio:
|
|
61969
|
-
momentumRatioCap:
|
|
61970
|
-
quiescenceMs:
|
|
62345
|
+
baseFloorMs: numEnv5(env2, "ERRATA_CONSOLIDATE_BASE_FLOOR_MS", DEFAULT_CONSOLIDATION_POLICY.baseFloorMs),
|
|
62346
|
+
dutyFactor: numEnv5(env2, "ERRATA_CONSOLIDATE_DUTY_FACTOR", DEFAULT_CONSOLIDATION_POLICY.dutyFactor),
|
|
62347
|
+
momentumThreshold: numEnv5(env2, "ERRATA_CONSOLIDATE_MOMENTUM", DEFAULT_CONSOLIDATION_POLICY.momentumThreshold),
|
|
62348
|
+
momentumRatio: numEnv5(env2, "ERRATA_CONSOLIDATE_MOMENTUM_RATIO", DEFAULT_CONSOLIDATION_POLICY.momentumRatio),
|
|
62349
|
+
momentumRatioCap: numEnv5(env2, "ERRATA_CONSOLIDATE_MOMENTUM_RATIO_CAP", DEFAULT_CONSOLIDATION_POLICY.momentumRatioCap),
|
|
62350
|
+
quiescenceMs: numEnv5(env2, "ERRATA_CONSOLIDATE_QUIESCENCE_MS", DEFAULT_CONSOLIDATION_POLICY.quiescenceMs)
|
|
61971
62351
|
};
|
|
61972
62352
|
}
|
|
61973
62353
|
function consolidationGapMs(lastPassMs, policy) {
|
|
@@ -62105,6 +62485,8 @@ async function main() {
|
|
|
62105
62485
|
return cmdConsent(rest);
|
|
62106
62486
|
case "notifications":
|
|
62107
62487
|
return cmdNotifications(rest[0] ?? "");
|
|
62488
|
+
case "ops-notices":
|
|
62489
|
+
return cmdOpsNotices(rest[0] ?? "");
|
|
62108
62490
|
case "update":
|
|
62109
62491
|
return cmdUpdate(rest);
|
|
62110
62492
|
case "projects":
|
|
@@ -62228,6 +62610,9 @@ Commands:
|
|
|
62228
62610
|
Send scrubbed feedback to the maintainers (needs login)
|
|
62229
62611
|
notifications <on|off>
|
|
62230
62612
|
Desktop toasts on problem open/resolve + review ready
|
|
62613
|
+
ops-notices <on|off>
|
|
62614
|
+
Ops-grade health surfaces (daemon-distress toast + agent
|
|
62615
|
+
notice). Default off; for people operating errata itself
|
|
62231
62616
|
|
|
62232
62617
|
Environment:
|
|
62233
62618
|
ERRATA_CLOUD_URL Cloud base URL (default ${DEFAULT_CLOUD_URL})
|
|
@@ -64753,6 +65138,19 @@ async function cmdNotifications(state) {
|
|
|
64753
65138
|
saveConfig(cfg);
|
|
64754
65139
|
console.log(`desktop notifications ${cfg.notifications ? "enabled" : "disabled"}`);
|
|
64755
65140
|
}
|
|
65141
|
+
async function cmdOpsNotices(state) {
|
|
65142
|
+
const s = state.toLowerCase();
|
|
65143
|
+
if (s !== "on" && s !== "off") {
|
|
65144
|
+
console.error("usage: errata ops-notices <on|off>");
|
|
65145
|
+
process.exit(2);
|
|
65146
|
+
}
|
|
65147
|
+
const cfg = loadConfig();
|
|
65148
|
+
cfg.opsNotices = s === "on";
|
|
65149
|
+
saveConfig(cfg);
|
|
65150
|
+
console.log(
|
|
65151
|
+
`ops health notices ${cfg.opsNotices ? "enabled \u2014 distress conditions will toast and reach the agent context" : "disabled \u2014 detection and `errata status` still run"}`
|
|
65152
|
+
);
|
|
65153
|
+
}
|
|
64756
65154
|
async function cmdFeedback(args2) {
|
|
64757
65155
|
const cfg = loadConfig();
|
|
64758
65156
|
if (!hasCloudCredential(cfg)) {
|