Goal
Make framework results, metadata, JSON envelopes, history, and the actual process agree on exit status.
Background
_normalize_command_result() accepts every Python int, including booleans, negative values, and values above 255:
|
def _normalize_command_result(result: Any) -> int: |
|
if result is None: |
|
return ExitCode.SUCCESS |
|
if isinstance(result, int): |
|
return result |
|
raise TypeError(f"Commands must return None or an int exit code; got {type(result).__name__}.") |
. On a Unix process, returning 300 from
run_app() reports 300 in-process, while
SystemExit(300) is observed by the parent shell as 44.
This disagreement is unsafe for scripts, schedulers, and cross-language conformance.
Scope
- Define the portable public exit-code domain.
- Reject or deterministically normalize invalid command results before metadata and envelopes are finalized.
- Apply the same rule to native, attached, async, test, and subprocess entry paths.
Acceptance Criteria
- The documented success/failure/usage/interrupted codes remain stable.
bool, negative integers, and out-of-domain integers cannot produce metadata that disagrees with the process.
- Invalid callback results yield one actionable framework error and a portable failure code.
- Direct
run_app(), testing.invoke(), JSON output, run metadata, history, and real subprocess tests agree.
- Windows and POSIX expectations are explicit.
Validation
Add boundary cases for -1, 0, 1, 130, 255, 256, 300, True, and non-integers; run the OS matrix.
Non-Goals
Do not redefine consumer-specific meanings for otherwise valid non-zero codes.
Project Fields
- Status: Backlog
- Priority: P1
- Area: CLI
- Initiative: v1.0 Readiness
- Size: S
Ownership
Goal
Make framework results, metadata, JSON envelopes, history, and the actual process agree on exit status.
Background
_normalize_command_result()accepts every Pythonint, including booleans, negative values, and values above 255:base-cli/lib/python/base_cli/_run.py
Lines 309 to 314 in 8a93d22
run_app()reports 300 in-process, whileSystemExit(300)is observed by the parent shell as 44.This disagreement is unsafe for scripts, schedulers, and cross-language conformance.
Scope
Acceptance Criteria
bool, negative integers, and out-of-domain integers cannot produce metadata that disagrees with the process.run_app(),testing.invoke(), JSON output, run metadata, history, and real subprocess tests agree.Validation
Add boundary cases for -1, 0, 1, 130, 255, 256, 300,
True, and non-integers; run the OS matrix.Non-Goals
Do not redefine consumer-specific meanings for otherwise valid non-zero codes.
Project Fields
Ownership