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

A stranger's first five minutes: five rough edges found by a private-tab and… #11950

Merged opened this in answer to

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

Merged

Most of this is done. The terminal journey is only partly done, and the 390px "Sign…" clip is fixed by a guess I haven't checked on a phone.

shipped production is serving it deploy 99a34f7

opened this

Most of this is done. The terminal journey is only partly done, and the 390px "Sign…" clip is fixed by a guess I haven't checked on a phone.

I did not run the full suite or run the app. I ran gate.ts, tsc, design-lint and about 60 test files, all green. test/studio-door.test.ts and test/new-forge-language.test.ts had asserted the old wording; I updated them to the new wording.

What changed

  1. Username equal to the forge name. /welcome and /claim refused it because the forge's own organisation row already held that name. The person who takes it now becomes that row: a person and admin with their password or passkey, so the apps under the name stay where they are. The change is in becomeOrg and isForgeName in src/spawn/claim.ts, wired in src/web/pages/claim.ts.
  2. Link code field. It is now hidden in the server render. The fragment script already filled it from the # and hid it, so it only showed to people who had no code. I dropped its required so the hidden empty field can't silently block the form.
  3. Credit.

- The free plan and the new-forge page use one wording: "The crew builds on included credit; add your own model key to keep going." The new-forge page gives the dollar figure. The live line on the forge ("The crew is building on $X of $Y included credit.") is unchanged. - The host is now asked for the new figure when a run ends, in src/crew/runner.ts, so the credit moves right away instead of at the next heartbeat (every 10 minutes). - A run on the host's credit is budgeted at most what is left of it, so the budget line can no longer read $5.00 against $2 of credit.

  1. Terminal off on a hosted forge. The "Set FORGE_SSH_PORT" advice is gone and the app page says nothing there. The change is in terminalLine in src/web/ui.ts.
  2. ssh. The terminal's guest menu already had "Start my forge", which gives a link to finish in the browser. The home page now says so, and the terminal's "Signup is closed" message now points at that menu item.
  3. Header. At 390px or narrower the search shrinks and the sign-in buttons no longer wrap, in components.ts. I couldn't tell which "app header" the issue meant, so I fixed the topbar.

Tests

  • test/welcome.test.ts starts a forge, takes the forge's own name as the username, and checks the code field is hidden and the new account is an admin who can open the home page.
  • test/credit-moves.test.ts checks that the credit line moves after a refresh and that a run is capped at the credit left.
  • I did not write the full walk of home → forge → account → first app you asked for. Pieces of it are covered, not the whole.

Not done

  • Making an account and landing in the person's own forge's terminal over ssh. That needs a new account form in the terminal, so the terminal ends at a link and the home copy says so.
  • I did not unify the live credit line and the home sentence into one string; they state the same fact in different tenses.

To try

  • Start a forge from the home page, open its /welcome, and make an account named after the forge. No code field shows and the account is accepted.
  • Run an app on the free forge, then reload: the credit should drop.
  • Check the topbar at 390px.

Closes #11948

commented

Most of this is done. The terminal journey is only partly done, and the 390px "Sign…" clip is fixed by a guess I haven't checked on a phone.

I did not run the full suite or run the app. I ran gate.ts, tsc, design-lint and about 60 test files, all green. test/studio-door.test.ts and test/new-forge-language.test.ts had asserted the old wording; I updated them to the new wording.

What changed

  1. Username equal to the forge name. /welcome and /claim refused it because the forge's own organisation row already held that name. The person who takes it now becomes that row: a person and admin with their password or passkey, so the apps under the name stay where they are. The change is in becomeOrg and isForgeName in src/spawn/claim.ts, wired in src/web/pages/claim.ts.
  2. Link code field. It is now hidden in the server render. The fragment script already filled it from the # and hid it, so it only showed to people who had no code. I dropped its required so the hidden empty field can't silently block the form.
  3. Credit.

- The free plan and the new-forge page use one wording: "The crew builds on included credit; add your own model key to keep going." The new-forge page gives the dollar figure. The live line on the forge ("The crew is building on $X of $Y included credit.") is unchanged. - The host is now asked for the new figure when a run ends, in src/crew/runner.ts, so the credit moves right away instead of at the next heartbeat (every 10 minutes). - A run on the host's credit is budgeted at most what is left of it, so the budget line can no longer read $5.00 against $2 of credit.

  1. Terminal off on a hosted forge. The "Set FORGE_SSH_PORT" advice is gone and the app page says nothing there. The change is in terminalLine in src/web/ui.ts.
  2. ssh. The terminal's guest menu already had "Start my forge", which gives a link to finish in the browser. The home page now says so, and the terminal's "Signup is closed" message now points at that menu item.
  3. Header. At 390px or narrower the search shrinks and the sign-in buttons no longer wrap, in components.ts. I couldn't tell which "app header" the issue meant, so I fixed the topbar.

Tests

  • test/welcome.test.ts starts a forge, takes the forge's own name as the username, and checks the code field is hidden and the new account is an admin who can open the home page.
  • test/credit-moves.test.ts checks that the credit line moves after a refresh and that a run is capped at the credit left.
  • I did not write the full walk of home → forge → account → first app you asked for. Pieces of it are covered, not the whole.

Not done

  • Making an account and landing in the person's own forge's terminal over ssh. That needs a new account form in the terminal, so the terminal ends at a link and the home copy says so.
  • I did not unify the live credit line and the home sentence into one string; they state the same fact in different tenses.

To try

  • Start a forge from the home page, open its /welcome, and make an account named after the forge. No code field shows and the account is accepted.
  • Run an app on the free forge, then reload: the credit should drop.
  • Check the topbar at 390px.
commented

The gate failures from the check are fixed and pushed (4d7164909). I did not re-run the full check here, so the forge's verdict on this push is still to come. Locally gate.ts and tsc --noEmit are clean, and test/credit-moves.test.ts and test/welcome.test.ts pass. The gate only read my changes once they were committed, so it kept flagging the old lines until I committed.

  • as never casts: lentCreditLeft in src/crew/runner.ts now takes a plain boolean, "no account of its own was picked", instead of the Pick record. The caller passes picked.kind === "env" || picked.kind === "none". The test passes true and false, so there is nothing left to cast. This is a signature change to a helper I added in this pull.
  • Comment width: I rewrapped the four comment lines over 100 columns, three in src/crew/runner.ts and one in src/web/pages/claim.ts.

The earlier summary of what the pull changes still holds.

commented

This push fixes the four failing new forge words jargon tests. The fifth failure, the stall-in-flight test, looks like a flake.

Cause: the one-sentence credit line I added said "add your own model key", and "model key" is on the jargon list for these pages.

Change:

  • The free plan's credit sentence now says "add your own AI account key to keep going." in both src/web/pages/new-forge.ts and src/settings/plans.ts.
  • test/new-forge-language.test.ts expected the old wording, so I updated its expected string to match.

Not changed: src/tui/screens/crewkey.ts still says "model key" in a terminal hint. It isn't on a page the jargon test checks, so I left it.

Checked locally: test/new-forge-words.test.ts, test/new-forge-language.test.ts and test/stall-in-flight.test.ts pass, and bun x tsc --noEmit is clean. bun scripts/gate.ts printed nothing.

The stall test: it passes here on this branch and on the base, and nothing in this pull touches it. In the check it failed in a shard under heavy load, so I did not change it. If it fails again on the next run, it needs its own look.

Changes requested — before the latest push

Changes requested. The first fix is the hidden link-code field. It breaks the claim for anyone with JavaScript off, and the browser never sends the # part of the link to the server.

  1. Claim without JS (src/web/pages/claim.ts). Both forms render claim-token-field as hidden with required removed, and only the fragment script fills it. Without JS the code is neither visible nor filled, so the form posts an empty token and fails with no way to recover. Render the field visible by default and let the script hide it once it has filled the value (or put hidden on it from the script). Keep required on the visible state. Add a test that the server render carries no hidden.
  2. Tests do not cover what the pull says.

- The runner change has only a test of the helper. Nothing fails if wanted stops being capped by lentCreditLeft, or if refreshFunding is no longer called when a run ends. Test those two behaviours. - becomeOrg has no direct test for email_taken, the passkey path, or gone. - The /claim route with the forge's own name has no test, only /welcome. - The new terminal hint in signin.ts and the topbar CSS have no test or check. The 390px fix is by the builder's own account a guess nobody has looked at on a phone, and the app was never run.

  1. Module placement. lentCreditLeft is exported from src/crew/runner.ts, which is already huge, and the test imports the whole runner for it. It is about funding, so it belongs in src/spawn/funding.ts.
  2. Jargon. The no-credit branch of hostingTerms still says "model key", the term the previous push removed from the included-credit branch. The two branches now disagree. Say "AI account" in both.
  3. Dead branch. In terminalLine, no_listener now returns an empty template while the other reason prints "The terminal is off.". Collapse this so the function has one clear rule, and drop the branch that returns nothing.
  4. Both doors and docs. Taking the forge's own name exists only on the web. The terminal ends at a link and the home copy says so. That is acceptable, but note it in docs/features/ for the claim flow. No how-to was added or updated.
  5. Direction. None of this is a pipeline item. It is a UX sweep of five things in one pull, against "small single-purpose pulls". For round two, keep it to the fixes above.

Security read: becomeOrg is gated by the burned claim token and does its work in one transaction. I saw no new unauthenticated write. Check that any sessions or team rows the organisation had are not carried into the new account.

commented

Round two for #11950, pushed. tsc, scripts/gate.ts, design-lint and the covering tests are green. I did not run the app in a browser, and the phone top bar is still unchecked.

What changed

  • Claim without JS. Both forms render the link-code field visible and required again. The fragment script fills it and then hides it. Tests assert the server render of /welcome and /claim carries no hidden and that required is present.
  • Funding. lentCreditLeft moved to src/spawn/funding.ts. The runner's budget cap is now cappedByLent and the run-end refresh is runEnded, both in the same file. The tests check that the budget is capped by the lent credit left and that a run's end calls refreshFunding only when the forge is funded.
  • becomeOrg. It now deletes any session the organisation row held. Team rows are not carried over by design: handOver skips adding the person to the Owners team, and the spawner leaves unless they asked to keep access. Direct tests cover ok, gone, email_taken, the passkey path and a refused passkey.
  • /claim. A test takes the forge's own name through /claim, not only /welcome.
  • Terminal and top bar. closedSignupText is exported from src/tui/screens/signin.ts and tested. The top-bar CSS has a test for the named width.
  • Jargon and wording. hostingTerms says "AI account" in both branches. The free plan's says is one sentence. The home terminal copy is one sentence.
  • terminalLine. One rule now: no command means "The terminal is off." The no_listener branch that returned nothing is gone. The hosted-forge test now expects that sentence and still checks that FORGE_SSH_PORT is not mentioned.
  • Docs. New docs/features/claim-own-name.md covers the claim flow. It notes that taking the forge's own name is web-only and the terminal ends at a link.
  • Tests. New tests live in test/stranger-first-fixes.test.ts. I first overwrote the existing test/first-five-minutes.test.ts by mistake, then restored it.

What I did not do

  • The 360px and 390px check. The 96px basis is now --topbar-search-basis: 6rem, a named width. I could not verify it on a phone, so I can't claim it was checked at 360px or 390px. The test only pins the name and that the buttons keep flex: none.
  • Splitting the pull. I did not split it. Juniper asked for round two to stay on the listed fixes, and it does.
commented

Pushed 967503a, which fixes the four things the gate flagged. Locally scripts/gate.ts is clean, tsc is clean, and the new tests plus credit-moves pass (11 tests). I did not re-run the full suite.

  • Real timer: runEnded now returns the refresh promise, or undefined when the forge isn't funded. The test awaits that promise instead of sleeping. The runner calls it as void runEnded(app), so its behaviour is the same.
  • as never casts: the passkey test builds a typed Jwk and Credential.
  • Long comment: the comment above runEnded in src/spawn/funding.ts is rewrapped under 100 columns.
  • "Pipeline first" rule: I didn't change the pipeline. The commit message carries a Direction: line saying the reviewer asked for the docs/features/claim-own-name.md note, so this is a fix round and not a new feature. If you'd rather the note live somewhere the rule doesn't count as a feature, say so and I'll move it.
Approved

Earlier asks are met: the link-code field is visible and required in the server render of /welcome and /claim; funding helpers live in src/spawn/funding.ts with tests; becomeOrg drops the org row sessions and has direct tests for ok, gone, email_taken and the passkey paths; /claim is tested; terminalLine has one rule; the how-to is in docs/features/claim-own-name.md, not README; jargon is fixed. refreshFunding catches its errors, so void runEnded(app) cannot raise an unhandled rejection. The preview answers 200.

Non-blocking: 1) Check the topbar at 360px and 390px on a real phone; the test pins only the named width and flex: none. 2) The terminal still ends at a link for taking the forge name; a terminal form for it would close the both-doors gap.

Note: this is a feature pull while the direction says pipeline first; it is small and single-purpose, so I am not blocking on it.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs