Skip to content

fix(scripts): answer a missing --files with a usage message, not a stack trace - #117

Merged
os-elon merged 1 commit into
mainfrom
claude/issue-113-ownership-usage-error
Aug 18, 2026
Merged

fix(scripts): answer a missing --files with a usage message, not a stack trace#117
os-elon merged 1 commit into
mainfrom
claude/issue-113-ownership-usage-error

Conversation

@os-elon

@os-elonos-elon commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Fixes#113

Run without --files, this gate threw an uncaught Error: Node printed the source frame plus a seven-line stack and its own version banner. The message was right and the exit code was already 1 — the shape was wrong. A stack trace says "this script is broken" when the truth is "you called it wrong", and it misled exactly that way once: a dispatch prompt listed the bare command as a check to run clean, and the trace read as a regression until it was diffed against main.

Argument validation now throws a UsageError, and the entry point catches that class only — message and usage line on stderr, no stack, still exit 1. Every other throw is rethrown untouched, so an unparseable i18n.ts, an unreadable list file or a bug in the check keeps the stack a real fault needs.

Missing required argument — before and after

Before:

$ node .github/scripts/check-translation-ownership.mjs
file:///home/user/objectos-issue-113/.github/scripts/check-translation-ownership.mjs:65
if (!listFile) throw new Error('--files path is required');
^
Error: --files path is required
at main (file:///…/check-translation-ownership.mjs:65:24)
at file:///…/check-translation-ownership.mjs:116:1
at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
Node.js v22.22.2
[exit 1]

After:

$ node .github/scripts/check-translation-ownership.mjs
✗ --files path is required
usage: node .github/scripts/check-translation-ownership.mjs --actor login --files path
--actor login PR author login (workflows pass github.event.pull_request.user.login)
--files path file listing one changed path per line (git diff --name-only)
[exit 1]

(The two placeholders are angle-bracketed in the real source and the real output; GitHub's body sanitizer eats that token, which is also why the card's own quote of line 65 is missing it.)

Same result for the other three shapes of the same mistake: no arguments at all, --actor alone, --files with no value, and --files= with an empty value.

It still fails. Exiting 0 on a missing required argument would turn a misinvocation into a silent pass, which is the species of dishonest green this repo removed from the test step in #112.

Correct invocations are byte-identical

Twelve invocations were recorded on main and again on this branch and diffed: four inert-path shapes (TRANSLATION_BOT_LOGIN unset), four enforcing-path shapes with the variable set (human clean, human with artifacts rejected, bot artifacts-only accepted, bot mixed rejected), the --files= spelling, and three shapes with --actor absent. Zero diff lines across all of them — the only changes in the transcript are the four usage-error shapes above.

A genuine internal failure keeps its stack, verified with a --files path that does not exist: same node:fs:440 source frame, same ENOENT properties, same version banner, only the line numbers moved as the file grew.

Deliberately not in this PR

  • --actor is left alone. It is documented as required but implemented as optional ((value('actor') ?? '').trim()), so omitting it does not crash — it silently classifies the run as a human PR. That is a different defect from this card's, and requiring it would flip an invocation that exits 0 today into exit 1 through the inert path that The ownership check is inert on main until TRANSLATION_BOT_LOGIN is set #68 owns. Measured and filed as check-translation-ownership.mjs accepts a missing --actor silently and then judges the PR anyway #116 with options.
  • No self-test mode, so tools/ci-scripts/run-self-tests.mjs and its SELF_TESTED list are untouched — the runner's own comment names this script as deliberately not run there. Its guard was exercised anyway: turbo run test executed for real on this edit (.github/scripts/** is an input to that task) and passed.
  • No change to ownership logic, the TRANSLATION_BOT_LOGIN inertness, or the workflow. .github/workflows/translations.yml passes both flags, so the one real caller never reaches the changed path.

Gates, at b48bc8c

gateresult
pnpm install --frozen-lockfile0
pnpm turbo run type-check --continue0
pnpm turbo run build0
pnpm turbo run test0 — first run after the edit: 0 cached, 1 total, real execution, 20 self-test cases
node .github/scripts/check-translations.mjs0, gate passed
node .github/scripts/check-translation-ownership.mjs --actor os-elon --files changed.txt0, inert report
node .github/scripts/check-translation-output.mjs --files changed.txt0, 127 pre-existing findings reported

…ack trace
`check-translation-ownership.mjs` run without `--files` threw an uncaught
Error: Node printed the source frame plus a seven-line stack, and the
reader's first conclusion was "this script is broken" rather than "I
called it wrong". It misled exactly that way once already — a dispatch
prompt listed the bare command as a check to run and the stack read as a
regression until it was diffed against main.
Argument validation now throws a UsageError and the entry point catches
that class only: message plus the usage line on stderr, no stack, still
exit 1. A misinvocation stays a failure — exiting 0 would turn it into a
silent pass. Every other throw is rethrown untouched, so a genuine
internal fault (unparseable i18n.ts, unreadable list file, a bug here)
keeps the stack it needs.
No change to what the gate accepts, rejects or reports when it is invoked
correctly: all eight correct-invocation shapes, on both the inert and the
enforcing path, are byte-identical before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Yaqu7kmKZM3tRPd9Y4xivo
Sign up for freeto 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.

check-translation-ownership.mjs answers a missing --files with a stack trace

1 participant

@os-elon