• Timmy daily-2026-10-29 — operator backup, restore, and runbook
    Quality gates / quality (push) Canceled after 0s
    Pre-Release

    timmy released this 2026-10-29 12:46:43 +00:00 | 16 commits to main since this release

    Prerelease for review — tag daily-2026-10-29 on commit 65a0258bba891a99b0d827fa67b79b36bb70dd11 (America/New_York release date 2026-10-29, built 2026-10-29T12:35:27Z).

    What changed

    Operator backup, restore, and production runbook (issue #39, PR #90). The corpus could ingest a consented sample, record its provenance, survive a withdrawal, and publish a card about itself — but nobody could take a copy of the records away, and no operator could bring them back. This release adds both, plus the page that says how.

    • scripts/operator_backup.py — backup archives only records: manifest.jsonl (provenance and withdrawal tombstones) and the derived images it points at. Model weights, transient inference images, secrets, logs, symlinks, and oversize files are refused by name, and every skipped path is written into the manifest with its reason. An omission is an auditable decision, not silence.
    • Every stored member carries a SHA-256, and the archive is reproducible — sorted members, zeroed mtime/uid/gid, gzip mtime 0 — so two backups of unchanged records are byte-identical.
    • restore fails closed. It verifies every digest, rejects absolute, traversal, link, and unlisted members, refuses a non-empty target, and only then writes. Afterwards it recounts manifest.jsonl and refuses if the counts differ, so a lost tombstone cannot restore quietly.
    • docs/PRODUCTION-RUNBOOK.md — the real topology, the network boundaries (the app's own HOST default is 0.0.0.0; the deployment sets HOST=127.0.0.1 from the mode-600 env template; the worker refuses a non-loopback publish), the four secrets and their rotation, retention, and both rollback paths. Its own test parses every documented python3 scripts/... command and checks each flag against the real CLI help, so the runbook cannot drift from the tools.
    • Release gates — scripts/build_release.py now runs the 11-case backup suite and the 9-case runbook suite before a release may exist, and requires four new on-camera captions.
    • Demo — an OPERATOR BACKUP card drawn from a real run: the real ingest and revoke commands build a throwaway corpus, one contribution is withdrawn, four decoy files are planted (weight shard, transient upload, env file, log), then the real backup, verify, and restore run and every value on the card is read back from them.

    Scope note, stated plainly: the operator backup is a command line, not a screen in the app. This release does not imply a UI. The app holds no server-side journal of its own, so there is no server copy to back up — the recording says so on screen rather than inventing one.

    Not closed by this release. PR #90 deliberately does not close #39. Acceptance criterion 3 is "rollback procedure is exercised on staging": the rollback code path is exercised by tests/staging-deploy.test.py against a rootless fixture, but the live drill on the private staging host is a human gate under #53 and was not run. The runbook states exactly that.

    Completed ticket / PR

    • Issue #39 — Define production topology, secrets, backup, and rollback runbook (open; two of three acceptance criteria delivered, the third is the #53 human gate).
    • PR #90 Operator backup, restore, and production runbook (#39) — merged as 65a0258 after diff review: 9 files, +1170/−4, text and code only (no binaries, model weights, real medical imagery, or secrets; the env file is a credential-free example template), linked to a non-human-gate issue (area/privacy, area/release, priority/P0, type/ops — no human-gate label).
    • Pre-merge verification on the PR head 48f0f56: npm test 230/230, backup 11/11, runbook 9/9, corpus 8/8, review 21/21, dataset-card 14/14, check:syntax, check:diff — all green before the merge was issued.

    Gates — 28/28 passed

    npm test 230/230; backup-restore 11/11; production-runbook 9/9; corpus manifest 8/8; corpus review 21/21; dataset card 14/14; calibration harness 19/19; mobile green path; photo-first acceptance; sleek Hermes chat; manual fallback + draft recovery; provisional suggestion state; hard-stool follow-up; vision corpus refusal; abstention quality gate; worker container receipts; worker post-load readiness; longitudinal pattern summary; grounded chat citations; training contribution consent; production backup restore; production runbook commands; portable backup restore; dependency audit (0 high); syntax (node/bash/python); secret scan; forbidden artifacts; demo H.264 720×1280 probe; complete MP4 decode; release delta verified painted on camera (91/91 captions, minimum bottom padding 14px, delta caption 9 lines / 139px / padBottom 15).

    Build note: the first gated build of this release ran without TIMMY_RELEASE_DELTA set, so the manifest carried the builder's fallback string "Verified rebuild of the current user-visible feature path — no new feature is claimed." That claim is false for this commit, so the build was moved to _superseded-2026-10-29-65a0258bba89-false-no-new-feature-delta and never published; the whole gated build was re-run end to end with the real delta and passed 28/28. No gate was skipped, loosened, or interpreted away.

    Operating note: timmy-app.service occupies 127.0.0.1:4173, the port the gated build binds. Production was stopped for the build and restored by a trap-guarded script on both paths; it reported active and /api/healthz 200 within two polls after each run. The build refuses to start over a live listener and hash-compares served files against the tree under test.

    Artifacts

    • source archive timmy-talking-turd-2026-10-29-65a0258bba89.tar.gz — 343,940 bytes — sha256 560f0fb46d499d26eb5ef29edd463fedd244c497f896323ad28336cadec71b2a
    • demo video timmy-talking-turd-2026-10-29-65a0258bba89-demo.mp4 — 27,064,343 bytes — H.264 720×1280 yuv420p, 25 fps, 425.24 s — sha256 0ef579708a7b59d828798f80750f72a519d2ff4eabd16e2c458572c07bfa205c

    Model / profile

    Recorded against the real app running from the built commit with TIMMY_VISION_ENABLED=false; vision replies replay the committed synthetic corpus through the app's real analysis path. The OPERATOR BACKUP card is a real scripts/operator_backup.py run over a corpus built on camera by the real ingest and revoke commands from CC0 synthetic fixtures — no model backed anything up or verified anything here, and the card says so on screen. No live model inference appears in the video and no real photo is used anywhere.

    Demonstrated on camera

    • OPERATOR BACKUP — subtitle command line, not app UI — real run, synthetic corpus
    • COMMAND python3 scripts/operator_backup.py backup --source srv-timmy-corpus
    • STORED 2 members · …jpg + manifest.jsonl
    • RECORDS samples 2 · tombstones 1 · active 1 — the withdrawal is carried, not dropped
    • REFUSED model-weights 1 · transient-inference 1 / REFUSED secret 1 · log 1
    • REASON every skipped path is named in the manifest, not silently dropped
    • DIGEST sha256 e646e7a8066d… · reproducible (sorted, mtime 0)
    • VERIFY ok · 2 members re-hashed against the manifest
    • RESTORE ok · samples 2 · tombstones 1 · active 1 / MANIFEST returned byte-for-byte, tombstone included
    • SERVER the app holds no journal of its own — there is no server copy to back up

    Known accuracy limitation

    Open-weight VLM acceptance is proven; Bristol accuracy is not clinically validated. The calibration receipt still reports BLOCKED because no candidate model clears every gate (issue #31). This release publishes a survivable copy of the record, not a measured classifier. User confirmation remains mandatory everywhere: photo analysis may suggest visible Bristol form, broad color, and image quality only, and never symptoms or a diagnosis. Visible color is not blood.

    Downloads