Skip to content

intent: a create-from's items where:/refuse: refusal is decided before the header save (#7224) - #7277

Merged
delchev merged 2 commits into
masterfrom
fix/7224-refuse-before-header-save
Sep 10, 2026
Merged

intent: a create-from's items where:/refuse: refusal is decided before the header save (#7224)#7277
delchev merged 2 commits into
masterfrom
fix/7224-refuse-before-header-save

Conversation

@ThuF

@ThuF ThuF commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

#7164 (#7091) added items: where: + refuse: to a create-from, and both refusals - "an unqualified row exists" and "no row qualifies, the target would have no lines" - were evaluated after the target header had been saved. The header row itself is rolled back by the UnitOfWork, but two things the generated repository does inside save() deliberately live outside the unit (#7069): the document-number allocation (the counter is a sequence in its own transaction, so concurrent creates never serialize on it) and the History create entry. Every refused click therefore spent a number of a "continuous, never auto-reset" series and left a trail row for a document that never existed - pressing "Invoice this month" three times on a month with one rejected timesheet burned three invoice numbers.

Fix

Both refusal queries depend on nothing but the source rows, so Generate.java.template now runs the whole rule block - the narrowed item query, the refuse: scan and the empty-result check - right after the at-most-once guard, before the target header is built and saved. It stays inside the unit; the clone loop below the save reads the same qualifying list. A refused click now writes nothing at all, which is what the template already promised for a missing required prompt: input. The comment carries the reasoning; the generated class's Javadoc states the guarantee.

Tests

IntentEmissionCoverageIT:

  • The fixture's Bill - the target of both rule-carrying create-froms - now declares history: true and a number: field on a new Emission Bill series in the authored .numbers.
  • Emission layer: both refusals precede BillRepository().save(target) in the generated classes.
  • Runtime layer: the count of CREATE rows in EMISSION_BILL_HISTORY is unchanged across the two refused runs (CREATE rows only, because the BillFlow process writes its ProcessId back asynchronously), and the next Bill minted takes the very next number after the last successful one.

Green locally with the fix (239 s). With the original template swapped back in, the IT fails at the new emission-order assertion. formatter:validate green.

Docs

.claude/docs/client-java.md, .claude/docs/intent-layer.md, engine-intent/CLAUDE.md, intent-assistant-guide.md - one sentence each on the ordering rule.

Fixes #7224

🤖 Generated with Claude Code

…e the header save (#7224)

The two refusals of a create-from's source-row rule ran after the target
header was saved. The unit of work took the header back, but the document
number allocation and the History create entry live outside it by design,
so every refused click spent a number of a gap-free series and left a trail
row for a document that never existed. The rule block now runs right after
the at-most-once guard, before the target is built; IntentEmissionCoverageIT
asserts the emitted order and, end to end, that a refused run leaves the
Bill series counter and the Bill history trail unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ThuF
ThuF force-pushed the fix/7224-refuse-before-header-save branch from fd87883 to a457bb2 Compare September 10, 2026 07:46
/** The number the Bill series hands out right after the given one: same prefix, same width. */
private static String nextBillNumber(String number) {
String digits = number.substring(BILL_NUMBER_PREFIX.length());
return BILL_NUMBER_PREFIX + String.format("%0" + digits.length() + "d", Integer.parseInt(digits) + 1);
…ore-header-save

# Conflicts:
#	.claude/docs/intent-layer.md
@delchev
delchev merged commit 83b87d7 into master Sep 10, 2026
10 checks passed
@delchev
delchev deleted the fix/7224-refuse-before-header-save branch September 10, 2026 10:37
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.

intent: a create-from's items where:/refuse: refusal fires AFTER the header save - every refused click burns a document number and a history row

3 participants