Wave 2 — the front door: a demo comparison, a backup card, and phrase entry that forgives - #25
Merged
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
/demorenders 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 inapp.config.tsappears on both/compareand/demo, or neither.B1 · The backup card
/backupprints 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, ande2e.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
mainbefore wave 1; 528.10 kB here). The demo route and the EFF wordlist are both lazy chunks.Generated by Claude Code