fix(state): never drop manual flags — non-destructive sync, scoped accept-edits - #4
Merged
Merged
Conversation
…s, accept-edits scoped
Three extraction-era flaws made .i18n-state churn unrelated keys across
consuming-app PRs:
- sync_state! replaced every target entry with {source_hash} only, wiping
target_hash and manual: true app-wide (the pre-extraction bin/translate
preserved both). Sync now merges: it refreshes source_hash and leaves
hand-edit protection untouched.
- translate --force-key rewrote the state entry without the manual flag —
and the outdated validator's suggestion pushed agents to run exactly that.
The flag now survives retranslation; no command removes manual: true.
- accept-edits stamped manual: true on every key of a locale. Unscoped it
now accepts only the keys the manual_edits validator flags; --key KEY
(repeatable) accepts specific keys; --all keeps the blanket for adoption.
StateStore#save also skips byte-identical files so unrelated namespace
files are never rewritten, and validator suggestions are key-scoped so
they are safe to follow verbatim.
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.
Problem
In consuming-app PRs (e.g. getzazu/app#2918), locallingo churned state unrelated to the PR:
manual: trueremoved from hand-curated keys, added to unrelated namespaces, inconsistently across PRs.This was not a version regression — the core files were unchanged since the
bin/translateextraction (b007b34). Three extraction-era flaws interacted:lingo syncdestroyed state. It replaced every target entry with{source_hash}only, wipingtarget_hash+manual: trueapp-wide. The pre-extractionbin/translatesync preserved both (entry["manual"] = true if locale_state.dig(key, "manual")); the extraction dropped that line. zazu auto-runsbin/lingo syncfrom a Claude Code Stop hook whenever locales change, so the wipe fired silently in unrelated PRs.translate --force-keydroppedmanualwhen rewriting the entry — and the outdated validator's suggestion text told agents to run exactly that command.accept-editsblanket-stampedmanual: trueon every key of every namespace, and the manual-edits validator suggested that blanket command for a single drifted key.Which way flags flipped depended on which command ran last on a branch — hence the "random" churn.
Fix
sync_state!merges: refreshessource_hash, preservestarget_hash/manual. Hand-edit drift stays visible until explicitly accepted (deliberate divergence from the old script, which silently absorbed edits unprotected).update_locale_statecarriesmanual: truethrough retranslation. No command removes the flag anymore — unprotecting requires editing the state JSON by hand.accept-editsscoped: unscoped accepts exactly the keys themanual_editsvalidator flags;--key KEY(repeatable) for surgical accepts;--allfor the old blanket (initial adoption).StateStore#saveskips byte-identical files — unrelated namespace files are never rewritten.outdatedon a manual key now says hand-update + re-accept instead of pushing machine translation over curated text.rake release[x.y.z]owns it.Verification
bin/lingo syncon a clean tree: zero fr/af churn (previously stripped thousands ofmanual/target_hashlines); only legitimate additions of previously untracked en hashes.validate --strictflags it with the scoped suggestion → running that suggestion verbatim changed exactly one entry in one file, everything else byte-identical.Follow-up in getzazu/app (after release)
Bump the gem, update agent docs referencing blanket
accept-editsto the--keyform, and optionally reconsider the Stop hook (sync, even fixed, masksoutdateddetection for en-only edits made without translating in the same turn).