feat(types): enforce typecheck for frontend#144
Merged
Conversation
each empty `catch {}` in public/app.ts now has a \u22655 word inline
comment explaining why swallowing the error is correct (teardown race,
quota limit, private-mode storage, best-effort discovery, etc.).
no behavior change. addresses antipattern-scan finding #5
(error hiding) on PR #143's followup branch.
types primitive-only function signatures in public/app.ts: quick-cmd ops (4), session/recents helpers (2), error/url validation (3), machine list mutators (3), small UI fns (8). NOTE: public/ is not yet included in tsconfig's typecheck \u2014 these annotations are documentation today, but become enforced when frontend typechecking is enabled. behavior unchanged; bundle still parses clean (`bun build public/app.ts`). addresses subset 6a of antipattern-scan finding #6 (untyped function parameters). 26 remaining will be split across follow-up commits (tracer helpers, controller factory opts, renderer/data shapes).
moves the `__wfTrace*` family + `__wf_lastCrash` capture out of app.ts
into a self-contained, fully-typed module. `window` mutations are now
declared via `declare global { interface Window { ... } }` instead of
`(window as any)` casts.
app.ts: 4470 \u2192 4352 LOC (-118)
app-debug.ts: new, 209 LOC, full type annotations
resolves antipattern-scan finding #4 (action at a distance via window
mutations) AND partial #6b (6 tracer fns now typed). bundle still parses
clean; unit tests green (1150 pass).
the localStorage gate (`wolfpackDebug = "1"`) is preserved \u2014 helpers
no-op when disabled, no `window.__wf*` globals installed. follow-up
could explore build-time exclusion via bundler defines, but not needed
for the antipattern fix.
the `if (connState === "displaced")` branch at setConnState was unreachable \u2014 b85a00e (Mar 2026) deleted the `takeBackControl` helper the branch referenced, AND rerouted the displaced disconnect path through `showDesktopConflictOverlay()` instead. the only caller that WOULD have triggered this branch (`onDisconnected` w/ classify=='displaced') now calls `showDesktopConflictOverlay()` directly (line 2728). confirmed: setConnState is only called with 'live', 'reconnecting', 'offline', 'session-ended' \u2014 never 'displaced'. takeBackControl would have been a runtime ReferenceError if reached. cleanup, not behavior change. removes 9 LOC of dead code and one of the 24 TS2304 errors blocking public/ typecheck.
declares the globals that the frontend bundle relies on but cannot import directly because they're installed via script tags: - WP \u2192 typed via src/wolfpack-client-lib.ts (source of truth) - Terminal \u2192 ghostty-web (`any` for now; refine in follow-up) - FitAddon \u2192 ghostty-web (`any` for now; refine in follow-up) - window.ghosttyReady, .wasmFailed, .createIsolatedGhostty removes the need for ad-hoc `(window as any).X` casts at every callsite and kills 24 TS2304 errors that would surface when public/ is added to tsconfig include (planned in follow-up commits). groundwork for relaxed-strict frontend typecheck (Option B in plans/PLAN-typecheck-frontend.md \u2014 plan file is gitignored).
four function signatures had params marked required even though all callers omitted them and the bodies handled `undefined` correctly. mark them optional with `?` so tsc accepts the actual calling convention: api(path, opts?, machineUrl?) showView(name, skipAnimation?) initTerminal(cached?) showProjectPicker(machineUrl?) closeDrawer(instant?) (was typed required in earlier commit) also: backBtn.onclick() in app.ts:3939 calls the handler with 0 args programmatically. DOM `onclick` signature expects MouseEvent \u2014 pass a synthetic one. Handlers ignore the event arg in practice. resolves 33 TS2554 errors (87 remaining; mostly TS2339 "property doesn't exist" which is B3). bundle clean, 1150 tests pass.
`document.getElementById()` returns the generic HTMLElement type, which
doesn't have `.value`, `.placeholder`, `.type`, `.checked` properties.
cast at each declaration to the actual element subtype:
- msg-input (textarea) \u2192 HTMLTextAreaElement
- mobile-kb-proxy, *-name-input,
agent-add-input, setting-* (input) \u2192 HTMLInputElement
also declares state.currentRalphCleanup/AuditFix in app-state.ts so
app-ralph.ts can read+write them with proper types instead of relying
on dynamic property assignment.
resolves 32 TS2339 errors (`Property X does not exist on HTMLElement`
+ 8 `Property does not exist on state`). bundle clean, 1150 tests pass.
remaining public/ errors: 41 (mostly EventTarget/Element narrowing,
opts:\{\} on createReconnector, and a few real type mismatches).
- typed createReconnector opts (interface ReconnectorOpts) \u2014 4 props
- fixed triageUi() return type: was string, is { dot, card, label, title }
- cast textContent number assignments to String() (5 sites in debug panel)
- narrowed errorMessage(err: unknown) with proper in-check
- typed api() local data as `any` (response shape varies per endpoint)
- augmented Error w/ .status + .data fields for /api/ rejections
- cast Element \u2192 HTMLElement at .dataset/.classList/.onclick callsites
- cast e.target \u2192 HTMLElement | Element for EventTarget narrowing
- cast unknown \u2192 boolean/string in initSettings DOM write boundary
- cast urlBase64ToUint8Array result \u2192 BufferSource for PushManager
- synthetic event in backBtn.onclick() switched MouseEvent \u2192 PointerEvent
result: ALL public/ tsc errors resolved against the relaxed config.
remaining 12 errors are pre-existing in src/ and tests/ \u2014 not from
this branch. bundle clean, 1150 unit tests pass.
`BrokerClient.unsubscribe()` returns `Promise<void>` but the `BrokerClientApi` interface in broker-backend.ts declared `Promise<ControlResponse | null>`. unify to the impl's contract: - update interface return type to `Promise<void>` - update FakeBrokerClient in tests/unit/broker-backend.test.ts to match confirmed safe: the only caller (broker-backend.ts:401) does `.unsubscribe(id).catch(...)` and never reads the response. also: fix TS7019 in tests/e2e/grid.e2e.ts \u2014 rest param `args` typed explicitly as `unknown[]` in monkey-patched reconnect callback. resolves 8 pre-existing tsc errors. `tsc -p .` now zero errors; 1150 tests pass.
separate tsconfig for public/*.ts so the frontend can be typechecked independently. uses relaxed flags (`noImplicitAny: false`, `strictNullChecks: false`) for now \u2014 strict pass is deferred (see PLAN-typecheck-frontend.md \u2192 S1/S2 in a future branch). with this: bunx tsc --noEmit -p . \u2192 0 errors (strict) bunx tsc --noEmit -p public/ \u2192 0 errors (relaxed) devs running typecheck locally now have a meaningful frontend gate even before CI picks it up (added in follow-up commit).
adds:
- new CI step "Typecheck (root + public/)" running
`tsc --noEmit -p .` and `tsc --noEmit -p public/` sequentially,
placed before `bun test` so type errors surface fast
- `bun run typecheck` npm script for local invocation (matches CI)
both must pass for the workflow to succeed. catches:
- any strict tsc violation in src/, tests/, scripts/ (strict config)
- any relaxed tsc violation in public/ (no implicit any check or
null-check enforcement, but everything else: TS2339 missing props,
TS2554 wrong args, TS2322 mismatches \u2014 all enforced)
local devs run `bun run typecheck` to validate the same gate.
…setConnState - errorMessage: replace triple-cast chain with single Record<string, unknown> intermediate — cleaner narrowing, same behavior - setConnState: remove meaningless literal union with string (string subsumes the literals; they were documentation noise)
… any, add structural types - add typescript@^6.0.3 as pinned devDependency - ci typecheck uses bun run typecheck (not duplicated bunx commands) - replace ghostty Terminal/FitAddon any globals with structural interfaces - make diagnostic types readonly where mutation isn't needed - api() is now generic with unknown internals instead of any - add return type annotations on touched module-level functions - add PtySocketClient, Reconnector, PtyTerminalController interfaces - replace (window as any) with properly declared window globals - replace this: any event handlers with this: HTMLInputElement - document public/tsconfig.json strictness gap as intentional migration debt
ghostty-web's dirty-cell tracking may think it already painted while the canvas was hidden (opacity:0 during hydration). When hydration.finish() reveals the terminal, it now calls forceRepaint() via onReveal callback to force a full canvas repaint, fixing the stale/blank grid cell issue that previously required a manual resize to recover.
…InitialHydrationController, fix formatSnapshotTtl coercion
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
summary
Enables typechecking for the frontend and fixes all resulting errors under a relaxed public/ config.
included
verification
{"dir":"/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-boundary-3fgaUT/project","value":"/etc/passwd","ts":"2026-05-16T20:23:21.753Z","level":"warn","component":"ralph","msg":"parseRalphLog: rejected unsafe planFile"}
{"dir":"/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-boundary-4C4s2b/project","value":"../../etc/passwd","ts":"2026-05-16T20:23:21.754Z","level":"warn","component":"ralph","msg":"parseRalphLog: rejected unsafe planFile"}
{"endpoint":"https://fcm.googleapis.com/fcm/send/test-1778963001783","ts":"2026-05-16T20:23:21.784Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/fcm/send/test-1778963001783","ts":"2026-05-16T20:23:21.784Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/fcm/send/test-1778963001783","ts":"2026-05-16T20:23:21.785Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-0-1778963001788","ts":"2026-05-16T20:23:21.788Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-1-1778963001788","ts":"2026-05-16T20:23:21.789Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-2-1778963001789","ts":"2026-05-16T20:23:21.789Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-3-1778963001789","ts":"2026-05-16T20:23:21.789Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-4-1778963001789","ts":"2026-05-16T20:23:21.789Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-5-1778963001789","ts":"2026-05-16T20:23:21.789Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-6-1778963001789","ts":"2026-05-16T20:23:21.790Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-7-1778963001790","ts":"2026-05-16T20:23:21.790Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-8-1778963001790","ts":"2026-05-16T20:23:21.790Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-9-1778963001790","ts":"2026-05-16T20:23:21.790Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-10-1778963001790","ts":"2026-05-16T20:23:21.790Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-11-1778963001790","ts":"2026-05-16T20:23:21.790Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-12-1778963001790","ts":"2026-05-16T20:23:21.790Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-13-1778963001790","ts":"2026-05-16T20:23:21.791Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-14-1778963001791","ts":"2026-05-16T20:23:21.791Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-15-1778963001791","ts":"2026-05-16T20:23:21.791Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-16-1778963001791","ts":"2026-05-16T20:23:21.792Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-17-1778963001792","ts":"2026-05-16T20:23:21.793Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-18-1778963001793","ts":"2026-05-16T20:23:21.793Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-19-1778963001793","ts":"2026-05-16T20:23:21.793Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-0-1778963001788","ts":"2026-05-16T20:23:21.793Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/cap-test-0-1778963001788","ts":"2026-05-16T20:23:21.794Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-1-1778963001788","ts":"2026-05-16T20:23:21.794Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-2-1778963001789","ts":"2026-05-16T20:23:21.794Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-3-1778963001789","ts":"2026-05-16T20:23:21.796Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-4-1778963001789","ts":"2026-05-16T20:23:21.796Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-5-1778963001789","ts":"2026-05-16T20:23:21.796Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-6-1778963001789","ts":"2026-05-16T20:23:21.796Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-7-1778963001790","ts":"2026-05-16T20:23:21.797Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-8-1778963001790","ts":"2026-05-16T20:23:21.797Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-9-1778963001790","ts":"2026-05-16T20:23:21.797Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-10-1778963001790","ts":"2026-05-16T20:23:21.797Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-11-1778963001790","ts":"2026-05-16T20:23:21.797Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-12-1778963001790","ts":"2026-05-16T20:23:21.798Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-13-1778963001790","ts":"2026-05-16T20:23:21.798Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-14-1778963001791","ts":"2026-05-16T20:23:21.798Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-15-1778963001791","ts":"2026-05-16T20:23:21.798Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-16-1778963001791","ts":"2026-05-16T20:23:21.798Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-17-1778963001792","ts":"2026-05-16T20:23:21.798Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-18-1778963001793","ts":"2026-05-16T20:23:21.799Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/cap-test-19-1778963001793","ts":"2026-05-16T20:23:21.799Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/prune-test-1778963001799","ts":"2026-05-16T20:23:21.800Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/prune-test-1778963001799","ts":"2026-05-16T20:23:21.800Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/ralph-test-1778963001800","ts":"2026-05-16T20:23:21.800Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/ralph-test-1778963001800","ts":"2026-05-16T20:23:21.804Z","level":"info","component":"push","msg":"push subscription removed"}
{"error":"Public key is not valid for specified curve","ts":"2026-05-16T20:23:21.804Z","level":"warn","component":"push","msg":"push send error"}
{"endpoint":"https://fcm.googleapis.com/ralph-classify-1778963001804","ts":"2026-05-16T20:23:21.804Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/ralph-classify-1778963001804","ts":"2026-05-16T20:23:21.805Z","level":"info","component":"push","msg":"push subscription removed"}
{"endpoint":"https://fcm.googleapis.com/ralph-prune-1778963001805","ts":"2026-05-16T20:23:21.805Z","level":"info","component":"push","msg":"push subscription added"}
{"endpoint":"https://fcm.googleapis.com/ralph-prune-1778963001805","ts":"2026-05-16T20:23:21.805Z","level":"info","component":"push","msg":"push subscription removed"}
{"path":"/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-sandbox-test-316aZ7/PLAN.md","error":"ENOENT: no such file or directory, open '/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-sandbox-test-316aZ7/PLAN.md'","ts":"2026-05-16T20:23:21.814Z","level":"warn","component":"ralph","msg":"failed to read plan file"}
{"path":"/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-sandbox-test-rulgLk/PLAN.md","error":"ENOENT: no such file or directory, open '/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-sandbox-test-rulgLk/PLAN.md'","ts":"2026-05-16T20:23:21.815Z","level":"warn","component":"ralph","msg":"failed to read plan file"}
{"path":"/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-sandbox-test-7IBJSp/PLAN.md","error":"ENOENT: no such file or directory, open '/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-sandbox-test-7IBJSp/PLAN.md'","ts":"2026-05-16T20:23:21.815Z","level":"warn","component":"ralph","msg":"failed to read plan file"}
{"worktreePath":"/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-ZEhOG7","error":"Command failed: git worktree remove /private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-ZEhOG7\nfatal: '/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-ZEhOG7' is a main working tree\n","ts":"2026-05-16T20:23:22.008Z","level":"warn","component":"worktree","msg":"removeWorktree: graceful remove failed; forcing worktree removal (uncommitted changes may be lost)"}
{"worktreePath":"/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-BnG6u5","error":"Command failed: git worktree remove /private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-BnG6u5\nfatal: '/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-BnG6u5' is a main working tree\n","ts":"2026-05-16T20:23:22.225Z","level":"warn","component":"worktree","msg":"removeWorktree: graceful remove failed; forcing worktree removal (uncommitted changes may be lost)"}
{"worktreePath":"/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-vVOXN1","error":"Command failed: git worktree remove /private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-vVOXN1\nfatal: '/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-vVOXN1' is a main working tree\n","ts":"2026-05-16T20:23:22.423Z","level":"warn","component":"worktree","msg":"removeWorktree: graceful remove failed; forcing worktree removal (uncommitted changes may be lost)"}
{"worktreePath":"/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-RJ34fH","error":"Command failed: git worktree remove /private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-RJ34fH\nfatal: '/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-RJ34fH' is a main working tree\n","ts":"2026-05-16T20:23:22.647Z","level":"warn","component":"worktree","msg":"removeWorktree: graceful remove failed; forcing worktree removal (uncommitted changes may be lost)"}
{"worktreePath":"/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-RJ34fH/.wolfpack/worktrees/plan-restart","error":"Command failed: git worktree remove /private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-RJ34fH/.wolfpack/worktrees/plan-restart\nfatal: '/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-RJ34fH/.wolfpack/worktrees/plan-restart' contains modified or untracked files, use --force to delete it\n","ts":"2026-05-16T20:23:22.679Z","level":"warn","component":"worktree","msg":"removeWorktree: graceful remove failed; forcing worktree removal (uncommitted changes may be lost)"}
{"worktreePath":"/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-HU8xRH","error":"Command failed: git worktree remove /private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-HU8xRH\nfatal: '/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-HU8xRH' is a main working tree\n","ts":"2026-05-16T20:23:23.090Z","level":"warn","component":"worktree","msg":"removeWorktree: graceful remove failed; forcing worktree removal (uncommitted changes may be lost)"}
{"worktreePath":"/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-84FjOk","error":"Command failed: git worktree remove /private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-84FjOk\nfatal: '/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-84FjOk' is a main working tree\n","ts":"2026-05-16T20:23:23.423Z","level":"warn","component":"worktree","msg":"removeWorktree: graceful remove failed; forcing worktree removal (uncommitted changes may be lost)"}
{"worktreePath":"/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-INOugD","error":"Command failed: git worktree remove /private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-INOugD\nfatal: '/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/ralph-wt-test-INOugD' is a main working tree\n","ts":"2026-05-16T20:23:23.831Z","level":"warn","component":"worktree","msg":"removeWorktree: graceful remove failed; forcing worktree removal (uncommitted changes may be lost)"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer response from peer1: missing 'loops' key"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer response from peer1: missing 'loops' key"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer response from peer1: missing 'loops' key"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer response from peer1: missing 'loops' key"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer response from peer1: 'loops' is not an array"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer response from peer1: 'loops' is not an array"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer response from peer1: 'loops' is not an array"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer loop entry from peer1: not an object, skipping"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer loop entry from peer1: not an object, skipping"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer loop entry from peer1: not an object, skipping"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer loop entry from peer1: missing 'project', skipping"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer loop entry from peer1: missing 'project', skipping"}
{"ts":"2026-05-16T20:23:23.918Z","level":"warn","component":"routes","msg":"malformed peer loop entry from peer1: not an object, skipping"}
{"name":"newone","id":"550e8400-e29b-41d4-a716-446655440000","cwd":"/tmp/proj","cmd":"shell","ts":"2026-05-16T20:23:25.050Z","level":"info","component":"broker-backend","msg":"session created"}
{"name":"live","key":"F99","ts":"2026-05-16T20:23:25.055Z","level":"warn","component":"broker-backend","msg":"sendKey: unknown key"}
{"name":"live","id":"550e8400-e29b-41d4-a716-446655440000","error":"transport error","ts":"2026-05-16T20:23:25.056Z","level":"warn","component":"broker-backend","msg":"subscribe rpc failed; unwinding"}
{"name":"live","id":"550e8400-e29b-41d4-a716-446655440000","error":"broker exploded","ts":"2026-05-16T20:23:25.059Z","level":"warn","component":"broker-backend","msg":"subscribe rpc failed; unwinding"}
{"name":"live","id":"550e8400-e29b-41d4-a716-446655440000","error":"broker exploded","ts":"2026-05-16T20:23:25.062Z","level":"warn","component":"broker-backend","msg":"subscribe rpc failed; unwinding"}
{"name":"live","id":"550e8400-e29b-41d4-a716-446655440000","error":"transport error","ts":"2026-05-16T20:23:25.063Z","level":"warn","component":"broker-backend","msg":"subscribe rpc failed; unwinding"}
{"name":"new-sess","backend":"broker","ts":"2026-05-16T20:23:25.096Z","level":"info","component":"backend","msg":"session created via router"}
{"socketPath":"/tmp","ts":"2026-05-16T20:23:25.097Z","level":"info","component":"backend","msg":"broker recovered"}
{"worktreePath":"/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/wt-test-OOlu0z/.wolfpack/worktrees/30-plan-copy","error":"Command failed: git worktree remove /private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/wt-test-OOlu0z/.wolfpack/worktrees/30-plan-copy\nfatal: '/private/var/folders/5q/bkddpjlx3tz5l_s3k5bgk4m80000gn/T/wt-test-OOlu0z/.wolfpack/worktrees/30-plan-copy' contains modified or untracked files, use --force to delete it\n","ts":"2026-05-16T20:23:29.090Z","level":"warn","component":"worktree","msg":"removeWorktree: graceful remove failed; forcing worktree removal (uncommitted changes may be lost)"}
�[1m
Attempting fixes...�[0m
�[32m✓�[0m fixed: devDir
�[1m
Attempting fixes...�[0m
�[32m✓�[0m fixed: devDir
�[1m
Attempting fixes...�[0m
�[31m✗�[0m fix failed: first — boom
�[32m✓�[0m fixed: second
�[1m Dependencies�[0m
�[32m✓�[0m tailscale�[2m — 1.94.2�[0m
�[32m✓�[0m tailscale connected�[2m — sgt.tail03f8e8.ts.net�[0m
�[32m✓�[0m shell�[2m — /bin/zsh�[0m
�[1m Config�[0m
�[32m✓�[0m ~/.wolfpack/�[2m — exists�[0m
�[32m✓�[0m config.json�[2m — port=18790, devDir=/Users/home/Dev�[0m
�[32m✓�[0m devDir�[2m — /Users/home/Dev�[0m
�[1m Service�[0m
�[32m✓�[0m service installed�[2m — launchd�[0m
�[32m✓�[0m service running�[2m — active�[0m
�[32m✓�[0m port 18790�[2m — in use, service active�[0m
�[1m Connectivity�[0m
�[32m✓�[0m localhost�[2m — v1.6.3�[0m
�[32m✓�[0m tailscale hostname�[2m — sgt.tail03f8e8.ts.net�[0m
�[1m Binary�[0m
�[32m✓�[0m binary�[2m — 58.4MB�[0m
�[32m✓�[0m binary executable�[2m — yes�[0m
�[32m✓�[0m codesign�[2m — valid�[0m
�[1m Broker�[0m
�[32m✓�[0m broker binary�[2m — /Users/home/.wolfpack/bin/wolfpack-broker�[0m
�[32m✓�[0m broker handshake�[2m — list_sessions ok (6ms)�[0m
�[1m Environment�[0m
�[32m✓�[0m PATH includes /opt/homebrew/bin�[2m — yes�[0m
�[32m✓�[0m PATH includes /usr/local/bin�[2m — yes�[0m
�[32m✓�[0m HOME�[2m — /Users/home�[0m
�[1m Logs�[0m
�[32m✓�[0m wolfpack.log�[2m — 436KB, modified 1m ago�[0m
�[33m⚠�[0m recent errors�[2m — 64 error(s) in last 100 lines�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[32m Result: 20/24 passed�[0m�[33m, 4 warning(s)�[0m
�[1m Dependencies�[0m
�[32m✓�[0m tailscale�[2m — 1.94.2�[0m
�[32m✓�[0m tailscale connected�[2m — sgt.tail03f8e8.ts.net�[0m
�[32m✓�[0m shell�[2m — /bin/zsh�[0m
�[1m Config�[0m
�[32m✓�[0m ~/.wolfpack/�[2m — exists�[0m
�[32m✓�[0m config.json�[2m — port=18790, devDir=/Users/home/Dev�[0m
�[32m✓�[0m devDir�[2m — /Users/home/Dev�[0m
�[1m Service�[0m
�[32m✓�[0m service installed�[2m — launchd�[0m
�[32m✓�[0m service running�[2m — active�[0m
�[32m✓�[0m port 18790�[2m — in use, service active�[0m
�[1m Connectivity�[0m
�[32m✓�[0m localhost�[2m — v1.6.3�[0m
�[32m✓�[0m tailscale hostname�[2m — sgt.tail03f8e8.ts.net�[0m
�[1m Binary�[0m
�[32m✓�[0m binary�[2m — 58.4MB�[0m
�[32m✓�[0m binary executable�[2m — yes�[0m
�[32m✓�[0m codesign�[2m — valid�[0m
�[1m Broker�[0m
�[32m✓�[0m broker binary�[2m — /Users/home/.wolfpack/bin/wolfpack-broker�[0m
�[32m✓�[0m broker handshake�[2m — list_sessions ok (2ms)�[0m
�[1m Environment�[0m
�[32m✓�[0m PATH includes /opt/homebrew/bin�[2m — yes�[0m
�[32m✓�[0m PATH includes /usr/local/bin�[2m — yes�[0m
�[32m✓�[0m HOME�[2m — /Users/home�[0m
�[1m Logs�[0m
�[32m✓�[0m wolfpack.log�[2m — 436KB, modified 1m ago�[0m
�[33m⚠�[0m recent errors�[2m — 64 error(s) in last 100 lines�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[32m Result: 20/24 passed�[0m�[33m, 4 warning(s)�[0m
�[1m Dependencies�[0m
�[32m✓�[0m tailscale�[2m — 1.94.2�[0m
�[32m✓�[0m tailscale connected�[2m — sgt.tail03f8e8.ts.net�[0m
�[32m✓�[0m shell�[2m — /bin/zsh�[0m
�[1m Config�[0m
�[32m✓�[0m ~/.wolfpack/�[2m — exists�[0m
�[32m✓�[0m config.json�[2m — port=18790, devDir=/Users/home/Dev�[0m
�[32m✓�[0m devDir�[2m — /Users/home/Dev�[0m
�[1m Service�[0m
�[32m✓�[0m service installed�[2m — launchd�[0m
�[32m✓�[0m service running�[2m — active�[0m
�[32m✓�[0m port 18790�[2m — in use, service active�[0m
�[1m Connectivity�[0m
�[32m✓�[0m localhost�[2m — v1.6.3�[0m
�[32m✓�[0m tailscale hostname�[2m — sgt.tail03f8e8.ts.net�[0m
�[1m Binary�[0m
�[32m✓�[0m binary�[2m — 58.4MB�[0m
�[32m✓�[0m binary executable�[2m — yes�[0m
�[32m✓�[0m codesign�[2m — valid�[0m
�[1m Broker�[0m
�[32m✓�[0m broker binary�[2m — /Users/home/.wolfpack/bin/wolfpack-broker�[0m
�[32m✓�[0m broker handshake�[2m — list_sessions ok (2ms)�[0m
�[1m Environment�[0m
�[32m✓�[0m PATH includes /opt/homebrew/bin�[2m — yes�[0m
�[32m✓�[0m PATH includes /usr/local/bin�[2m — yes�[0m
�[32m✓�[0m HOME�[2m — /Users/home�[0m
�[1m Logs�[0m
�[32m✓�[0m wolfpack.log�[2m — 436KB, modified 1m ago�[0m
�[33m⚠�[0m recent errors�[2m — 64 error(s) in last 100 lines�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[32m Result: 20/24 passed�[0m�[33m, 4 warning(s)�[0m
�[1m Dependencies�[0m
�[32m✓�[0m tailscale�[2m — 1.94.2�[0m
�[32m✓�[0m tailscale connected�[2m — sgt.tail03f8e8.ts.net�[0m
�[32m✓�[0m shell�[2m — /bin/zsh�[0m
�[1m Config�[0m
�[32m✓�[0m ~/.wolfpack/�[2m — exists�[0m
�[32m✓�[0m config.json�[2m — port=18790, devDir=/Users/home/Dev�[0m
�[32m✓�[0m devDir�[2m — /Users/home/Dev�[0m
�[1m Service�[0m
�[32m✓�[0m service installed�[2m — launchd�[0m
�[32m✓�[0m service running�[2m — active�[0m
�[32m✓�[0m port 18790�[2m — in use, service active�[0m
�[1m Connectivity�[0m
�[32m✓�[0m localhost�[2m — v1.6.3�[0m
�[32m✓�[0m tailscale hostname�[2m — sgt.tail03f8e8.ts.net�[0m
�[1m Binary�[0m
�[32m✓�[0m binary�[2m — 58.4MB�[0m
�[32m✓�[0m binary executable�[2m — yes�[0m
�[32m✓�[0m codesign�[2m — valid�[0m
�[1m Broker�[0m
�[32m✓�[0m broker binary�[2m — /Users/home/.wolfpack/bin/wolfpack-broker�[0m
�[32m✓�[0m broker handshake�[2m — list_sessions ok (1ms)�[0m
�[1m Environment�[0m
�[32m✓�[0m PATH includes /opt/homebrew/bin�[2m — yes�[0m
�[32m✓�[0m PATH includes /usr/local/bin�[2m — yes�[0m
�[32m✓�[0m HOME�[2m — /Users/home�[0m
�[1m Logs�[0m
�[32m✓�[0m wolfpack.log�[2m — 436KB, modified 1m ago�[0m
�[33m⚠�[0m recent errors�[2m — 64 error(s) in last 100 lines�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[2m{"error":"BrokerRequestTimeoutError: broker request 'list_sessions' timed out after 10000ms","ts":"2�[0m
�[32m Result: 20/24 passed�[0m�[33m, 4 warning(s)�[0m — 1162 pass
\Bundled 6 modules in 23ms
wp-test-build.js 203.74 KB (entry point) — pass
\bundling ghostty-web...
bundled ghostty-web → /Users/home/Dev/wolfpack/public/ghostty-web.bundle.js (625.9 KB)
bundling wolfpack-lib...
bundled wolfpack-lib → /Users/home/Dev/wolfpack/public/wolfpack-lib.js (5.6 KB)
bundling app...
bundled app → /Users/home/Dev/wolfpack/public/app.bundle.js (199.0 KB)
generated /Users/home/Dev/wolfpack/src/public-assets.ts (17 files embedded) — pass, committed \
notes
public/ is typechecked with relaxed flags for now. follow-up branch should tighten:
Untracked antipattern report/catalog files were intentionally left out of this PR.