CI passed at 6fc76ab: vet (with -tags browser), fmt, build, migrations (go tool rastrillo), go-test and race. Not merged; waiting for review.
Review: rastrillo off the frozen path (iOS zoom-on-tap) open
On branch chore/rastrillo-path
Review request: move Sheets' rastrillo imports off the frozen path. Paul approved this on 2026-09-30. Polish audit r3 SUMMARY, item 1.
Why. Sheets imported github.com/carlosframework/rastrillo, which stops at v0.23.0. It had received no rastrillo ui fix since 2026-08-31, including the iOS zoom-on-tap fix in tokens.css ("Touch: the two zooms nobody asked for"). On iOS, every text field in Sheets zoomed the page on its first tap.
Pin. amadan.net/rastrillo/rastrillo at 29e4491 (v0.26.0+8). home/connect, home/handoff and the assertion tests already required that version, so the build now has one amadan rastrillo, not two. I chose it over rastrillo main because main also brings v0.27's CSP (inline styles blocked, and Sheets still has some), the UTC/SQLite time-layout change to stored rows, and the migrate.Fn API change. Each of those deserves its own branch.
Commits
- be56866: TestNoZoomOnTap (browser tag,
make test-browser). This commit is red on purpose. It ran against origin/main's code and every text control was 12.5 to 14px under a coarse pointer: the door, the title, the formula bar, the size fields, share, comments, the table view and the cell editor. - c58a414: the move. - Every import is rewritten. - The CLI is pinned as a go.mod
tool, andmake test-migrationsrunsgo tool rastrillo. -make releasenow stampsamadan.net/rastrillo/rastrillo.BuildVersion. The old symbol would have left /api/version reporting "dev". Checked: a stamped build answers its version. -go tool rastrillo doctor --fixre-copied tokens.css. I deleted the four scripts it also added (rastrillo.js, select.js, datetime.js, calendar.js). At this pin, doctor's rastrillo.js is the combined shim with the old busy rule inside, and Sheets serves estilo's split shim plus busy.js. TestBusyRuleIsLinkedAndServed now fails if any of them is left on disk. - One old-path import stays: native.go's handle for rastrillo/native's server, which still takes the old*sessions.Sessions. Both packages share the frozen sessions table. The new TestNoPreMoveRastrillo forbids every other old-path import. - 48c1052: two fields the touch rule doesn't reach. - The cell editor and estilo's title field set
font: inheritwith a selector heavier than tokens.css's bareinput, so they stayed at 14px. They now get the samemax(1rem, 1em)under a coarse pointer. - In the Format menu, the Set buttons hung 11 to 21px past the panel's edge on touch. This was already true before the upgrade, and 16px fields made it worse. The label column narrows to 5.5rem on touch. - 6fc76ab: tools/drive. chrome, clipboard, menus and tableformat waited for networkidle after the sign-in submit. Since the busy rule (a 650ms hold), that wait resolves before the POST is sent, so the scripts read the log before the magic link was in it.
What the upgrade changes (tokens.css, +303/-6)
- Touch: text controls at 16px under
(pointer: coarse), andtouch-action: manipulationon interactive elements. - Buttons:
rst-btnsizes sm, lg and block. Sheets uses none of them, so no change. - Type:
--rst-fs-lgtoken. The 17px it replaces renders the same. - Partials: -
rst-input="primary"is new. - The stat band is new. - meter is now a native<meter>, and--rst-meter-fillis gone. - job-status draws a<progress>only when it is given Percent. Sheets doesn't pass it, so no change. - Date picker and calendar styles: unused by Sheets.
- JS: no change in behaviour. The shim and busy.js are still estilo's, and Sheets serves no other rastrillo script.
- Server side: - mail honours its context (bounded at 30s) and sends a Date header. -
auth.Config.SubjectForis new, and Sheets doesn't set it. - Every other change is the path itself. The migrations agree, and a v0.23 database boots and migrates.
Evidence
- Screenshots are in polish-audit/r3/shots/migrate-sheets-{before,after}-*.png. They cover signin (with its sent and error states), the list, the grid, share, table, history, comments, import, the public link and 404, at 1440, 390 and 320, plus every menubar menu, the toolbar menu, the account and overflow menus, and Format at 390 and 320.
- With a mouse, before and after match. The only pixel differences are the door's sky and timestamp text.
- On a touch screen (
migrate-sheets-*-touch-*), controls are 16px and about 3px taller. No page overflows, and the Format menu fix is shown. make test: green, including -race.make test-browser: green.- Shared chrome check: 989 ok, 0 FAIL.
- tools/drive results were identical on main and this branch: - pass: drive, drop, tableformat, menus, editing, contextmenu, menu-actions, comments and autofit. editing is flaky on both builds. - pre-existing failures: chrome.mjs, 4 paint and bold checks; clipboard.mjs, 5 checks. - not run: modal.mjs's WebKit half, because the script ignores WEBKITEXECUTABLEPATH.
Not in this branch. Moving rastrillo/native's server to the new path (Docs will hit the same import). Rastrillo main (v0.27 and the rest).
Reviewer: Astra (GPT-6-Astra via Codex).
VERDICT: APPROVE-WITH-NITS
Reviewed the whole branch at 6fc76ab02da1c45d827543250d4f219317b5e746 against freshly fetched origin/main / merge base 2335fe1d8f4fde09c0bcfa9d19c209c322d042b7, following AGENTS.md and REVIEW-BRIEF.md. No blocking correctness, security, migration or data-loss finding.
- P3 —
internal/sheets/native.go:27: put the two-package native-session bridge in the ordinary regression gate. The bridge works at these pins, including native mint → canonical reader and expiry rejection, which I exercised. However, the existing approval/exchange helper is reached only byTestNativeReview, which skips unless a native binary/simulator is configured (native_review_test.go:27). Ordinary native API tests do not cover that handshake. A future canonical sessions upgrade could therefore break interoperability whilemake teststays green. Suggested fix: add a plain Go test using the existing approval/exchange helper, then verify canonical authentication, expiration and revocation of the resulting token. No native client is needed.
- P3 —
internal/sheets/static/sheets.css:1290: add an overflow assertion for the Format fix. Removing the new coarse-pointer label-width block in the scratch tree still leavesmake test-browsergreen. The current test measures text size, not menu containment. The fix itself works: I measured both Set buttons inside the panel at 320px and 390px with touch enabled. Suggested fix: open Format at those widths with a coarse pointer and assert both buttons' bounds and hit targets remain within the panel; removing this block should fail it.
Verification and behavior review:
make testpassed, including browser-tag vet, the pinned CLI migration check, all Go tests and race tests.make test-browserpassed using Playwright Chromium 1148 throughRASTRILLO_CHROME. Remote CI also reports passed at this exact SHA.- The unchanged
TestNoZoomOnTapwas copied into a separate main worktree, with only its build dependencies supplied. It failed on main's sign-in, title, size controls, formula bar, sharing, comments, table controls and real cell editor (12.5–14px). This is a meaningful computed-style regression test with an asserted coarse pointer; it is not a direct Mobile Safari viewport-zoom measurement. - Shared chrome command with the supplied
r2-sheets-check.config.mjs: 989 ok, 0 FAIL. make releasesucceeded for its default Linux/arm64 target. A host-runnable build using the same target withRELEASE_GOOS=darwin RELEASE_GOARCH=arm64returnedv0.1.0-36-g6fc76abfrom/api/version.- Built clean main using its original go.mod (v0.23 application imports), booted it, and seeded a sheet, encrypted cells, a session, grant and public link. Stopped it and booted the release build over the same DB/key. Migration succeeded; schema and all 16 ledger entries were unchanged; the old cookie authenticated and old cell content decrypted. This upgrade needs no new schema migration.
- Compared complete module trees, normalizing only the old module path. Sessions, CSRF, db, migrate, jobs and blobs have no other production-code differences. Auth's substantive change is the optional
SubjectForhook after address admission and before session creation/second factor; nil retains the verified-address subject, as Sheets requires. Middleware, authorization and existing migration SQL do not change. - Mail is more than a Date-header change: SMTP now owns a context-aware connection, cancels blocking work when the context ends, and adds a 30-second fallback only when the caller has no deadline (not an unconditional 30-second ceiling). STARTTLS remains opportunistic, and PlainAuth retains its cleartext restrictions. One edge case becomes stricter: when credentials are configured but a server falls back to HELO without AUTH, the new sender refuses, whereas the former net/smtp.SendMail path could skip AUTH. That is fail-closed, not a security weakening. List-mail/Deliverer APIs are additive and unused by Sheets. Upstream mail, auth, sessions and CSRF tests all passed.
- Both sessions packages use
__Host-rastrillo_sessionon HTTPS,rastrillo_sessionon HTTP, Path=/, HttpOnly, SameSite=Lax and HTTPS Secure. Tokens are 32 random bytes in unpadded base64url (43 characters); the DB stores their hex SHA-256. Both default to 30 days and use identical UTC RFC3339 storage, expiry checks and row revocation. Native exchange performs fresh cookie/DB lookup and delegates admission to the app; it does not rely on the other package's distinct context key. I exercised approval using the pre-upgrade browser cookie, old-package Mint, new-package authentication, and expiry rejection. - Doctor re-added all four scripts in the scratch tree.
TestBusyRuleIsLinkedAndServedthen failed for each one, as intended. Independently,sharedStaticFilesoverwrites the copied shim withbusy.ShimJS(), and the linked/fingerprinted response is checked against Estilo's bytes. Running doctor alone cannot substitute the combined shim at that URL. No live template links the unused picker scripts. - Reviewed the CSS/module partial changes and the supplied screenshots, including pixel comparison of 52 mouse before/after pairs. Grid, table, Format and most menu screenshots are identical; differences are consistent with dynamic sky/spinner/timestamp content and timestamp-driven table sizing. Touch screenshots and live measurements show the larger controls and contained Set buttons. Narrow mouse windows still have the pre-existing Format overflow; the coarse-only fix does not change mouse CSS. Other upstream changes include lg form/confirmation buttons, optional primary fields/detail-list time markup, native meters/progress, date/calendar assets, and localized money errors. Sheets does not invoke the affected form/meter/date partials or money parser; its job-status map supplies no Percent. New assertion/PoW functionality does not add a Sheets route. This pin does not bring the later CSP/time-storage migration.
- The drive-script waits now wait for the actual sign-in result URL before reading the log, covering the busy delay. I did not rerun the unrelated full drive suite.
For the other apps: the fetched #44 currently contains the report and CI reply, but no explicit recipe at its end. The general import/tool/stamp/doctor/test sequence is sound; copying Sheets' exact cleanup or pin everywhere is not. In the inspected checkouts:
- Docs: already requires
d9c28d708556, andteam_session_reader.gouses itsFromContextAPI. Do not downgrade it to29e4491. Audit existing canonical/legacy adapters, preserve the native-server bridge where needed, and change the stamp inscripts/build-release.sh. - Calendar: its layout actually links select.js/datetime.js and its event form uses the enhanced date controls. Preserve/update the used assets and include calendar.js before datetime.js if adopting the new calendar overlay. Do not copy Sheets' blanket picker deletion.
- Memoria: also uses the old-path native server, so it needs the same deliberate session bridge. It already links a separate busy.js: ensure the served shim is the split one when doctor updates assets.
- Home: has its own sign-in/session wiring rather than Sheets' auth setup. Preserve its subject and freshness semantics while migrating all readers/writers consistently. Verify existing sessions and its actual release stamp.
For each app, resolve the effective module version, pin the CLI to that version, audit served assets and specific CSS overrides, and repeat the old-DB, release-stamp, touch and main-negative checks. Keep the later CSP/time-format upgrade separate.
Runtime checks here were isolated local fixtures as requested; no deployed canary was supplied or deployed, so this is not a canary deployment sign-off. No existing checkout was moved or edited; no commit, push or merge was made. Scratch worktrees and fixture processes were removed after testing. Disk was measured with df -h /System/Volumes/Data (49 GiB available before, approximately 48 GiB after).
Posting note: the initial agent post was refused because this Codex environment did not supply Amadan session metadata. I followed amadan agent skill, supplying AMADAN_AGENT_SESSION from this session's CODEX_THREAD_ID and AMADAN_AGENT_HARNESS=codex; the account remains the configured agent account, with no -as-human use.
Both of Astra's nits are fixed. Head is 477ea96, and CI passed on all six steps. Not merged.
1. native.go (095b010). nativeexchangetest.go runs the native sign-in through the router, across both sessions packages, in three tests:
- Exchange: - Before approval, the exchange answers 202, and a wrong verifier gets 404. - The minted cookie has the name the app reads, and it is a new token, not the browser's. - The app accepts it as the same person: the browser's sheet appears in its list. - The id can't be exchanged a second time.
- Expiry: - An expired browser session can't approve, and an approval that expires before the exchange gets 401. - A minted session expires like any other.
- Revocation: - If the browser signs out between approval and exchange, the exchange gets 401. - The app's
/signoutends a minted session and leaves the browser's session alone.
Each mutation of native.go fails the tests; the output is in polish-audit/r3/scripts/migrate-sheets-native-red.txt:
- an http origin, so the cookie gets a different name
- sessions that are already expired when minted
- a minted row that resolves to someone else
2. sheets.css (477ea96). TestFormatMenuFitsOnTap opens Format on a touch screen at 320 and 390 wide. It checks that each Set button sits inside its panel and that a tap at the button's centre lands on it.
- Without 48c1052's label rule it fails at both widths: Set spans 225–263 in a panel that ends at 245.
- With the rule, it passes.
- The output is in polish-audit/r3/scripts/migrate-sheets-format-red.txt.
The title field belongs upstream in estilo's chrome.css. I opened that as oficina/estilo#22, branch fix/title-touch-size at bb2db36. Its test failed before the fix, and its CI passed. I proved it in a browser too: with Sheets' own title rule removed, Sheets' TestNoZoomOnTap fails on estilo's current pin and passes with the estilo fix. Once that merges and Sheets bumps estilo, the [estilo-bar-title-input] line in sheets.css can go.
Merging. Astra approved with two nits, both fixed at 477ea96 and each shown red by mutation. CI passed. The estilo title-field fix landed separately (estilo #22).