Skip to content

Wave 2 — the front door: a demo comparison, a backup card, and phrase entry that forgives - #25

Merged
Randolin merged 4 commits into
mainfrom
claude/product-strategy-positioning-u1vwig
Aug 28, 2026
Merged

Randolin merged 4 commits into
mainfrom
claude/product-strategy-positioning-u1vwig

Conversation

@Randolin

Copy link
Copy Markdown
Owner

The second wave of docs/feature-plan.md: what a newcomer meets, and what keeps them from losing everything.

A2 · A demo comparison, before you hatch

/demo renders the real panels, with real scoring, against a fictional pair — and reaches the network for none of it, which makes it the one page that still works when the profile server is down. The e2e proves exactly that: it drives the demo on the unconfigured landing page, before any server exists.

The cast is hand-written rather than reused from qa/. That cast is generated and reads like it — twins and exact inversions prove score bounds, they don't look like two people. These two are written to land the three moments worth showing: a strong overall fit, one dealbreaker that needs a conversation, and a mutual desire neither of them could have seen alone. Their phrases are real, well-formed view phrases, so the personas and their art derive the way everyone else's do.

A demo that rots is worse than none, so a core spec holds it to the schema: every answered id must exist, the phrases must still mint creatures, the fit stays in a believable band, exactly one dealbreaker trips and only in one direction, and one-sided desires stay hidden — which is the claim the demo makes about how desires travel.

Panel rendering moves into a shared ComparePanelsComponent, so a panel registered in app.config.ts appears on both /compare and /demo, or neither.

B1 · The backup card

/backup prints both phrases, the creature, the view QR and the date — to paper for a drawer, or to PDF for a password manager, using nothing but the browser's own print dialog. No dependency, no canvas rasterizing.

It prints the edit phrase in plain text, so it says so in a warning a spec pins, and the route sits behind the session guard: a view-only visitor must never reach a page that prints edit control. A spec pins that too, since the guard is one line someone could drop while refactoring routes with nothing else noticing.

Printing is treated as a real output format: the print block hides the shell, toast, skip link and the QR's share button, keeps colour (browsers drop backgrounds unless told, and the creature and QR are identity, not decoration), and refuses to split a phrase across a page break.

B2 · Phrase entry that forgives

Both phrases come from fixed public wordlists, so a phrase containing a word that isn't in them cannot possibly be right — and checking that costs microseconds where the Argon2id derivation it would otherwise reach costs seconds. The edit-phrase path had no check at all: one mistyped letter bought a multi-second wait and "no profile answers to that phrase", indistinguishable from a profile that was deleted.

It now names the wrong word and, when exactly one word is an edit away, what it probably should be. Two plausible corrections is a guess rather than a suggestion, so the suggester stays silent when the neighbour is ambiguous. It checks the words that exist even when the count is wrong, and accepts a pasted link, because the extractor does.

Messages land inline beside the field they fix, not in a toast that expires while you're still retyping. The EFF list stays a lazy chunk — verified at 77.72 kB, still separate.

Also fixes the landing page's view placeholder, which showed amber-azure-fox-canal-stove-plume: those tail words predate the compound-tail grammar, so the example was a phrase the app itself would now reject.

Verification

Full ladder green: format:check, both typechecks, all four unit suites (211 core / 34 ui / 18 app / 35 server), build, and e2e.

New e2e coverage: the demo renders a full comparison with no server configured (asserting the dealbreaker alert and the mutual reveal are both present, and that a one-sided desire is not), and a typo'd edit phrase names word 1 and returns in well under the time a derivation takes.

Note for reviewers: the initial-bundle budget warning predates this work (519.91 kB on main before wave 1; 528.10 kB here). The demo route and the EFF wordlist are both lazy chunks.


Generated by Claude Code

claude added 4 commits August 28, 2026 06:07
Everything else here needs two finished profiles and a reachable server
before it shows anything, which asks a newcomer to spend twenty minutes
on faith. /demo renders the real panels, with real scoring, against a
fictional pair — and reaches the network for none of it, which makes it
the one page that still works when the profile server is down. The e2e
proves exactly that: it drives the demo on the unconfigured landing
page, before any server exists.

The cast is hand-written, not generated. The QA cast in qa/ is generated
and reads like it — twins and exact inversions prove score bounds, they
do not look like two people. These two are written to land the three
moments worth showing: a strong overall fit, one dealbreaker that needs
a conversation, and a mutual desire neither of them could have seen
alone. Their phrases are real, well-formed view phrases, so the personas
and their art derive the way everyone else's do.

A demo that rots is worse than none, so a core spec holds it to the
schema: every answered id must exist, the phrases must still mint
creatures, the fit stays in a believable band, exactly one dealbreaker
trips and only in one direction, and one-sided desires stay hidden —
which is the claim the demo is making about how desires travel.

Panel rendering moves into a shared ComparePanelsComponent so the demo
shows exactly what /compare shows: a panel registered in app.config.ts
appears in both or neither, with no second list to keep in step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaDanzXTm6kVSgbNh1eNrs
The worst thing that can happen to a profile has had exactly one
mitigation: telling people to write the phrase down. This gives them
something worth writing down — both phrases, the creature, the view QR,
the date, and one line each on what they do. Print it to paper for a
drawer, or to PDF for a password manager; the browser's own print dialog
does the export, so no dependency and no canvas rasterizing.

The card prints the edit phrase in plain text, so it says so in a
warning that a spec pins, and the route sits behind the session guard —
a view-only visitor must never reach a page that prints edit control.
A spec pins that too, since the guard is one line someone could drop
while refactoring routes and nothing else would notice.

Printing is a real output format here rather than an afterthought: the
print block hides the shell, the toast, the skip link and the QR's share
button, keeps colour (browsers drop backgrounds unless told, and the
creature and QR are identity, not decoration), and refuses to split a
phrase across a page break — half a phrase is worthless.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaDanzXTm6kVSgbNh1eNrs
Both phrases come from fixed public wordlists, so a phrase containing a
word that is not in them cannot possibly be right — and checking that
costs microseconds where the Argon2id derivation it would otherwise
reach costs seconds. The edit-phrase path had no check at all: one
mistyped letter bought a multi-second wait and "no profile answers to
that phrase", which is indistinguishable from a profile that was
deleted. It now says which word is wrong and, when exactly one word is
an edit away, what it probably should be.

Two plausible corrections is not a suggestion, it is a guess, so the
suggester returns nothing when the neighbour is ambiguous. The check
runs on the words that exist even when the count is wrong, so a phrase
that is both short and misspelled says both rather than dealing out
complaints one at a time. It also accepts a pasted link, because the
extractor does and a diagnostic that rejects what the real parser takes
would send people hunting the wrong problem.

Messages land inline beside the field they fix, not in a toast that
expires while you are still retyping. The EFF list stays a lazy chunk
(verified: 77.72 kB, still separate) — it loads when a phrase is
submitted, not on landing-page paint.

Also fixes the view placeholder, which showed
`amber-azure-fox-canal-stove-plume`: those tail words predate the
compound tail grammar, so the example phrase was one the app would now
reject.

The e2e asserts the whole point — a typo'd edit phrase names word 1 and
comes back in well under the time a derivation takes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaDanzXTm6kVSgbNh1eNrs
The plan is the working document the next session reads first; leaving
it silent about what already exists would send someone to rebuild the
demo page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaDanzXTm6kVSgbNh1eNrs
@Randolin
Randolin merged commit 4ea8be6 into main Aug 28, 2026
2 checks passed
@Randolin
Randolin deleted the claude/product-strategy-positioning-u1vwig branch August 28, 2026 07:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants