feat(flow): object-write can pass an item through unwritten (skipWhen) - #2592
Merged
Conversation
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.
Contributor
Quality Report — ConductionNL/openregister @
|
| 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.
Contributor
Quality Report — ConductionNL/openregister @
|
| 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.
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.
Why filter cannot do this job
The decomposed synchronization now decides
skipfor 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 intoobject-write.openregister.filterwould DROP it — andopenconnector.contract-sweepdecides 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, becauseSaveObject::updateObject()stamps it unconditionally.What this adds
skipWhen— a dot-path on the item. When it resolves totrueor the stringskip, that item is emitted unchanged and unwritten: no write, no cap consumption, position and identity preserved. Honoured by both the per-item and thebulkpath; an all-skipped bulk page never callssaveObjects()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 stringskip— 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 hasexcludePathsthat 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)
SynchronizationFlowGeneratorshould then emitskipWhen: "contract.outcome"on itsobject-writenode, which completes task 2.3's zero-write re-run. openconnector #1297's partial-idempotency caveat and #1296'stest.fail()can both be revisited once that lands.🤖 Generated with Claude Code