Skip to content

fix(cli): split permanent identifier failures out of exit 3 (WI-P1)#96

Merged
tjdwls101010 merged 1 commit into
mainfrom
wi-p1-exit-reclassification
Jul 19, 2026
Merged

fix(cli): split permanent identifier failures out of exit 3 (WI-P1)#96
tjdwls101010 merged 1 commit into
mainfrom
wi-p1-exit-reclassification

Conversation

@tjdwls101010

Copy link
Copy Markdown
Owner

What

exit 3 conflated two unrelated situations, and one of them could not produce an envelope at all.

1. Missing import — a third 0.2.3 refactor regression, live in 0.2.4.
_normalization/unwrap.py raises ParseFailureError but the file-split refactor dropped it from the imports. Every unwrap failure hard-crashed with a NameError traceback instead of the JSON envelope the CLI contract promises. This is the same class as 50169cf and 41bcd43 and it shipped to PyPI in 0.2.4.

2. Bad identifiers were sold as transient failures.
law.go.kr answers a detail lookup for a nonexistent identifier with an empty body ({} on eflawjosub) rather than the 일치하는 … sentence it returns on target=law. That empty body became parse_error → exit 3, whose discipline read 일시적 소스 접근 실패 … 잠시 후 재시도 — advice for a lookup that can never succeed, with 법령 부재 아님 steering the caller away from the actual cause.

How

  • Restore the ParseFailureError import; pin it with a test that fails on a NameError.
  • Classify an empty service body as NoResultErrorno_result (exit 4), with discipline that says a retry is pointless and points at search-*. is_empty_payload is deliberately conservative — only blank scalars and empty containers count, so an unrecognized-but-populated shape still raises ParseFailureError rather than being mislabeled a missing record.
  • Keep parse_error at exit 3 but drop the transient story: retrying an unrecognized shape changes nothing, so the discipline now says to rule out an identifier error first.
  • Catalog and constants.py now state that exit 3 carries two kinds and which one is worth retrying.

Verification

  • get-article --law 999999 제3조 → exit 4, no_result, no "일시 장애" narrative (live).
  • Simulated DNS failure → exit 3, source_access_error, retry discipline intact (live).
  • 344 deterministic tests pass (8 new).

🤖 Generated with Claude Code

Two defects on the same path.

First, unwrap.py raised ParseFailureError without importing it — a third
import-loss regression from the 0.2.3 file-split refactor, still live in
0.2.4 and therefore in the published PyPI latest. Every unwrap failure
produced a NameError traceback instead of the JSON envelope the CLI
contract promises. Restore the import and pin it with a test.

Second, a nonexistent identifier exited 3 with "일시적 … 잠시 후 재시도"
discipline. law.go.kr answers a detail lookup for a bad identifier with an
empty body ({} on eflawjosub), which is a permanent fact about the
identifier — telling the caller to retry sent it into a loop that could
never succeed, and "법령 부재 아님" actively misled it. Classify the empty
body as NoResultError so it emits no_result (exit 4) and steers to
search-*. The empty test is deliberately conservative: a payload we merely
failed to recognize still raises ParseFailureError rather than being
mislabeled a missing record.

exit 3 now carries two kinds and the catalog says so: source_access_error
is the genuinely transient one worth retrying, parse_error is an
unrecognized shape where a retry changes nothing.

Verified live: get-article --law 999999 제3조 → exit 4; a simulated DNS
failure → exit 3 source_access_error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tjdwls101010
tjdwls101010 merged commit 3f3610e into main Jul 19, 2026
5 checks passed
@tjdwls101010
tjdwls101010 deleted the wi-p1-exit-reclassification branch July 19, 2026 13:25
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.

1 participant