Fix #234: owner profile page #269

Merged
crueber merged 2 commits from fix/issue-234 into main 2026-09-10 04:21:41 +00:00
Owner

Fixes #234 — /:owner is now a profile page (display name, location, IANA timezone picker, markdown bio via renderBody) over a new CAS'd sidecar owners//profile.json, with the activity-ordered repo list (#247 shape, already on main).

Auth (P6 decision in docs/features/01): PUT = AuthWrite + host-admin / name-match / org-owner-role via the new api.OwnerEditor seam (law 8). GET carries request-scoped can_edit so the UI never guesses.

Tests: Go httptest auth matrix + CAS/validation/restart (profile.go 100%, api 95.2%, identity 97.2%, store 95.0%, -race clean); node --test 542/542 incl. new timezone+profile suites; vite build + live-server curl smoke (twins, discovery, PUT/GET, restart survival) green. Browser both-themes console check explicitly open (shared daemon blocks loopback per task rules).

Deviations: none from the issue (JSON not .pb per sidecar convention; no client version on PUT — idempotent CAS loop converges; can_edit on GET instead of a new endpoint).

Fixes #234 — /:owner is now a profile page (display name, location, IANA timezone picker, markdown bio via renderBody) over a new CAS'd sidecar owners/<owner>/profile.json, with the activity-ordered repo list (#247 shape, already on main). Auth (P6 decision in docs/features/01): PUT = AuthWrite + host-admin / name-match / org-owner-role via the new api.OwnerEditor seam (law 8). GET carries request-scoped can_edit so the UI never guesses. Tests: Go httptest auth matrix + CAS/validation/restart (profile.go 100%, api 95.2%, identity 97.2%, store 95.0%, -race clean); node --test 542/542 incl. new timezone+profile suites; vite build + live-server curl smoke (twins, discovery, PUT/GET, restart survival) green. Browser both-themes console check explicitly open (shared daemon blocks loopback per task rules). Deviations: none from the issue (JSON not .pb per sidecar convention; no client version on PUT — idempotent CAS loop converges; can_edit on GET instead of a new endpoint).
/:owner becomes a profile page: display name (slug fallback), location,
IANA timezone, markdown bio via the shared renderBody pipeline, plus the
existing activity-ordered repo list (#247 detailed shape, already on main).

Backend (07_api.md §8, 14_extensibility.md §14.11/§14.12):
- New CAS'd sidecar owners/<owner>/profile.json (store.OwnerProfileKey),
  added to the frozen overwritable list in the same change (rule 2).
- GET/PUT /api/v1/owners/{owner}/profile (+ api-browser + services/api
  twins, discovery-listed, SDK owners.profile/updateProfile). GET is
  AuthRead with the §8 unknown-owner convention (200 empty, never 404);
  PUT is AuthWrite + owner rule with the bounded CAS loop as commit
  point (no WAL at owner scope).

Auth (docs/features/01 P6 decision): host admin, name-matched principal,
or org-owner role via the new api.OwnerEditor seam (identity implements,
wired in cmd/walhub composition — law 8, the OrgGate shape). Timezone is
shape-checked server-side (no tzdata dependency); IANA validity comes
from the picker (Intl.supportedValuesOf, no tz database).

Frontend: Repos.jsx profile header + owner-gated edit form (tz picker,
bio preview, save→invalidate, no reload); new lib/timezone.js +
lib/profile.js, headless-covered by node --test.
Sign in to join this conversation.
No description provided.