Chore/production hardening - #1
Open
avase33 wants to merge 3 commits into
Open
Conversation
… green `ruff check` was failing on 49 errors even though nothing in the repo had changed. The cause was configuration, not code: the dev extra asked for `ruff>=0.5` with no upper bound, and ruff widened its *default* rule set from ~60 to ~400 rules in 0.16, so CI installed a linter that enforced far more than the one this code was written against. Two changes make the check mean the same thing tomorrow as today: * `[tool.ruff.lint].select` now names the rule families explicitly instead of inheriting ruff's default, and the `dev` extra bounds ruff (plus pytest, pytest-asyncio and mypy) so a release can't silently change what CI enforces. * the 49 findings are fixed rather than suppressed: `Optional[X]` -> `X | None`, `typing.AsyncIterator`/`Callable` -> `collections.abc`, `Union[...]` -> `|`, `%`-formatting -> f-strings, `re.I` -> `re.IGNORECASE`, sorted `__all__` and import blocks, `int(round(...))` -> `round(...)`, and every line brought under the 100-column limit the config already declared but never enforced. The one suppression is a `noqa: BLE001` on the tool dispatcher's catch-all, which is deliberate: a failing tool has to come back as an error the model can talk about, never as an exception that drops a live call. The pylint refactor family (PL*) is left unselected because PLR0911/PLR0912 fire on the dialog state machine's branch-per-slot design and PLR2004 on the codec constants; both are intentional, so the family stays off rather than being papered over per line. Also hoists the per-call `import sys` in the PCM codec to module scope and resolves the byte-order test once at import instead of per frame, chains the `ImportError` behind `vox serve`'s missing-extra message, and extends lint coverage to `scripts/` and `examples/`. CI gains `permissions: contents: read`, `fail-fast: false` and per-ref concurrency cancellation. Verified: `ruff check vox_agent tests scripts examples` exits 0, `pytest -q` 25 passed, `python scripts/verify_full.py` 19 passed / 0 failed on Python 3.10, 3.11 and 3.12. Audio output is byte-identical to before the change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011mYwvHvELPE8nvgjP3AMg8
…e diary keys `_looks_iso` accepted `2026-7-9` via `strptime` and then passed the string through untouched, while every date the system generates itself comes from `date.isoformat()` and is zero-padded. Two spellings of the same day therefore produced two different `day` values, and since double-booking is prevented by `UNIQUE(day, time)` on that column, a partly padded date could book a slot that was already taken. `_as_iso_date` replaces it: one strict regex, explicit construction, and a canonical `YYYY-MM-DD` string out. `_resolve_day` folds the "ISO date or weekday name" decision that both `check_availability` and `book` were duplicating into one place. Parsing stays explicit rather than delegating to `date.fromisoformat`, which from 3.11 also accepts `20260709` and ISO week dates — behaviour that would differ across the supported Python range. Also makes "today" explicitly local (`datetime.now().astimezone().date()` instead of a naive `date.today()`): same result, no implicit assumption, and it satisfies the DTZ rules now that they are enforced. Verified: `2026-07-09` then `2026-7-9` at the same time is now correctly rejected as `slot_taken`; every other input in a 15-case table resolves exactly as before. `pytest -q` 25 passed, `python scripts/verify_full.py` 19 passed / 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011mYwvHvELPE8nvgjP3AMg8
Removals:
* `docs/architecture.md` — a 30-line generated template ("Repository pattern for
data access layer", for a project with no repository pattern) sitting next to
the real `docs/ARCHITECTURE.md`. Two paths differing only in case cannot both
be checked out on a default macOS or Windows filesystem, so this pair broke
clone for a large share of readers.
* `docs/dev-log-2026-07-{17,20,21,22}.md` — four byte-identical generated files
claiming "Refactored core modules for better maintainability" on days when no
code changed.
* `CHANGELOG.md` is rewritten in Keep-a-Changelog form. What was there described
a session id (`## [20260722212147]`) and invented work; what is there now is
0.1.0 as it actually shipped, plus this branch's changes.
README corrections, all measured on this machine rather than asserted:
* The pasted `vox demo` transcript was stale — it showed two tool calls and a
date-based slot list, where a real run makes three calls (the second checks the
specific requested time) and offers weekday-relative slots. Replaced with
verbatim output, and the two lines that legitimately vary between runs (the
confirmation date, the latency line) are called out as such.
* The latency block contained literal `...` where the numbers should be. It now
carries real figures from `vox demo` and `vox bench -n 20`, the commands and
runtimes to reproduce them, and the machine they came from.
* "speaks back in well under half a second" was true of time-to-first-byte only;
`total_p50_ms` is ~2.5s. A new "Latency, precisely" section defines all four
metrics, states that any sub-500ms figure in this project refers to `ttfb_ms`
and nothing else, and explains why `total_ms` is seconds: it includes streaming
every audio frame of the reply, and measures 0.54-0.55x the emitted audio
duration, so it tracks how long the sentence is rather than any compute cost.
* Three claims the code does not support are corrected: barge-in credited
`audio/vad.py`, but the interrupt flag lives in `pipeline.py` and the browser
demo's signal comes from the browser's own `onspeechstart`; `audio/` claimed
"resampling" where only decimation exists; the metrics row implied p50/p95 for
all four timings where `summary()` aggregates TTFB and total.
* A new "Limitations" section records what is honestly unfinished: `VOX_STATE=redis`
persists only the dialog stage, the paid-provider adapters are untested code
paths, the server drops raw mic audio unless a real STT is configured, the
calendar is in-memory by default, and the dialog policy repeats itself when a
requested slot is already gone. The existing "How offline works honestly"
section is kept as-is.
* `RedisStore`'s docstring claimed history was serialised as JSON. It isn't —
only `stage` is written. Docstring now says so.
Verified: every number and transcript above came from running `vox demo`,
`vox bench -n 20`, `vox bench -n 2` and `scripts/verify_full.py` on the tree at
this commit; timings are wall-clock measurements on the machine named in the
README.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011mYwvHvELPE8nvgjP3AMg8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.