diff --git a/docs/NEXT.md b/docs/NEXT.md index ab5fea0..d7b6b7d 100644 --- a/docs/NEXT.md +++ b/docs/NEXT.md @@ -2,7 +2,7 @@ The flight plan. Each item carries enough context to start cold; update this file whenever an item lands (move it to "Done") or a new one is queued. Decisions made while working an item still go through `decisions/` as usual. -_Last updated: 2026-08-25 (sign-off band + Atmosphere actions landed from the theme-UX tail; the `signOff` lexicon republication is queued for Jacob. PR #27 merged: custom domains are invite-only pending pricing — **PD-9, and it needs `CUSTOM_DOMAIN_ALLOWLIST` set on the next deploy or nobody, Jacob included, can add one** (queue item 0 below); subdomains stay open as PD-4's free tier. Critique trend for the push: 25 → 31 → 29 → 32, snapshots in `.impeccable/critique/`)._ +_Last updated: 2026-08-25 (PRs #27 and #28 are **merged and deployed** — the domain lock went out with `CUSTOM_DOMAIN_ALLOWLIST` set, and the sign-off band is live on jzweifel.dev. Still on Jacob's plate: the `signOff` lexicon republication, and optionally setting a valediction in `/admin` settings. Critique trend for the push: 25 → 31 → 29 → 32, snapshots in `.impeccable/critique/`)._ ## Where things stand @@ -10,10 +10,6 @@ Feasibility is done and the verdict was **build it** (see `FEASIBILITY.md`). All ## Queued, roughly in order -### 0. Operator step, before or with the next deploy - -`wrangler secret put CUSTOM_DOMAIN_ALLOWLIST` — a whitespace- or comma-separated DID list, starting with Jacob's `did:web:malpercio.dev` and `did:plc:o3zuar7kk2mrz7d4sqxdisy2` plus whoever is invited. Unset means **nobody** can add a custom domain, so a deploy without it locks the operator out too (domains already added keep serving either way). `*` opens it to every account — the value that lifts the lock when PD-4's paid tier ships. Subdomains need no secret: `SUBDOMAIN_ALLOWLIST` is a var in `wrangler.jsonc`, set to `*`, and deploys with the code. - ### 1. Continue v1 (per ADR 0008 — the scope is ratified; don't re-scope) Done so far: OAuth login (loopback dev client; hosted-client path ready pending a real key + deploy) with D1-backed state/session stores; lexicon convention tests; **read-only adapters** for Bluesky (profile + posts, replies filtered), standard.site (documents/publications, `pub.leaflet.document` fallback only when no standard.site docs exist), and sifa (profile/positions/education/skills, defensively parsed) — fetch-injected modules in `apps/web/src/lib/server/atmosphere/` with unit tests, plus `detectSources` (drives the ADR 0012 default section order) and a source-overview admin page; **site/page authoring** — record builders, PDS writes through the OAuth session, and the `/admin` + `/admin/pages` CRUD routes; **the professional-presence theme** — the render pipeline in `apps/web/src/lib/server/render/` and the public routes at `/s/[handle]`. Remaining, roughly in dependency order: @@ -37,8 +33,8 @@ Done so far: OAuth login (loopback dev client; hosted-client path ready pending ## Done -- 2026-08-25 — **The airmail sign-off band landed — the letter signs off, with Atmosphere-native actions** (tail items "sign-off block" and "Atmosphere-native actions", resolved together). Three directions were mocked on the design canvas (letter closing / postmark band / "From" block); Jacob picked the **postmark band**: a sunk-paper band above the footer where the airmail stripe returns to bookend the page, a rotated circular postmark (handle + render-month, airmail-blue ink), the owner's name, and three actions — Follow (Bluesky profile link, where Follow/Message actually live; Bluesky has no follow/DM intent URL), Say hello (`bsky.app/intent/compose` prefilled with the @mention), Write to me (mailto, only when `contactEmail` is set). The valediction is **configurable, not default**: new optional `signOff` field on `page.mooring.site` (add-optional-only per ADR 0012; republication queued above) flowing lexicon → record builder → settings form → `SiteView` → `SiteLayout`; empty renders the name alone — Jacob was unsure "Warmly," suits every site, so it's a choice ("whimsy seasons the dish"). Band renders on every page like the footer; verified locally against live PDS data (jzweifel.dev): actions resolve, dark tokens flip, no horizontal overflow at 375px, chips ≥24px targets. -- 2026-08-25 — **Costly hosting actions gated behind DID allowlists** (PD-9, PR #27). `hosting/access.ts` holds `allows(list, did)`; the two actions that take a *new* host — `claim` and `addDomain` in `/admin/hosting` — check it and 403, and the page hides their forms. On mooring.page only custom domains are actually locked: subdomains are PD-4's free tier and cost nothing per account, so `SUBDOMAIN_ALLOWLIST` ships as a `*` var while `CUSTOM_DOMAIN_ALLOWLIST` is a secret naming individuals. The subdomain gate stays in the code because a self-hosted instance may want it, and flipping it is a config change. Nothing else needed gating: `release`, `verifyDomain` and `removeDomain` already 404 on rows the account does not hold, so accounts that already have a subdomain or a domain keep them, keep serving, and can still retry TLS. Serving is untouched — `hooks.server.ts` never consults the lists. 8 new unit tests (194 in `web`). Caveat by design: a grandfathered account that *releases* its subdomain cannot reclaim one while the lock is on, and the release copy says so. +- 2026-08-25 — **The airmail sign-off band landed — the letter signs off, with Atmosphere-native actions** (PR #28, merged and deployed; verified live on jzweifel.dev. Tail items "sign-off block" and "Atmosphere-native actions", resolved together). Three directions were mocked on the design canvas (letter closing / postmark band / "From" block); Jacob picked the **postmark band**: a sunk-paper band above the footer where the airmail stripe returns to bookend the page, a rotated circular postmark (handle + render-month, airmail-blue ink), the owner's name, and three actions — Follow (Bluesky profile link, where Follow/Message actually live; Bluesky has no follow/DM intent URL), Say hello (`bsky.app/intent/compose` prefilled with the @mention), Write to me (mailto, only when `contactEmail` is set). The valediction is **configurable, not default**: new optional `signOff` field on `page.mooring.site` (add-optional-only per ADR 0012; republication queued above) flowing lexicon → record builder → settings form → `SiteView` → `SiteLayout`; empty renders the name alone — Jacob was unsure "Warmly," suits every site, so it's a choice ("whimsy seasons the dish"). Band renders on every page like the footer; verified locally against live PDS data (jzweifel.dev): actions resolve, dark tokens flip, no horizontal overflow at 375px, chips ≥24px targets. +- 2026-08-25 — **Costly hosting actions gated behind DID allowlists** (PD-9, PR #27; deployed 2026-08-25 with `CUSTOM_DOMAIN_ALLOWLIST` set, so the operator step cleared without a lockout). `hosting/access.ts` holds `allows(list, did)`; the two actions that take a *new* host — `claim` and `addDomain` in `/admin/hosting` — check it and 403, and the page hides their forms. On mooring.page only custom domains are actually locked: subdomains are PD-4's free tier and cost nothing per account, so `SUBDOMAIN_ALLOWLIST` ships as a `*` var while `CUSTOM_DOMAIN_ALLOWLIST` is a secret naming individuals. The subdomain gate stays in the code because a self-hosted instance may want it, and flipping it is a config change. Nothing else needed gating: `release`, `verifyDomain` and `removeDomain` already 404 on rows the account does not hold, so accounts that already have a subdomain or a domain keep them, keep serving, and can still retry TLS. Serving is untouched — `hooks.server.ts` never consults the lists. 8 new unit tests (194 in `web`). Caveat by design: a grandfathered account that *releases* its subdomain cannot reclaim one while the lock is on, and the release copy says so. - 2026-08-25 — **Critique rounds 3–4 and their fix PRs (#24, #25) merged and deployed.** Round 3 (29/40) discovered pre-existing issues all earlier reviews missed; PR #24 fixed the mechanical ones: per-site favicons (the stock Svelte logo was the P0 — sites use the record `icon` blob → avatar fallback via `/blob`, app pages get an airmail-striped icon), sifa positions/education sorted newest-first (record order was never meaningful), `yearOf`/`yearSpan` tolerant of formless date strings, the career "More" disclosure made reversible with focus retained, visible contact address in the footer, banner `srcset` + 768 blob width, absolute `og:image` via `/blob`, `color-scheme`/`theme-color`. Round 4 scored **32/40** ("an authored design with a real point of view") and caught PR #24's own regression; PR #25 fixed it: footer wraps at phone widths (the visible email had overflowed the nowrap row), the career summary leads with its full first paragraph so the quantified proof ("~1M → 5M+") is visible without clicking, Skills sublabel, "+21 more"→"Show fewer" swap, ≥24px disclosure targets, banner double-download eliminated. Working pattern that held all push: dual-assessment critique → decisions with Jacob (AskUserQuestion) → design canvas when the change is visual → PR with local verification against live PDS data → deploy → re-critique. - 2026-08-25 — **PR #22 merged: contact affordance, career clamp, blob proxy, polish.** The site record gained optional `contactEmail` (admin settings field; renders a "Write to me" mailto stamp in every hero and a filled "Say hello →" chip on the Open to row — answering the re-critique's P0). The sifa career summary clamps to two sentences behind a native disclosure; record-level curation fields stay queued. Profile images serve through `/blob/{did}/{cid}?w=` (ADR 0015, **Accepted**): DID-document-pinned source, width allowlist, immutable cache, `cf.image` transform with pass-through fallback. Sweep: own-host links suppressed in bios, single writing link, consistent new-tab externals, permalink `aria-label`s, ≥24px tap targets, dark-mode airmail token, " · " paragraph separator in meta descriptions, avatar alt text. Re-critique of the deployed overhaul scored **31/40** (from 25; dual-assessment snapshots in `.impeccable/critique/`). - 2026-08-24 — **Theme UX overhaul landed** (PR #20 merged: linkification everywhere, two CSS specificity/background bugs, sifa `startedAt`/`endedAt` date fix, writing URLs joined from publication url + doc path, skill cap, dates in `