oficina / calendar Public

Review: rastrillo off the frozen path (iOS zoom-on-tap) open

On branch chore/rastrillo-path

Review request: move Calendar's rastrillo imports off the frozen path. Paul approved this on 2026-09-30. Polish audit r3 SUMMARY, item 1. Sheets did the same in #44 (oficina/sheets, main a5c17ab); this follows its recipe, with Calendar's differences called out.

Why. Calendar imported github.com/carlosframework/rastrillo, which stops at v0.23.0, so it never received tokens.css's iOS zoom-on-tap fix. On iOS, every text field in Calendar zoomed the page on its first tap.

Pin. amadan.net/rastrillo/rastrillo at 29e4491 (v0.26.0+8). home/connect already required it, so the build has one amadan rastrillo, not two. Rastrillo main (v0.27's CSP, the time-layout change, migrate.Fn) is left for its own branch. estilo is pinned at main 530cb93, which includes the bar title's touch fix (4fc9be3).

Commits

  • d7ac53b: TestNoZoomOnTap (browser tag, run by make browser). Red on purpose: against origin/main every text control was 13.3 to 14px under a coarse pointer. That covered the door, quick add (opened), the event form and its date comboboxes, reminders, settings, import, the booking-page form and the stranger's booking form. make vet now also vets -tags browser.
  • edfa3ba: the move. - Every import is rewritten. The CLI is a go.mod tool, and make migration-check runs go tool rastrillo. make release stamps amadan.net/rastrillo/rastrillo.BuildVersion. - One old-path import stays: native.go's presessions, because rastrillo/native's server still takes the old *sessions.Sessions. TestNoPreMoveRastrillo forbids every other one. TestNeverSessionsUserID now watches both paths. - go tool rastrillo doctor --fix re-copied tokens.css and datetime.js. select.js was already current. - Calendar's differences from Sheets. Calendar does use select.js and datetime.js (the event form, settings' zone), so both stay. static/calendar.js is Calendar's own script (it scrolls to the working day), not rastrillo's. Rastrillo v0.26 vendors a month-grid overlay under the same name, so calendar.js is recorded in vendoredIsMine and doctor leaves it alone. Without the overlay, the date button opens the browser's own picker, as it did on v0.23. Adopting the overlay would mean renaming ours first; that is not in this branch. - rastrillo.js was already estilo's split shim, written by scripts/writeestilo and pinned by TestBusyFilesAreEstilos. Doctor leaves it alone (vendoredIsMine), and the served shim's hash is unchanged before and after. The old combined shim never loads beside busy.js. - Credits are regenerated.
  • 3de83ef: nativeexchangetest.go covers the bridge in all three ways the app reads a session: local with no Home, local with Home configured, and a Home sign-in. - Exchange: 202 before approval, 404 for a wrong verifier, a new token under the app's cookie name, the same person, and no second exchange. - Expiry and revocation hold on both sides. - Red by mutation: an http origin fails the Home-configured shapes, and sessions minted already expired fail all three. Putting viewer.go back on the old path fails the guard. Output: polish-audit/r3/scripts/migrate-calendar-native-red.txt.
  • c974f55: the quick-add box ([cal-quick-text], font: inherit under a selector heavier than tokens.css's bare textarea) stayed 14px. It now gets max(1rem, 1em) under (pointer: coarse). The red output from after the move is in migrate-calendar-zoom-red.txt.
  • 570e90c + f46ede6: a regression the upgrade caused, fixed. v0.26's form-foot draws its buttons at lg. On the stranger's booking form, "Hold this time" and "Pick another time" no longer fit side by side, and the row squeezed them: 2-line labels at 390, and up to 3 lines at 320 (2 before). TestFormFootOnANarrowScreen is red first (migrate-calendar-formfoot-red.txt). The row now wraps ([cal-book-details] > div:has(> button[type="submit"]), because calendar.css may name no rst- selector), and the second button takes its own row.

What the upgrade changes for Calendar

  • Touch: text controls are 16px under (pointer: coarse), and interactive elements get touch-action: manipulation.
  • Buttons: form-foot and confirm-form buttons are lg, about 10px taller. This affects the event, settings, import and booking-page forms and the booker's form.
  • datetime.js (+480 lines): natural-language completion ("5j" offers 5 January/June/July), a clock list on time-only fields, and aria-haspopup="listbox" on the date button. With no calendar overlay, the button falls back to the native picker, as before.
  • Partials: native <meter>/<progress>, rst-input="primary" and the stat band are new. Calendar uses none of them.
  • Server side: mail honours its context (a 30s fallback when it has no deadline) and sends a Date header, and auth.Config.SubjectFor is new (unset here). The migrations agree.

Evidence

  • make ci is green. The known scheduler flake (TestTheTickExpiresHoldsAndWakesForThem) failed once and passed five reruns.
  • make browser is green: the Playwright gates plus OnTap and OnANarrowScreen.
  • Old database: the old build seeded a DB (5 sealed events, settings, a published booking page) with a real DEK and instance key. On that DB the new release binary migrated nothing (9 skipped). The old session cookie still answered 200, sealed titles and the booking page decrypted, and /api/version = v0.1.0-32-gf46ede6. make release (linux/arm64) stamps the same.
  • Screenshots are in polish-audit/r3/shots/migrate-calendar-{before,after}[-touch]-*.png, at 1440, 390 and 320. They cover the door (sent, error, signed-out), day, week, month and agenda, quick add opened, the event form, page and edit, a date combobox open, settings, calendars, share, import, booking pages and the new-page form, the public booking page, day and details, credits, 404, the account menu, and the busy button. - With a mouse, the only differences are the lg buttons, the datetime aria attribute and the booker's wrapped row. - On touch, small text controls went from 78 to 0, and no page overflows.
  • Shared chrome check (r2-calendar-check.config.mjs): 786 ok, 0 FAIL. The config's event-form selector became rst-btn~="primary", because the button is now primary lg.

Not in this branch: moving rastrillo/native's server, rastrillo main, rastrillo's calendar overlay. Two pre-existing issues were seen and left alone: the quick-add panel runs about 15px past the right edge at 390, and TestBrowserWalk (not in any gate) fails at its first sign-in.

CI passed at 559a5bb (vet with -tags browser, fmt, test, migration-check via go tool rastrillo). Not merged; waiting for review.

The first two runs failed vet. The runner's cached rastrillo 29e4491 was missing its embedded files (locales/*.toml, migrations/*.sql, ui/calendar.js), and the same commit passed locally. The runner is /tmp/oficina-extra-ci-runner on this Mac. go mod verify there reported a dozen modules as "dir has been modified", which looks like a /tmp cleanup deleting files. main never noticed because it only compiled rastrillo's scope, which embeds nothing.

559a5bb adds make deps ahead of vet. It runs go mod download and go mod verify, and re-extracts any module that verify names as modified from its checksummed zip. I reproduced the failure with a scratch GOMODCACHE and two deleted files: bare go vet fails exactly as CI did, and make vet repairs the cache and passes. (61e25be is an empty commit that re-ran CI before the cause was clear.)

Reviewer: Astra (GPT-6-Astra via Codex), posting as the configured agent account.

VERDICT: APPROVE-WITH-NITS

Whole-branch review of chore/rastrillo-path at 559a5bb15abe0789a9ce186ce34f06369d9fa19a, against freshly fetched origin/main baa9e70 (also its merge base). Read REVIEW-BRIEF.md, AGENTS.md, the polish context and this discussion; used Sheets #44 and Fichas #6 as the migration baseline.

  1. Low — internal/calendar/static/datetime.js:1373–1374: native-picker fallback advertises an authored listbox that it does not open. Calendar deliberately has no window.rastrilloCalendar. Its date/datetime button therefore calls native.showPicker(), but now carries aria-haspopup="listbox" and aria-expanded="false"; the native fallback never updates the latter. These attributes describe the custom clock list, not the browser-controlled date picker, and give assistive technology misleading state. Fix upstream by adding the popup/state attributes only for the custom calendar and time list; leave the native fallback without unobservable expanded state. Add a browser assertion for the no-overlay case, then refresh the pinned asset. This is a nonblocking accessibility nit, not a date-saving failure.

Verification:

  • GOTOOLCHAIN=go1.26.5 CGO_ENABLED=0 make ci and make browser passed. RASTRILLO_CHROME named Playwright Chromium 1148. The scheduler test passed on this run. Remote CI is passed at 559a5bb.
  • The unmodified shared r2 chrome checker with the r2-calendar config passed 786 checks, zero failures, against the branch release process.
  • Main's clean host release seeded a real database through magic-link sign-in and an event form, with a random nonzero DEK and instance key. The branch host release booted over that same database/key: migrate applied=[] skipped=9; the complete schema and migration ledger were unchanged; the old cookie returned 200; the old sealed title and notes decrypted. /api/version reported v0.1.0-34-g559a5bb. Browser date edits subsequently saved and read back correctly.
  • After module-path normalization, the old/new sessions, CSRF, database and migration sources are unchanged. The auth/mail changes match the baseline reviews: optional SubjectFor remains unset, SMTP honors context with a 30-second fallback only absent a deadline, and its credentials handling fails closed. Upstream UI (including datetime fixtures and round trips), sessions, auth, mail and CSRF tests passed.
  • Native exchange, expiry and revocation tests passed for local, local-with-Home and Home sessions. The old-path handle shares the app's table and origin; token/cookie/expiry formats agree. Mutating its mint TTL to one nanosecond made TestNativeExchangeMintsASessionTheAppAccepts fail in all three modes. Removing wrapping made TestFormFootOnANarrowScreen fail at both 320 and 390, with two/three-line labels. Restored tests passed.
  • Copied TestNoZoomOnTap into main with only its import spelling adjusted: it failed across the named screens, at 13.3–14px. The branch passes, including the stronger quick-add selector.
  • Exercised datetime on the real event form: exact entry, 5j completion candidates, alternative date selection, Escape, invalid-text restoration, Tab out of the whole field, native-picker return/repaint (simulated picker return), and clicking Save with an uncommitted valid edit. Wire values and persisted values agreed. The new clock list is not used by these event templates; an isolated browser check verified its 48 slots, keyboard selection and expanded-state transitions.
  • go tool rastrillo doctor --fix left Calendar's own calendar.js, Estilo shim and theme alone; it reported the retained select.js/datetime.js/tokens.css matching the pin. The working tree stayed clean. The app-owned calendar script is unchanged from main.
  • Compared all 76 supplied mouse screenshot pairs: 39 pixel-identical; form differences match the larger buttons and booking-row wrapping. Other differences are localized to dynamic content/sky/spinner. Inspected the narrow booking/form/date views and the supplied geometry dumps. The mouse layout claim needs its documented lg-button/wrapping exceptions; no additional layout regression found.
  • Exercised this exact Makefile's deps recipe in an isolated module cache: a deleted extracted source file was restored; modifying source content inside the cached zip made the final verify fail and make exit nonzero. Re-extraction uses the existing checked archive, changes no pin, and does not suppress the subsequent vet result. Merely appending bytes outside the zip entries does not change Go's module-content hash; the substantive zip mutation did fail.

Review only: no commits, pushes, merges or existing-checkout edits. All four Calendar/Memoria scratch worktrees and fixture servers were removed. Disk checked before builds (39 GiB free; 38 GiB after). Runtime evidence used isolated local fixtures as expressly requested; this is not a deployed-canary sign-off.