ai-or-die 0.1.22 → 0.1.24

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.
@@ -52,7 +52,7 @@ jobs:
52
52
  playwright-report/
53
53
  retention-days: 14
54
54
 
55
- test-browser-functional:
55
+ test-browser-functional-core:
56
56
  runs-on: ${{ matrix.os }}
57
57
  strategy:
58
58
  matrix:
@@ -65,13 +65,38 @@ jobs:
65
65
  - run: npm ci
66
66
  - name: Install Playwright browsers
67
67
  run: npx playwright install chromium --with-deps
68
- - name: Run functional browser tests
69
- run: npx playwright test --config e2e/playwright.config.js --project functional
68
+ - name: Run functional core tests
69
+ run: npx playwright test --config e2e/playwright.config.js --project functional-core
70
70
  - name: Upload Playwright report
71
71
  uses: actions/upload-artifact@v4
72
72
  if: ${{ !cancelled() }}
73
73
  with:
74
- name: playwright-functional-${{ matrix.os }}
74
+ name: playwright-functional-core-${{ matrix.os }}
75
+ path: |
76
+ e2e/test-results/
77
+ playwright-report/
78
+ retention-days: 14
79
+
80
+ test-browser-functional-extended:
81
+ runs-on: ${{ matrix.os }}
82
+ strategy:
83
+ matrix:
84
+ os: [ubuntu-latest, windows-latest]
85
+ steps:
86
+ - uses: actions/checkout@v4
87
+ - uses: actions/setup-node@v4
88
+ with:
89
+ node-version: '22'
90
+ - run: npm ci
91
+ - name: Install Playwright browsers
92
+ run: npx playwright install chromium --with-deps
93
+ - name: Run functional extended tests
94
+ run: npx playwright test --config e2e/playwright.config.js --project functional-extended
95
+ - name: Upload Playwright report
96
+ uses: actions/upload-artifact@v4
97
+ if: ${{ !cancelled() }}
98
+ with:
99
+ name: playwright-functional-extended-${{ matrix.os }}
75
100
  path: |
76
101
  e2e/test-results/
77
102
  playwright-report/
package/CLAUDE.md CHANGED
@@ -19,17 +19,29 @@ Available agents: **Architect**, **Engineer**, **QA Reviewer**, **Troubleshooter
19
19
 
20
20
  ### Documentation-Driven Workflow
21
21
  Before starting any task, consult the relevant documentation:
22
- - `docs/agent-instructions/` -- Philosophy, research guidelines, testing standards, tooling conventions
22
+ - `docs/agent-instructions/` -- Agent workflow guides:
23
+ - `00-philosophy.md` -- Core principles
24
+ - `01-research-and-web.md` -- Research guidelines
25
+ - `02-testing-and-validation.md` -- Testing standards
26
+ - `03-tooling-and-pipelines.md` -- Tooling conventions
27
+ - `04-handoff-protocol.md` -- How to leave the repo clean for the next agent
28
+ - `05-defensive-coding.md` -- Error prevention, cross-platform traps
29
+ - `06-ci-first-testing.md` -- CI-only testing, E2E debugging, performance budget
30
+ - `07-docs-hygiene.md` -- Keeping documentation in sync
31
+ - `08-multi-agent-consultation.md` -- When and how to consult expert subagents
23
32
  - `docs/adrs/` -- Architecture Decision Records (check before proposing new patterns)
24
33
  - `docs/specs/` -- Component specifications (read before implementing, update after changing behavior)
25
34
  - `docs/architecture/` -- System diagrams and component overviews
26
- - `docs/history/` -- Incident post-mortems and debugging notes
35
+ - `docs/history/` -- Solved problems and debugging notes (check before debugging any issue)
27
36
 
28
37
  ### Mandatory Rules
29
38
  1. **Spec updates with code changes**: When code behavior changes, the corresponding spec in `docs/specs/` must be updated in the same commit or PR.
30
39
  2. **ADR compliance**: Never contradict an accepted ADR. To change direction, write a new ADR that supersedes the old one.
31
40
  3. **Cross-platform support**: All code must work on both Windows and Linux. Use `path.join()` for file paths, provide `.sh` and `.ps1` script variants, and test on both platforms in CI.
32
41
  4. **Test coverage**: Every feature and bug fix requires tests. No exceptions.
42
+ 5. **CI-only testing**: All testing happens on GitHub Actions runners. Never test locally. E2E tests are the only true validation. Push → draft PR → CI → iterate.
43
+ 6. **Document what you solve**: Every solved problem goes in `docs/history/`. LLMs don't carry memories — written docs are the only institutional memory.
44
+ 7. **Consult before committing**: For significant decisions, spawn expert subagents (architect, principal engineer, lead QA, PM, designer, user researcher) in parallel. See `docs/agent-instructions/08-multi-agent-consultation.md`.
33
45
 
34
46
  ## Common Commands
35
47
 
@@ -0,0 +1,65 @@
1
+ # ADR-0008: E2E Test Parallelization Strategy
2
+
3
+ ## Status
4
+
5
+ **Accepted**
6
+
7
+ ## Date
8
+
9
+ 2026-02-07
10
+
11
+ ## Context
12
+
13
+ The E2E test suite has grown to 16 spec files across 6 Playwright projects. The `functional` project — containing tests 02-07, 09-image-paste, and 09-background-notifications — runs approximately 30 tests sequentially with `workers: 1`. On GitHub Actions runners, this takes 7-15 minutes per platform, exceeding the 7-minute performance budget for CI feedback loops.
14
+
15
+ Fast CI feedback is critical because all testing happens exclusively on GitHub runners (no local testing). The push → CI → fix → push cycle must be fast enough that agents can iterate efficiently.
16
+
17
+ ## Decision
18
+
19
+ Split the functional test group into two sub-groups and enable parallel workers in CI:
20
+
21
+ ### Test Split
22
+ - **`functional-core`**: Tests `02-terminal-io`, `03-clipboard`, `04-context-menu`, `05-tab-switching` (core terminal interaction features)
23
+ - **`functional-extended`**: Tests `06-large-paste`, `07-vim-and-session`, `09-image-paste`, `09-background-notifications` (extended features and cross-cutting concerns)
24
+
25
+ ### Parallel Workers
26
+ - Set `workers: process.env.CI ? 2 : 1` in `e2e/playwright.config.js`
27
+ - CI runs 2 Playwright workers per job for parallel test execution
28
+ - Local development retains 1 worker for debugging simplicity (though local testing is not the primary workflow)
29
+
30
+ ### CI Pipeline Changes
31
+ - Replace single `test-browser-functional` job with two: `test-browser-functional-core` and `test-browser-functional-extended`
32
+ - Each runs independently and in parallel with all other browser test jobs
33
+ - Each uploads artifacts with distinct names for failure diagnosis
34
+
35
+ ### Why this works
36
+ - Each test already creates its own server instance via `createServer()` with an ephemeral port (port 0)
37
+ - Sessions are per-server, eliminating cross-test state contamination
38
+ - Playwright provides browser context isolation between parallel tests
39
+ - No shared filesystem resources detected in the test suite
40
+
41
+ ## Consequences
42
+
43
+ ### Positive
44
+
45
+ - No CI job exceeds 7 minutes — faster feedback for the push-fix-push workflow
46
+ - More granular job names in CI (functional-core vs functional-extended) aid debugging — agents can immediately see which category of tests failed
47
+ - Parallel workers within jobs further reduce wall-clock time
48
+ - Sets a pattern for future test group splits as the suite grows
49
+
50
+ ### Negative
51
+
52
+ - More CI jobs to monitor (6 browser test job types instead of 5, plus unit tests and build-binary)
53
+ - Artifact names become longer and more numerous
54
+ - If test isolation assumptions prove wrong, parallel execution could introduce flakiness (mitigated by the existing ephemeral-port pattern)
55
+
56
+ ### Neutral
57
+
58
+ - Existing test files require no code changes — only configuration and CI workflow updates
59
+ - The `workers: 2` setting is conservative and can be increased if runners have sufficient resources
60
+
61
+ ## Notes
62
+
63
+ - When any job approaches 6 minutes consistently, split it further
64
+ - When the test suite exceeds 80 tests, re-evaluate the overall split strategy
65
+ - Monitor for flaky tests that may indicate parallel execution issues
@@ -0,0 +1,82 @@
1
+ # ADR-0008: File Browser Architecture
2
+
3
+ ## Status
4
+
5
+ **Accepted**
6
+
7
+ ## Date
8
+
9
+ 2026-02-07
10
+
11
+ ## Context
12
+
13
+ Users access ai-or-die remotely over devtunnels and similar tunnel services. In this environment there is no way to view visual content (images, PDFs), edit files with syntax highlighting, or upload/download files without native desktop tools -- which are not available through the tunnel. The existing `/api/folders` endpoint only lists directories for the working-directory selector and does not expose file contents or metadata.
14
+
15
+ The application needs a web-based file manager that supports browsing, previewing, editing, uploading, and downloading files directly within the terminal UI, without requiring users to install additional software or leave the browser.
16
+
17
+ ## Decision
18
+
19
+ We introduce a file browser feature built on these architectural choices:
20
+
21
+ ### REST API (not WebSocket) for file operations
22
+
23
+ File operations (list, read, write, upload, download) follow a request-response pattern. REST is a natural fit: clients send a request, wait for the response, and render the result. WebSocket would add unnecessary complexity for operations that do not require real-time streaming. The six new endpoints are:
24
+
25
+ | Endpoint | Method | Purpose |
26
+ |----------|--------|---------|
27
+ | `/api/files` | GET | List directory contents (files + directories), paginated |
28
+ | `/api/files/stat` | GET | File metadata (size, modified, MIME category, hash) |
29
+ | `/api/files/content` | GET | Read text file content in a JSON envelope with hash |
30
+ | `/api/files/content` | PUT | Save text file content with optimistic concurrency (hash) |
31
+ | `/api/files/upload` | POST | Upload file as base64 JSON (10 MB limit) |
32
+ | `/api/files/download` | GET | Stream file for download or inline preview |
33
+
34
+ ### Right-docked side panel (not modal)
35
+
36
+ The file browser opens as a docked panel on the right side of the viewport. The terminal remains visible and interactive alongside it. A modal would block terminal access, which defeats the purpose of a file browser in a terminal application. The panel auto-switches to a full-screen overlay on mobile viewports or when the terminal would be squeezed below 80 columns.
37
+
38
+ ### Ace Editor from CDN (not bundled)
39
+
40
+ Text editing uses the Ace Editor loaded from cdnjs, matching the existing pattern of loading xterm.js from unpkg CDN. This avoids adding npm dependencies for a frontend-only library and keeps the server-side `node_modules` minimal. Ace is lazy-loaded on first editor open, with a loading spinner and a 5-second timeout with fallback error.
41
+
42
+ ### Hash-based optimistic concurrency for file saves
43
+
44
+ Every text file response includes an MD5 hash computed via streaming (`crypto.createHash` + `fs.createReadStream`). When saving, the client sends the original hash; the server recomputes and returns 409 Conflict if the file was modified externally. This prevents silent overwrites in multi-user or multi-tab scenarios.
45
+
46
+ ### Extension-based MIME detection with binary heuristic
47
+
48
+ File type detection uses a built-in extension-to-MIME map for known types, supplemented by a null-byte heuristic (reading the first 512 bytes) for unknown extensions. This avoids depending on system-level `file` commands or npm packages like `mime-types`.
49
+
50
+ ### Enhanced validatePath() with symlink resolution
51
+
52
+ The existing `validatePath()` function is extended to resolve symlinks via `fs.realpathSync()` before the `startsWith` check. This eliminates TOCTOU (time-of-check-to-time-of-use) race conditions where a symlink could be swapped between validation and access.
53
+
54
+ ### File utilities extracted to src/utils/file-utils.js
55
+
56
+ File-related utility functions (`getFileInfo`, `computeFileHash`, `isBinaryFile`, `sanitizeFileName`) are extracted into a dedicated module rather than being added inline to `server.js` (already 1507 lines). This keeps the server file focused on routing and makes the utilities independently testable.
57
+
58
+ ### No delete or rename in initial release
59
+
60
+ Destructive file operations are excluded from the MVP to limit the security surface area. Users can still delete or rename files through the terminal. These operations may be added in a follow-up phase.
61
+
62
+ ## Consequences
63
+
64
+ ### Positive
65
+
66
+ - Visual file preview (images, PDFs, JSON, CSV) works over tunnels without native tools
67
+ - Code editing with syntax highlighting and auto-save directly in the browser
68
+ - Drag-drop, file picker, and clipboard paste upload for getting files onto the remote machine
69
+ - Hash-based conflict detection prevents accidental data loss
70
+ - Extracted file utilities are independently testable
71
+
72
+ ### Negative
73
+
74
+ - No real-time file watching -- directory listings are point-in-time snapshots (would require WebSocket, deferred to Phase 2)
75
+ - Ace Editor introduces a CDN dependency for the editing feature (editing is degraded if CDN is unreachable)
76
+ - 10 MB upload limit may be insufficient for large assets (chunked upload deferred to Phase 2)
77
+
78
+ ### Neutral
79
+
80
+ - `/api/folders` and `/api/files` coexist: `/api/folders` lists only directories for the working-directory selector, `/api/files` lists both files and directories for the file browser
81
+ - The same `validatePath()` function secures both old and new endpoints
82
+ - No new npm dependencies are introduced
@@ -26,14 +26,26 @@ Write tests alongside implementation, not after. The workflow:
26
26
  - Use temp directories for file system tests (see `session-store.test.js` pattern)
27
27
  - Test cross-platform behavior: path construction, command resolution, shell detection
28
28
 
29
+ ## CI-Only Testing
30
+
31
+ All testing happens on GitHub Actions runners. No local test runs. Ever.
32
+
33
+ - Local environments are unreliable: missing native modules, stale state, platform differences
34
+ - CI provides fresh, reproducible, cross-platform results every time
35
+ - E2E tests are the only true validation — if they pass on CI, the feature works
36
+
37
+ The workflow: write code → push to branch → open draft PR → CI runs → read results → fix → push again.
38
+
39
+ See `docs/agent-instructions/06-ci-first-testing.md` for the complete CI workflow guide, job map, and debugging playbook.
40
+
29
41
  ## Self-Validation
30
42
 
31
43
  Before committing, every agent must:
32
44
 
33
- 1. Run `npm test` all tests pass
34
- 2. Run `npm start` server boots without errors
35
- 3. Run `scripts/validate.sh` (Linux) or `scripts/validate.ps1` (Windows)
36
- 4. Verify the change doesn't break existing functionality
45
+ 1. Push to branch and open a draft PR to trigger CI
46
+ 2. Verify all CI jobs pass on both ubuntu-latest and windows-latest
47
+ 3. Check `docs/history/` for known issues if any job fails
48
+ 4. Verify the change doesn't break existing functionality (CI confirms this)
37
49
 
38
50
  ## What to Test
39
51
 
@@ -50,9 +62,9 @@ Before committing, every agent must:
50
62
  - Auth middleware behavior
51
63
 
52
64
  ### For Client Changes
53
- - Manual browser testing (create session, select tool, verify output)
54
- - Check mobile responsiveness
55
- - Verify WebSocket reconnection
65
+ - E2E tests via Playwright (verified on CI, never locally)
66
+ - Mobile viewport tests via mobile-iphone and mobile-pixel Playwright projects
67
+ - WebSocket reconnection covered by E2E functional tests
56
68
 
57
69
  ## When Tests Fail
58
70
 
@@ -14,14 +14,13 @@ If you perform a verification task twice, script it. All scripts live in the `sc
14
14
 
15
15
  ### GitHub Actions
16
16
 
17
- The CI pipeline (`.github/workflows/ci.yml`) runs on every push and PR:
18
-
19
- 1. **Matrix**: Runs on both `ubuntu-latest` and `windows-latest`
20
- 2. **Install**: `npm ci`
21
- 3. **Lint**: ESLint check
22
- 4. **Test**: `npm test` with coverage reporting
23
- 5. **Audit**: `npm audit` for security vulnerabilities
24
- 6. **Docs Check**: Verify docs/ structure exists
17
+ The CI pipeline (`.github/workflows/ci.yml`) runs on every push and PR. It runs 8 job types in parallel across ubuntu-latest and windows-latest (16 total jobs):
18
+
19
+ - **Unit tests**: `npm test` + `npm audit`
20
+ - **Browser E2E tests**: 6 Playwright job types (golden-path, functional-core, functional-extended, mobile, visual-regression, new-features)
21
+ - **Binary build**: SEA binary compilation + smoke tests
22
+
23
+ See `06-ci-first-testing.md` for the full CI job map, artifact details, and debugging workflow. CI is the only authority on whether code works (see ADR-0008 for the parallelization strategy).
25
24
 
26
25
  ### Release Pipeline
27
26
 
@@ -0,0 +1,63 @@
1
+ # Handoff Protocol
2
+
3
+ ## The Golden Rule
4
+
5
+ Every session ends with a cleaner repo than it started. If you touched it, you documented it. If you broke it, you fixed it. If you couldn't finish, you left a trail.
6
+
7
+ ## Pre-Handoff Checklist
8
+
9
+ Before ending any work session, verify:
10
+
11
+ 1. **All CI jobs pass.** Push to your branch and check GitHub Actions. Both `ubuntu-latest` and `windows-latest` must be green. Do not hand off a red build.
12
+ 2. **Documentation is updated.** Specs in `docs/specs/` match the current code. ADRs are written for any architectural decisions made during the session.
13
+ 3. **No orphaned work-in-progress.** No half-implemented features sitting uncommitted. Everything is either committed and pushed, or explicitly tracked in a GitHub issue.
14
+ 4. **Commit messages explain "why", not just "what".** A future agent reading the git log should understand the reasoning without opening the diff.
15
+ 5. **New patterns and conventions are documented.** If you introduced a new coding pattern, utility, or convention, write it down in the relevant spec or instruction doc.
16
+
17
+ ## Work-in-Progress Protocol
18
+
19
+ When you cannot finish a task:
20
+
21
+ - Create a GitHub issue with full context: what was attempted, where it stopped, what blockers exist, and what the next steps are.
22
+ - Use `[WIP]` prefix in commit messages for incomplete work.
23
+ - List which files are mid-change and what state they are in.
24
+ - Reference relevant specs, ADRs, and CI run links.
25
+ - Never leave broken tests on main. If your work breaks tests, either fix them or revert before ending.
26
+
27
+ ## Clean Commit Hygiene
28
+
29
+ - Follow Conventional Commits: `feat:`, `fix:`, `docs:`, `test:`, `chore:`, `refactor:`.
30
+ - One concern per commit. Do not mix a bug fix with a feature addition.
31
+ - Reference GitHub issues in the message: `fix: resolve WebSocket race in image upload (#42)`.
32
+ - Commit messages should be self-contained. Another agent reading the git log should understand what happened and why without reading the diff.
33
+
34
+ ## Session Context Dump
35
+
36
+ What to leave behind for the next agent:
37
+
38
+ - Updated specs in `docs/specs/` reflecting any behavior changes.
39
+ - Research findings documented in the relevant ADR or spec.
40
+ - Error patterns discovered during debugging added to `docs/history/`.
41
+ - Decisions made and their rationale recorded in ADRs.
42
+ - If you modified the CI pipeline, document what changed and why.
43
+
44
+ ## Log What You Solved
45
+
46
+ When you encounter and solve a problem, document it in `docs/history/`. LLMs do not carry memories between sessions -- written docs are the only institutional memory. Every solved problem that is not documented is a problem that will be solved again.
47
+
48
+ See `07-docs-hygiene.md` for the history entry format and full guidelines. Before debugging any issue, always check `docs/history/` first.
49
+
50
+ ## Anti-Patterns
51
+
52
+ Do NOT do any of these:
53
+
54
+ - Leave vague commit messages like "Made some changes" or "Updated stuff".
55
+ - Push uncommitted or unstaged work.
56
+ - Leave broken tests and move on.
57
+ - Make architectural decisions without writing an ADR.
58
+ - Solve a problem without documenting the solution.
59
+ - Skip spec updates when behavior changes.
60
+ - Assume the next agent will "figure it out".
61
+ - Delete or disable tests to make CI pass.
62
+ - Commit secrets, API keys, tokens, or `.env` files. Check `git diff --staged` for sensitive data before every commit.
63
+ - Expand scope beyond what was asked. If you discover adjacent issues, file them as separate GitHub issues. Do not expand scope without explicit approval.
@@ -0,0 +1,170 @@
1
+ # Defensive Coding
2
+
3
+ ## Validate at Boundaries
4
+
5
+ Trust nothing that crosses a system boundary. Every REST endpoint, WebSocket handler, and bridge method should validate its inputs before processing.
6
+
7
+ Where boundaries exist in this codebase:
8
+
9
+ - REST API handlers in `src/server.js` -- validate request params, body, headers
10
+ - WebSocket message handlers -- validate `type` field, required fields per message type
11
+ - Bridge methods (`startSession`, `sendInput`, `resize`) -- validate sessionId exists, dimensions are positive integers
12
+ - Client-to-server messages -- validate session ownership, check session is active
13
+
14
+ Pattern:
15
+
16
+ ```javascript
17
+ // Bad
18
+ handleMessage(wsId, message) {
19
+ const session = this.sessions.get(message.sessionId);
20
+ session.bridge.sendInput(message.data); // crashes if session doesn't exist
21
+ }
22
+
23
+ // Good
24
+ handleMessage(wsId, message) {
25
+ if (!message.sessionId) {
26
+ return this.sendError(wsId, 'Missing sessionId');
27
+ }
28
+ const session = this.sessions.get(message.sessionId);
29
+ if (!session) {
30
+ return this.sendError(wsId, `Session '${message.sessionId}' not found`);
31
+ }
32
+ if (!session.active) {
33
+ return this.sendError(wsId, `Session '${message.sessionId}' is not active`);
34
+ }
35
+ session.bridge.sendInput(message.data);
36
+ }
37
+ ```
38
+
39
+ ## Error Messages Are UI
40
+
41
+ Error messages are read by other agents trying to debug. Make them actionable.
42
+
43
+ Every error message should answer three questions:
44
+
45
+ 1. What went wrong?
46
+ 2. What was expected?
47
+ 3. What should be done about it?
48
+
49
+ ```javascript
50
+ // Bad
51
+ throw new Error('Invalid');
52
+ throw new Error('Not found');
53
+ throw new Error('Failed');
54
+
55
+ // Good
56
+ throw new Error(`Session '${sessionId}' not found. Available sessions: [${[...sessions.keys()].join(', ')}]`);
57
+ throw new Error(`Bridge '${toolId}' is not available. Run 'which ${command}' to verify installation. Searched paths: ${searchPaths.join(', ')}`);
58
+ throw new Error(`WebSocket message missing required field 'type'. Received: ${JSON.stringify(message)}`);
59
+ ```
60
+
61
+ ## Cross-Platform Landmines
62
+
63
+ This codebase runs on both Windows and Linux. Every line of code that touches the filesystem, spawns a process, or handles paths must account for both.
64
+
65
+ ### Paths
66
+
67
+ - ALWAYS use `path.join()`, never string concatenation with `/` or `\\`
68
+ - Use `os.homedir()`, never `process.env.HOME` (undefined on Windows)
69
+ - File paths are case-insensitive on Windows, case-sensitive on Linux
70
+ - Use `path.resolve()` to normalize paths before comparison
71
+
72
+ ### Process Spawning
73
+
74
+ - `where` on Windows, `which` on Linux -- check `process.platform`
75
+ - Windows uses ConPTY, Linux uses standard PTY -- different buffering behavior
76
+ - Executable extensions: `.exe`, `.cmd` on Windows, none on Linux
77
+ - Shell: `cmd.exe` or `powershell.exe` on Windows, `bash` or `sh` on Linux
78
+
79
+ ### Line Endings
80
+
81
+ - Never match output with exact strings -- use `.includes()` or `.trim()`
82
+ - Windows may inject `\r\n` where Linux gives `\n`
83
+ - PTY output may contain ANSI escape sequences -- strip them before comparing
84
+
85
+ ### The ConPTY Quirks
86
+
87
+ - Writes larger than 4096 bytes can overflow the ConPTY buffer on Windows
88
+ - Solution: chunked writes with delays (see `base-bridge.js` chunked write pattern)
89
+ - ConPTY may echo input back -- don't assume output is only from the spawned process
90
+
91
+ ## Async Safety
92
+
93
+ Node.js is async-first. Unhandled promise rejections crash the process.
94
+
95
+ Rules:
96
+
97
+ - Every `async` function must have try-catch at the top level
98
+ - Every `.then()` chain must have a `.catch()`
99
+ - Event handlers that call async code must wrap in try-catch
100
+ - Use the spawn watchdog pattern from `base-bridge.js`: set a timer when spawning a process, kill it if no output arrives within 30 seconds
101
+
102
+ ```javascript
103
+ // Bad -- unhandled rejection if startSession throws
104
+ ws.on('message', (data) => {
105
+ const msg = JSON.parse(data);
106
+ this.startSession(msg.sessionId);
107
+ });
108
+
109
+ // Good
110
+ ws.on('message', (data) => {
111
+ try {
112
+ const msg = JSON.parse(data);
113
+ this.startSession(msg.sessionId).catch(err => {
114
+ console.error(`Failed to start session ${msg.sessionId}:`, err);
115
+ this.sendError(wsId, err.message);
116
+ });
117
+ } catch (err) {
118
+ console.error('Failed to parse WebSocket message:', err);
119
+ }
120
+ });
121
+ ```
122
+
123
+ ## Fail Fast, Fail Loud
124
+
125
+ Silent failures are the worst kind. They create bugs that surface hours or sessions later, with no trail.
126
+
127
+ - Assert preconditions at function entry -- don't wait until line 50 to discover the input was invalid
128
+ - Log errors with full context before re-throwing: what function, what inputs, what state
129
+ - Never `catch` and silently swallow: `catch (err) { /* ignore */ }` -- this is forbidden
130
+ - If something "shouldn't happen," make it throw, not silently return null
131
+
132
+ ```javascript
133
+ // Bad -- silent null propagation
134
+ function getSession(id) {
135
+ return sessions.get(id); // returns undefined silently
136
+ }
137
+
138
+ // Good -- fail fast with context
139
+ function getSession(id) {
140
+ const session = sessions.get(id);
141
+ if (!session) {
142
+ throw new Error(`getSession: no session with id '${id}'. Active sessions: ${sessions.size}`);
143
+ }
144
+ return session;
145
+ }
146
+ ```
147
+
148
+ ## The "Fresh Machine" Test
149
+
150
+ Before considering any code complete, ask yourself: "Would this work on a brand new GitHub Actions runner with nothing pre-installed except Node.js 22?"
151
+
152
+ This means:
153
+
154
+ - No reliance on globally installed tools (unless you check for them and give a clear error)
155
+ - No hardcoded paths that only exist on your dev machine
156
+ - No cached `node_modules` assumptions -- `npm ci` installs from scratch
157
+ - No file system state left over from previous runs
158
+ - No environment variables that aren't set in CI
159
+
160
+ If the answer is "maybe," add a runtime check:
161
+
162
+ ```javascript
163
+ const commandPath = await this.findCommandAsync();
164
+ if (!commandPath) {
165
+ throw new Error(
166
+ `${this.toolName} CLI not found. Searched: ${this.searchPaths.join(', ')}. ` +
167
+ `Install ${this.toolName} or add it to PATH.`
168
+ );
169
+ }
170
+ ```