# JBNX Studio Model m/002 — Joey Nguyen

Zero-drift AI video presenter / digital-twin asset reference.
Status: **ACTIVE ANCHOR • SEED LOCKED** · Catalog ID `jbnx-m-002` · Route `/design/m/002`
Theme: **Capture OS (Navy & Gold)** — see `tokens.json`. Parent: JBNX Design Standards (`/design`).

## Adaptation note (read first)

The owner spec named a Next.js tree (`/app/design/m/002/page.tsx` + components).
This repo is **not** Next.js: www.jbnx.io is a Flask app (`app.py`, blueprint
`model_design.py`) that serves model pages from `static/design/m/<id>/` with
Jinja templates in `templates/design/`. The spec tree is preserved in the repo
at `app/design/m/002/` as the migration source of record, and the **live**
implementation is:

| Spec file | Live equivalent in this repo |
|---|---|
| `page.tsx` | `templates/design/model-002.html` (route added in `model_design.py`) |
| `components/ViewportToggle.tsx` | `templates/design/m002/components.html` macro `viewport_toggle` + `static/design/m002.js` |
| `components/VariantTabs.tsx` | macro `variant_tabs` in the same file + `m002.js` |
| `components/MetadataCard.tsx` | macro `metadata_card` in the same file (collapsible `<details>`) |
| `catalog.json` | `static/design/m/002/catalog.json` (verbatim spec content + `registration` block) |
| `tokens.json` | `static/design/m/002/tokens.json` (verbatim spec content, real file) |

## The 404 fix

For m/001, `static/design/m/catalog.json` points at `/design/m/001/tokens.json`
and `/design/m/001/style.md`, but neither file exists on disk — `model_design.py`
silently aliases them (`tokens.json -> model.json`, `style.md -> framework.md`).
For m/002 every registered route resolves to a **real file** in
`static/design/m/002/`: `catalog.json`, `tokens.json`, `model.json`,
`framework.md` (`style.md` is an explicit alias of it), `prompts.txt`,
`production.json`, `production-ledger.json`, and the three labeled asset slots.
`tokens.json` inherits the Capture OS master theme and is **not** an alias of
`model.json`.

## Identity rules (zero drift)

- East/Southeast Asian man, mid-40s, short dark hair.
- **Strictly clean-shaven in every variant and every frame.** No facial hair, no goatee.
- Eyewear: Variant B only (modern clear-frame glasses). A and C: none.
- Wardrobe is fixed per variant — see `catalog.json` / `model.json`.
- Capture: 85mm f/1.8, eye level, Capture OS lighting (soft neutral key, warm
  gold `#D4AF37` rim/hair light), deep-navy background.
- Anchor prompts are exact strings in `prompts.txt`. Do not paraphrase them
  into a generation run; record generator, version, seed and settings in
  `production-ledger.json` when an anchor is accepted.

## Asset status — reference photos wired, generated anchors pending

Real reference photos are wired (2026-10-28): 13 full-resolution photos of
Joey Nguyen, selected from the owner's "Joey Only" album, live at
`assets/anchor-*.jpg`, are whitelisted in `model_design.py`, and are recorded
in `model.json` `assets[]` with SHA-256 (seed: `anchor-c001-seed-front.jpg`,
JEV 3.68/4). The viewport shows each variant's reference photo. Honest gaps:
Variant A's reference is a brown blazer, not the midnight-navy spec shot;
Variant B's glasses photo wears a cap (no no-cap glasses shot exists);
Variant C's quarter-zip is cream, not midnight-navy; every reference needs a
background crop/replace.

Generated anchors and video loops do **not** exist yet. The labeled SVG slots
(`assets/anchor-slot-{a,b,c}.svg`) are placeholders for those generated
assets only, **not** photos of Joey Nguyen. Expected generated assets, when
produced and owner-accepted:

- `assets/joey-anchor-a-v001.png`, `assets/joey-loop-a-v001.mp4`
- `assets/joey-anchor-b-v001.png`, `assets/joey-loop-b-v001.mp4`
- `assets/joey-anchor-c-v001.png`, `assets/joey-loop-c-v001.mp4`

Adding a real asset = add the file, add its path to the `_MODELS_002_FILES`
whitelist in `model_design.py`, and record it in `model.json` `assets[]` with
its sha256 — the same discipline as m/001.

## Voice pipeline

Eleven Multilingual v2 (Custom Clone) — stability 0.55, similarity 0.75,
style 0.08, speaker boost on. Clone not yet enrolled; no voice hash exists.
See `production.json`.

## Watermark

Fixed footer on the page, bottom-left:
`CipherDeck • Illustrator: Joseph B. Nguyen`

## Voice key — ELEVENLABS_KEY (added 2026-10-28)

The ElevenLabs key exists as a platform secret named **`ELEVENLABS_KEY`** —
reference it by name only, never by value:

- Supabase Edge Function Secrets — **jbnx** project (ref `ngjmqdzpnhwpybtssykz`): present (name verified 2026-10-28).
- Supabase Edge Function Secrets — **fedm8** project: present (name verified 2026-10-28).
- Railway variable: owner-set 2026-10-28 (the stored workspace credential cannot read Railway project variables — known token-scope limit; not evidence of absence).

Runtime code resolves the key from the environment (`ELEVENLABS_KEY` injected
by Railway / Edge Functions). `voice-healthcheck.py` in this folder is the
check: it calls ElevenLabs `/user` + `/voices` and reports **status only**
(accepted / not accepted / key missing, voice count, clone-enrolled boolean).
It never prints the key, a header, or a voice ID. In a runtime without the
secret it reports `key_missing` — run it where the secret is injected to get
the acceptance verdict. Clone status: **not enrolled yet**.

**Correction (2026-10-28):** an earlier projects-record claim that the stored
`ELEVENLABS_KEY` value "is the API key ID, not the secret" was **wrong**. It
came from testing the value returned by the Supabase Management API
list-secrets endpoint — which returns only a **SHA-256 digest** of each
secret, never the plaintext (every secret in the project, including URLs and
JWTs, lists as a 64-char hex string). The digest was fed to ElevenLabs and
the resulting 400 said nothing about the owner's real key, which has **never
been tested**. Correct status: key **present, UNVERIFIED**, pending a
health-check run inside an injected runtime (Railway/Edge) — exactly what
`voice-healthcheck.py` is for.

## Anchor photos — selected and wired (2026-10-28)

The picks were selected by the lead from the owner's "Joey Only" album:
8,319 items filtered to 2023+ at ≥2880px on both dimensions (1,165),
de-bursted to 202 shoots, all visually reviewed, 20 shortlisted, 14 finalists
JEV-scored (jev-1.13.0, 0–4 legend). The 13 wired files, their album item
IDs, dates, dimensions and scores are recorded in `model.json` and
`production-ledger.json`. Seed = `anchor-c001-seed-front.jpg` (2026-10-01,
JEV 3.68/4, seed probability 0.88). A dedicated short shoot would still close
the three gaps listed under Asset status above.

## v0.3.0 reference pack (2026-10-31) — rejected by Joey, archived

Framework: https://projects.jbnx.io/framework/ai-model-reference-pack-framework · job file:
`reference-pack-job.json` (rerun it with `scripts/model_reference_pack/`) · QA: `qa.json`.

**Diagnosis.** The 13 owner photos are sharp 12 MP phone shots, so compression was not the problem.
They are casual: wide-angle selfies, other people in 8 of 13, caps in 5, real-world backgrounds.
v0.2 had no generated references at all. Root cause of the parity gap: no clean studio references.

**Inputs (privacy).** Four Joey-only crops: face `anchor-c010` (face authority), full body
`anchor-c050` (the woman beside him masked), right profile `anchor-c027`, second front `anchor-c025`.
Only Joey is identifiable in anything sent to a provider. The crops are recorded in `model.json`
`working_inputs` with SHA-256 and are not published.

**Face authority.** `assets/joey-face-source-v003.jpg`: the c010 crop restored 2x with fal Topaz
Recovery V2 (face creativity 0; geometry unchanged). c010 was chosen over the c001 seed because c001
is shot from below with visible stubble; c010 is level, clean-shaven and unobstructed. The owner
photos remain the originals and win any conflict.

**References (4K, Nano Banana Pro).** Eight body angles, five face turns and three expressions in
the Variant A outfit (extended to full body with matching trousers, black belt and black shoes, an
agent choice Joey may change) on a mid slate-grey backdrop so the navy suit separates; the three
variant anchors (`assets/joey-anchor-{a,b,c}-v003.jpg`) on the Capture OS deep-navy backgrounds,
replacing the labeled slots. Two scene first frames (floor, chair). A 360° sheet.

**Motion and voice.** Four Kling 3.0 pro baselines (turn, walk, sit, talk; sound off). Cloned voice
**Joey v4.0** found in the owner's ElevenLabs account; sample line (agent-drafted, awaiting Joey's
approval): "Hi, I'm Joey Nguyen. This is my reference model for JBNX Design." Voice proof: Sync
Lipsync 2 Pro on the talk baseline; ASR exact, waveform 0.994 at 0 s, no added text.

**Rejected / deviations.** body-045 (only the head turned) and scene-chair (armchair behind him)
were re-rolled. scene-chair-r2 is accepted with a deviation (armchair on image right; fully in
frame). The turn baseline reached a right-facing profile instead of left. The gold lapel pin varies
between bar and round shapes (accessory only).

**Status.** Generated anchors and references await Joey's acceptance. Video loops per variant are
not made; the motion baselines use Variant A. Spend: $9.35 quoted of the $12 cap (owner directive
2026-10-31); cash bills not reconciled.


## v0.4.0 reference pack (2026-10-31) — rebuilt on Joey's own anchor photos

**Why.** Joey rejected v0.3.0: "the generated images are fatter than me. they do not look like me.
you used the wrong photo to anchor to." He supplied the photos it should look like (mirror, cap
three-quarter, cap front), then a balcony selfie: "use this to anchor photo". All four are published
as `assets/anchor-u01…u04-*.jpg` and listed in `model.json` as reference photos. v0.3.0 assets are
archived, not deleted.

**What changed.** Face authority is the balcony selfie (level camera, his real lean face), restored
2x with Topaz (face creativity 0) into `assets/joey-face-source-v004.jpg`. Body authority is the
mirror photo, flipped back to his real left-right. The mirror face crop is used for the hair only
(shot from below, so it widens the face), and the cap front crop for eyes, nose and mouth only. The
identity lock now names his lean face (flat cheeks, firm jaw, tapered chin), smooth under-eyes, the
tall separate spikes, and a slim build, from his test-1 feedback: "face structure is wrong. cheeks
too big. hair is wrong. too much bags under eyes". Outfit is slim fit.

**Likeness (Jev + face-ID).** Scored against his photos; table in `model.json` → `likeness` and on
the page. Face-ID: his own photos 0.63–0.67, a different person 0.02, v0.4.0 face 0.61, Variant A
0.60, B 0.61, C 0.55. Jev: his real photos 86–88% same person (3.45/4); v0.4.0 face 85% (3.31/4,
confidence 0.45), hair match 84% vs 36% for v0.3.0. The tools confirm identity but do not separate
v0.3.0 from v0.4.0 well. Joey's eye decides.

**Rejected / notes.** Variant C first take was a different man (face-ID 0.004); re-rolled once
(seed 7002) and accepted at 0.55. Motion sit and talk failed at the provider on a parallel
character upload (no charge) and passed on a sequential retry. Walk: hair spikes soften and the
shoes leave the frame at the end. Voice proof: ASR exact, waveform 0.994 at 0 s, no added text.
Joey approved the sample line on 2026-10-31.

**Spend.** $20.43 quoted of the $21 cap (owner raised it from $12 on 2026-10-31: "Face A, rebuild,
cap $21"); Jev usage is billed on the Jev account and is unknown here. Cash bills not reconciled.

**Status.** Accepted by Joey on 2026-10-31 (anchors, references and the voice line).

**Privacy (2026-10-31, Joey's approval).** The eight album photos that showed other people (c010, c025,
c039, c050, c071, c074, c102, c145) are now published as Joey-only crops; any face left inside a crop is
pixelated, and a recheck finds only Joey's face. GPS location data was removed from all 13 album photos
(the five solo photos keep identical pixels). `model.json` keeps each original SHA-256 and the crop box.

## v0.5.0 (2026-11-08) — Joey's chosen look and character sheet

Session with Joey on 2026-11-08 (audit, five likeness rounds, character sheets):

- **Look.** Joey uploaded a better portrait (`assets/anchor-u05-owner-portrait.jpg`) and picked its
  film polish, round-5 P2 (`assets/joey-look-p2-v005.jpg`). His smile is a relaxed **closed-mouth
  half smile**, lips together — never a toothy or posed grin ("i don't smile like the other 2").
- **Signature outfit (Variant A and the reference pack).** Black pique polo **buttoned to the top
  button**, open black blazer, black trousers, black dress shoes ("i never unbutton top two").
- **Face.** Broad lower face, soft rounded jaw, natural cheek fullness, narrow hooded eyes, wide nose
  base, thin upper lip, his real age. The v0.4.0 "lean face, tapered chin" lock drifted narrower and
  younger than him and is retired.
- **References.** Character sheet V3 (`assets/joey-character-sheet-v005.jpg`), eight body angles,
  five face turns, three expressions and three variant anchors (`*-v005.jpg`), all Seedream 4.5 edit
  from his real photos + the approved look. Profiles and rear views are interpretations.
- **Likeness scoring.** ArcFace (InsightFace buffalo_l), mean against six of his real photos; his
  photos score ~0.63–0.67 against each other. Gate 0.60 for front and three-quarter views. Jev
  (jev-1.14.0) agreed with Joey's picks 2/6 after recalibration and now checks technical quality only.
  Nano Banana Pro produced two identity swaps; prefer Seedream 4.5 for this model.
- **Not yet redone.** Motion baselines, scene first frames and the voice proof are still v0.4.0;
  re-render from the v0.5.0 anchors on Joey's go-ahead.
- Build: `scripts/model_reference_pack/build_manifests_m002_v050.py`.
