Uh oh!
There was an error while loading. Please reload this page.
feat(interactive): the guided ? line carries a label, not just an answer (cli#504) - #518
Conversation
…nswer (cli#504)
The guided flow built its prompter as `surveyPrompter{bare: true}`, which set
survey's Message to "". The prompt line rendered as a lone `?` — and since #505
started pre-filling answers, as `? [~/mydata]`: a question mark, a bracket and a
path, with no verb.
The bare mode's premise was sound (the CLI already prints `Step 3 of 4 · Where
is your data?`, so repeating it on the `?` line would duplicate it) but the
conclusion was not — and the codebase already said so. `Confirm` refused to go
bare because "a bare `? (y/N)` there would be a label-less destructive prompt";
that objection was never Confirm-specific.
Each guided prompt now passes a short noun label: the shortest noun phrase that
names the answer, with a trailing colon. `? Path: ~/mydata`, `? Task:
tabular_classification`, `? Column types:`. The header still asks the question;
the label says what you are typing into. The label-column question keeps its
two wordings on both lines — `Label:` for a class, `Target:` for a numeric
value — so the branches stay distinguishable on the prompt line too.
`bare` is deleted rather than left unused, so no future call site can reach the
label-less rendering.
Flows with no step header of their own (client create, delete, resources set)
are untouched: they still pass the whole question, which is right for them.
Tests: the ~110 scripted answers keyed by prompt label are rekeyed across
interactive_test.go, copy_catalog_test.go and task_scope_test.go (the issue's
file list missed the third; path_examples_test.go turned out to key on
nothing). Two assertions were rewritten rather than rekeyed, because rekeying
would have made them vacuous: the #181 file-or-folder copy check now reads the
PRINTED step (a short label cannot carry that sentence), and the MLM
no-label-question check names both `Label:` and `Target:` instead of matching a
shared "Which column holds" stem that no longer exists.
New guard TestRunInteractive_EveryGuidedPromptCarriesAShortLabel drives the
real flow across seven scenarios and asserts a property of whatever it asks —
non-empty, ends in ':', carries no '?', within a 16-rune budget — with the
confirm asserted to be the opposite (a whole question). Nothing is scripted by
label, so there is no list agreeing with itself; zero recorded prompts is a
failure, not a pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>version-bump-gate failed this PR: v0.10.8 is already released and the diff touches published paths (internal/*). The release train reads VERSION and cuts the tag from it — it never bumps for you, so leaving it stale does not fail here, it fails the next prod hop days later on somebody else (backend#1561). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
saadqbal
left a comment
There was a problem hiding this comment.
Careful, well-tested PR 👍 The label is pure presentation — it becomes survey's Message and never touches answer capture (promptLabelColumn's resolution still keys on supplied/headers), so the stored values are unchanged; the golden diff bears that out (? Path: ~/data/patients — same answer, label prepended). Nice that bare is deleted rather than left dead, that Label:/Target: and the header wording stay in lockstep, and that the #504 guard derives labels from the real flow instead of restating them. Verified go test ./internal/cli/ green.
LukasWodka
commented
Aug 19, 2026
/fr-pass Advanced at @LukasWodka's explicit direction. Held in the automated pass for a verification gap (interactive/TTY, GPU-only, or journey-dependent while e2e journey is red — backend#2206); Lukas is accepting that gap for this card. |
Closes part 1 of #504. Part 2 (answer recall + Tab completion) is deliberately not here — see Why this is split at the bottom.
The problem
The guided flow built its prompter as
surveyPrompter{bare: true}, which set survey'sMessageto"". The prompt line rendered as a lone?— and since #505 started pre-filling answers from the command line, as? [~/mydata]: a question mark, a bracket and a path, with no verb.The bare mode's premise was sound (the CLI already prints the question as a
Step 3 of 4 · Where is your data?header, so repeating it on the?line would duplicate it). The conclusion was not, and the codebase already said so —Confirmrefused to go bare because "a bare? (y/N)there would be a label-less destructive prompt". That objection was never Confirm-specific.Before / after
One prompt line, as the user sees it (path step, with
~/mydatapre-filled from the command line):And the accept-the-default case, which was the worst of them — a bare
?alone on the line:The rule
The label is the shortest noun phrase that names the answer, with a trailing colon. The header asks the question; the label says what you are typing into.
Step 1 of 4 · Do you want to ingest training or test data?Split:Step 2 of 4 · Please name the dataset.Name:Step 3 of 4 · Where is your data?Path:Step 4 of 4 · What kind of machine learning task is this data for?Task:What kind of data is this?Data type:Which column holds the label?Label:Which column holds the value to predict?Target:How many keypoints per sample?Keypoints:Image resolutionResolution:Column typesColumn types:Label policyLabel policy:Time columnTime column:Three deliberate calls:
bareis deleted, not left unused. A field nobody sets is a rendering a future call site can still reach. With it gone,surveyPrompterhas no way to blank the Message.? Proceed with the ingest? (y/N)). It has no header of its own, and the overwrite-replace confirm fires during the cluster phase with nothing printed before it —? Overwrite: (y/N)would name the noun and hide the stakes. This asymmetry is now asserted, not just commented.Label:vsTarget:stay distinct. Collapsing both toLabel:would have been tidier and would have quietly costTestRunInteractive_RegressionLabelWordingits teeth.Flows with no step header of their own (
client create,delete,resources set) are untouched — they still pass the whole question, which is right for them. The register follows the header, not the prompter.Test churn
~110 scripted answers are keyed by prompt label, so the labels rekey them. The rekeying is mechanical and lands in three files, not the two the issue named:
interactive_test.go— 73task_scope_test.go— 20 (not in the issue's list; it arrived with feat(ingest): guided mode always asks, pre-filled — never skips a question #505)copy_catalog_test.go— 16path_examples_test.go— 0 (it keys on nothing; the issue's list was cautious here)Two assertions were rewritten rather than rekeyed, because rekeying them would have left a check that nothing can fail:
TestRunInteractive_PathPromptCopyIsFileOrFolderasserted the data ingest: accept a file or a folder + instruction clarity + path bugs (RFC-0002 phase 3) #181 "file or folder" copy by searching the prompt label. A short noun cannot carry that sentence, so the check would have passed forever. It now reads the printed step — where the copy actually lives — and asserts both that the new wording is present and the old folder-only wording is gone.TestRunInteractive_MLMSkipsLabelmatchedstrings.HasPrefix(l, "Which column holds"), a stem the two label-column spellings shared.Label:andTarget:share no stem, so it now names both.New guard
TestRunInteractive_EveryGuidedPromptCarriesAShortLabeldrives the realrunInteractiveacross seven scenarios (one per family, plus the variants that unlock the extras: regression → label policy, time-to-event → time column, keypoints → count + resolution, self-supervised text → no label, ambiguous layout → the data-type question) and asserts a property of whatever the flow asks::?(it is a label, not a question)The confirm label is asserted to be the opposite — a whole question.
Two things it deliberately does not do:
runDataIngestArgs. So the recorded labels are the flow's, not the test's, and a question added later is covered without editing the test.t.Fatalf, not a green run — every property above is trivially true of an empty slice.Each property is its own
if, not aswitch. A full question violates several at once; aswitchreports only the first, which would leave the length and?checks never exercised by any mutation and therefore unproven. (That is exactly what the first draft did, and the mutation run below is how it was caught.)Mutation evidence
Each mutation applied to the real production code, test run, then restored.
pr.Input("Path:", …)→pr.Input("", …)— the exact old bare-mode defect?line would have no verbpr.Select("Task:", …)→pr.Select("Which task?", …)pr.Select("Split:", …)→ the full 44-rune questionpr.Confirm("Proceed with the ingest?", …)→pr.Confirm("Proceed:", …)"Target:"→"Label:"TestRunInteractive_RegressionLabelWording: 3 assertions, incl.LabelColumn = "age"— the wrong-column defect the wording protects againstTestRunInteractive_PathPromptCopyIsFileOrFolder: both directions (lost the new copy, restored the old)fakePrompter.answerstops recording intoaskedMutation 3 is the one that mattered: under the original
switchit reported only the colon failure, so the?and length properties were passing without ever being reachable. Independentifs fixed that, and the table above is the re-run.Golden diff — every line accounted for
01-data-ingest.golden— every? <answer>becomes? <label> <answer>, matching how survey rendersMessage+ space + answer. The one shape change is the accept-the-default line:?→? Column types:. The catalog's own description gains a sentence about the label. No copy is lost.zz-all-strings.golden— 10 additions, 1 removal:"Column types:","Data type:","Keypoints:","Label policy:","Name:","Path:","Resolution:","Split:","Task:","Time column:"."Which task?"— and it is the only removal. It was reachable by the AST harvester only as thepr.Selectlabel, because unlike every other guided question it has noPromptStep/Sectiontwin: the task step's header is"What kind of machine learning task is this data for?", which is still there (unchanged, line 352 of the golden). So the question is gone from the source, not hidden from the backstop.Everything else in that file is untouched. Notably the other question strings —
"Please name the dataset.","Where is your data?","What kind of data is this?","Column types","Image resolution","Label policy","Time column"— all stay, because each is also a header literal. That is why the removal count is 1 and not 8.Known, pre-existing gap, not introduced here:
"Label:"and"Target:"do not appear in the backstop. They are held in a local variable before reachingpromptLabelColumn, and the harvester only collects string literals passed directly to a copy call — the same reason the questions"Which column holds the label?"/"…the value to predict?"and their twodeschints were already absent before this PR.Label:is visible in01-data-ingest.golden(rendered as a screen), and both labels are covered by the new guard plus the regression-wording test. Widening the harvester to follow variables is a separate change; happy to file it if you want it.Docs
STYLE.mdsaid the prompt line is "answer-only (? train) … the prompter runsbare". This PR makes that false, so it is updated in the same PR (org standard): the spacing rule keeps its section, and a new Guided-prompt labels section carries the rule, the label table, the confirm exception, and a pointer to the test that enforces it rather than restating it as prose.Verification
make check— greenmake check-all— green (full CI set: vet, tests + coverage gate, errcheck, ineffassign, misspell, staticcheck, golangci-lint0 issues, schema check, govulncheckNo vulnerabilities found, file budget, deadcode, style guard, tool pins)go test ./...— greenWhy this is split
Part 2 of #504 (remember the previous run's answers as defaults; Tab completion for the path prompt via survey's
Suggest) is materially bigger than this: it needs a new state file under the tracebloc config dir, wiring intotracebloc deleteso it is wiped like the rest, and a widened prompter seam. Landing it here would bury a ~110-line mechanical rekey under a behaviour change. It follows as its own PR.🤖 Generated with Claude Code
Note
Low Risk
CLI prompt UX and test/golden churn only; ingest logic and cluster behavior are unchanged.
Overview
Fixes cli#504: guided
tb data ingestno longer renders a bare?or? [~/mydata]when defaults are pre-filled.surveyPrompter'sbaremode is removed; survey'sMessageis always set. Step headers still carry the full question; the?line uses short noun labels (Split:,Path:,Task:,Label:/Target:, etc.).STYLE.mddocuments guided-prompt labels (confirm prompts stay full questions). Tests and copy-catalog goldens are rekeyed to label-based prompter keys;TestRunInteractive_EveryGuidedPromptCarriesAShortLabeldrives the real flow and asserts non-empty labels ending in:, no?in labels, and a length budget. Version 0.10.9.Reviewed by Cursor Bugbot for commit 699b8f5. Bugbot is set up for automated code reviews on this repo. Configure here.