Skip to content

The onboarding is one screen with two doors, and three bridges the server drives - #462

Draft
serenakeyitan wants to merge 11 commits into
mainfrom
feat/onboarding-journey
Draft

The onboarding is one screen with two doors, and three bridges the server drives#462
serenakeyitan wants to merge 11 commits into
mainfrom
feat/onboarding-journey

Conversation

@serenakeyitan

Copy link
Copy Markdown
Collaborator

Draft — not for merging yet. Design: https://tdoc.dev/d/tdoc-onboarding-journey/v/4. The owner tests it by hand first, and a no-context agent walks the flow and grades it; this merges only when both are satisfied.

What it does

The landing page and its CTA are untouched. Create a free doc now opens one screen with two doors:

  • Use my own agent — "agent" carries its definition on hover (Claude Code · Codex · Claude Cowork · ChatGPT Work). Signs the person in, returns them to the door (?onboard=own), states the one concept once ("Two windows: you read and comment here. Your agent writes and fixes."), hands over the line, and waits.
  • Use tdoc's agent — coming soon — records a waitlist, sends them to the example.
  • See an example beside both.

Three bridges, no daemon

The waiting is read off the server. Every step the agent takes is already a request:

the page shows the server learned it from
agent connected issueHostedTokenagent_connected
first doc landed → page leaves for it /api/upload first hosted publish → published_first + first_doc
agent is reading this GET /api/comments with a Bearer, or version=all (the shape only tdoc-pull asks for) → doc-agent-read:<slug>
replied ✓ the existing agent-reply, brought in by the 3 s refresh
v2 → page moves /api/upload v≥2 → revised; latest_version on /api/doc/agent-status

Bridge 2 is on the owner's comment card: the one instruction in the journey — Read my tdoc comments and fix them + Copy — then Waiting for your agent…Your agent is reading this → ✓ → the new version. Past five minutes: Still waiting — did you paste it into your agent?

The first doc of a journey that came through the door arrives with one comment already on it, signed tdoc (not a person): "First reader here. Which claim on this page would you defend least? Highlight it and say so." A CLI-first publisher who never opened the door gets nothing.

Record

account-onboarding:<account_id>started · agent_connected · published_first · seeded_comment · commented · comments_read · revised · tagged · shared · waitlist · tour_seen, all timestamps, first writer wins. onboarding-event:<id>:<ts> — one row per action the page saw (door_own_agent, waitlist, example_opened, copy_clicked, fix_copy_clicked, timeout_shown, share_link_copied, tour_seen); the funnel is derived from these. Routes: GET /api/onboarding, POST /api/onboarding/event (unknown action → 400; only waitlist and example_opened are open to a visitor with no account), GET /api/doc/agent-status.

Resuming reads the record: a started, unfinished journey reopens its door on the next landing visit. Nothing in localStorage.

Also

  • A visitor with no session sees Sign in to comment in the mode control instead of a picker with Read alone (cfg.signInToComment).
  • On a revised doc the owner gets the exit line — Now get a real one. Tag someone and send them the link. — with Copy link, once, until they copy it.
  • Local server twin: same routes and stamps; discovers the first doc on read (it cannot see tdoc-new write one); pure helpers pinned byte-identical by no-drift.

Tests

test/onboarding-journey.test.js (11) — the pure helpers run, not grepped; the routes, stamps, doors, bridges, exit and resume pinned. Four static tests that pinned the old dialog's shape now pin this one. Offline suite 78/78. Shell rebuilt; server/runtime committed.

🤖 Generated with Claude Code

…rver drives

Design: tdoc.dev/d/tdoc-onboarding-journey/v/4. The landing page and its CTA
are untouched; the CTA now opens "Create a free doc" — two doors instead of
a recipe. "Use my own agent" (the word carries its definition on hover:
Claude Code · Codex · Claude Cowork · ChatGPT Work) signs the person in, hands
over the one line, and waits. "Use tdoc's agent — coming soon" records a
waitlist and sends them to the example. "See an example" sits beside both.

The waiting is read off the server, not guessed. Every step the agent takes
is already a request: minting a token stamps agent_connected, the first
publish stamps published_first (and seeds one comment, signed tdoc, for a
journey that came through the door), a second version stamps revised, and
tdoc-pull's GET /api/comments — Bearer, or the version=all shape only the CLI
asks for — marks the doc read. The landing page's door polls the record and
leaves for the first doc on its own; the owner's comment card carries the one
instruction in the journey ("Read my tdoc comments and fix them" + Copy) and
flips waiting → reading → ✓ → new version as those requests land. Past five
minutes it asks the one question that resolves it. No daemon, no port, no
doctor state: the only failure mode is "nothing happened", and the fix is
always the same sentence.

A visitor with no session sees a Comment option that says what it will do —
Sign in to comment — instead of a mode picker with Read alone. On a revised
doc the owner gets the exit line, once, until they copy the link.

Every step is a timestamp on the account (account-onboarding:<id>), every
action the page saw is one event row, and resuming reads the first empty
step: a started, unfinished journey reopens its door on the next visit.
The pure helpers are shared with server.js and pinned by no-drift; the
local twin discovers the first doc on read, since it cannot see tdoc-new
write one.

Four static tests pinned the old dialog's shape (its details block, its
hosted-token probe, its identity-less mount); they now pin this one.

Refs tdoc.dev/d/tdoc-onboarding-journey/v/4

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread server/server.js Fixed
Comment thread server/server.js Fixed
Comment thread worker/worker.js Fixed
Comment thread worker/worker.js Fixed
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

Preview

Open this: https://pr-462-tdoc-preview.jyshi1107.workers.dev/ — the homepage, this PR's code
carrying the landing version in this PR's checkout.
Or a document: https://pr-462-tdoc-preview.jyshi1107.workers.dev/d/conway-life/v/2

This link is unique to this PR. New commits update the same URL. It is not tdoc.dev.

Preview has no Durable Object — concurrent comments use the KV fallback. Data expires in 14 days.

serenakeyitan and others added 10 commits September 4, 2026 10:30
… it inline

Positioned from the word, the hover tip ran rightwards across the gap and
sat on top of the other door, where it read as that door's description. It
is anchored to the door now — left and right edges inside the card, under
the title — so it can only ever describe the door it belongs to.

A phone has no hover, so below 560px (and on any hover-less pointer) the
same sentence sits under the title as a plain line, and the dotted
underline that promised a hover goes with it. Measured at 375px: nothing
overflows the viewport.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ough v2

Two testers walked the onboarding as strangers. The third came in through
/me and got the bare recipe line: no wait, no arrival, no seeded comment,
no exit, because the Docs Hub's "Build it with your agent" card rendered
FirstDocRecipe on its own. It now opens OwnAgentDoor — one door, two
entrances — with the definition of "agent" under the card title.

The rest is what the two rounds found on the way:
- a refused clipboard leaves the line selected and says so, on the door
  and on the fix line, and the wait starts anyway
- the wait says how long it usually takes, nudges at 90s, asks at 5 min
- arrival opens the seeded card with a toast; v2 arrival opens the
  person's own answered thread and shows resolved threads
- a reply on the seeded card counts as the first gesture; the fix line
  shows only on the latest version, only after it
- the agent-status carries replied_at, so the card says "Replied ✓"
  before v2 lands
- the exit banner sits in the flow, stays as the confirmation after the
  link is copied, and does not close the open card
- the landing shows "Your doc →"; an existing doc opens from the door
  instead of a silent jump; the local server shows the old-version strip

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Four things the round-4 stranger left, decided with the owner:

- "v1 · current" read as "latest" from an old version. The menu now says
  "viewing" for where you are and "latest" for the newest, separately.
- A person with a doc was handed "make my first doc" again. The door's
  second line is the ordinary way to make one, with a blank for what it
  is about; FIRST-DOC.md would only have built the same portrait twice.
- A triple-click on a sentence beside an open card only closed the card,
  and the sentence had to be selected again. A multi-click selection now
  closes the card and is the next comment; a single click stays a
  dismissal, so `user-select: all` blocks do not open composers.
- The wait had a pulsing dot a screenshot cannot show. A sweep runs under
  the status while the page is listening; reduced motion gets a still bar.

And the card no longer claims "Replied ✓" for a thread the agent has not
answered — the status is the doc's, so it says the agent is replying and
the next version is on its way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The agent answers thread by thread and publishes seconds later. A
doc-level stamp taken at the first reply lit "Replied ✓" on a thread the
agent had not answered yet (round-4 tester), and the true version of the
sentence said nothing the next state did not say better. So the card goes
waiting → reading → the next version arrives. A reply that comes without
a version, a question, shows up in the thread on the next refresh.

The replied_at field leaves agent-status on the worker and the local
server with it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s the bridge

The two doors and the scattered bridge — a card line here, a banner there,
a toast that was gone before the person looked — become one window that
stays on the landing page and moves on its own. It asks the server every
few seconds what the person's agent has done and steps forward: welcome,
sign in, connect, your doc, send back, done. The doc opens in another
tab; this window is the guide. Every step is read off the account's
onboarding record, so a reload or a second device resumes in place.

- Welcome draws the loop instead of describing it: the agent's terminal,
  the doc in a browser, the doc going one way and the comments back.
- Sign-in runs in a small window (a POC of the cheap route: the same
  OIDC redirect, in a 480×640 pop-up that lands on /auth/done?popup=1,
  posts `tdoc:signed-in` to its opener and closes). A blocked pop-up
  falls back to the full-page redirect. Not yet exercised against the
  live provider.
- The code a terminal shows is typed in this window, through the same
  lookup and approve routes /activate uses, with the terminal named
  before it is bound.
- The hub's "Build it with your agent" card opens the same wizard at the
  connect step. The old door CSS goes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e blue

The wizard's buttons were painting white on white: chrome.css's
`.tdoc-modal button` outranked the wizard's own rules, so every primary
was a blank box with a grey border (the owner's screenshots). Every
wizard rule now carries the element, and outranks it.

The rest is the owner's cut: sign-in is the site's own (the page leaves
and returns to the paste step) — the small-window POC goes; the pairing
code gets a step of its own instead of a field under the line; the doc
step is the comment ("Now comment on it." and one button); the Advanced
fold, the notes, the rows on the doc step and "Make another" all go;
every step can be skipped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The subtitles, the explanations and the long labels go. What is left on
each screen is the one thing to do, and a status line while the page
waits: "Paste this into your agent." — the line — "Copy". The doc step
says what the gesture is, not that there is one: "Highlight a sentence.
Say what you think."

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CodeQL on #462: one pass over `<[^>]+>` can leave a tag behind, and
decoding `&amp;` before `&lt;` double-unescapes. The text is an anchor,
never markup, but the order is what makes that true. Same body on
both hosts, as no-drift requires.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The send-back step inherited the paste step's copy and showed its three
rows before Copy was pressed (seen on the local-worker walk-through).
The copy state resets on every step change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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