55 Commits
Author SHA1 Message Date
timmy 3e2253a052 wip(#19): privacy-safe model metrics in progress — preserved snapshot before nightly release
Local work in progress, not reviewed and not part of any PR. Committed only so the
nightly release can build from a clean main without discarding it.
2026-11-01 12:02:43 +00:00
timmy bc64055196 Merge main into the #16 ingress branch and close what the RC gate caught
Quality gates / quality (pull_request) Canceled after 0s
Brings #82 up to today's `main` (1bbcc10) and resolves the two conflicts as ordered
unions: `package.json` `test`/`check:syntax` and the release builder's syntax allow-list
and evidence keys. Both sides only appended, so nothing was dropped.

Verifying the merge against main's RC attack suite (#40) found two real defects, each
fixed test-first:

- An oversized body destroyed the socket mid-upload, so the caller saw `fetch failed`
  instead of the 413 it had already earned, and `security_attack_suite.mjs` could not
  complete at all. New test in `tests/analyze-ingress.acceptance.test.js` failed with
  `fetch failed` before the fix; `src/http-body.js` now keeps draining without buffering
  and refuses on `end`, so the ceiling still holds and the refusal is readable.
- #16's per-caller budget turned the suite's own load phase into 8/8 429s, which would
  have published refusals as analysis latency. The load phase now runs against a second
  permissive target and the suite refuses to publish a load figure if any sample was
  refused. `buildReport` also publishes `evidenceVerified`, so the verdict is
  re-derivable from the report — asserted by #40's acceptance test, which only trips once
  the verdict is GO.

Evidence on the merged tree: `npm test` 277/277, `test:security-attack` 8/8, the RC attack
suite returns VERDICT: GO with both P0 ingress cases passing (main is BLOCKED on exactly
those two), 14 browser acceptance suites green, and the release demo records end to end
(8:13, 97 captions) including the on-camera refusal scene.
2026-10-31 11:17:13 +00:00
timmy 1bbcc10a35 fix(demo): print the attack-case fields that were actually observed
Quality gates / quality (push) Canceled after 0s
The RC security card rendered `http undefined · worker 12` for the flood case,
which records no status, and repeated its bare status for the malformed case,
which records no provider call. Both read as a broken card rather than the
measurement it is. Build each row from the observed fields that exist and leave
the rest out.
2026-10-30 12:22:56 +00:00
timmy 0bf8230280 Merge pull request #91: RC attack suite and P0 release-security gate (#40)
Quality gates / quality (push) Canceled after 0s
Executable abuse suite against the real boundary with a counting stub worker, plus the verdict rule that makes a failing P0 case block RC approval. First run: VERDICT BLOCKED (two P0 ingress holds, one P1 queue hold).
2026-10-30 12:02:43 +00:00
timmy f0a9a734db feat(security): attack the RC before users do and gate approval on P0 findings (#40)
Quality gates / quality (pull_request) Canceled after 0s
Add an executable abuse suite that drives the real server with synthetic hostile bytes while a
counting stub worker records every call that reaches the model boundary, plus the verdict rule that
turns those observations into a release decision.

- src/release-security-gate.js: nearest-rank latency percentiles, per-case pass/fail derivation that
  names the field that broke, and the rule that any failing P0 case blocks RC approval while a
  required class with no verified evidence blocks it too. Reports are redacted and fail closed.
- scripts/security_attack_suite.mjs: oversized body, HTML polyglot as JPEG, undecodable bytes,
  missing consent, non-conforming worker answer, byte-identical replay, concurrent burst, captured
  log scan, and an out-of-surface deletion case counted only against a real test file. Writes the
  report, prints the verdict, exits 1 when blocked.
- docs/RC-SECURITY-EVIDENCE.md: procedure, rule, and the current finding stated plainly.
- Demo: a real run of the suite on camera, printing the verdict it derived instead of a flattering
  one; CI regenerates the same evidence on every change.

The suite currently returns VERDICT: BLOCKED: bytes are trusted from the data-URL label, so an HTML
polyglot still reaches the worker and only fails afterwards on schema. That is a finding against
#16, not a passing check, and it is why the gate treats a failing case as a block.
2026-10-30 11:12:52 +00:00
timmy 65a0258bba Merge pull request 'Operator backup, restore, and production runbook (#39)' (#90)
Quality gates / quality (push) Canceled after 0s
2026-10-29 12:05:42 +00:00
timmy 7fe1901703 fix(demo): make the on-camera caption bar opaque and prove its last line is not clipped
Quality gates / quality (push) Canceled after 0s
The release-delta caption is paragraph length. Rendered at 94% opacity over the running app, the
app's own text bled through it, and the final line sat flush against the bar's bottom edge — the one
caption a reviewer most needs to read was the hardest one to read. The bar is now opaque, long copy
steps down to 10.5/9.5px, and caption() measures the last line box with a Range and fails the run when
the bottom padding is under 3px, so a cramped caption can no longer be recorded as proof.
2026-10-28 12:47:04 +00:00
timmy b191d0427d Merge pull request 'Blind reviewer and adjudication tool for corpus labels (#26)' (#88) from timmy/26-review-adjudication into main
Quality gates / quality (push) Canceled after 0s
2026-10-27 12:05:58 +00:00
timmy d54665dbd9 chore(review): drop the trailing blank line that fails check:diff
Quality gates / quality (pull_request) Canceled after 0s
Release builder runs `git diff --check`; scripts/review_corpus.py ended with an extra newline, so the gate failed on the PR head. Whitespace only; no behaviour change.
2026-10-27 12:05:00 +00:00
timmy 209e21a951 Gate visual prefill on a versioned abstention policy (#32)
Quality gates / quality (pull_request) Successful in 13m29s
Confidence was the only thing standing between a model answer and a
prefilled journal entry, so a confident reading of a blurry photo could
still be offered as fact. Quality is now part of the policy: `poor`
abstains however confident the reply looked, with its own fixed copy.

The confidence floor, accepted quality levels, and refusal copy move into
src/abstention-policy.js so the parser, the corpus, and the browser cannot
drift into three explanations of the same refusal. parseVisionResponse
takes an optional policy, which is how the tests prove the parser carries
no hidden limits of its own.

evaluatePrefillGate turns the #29 calibration report into one release
decision: GO plus a validation-selected threshold scored on the held-out
test split, or prefill is blocked with each reason named. A missing report
is a block, never a default allow. scripts/abstention_policy_receipt.mjs
exposes that as a receipt (exit 0 / 3 / 2).

The committed synthetic predictions currently report BLOCKED and the
receipt says so. No candidate exists until #31, and the builder does not
hard-gate on the receipt yet; the on-camera quality gate is the evidence
this release carries.

The corpus gains a case that replays a 0.94-confidence answer about the
synthetic motion-blur fixture, and the release demo asserts in the browser
that nothing is prefilled before captioning it.

Verified: 226 unit tests, calibration harness (19), corpus manifest (8),
vision-refusal, photo-first, and provisional acceptance, check:syntax,
check:diff, and a full real-browser demo recording whose caption receipt
confirms the new scene painted on screen.
2026-10-26 10:58:38 +00:00
timmy 179f8fe6e9 feat(eval): score candidates against the release gates on held-out contributors (#29)
Quality gates / quality (pull_request) Successful in 13m25s
The ROADMAP gates (macro-F1 >= 0.80, precision@coverage >= 0.90, ECE <= 0.05) were prose with no
tool behind them. scripts/calibration_report.py now computes them from prediction records and
refuses input it cannot honestly score:

- a split is re-derived from the pseudonymous subject key with the ingestion rule, so a corpus
  cannot re-label its own splits; a contributor spanning two splits is refused before scoring
- the threshold is selected on validation only, and selecting on test or train is refused
- coverage, precision-at-coverage, macro-F1, per-class precision/recall, confusion, and expected
  calibration error are computed on accepted suggestions, with classes that abstained away named
  in excludedClasses instead of being scored as a silent zero
- exit codes carry the verdict: 0 GO, 3 BLOCKED, 2 refused

The release demo records the real harness against committed synthetic fixtures: a blocked
three-candidate comparison, a fixture engineered to clear every gate, and a leaked corpus refused.
Every number on the card is read back from the report the run wrote, and the captions state that
no model was measured, because a fixture is not evidence about accuracy.

The harness suite is wired into CI, check:syntax, and the release builder's own gates.
2026-10-25 11:05:28 +00:00
timmy 578cc9f129 style: end the new corpus files with a single newline
Quality gates / quality (pull_request) Successful in 13m1s
CI's diff-hygiene gate rejects a trailing blank line at EOF; the local
check:diff run happened before the first commit existed, so it checked the
previous range and missed it.
2026-10-24 11:12:50 +00:00
timmy 860c84f0fe feat(corpus): auditable manifest provenance, tombstones, and lifecycle guards (#25)
Quality gates / quality (pull_request) Failing after 9m42s
Promote the ingest script into an auditable corpus pipeline. Each sample now
records consent (version + timestamp), source kind and raw-input digest, both
hashes, the derivation pipeline, review status, split, and lifecycle state, with
the caller's path never written down.

Withdrawal is a new `--revoke <contribution-id> --reason` operation: it deletes
the derived image and any raw pre-derivation copy, then appends a tombstone that
addresses each removed artifact by contribution id and digest while dropping
labels, subject key, and paths. History is append-only, so the original sample
record survives the withdrawal it documents.

Illegal moves are refused instead of silently applied: revoked -> active,
revoking an unknown or already-revoked contribution, an unknown state, a
duplicate contribution id, and a second label set for the same derived image.
The duplicate guard runs before any byte is written, so a refused ingest cannot
strand an image in the active dataset.

Tests written first and watched fail (tests/corpus-manifest.test.py): provenance,
tombstone + byte removal, active-record filtering, and the transition guards.
The transition helper itself passed on first write and is reported as a
hold-out check rather than a RED cycle. Wired into the quality workflow and
check:syntax; contract documented in docs/CORPUS-MANIFEST.md.

No browser surface changed, so record_release_demo.mjs is untouched and claims
nothing new: the nearest honest visible effect is the withdrawn-receipt
tombstone already recorded for #24.
2026-10-24 10:58:30 +00:00
timmy baabeb0f4f fix(privacy): give training-contribution rows a readable layout (#24)
Quality gates / quality (push) Successful in 3m52s
The opt-in row and its tombstone rendered as one run-on line ('Type 4 ·
brownReceipt contrib-…', 'Withdrawn Oct 21, 12:09 PMraw image, …') because
nothing styled .contribution-row: the label and the receipt/artifact line
shared a line and the Withdraw control butted against the text.

Split label and detail into their own lines, right-align the row action,
mute withdrawn tombstones, and keep long receipt ids wrapping inside the
card. Verified on a 390px viewport: queued, withdrawn, and offer rows are
separately legible with no horizontal overflow.
2026-10-21 12:15:25 +00:00
timmy 11c63a8d25 Merge pull request #83: feat: separate opt-in training contribution receipts (#24)
Quality gates / quality (push) Successful in 3m47s
2026-10-21 12:01:52 +00:00
timmy ef22ea8970 feat(training): separate opt-in contribution receipts with withdrawable tombstones
Quality gates / quality (pull_request) Successful in 3m44s
A journal save now records no training contribution. A contribution is a second
consent: the privacy page states consent version training-consent-v1 and the
receipt (contribution id, included fields, never-included fields) before the
confirm control is enabled, the receipt is queued on this device only and copies
no photo bytes, note text, or symptom answers, and withdrawal keeps a tombstone
naming every derived artifact by contribution id. Delete-all clears the receipts
with the ledger.

Closes #24
2026-10-21 10:58:50 +00:00
timmy 2b773ba58c test(release): record the ingress refusal on camera and gate its caption (#16)
Quality gates / quality (pull_request) Successful in 2m57s
The demo now picks a committed text fixture that only claims to be a photo, asserts on camera that
nothing was posted to /api/analyze, and keeps manual entry in the same scene. The builder requires
that caption to have painted and records image_ingress_refusal in the manifest; the threat model
names each implemented control, its module, and the tests that enforce it.
2026-10-20 08:21:24 +00:00
timmy 9b72872466 feat(photo): give an unreadable file its own upload-free state instead of a failed analysis (#16)
Selecting a file the browser cannot decode used to render "Timmy couldn't analyze that safely",
which described an upload that never happened. It now states that the file isn't a photo Timmy can
read and that nothing was uploaded, with manual entry and the picker both one tap away. The state is
asserted in a new browser suite that also asks the running server to refuse the same bytes, and the
existing mobile capture suite asserts the new copy across both viewports.
2026-10-20 08:21:24 +00:00
timmy ba9afe0a1d feat(ingress): decide photo validity from bytes, budget, and header before any provider work (#16)
The declared data-URL MIME was a claim the boundary believed. Every image decision now comes from
the bytes: the container is sniffed, walked to its own end marker, refused when metadata segments
remain, and refused above a 4096px edge or 16MP. The request path adds a per-caller sliding-window
budget (429 + Retry-After) and a body deadline (408), so a flood, a stalled body, or a decode bomb
fails before a provider call.

Log hygiene is part of the contract: refusal copy is fixed prose, the bucket key is a salted hash
kept in memory only, and the acceptance test asserts no image bytes reach the process log.
2026-10-20 08:21:20 +00:00
timmy ebee52fd8a feat: separate saved-photo lifecycle from the confirmed log (#36)
Quality gates / quality (pull_request) Successful in 3m9s
A confirmed log and the image attached to it are separate things the
person owns. The journal now reports a saved photo as a label instead of
a thumbnail, reveals the image only on explicit request, removes the
photo without touching any confirmed field, and deletes a log through a
separate two-step decision that takes its photo with it. Delete-all is
regression tested to remove the ledger and every stored photo together.

- src/photo-lifecycle.js: presence decided by stored image bytes;
  withoutPhoto/withoutEntry return new lists and never mutate the ledger
- app.js: journal rows expose Open-entry controls; entry sheet states
  presence, offers explicit reveal, remove-photo-keeps-log, and a
  two-step log delete; privacy copy names photo removal and delete-all
- tests: 6 unit cases + browser acceptance (presence, no default
  thumbnail, explicit reveal, remove keeps log, two-step delete,
  delete-all leaves no image bytes, Escape dismiss, 44px targets)
- record_release_demo.mjs: on-camera photo lifecycle scenes driven by a
  synthetic 1x1 PNG, with delete-all actually performed and asserted;
  build_release gates the new captions
- CI/release/syntax gates include the new suite

Closes #36
2026-10-19 08:24:44 +00:00
timmy b838beb548 feat: ground Timmy chat answers in cited ledger records (#37)
Quality gates / quality (pull_request) Failing after 2m50s
Chat now has to show its work. Every answer that states a count, a date, or a
visible form cites the confirmed records behind it, and following a citation
opens exactly that log. Questions the journal cannot answer (food safety,
medicine, what a symptom means) get boundary copy with no borrowed evidence,
and a sparse window refuses to describe a trend instead of naming one.

The Hermes path is held to the same rule: the server gives each ledger record a
positional ref, the prompt requires inline [entry:L<n>] citations, and the
browser resolves them against the ledger it sent. A ref that matches no record
is stripped and the answer is labelled unverified rather than rendered as a
source, so an invented citation cannot look like evidence.

The release recording now exercises the grounded local answer, its citation
sheet, the refusal, and a Hermes reply whose refs resolve, each asserted in the
browser with fictional logs.
2026-10-18 08:17:40 +00:00
timmy b4be7049ea fix: escalate the symptom phrasing people actually use, and never clear a reported symptom (#21)
Quality gates / quality (pull_request) Successful in 2m38s
Deterministic escalation only fired on a narrow set of literal phrases, so real reports
like "there is blood when I wipe" or "I have tarry stools" reached the chat provider
without the fixed medical guidance. Expanding the suite found 18 of 18 checked clinical
phrasings missing.

- domain: cover bleeding reported by wiping/toilet paper/anatomy/mixed-in phrasing,
  black-tarry-dark-red stool, reported temperature and high temperature, abdominal-scoped
  severe pain in every natural word order, and past-tense/slang inability to pass gas.
  Near-miss wording that must stay quiet (paper cut, blood pressure, black beans, dark red
  wine, a normal temperature, knee or back pain, passed gas, no gas in the car) is pinned.
- domain: a symptom reported in any truthy shape (1, "yes", "true") now counts as reported.
  Reading only the literal boolean `true` silently cleared a symptom, which is the one
  direction this boundary may never fail in.
- hermes-agent-service: the gateway ledger sanitizer used the same strict check, so a
  browser-supplied ledger entry could have its symptom cleared before the escalation check.
- tests/escalation-regression.test.js: per-flag positive and negative language coverage,
  model output can neither set nor clear a symptom/urgency/discomfort/note, and the
  escalation copy is byte-identical for every flag and under a missing, throwing, or
  malicious provider. Registered in the default unit gate.
- scripts/record_release_demo.mjs: the recording now types the new bleeding and tarry
  phrasing, and seeds a fictional ledger whose note says "stool was tarry yesterday", then
  asserts in the browser that exactly one new Timmy bubble carries the app's own copy and
  that the turn never reaches Hermes. Three new captions are required release gates.

Boundary unchanged: no new inference, no diagnosis, no image claim. Escalation only ever
gets broader on user-reported symptoms.
2026-10-15 08:15:00 +00:00
timmy d670360560 feat: explainable longitudinal pattern summaries with cited records (#34)
Quality gates / quality (pull_request) Successful in 2m59s
The home insight card now reports the confirmed-log count and form
groupings for a window, every line names the exact confirmed records it
was built from, and following a citation opens those records. A window
below the minimum states that it is too sparse instead of describing a
trend, and a window with both firm and loose logs says it is mixed rather
than picking a winner. No line explains a cause, rules on food, or offers
a health verdict.

- src/summary.js: window filtering, insufficient-data guard, per-form
  lines, mixed-pattern line, boundary copy, record citations
- app.js: explainable home card plus a pattern-source sheet listing the
  cited records
- tests/summary.test.js, tests/pattern-summary.acceptance.mjs: unit and
  real-browser coverage, gated in CI and the release builder
- scripts/record_release_demo.mjs: fictional multi-day sample logs,
  citation follow-through, and the sparse-window refusal on camera
2026-10-14 08:13:02 +00:00
timmy 5c759078c4 fix: fail closed when a filled-in visual field is out of range (#73)
Quality gates / quality (pull_request) Failing after 14s
The blank-first ordering let one blank field short-circuit the whole reply, so a
response carrying an impossible value came back as `unusable_visual_fields`:
the user was told the photo was unreadable when the model had in fact returned
something Timmy must never apply.

Blank fields still abstain and still prefill nothing. A filled-in field is a
claim, and a claim outside the contract now fails closed even when the other
field came back blank.

The corpus earns its label too. `out-of-range-fields` drops from confidence 1.4
to 0.9 so the case reaches the field-range check instead of dying at the
confidence gate, and a new `blank-plus-out-of-range` case covers the class the
old ordering let through. The release demo replays that case on camera and the
fail-closed 503 budget moves from one to two, so the scene cannot be dropped
silently.

Closes #73
2026-10-12 08:07:22 +00:00
timmy d9f82299d0 test: prove schema conformance and refusal with a committed vision corpus (#18)
Quality gates / quality (pull_request) Successful in 2m26s
The worker contract was only proven by one happy-path fixture and a handful of
inline assertions, so nothing continuously checked that a reply Timmy cannot use
is refused instead of guessed at.

Corpus: a CC0 synthetic corpus (stool, non-stool, blur, glare, empty bowl,
malformed response) generated deterministically by scripts/build_vision_corpus.py.
Every fixture records its sha256, provenance, licence, case class, the provider
reply it replays, and the outcome that reply must produce. loadVisionCorpus
refuses a fixture whose bytes no longer match the manifest, so the corpus cannot
be edited into a different test. No real medical imagery, camera, or third-party
content is involved.

Contract fixes the corpus exposed:
- A schema-valid reply with a null form and colour is now an abstention
  (abstainedFor: unusable_visual_fields) instead of a hard provider failure. The
  declared response schema permits those nulls, so treating the reply as an error
  was wrong; it now abstains calmly and still prefill nothing.
- Fail-closed replies carry a machine-readable code (invalid_json, invalid_fields)
  instead of prose matching, and visionFailureResponse() maps them to plain,
  user-safe copy with no provider text, image bytes, or credentials echoed. A
  running server now returns that copy for unparsable and out-of-range replies.

Evidence: unit corpus contract and hash integrity, corpus-wide conformance
through the real analysis path, the same corpus through a running server.mjs with
a stub worker (200 abstain vs 503 fail-closed, no leaked bytes), and a real
browser acceptance run for the refusal UI. The release demo replays three corpus
cases through the real code path so the video shows the status the server would
actually send, with the on-camera caption gated by the release builder.
2026-10-11 08:14:36 +00:00
timmy 29ea045671 docs: add end-to-end photo data-flow threat model (#20)
Quality gates / quality (pull_request) Successful in 2m4s
- docs/PHOTO-DATAFLOW-THREAT-MODEL.md: browser, API ingress, model worker,
  transient buffers, training opt-in store, logs, exports, and deletion,
  with trust boundaries and a numbered threat register
- every named abuse case (replay, oversized input, metadata leakage,
  poisoning, unauthorized training, operator access) carries a mitigation,
  an owner, a tracked follow-up ticket, and the artifact that will prove it
- tests/threat-model.test.js enforces diagram coverage, owner/follow-up
  presence against ROADMAP ticket numbers, and the non-diagnostic boundary
  with synthetic-only fixtures
- link the model from the accepted product decision record
2026-10-10 08:03:31 +00:00
timmy 4cc5f4f7b1 feat: user-reported hard-stool follow-up with deterministic red-flag handoff (#67)
Quality gates / quality (pull_request) Successful in 2m53s
Field finding #67: a hard/lumpy (Bristol 1-2) entry now asks a short follow-up
before saving — difficult to pass, soreness, visible red blood, truly
black/sticky/tarry versus dark brown, and dehydration. Every answer is
user-reported and starts unanswered; nothing is inferred from the photo, and
answering "dark brown" never silently escalates as black stool.

A confirmed red-flag answer writes the same symptom flag manual logging already
uses, so the existing deterministic escalation is the only escalation path.
Mild reports get concise non-diagnostic self-care that states the photo cannot
rule out bleeding, and it is withdrawn whenever a red flag is confirmed.

- src/followup.js: pure follow-up contract (9 unit tests, tests/followup.test.js)
- app.js + styles.css: follow-up block on the safety step for Type 1-2 entries only
- tests/followup.acceptance.mjs: browser acceptance incl. that the AI suggestion
  surface still contains no symptom wording
- CI + release builder gates: npm run test:followup, syntax checks, manifest gate
- scripts/record_release_demo.mjs: records the real follow-up, mild guidance, and
  the black/tarry alert handoff with synthetic data
2026-10-09 08:10:43 +00:00
timmy dce668e72b feat: guarantee manual fallback and in-session draft recovery (#12)
Quality gates / quality (pull_request) Successful in 1m54s
Logging must never depend on AI availability, and an unsaved entry must not
disappear when the user navigates away mid-log.

- add src/vision-readiness.js so the photo flow consumes one readiness contract
- when the worker is offline/disabled/unknown, offer no AI action and no
  analysis-consent prompt, and keep manual Bristol entry one action away
- add src/draft.js and make the draft own the photo so a chosen photo and
  entered context survive a recoverable close or navigation, with a visible
  kept-draft summary and an explicit start over
- drafts stay in session memory only: a photo awaiting consent is never
  persisted, and reload starts clean
- service worker now refuses to cache image bodies in addition to analysis
  requests and no-store responses
- tests: 2 new unit files, 3 service-worker privacy assertions (1 hold-out),
  1 browser acceptance file, release-demo and CI/package assertions
- release demo now visibly replays the vision-offline pass and the retained
  draft with synthetic data and an in-script assertion
2026-10-07 08:12:43 +00:00
timmy 5dac04b1e6 Merge pull request 'Harden mobile camera and gallery recovery' (#66) from timmy/11-mobile-capture-recovery into main
Quality gates / quality (push) Successful in 1m56s
Merge nightly mobile capture recovery for issue #11 after exact-head review and green gates
2026-08-26 12:19:21 +00:00
timmy 87c7680aad ci: retry transient runner port collision
Quality gates / quality (pull_request) Successful in 1m26s
2026-08-26 08:10:55 +00:00
timmy 463dff7f22 feat: harden mobile capture recovery (#11)
Quality gates / quality (pull_request) Failing after 58s
2026-08-26 08:07:30 +00:00
timmy 47294a98aa Merge pull request 'Make staging runtime configuration compatible with strict CSP' (#59) from timmy/58-strict-csp-runtime-config into main
Quality gates / quality (push) Successful in 1m56s
2026-08-21 15:04:43 +00:00
timmy 877b8a01f3 fix: make staging compatible with strict CSP
Quality gates / quality (pull_request) Successful in 1m37s
2026-08-21 15:01:05 +00:00
timmy 067afaed4c Merge pull request 'Hardened atomic private-staging deployment tooling' (#56) from timmy/55-staging-deployment-tooling into main
Quality gates / quality (push) Successful in 1m41s
2026-08-21 14:54:11 +00:00
timmy f8f01c4b2e feat: add hardened atomic staging deployment
Quality gates / quality (pull_request) Successful in 1m28s
2026-08-21 14:49:28 +00:00
timmy fc763fb758 Merge pull request 'Private subpage staging foundation' (#54) from timmy/53-private-subpage-staging into main
Quality gates / quality (push) Successful in 2m7s
2026-08-21 14:16:15 +00:00
timmy 7e11154302 feat: add private subpage staging slice
Quality gates / quality (pull_request) Successful in 1m43s
2026-08-21 14:12:17 +00:00
timmy 11d1b364ab fix: gate bootstrap syntax in release build
Quality gates / quality (push) Successful in 1m45s
2026-08-21 12:56:48 +00:00
timmy 982fab273c feat: package pinned self-host vision bootstrap
Quality gates / quality (pull_request) Successful in 1m30s
2026-08-21 08:15:32 +00:00
timmy ca31e6d38b Merge pull request 'Hotfix: recognize past-tense vomiting in deterministic chat safety' (#51) from timmy/48-vomiting-phrase-hotfix into main
Quality gates / quality (push) Successful in 1m26s
2026-08-20 17:17:33 +00:00
timmy 0f3104ebde fix: recognize past-tense vomiting in chat safety
Quality gates / quality (pull_request) Successful in 1m21s
2026-08-20 17:14:20 +00:00
timmy cc1fc6ee36 Merge pull request 'Hotfix: enforce deterministic urgent-symptom override at the server boundary' (#50) from timmy/48-server-safety-override into main
Quality gates / quality (push) Successful in 1m24s
2026-08-20 16:48:02 +00:00
timmy 05b4bafd96 fix: enforce urgent chat override server-side
Quality gates / quality (pull_request) Successful in 1m25s
2026-08-20 16:41:35 +00:00
timmy 09f9528434 Merge pull request 'Sleek Timmy mobile shell and secure Hermes Agent chat' (#49) from timmy/47-sleek-hermes-agent into main
Quality gates / quality (push) Successful in 1m32s
2026-08-20 16:25:47 +00:00
timmy a50afa6722 feat: simplify Timmy and add secure Hermes chat
Quality gates / quality (pull_request) Successful in 1m24s
2026-08-20 16:21:00 +00:00
timmy 3c51308a9e docs: fix product boundary and release authority
Quality gates / quality (pull_request) Successful in 1m14s
2026-08-20 08:32:16 +00:00
timmy 8ab1e85bd1 Merge pull request #45: enforce reproducible quality gates
Quality gates / quality (push) Successful in 1m18s
2026-08-19 12:06:38 +00:00
timmy 47eec47a31 ci: install pinned image test dependency
Quality gates / quality (pull_request) Successful in 1m12s
2026-08-19 08:14:43 +00:00
timmy 29ba8594b8 ci: enforce reproducible quality gates
Quality gates / quality (pull_request) Failing after 1m56s
2026-08-19 08:09:38 +00:00
timmy 535834c060 feat: require verified feature demo video in every release 2026-08-19 03:36:04 +00:00
timmy ce84ea577c test: isolate photo acceptance from service workers 2026-08-19 03:02:33 +00:00
timmy 3899eac34f fix: start isolated server for release acceptance gates 2026-08-19 03:01:28 +00:00
timmy 074f52fddc build: add gated reproducible review releases 2026-08-19 03:00:54 +00:00
timmy 0ecf1d1205 docs: add Gitea roadmap and epic map 2026-08-19 02:00:40 +00:00
timmy 58906f11d0 feat: ship photo-first Timmy prototype and sovereign vision spike 2026-08-19 01:54:29 +00:00