package_bin_boot: stop pinning the --help marketing banner (#758) - #762
Conversation
The smoke asserted `hyp - HypAware kernel CLI`, a string b9e50ee (#711, LLP 0211) replaced with an onboarding banner. LLP 0211 and the other CLI/onboarding LLPs (0009, 0129, 0214) treat that sentence as copy, not a contract: nothing pins the exact banner text. Assert the stable things the flow is actually named for instead: the `usage: hyp <command> [args...]` line and the presence of known command names. Exit status 0 was already asserted and stays. Verified the new assertion still catches a real break: swapped the usage-line write for a sentinel string, watched the smoke go red on "hypaware --help prints the usage line", then restored it and confirmed green. Co-Authored-By: Claude <noreply@anthropic.com>
neutral review - round 1Head reviewed: Correction to something I asserted when dispatching this review. I told the That does not make the PR's choice wrong, but it inverts the risk I described: this Finding 1 is a real one and it is the load-bearing half of the PR. The Worktree clean, my scratch files removed (others' untouched). VERDICT: findings 1. major — Both new command-name checks are plain
I verified this empirically. With a filter that removes every Why it matters: this assertion is the entire load-bearing half of the PR's thesis. The banner check it replaced was rot-prone but at least checked a string that existed exactly once. The replacement reads as "the registry-assembled command table rendered" and does not test that. It also means a real rename is detected by accident rather than by design: renaming the whole This is not a regression against Exact fix — parse the rendered rows instead of substring-matching the blob. const helpRows = String(helpResult.stdout ?? '')
.split('\n')
.flatMap((line) => {
const m = /^ {2}(\S+) {2,}\S/.exec(line)
return m ? [m[1]] : []
})
expect.that(
`hypaware --help lists the core command rows (got rows=${helpRows.join(',') || '<none>'})`,
helpRows,
(v) => v.includes('daemon') && v.includes('status') && v.length >= 10
)This is anchored to what LLP 0009 On whether What this smoke can and cannot now catchAll cases run against this worktree; each was injected,
Two gaps that pre-date the PR and are not findings against it, but are worth recording: (a) plugin-contributed help rows are never exercised — I confirmed Bottom line for a release gate: after the finding-1 fix, the flow catches every breakage class in the "packaged binary boots and Also checked, clean
|
…ubstring (#758) Both `daemon` and `status` also appear in incidental --help prose (the epilogue and the `query`/`join` row summaries), so the previous `String.includes` check stayed green even when every daemon*/status row was dropped from the command table, or when the whole daemon family was renamed. Parse rows by their `renderHelp` shape (two-space indent, name, two-or-more spaces) instead, keep the daemon/status name checks (both LLP 0009-anchored), and add a >=10 row floor as a partial-collapse net. Co-Authored-By: Claude <noreply@anthropic.com>
|
Worktree clean, scratch removed. Here is the review. neutral review - round 2 (final)Head reviewed: Round 1's finding is fixed, and I re-derived both proof cases independently rather than trusting the report. Nothing here is ship-blocking. The three items below are a diagnosability weakness and two nits; all are safe to land as follow-ups or as a trivial amendment if triage wants one. VERDICT: findings 1. minor (NOT ship-blocking) - The file's idiom (confirmed again in round 2) is that stdout-content assertions pass 25 lines to convey one list, and no stdout. That is survivable for B10. It is not survivable for the failure mode a parse-based assertion is most likely to hit: the renderer's row formatting changing while the table is fine. I injected exactly that (case N1, row indent 2 spaces to 3) and the entire diagnostic is: Nothing there distinguishes "the command table is empty" from "the regex stopped matching a perfectly good table", and the run's stdout is gone. Same output for N2 (all summaries empty). This matters more than usual because, as round 1 established and I re-confirmed by grep, this smoke is the sole guard for top-level It also mitigates the one coupling this PR adds. LLP 0009 Exact fix, keeping the parsed rows in the message and restoring the file's idiom for the value: expect.that(
`hypaware --help lists the core command rows (got rows=${helpRows.join(',') || '<none>'})`,
{ rows: helpRows, stdout: helpResult.stdout },
(v) => v.rows.includes('daemon') && v.rows.includes('status') && v.rows.length >= 10
)( 2. nit (NOT ship-blocking) - Settling the 23-versus-24 discrepancy: both numbers are right, and 23 is the one that governs. The row count is install-specific, because The floor is not decoration; I proved it fires (N7: table cut to 6 rows with If you want the floor to do real work: change 3. nit (NOT ship-blocking) - The comment cites // @ref LLP 0009#layered-help [tests]: one row per top-level token is the rendered contract this parsesRound-1 finding, re-derivedFixed. Not fixed-but-with-a-new-problem; the new assertion means what its message says. I re-injected both proof cases myself against this head, ran the real smoke each time, and reverted:
I also independently re-derived the two premises the fix rests on. The regex handles the tightest real case: What this smoke can and cannot now catchEvery case injected into
On N3b and N9, the answers to the questions posed for this round: yes, The two out-of-scope gaps round 1 recorded both still stand.
expect.that(
`hypaware --help lists the command rows sorted (got rows=${helpRows.join(',') || '<none>'})`,
helpRows,
(v) => v.join('\u0000') === [...v].sort().join('\u0000')
)That would make this flow cover both halves of what LLP 0009 actually specifies, instead of one. Not ship-blocking; if triage ships as-is, file it together with the plugin-rows gap. Also checked, clean
|
neutral triage - shipReview budget exhausted with three residual findings. All three are preferences. The one that needed arguing rather than labellingFinding 1 is that the row assertion's failure output never shows the actual The case for treating it as more than a nit is genuine: this is the sole guard What settled it against blocking: in every failure mode either reviewer or triage The sorted check: deferred deliberately, not overlookedLLP 0009 Also settledThe 23-versus-24 row-count discrepancy between the two rounds: both are right and Verified at head |
Summary
package_bin_bootwas red onmaster: it assertedhypaware --helpprints the literal string
hyp - HypAware kernel CLI, whichb9e50ee(Onboarding copy overhaul: explain the product, lead with shared collection (LLP 0211) #711, LLP 0211) replaced with the current onboarding banner.
and the other CLI/help LLPs that touch
dispatch.js'srenderHelp(LLP 0009
#layered-help, LLP 0129, LLP 0214). None of them treat thebanner's marketing sentence as a contract: LLP 0211 documents the fork
wizard intro line and menu copy as decisions, and only mentions the
--helpbanner in passing in its commit message ("README and hyp--help lead with the product, not the architecture"). LLP 0009's ref
on
renderHelpcovers the layout contract (one row per top-levelcommand, subcommands collapsed into group help), not the leading
sentence's exact wording.
what the flow is actually named for (
the packaged binary boots and --help works), not the marketing copy. It now asserts theusage: hyp <command> [args...]line and the presence of known command names(
daemon,status), on top of the exit-0 check that was alreadythere.
deliberate: no LLP claims it as a contract, so no unit test was added
next to the copy either. If a future LLP wants the exact sentence
pinned, that's a new decision, not a reason to keep the boot smoke
pinned to marketing copy.
Change
hypaware-core/smoke/flows/package_bin_boot.js: replaced thehyp - HypAware kernel CLIsubstring assertion with two assertions:the
usage: hyp <command> [args...]line, and that the output listsknown command names.
Verified the new assertion still catches a real break
Temporarily swapped the
usage: hyp <command> [args...]write insrc/core/cli/dispatch.js'srenderHelpfor a sentinel string, rannpm run smoke -- package_bin_boot, confirmed it failed onhypaware --help prints the usage line, then restored the file andconfirmed the smoke passes again.
git statusis clean; onlypackage_bin_boot.jsis changed.Checks (fresh
npm install)npm test: 4029 pass, 1 skipped, 0 failnpm run typecheck: cleannpm run smoke -- package_bin_boot: okFull release battery (CLAUDE.md)
package_bin_bootcli_bundled_plugins_activateddaemon_foreground_start_stopdaemon_install_renderwalkthrough_picker_to_first_queryclient_attach_idempotentgateway_claude_capturegateway_codex_capturehypignore_capture_droplocal_only_export_withholdotel_loopback_capturelocal_parquet_exportstatus_diagnostics12 of 13 green. The one red flow is
walkthrough_picker_to_first_query,already tracked and being fixed by #757; not expanded here per the
issue's instruction to keep this PR scoped to #758.
Deliberately not done
repo-owner decision; walkthrough_picker_to_first_query is red on master, and no smoke runs in CI #750's PR already carries the timing data
(~4.9s for all 13 flows) and a recommendation.
exact banner sentence as a pinned contract (see above).
🤖 Generated with Claude Code