Skip to content

Add a preflight plan that binds the conversion to the files it reviewed - #46

Merged
amrali-eg merged 1 commit into
masterfrom
feat/preflight-plan
Aug 26, 2026
Merged

Add a preflight plan that binds the conversion to the files it reviewed#46
amrali-eg merged 1 commit into
masterfrom
feat/preflight-plan

Conversation

@amrali-eg

Copy link
Copy Markdown
Owner

Phase D — preflight

-WhatIf answers "what would happen", then throws the answer away. The real conversion detects everything a second time, and a second pass over changed bytes can reach different conclusions than the one the user read and approved. The preview was a demonstration, not a promise.

-Plan

Writes the answer down and changes nothing. For every file: the action, the source encoding, whether it was detected or specified, whether the bytes identify it uniquely, which encodings compete for it, and the reason behind any refusal — as JSON, plus a summary on stdout whose parts sum to the whole:

Selected:                     3

Will convert:                 2
  encoding determined:        2
  same text either way:       0
Already in target encoding:   0
Encoding not identified:      0
Refused, ambiguous encoding:  1
Refused, unreadable:          0

Backups:                      enabled
Target:                       utf-8 without BOM

No files modified.

-Apply

Carries out that plan without detecting anything again.

What makes it a promise is the binding, not the preview. Every scheduled file carries the SHA-256 it had when the plan was made, and -Apply verifies all of them before writing anything. If any file changed or was deleted in between, nothing is converted — not even the files that still match. A plan reviewed as a whole belongs to the directory it was reviewed against, and the files most likely to have changed are the ones something else is actively writing.

-BasePath, -Target, -From and -Backup are rejected under -Apply rather than silently ignored. -Backup is the one that matters: accepting it would let a user write what reads as an instruction to keep the originals and get a run that does not.

A classifier fix this surfaced

The ambiguity classifier had been collapsing two different situations. A file whose encoding is settled by its own structure — valid Shift_JIS, valid UTF-8 — was reported as TextEquivalent, i.e. "several codecs, same text". But the dismissed codecs do read those bytes differently; they were set aside for having no hold on them, not for agreeing. Those files are now Unambiguous, matching the StructurallyDetermined reason already recorded alongside.

Conversion behaviour is unchanged — only TextChanging gates a conversion — but the plan is the first thing that shows the class to a user, and it was stating something false about the file.

Also

The README documented neither -From nor the ambiguity refusal; both shipped in #45 without it. Added, along with the preflight section, and the refusal example is the tool's real output rather than a paraphrase.

Tests

17 new (ConversionPlanTests), 373 passing. They pin: planning writes nothing; applying converts exactly what was previewed; a changed or deleted file invalidates the plan whole; applying uses the plan's encoding rather than re-detecting (a file planned -From koi8-r that detects as windows-1252 comes out as koi8-r); a refusal is recorded and never converted; the backup choice comes from the plan; an unreadable or future-version plan is refused rather than guessed at.

Not in scope, still tracked: GUI source-encoding override (Phase E), conversion journal (Phase F).

🤖 Generated with Claude Code

-WhatIf answers "what would happen", then throws the answer away. The real
conversion detects everything a second time, and a second pass over changed
bytes can reach different conclusions than the one the user read and approved.
So the preview was a demonstration, not a promise.

-Plan writes the answer down: for every file, the action, the source encoding,
whether it was detected or specified, whether the bytes identify it uniquely,
which encodings compete for it, and the reason behind any refusal. -Apply
carries out that plan without detecting anything again.

What makes it a promise is the binding. Every scheduled file carries the
SHA-256 it had when the plan was made, and -Apply verifies all of them before
writing anything. If any file changed or was deleted in between, nothing is
converted - not even the files that still match. A plan reviewed as a whole
belongs to the directory it was reviewed against, and the files most likely to
have changed are the ones something else is actively writing.

-BasePath, -Target, -From and -Backup are rejected under -Apply rather than
silently ignored. -Backup is the one that matters: accepting it would let a
user write what reads as an instruction to keep the originals and get a run
that does not.

Also separates two things the ambiguity classifier had been collapsing. A file
whose encoding is settled by its own structure was being reported as
TextEquivalent - "several codecs, same text" - when the dismissed codecs do
read those bytes differently and were set aside for having no hold on them, not
for agreeing. Conversion behaviour is unchanged, since only TextChanging gates
it, but the plan is the first thing to show the class to a user, and it was
stating something false about the file.

Documents -From and the ambiguity refusal in the README, which had gone in
without them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@amrali-eg
amrali-eg merged commit 5815f93 into master Aug 26, 2026
1 check passed
@amrali-eg
amrali-eg deleted the feat/preflight-plan branch August 26, 2026 21:54
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