Problem
The CLI export script at scripts/export.py:544-548 catches parse failures per-session with except Exception as e: print(..., file=sys.stderr); continue and always exits with code 0, regardless of how many sessions failed. There is no machine-readable distinction between "exported 50 of 50" and "exported 48 of 50, 2 failed."
For scripted use (CI pipelines, cron jobs, monitoring), callers cannot detect data loss in exported archives.
Goal
Add exit-code semantics and a stderr summary to cmd_export() wrapper only — not the shared export engine. The engine (PR1) exposes failure_count; the CLI maps it to process exit codes.
Prerequisites
Merge PR1 first (feat/export-engine — shared export engine with failure_count in result).
git checkout master
git pull
git checkout -b feat/cli-export-exit-codes
Scope
Exit code mapping (in scripts/export.py cmd_export())
| Condition |
Exit code |
| All attempted sessions exported successfully |
0 |
| Some exported, some failed (partial failure) |
2 |
| Zero exported, one or more failures (total failure) |
1 |
Stderr summary
Print at end of bulk export:
Exported N of M sessions (K failed)
Use result.exported_count, result.total_candidates, and result.failure_count from the shared engine.
Preserve existing behavior where appropriate
- Per-session failure: continue exporting remaining sessions (do not remove the catch-and-continue pattern).
- Empty export with no failures (all skipped / nothing new): preserve current "Nothing to export." messaging and exit 0 unless product explicitly wants otherwise — document in PR.
- HTTP API unchanged (still uses JSON error responses, not process exit codes).
sys.exit() logic lives in CLI wrapper, not utils/export_engine.py.
Documentation
Document exit codes in build_parser() epilog and/or README CLI Export section:
Exit codes (export subcommand):
0 — all sessions exported successfully
1 — total failure (no sessions exported; one or more errors)
2 — partial failure (some sessions exported, some failed)
Tests (≥2)
Add to tests/test_cli_e2e.py or new tests/test_cli_export_exit_codes.py:
| Test |
Setup |
Assert |
| Clean export |
Valid session_minimal.jsonl seeded via _seed_base_dir |
returncode == 0 |
| Partial failure |
One valid + one corrupt session file |
returncode == 2; stderr contains failure summary |
Optional third test: all corrupt → returncode == 1.
Use subprocess via existing _run_cli() helper; isolate export state to tmp_path.
Out of Scope
- HTTP export error semantics
- Changing shared engine to call
sys.exit
- JSONL parser split (Thursday)
- Structured logging replacement for
print() warnings (separate backlog item if any)
Acceptance Criteria
Problem
The CLI export script at
scripts/export.py:544-548catches parse failures per-session withexcept Exception as e: print(..., file=sys.stderr); continueand always exits with code 0, regardless of how many sessions failed. There is no machine-readable distinction between "exported 50 of 50" and "exported 48 of 50, 2 failed."For scripted use (CI pipelines, cron jobs, monitoring), callers cannot detect data loss in exported archives.
Goal
Add exit-code semantics and a stderr summary to
cmd_export()wrapper only — not the shared export engine. The engine (PR1) exposesfailure_count; the CLI maps it to process exit codes.Prerequisites
Merge PR1 first (
feat/export-engine— shared export engine withfailure_countin result).Scope
Exit code mapping (in
scripts/export.pycmd_export())Stderr summary
Print at end of bulk export:
Use
result.exported_count,result.total_candidates, andresult.failure_countfrom the shared engine.Preserve existing behavior where appropriate
sys.exit()logic lives in CLI wrapper, notutils/export_engine.py.Documentation
Document exit codes in
build_parser()epilog and/or README CLI Export section:Tests (≥2)
Add to
tests/test_cli_e2e.pyor newtests/test_cli_export_exit_codes.py:session_minimal.jsonlseeded via_seed_base_dirreturncode == 0returncode == 2; stderr contains failure summaryOptional third test: all corrupt →
returncode == 1.Use subprocess via existing
_run_cli()helper; isolate export state totmp_path.Out of Scope
sys.exitprint()warnings (separate backlog item if any)Acceptance Criteria
Exported N of M sessions (K failed)--helpepilog or READMEpytest -qgreen in CI