Skip to content

Logging and speech to text refactoring - #18

Open
vinniefalco wants to merge 62 commits into
cppalliance:masterfrom
vinniefalco:master
Open

Logging and speech to text refactoring#18
vinniefalco wants to merge 62 commits into
cppalliance:masterfrom
vinniefalco:master

Conversation

@vinniefalco

Copy link
Copy Markdown
Member

In the commit log and plans

Move asset and gateway-config route tests into child modules so the production modules stay below their ratchet ceilings without changing behavior. Record the measured parent and test-module sizes in `module-ceilings.toml`.
The gateway command line has one job, to serve, so the `serve` subcommand and the positional config argument come out: the bare invocation serves with boot discovery, and `--config PATH` names an explicit config, winning over `PROMPTFORGE_GATEWAY_CONFIG`. `parse_args` drops its subcommand match and rejects any non-flag argument as a usage error; every repository-owned caller - the systemd unit, the installer, the Workshop launcher, the tray autostart entries, the release-test workflow, and the guides - moves to the new shape in the same change.

- `init_logging` now runs after the already-running handoff check, so `--help`, `--version`, and a second-instance handoff never rotate the running gateway's log; the handoff's browser-open failure reports through `eprintln!` because no subscriber is installed yet.
- `--version` is accepted at any position in the argument list, and a second `--config` is a usage error.
- On the handoff path in `relaunch.rs`, the connection-file resolution warning is dropped with no subscriber installed; the boot that follows logs its own failure once logging is live.

Plan: 2026-09-05-1-gateway-logging-cli
Move the log pipeline out of `crates/gateway/src/main.rs` into a new `gateway-logging` crate so the queue, rotation, sink, and worker lifecycle are owned and tested in one place. The crate exports `LogConfig`, `LogRuntime`, `LogWriter`, and the opaque `LogError`; `LogRuntime::start` rotates and opens `gateway.log`, spawns one worker thread, and `shutdown` closes admission, drains, flushes, and joins. `main.rs` keeps global subscriber installation, holds the returned `LogRuntime`, and shuts the logger down last so fatal error chains logged through `log_error_chain` reach the disk.

- Queue policy is fixed in `queue.rs`: `CAPACITY` of 8192 records, drain `BATCH` of 256, one deque per `LogPriority` under one mutex. A full queue evicts the oldest Debug, then Trace, then Info; Warn and Error records are never evicted, and a producer with no eligible record blocks on a condition variable.
- `LogEventWriter` buffers every `Write` call for one event and enqueues on `Drop`, moving the buffer through `String::from_utf8` and paying the lossy copy only for invalid UTF-8. It is public but `#[doc(hidden)]` because `MakeWriter::Writer` cannot name a private type.
- A failed write or flush on the file sink falls back to synchronous stderr, and a worker panic surfaces from `shutdown` as a `LogError` that `is_io` classifies separately from filesystem and spawn failures.
- Rotation keeps one previous run: an existing `gateway.log` renames to `gateway.log.1`, overwriting the older rotation.

Plan: 2026-09-05-1-gateway-logging-cli
A failed gateway run must be discoverable without config knowledge, and no log record may carry secret material. The gateway gains a `diagnostics` subcommand that prints a read-only JSON report of the state dir, config, logs, and connection file, the log rotation retains five previous runs, and every queued record crosses a redaction pass that masks bearer tokens, authorization and cookie header values, and `api_key` assignments.

- The log layout gets one owner: `LogConfig::log_path` and `LogConfig::retained_log_paths` name every path, so `diagnostics_json` enumerates the logs without starting a runtime and `open_log_file` rotates the same chain.
- Redaction sits at the one chokepoint every record crosses: `LogEventWriter::drop` masks the formatted line with `redact_line` before the record enters the queue.
- `is_running` in `shared-sidecar` is read-only: a stale or corrupt connection file reads as not-running and stays on disk for the next launch to clean.
- `discover_in(explicit, gather)` splits the report's config discovery into a testable inner in the `resolve_in` pattern; unit tests pin the explicit, discovered, profile-fallback, and gather-failure branches, and both `diagnostics` integration tests assert `config.path` and `config.exists`.
- `diagnostics` runs before the handoff check and before logging starts; it never serves, rotates a log, parses a config, or mutates the state directory, and `parse_diagnostics_args` accepts only `--config PATH`.
- The generated config carries `# Diagnostics: promptforge-gateway diagnostics` as a comment, so the file stays parseable.
- New tests pin the sink's stderr fallback on rejected writes and flushes, saturation that never evicts or duplicates Warn or Error records, and a shutdown that writes every record in enqueue order before the join returns.
- `Sink::Null` and `Sink::is_stderr` are `#[cfg(test)]` seams for the fallback and latency tests.
- `production_logging_stays_within_latency_budget` is `#[ignore]`d; it runs only through `cargo test -p gateway-logging --release -- --ignored`.

Plan: 2026-09-05-1-gateway-logging-cli
The public `serve` docs linked the private `GRACEFUL_DRAIN_TIMEOUT` and `WORKER_JOIN_TIMEOUT` constants, which `RUSTDOCFLAGS="-D warnings" cargo doc` rejects as private intra-doc links. The constants are now plain backticked names. The break was introduced in 7f24bb0 and predates the logging work.
The logging contract now lives in the documentation, and the dependency boundary has a test that enforces it. A new integration test `the_manifest_declares_only_the_tracing_dependencies` reads the crate's own `Cargo.toml` and fails when any dependency other than `tracing` and `tracing-subscriber` appears. The `gateway-logging` `AGENTS.md`, the gateway `README.md`, and both gateway guides now describe the `gateway.log` rotation, the five-run retention, the redaction pass, and the `promptforge-gateway diagnostics` report.

- The boundary test rejects build, dev, and target-specific dependency tables in addition to extra `[dependencies]` entries, so the allowlist covers every way a crate can enter the build. It parses the manifest line by line and adds no TOML parser dependency.
- `AGENTS.md` records that `LogEventWriter` is public but `#[doc(hidden)]` because `MakeWriter::Writer` cannot name a private type, and that the test seams `Sink::Null` and `Sink::is_stderr` exist only under `cfg(test)`.

Plan: 2026-09-05-1-gateway-logging-cli
Plan: 2026-09-05-1-gateway-logging-cli
Characterize current batch routing and realtime transcription before the speech subsystem changes. Separate physical-model routing checks from legacy socket cases, and add deterministic coverage for stream policy, generation, origin, ordering, shutdown, and final-model authority.

- `crates/gateway-stt/tests/it/main.rs` now separates batch model selection from legacy socket characterization.
- `fixture_runtime_with_models` starts caller-selected interim and final fixture models on a dedicated thread, while `TestServer::shutdown` stops the server before blocking runtime shutdown.
- `batch_selects_each_loaded_physical_model_by_name` changes one vocabulary token to verify direct routing to each loaded physical model.
- `final_model_segments_and_tail_are_authoritative_at_stop` verifies that interim text stays provisional and that the final worker produces committed segments and the remaining tail.
- `crates/gateway-stt/tests/it/batch.rs` keeps its physical-model case ignored because it requires `tests/fixtures/`. Other native speech cases remain ignored for the same reason.

Design: extends oversized-unit @ crates/gateway-stt/tests/common/mod.rs
Design: new flag-parameter @ crates/gateway-stt/tests/common/mod.rs::fixture_runtime deps: bool
Design: replaces flag-parameter @ crates/gateway-stt/tests/common/mod.rs::fixture_server deps: bool was: crates/gateway-stt/tests/it/stt.rs::fixture_server
Design: new stringly-typed @ crates/gateway-stt/tests/common/mod.rs::multipart_body deps: &[u8],&str boundary: wire
Design: new stringly-typed @ crates/gateway-stt/tests/common/mod.rs::transcribe_batch deps: &[f32],&str,SttState boundary: wire
Design: replaces oversized-unit @ crates/gateway-stt/tests/it/legacy_stream.rs was: crates/gateway-stt/tests/it/stt.rs
Design: new pure-function @ crates/gateway-stt/tests/it/legacy_stream.rs::transcript_words deps: &str
Design: new pure-function @ crates/gateway-stt/tests/it/legacy_stream.rs::distinguishing_word deps: &str,&str
Deferred: physical-model characterizations remain ignored without whisper fixtures
Plan: 2026-09-05-2-generic-realtime-stt
Add an ignored native integration target that loads the packaged runtime and exact model fixture. It fixes interim, final, transcript-conditioning, glossary-prompt, silence-gating, and cleanup behavior so the upcoming engine split can be checked against one baseline.

- `packaged_runtime_preserves_native_transcription_contract` copies the exact tiny model into a temporary directory, loads the packaged library, and configures the same model for interim and final decoding.
- `JFK_TRANSCRIPT` anchors assertions for the full interim transcription, unprompted and transcript-conditioned final output, glossary bias, silence gating, and conditioning divergence.
- `std::fs::remove_file` verifies that dropping both engines releases the copied model.
- `#[ignore = "requires whisper test fixtures (tests/fixtures/)"]` keeps native characterization outside default test runs because it requires packaged runtime fixtures.

Design: new oversized-unit @ crates/gateway-transcribe/tests/native_whisper.rs::packaged_runtime_preserves_native_transcription_contract
Plan: 2026-09-05-2-generic-realtime-stt
Add canonical client, server, session, error, and sequence fixtures for realtime transcription. Validate one shared fixture set in Rust and the Workshop UI to pin strict fields, event ordering, capacity errors, item isolation, and hypothesis semantics before the subsystem changes.

- `realtime-wire-fixtures.mjs` consumes the Gateway-owned fixtures directly, which makes Rust and Workshop share one canonical corpus.
- `canonical_realtime_events_are_complete_strict_and_round_trip` enforces exact case sets, strict field sets, identifier separation, session defaults, and hypothesis composition.
- `canonical_realtime_sequences_cover_valid_and_invalid_contract_paths` validates event order, error correlation, minimum audio, and recovery metadata across all declared sequences.
- `crates/gateway-stt/tests/it/realtime_fixtures.rs` does not call production realtime parsers or session handlers, so these tests pin fixture consistency rather than implementation conformance.

Design: new oversized-unit @ crates/gateway-stt/tests/it/realtime_fixtures.rs::assert_server_event_fields deps: &Value,&str
Design: new oversized-unit @ crates/gateway-stt/tests/it/realtime_fixtures.rs::canonical_realtime_events_are_complete_strict_and_round_trip
Design: new oversized-unit @ crates/gateway-stt/tests/it/realtime_fixtures.rs::canonical_realtime_sequences_cover_valid_and_invalid_contract_paths
Violates: A2 - not determinable from diff
Violates: A96 - not determinable from diff
Deferred: batch transcription characterization is absent
Deferred: native two-model characterization is absent
Plan: 2026-09-05-2-generic-realtime-stt
Give the backend-neutral engine its intended role-specific identity. Update workspace metadata, runtime references, tests, fixture exclusions, and documentation while preserving the engine implementation.

- `crates/gateway-stt-engine` changes the package, path, and Rust import identity.
- `crates/gateway-stt-engine/src/engine.rs` and the other engine source files move with 100 percent similarity.
- `crates/gateway-stt-engine/tests/native_whisper.rs` changes only its import path. The commit adds no test assertions or compatibility crate.

Design: new shotgun-surgery @ crates/gateway-stt-engine/Cargo.toml
Violates: A2 - not determinable from diff
Violates: A96 - not determinable from diff
Plan: 2026-09-05-2-generic-realtime-stt
Move per-take speech state out of decode workers so each decode job is independent and every take owns its lifecycle. Carry immutable guidance and finalized history with each job, aggregate segment results in one ordered pipeline, and preserve interim fallback after failures.

- `Take` consolidates guidance, finalized history, segmentation, local agreement, transcript aggregation, completion, and failure behind one per-take state object.
- `FinalJob` carries all decode inputs and the reply channel, so final-model workers retain no take identity or transcript between jobs.
- `Segmenter` moves from the engine surface to the gateway speech surface with take orchestration.
- `run_final_pipeline` serializes closed segments and the closing tail, records only successful sample boundaries, and stops decode work after the first failure.
- `next_interim` promotes token prefixes confirmed by two hypotheses and keeps committed text append-only.
- `TestServer` now bounds fixture server and runtime cleanup at 30 seconds.
- `guidance` has no end-to-end assertion from runtime activation through both batch and streaming decode paths.

Design: new surface-growth @ crates/gateway-stt-engine/src/engine.rs::SttEngine::transcribe boundary: pub
Design: new surface-growth @ crates/gateway-stt-engine/src/engine.rs::SttEngine::transcribe_final boundary: pub
Design: new shared-parameter-cluster @ crates/gateway-stt-engine/src/engine.rs::SttEngine::transcribe_final boundary: pub
Design: new shared-parameter-cluster @ crates/gateway-stt-engine/src/final_pass.rs::FinalTranscriber::transcribe
Design: new surface-growth @ crates/gateway-stt/src/lib.rs::Segmenter boundary: pub
Design: new value-object @ crates/gateway-stt/src/take.rs::AgreementSnapshot
Design: new parameter-object @ crates/gateway-stt/src/take.rs::Take
Design: new shared-mutable-state @ crates/gateway-stt/src/take.rs::TakeState
Design: new message-passing @ crates/gateway-stt/src/take.rs::FinalPipeline
Design: new pure-function @ crates/gateway-stt/src/take.rs::matching_token_prefix_end deps: &str,&str
Design: new pure-function @ crates/gateway-stt/src/take.rs::token_spans deps: &str
Design: new pure-function @ crates/gateway-stt/src/take.rs::after_token_prefix deps: &str,usize
Design: new oversized-unit @ crates/gateway-stt/src/take.rs
Design: extends oversized-unit @ crates/gateway-stt/tests/common/mod.rs
Design: extends oversized-unit @ crates/gateway-stt/tests/it/legacy_stream.rs
Violates: A2 - not determinable from diff
Violates: A96 - not determinable from diff
Deferred: configured guidance propagation lacks an end-to-end assertion
Plan: 2026-09-05-2-generic-realtime-stt
Make speech decoding backend-neutral while keeping Whisper construction, prompting, progress, and error translation in a safe adapter. Inject model factories into dedicated workers so model creation and decoding stay on their owning threads. Preserve batch and native transcription behavior through relocated and expanded tests.

- `Decoder` and `ModelFactory` establish backend strategy contracts, while `WhisperModelFactory` contains safe model construction and decode policy.
- `SttEngine::new` constructs each decoder on its owning worker and returns initialization errors before activation.
- `native_whisper.rs` relocates native characterization and adds isolation, optional-final, and load-progress checks. These fixture-dependent tests remain ignored.
- `std::sync::mpsc::channel` leaves both worker job queues unbounded.
- `require_fixture` duplicates native fixture loading across unit and integration test support.

Design: new strategy @ crates/gateway-stt-engine/src/decoder.rs::Decoder boundary: pub
Design: new surface-growth @ crates/gateway-stt-engine/src/decoder.rs::Decoder boundary: pub
Design: new strategy @ crates/gateway-stt-engine/src/decoder.rs::ModelFactory boundary: pub
Design: new surface-growth @ crates/gateway-stt-engine/src/decoder.rs::ModelFactory boundary: pub
Design: new facade @ crates/gateway-stt-backend-whisper/src/lib.rs boundary: pub
Design: new constructor-injection @ crates/gateway-stt-engine/src/engine.rs::SttEngine::new
Design: new flag-parameter @ crates/gateway-stt-backend-whisper/src/model.rs::WhisperDecoder::load
Design: new flag-parameter @ crates/gateway-stt-engine/src/worker.rs::worker_loop deps: &dyn ModelFactory,&std::sync::mpsc::Receiver<Job>,&std::sync::mpsc::SyncSender<Result<bool, TranscribeError>>,bool
Design: replaces shared-mutable-state @ crates/gateway-stt/src/runtime.rs::SttSlot was: crates/gateway-stt-engine/src/slot.rs::SttSlot
Design: new global-state @ crates/gateway-stt-backend-whisper/src/prompt.rs::NATIVE_TEST
Design: new global-state @ crates/gateway-stt-backend-whisper/tests/native_whisper.rs::NATIVE_TEST
Design: new clone-block @ crates/gateway-stt/src/test_fixtures.rs
Design: new clone-block @ crates/gateway-stt/tests/common/mod.rs
Violates: A2 - crates/gateway-stt/src/runtime.rs is not determinable from diff
Pending: N9 - compounds
Deferred: model worker queues remain unbounded
Deferred: native Whisper characterization remains ignored behind external fixtures
Plan: 2026-09-05-2-generic-realtime-stt
Add mandatory checks for workspace edges, module cycles, public exports, source size, migration targets, and unsafe isolation. Split compiler-resolved checks from strict policy checks and run both in the normal continuous integration path.

- `parseCargoModulesDot` is a 110-line parser that collapses item edges into module edges and rejects malformed graph output.
- `module-ceilings.toml` files set strict source and public-root ceilings and name each planned migration target.
- `architecture` runs with pinned tool versions in the normal continuous integration job.

Design: new global-state @ crates/gateway-stt/tests/it/architecture.rs::workspace_metadata
Design: new oversized-unit @ tools/check-stt-architecture.mjs::parseCargoModulesDot deps: output
Violates: A2 - not determinable from diff
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Bounded queues now reject excess speech jobs, contain worker panics, and join decoding threads during shutdown. Feature-gated scripted decoders provide deterministic downstream and route tests without a production constructor.

- `Transcriber` replaces unbounded submission with fixed-capacity admission and owns a shared stop flag that closes admission before its thread joins.
- `test_fixtures` exposes scripted factory and decoder controls only when consumers enable the fixture feature.
- `TranscribeError::Overloaded` reports a full queue without waiting, and `TranscribeError::WorkerPanicked` separates panics from a disconnected worker.
- `scripted_workers_can_be_injected_without_a_production_constructor` sends a multipart request through the router and checks the scripted transcript and decoded samples.

Design: new feature-flag @ crates/gateway-stt-backend-whisper/Cargo.toml::test-fixtures
Design: new feature-flag @ crates/gateway-stt-engine/Cargo.toml::test-fixtures
Design: new surface-growth @ crates/gateway-stt-engine/src/engine.rs::SttEngine::shutdown boundary: pub
Design: new surface-growth @ crates/gateway-stt-engine/src/error.rs::TranscribeError boundary: pub
Design: new surface-growth @ crates/gateway-stt-engine/src/lib.rs::test_fixtures boundary: pub
Design: new shared-mutable-state @ crates/gateway-stt-engine/src/test_fixtures.rs::ScriptedDecoder
Design: new temporal-coupling @ crates/gateway-stt-engine/src/test_fixtures.rs::ScriptedDecoder
Design: new shared-mutable-state @ crates/gateway-stt-engine/src/worker.rs::Transcriber
Design: extends flag-parameter @ crates/gateway-stt-engine/src/worker.rs::worker_loop deps: &AtomicBool,&dyn ModelFactory,&mpsc::Receiver<Job>,&mpsc::SyncSender<Result<bool, TranscribeError>>,bool
Design: new feature-flag @ crates/gateway-stt/Cargo.toml::test-fixtures
Design: new surface-growth @ crates/gateway-stt/src/lib.rs::test_fixtures boundary: pub
Design: new facade @ crates/gateway-stt/src/lib.rs::test_fixtures
Design: new constructor-injection @ crates/gateway-stt/src/runtime.rs::SttRuntime::from_scripted_engine
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Run backend-neutral ownership and bounded queue tests under a pinned interpreter. Make decode jobs and engine policy explicit values, bound worker startup and cleanup, then place the approved serving-log correction before final release verification.

- `DecodeRequest` replaces separate mode, sample, guidance, and finalized-history arguments with one owned job. `EnginePolicy` owns validated capture settings, the hardware capability fact, and the shared startup timeout.
- `vibe/2026-09-05-2-generic-realtime-stt.md` records completed markers through the interpreter work, replaces commit hashes in completed headings, and inserts serving-log bookends before full release verification.
- `SttEngine::new` starts interim and final construction under one absolute deadline. It aggregates role failures, preserves partial-cleanup failures, and explicitly abandons only non-preemptible timed-out startup handles.
- `SttEngine::shutdown` joins every ordinary worker and returns one or multiple panic outcomes. Repeated calls preserve the observed failures.
- `.github/workflows/stt-miri.yml` pins pure ownership and queue checks to the selected toolchain and adds native checks with hash-verified runtime, model, and audio fixtures.
- `ScriptedDecoder` adds construction rendezvous and drop-panic controls to shared fixture state. Tests cover exact queue boundaries, cancellation, startup deadlines, failure aggregation, cleanup, thread confinement, and shutdown.
- `.github/workflows/stt-miri.yml` keeps sockets, dynamic FFI, native callbacks, and model loading outside the interpreter and assigns them to native CI.

Design: new parameter-object @ crates/gateway-stt-engine/src/decoder.rs::DecodeRequest boundary: pub
Design: new encapsulated-invariant @ crates/gateway-stt-engine/src/policy.rs::EnginePolicy boundary: pub
Design: removes shared-parameter-cluster @ crates/gateway-stt-engine/src/engine.rs::SttEngine::transcribe_final
Design: flag-parameter -> dispatch-on-tag @ crates/gateway-stt-engine/src/worker.rs::worker_loop deps: &AtomicBool,&dyn ModelFactory,&mpsc::Receiver<Job>,&mpsc::SyncSender<Result<bool, TranscribeError>>,DecodeMode
Design: new dispatch-on-tag @ crates/gateway-stt-engine/src/engine.rs::SttEngine::decode boundary: pub
Design: extends shared-mutable-state @ crates/gateway-stt-engine/src/test_fixtures.rs::ScriptedDecoder
Design: extends temporal-coupling @ crates/gateway-stt-engine/src/test_fixtures.rs::ScriptedDecoder
Violates: A2 - credential ownership in crates/gateway-stt/src/runtime.rs is not determinable from diff
Violates: A96 - browser content bounds in crates/gateway-stt/src/api.rs are not determinable from diff
Violates: A115 - control readiness during crates/gateway-stt/src/runtime.rs model startup is not determinable from diff
Pending: N21 - compounds
Pending: N22 - compounds
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Move speech pipeline tuning to a canonical top-level configuration while preserving legacy input during parsing. Reject mixed canonical and legacy forms, serialize only the canonical form, and apply tuning changes without a restart.

- `SttPipelineConfig` keeps validated window, interval, and vocabulary state private and exposes read-only access. The `stt` accessor replaces speech tuning access through `WorkshopConfig`.
- `migrate_legacy_stt` accepts legacy input only when canonical input is absent, and `canonicalizeStt` prevents browser saves from writing the legacy shape. Validation tests cover zero bounds, conflicting forms, serialization, hot apply, and UI persistence.

Design: new value-object @ crates/gateway-config/src/config/stt.rs::SttPipelineConfig boundary: pub
Design: new encapsulated-invariant @ crates/gateway-config/src/config/stt.rs::SttPipelineConfig boundary: pub
Design: new surface-growth @ crates/gateway-config/src/config/accessors.rs::Config::stt boundary: pub
Design: new shim @ crates/gateway-config/src/config/imp.rs::migrate_legacy_stt deps: &mut toml::Value boundary: persisted
Design: new stringly-typed @ crates/gateway-config/src/config/imp.rs::migrate_legacy_stt deps: &mut toml::Value boundary: persisted
Design: new shim @ crates/gateway-config-ui/ui/src/services/config-store.ts::canonicalizeStt deps: EntryData boundary: persisted
Design: new stringly-typed @ crates/gateway-config-ui/ui/src/services/config-store.ts::canonicalizeStt deps: EntryData boundary: persisted
Violates: A2 - credential ownership in SttRuntime is not determinable from diff
Violates: A96 - bounded third-party model content in canonicalizeStt is not determinable from diff
Violates: A115 - control readiness in SttRuntime is not determinable from diff
Violates: A116 - publication consistency in stt_pipeline_change_reloads_without_restart is not determinable from diff
Pending: N9 - compounds
Pending: N27 - compounds
Deferred: gateway configuration test module registration is absent
Deferred: generated guide summary and index updates are absent
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Decode canonical Base64 PCM audio into one continuous resampled stream for realtime transcription. Enforce append size, buffered duration, sample integrity, and minimum commit limits before downstream decoding.

- `AudioBuffer` owns odd-byte carry, input duration, and the `Resampler24To16` timeline. A successful `commit` flushes the final output position and resets all ingestion state.
- `decode_base64` rejects malformed, noncanonical, and oversized input. `pcm16le-24khz.json` pins language-neutral little-endian bytes for other consumers.
- `audio` remains private under `allow(dead_code)` and has no runtime caller in the touched files.

Design: new oversized-unit @ crates/gateway-stt/src/audio.rs
Design: new pure-function @ crates/gateway-stt/src/audio.rs::decode_base64 deps: str
Design: new value-object @ crates/gateway-stt/src/audio.rs::CommittedAudio
Violates: A2 - crates/gateway-stt/src/audio.rs does not determine credential ownership
Pending: N6 - compounds
Pending: N9 - compounds
Deferred: Realtime session wiring remains absent from this commit
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Define private client and server events for realtime transcription. Reject unsupported fields and invalid shapes before state changes, preserve atomic session updates, and issue independent opaque identifiers. Record CI repairs and align migration deadlines with the expanded schedule.

- `ClientEvent` and `ServerEvent` encode the accepted event families as crate-private types with strict shape checks.
- `NEXT_GENERATOR` allocates generator namespaces from process-wide atomic state, and `parse_empty` selects commit or clear behavior through a Boolean parameter.
- `apply_update_text` clones the effective session and publishes only a fully valid update. `validate` accepts only the exact transcription query.
- `canonical_client_events_parse_and_updates_are_atomic` and `canonical_server_events_round_trip_with_exact_shapes` pin atomic updates and exact fixture parity.
- `realtime` remains private and intentionally unwired to a socket route.

Design: new pure-function @ crates/gateway-stt/src/realtime/query.rs::validate deps: Option
Design: new dispatch-on-tag @ crates/gateway-stt/src/realtime/wire/client.rs::parse_client_event deps: str
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::parse_client_event deps: str
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::correlation deps: Map
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::required_string deps: Correlation,Map,str,str
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::reject_unknown deps: Correlation,Map,str,str
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::object_at deps: Correlation,Value,str
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::parse_append deps: Correlation,Map
Design: new flag-parameter @ crates/gateway-stt/src/realtime/wire/client.rs::parse_empty deps: Correlation,Map,bool
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::parse_empty deps: Correlation,Map,bool
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::client_id deps: Correlation
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::parse_update deps: Correlation,Map
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::parse_audio deps: Correlation,Value
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::parse_format deps: Correlation,Value
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::parse_transcription deps: Correlation,Value
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/client.rs::parse_include deps: Correlation,Value
Design: new stringly-typed @ crates/gateway-stt/src/realtime/wire/shared.rs::ClientEvent
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/shared.rs::deserialize_required_nullable deps: D
Design: new global-state @ crates/gateway-stt/src/realtime/wire/shared.rs::NEXT_GENERATOR
Design: new stringly-typed @ crates/gateway-stt/src/realtime/wire/server.rs::EffectiveSession
Design: new stringly-typed @ crates/gateway-stt/src/realtime/wire/server.rs::ServerEvent
Design: new stringly-typed @ crates/gateway-stt/src/realtime/wire/server.rs::ConversationItem
Design: new stringly-typed @ crates/gateway-stt/src/realtime/wire/server.rs::WireError
Design: new oversized-unit @ crates/gateway-stt/src/realtime/wire/server.rs::ServerEvent::validate
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/server.rs::validate_id deps: str
Design: new pure-function @ crates/gateway-stt/src/realtime/wire/server.rs::validate_optional_id deps: Option
Violates: A2 - not determinable from diff
Deferred: Realtime socket integration remains unwired
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Run architecture checks with the repository-supported Cargo release so ambient stable updates cannot break the pinned tools. Install the matching toolchain in continuous integration, force every architecture child process to use it, and fail closed when the Cargo release or a required tool is unavailable.

- `runCargo` passes a copied environment with `RUSTUP_TOOLCHAIN` fixed to `1.89`, and its injected `spawn` and `env` inputs make child selection testable.
- `requireCargoVersion` rejects other Cargo releases before module or public API checks run.
- `tools/check-stt-architecture.test.mjs` pins ambient Cargo 1.98 rejection, the child environment for both tools, and failures for an absent toolchain or command.
- `.github/workflows/ci.yml` installs Rust 1.89 and builds both pinned architecture tools with it while the job keeps stable as its default.

Design: new surface-growth @ tools/check-stt-architecture.mjs::requireCargoVersion deps: output boundary: pub
Design: new pure-function @ tools/check-stt-architecture.mjs::requireCargoVersion deps: output boundary: pub
Design: new surface-growth @ tools/check-stt-architecture.mjs::runCargo deps: args,env,root,spawn boundary: pub
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Remove extension trait imports that the test module does not use.

Plan: none
Build featureless Gateway binaries before Workshop compilation so Tauri can resolve its required external binary. Stage each binary under the target-qualified name for Windows and Linux, then remove it even when later checks fail.

- `TARGETS` centralizes the supported source and sidecar names for the Windows and Linux build targets.
- `.github/workflows/ci.yml` builds each featureless Gateway, stages it before Workshop checks, and removes it with an unconditional cleanup step.
- `stageGatewaySidecar` rejects unsupported targets, missing sources, non-file sources, and platform-name mismatches before it copies a binary.
- `tools/stage-gateway-sidecar.test.mjs` pins both target mappings, rejection paths, byte-preserving staging, and repeatable removal.

Design: new surface-growth @ tools/stage-gateway-sidecar.mjs::gatewayBinaryName deps: target boundary: pub
Design: new pure-function @ tools/stage-gateway-sidecar.mjs::gatewayBinaryName deps: target boundary: pub
Design: new surface-growth @ tools/stage-gateway-sidecar.mjs::gatewaySidecarName deps: target boundary: pub
Design: new pure-function @ tools/stage-gateway-sidecar.mjs::gatewaySidecarName deps: target boundary: pub
Design: new surface-growth @ tools/stage-gateway-sidecar.mjs::stageGatewaySidecar deps: root,source,target boundary: pub
Design: new surface-growth @ tools/stage-gateway-sidecar.mjs::removeGatewaySidecar deps: root,target boundary: pub
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Own each uncommitted audio stream, immutable configuration snapshot, interim epoch, and cleanup task within one isolated session. Reject excess sessions or canceled-task retention immediately, preserve retryable state on overload, and run pure ownership paths under Miri.

- `InputSnapshot` freezes the effective prompt and hypothesis option on the first successful append, while `UncommittedInput` owns audio conversion and take state until clear.
- `SessionRegistry` shares synchronized admission state across handles and keeps retiring sessions counted until every aborted interim task joins.
- `Session` receives its registration and optional engine at construction, owns bounded canceled-task joins, and rejects stale epochs before allocating event identifiers.
- `test_fixtures` adds a feature-gated public session surface for deterministic integration coverage.
- `realtime_session` pins exact session and canceled-join capacities, cancellation-safe retries, reset behavior, and immutable first-append state.
- `realtime` remains private and is not wired to a socket route.

Design: new value-object @ crates/gateway-stt/src/realtime/input.rs::InputSnapshot
Design: new constructor-injection @ crates/gateway-stt/src/realtime/input.rs::UncommittedInput::new
Design: new oversized-unit @ crates/gateway-stt/src/realtime/input.rs
Design: new shared-mutable-state @ crates/gateway-stt/src/realtime/registry.rs::SessionRegistry
Design: new oversized-unit @ crates/gateway-stt/src/realtime/registry.rs
Design: new constructor-injection @ crates/gateway-stt/src/realtime/session.rs::Session::new
Design: new oversized-unit @ crates/gateway-stt/src/realtime/session.rs
Design: new surface-growth @ crates/gateway-stt/src/test_fixtures.rs::RealtimeSessionRegistryFixture boundary: pub
Design: new surface-growth @ crates/gateway-stt/src/test_fixtures.rs::RealtimeInterimEpoch boundary: pub
Design: new surface-growth @ crates/gateway-stt/src/test_fixtures.rs::RealtimeInputSnapshotFixture boundary: pub
Design: new surface-growth @ crates/gateway-stt/src/test_fixtures.rs::RealtimeSessionFixture boundary: pub
Design: new oversized-unit @ crates/gateway-stt/src/test_fixtures.rs
Design: new clone-block @ crates/gateway-stt/tests/it/realtime_session.rs
Design: new oversized-unit @ crates/gateway-stt/tests/it/realtime_session.rs
Violates: A2 - credential ownership in crates/gateway-stt/src/realtime is not determinable from diff
Pending: N5 - compounds
Pending: N6 - compounds
Pending: N24 - compounds
Pending: N26 - compounds
Pending: N30 - compounds
Deferred: Realtime socket route integration remains unwired
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Give each committed transcription item its own immutable input snapshot, take state, finalization task, durable lineage, and terminal outcome. Reserve bounded result, hypothesis, terminal, and final-segment capacity before detaching input so overload stays retryable and items can finish out of order. Extend deterministic and interpreter coverage for exact capacities, isolation, cancellation, failure, and cleanup. Record native runner remediation as deferred work, synchronize later migration deadlines, and preserve the promoted architecture comparison reports.

- `CommittedItem` owns one sealed input, one take, one finalization task, and one terminal transition. `Session::commit` validates and reserves capacity before it invalidates the interim epoch or detaches input.
- `FinalPipeline` uses bounded message passing for accurate segments and completion. `SessionRegistry` keeps admission occupied until canceled interim and finalization tasks finish joining.
- `ResultMailbox` bounds ordinary results at 16 while reserving one replaceable hypothesis and one terminal slot per item. `MAX_COMMITTED_ITEMS_PER_SESSION` and `FINAL_SEGMENT_CAPACITY` enforce four-item and four-segment limits.
- `.github/workflows/stt-miri.yml` expands interpreter coverage to committed-item ownership and queue bounds. `module-ceilings.toml` removes the completed take migration and shifts later migration targets.
- `vibe/stt-field-comparison-and-adoption.md` and `vibe/agent-runtime-field-comparison-and-adoption.md` preserve the promoted field reports.
- `ci-native-rustup` records the self-hosted runner preflight as pending; the native job remains unchanged.

Design: new shared-mutable-state @ crates/gateway-stt/src/realtime/item.rs::CommittedItem
Design: new oversized-unit @ crates/gateway-stt/src/realtime/item.rs
Design: extends oversized-unit @ crates/gateway-stt/src/realtime/input.rs
Design: extends shared-mutable-state @ crates/gateway-stt/src/realtime/registry.rs::SessionRegistry
Design: extends oversized-unit @ crates/gateway-stt/src/realtime/registry.rs
Design: new oversized-unit @ crates/gateway-stt/src/realtime/result_mailbox.rs
Design: extends oversized-unit @ crates/gateway-stt/src/realtime/session.rs
Design: new oversized-unit @ crates/gateway-stt/src/realtime/session/items.rs
Design: new oversized-unit @ crates/gateway-stt/src/realtime/session/state.rs
Design: replaces message-passing @ crates/gateway-stt/src/take/finalization.rs::FinalPipeline was: crates/gateway-stt/src/take.rs::FinalPipeline
Design: new shared-mutable-state @ crates/gateway-stt/src/take/finalization.rs::FinalPipeline
Design: replaces shared-mutable-state @ crates/gateway-stt/src/take/state.rs::TakeState was: crates/gateway-stt/src/take.rs::TakeState
Design: new oversized-unit @ crates/gateway-stt/src/take/agreement.rs
Design: new oversized-unit @ crates/gateway-stt/src/take/finalization.rs
Design: new oversized-unit @ crates/gateway-stt/src/take/state.rs
Design: new surface-growth @ crates/gateway-stt/src/test_fixtures.rs::RealtimeCommitFixture boundary: pub
Design: extends surface-growth @ crates/gateway-stt/src/test_fixtures.rs::RealtimeSessionFixture boundary: pub
Design: extends oversized-unit @ crates/gateway-stt/src/test_fixtures.rs
Design: extends clone-block @ crates/gateway-stt/tests/it/realtime_session.rs
Design: extends oversized-unit @ crates/gateway-stt/tests/it/realtime_session.rs
Violates: A2 - credential ownership in crates/gateway-stt/src/realtime is not determinable from diff
Pending: N6 - compounds
Pending: N24 - compounds
Pending: N30 - compounds
Pending: N34 - compounds
Deferred: self-hosted native runner Rust preflight remains unimplemented
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Make the self-hosted native job use its provisioned stable Rust toolchain. Fail before caching when required binaries or the stable toolchain are absent, then expose the tool directory to later steps. Add source tests that preserve the hosted interpreter setup and reject installer regressions.

- `.github/workflows/stt-miri.yml` replaces the native toolchain installer with a preflight that resolves `rustup.exe` and `cargo.exe` under `$env:USERPROFILE`, disables automatic installation, and writes `$cargoBin` to `$env:GITHUB_PATH`.
- `Verify preinstalled stable Rust` lists installed toolchains, requires a stable entry, and invokes `$cargo` with `+stable`; each failed precondition throws a provisioning error.
- `tools/check-stt-native-workflow.test.mjs` pins preflight order and failure text, rejects installer actions in the native job, and confirms that `pure-stt-state` keeps `nightly-2026-09-05`.

Design: new hidden-dependency @ .github/workflows/stt-miri.yml boundary: persisted
Design: new temporal-coupling @ .github/workflows/stt-miri.yml boundary: persisted
Design: new oversized-unit @ tools/check-stt-native-workflow.test.mjs
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Unify speech lifecycle, model facts, and routes behind one cloneable service so each caller observes one complete generation. Stage verified artifacts and worker state before publication, preserve batch and temporary legacy behavior through service methods, and reduce the production root to six types.

- `SpeechService` owns preparation, staged replacement, publication, shutdown, status, model discovery, and route construction through one public facade.
- `GenerationState` stores the engine, physical names, guidance, backend, admission, and generation identifier in one lock-published snapshot shared by service clones.
- `module-ceilings.toml` fixes the public-root budget at six and removes completed migration targets for `api.rs` and `runtime.rs`.
- `batch::routes` owns the upload limit and OpenAI error envelopes while `authorize_stt_route` applies authentication and cancellation to every speech route.
- `scripted_service` and `service.rs` pin complete clone snapshots, physical model selection, temporary legacy capability, unload, and Gateway error envelopes.
- `Admission` has only an open state, and `unload` waits without a deadline for generation and engine references. Bounded quiescence remains absent.

Design: new encapsulated-invariant @ crates/gateway-stt/src/artifacts.rs::PreparedSpeech boundary: pub
Design: new oversized-unit @ crates/gateway-stt/src/artifacts.rs
Design: replaces oversized-unit @ crates/gateway-stt/src/batch.rs was: crates/gateway-stt/src/api.rs
Design: new oversized-unit @ crates/gateway-stt/src/batch/native_tests.rs
Design: new oversized-unit @ crates/gateway-stt/src/batch/tests.rs
Design: new parameter-object @ crates/gateway-stt/src/generation.rs::Generation
Design: replaces shared-mutable-state @ crates/gateway-stt/src/generation.rs::GenerationState was: crates/gateway-stt/src/runtime.rs::SttSlot
Design: new encapsulated-invariant @ crates/gateway-stt/src/generation.rs::SpeechReplacement boundary: pub
Design: replaces hidden-dependency @ crates/gateway-stt/src/generation.rs::unload deps: Option was: crates/gateway-stt/src/runtime.rs::unload_engine
Design: new oversized-unit @ crates/gateway-stt/src/generation.rs
Design: new newtype @ crates/gateway-stt/src/model.rs::SpeechModelInfo boundary: pub
Design: new facade @ crates/gateway-stt/src/service.rs::SpeechService boundary: pub
Design: new temporal-coupling @ crates/gateway-stt/src/service.rs::SpeechService::commit_replacement
Design: new oversized-unit @ crates/gateway-stt/src/service.rs
Design: new value-object @ crates/gateway-stt/src/status.rs::SpeechStatus boundary: pub
Design: replaces constructor-injection @ crates/gateway-stt/src/test_fixtures.rs::scripted_service deps: ScriptedModelFactory,u64,u64 was: crates/gateway-stt/src/runtime.rs::SttRuntime::from_scripted_engine
Design: new pure-function @ crates/gateway-stt/src/test_fixtures.rs::segment_ranges deps: &[f32] boundary: pub
Design: replaces surface-growth @ crates/gateway-stt/src/test_fixtures.rs::segment_ranges boundary: pub was: crates/gateway-stt/src/lib.rs::Segmenter
Design: extends facade @ crates/gateway-stt/src/lib.rs::test_fixtures
Design: extends oversized-unit @ crates/gateway-stt/src/test_fixtures.rs
Violates: A2 - credential ownership in SpeechService is not determinable from diff
Violates: A115 - control readiness during speech provisioning is not determinable from diff
Pending: N6 - compounds
Pending: N24 - compounds
Pending: N25 - compounds
Deferred: generation admission has no closed state
Deferred: generation unload has no finite wait bound
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Resolve the current Windows process identity as a canonical SID before restricting artifact cache access. This keeps private cache setup valid for interactive users and service accounts while failing closed on identity or ACL errors.

- `current_windows_sid` replaces profile environment names with one quoted CSV identity record from `whoami` and gives the validated SID to the ACL grant path.
- `target_step` and `removal_step` advance the legacy socket removal target by one execution position so the architecture ratchets preserve the same migration boundary.
- `parse_whoami_user_sid` delegates shape checks to `is_canonical_windows_sid`, accepts ordinary and service identities, rejects command failures and malformed or noncanonical output, and returns command error detail.
- `windows_sid_grant` renders the required SID principal prefix, while `artifact_store_enforces_private_windows_dacl` verifies that the current process retains write access after restriction.

Design: replaces hidden-dependency @ crates/gateway-local/src/artifacts/confine.rs::current_windows_sid deps: &Path was: crates/gateway-local/src/artifacts/confine.rs::current_windows_account
Design: new stringly-typed @ crates/gateway-local/src/artifacts/confine.rs
Design: new flag-parameter @ crates/gateway-local/src/artifacts/confine.rs::parse_whoami_user_sid deps: &[u8],&[u8],bool
Design: new pure-function @ crates/gateway-local/src/artifacts/confine.rs::parse_whoami_user_sid deps: &[u8],&[u8],bool
Design: new pure-function @ crates/gateway-local/src/artifacts/confine.rs::is_canonical_windows_sid deps: &str
Design: new pure-function @ crates/gateway-local/src/artifacts/confine.rs::windows_sid_grant deps: &str
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Serialize speech generation replacement and close admission while old requests and worker jobs drain. Explicit ownership counters and cancellation epochs prevent canceled requests from hiding live native work, while deadlines reopen the old snapshot and shutdown invalidates staged publication. Exact module ceilings and focused ownership tests enforce the lifecycle.

- `ReplacementCoordinator` owns one replacement lane, while `AdmissionGate` counts request and worker ownership under a fresh `SessionEpoch`.
- `GenerationLease` keeps each request attached to one complete snapshot, and `GenerationJob` keeps native work owned after request cancellation.
- `GenerationState` removes the active snapshot only after bounded drain, shuts down its workers, and builds the unpublished replacement afterward.
- `SttEngine::shutdown` now uses shared access and serializes worker cleanup inside `Transcriber`.
- `validate_module_ceiling` requires every manifest ceiling to equal the measured file size and rejects settled Gateway STT modules above 500 lines.
- `SpeechError` distinguishes a drain deadline from replacement invalidation by shutdown.

Design: extends surface-growth @ crates/gateway-stt-engine/src/engine.rs::SttEngine::shutdown boundary: pub
Design: extends shared-mutable-state @ crates/gateway-stt-engine/src/worker.rs::Transcriber
Design: new surface-growth @ crates/gateway-stt/src/artifacts.rs::SpeechError boundary: pub
Design: extends shared-mutable-state @ crates/gateway-stt/src/generation.rs::GenerationState
Design: extends encapsulated-invariant @ crates/gateway-stt/src/generation.rs::SpeechReplacement boundary: pub
Design: removes hidden-dependency @ crates/gateway-stt/src/generation.rs::unload deps: Option
Design: replaces parameter-object @ crates/gateway-stt/src/generation/snapshot.rs::Generation was: crates/gateway-stt/src/generation.rs::Generation
Design: new shared-parameter-cluster @ crates/gateway-stt/src/generation/snapshot.rs::Generation::from_factory
Design: new shared-mutable-state @ crates/gateway-stt/src/replacement.rs::ReplacementCoordinator
Design: new shared-mutable-state @ crates/gateway-stt/src/replacement.rs::SessionEpoch
Design: new shared-mutable-state @ crates/gateway-stt/src/replacement.rs::AdmissionGate
Design: new oversized-unit @ crates/gateway-stt/src/replacement.rs
Design: extends facade @ crates/gateway-stt/src/service.rs::SpeechService boundary: pub
Design: extends temporal-coupling @ crates/gateway-stt/src/service.rs::SpeechService::commit_replacement
Design: replaces constructor-injection @ crates/gateway-stt/src/test_fixtures/generation.rs::scripted_service deps: ScriptedModelFactory,u64,u64 boundary: pub was: crates/gateway-stt/src/test_fixtures.rs::scripted_service
Design: replaces pure-function @ crates/gateway-stt/src/test_fixtures/segment.rs::segment_ranges deps: &[f32] boundary: pub was: crates/gateway-stt/src/test_fixtures.rs::segment_ranges
Design: replaces surface-growth @ crates/gateway-stt/src/test_fixtures/segment.rs::segment_ranges boundary: pub was: crates/gateway-stt/src/test_fixtures.rs::segment_ranges
Design: replaces clone-block @ crates/gateway-stt/src/test_fixtures/native.rs was: crates/gateway-stt/src/test_fixtures.rs
Design: extends surface-growth @ crates/gateway-stt/src/test_fixtures.rs boundary: pub
Design: extends facade @ crates/gateway-stt/src/test_fixtures.rs boundary: pub
Design: new pure-function @ crates/gateway-stt/tests/it/architecture.rs::validate_module_ceiling deps: Option<usize>,usize,usize
Design: new pure-function @ crates/gateway-stt/tests/it/architecture.rs::calls_associated_method deps: &str,&str,&str
Design: new pure-function @ crates/gateway-stt/tests/it/architecture.rs::refcount_introspection deps: &str
Design: new oversized-unit @ crates/gateway-stt/tests/it/generation.rs::active_replacement_drains_request_and_job_before_unload_and_publication
Pending: N24 - compounds
Pending: N25 - compounds
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Keep warnings-denied Gateway builds clean on non-Windows hosts. Compile the application manifest and unsafe-code expectation only where their Windows consumers exist.

- `MANIFEST` is compiled only on Windows, where resource embedding consumes it.
- `main` applies its unsafe-code expectation only on Windows while the DPI call remains target-gated.
- `the_manifest_constant_is_compiled_only_for_windows` and `the_dpi_unsafe_expectation_exists_only_for_windows` pin both conditional declarations in source checks.

Violates: A2 - not determinable from diff
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Expose a same-origin Realtime transcription socket through Workshop while Gateway remains responsible for authentication and the fixed upstream target. Relay payload and close frames without interpreting content, terminate control frames per hop, and bound peer cleanup.

- `DEPENDENCY_PHASE` advances the exact dependency policy to Phase B and permits Workshop to depend on shared loopback validation.
- `connect_socket` centralizes authenticated WebSocket setup for legacy STT and Realtime, with `workshop_status` selecting the legacy status header.
- `routes` rejects missing or cross-origin authority and any requested subprotocol before it opens the fixed transcription connection.
- `relay` forwards text, binary, and close frames, handles ping and pong per transport hop, and applies a 500 millisecond deadline to relay I/O and cleanup.
- `realtime_relay_is_authenticated_fixed_and_payload_opaque` pins bearer ownership, the fixed upstream target, opaque payloads, hop-local control frames, close propagation, bounded disconnect cleanup, and browser handshake policy.

Design: new flag-parameter @ crates/workshop-server/src/gateway/socket.rs::GatewayClient::connect_socket
Design: new hidden-dependency @ crates/workshop-server/src/gateway/socket.rs::GatewayClient::connect_realtime
Design: new surface-growth @ crates/workshop-server/src/routes/realtime.rs::routes deps: AppState boundary: wire
Design: new pure-function @ crates/workshop-server/src/routes/realtime.rs::same_origin_allowed deps: HeaderMap,Uri boundary: wire
Design: new pure-function @ crates/workshop-server/src/routes/realtime.rs::single_header deps: HeaderMap,HeaderName boundary: wire
Design: new value-object @ crates/workshop-server/tests/it/realtime_relay.rs::UpstreamRequest boundary: wire
Design: new shared-mutable-state @ crates/workshop-server/tests/it/realtime_relay.rs::UpstreamProbe
Design: new oversized-unit @ crates/workshop-server/tests/it/realtime_relay.rs::upstream deps: HeaderMap,State<UpstreamProbe>,Uri,WebSocketUpgrade boundary: wire
Design: new oversized-unit @ crates/workshop-server/tests/it/realtime_relay.rs::realtime_relay_is_authenticated_fixed_and_payload_opaque boundary: wire
Design: new shared-mutable-state @ crates/workshop-server/tests/it/realtime_relay.rs::StalledPeerProbe
Design: new pure-function @ crates/workshop-server/tests/it/realtime_relay.rs::request_with deps: Option<&str>,Option<&str>,str boundary: wire
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Add a browser-owned 24 kHz microphone lifecycle that emits transferable PCM16 chunks and reports permission, device, start, stop, and clear failures as recoverable outcomes. Encode exact little-endian samples in the audio worklet, retain legacy float capture, and flush carried samples before graph teardown.

- `SpeechCaptureBackend` isolates browser audio graph creation behind an injected session boundary, while `SpeechCaptureService` owns lifecycle phase and emitted audio state.
- `Pcm16CaptureProcessor` clips float samples, encodes signed little-endian PCM16, chunks output, and accepts `clear` and `flush` commands.
- `pcm-worklet.mjs` loads the production worklet and pins fixture parity, clipping, transfer ownership, carry, clear, flush, and sample-rate rejection. `speech-capture.mjs` pins browser graph ownership, recoverable failures, disposal, and resource cleanup.
- `SpeechCaptureService` has no production caller in this change.

Design: new message-passing @ crates/workshop-server/ui/pcm-worklet.js::Pcm16CaptureProcessor boundary: wire
Design: new dispatch-on-tag @ crates/workshop-server/ui/pcm-worklet.js::Pcm16CaptureProcessor boundary: wire
Design: new surface-growth @ crates/workshop-server/ui/pcm-worklet.js::Pcm16CaptureProcessor boundary: pub
Design: new surface-growth @ crates/workshop-server/ui/src/services/speech-capture.ts boundary: pub
Design: new speculative-abstraction @ crates/workshop-server/ui/src/services/speech-capture.ts::SpeechCaptureBackend
Design: new constructor-injection @ crates/workshop-server/ui/src/services/speech-capture.ts::SpeechCaptureService
Design: new temporal-coupling @ crates/workshop-server/ui/src/services/speech-capture.ts::SpeechCaptureService
Design: new event-hook @ crates/workshop-server/ui/src/services/speech-capture.ts::SpeechCaptureService::onAudio boundary: pub
Deferred: Workshop browser integration remains unwired
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Route browser dictation through a reusable Realtime transcription connection and production microphone capture. Keep concurrent takes isolated by item and client event, replace hypotheses in place, and restore local text on recoverable failures.

- `speechCapture` gives all agent panels one composition-root microphone owner, while `AgentSessionView` receives that owner through the panel service chain.
- `RealtimeTranscriptionService` negotiates the hypothesis extension, emits canonical append, commit, and clear events, validates server event shapes, and converts failures to local typed events.
- `setupStt` preserves each selected range, waits for carried audio before commit, binds acknowledgments to queued takes, and applies overlapping results by item.
- `setupLegacyStt` remains private until installed-package acceptance permits removal of the old browser protocol.

Design: new shared-mutable-state @ crates/workshop-server/ui/src/main.ts::speechCapture
Design: new surface-growth @ crates/workshop-server/ui/src/services/realtime-transcription.ts boundary: pub
Design: new event-hook @ crates/workshop-server/ui/src/services/realtime-transcription.ts::RealtimeTranscriptionService
Design: new temporal-coupling @ crates/workshop-server/ui/src/services/realtime-transcription.ts::RealtimeTranscriptionService
Design: new oversized-unit @ crates/workshop-server/ui/src/services/realtime-transcription.ts::RealtimeTranscriptionService
Design: new hidden-dependency @ crates/workshop-server/ui/src/services/realtime-transcription.ts::socketUrl
Design: new dispatch-on-tag @ crates/workshop-server/ui/src/services/realtime-transcription.ts::handleMessage
Design: new parallel-abstraction @ crates/workshop-server/ui/src/ui/realtime-stt.ts::Take
Design: new shared-parameter-cluster @ crates/workshop-server/ui/src/ui/realtime-stt.ts::setupStt deps: RealtimeTranscriptionService,SpeechCaptureService,SttBlocker,SttElements,SttStatus boundary: pub
Design: new oversized-unit @ crates/workshop-server/ui/src/ui/realtime-stt.ts::setupStt deps: RealtimeTranscriptionService,SpeechCaptureService,SttBlocker,SttElements,SttStatus boundary: pub
Design: new surface-growth @ crates/workshop-server/ui/src/ui/stt.ts::SttInputTarget boundary: pub
Design: new constructor-injection @ crates/workshop-server/ui/src/ui/agent-session-view.ts::AgentSessionView
Deferred: Remove the legacy Workshop speech path after installed-package acceptance.
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Drive Gateway and Workshop Realtime tests from the same canonical event sequences without coupling their production servers. Cover hypotheses, completion, clear, overlap, saturation retry, relay bearer isolation, and replacement closure. Preserve insertion whitespace when authoritative completion replaces an empty selection.

- `canonical_sequences` and `canonicalMessage` load the shared fixture contract into independent Rust and browser harnesses.
- `FixtureUpstream` emits canonical server frames, accepts only the Gateway bearer, detects browser bearer leakage, and echoes opaque payloads.
- `canonical_fixture_drives_hypothesis_completion_and_clear` and `saturated_commit_preserves_the_canonical_input_for_retry` pin fixture-driven Gateway behavior, including exact retry audio and lineage.
- `applyCompletion` retains insertion whitespace when a completion replaces provisional text at an empty selection.
- `crates/gateway/tests/it/realtime_stt.rs` and `crates/workshop-server/tests/it/realtime_relay.rs` remain independent harnesses; the change adds neither a dual-server test nor a package dependency.

Design: new hidden-dependency @ crates/gateway/tests/it/realtime_stt.rs::canonical_sequences
Design: new shared-parameter-cluster @ crates/gateway/tests/it/realtime_stt.rs::canonical_message deps: &serde_json::Value,&str,&str,&str,usize
Design: new pure-function @ crates/gateway/tests/it/realtime_stt.rs::canonical_message deps: &serde_json::Value,&str,&str,&str,usize
Design: new shared-parameter-cluster @ crates/gateway/tests/it/realtime_stt.rs::canonical_first_message deps: &serde_json::Value,&str,&str,&str
Design: new pure-function @ crates/gateway/tests/it/realtime_stt.rs::canonical_first_message deps: &serde_json::Value,&str,&str,&str
Design: new shared-parameter-cluster @ crates/gateway/tests/it/realtime_stt.rs::canonical_client deps: &serde_json::Value,&str,&str
Design: new pure-function @ crates/gateway/tests/it/realtime_stt.rs::canonical_client deps: &serde_json::Value,&str,&str
Design: new shared-parameter-cluster @ crates/gateway/tests/it/realtime_stt.rs::canonical_server deps: &serde_json::Value,&str,&str
Design: new pure-function @ crates/gateway/tests/it/realtime_stt.rs::canonical_server deps: &serde_json::Value,&str,&str
Design: extends oversized-unit @ crates/gateway/tests/it/realtime_stt.rs
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt.rs::canonical_fixture_drives_hypothesis_completion_and_clear
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt.rs::saturated_commit_preserves_the_canonical_input_for_retry
Design: new pure-function @ crates/workshop-server/tests/it/realtime_relay.rs::canonical_server_frames
Design: new shared-mutable-state @ crates/workshop-server/tests/it/realtime_relay.rs::FixtureUpstream
Design: extends oversized-unit @ crates/workshop-server/ui/src/ui/realtime-stt.ts::setupStt deps: RealtimeTranscriptionService,SpeechCaptureService,SttBlocker,SttElements,SttStatus boundary: pub
Design: new hidden-dependency @ crates/workshop-server/ui/test/agent-stt.mjs::canonicalMessage
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Block chat submission until Workshop has a model selection, and surface a failed model turn if the selected binding disappears before dispatch. Preserve the pending draft so a later selection can resume without sending an unbound Gateway request. Keep the temporary legacy boundary aligned with the revised execution order.

- `migration_targets."stt.rs"` and `DEPENDENCY_POLICIES` retain the temporary legacy socket boundary until its revised removal point.
- `AgentSessionView` stores its status and model collaborators, reacts to selection changes, and gates click and keyboard submission while the selection is empty.
- `dispatch_chat` emits `MODEL_TURN_FAILED` for absent named or default bindings before it returns the program error.
- `deliver_input_response_before_completion`, `reconcile_catalog_for_test`, and `deliver_input_after_acceptance_for_test` expose fixture seams that remove a selection after input acceptance but before Lua resumes.
- `a_missing_chat_binding_reports_one_failed_turn_before_lua_pcall_resumes`, `gate_binding_loss_surfaces_one_error_and_recovers_after_selection`, and the browser checks pin one visible failure, no unbound Gateway request, retained text, and recovery after selection.

Design: new shared-parameter-cluster @ crates/workshop-server/src/input.rs::deliver_input_response_before_completion deps: InputResponse,WaitRegistry,dyn Observer,impl FnOnce(),str,str
Design: new feature-flag @ crates/workshop-server/src/menu.rs::MenuBus::reconcile_catalog_for_test
Design: new surface-growth @ crates/workshop-server/src/menu.rs::MenuBus::reconcile_catalog_for_test boundary: pub
Design: new feature-flag @ crates/workshop-server/src/session_agents.rs::AgentSessions::deliver_input_after_acceptance_for_test
Design: new surface-growth @ crates/workshop-server/src/session_agents.rs::AgentSessions::deliver_input_after_acceptance_for_test boundary: pub
Design: new oversized-unit @ crates/workshop-server/tests/it/chat_gate.rs::gate_binding_loss_surfaces_one_error_and_recovers_after_selection
Design: extends constructor-injection @ crates/workshop-server/ui/src/ui/agent-session-view.ts::AgentSessionView
Design: new event-hook @ crates/workshop-server/ui/src/ui/agent-session-view.ts::AgentSessionView
Design: new surface-growth @ crates/workshop-server/ui/src/ui/agent-session-view.ts::AgentSessionView boundary: pub
Pending: N49 - compounds
Pending: N50 - compounds
Pending: N57 - compounds
Pending: N58 - compounds
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Retry incomplete profile and catalog refreshes, reconnect failed transcription sockets with capped backoff, and release imported progress by operation while the shared stream stays open. This lets simultaneous Gateway and Workshop startup reach a selectable model and clears completed status without another microphone action.

- `EventState::OperationFinished` adds an operation-level terminal wire event before source detachment. `RemoteOperation` ignores that marker in `apply`, and dropping the import publishes local completion.
- `remotes` keys imported progress by `OperationId`, so one terminal event removes only its matching `RemoteOperation`.
- `profiles_ready`, `catalog_ready`, and `selection_restored` keep healthy retries independent and restore the model selection once after both sources converge.
- `scheduleReconnect` doubles `reconnectDelayMs` from `RECONNECT_INITIAL_MS` to `RECONNECT_MAX_MS`; `dispose` cancels the active `reconnectTimer`.
- `startup_convergence` tests continuously healthy startup with delayed catalog and profile readiness. `stt-stream.mjs` tests retry timing, capped delay, readiness reset, and disposal cancellation.

Design: new surface-growth @ crates/shared-progress/src/event.rs::EventState boundary: wire
Design: new temporal-coupling @ crates/shared-progress/src/tree.rs::TreeState::finish_operation
Design: extends dispatch-on-tag @ crates/shared-progress/src/remote.rs::RemoteOperation::apply
Design: new registry @ crates/workshop-server/src/gateway_progress.rs::run deps: Arc<ProgressHub>,Duration,GatewayHealth,oneshot::Receiver<()>,str,str
Design: new oversized-unit @ crates/workshop-server/src/heartbeat.rs::run deps: Duration,GatewayClient,GatewayHealth,Push,ReconnectBackoff,oneshot::Receiver<()>
Design: extends surface-growth @ crates/workshop-server/ui/src/services/realtime-transcription.ts boundary: pub
Design: extends event-hook @ crates/workshop-server/ui/src/services/realtime-transcription.ts::RealtimeTranscriptionService
Design: extends temporal-coupling @ crates/workshop-server/ui/src/services/realtime-transcription.ts::RealtimeTranscriptionService
Design: extends oversized-unit @ crates/workshop-server/ui/src/services/realtime-transcription.ts::RealtimeTranscriptionService
Design: extends dispatch-on-tag @ crates/workshop-server/ui/src/services/realtime-transcription.ts::handleMessage
Pending: N53 - compounds
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Bind a lone unknown hypothesis to the active uncommitted take so interim revisions appear while recording. Confirm matching acknowledgments without changing text, retire mismatches with rollback, and preserve tombstone and overlap ordering.

- `retiredItems` keeps rejected and removed item identifiers in setup-local state so late hypotheses cannot bind to another take.
- `applySnapshot` binds only when one unbound take exists and rejects retired or tombstone-ambiguous items. `realtime.onCommitted` confirms matching identities, consumes FIFO tombstones, and rolls back mismatches.
- `agent-stt.mjs` covers repeated precommit revisions, unknown items, mismatched acknowledgments, FIFO recovery, tombstones, and overlapping takes.

Design: new shared-mutable-state @ crates/workshop-server/ui/src/ui/realtime-stt.ts::setupStt::retiredItems
Design: extends oversized-unit @ crates/workshop-server/ui/src/ui/realtime-stt.ts::setupStt deps: RealtimeTranscriptionService,SpeechCaptureService,SttBlocker,SttElements,SttStatus boundary: pub
Violates: A96 - bounded third-party model content in setupStt is not determinable from diff
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Make the chat-only catalog the shared boundary for model menus and agent runs. Wait for a usable model before each run, then retire stale generations only after accepted input settles so profile switches preserve history without replaying or dropping turns.

- `ChatCatalogBus` retains filtered snapshots and advances its generation only when chat-capable models change. `is_chat_capable` now governs publication, readiness, selection, and agent model construction.
- `spawn` freezes one catalog per run, waits through empty startup, and relaunches on usable replacement over the retained event log. `RunLifecycle` distinguishes operator cancellation from catalog retirement and protects accepted input until a terminal event.
- `gate_delayed_catalog_starts_chat_only_after_a_chat_model_arrives`, `gate_profile_switch_relaunches_chat_with_history_and_the_new_catalog`, and `gate_catalog_replacement_during_acceptance_settles_the_turn_exactly_once` pin startup, profile-switch, and accepted-input races.

Design: new shared-mutable-state @ crates/workshop-server/src/catalog/chat.rs::ChatCatalogBus
Design: new pure-function @ crates/workshop-server/src/catalog/chat.rs::is_chat_capable deps: serde_json::Value
Design: new shared-mutable-state @ crates/workshop-server/src/session_agents/lifecycle.rs::RunLifecycle
Design: new oversized-unit @ crates/workshop-server/src/session_agents/supervisor.rs::spawn deps: AgentSession,AgentSessions,ModelClient,SessionHost
Design: new oversized-unit @ crates/workshop-server/tests/it/chat_gate.rs::gate_catalog_replacement_during_acceptance_settles_the_turn_exactly_once
Pending: N49 - compounds
Pending: N50 - compounds
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Build each live hypothesis from one producer-owned snapshot. Keep each leading separator on the finalized, agreed, or tentative field that introduces its text, so direct concatenation preserves exact spacing without duplicate prefixes. Use the same snapshot for legacy deltas and Realtime events.

- `InterimSnapshot` derives `transcript` once from its private `finalized`, `agreed`, and `tentative` fields. `ServerEvent::hypothesis` consumes that snapshot instead of assembling overlapping text.
- `InterimState::next` retains promoted words separately, reconciles divergent final text, and assigns boundary whitespace through `owned_piece`.
- `producer_hypothesis_ownership` drives producer-generated snapshots through Gateway serialization and Workshop replacement. It checks exact fields, spaces, revisions, and the absence of a duplicated prefix.

Design: new surface-growth @ crates/gateway-stt-engine/src/test_fixtures.rs::ScriptedDecoder::wait_for_completed boundary: pub
Design: extends shared-mutable-state @ crates/gateway-stt-engine/src/test_fixtures.rs::ScriptedDecoder
Design: extends temporal-coupling @ crates/gateway-stt-engine/src/test_fixtures.rs::ScriptedDecoder
Design: removes oversized-unit @ crates/gateway-stt/src/take/agreement.rs
Design: new parameter-object @ crates/gateway-stt/src/take/interim.rs::InterimSnapshot boundary: wire
Design: new encapsulated-invariant @ crates/gateway-stt/src/take/interim.rs::InterimSnapshot boundary: wire
Design: new oversized-unit @ crates/gateway-stt/src/take/interim.rs
Design: new pure-function @ crates/gateway-stt/src/take/interim.rs::after_token_prefix deps: &str,usize
Design: new pure-function @ crates/gateway-stt/src/take/interim.rs::owned_piece deps: &str,bool
Design: new pure-function @ crates/gateway/tests/it/realtime_stt.rs::audio_samples deps: &[i16]
Design: new pure-function @ crates/gateway/tests/it/realtime_stt.rs::closed_segment
Design: extends oversized-unit @ crates/gateway/tests/it/realtime_stt.rs
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt.rs::producer_snapshots_partition_finalized_agreed_and_tentative_text
Violates: A2 - credential ownership in crates/gateway-stt/src/realtime is not determinable from diff
Pending: N6 - compounds
Pending: N20 - compounds
Pending: N21 - compounds
Pending: N22 - compounds
Pending: N30 - compounds
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Drive interim decoding on the configured engine cadence instead of on every audio append, suppressing undersized and silent windows while allowing one active decode and coalescing later audio into the next snapshot. Rebase each whole-window replacement by its sample origin and token overlap, while keeping finalized text and its sample watermark atomic and authoritative. Reap completed canceled work during completion ticks so repeated clears release bounded task capacity.

- `run_socket` separates interim scheduling cadence from completion polling, and `Session::schedule_interim` accepts only the newest eligible snapshot after the active decode finishes.
- `WholeWindowState` replaces same-origin hypotheses, carries text across consumed segment boundaries, and rebases advancing origins through explicit token overlap. `ServerEvent::hypothesis` reports the accepted window's sample offsets in milliseconds.
- `TakeState::finalized_snapshot` reads finalized text and its consumed-sample watermark under one lock so rebase decisions cannot combine different finalization states.
- `realtime_stt_native_incremental` adds ignored packaged-native coverage for growing and sliding JFK audio. Final Verify skipped its execution because the external Whisper library, model, and audio fixtures were unavailable.

Design: extends oversized-unit @ crates/gateway-stt/src/realtime/route.rs
Design: extends oversized-unit @ crates/gateway-stt/src/realtime/session.rs
Design: extends oversized-unit @ crates/gateway-stt/src/realtime/session/route.rs
Design: new pure-function @ crates/gateway-stt/src/realtime/session/route.rs::sample_millis deps: usize
Design: extends oversized-unit @ crates/gateway-stt/src/realtime/wire/server/events.rs
Design: extends oversized-unit @ crates/gateway-stt/src/take.rs
Design: new shared-parameter-cluster @ crates/gateway-stt/src/take.rs::Take::next_window_snapshot
Design: extends oversized-unit @ crates/gateway-stt/src/take/interim.rs
Design: extends oversized-unit @ crates/gateway-stt/src/take/state.rs
Design: new oversized-unit @ crates/gateway-stt/src/take/window.rs
Design: new shared-parameter-cluster @ crates/gateway-stt/src/take/window.rs::WholeWindowState::next
Design: new pure-function @ crates/gateway-stt/src/take/window.rs::rebase_sliding_window deps: &str,&str
Design: new pure-function @ crates/gateway-stt/src/take/window.rs::equivalent_token deps: &str,&str
Design: new pure-function @ crates/gateway-stt/src/take/window.rs::owned_piece deps: &str,bool
Design: extends oversized-unit @ crates/gateway/tests/it/realtime_stt.rs
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt.rs::interim_scheduler_enforces_cadence_minimum_silence_and_coalescing
Design: removes oversized-unit @ crates/gateway/tests/it/realtime_stt.rs::producer_snapshots_partition_finalized_agreed_and_tentative_text
Design: new hidden-dependency @ crates/gateway/tests/it/realtime_stt.rs::native_fixture deps: &str,&str
Design: new pure-function @ crates/gateway/tests/it/realtime_stt.rs::normalized_words deps: &str
Violates: A2 - credential ownership in crates/gateway-stt/src/realtime is not determinable from diff
Pending: N6 - compounds
Pending: N30 - compounds
Deferred: packaged-native test execution awaits external fixtures
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Preserve accepted hypothesis text only when explicit skip outcomes continuously cover its committed audio range. Keep every decoded final authoritative, including empty text, and preserve first-failure fallback order while final work interleaves with later input.

- `SegmentOutcome`, `FinalRangeOutcome`, and `AcceptedHypothesis` bind each decision and accepted text to exact sample coverage. Skip reasons distinguish short speech, undersized final windows, and silence from decoded empty text.
- `assemble_completion` appends decoded results in order and uses an accepted hypothesis once only when contiguous skipped outcomes cover its committed range without a decoded interruption or gap.
- `run_final_pipeline` records leading silence and range outcomes in command order, stops later decode work after the first failure, and returns that failure before fallback transcription.
- `assert_stop_reconciles_skipped_range` parks earlier final work while later speech and silence commit, then verifies one final decode and recovered skipped-range text. `assert_same_range_final_authority` pins divergent and empty decoded finals, while `skipped_then_decoded_then_failed_falls_back_once_in_audio_order` pins failure ordering.
- `module-ceilings.toml` raises the ratchets for the expanded range-tracking modules and registers the new outcome module.

Design: new value-object @ crates/gateway-stt/src/segment.rs::SegmentOutcome
Design: new value-object @ crates/gateway-stt/src/take/final_outcome.rs::SkipReason
Design: new value-object @ crates/gateway-stt/src/take/final_outcome.rs::FinalRangeResult
Design: new value-object @ crates/gateway-stt/src/take/final_outcome.rs::FinalRangeOutcome
Design: new oversized-unit @ crates/gateway-stt/src/take/final_outcome.rs
Design: new pure-function @ crates/gateway-stt/src/take/final_outcome.rs::assemble_completion deps: &[AcceptedHypothesis],&[FinalRangeOutcome],usize
Design: new pure-function @ crates/gateway-stt/src/take/final_outcome.rs::skipped_outcomes_exactly_cover deps: &Range<usize>,&[FinalRangeOutcome],usize
Design: extends message-passing @ crates/gateway-stt/src/take/finalization.rs::FinalPipeline
Design: extends oversized-unit @ crates/gateway-stt/src/take/finalization.rs
Design: extends shared-mutable-state @ crates/gateway-stt/src/take/state.rs::TakeState
Design: extends oversized-unit @ crates/gateway-stt/src/take/state.rs
Design: new value-object @ crates/gateway-stt/src/take/window.rs::AcceptedHypothesis
Design: extends oversized-unit @ crates/gateway-stt/src/take/window.rs
Design: extends oversized-unit @ crates/gateway-stt/tests/it/legacy_stream.rs
Design: extends oversized-unit @ crates/gateway-stt/tests/it/realtime_session.rs
Design: extends oversized-unit @ crates/gateway/tests/it/realtime_stt.rs
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt.rs::assert_stop_reconciles_skipped_range
Violates: A2 - credential ownership in crates/gateway-stt/src/take is not determinable from diff
Pending: N8 - compounds
Deferred: pure silence without an accepted hypothesis has no new assertion
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Record the authoritative operator acceptance of the unsigned Windows package built from the accepted implementation identity. Preserve earlier failed attempts as audit history and document installed executable identities without claiming an invalid Workshop hash equality. Keep production and packaging inputs unchanged while deferring signing to release CI.

- `design/generic-realtime-stt-acceptance.md` records exact installed paths, versions, hashes, timestamps, process identities, and the explicit `Works correctly. Accepted.` verdict.
- `producer_hypothesis_ownership` joins `validSequenceCases` and repairs the canonical UI fixture expectation for producer-owned hypotheses.
- `design/generic-realtime-stt-acceptance.md` leaves checklist details beyond live transcription and short-utterance Stop unmeasured. Production and packaging inputs remain unchanged from `2d1ecca8`, and signing remains untested.

Violates: A96 - not determinable from diff
Pending: N2 - compounds
Deferred: Signing remains untested until release CI
Deferred: Checklist items beyond live transcription and short-utterance Stop remain unmeasured
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Repair the Workshop baseline ratchet while keeping the tests equivalent. Add inputs to RecordingObserver and use it for all observer mutex access. Shorten or remove helper comments.

- The staged change stays inside mod tests. It does not add or remove test cases or expected values.
Retire the custom speech socket, capability proxy, status channel, and browser fallback after canonical fixtures and independent Gateway, relay, and browser suites cover their behavior. Leave one authenticated Realtime transcription path beside batch transcription and remove the reverse Workshop dependency. Enforce the final graph, route surface, and zero-symbol state as permanent architecture gates.

- `gateway-stt` now depends on configuration, artifact, backend, engine, and progress crates only. `gateway` owns route mounting, the Whisper backend depends on the engine and FFI leaf, and `workshop-server` remains an independent authenticated relay with no temporary dependency exceptions.
- `SpeechService` merges only batch and Realtime routes. Gateway keeps `POST /v1/audio/transcriptions` and `WS /v1/realtime?intent=transcription`; Gateway and Workshop both return not found for `/stt` and `/stt/capability`, and Workshop fixes its upstream socket to Realtime.
- `legacy_stream.rs` policy, generation, origin, interim, final, fallback, segmentation, silence, and disconnect assertions map to the canonical wire contract and the mounted scheduler, hypothesis, completion, authority, privacy, and typed-error cases in `realtime_stt.rs`. The removed `take.rs` agreement and fallback cases map to the same producer-partition, skipped-range, divergent-final, and terminal-failure evidence.
- `routes/stt.rs` relay assertions map to the authenticated, same-origin, payload-opaque, control-frame, and close propagation cases in `realtime_relay.rs`. The boolean, malformed-body, and network cases in `stt-capability.mjs` map to removal of the probe, not-found route checks, boot-time unexpected-fetch rejection, and Realtime ready or unavailable cases in `agent-stt.mjs`; insertion, second-take, cleanup, and capture remain covered by `agent-stt-boot.mjs` and the sole `pcm16-capture` processor checks in `pcm-worklet.mjs`.
- `legacy_speech_seams_are_absent_from_production_sources` rejects the old Rust modules, connectors, headers, route factories, browser types, capability probe, and processor name. Its companion zero-symbol test injects every forbidden UI form, checks adversarial processor contexts, and proves current Realtime and browser-capture symbols remain accepted.

Design: removes surface-growth @ crates/gateway-stt/src/stt.rs boundary: wire
Design: removes shared-mutable-state @ crates/gateway-stt/src/generation.rs::Shared::changes
Design: removes flag-parameter @ crates/workshop-server/src/gateway/socket.rs::GatewayClient::connect_socket
Design: removes surface-growth @ crates/workshop-server/src/routes/stt.rs boundary: wire
Design: removes speculative-abstraction @ crates/workshop-server/src/serve.rs::RouteFactory
Design: removes surface-growth @ crates/workshop-server/src/lib.rs::spawn_with_routes boundary: pub
Design: removes surface-growth @ crates/workshop-server/ui/src/ui/stt.ts::sttCapability boundary: pub
Violates: A2 - credential ownership in crates/gateway-stt/src/service.rs::SpeechService is not determinable from diff
Pending: N36 - compounds
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Replace migration allowances with exact final architecture gates, isolate Gateway builds from Workshop tooling, and make the completed speech topology discoverable from maintained documentation. Record verified debt reduction and deterministic guide generation so later changes have explicit dependency, public-surface, and source-size baselines.

- `crates/gateway-stt/tests/it/architecture.rs` enforces exact workspace edges, exact public-root counts of 6, 7, 2, and 6, complete source manifests, and a 500-line maximum for every STT source module. `tools/check-stt-architecture.mjs` requires acyclic production module graphs and exact root counts for all four crates with pinned Cargo and analysis tools.
- `.github/workflows/ci.yml` builds Gateway after installing only the config UI dependencies, then runs scripted and Rust architecture checks in normal CI. `.github/workflows/stt-miri.yml` removes Workshop UI setup from the native speech lane.
- `crates/gateway-stt-engine/src/test_fixtures/tests.rs` separates 304 lines of deterministic worker tests from the fixture implementation so both modules satisfy the final ceiling without changing their assertions.
- `AGENTS.md` and `crates/workshop-server/AGENTS.md` correct build and ownership rules. Gateway, configuration, Workshop, and source-guide documentation now describe the generic Realtime route, exact bounds, discovery facts, and payload-opaque relay.
- `design/generic-realtime-stt.md` records the final ownership, dependency, wire, lifecycle, CI, and debt architecture. `design/generic-realtime-stt-acceptance.md` records passing gates, the before and after counts, refreshed generated Gateway and Workshop guides, and identical hashes for all nine generated artifacts on a clean second run.

Design: replaces oversized-unit @ crates/gateway-stt-engine/src/test_fixtures/tests.rs was: crates/gateway-stt-engine/src/test_fixtures.rs::tests
Design: new pure-function @ tools/check-stt-architecture.mjs::publicRootCount deps: crateName,source boundary: pub
Design: new surface-growth @ tools/check-stt-architecture.mjs::publicRootCount deps: crateName,source boundary: pub
Design: new pure-function @ tools/check-stt-architecture.mjs::requireExactPublicRootCount deps: actual,crateName,expected boundary: pub
Design: new surface-growth @ tools/check-stt-architecture.mjs::requireExactPublicRootCount deps: actual,crateName,expected boundary: pub
Design: new pure-function @ crates/gateway-stt/tests/it/architecture.rs::dependency_drift_message deps: &str
Design: extends pure-function @ crates/gateway-stt/tests/it/architecture.rs::validate_module_ceiling deps: usize,usize
Violates: A2 - credential ownership in crates/gateway-stt/tests/it/architecture.rs is not determinable from diff
Pending: N17 - compounds
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Bookend every serving file log with a versioned launch record and one terminal outcome. Keep the launch record first, place fatal termination after the complete error chain, and leave no-subscriber launches on their existing output paths.

- `main` emits terminal records only when `logging.is_some()` and shuts down the runtime afterward, so the final file record drains before process exit.
- `init_logging` emits `promptforge-gateway {} starting` immediately after subscriber installation and before `logging to {}`.
- `headless_serve_bookends_the_log_file` spawns the real executable, waits for its connection file, posts the shutdown route, waits for successful child exit, and asserts the first and last log lines.
- `a_fatal_boot_error_lands_in_the_log_with_its_chain` runs a failing child and asserts that the fatal terminal record follows the last `caused by:` record and remains last.
- `main` still returns before `init_logging` for help, version, diagnostics, and second-instance handoff. `init_logging` keeps both stdout-only branches without a file runtime, and `print_error_chain` remains the no-subscriber error fallback.

Design: new surface-growth @ crates/gateway/src/main.rs::main boundary: persisted
Design: new surface-growth @ crates/gateway/src/main.rs::init_logging boundary: persisted
Violates: A2 - not determinable from diff
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Supervise local sidecar Gateways and publish each validated replacement as one endpoint and credential generation. Wake heartbeat, progress, catalog, chat, proxy, and Realtime consumers only after the complete snapshot is live, while explicit LAN targets stay fixed. Preserve configured bearer identity across unchanged restarts and accept replacement identity by process or boot data, independent of port reuse. Complete all release, package, recovery, and operator acceptance gates.

- `GatewayBinding` centralizes the HTTP client, model client, endpoint, bearer, and generation in one immutable snapshot. Replacement builds the complete snapshot before atomic publication and consumer notification.
- `run_supervision` re-resolves the connection file, validates process image, health, and bearer acceptance, and launches the installed sibling under bounded backoff when no live local Gateway remains. New process or boot identity permits unchanged ports and keys, while configured key edits publish with their replacement.
- `composeTranscript` owns one separator only for standalone dictation at the logical document end. Hypotheses, completions, rollback, selected replacement, and producer-supplied whitespace keep consistent composition.
- `design/generic-realtime-stt-acceptance.md` records the complete release suite, native and architecture gates, generated-document hashes, installed package identities, recovery after more than 60 seconds, and final operator acceptance. Signing remains untested and deferred to release CI.

Design: new facade @ crates/workshop-server/src/gateway_binding.rs::GatewayBinding
Design: new parameter-object @ crates/workshop-server/src/gateway_binding.rs::GatewayBinding
Design: new shared-mutable-state @ crates/workshop-server/src/gateway_binding.rs::GatewayBinding
Design: new surface-growth @ crates/workshop-server/src/gateway_binding.rs::GatewayUpdater boundary: pub
Design: new surface-growth @ crates/workshop-server/src/gateway.rs::GatewayError::InvalidSidecar boundary: pub
Design: new surface-growth @ crates/workshop-server/src/serve.rs::ServerHandle::gateway_updater boundary: pub
Design: new surface-growth @ crates/workshop-server/src/lib.rs::fixtures::gateway_updater boundary: pub
Design: extends oversized-unit @ crates/workshop-server/src/session_agents/supervisor.rs::spawn deps: AgentSession,AgentSessions,GatewayBinding,SessionHost
Design: new shared-mutable-state @ crates/workshop/src/main.rs::GatewaySlot
Design: extends service-locator @ crates/workshop/src/main.rs::run
Design: new pure-function @ crates/workshop/src/gateway.rs::same_gateway_identity deps: &ConnectionFile,&ConnectionFile
Design: extends parallel-abstraction @ crates/workshop-server/ui/src/ui/realtime-stt.ts::Take
Design: extends oversized-unit @ crates/workshop-server/ui/src/ui/realtime-stt.ts::setupStt deps: RealtimeTranscriptionService,SpeechCaptureService,SttBlocker,SttElements,SttStatus boundary: pub
Design: extends surface-growth @ crates/workshop-server/ui/src/ui/stt.ts::SttInputTarget boundary: pub
Design: new pure-function @ crates/workshop-server/ui/test/agent-stt.mjs::producerHypothesis deps: itemId,revision,transcript
Design: new pure-function @ crates/workshop-server/ui/test/agent-stt.mjs::producerCommitted deps: itemId
Design: new pure-function @ crates/workshop-server/ui/test/agent-stt.mjs::producerCompletion deps: itemId,transcript
Pending: N57 - compounds
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Plan: vibe/2026-09-05-2-generic-realtime-stt.md
Split Gateway Realtime integration coverage into six concern-focused files while retaining shared support in the parent module. Preserve all 18 tests, including the ignored native case, with unchanged test bodies. Seed and activate the attributable debt-removal plan for the remaining work.

- `crates/gateway/tests/it/realtime_stt.rs` keeps shared fixtures and uses `include!` to assemble the authentication, protocol, lifecycle, recovery, overload, and canonical sequence coverage in one module scope.
- `vibe/2026-09-07-1-promptforge-debt.md` records the debt program and marks `Step 1` complete, while `vibe/ACTIVE` selects it for continued execution.

Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt/authentication.rs
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt/authentication.rs::gateway_auth_origin_query_and_final_speech_surfaces_precede_upgrade
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt/canonical_sequence.rs
Design: replaces oversized-unit @ crates/gateway/tests/it/realtime_stt/canonical_sequence.rs::canonical_fixture_drives_hypothesis_completion_and_clear was: crates/gateway/tests/it/realtime_stt.rs::canonical_fixture_drives_hypothesis_completion_and_clear
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt/lifecycle.rs
Design: replaces oversized-unit @ crates/gateway/tests/it/realtime_stt/lifecycle.rs::interim_scheduler_enforces_cadence_minimum_silence_and_coalescing was: crates/gateway/tests/it/realtime_stt.rs::interim_scheduler_enforces_cadence_minimum_silence_and_coalescing
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt/overload.rs
Design: replaces oversized-unit @ crates/gateway/tests/it/realtime_stt/overload.rs::saturated_commit_preserves_the_canonical_input_for_retry was: crates/gateway/tests/it/realtime_stt.rs::saturated_commit_preserves_the_canonical_input_for_retry
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt/protocol.rs
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt/protocol.rs::mounted_route_drives_scripted_wire_ownership_errors_and_privacy
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt/protocol.rs::mounted_session_errors_keep_canonical_codes_parameters_and_correlation
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt/recovery.rs
Design: new oversized-unit @ crates/gateway/tests/it/realtime_stt/recovery.rs::admission_is_bounded_and_replacement_closes_with_1012
Plan: vibe/2026-09-07-1-promptforge-debt.md
Split Workshop chat and Realtime relay integration tests into concern-focused modules while each parent retains shared fixtures. Preserve all 11 chat tests and all 9 relay tests with unchanged test bodies.

- `crates/workshop-server/tests/it/chat_gate.rs` uses `include!` to assemble protocol, lifecycle, recovery, overload, and canonical sequence coverage.
- `crates/workshop-server/tests/it/realtime_relay.rs` uses `include!` to assemble authentication, protocol, lifecycle, recovery, overload, and canonical sequence coverage.
- `crates/workshop-server/tests/it/chat_gate.rs` and `crates/workshop-server/tests/it/realtime_relay.rs` add no persistent count or physical-line ceiling.

Design: new oversized-unit @ crates/workshop-server/tests/it/chat_gate/lifecycle.rs
Design: replaces oversized-unit @ crates/workshop-server/tests/it/chat_gate/lifecycle.rs::gate_catalog_replacement_during_acceptance_settles_the_turn_exactly_once was: crates/workshop-server/tests/it/chat_gate.rs::gate_catalog_replacement_during_acceptance_settles_the_turn_exactly_once
Design: new oversized-unit @ crates/workshop-server/tests/it/chat_gate/recovery.rs
Design: replaces oversized-unit @ crates/workshop-server/tests/it/chat_gate/recovery.rs::gate_restart_reloads_the_jsonl_and_resumes_waiting_for_input was: crates/workshop-server/tests/it/chat_gate.rs::gate_restart_reloads_the_jsonl_and_resumes_waiting_for_input
Design: replaces oversized-unit @ crates/workshop-server/tests/it/chat_gate/recovery.rs::gate_binding_loss_surfaces_one_error_and_recovers_after_selection was: crates/workshop-server/tests/it/chat_gate.rs::gate_binding_loss_surfaces_one_error_and_recovers_after_selection
Design: new oversized-unit @ crates/workshop-server/tests/it/realtime_relay/authentication.rs
Design: replaces oversized-unit @ crates/workshop-server/tests/it/realtime_relay/authentication.rs::realtime_relay_is_authenticated_fixed_and_payload_opaque boundary: wire was: crates/workshop-server/tests/it/realtime_relay.rs::realtime_relay_is_authenticated_fixed_and_payload_opaque
Pending: N49 - compounds
Pending: N50 - compounds
Plan: vibe/2026-09-07-1-promptforge-debt.md
Freeze the split integration suites behind exact source, include, size, and test-count contracts. Use syntax-aware Rust discovery so comments, literals, attributes, macros, and configuration gates cannot hide drift.

- `checkIntegrationTestCeilings` normalizes repository paths, requires exact suite and file coverage, verifies every direct `include!` exactly once, enforces physical-line ceilings, and checks exact test totals.
- `analyzeRust` tokenizes Rust syntax and fails closed on nested includes, generated tests, conditional tests, unsupported test attributes, and malformed delimiters or literals.
- `tools/integration-test-ceilings.json` records 18 Gateway Realtime tests, 11 Workshop chat tests, and 9 Workshop relay tests with ceilings for every entry and concern file.
- `tools/check-integration-test-ceilings.test.mjs` covers newline variants, path separators, comments, multiline attributes, configuration gates, macro generation, missing and extra files, include drift, ceiling overruns, and total drift.
- `.github/workflows/ci.yml` runs the adversarial driver tests and the repository gate before architecture tool installation.

Design: new pure-function @ tools/check-integration-test-ceilings.mjs::repoPath deps: value
Design: new surface-growth @ tools/check-integration-test-ceilings.mjs::repoPath deps: value boundary: pub
Design: new pure-function @ tools/check-integration-test-ceilings.mjs::physicalLineCount deps: source
Design: new surface-growth @ tools/check-integration-test-ceilings.mjs::physicalLineCount deps: source boundary: pub
Design: new oversized-unit @ tools/check-integration-test-ceilings.mjs::analyzeRust deps: label,source
Design: new oversized-unit @ tools/check-integration-test-ceilings.mjs::checkIntegrationTestCeilings deps: manifest,requiredSuites,root
Design: new surface-growth @ tools/check-integration-test-ceilings.mjs::checkIntegrationTestCeilings deps: manifest,requiredSuites,root boundary: pub
Plan: vibe/2026-09-07-1-promptforge-debt.md
Apply one immutable budget set to formatted records and later queue, wait, shutdown, segment, and retention work. Use fixed-capacity buffers through formatting and text redaction, validate the complete input as UTF-8 even after retention stops, and keep only valid prefixes with an explicit truncation marker.

- `LOG_LIMITS` centralizes six memory, latency, and disk budgets with compile-time relationships; only `max_formatted_record_bytes` takes effect in this change.
- `LogEventWriter` replaces growable event storage with `BoundedBytes`, scans retained and discarded input through `Utf8Validator`, rejects invalid or incomplete UTF-8, and marks valid truncation with `TRUNCATION_MARKER`.
- `RedactedLine` keeps every redaction buffer at the record capacity and preserves valid character boundaries when replacement text expands the result.
- `LossCounts` combines eviction, truncation, and rejection in one pressure summary; rejected records count as dropped while retained truncated records count as affected.
- `crates/gateway-logging/src/queue.rs` still reserves sequence before locking, blocks protected producers without a timeout, and reports loss only when empty; aggregate queued bytes, segment rotation, and broader redaction remain outside this change.

Deferred: Enforce aggregate queued bytes and admission ordering.
Deferred: Bound producer waits and shutdown.
Deferred: Rotate log segments under the aggregate retention budget.
Deferred: Expand structured and textual redaction coverage.
Plan: vibe/2026-09-07-1-promptforge-debt.md
Bind sequence assignment to successful admission and enforce record and byte ceilings under the same queue lock. Preserve priority eviction while fencing pressure summaries after all records admitted before the low-water transition, so repeated pressure episodes remain distinct and observable.

- `State` owns queued bytes, the next sequence, loss counts, and pending summaries under one mutex, while `QueueLimits` defines record and byte ceilings and their shared half-capacity low-water mark.
- `enqueue_after` rejects records larger than the byte budget, evicts eligible lower-priority records until both ceilings permit admission, and blocks producers when neither admission nor priority-safe eviction can proceed.
- `PendingSummary` fixes each closed loss episode after its admitted tail and before later records; a second pressure episode receives independent dropped, truncated, and rejected counts.
- `byte_blocked_producers_wake_after_drain_and_close` proves byte-blocked producers wake after capacity returns or admission closes.
- `crates/gateway-logging/src/queue.rs` retains unbounded producer waits; shutdown timeout handling remains outside this change.

Design: new oversized-unit @ crates/gateway-logging/src/queue.rs::byte_blocked_producers_wake_after_drain_and_close
Violates: A2 - credential ownership in gateway logging is not determinable from diff
Deferred: Producer wait and shutdown timeout handling remain outside this commit.
Plan: vibe/2026-09-07-1-promptforge-debt.md
Apply configured deadlines to protected producers and logger shutdown, including time spent acquiring the queue mutex. Preserve loss counts across close races, attempt an emergency diagnostic after timeout, and detach stalled workers while healthy sinks still drain, flush, and join.

- `LogQueue` registers active producers through `admission_gate` before mutex acquisition and keeps outstanding record, summary, and pressure counts in preallocated atomics.
- `LogWorker` replaces the unit owner with a single-field handle owner; `is_finished` supports bounded waiting and `join` consumes only a finished worker.
- `enqueue_after` starts the producer deadline before its admission hook, includes mutex and condition-variable waits, and records a timeout as rejected pressure.
- `close_locked` preaccounts `blocked_producers`; `close_accounted` prevents an awakened producer from reporting the same close loss twice.
- `shutdown_with_waiter` reserves up to `MAX_EMERGENCY_START_WAIT` for diagnostic startup, shares the remaining budget across queue closure and worker completion, and invokes `abandon` before it drops an unfinished owner.
- `shutdown` converts both `Joined` and `Detached` outcomes into a successful unit result; detachment loss reaches best-effort stderr through `write_emergency_diagnostic`.
- `complete_batch` runs only after `flush`; deterministic `StallPoint` tests block `Write`, `Flush`, queue closure, and the diagnostic helper while the healthy path still joins.
- `attempt_emergency_diagnostic` starts a detached stderr helper and waits only for its startup handshake; it adds no spool and does not await the diagnostic write.

Design: new shared-parameter-cluster @ crates/gateway-logging/src/queue.rs::LogQueue::new_for_test_with_wait
Design: new oversized-unit @ crates/gateway-logging/src/queue.rs::LogQueue::enqueue_after
Design: new flag-parameter @ crates/gateway-logging/src/queue.rs::LogQueue::complete_batch
Design: removes oversized-unit @ crates/gateway-logging/src/queue.rs::byte_blocked_producers_wake_after_drain_and_close
Design: new surface-growth @ crates/gateway-logging/src/runtime.rs::LogRuntime::shutdown boundary: pub
Design: new swallowed-exception @ crates/gateway-logging/src/runtime.rs::LogRuntime::shutdown boundary: pub
Design: new oversized-unit @ crates/gateway-logging/src/runtime.rs::assert_stalled_shutdown
Design: new newtype @ crates/gateway-logging/src/worker.rs::LogWorker
Violates: A2 - credential ownership in gateway logging is not determinable from diff
Plan: vibe/2026-09-07-1-promptforge-debt.md
Remove floating Rust selection and the fixed user-profile tool layout from native CI. Pin RUSTUP_TOOLCHAIN to 1.89.0, set RUSTUP_AUTO_INSTALL to zero, and validate cargo.exe and rustc.exe from PATH or PROMPTFORGE_RUST_1_89_0_BIN before Cargo caching.

- The versioned contract accepts only an absolute existing directory. It adds that directory to GITHUB_PATH only after both tools pass.
- The preflight rejects missing tools, command failures, unknown version output, and versions other than 1.89.0.
- tools/check-stt-native-workflow.test.mjs adds checks for the pinned MSRV contract and keeps Miri setup and fixture hashes unchanged.
Keep the STT architecture API gate deterministic across runners. Run standard cargo checks with 1.89.0, run cargo-public-api through nightly-2026-09-05, and select each crate by manifest and package name.

- Provision 1.89.0 and nightly-2026-09-05 in .github/workflows/ci.yml.
- Separate runCargo and runRustdocCargo so only public API inspection uses the pinned nightly.
- Make runPublicApi stop on a missing nightly or virtual manifest error without a fallback.
- Tests pin the command arguments, toolchain environment, exact package selection, and one-call failure behavior.
- This diff does not change public API snapshots, module ceilings, or legacy STT configuration parsing.
Bound each active and retained log segment and prune the oldest bytes before new writes exceed the aggregate disk budget. Preserve the current and numbered diagnostic names while reserving space for a truncation marker and one complete terminal record. Stage and sync replacements before transactional installation so rollback and restart recovery choose a complete old or committed chain.

- `SegmentedFile` owns active and retained byte counts, enforces both budgets before admission, and rotates only after flushing and syncing the active file.
- `rotate_files` creates durable staged copies, a preparation marker, rollback copies, and a durable commit marker. It restores old targets after an uncommitted failure and keeps committed targets during restart recovery.
- `open_log_file_with_limits` recovers interrupted work, compacts oversized legacy segments at valid text boundaries, prunes oldest retained data, and shifts the existing active log into the same numbered layout.
- `live_rotation_recovers_every_injected_filesystem_failure` and `restart_compaction_recovers_every_injected_filesystem_failure` inject each filesystem checkpoint, including remove-then-rename gaps, and prove recovery keeps one complete state.

Design: new oversized-unit @ crates/gateway-logging/src/worker.rs
Design: new pure-function @ crates/gateway-logging/src/worker.rs::valid_utf8_tail deps: &[u8]
Design: new pure-function @ crates/gateway-logging/src/worker.rs::artifact_path deps: &Path,&str
Design: new pure-function @ crates/gateway-logging/src/worker.rs::rotation_prepared_path deps: &Path
Design: new pure-function @ crates/gateway-logging/src/worker.rs::rotation_committed_path deps: &Path
Design: new pure-function @ crates/gateway-logging/src/worker.rs::rotation_targets deps: &Path,&[PathBuf]
Design: new pure-function @ crates/gateway-logging/src/worker.rs::crashing_fault deps: usize
Design: new oversized-unit @ crates/gateway-logging/src/worker.rs::live_rotation_recovers_every_injected_filesystem_failure
Design: new oversized-unit @ crates/gateway-logging/src/worker.rs::byte_boundaries_rotate_a_full_numbered_chain_without_splitting_utf8
Violates: A2 - credential ownership in gateway logging is not determinable from diff
Plan: vibe/2026-09-07-1-promptforge-debt.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants