@ngockhoale/ukit 3.0.12 → 3.1.1

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +1 -0
  3. package/manifests/documentation.yaml +12 -0
  4. package/manifests/platform.full.yaml +24 -0
  5. package/package.json +1 -1
  6. package/scripts/bench/data-foundation.mjs +368 -50
  7. package/src/cli/commands/doctor.js +232 -3
  8. package/src/cli/commands/feedback.js +64 -1
  9. package/src/cli/commands/install.js +18 -0
  10. package/src/cli/commands/memory.js +42 -37
  11. package/src/cli/commands/telemetry.js +460 -0
  12. package/src/cli/index.js +7 -0
  13. package/src/core/agentRuntime/adapters.js +83 -2
  14. package/src/core/agentRuntime/diagnostics.js +104 -0
  15. package/src/core/agentRuntime/supervisor.js +137 -0
  16. package/src/core/agentRuntime/telemetry.js +204 -0
  17. package/src/core/memory/memoryEmit.js +131 -0
  18. package/src/core/memory/memoryHit.js +1 -1
  19. package/src/core/memory/migrate.js +18 -11
  20. package/src/core/memory/migrateMapping.js +15 -7
  21. package/src/core/memory/mutateMemory.js +22 -4
  22. package/src/core/memory/recordIndex.js +10 -3
  23. package/src/core/memory/recordStore.js +28 -3
  24. package/src/core/memory/retrieval.js +79 -38
  25. package/src/core/memory/store.js +37 -37
  26. package/src/core/memory/storeV2.js +28 -25
  27. package/src/core/memory/storeV2Loader.js +2 -2
  28. package/src/core/observability/adapters/ingest.js +576 -0
  29. package/src/core/observability/analytics/anomalies.js +415 -0
  30. package/src/core/observability/analytics/summary.js +16 -1
  31. package/src/core/observability/emit/config.js +69 -1
  32. package/src/core/observability/emit/crash.js +434 -0
  33. package/src/core/observability/emit/lifecycle.js +349 -0
  34. package/src/core/observability/emit/recorder.js +135 -9
  35. package/src/core/observability/evaluation/aiPacket.js +52 -10
  36. package/src/core/observability/evaluation/outcomes.js +95 -0
  37. package/src/core/observability/evaluation/runner.js +225 -0
  38. package/src/core/observability/privacy/allowlist.js +23 -3
  39. package/src/core/observability/schema/compatibility.js +48 -3
  40. package/src/core/observability/schema/constants.js +5 -0
  41. package/src/core/observability/schema/registry.js +57 -0
  42. package/src/core/observability/schema/validate.js +68 -6
  43. package/src/core/observability/segments/internal.js +42 -8
  44. package/src/core/observability/segments/readSegments.js +35 -1
  45. package/src/core/observability/segments/recovery.js +3 -2
  46. package/src/core/observability/segments/retention.js +137 -33
  47. package/src/core/observability/support/projector.js +88 -18
  48. package/src/core/observability/support/provision.js +160 -0
  49. package/src/core/observability/support/renderer.js +2 -2
  50. package/src/core/observability/support/schedule.js +174 -0
  51. package/src/decision/registry.js +144 -0
  52. package/src/decision/reviewVerdict.js +309 -0
  53. package/template_project/.claude/agents/code-reviewer.md +25 -1
  54. package/template_project/.claude/agents/ukit-small-task-maintainer.md +16 -0
  55. package/template_project/.claude/commands/ukit/handoff-fullstack.md +2 -0
  56. package/template_project/.claude/commands/ukit/handoff-review.md +12 -0
  57. package/template_project/.claude/hooks/auto-allow-bash.sh +7 -1
  58. package/template_project/.claude/hooks/auto-prune-bash.sh +16 -7
  59. package/template_project/.claude/hooks/verification-guard.sh +13 -4
  60. package/template_project/.claude/ukit/index/review-verdict.mjs +592 -0
  61. package/template_project/.claude/ukit/index/sidecar-decision.mjs +595 -0
  62. package/template_project/.claude/ukit/index/unic-decision.mjs +10 -1
  63. package/template_project/.claude/ukit/runtime/async-lock.mjs +26 -0
  64. package/template_project/.codex/settings.json +3 -0
  65. package/template_project/.omp/agents/code-reviewer.md +25 -1
  66. package/template_project/.omp/agents/ukit-small-task-maintainer.md +16 -0
@@ -3,9 +3,10 @@
3
3
  // validateSemanticRecord(record, context?) → { ok: boolean, errors: string[] }
4
4
  //
5
5
  // Validates one semantic envelope against SCHEMA_VERSION 1. Never throws:
6
- // malformed input returns { ok: false, errors }. Unknown optional fields are
7
- // tolerated (DF-FR03); unknown REQUIRED-field values (schema_version,
8
- // record_type, semantic_name, vocabularies) are rejected.
6
+ // malformed input returns { ok: false, errors }. Unknown ENVELOPE fields
7
+ // reject (the envelope is the frozen contract surface — DF2-FR01); unknown
8
+ // payload fields are tolerated. Unknown REQUIRED-field values
9
+ // (schema_version, record_type, semantic_name, vocabularies) are rejected.
9
10
  //
10
11
  // `context` is optional cross-record state for stream validation:
11
12
  // { knownSpanIds?: Set<string>, seenSequences?: Set<string> }
@@ -17,6 +18,7 @@
17
18
 
18
19
  import {
19
20
  SCHEMA_VERSION,
21
+ ENVELOPE_FIELDS,
20
22
  REQUIRED_ENVELOPE_FIELDS,
21
23
  RECORD_TYPES,
22
24
  PRIVACY_CLASSES,
@@ -26,8 +28,23 @@ import {
26
28
  } from './constants.js';
27
29
  import { SEMANTIC_REGISTRY, REASON_CODES } from './registry.js';
28
30
 
29
- const OPTIONAL_ID_FIELDS = ['trace_id', 'span_id', 'parent_span_id', 'execution_id', 'session_id', 'project_ref'];
31
+ const OPTIONAL_ID_FIELDS = [
32
+ 'trace_id',
33
+ 'span_id',
34
+ 'parent_span_id',
35
+ 'execution_id',
36
+ 'session_id',
37
+ 'project_ref',
38
+ 'agent_id',
39
+ 'project_instance_id',
40
+ ];
30
41
  const REASON_CODE_FIELDS = ['error_code', 'reason_code', 'wait_code'];
42
+ // Fields a v1 reader knows about: the frozen envelope plus the stamp
43
+ // sanitizeObserved adds before persistence. Anything else at the top level
44
+ // is an unknown envelope field and rejects.
45
+ const KNOWN_ENVELOPE_FIELDS = new Set([...ENVELOPE_FIELDS, 'redaction_version']);
46
+ // DF2-FR01: correlation ids are bounded opaque strings.
47
+ const MAX_ID_FIELD_CHARS = 128;
31
48
  const ISO_8601_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
32
49
 
33
50
  function isPlainObject(value) {
@@ -106,8 +123,19 @@ function validatePayload(payload, errors) {
106
123
  errors.push(`payload.${field} ${JSON.stringify(code)} is not in the reason-code registry`);
107
124
  }
108
125
  }
109
- if (payload.confidence !== undefined && !CONFIDENCE_TYPES.includes(payload.confidence)) {
110
- errors.push(`payload.confidence must be one of ${CONFIDENCE_TYPES.join('|')}`);
126
+ // Confidence is either the typed enum (v1) or the v1.1 scored shape
127
+ // {type: CONFIDENCE_TYPE, value: finite number} used by anomaly.detected.
128
+ if (payload.confidence !== undefined && payload.confidence !== null) {
129
+ if (isPlainObject(payload.confidence)) {
130
+ if (!CONFIDENCE_TYPES.includes(payload.confidence.type)) {
131
+ errors.push(`payload.confidence.type must be one of ${CONFIDENCE_TYPES.join('|')} (got ${JSON.stringify(payload.confidence.type)})`);
132
+ }
133
+ if (typeof payload.confidence.value !== 'number' || !Number.isFinite(payload.confidence.value)) {
134
+ errors.push('payload.confidence.value must be a finite number');
135
+ }
136
+ } else if (!CONFIDENCE_TYPES.includes(payload.confidence)) {
137
+ errors.push(`payload.confidence must be one of ${CONFIDENCE_TYPES.join('|')}`);
138
+ }
111
139
  }
112
140
  if (payload.telemetry_complete !== undefined && typeof payload.telemetry_complete !== 'boolean') {
113
141
  errors.push('payload.telemetry_complete must be a boolean');
@@ -128,6 +156,16 @@ export function validateSemanticRecord(record, context) {
128
156
  return { ok: false, errors: ['record must be a plain object'] };
129
157
  }
130
158
 
159
+
160
+ // The envelope is the frozen contract surface: any top-level key outside
161
+ // the known field set (envelope + the persisted redaction stamp) rejects.
162
+ // New fields arrive only via schema-versioned additive releases.
163
+ for (const key of Object.keys(record)) {
164
+ if (!KNOWN_ENVELOPE_FIELDS.has(key)) {
165
+ errors.push(`unknown envelope field: ${key}`);
166
+ }
167
+ }
168
+
131
169
  for (const field of REQUIRED_ENVELOPE_FIELDS) {
132
170
  if (record[field] === undefined || record[field] === null) {
133
171
  errors.push(`missing required field: ${field}`);
@@ -199,6 +237,30 @@ export function validateSemanticRecord(record, context) {
199
237
  }
200
238
  }
201
239
 
240
+ // DF2-FR01 v1.1 additive shapes — correlation ids are bounded opaque
241
+ // strings; sampling stamps the policy that produced this record.
242
+ for (const field of ['agent_id', 'project_instance_id']) {
243
+ const value = record[field];
244
+ if (value !== undefined && value !== null) {
245
+ if (typeof value !== 'string' || value.length === 0 || value.length > MAX_ID_FIELD_CHARS) {
246
+ errors.push(`${field} must be a non-empty string of at most ${MAX_ID_FIELD_CHARS} characters`);
247
+ }
248
+ }
249
+ }
250
+ if (record.sampling !== undefined && record.sampling !== null) {
251
+ const sampling = record.sampling;
252
+ if (!isPlainObject(sampling)) {
253
+ errors.push('sampling must be an object {policy_version, rate}');
254
+ } else {
255
+ if (!isNonEmptyString(sampling.policy_version) || sampling.policy_version.length > MAX_ID_FIELD_CHARS) {
256
+ errors.push(`sampling.policy_version must be a non-empty string of at most ${MAX_ID_FIELD_CHARS} characters`);
257
+ }
258
+ if (typeof sampling.rate !== 'number' || !Number.isFinite(sampling.rate) || sampling.rate < 0 || sampling.rate > 1) {
259
+ errors.push('sampling.rate must be a number in [0, 1]');
260
+ }
261
+ }
262
+ }
263
+
202
264
  if (!isPlainObject(record.payload)) {
203
265
  errors.push('payload must be an object');
204
266
  } else {
@@ -10,11 +10,38 @@
10
10
  import fs from 'node:fs';
11
11
  import path from 'node:path';
12
12
  import crypto from 'node:crypto';
13
+ import zlib from 'node:zlib';
13
14
 
14
15
  export const ACTIVE_FILE = 'active.jsonl';
15
16
  export const STATE_FILE = '_state.json';
16
17
  export const QUARANTINE_DIR = 'quarantine';
17
18
  export const SEALED_RE = /^seg-(\d+)-([A-Za-z0-9_-]+)-([A-Za-z0-9_-]+)\.jsonl$/;
19
+ export const SEALED_ZST_RE = /^seg-(\d+)-([A-Za-z0-9_-]+)-([A-Za-z0-9_-]+)\.jsonl\.zst$/;
20
+
21
+ // Meta sidecar `compression` field values (SPEC §10): 'zst' sealed via
22
+ // zlib.zstdCompressSync, 'none' plain JSONL (legacy segments and Node <22.15).
23
+ export const COMPRESSION_ZST = 'zst';
24
+ export const COMPRESSION_NONE = 'none';
25
+
26
+ /** zlib provider for a policy/opts object — injectable for tests (policy.zlib). */
27
+ export function zlibOf(source) {
28
+ const z = source && source.zlib;
29
+ return z && typeof z === 'object' ? z : zlib;
30
+ }
31
+
32
+ let zstdProbe;
33
+
34
+ /**
35
+ * True when zstd compression is usable on this runtime (Node ≥22.15).
36
+ * Presence-checked against `zlib.zstdCompressSync`; the real-zlib result is
37
+ * cached per process — an injected `source.zlib` is evaluated every call.
38
+ */
39
+ export function zstdAvailable(source) {
40
+ const z = source && source.zlib !== undefined ? zlibOf(source) : null;
41
+ if (z) return typeof z.zstdCompressSync === 'function';
42
+ if (zstdProbe === undefined) zstdProbe = typeof zlib.zstdCompressSync === 'function';
43
+ return zstdProbe;
44
+ }
18
45
 
19
46
  export const DEFAULT_IO_TIMEOUT_MS = 5000;
20
47
 
@@ -136,7 +163,11 @@ export async function resolveFileInDir(filePath, timeoutMs = DEFAULT_IO_TIMEOUT_
136
163
  }
137
164
 
138
165
  export function metaPathFor(segmentPath) {
139
- return segmentPath.replace(/\.jsonl$/, '.meta.json');
166
+ // Per-format sidecar: a .zst segment pairs with seg-*.jsonl.zst.meta.json so
167
+ // an orphaned plain .jsonl (crash mid-seal) can never inherit the zst meta.
168
+ return segmentPath.endsWith('.jsonl.zst')
169
+ ? `${segmentPath}.meta.json`
170
+ : segmentPath.replace(/\.jsonl$/, '.meta.json');
140
171
  }
141
172
 
142
173
  export function sha256Buffer(buf) {
@@ -181,11 +212,12 @@ export function sanitizeNameComponent(value) {
181
212
 
182
213
  /**
183
214
  * Enumerate the segment directory.
184
- * → { ok: true, sealed: [{ name, seq, path, size }], active: { path, size } | null,
215
+ * → { ok: true, sealed: [{ name, seq, path, size, compressed }], active: { path, size } | null,
185
216
  * skipped: [{ name, reason }] }
186
217
  * | { ok: false, reason }
187
- * Symlinks are never followed — reported as skipped. Foreign .jsonl files that
188
- * match neither the active nor sealed naming scheme are skipped, never read.
218
+ * Symlinks are never followed — reported as skipped. Foreign .jsonl/.jsonl.zst
219
+ * files that match neither the active nor sealed naming scheme are skipped,
220
+ * never read. `compressed` marks seg-*.jsonl.zst entries (zstd-sealed).
189
221
  */
190
222
  export async function listSegments(root, timeoutMs = DEFAULT_IO_TIMEOUT_MS) {
191
223
  let entries;
@@ -201,7 +233,7 @@ export async function listSegments(root, timeoutMs = DEFAULT_IO_TIMEOUT_MS) {
201
233
  for (const entry of entries) {
202
234
  const name = entry.name;
203
235
  if (name === QUARANTINE_DIR || name === STATE_FILE || name.endsWith('.meta.json')) continue;
204
- if (!name.endsWith('.jsonl')) continue;
236
+ if (!name.endsWith('.jsonl') && !name.endsWith('.jsonl.zst')) continue;
205
237
  if (entry.isSymbolicLink()) {
206
238
  skipped.push({ name, reason: 'symlink_skipped' });
207
239
  continue;
@@ -223,8 +255,10 @@ export async function listSegments(root, timeoutMs = DEFAULT_IO_TIMEOUT_MS) {
223
255
  continue;
224
256
  }
225
257
  const m = SEALED_RE.exec(name);
226
- if (m) {
227
- sealed.push({ name, seq: Number.parseInt(m[1], 10), path: full, size });
258
+ const mz = m ? null : SEALED_ZST_RE.exec(name);
259
+ if (m || mz) {
260
+ const hit = m || mz;
261
+ sealed.push({ name, seq: Number.parseInt(hit[1], 10), path: full, size, compressed: Boolean(mz) });
228
262
  } else {
229
263
  skipped.push({ name, reason: 'unrecognized_segment_name' });
230
264
  }
@@ -233,7 +267,7 @@ export async function listSegments(root, timeoutMs = DEFAULT_IO_TIMEOUT_MS) {
233
267
  return { ok: true, sealed, active, skipped };
234
268
  }
235
269
 
236
- /** Total bytes held in live .jsonl segment files (meta/state excluded). */
270
+ /** Total bytes held in live segment files (meta/state excluded). */
237
271
  export function totalSegmentBytes(listing) {
238
272
  let total = listing.active ? listing.active.size : 0;
239
273
  for (const seg of listing.sealed) total += seg.size;
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Typed reader over the append-only JSONL segment store.
5
5
  *
6
- * readSegments(root, { fromInclusive?, limit?, ioTimeoutMs? })
6
+ * readSegments(root, { fromInclusive?, limit?, ioTimeoutMs?, zlib? })
7
7
  * → AsyncIterable<record> with attached:
8
8
  * .coverage → { expired_segments, quarantined_segments, corrupt_lines,
9
9
  * partial_tail_bytes, cursor_missed, degraded[] }
@@ -19,6 +19,10 @@
19
19
  * Integrity: every sealed segment's sha256 is verified against its meta
20
20
  * sidecar before a single row is served; a mismatch or missing meta sends the
21
21
  * segment to quarantine/ — corrupt data is never falsified into the stream.
22
+ * seg-*.jsonl.zst payloads are decoded via zlib.zstdDecompressSync after the
23
+ * checksum passes (checksum covers the stored bytes); a decode failure is
24
+ * quarantined like a corrupt .jsonl, and a missing decoder on old Node
25
+ * degrades 'zstd_unavailable' instead of throwing.
22
26
  * A partial tail row in the active segment (hard-kill artifact) is skipped
23
27
  * and counted, never parsed. Symlinked or foreign-named files are never
24
28
  * followed or read.
@@ -38,6 +42,7 @@ import {
38
42
  resolveRoot,
39
43
  sha256Buffer,
40
44
  withTimeout,
45
+ zlibOf,
41
46
  } from './internal.js';
42
47
 
43
48
  export function readSegments(root, opts = {}) {
@@ -129,6 +134,35 @@ export function readSegments(root, opts = {}) {
129
134
  }
130
135
  continue;
131
136
  }
137
+ if (seg.compressed) {
138
+ const decompress = zlibOf(opts).zstdDecompressSync;
139
+ let decoded = null;
140
+ if (typeof decompress !== 'function') {
141
+ // Old Node can't decode — degrade typed, never claim the records.
142
+ degrade('zstd_unavailable', seg.name);
143
+ continue;
144
+ }
145
+ try {
146
+ decoded = decompress(buf);
147
+ } catch {
148
+ decoded = null;
149
+ }
150
+ if (decoded === null || decoded.length === 0) {
151
+ // Stored bytes verified but not a valid/non-empty zstd stream →
152
+ // corrupt like a bad .jsonl: quarantine, never falsify rows.
153
+ const q = await quarantineSegment(resolved.root, seg.name, 'decode_failed', {
154
+ ioTimeoutMs: timeoutMs,
155
+ });
156
+ if (q.ok) {
157
+ coverage.quarantined_segments += 1;
158
+ degrade('decode_failed_quarantined', seg.name);
159
+ } else {
160
+ degrade(`quarantine_failed:${q.reason}`, seg.name);
161
+ }
162
+ continue;
163
+ }
164
+ buf = decoded;
165
+ }
132
166
  const text = buf.toString('utf8');
133
167
  const complete = text.endsWith('\n') ? text.slice(0, -1) : text;
134
168
  for (const line of complete.split('\n')) {
@@ -31,14 +31,15 @@ import {
31
31
  } from './internal.js';
32
32
 
33
33
  /**
34
- * Move a corrupt sealed segment (and its meta sidecar) into `quarantine/`.
34
+ * Move a corrupt sealed segment (.jsonl or .jsonl.zst, and its meta sidecar)
35
+ * into `quarantine/`.
35
36
  * The segment is never deleted and never silently repaired — it is preserved
36
37
  * for inspection outside the live read path.
37
38
  */
38
39
  export async function quarantineSegment(root, segmentName, reason, opts = {}) {
39
40
  const timeoutMs = Number.isFinite(opts.ioTimeoutMs) ? opts.ioTimeoutMs : DEFAULT_IO_TIMEOUT_MS;
40
41
  const base = path.basename(String(segmentName));
41
- if (base !== segmentName || !base.endsWith('.jsonl')) {
42
+ if (base !== segmentName || !(base.endsWith('.jsonl') || base.endsWith('.jsonl.zst'))) {
42
43
  return { ok: false, reason: 'unsafe_path' };
43
44
  }
44
45
  const quarantineDir = path.join(root, QUARANTINE_DIR);
@@ -6,9 +6,13 @@
6
6
  *
7
7
  * active.jsonl shared append target (O_APPEND — concurrent
8
8
  * writers interleave whole rows, never bytes)
9
- * seg-<seq>-<boot>-<writer>.jsonl sealed segment
9
+ * seg-<seq>-<boot>-<writer>.jsonl sealed segment (or .jsonl.zst when the
10
+ * runtime has zlib.zstdCompressSync)
10
11
  * seg-<...>.meta.json sidecar: checksum, record/seq/importance
11
- * stats, sealed_at_ms, recovered flag
12
+ * stats, sealed_at_ms, recovered flag,
13
+ * compression ('zst'|'none')
14
+ * seg-<seq>.claim 0-byte seq allocator: 'wx' claim at seal,
15
+ * never released — a seq can never be reused
12
16
  * quarantine/ corrupt sealed segments (never falsified)
13
17
  * _state.json next_segment_seq, expired/quarantined
14
18
  * counters, disabled flag, active_since_ms
@@ -32,7 +36,8 @@
32
36
  * | { ok: false, reason }
33
37
  *
34
38
  * policy: { maxSegmentBytes, maxSegmentAgeMs, maxTotalBytes, ioTimeoutMs,
35
- * now } — `now` is injectable for deterministic tests.
39
+ * now, zlib } — `now` and `zlib` are injectable for deterministic
40
+ * tests (zlib stub → compression 'none' fallback).
36
41
  */
37
42
 
38
43
  import fs from 'node:fs';
@@ -42,6 +47,8 @@ import { validateSemanticRecord } from '../schema/validate.js';
42
47
  import { IMPORTANCE_LEVELS } from '../schema/constants.js';
43
48
  import {
44
49
  ACTIVE_FILE,
50
+ COMPRESSION_NONE,
51
+ COMPRESSION_ZST,
45
52
  DEFAULT_IO_TIMEOUT_MS,
46
53
  ioReason,
47
54
  ioTimeoutMsOf,
@@ -56,6 +63,8 @@ import {
56
63
  totalSegmentBytes,
57
64
  withTimeout,
58
65
  writeState,
66
+ zlibOf,
67
+ zstdAvailable,
59
68
  } from './internal.js';
60
69
 
61
70
  function importanceRank(level) {
@@ -64,10 +73,15 @@ function importanceRank(level) {
64
73
  }
65
74
 
66
75
  /**
67
- * Seal the current active segment: parse stats, checksum the exact bytes,
68
- * move it aside via link+unlink (atomic, never overwrites an existing seal),
69
- * and write its meta sidecar. Concurrent rotators lose the race on ENOENT
70
- * and simply report not-sealed.
76
+ * Seal the current active segment: claim a seq via seg-<seq>.claim ('wx',
77
+ * never released), move the file aside via an atomic rename, then parse
78
+ * stats + checksum the exact moved bytes, compress to seg-*.jsonl.zst when
79
+ * zstd is available (compression:'zst'), and write its meta sidecar.
80
+ * The meta write is the commit point — the plain
81
+ * .jsonl is deleted only after meta lands, so a crash mid-seal leaves an
82
+ * orphan .jsonl whose missing/meta-less state routes it to quarantine rather
83
+ * than being served twice. Concurrent rotators lose the race on ENOENT and
84
+ * simply report not-sealed.
71
85
  */
72
86
  async function sealActive(root, state, policy) {
73
87
  const timeoutMs = ioTimeoutMsOf(policy);
@@ -87,9 +101,91 @@ async function sealActive(root, state, policy) {
87
101
  return { ok: true, sealed: false };
88
102
  }
89
103
 
104
+ // Peek only to name the segment — boot/writer from the first valid row.
105
+ // Authoritative stats/checksum come from a post-rename read: the file may
106
+ // gain rows between this read and the move.
107
+ let bootId = 'unknown';
108
+ let writerId = 'unknown';
109
+ try {
110
+ const peek = await withTimeout(fs.promises.readFile(activePath), timeoutMs);
111
+ for (const line of peek.toString('utf8').split('\n')) {
112
+ if (line.length === 0) continue;
113
+ let parsed = null;
114
+ try {
115
+ parsed = JSON.parse(line);
116
+ } catch {
117
+ continue;
118
+ }
119
+ if (bootId === 'unknown' && typeof parsed.boot_id === 'string') bootId = parsed.boot_id;
120
+ if (writerId === 'unknown' && typeof parsed.writer_id === 'string') writerId = parsed.writer_id;
121
+ if (bootId !== 'unknown' && writerId !== 'unknown') break;
122
+ }
123
+ } catch (err) {
124
+ if (err && err.code === 'ENOENT') return { ok: true, sealed: false };
125
+ return { ok: false, reason: ioReason(err) };
126
+ }
127
+
128
+ // Segments are named seg-<seq>-<boot>-<writer>.* — the name alone cannot
129
+ // arbitrate seq ownership because concurrent sealers see different
130
+ // boot/writer components (a stale next_segment_seq otherwise commits the
131
+ // same seq twice under different names and scrambles read order). The
132
+ // seg-<seq>.claim marker is the atomic allocator: 'wx' creation wins the
133
+ // seq, and a claim is NEVER released — a burned seq cannot be reused, so a
134
+ // segment sealed later can never sort before an earlier commit even when
135
+ // _state.json reads stale. Claims happen before the move, so seq follows
136
+ // segment append order. Orphan claims from a lost race or crash are
137
+ // 0-byte and ignored by the listing.
138
+ //
139
+ // The move itself is rename(active → seg-N.jsonl), not link+unlink: under
140
+ // concurrent sealers link+unlink lets BOTH movers commit the same inode —
141
+ // a recreated active.jsonl satisfies the second unlink. Rename is atomic:
142
+ // exactly one mover wins; the loser sees ENOENT and reports not-sealed.
143
+ let seq = state.next_segment_seq;
144
+ let name = null;
145
+ let sealedPath = null;
146
+ for (let attempt = 0; attempt < 64; attempt++) {
147
+ const padded = String(seq).padStart(6, '0');
148
+ name = `seg-${padded}-${sanitizeNameComponent(bootId)}-${sanitizeNameComponent(writerId)}.jsonl`;
149
+ const target = path.join(root, name);
150
+ try {
151
+ await withTimeout(
152
+ fs.promises.writeFile(path.join(root, `seg-${padded}.claim`), '', { flag: 'wx' }),
153
+ timeoutMs,
154
+ );
155
+ } catch (err) {
156
+ if (err && err.code === 'EEXIST') {
157
+ seq += 1;
158
+ continue;
159
+ }
160
+ return { ok: false, reason: ioReason(err) };
161
+ }
162
+ try {
163
+ // Claim won — but a crash-orphan segment may still sit at this name
164
+ // (its claim predates this run). Never rename over it: burn the seq
165
+ // and let the reader quarantine the meta-less orphan.
166
+ await withTimeout(fs.promises.lstat(target), timeoutMs);
167
+ seq += 1;
168
+ continue;
169
+ } catch (err) {
170
+ if (!(err && err.code === 'ENOENT')) return { ok: false, reason: ioReason(err) };
171
+ }
172
+ try {
173
+ await withTimeout(fs.promises.rename(activePath, target), timeoutMs);
174
+ sealedPath = target;
175
+ break;
176
+ } catch (err) {
177
+ // ENOENT: another sealer moved active.jsonl first — our claim stays
178
+ // (the seq is burned), nothing else to unwind.
179
+ if (err && err.code === 'ENOENT') return { ok: true, sealed: false };
180
+ return { ok: false, reason: ioReason(err) };
181
+ }
182
+ }
183
+ // Every attempt collided — never write meta for a name that was not sealed.
184
+ if (!sealedPath) return { ok: false, reason: 'seal_conflict' };
185
+
90
186
  let buf;
91
187
  try {
92
- buf = await withTimeout(fs.promises.readFile(activePath), timeoutMs);
188
+ buf = await withTimeout(fs.promises.readFile(sealedPath), timeoutMs);
93
189
  } catch (err) {
94
190
  return { ok: false, reason: ioReason(err) };
95
191
  }
@@ -101,8 +197,6 @@ async function sealActive(root, state, policy) {
101
197
  let seqMax = null;
102
198
  let importanceMax = 'low';
103
199
  let maxRank = -1;
104
- let bootId = 'unknown';
105
- let writerId = 'unknown';
106
200
  const text = buf.toString('utf8');
107
201
  const complete = text.endsWith('\n') ? text.slice(0, -1) : text;
108
202
  for (const line of complete.split('\n')) {
@@ -128,30 +222,29 @@ async function sealActive(root, state, policy) {
128
222
  }
129
223
  }
130
224
 
131
- // link+unlink: fails atomically if the target seal already exists (another
132
- // writer won the seq) — rename would silently overwrite it.
133
- let seq = state.next_segment_seq;
134
- let name = null;
135
- let linked = false;
136
- for (let attempt = 0; attempt < 64; attempt++) {
137
- name = `seg-${String(seq).padStart(6, '0')}-${sanitizeNameComponent(bootId)}-${sanitizeNameComponent(writerId)}.jsonl`;
138
- const target = path.join(root, name);
225
+ // Compression happens only here, at seal time — the emit/append hot path is
226
+ // untouched. The payload lands via tmp+rename (atomic, never a half-written
227
+ // .zst); any compress/write failure drops back to 'none' on the plain
228
+ // .jsonl — a seal never fails over compression.
229
+ let payloadPath = sealedPath;
230
+ let payloadBuf = buf;
231
+ let compression = COMPRESSION_NONE;
232
+ if (zstdAvailable(policy)) {
139
233
  try {
140
- await withTimeout(fs.promises.link(activePath, target), timeoutMs);
141
- await withTimeout(fs.promises.unlink(activePath), timeoutMs);
142
- linked = true;
143
- break;
144
- } catch (err) {
145
- if (err && err.code === 'ENOENT') return { ok: true, sealed: false }; // lost the race
146
- if (err && err.code === 'EEXIST') {
147
- seq += 1;
148
- continue;
234
+ const out = zlibOf(policy).zstdCompressSync(buf);
235
+ if (Buffer.isBuffer(out) && out.length > 0) {
236
+ const zstPath = `${sealedPath}.zst`;
237
+ const tmpPath = `${zstPath}.tmp-${process.pid}-${seq}`;
238
+ await withTimeout(fs.promises.writeFile(tmpPath, out), timeoutMs);
239
+ await withTimeout(fs.promises.rename(tmpPath, zstPath), timeoutMs);
240
+ payloadPath = zstPath;
241
+ payloadBuf = out;
242
+ compression = COMPRESSION_ZST;
149
243
  }
150
- return { ok: false, reason: ioReason(err) };
244
+ } catch {
245
+ // 'none' fallback — the reserved .jsonl simply stays the segment.
151
246
  }
152
247
  }
153
- // Every attempt collided — never write meta for a name that was not sealed.
154
- if (!linked) return { ok: false, reason: 'seal_conflict' };
155
248
 
156
249
  const meta = {
157
250
  sealed_seq: seq,
@@ -164,21 +257,32 @@ async function sealActive(root, state, policy) {
164
257
  importance_max: importanceMax,
165
258
  corrupt_lines: corruptLines,
166
259
  recovered: false,
167
- checksum: sha256Buffer(buf),
260
+ compression,
261
+ checksum: sha256Buffer(payloadBuf),
168
262
  };
169
263
  try {
170
264
  await withTimeout(
171
- fs.promises.writeFile(metaPathFor(path.join(root, name)), JSON.stringify(meta, null, 2)),
265
+ fs.promises.writeFile(metaPathFor(payloadPath), JSON.stringify(meta, null, 2)),
172
266
  timeoutMs,
173
267
  );
174
268
  } catch (err) {
175
269
  return { ok: false, reason: ioReason(err) };
176
270
  }
177
271
 
272
+ // Commit point passed: drop the plain .jsonl when a .zst payload exists.
273
+ if (compression === COMPRESSION_ZST) {
274
+ try {
275
+ await withTimeout(fs.promises.unlink(sealedPath), timeoutMs);
276
+ } catch {
277
+ // Orphan .jsonl beside a committed .zst: it has no meta of its own, so
278
+ // the reader quarantines it — never falsified, never double-served.
279
+ }
280
+ }
281
+
178
282
  state.next_segment_seq = Math.max(state.next_segment_seq, seq + 1);
179
283
  state.active_since_ms = null;
180
284
  await writeState(root, state, timeoutMs);
181
- return { ok: true, sealed: true, name };
285
+ return { ok: true, sealed: true, name: path.basename(payloadPath) };
182
286
  }
183
287
 
184
288
  /**