fix(run): accept and forward --payable-now; JSON refusal for no-runnable-command - #200
Merged
Merged
Conversation
The "no runnable selat-pay command in pick output" error told the user to "Try --payable-now", but `selat run` rejected that flag as unknown and never forwarded it to the rank.mjs spawn, so following the advice dead-ended in an unknown-flag error. rank.mjs already parses --payable-now on its own (a boolean applied to the catalog before the pick / pin step, independent of `selat search`), so the flag now works end to end: - --payable-now joins KNOWN_RUN_FLAGS, the usage line, and `run --help` - parseRunArgs sets `payableNow`; rankPickArgv forwards it via a new payableNowArgs seam, omitted when absent so default ranking is unchanged - the hint is now noRunnableHintLine(): it recommends --payable-now, and when the caller already passed it, points at the live 402 instead of repeating advice they already followed Adds test/run-payable-now.test.mjs: parse/argv seam pins, help text, a guard that every flag the hint names is one the parser accepts, and a hermetic end-to-end run against a fake rank.mjs that records its argv (--dry-run, fake selat-pay, no session budget — nothing is paid). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BxtLkUJD5kZfi4ggn4YBJE
The "no runnable selat-pay command in pick output" path wrote prose to
stderr and returned 1 even under --json, leaving stdout empty — which
JSON.parse("") turns into a crash at a machine caller. Every other run
failure goes through emitRunError; this one now does too.
Under --json the refusal is {ok:false, error, reason:"no-runnable-command",
detail, hint, payableNow, note?} on stdout, with the retry advice carried
as data (`hint`) and `payableNow` telling the caller whether that retry has
already been tried. Human output is unchanged. Nothing is charged on this
path either way.
Adds a --json case to test/run-payable-now.test.mjs covering the first
refusal and the --payable-now retry.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BxtLkUJD5kZfi4ggn4YBJE
SELAT-DEV
marked this pull request as ready for review
September 24, 2026 18:36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
selat run's "no runnable selat-pay command in pick output" error told the user to "Try --payable-now", butrunrejected that flag as unknown (it was not inKNOWN_RUN_FLAGS) and never forwarded it to therank.mjsspawn, so following the advice dead-ended in an unknown-flag error.rank.mjsalready parses--payable-nowon its own (a boolean applied to the catalog before the pick / pin step, independent ofselat search), so the flag now works end to end.Changes
lib/commands/run.mjs--payable-nowjoinsKNOWN_RUN_FLAGS, the usage line, andrun --help.parseRunArgssetspayableNow;rankPickArgvforwards it via a newpayableNowArgsseam. Omitted when absent, so default ranking argv is unchanged.noRunnableHintLine(): it recommends--payable-now, and when the caller already passed it, points at the live 402 instead of repeating advice they already followed.emitRunError. Under--jsonit is{ok:false, error, reason:"no-runnable-command", detail, hint, payableNow, note?}on stdout instead of prose on stderr with an empty stdout (whichJSON.parse("")crashed at machine callers). Human output is unchanged. Nothing is charged on this path either way.README.md: one sentence on the flag in theselat runrow.Tests
New
test/run-payable-now.test.mjs(10 tests): parse/argv seam pins, help text, a guard that every--flagthe hint names is one the parser accepts, and hermetic end-to-end runs throughbin/selat.mjsagainst a fakerank.mjsthat records its argv. The end-to-end cases reproduce the original loop: the first run hits the refusal with the hint, the retry with--payable-nowreaches the ranker carrying the flag and never sees "unknown flag". A--jsoncase checks the stdout contract. Nothing is paid (--dry-run, fake selat-pay, no session budget).Full suite: 592 pass, 0 fail.
npm run checkclean.🤖 Generated with Claude Code
https://claude.ai/code/session_01BxtLkUJD5kZfi4ggn4YBJE
Generated by Claude Code