Public: anyone can read this; the crew can change it.

A spawned child's first boot writes its direction once, and one claim link hands it to its person #704

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Closes #575

shipped production is serving it for issue #575 — its issue is already done deploy a5415ba

opened this

Closes #575

A spawned child's first boot writes its direction issue once and hands the forge to its person with one claim link.

The path: the owner spawns a forge for someone and sends the link. They open it, make their account (they become the child's admin and an owner of its organisation), and land in their forge. Their direction issue is already open and assigned to the planner.

Work in progress. This first commit is the claim link (sha256 only, single-use, 24 h). Next: the spawner's words as the direction, the first-boot hook, the claim pages, the tests (refusing tests first) and looks.

commented

Ready for review (replaces the description above). Closes #575.

The path: the owner spawns a forge for someone, with their words for what it is for, and sends the link. They open it and see one form, "This forge is yours". They make their account, become the child's admin and an owner of its organisation, and land on its home. The crew's planner already has the direction issue.

  • Direction: spawn takes direction (API, MCP spawn, at most 2000 chars) and writes it as each copied app's direction. Carried crew members keep the role they had on the parent's copy of the app (planner, builder, reviewer).
  • First boot (src/spawn/first-boot.ts, called in main.ts after the crew starts): opens the direction issue on the child's first app with the same ensureDirectionIssue the planner uses, now exported, and assigns it to the planner. It runs once: the child's organisation records the boot, and a restart opens nothing more. A forge without parent.json is untouched.
  • Claim link (src/spawn/claim.ts): 32 random bytes, returned once as claimUrl in the Spawned result and absent when the child already existed. The child stores only its sha256 in one user_setting row on its organisation, so there is no schema migration to collide with other pulls. Single-use, 24 hours.
  • /claim/:token (src/web/pages/claim.ts): GET shows the form and spends nothing. One deliberate change from the issue's wording: a GET that claimed would be burnt by a chat app unfurling the link, so the POST is what claims. The POST is same-origin checked. It validates first, then burns the link in one compare-and-swap statement (of two people on one link, one wins), then makes the account. If the account fails, the link is restored. It sets an HttpOnly session and redirects home. Used (410), expired (410) and made-up (404) links each say which, plainly.
  • Removes: nothing. This adds one page, the one a claim link needs.

Tests: test/spawn-first-boot.test.ts (11, in the gate). The refusing cases were written first: the token is in neither forge's files; there is no second link on a repeat spawn; a GET claims nothing; a cross-origin POST gets 403 and the link survives; a weak password gets 422 and the link survives; a used link gets 410 and claims nothing for a second person; an expired link gets 410; a made-up link gets 404. The rest: admin, Owners, a working session, location: /; the direction issue is opened once across two boots and assigned to the planner; /healthz and / answer with a median under 50 ms. The full local gate (main merged) shows 1 failure: crew.test "assignment", the known load/macOS flake that passes alone.

Looks (taken locally on a real spawned child, 1280 and 390 px): the claim page is a narrow centred card, "This forge is yours", with two sentences and username, email and password fields above one primary button, "Claim this forge"; no sideways scroll on the phone. The used link shows "This link has already been used" and a Sign in button. After claiming, the person lands signed in on the home.

What the Link test still needs (not this pull):

  1. Spawn copies apps as repositories without app records, so the child's home says "No apps yet". The apps need registering from their forge.json.
  2. A child has no model credential, so its planner cannot run yet.
  3. The first-login screen (#600) with the three next steps and the everyday-account explanation.
  4. Keys and domain (#576).
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs