@kelceyp/caw-server 1.0.278 → 1.0.280

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.
@@ -0,0 +1,100 @@
1
+ /**
2
+ * teardownReasons.mjs — closed enum for the pty-wrapper's `teardown()` reason,
3
+ * shared between the observability path (As1012) and the classification path
4
+ * (As1033).
5
+ *
6
+ * Authored under As1012 as part of the S2 workshop's Q4 outcome — imported by
7
+ * `pty-wrapper.mjs` (emit site) and by `JobRunner.js`'s watchdog gate + the
8
+ * stream-handler metadata plumbing on the As1033 side.
9
+ *
10
+ * Non-forced-path contract (joint As1012 × As1033)
11
+ * ------------------------------------------------
12
+ *
13
+ * 1. `teardown()` runs on every graceful exit path. Four call sites; four
14
+ * enum values: `WATCHDOG` (expiry), `SENTINEL_UNREADABLE`, `STOP_FAILURE`,
15
+ * `STOP` (normal completion via the Stop hook). Normal completion is NOT a
16
+ * no-teardown case — it is `teardown('stop')`. **`STOP` is a real value.**
17
+ *
18
+ * 2. `emitResult` always writes `metadata.teardownReason` when it runs, with
19
+ * a value from this enum. The field is NOT optional on the graceful path —
20
+ * consumers read it directly without an `undefined` check.
21
+ *
22
+ * 3. **`metadata.teardownReason` is omitted (not `null`) whenever the pty
23
+ * child exits without `teardown()` firing.** Absence is the signal — the
24
+ * enum does not carry a *no-teardown* value. Four non-teardown exit
25
+ * paths, listed exhaustively so a future debugger does not misread the
26
+ * gap:
27
+ *
28
+ * a. **Child self-exit** — `claude` returns of its own accord before
29
+ * the Stop hook fires (rare; e.g. an unhandled internal error).
30
+ * `onExit` runs with a non-zero code; `emitResult` runs; the field
31
+ * is omitted because `teardown()` never called `emitResult` with a
32
+ * reason. Classifier reads a distinct exit class from `exitCode`.
33
+ * b. **External signal to the wrapper** (SIGTERM/SIGINT delivered
34
+ * externally, not from the wrapper's own deadline path). Wrapper
35
+ * may or may not manage to emit anything depending on the signal
36
+ * and its handler set at the moment.
37
+ * c. **Prompt-too-long emission** — the wrapper's stdout emits its
38
+ * result branch directly without routing through `teardown()`
39
+ * (`pty-wrapper.mjs:733` and its neighbours; see
40
+ * Easy-to-Miss #5 in As1012's design). Field omitted.
41
+ * d. **As1006's F2 SIGKILL** — the wrapper-self-terminate path with a
42
+ * process-group SIGKILL of the PTY child under the wrapper's own
43
+ * deadline. SIGKILL is unmaskable; the wrapper is dead before it
44
+ * can emit; the entire `type:"result"` JSONL line is absent.
45
+ *
46
+ * **`As1006's F2 SIGKILL path is an OUT-OF-ENUM EXIT PATH`, as are the
47
+ * other three above.** Do not read this enum as enumerating every wrapper
48
+ * exit. An F2-killed job leaves NO wrapper-side breadcrumb via this
49
+ * enum — that gap is by design, not by omission. As1033's dissent from
50
+ * workshop round 3 is honoured by this note.
51
+ *
52
+ * The classifier's contract for any of the four non-teardown cases:
53
+ * `turnSignals.teardownReason === undefined` is a **distinct exit
54
+ * class** — spawn failure / SIGKILL / crash / prompt-too-long / external
55
+ * signal — not a teardown. Classification gate ordering (As1033):
56
+ * `WATCHDOG` gate first (positive match on `reason === WATCHDOG`); other
57
+ * reasons fall through unchanged; missing reason routes to the
58
+ * spawn-failure / no-result branch.
59
+ *
60
+ * 4. Lifecycle events (`armed`, `reset`, `expired`, `teardown`) share the
61
+ * enum only via the `teardown` event's `reason` field. `armed` / `reset`
62
+ * / `expired` observability events carry `type`, not `reason` — no enum
63
+ * value on those.
64
+ *
65
+ * 5. **The enum is closed at story-close.** Adding a value requires a
66
+ * coordinated change to both this module AND the classification gate in
67
+ * `JobRunner.js`. This is a maintenance rule, not a note — treat the set
68
+ * below as fixed.
69
+ *
70
+ * Consumers
71
+ * ---------
72
+ *
73
+ * - `pty-wrapper.mjs` — emit sites: `teardown(reason)` calls at the watchdog
74
+ * expiry closure (`WATCHDOG`), sentinel-read failure (`SENTINEL_UNREADABLE`),
75
+ * StopFailure hook (`STOP_FAILURE`), clean no-pending-work path (`STOP`).
76
+ * - `pty-emit-result.mjs` — writes `metadata.teardownReason` on the result
77
+ * JSONL line (As1033).
78
+ * - `claude-session.js` — `getMetadata()` propagates the value into
79
+ * `turnSignals.teardownReason` (As1033).
80
+ * - `JobRunner.js` — watchdog classification gate reads
81
+ * `turnSignals.teardownReason` and compares against `TEARDOWN_REASONS.WATCHDOG`
82
+ * (As1033).
83
+ * - `Log.js` observability — the `WATCHDOG event=teardown reason=<value>`
84
+ * line format (As1012) references the enum values verbatim.
85
+ *
86
+ * Packaging
87
+ * ---------
88
+ *
89
+ * Copied verbatim into `dist/` by `server/package.json`'s `build:all` script,
90
+ * alongside `pty-wrapper.mjs`, `pty-emit-result.mjs`, and
91
+ * `pty-deferred-teardown.mjs`. Not bundled — the wrapper is executed as a
92
+ * standalone node process and imports this module by relative path.
93
+ */
94
+
95
+ export const TEARDOWN_REASONS = Object.freeze({
96
+ WATCHDOG: 'watchdog',
97
+ STOP: 'stop',
98
+ SENTINEL_UNREADABLE: 'sentinel-unreadable',
99
+ STOP_FAILURE: 'stop-failure',
100
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kelceyp/caw-server",
3
- "version": "1.0.278",
3
+ "version": "1.0.280",
4
4
  "description": "CAW server - REST API, MCP server and core features",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -19,7 +19,7 @@
19
19
  "build": "bash scripts/bun-build.sh",
20
20
  "build:client": "cd ../client && bun run build",
21
21
  "build:cli": "cd ../cli && bun run build && cp dist/cli.js ../server/dist/",
22
- "build:all": "bun run clean && bun run generate:applyPojo && bun run build:client && bun run build && cp src/core/services/embedding-worker.js dist/embedding-worker.js && cp src/core/agents/pty-wrapper.mjs dist/pty-wrapper.mjs && cp src/core/agents/pty-deferred-teardown.mjs dist/pty-deferred-teardown.mjs && cp src/core/agents/pty-emit-result.mjs dist/pty-emit-result.mjs && bun run build:cli",
22
+ "build:all": "bun run clean && bun run generate:applyPojo && bun run build:client && bun run build && cp src/core/services/embedding-worker.js dist/embedding-worker.js && cp src/core/agents/pty-wrapper.mjs dist/pty-wrapper.mjs && cp src/core/agents/pty-deferred-teardown.mjs dist/pty-deferred-teardown.mjs && cp src/core/agents/pty-emit-result.mjs dist/pty-emit-result.mjs && cp src/core/agents/teardownReasons.mjs dist/teardownReasons.mjs && bun run build:cli",
23
23
  "dev": "STATIC_PATH=../client/src bun --define __BUILTIN_POSTHOG_KEY__=null run src/main.js",
24
24
  "start": "node dist/main.js",
25
25
  "test": "bun test --define __BUILTIN_POSTHOG_KEY__=null test/unit test/integration test/component test/cli test/investigation && (cd ../client && bun test)",