mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
WHAT WAS SHIPPED AS IF IT WERE LIVE. `Shell.tsx` carried DEFAULT_FOLK —
two colleagues by name with their working addresses — and DEFAULT_AGENTS,
substituted whenever the live list came back empty. `rooms.ts` carried
nineteen invented channels with rosters and message counts, and an
invented conversation attributed to those same people. `OrgSwitcher`
carried three orgs claiming role grants ("Owner", "Admin") IAM never made.
Directory carried install counts. Chat carried bios for two named
colleagues and a seven-step "thinking" trace under every turn. DevStudio
carried a run id, a 925ms waterfall and a path from this laptop. Vibe
carried a person, stamped online, with runs priced to four decimals.
All of it rendered on failure, so a broken service and a healthy one drew
the same populated product. They are gone, replaced by the ladder the app
should always have had: reading, refused, empty, ready.
THE SEND THAT LOOKED LIKE IT WORKED. `rooms.ts` appended each message
locally, posted it, and dropped the post's failure in a bare `catch {}`.
/v1/team/rooms answers 503 today, so every message anyone typed into a
room was discarded while the thread showed it delivered. Proven against a
mocked 503 carrying the live body: one POST attempt, not shown as
delivered, the draft still in the box, and a line saying nothing was lost.
`Talk.tsx` already had the right three states and a catch that reported —
both were dead code the hook could never reach.
ONE ADDRESS. Eight modules each wrote `process.env… || 'https://api.hanzo.ai'`
as a module-scope const, evaluated during the static prerender where there
is no window, freezing production into the bundle — so the dev proxy sat
unused beside them and every read went straight out and was refused by
CORS. One derivation now, in lib/hanzo/api.ts, exported as functions.
The bottom-left account block reads GET /v1/billing/tier and shows
`effectiveAvailable`, which the server's own description calls the only
figure to compare against zero. It never invents one: the server answers
502 rather than 0 for a balance it cannot read, and that survives to the
screen. The footer gated on the profile rather than on holding a token, so
a live session whose userinfo had not returned lost the whole foot.
The right pane is the room's own: Outputs and Sources derived from the
turns in hand, a tabbed preview, per-conversation tabs that survive a
reload, and a `+` whose every row is wired to an endpoint verified against
the served openapi.json. A blob iframe inherits our origin, so granting it
allow-same-origin would have handed model-written HTML the IAM token; it
is split by scheme, with a test asserting the ACCESS is refused.
The sidebar no longer names the org the bar above already names.