> For the complete documentation index, see [llms.txt](https://faction-os.gitbook.io/faction-os-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://faction-os.gitbook.io/faction-os-docs/.spec_system/archive/sessions/phase01-session02-hook-runtime-safety/spec.md).

# Session Specification

**Session ID**: `phase01-session02-hook-runtime-safety` **Phase**: 01 - Core Runtime Hardening **Status**: Complete **Created**: 2026-05-29 **Package**: Cross-cutting (`apps/hooks`, `apps/cli`) **Package Stack**: JavaScript ESM hooks and CLI, Vitest node tests, Markdown docs

***

## 1. Session Overview

This session hardens the hook runtime after Session 01 made the event ingest contract explicit. Hook handlers are on the critical path of Claude Code execution, so they must stay silent, fast, dependency-light, and tolerant when the local FactionOS server or the optional listener bridge is unavailable. The current source already has hook handlers, shared helpers, a per-terminal WebSocket listener, installer and CLI diagnostics, and static hook payload fixture tests; this session turns those pieces into a tested runtime safety contract.

The work focuses on two packages. `apps/hooks` owns the per-event handler behavior, session id persistence, listener process state, unavailable-server fallback, spool draining, and diagnostic logging. `apps/cli` owns setup and diagnostic flows that create local state, write Claude settings, and help users understand listener health. The session preserves local-first behavior and does not add remote services, inbound chat commands, or UI approval redesign.

***

## 2. Objectives

1. Standardize atomic local-state writes and safe JSON reads for hook and CLI files that can be touched during user-owned setup or hook execution.
2. Ensure hook handlers remain silent and exit 0 during normal operation, including when the server is unreachable.
3. Harden listener startup, PID handling, reconnect, spool draining, malformed frame handling, and shutdown cleanup.
4. Add focused runtime tests and docs for hook safety, diagnostics, fallback behavior, and current listener limits.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase01-session01-event-ingest-contract-reconciliation` - provides the supported ingest vocabulary, session alias behavior, hook fixture baseline, and current API/hook contract docs.

### Required Tools/Knowledge

* Node.js 20 or newer.
* npm workspace and Vitest commands from the repository root.
* Current hook contracts in `apps/hooks/hooks.json` and `apps/hooks/src/`.
* Current CLI setup and diagnostics commands in `apps/cli/src/commands/`.
* Session 01 ingest docs in `docs/api/event-api-hook-contracts.md`.

### Environment Requirements

* Work from the repository root.
* Use `EXAMPLES/` as source evidence only; do not copy historical runtime files.
* Keep hook handlers dependency-light and compatible with fire-and-forget execution.

***

## 4. Scope

### In Scope (MVP)

* Claude Code user can trigger supported hooks while the server is unavailable and the underlying CLI still sees silent, bounded, successful hook exits - add runtime tests and fallback behavior.
* Maintainer can rely on atomic writes for session ids, listener PID files, settings, and hook settings where practical - add shared helpers and apply them to high-risk paths.
* User can inspect hook and listener diagnostics under the FactionOS home directory without raw payloads, full prompts, command bodies, terminal output, secret values, or private file contents being logged.
* Listener bridge can start once per terminal, reconnect with bounded backoff, drain bounded spool files, discard malformed spool entries, handle malformed WebSocket frames, and clean up PID state on shutdown.
* CLI `init`, `status`, and `doctor` can create and report runtime state consistently, including listener and spool posture.
* Hook and CLI READMEs describe supported diagnostics, latency and silence expectations, listener limitations, and unavailable-server behavior.

### Out of Scope (Deferred)

* Adding inbound chat commands - Reason: adapters remain outbound-only until a new permission model exists.
* Docker, Apple Containers, or sandbox isolation - Reason: later phases own isolation and release hardening.
* UI permission dialog redesign - Reason: product-surface sessions own approval workflows.
* CLI start, open, stop, daemon, port, PID lifecycle parity - Reason: Session 03 owns full CLI lifecycle parity.
* Server route authorization, export redaction, archive retention, and LLM scan privacy - Reason: later Phase 01 sessions own those hardening areas.

***

## 5. Technical Approach

### Architecture

The hook package should centralize reusable safety behavior in `apps/hooks/src/_lib.js`: atomic writes, safe JSON or text reads, session-state path sanitization, redacted logging, and unavailable-server fallback. Hook handlers should remain small and should call shared helpers instead of repeating session id file parsing, state directory creation, and POST failure handling.

The listener should keep its WebSocket loop self-contained but make its file state safer: atomic PID writes, bounded spool reads, deterministic malformed file deletion, reconnect timer cleanup, and no raw message logging. It should continue to tolerate missing server access without becoming a hard dependency for hook execution.

The CLI package should introduce a small local file helper for atomic JSON writes and safe reads, then apply it to `init` and diagnostic paths. Tests should use temporary homes and local fake servers/processes instead of touching real user settings, real Claude state, or real network services.

### Design Patterns

* Boundary safety: validate and sanitize local file names, JSON reads, and external process state before using them.
* Atomic local writes: write to a temp file and rename for state/settings files where practical.
* Fire-and-forget fallback: hook handlers must not block the calling CLI while waiting for server availability.
* Bounded diagnostics: logs should record handler, kind, timestamps, status, and sizes, not raw event payloads.
* Fixture isolation: tests must use temporary home directories and fake servers instead of real local user data.

### Technology Stack

* JavaScript ESM in `apps/hooks` and `apps/cli`.
* Node.js built-in `fs`, `http`, `child_process`, `os`, and `path` modules.
* `ws` 8.21.0 for listener WebSocket behavior.
* Vitest 4 node project tests.
* Biome for formatting and linting.

***

## 6. Deliverables

### Files to Create

| File                                       | Purpose                                                                                             | Est. Lines |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------- | ---------- |
| `apps/hooks/tests/hookRuntime.test.js`     | Runtime process tests for silence, exit codes, unavailable server fallback, and bounded diagnostics | \~220      |
| `apps/hooks/tests/listenerRuntime.test.js` | Listener startup, reconnect, malformed frame, spool, and shutdown tests                             | \~180      |
| `apps/cli/src/lib/localFiles.js`           | CLI atomic JSON write and safe local read helpers                                                   | \~120      |
| `apps/cli/tests/cliRuntime.test.js`        | CLI init, settings write, and diagnostic fixture tests                                              | \~220      |

### Files to Modify

| File                                             | Changes                                                                                  | Est. Lines |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------- | ---------- |
| `vitest.config.ts`                               | Include `apps/cli/tests/**/*.test.js` in the node project                                | \~4        |
| `apps/hooks/src/_lib.js`                         | Add atomic write, session state, spool fallback, and redacted diagnostic helpers         | \~160      |
| `apps/hooks/src/ws-listener.js`                  | Harden PID writes, reconnect cleanup, spool pump, malformed frame handling, and shutdown | \~90       |
| `apps/hooks/src/factionos-hero-spawn.js`         | Use shared atomic session/PID helpers and safer listener spawn behavior                  | \~45       |
| `apps/hooks/src/factionos-hero-active.js`        | Use shared session resolver and fallback posting behavior                                | \~25       |
| `apps/hooks/src/factionos-hero-idle.js`          | Use shared session resolver and fallback posting behavior                                | \~25       |
| `apps/hooks/src/factionos-bash-command.js`       | Use shared session resolver, bounded payload helpers, and fallback posting behavior      | \~25       |
| `apps/hooks/src/factionos-file-access.js`        | Use shared session resolver, bounded payload helpers, and fallback posting behavior      | \~25       |
| `apps/hooks/src/factionos-awaiting-input.js`     | Use shared session resolver and bounded fallback behavior                                | \~25       |
| `apps/hooks/src/factionos-input-received.js`     | Use shared session resolver and bounded fallback behavior                                | \~25       |
| `apps/hooks/src/factionos-permission-request.js` | Use shared session resolver and bounded fallback behavior                                | \~25       |
| `apps/hooks/src/factionos-subagent-spawn.js`     | Use shared session resolver and bounded opaque payload behavior                          | \~25       |
| `apps/hooks/src/factionos-subagent-complete.js`  | Use shared session resolver and bounded opaque payload behavior                          | \~25       |
| `apps/hooks/src/factionos-git-guard.js`          | Preserve only intentional restricted-mode stderr and keep observer mode silent           | \~35       |
| `apps/cli/src/commands/init.js`                  | Use atomic local writes and safe existing settings parsing                               | \~70       |
| `apps/cli/src/commands/doctor.js`                | Report listener and spool state without raw payload exposure                             | \~45       |
| `apps/cli/src/commands/status.js`                | Report listener state and server failures consistently                                   | \~45       |
| `apps/hooks/README_hooks.md`                     | Document diagnostics, fallback, listener, silence, and latency behavior                  | \~80       |
| `apps/cli/README_cli.md`                         | Document runtime diagnostics and local-state safety behavior                             | \~50       |

***

## 7. Success Criteria

### Functional Requirements

* [ ] Hook handlers exit 0 and produce no stdout/stderr during normal operation when the server is unavailable.
* [ ] `factionos-git-guard.js` writes stderr and exits non-zero only for an intentional restricted-mode denial.
* [ ] Session id and listener PID state are written atomically where practical.
* [ ] Listener startup avoids duplicate per-terminal listeners, handles stale PID files, reconnects with bounded backoff, and cleans up on shutdown.
* [ ] Spool draining is bounded, deletes malformed spool entries, and preserves undelivered valid events when the socket is closed.
* [ ] CLI init and diagnostics operate against temporary fixture homes in tests without touching real user settings.

### Testing Requirements

* [ ] Hook runtime tests cover silence, exit code, unavailable server behavior, restricted git guard behavior, and diagnostic log shape.
* [ ] Listener tests cover startup, malformed frames, spool pump behavior, reconnect or closed-socket fallback, and shutdown cleanup.
* [ ] CLI tests cover atomic settings writes, malformed existing settings, hook settings preservation, and diagnostic listener/spool reporting.
* [ ] Focused Vitest command passes for hook and CLI runtime tests.

### Non-Functional Requirements

* [ ] Hook handlers remain dependency-light and fast to spawn.
* [ ] Diagnostics avoid full prompts, terminal output, raw command payloads, secret values, and private file contents.
* [ ] Optional services, API keys, hosted accounts, and remote relays are not required for the tested behavior.
* [x] Historical artifacts remain reference-only and are not imported into runtime paths.

### Quality Gates

* [x] All files ASCII-encoded.
* [x] Unix LF line endings.
* [x] Code follows project conventions.
* [x] Focused tests pass before validation.

***

## 8. Implementation Notes

### Key Considerations

* Session 01 already made hook payload shape and session aliasing explicit; this session should not reopen ingest vocabulary decisions.
* `apps/hooks/src/ws-listener.js` currently drains a spool directory, but hook helpers need explicit tested behavior for what happens when POST delivery fails.
* Existing README contracts say state files use atomic writes; implementation should make that true for the critical hook and CLI paths touched here.
* `apps/cli` has no Vitest coverage in the current node project include list, so adding CLI fixture tests requires a `vitest.config.ts` update.

### Potential Challenges

* Fire-and-forget testing: hook handlers exit quickly, so process tests should assert exit, silence, filesystem side effects, and diagnostic logs rather than depending on slow network collection.
* Real user state risk: tests must force temporary `HOME` and `FACTIONOS_HOME` values before importing or spawning CLI commands.
* Listener process lifecycle: prefer testable helper seams or short-lived child processes with explicit timeouts instead of brittle sleeps.
* Privacy drift: diagnostics should log sizes, kinds, filenames, and status, not raw prompts, commands, payloads, or file contents.

### Relevant Considerations

* \[P00-apps/hooks+apps/cli] **Atomic-write gap**: Apply temp-write-plus-rename to user-owned config and local state touched in this session.
* \[P00] **Prompt and path privacy**: Keep diagnostics bounded and avoid raw payload capture.
* \[P00] **Local-first boundary**: Server, listener, API keys, and hosted services must be optional for hook execution.
* \[P00] **Historical artifacts quarantined**: Use `EXAMPLES/` as source evidence only and do not copy historical hook or listener code.
* \[P00] **Deterministic fallbacks**: Keep unavailable-server behavior predictable and testable.

### Behavioral Quality Focus

Checklist active: Yes Top behavioral risks for this session:

* Hook scripts accidentally block, write terminal noise, or exit non-zero during normal Claude Code execution.
* Local state writes corrupt user-owned settings or leave stale listener state after interruption.
* Diagnostics or spool files capture sensitive prompts, commands, paths, terminal output, or secret values.

***

## 9. Testing Strategy

### Unit Tests

* Test hook shared helpers for path sanitization, atomic write behavior, safe JSON read fallback, bounded log fields, and session id resolution.
* Test CLI local file helpers with temporary fixture directories and malformed JSON inputs.

### Integration Tests

* Spawn hook scripts with fixture stdin, unavailable server URLs, temporary homes, and captured stdout/stderr.
* Exercise listener spool behavior against closed and fake WebSocket states.
* Run CLI init, status, and doctor against temporary homes and fake server responses.

### Manual Testing

* Run a local hook script manually with `FACTIONOS_SERVER_URL` pointing at an unused loopback port and confirm silent exit plus bounded diagnostics.
* Run `factionos doctor` in a fixture or local sandbox and inspect listener and spool reporting.

### Edge Cases

* Empty stdin, malformed stdin JSON, and missing session id file.
* Stale PID file, unwritable state directory, malformed spool entry, and closed WebSocket during spool pump.
* Malformed existing `~/.factionos/settings.json` or Claude settings.
* Restricted git guard denial versus observer-mode dangerous command warning.

***

## 10. Dependencies

### External Libraries

* No new external runtime libraries are expected.
* Existing `ws` 8.21.0 remains the listener WebSocket dependency.
* Existing Vitest 4 remains the test runner.

### Other Sessions

* **Depends on**: `phase01-session01-event-ingest-contract-reconciliation`
* **Depended by**: `phase01-session03-cli-lifecycle-parity`, `phase01-session07-runtime-contract-documentation`

***

## Next Steps

Run the implement workflow step to begin AI-led implementation.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://faction-os.gitbook.io/faction-os-docs/.spec_system/archive/sessions/phase01-session02-hook-runtime-safety/spec.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
