@hardkas/core 0.7.10-alpha → 0.7.11-alpha

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/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # `@hardkas/core`
2
+
3
+ The Core package provides the foundational safety rails, filesystem abstractions, and atomic persistence primitives for the entire HardKAS ecosystem.
4
+
5
+ ## 1. Atomic Persistence Variants
6
+
7
+ All state mutation in HardKAS relies on strict atomic persistence to prevent corruption during unexpected crashes or power loss. The standard flow follows the `temp + rename + fsync` pattern.
8
+
9
+ ### Flow: Standard Atomic Write
10
+ 1. Data is written to a temporary file (`.hardkas/tmp/<uuid>.json`).
11
+ 2. `fs.fsyncSync()` is called on the temporary file to flush buffers to disk.
12
+ 3. The temporary file is atomically renamed over the target file (e.g., `state.json`).
13
+ 4. `fs.fsyncSync()` is called on the **parent directory** (`.hardkas/`) to ensure the directory entry is durably linked.
14
+
15
+ ### Variant: Fallback Write
16
+ If the filesystem does not support directory `fsync` (e.g., certain Windows/WSL configurations), the engine catches `EINVAL` or `EISDIR` and gracefully degrades to a standard atomic rename without the parent directory flush, logging a warning to the telemetry stream.
17
+
18
+ ## 2. Workspace Lock Mechanisms
19
+
20
+ To prevent concurrent modifications to the developer workspace, `@hardkas/core` uses a conservative file-based locking strategy (`.hardkas/locks/<domain>.lock`).
21
+
22
+ ### Flow: Lock Acquisition
23
+ 1. Process attempts to create a lock file using `fs.openSync(path, 'wx')` (exclusive write).
24
+ 2. If successful, the process PID and timestamp are written.
25
+ 3. If `EEXIST` is thrown, the process enters a **spin-wait loop** with exponential backoff (up to 30 seconds).
26
+
27
+ ### Variant: Stale Lock Detection & Recovery (LockHell Defense)
28
+ If a lock cannot be acquired after 30 seconds, the engine checks if the holding process is still alive.
29
+ - **Dead Process:** If `process.kill(pid, 0)` fails (indicating the PID no longer exists), the lock is deemed **stale**. The engine atomically overrides the lock and logs a `STALE_LOCK_RECOVERY` telemetry event.
30
+ - **Live Process:** If the PID is active, HardKAS strictly aborts with `HARDKAS_LOCK_CONTENTION`. It will *never* violently break a lock held by a live process.
31
+ - **Zero-Byte Locks:** If a system crash occurs precisely when the `wx` descriptor is created but before the PID is written (a TOCTOU scenario), HardKAS considers any 0-byte lock older than 10 seconds as implicitly stale.
32
+
33
+ ## 3. AppendCoordinator (Event Ledger)
34
+
35
+ The `events.jsonl` ledger is the source of truth for the workspace. It is strictly append-only.
36
+
37
+ ### Flow: Ledger Append
38
+ 1. Acquire the exclusive `events` lock.
39
+ 2. Read the tail of the stream to determine the last `eventId`.
40
+ 3. Append the new JSON payload with a trailing newline.
41
+ 4. `fs.fsyncSync()` the file descriptor.
42
+
43
+ ### Variant: Tail Corruption Repair
44
+ If the chaos engine (or a crash) leaves a partial JSON object at the tail of `events.jsonl` (e.g., `{"eventId": 142, "domain": "tx"` missing the closing brace):
45
+ 1. The `AppendCoordinator` detects `Unexpected end of JSON input` during tail-read.
46
+ 2. It explicitly scans backward to find the last valid newline boundary.
47
+ 3. The corrupted tail is truncated automatically.
48
+ 4. A `CORRUPT_TAIL_RECOVERY` event is dispatched to `telemetry.jsonl`.
package/dist/index.d.ts CHANGED
@@ -787,7 +787,7 @@ declare class AppendCoordinator {
787
787
  };
788
788
  }
789
789
 
790
- declare const CURRENT_RUNTIME_VERSION = "0.7.10-alpha";
790
+ declare const CURRENT_RUNTIME_VERSION = "0.7.11-alpha";
791
791
  declare const MIN_SUPPORTED_VERSION = "0.5.0-alpha";
792
792
  interface MigrationStatus {
793
793
  needsMigration: boolean;
package/dist/index.js CHANGED
@@ -956,7 +956,7 @@ async function createSnapshot(options) {
956
956
  const manifest = {
957
957
  snapshotVersion: 1,
958
958
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
959
- hardkasVersion: "0.7.10-alpha",
959
+ hardkasVersion: "0.7.11-alpha",
960
960
  stateAuthority: "filesystem",
961
961
  projectionAuthority: "sqlite",
962
962
  deterministicScope,
@@ -1205,7 +1205,7 @@ Resolution Command: ${report.exactReplayCommand}`
1205
1205
  // src/migrations.ts
1206
1206
  import fs6 from "fs";
1207
1207
  import path8 from "path";
1208
- var CURRENT_RUNTIME_VERSION = "0.7.10-alpha";
1208
+ var CURRENT_RUNTIME_VERSION = "0.7.11-alpha";
1209
1209
  var MIN_SUPPORTED_VERSION = "0.5.0-alpha";
1210
1210
  var MigrationManager = class {
1211
1211
  static checkVersion(rootDir) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardkas/core",
3
- "version": "0.7.10-alpha",
3
+ "version": "0.7.11-alpha",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",