Skip to content

fix(templates): the rekey pair and the transition announce commit with their writes - #6869

Merged
delchev merged 3 commits into
masterfrom
fix/outbox-glue-sweep
Aug 20, 2026
Merged

fix(templates): the rekey pair and the transition announce commit with their writes#6869
delchev merged 3 commits into
masterfrom
fix/outbox-glue-sweep

Conversation

@delchev

@delchev delchev commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Part one of #6867 — the single-transaction write+publish pairs that contradicted #6854's contract ("announce an entity change through its repository, not by publishing next to it") and remained live instances of #6816's failure class: row committed, event lost for good when the broker is briefly down, downstream reactions silently never run.

What changes

  • Engine (data-store-java): JavaEntityStore/JavaRepository gain the event-carrying overloads save(entity, topic, additionalEvents) and updateProperties(id, values, topic, additionalEvents) — mirroring the trio update already has. Additional events are recorded in the outbox inside the write's own transaction, and only when the row actually existed to be written.
  • DAO template: the targeted -rekeyed pair (fix(intent): re-parenting repairs both sides, from every write path #6845) no longer publishes beside the write. One mutation records two bodies — the row as it stood (naming the group it left) and the row as written (naming the one it joined) — through the base write. The only reason it was a bare publish was the single-topic limitation the overload removes; the template's own comment said so. A model without grouping keys regenerates byte-identically.
  • Transition template: updateProperty + post-commit re-read + bare publish becomes one targeted write carrying the -transitioned topic — flip and announcement commit together, and the payload is the row exactly as the statement left it. The reload stays only for the HTTP response and the notify block.

Deliberately not here (reasoning on #6867)

  • Resolve.java.templateFix discarding identification and audit #6841 rewrites that exact region; it converts in a follow-up the moment Fix discarding identification and audit #6841 lands, to avoid crossing an open PR.
  • The five deferred/ordered announces (SetField, Writer, Numbering, StepEvent via Process.executeAfterCommit, and Generate's after-items completion announce) — write-attachment would regress the Flowable chain-visibility ordering fixes; they need a durable-enqueue primitive, designed as its own PR.

Verification

  • JavaEventOutboxIT gains a retarget phase: one targeted mutation, two notices, both delivered and both cleared from the outbox.
  • IntentEmissionCoverageIT pins the new contract, including assertFalse on any bare Producer.sendToTopic in a generated repository or transition controller.
  • ModelGenerationIT green (Velocity renders, the fix(templates): EntityController.java no longer gains a stray blank line without unique constraints (#6823) #6835 consecutive-blank-lines guard holds).
  • data-store-java unit tests and the release-profile javadoc gate green; formatter validated.

Refs #6867, follows #6854/#6816, touches the outputs of #6845.

🤖 Generated with Claude Code

delchev and others added 3 commits August 20, 2026 10:42
…h their writes (#6867)

#6854 established the contract - announce an entity change by handing the
topic to the WRITE, never by publishing beside it - and converted the
generated repository's main paths. Two write+publish pairs survived it,
each a live instance of #6816's failure class (row committed, event lost
for good when the broker is briefly down, downstream reactions silently
never run):

- The DAO's targeted "-rekeyed" pair (#6845): one mutation had to record
  TWO bodies - the row as it stood, naming the group it left, and the row
  as written, naming the one it joined - and the targeted write could
  carry only one topic. The store and JavaRepository gain the
  event-carrying overloads (save and updateProperties with
  additionalEvents, mirroring update's trio), recorded only when the row
  actually existed to be written; the template hands both notices to the
  base write. A model without grouping keys regenerates byte-identically.

- The transition controller (Transition.java.template): updateProperty +
  post-commit re-read + bare publish becomes one targeted write carrying
  the "-transitioned" topic, so the flip and its announcement commit
  together and the event payload is the row exactly as the statement left
  it - never a re-read a concurrent write could have moved on. The reload
  stays only for the HTTP response and the notify block.

Deliberately NOT converted here, with the reasoning on #6867: the five
deferred/ordered announces (SetField, Writer, Numbering, StepEvent via
Process.executeAfterCommit - deferred so consumers observe the whole
synchronous Flowable chain's writes, which write-attachment would regress
- and Generate's completion announce, ordered after the target and its
items across several transactions). Those need a durable-enqueue
primitive, a follow-up of its own. Resolve.java.template follows once
#6841, which rewrites that exact region, lands.

JavaEventOutboxIT gains the retarget phase - one targeted mutation, two
notices, both delivered and both cleared - and IntentEmissionCoverageIT
pins the new contract, including that no bare Producer.sendToTopic
remains in a generated repository or transition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…6867)

With #6841 merged, the lookup's routing status was a targeted
updateProperty followed by a post-commit re-read and a bare publish of
"-transitioned" - the same write-beside-publish pair the rest of this
branch removes. The topic now rides the routing write into the outbox:
the flip and its announcement commit together, the payload is the row
exactly as the statement left it, and a rejected move (the
ValidationException the routing write catches) records no event at all -
a status the record could not take is not a transition.

The emission oracles pin the new contract: IntentEngineIT's two-writes
ordering assertion anchors on the topic-carrying updateProperties, and
IntentEmissionCoverageIT's resolve block asserts the topic on the write
and refuses any bare Producer.sendToTopic in the generated lookup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@delchev
delchev merged commit 9774d7a into master Aug 20, 2026
10 checks passed
@delchev
delchev deleted the fix/outbox-glue-sweep branch August 20, 2026 10:44
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