Add a preflight plan that binds the conversion to the files it reviewed - #46
Merged
Conversation
-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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase D — preflight
-WhatIfanswers "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.-PlanWrites 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:
-ApplyCarries 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
-Applyverifies 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,-Fromand-Backupare rejected under-Applyrather than silently ignored.-Backupis 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 nowUnambiguous, matching theStructurallyDeterminedreason already recorded alongside.Conversion behaviour is unchanged — only
TextChanginggates 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
-Fromnor 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-rthat 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