Race-Condition and Redundancy Audit
src/kiss/core, src/kiss/agents/sorcar, src/kiss/server, src/kiss/agents/vscode — branch kiss/wt-1788470827-0b6a10f8, 2026-09-03

This audit hunted two defect classes only — race conditions (any concurrent interleaving that produces wrong behavior: torn reads and writes, check-then-act windows, lost updates, missing locks, async-exception hazards) and redundancies (duplicated logic that has drifted or can drift, duplicate state, dead code). Inconsistencies were explicitly out of scope per the task. A previous audit of the same four directories (2026-09-02, 86 defects) is already merged into this branch's base; this pass therefore concentrated extra scrutiny on the roughly six commits this branch adds on top of it — a rewritten daemon client with stop-on-timeout, a new run_agent timeout, WebUse renderer-hang hardening, installer lock/marker repairs, and a refreshed model catalog — while still sweeping every file end to end.

52defects fixed 36races 12redundancies 4test/lint gaps 34new test files ~60suspicions investigated & rejected 8,047Python tests green* 281/281JS suites green

1. How the audit worked

10 audit agents claude-fable-5 disjoint file ownership 32 fixes failing e2e test first, then root-cause fix 4 reviewers + 1 debugger gpt-5.6-sol, read-only 17 review fixes 4 fix agents claude-fable-5 Verification check --full, 8,047 py + 281 js + 3 cross-boundary fixes by the orchestrator
The pipeline. Every production fix was preceded by a real end-to-end test that failed on the unfixed code (real processes, sockets, git repositories, Docker containers, jsdom DOMs — no mocks of the code under test). Suspected races were confirmed with temporary sub-100 ms random sleeps at the racing statements, removed once confirmed.

Ten audit agents owned disjoint file partitions (core base, core models, sorcar agents, sorcar infrastructure ×2, server core, server web, vscode extension, vscode installer, vscode webview). Four gpt-5.6-sol reviewers then re-derived every claimed fix from the diff, hunted for missed twin sites and introduced bugs, and re-checked the rejected suspicions; a fifth gpt-5.6-sol agent triaged a Python 3.14 segfault seen once during review (verdict: not reproducible, not attributable to these changes; the leaky test fixture it exposed was fixed). Four fix agents closed all 17 review findings, again test-first. The gpt-5.6-sol spend stayed far below the 50% budget cap.

2. Defects found and fixed — audit fan-out (32)

#ClassWhereDefect and fix
1racecore/vscode_config.pysource_shell_env applied RC-sourced keys to os.environ/config without _config_lock; a concurrent API-key save was silently reverted (28/120 stale in the probe). Now holds the lock from RC snapshot through refresh.
2redundancycore/kiss_agent.pyThe agentic loop kept an inline, drifted copy of _print_result (empty results rendered differently per path). All three run modes now share one implementation.
3racecore/models/model.pyCLI stdin writer guarded writes with select.select, which raises for any fd ≥ 1024; the handler swallowed it and closed stdin after zero bytes — in a busy daemon every cc/*/codex/* turn silently got an empty prompt. (Final form of the fix: finding 36.)
4redundancycore/models/MODEL_INFO.jsonThe catalog refresh resurrected three Codex models verified to fail on ChatGPT subscriptions, because the exclusion knowledge lived only in a test. Entries removed. (Root cause: finding 33.)
5racesorcar/sorcar_agent.py, relentless_agent.pyCumulative usage counters (budget_used, tokens, steps) were read-modify-written from the agent thread and server reclaim threads with no lock — the failing test banked only 22,295 of 24,000 increments. One per-agent _usage_lock now guards every writer.
6racesorcar/sorcar_agent.pyIn the fan-out engine, a worker publishing its final usage slot between abandoned-registration and totals summation got its spend banked twice. Registration and summation now happen under one hold of sub_usage_lock.
7redundancysorcar/worktree_sorcar_agent.pyByte-identical exception-wrapping blocks on the direct-fallback and worktree paths merged into one.
8redundancysorcar/sorcar_agent.pyTwo hand-rolled readers of the live executor's spend (an easy field to copy wrong) extracted into _executor_usage().
9racesorcar/daemon_client.pystop_on_timeout discarded a natural completion: a task finishing just as the deadline expired still surfaced as "stopped", losing the finished work. The terminal-status wait now returns a stored success: true result (sound: every daemon stop/cancel/failure path broadcasts success: false). (Hardened in finding 38.)
10redundancysorcar/daemon_client.pyDuplicated oversize-frame error and run-token-guarded stop frame extracted into _frame_limit_error() / _send_stop() — drift there would silently lose the tab-reuse guard.
11redundancysorcar/web_use_tool.pyThe "live killable foreign pid" predicate existed twice, once inverted — a drift hazard whose failure mode is signalling the wrong process. Extracted _killable_live_pid().
12racesorcar/worktree_pool.pyOverlapping discard_all() calls defeated the _discarding boolean: the first caller cleared it while the second still swept, letting a refill publish a spare behind a teardown. Replaced with an _active_discards counter decremented in finally.
13racesorcar/docker_manager.pyFive methods read self.container more than once without the lifecycle lock; racing close() turned orderly refusals into AttributeError, and killed the timed-out-exec reaper thread. One snapshot per method; the reaper treats a closed container as nothing to kill.
14redundancysorcar/docker_manager.pyToken-tagged exec_create duplicated across both Bash paths extracted into _tagged_exec_create().
15redundancysorcar/persistence.pyDuplicated thread-local connection teardown extracted into _close_cached_thread_conn().
16raceserver/task_runner.pyA stop arriving after the worker thread was registered but before thread.start() armed no force-stop watchdog (the liveness check had drifted from the ident-aware helper) — the run then executed untrusted setup forever. Ident-aware liveness plus a watchdog that waits for thread start under STATE_LOCK.
17raceserver/task_runner.pyThe untrusted-code loaders wrapped BaseException — including the stop watchdog's injected KeyboardInterrupt — into task errors, so a stop landing in a getter was reported as a failure and never acknowledged. New _stop_interrupt_wrapped() routes a genuine stop through _cancel_outcome.
18raceserver/server.pyHistory-resume subscribed and broadcast running=true after releasing the state lock; a task ending in that window left viewer tabs spinning forever. The attach now re-verifies the state object's liveness after the optimistic broadcast. (Extended in finding 42.)
19racevscode/src/SorcarSidebarView.tsThree interleavings used voiceWake.running (true while a stopped listener is still dying) as a stand-in for user intent, turning the microphone ON against the user's wishes. A _voiceEnabled intent flag now gates sensitivity restarts and hide/show suspension, cleared on dispose.
20racevscode/src/SorcarTab.tsThe one-time tips popup was claimed with exists-then-write; two windows activating together both won. Atomic writeFileSync(..., {flag:'wx'}).
21racescripts/install.shA skip-worktree pin left by an old installer bricked the curl bootstrap forever (pull, checkout, stash and reset all fail). The bootstrap now clears the pin first.
22redundancyvscode/A committed rescued-VSIX binary artifact (kiss-sorcar.vsix.kiss-rescued-…) removed from git. (Root cause: findings 34/39.)
23redundancyinstall.shupdate_repo ran guard_vsix_tracking twice (silenced probe plus loud re-run); now one captured run.
24racevscode/src/DependencyInstaller.tsTruncate-then-write of the executable ~/.local/bin/sorcar let a shell exec an empty wrapper. New writeFileAtomicSync (unique temp + rename).
25racevscode/src/DependencyInstaller.tsSame torn-write hazard for the user's shell rc (a terminal opened mid-write sourced a truncated .bashrc). Same atomic-write fix, symlinks and modes preserved.
26racevscode/src/DependencyInstaller.tsTwo VS Code windows both prompted for the remote password and the saves raced last-write-wins. A pid+token lock file now elects one prompter. (Hardened in finding 46.)
27–30racevscode/media/*.jsFour untracked flash/revert setTimeout sites (panel copy button, tips copy label, share button, remote-URL copy) let an earlier click's stale timer cut short or corrupt a later click's feedback. Per-element tracked timers, cleared before re-arm.
31racevscode/media/main.jsThe resetFlashTimer opt-in flag in wireCopyButton left sidebar history/frequent copy buttons racy while others were fixed; flag removed, every caller restarts the timer.
32redundancyvscode/media/main.jsTwo identical 9-line composer-reset blocks extracted into resetComposerAfterSend().

3. Cross-boundary fixes by the orchestrator (3)

#ClassWhereDefect and fix
33redundancyscripts/update_models.pyWhich Codex slugs are subscription-incompatible was known only to a guard test, so a catalog refresh resurrected the bad entries. A single SUBSCRIPTION_INCOMPATIBLE_CODEX_SLUGS set is now consulted by both the candidate-adder and the deprecation scan, and the guard test imports it.
34racesorcar/git_worktree.pyWorktree-rescue collision copies were named app.log.kiss-rescued-<ns> — no longer matched by *.log-style ignore rules, so the next auto-commit committed the rescue copy (exactly how the binary VSIX landed in git). The marker now goes before the extension. (Completed by finding 39.)
35racetests/core/test_artifact_dir_and_step_limit.pyPre-existing test-order dependency: a bare *_{id}_*.yaml glob matched same-id trajectories from earlier tests in the process. Scoped to the test's own trajectory name.

4. Review findings fixed — gpt-5.6-sol round (17)

The reviewers verified every fan-out fix against the diff (their verdict sections confirm items 1–32 individually) and found the following gaps, all closed test-first:

#SeverityWhereFinding and fix
36HIGHcore/models/model.pyThe select.poll() replacement (item 3) does not exist on native Windows — the writer thread died and every CLI turn silently sent an empty prompt there. The readiness primitive is gone entirely: direct non-blocking os.write, retrying on BlockingIOError with a cancellable wait. The fd-pressure test now also runs with the select APIs removed, and the test module collects on Windows.
37LOWtests/scripts/The exclusion tests were helper-level; a real pipeline test now seeds a temp MODEL_INFO.json and runs deprecation → compute_changesapply_updates_to_file twice, proving removal and non-resurrection through the written JSON.
38HIGHsorcar/daemon_client.pyItem 9's grace-expiry branch returned a stored success result without terminal status — but persistence/auto-commit/worktree cleanup still run after the result is emitted, so the task could outlive its workspace reservation. Without terminal running=false the client now always raises StopUnconfirmedTimeoutError; the sound terminal-status return stays.
39MEDsorcar/git_worktree.pyItem 34's renaming still leaked compound suffixes (*.tar.gz) and exact-name rules (.env). The rescue now installs an idempotent *.kiss-rescued-* pattern in the destination repo's info/exclude (via git rev-parse --git-path, worktree/submodule-safe) before landing, and verifies siblings with git check-ignore.
40HIGHserver/web_server.py, commands.pyGraceful shutdown crashed with RuntimeError: cannot join thread before it is started in the same pre-start window as item 16, and an in-flight run could start after the shutdown sweep. _cmd_run now takes the start decision atomically under STATE_LOCK (a swept run is routed through terminal cancellation before any user setup), and shutdown shares the watchdog's wait_for_thread_start() primitive.
41MEDserver/task_runner.pyThe watchdog's 30 s start-wait could abandon an accepted stop if registry I/O stalled longer. The finding-40 handshake honors the stop in _cmd_run itself, making the deadline a harmless backstop — covered by a real two-client UDS test.
42MEDserver/server.pyThe replay correction could emit a stale running=true/running=false pair that killed a newer run's spinner on the same viewer tab. Both broadcasts now happen under one state-lock hold with a current-owner check; _broadcast_status_end_to_viewers aligned.
43MEDserver/An attaching viewer could miss the terminal result entirely (early setup failures were addressed only to the launcher tab and never recorded). Setup results are now stamped with the run's task id and recorded; _replay_session re-snapshots and delivers the terminal result before the corrective status. One residual window (recording already dropped while the events table lags) is documented precisely in the fix report.
44MEDserver/autocomplete.pyA superseded background refresh could emit a stale old-workspace file list last (the server-web report's rejection of this was wrong). Ranking now happens outside the lock; the request token is re-verified under the lock at emission and removed only after emitting.
45LOWtests/server/The UDS test fixture leaked pending handler tasks; it now drains them before stopping the loop. Real two-client UDS tests added for the pre-start stop and shutdown paths.
46MEDvscode/src/DependencyInstaller.tsItem 26's waiter only polled for lock-file disappearance: a crashed prompter stuck it for 10 minutes, while a live 10-minute prompt got evicted (duplicate prompts). Prompt-specific pid+token protocol: waiters detect dead owners and take over in under a second, live holders are never age-evicted, and a token-stamped terminal outcome distinguishes a deliberate Esc from a crash.
47MEDvscode/src/DependencyInstaller.tsThe atomic writer destroyed dangling dotfile symlinks (realpath throws, rename replaced the link). It now walks the link chain with readlinkSync and creates the missing referent.
48LOWvscode/src/SorcarTab.tsEvery window seeing the update marker independently reset the tips claim, so two windows could each reset-then-claim. A per-update-version wx reset-claim elects exactly one resetter.
49LOWvscode/media/tips.jsOut-of-order async clipboard completions could overwrite the latest copy result with a stale failure. Per-button copy generation; only the newest completion updates the label.
50LOWvscode/src/DependencyInstaller.tsThe temp file wasn't cleaned up when the write itself failed (reproduced with a real ulimit -f file-size limit). Whole temp lifecycle under one try/finally.
51LOWvscode/test/The installer fixes had no coverage gate; a new V8 gate covers all fenced audit0903 regions of DependencyInstaller.js at 164/164 lines.
52LOWvscode/src/DependencyInstaller.tsOne prettier violation introduced by the fixes; resolved, scoped eslint clean.

5. Tests written

6. Verification

7. Investigated and deliberately left alone

About sixty suspicions were investigated and rejected with recorded evidence in the per-partition reports (tmp/audit2-report-*.md, tmp/review-*.md, tmp/fix-report-*.md): among them the daemon client's frame-assembly index (proven correct for every chunking), the abort-cascade vs tab reuse (guarded by the run token, verified server-side), pool spare consumption vs reclaim (protected by repo_lock), cron tick/run-now overlap (documented tradeoff), voice-wake double-start (unreachable through per-connection serialization), and the CPython-level impossibility of closing sub-bytecode async-exception windows. Also left alone: one residual replay window documented in finding 43, and the sandbox-only npm audit stall above.

State of the branch: nothing is committed. All changes — 69 files, roughly +9,800/−450 lines — are in the working tree with new files staged. Development model: claude-fable-5. Review and debugging model: gpt-5.6-sol (well under the 50% budget cap).