Skip to content

refactor: type the EOF-action seam with a sealed union#22

Merged
lesnik512 merged 4 commits into
mainfrom
feat/type-eof-action-seam
Jun 26, 2026
Merged

refactor: type the EOF-action seam with a sealed union#22
lesnik512 merged 4 commits into
mainfrom
feat/type-eof-action-seam

Conversation

@lesnik512

Copy link
Copy Markdown
Member

What & why

A behavior-preserving deepening (architecture-review Candidate 2): replace the stringly-typed EOF-action seam in eof_fixer/fixer.py with a sealed union.

Before: _detect_trailing -> tuple[str, int] with magic strings "none"/"append_lf"/"truncate" and an offset meaningful only for truncate; fix_file branched on the strings with a comment-only else: # truncate.

After: _detect_trailing -> _EofAction = Noop | AppendLf | Truncate(offset) (frozen dataclasses; offset only on Truncate — illegal states unrepresentable), consumed by an exhaustive match. Magic strings and the dummy offset are gone. No behavior change.

Exhaustiveness

The match is closed with case _: _assert_never(action), pinning completeness at the match site: an unhandled future variant is a type error there (verified — ty reports Expected Never, found <variant> & ~Noop & ~AppendLf & ~Truncate), not merely via the return type.

_assert_never is vendored locally (def _assert_never(value: NoReturn) -> NoReturn) because typing.assert_never is 3.11+ and the package supports 3.10. Rationale + revisit trigger recorded in planning/decisions/2026-06-26-keep-py310-vendored-assert-never.md (revisit when 3.10 EOLs, 2026-10-31).

Tests

Behavior-preserving, so existing tests stay green; added one coverage test (test_check_mode_truncate_does_not_write) for the Truncate-under---check branch that the per-case if not check split created. 36 passed, 100% coverage; just lint-ci clean (ruff select=["ALL"], ty).

Planning artifacts (Full lane)

  • Spec + plan: planning/changes/2026-06-26.03-type-eof-action-seam/
  • Decision: planning/decisions/2026-06-26-keep-py310-vendored-assert-never.md
  • Architecture promotion: architecture/eof-normalization.md

Built via subagent-driven TDD (Task 1 review SHIP; final whole-branch review Ready to merge: Yes, which surfaced the structural-exhaustiveness subtlety now hardened here).

🤖 Generated with Claude Code

lesnik512 and others added 4 commits June 26, 2026 22:34
Full-lane bundle 2026-06-26.03-type-eof-action-seam: replace the
stringly-typed _detect_trailing/fix_file seam with a sealed union
(Noop | AppendLf | Truncate) consumed by an exhaustive match;
exhaustiveness enforced by ty via fix_file's bool return (no assert_never,
3.10-safe). Behavior-preserving; two-task plan with complete code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a vendored `_assert_never(value: NoReturn) -> NoReturn` guard and a
`case _: _assert_never(action)` wildcard arm in `fix_file`, so ty errors
at the match site (not the return type) when a future `_EofAction` variant
goes unhandled. Python 3.10 precludes `typing.assert_never` (3.11+);
decision doc records the rationale and 2026-10-31 revisit trigger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lesnik512 lesnik512 merged commit beee62d into main Jun 26, 2026
6 checks passed
@lesnik512 lesnik512 deleted the feat/type-eof-action-seam branch June 26, 2026 20:02
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