@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 +48 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
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.
|
|
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.
|
|
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.
|
|
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) {
|