oficina / memoria Public

Merge native/testflight (Mac + iPhone clients) into main open

On branch merge/native-testflight

Polish round 1, native merge. Paul 2026-09-23: "Merge away."

What. git merge --no-ff origin/native/testflight (canonical 26497c8, 2026-09-14) into a branch cut from fresh origin/main (29247af). Merge commit cb534c3. 7 native commits: 5899232, d72e5bd (the earlier main→native merge production runs), 439e5c7, 9b79152, 15e39ca Sparkle, cc3b29e sandbox/TestFlight packaging, 26497c8 recent accounts.

Liveness check. amadan discuss list oficina/memoria -branch native/testflight shows no discussions. The last commit is 9 days old. Local native/apple-clients (9b79152, worktree memoria-native) is a strict ancestor with no unique commits (git log origin/native/testflight..native/apple-clients is empty) and a clean tree. It was left untouched. native/sparkle-updates (15e39ca) is also fully contained.

Conflicts. None. Outside native/, the merge adds: the /native/* auth routes and /api/native/{session,search,notes} behind requireSession (app.go), native.go and native_test.go, the rastrillo native/server go.mod requirement, its credits exclusion (Makefile + credits_inventory_test.go), the declared-routes list, a localAs test helper and spec §additions. It removes nothing main added. Diff it against each parent, per AGENTS.md §1.11.

Gates run locally (GOTOOLCHAIN=go1.26.5):

  • make ci: vet, fmt-check, test, migration-check all green.
  • NATIVE_REVIEW_BINARY=<swift build bin>/MemoriaReview go test -run TestNativeSearchUsesCurrentPermission ./internal/memoria passed in 10.6s. The real review binary ran against the fixture server and wrote a PNG of the workspace showing both permitted results and the on-device answer. The binary really ran, so this is not a skipped pass.
  • swift build (all products) in native/apple: green. swift test does not apply because the package has no test targets.
  • native/scripts/build-iphone.sh simulator (xcodegen regenerated first): BUILD SUCCEEDED. native/scripts/build-mac.sh: built and signed Oficina Memoria.app.
  • Not run: the simulator XCTest UI suite (NATIVE_REVIEW_UI_TESTS) and device builds.

Known and deliberately not fixed here (see offline-audit/memoria.md): B10 duplicate-on-retry is on its own follow-up branch fix/r1-native-note-idempotency, cut from this one. The README says the Rastrillo Native pin is 33ffe99, but Package.swift pins 2295259. That doc drift is left as-is.

Needs the Fable whole-branch review (against both parents) before merge.

LLM

Brings origin/native/testflight 26497c8 onto main. Clean merge, no conflicts; make ci, the native Go fixture test with the real MemoriaReview binary, and Mac + iOS simulator builds all green.

Whole-branch review — Fable 5.1 (dispatched as claude-fable-5-1), 2026-09-23

Merge cb534c3 reviewed against both parents (AGENTS.md §1.11): origin/main...origin/merge/native-testflight and origin/native/testflight..origin/merge/native-testflight. Scratch worktree; nothing edited.

VERDICT: APPROVE-WITH-NITS

Nothing main had is reverted

  • main gained three commits since native forked at aab25c0: c8eea5e (make release stamped linux-arm64 binary), the release-target merge, and 29247af (AGENTS.md worktree rule). Diffing the merge against the native side shows exactly those two files changing (AGENTS.md +38, Makefile +17/-2) and nothing else — i.e. the merge carried main's later work and only that.
  • Diffing the merge against main shows no deletion of main content anywhere: the only - lines are the credits -exclude list and catalog.Collect map (both extended, not replaced) and homeRouterFixture.local refactored into localAs with local kept. AGENTS.md is byte-identical to main. Conflict-free claim holds; there were no resolutions to judge.

The /api/native/* routes are authenticated and authorised

  • All three (GET /api/native/session, POST /api/native/search, POST /api/native/notes) are registered inside the r.Group that applies a.requireSession, which fails closed (a.auth == nil -> 500), runs rastrillo's RequireSession, then identity + Admit (owners list) and 404s otherwise. The whole router below homeMiddleware is wrapped in csrf.Protect(origin) (home.go:72), so the JSON POSTs get the same-origin check the web forms get.
  • nativeSearch passes the session's own address as the suite.Subject to Store.Search, the same call the web results page makes (handlers.go:628), so §4.1's per-person, per-query custody ruling is asked the same way. It strips <mark> from snippets and adds no new corpus read path. nativeNote goes through Store.Ingest like the web paste path.
  • TestNativeSearchUsesCurrentPermission proves the state: anonymous -> non-200; native-exchanged cookie -> 200 with both docs; custody NOT_VISIBLE/UNKNOWN -> sibling doc withheld; owner removed via SetOwners -> refused; second member searches as themselves. I read the test rather than trusting the discussion; it asserts on the custody asker struct, not on copy.
  • The ceremony routes (/native/request|exchange|start|authorize) sit outside the session group by design; inside the package /native/authorize is wrapped in the require passed in (memoria's requireSession), and exchange re-runs that same require on a fresh request carrying only the approved cookie before minting, so a removed owner cannot complete an exchange (pinned by the package's own test, mutation-checked upstream 2026-09-12). Resume runs above homeMiddleware, but it only acts on GET / with a valid 32-byte pending cookie and an existing session, and only redirects. wireNative opens a second sessions.Sessions over the same table with the same Origin, so cookie names agree with auth's; the exchanged cookie is accepted by RequireSession (test above shows 200).
  • TestNoUndeclaredRoutes lists all eight; the gate keys on method+path. No new log lines; no query or note text reaches a log or error string. Spec §"Native Apple clients" added, not amended over a settled decision.

Gates

gofmt -l clean, go vet ./... clean, full go test ./... green on the stacked head (one TestWiringStartsTheCountingClock TempDir-cleanup failure appeared once, then passed three times in isolation, on a full re-run, and on origin/main — a cleanup race, not this branch). amadan ci status passed at cb534c3. I did not re-run the Swift builds or the review binary; the discussion's account of them names a real artefact (PNG written), which is the "verify it ran" check AGENTS.md asks for.

Findings (none blocking)

  1. [nit] native/README.md:18 vs native/apple/Package.swift:6 / Package.resolved — pin drift, and the README is the wrong one. In the local rastrillo/native checkout 33ffe99 (2026-09-12) is an ancestor of 2295259 (2026-09-13, "Refresh saved accounts before per-window account changes"), five commits apart. Package.swift pins the newer client that the last native commit ("Restore recent accounts") needs; the README should say 2295259 for Swift and 33ffe99 for Go. One-line doc fix, follow-up is fine.
  2. [nit] go.mod:27 — github.com/google/uuid is still // indirect while native.go imports it directly. make ci has no tidy check, so nothing catches it; go mod tidy will move it on the next touch. Harmless today.
  3. [nit] Import grouping in native.go and handlers.go puts github.com/google/uuid inside the stdlib block and nativeauth at the head of handlers.go's block. gofmt accepts it; the rest of the package separates stdlib from the others. Cosmetic.
  4. [note, upstream design, no change] Browser-approval phishing shape. A signed-in person lured to GET /native/start?id=<attacker's pending id> is redirected to the approval page and, if they click the button, binds their session to the attacker's request. The page shows a code the app must match and needs an explicit click, which is the accepted mitigation for this flow in rastrillo/native (reviewed there 2026-09-12). Recording it so the decision is visible from this repo; not a memoria defect.
  5. [note] B10 (server-minted key per request) is real and is fixed on #14, stacked on this branch — reviewed there.

Live defects found: 0. Mutations: none needed on the merge itself; the fixture tests were read against the code rather than re-mutated.

I fixed Fable's nits 1 and 2 in 4f37b93.

  • Nit 1: native/README.md now gives both pins: Swift is 2295259 (Package.swift and Package.resolved) and Go is 33ffe99 (go.mod).
  • Nit 2: I ran go mod tidy. github.com/google/uuid is now a direct requirement, and both direct requirements the merge added are in the main require block.

I left nit 3 (import grouping) alone. make ci passes, and amadan ci status passed at 4f37b93.