Conversation
…spension A Z-Wave lock whose verification read-back times out reports writes and clears as unknown, and its cache keeps showing the old state. Those are now unconfirmed rather than failed: the slot says so, retries on a doubling wait capped at an hour, and is never suspended for it. Nothing is inferred about what the lock holds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 714b734ac436
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1577 +/- ##
========================================
Coverage 99.23% 99.24%
========================================
Files 66 66
Lines 8935 9038 +103
Branches 530 531 +1
========================================
+ Hits 8867 8970 +103
Misses 68 68
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
A believed value given up after a failed read is withdrawn, so the slot no longer reads in sync on the strength of the write alone. The unconfirmed flag carries its PIN and is dropped while the slot is in sync, so it cannot hold up a different PIN. The retry wait is keyed on the target and time, so an outage in between does not skip it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 54fd0bca1fc1
…ed back Restoring the value from before the write could restore another believed value, and on a lock that hides its codes the old unreadable value matched the last PIN written anyway. The address now stays unverified until a read, push or clear speaks for it, which sync already refuses to call in sync. The retry wait's exponent is bounded so a lock that never confirms does not overflow it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 61e94f4a0909
…believed value A clear that found nothing to clear, and a write sync dropped when the target changed, both cleared the unverified mark, so re-enabling a slot showed it in sync on the write alone. A dropped believed write now stays unverified, a clear that changed something replaces it with empty, and a push provider's confirmed write settles it explicitly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 3a736458ddc3
A clear the stack could not verify left nothing pending, so nothing read it back and a clear that landed waited out the retry. Sync now reads that slot back right away. A slot the lock has already shown in sync is no longer marked unconfirmed on the way there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: a425a616fa5c
With the slot's value unverified and nothing for the clear to remove, sync retried the clear on every tick. It now reads the slot back and, if that cannot settle it, waits like any unconfirmed clear. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: acffc1286540
…ead error The slot was marked unconfirmed before its read-back, so a slow read could let another tick send a second clear meanwhile; it now stays syncing until the read returns. The read-back also catches unexpected errors, which would otherwise leave the slot stuck syncing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 76bf786c8a3e
The driver's cache can keep showing the slot's old code, so for a write the stack could not verify that read is judged like an empty one: waited out, then unconfirmed. Writing a different target ends the old wait, so switching back to the earlier PIN is not held. A manager discards an unconfirmed flag left from before it started. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 12f32561cab4
…ified A same-PIN direct write whose confirmation read failed marked a slot the lock had already confirmed as unverified, so it showed Unconfirmed and was rewritten needlessly. Only a believed value that displaced something the lock had not reported leaves the slot unverified. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 38ffbe84e53d
|
Closing in favour of a simpler model. This PR treats an unverifiable write as something that must eventually be resolved — a seventh The replacement model makes "unverifiable" a resting state rather than a pending one. A write that returns without error is recorded as believed, and belief is erased only by an observation that actually contradicts it — never by silence or by a failed read. So there is nothing to time out and nothing to back off from:
That removes Branch and worktree are left intact. Superseded by the optimistic/confirmed credential-state work. |
Proposed change
A smaller replacement for #1575, for the loop in #1307 (and #1397 before it).
A Z-Wave lock without the Supervision command class is verified by the driver reading the code back after each write. On a lossy link that read often times out, so the driver reports the outcome as unknown (
credential_rejected_unknown, mapped toWriteResult.OPTIMISTIC) and its cache keeps showing the old state. The coordinator gave the write up at its deadline, sync charged the slot's breaker, and three laps later the slot was suspended, although the PIN worked at the keypad.#1575 tried to work out what the lock held despite the broken read path, which took a pile of rules to get right. This PR doesn't guess:
take_unconfirmed_write). Sync doesn't charge the slot breaker for it; writes the stack did vouch for (a polled lock's service accepting it) are still charged, as before.LockOperationUnconfirmed(aLockOperationFailed) instead of a plain failure, and sync treats it the same way. Nothing is pending for a clear, so sync reads that slot back right away (a hard refresh of the one slot on a push lock); a clear that landed settles there and then. The slot stays syncing while that read runs, so no other tick starts meanwhile, and a read that fails in any way changes nothing.unconfirmed, shows on the in-sync sensor'ssync_statusand in the slot card ("Unconfirmed"). The sensor is off: nothing has shown the lock holds the code. Ranked just afterout_of_syncwhen statuses fold across credentials.UNCONFIRMED_RETRY_MAX); the exponent is bounded too, so a lock that never confirms is retried hourly indefinitely. A changed PIN or enabled state is a new target and is tried at once, and writing it ends the old wait, so switching back to the earlier PIN is tried at once too. The count resets once the slot is seen in sync.settle_pending). Sync never calls an unverified slot in sync, so the slot can't read in sync on the strength of the write alone, including on locks that hide their codes (where the last PIN written would otherwise match).pending_confirmationtranslation forsync_statusis added alongsideunconfirmed.Behaviour change: a lock whose writes land but whose reads never return used to end in a suspension and a repair. It now shows
unconfirmedand keeps retrying, at most hourly, without a repair. Only Z-Wave returns an unconfirmed outcome today, and only forERROR_UNKNOWN.Accepted trade-off: while a slot waits, a read showing a different code doesn't cut the wait short. On this link that is often the driver's cache still showing the slot's old code, and retrying on it would bring back the loop this PR removes. A code set at the keypad on such a slot can stay for up to the current wait (at most an hour) before it is overwritten.
Also accepted: an unverified slot is settled by any read or push that mentions it, including a read from the driver's cache. A cache that still shows the slot empty (or its old code) then drives a retry, which is the point; one that shows a hidden code on a lock that masks its codes can settle the slot as in sync, which is no weaker than any read of such a lock.
Known gap, unchanged from
main: once an unconfirmed write is given up, the slot reads empty, so disabling that user sends no clear, and if the write did land, the code keeps working. Fixing that needs a reliable way to address the credential when the driver's cache doesn't show it, which #1575 tried with a remembered user and couldn't get right. A follow-up could use the fixed per-slot user on User Code CC locks, if node-zwave-js guarantees one; I haven't confirmed that.Tests:
mainit is, after three writes); it showsunconfirmedwith three to five writes, and goes in sync, with nothing written again, once the cache shows the code.mainit is suspended after three writes).unconfirmedand keeps retrying, instead of reading in sync.unconfirmed.unconfirmed; the slot card renders it.Full suite green at 100% coverage; TypeScript tests green.
Type of change
Additional information
🤖 Generated with Claude Code