Skip to content

feat(flow): object-write can pass an item through unwritten (skipWhen) - #2592

Merged
rubenvdlinde merged 3 commits into
developmentfrom
feat/object-write-skip-when
Aug 19, 2026
Merged

feat(flow): object-write can pass an item through unwritten (skipWhen)#2592
rubenvdlinde merged 3 commits into
developmentfrom
feat/object-write-skip-when

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Why filter cannot do this job

The decomposed synchronization now decides skip for an unchanged object and commits zero contracts on a re-run. It still rewrote every target object, because a skipped item has to keep flowing into object-write.

openregister.filter would DROP it — and openconnector.contract-sweep decides what to delete from the target ids its items carry. So dropping an unchanged item is exactly what makes the next sweep delete the object that was fine. That hazard is why skips were still being written.

And reaching the write at all moves @self.updated, because SaveObject::updateObject() stamps it unconditionally.

What this adds

skipWhen — a dot-path on the item. When it resolves to true or the string skip, that item is emitted unchanged and unwritten: no write, no cap consumption, position and identity preserved. Honoured by both the per-item and the bulk path; an all-skipped bulk page never calls saveObjects() at all.

Absent, behaviour is byte-identical to today. This node is heavily guarded and widely used, so the guard is strictly opt-in.

Truthiness is deliberately narrow: true, or the string skip — the vocabulary the contract step already stamps. "false" and "0" are truthy in PHP and must not silence a write; there is a test for exactly that.

Verification

Six new cases, all positive-controlled: neutering isSkipped() back to the pre-fix behaviour fails 5 of the 8 skip tests.

581 flow tests green. phpcs, phpmd and phpstan clean — phpstan positive-controlled with a deliberate strlen(array) to prove the file is genuinely analysed rather than silently excluded (this repo has excludePaths that have produced exactly that false [OK]).

writeBulk() crossed phpmd's length and complexity thresholds once the skip branch landed, so the row planning and the pass-through were extracted into their own methods rather than baselined.

Follow-up (openconnector)

SynchronizationFlowGenerator should then emit skipWhen: "contract.outcome" on its object-write node, which completes task 2.3's zero-write re-run. openconnector #1297's partial-idempotency caveat and #1296's test.fail() can both be revisited once that lands.

🤖 Generated with Claude Code

Conduction Release Bot added 3 commits August 19, 2026 21:15
The decomposed synchronization can now decide skip for an unchanged object
and commits zero contracts on a re-run, but it still rewrote every target
OBJECT — because a skipped item has to keep flowing into object-write.
openregister.filter would DROP it, and openconnector.contract-sweep decides
what to delete from the target ids its items carry, so dropping an unchanged
item is exactly what makes the next sweep delete the object that was fine.
Reaching the write at all moves @self.updated, because SaveObject stamps it
unconditionally.

skipWhen is the missing primitive: pass through, do not drop. No write, no
cap, position and identity preserved, in both the per-item and bulk paths.
An all-skipped bulk page calls saveObjects() not at all.

Truthiness is deliberately narrow — true, or the string 'skip', which is the
vocabulary the contract step already stamps. 'false' and '0' are truthy in
PHP and must not silence a write.

Absent, behaviour is byte-identical to today.
Six cases: a skipped item is emitted but not written; a mixed page writes
only the unskipped and keeps every item in order; skips do not consume the
write cap; only a deliberate value skips ('false' and '0' are truthy in PHP
and must NOT silence a write); with skipWhen unset nothing changes; and in
bulk a skipped row is not sent, with an all-skipped page never calling
saveObjects() at all.

Positive control: neutering isSkipped() to the pre-fix behaviour fails 5 of
the 8 skip tests.

The configKeys vocabulary pin caught the new key, as designed, and is
updated. writeBulk() crossed phpmd's length and complexity thresholds once
the skip branch landed, so the row planning and the pass-through are now
their own methods rather than a suppression.

581 flow tests green; phpcs, phpmd, phpstan clean — phpstan positive-
controlled with a deliberate strlen(array) to prove the file is analysed
rather than silently excluded.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ a71142f

Check PHP Vue Security License Tests
lint ⏭️
phpcs ⏭️
phpmd ⏭️
psalm ⏭️
phpstan ⏭️
phpmetrics ⏭️
eslint ⏭️
stylelint ⏭️
build ⏭️
composer ⏭️ ⏭️
npm ⏭️ ⏭️
app:check-code ⏭️
info.xml ⏭️
REUSE ⏭️
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-19 22:12 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ c25f7b3

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-specs
test-l10n
format
composer ✅ 175/175
npm ✅ 528/528
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-19 23:05 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit 0d0b0a4 into development Aug 19, 2026
44 checks passed
@rubenvdlinde
rubenvdlinde deleted the feat/object-write-skip-when branch August 19, 2026 23:06
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