Wave 5A: /u house-mode public profile (D2)

Reserved house handle (default 'vyndr', env HOUSE_HANDLE) resolves to the
PUBLIC model record — getModelAggregate() with no userId (user_id=NULL rows)
— WITHOUT a public_profiles row. It is the ONLY special case; every other
handle keeps the private-by-default, byte-identical-404 no-existence-leak
contract. The house profile is always public and never 404s (a fetch failure
degrades to an honest building state).

- src/routes/profiles.js: house short-circuit + sendHouseProfile (public
  aggregate + by_tier + public settled entries), reserved before the publish
  lookup so a user claim is shadowed.
- PublicProfile.tsx: house label 'VYNDR MODEL · PUBLIC RECORD' + hero/subtitle
  off data.house; keeps the CLV-VERIFIED record hero + TierRecord calibration
  + recent settled reads (misses included).
- opengraph-image.tsx (1200x630): house-branded eyebrow/heading.
- portrait/route.tsx: new 1080x1350 share crop (real aggregate or tagline
  fallback, never a fabricated number).
- Discoverability: 'VIEW AS PUBLIC PAGE ->' on the ledger MODEL header +
  'VIEW PUBLIC RECORD ->' under the landing ModelRecord, both to /u/vyndr.
- tests/unit/houseProfile.test.js: house resolves to user_id=NULL aggregate
  (no public_profiles row) + by_tier; unknown/unpublished user handles stay
  byte-identical 404; page renders house label + TierRecord + portrait crop.

3019 tests green (3012 -> 3019); next build EXIT=0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-13 15:26:48 -04:00
parent da144ecb5e
commit 76c289d4c1
8 changed files with 399 additions and 8 deletions
+63
View File
@@ -19,6 +19,13 @@
* PRIVACY: PRIVATE BY DEFAULT — `published` only flips via the explicit
* toggle. NO EXISTENCE LEAK: an unknown handle and an unpublished handle
* return the byte-identical 404 body.
*
* HOUSE PROFILE (Wave 5A, D2) — the reserved `HOUSE_HANDLE` (default `vyndr`)
* resolves to the PUBLIC model record (`getModelAggregate()` with NO userId →
* the `user_id = NULL` ledger rows). It needs NO public_profiles row and is
* ALWAYS public (it's the partner-pitch weapon: the real house record). It is
* the ONLY special case; every OTHER handle keeps the private-by-default,
* no-existence-leak contract intact.
*/
const express = require('express');
@@ -30,6 +37,10 @@ const router = express.Router();
router.use(createRateLimit({ windowMs: 60_000, max: 60 }));
const HANDLE_RE = /^[a-z0-9_]{3,20}$/;
// Reserved house handle → the public model record (user_id = NULL). Operators
// can override via env; it must still satisfy HANDLE_RE to be reachable.
const HOUSE_HANDLE = String(process.env.HOUSE_HANDLE || 'vyndr').trim().toLowerCase();
const HOUSE_LABEL = 'VYNDR MODEL · PUBLIC RECORD';
// Same 404 body for unknown AND unpublished — never confirm a handle exists.
const NOT_FOUND = { error: 'Profile not found' };
// Same columns as /api/ledger (routes/ledger.js ROW_COLUMNS).
@@ -94,6 +105,10 @@ router.get('/:handle', async (req, res) => {
const handle = String(req.params.handle || '').trim().toLowerCase();
// Invalid shape can't exist (DB CHECK) → same 404, no query needed.
if (!HANDLE_RE.test(handle)) return res.status(404).json(NOT_FOUND);
// HOUSE handle → the public model record. Checked BEFORE the
// public_profiles lookup so the handle is reserved (a user who claims it is
// shadowed). This is the ONLY handle that bypasses the publish gate.
if (handle === HOUSE_HANDLE) return sendHouseProfile(res);
const sb = sbOrNull();
if (!sb) return res.status(404).json(NOT_FOUND);
try {
@@ -131,4 +146,52 @@ router.get('/:handle', async (req, res) => {
}
});
/**
* The house/model profile — the PUBLIC model record (user_id = NULL), served
* as a shareable /u profile WITHOUT a public_profiles row. Same response shape
* the page already consumes (aggregate + by_tier + settled entries), plus
* `house: true` + a label so the UI can distinguish it from a user profile.
* It always "exists" → a fetch failure degrades to an honest empty/building
* state, never a 404.
*/
async function sendHouseProfile(res) {
const sb = sbOrNull();
try {
// NO userId → the public `user_id = NULL` aggregate (the real house
// record + Wave-3 by_tier). getModelAggregate self-empties without env.
const aggregate = await ledgerService.getModelAggregate(sb ? { sb } : {});
let entries = [];
if (sb) {
// ALL public settled reads, misses included — nothing curated.
const { data, error } = await sb.from('ledger_entries')
.select(ROW_COLUMNS)
.is('user_id', null)
.not('outcome', 'is', null)
.order('graded_at', { ascending: false })
.limit(ENTRY_LIMIT);
if (error) throw new Error(error.message);
entries = data || [];
}
res.set('Cache-Control', 'public, max-age=60');
return res.json({
handle: HOUSE_HANDLE,
house: true,
label: HOUSE_LABEL,
aggregate,
entries,
min_sample: ledgerService.MIN_AGG_SAMPLE,
});
} catch (err) {
console.error('[profiles/house]', err.message);
return res.status(200).json({
handle: HOUSE_HANDLE,
house: true,
label: HOUSE_LABEL,
aggregate: null,
entries: [],
min_sample: ledgerService.MIN_AGG_SAMPLE,
});
}
}
module.exports = router;