fix(intent): an expansion reconciles its child set instead of rebuilding it - #6847
Merged
Conversation
delchev
force-pushed
the
fix/expansion-diff-not-rebuild
branch
from
August 19, 2026 13:07
c41b539 to
30a537a
Compare
…ing it Closes #6817. The generated expansions handler regenerated a master's child rows by deleting every existing row and then recreating the set. A client-Java handler has no transaction boundary - each delete and each save commits on its own - so a failure partway through the recreation left the deletes committed with only some of the inserts applied: rows gone for good, a stale count column, and roll-ups that had consumed a shrink which never really happened. Nothing read as inconsistent, because no individual write failed. The handler now diffs: a period with no row gains one, a row whose period fell out of the span is deleted, and a row the span still covers is kept - same row, same identifier, with whatever was edited on it. A second row on an already kept period is deleted, which is what makes a set left inconsistent by an earlier partial run converge. A failure can now leave the set incomplete, never destroy a row the span still wants; and it is cheaper, since most span edits move an edge. With spread, a kept row's share is derived from the master's total and the row count, so it is re-spread through updateDerived - the targeted write that still publishes "-updated", so a roll-up over these rows recomputes exactly as it did when the row was deleted and recreated. Asserted on both layers: IntentEngineIT on the generated source, and IntentEmissionCoverageIT at runtime - an edited span must answer with the same row identifiers for the days it still covers, re-spread shares and a count that followed the new row count. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
delchev
force-pushed
the
fix/expansion-diff-not-rebuild
branch
from
August 19, 2026 13:30
30a537a to
6d7741b
Compare
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.
Closes #6817.
The failure
The generated
expansionshandler regenerated a master's child rows by deleting every existing row and then recreating the set. A client-Java handler has no transaction boundary — eachdeleteand eachsavecommits on its own — so a failure partway through the recreation left the deletes committed with only some of the inserts applied: rows gone for good, a stale count column, and roll-ups that had consumed a shrink which never really happened. Nothing read as inconsistent, because no individual write failed. The template's own repair check only ran on a later delivery, i.e. "when a human edits the master again".The fix — diff, don't rebuild
Expansion.java.templatenow reconciles:A failure can now leave the set incomplete — repaired on the next delivery — but can no longer destroy a row the span still wants. It is also cheaper: most span edits move an edge, so most rows were being deleted and recreated identically.
With
spread, a kept row's share is derived from the master's total and the row count, so it is re-spread in place throughupdateDerived— the targeted single-column write that still publishes"-updated", so a roll-up over these rows recomputes exactly as it did when the row was deleted and recreated. The last-row rounding remainder is unchanged.The descriptor gained
childPk(the child's primary key) — that is what addresses a kept row for the in-place re-spread — carried throughGlueGenerator.bindExpansion.The interim option in the issue (build the new set first, delete afterwards) is not taken: the diff removes the destructive window entirely rather than trading it for recoverable duplicates. A real transaction boundary for client-Java handlers remains the open follow-up the issue mentions; this handler needs none.
Tests
GlueExpansionsTest(new) — the descriptor carries the reconciliation coordinates,childPkincluded: a missing one would emitchild.with no member and take the whole client-Java batch down with it.IntentEngineIT.expansion_generates_the_span_handlers_and_the_status_badge_stack— the generated source keeps the surviving rows, deletes only what fell out of the span, re-spreads throughupdateDerived, and no longer contains the unconditional wipe.IntentEmissionCoverageIT— aStay/StayNightexpansion is now part of the published-app fixture, so the handler is compiled by the realjavacbatch and the runtime promise is asserted: a three-day span expands into three rows, the span is edited to move one day out and two days in, and the two surviving days must answer with the identifiers they had — plus shares re-spread to 75.00 over four nights and a count write-back of 4. (Run against the pre-fix template, this fails with a new identifier for a surviving day, which is the bug.)Both ITs verified locally (H2, headless), the changed modules' unit suites are green (692 tests in
engine-intent),formatter:validatepasses with a cold cache, and-P releasejavadoc builds clean on both changed Java modules.Docs: dirigible-io/dirigible-io.github.io#206 (merged); spec proposal IntentFile/intent-specification#45 and IntentFile/intentfile.github.io#36 are open. The in-repo
intent-assistant-guide.md, module README andExpansionIntentjavadoc are updated here.🤖 Generated with Claude Code