Merge S10 (a1): public ledger profiles v1

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

# Conflicts:
#	BUILD-STATE.md
#	CLAUDE.md
This commit is contained in:
Kev
2026-07-11 19:40:43 -04:00
19 changed files with 1224 additions and 6 deletions
+20
View File
@@ -913,6 +913,26 @@ phased plan in the Session-57 conversation / BUILD-STATE Next section).
plumbing). Sport selector is MLB/NBA/WNBA (ParlayLeg's sport union).
- **Vision-model upgrade is POST-REVENUE** (specs/vyndr-roadmap.md) — the
route contract is the stable interface; only the extraction engine swaps.
## Public Ledger Profiles v1 (Session 10, A1 board — non-obvious)
- **`public_profiles` (migration 022) is PRIVATE BY DEFAULT** — `published`
DEFAULT FALSE, one explicit toggle in Settings, service-role writes only.
`GET /api/profiles/:handle` returns the SAME 404 body for unknown AND
unpublished handles (no existence leak) — keep them byte-identical; a
test diffs the two responses. A malformed handle 404s WITHOUT a DB query.
- **`getModelAggregate({ userId })`** swaps `.is('user_id', null)` for
`.eq('user_id', uid)` on BOTH the settled and pending queries — the same
30d window + n≥20 gate over one user's ledger. The no-userId default is
the public model record and must stay untouched.
- **The user's public entries are SETTLED rows only** (`.not('outcome',
'is', null)`, newest 50, same columns as /api/ledger) — pending reads are
not public until they settle, and nothing is curated: misses included.
- **`/u` is in OPEN_ROUTES on purpose** — the share surface must load
anonymous; the privacy gate is the API's 404, never the router.
- **Jest from a worktree gotcha:** the repo config's `testPathIgnorePatterns`
includes `/.claude/`, which matches EVERY path inside
`.claude/worktrees/...` → "No tests found". Run with
`npx jest --testPathIgnorePatterns "/node_modules/"` from a worktree
(CLI replaces the config array).
## Active Skills
- vyndr-voice (all user-facing output)