Launch QA: six first-time-user personas before launch #247

Open
opened 2026-10-19 11:01:53 +00:00 by kayg · 49 comments
Owner

Owner, 2026-10-19: "please save this as QA testing before launch", linking https://x.com/roundtablespace/status/2111717667228770611 .

The idea (from the post)

Before launch, run five AI subagents as first-time users. Each opens the product in a real browser, goes from the first page to a first success, and screenshots every moment of confusion, being stuck or annoyance. Merge the findings into one list ranked by how many users hit each problem, fix them in one pass, then rerun all five to prove the fixes.

Our version for infraternal

Every persona starts from nothing: a fresh server install (/install, D258) and a first browser visit. The goal is the first real success: sign in, add a host, open a terminal and type a command.

  1. The impatient one: skims, clicks the first button, quits at the first error. Example: skips reading the add-host form and pastes only an IP address.
  2. The skeptic: reads the security, privacy and licence material (AGPL, what is stored, where the keys are) before trusting the product with a server. Example: looks for what the infraternal agent can do as root, and whether a password is ever stored.
  3. The phone user: does everything on an iPhone-sized screen (375–393 px) over a slow, lossy link at India ↔ Germany latency (~150 ms). Example: adds a host and types in a terminal from the PWA.
  4. The non-native English speaker: follows only what the UI literally says. Example: gets stuck if a button says "Adopt" where it means "Replace".
  5. The breaker: pastes junk into forms, presses Back mid-flow, double-clicks everything, closes tabs during setup. Example: double-clicks "Add host" and reloads during install.
  6. (From a reply to the post.) The one who never reads instructions: ignores every hint, tooltip and empty-state text.

Rules for the run

  • Real browsers (Playwright with Chromium and WebKit, plus a real iPhone where possible), against a production build on a staging server, never against production data.
  • Screenshots and short recordings are attached to this issue with issue-attach. The orchestrator looks at them; the personas do not judge visuals themselves (CLAUDE.md).
  • One merged list ranked by how many personas hit each problem, each with a screenshot and the exact step.
  • Fix in one pass (Codex for backend, Sonnet for UI), then rerun all six personas to prove the fixes.
  • Every annoyance found goes into docs/design/TASTE.md the same day.

When: before any public launch, and again before each major release. Not started yet; this issue records the plan.

Owner, 2026-10-19: "please save this as QA testing before launch", linking https://x.com/roundtablespace/status/2111717667228770611 . ## The idea (from the post) Before launch, run five AI subagents as first-time users. Each opens the product in a real browser, goes from the first page to a first success, and screenshots every moment of confusion, being stuck or annoyance. Merge the findings into one list ranked by how many users hit each problem, fix them in one pass, then rerun all five to prove the fixes. ## Our version for infraternal Every persona starts from nothing: a fresh server install (`/install`, D258) and a first browser visit. The goal is the first real success: sign in, add a host, open a terminal and type a command. 1. **The impatient one:** skims, clicks the first button, quits at the first error. *Example:* skips reading the add-host form and pastes only an IP address. 2. **The skeptic:** reads the security, privacy and licence material (AGPL, what is stored, where the keys are) before trusting the product with a server. *Example:* looks for what the infraternal agent can do as root, and whether a password is ever stored. 3. **The phone user:** does everything on an iPhone-sized screen (375–393 px) over a slow, lossy link at India ↔ Germany latency (~150 ms). *Example:* adds a host and types in a terminal from the PWA. 4. **The non-native English speaker:** follows only what the UI literally says. *Example:* gets stuck if a button says "Adopt" where it means "Replace". 5. **The breaker:** pastes junk into forms, presses Back mid-flow, double-clicks everything, closes tabs during setup. *Example:* double-clicks "Add host" and reloads during install. 6. (From a reply to the post.) **The one who never reads instructions:** ignores every hint, tooltip and empty-state text. ## Rules for the run - Real browsers (Playwright with Chromium and WebKit, plus a real iPhone where possible), against a production build on a staging server, never against production data. - Screenshots and short recordings are attached to this issue with issue-attach. The orchestrator looks at them; the personas do not judge visuals themselves (CLAUDE.md). - One merged list ranked by how many personas hit each problem, each with a screenshot and the exact step. - Fix in one pass (Codex for backend, Sonnet for UI), then rerun all six personas to prove the fixes. - Every annoyance found goes into docs/design/TASTE.md the same day. When: before any public launch, and again before each major release. Not started yet; this issue records the plan.
Author
Owner

Launch QA plan: six first-time-user personas (executable)

Written 2026-10-21 against origin/dev @ aba6d85ce (release 9.2 merged into dev). File:line references are to that commit. This is a plan only. Nothing has run yet.

0. Ground rules for every run

  • Build under test: the production release artifact (static binary, scripts/auth-toolchain + cargo, D213), with the web app it ships. Never a dev server. Record the version string and SHA in each report.
  • Server venue: a fresh infraternal-test-qa-server VM that the orchestrator creates on minipc01 (Proxmox; only our own infraternal-test-* guests). It gets a real DNS name and TLS, because passkeys need a secure origin with a domain (see prediction 1). Reset it to a clean snapshot before each persona so every persona really starts from nothing (no owner, no CA, no hosts).
  • Hosts to add: two or three fresh infraternal-test-qa-host-* guests on minipc01: Debian (sudo user), Alpine (root login only), plus one host with password-only SSH. No other hosts are touched.
  • Never used: infraternal-dev 10.69.69.125, monitor01 prod data, and any VM on minipc01/ash1/ash2 that is not our own infraternal-test-* guest. Known_hosts entries for the test guests are cleaned after the run.
  • Browser venues:
    • Playwright Chromium and WebKit run on the perf VM 10.69.69.208 (ours, with flock). Network shaping uses tc netem there: 150 ms RTT, plus 2% loss for persona 3.
    • The real iPhone (Safari, then installed as a PWA from the home screen) and Chromium on the Mac VM (for glass/blur checks) take the MacBook/Mac VM lock. Capture only through our signed helper, never over netbird ssh.
  • Driving: each persona is one AI-agent job with a persona prompt (section 1) and the step script (section 2). It acts only through the browser and a terminal on the server VM, as a real newcomer would. It may read only what a newcomer can see: the product, --help, and the /install page. It must not read the repo.
  • Personas do not judge visuals (CLAUDE.md). They record what they did, what they expected and what happened. The orchestrator reviews the screenshots.

1. The six personas

# Persona Device / browser Goal Quits when
P1 Impatient: ops engineer, skims and clicks the first button Desktop Chromium 1440x900, no throttling Terminal on a host in under 5 min the first error has no obvious next button; any wait over 10 s without visible progress; must read a doc
P2 Skeptic: security lead Desktop WebKit (Safari engine) 1280x800 Trust it with one sudo host cannot find what is stored, where keys live, what root can do, or the licence (AGPL); a password is kept after use; vague wording about secrets
P3 Phone user: on-call SRE in India Real iPhone Safari, then PWA from the home screen, plus Playwright WebKit 393x852 with 150 ms RTT and 2% loss Add a host and type uptime from the PWA a control is hidden under the keyboard or notch; a tap target is under 44 px; typing lags (more than RTT+50 ms, D259); the passkey sheet fails inside the PWA
P4 Non-native English speaker: reads the UI word for word Desktop Chromium, 1366x768, OS language set to German (the app stays in English) Same as P1 a label means something other than what it says (Adopt/Replace, "join", "authority", "locked"); jargon with no explanation
P5 Breaker: QA engineer Desktop Chromium plus a second tab Get the flow into a broken or duplicated state (does not quit) records every state that cannot be recovered without a server shell
P6 Never reads: ignores hints, tooltips and empty-state text Desktop WebKit 1280x800 Same as P1 stuck for 60 s with no clickable next step

2. Fresh-install path (the same steps for all; each persona's twist is in brackets)

Each step is timed from its first input to its result. Pass means it was done without outside help, inside the budget, with no unexplained error. Fail means stuck for more than the persona's quit trigger, an error with no next step, or a state that needs a server shell or a DB reset.

Step What the persona does Pass criteria
S0 Find install Opens the server VM shell with only a download URL for the release binary Finds from infraternal --help that infraternal server init is the command (docs/CLI.md:56). ≤2 min
S1 Server init infraternal server init --dry-run, then for real (needs a domain: --public-origin https://qa.<domain>) The plan is understandable. The service runs. /healthz returns OK. The persona knows which URL to open. ≤3 min [P1 skips --dry-run; P2 reads the dry-run plan line by line and records what runs as root; P5 runs init twice and presses Ctrl-C in the middle]
S2 Find the token Gets the setup token: the journal banner (crates/infraternal-server/src/auth/setup.rs:16) or infraternal server setup-token Token found ≤1 min [P6 must find it with no hint]
S3 Wizard 1/4 token Opens the URL and pastes the token (apps/tracer/src/onboarding.ts:12,175) Accepted. A wrong or expired token gives a next step. [P5 pastes junk and a token with spaces, then uses Back]
S4 Wizard 2/4 name Username and display name (onboarding.ts:13,230-244) Valid on the first try or a clear hint [P5: 300-character display name, emoji, uppercase]
S5 Wizard 3/4 passkey Creates the passkey (Face ID, Touch ID or a virtual authenticator in Playwright) Registered. A cancel gives a retry message (onboarding.ts:451). [P3 on iPhone Safari AND inside the PWA]
S6 Wizard 4/4 recovery key Saves the 24 words, ticks saved, then "Open infraternal" (onboarding.ts:15,91,139) The persona actually stored the words (the orchestrator checks the persona's notes). ≤1 min. [P5 reloads here, then closes the tab, then comes back]
S7 Empty canvas Sees "No hosts yet" and the "Add host" pill (apps/tracer/src/canvas/views/world.ts:366-371) Opens add-host ≤15 s
S8 Target Types the host (canvas/addhost/wizard.ts:319-320) Resolves to a host chip [P1 pastes only an IP; P4 types ssh debian@10.x; P5 pastes a URL and double-clicks Add]
S9 CA / authority First host: confirm with the passkey to create the certificate authority (wizard.ts:534) Persona can say in their own words what they just approved
S10 Sign-in method Server key, key or password (wizard.ts:833-846) Picks one that works. [P2 checks the "not stored" claim: after the step, search the server data dir and journal for the password or key bytes; must be absent]
S11 Host key / root Fingerprint check (wizard.ts:815-818), root consent (wizard.ts:488-489) Correct choice made; [P2 on the Alpine root-only host must understand root scope]
S12 Install stages Watches Connect → Upload → Install → Join → Start → Readiness (wizard.ts:43-46) Done. Each stage has visible progress within 1 s. Target ≤60 s on the LAN, ≤90 s at 150 ms RTT. [P5 reloads and goes Back mid-install; closes the tab and reopens: no duplicate host, no setup that cannot be resumed]
S13 First success Terminal opens; types uptime, then ls -la / Output appears; keystroke echo ≤ RTT+50 ms (D259); [P3 types from the PWA with the key bar]
S14 Return Closes the browser, comes back the next day (simulated: a new context and the session cookie) Back in with one "Continue with passkey" tap (onboarding.ts:387-390); host still there
S15 Server restart Orchestrator restarts the service once Persona understands "Host connections are locked" and unlocks it (canvas/addhost/authority-unlock.ts:40-43) ≤1 min

3. What each persona records (per step)

  • A screenshot before and after every action, and on every surprise. On the iPhone, a short screen recording of S5, S12 and S13.
  • Timings: wall clock per step, time to first visible progress, and terminal key-to-echo (median and worst) for S13.
  • Confusion log: one line per moment, step | what I expected | what I saw | what I did next | seconds lost.
  • Errors: the exact text on screen, the browser console, failed network requests (HAR), and the server journal slice for that window.
  • Final result: reached S13 (yes/no), total time, and the step and reason where they would have quit.
  • Artifacts are attached to this issue with issue-attach (D262), named p<N>-s<step>-<n>.png. They are never committed.

4. Findings become fj issues

  1. The orchestrator merges the six confusion logs into one table: problem, how many personas hit it (1-6), worst severity, step, screenshot. Severity: P0 blocks first success; P1 loses more than 60 s or needs outside help; P2 an annoyance.
  2. Each row with ≥2 personas, or any P0/P1, gets its own fj issue on kayg/infraternal: title QA #247: <problem>, body with the step, the exact UI text, file:line, the screenshot, the persona count, and a link back here. Label launch-qa. Duplicates of open issues get a comment on that issue instead.
  3. Every annoyance goes into docs/design/TASTE.md the same day.
  4. Fix in one pass (Codex for backend, Sonnet for UI, each with a break-it review), then re-run all six from a fresh VM snapshot. This issue stays open until a rerun has no P0/P1 and all six reach S13.

5. Ten places predicted to confuse a newcomer (origin/dev @ aba6d85ce)

  1. The default public origin is an IP address, so passkeys fail. crates/infraternal/src/main.rs:1353-1359: without --public-origin, the origin is https://<listen IP>:<port>. WebAuthn does not accept an IP address as the RP ID, so S5 fails late, after the token and name steps. Init should require a domain name or warn about it.
  2. There is no server quick-start a newcomer can find. There is no root README. /download arrives "when apps ship" (D258). server init is documented only in docs/CLI.md:56-60. The legacy server install is hidden and only prints migration guidance (CLI.md:59), and that is the first command people guess.
  3. The setup token shows up in the journal. crates/infraternal-server/src/auth/setup.rs:16 prints it once at startup. The wizard tells people to run infraternal server setup-token (apps/tracer/src/onboarding.ts:177,217), but not as which user (root or the service account), and not that the token expires after 24 h.
  4. The display-name limit disagrees with the field. onboarding.ts:233 sets maxlength="256", but the hint at onboarding.ts:244 says 64 characters. P5 will type 65-256 characters and be blocked by a hint that appears only after typing.
  5. Step 2 asks for two names, and step 3 mentions SSH keys too early. "Who are you?" asks for a username and a display name (onboarding.ts:13,230-233). The username must be lowercase, 2-32 characters, and the hint appears only after a bad entry (:241-242). P4 will type "Ada Lovelace" into the username field. Step 3 says "Your SSH keys are sealed with it" (onboarding.ts:14) before the person has any SSH keys here. The recovery step is gated correctly: Continue needs the saved tick (onboarding.ts:91,322). Verify that P1 and P6 do not just tick it without saving the words.
  6. The add-host hint is hidden from screen readers and shown only sometimes. canvas/addhost/wizard.ts:315 sets aria-hidden="true" on "Type user@host, for example kayg@o2", and the example host o2 means nothing to a newcomer. The parser (canvas/addhost/model.ts:199-203) accepts a bare IP, but it rejects ssh user@ip, user@ip -p 2222, a pasted ssh:// URL and a trailing space with a comment. P4 and P5 will paste exactly these, so check that the rejection explains itself. For a bare IP (P1), check which user is tried and whether the screen says so.
  7. "Certificate authority" jargon on the first host. wizard.ts:534 asks for the passkey "to create the certificate authority for your hosts" in the middle of add-host. P4 and P6 will not know what they are approving. P2 will want to know where its key lives.
  8. "Host connections are locked" after every restart. canvas/addhost/authority-unlock.ts:40-43. Every server restart locks the CA. A newcomer whose VM rebooted sees hosts that cannot be reached and a wording that sounds like a security incident.
  9. The retry-limit dead end sends people to a hidden route. canvas/addhost/setup-stages.ts:140: "Use Remove host in the command palette, then Add host to start again." There is no button. P6 never opens the palette, and P5 will reach this limit on purpose.
  10. Internal words in the stage names. wizard.ts:43-46: "Get the x86_64 binary", "Join the host to this server", "Check terminal readiness". With "Your terminal keeps working" in failure text (setup-stages.ts:141, it means the other terminals), P4 will read these literally and not know whether they have to act. P3 will see them on a 393 px screen where long host labels may truncate the stage row (wizard.ts:681-694).

Also check (no prediction, just verify): passkey creation inside the installed iOS PWA; double-clicking "Add host" across two tabs (one tab is guarded by canvas/addhost/index.ts:39-41, but two tabs may each start a setup for the same host); Back during S12.

6. Order of the run

Day 1: create the VMs and the snapshot, and do a dry run with P1 only to check the harness. Day 2: P1-P6 in parallel (5 browser jobs on the perf VM; the iPhone and Mac VM under their lock), then the merged table and issues. After the fixes: rerun all six from the snapshot and post the comparison table here.

## Launch QA plan: six first-time-user personas (executable) Written 2026-10-21 against `origin/dev` @ `aba6d85ce` (release 9.2 merged into dev). File:line references are to that commit. This is a plan only. Nothing has run yet. ### 0. Ground rules for every run - **Build under test:** the production release artifact (static binary, `scripts/auth-toolchain` + cargo, D213), with the web app it ships. Never a dev server. Record the version string and SHA in each report. - **Server venue:** a fresh `infraternal-test-qa-server` VM that the orchestrator creates on **minipc01** (Proxmox; only our own `infraternal-test-*` guests). It gets a real DNS name and TLS, because passkeys need a secure origin with a domain (see prediction 1). Reset it to a clean snapshot before each persona so every persona really starts from nothing (no owner, no CA, no hosts). - **Hosts to add:** two or three fresh `infraternal-test-qa-host-*` guests on minipc01: Debian (sudo user), Alpine (root login only), plus one host with password-only SSH. No other hosts are touched. - **Never used:** infraternal-dev 10.69.69.125, monitor01 prod data, and any VM on minipc01/ash1/ash2 that is not our own `infraternal-test-*` guest. Known_hosts entries for the test guests are cleaned after the run. - **Browser venues:** - Playwright Chromium and WebKit run on the perf VM `10.69.69.208` (ours, with flock). Network shaping uses `tc netem` there: 150 ms RTT, plus 2% loss for persona 3. - The real iPhone (Safari, then installed as a PWA from the home screen) and Chromium on the Mac VM (for glass/blur checks) take the MacBook/Mac VM lock. Capture only through our signed helper, never over netbird ssh. - **Driving:** each persona is one AI-agent job with a persona prompt (section 1) and the step script (section 2). It acts only through the browser and a terminal on the server VM, as a real newcomer would. It may read only what a newcomer can see: the product, `--help`, and the `/install` page. It must not read the repo. - **Personas do not judge visuals** (CLAUDE.md). They record what they did, what they expected and what happened. The orchestrator reviews the screenshots. ### 1. The six personas | # | Persona | Device / browser | Goal | Quits when | |---|---|---|---|---| | P1 | **Impatient**: ops engineer, skims and clicks the first button | Desktop Chromium 1440x900, no throttling | Terminal on a host in under 5 min | the first error has no obvious next button; any wait over 10 s without visible progress; must read a doc | | P2 | **Skeptic**: security lead | Desktop WebKit (Safari engine) 1280x800 | Trust it with one sudo host | cannot find what is stored, where keys live, what root can do, or the licence (AGPL); a password is kept after use; vague wording about secrets | | P3 | **Phone user**: on-call SRE in India | **Real iPhone Safari, then PWA from the home screen**, plus Playwright WebKit 393x852 with 150 ms RTT and 2% loss | Add a host and type `uptime` from the PWA | a control is hidden under the keyboard or notch; a tap target is under 44 px; typing lags (more than RTT+50 ms, D259); the passkey sheet fails inside the PWA | | P4 | **Non-native English speaker**: reads the UI word for word | Desktop Chromium, 1366x768, OS language set to German (the app stays in English) | Same as P1 | a label means something other than what it says (Adopt/Replace, "join", "authority", "locked"); jargon with no explanation | | P5 | **Breaker**: QA engineer | Desktop Chromium plus a second tab | Get the flow into a broken or duplicated state | (does not quit) records every state that cannot be recovered without a server shell | | P6 | **Never reads**: ignores hints, tooltips and empty-state text | Desktop WebKit 1280x800 | Same as P1 | stuck for 60 s with no clickable next step | ### 2. Fresh-install path (the same steps for all; each persona's twist is in brackets) Each step is timed from its first input to its result. **Pass** means it was done without outside help, inside the budget, with no unexplained error. **Fail** means stuck for more than the persona's quit trigger, an error with no next step, or a state that needs a server shell or a DB reset. | Step | What the persona does | Pass criteria | |---|---|---| | S0 Find install | Opens the server VM shell with only a download URL for the release binary | Finds from `infraternal --help` that `infraternal server init` is the command (docs/CLI.md:56). ≤2 min | | S1 Server init | `infraternal server init --dry-run`, then for real (needs a domain: `--public-origin https://qa.<domain>`) | The plan is understandable. The service runs. `/healthz` returns OK. The persona knows which URL to open. ≤3 min [P1 skips `--dry-run`; P2 reads the dry-run plan line by line and records what runs as root; P5 runs init twice and presses Ctrl-C in the middle] | | S2 Find the token | Gets the setup token: the journal banner (`crates/infraternal-server/src/auth/setup.rs:16`) or `infraternal server setup-token` | Token found ≤1 min [P6 must find it with no hint] | | S3 Wizard 1/4 token | Opens the URL and pastes the token (`apps/tracer/src/onboarding.ts:12,175`) | Accepted. A wrong or expired token gives a next step. [P5 pastes junk and a token with spaces, then uses Back] | | S4 Wizard 2/4 name | Username and display name (`onboarding.ts:13,230-244`) | Valid on the first try or a clear hint [P5: 300-character display name, emoji, uppercase] | | S5 Wizard 3/4 passkey | Creates the passkey (Face ID, Touch ID or a virtual authenticator in Playwright) | Registered. A cancel gives a retry message (`onboarding.ts:451`). [P3 on iPhone Safari AND inside the PWA] | | S6 Wizard 4/4 recovery key | Saves the 24 words, ticks saved, then "Open infraternal" (`onboarding.ts:15,91,139`) | The persona actually stored the words (the orchestrator checks the persona's notes). ≤1 min. [P5 reloads here, then closes the tab, then comes back] | | S7 Empty canvas | Sees "No hosts yet" and the "Add host" pill (`apps/tracer/src/canvas/views/world.ts:366-371`) | Opens add-host ≤15 s | | S8 Target | Types the host (`canvas/addhost/wizard.ts:319-320`) | Resolves to a host chip [P1 pastes only an IP; P4 types `ssh debian@10.x`; P5 pastes a URL and double-clicks Add] | | S9 CA / authority | First host: confirm with the passkey to create the certificate authority (`wizard.ts:534`) | Persona can say in their own words what they just approved | | S10 Sign-in method | Server key, key or password (`wizard.ts:833-846`) | Picks one that works. [P2 checks the "not stored" claim: after the step, search the server data dir and journal for the password or key bytes; must be absent] | | S11 Host key / root | Fingerprint check (`wizard.ts:815-818`), root consent (`wizard.ts:488-489`) | Correct choice made; [P2 on the Alpine root-only host must understand root scope] | | S12 Install stages | Watches Connect → Upload → Install → Join → Start → Readiness (`wizard.ts:43-46`) | Done. Each stage has visible progress within 1 s. Target ≤60 s on the LAN, ≤90 s at 150 ms RTT. [P5 reloads and goes Back mid-install; closes the tab and reopens: no duplicate host, no setup that cannot be resumed] | | S13 First success | Terminal opens; types `uptime`, then `ls -la /` | Output appears; keystroke echo ≤ RTT+50 ms (D259); [P3 types from the PWA with the key bar] | | S14 Return | Closes the browser, comes back the next day (simulated: a new context and the session cookie) | Back in with one "Continue with passkey" tap (`onboarding.ts:387-390`); host still there | | S15 Server restart | Orchestrator restarts the service once | Persona understands "Host connections are locked" and unlocks it (`canvas/addhost/authority-unlock.ts:40-43`) ≤1 min | ### 3. What each persona records (per step) - A screenshot before and after every action, and on every surprise. On the iPhone, a short screen recording of S5, S12 and S13. - Timings: wall clock per step, time to first visible progress, and terminal key-to-echo (median and worst) for S13. - Confusion log: one line per moment, `step | what I expected | what I saw | what I did next | seconds lost`. - Errors: the exact text on screen, the browser console, failed network requests (HAR), and the server journal slice for that window. - Final result: reached S13 (yes/no), total time, and the step and reason where they would have quit. - Artifacts are attached to this issue with `issue-attach` (D262), named `p<N>-s<step>-<n>.png`. They are never committed. ### 4. Findings become fj issues 1. The orchestrator merges the six confusion logs into one table: **problem, how many personas hit it (1-6), worst severity, step, screenshot**. Severity: **P0** blocks first success; **P1** loses more than 60 s or needs outside help; **P2** an annoyance. 2. Each row with ≥2 personas, or any P0/P1, gets its own fj issue on `kayg/infraternal`: title `QA #247: <problem>`, body with the step, the exact UI text, file:line, the screenshot, the persona count, and a link back here. Label `launch-qa`. Duplicates of open issues get a comment on that issue instead. 3. Every annoyance goes into `docs/design/TASTE.md` the same day. 4. Fix in one pass (Codex for backend, Sonnet for UI, each with a break-it review), then re-run **all six** from a fresh VM snapshot. This issue stays open until a rerun has no P0/P1 and all six reach S13. ### 5. Ten places predicted to confuse a newcomer (origin/dev @ aba6d85ce) 1. **The default public origin is an IP address, so passkeys fail.** `crates/infraternal/src/main.rs:1353-1359`: without `--public-origin`, the origin is `https://<listen IP>:<port>`. WebAuthn does not accept an IP address as the RP ID, so S5 fails late, after the token and name steps. Init should require a domain name or warn about it. 2. **There is no server quick-start a newcomer can find.** There is no root README. `/download` arrives "when apps ship" (D258). `server init` is documented only in `docs/CLI.md:56-60`. The legacy `server install` is hidden and only prints migration guidance (CLI.md:59), and that is the first command people guess. 3. **The setup token shows up in the journal.** `crates/infraternal-server/src/auth/setup.rs:16` prints it once at startup. The wizard tells people to run `infraternal server setup-token` (`apps/tracer/src/onboarding.ts:177,217`), but not as which user (root or the service account), and not that the token expires after 24 h. 4. **The display-name limit disagrees with the field.** `onboarding.ts:233` sets `maxlength="256"`, but the hint at `onboarding.ts:244` says 64 characters. P5 will type 65-256 characters and be blocked by a hint that appears only after typing. 5. **Step 2 asks for two names, and step 3 mentions SSH keys too early.** "Who are you?" asks for a username and a display name (`onboarding.ts:13,230-233`). The username must be lowercase, 2-32 characters, and the hint appears only after a bad entry (`:241-242`). P4 will type "Ada Lovelace" into the username field. Step 3 says "Your SSH keys are sealed with it" (`onboarding.ts:14`) before the person has any SSH keys here. The recovery step is gated correctly: Continue needs the saved tick (`onboarding.ts:91,322`). Verify that P1 and P6 do not just tick it without saving the words. 6. **The add-host hint is hidden from screen readers and shown only sometimes.** `canvas/addhost/wizard.ts:315` sets `aria-hidden="true"` on "Type user@host, for example kayg@o2", and the example host `o2` means nothing to a newcomer. The parser (`canvas/addhost/model.ts:199-203`) accepts a bare IP, but it rejects `ssh user@ip`, `user@ip -p 2222`, a pasted `ssh://` URL and a trailing space with a comment. P4 and P5 will paste exactly these, so check that the rejection explains itself. For a bare IP (P1), check which user is tried and whether the screen says so. 7. **"Certificate authority" jargon on the first host.** `wizard.ts:534` asks for the passkey "to create the certificate authority for your hosts" in the middle of add-host. P4 and P6 will not know what they are approving. P2 will want to know where its key lives. 8. **"Host connections are locked" after every restart.** `canvas/addhost/authority-unlock.ts:40-43`. Every server restart locks the CA. A newcomer whose VM rebooted sees hosts that cannot be reached and a wording that sounds like a security incident. 9. **The retry-limit dead end sends people to a hidden route.** `canvas/addhost/setup-stages.ts:140`: "Use Remove host in the command palette, then Add host to start again." There is no button. P6 never opens the palette, and P5 will reach this limit on purpose. 10. **Internal words in the stage names.** `wizard.ts:43-46`: "Get the x86_64 binary", "Join the host to this server", "Check terminal readiness". With "Your terminal keeps working" in failure text (`setup-stages.ts:141`, it means the *other* terminals), P4 will read these literally and not know whether they have to act. P3 will see them on a 393 px screen where long host labels may truncate the stage row (`wizard.ts:681-694`). Also check (no prediction, just verify): passkey creation inside the installed iOS PWA; double-clicking "Add host" across two tabs (one tab is guarded by `canvas/addhost/index.ts:39-41`, but two tabs may each start a setup for the same host); Back during S12. ### 6. Order of the run Day 1: create the VMs and the snapshot, and do a dry run with P1 only to check the harness. Day 2: P1-P6 in parallel (5 browser jobs on the perf VM; the iPhone and Mac VM under their lock), then the merged table and issues. After the fixes: rerun all six from the snapshot and post the comparison table here.
Author
Owner

onboarding papercuts: branch onboarding-papercuts, head 0c6fbf8d7 (base aba6d85ce).

Commits: ssh paste parser (model.ts), add-host hint described + neutral example + ssh paste (wizard.ts), display-name maxlength from one constant, username rule shown up front.

Targeted tests (apps/tracer):


 79 pass
 0 fail
 244 expect() calls
Ran 79 tests across 8 files. [246.00ms]

Playwright: addhost-chips.spec.ts (new ssh paste and accessible-description tests) passed; onboarding.spec.ts identity test passed with the new hint and maxlength assertions (a separate invalid-token test hit a one-off socket hang up on one run and passed on the next).

scripts/verify fast: did not finish within 10 minutes (cold Rust build, still compiling crates); not run to completion.

onboarding papercuts: branch `onboarding-papercuts`, head 0c6fbf8d7 (base aba6d85ce). Commits: ssh paste parser (model.ts), add-host hint described + neutral example + ssh paste (wizard.ts), display-name maxlength from one constant, username rule shown up front. Targeted tests (apps/tracer): ``` 79 pass 0 fail 244 expect() calls Ran 79 tests across 8 files. [246.00ms] ``` Playwright: addhost-chips.spec.ts (new ssh paste and accessible-description tests) passed; onboarding.spec.ts identity test passed with the new hint and maxlength assertions (a separate invalid-token test hit a one-off socket hang up on one run and passed on the next). scripts/verify fast: did not finish within 10 minutes (cold Rust build, still compiling crates); not run to completion.
Author
Owner

Starting gate and screenshot pass on branch onboarding-papercuts-gate, head 0c6fbf8d74e4e5a83115d7b74853f8c61067032f, based on origin/dev aba6d85ce36abea9e69f34ed772cea049dac41fb. Worktree is clean. I’ll run the requested fast gate, targeted tests, and production screenshots for #247.

Starting gate and screenshot pass on branch `onboarding-papercuts-gate`, head `0c6fbf8d74e4e5a83115d7b74853f8c61067032f`, based on `origin/dev` `aba6d85ce36abea9e69f34ed772cea049dac41fb`. Worktree is clean. I’ll run the requested fast gate, targeted tests, and production screenshots for #247.
Author
Owner

Completed scripts/verify fast on onboarding-papercuts-gate at 0c6fbf8d74e4e5a83115d7b74853f8c61067032f; command exit code 0. The complete raw transcript is attached: verify-fast.log.

Rust gate output:

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

Static binary caps and tracer test summaries:

x86_64-unknown-linux-musl: exact 21387248-byte cap PASS; 21387249 bytes rejected PASS; smaller artifact requires ratchet PASS
aarch64-unknown-linux-musl: exact 19559400-byte cap PASS; 19559401 bytes rejected PASS; smaller artifact requires ratchet PASS
 348 pass
 0 fail
 21630 expect() calls
Ran 348 tests across 59 files. [46.99s]
 105 pass
 0 fail
 413 expect() calls
Ran 105 tests across 13 files. [318.00ms]
 17 pass
 0 fail
 1794 expect() calls
Ran 17 tests across 4 files. [336.00ms]
 39 pass
 0 fail
 36135 expect() calls
Ran 39 tests across 10 files. [3.07s]
Completed `scripts/verify fast` on `onboarding-papercuts-gate` at `0c6fbf8d74e4e5a83115d7b74853f8c61067032f`; command exit code 0. The complete raw transcript is attached: [verify-fast.log](https://git.kayg.org/attachments/41d3cf85-d6c8-4105-9d26-4411f44d0bb8). Rust gate output: ``` +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | static builds | PASS | +---------------------------+--------------+ ``` Static binary caps and tracer test summaries: ``` x86_64-unknown-linux-musl: exact 21387248-byte cap PASS; 21387249 bytes rejected PASS; smaller artifact requires ratchet PASS aarch64-unknown-linux-musl: exact 19559400-byte cap PASS; 19559401 bytes rejected PASS; smaller artifact requires ratchet PASS 348 pass 0 fail 21630 expect() calls Ran 348 tests across 59 files. [46.99s] 105 pass 0 fail 413 expect() calls Ran 105 tests across 13 files. [318.00ms] 17 pass 0 fail 1794 expect() calls Ran 17 tests across 4 files. [336.00ms] 39 pass 0 fail 36135 expect() calls Ran 39 tests across 10 files. [3.07s] ```
Author
Owner

Completed bun test src/canvas/addhost from apps/tracer at head 0c6fbf8d74e4e5a83115d7b74853f8c61067032f: 79 pass, 0 fail, 244 expect() calls, 8 files. Full output: addhost-unit.log. No failure to fix.

Completed `bun test src/canvas/addhost` from `apps/tracer` at head `0c6fbf8d74e4e5a83115d7b74853f8c61067032f`: 79 pass, 0 fail, 244 expect() calls, 8 files. Full output: [addhost-unit.log](https://git.kayg.org/attachments/c8315d7d-da25-4c3c-9082-aa2f3bc68456). No failure to fix.
Author
Owner

Finding: the first addhost-chips.spec.ts run (port 5231) failed test 4, an invalid token shows the hint and never blocks the chips beside it at apps/tracer/test/addhost-chips.spec.ts:83. The run is continuing and the failure details are being collected; no change made.

Finding: the first `addhost-chips.spec.ts` run (port 5231) failed test 4, `an invalid token shows the hint and never blocks the chips beside it` at `apps/tracer/test/addhost-chips.spec.ts:83`. The run is continuing and the failure details are being collected; no change made.
Author
Owner

Finding confirmed on port 5231: an invalid token shows the hint and never blocks the chips beside it failed only because apps/tracer/test/addhost-chips.spec.ts:90 still expects Type user@host, for example kayg@o2. The built UI returned Type user@host, or user@host:2222 for another port; hint visibility, adjacent chip, and Continue checks passed. This is stale test copy after the neutral hint change, not a product behavior change. I’ll confirm on the remaining repetitions and update the expectation only.

Finding confirmed on port 5231: `an invalid token shows the hint and never blocks the chips beside it` failed only because `apps/tracer/test/addhost-chips.spec.ts:90` still expects `Type user@host, for example kayg@o2`. The built UI returned `Type user@host, or user@host:2222 for another port`; hint visibility, adjacent chip, and Continue checks passed. This is stale test copy after the neutral hint change, not a product behavior change. I’ll confirm on the remaining repetitions and update the expectation only.
Author
Owner

Finding: onboarding run 2 (port 5235) passed invalid setup token stays on step 1 with focused guidance and failed the second test at apps/tracer/test/onboarding.spec.ts:209. expect.poll(() => page.evaluate(() => window.__tracerTestDouble?.isConnected() === true)) timed out after 5 seconds with false. Run 1 passed both tests; run 3 is in progress to determine whether this recurs.

Finding: onboarding run 2 (port 5235) passed `invalid setup token stays on step 1 with focused guidance` and failed the second test at `apps/tracer/test/onboarding.spec.ts:209`. `expect.poll(() => page.evaluate(() => window.__tracerTestDouble?.isConnected() === true))` timed out after 5 seconds with false. Run 1 passed both tests; run 3 is in progress to determine whether this recurs.
Author
Owner

The previously noted /__mock/setup-token socket hang recurred on onboarding run 3 (port 5236), at apps/tracer/test/onboarding.spec.ts:85: apiRequestContext.get: socket hang up for GET http://localhost:5236/__mock/setup-token. The same request passed in onboarding runs 1 and 2. I’m tracing the test proxy/mock route and preserving the full run log.

The previously noted `/__mock/setup-token` socket hang recurred on onboarding run 3 (port 5236), at `apps/tracer/test/onboarding.spec.ts:85`: `apiRequestContext.get: socket hang up` for `GET http://localhost:5236/__mock/setup-token`. The same request passed in onboarding runs 1 and 2. I’m tracing the test proxy/mock route and preserving the full run log.
Author
Owner

Completed the requested browser pass on the minified production tracer build with preview fixtures, all on distinct ports. Raw run logs are attached below.

  • addhost-chips.spec.ts, ports 5231–5233: each run was 11 passed, 1 failed, 4 skipped. The sole failure was the same stale exact hint copy assertion at apps/tracer/test/addhost-chips.spec.ts:90; the actual neutral port hint and all other chip cases passed. Logs: run 1, run 2, run 3.
  • onboarding.spec.ts, ports 5234–5236: run 1 passed 2/2. Run 2 passed the invalid-token test but timed out at line 209 waiting 5 seconds for the mock connection. Run 3 passed the invalid-token test but got socket hang up at line 85 reading /__mock/setup-token. Logs: run 1, run 2, run 3.
  • The /__mock/setup-token reset was isolated from port collision: the proxy/test-double pair was 5236/5237. The Bun node:http server reports the default keepAliveTimeout as 5000 ms; the test held its API request context idle while rendering/capturing the first step. This points to a stale keep-alive socket as the cause. The reset response already includes the fresh setup token (apps/tracer/test/mock-server.ts:665–667), so I’ll consume it immediately and remove the later request.
  • Screenshot harness passed 1/1 and produced 24 PNGs: six requested states × desktop 1440×900 and iPhone 390×844 × dark and light. All 24 dimensions were checked; files are outside the checkout. Capture log: issue-247-captures.log.

I’ll update the stale assertion and onboarding fixture request as separate test-only commits, without changing UI behavior.

Completed the requested browser pass on the minified production tracer build with preview fixtures, all on distinct ports. Raw run logs are attached below. - `addhost-chips.spec.ts`, ports 5231–5233: each run was 11 passed, 1 failed, 4 skipped. The sole failure was the same stale exact hint copy assertion at `apps/tracer/test/addhost-chips.spec.ts:90`; the actual neutral port hint and all other chip cases passed. Logs: [run 1](https://git.kayg.org/attachments/a54c42f2-5d9d-46d7-9c3c-a386399762e0), [run 2](https://git.kayg.org/attachments/d2ee0879-e5af-43d3-9d05-365f57420175), [run 3](https://git.kayg.org/attachments/f918d649-ec29-4e83-a3d1-6c078a57afff). - `onboarding.spec.ts`, ports 5234–5236: run 1 passed 2/2. Run 2 passed the invalid-token test but timed out at line 209 waiting 5 seconds for the mock connection. Run 3 passed the invalid-token test but got `socket hang up` at line 85 reading `/__mock/setup-token`. Logs: [run 1](https://git.kayg.org/attachments/da7d53c6-994c-4389-bf72-6c0bcde60153), [run 2](https://git.kayg.org/attachments/729391df-357f-4cca-ad87-c8da4332374a), [run 3](https://git.kayg.org/attachments/df7649c1-4532-4b3a-b263-e5571d0baa99). - The `/__mock/setup-token` reset was isolated from port collision: the proxy/test-double pair was 5236/5237. The Bun `node:http` server reports the default `keepAliveTimeout` as 5000 ms; the test held its API request context idle while rendering/capturing the first step. This points to a stale keep-alive socket as the cause. The reset response already includes the fresh setup token (`apps/tracer/test/mock-server.ts:665–667`), so I’ll consume it immediately and remove the later request. - Screenshot harness passed 1/1 and produced 24 PNGs: six requested states × desktop 1440×900 and iPhone 390×844 × dark and light. All 24 dimensions were checked; files are outside the checkout. Capture log: [issue-247-captures.log](https://git.kayg.org/attachments/4b7857dd-39f2-4bef-820e-f2f63dec5cda). I’ll update the stale assertion and onboarding fixture request as separate test-only commits, without changing UI behavior.
Author
Owner

#247 screenshots attached: 24 production-build captures (six states × desktop 1440×900 and iPhone 390×844 × dark and light). PNG dimensions were checked; no visual-quality judgment was made.

#247 screenshots attached: 24 production-build captures (six states × desktop 1440×900 and iPhone 390×844 × dark and light). PNG dimensions were checked; no visual-quality judgment was made. - Add-host at rest: [dark desktop](https://git.kayg.org/attachments/e567bd6f-5a00-4a36-931a-38ab5e3335af), [dark iPhone](https://git.kayg.org/attachments/9bb90504-cee1-43e6-832b-057bf3a8e37d), [light desktop](https://git.kayg.org/attachments/55ecb5de-61b9-4ff2-8493-14486e127cdf), [light iPhone](https://git.kayg.org/attachments/4eed24d2-b376-493e-b9a4-bcfa2030cdbe). - Add-host error hint: [dark desktop](https://git.kayg.org/attachments/f419134e-dc3c-4044-b482-e2715aaf9e5a), [dark iPhone](https://git.kayg.org/attachments/e3520e6c-a708-4f13-9b87-58a947b2c807), [light desktop](https://git.kayg.org/attachments/6eb187d5-1c80-4730-8f68-31be12a540e6), [light iPhone](https://git.kayg.org/attachments/c24c5c63-eb16-4a25-a12f-8f465ceb8e76). - Pasted `ssh -p 2222 user@host` as one chip: [dark desktop](https://git.kayg.org/attachments/074da561-1d94-40b5-9e07-9f15452b4bbd), [dark iPhone](https://git.kayg.org/attachments/e3a1d6f4-662d-44a9-80f9-b795cbfedebc), [light desktop](https://git.kayg.org/attachments/4c24702a-37f9-452e-9458-39228c171cd5), [light iPhone](https://git.kayg.org/attachments/7e106e94-3d20-48e7-b151-6792e3b578db). - Display name at 64 characters: [dark desktop](https://git.kayg.org/attachments/1260c411-31d8-427c-a99a-fdd450e6dbc8), [dark iPhone](https://git.kayg.org/attachments/dfa53afd-e948-4272-a582-6c0f1f0a3146), [light desktop](https://git.kayg.org/attachments/e6251701-e018-4426-843e-3718be35daf0), [light iPhone](https://git.kayg.org/attachments/e117484f-7a12-433f-913f-23bfe04a536d). - Identity step at rest: [dark desktop](https://git.kayg.org/attachments/248caae7-4068-4bc5-92d1-584db6223d34), [dark iPhone](https://git.kayg.org/attachments/20722ca4-8437-4478-81c4-d1817e967587), [light desktop](https://git.kayg.org/attachments/2f79d599-4d6f-4677-8b84-60b0b105c24c), [light iPhone](https://git.kayg.org/attachments/d2652c55-fc63-433e-8b00-31c81da1b1cf). - Username hint: [dark desktop](https://git.kayg.org/attachments/999f4c4b-b21a-4edc-b894-a2b85ac85cd3), [dark iPhone](https://git.kayg.org/attachments/4cf61865-7691-4a4f-8dcb-d5717588b6e4), [light desktop](https://git.kayg.org/attachments/3437a0f8-5e55-44dc-85d5-6fef17065818), [light iPhone](https://git.kayg.org/attachments/a274676f-fca2-4806-967e-6baeb17047ff).
Author
Owner

Committed and pushed 2453375a7c7e31865c13d772968ddbab67e93f7e (test(tracer): sync add-host hint expectation to neutral copy) to origin/onboarding-papercuts. It changes only the stale browser expectation.

Gate output: bun run check passed; git diff --check passed; TRACER_TEST_PORT=5241 bun x playwright test test/addhost-chips.spec.ts: 12 passed, 4 capture-only tests skipped, 0 failed. Full browser output: addhost-chips-copy-fix.log.

Committed and pushed `2453375a7c7e31865c13d772968ddbab67e93f7e` (`test(tracer): sync add-host hint expectation to neutral copy`) to `origin/onboarding-papercuts`. It changes only the stale browser expectation. Gate output: `bun run check` passed; `git diff --check` passed; `TRACER_TEST_PORT=5241 bun x playwright test test/addhost-chips.spec.ts`: 12 passed, 4 capture-only tests skipped, 0 failed. Full browser output: [addhost-chips-copy-fix.log](https://git.kayg.org/attachments/526359dd-f37a-4d85-8bf5-0cbf39fd6821).
Author
Owner

Post-fix onboarding repeat result: --repeat-each=3 on port 5242 had 5 passed and 1 failed. The setup-token hang did not recur; the remaining failure is the final isConnected() poll at apps/tracer/test/onboarding.spec.ts:209, which hit its default 5000 ms timeout once in 3 repetitions. The same check timed out once in the baseline 3 runs. Nearby browser connection checks use 30000 ms (apps/tracer/test/cursor-blink.spec.ts:21), so I’ll align this functional wait to that limit and keep the test’s overall timeout sufficient.

Post-fix onboarding repeat result: `--repeat-each=3` on port 5242 had 5 passed and 1 failed. The setup-token hang did not recur; the remaining failure is the final `isConnected()` poll at `apps/tracer/test/onboarding.spec.ts:209`, which hit its default 5000 ms timeout once in 3 repetitions. The same check timed out once in the baseline 3 runs. Nearby browser connection checks use 30000 ms (`apps/tracer/test/cursor-blink.spec.ts:21`), so I’ll align this functional wait to that limit and keep the test’s overall timeout sufficient.
Author
Owner

Committed and pushed 78baf848d85a58f54d4c02f7d140c6ab793b53e4 (test(tracer): reuse reset response for onboarding token) to origin/onboarding-papercuts. Both onboarding cases now consume setup_token from the immediate /__mock/reset response; no UI or server behavior changed.

Gate output: bun run check passed; git diff --check passed; invalid-token test with --repeat-each=3 on port 5243: 3 passed, 0 failed. Full browser output: onboarding-token-read.log.

Committed and pushed `78baf848d85a58f54d4c02f7d140c6ab793b53e4` (`test(tracer): reuse reset response for onboarding token`) to `origin/onboarding-papercuts`. Both onboarding cases now consume `setup_token` from the immediate `/__mock/reset` response; no UI or server behavior changed. Gate output: `bun run check` passed; `git diff --check` passed; invalid-token test with `--repeat-each=3` on port 5243: 3 passed, 0 failed. Full browser output: [onboarding-token-read.log](https://git.kayg.org/attachments/bd3cbb28-fcec-4f29-aa49-37d1c8d37c52).
Author
Owner

Correction and new evidence: after moving both setup-token reads to the reset response, the 3-repeat onboarding run had 5 passed and 1 failed. The failure was an immediate apiRequestContext.post: socket hang up on POST http://localhost:5245/__mock/reset at apps/tracer/test/onboarding.spec.ts:58 (283 ms into that test), before page work. Therefore the earlier idle keep-alive explanation was incomplete. Existing tracer browser specs use maxRetries: 1 for idempotent mock reset requests (apps/tracer/test/addhost-start-fresh-captures.spec.ts:10, apps/tracer/test/addhost-wizard.spec.ts:26); I’ll apply the same retry to both onboarding resets and rerun the suite.

Correction and new evidence: after moving both setup-token reads to the reset response, the 3-repeat onboarding run had 5 passed and 1 failed. The failure was an immediate `apiRequestContext.post: socket hang up` on `POST http://localhost:5245/__mock/reset` at `apps/tracer/test/onboarding.spec.ts:58` (283 ms into that test), before page work. Therefore the earlier idle keep-alive explanation was incomplete. Existing tracer browser specs use `maxRetries: 1` for idempotent mock reset requests (`apps/tracer/test/addhost-start-fresh-captures.spec.ts:10`, `apps/tracer/test/addhost-wizard.spec.ts:26`); I’ll apply the same retry to both onboarding resets and rerun the suite.
Author
Owner

Committed and pushed c7b0524133f1522c28a93e5fb3bd8ae96337bc2d (test(tracer): tolerate fixture resets and connection delays) to origin/onboarding-papercuts.

Gate output: bun run check passed; git diff --check passed; full onboarding spec with --repeat-each=3 on port 5247: 6 passed, 0 failed (both tests passed 3/3). Full output: onboarding-connect-fix.log.

Committed and pushed `c7b0524133f1522c28a93e5fb3bd8ae96337bc2d` (`test(tracer): tolerate fixture resets and connection delays`) to `origin/onboarding-papercuts`. Gate output: `bun run check` passed; `git diff --check` passed; full onboarding spec with `--repeat-each=3` on port 5247: 6 passed, 0 failed (both tests passed 3/3). Full output: [onboarding-connect-fix.log](https://git.kayg.org/attachments/e2385664-87ce-4570-af7a-ade323b90f44).
Author
Owner

Static x86_64 size comparison, same stripped static recipe and target: branch c7b0524133f1522c28a93e5fb3bd8ae96337bc2d = 21,387,248 bytes; merge base aba6d85ce36abea9e69f34ed772cea049dac41fb = 21,387,248 bytes; delta = 0 bytes. Both artifacts are stripped x86-64 static PIE binaries. The merge-base build completed successfully.

Static x86_64 size comparison, same stripped static recipe and target: branch `c7b0524133f1522c28a93e5fb3bd8ae96337bc2d` = 21,387,248 bytes; merge base `aba6d85ce36abea9e69f34ed772cea049dac41fb` = 21,387,248 bytes; delta = 0 bytes. Both artifacts are stripped x86-64 static PIE binaries. The merge-base build completed successfully.
Author
Owner

Finished on onboarding-papercuts-gate at c7b0524133f1522c28a93e5fb3bd8ae96337bc2d, pushed to origin/onboarding-papercuts. Commits: 2453375a7c7e31865c13d772968ddbab67e93f7e, 78baf848d85a58f54d4c02f7d140c6ab793b53e4, c7b0524133f1522c28a93e5fb3bd8ae96337bc2d. No deploy.

scripts/verify fast completed with exit 0. Verbatim gate summary:

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+
x86_64-unknown-linux-musl: exact 21387248-byte cap PASS; 21387249 bytes rejected PASS; smaller artifact requires ratchet PASS
aarch64-unknown-linux-musl: exact 19559400-byte cap PASS; 19559401 bytes rejected PASS; smaller artifact requires ratchet PASS
unsupported target: rejected PASS

Final TypeScript protocol output:

 39 pass
 0 fail
 36135 expect() calls
Ran 39 tests across 10 files. [3.15s]

VERIFY_EXIT=0

Other requested runs:

  • bun test src/canvas/addhost: 79 passed, 0 failed, 244 expectations across 8 files.
  • addhost-chips.spec.ts after the assertion fix: 36 passed, 12 capture-only skipped, 0 failed across 3 repeats on port 5253.
  • onboarding.spec.ts: 6 passed, 0 failed across 3 repeats. Initial repeats exposed one setup-token socket hang-up and one slow connection poll; a later reset POST also hung up, so both onboarding tests now use the reset response token and retry the reset POST once, and the connection poll has a 30s timeout. No recurrence in the final three repeats.

The add-host copy assertion was stale; it now matches the neutral hint text. The add-host failure hint continues to use the existing component behavior. Display name limit is shared at 64 and the username rule is shown up front.

Stripped static x86_64 size: branch 21,387,248 bytes; merge base aba6d85ce36abea9e69f34ed772cea049dac41fb 21,387,248 bytes; delta 0 bytes.

Evidence attachments: final fast gate log, fixed add-host 3-repeat log, onboarding 3-repeat log, add-host unit log, 24 screenshot links. Screenshots cover six requested states at desktop 1440x900 and iPhone 390x844 in dark and light; the pasted ssh -p 2222 user@host appears as one chip.

Finished on `onboarding-papercuts-gate` at `c7b0524133f1522c28a93e5fb3bd8ae96337bc2d`, pushed to `origin/onboarding-papercuts`. Commits: `2453375a7c7e31865c13d772968ddbab67e93f7e`, `78baf848d85a58f54d4c02f7d140c6ab793b53e4`, `c7b0524133f1522c28a93e5fb3bd8ae96337bc2d`. No deploy. `scripts/verify fast` completed with exit 0. Verbatim gate summary: ``` +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | static builds | PASS | +---------------------------+--------------+ ``` ``` x86_64-unknown-linux-musl: exact 21387248-byte cap PASS; 21387249 bytes rejected PASS; smaller artifact requires ratchet PASS aarch64-unknown-linux-musl: exact 19559400-byte cap PASS; 19559401 bytes rejected PASS; smaller artifact requires ratchet PASS unsupported target: rejected PASS ``` Final TypeScript protocol output: ``` 39 pass 0 fail 36135 expect() calls Ran 39 tests across 10 files. [3.15s] ``` `VERIFY_EXIT=0` Other requested runs: - `bun test src/canvas/addhost`: 79 passed, 0 failed, 244 expectations across 8 files. - `addhost-chips.spec.ts` after the assertion fix: 36 passed, 12 capture-only skipped, 0 failed across 3 repeats on port 5253. - `onboarding.spec.ts`: 6 passed, 0 failed across 3 repeats. Initial repeats exposed one setup-token socket hang-up and one slow connection poll; a later reset POST also hung up, so both onboarding tests now use the reset response token and retry the reset POST once, and the connection poll has a 30s timeout. No recurrence in the final three repeats. The add-host copy assertion was stale; it now matches the neutral hint text. The add-host failure hint continues to use the existing component behavior. Display name limit is shared at 64 and the username rule is shown up front. Stripped static x86_64 size: branch 21,387,248 bytes; merge base `aba6d85ce36abea9e69f34ed772cea049dac41fb` 21,387,248 bytes; delta 0 bytes. Evidence attachments: [final fast gate log](https://git.kayg.org/attachments/7add87af-f313-424f-99ed-c107d72d785d), [fixed add-host 3-repeat log](https://git.kayg.org/attachments/c125dd66-1ff5-4b9b-8a8f-a3a9b9866f8b), [onboarding 3-repeat log](https://git.kayg.org/attachments/e2385664-87ce-4570-af7a-ade323b90f44), [add-host unit log](https://git.kayg.org/attachments/c8315d7d-da25-4c3c-9082-aa2f3bc68456), [24 screenshot links](https://git.kayg.org/attachments/db673e99-af8c-437f-a870-0a664ae34a2d). Screenshots cover six requested states at desktop 1440x900 and iPhone 390x844 in dark and light; the pasted `ssh -p 2222 user@host` appears as one chip.
Author
Owner

Merged into dev as adee19da0 (merge of onboarding-papercuts c7b052413 onto dev ee3dab018). Orchestrator reviewed the screenshots: pasted ssh command becomes one user@host:2222 chip; add-host hint visible with neutral example; username rule shown up front. On the merge: add-host unit tests 79/0, tracer tsc clean. Not deployed yet; goes out with the next release.

Merged into dev as adee19da0 (merge of onboarding-papercuts c7b052413 onto dev ee3dab018). Orchestrator reviewed the screenshots: pasted ssh command becomes one `user@host:2222` chip; add-host hint visible with neutral example; username rule shown up front. On the merge: add-host unit tests 79/0, tracer tsc clean. Not deployed yet; goes out with the next release.
Author
Owner

Starting the P1 impatient persona dry run on branch launch-qa-dryrun, based on origin/dev adee19da07cea19e538cb801858ce0d901841b3a. Worktree is clean. I’ll create only the named test guests on minipc01, build the production release from this origin/dev head, and run the persona through the product, --help, and /install, recording evidence outside the checkout. No changes to product code and no deploy to production.

Starting the P1 impatient persona dry run on branch `launch-qa-dryrun`, based on `origin/dev` `adee19da07cea19e538cb801858ce0d901841b3a`. Worktree is clean. I’ll create only the named test guests on minipc01, build the production release from this `origin/dev` head, and run the persona through the product, `--help`, and `/install`, recording evidence outside the checkout. No changes to product code and no deploy to production.
Author
Owner

Blocked venue access finding: monitor01’s local NetBird status, filtered to exclude 10.69.69.125, identifies minipc01 as 10.69.69.217. From monitor01, ssh -o StrictHostKeyChecking=yes -o BatchMode=yes -o ConnectTimeout=8 root@10.69.69.217 'hostname -f' failed with No ED25519 host key is known for 10.69.69.217 / Host key verification failed. minipc01.vpn.kayg.org did not resolve, and no existing known_hosts entry was found for the short name, FQDN, or IP. No key was accepted or added. I cannot safely create/delete the requested guests until monitor01 has an existing trusted connection path for minipc01; please provide that path through the orchestrator/owner channel.

Blocked venue access finding: monitor01’s local NetBird status, filtered to exclude 10.69.69.125, identifies minipc01 as 10.69.69.217. From monitor01, `ssh -o StrictHostKeyChecking=yes -o BatchMode=yes -o ConnectTimeout=8 root@10.69.69.217 'hostname -f'` failed with `No ED25519 host key is known for 10.69.69.217` / `Host key verification failed`. `minipc01.vpn.kayg.org` did not resolve, and no existing known_hosts entry was found for the short name, FQDN, or IP. No key was accepted or added. I cannot safely create/delete the requested guests until monitor01 has an existing trusted connection path for minipc01; please provide that path through the orchestrator/owner channel.
Author
Owner

Raw venue-access evidence: minipc01-hostkey.log.

Raw venue-access evidence: [minipc01-hostkey.log](https://git.kayg.org/attachments/d2527346-225f-4589-a407-c7c0a336bc96).
Author
Owner

Correction to my earlier venue blocker: I read the venue-specific rule as permitting first use on minipc01 while rejecting changed keys; the absolute no-add rule is under the separate perf/Mac lab-machine section. No Proxmox guest has been touched yet. I’ll make the first-use connection with StrictHostKeyChecking=accept-new, which rejects later key changes, and remove only the new minipc01 entry from monitor01’s known_hosts during cleanup.

Correction to my earlier venue blocker: I read the venue-specific rule as permitting first use on minipc01 while rejecting changed keys; the absolute no-add rule is under the separate perf/Mac lab-machine section. No Proxmox guest has been touched yet. I’ll make the first-use connection with `StrictHostKeyChecking=accept-new`, which rejects later key changes, and remove only the new minipc01 entry from monitor01’s known_hosts during cleanup.
Author
Owner

Step: created the fresh test server guest on minipc01. CT 100 is infraternal-test-qa-server (Debian 12.7, 2 cores, 2 GiB RAM, 16 GiB rootfs, DHCP 10.69.0.250). No prior infraternal-test-* guests existed. PID 1 is systemd; systemctl is-system-running reports degraded (exit 1). Venue receipt: venue.md.

Step: created the fresh test server guest on minipc01. CT 100 is `infraternal-test-qa-server` (Debian 12.7, 2 cores, 2 GiB RAM, 16 GiB rootfs, DHCP `10.69.0.250`). No prior `infraternal-test-*` guests existed. PID 1 is systemd; `systemctl is-system-running` reports `degraded` (exit 1). Venue receipt: [venue.md](https://git.kayg.org/attachments/0dcb9200-59ac-4bcc-a6d8-89a42b491c8b).
Author
Owner

Step: prepared the authorized SSH target guest CT 101 (infraternal-test-qa-host-1, DHCP 10.69.0.222). The Debian template already has OpenSSH 9.2p1; SSH is enabled. apt-get update could not resolve Debian mirrors through the guest's configured DNS (10.69.0.1), so sudo could not be installed; no password was set or retained. Host receipt and shaping availability: host-network-prep.md. Build-host tc is unavailable, so no 150 ms/2% shaping is applied.

Step: prepared the authorized SSH target guest CT 101 (`infraternal-test-qa-host-1`, DHCP `10.69.0.222`). The Debian template already has OpenSSH 9.2p1; SSH is enabled. `apt-get update` could not resolve Debian mirrors through the guest's configured DNS (`10.69.0.1`), so `sudo` could not be installed; no password was set or retained. Host receipt and shaping availability: [host-network-prep.md](https://git.kayg.org/attachments/7732941b-d226-4068-8e32-5bfaa9607057). Build-host `tc` is unavailable, so no 150 ms/2% shaping is applied.
Author
Owner

First-run copy fixes, branch firstrun-copy (pushed, not merged), head ece6fc867 on top of origin/dev adee19da0.

Commits (one per item, plus one layout fix found in review):

  1. 5a2148f90 post-restart lock: "The server restarted. Confirm your passkey once to reconnect your hosts."
  2. f8ac54629 visible "Remove host" in the setup failure popover at the retry limit (shared mountConfirmTip gains an optional quiet secondary action; wired to the existing host removal adapter)
  3. cb027b07e plain stage/failed-step names ("Download infraternal (x86_64)", "Connect the host to this server", ...)
  4. 4e0a1a3ae "Your other sessions keep working."
  5. 5c3b9f3cf setup-token hint: run as the user the server runs as; expires after 24 hours (verified in auth/setup.rs; socket is 0600)
  6. a2ba5adc7 one quiet line on why the certificate authority is created
  7. ece6fc867 the confirmation stays inside a narrow window card (the third button clipped it at 390 px)

Tests: bun test apps/tracer/src packages/ui 478 pass, 0 fail; tsc clean; Playwright addhost-pill, addhost-wizard, addhost-pill-stream, inbox, ca-unlock, onboarding all pass (29 + wizard/pill run).
Note: in the mock, Remove host only shows a toast (the passkey ceremony needs the real server). Item 4 uses the requested wording; the new host's own terminal also keeps working.

Screenshots (1 lock toast/inbox, 2 setup failure normal/exhausted, 3 wizard failure details, 5 token step, 6 first-time CA page; desktop 1440x900 and phone 390x844, ink and paper):

First-run copy fixes, branch `firstrun-copy` (pushed, not merged), head ece6fc867 on top of origin/dev adee19da0. Commits (one per item, plus one layout fix found in review): 1. 5a2148f90 post-restart lock: "The server restarted. Confirm your passkey once to reconnect your hosts." 2. f8ac54629 visible "Remove host" in the setup failure popover at the retry limit (shared mountConfirmTip gains an optional quiet `secondary` action; wired to the existing host removal adapter) 3. cb027b07e plain stage/failed-step names ("Download infraternal (x86_64)", "Connect the host to this server", ...) 4. 4e0a1a3ae "Your other sessions keep working." 5. 5c3b9f3cf setup-token hint: run as the user the server runs as; expires after 24 hours (verified in auth/setup.rs; socket is 0600) 6. a2ba5adc7 one quiet line on why the certificate authority is created 7. ece6fc867 the confirmation stays inside a narrow window card (the third button clipped it at 390 px) Tests: `bun test apps/tracer/src packages/ui` 478 pass, 0 fail; `tsc` clean; Playwright addhost-pill, addhost-wizard, addhost-pill-stream, inbox, ca-unlock, onboarding all pass (29 + wizard/pill run). Note: in the mock, Remove host only shows a toast (the passkey ceremony needs the real server). Item 4 uses the requested wording; the new host's own terminal also keeps working. Screenshots (1 lock toast/inbox, 2 setup failure normal/exhausted, 3 wizard failure details, 5 token step, 6 first-time CA page; desktop 1440x900 and phone 390x844, ink and paper): - [1-lock-inbox-desktop-ink.png](https://git.kayg.org/attachments/702fac4b-4dfd-42fe-9992-094599144e5f) - [1-lock-inbox-desktop-paper.png](https://git.kayg.org/attachments/38bb2d75-2d71-454f-b68c-2dacdc11a549) - [1-lock-inbox-phone-ink.png](https://git.kayg.org/attachments/584d8910-94f3-43cd-bfe8-147eb1fe9803) - [1-lock-inbox-phone-paper.png](https://git.kayg.org/attachments/fcdc5ff0-eb0b-4697-976c-1a71ff3b7f70) - [1-lock-toast-desktop-ink.png](https://git.kayg.org/attachments/09581b4a-0ffa-41bd-812a-2f5298087891) - [1-lock-toast-desktop-paper.png](https://git.kayg.org/attachments/965b75e5-68fc-4cb3-b144-8eaff1e6b9ae) - [1-lock-toast-phone-ink.png](https://git.kayg.org/attachments/8c38e7de-bfd0-4545-8b80-f60d5f0fc379) - [1-lock-toast-phone-paper.png](https://git.kayg.org/attachments/81ad2dae-9014-42cb-89d9-66c0ef21467b) - [2-setup-exhausted-desktop-ink.png](https://git.kayg.org/attachments/62409900-b197-405c-addc-0378819c6e34) - [2-setup-exhausted-desktop-paper.png](https://git.kayg.org/attachments/8ac685c4-9f49-491d-b361-ded54c07a1f4) - [2-setup-exhausted-phone-ink.png](https://git.kayg.org/attachments/fcfba3b1-5cc8-4b24-b311-02c14d5fb88a) - [2-setup-exhausted-phone-paper.png](https://git.kayg.org/attachments/3903cd6a-ea19-4ecc-abba-013418eb0749) - [2-setup-normal-desktop-ink.png](https://git.kayg.org/attachments/68f154d1-99ba-46e2-945b-a6a70885e1f0) - [2-setup-normal-desktop-paper.png](https://git.kayg.org/attachments/41f1ce85-07f5-40b6-80dc-6908421d0779) - [2-setup-normal-phone-ink.png](https://git.kayg.org/attachments/86726a59-47d3-4661-9dfc-0febab2d4505) - [2-setup-normal-phone-paper.png](https://git.kayg.org/attachments/b3b6128a-52f7-4a5c-8a56-952f646a1277) - [3-failure-details-desktop-ink.png](https://git.kayg.org/attachments/86b306ae-3f24-4894-a066-604a330fd915) - [3-failure-details-desktop-paper.png](https://git.kayg.org/attachments/d3ab9461-ea8b-459a-87a6-8db84db60c24) - [3-failure-details-phone-ink.png](https://git.kayg.org/attachments/8040c633-75dd-4da1-8855-2589cd6b2115) - [3-failure-details-phone-paper.png](https://git.kayg.org/attachments/9888719c-413c-4f94-876b-d91683135492) - [5-token-desktop-ink.png](https://git.kayg.org/attachments/128660e1-d4ba-4beb-a326-2828bb398496) - [5-token-desktop-paper.png](https://git.kayg.org/attachments/cb74325e-5899-442f-9cb8-20a18f2341d5) - [5-token-phone-ink.png](https://git.kayg.org/attachments/753e9467-35a1-4676-bef4-55d4191e8f8e) - [5-token-phone-paper.png](https://git.kayg.org/attachments/066576c9-4071-4184-b39b-1bcf1cc22c8f) - [6-fresh-ca-desktop-ink.png](https://git.kayg.org/attachments/b3dc3055-7f58-4a82-86eb-2a49baf356fb) - [6-fresh-ca-desktop-paper.png](https://git.kayg.org/attachments/6dc249b9-9a25-4cdf-b548-1b80767ad1c0) - [6-fresh-ca-phone-ink.png](https://git.kayg.org/attachments/8a9e9645-d551-4e83-8334-6dc9c0723223) - [6-fresh-ca-phone-paper.png](https://git.kayg.org/attachments/61fb6b00-3d8a-4199-b012-939919302e84)
Author
Owner

Build pending: the production stable release build from adee19da07cea19e538cb801858ce0d901841b3a has waited 27 minutes for a host build slot. All four slots are currently occupied. Load average at 20:22 UTC was 15.14 / 18.97 / 22.17. I’m continuing independent venue and capture preparation; no user action is needed.

Build pending: the production stable release build from `adee19da07cea19e538cb801858ce0d901841b3a` has waited 27 minutes for a host build slot. All four slots are currently occupied. Load average at 20:22 UTC was 15.14 / 18.97 / 22.17. I’m continuing independent venue and capture preparation; no user action is needed.
Author
Owner

Correction to the prior queue comment: the load average sample was taken at 20:20 CEST (18:20 UTC), not 20:22 UTC. The recorded values remain 15.14 / 18.97 / 22.17.

Correction to the prior queue comment: the load average sample was taken at 20:20 CEST (18:20 UTC), not 20:22 UTC. The recorded values remain 15.14 / 18.97 / 22.17.
Author
Owner

Round 2 on firstrun-copy, head 5868ef5360.

  • 420128160: the shared stacked toast now lays out as mark | message | close on row one, actions on row two (cause: auto-placed grid at phone width; fixed in packages/ui/styles.css for every stacked toast; the short unlocked toast was checked and is unchanged).
  • 5868ef536: with three confirmation actions on a viewport up to 480 px they stack full width in priority order (Try again, Remove host, Dismiss); desktop keeps one row.

Tests: bun test apps/tracer/src packages/ui earlier 478 pass; Playwright addhost-wizard, addhost-pill, addhost-pill-stream, inbox, ca-unlock, onboarding: 72 passed (16.3m). There is no separate toast spec; inbox/ca-unlock cover toasts.

Retaken screenshots (390x844 phone, 1440x900 desktop for toast and popover; ink and paper), all opened and checked:

Round 2 on firstrun-copy, head 5868ef5360770f0c634bd264b7ec5473a155decf. - 420128160: the shared stacked toast now lays out as mark | message | close on row one, actions on row two (cause: auto-placed grid at phone width; fixed in packages/ui/styles.css for every stacked toast; the short unlocked toast was checked and is unchanged). - 5868ef536: with three confirmation actions on a viewport up to 480 px they stack full width in priority order (Try again, Remove host, Dismiss); desktop keeps one row. Tests: `bun test apps/tracer/src packages/ui` earlier 478 pass; Playwright addhost-wizard, addhost-pill, addhost-pill-stream, inbox, ca-unlock, onboarding: 72 passed (16.3m). There is no separate toast spec; inbox/ca-unlock cover toasts. Retaken screenshots (390x844 phone, 1440x900 desktop for toast and popover; ink and paper), all opened and checked: - [1-lock-inbox-phone-ink.png](https://git.kayg.org/attachments/ed3cb45e-a52c-4be1-8ea8-9bb46d3aeb2a) - [1-lock-inbox-phone-paper.png](https://git.kayg.org/attachments/c2b13b5a-b933-474e-ba81-522b7508b39b) - [1-lock-toast-desktop-ink.png](https://git.kayg.org/attachments/c6d130fc-b06a-4cab-ad2e-532c2fcd3b6e) - [1-lock-toast-desktop-paper.png](https://git.kayg.org/attachments/148aca4a-b337-4e2a-b807-2b069e39e5e8) - [1-lock-toast-phone-ink.png](https://git.kayg.org/attachments/44bb8eec-55ba-4571-b979-36d4f6af1130) - [1-lock-toast-phone-paper.png](https://git.kayg.org/attachments/7e1b7463-ae03-4ea5-8e1e-e46c2af358ba) - [2-setup-exhausted-desktop-ink.png](https://git.kayg.org/attachments/9a45ce94-62e4-4dc4-8539-8065acdef63e) - [2-setup-exhausted-desktop-paper.png](https://git.kayg.org/attachments/22f854a3-2fb6-4549-b948-2dd3cb32d513) - [2-setup-exhausted-phone-ink.png](https://git.kayg.org/attachments/e1b85654-d3bc-427e-aceb-2fbca60a0e4c) - [2-setup-exhausted-phone-paper.png](https://git.kayg.org/attachments/4a0181cb-4c9c-4883-87fa-e818eae5f85b) - [3-failure-details-phone-ink.png](https://git.kayg.org/attachments/19c9a4d6-9239-48b7-b53e-4d6de2314e29) - [3-failure-details-phone-paper.png](https://git.kayg.org/attachments/2a8f6245-3c0e-4d96-b277-a5c63e279140) - [5-token-phone-ink.png](https://git.kayg.org/attachments/8d6a9896-bc03-480b-9e2c-39ae2d69737e) - [5-token-phone-paper.png](https://git.kayg.org/attachments/90055c79-a460-48c1-92e6-0e083d246e13) - [6-fresh-ca-phone-ink.png](https://git.kayg.org/attachments/1a9dbc1b-64bd-4715-835b-0be2817f0ac2) - [6-fresh-ca-phone-paper.png](https://git.kayg.org/attachments/9214a010-c209-4332-8122-2384f3d37d41)
Author
Owner

dev fast-forwarded adee19da0 → 5868ef536 (firstrun-copy: calmer restart-lock toast, Remove host at the retry limit, plain stage names, token hint with user + 24 h expiry, one-line CA explanation, shared toast phone layout, stacked confirmation actions on phones). Orchestrator reviewed the retaken phone screenshots (toast and retry-limit popover). Specs: addhost-wizard, addhost-pill, addhost-pill-stream, inbox, ca-unlock, onboarding — 72 passed. Not deployed; next release.

dev fast-forwarded adee19da0 → 5868ef536 (firstrun-copy: calmer restart-lock toast, Remove host at the retry limit, plain stage names, token hint with user + 24 h expiry, one-line CA explanation, shared toast phone layout, stacked confirmation actions on phones). Orchestrator reviewed the retaken phone screenshots (toast and retry-limit popover). Specs: addhost-wizard, addhost-pill, addhost-pill-stream, inbox, ca-unlock, onboarding — 72 passed. Not deployed; next release.
Author
Owner

Build finding: while compiling the second static target, scripts/package-release emitted a dead_code warning for write_output at crates/infraternal/src/cli/output.rs:616. The build is continuing; no source was changed. Receipt: release-build-warning-1.txt.

Build finding: while compiling the second static target, `scripts/package-release` emitted a `dead_code` warning for `write_output` at `crates/infraternal/src/cli/output.rs:616`. The build is continuing; no source was changed. Receipt: [release-build-warning-1.txt](https://git.kayg.org/attachments/8deb74fa-b893-4153-b347-277d3c8701e5).
Author
Owner

Update: issue history shows the second #247 fix round has now landed on origin/dev (5868ef5360770f0c634bd264b7ec5473a155decf) while the release build waited for a slot. I fast-forwarded this clean job branch to that same head. The first bundle built from the start-time head adee19da0 will not be used for the persona; I’m rebuilding the current origin/dev release. No code was authored in this dry-run branch.

Update: issue history shows the second #247 fix round has now landed on `origin/dev` (`5868ef5360770f0c634bd264b7ec5473a155decf`) while the release build waited for a slot. I fast-forwarded this clean job branch to that same head. The first bundle built from the start-time head `adee19da0` will not be used for the persona; I’m rebuilding the current `origin/dev` release. No code was authored in this dry-run branch.
Author
Owner

Step complete: built stable release 0.1.0 from current origin/dev head 5868ef5360770f0c634bd264b7ec5473a155decf; scripts/release/build --channel stable exited 0. The 43 MiB bundle contains a 21 MiB x86_64 server binary (SHA-256 1e2046fc15d3687cc4f999780927f01a5c2fd07e385123c40ddc3adf70da073c). The repeated write_output dead-code warning at crates/infraternal/src/cli/output.rs:616 is non-fatal. Receipts: build summary, build log.

Step complete: built stable release `0.1.0` from current `origin/dev` head `5868ef5360770f0c634bd264b7ec5473a155decf`; `scripts/release/build --channel stable` exited 0. The 43 MiB bundle contains a 21 MiB x86_64 server binary (SHA-256 `1e2046fc15d3687cc4f999780927f01a5c2fd07e385123c40ddc3adf70da073c`). The repeated `write_output` dead-code warning at `crates/infraternal/src/cli/output.rs:616` is non-fatal. Receipts: [build summary](https://git.kayg.org/attachments/4cbd51df-54ff-4917-bfe3-7a448c62e4aa), [build log](https://git.kayg.org/attachments/d6c70413-5dd0-4f6b-9d4c-a5aff04c9608).
Author
Owner

P1 S0 completed: pass in 6.4 s (budget 2 min). Top-level infraternal --help lists only the server group; infraternal server --help reveals server init. One extra help hop, no documentation or outside help. I recorded that 6.4 s as the confusion moment. Evidence: S0 result, CLI help.

P1 S0 completed: pass in 6.4 s (budget 2 min). Top-level `infraternal --help` lists only the `server` group; `infraternal server --help` reveals `server init`. One extra help hop, no documentation or outside help. I recorded that 6.4 s as the confusion moment. Evidence: [S0 result](https://git.kayg.org/attachments/8e9ace70-352b-4c06-b6f9-ce875d889b3f), [CLI help](https://git.kayg.org/attachments/c4698564-dbfd-4d2c-8719-f00cb7037c31).
Author
Owner

P1 S1 finding: server init confirmed and reported the service installed/enabled, then infraternal status said the server was running. In fact, systemd had MainPID=0, Result=exit-code, and SubState=auto-restart (45 restarts); there was no listener on 7443/7444, and /healthz reset through the tunnel. The direct server command returned invalid server configuration / Check configuration and retained public server trust. The init help calls out HTTPS certificate files or a trusted proxy; neither was supplied. I stopped the service to prevent further restart churn and am verifying whether this missing TLS setup caused the failure. Evidence: S1 record, sanitized journal, health check.

P1 S1 finding: `server init` confirmed and reported the service installed/enabled, then `infraternal status` said the server was running. In fact, systemd had `MainPID=0`, `Result=exit-code`, and `SubState=auto-restart` (45 restarts); there was no listener on 7443/7444, and `/healthz` reset through the tunnel. The direct server command returned `invalid server configuration` / `Check configuration and retained public server trust.` The init help calls out HTTPS certificate files or a trusted proxy; neither was supplied. I stopped the service to prevent further restart churn and am verifying whether this missing TLS setup caused the failure. Evidence: [S1 record](https://git.kayg.org/attachments/974445b3-eaea-48e3-8ef4-12f334818fa8), [sanitized journal](https://git.kayg.org/attachments/98fbed53-e8ae-4a51-a2fa-1768ac81cdb3), [health check](https://git.kayg.org/attachments/f01a7f88-facf-44ff-8e81-ac044743ff52).
Author
Owner

S1 configuration isolation: with HTTPS and the same data/listen settings, the server starts and /healthz returns 200 when --remote-listen is omitted. Adding --remote-listen exits with the same generic “invalid server configuration” even with an empty --remote-ca-dir. The --dry-run still says the plan is ready. Exact missing trust prerequisite is not yet established. Confirmed status bug: during the initial service crash loop, infraternal status said Running while systemd had MainPID=0, Result=exit-code, NRestarts=45, and no listener. The minimal service is now healthy; /install is 404 because the stable binary bundle has no web assets and no --web-root was configured. Evidence: configuration isolation.

S1 configuration isolation: with HTTPS and the same data/listen settings, the server starts and `/healthz` returns 200 when `--remote-listen` is omitted. Adding `--remote-listen` exits with the same generic “invalid server configuration” even with an empty `--remote-ca-dir`. The `--dry-run` still says the plan is ready. Exact missing trust prerequisite is not yet established. Confirmed status bug: during the initial service crash loop, `infraternal status` said Running while systemd had `MainPID=0`, `Result=exit-code`, `NRestarts=45`, and no listener. The minimal service is now healthy; `/install` is 404 because the stable binary bundle has no web assets and no `--web-root` was configured. Evidence: [configuration isolation](https://git.kayg.org/attachments/61ba1ae8-d77c-4371-a9ac-0e0cfb729d18).
Author
Owner

Test server web assets are installed: production tracer build from 5868ef5360770f0c634bd264b7ec5473a155decf, 272 files / 4.1 MiB; guest hashes match. After restarting the service to load --web-root, /install and / return HTTP 200 (1,759 bytes), and /healthz returns 200. Receipt: web build and install.

Test server web assets are installed: production tracer build from `5868ef5360770f0c634bd264b7ec5473a155decf`, 272 files / 4.1 MiB; guest hashes match. After restarting the service to load `--web-root`, `/install` and `/` return HTTP 200 (1,759 bytes), and `/healthz` returns 200. Receipt: [web build and install](https://git.kayg.org/attachments/83996337-4b0c-4da3-b9a8-b58274d08e68).
Author
Owner

Blocked on the shared build-slot queue for the required Chromium harness. The slot-wrapped process has waited about 10 minutes and has not launched a browser. Server, tracer assets, and host guest are ready; continuing as soon as a slot is available. No help is needed from the owner at this point.

Blocked on the shared build-slot queue for the required Chromium harness. The slot-wrapped process has waited about 10 minutes and has not launched a browser. Server, tracer assets, and host guest are ready; continuing as soon as a slot is available. No help is needed from the owner at this point.
Author
Owner

S2 plan issue: on the system service, the literal infraternal server setup-token command exits 1 without a data directory. server setup-token --help lists --data <DATA>; adding --data /var/lib/infraternal exits 0. I discarded both command outputs to keep the token private. Update S2 to pass the configured --data path (or tell the operator to find it in help). Evidence: S2 command check.

S2 plan issue: on the system service, the literal `infraternal server setup-token` command exits 1 without a data directory. `server setup-token --help` lists `--data <DATA>`; adding `--data /var/lib/infraternal` exits 0. I discarded both command outputs to keep the token private. Update S2 to pass the configured `--data` path (or tell the operator to find it in help). Evidence: [S2 command check](https://git.kayg.org/attachments/f96e4378-f3b5-4bd3-8844-d0046dd127fb).
Author
Owner

The corrected Chromium harness is blocked on the shared build-slot queue for about 10 minutes. The wrapper has not launched Node/Chromium yet. Server and tracer assets remain ready; no owner action is needed, and I’m continuing to wait.

The corrected Chromium harness is blocked on the shared build-slot queue for about 10 minutes. The wrapper has not launched Node/Chromium yet. Server and tracer assets remain ready; no owner action is needed, and I’m continuing to wait.
Author
Owner

The corrected Chromium harness has waited 20 minutes in the shared build-slot wrapper and has not launched a browser. There is no process running inside the slot yet; the server and production assets are ready. Continuing to wait; no action is needed from the owner.

The corrected Chromium harness has waited 20 minutes in the shared build-slot wrapper and has not launched a browser. There is no process running inside the slot yet; the server and production assets are ready. Continuing to wait; no action is needed from the owner.
Author
Owner

The corrected Chromium harness has waited about 30 minutes in the shared build-slot queue without launching a browser. The service/assets/venue are ready; no owner action is needed. Continuing to wait for the slot before WebKit or the fast gate.

The corrected Chromium harness has waited about 30 minutes in the shared build-slot queue without launching a browser. The service/assets/venue are ready; no owner action is needed. Continuing to wait for the slot before WebKit or the fast gate.
Author
Owner

The corrected Chromium harness has waited 45 minutes in the shared build-slot queue without launching. Wrapper PID 143895 remains in its 20-second polling loop; no browser process exists. The test guest and app are ready. Continuing to wait; no owner action is needed.

The corrected Chromium harness has waited 45 minutes in the shared build-slot queue without launching. Wrapper PID 143895 remains in its 20-second polling loop; no browser process exists. The test guest and app are ready. Continuing to wait; no owner action is needed.
Author
Owner

The corrected Chromium harness has waited one hour in the shared build-slot queue without launching. Wrapper PID 143895 remains in its wait loop, with no child browser process. The server and assets are ready. Continuing to wait; no owner action is needed.

The corrected Chromium harness has waited one hour in the shared build-slot queue without launching. Wrapper PID 143895 remains in its wait loop, with no child browser process. The server and assets are ready. Continuing to wait; no owner action is needed.
Author
Owner

S2 blocker confirmed: the /install setup screen tells the operator to run infraternal server setup-token, which exits 1 on this system service. Adding --data /var/lib/infraternal exits 0. The P1 persona stops here because the displayed command fails. Opened #252, labelled launch-qa; the screenshot and safe exit-status evidence are attached there. Plan S2 also needs the configured data path in its command.

S2 blocker confirmed: the `/install` setup screen tells the operator to run `infraternal server setup-token`, which exits 1 on this system service. Adding `--data /var/lib/infraternal` exits 0. The P1 persona stops here because the displayed command fails. Opened [#252](https://git.kayg.org/kayg/infraternal/issues/252), labelled `launch-qa`; the screenshot and safe exit-status evidence are attached there. Plan S2 also needs the configured data path in its command.
Author
Owner

Correction and browser results: the first S1 attempt was a test-fixture setup error (no TLS/proxy settings and an extra --remote-listen); server init reported success in 9.8 s but the service crash-looped. After configuring HTTPS, omitting the dedicated remote listener, adding the web root, and restarting the service, /healthz returned 200. Treating that as venue correction, P1 passes S0 in 6.4 s and reaches the setup screen. It stops at S2: /install's exact infraternal server setup-token command exits 1; --data /var/lib/infraternal exits 0. No token was exposed. S3-S15 were not reached. The separate crash-loop status false positive is being recorded on #237.

Chromium 149 at 1440x900: setup screen loaded, 16/16 requests HTTP 200, 0 console errors. WebKit 26.5 at 1280x800: same screen, 16/16 requests HTTP 200; one error entry says the interactive-widget viewport key is ignored. Network shaping was unavailable (tc absent). Summary: browser results. P1 blocker: #252.

Correction and browser results: the first S1 attempt was a test-fixture setup error (no TLS/proxy settings and an extra `--remote-listen`); `server init` reported success in 9.8 s but the service crash-looped. After configuring HTTPS, omitting the dedicated remote listener, adding the web root, and restarting the service, `/healthz` returned 200. Treating that as venue correction, P1 passes S0 in 6.4 s and reaches the setup screen. It stops at S2: `/install`'s exact `infraternal server setup-token` command exits 1; `--data /var/lib/infraternal` exits 0. No token was exposed. S3-S15 were not reached. The separate crash-loop status false positive is being recorded on #237. Chromium 149 at 1440x900: setup screen loaded, 16/16 requests HTTP 200, 0 console errors. WebKit 26.5 at 1280x800: same screen, 16/16 requests HTTP 200; one error entry says the `interactive-widget` viewport key is ignored. Network shaping was unavailable (`tc` absent). Summary: [browser results](https://git.kayg.org/attachments/d64f2b9a-19c1-4526-af47-8457cb0904ea). P1 blocker: [#252](https://git.kayg.org/kayg/infraternal/issues/252).
Author
Owner

Separate launch QA P1 finding: in the initial S1 fixture trial, infraternal status reported Running during a systemd crash loop (MainPID=0, Result=exit-code, SubState=auto-restart, 45 restarts, no listener). I added the evidence and launch-qa label to existing status-truth issue #237 rather than opening a duplicate.

Separate launch QA P1 finding: in the initial S1 fixture trial, `infraternal status` reported Running during a systemd crash loop (`MainPID=0`, `Result=exit-code`, `SubState=auto-restart`, 45 restarts, no listener). I added the evidence and `launch-qa` label to existing status-truth issue [#237](https://git.kayg.org/kayg/infraternal/issues/237) rather than opening a duplicate.
Author
Owner

Venue cleanup complete: only CT100/101 (infraternal-test-qa-server, infraternal-test-qa-host-1) were destroyed; filtered guest list is empty. monitor01 known_hosts no longer contains minipc01, the inner tunnel and both local NetBird sessions are gone, and local port 7443 is released. Browser harness/slot process is gone. Worktree is clean at 5868ef5360770f0c634bd264b7ec5473a155decf = origin/dev; no code changes or deploy. Cleanup proof.

Venue cleanup complete: only CT100/101 (`infraternal-test-qa-server`, `infraternal-test-qa-host-1`) were destroyed; filtered guest list is empty. monitor01 known_hosts no longer contains minipc01, the inner tunnel and both local NetBird sessions are gone, and local port 7443 is released. Browser harness/slot process is gone. Worktree is clean at `5868ef5360770f0c634bd264b7ec5473a155decf` = `origin/dev`; no code changes or deploy. [Cleanup proof](https://git.kayg.org/attachments/740203fc-923a-4667-b45f-f0bf85656be8).
Author
Owner

Dry run complete on head 5868ef5360770f0c634bd264b7ec5473a155decf (origin/dev). No product code changes or commits.

Persona outcome:

  • S0 passed in 6.4 s. Top-level help shows only the server group; server --help reveals server init.
  • S1 passed after correcting the QA fixture to use its HTTPS/proxy setup. The first 9.8 s attempt omitted those options and added a dedicated remote listener; it was a fixture error, not counted as the persona result. During that invalid attempt the service restarted continuously while infraternal status reported Running; evidence is on related issue #237.
  • S2 failed in 3.231 s. /install shows infraternal server setup-token without --data; that command fails for the system service. Supplying --data /var/lib/infraternal exits 0. The token output was discarded. P1 stops here; S3–S15 were not reached. Opened #252 as a launch-qa P1.

Browser results: Chromium 149.0.7827.55 at 1440×900 loaded 16 requests, all HTTP 200, with no console warnings/errors. WebKit 26.5 at 1280×800 loaded 16 requests, all HTTP 200; its console reported one ignored interactive-widget viewport argument. Network shaping was unavailable (tc absent), so the requested RTT/loss profile was not run. HARs, screenshots, console output and timings are attached in earlier comments.

Plan updates: record the working HTTPS/proxy server-init setup and require a health check before the persona starts. Include --data /var/lib/infraternal in the displayed setup-token command. The current run stops at S2 as specified by the P1 boundary.

Cleanup is complete: both guests created for this run were destroyed and no infraternal-test-* guests remain; the minipc01 entry was removed from monitor01's known_hosts; the browser, tunnels and test service were stopped. Cleanup proof is attached in an earlier comment. Network port 7443 was released.

Final scripts/verify fast result: failed at the production JavaScript transfer budget after the Rust gate and both static builds passed. x86_64 static artifact: 21,389,232 bytes, cap 21,389,232, delta +0; aarch64: 19,561,656 bytes, cap 19,561,656, delta +0. Critical JavaScript gzip was 307,172/307,200 bytes; Brotli was 256,067/256,000, over by 67 bytes. Exact safe receipt: verify-fast-receipt.txt.

Gate output: scripts/verify fast exit=1.

Dry run complete on head `5868ef5360770f0c634bd264b7ec5473a155decf` (`origin/dev`). No product code changes or commits. Persona outcome: - S0 passed in 6.4 s. Top-level help shows only the `server` group; `server --help` reveals `server init`. - S1 passed after correcting the QA fixture to use its HTTPS/proxy setup. The first 9.8 s attempt omitted those options and added a dedicated remote listener; it was a fixture error, not counted as the persona result. During that invalid attempt the service restarted continuously while `infraternal status` reported Running; evidence is on related issue #237. - S2 failed in 3.231 s. `/install` shows `infraternal server setup-token` without `--data`; that command fails for the system service. Supplying `--data /var/lib/infraternal` exits 0. The token output was discarded. P1 stops here; S3–S15 were not reached. Opened [#252](https://git.kayg.org/kayg/infraternal/issues/252) as a `launch-qa` P1. Browser results: Chromium 149.0.7827.55 at 1440×900 loaded 16 requests, all HTTP 200, with no console warnings/errors. WebKit 26.5 at 1280×800 loaded 16 requests, all HTTP 200; its console reported one ignored `interactive-widget` viewport argument. Network shaping was unavailable (`tc` absent), so the requested RTT/loss profile was not run. HARs, screenshots, console output and timings are attached in earlier comments. Plan updates: record the working HTTPS/proxy server-init setup and require a health check before the persona starts. Include `--data /var/lib/infraternal` in the displayed setup-token command. The current run stops at S2 as specified by the P1 boundary. Cleanup is complete: both guests created for this run were destroyed and no `infraternal-test-*` guests remain; the minipc01 entry was removed from monitor01's known_hosts; the browser, tunnels and test service were stopped. Cleanup proof is attached in an earlier comment. Network port 7443 was released. Final `scripts/verify fast` result: **failed** at the production JavaScript transfer budget after the Rust gate and both static builds passed. x86_64 static artifact: 21,389,232 bytes, cap 21,389,232, delta +0; aarch64: 19,561,656 bytes, cap 19,561,656, delta +0. Critical JavaScript gzip was 307,172/307,200 bytes; Brotli was 256,067/256,000, over by 67 bytes. Exact safe receipt: [verify-fast-receipt.txt](https://git.kayg.org/attachments/5ceb4d9c-bcc0-49ec-bba8-fd0c8dc40eed). Gate output: `scripts/verify fast exit=1`.
Sign in to join this conversation.
No labels
launch-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/infraternal#247
No description provided.