Uh oh!
There was an error while loading. Please reload this page.
hyp init: show the user where they are in the onboarding flow - #434
Conversation
The wizard was a sequence of standalone screens: each phase printed its own title and knew nothing about what came before or after, so the user could not tell whether they were halfway through or two prompts from the end (#415). Adds a position line, per the decision on the issue: - The denominator resolves after the fork, never before. The pathway is what fixes the total and the fork is the question that asks for it, so the fork carries no counter. A failed join returns to the fork, which states nothing, so a retry onto a different pathway never contradicts a total already on screen. - Steps are prompt lanes, not phases. `configure` and the privacy narration are output, and `first look` is a report rather than a decision, so none of them count and `configure` needs no changes. - A lane counts once however many prompts it contains, so the whole join lane (which delegates to a login that can prompt for an org) is one step and the total never moves once the pathway is committed. Itineraries: team = join, pick, finale; local and scoped = pick, finale. The seam is an optional `progress` field rather than text folded into a title. `PromptChrome` becomes the common base for all six prompt-spec and prompt-state interfaces so the field reaches every kind and cannot drift between them; `render` opens each frame with the position dim above the bold title, and the legacy numbered picker prompt prints the same text as plain text. Lanes with no prompt spec (join, finale) write the line themselves where the lane starts. `wizardStepProgress` returns undefined whenever no position can be stated honestly, and callers thread that through as an omitted field, so non-interactive runs emit no breadcrumb and their output is byte-identical to before (verified by diffing `hyp init --yes`, `--yes --dry-run` and `--yes --dry-run --export keep-local` stdout and stderr against the same runs on master). LLP 0135 gains a #progress section documenting the three rules and the seam, and the computation carries an @ref back to it. Co-Authored-By: Claude <noreply@anthropic.com>
philcunliffe
commented
Jul 28, 2026
Verdict: clean, no actionable findingsReviewed The implementation matches the specification in bgmcmullen's comment on #415 on every structural point, and I verified each one independently rather than reading the PR description. Details below, including the byte-identity comparison re-run from scratch. 1. The counter cannot lie
Both short-circuiting gate actions return before Failed-join retry ( I mutation-tested this rather than trusting it, see section 6. 2. The join lane really is one step
Verified in a real attended run under a pty on the branch (TUI path, and the fork frame above it carries no counter. 3. Byte-identical non-interactive output, re-verifiedI did not take the author's word for this. Ran the packaged CLI from both worktrees under
I added the The mechanism holds up structurally too: 4. The common base is soundDiffed the effective member set of every interface in
5. The two disclosed deviations(a) (b) The finale states its position once at lane start. I judge this right, and consistent rather than a special case: it is the same rule the human mandated for the join lane, applied to the other multi-action lane. Repeating the identical string on the backfill consent spec would not change any number, so nothing about the counter's honesty depends on it. There is a real but small UX cost, since the consent select at 6. The regression test is real
The last one matters most: the byte-identity contract has an executable guard, not just a manual check. 7. Checks, conventions and LLP
FindingsNone actionable. Two non-blocking nits, recorded for completeness only:
Nothing was changed or pushed. |
Uh oh!
There was an error while loading. Please reload this page.
hyp initwas a sequence of standalone screens: each phase printed its own title and knew nothing about what came before or after, so you answered a question, another screen appeared, and you could not tell whether you were halfway through or two prompts from the end.Implements the decision on the issue (option b, denominator resolved after the fork) including all four structural refinements.
What landed
The denominator resolves after the fork, never before. The pathway is what fixes the total, and the fork is the question that asks for it, so the fork carries no counter at all. One line after the fork resolves, the itinerary is known and never moves again. This is what makes the failed-join retry safe: it loops back to the fork (
wizard/index.js), which states nothing, so a retry that lands onlocalstarts the local count rather than contradicting a team total already on screen. The scoped re-entry skips the fork and knows its pathway from the gate.Steps are prompt lanes, not phases.
configureis narration plus a shelled-out command, the privacy narration is closing text, andfirst lookis a report rather than a decision. None of them count, andconfigureneeded no changes at all.A lane counts once, however many prompts it contains. The whole join lane is one step even though
runRemoteLogincan prompt for an org; the finale is likewise one step across install / attach / assets / consent / restart.first lookandprivacydo not count and render with no counter.Itineraries:
teamStep 1 of 3 · Join your team,Step 2 of 3 · Choose what to collect,Step 3 of 3 · Finish setuplocalStep 1 of 2 · Choose what to collect,Step 2 of 2 · Finish setupscopedDisplay names are the ones proposed on the issue, unchanged.
The seam
An optional
progressfield on the prompt spec, not composed intotitle. As flagged, this meant reaching six interfaces, so they now derive from a commonPromptChromebase (title,hint,progress) covering the three specs and the three reducer states.render.jsopens every frame with the position dim on its own line above the bold title, and the legacy numbered picker prompt prints the same text as plain text in the same position.Two lanes own no prompt spec (
joinnarrates then delegates; the finale is a sequence of actions), so they write the line themselves where the lane starts, once.wizardStepProgressreturnsundefinedwhenever no position can be stated honestly, and every caller threads that through as an omitted field.Verification
The new test file was written first and run against unmodified
master: 7 of its 15 cases failed on real assertions (+ undefined - 'Step 1 of 2 · Choose what to collect', the join narration missing its line,renderemitting the title as line 0). All 15 pass after.Non-interactive byte-identity was checked for real, not just asserted in a unit test:
hyp init --yes --no-daemon,--yes --dry-run --no-daemon, and--yes --dry-run --no-daemon --export keep-localwere each run against this branch and againstmasterwith an identical tempHOME, and both stdout and stderr are byte-for-byte identical in all three.npm test: 2798 tests, 2789 pass, 8 fail. The 8 aretest/core/leave-command.test.jsand fail identically on unmodifiedmasterin this container (nosystemctlon PATH).npm run typecheck: clean.npm run smoke -- walkthrough_picker_to_first_query: ok.npm run smoke -- walkthrough_to_first_query: FAILS, but fails identically on unmodifiedmasterhere (ai_gateway_messages has exactly one rowreturns 0) - environmental, not caused by this change.LLP 0135 gains a
#progresssection documenting the three rules, the itinerary table and the seam, and every piece of the computation carries an@refback to it.One deviation worth flagging
The issue names
legacyMenuPrompt(wizard/fork.js:347) as the fallback that should print the plain-text form. That function is used only by the fork and the returning gate, and under option (b) neither carries a counter, so there is nothing for it to render. The pick lane's actual non-TUI fallback islegacyNumberedPromptFactoryinwalkthrough.js, and that is where the plain-text form landed. Adding an unused parameter tolegacyMenuPromptwould have been dead code.Fixes#415