-- v0.23.0 / roadmap item #29: server-side sign-in state resume. -- -- Track each authenticated user's last-viewed route + a small bag of -- "light" component state so that the *next* sign-in can land the user -- back where they left off, rather than always dropping them on the -- empty-state home view. -- -- Storage shape (one row per user — per-user, NOT per-device, per the -- #29 "safe default"): -- -- * `user_id` — PRIMARY KEY and FK into users(id) with cascade on -- delete. INTEGER to match users.id (INTEGER PRIMARY KEY -- AUTOINCREMENT). A deleted user automatically loses their stored -- resume state. One row per user means a later sign-in on any -- device resumes the most-recently-recorded route — the per-user -- model the roadmap asks for. -- -- * `last_route` — the frontend pathname the user was last on -- (e.g. "/rfc/open-human-model"). TEXT, nullable until the first -- route-change POST lands. NEVER contains draft-buffer contents — -- it is a route only. See SPEC §6.2 "Sign-in state resume -- (privacy)". -- -- * `last_route_state` — a JSON-encoded bag of *light* component -- state (scroll anchors, open-tab selection, filter chips, etc.). -- SQLite has no native JSONB; we store JSON as TEXT exactly as the -- rest of the app stores its JSON blobs (json.dumps / json.loads, -- cf. permission_events.details, actions.details). Nullable. -- PRIVACY INVARIANT: this column MUST NOT carry draft-buffer text, -- PR bodies, comment drafts, or any user-typed content — only -- ephemeral view state safe to replay. The PUT handler is the -- enforcement point; the column comment is the contract. -- -- * `resume_enabled` — the per-user opt-out flag. 1 (default) means -- "resume me where I left off"; 0 means "always land on home". The -- PUT handler no-ops the upsert when this is 0, and the read path -- refuses to hand back a stored route when this is 0. A -- profile-settings toggle UI to flip this is a follow-up (the -- column + default-on behavior ship now); see CHANGELOG v0.23.0. -- -- * `last_updated_at` — TEXT timestamp, app convention -- `datetime('now')`, matching device_trust.last_seen_at / -- users.last_seen_at. Refreshed on every successful upsert. -- -- No new env vars. The debounce interval for the frontend route-change -- POST is a frontend constant (~1s), not a server knob. CREATE TABLE user_session_state ( user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, last_route TEXT, last_route_state TEXT, -- JSON-encoded light state, nullable resume_enabled INTEGER NOT NULL DEFAULT 1, last_updated_at TEXT NOT NULL DEFAULT (datetime('now')) );