7.6 KiB
context, phase, task, total_tasks, status, last_updated
| context | phase | task | total_tasks | status | last_updated |
|---|---|---|---|---|---|
| phase | 03-event-write-back-pwa-install | Gate 2 Part D — edit/delete join fix (diagnosed, not started) | null | in_progress | 2026-06-07T02:39:57.236Z |
Critical Anti-Patterns
| Pattern | Description | Severity | Prevention Mechanism |
|---|---|---|---|
| playwright-cli wedges in this WSL2 env | After loading a never-settling page (e.g. the infinite-spinner state), the playwright-cli daemon hangs and even open/run-code fail afterward. Burned a lot of effort on it. |
blocking | Do NOT use playwright-cli for verification here. Verify via curl against the tunnel + ask the operator to test in their real browser/incognito. |
| Drizzle: referencing joined-table columns without the join | events.ts edit + delete handlers select calendars.url/calendars.userId from .from(calendarEvents) with no .innerJoin(calendars,...) → runtime 503 "table calendars is not part of the query". Unit tests mock db.select() so they DON'T catch it. |
blocking | Any handler selecting another table's columns MUST .innerJoin it. Regression tests for write endpoints must exercise the REAL query builder (test DB), not a mocked db.select(). |
.env is permission-locked |
The Read/Edit/Bash tools are denied on .env (and .env.spike). |
advisory | Hand the operator exact .env lines to apply via the ! prefix; never assume you can read/write it. |
| docker compose recreate drops the newt target ~30s | Every docker compose up -d recreates the api container, resetting newt's held TCP connection → tunnel returns 503 "no available server" for ~30s, then self-recovers. |
advisory | After any recreate, poll /health through the tunnel until 200 before testing. Not a bug — do not chase it. |
<current_state> Phase 03 Gate 2 live verification is largely working. The PWA now loads through Pangolin/newt, real Authelia OIDC login works, and create-event round-trips to Fastmail correctly (right time, right user) after this session's fixes. Working tree is clean (all committed).
Immediate blocker: deleting (and latently editing) an event 503s — the events.ts edit/delete handlers are missing a calendars join. Diagnosed, fix NOT yet applied. The operator paused right as I asked how to land the fix.
</current_state>
<completed_work>
This session (commits b46b25b → bdbb9b8):
- Tunnel works — operator set
newt -mtu 1200(was 1280 == eth0 underlay; WireGuard overhead blackholed large packets). THIS was the real cause of every "spinner" — the 510KB JS bundle never downloaded. Plus API now serves the full./publictree (431ab31), so manifest/sw/icons stop returning HTML. - Auth works —
/api/loginroute + redirect (quick task 260606-tv8),fetchMeusesredirect:'manual'(1adb460),OIDC_CLIENT_ID=familysync-dev,OIDC_SCOPES=openid profile email offline_access, redirect URI corrected. Operator addedoffline_accessto the Authelia client. - Bring-up (
b46b25b) — PWA built into the API image (single port :3000),NODE_ENV=production, broker credential seeded (reused the spike app password). - BUG A (timezone) + BUG B (calendar identity) fixed via /gsd-debug (
a9d3de6, session.planning/debug/write-path-event-bugs.md). Migration0001_calendars_user_url_unique.sqlapplied to the live DB. - Spike cleanup — deleted obsolete user id=1 ("Dev User") + calendar id=1 + cached events (operator-approved DB op).
- Backlog 999.2 added — slick unauthenticated-entry (no login flash). </completed_work>
<remaining_work>
- BLOCKING — edit/delete join fix.
apps/api/src/routes/events.ts: thePATCH /:uid/editlookup (~line 295) andDELETE /:uidlookup (~line 392) selectcalendarUrl: calendars.url+userId: calendars.userIdfrom.from(calendarEvents).where(eq(calendarEvents.uid, uid))with NO join. Add.innerJoin(calendars, eq(calendarEvents.calendarId, calendars.id))to both (mirror the workingGET /api/eventsquery at ~line 153). Add a regression test that runs the real query builder. me.tsblank displayName — passes the (missing)emailclaim as displayName, never readsname/preferred_username;oidc_issalso blank. Legend name is blank (user id=2display_name=''). May also need Authelia to include name/email in the ID token, or call userinfo.GET /api/eventshas nouserId/isSharedfilter (returns all users' events) — latent now (1 real user), real bug for a 2nd member.- "Syncing" toast not animated / ~27s — UI polish (backlog candidate).
- Gate 2 remaining: A2 (session persistence), A3 (2nd-member color), B (iOS standalone install+login — device-only), C (5-min SSE smoke — Phase 4 entry gate).
The operator was choosing how to land #1: (a) batch #1+#2+#3 in one /gsd-quick, (b) /gsd-quick just #1, or (c) fix #1 inline. Re-offer that. </remaining_work>
<decisions_made>
newt -mtu 1200— fixes large-asset blackhole through the WireGuard tunnel.- Deleted the spike user (id=1) + its calendar/events — obsolete test identity, re-syncable cache.
fetchMeredirect:'manual', serve full./public, constrainedOIDC_SCOPES— see HANDOFF.json. </decisions_made>
Required Reading (in order)
.planning/HANDOFF.json— machine-readable mirror of this state.apps/api/src/routes/events.ts— the edit (~line 295) + delete (~line 392) handlers missing thecalendarsjoin; compare to the working GET query (~line 153)..planning/debug/write-path-event-bugs.md— the resolved timezone + calendar-identity debug session (context for the write path)..planning/phases/03-event-write-back-pwa-install/03-GATE2-RESULTS.md— the Gate 2 checklist (still needs updating with what now passes).
Infrastructure State
- Stack:
docker compose(production target) — api + mariadb (healthy) + redis up./health200 locally AND throughhttps://familysync-dev.bergerhouse.net. - newt: systemd service,
-mtu 1200drop-in applied by operator; WireGuard tunnel carries large transfers now. - DB: 1 user (id=2, real OIDC,
display_name='', color #E8734A); calendars id=2 "Calendar" (509 ev) + id=3 "USA Holidays" (32).uniq_calendar_user_url(user_id,url)present. - OIDC:
client_id=familysync-devregistered in Authelia withoffline_access; redirecthttps://familysync-dev.bergerhouse.net/callback. - An old test event (uid
92dd5a80…) exists in Fastmail at the WRONG time (written pre-BUG-A-fix) — operator should delete it via the app once delete works.
<next_action>
Start with: re-offer the operator the landing choice for the edit/delete join fix (batch #1+#2+#3 / quick-only #1 / inline). Then apply .innerJoin(calendars, eq(calendarEvents.calendarId, calendars.id)) to the edit (~line 295) and delete (~line 392) handlers in apps/api/src/routes/events.ts, add a real-query regression test, docker compose up -d --build, wait for tunnel /health 200, and have the operator re-test delete in the browser.
</next_action>