fix: harden Agent recovery, corpus proof, and release signing - #74
fix: harden Agent recovery, corpus proof, and release signing#74div0-space wants to merge 18 commits into
Conversation
There was a problem hiding this comment.
An organization admin can view or raise the cap at claude.ai/admin-settings/claude-code. The cap resets at the start of the next billing period.
Once the cap resets or is raised, reopen this pull request to trigger a review.
There was a problem hiding this comment.
Pull request overview
This PR hardens release-signing keychain handling, introduces corpus census/replay tooling for parity analysis, and updates build/docs/UI to reflect the new “MiniLM as signed runtime resource” asset strategy (with Whisper still runtime by default, embedded only in the full SKU).
Changes:
- Replaces inlined ephemeral keychain manipulation in the release workflow with a concurrency-safe
keychain-session.sh+ adds hermetic regression tests and a read-only “doctor” canary. - Adds
codescribe-corpusfor private corpus census and isolated production-session replay/parity reporting (with Keychain disabled and operator config untouched). - Refactors model asset embedding/bundling policy and updates app/overlay UX (native selectable live transcript) + adds a narrowly-scoped “Reset Agent” destructive action.
Reviewed changes
Copilot reviewed 40 out of 40 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/release.yml |
Uses keychain-session.sh for robust keychain lifecycle; adds post-cleanup diagnostic warning. |
Cargo.toml |
Registers codescribe-corpus binary; promotes sha2 to non-dev dependency. |
Makefile |
Adds corpus targets and test-keychain-session; updates release/model policy messaging and gate ledger notes. |
README.md |
Updates public docs for MiniLM runtime resource + Whisper runtime/full-SKU embedding. |
app/controller/helpers.rs |
Ensures legacy formatter fallback always emits a terminal Agent UI event sequence. |
bin/codescribe-corpus.rs |
New corpus census + replay/parity tool with redacted machine reports + private Qube HTML output. |
bridge/src/config.rs |
Adds Agent-only reset preview/reset APIs and scoped secret/env/settings cleanup. |
core/build.rs |
Changes embedder policy to runtime bundle/cache by default; adds explicit CODESCRIBE_EMBED_EMBEDDER=1 embed path. |
core/config/loader.rs |
Adds surgical .env key removal helper for scoped reset flows. |
core/config/settings.rs |
Adds surgical Agent-owned settings removal (preserving unknown/non-Agent fields; fail-closed on malformed JSON). |
core/embedder/engine.rs |
Adds app-bundle resource resolution for MiniLM under Contents/Resources/models/embedder before HF cache. |
core/embedder/mod.rs |
Updates module docs to match bundled/cached/embedded runtime model resolution. |
core/mcp/config_store.rs |
Makes list_servers_at public for path-explicit callers (reset tooling). |
core/mcp/mod.rs |
Re-exports list_servers_at. |
core/quality/qube_report.rs |
Exposes render_html publicly for reuse in replay tooling. |
core/stt/apple_stt/mod.rs |
Adds ensure_noninteractive_ready for fail-closed unattended probes (no dialogs/downloads). |
docs/ARCHITECTURE.md |
Updates architecture docs for runtime model policy and MiniLM resolution order. |
docs/ENV_REGISTRY.toml |
Updates embedder defaults and adds CODESCRIBE_EMBED_EMBEDDER. |
docs/INSTALLATION.md |
Updates installation docs for slim vs full DMG model packaging policy. |
docs/TEAM_SETUP.md |
Updates team setup docs for runtime model policy and explicit fat/debug embed toggles. |
macos/Codescribe/Bridge/codescribe_ffi.swift |
Adds UniFFI surface for Agent reset preview/reset + CsAgentResetPreview. |
macos/Codescribe/Bridge/codescribe_ffiFFI.h |
Adds generated UniFFI symbols for Agent reset methods. |
macos/Codescribe/Screens/AgentChat/AgentChatStore.swift |
Exposes attachment defaults key for Agent reset cleanup. |
macos/Codescribe/Screens/Overlay/DictationOverlayView.swift |
Switches live transcript to native AppKit-backed selectable view; clips to prevent platform view paint bleed. |
macos/Codescribe/Screens/Overlay/LiveTranscriptTextView.swift |
New NSTextView representable preserving selection across live updates + tail-follow behavior. |
macos/Codescribe/Screens/Settings/SettingsEngine.swift |
Extends Settings engine protocol/implementations for Agent reset preview/reset. |
macos/Codescribe/Screens/Settings/SettingsViewModel.swift |
Adds Agent reset state/flows and narrow defaults cleanup + relaunch behavior. |
macos/Codescribe/Screens/Settings/UserPanel.swift |
Adds “Reset Agent” danger-zone section with separate confirmation phrase. |
macos/CodescribeTests/LiveTranscriptTextViewTests.swift |
Adds tests for live transcript text view behavior and selection policy. |
macos/CodescribeTests/OverlayStateTests.swift |
Adds snapshot guard for formatted overlay minimum height rendering regression. |
macos/CodescribeTests/SettingsTruthTests.swift |
Adds tests covering Agent reset confirmation, scope, and relaunch/error markers. |
scripts/build-app.sh |
Bundles MiniLM into app resources for normal builds; resolves model source from HF cache or explicit path. |
scripts/build-dmg.sh |
Updates help/messaging and env unsets for new embedder bundling policy. |
scripts/canaries.sh |
Adds host canary for keychain-domain cleanliness using read-only doctor. |
scripts/entitlements.appstore-basic.plist |
Updates commentary to match Candle ML dylib signing context. |
scripts/entitlements.plist |
Updates commentary to match Candle ML execution requirements. |
scripts/keychain-doctor.sh |
New read-only keychain domain diagnostic + derived recovery command. |
scripts/lib/keychain-session.sh |
New concurrency-safe keychain session manager with robust unlist-before-delete and trap chaining. |
scripts/tests/keychain-session-test.sh |
New hermetic regression suite with fake security + redirected HOME (no real keychain touches). |
scripts/verify-dmg-payload.sh |
Updates DMG payload gate to validate MiniLM as resource and adjust dylib/DMG thresholds accordingly. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 47 changed files in this pull request and generated no new comments.
Suppressed comments (1)
scripts/canaries.sh:237
- The FAIL evidence extraction truncates keychain paths that contain spaces (e.g. "Team Signing.keychain-db") because the awk script prints only
$2. This can misreport the stale/foreign/hijacked path and make recovery harder.
- Publish TextDone and Done after a persisted legacy fallback - Publish an actionable Error when both provider paths fail - Guard the terminal mapping against blank fallback output Authored-By: Codex <agents@vetcoders.io>
- Census private recordings without loading operator state or Keychain - Replay explicit production profiles in isolated worker processes - Emit redacted JSON/Markdown plus private keyboard-driven Qube HTML - Preserve host configuration hashes and fail closed on TCC readiness Authored-By: Codex <agents@vetcoders.io>
- Keep MiniLM bytes out of normal Cargo targets - Resolve signed app resources before falling back to the HF cache - Bundle and sign the model during app assembly - Verify the resource directly instead of inferring it from dylib size - Mark corpus source identity dirty when the Living Tree is not clean Authored-By: Codex <agents@vetcoders.io>
- move only Agent conversations and MCP/tool files to Trash\n- clear Agent provider state and credentials without touching dictation data\n- add separate Settings confirmation, previews, bindings, and preservation tests\n\nAuthored-By: Codex <agents@vetcoders.io>
- stop animating the AppKit-backed TextEditor across overlay modes - clip the transcript body at its sibling boundary - render the observed minimum-height geometry and assert the action row stays clean Authored-By: Codex <agents@vetcoders.io>
- preserve every non-Agent settings value and fail closed on malformed JSON - delete only MCP connector tokens referenced by the active Agent config - keep Agent relaunch handling out of the hotkey reset path Authored-By: Codex <agents@vetcoders.io>
- Normalize the operator preferences plist through plutil before hashing. - Keep settings.json and dotenv byte-exact while avoiding plist encoding false positives. - Cover equivalent XML and binary plist storage with a regression test. Authored-By: Codex <agents@vetcoders.io>
- Replace reset helper lint debt with explicit control flow. - Bundle replay execution inputs so report rows cannot receive mismatched arguments. - Bound corpus reads and private audio publication through capability-safe helpers. - Pass the full static gate with zero Semgrep findings. Authored-By: Codex <agents@vetcoders.io>
…eychain A Codescribe run on 2026-08-15 popped "Codescribe wants to use the 'Vibecrafted-signing' keychain", with a password no human has. Measured at the time: the user search list AND the default keychain both pointed at a release's ephemeral keychain, while that release was still running. - add scripts/lib/keychain-session.sh — an ephemeral signing keychain that always gives the user domain back. It never takes the login session's default keychain (opt-in via KEYCHAIN_SESSION_SET_DEFAULT), snapshots the search list as structured argv instead of one joined string, traps EXIT INT TERM HUP, and unlists BEFORE deleting so cleanup still works when the keychain file was already destroyed - make restoration remove-self rather than replay-snapshot: two overlapping releases can no longer drop each other's keychain or resurrect a dead one - add scripts/keychain-doctor.sh — read-only diagnosis grading each entry resident/FOREIGN/STALE and the default ok/HIJACKED, printing a recovery line derived from the surviving entries, never canned login-only, and never mutating - add scripts/tests/keychain-session-test.sh (43 assertions) against a fake `security` and a temp HOME: spaces in paths, multiple keychains, failed build, SIGINT/SIGTERM, vanished keychain file, concurrent sessions, stale-entry reclaim, empty-list refusal, no secret leakage - wire release.yml onto the library; its cleanup step only deleted the keychain and never restored the search list - add the keychain-domain-clean canary and its GATE LEDGER row Authored-By: claude <agents@vetcoders.io>
…aining The trap-chaining path had no test. Adding one found a real defect: the caller's own handler was preserved on EXIT and silently dropped on INT, TERM and HUP — the three signals a bare EXIT trap cannot cover, which is the whole reason the chaining exists. - `trap -p` answers with the normalized signal name: `trap -p INT` prints `... SIGINT`, while the pseudo-signal EXIT stays bare. Stripping a literal " $sig" matched only EXIT and left the other three bodies malformed. Strip the last whitespace-delimited word instead - cover both orderings: the caller's EXIT handler still runs and ours runs first, and the caller's INT handler survives alongside ours - write child scripts verbatim instead of through an unquoted heredoc. The heredoc expanded the snippet's own `$$` and `$(...)` in the test harness, so `kill -INT $$` signalled the harness rather than the child under test 48 assertions, all green. Authored-By: claude <agents@vetcoders.io>
…ning shellcheck flags the trap bodies as expanding now rather than at signal time. Here that is the intent: $body holds the handler that was already registered, read back from `trap -p`, and it must be baked in at arm time — deferring would splice an empty variable when the signal fires. Documented rather than silenced. Authored-By: claude <agents@vetcoders.io>
… shell bar The sibling repo that also needs this library gates commits on scripts/check_shell.py, which fails on ANY shellcheck finding — info and style included. The suite carried ten. - replace `A && ok ... || bad ...` with explicit if/else (SC2015): the short-circuit form is not if-then-else and would have run the failure branch had `ok` ever returned non-zero - read the doctor's exit code from the command itself rather than `$?` (SC2181), and capture it without losing it to the assignment - drop an unused local (SC2034) - annotate the three single-quoted child snippets whose `$(...)` is meant for the child shell, not this one (SC2016) 48 assertions still green; shellcheck clean in both repos. Authored-By: claude <agents@vetcoders.io>
…hable Smoke-testing the new row against a substituted `security` reporting a poisoned domain printed nothing at all: canaries.sh runs under `set -e`, and `out="$(cmd)"; rc=$?` never reaches the second statement — the assignment carries the substitution's exit status and aborts the whole run before any row is recorded. So the canary could only ever report PASS. That is the exact class of silent hole the catalog exists to catch, in the catalog itself. - take the status with `|| rc=$?`, which suspends `set -e` for the call - state what PASS actually proves: the search list holds only the operator's own keychains and the default is one of them. "Every path resolves to an existing file" was the weaker claim the doctor stopped making when it learned to grade FOREIGN and HIJACKED - widen the FAIL evidence to name STALE, FOREIGN or HIJACKED Verified both branches: FAIL row recorded and the run exits 1 against a fake poisoned domain; PASS on the live host. Authored-By: claude <agents@vetcoders.io>
codescribe-corpus now owns the HTML reports. Those pages were still a WER table an agent could narrate any way it liked. Rust constants in core/quality/engine_contract.rs are the source of truth (the-engine/v1). Qube HTML, Teacher HTML, and corpus schema v3 carry them. HQ/Cloud columns are proposals. The rejected line "whole text is mutable until session seal" is a forbidden token. docs/THE_ENGINE_CONTRACT.md is the one prose lock. Do not re-derive the three bars from chat.
The Claude artifact is the instrument: one take, one PCM clock, production Silero, SealedSpan.words from the live dump, Whisper mapped backward, word-grain vs utterance-grain, clock-lie as a finding, letters marked as interpolation. Take 01 („no to dobra") is the gold HTML. Contract tests pin span 2 (41 chars / 100 ms) as the canonical clock-lie.
voice-lab already owns the Seal Atlas tab. It classifies private HTML under the Codescribe artifacts tree: 'seal atlas' wins, Qube WER titles fall through to quality_report. Mirror that classifier in engine_contract so corpus HTML that fails the handshake cannot pretend it is the atlas.
97090b8 to
ba8b60a
Compare
Co-authored-by: div0-space <155201063+div0-space@users.noreply.github.com>
Summary
Foundation cut stacked on
fix/the-tail-patches. It hardens four existing product surfaces before the single-owner runtime cuts that follow:User impact
Scope boundary
This is a broad foundation cut, not the owner of every runtime axis. Microphone ownership, committed transcript truth, Agent-thread capture ownership, and delivery routing are intentionally handled by later stacked cuts. Do not reintroduce those decisions here.
Verification
Remote checks currently green:
Operator evidence represented by this cut:
Review status
Copilot found one real corpus-report issue: Layer 1 observation was inferred from tail-patch count rather than the provider-armed signal. A tested local correction exists, but it is not on the remote branch yet; keep the PR review-required until the operator pushes that checkpoint and the checks rerun.
Release notes
Internal reliability and tooling foundation. User-visible reset and transcript-selection changes should be folded into the eventual product release notes, not shipped independently from the later runtime-owner cuts.