Repository navigation
Conversation
…ead-backs Part of #1671. rollback fresh_presence and the qualification RecordReadback compared values with str(), so a stored null equalled the text "None" and a missing field equalled a null. The on-screen verifier fell back to a selector value when the declared value had no text. The reward worker's db channel is a SQL read, so it now compares numbers by value.
Independent adversarial review: BLOCKFrom: Codex. Reviewed actual head 558d679 in a fresh detached worktree and locked environment. [P1] A failed mapping is still treated as an explicit null during rollback. In An independent probe reproduces this through validated Workflow/UndoStep models, real persisted prior state, and Validation: all eight affected suites pass on Linux, including the campaign fixture that timed out on the Mac: 213 tests passed. Full Flow Ruff/format and mypy pass (591 sources). Seventeen independent behavior probes pass, covering real HTTP qualification reads, null/missing/text selectors and fields, on-screen refusal, and persisted old-null rollback state. The mapped-value case above fails. Synthetic data only, headless execution under the resource gate; no reviewed source or test was changed or pushed. |
Part of #1671 (the "Matching" items that compare a null as text).
What changed
rollback.pyfresh_presence, restore branch: old and new values compare through a new_same_text. A null equals only a null, so a stored null no longer equals the text"None". A field missing from the row or from the kept old values gives "unknown" instead of matching. A new value counts as null only when it is declared null or the run passed the parameter as None. An unset parameter no longer counts as a null new value.qualify/campaign.pyRecordReadback.judge: the selector and the field checks use a new_same_field, with the same rule. The field has to be there, and a null equals only a null.runtime/effects/onscreen.py_expected_text: when afield_equalsdeclares a value that has no text (a null or unset parameter, or""), the check is now INDETERMINATE. Before, it fell back to a selector value, which only showed that the record was on screen.runtime/effects/_common.py:TYPED_SUBSTRATESnow includesdb. That is the reward worker's channel for its SQL read (SqlRecordVerifierover SQLite), so a stored number compares by value there, as it already does forsql.All four changes fail closed or match the existing
record_matchesrule. None of them lets a write pass that passed before only because of the str() compare.How it was tested
tests/test_null_text_matching.py, which covers both helpers and thedbnumber match. There is also a new test intests/test_onscreen_verifier.py.test_qualification_gate_campaign.py. It hit the 600 s pytest-timeout insidecv2.imdecode(vision template match) while the Mac's load average was about 16. I reran one of them alone and it timed out the same way. That fixture runs the benchmark replayer and doesn't touch the changed code, but I couldn't get it to finish locally, so CI has to show it.Not done here
These remaining #1671 items are left open:
undo_proposaldropping None selector keys. Fixing it means carrying nulls through_flat_itemsrequest matching.