Audit of core/, agents/sorcar/, server/, agents/vscode/

Redundancies, inconsistencies and race conditions. Date: 2026-09-02. Development model: claude-fable-5-1. Read-only review and debugging model: gpt-5.6-sol.

1. Summary

Audit fan-out 8 partitions, disjoint files claude-fable-5-1 fail-first e2e tests 45 defects fixed Cross-boundary triage claims verified by hand 2 fix subagents + 1 rejected claim (new_tab is live) Review round 1 gpt-5.6-sol, read-only 4 area reviewers 5 fix subagents 31 findings fixed Review round 2 gpt-5.6-sol on the fix pass 4 fix subagents 10 findings fixed Verification check --full 7,905 Python tests 268 JS suites Each stage wrote its own report under ./tmp (audit-report-*.md, review*.md, fix*-report-*.md); this page aggregates them.
Figure 1. How the work was organised. Reviewers never edited code; every review finding was re-verified before a fix subagent was dispatched.
core 12 10 1 = 23 agents/sorcar 13 8 2 = 23 server 8 6 3 = 17 agents/vscode 12 7 4 = 23 audit fan-out (45) review round 1 (31) review round 2 (10)
Figure 2. Fixes per area and round (86 in total). "agents/vscode" includes the two install scripts the extension's update button runs.

2. Method

  1. The four trees were split into eight partitions with disjoint file ownership (core base, core models, sorcar agents, sorcar infrastructure, server web, server core, webview main.js, extension host). Each claude-fable-5-1 subagent read its files in full, wrote a failing end-to-end test per suspected defect, fixed the root cause, ran the impacted existing tests, and wrote a report. Findings that needed a file outside the partition went to a cross-boundary note.
  2. The cross-boundary notes were verified by hand. One claim was wrong and rejected: the webview's new_tab handler is not dead; chat_sorcar_agent.py emits it. The remaining items were fixed by two more subagents.
  3. Four read-only gpt-5.6-sol reviewers (one per area) checked the changed code for missed wiring and regressions. Each finding was re-verified before a fix subagent was dispatched; findings judged not to be defects were left alone and are listed in section 5.
  4. A second gpt-5.6-sol review of the fix pass (instructed not to invent problems) produced ten more verified findings, which were fixed the same way.
  5. Final verification: lint and type checks, the complete JS suite, and the complete Python suite in 30 parallel splits.

3. Defects fixed in the audit fan-out (45)

Categories: race concurrent or cross-process interleaving; inconsistency two code paths or a docstring and its code disagree; redundancy duplicated logic that had already drifted or was about to.

3.1 src/kiss/core/

#CategoryWhereWhat was wrongFixTest
1inconsistencykiss_agent.py _execute_stepThe text-only implicit finish returned raw text without consulting the finish guard or hook, so Sorcar's "block finish while a user message is pending" guard was bypassed and callers expecting the structured finish contract (RelentlessAgent) got plain text.Shared _implicit_finish_allowed() and _implicit_finish_result() used by both implicit-finish paths.tests/core/test_audit0902_core_base_implicit_finish.py
2inconsistencykiss_agent.py _resetThe model was built before the per-run state was reset; a second run() with a bad model name saved its trajectory over the previous run's file.Reset all per-run state first.test_audit0902_core_base_trajectory_reset.py
3redundancy inconsistencyvscode_config.py load_config/save_configTwo copies of the config-reading block; both let UnicodeDecodeError escape although bad JSON was handled.One _read_stored_config() helper catching OSError and any ValueError.test_audit0902_core_base_config_junk_bytes.py
4docconfig.pymax_budget description named the wrong RelentlessAgent default.Corrected.n/a
5racemodels/model.py _CLIProcessA grandchild that inherited the CLI's stdin pipe kept the prompt writer parked in write(2); the turn waited for its whole deadline and close() blocked for the grandchild's lifetime.Writer thread tracked; send_prompt returns once the child exits; close() closes each pipe only when its own thread finished.tests/core/models/test_audit0902_core_models_cli_process.py
6inconsistencyclaude_code_model.py vs codex_model.pyClaude Code accepted exit status −15 (SIGTERM) as success; Codex reported it. A killed claude returned truncated text as the answer.One exit-status policy in _CLIProcess.raise_for_exit().same file
7redundancyboth CLI adaptersThe whole turn skeleton (timeout, stall handling, interrupt handling, exit check) was duplicated line for line, which is how #6 drifted.CLITextModel._cli_turn() context manager.#5/#6 tests + existing lifecycle tests
8inconsistencyanthropic_model.py _create_messageA connection dropped mid-thinking left the thinking bracket open (OpenAI and Gemini transports close it in finally); the retry's answer rendered as reasoning.finally: _close_thinking_if_open(); duplicate local flag removed.test_audit0902_core_models_anthropic_cut_mid_thinking.py
9inconsistencygemini_model.py _stream_turnByte-level stall built its own message instead of the shared stall_error wording.raise stall_error(...) from e.test_audit0902_core_models_gemini_stall_wording.py
10redundancyopenai_compatible_model2.pyRestated the TOOL_RESULT_ATTACHMENT_NOTE literal.Import the constant.existing
11redundancymodel_info.pyProvider-prefix fallback lookup written out three times._lookup_model_info().existing
12inconsistencyMODEL_INFO.json gemini-3.8-flash"emb": true on a generation model; the catalog invariant test failed at HEAD."emb": false; README embedding count 12 → 11.existing invariant tests

3.2 src/kiss/agents/sorcar/

#CategoryWhereWhat was wrongFixTest
13inconsistencyworktree_sorcar_agent.py _finalize_worktree, _release_worktree, mergeFive distinct preserve outcomes were collapsed to a bool, so the user was told "a pre-commit hook may have rejected the commit" when the worktree was really kept because a sub-agent was still writing or an ignored file could not be rescued.Outcome recorded in _last_preserve_outcome; shared warning helpers.tests/agents/sorcar/test_audit0902_sorcar_agents_preserve_outcome_messages.py
14racesorcar_agent.py _collect_unfinished_usageCheck-then-assign on abandoned sub-agent usage: a child publishing its final figure between the check and the write lost that figure for good.Component-wise max (later hardened with a lock, see 4.2).test_audit0902_sorcar_agents_unfinished_usage_race.py
15racedocker_manager.py open/closeA second open() started a second container and orphaned the first; a rejected container leaked its shared-volume temp dir.open() refuses when already open; temp dir removed on failure; removal factored into one helper.test_audit0902_sorcar_agents_docker_open_lifecycle.py (real Docker, -m slow)
16inconsistencychat_sorcar_agent.py final history saveGuarded model/max_budget for setup failures but took work_dir from stale instance state, blanking the early row's directory.Persist resolved_work_dir.test_audit0902_sorcar_agents_failed_setup_work_dir.py
17inconsistencyrelentless_agent.pyStrict read_text() of ~/.kiss/SORCAR.md: one cp1252 byte killed every task on the machine, while skills.py tolerates such bytes.errors="replace".test_audit0902_sorcar_agents_sorcar_md_encoding.py
18redundancyuseful_tools.pyDuplicate optional fcntl import.Import from _concurrency.existing lock tests
19racegit_worktree.py create, reclaim_orphaned_worktreesThe owner-pid stamp was written after git worktree add and never for pool spares; a peer process's reclaim in that window deleted a live worktree, and a live spare was discarded before the owner check ran.create adds and stamps under the reclaim lock; owner-liveness check moved ahead of the spare discard.test_audit0902_sorcar_infra_reclaim_live_owner.py (real second interpreter)
20inconsistencygit_worktree.py _branch_is_expendable--all also lists linked worktrees' HEADs, so a branch checked out in its own worktree was always "expendable" even with unique commits; the "spare has content" guard never fired.--single-worktree.test_audit0902_sorcar_infra_spare_content_probe.py
21redundancyworktree_pool.py, git_worktree.pyThe four-condition "spare holds foreign content" predicate copied into three destructive paths.GitWorktreeOps.spare_has_content().same file
22redundancycron_agent.pyRe-implemented the daemon socket path precedence owned by daemon_client._resolve_sock_path.Call the owner.test_audit0902_sorcar_infra_cron_sock_path.py
23docgit_worktree.py reclaim docstringClaimed a worktree without kiss-original is left untouched; the code reclaims it with a fallback.Docstring corrected.existing
24inconsistencychat_sorcar_agent.py _build_extra_payloadThe early history row never carried auto_commit_mode, and persistence._add_task maps absence to 0 while the schema default is 1, so every running or killed task showed "manual commit".Toggle snapshotted once per run and written to both rows.test_audit0902_sorcar_xb_early_row_auto_commit.py
25redundancygit_worktree.py, web_use_tool.py, server/web_server.pyThree pid-liveness helpers with different error mappings; web_use_tool._pid_alive(0) returned True.One pid_alive() in _concurrency.py.test_audit0902_sorcar_xb_pid_alive.py, tests/server/test_audit0902_server_xb_pid_alive.py

3.3 src/kiss/server/

#CategoryWhereWhat was wrongFixTest
26inconsistencytricks.py _parse_trick_sectionsDid not strip CommonMark backslash escapes like the TS twin, so the remote web app and ghost-text completions carried literal backslashes.Same character class as TS unescapeMarkdown.tests/server/test_audit0902_server_web_tricks_unescape.py
27raceweb_server.py ws shimThe lost-session latch was set only in the old socket's onclose; a wake-up while the dead socket was still CLOSING nulled that handler, so the replacement socket never reloaded the page.Latch set inside connect() when replacing an authenticated socket.test_audit0902_server_web_shim_closing_latch.py (Playwright)
28race redundancyweb_server.py _write_server_reset_flagHand-rolled temp file with a fixed name next to the module's _atomic_write_text; two instances produced an empty flag.Use _atomic_write_text.test_audit0902_server_web_reset_flag_atomic.py
29inconsistencyweb_server.py GET /chat.html rendered once at start: the remote app's trick list froze while completions re-read the file per keystroke.Render per request.test_audit0902_server_web_html_tricks_fresh.py
30redundancyweb_server.py five command handlersFive copies of the field-sanitising / work-dir fallback block._cmd_str(), _cmd_work_dir().test_audit0902_server_web_cmd_field_sanitizing.py
31racemerge_flow.py _handle_worktree_actionState, agent and pending worktree resolved outside _state_lock; a concurrent closeTab or run let merge() run alongside the other path's disposal of the same worktree (2 disposals reproduced).Resolution, guards and the is_merging claim form one locked section.test_audit0902_server_core_merge_state_toctou.py
32inconsistencytask_runner.py subtask metricsBaselines captured before agent.run(), but _reset zeroes the counters, so the 2nd+ subtask persisted 0 tokens/cost.Counters zeroed before each run; raw per-run values persisted.test_audit0902_server_core_subtask_metrics_reset.py
33inconsistency redundancytab_registry.pyclose_tab did not strip ids like the other methods; two drifted copies of the entry-sanitising loop left blank titles from disk.Shared _sanitize_entries(); close_tab strips.test_audit0902_server_core_tab_registry.py

3.4 src/kiss/agents/vscode/

#CategoryWhereWhat was wrongFixTest
34inconsistencymedia/main.js worktree barThe daemon marks a deferred discard retryable so the bar's buttons are kept, but nothing re-enabled them: the bar the user was told to retry with was dead.setActionBarBtnsDisabled(bar, false) on retryable results, foreground and background.test/audit0902_vscode_main_retryable_bar.test.js
35inconsistencymain.js markTabDone, task_eventsTerminal task_done carries no verdict but wrote lastTaskFailed=false, so a failed live task got a green dot while its replay got red; replays never reset a stale red dot.Flag only raised by terminal events; reset before replay.audit0902_vscode_main_tab_fail_dot.test.js
36inconsistencymain.js sendReadyReported activeTabId verbatim (possibly a content tab) while every sibling reported a chat tab; Git Commit then targeted a content tab.chatTabIdForHost() shared by all callers.audit0902_vscode_main_ready_chat_tab.test.js
37redundancy inconsistencymain.js task_doneRe-implemented doneLabelFor and measured a background tab's duration with the visible tab's clock.Use doneLabelFor(); per-tab clock fallback.audit0902_vscode_main_done_label.test.js
38redundancymedia/main.css60 duplicated lines across four overlays, four close buttons and two list panes.Grouped selectors.audit0902_vscode_main_css_parity.test.js (jsdom cascade)
39racesrc/SorcarSidebarView.ts voiceSensitivityawait stop(); if (!running) start(): a voiceToggle off during the awaited stop was overridden and the microphone re-opened.Queued start; a later stop() cancels it.audit0902_vscode_ext_voice_lifecycle.test.js
40racesrc/voiceWake.ts stopA child whose spawn failed never emits exit; stop() waited on it for ever and every later start() queued behind it.Pid-less child settles the stop on error.same file
41racesrc/UpdateChecker.js writeCacheFixed temp-file name: two windows truncated each other's temp; ~35% of reads saw an empty cache and re-hit PyPI.Per-process temp name; unlink on failure.audit0902_vscode_ext_update_cache_atomic.test.js
42inconsistencysrc/types.ts, WebviewNotifications.tsSix message types relayed to the webview were missing from ToWebviewMessage; the poster used Record<string, unknown> and a cast.Variants added; cast removed.tsc --noEmit
43raceSorcarSidebarView.ts runUpdateNo single-flight guard: two clicks started two installers on the same checkout.Guard (later replaced by a cross-process lock in the installer, see 4.4 and 4.5).audit0902_vscode_ext_update_single_flight.test.js
44racesrc/userAssets.tsTruncating writeFileSync after existsSync: the daemon's per-keystroke reader saw an empty MY_INJECTION.md 16,510 times in 300 seeds; two seeders mixed contents.Stage + linkSync, EEXIST means the other seeder won (mirrors user_assets.py).audit0902_vscode_ext2_user_asset_seed_atomic.test.js
45racesrc/DependencyInstaller.ts ensureRemotePasswordUnlocked read-modify-replace of config.json against the daemon's locked save_config: 211 of 400 extension saves lost.Route through vscode_config.save_config via uv run python, payload on stdin.audit0902_vscode_ext2_remote_password_single_writer.test.js

4. Findings from the gpt-5.6-sol reviews (41 fixed)

The reviewers read the diff and the surrounding code without editing anything. Findings were checked before dispatching a fix; items rejected as non-defects are in section 5.

4.1 Round 1, core (10)

WhereWhat the review foundFix
kiss_agent.py text-only implicit finishReturned success=False, is_continue=True, so RelentlessAgent resumed a text-only model until max_sub_sessions ran out.Explicit outcome per path: text-only is terminal (success=True); stagnation unchanged.
kiss_agent.py _implicit_finish_allowedGuard consulted before hook, unlike a real finish call.Hook first; guard only if the hook accepted.
kiss_agent.py _resetTwo runs in the same second shared one trajectory filename.Strictly increasing stamp (refined in round 2).
config.pyWording still ambiguous about which DEFAULT_MAX_BUDGET.Names the module explicitly.
model_info.py _read_my_modelsInvalid UTF-8 in MY_MODELS.json crashed import kiss.core.models.model_info.except (OSError, ValueError).
README.mdEmbedding-model count stale after the catalog fix.12 → 11.
model.py _cli_turnThinking bracket closed only for stall/interrupt; any other exception left it open.Unconditional finally.
model.py wait_for_exitBlocked up to 10 s after stdout EOF without checking the stop signal.Polls in stop slices; raises KeyboardInterrupt on Stop.
model.py _write_promptBlocking buffered stdin.write() still pinned the writer thread and fd for a grandchild's lifetime.Non-blocking fd, select-guarded os.write loop re-checking cancel, child exit and deadline; stdin closed in the writer's finally.
two audit testsWall-clock discriminators would flake under load.Semantic assertions (__cause__, thread/fd state).

4.2 Round 1, agents/sorcar (8)

WhereWhat the review foundFix
git_worktree.py createIgnored save_owner_pid()'s result: with .git/config.lock held the worktree came back owner-less and a peer reclaim deleted it.Stamp failure = creation failure (cleanup_partial, return False); redundant later stamp removed.
worktree_sorcar_agent.py preserve pathsBoth automatic preserve paths ignored a failed save_preserve_marker() and dropped the claim; the next reclaim merged and deleted work kept for review.Fail-closed _keep_for_review(); claim dropped only on success; _try_setup_worktree falls back to direct execution instead of overwriting the claim.
sorcar_agent.py _collect_unfinished_usageComponent-wise max was still unsynchronised with the worker's final write.Per-fan-out sub_usage_lock held by both sides.
docker_manager.pyTwo concurrent open() calls both passed the guard.Per-manager _lifecycle_lock.
docker_manager.py _remove_shared_volume_dirCleared the path before rmtree, turning a failed removal into an untraceable leak.Cleared only after successful deletion; retried by the next open()/close().
chat_sorcar_agent.py final saveOn a reused agent whose setup failed, the row got the previous run's model, mode, tokens and cost.run_started flag; zeroes and resolved values for a never-run task.
chat_sorcar_agent.pyStandalone final payload lacked steps.Added.
worktree_pool.py discard_allA refill scheduled after the thread snapshot published a spare after discard returned.Pool generation counter checked at publication.

4.3 Round 1, server (6)

WhereWhat the review foundFix
sorcar.py dispatchRegistry stripped tab ids but handlers kept the raw wire string: one tab, two identities, leaked state.Canonicalise once at the API boundary.
task_runner.py _force_stop_threadSecond KeyboardInterrupt injection landed in legitimate post-stop cleanup (Cleanup interrupted, row left at "Agent Failed Abruptly").AgentState.stop_acknowledged set by _cancel_outcome; retry only for a swallowed interrupt.
tab_registry.py _save_lockedFixed tabs.json.tmp name: sibling instances published an empty file.kiss.core.utils.atomic_write_text.
sorcar.py work-dir pinTruthy non-string workDir escaped the pin and resolved to another window's folder._usable_work_dir(); pin stamped unless an explicit non-empty string.
web_server.py reset-complete toastAnnounced "restart complete" on every start if the marker was a directory or unlinkable.Claim by os.replace, validate JSON object, always unlink.
test docstringNamed the deleted _subtask_metric_deltas.Updated.

4.4 Round 1, agents/vscode and installers (7)

WhereWhat the review foundFix
DependencyInstaller.ts saveKissConfigFell back to the very unlocked write the fix removed, and execFileSync blocked the extension host for up to 60 s.Async execFile, no fallback, error notification; writeKissConfig deleted.
scripts/install.sh, SorcarSidebarView.tsPer-window guard still allowed two windows (or window + daemon) to run two installers; git preflight ran outside any lock.Cross-process lock in the installer; the extension runs the locked bootstrap.
install.sh (root)The daemon and the legacy extension path run the root script directly, outside the lock.Same lock block in the root script; chained with the stash-restoring EXIT trap.
main.js Git CommitWith no visible chat tab the button posted autocommitAction for a content tab.No fallback to a content tab; warning toast.
voiceWake.ts error handlerA requested stop of a pid-less child was reported as an unexpected error and the dead child kept.Mirrors the exit handler.
types.tschat_id typed number (uuid strings are sent); startTs/endTs, task_id, retryable, task_settings missing.Typed from the emitters; contract test compiles real payloads with satisfies.
main.js sendMessageGlobal attachment latch dropped tab B's Enter while tab A waited on a HEIC conversion.Per-tab latch.

4.5 Round 2 (10)

AreaWhereWhat the review foundFix
corekiss_agent.py, base.pyThe uniqueness trick set a synthetic run_start_timestamp later than run_end_timestamp.Real wall clock restored; separate monotonic _trajectory_stamp for the filename only.
sorcarserver.py teardown, worktree_sorcar_agent.py, git_worktree.pyTab disposal unregistered the state before preserving, so a fail-closed keep was invisible to the live-worktree exclusion; own-pid reclaim exemption had no in-process protection.Claim, retire via retire_for_disposal(), unregister only when the claim was dropped; volatile in-process preserve claims honoured by load_preserve_marker.
sorcarworktree_pool.pyA refill capturing the new generation could still publish while the sweep was running._discarding gate in the publication check.
servertask_runner.py outer setup catchInterrupt before the inner try was labelled by hand, never acknowledged (re-injection into cancellation handling) and mislabelled shutdown as user stop.Routes through _cancel_outcome.
server_kiss_web_launcher.py, server.pyThe embedded channel-agent server shared the canonical tabs.json; two registries overwrote each other.VSCodeServer.use_private_tab_registry(); launcher uses a private file.
serverpinned-workdir testOrdering flake (1/22).A-side probe awaited before B's commands.
vscodeboth install scriptsmkdir lock with stale-breaking rm -rf admitted 2 of 8 contenders; lock keyed on $KISS_HOME while the checkout follows $HOME.perl flock on fd 9 at $HOME/.kiss/.update.lock, released by the kernel; fd closed on long-lived launches.
vscodeSorcarSidebarView.tsTerminal-lifetime guard refused every later click until the terminal was closed.Removed; the installer's lock decides.
vscodetypes.tsopenSubagentTab.task_id and the fileContent variant missing.Added; 13 real payloads type-check.
vscodeweb_server.py runUpdateLock refusal exited 1 silently; the browser was told the update was installing._watch_update_exit reports non-zero exit (with the refusal line) to the requesting connection.

4.6 Found during final verification

Nine pre-existing test_cc_* files stubbed the CLI's Popen with a _FakeStdin lacking fileno(); after the non-blocking writer (4.1) the writer thread raised AttributeError, which pytest surfaced as PytestUnhandledThreadExceptionWarning in 8 of the 30 splits. The stubs now hand the writer a real open(os.devnull, "wb"); all 76 tests in those files pass with the warning promoted to an error.

5. Left alone deliberately

WhereObservationWhy no change
core/printer.py, json_printer.py reset()No production caller.Public API used by many tests.
kiss_agent.py step_count before _check_limitsOff by one in a saved trajectory after a budget error."Step in progress" semantics; RelentlessAgent's first-step heuristic depends on it.
codex_model.py speculative text_delta/thinking_* branchesNot part of codex exec --json.Not reproducible against real output; an existing coverage test feeds them.
openai_compatible_model2.py in_reasoning vs _thinking_openDuplicate state.Always toggled together; no observable defect.
auto-commit fallback subject (3 copies, 2 wordings)Cosmetic drift.Tests pin both wordings.
mcp_servers.py shutdown before run_foreverSuspected thread leak.200 rapid cycles never reproduced it.
helpers.py model_vendor labelsRestates the provider registry ("Together AI" vs "Together").UI label pinned by tests; not a bug.
autocomplete.py refresh tokenTwo getFiles could interleave.Unreachable: commands on one connection are dispatched sequentially.
voice_wake_control.py start, merge_flow._present_pending_worktreeSuspected races.Callers already serialise or hold the claim.
commands.py pending follow-up TOCTOUReviewer's atomic-transition proposal.Documented design; the window is closed by the finish guard.
SorcarTab.ts tips first run, restart-lock stale breakingexistsSync → write TOCTOU.Effect is one extra tips panel or a rare lock hand-off; not reproducible without injected sleeps.
double PyPI fetch on activation; 0600 perms on a non-secret assetReviewer nits.Harmless; no behaviour to test.
main.js new_tab handlerReported dead.Wrong: chat_sorcar_agent.py emits it.
tests/core/test_artifact_dir_and_step_limit.pyFails only after test_kiss_agent.py in one process.Pre-existing test-isolation defect (fails at HEAD with all changes stashed); outside the four audited trees.

6. Verification

CheckResult
uv run check --full (ruff, mypy, pyright, extension typecheck and lint)All checks passed.
Full Python suite, 7,905 tests, 30 parallel splits29 splits green. One failure, tests/agents/vscode/test_content_tab_file_links.py::test_code_link_opens_separate_tab_with_code: Playwright selector timeout under 30 concurrent browser-driving splits; 8/8 pass in isolation. The same class of load flake (test_remote_panels_match_extension) appeared in the previous session's run and also passes alone.
Nine repaired test_cc_* files with -W error::pytest.PytestUnhandledThreadExceptionWarning76 passed.
JS suite, node test/run-all.js, 268 suites268/268 suites passed (functional suites and V8 line-coverage gates; the gates require 100% of the fenced lines to execute).

7. Files

Source files changed (45): core/base.py, config.py, kiss_agent.py, vscode_config.py, models/{MODEL_INFO.json, anthropic_model.py, claude_code_model.py, codex_model.py, gemini_model.py, model.py, model_info.py, openai_compatible_model2.py}; agents/sorcar/_concurrency.py, chat_sorcar_agent.py, cron_agent.py, docker_manager.py, git_worktree.py, relentless_agent.py, sorcar_agent.py, useful_tools.py, web_use_tool.py, worktree_pool.py, worktree_sorcar_agent.py; agents/third_party_agents/_kiss_web_launcher.py; server/agent_state.py, merge_flow.py, server.py, sorcar.py, tab_registry.py, task_runner.py, tricks.py, web_server.py; agents/vscode/media/main.js, media/main.css, src/{DependencyInstaller.ts, SorcarSidebarView.ts, UpdateChecker.js, WebviewNotifications.ts, types.ts, userAssets.ts, voiceWake.ts}; install.sh, scripts/install.sh; README.md, API.md (regenerated).

New tests: src/kiss/tests/**/test_audit0902_*.py (45 files) and src/kiss/agents/vscode/test/audit0902_*.{test,coverage}.js (21 files). Nothing has been committed; all new files are staged with git add.