instar 1.3.635 → 1.3.637
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/dist/core/SleepWakeDetector.d.ts +42 -1
- package/dist/core/SleepWakeDetector.d.ts.map +1 -1
- package/dist/core/SleepWakeDetector.js +50 -0
- package/dist/core/SleepWakeDetector.js.map +1 -1
- package/dist/core/storage/JsonlStore.d.ts +54 -0
- package/dist/core/storage/JsonlStore.d.ts.map +1 -0
- package/dist/core/storage/JsonlStore.js +81 -0
- package/dist/core/storage/JsonlStore.js.map +1 -0
- package/dist/utils/jsonl-rotation.d.ts +37 -0
- package/dist/utils/jsonl-rotation.d.ts.map +1 -1
- package/dist/utils/jsonl-rotation.js +86 -0
- package/dist/utils/jsonl-rotation.js.map +1 -1
- package/package.json +2 -2
- package/scripts/bounded-accumulation-retention-baseline.json +81 -0
- package/scripts/bounded-accumulation-wholefile-read-baseline.json +5 -0
- package/scripts/lint-no-wholefile-sync-read.js +128 -0
- package/scripts/lint-store-retention-declared.js +93 -0
- package/src/data/builtin-manifest.json +2 -2
- package/src/data/state-coherence-registry.json +71 -10
- package/upgrades/1.3.636.md +59 -0
- package/upgrades/1.3.637.md +41 -0
- package/upgrades/side-effects/bounded-accumulation-increment-1.md +103 -0
- package/upgrades/side-effects/sleepwake-stall-not-sleep.md +102 -0
|
@@ -104,12 +104,42 @@ export interface SleepWakeDetectorConfig {
|
|
|
104
104
|
cpuCountProvider?: () => number;
|
|
105
105
|
/** Injectable wall-clock source (testing). Default: Date.now. */
|
|
106
106
|
nowProvider?: () => number;
|
|
107
|
+
/**
|
|
108
|
+
* Fraction of a drift gap during which THIS process must have consumed CPU for the
|
|
109
|
+
* drift to be classified as an event-loop BLOCK (a wedge/stall) rather than sleep.
|
|
110
|
+
*
|
|
111
|
+
* This is the per-PROCESS signal the load heuristics can't provide: a suspended
|
|
112
|
+
* (sleeping) process burns ~0 CPU during the gap, while a blocked event loop burns
|
|
113
|
+
* CPU for most of it. One Node thread blocking for 14s doesn't move a 16-core
|
|
114
|
+
* `loadavg` above `maxLoadRatio`, so the load guards emit a FALSE "sleep" — but the
|
|
115
|
+
* process's own CPU usage exposes it definitively. Applies to LONG drifts too: a
|
|
116
|
+
* multi-minute CPU-busy drift is the event-loop wedge, never sleep. Default: 0.5.
|
|
117
|
+
* Set 0 to disable.
|
|
118
|
+
*/
|
|
119
|
+
cpuBlockBusyRatio?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Injectable cumulative-process-CPU source in MICROSECONDS (testing). Default sums
|
|
122
|
+
* `process.cpuUsage()` user + system. Used to compute CPU burned across a drift gap.
|
|
123
|
+
*/
|
|
124
|
+
cpuUsageProvider?: () => number;
|
|
107
125
|
}
|
|
108
126
|
export interface WakeEvent {
|
|
109
127
|
sleepDurationSeconds: number;
|
|
110
128
|
timestamp: string;
|
|
111
129
|
}
|
|
112
|
-
export type WakeSuppressionReason = 'cpu-starvation' | 'cooldown';
|
|
130
|
+
export type WakeSuppressionReason = 'cpu-starvation' | 'cooldown' | 'event-loop-block';
|
|
131
|
+
/**
|
|
132
|
+
* Emitted when a drift is PROVEN to be an event-loop block (this process burned CPU
|
|
133
|
+
* through the gap), not sleep. Signal-only so wedge watchers can see a real stall that
|
|
134
|
+
* the detector would otherwise have mislabeled as a wake. No consumer = harmless.
|
|
135
|
+
*/
|
|
136
|
+
export interface StallEvent {
|
|
137
|
+
/** Drift duration in seconds (how long the loop was blocked). */
|
|
138
|
+
stallSeconds: number;
|
|
139
|
+
/** Fraction of the gap this process spent on CPU (≈1 = fully CPU-bound block). */
|
|
140
|
+
cpuBusyRatio: number;
|
|
141
|
+
timestamp: string;
|
|
142
|
+
}
|
|
113
143
|
export interface SuppressedWakeEvent {
|
|
114
144
|
reason: WakeSuppressionReason;
|
|
115
145
|
driftSeconds: number;
|
|
@@ -152,11 +182,22 @@ export declare class SleepWakeDetector extends EventEmitter {
|
|
|
152
182
|
private loadAvgProvider;
|
|
153
183
|
private cpuCountProvider;
|
|
154
184
|
private now;
|
|
185
|
+
private cpuBlockBusyRatio;
|
|
186
|
+
private cpuUsageProvider;
|
|
187
|
+
/** Cumulative process CPU (µs) sampled at the previous tick — drives the per-process
|
|
188
|
+
* CPU-busy-through-the-gap discriminator that separates a real sleep from a block. */
|
|
189
|
+
private lastCpuMicros;
|
|
155
190
|
private wakeHistory;
|
|
156
191
|
private suppressionHistory;
|
|
157
192
|
constructor(config?: SleepWakeDetectorConfig);
|
|
158
193
|
start(): void;
|
|
159
194
|
stop(): void;
|
|
195
|
+
/** Read cumulative process CPU (µs) defensively. A provider error (or a non-finite
|
|
196
|
+
* reading) yields the prior value, so the gap delta becomes 0 → no block signal →
|
|
197
|
+
* the tick falls through to the load guards. That is the safe direction: a CPU-read
|
|
198
|
+
* failure can never crash the tick (it runs inside setInterval) nor force a false
|
|
199
|
+
* block classification — it just disables this one discriminator for that tick. */
|
|
200
|
+
private readCpuMicros;
|
|
160
201
|
/** Current `loadavg[0] / cpuCount` ratio. Returns 0 when load is unavailable
|
|
161
202
|
* (e.g. Windows reports [0,0,0]), which disables the starvation guard there. */
|
|
162
203
|
private currentLoadRatio;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SleepWakeDetector.d.ts","sourceRoot":"","sources":["../../src/core/SleepWakeDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;IACjC,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,MAAM,CAAC;IAChC,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"SleepWakeDetector.d.ts","sourceRoot":"","sources":["../../src/core/SleepWakeDetector.ts"],"names":[],"mappings":"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"}
|
|
@@ -80,6 +80,11 @@ export class SleepWakeDetector extends EventEmitter {
|
|
|
80
80
|
loadAvgProvider;
|
|
81
81
|
cpuCountProvider;
|
|
82
82
|
now;
|
|
83
|
+
cpuBlockBusyRatio;
|
|
84
|
+
cpuUsageProvider;
|
|
85
|
+
/** Cumulative process CPU (µs) sampled at the previous tick — drives the per-process
|
|
86
|
+
* CPU-busy-through-the-gap discriminator that separates a real sleep from a block. */
|
|
87
|
+
lastCpuMicros = 0;
|
|
83
88
|
wakeHistory = [];
|
|
84
89
|
suppressionHistory = [];
|
|
85
90
|
constructor(config = {}) {
|
|
@@ -95,16 +100,25 @@ export class SleepWakeDetector extends EventEmitter {
|
|
|
95
100
|
this.loadAvgProvider = config.loadAvgProvider ?? (() => os.loadavg());
|
|
96
101
|
this.cpuCountProvider = config.cpuCountProvider ?? (() => os.cpus().length);
|
|
97
102
|
this.now = config.nowProvider ?? (() => Date.now());
|
|
103
|
+
this.cpuBlockBusyRatio = config.cpuBlockBusyRatio ?? 0.5;
|
|
104
|
+
this.cpuUsageProvider =
|
|
105
|
+
config.cpuUsageProvider ?? (() => { const c = process.cpuUsage(); return c.user + c.system; });
|
|
98
106
|
this.lastTick = this.now();
|
|
107
|
+
this.lastCpuMicros = this.readCpuMicros();
|
|
99
108
|
}
|
|
100
109
|
start() {
|
|
101
110
|
if (this.interval)
|
|
102
111
|
return;
|
|
103
112
|
this.lastTick = this.now();
|
|
113
|
+
this.lastCpuMicros = this.readCpuMicros();
|
|
104
114
|
this.interval = setInterval(() => {
|
|
105
115
|
const now = this.now();
|
|
106
116
|
const elapsed = now - this.lastTick;
|
|
107
117
|
this.lastTick = now;
|
|
118
|
+
// CPU burned by THIS process across the gap — the per-process sleep-vs-block signal.
|
|
119
|
+
const cpuNowMicros = this.readCpuMicros();
|
|
120
|
+
const cpuBusyRatio = elapsed > 0 ? ((cpuNowMicros - this.lastCpuMicros) / 1000) / elapsed : 0;
|
|
121
|
+
this.lastCpuMicros = cpuNowMicros;
|
|
108
122
|
if (elapsed <= this.driftThresholdMs) {
|
|
109
123
|
this.consecutiveDrifts = 0;
|
|
110
124
|
return;
|
|
@@ -118,6 +132,27 @@ export class SleepWakeDetector extends EventEmitter {
|
|
|
118
132
|
const prevShortDriftAtMs = this.lastShortDriftAtMs;
|
|
119
133
|
if (!isLongSleep)
|
|
120
134
|
this.lastShortDriftAtMs = now;
|
|
135
|
+
// Per-process CPU check — the DEFINITIVE sleep-vs-block discriminator, ahead of the
|
|
136
|
+
// load heuristics. A suspended (sleeping) process burns ~0 CPU during the gap; a
|
|
137
|
+
// blocked event loop burns CPU through most of it. So a high busy ratio means the
|
|
138
|
+
// loop was BLOCKED and the machine did NOT sleep — regardless of duration (a
|
|
139
|
+
// multi-minute CPU-busy drift is the event-loop WEDGE, not sleep) and regardless of
|
|
140
|
+
// the system loadavg (one blocked Node thread doesn't move a 16-core average). Emit a
|
|
141
|
+
// `stall` signal for the wedge watchers; never a `wake`. (2026-06-21: fixes the
|
|
142
|
+
// misdiagnosis where 11-18s event-loop blocks on a caffeinated host — where sleep is
|
|
143
|
+
// physically impossible — were logged as "Wake detected after ~Ns sleep".)
|
|
144
|
+
if (this.cpuBlockBusyRatio > 0 && cpuBusyRatio >= this.cpuBlockBusyRatio) {
|
|
145
|
+
this.recordSuppression('event-loop-block', sleepDuration, loadRatio, now);
|
|
146
|
+
console.warn(`[SleepWakeDetector] Drift ~${sleepDuration}s but this process burned ` +
|
|
147
|
+
`${Math.round(cpuBusyRatio * 100)}% CPU through the gap — event-loop BLOCK, not ` +
|
|
148
|
+
`sleep (the machine did not sleep). Emitting stall, suppressing wake.`);
|
|
149
|
+
this.emit('stall', {
|
|
150
|
+
stallSeconds: sleepDuration,
|
|
151
|
+
cpuBusyRatio,
|
|
152
|
+
timestamp: new Date(now).toISOString(),
|
|
153
|
+
});
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
121
156
|
// Consecutive-drift burst = sustained CPU starvation, not sleep. A genuine sleep
|
|
122
157
|
// is ONE isolated drift (the next on-time tick resets the counter); the 2nd+
|
|
123
158
|
// back-to-back SHORT drift is a storm. Suppress it regardless of the (lagging,
|
|
@@ -185,6 +220,20 @@ export class SleepWakeDetector extends EventEmitter {
|
|
|
185
220
|
this.interval = null;
|
|
186
221
|
}
|
|
187
222
|
}
|
|
223
|
+
/** Read cumulative process CPU (µs) defensively. A provider error (or a non-finite
|
|
224
|
+
* reading) yields the prior value, so the gap delta becomes 0 → no block signal →
|
|
225
|
+
* the tick falls through to the load guards. That is the safe direction: a CPU-read
|
|
226
|
+
* failure can never crash the tick (it runs inside setInterval) nor force a false
|
|
227
|
+
* block classification — it just disables this one discriminator for that tick. */
|
|
228
|
+
readCpuMicros() {
|
|
229
|
+
try {
|
|
230
|
+
const v = this.cpuUsageProvider();
|
|
231
|
+
return Number.isFinite(v) ? v : this.lastCpuMicros;
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return this.lastCpuMicros;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
188
237
|
/** Current `loadavg[0] / cpuCount` ratio. Returns 0 when load is unavailable
|
|
189
238
|
* (e.g. Windows reports [0,0,0]), which disables the starvation guard there. */
|
|
190
239
|
currentLoadRatio() {
|
|
@@ -253,6 +302,7 @@ export class SleepWakeDetector extends EventEmitter {
|
|
|
253
302
|
const suppressedByReason = {
|
|
254
303
|
'cpu-starvation': 0,
|
|
255
304
|
cooldown: 0,
|
|
305
|
+
'event-loop-block': 0,
|
|
256
306
|
};
|
|
257
307
|
for (const e of suppressed)
|
|
258
308
|
suppressedByReason[e.reason]++;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SleepWakeDetector.js","sourceRoot":"","sources":["../../src/core/SleepWakeDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"SleepWakeDetector.js","sourceRoot":"","sources":["../../src/core/SleepWakeDetector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JsonlStore — the registered accessor for append-only JSONL persistence
|
|
3
|
+
* (Bounded Accumulation standard §3a/§3b).
|
|
4
|
+
*
|
|
5
|
+
* Every persistent JSONL store routes through this class so that:
|
|
6
|
+
* 1. The retention policy (maxBytes / keepSegments / archive) is declared and
|
|
7
|
+
* ENFORCED at the one place data is written — not hoped for at each callsite.
|
|
8
|
+
* 2. The lint surface is a single funnel: raw `fs.appendFileSync` to a `.instar/`
|
|
9
|
+
* path in `src/` is itself a Lint-1 failure; an author must use this accessor,
|
|
10
|
+
* which makes the retention declaration unskippable.
|
|
11
|
+
* 3. Rotation is the event-loop-SAFE segment cut (rename, not read-filter-rewrite),
|
|
12
|
+
* gated behind a cached byte-counter so the size check is amortized — even the
|
|
13
|
+
* O(1) `statSync` is not paid on every append.
|
|
14
|
+
*
|
|
15
|
+
* This is the storage twin of the funnels SafeFsExecutor / SafeGitExecutor already
|
|
16
|
+
* establish for destructive fs/git ops: one chokepoint that a lint can enforce.
|
|
17
|
+
*/
|
|
18
|
+
export interface JsonlStoreOptions {
|
|
19
|
+
/** Maximum active-file size in bytes before a segment is cut. Default: 32MB. */
|
|
20
|
+
maxBytes?: number;
|
|
21
|
+
/** Rotated segments to retain (oldest beyond this are unlinked, unless archive). Default: 4. */
|
|
22
|
+
keepSegments?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Compliance-hold: rotated segments are NEVER unlinked. For audit/forensic trails
|
|
25
|
+
* that must never lose their oldest entries. Default: false.
|
|
26
|
+
*/
|
|
27
|
+
archive?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Amortize the rotation size-check: only check (and possibly rotate) after this many
|
|
30
|
+
* bytes have been appended since the last check. Keeps the hot append path a pure
|
|
31
|
+
* `appendFileSync` + counter bump. Default: 64KB.
|
|
32
|
+
*/
|
|
33
|
+
checkEveryBytes?: number;
|
|
34
|
+
}
|
|
35
|
+
export declare class JsonlStore {
|
|
36
|
+
private readonly filePath;
|
|
37
|
+
private readonly maxBytes;
|
|
38
|
+
private readonly keepSegments;
|
|
39
|
+
private readonly archive;
|
|
40
|
+
private readonly checkEveryBytes;
|
|
41
|
+
/** Bytes appended since the last rotation size-check (the cached counter). */
|
|
42
|
+
private bytesSinceCheck;
|
|
43
|
+
constructor(filePath: string, options?: JsonlStoreOptions);
|
|
44
|
+
/** Append one raw line (a trailing newline is added if absent). */
|
|
45
|
+
append(line: string): void;
|
|
46
|
+
/** Append one JSON-serialized object as a line. */
|
|
47
|
+
appendObject(obj: unknown): void;
|
|
48
|
+
/** Force a rotation size-check now (e.g. on a periodic sweep), bypassing the counter. */
|
|
49
|
+
checkRotationNow(): boolean;
|
|
50
|
+
/** The active file path this store writes to. */
|
|
51
|
+
get path(): string;
|
|
52
|
+
private ensureDir;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=JsonlStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JsonlStore.d.ts","sourceRoot":"","sources":["../../../src/core/storage/JsonlStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,8EAA8E;IAC9E,OAAO,CAAC,eAAe,CAAK;gBAEhB,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB;IAQ7D,mEAAmE;IACnE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAgB1B,mDAAmD;IACnD,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI;IAIhC,yFAAyF;IACzF,gBAAgB,IAAI,OAAO;IAS3B,iDAAiD;IACjD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,SAAS;CAOlB"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JsonlStore — the registered accessor for append-only JSONL persistence
|
|
3
|
+
* (Bounded Accumulation standard §3a/§3b).
|
|
4
|
+
*
|
|
5
|
+
* Every persistent JSONL store routes through this class so that:
|
|
6
|
+
* 1. The retention policy (maxBytes / keepSegments / archive) is declared and
|
|
7
|
+
* ENFORCED at the one place data is written — not hoped for at each callsite.
|
|
8
|
+
* 2. The lint surface is a single funnel: raw `fs.appendFileSync` to a `.instar/`
|
|
9
|
+
* path in `src/` is itself a Lint-1 failure; an author must use this accessor,
|
|
10
|
+
* which makes the retention declaration unskippable.
|
|
11
|
+
* 3. Rotation is the event-loop-SAFE segment cut (rename, not read-filter-rewrite),
|
|
12
|
+
* gated behind a cached byte-counter so the size check is amortized — even the
|
|
13
|
+
* O(1) `statSync` is not paid on every append.
|
|
14
|
+
*
|
|
15
|
+
* This is the storage twin of the funnels SafeFsExecutor / SafeGitExecutor already
|
|
16
|
+
* establish for destructive fs/git ops: one chokepoint that a lint can enforce.
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { maybeRotateJsonlSegment } from '../../utils/jsonl-rotation.js';
|
|
21
|
+
const DEFAULT_MAX_BYTES = 32 * 1024 * 1024; // 32MB
|
|
22
|
+
const DEFAULT_KEEP_SEGMENTS = 4;
|
|
23
|
+
const DEFAULT_CHECK_EVERY_BYTES = 64 * 1024; // 64KB
|
|
24
|
+
export class JsonlStore {
|
|
25
|
+
filePath;
|
|
26
|
+
maxBytes;
|
|
27
|
+
keepSegments;
|
|
28
|
+
archive;
|
|
29
|
+
checkEveryBytes;
|
|
30
|
+
/** Bytes appended since the last rotation size-check (the cached counter). */
|
|
31
|
+
bytesSinceCheck = 0;
|
|
32
|
+
constructor(filePath, options = {}) {
|
|
33
|
+
this.filePath = filePath;
|
|
34
|
+
this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
35
|
+
this.keepSegments = Math.max(1, options.keepSegments ?? DEFAULT_KEEP_SEGMENTS);
|
|
36
|
+
this.archive = options.archive ?? false;
|
|
37
|
+
this.checkEveryBytes = Math.max(1, options.checkEveryBytes ?? DEFAULT_CHECK_EVERY_BYTES);
|
|
38
|
+
}
|
|
39
|
+
/** Append one raw line (a trailing newline is added if absent). */
|
|
40
|
+
append(line) {
|
|
41
|
+
const data = line.endsWith('\n') ? line : line + '\n';
|
|
42
|
+
this.ensureDir();
|
|
43
|
+
fs.appendFileSync(this.filePath, data);
|
|
44
|
+
this.bytesSinceCheck += Buffer.byteLength(data);
|
|
45
|
+
if (this.bytesSinceCheck >= this.checkEveryBytes) {
|
|
46
|
+
this.bytesSinceCheck = 0;
|
|
47
|
+
// O(1) segment cut when over maxBytes; never throws.
|
|
48
|
+
maybeRotateJsonlSegment(this.filePath, {
|
|
49
|
+
maxBytes: this.maxBytes,
|
|
50
|
+
keepSegments: this.keepSegments,
|
|
51
|
+
archive: this.archive,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Append one JSON-serialized object as a line. */
|
|
56
|
+
appendObject(obj) {
|
|
57
|
+
this.append(JSON.stringify(obj));
|
|
58
|
+
}
|
|
59
|
+
/** Force a rotation size-check now (e.g. on a periodic sweep), bypassing the counter. */
|
|
60
|
+
checkRotationNow() {
|
|
61
|
+
this.bytesSinceCheck = 0;
|
|
62
|
+
return maybeRotateJsonlSegment(this.filePath, {
|
|
63
|
+
maxBytes: this.maxBytes,
|
|
64
|
+
keepSegments: this.keepSegments,
|
|
65
|
+
archive: this.archive,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** The active file path this store writes to. */
|
|
69
|
+
get path() {
|
|
70
|
+
return this.filePath;
|
|
71
|
+
}
|
|
72
|
+
ensureDir() {
|
|
73
|
+
try {
|
|
74
|
+
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// directory likely exists; append will surface a real error if not
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=JsonlStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JsonlStore.js","sourceRoot":"","sources":["../../../src/core/storage/JsonlStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAoBxE,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AACnD,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAChC,MAAM,yBAAyB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO;AAEpD,MAAM,OAAO,UAAU;IACJ,QAAQ,CAAS;IACjB,QAAQ,CAAS;IACjB,YAAY,CAAS;IACrB,OAAO,CAAU;IACjB,eAAe,CAAS;IACzC,8EAA8E;IACtE,eAAe,GAAG,CAAC,CAAC;IAE5B,YAAY,QAAgB,EAAE,UAA6B,EAAE;QAC3D,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC;QACtD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,YAAY,IAAI,qBAAqB,CAAC,CAAC;QAC/E,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC;QACxC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC,CAAC;IAC3F,CAAC;IAED,mEAAmE;IACnE,MAAM,CAAC,IAAY;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC;QACtD,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,eAAe,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACjD,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;YACzB,qDAAqD;YACrD,uBAAuB,CAAC,IAAI,CAAC,QAAQ,EAAE;gBACrC,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,YAAY,CAAC,GAAY;QACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,yFAAyF;IACzF,gBAAgB;QACd,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,OAAO,uBAAuB,CAAC,IAAI,CAAC,QAAQ,EAAE;YAC5C,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAC;IACL,CAAC;IAED,iDAAiD;IACjD,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEO,SAAS;QACf,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;QACrE,CAAC;IACH,CAAC;CACF"}
|
|
@@ -21,7 +21,44 @@ export interface RotationOptions {
|
|
|
21
21
|
* Check if a JSONL file exceeds its size limit, and if so, rotate it
|
|
22
22
|
* by keeping only the most recent lines.
|
|
23
23
|
*
|
|
24
|
+
* NON-CONFORMANT to the Bounded Accumulation standard (§3.5): this rotates by
|
|
25
|
+
* `readFileSync` (whole file) + `split` + `writeFileSync`, which blocks the event
|
|
26
|
+
* loop for hundreds of ms on a multi-MB file — the exact stall the standard exists
|
|
27
|
+
* to kill. Prefer {@link maybeRotateJsonlSegment} (constant-time rename) for any
|
|
28
|
+
* store registered `access: 'streamed'`. Retained for back-compat callers pending
|
|
29
|
+
* their migration (Bounded Accumulation Increment 2).
|
|
30
|
+
*
|
|
24
31
|
* @returns true if rotation occurred, false otherwise
|
|
25
32
|
*/
|
|
26
33
|
export declare function maybeRotateJsonl(filePath: string, options?: RotationOptions): boolean;
|
|
34
|
+
export interface SegmentRotationOptions {
|
|
35
|
+
/** Maximum active-file size in bytes before a segment is cut. Default: 10MB */
|
|
36
|
+
maxBytes?: number;
|
|
37
|
+
/** Number of rotated segments to retain (oldest beyond this are unlinked). Default: 4 */
|
|
38
|
+
keepSegments?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Compliance-hold mode: rotated segments are NEVER unlinked. An audit/forensic
|
|
41
|
+
* trail (security.jsonl, destructive-ops.jsonl) must not lose its oldest entries —
|
|
42
|
+
* it is bounded by archive policy, never by drop. Default: false.
|
|
43
|
+
*/
|
|
44
|
+
archive?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Segment-based JSONL rotation — the event-loop-SAFE alternative to
|
|
48
|
+
* {@link maybeRotateJsonl}'s read-filter-rewrite (Bounded Accumulation standard §3.5).
|
|
49
|
+
*
|
|
50
|
+
* When the active file exceeds `maxBytes`, the active file is RENAMED to a numbered
|
|
51
|
+
* segment `<name>.<seq>` (a constant-time metadata op — NO file read, NO whole-file
|
|
52
|
+
* rewrite) and a fresh empty active file is opened. Oldest segments beyond
|
|
53
|
+
* `keepSegments` are unlinked — UNLESS `archive: true`, which retains every segment
|
|
54
|
+
* (compliance/audit trails that must never drop their oldest entries).
|
|
55
|
+
*
|
|
56
|
+
* Why it exists: rotating a 14MB hot log via the old read+split+rewrite path froze the
|
|
57
|
+
* event loop for hundreds of ms on the append path; renaming a segment is O(1). Callers
|
|
58
|
+
* SHOULD gate the size-check behind a cached byte-counter (see `JsonlStore`) so even the
|
|
59
|
+
* O(1) `statSync` is not paid on every append.
|
|
60
|
+
*
|
|
61
|
+
* @returns true if a segment was cut, false otherwise. Never throws.
|
|
62
|
+
*/
|
|
63
|
+
export declare function maybeRotateJsonlSegment(filePath: string, options?: SegmentRotationOptions): boolean;
|
|
27
64
|
//# sourceMappingURL=jsonl-rotation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonl-rotation.d.ts","sourceRoot":"","sources":["../../src/utils/jsonl-rotation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"jsonl-rotation.d.ts","sourceRoot":"","sources":["../../src/utils/jsonl-rotation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAyCrF;AAID,MAAM,WAAW,sBAAsB;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAqDnG"}
|
|
@@ -12,15 +12,27 @@
|
|
|
12
12
|
* - Lazy rotation — called before/after append, no background timers
|
|
13
13
|
*/
|
|
14
14
|
import fs from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
15
16
|
import { SafeFsExecutor } from '../core/SafeFsExecutor.js';
|
|
16
17
|
// ── Constants ────────────────────────────────────────────────────────
|
|
17
18
|
const DEFAULT_MAX_BYTES = 10 * 1024 * 1024; // 10MB
|
|
18
19
|
const DEFAULT_KEEP_RATIO = 0.75;
|
|
20
|
+
const DEFAULT_KEEP_SEGMENTS = 4;
|
|
21
|
+
function escapeRegExp(s) {
|
|
22
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
23
|
+
}
|
|
19
24
|
// ── Public API ───────────────────────────────────────────────────────
|
|
20
25
|
/**
|
|
21
26
|
* Check if a JSONL file exceeds its size limit, and if so, rotate it
|
|
22
27
|
* by keeping only the most recent lines.
|
|
23
28
|
*
|
|
29
|
+
* NON-CONFORMANT to the Bounded Accumulation standard (§3.5): this rotates by
|
|
30
|
+
* `readFileSync` (whole file) + `split` + `writeFileSync`, which blocks the event
|
|
31
|
+
* loop for hundreds of ms on a multi-MB file — the exact stall the standard exists
|
|
32
|
+
* to kill. Prefer {@link maybeRotateJsonlSegment} (constant-time rename) for any
|
|
33
|
+
* store registered `access: 'streamed'`. Retained for back-compat callers pending
|
|
34
|
+
* their migration (Bounded Accumulation Increment 2).
|
|
35
|
+
*
|
|
24
36
|
* @returns true if rotation occurred, false otherwise
|
|
25
37
|
*/
|
|
26
38
|
export function maybeRotateJsonl(filePath, options) {
|
|
@@ -61,4 +73,78 @@ export function maybeRotateJsonl(filePath, options) {
|
|
|
61
73
|
return false;
|
|
62
74
|
}
|
|
63
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Segment-based JSONL rotation — the event-loop-SAFE alternative to
|
|
78
|
+
* {@link maybeRotateJsonl}'s read-filter-rewrite (Bounded Accumulation standard §3.5).
|
|
79
|
+
*
|
|
80
|
+
* When the active file exceeds `maxBytes`, the active file is RENAMED to a numbered
|
|
81
|
+
* segment `<name>.<seq>` (a constant-time metadata op — NO file read, NO whole-file
|
|
82
|
+
* rewrite) and a fresh empty active file is opened. Oldest segments beyond
|
|
83
|
+
* `keepSegments` are unlinked — UNLESS `archive: true`, which retains every segment
|
|
84
|
+
* (compliance/audit trails that must never drop their oldest entries).
|
|
85
|
+
*
|
|
86
|
+
* Why it exists: rotating a 14MB hot log via the old read+split+rewrite path froze the
|
|
87
|
+
* event loop for hundreds of ms on the append path; renaming a segment is O(1). Callers
|
|
88
|
+
* SHOULD gate the size-check behind a cached byte-counter (see `JsonlStore`) so even the
|
|
89
|
+
* O(1) `statSync` is not paid on every append.
|
|
90
|
+
*
|
|
91
|
+
* @returns true if a segment was cut, false otherwise. Never throws.
|
|
92
|
+
*/
|
|
93
|
+
export function maybeRotateJsonlSegment(filePath, options) {
|
|
94
|
+
const maxBytes = options?.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
95
|
+
const keepSegments = Math.max(1, options?.keepSegments ?? DEFAULT_KEEP_SEGMENTS);
|
|
96
|
+
const archive = options?.archive ?? false;
|
|
97
|
+
try {
|
|
98
|
+
let size;
|
|
99
|
+
try {
|
|
100
|
+
size = fs.statSync(filePath).size; // O(1) — no file read
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return false; // no active file yet → nothing to rotate
|
|
104
|
+
}
|
|
105
|
+
if (size <= maxBytes)
|
|
106
|
+
return false;
|
|
107
|
+
const dir = path.dirname(filePath);
|
|
108
|
+
const base = path.basename(filePath);
|
|
109
|
+
// Rotated segments are "<base>.<seq>" (optionally ".gz"); seq is monotonic.
|
|
110
|
+
const segRe = new RegExp('^' + escapeRegExp(base) + '\\.(\\d+)(?:\\.gz)?$');
|
|
111
|
+
const segments = [];
|
|
112
|
+
let maxSeq = 0;
|
|
113
|
+
for (const f of fs.readdirSync(dir)) {
|
|
114
|
+
const m = f.match(segRe);
|
|
115
|
+
if (!m)
|
|
116
|
+
continue;
|
|
117
|
+
const seq = parseInt(m[1], 10);
|
|
118
|
+
if (!Number.isFinite(seq))
|
|
119
|
+
continue;
|
|
120
|
+
segments.push({ seq, name: f });
|
|
121
|
+
if (seq > maxSeq)
|
|
122
|
+
maxSeq = seq;
|
|
123
|
+
}
|
|
124
|
+
const nextSeq = maxSeq + 1;
|
|
125
|
+
// Cut the segment: rename active → segment (constant-time), open a fresh active.
|
|
126
|
+
fs.renameSync(filePath, path.join(dir, base + '.' + nextSeq));
|
|
127
|
+
fs.writeFileSync(filePath, '');
|
|
128
|
+
// Prune oldest segments beyond keepSegments — NEVER in archive (compliance) mode.
|
|
129
|
+
if (!archive) {
|
|
130
|
+
const cutoff = nextSeq - keepSegments; // segments with seq <= cutoff are dropped
|
|
131
|
+
for (const s of segments) {
|
|
132
|
+
if (s.seq <= cutoff) {
|
|
133
|
+
try {
|
|
134
|
+
SafeFsExecutor.safeUnlinkSync(path.join(dir, s.name), {
|
|
135
|
+
operation: 'src/utils/jsonl-rotation.ts:maybeRotateJsonlSegment',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// best effort — a failed unlink just leaves an extra old segment
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
64
150
|
//# sourceMappingURL=jsonl-rotation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonl-rotation.js","sourceRoot":"","sources":["../../src/utils/jsonl-rotation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"jsonl-rotation.js","sourceRoot":"","sources":["../../src/utils/jsonl-rotation.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "instar",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.637",
|
|
4
4
|
"description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"test:contract": "node scripts/run-contract-tests.js",
|
|
29
29
|
"test:contract:raw": "vitest run --config vitest.contract.config.ts",
|
|
30
30
|
"test:all": "vitest run && vitest run --config vitest.integration.config.ts && vitest run --config vitest.e2e.config.ts",
|
|
31
|
-
"lint": "tsc --noEmit && node scripts/lint-no-direct-destructive.js && node scripts/lint-no-direct-llm-http.js && node scripts/lint-no-direct-url-log.js && node scripts/lint-no-unfunneled-topic-creation.js && node scripts/lint-no-unfunneled-headless-launch.js && node scripts/lint-no-unfunneled-credential-write.js && node scripts/lint-state-registry.js && node scripts/lint-cas-emit-placement.js && node scripts/lint-journal-actuation-ban.js && node scripts/lint-no-blocking-process-scans.js && node scripts/lint-dev-agent-dark-gate.js && node scripts/lint-guard-manifest.js && node scripts/lint-llm-attribution.js && node scripts/lint-no-mainthread-cartographer-walk.js && node scripts/lint-scrape-fixture-realness.js && node scripts/check-codex-rule1-drift.js",
|
|
31
|
+
"lint": "tsc --noEmit && node scripts/lint-no-direct-destructive.js && node scripts/lint-no-direct-llm-http.js && node scripts/lint-no-direct-url-log.js && node scripts/lint-no-unfunneled-topic-creation.js && node scripts/lint-no-unfunneled-headless-launch.js && node scripts/lint-no-unfunneled-credential-write.js && node scripts/lint-state-registry.js && node scripts/lint-store-retention-declared.js && node scripts/lint-no-wholefile-sync-read.js && node scripts/lint-cas-emit-placement.js && node scripts/lint-journal-actuation-ban.js && node scripts/lint-no-blocking-process-scans.js && node scripts/lint-dev-agent-dark-gate.js && node scripts/lint-guard-manifest.js && node scripts/lint-llm-attribution.js && node scripts/lint-no-mainthread-cartographer-walk.js && node scripts/lint-scrape-fixture-realness.js && node scripts/check-codex-rule1-drift.js",
|
|
32
32
|
"lint:destructive": "node scripts/lint-no-direct-destructive.js",
|
|
33
33
|
"lint:dev-agent-dark-gate": "node scripts/lint-dev-agent-dark-gate.js",
|
|
34
34
|
"lint:guard-manifest": "node scripts/lint-guard-manifest.js",
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"note": "FROZEN baseline of registry categories without a retention policy at Bounded Accumulation Increment 1. The lint forbids NEW categories without retention; this set may only SHRINK (D6 set-monotonicity). Giving a baselined category retention counts the backlog down.",
|
|
3
|
+
"frozenAt": "2026-06-21",
|
|
4
|
+
"categories": [
|
|
5
|
+
"attention-queue",
|
|
6
|
+
"audit-a2a-messages",
|
|
7
|
+
"audit-activity",
|
|
8
|
+
"audit-apprenticeship-decisions",
|
|
9
|
+
"audit-decision-journal",
|
|
10
|
+
"audit-operation-log",
|
|
11
|
+
"audit-prompt-gate",
|
|
12
|
+
"audit-reap-log",
|
|
13
|
+
"audit-reaper",
|
|
14
|
+
"audit-recovery-events",
|
|
15
|
+
"audit-sentinel-events",
|
|
16
|
+
"audit-skill-telemetry",
|
|
17
|
+
"audit-trust-chain",
|
|
18
|
+
"audit-watchdog-interventions",
|
|
19
|
+
"autonomous-working-artifacts",
|
|
20
|
+
"commitment-opkeys",
|
|
21
|
+
"commitment-pending-mutations",
|
|
22
|
+
"commitment-replicas",
|
|
23
|
+
"commitments",
|
|
24
|
+
"conversation-live-tail",
|
|
25
|
+
"derived-discovery",
|
|
26
|
+
"derived-docs-code-sync",
|
|
27
|
+
"derived-framework-model-preferences",
|
|
28
|
+
"derived-memory-index",
|
|
29
|
+
"derived-project-map",
|
|
30
|
+
"derived-projects-digest",
|
|
31
|
+
"derived-topic-memory",
|
|
32
|
+
"ephemeral-caches",
|
|
33
|
+
"fleet-config",
|
|
34
|
+
"guard-posture-episodes",
|
|
35
|
+
"guard-posture-peers",
|
|
36
|
+
"guard-posture-snapshot",
|
|
37
|
+
"job-definitions",
|
|
38
|
+
"job-run-state",
|
|
39
|
+
"learned-preferences",
|
|
40
|
+
"lease-coordination-state",
|
|
41
|
+
"legacy-singletons",
|
|
42
|
+
"listener-daemon-internals",
|
|
43
|
+
"machine-identity-keys",
|
|
44
|
+
"message-store-and-threads",
|
|
45
|
+
"messaging-adapter-state",
|
|
46
|
+
"nonce-sequence-watermarks",
|
|
47
|
+
"pending-pulls",
|
|
48
|
+
"per-session-state",
|
|
49
|
+
"platform-attachment-caches",
|
|
50
|
+
"process-restart-coordination",
|
|
51
|
+
"projects-registry",
|
|
52
|
+
"pull-opkeys",
|
|
53
|
+
"quota-state",
|
|
54
|
+
"relationships",
|
|
55
|
+
"relay-delivery-queues",
|
|
56
|
+
"remote-ack-queue",
|
|
57
|
+
"remote-close-audit",
|
|
58
|
+
"resume-queue",
|
|
59
|
+
"secret-vault",
|
|
60
|
+
"session-monitor-ctx-notified",
|
|
61
|
+
"slack-pending-registrations",
|
|
62
|
+
"slack-permission-decisions",
|
|
63
|
+
"slack-relationship-baselines",
|
|
64
|
+
"soul-identity-documents",
|
|
65
|
+
"stream-tickets",
|
|
66
|
+
"threadline-conversation-state",
|
|
67
|
+
"threadline-pairing-result",
|
|
68
|
+
"threadline-transport-internals",
|
|
69
|
+
"threadline-trust-profiles",
|
|
70
|
+
"topic-ownership-current",
|
|
71
|
+
"topic-project-bindings",
|
|
72
|
+
"trust-elevation-incidents",
|
|
73
|
+
"uncertain-correction-capture-backlog",
|
|
74
|
+
"uncertain-instructions-tracking",
|
|
75
|
+
"uncertain-project-round-worktrees",
|
|
76
|
+
"uncertain-shared-state-ledger",
|
|
77
|
+
"uncertain-topic-intent-store",
|
|
78
|
+
"users-registry",
|
|
79
|
+
"visibility-guard"
|
|
80
|
+
]
|
|
81
|
+
}
|