Skip to content

fix(start): print no-terminal refusals on separate lines (#254) - #255

Merged
memtomem merged 1 commit into
mainfrom
fix/start-no-terminal-multiline-254
Nov 5, 2026
Merged

memtomem merged 1 commit into
mainfrom
fix/start-no-terminal-multiline-254

Conversation

@memtomem

@memtomem memtomem commented Nov 5, 2026

Copy link
Copy Markdown
Owner

Fixes #254.

What changes

Without a terminal, syncmill start and syncmill start -a <agent> (no --task) refuse and list the commands to use instead. Both used to raise a plain ClickException whose text embedded \n before _PROJECT_STEPS. The CLI error boundary escapes a ClickException message to one line, so the guidance came out joined by a literal \x0a. Both now raise _MultilineError with the sentence and each step as separate lines. Each line is still escaped on its own, so a value inside a line cannot start another.

Tests

  • tests/test_start.py checked only "start needs --task" in output, which passed on the broken output. Both refusals are now compared line by line against the exact expected output: Error: <sentence> followed by every _PROJECT_STEPS line, with no \x0a anywhere.
  • New test for the bare-start refusal, which had no text assertion.
  • Both tests fail with the cli.py change reverted and pass with it.

Verification

  • Full suite: suite-run CLEAN on macOS. The helper was tightened afterwards; tests/test_start.py still passes, 34/34.
  • The real binary, run through the [product] Guide the first verified change with an offline trial and task-first entry #171 smoke harness (stdin is a pipe that never closes): both refusals print on separate lines and stdin is never read.
  • Codex review: round 1 SHIP. Its one in-scope Minor was adopted: assert the exact ordered lines rather than membership. Round 2 was unprimed (diff only): SHIP, no findings.

Not in this PR

start --trial failures flatten the same way. TrialError messages join subprocess output and the "trial folder was kept" note with \n, and cli.py wraps them in a plain ClickException. That message carries external output, so each line needs its own escaping; it is filed as a separate issue.

🤖 Generated with Claude Code

Without a terminal, bare `start` and `start` without `--task` raised a
plain ClickException whose text embedded "\n" before the project steps.
The error boundary escapes a ClickException message to one line, so the
guidance came out joined by a literal \x0a. Raise _MultilineError with
the sentence and each step as separate lines instead; each line is still
escaped on its own.

The existing test only checked a substring, so it passed on the broken
output. Both refusals are now asserted line by line against the exact
expected output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@memtomem
memtomem merged commit 902f1c5 into main Nov 5, 2026
11 checks passed
@memtomem
memtomem deleted the fix/start-no-terminal-multiline-254 branch November 5, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

start: no-terminal refusals print \x0a instead of line breaks

1 participant