Repository navigation
Record the leaf a run sent and never got back - #205
Merged
SearheiParkhamchuk merged 1 commit intoOct 21, 2026
Merged
SearheiParkhamchuk merged 1 commit into
SearheiParkhamchuk merged 1 commit into
Conversation
A run used to claim every leaf it collected, including the ones the provider
never answered. The pipeline now reports an outcome per leaf it sent —
`LeafOutcome { idPath, answered }` in place of `translatedPaths` — and the
applicator returns what it wrote, so a leaf counts as answered only when every
chunk it was cut into came back and was written.
That correction was half a cure. A leaf sent and unanswered with no earlier
claim was recorded as DECLINED, which is deliberately inert: it never reads as
out of date, so under skip_existing it was never selected again. The
reconciler has already seated the source text in its target, so the field is
not empty either, and nothing else would have picked it up. The leaf was lost.
A receipt now records a third state for that leaf. UNANSWERED reads as
drifted, so the next run selects it again, and it clears the moment a run
answers. The column is plain text and cannot hold that rule, so the parse
schema and the types carry it instead: SourceHash is branded, and the stored
map is validated against the three forms it may hold. Without the brand a
reader that drops the new state still compiles; with it, the compiler refuses.
Closes #197
SearheiParkhamchuk
requested review from
ChiefCreator and
dogfrogfog
as code owners
October 21, 2026 19:23
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
🎉 This PR is included in version 1.0.0-next.3 🎉 The release is available on npm package (@next dist-tag) Your semantic-release bot 📦🚀 |
This branch had an error being deployed
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.
What was wrong
A run claimed every leaf it collected. If the provider answered three fields out of five, the
receipt recorded a source hash for all five, and the two it never answered read as freshly
translated for good.
Correcting that exposed a second, worse fault underneath it. A leaf sent and unanswered with no
earlier claim — a first run, a legacy document-shaped receipt, a newly appeared address — was
recorded as
DECLINED. That value is deliberately inert:matchesClaimreturnstruefor it andleafSourceChangedreturnsundefined, so the leaf never reads as out of date. Underskip_existingit was never selected again, and it was not empty either, because the reconciler hadalready seated the source text in its target. Nothing would ever pick it up.
What this changes
The pipeline reports an outcome per leaf it sent.
PipelineResult.translatedPaths: IdPath[]becomes
leaves: readonly LeafOutcome[], one entry per leaf that yielded a chunk. The applicatorreturns the chunks it actually wrote instead of a caller re-deriving it, so
LeafOutcome.answeredInFullis true only when every chunk a leaf was cut into came back and waswritten. A leaf that produced no chunk is absent, which is not the same as unanswered.
A receipt records a third state.
UNANSWEREDreads as drifted, so the next run selects the leafagain, and it clears the moment a run answers it. A leaf the run never sent keeps whatever the last
receipt claimed; a leaf the run sent and lost overwrites an earlier hash, because the target now
holds source text rather than that translation.
The rule lives in the schema and the types, because the column cannot hold it. The fingerprint
column is plain text.
SourceHashis branded and minted in one place, the stored map is validatedagainst the three forms it may hold, and
staleness.tsbranches on a taggedClaimrather than ona raw value. That combination is load-bearing, not decoration: with the brand removed, deleting the
UNANSWEREDbranch fromclaimOfcompiles silently; with it, the compiler refuses.Backward compatibility was checked rather than assumed. Per-leaf values have only ever been 16
lowercase hex or
null— one commit overcomputeFieldFingerprints.ts, one writer. The legacy64-hex digest lives in the
documentarm and is left unvalidated.Not solved here
translatedAt, so the admin reads"translated just now" over a run that translated nothing.
skip_existing, a leaf the provider deterministically never answers is now re-sent everyrun. Repeated spend in place of silent permanence — the trade this change makes on purpose.
Checks
179 files / 2398 checks ·
check-types0 ·oxlint44 warnings, 0 errors ·turbo run buildgreen, which is the real
tscdeclaration emit rather thantsgo.Four audits ran over the finished change — complexity, abstraction, a stub-and-mutation audit of the
suite, and comments. Two of them found checks that were green for the wrong reason:
toEqualignores keys whose value is
undefined, so a mutation removing a filter killed nothing; and one pindid not test what its text claimed, because
nullkept the directive in use whether or not thebrand was there. Both are fixed, and the full findings are in
docs/plans/2026-10-21-the-leaf-that-never-came-back.task.md.Closes #197