Repository navigation
docs: drop numeric server error codes from notes and comments - #37
Conversation
Comments and notes only; no behaviour change. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
There was a problem hiding this comment.
Reviewed as a follow-up to #36: drop numeric server verdict codes from notes and comments. Diff is 10 comment/doc lines across DESIGN.md, PROGRESS.md, queue.rs, runtime.rs, and bugsee-core transport.rs. Walked the live delivery path (HttpTransport → transport::deliver → deliver_queued / memory settle → blacklist_add) rather than the patch alone.
Findings
No P0–P3 issues. No behaviour change: the protocol mapping is unchanged in crates/bugsee/src/transport.rs (12003 → DuplicateDropped, 12004 → TooManySimilar { signatures: vec![] }) and is still covered by crates/bugsee/tests/http_transport.rs. The rewritten English names match those variants and the runtime fallback to queue::signatures_of when the transport returns an empty signature list.
The 12003/12004 sweep of notes and comments is complete. Remaining numeric codes are the implementation/tests (required) and an unrelated 99006 jailbreak-validation note in environment.rs that this PR did not claim to generalise.
Verdict
- Overall risk: Low
- Merge recommendation: Approve
- Most important issues to fix: None
- Positives: Comments stay accurate after the rename (silent drop vs local blacklist vs resume-at-PUT to avoid a duplicate create). HTTP 401/429/503 are correctly left as public status codes. No executable code changed.
Did not run cargo test. CI 38598203554: MSRV, Android lint, and iOS-device lint were green at review; Ubuntu/Windows/macOS test jobs were still running.
Sent by Cursor Automation: Bugsee code review
Tree unchanged here; the clean-up from #37 is already included by the parent merge or applied next. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Comments and notes only; no behaviour change. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
* feat: degrade to in-memory reporting on read-only/ephemeral file systems (#9) Resolve the data directory as explicit option, BUGSEE_DATA_DIR, platform default, then temp dir, taking the first that is actually writable. If none is, or the disk fails mid-run (full, read-only, removed), launch still succeeds and handled errors / manual reports are assembled without capture context and delivered from a bounded in-memory backlog. Native crash handler and panic snapshots are skipped in memory mode. Adds Bugsee::storage_status() so a host can check crash recovery is available and whether the dir may be ephemeral. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix: address review of data-directory fallback (#9) - memory backlog: entries stay queued while in flight so the cap is real; cache the presigned endpoint (no duplicate issue on a retried PUT); honour the flush deadline; clear the backlog in fork children - a requested data_dir / BUGSEE_DATA_DIR is final: unusable means memory mode (explicit_rejected), never another disk location - temp fallback is bugsee-<uid>/<token>, 0700, symlink/owner checked; probe uses create_new with a random name - recover and deliver leftovers from the previous default location once - mid-run degradation is visible (StorageStatus::degraded); fork child falls back to memory instead of failing; warn_once per message, never panics - #[non_exhaustive] on the new public types; ffi test no longer writes to the real data directory 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix: second review round for data-directory fallback (#9) - fork child that falls back to memory clears the panic snapshot path and updates the published storage resolution (dir None, source None) - legacy location is adopted only if <temp>/bugsee and the token dir are real directories owned by us and not our own directory (canonicalised compare) - legacy queue is included in flush, gc'd for orphans, and retired from polling once recovery has run and its queue is empty - do not resolve temp_dir() unless a default location is actually needed - tests: fork memory-fallback, symlinked/aliased legacy dir, end-to-end legacy crashed session + queued report delivered once and cleaned up 🤖 Generated with [Claude Code](https://claude.com/claude-code) * feat(bugsee): populate utc_offset, hardware model and Windows locale Adds crates/bugsee/src/hostinfo.rs with per-platform lookups (libc on unix, hand-declared Win32 calls on Windows; no new dependencies) and threads the results through HostFacts into platform.utc_offset and hardware.model. Closes #16 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(bugsee): address review of hostinfo (iOS model, tm_gmtoff gating, locale) Closes #16 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): exactly-once recovery and queue draining across processes sharing a data dir Several processes on one data directory could double-deliver (or lose) reports: - A session was recovered twice: a peer that listed it before another finished claimed it after the winner had discarded it, producing a phantom report. The marker is now re-checked after the claim is won. - Stale-claim takeover was remove-then-create, so two takers could each remove the other's fresh claim and both hold it. Takeover is now serialised by a guard file and re-validated under it; release only removes the claim's own content. - Claims and session markers identified owners by bare pid: a recycled pid (or a restarted container's pid 1) kept a dead owner's work blocked forever. Owners now carry a process start time (Linux /proc, macOS proc_pidinfo, Windows GetProcessTimes), with a 30 minute lease as a backstop. - On Windows process liveness always answered "dead", so live peers' claims and markers were taken over. Implemented via OpenProcess/GetExitCodeProcess. - queue::gc_orphans at a peer's launch reaped another process's in-flight enqueue (sidecars before the bundle rename), stranding the bundle and losing the report. Only files untouched for 60 s are reaped now. - Leftover claims of discarded sessions are swept. Adds tests/multiprocess.rs (real child processes racing; SIGKILLed holder) plus unit tests for claims, process identity and queue gc. Closes #14 🤖 Generated with [Claude Code](https://claude.com/claude-code) * feat: define the shutdown/flush contract; opt-in SIGTERM/SIGINT flush - Document per exit path what is persisted, delivered and recovered (Bugsee::stop docs, DESIGN.md section 8). - Bugsee::shutdown(timeout) = flush then stop. A stop during a flush now aborts the flush so the clean-stop acknowledgement bound holds. - Bugsee::install_termination_flush: opt-in, chaining SIGTERM/SIGINT hook (self-pipe; flush on a normal thread; re-raises with the default disposition so the exit status is unchanged) and a Windows console control handler. - LaunchOptions::flush_at_exit: atexit-based clean stop for deliberate process::exit. - Subprocess tests for every exit path. Closes #8 🤖 Generated with [Claude Code](https://claude.com/claude-code) * feat: general PII scrubber and report rate limiting Add a regex-free scrubber (emails, token shapes, keyed secrets, card-like numbers, home-dir usernames; allow/deny/sensitive_keys; off switch) applied at report assembly over every JSON section after before_send, and token bucket rate limiting (per-signature + global, crash floor, bounded memory) applied before the durable queue, surfaced via DropReason::RateLimited and a bugsee.reports_suppressed attribute. Both on by default, opt-out via LaunchOptions::disable_scrubbing / disable_rate_limiting. Closes #11 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): arbitrate cross-process claims and session liveness with OS file locks Review of the pid/start-time claim scheme found it unsound where #14 matters most: containers with separate pid namespaces sharing a volume (both apps pid 1) cannot compare pids or start times, so a peer's live claim was judged stale and its live session reported as a crash. Pid-in-file schemes also needed a lease, a takeover guard with a timer, and identity checks, each with its own race. Replace them with kernel locks: fcntl(F_SETLK) on unix (per process, released on death, not inherited by fork) and LockFileEx on Windows, on dedicated *.lock files (queue/<bundle>.lock, sessions/<gen>.recover.lock, and sessions/<gen>.lock held for a session's life). Nothing can go stale, so there is no lease, takeover or start-time logic. An in-process registry (pid-keyed, spin-locked with the holder's pid so a fork child cannot inherit a held lock) excludes threads and keeps a process from ever opening one lock file twice, which POSIX would punish by dropping the lock. - .alive stays a bare pid and lock files are new names, so older builds sharing a directory keep working (no exactly-once between old and new; documented). Markers with no .lock beside them still use the pid probe. - Liveness probe: zombies count as dead (Linux /proc state, macOS proc_pidinfo); Windows uses WaitForSingleObject. - Leftover lock files of finished items and dead sessions are swept when free. - Tests: a live peer whose marker names a colliding pid (parent's, init's) is not recovered until it really dies, then exactly once; concurrent claimers; probing never drops a held lock; zombie probe. Closes #14 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix: harden the termination hook and shutdown after review - Detect a handler installed after ours (tokio, signal-hook) by asking the kernel what is in force; flush only, never terminate the host. - A stop no longer waits on the uploader to end the session cleanly (an HTTP call cannot be interrupted); delivery is documented at-least-once. - Fork-child re-install ordering, SIG_IGN left alone, watcher-alive and pending-signal tracking, host sa_mask/SA_ONSTACK/SA_NODEFER/SA_RESETHAND carried over, fork-aware install lock, pipe2, clean rollback on failure. - flush_at_exit is unix only and skipped in fork children. - Flush aborts between deliveries on stop (memory and every queue dir). - Tests: late/SA_SIGINFO/ignored handlers, SIGINT, double signal, fork child, hanging transport; no fixed sleeps; temp dirs cleaned. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix: keep draining existing data dirs that cannot take new files (#9) - an existing directory that fails the write probe / session open is still recovered from and drained (read + delete only); claims are lenient there and a crash that cannot be re-queued on disk is delivered from memory; delivered but undeletable bundles are remembered so they are not resent every drain - in-memory TooManySimilar blacklist (cleared in fork children) - a clean stop gives the in-memory backlog a last bounded delivery attempt - legacy temp location adopted whenever it exists and is trusted, even if no new default directory was usable - rotation tick rewrites a missing liveness marker after a wipe and marks the recorder degraded - DESIGN.md: failure table, legacy/flush wording 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(scrub): second-review hardening of the PII scrubber Numeric secrets under secret keys and integer card numbers are redacted; colon-form prose ("invalid token: expected ident") and "my_app::token:" are left alone; Cookie headers take the whole line; escaped quotes inside quoted values are honoured; the "signatures" exemption moves out of the shared key matcher into crash.json handling; tighter token shapes (AWS, hf_, sk-, prose after Bearer); contextual stem exclusions (author, tokenizer, session_state, method_signature); exact key/sig only redact credential-looking strings; request.json no longer double-scrubs metadata; file names (icon@2x.png) and /Users/Shared are not PII; the pre-check handles \u escapes and separated key stems. Test secrets are assembled from halves for push protection. Refs #11 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): harden the file-lock claims (second review) - A launch that cannot hold <gen>.lock removes the file instead of leaving an unheld one, which every peer read as "that session died" and recovered (and deleted) the live session. Contended claims are retried briefly; an empty, fresh marker with a free lock is skipped (the pid is written after locking); on unix a probe uses F_GETLK and never takes the lock. - Filesystems that reject locks (ENOLCK, EOPNOTSUPP, ENOSYS, EINVAL) degrade to thread-only claims with a one-time message, and probes report unknown so recovery falls back to the marker pid, instead of blocking the queue forever. - An unclean shutdown (detached worker/uploader) keeps the session lock until the process exits so peers cannot recover a session still being written. - Registry keys are canonicalised; a fork child resets the registry from the fork hook; old-scheme .claim/.claim.takeover files are swept. - Tests: the real pid-namespace hazard (marker pid that does not exist here, reaped pid), crashed sessions with free lock files, the lock-race launch, unsupported-lock injection, and the probe/launch ordering cases. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix: keep the scheduled memory drain independent of the stop flag The stop-time memory drain relies on the scheduled pass having had its one attempt; only an explicit flush aborts on stop. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * test: gate unix-only helpers so Windows clippy stays clean (#9) 🤖 Generated with [Claude Code](https://claude.com/claude-code) * feat(native): best-effort flush of in-flight events at crash time A producer only queues an entry; the capture worker appends it later, so the last events before a fault (still in the channel) were lost. Measured: a 1500-event burst followed by SIGSEGV recovered ~50-60 events. After the marker is complete, the fatal-crash handler now does one atomic ticket, one non-blocking write(2) (Windows: SetEvent) to a pre-created wake handle and polls an atomic for at most 50 ms. A helper thread made at launch posts a Flush through the normal FIFO channel and publishes the worker's ack. No allocation or lock on the crashing thread; a stuck worker costs only the budget. A main-thread panic nothing will catch runs the same bounded flush. Tests: core handshake/stuck-worker/deregistration, torn part-tail recovery, and subprocess crash tests (burst then SIGSEGV recovers every event; wedged worker still dies promptly and reports). fork_safety's FileTransport now writes atomically since a dying worker can linger mid-upload. Closes #13 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): queue the previous crash on the launch path before launch returns A host that crashes again right after launch (crash loop) could die before the worker thread recovered the previous crash. launch now runs a bounded, newest-first recovery pass (8 sessions, 500 ms budget via LaunchOptions::launch_recovery_budget, 4096-entry scan cap) that enqueues the reports durably before returning; upload stays async and the worker's pass picks up the rest, skipping what the launch pass attempted. Closes #10 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix: drain every trusted unwritable default tree, not just one; stale unlinkable claims proceed (#9) - recovery_dirs: all rejected existing candidates (incl. the private temp tree) are drained/recovered even when a later default wins; core takes extra_data_dirs - a stale claim that cannot be unlinked is Unavailable, not Taken - seed the in-memory blacklist from the on-disk one 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(scrub,runtime): second re-review fixes Stem exclusions for cancellation_token, www-authenticate/authentication, token_endpoint/url/uri/expiry/ttl; the colon-prose leniency applies only to bare 'token', so password/passwd/secret/api_key values are always redacted; the rate limiter now runs before the on-disk blacklist read so a denied storm is lock + hash only (test counts disk reads). Refs #11 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix: PR #31 review and CI fixes - Android: errno via libc::__errno (and the BSD variants), not __errno_location. - Gate the flush_at_exit statics and import on unix (Windows clippy). - SA_RESETHAND: the handler decides "terminate" before emulating the reset and ships it in the pipe byte; the watcher no longer re-reads the slot. New test with a one-shot host handler. - Windows Ctrl+C/Break now run shutdown like close/logoff/shutdown; document that a host which swallows the event registers its handler after ours. - DESIGN section 6 no longer says guard Drop flushes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): run launch-path recovery on a bounded helper, after the crash handlers Address review of the launch-path recovery pass: - the pass runs on a helper thread and launch waits at most the budget, so a blocked before_send cannot stall or deadlock launch; the worker holds its own recovery until the pass is done (bounded) and skips what the pass claimed - Recorder::recover_on_launch_path is called by launch_with after the native handler and panic hook are installed, outside all locks - dedup repeated identical recovered crashes against the queue and cap it - recovered crashes carry no scope on either path; claimed-only attempted set; scan cap counts session markers only; docs, latency numbers, deterministic tests, facade test 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(native): harden in-flight crash flush (review feedback, #13) - One process-wide wake pipe/event and ONE helper thread, created once and re-created in a fork child; the flush target is swapped under a fork-aware lock (last registered wins). Handles are never closed, so no descriptor-reuse race and no SIGPIPE for C hosts; shared ticket counters are now coherent. - Dying flag raised by the crash handler before waking the helper: claims and recoveries start nothing in the window, so a report is never half-delivered (removes the 5 s UNOWNED_GRACE stall in fork_safety; test-helper workaround reverted). - Skip the wait when the crashing thread is the worker or helper. - Handler waits with poll (Unix) / ack event + QPC (Windows) instead of nanosleep / Sleep(1); pipe2(O_CLOEXEC) on Linux/Android; parent's fds closed in a fork child; unpublish-then-republish ordering. - Panic hook shares DEFAULT_BUDGET; docs cover host-caught panics, ordering, Windows filter order and last-registered-wins. - crash_inflight runs on Windows too (cfg any(unix, windows)); a hidden budget setter lets the burst test use a long budget deterministically, a small burst covers the real 50 ms budget, and the wedged test now requires the handler to wait the budget and no longer (all three fail without the feature). 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): review round 3 on lock claims - A leftover <gen>.lock that cannot be opened (0444, read-only volume) or whose lock call errors probes as Unknown, not Held, and the probe opens read-only, so it can no longer hide a crash forever. - Lock-less sessions (filesystem rejects locks) keep a <gen>.beat heartbeat file, touched every 2 s by the worker; a beat younger than 60 s means alive whatever pid the marker names, so a peer in another pid namespace is never mistaken for a crash. Without a beat the pid is probed as before. - claim_pending re-runs the liveness decision under the recovery claim, so a session that finished locking since the scan is not torn down. - Windows maps ERROR_INVALID_FUNCTION/NOT_SUPPORTED/CALL_NOT_IMPLEMENTED to Unsupported, so the documented degrade exists there; the dead-code allow is gone. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * feat(health): local session health records for release health (#12) Records how each session ends (ok/errored/crashed/abnormal) behind an off-by-default option. Recovered crashes settle their session exactly once. Nothing is sent: the backend has no session-health ingest endpoint. Refs #12 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): make the lock-less heartbeat robust to clock skew and slow launches - A heartbeat whose mtime is in the future (file server clock ahead) counts as fresh, like marker_is_fresh; only a demonstrably quiet beat condemns a session. The staleness window is 3 minutes, above NFS attribute-cache lifetimes. - Launch-time recovery (before the worker loop starts) now keeps the beat going. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): keep unique crashes the launch pass cannot queue; scan newest-first without a cap; keep attempts progressing - Admission::Full no longer discards the session; it is left to the worker - find_pending_bounded reads all marker names, sorts, then probes (no capped readdir), and includes sessions with prior attempts so a fault during recovery still reaches MAX_RECOVERY_ATTEMPTS 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(health): address review of release health (#12) Register the backend session eagerly at launch when release_health is on, finish records on any non-panicking stop, ignore stale panic snapshots, memory-mode and empty-dir guards, ack tombstones, generation floor, no file I/O under the facade lock, gc, monotonic durations, unstable/hidden API, docs moved to section 10. Refs #12 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): tried sessions first, and a queue-cap refusal is not an attempt - find_pending_bounded returns sessions with prior attempts before newer ones, so MAX_RECOVERY_ATTEMPTS progresses even if recovering the newest keeps faulting - Admission::Full gives the attempt back (undo_attempt) - make the full-queue test race-free 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(native): address review of the in-flight flush (#13) - P1: a dying process skips the worker's recovery scan, orphan GC and legacy drains and reaches worker_loop at once, so the queued Flush is answered. - P2: the flush target stays registered until the worker has acked Stop, so a crash during shutdown still waits for queued entries. - P2: Windows wait uses integer QPC math (no floating point in the filter). 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix: never recover the same directory twice in one launch (#9) - legacy_is_adoptable compares against the chosen dir AND recovery_dirs by canonical path (Windows: temp fallback is the legacy location) - spawn path dedups legacy/extra dirs against the data dir and each other - seed the in-memory blacklist from legacy/extra dirs too - test: an existing temp tree is offered in exactly one role 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): a lock file that vanishes under a scan is not evidence of a crash probe returned Free both for an unheld lock and for a file that disappeared between a caller's exists() and the open. A session without kernel locks unlinks its <gen>.lock before writing its heartbeat, so a peer that saw the file could classify that live session as crashed. probe now reports Probe::Vanished for a missing file (no pre-check), and session_is_crashed treats it like Unknown and falls through to the heartbeat/pid decision. Test injects the vanish between the existence check and the probe's open. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix: a stop during a flush's memory pass must not start a disk upload Re-check `stopping` after the memory drain and after each claim, so the uploader reaches its stop arm and the stop-time memory drain runs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(health): keep panic.info for abort correlation, no eager registration in memory mode Address review on #35: the worker-thread snapshot is expired by a detached thread after the process survived 1 s instead of deleted in the hook, and the eager session registration requires a disk session. Refs #12 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): a failed enqueue write is not a recovery attempt 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(health): do not expire panic.info from a hook timer Address re-review on #35: leave the worker-thread snapshot alone (abort correlation) and rule out a surviving process's leftover at recovery with a 5 s tail. Adds a worker-thread aborting-panic case to the crash matrix. Refs #12 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): a session the launch pass defers is picked up even if the pass outlived the worker's wait 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(health): mark a panicking drop so a slow unwind is still a crash Address re-review on #35: panic_is_recent no longer guesses from the clock when the recorder was dropped while unwinding; empty rotation directories no longer count as capture activity. Refs #12 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): deferred launch-pass recovery uses the recovery directory, not only the capture tree 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(scrub): do not redact public OIDC/OAuth metadata keys authorization_endpoint/url/uri, oauth* names (oauth_token and oauth_client_secret still hit the token/secret stems), auth_method and token_endpoint_auth_* are not secrets. Tests in bugsee-core and bugsee-reqwest (query params and headers). Refs #11 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix: remember every undeletable retired bundle, not just delivered ones (#9) One retire_queued helper for all arms (delivered, TooManySimilar, duplicate, permanent, retries exhausted); recovered crashes consult the in-memory blacklist. Test: terminal verdicts on a read-only queue are sent once. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * test(core): wait on an observable condition in the full-queue test 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(scrub): narrow the oauth exclusion to public metadata oauth_verifier, oauth_pin and a bare oauth key stay redacted again; oauth_client_id/redirect/scope/issuer/endpoint/... and auth_url/uri/endpoint are public. Tests in bugsee-core and bugsee-reqwest. Refs #11 🤖 Generated with [Claude Code](https://claude.com/claude-code) * Merge origin/fix/issue-9 into fix/issue-11 Adapt the new storage_failures test to assemble_docs' scrubber argument. Refs #11 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): declare kernel32 calls once (clashing_extern_declarations on Windows) process.rs and crash_flush.rs declared WaitForSingleObject with different handle types. Both now use one win32 module; no other duplicate extern declarations exist in bugsee-core, bugsee-native or bugsee. Verified with a standalone windows-msvc clippy of the affected modules. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(scrub): free-text oauth_verifier/oauth_pin and short pin/otp codes Free-text keyed matching now follows the structured matcher for the OAuth 1.0a verifier/PIN and redacts 4-10 digit pin=/otp= values. Refs #11 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(scrub): structured matching redacts code_verifier (PKCE) Refs #11 🤖 Generated with [Claude Code](https://claude.com/claude-code) * docs: keep the corrected native feature comment from main * fix: de-duplicate the unix libc dependency after merging main (#27's optional form vs the unconditional one) 🤖 Generated with [Claude Code](https://claude.com/claude-code) * fix(core): make the crash-flush handshake robust on slow runners Root cause of the windows flake in a_stuck_worker_costs_only_the_budget: the helper's ack wait (250 ms) was shorter than the 5 s budget the test asked for. After un-wedging, the worker had to drain a backlog (file creation on Windows is slow) and the helper gave up before the ack, never retrying, so the handler waited out its whole budget. The helper now waits 10 s (it only bounds a permanently wedged worker). The test also stopped assuming a 150 ms sleep wedges the worker (it waits for the sampler to report entry), uses generous budgets and bounds, and no longer relies on sleeps. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * docs: describe backend behaviour generically in release-health notes Refs #12 🤖 Generated with [Claude Code](https://claude.com/claude-code) * docs: keep design notes generic (no private-component references) (#36) Generalise the mobile native capture and out-of-process monitor design notes, the FFI audit and a few code comments so they describe behaviour in generic terms (the backend, the ingestion pipeline, existing report variants) instead of naming components of other projects. No behaviour change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * docs: drop numeric server error codes from notes and comments (#37) Comments and notes only; no behaviour change. 🤖 Generated with [Claude Code](https://claude.com/claude-code)


Follow-up to #36: generalises the remaining notes and comments that quoted numeric server verdict codes (DESIGN.md, PROGRESS.md, and a few comments in the queue, runtime and transport code). Comments and notes only; no behaviour change.
🤖 Generated with Claude Code