Uh oh!
There was an error while loading. Please reload this page.
Shared utilities: fs_atomic and json_util (hypaware/core/util) - #240
Conversation
Replace the bare-paragraph callback response with a self-contained landing page: inline Hyperparam mark, light/dark support, and a title/detail split in respond(). No external fetches, since the browser only reached an ephemeral loopback port.
One atomic tmp+rename writer (sync/async, JSON variants, optional mode/ dirMode/fsync/expectedMtimeMs guard) and one set of value-inspection and canonical-JSON helpers, to replace the 20+ hand-rolled tmp+rename writes and the isPlainObject/stringValue/canonicalJson/errCode copies scattered across core and plugins. Exported as hypaware/core/util for plugins.
Replaces 17 hand-rolled atomic-write sites (daemon pid/status/unit files, config control state and action markers, cache cursor/spool/progress/ ingest-seq, plugin lock, sink watermarks, remote credentials and seeds) with atomicWriteJson/atomicWriteFile(+Sync), and the copy-pasted catch-ENOENT read dance with readFileIfExistsSync. Every migrated site now cleans up its temp file when the rename fails; before, only the daemon unit writers did. Left alone on purpose: the iceberg resolver's writer (interleaved 412 collision checks), the config pointer flip (symlink, not a file write), and git_fetch's directory swap. Behavior notes: temp names are unified as <target>.<pid>.<rand>.tmp and JSON files now consistently end with a trailing newline.
The codex config and claude settings writers keep their exported signatures and domain errors but delegate the stat-guard/tmp/fsync/ cleanup dance to atomicWriteFile, rewrapping ConcurrentEditError as their CONCURRENT_EDIT error. central identity, context-graph-enrich state, claude session-context compaction, and vector-search shard metadata move to the plain helpers. The vector shard file itself still streams through hypvector's writer, so its tmp+rename stays.
Replaces the pasted per-file copies with imports: isPlainObject (16 copies), stringValue (9), parseMaybeJson (4), canonicalJson/sortKeys (4), sha256Hex (3, plus the two plugin_install hashString twins), and errCode (4). All copies were semantically identical; the divergent variants stay local on purpose (otel's stringValue returns null, and ai-gateway-graph's parseMaybeJson returns null on parse failure).
…rint Replace the @typedef in fs_atomic.js with an AtomicWriteOptions interface in a sibling types.d.ts imported via @import, per the repo's type-in-.d.ts convention. Drop identity_client's dead path import and route fingerprintToken through the shared sha256Hex, dropping its lone createHash import.
platypii
commented
Jul 5, 2026
ReviewRan a multi-angle review (line-by-line, removed-behavior, cross-file, reuse, efficiency, altitude, conventions, tests). Consensus: this is a clean mechanical hoist — no correctness regressions. Modes ( Two convention/cleanup items are already fixed in Worth fixing1. Unconditional
Suggest an opt-out (e.g. Design / quality notes (lower priority)2. 3. Rewrap boilerplate copy-pasted at 3 sites — 4. Test coverage gaps —
5. Minor: Bottom line: safe to merge on correctness. #1 is the only remaining item with runtime cost; the rest are follow-ups. |
atomicWriteFile ran a recursive mkdir on every call; the migrated per-batch writeProgress and per-block writeNextSeq previously did none. Add a mkdir option (default true) and pass mkdir:false at those two sites, whose target directory is already guaranteed to exist.
platypii
commented
Jul 5, 2026
Addressed issues. Merging. |
Uh oh!
There was an error while loading. Please reload this page.
Option A from the complexity review: hoist the two most-copied helper families into one shared module surface, exported as
hypaware/core/utilfor plugins and imported relatively within core.What's in here (discrete commits)
respond().src/core/util/:fs_atomic.js(async/sync atomic tmp+rename writers, JSON variants, optionalmode/dirMode/fsync, anexpectedMtimeMsoptimistic-concurrency guard throwingConcurrentEditError, and ENOENT-tolerantreadFileIfExists/readJsonIfExists) andjson_util.js(isPlainObject,stringValue,parseMaybeJson,sortKeys,canonicalJson,sha256Hex,errCode), with 14 new unit tests.ConcurrentEditErroras theirCONCURRENT_EDITerrors); central identity, context-graph-enrich state, claude session-context compaction, vector-search shard metadata.isPlainObjectx16,stringValuex9,parseMaybeJsonx4,canonicalJson/sortKeysx4,sha256Hex/hashStringx5,errCodex4.Net: +722 / -589 across 45 files, but the insertions are almost entirely the new documented+tested module and the loopback page; the migration itself deletes ~430 lines of hand-rolled copies.
Correctness gained, not just lines
.tmpfiles at 15+ sites.Deliberately left alone
ifNoneMatch412 collision checks around the rename), the config pointer flip (symlink swap), git_fetch's install-directory swap, and vector-search's shard file (streams through hypvector's writer).stringValue(returnsnull), ai-gateway-graph'sparseMaybeJson(returnsnullon parse failure), codex'stryParseJson, claude settle'ssafeParseJson.Behavior notes for review
<target>.<pid>.<rand>.tmp; nothing scans for the old patterns.JSON.parse.mkdir -pof the parent they previously assumed existed.Verification
npm run typecheckclean;npm run build:typesemitstypes/core/util/.npm test: 1829 tests, 1828 pass, 0 fail, 1 skipped (includes 14 new unit tests for the shared modules).