Skip to content

Patch test op: distinguish JSON boolean from number in Python - #187

Merged
brightbeamarsalan merged 1 commit into
BrightbeamAI:mainfrom
DevChiniwala:fix/patch-test-bool-int-equality
Oct 14, 2026
Merged

brightbeamarsalan merged 1 commit into
BrightbeamAI:mainfrom
DevChiniwala:fix/patch-test-bool-int-equality

Conversation

@DevChiniwala

Copy link
Copy Markdown
Contributor

Summary

  • Bug: The RFC 6902 test operation in both Python references (coordinator-py, chap-analytics) used bare != to compare values. Python's bool is a subclass of int, so True == 1 and False == 0 evaluate as equal — but JSON distinguishes them: true is a boolean, 1 is a number. The TypeScript reference's deepEqual correctly distinguishes types via typeof.
  • Impact: A decide.override carrying a test op that compares a boolean against a number (e.g. {"op": "test", "path": "/confident", "value": true} against actual value 1) succeeds in Python but fails in TypeScript — a cross-language consensus violation that produces divergent audit chains from identical inputs.
  • Fix: Add _json_equal() — a JSON-type-aware deep comparator that checks isinstance(…, bool) before falling through to ==, mirroring the TS deepEqual. Applied in both coordinator-py and chap-analytics. Added 7 conformance vectors (2 accept, 5 reject) to json-patch-vectors.json so both references are held to the same rule.

Reproducer

from chap_coordinator.patch import apply_json_patch
# Before fix: passes (wrong). After fix: raises PatchError (correct).
apply_json_patch({"status": 1}, [{"op": "test", "path": "/status", "value": True}])
import { applyJsonPatch } from './packages/coordinator/src/patch.js';
// Already correct: throws PatchError("'test' failed at /status")
applyJsonPatch({status: 1}, [{op: 'test', path: '/status', value: true}]);

End-to-end through the coordinator: the same decide.override envelope produces {"result": {"state": "completed"}} in Python but {"error": {"code": -32012}} in TypeScript.

Prior art in the repo

The whisper profile's _same_value() (whisper.py:29-42) already handles this pattern correctly — it checks isinstance(a, bool) before comparing. The JSON Patch test op was the remaining site where the check was missing.

Test plan

  • All 44 Python patch conformance tests pass (including 7 new vectors)
  • All 11 TS patch conformance tests pass (including 7 new vectors)
  • 493 Python tests pass, no regressions (1 pre-existing Windows-only Unicode failure)
  • End-to-end decide.override now agrees across both coordinators

🤖 Generated with Claude Code

Python's `bool` is a subclass of `int`, so `True == 1` and
`False == 0` evaluate as equal.  The RFC 6902 `test` operation
used bare `!=` to compare values, which made the Python reference
accept a patch the TypeScript reference correctly rejects — a
cross-language consensus violation that diverges audit chains
when the same `decide.override` is replayed against both
coordinators.

Add `_json_equal()` — a JSON-type-aware deep comparator that
checks `isinstance(…, bool)` before falling through to `==`,
mirroring the TS `deepEqual` which distinguishes via `typeof`.
Apply the fix in both `coordinator-py` and `chap-analytics`.
Add conformance vectors (accept + reject) so both references
are held to the same rule going forward.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 14, 2026 05:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@DevChiniwala

Copy link
Copy Markdown
Contributor Author

The current-head CI workflow is awaiting maintainer approval and has not run. Could a maintainer approve the validation workflow so the shared Python/TypeScript patch vectors can be checked upstream? The Copilot quota notice is not a code-review result, and no automated review pass is being claimed.

@brightbeamarsalan brightbeamarsalan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Dev, good catch. I reproduced it on main: a decide.override whose test op compares true with 1 completed in the Python coordinator and was refused with -32012 in TypeScript. With this change both coordinators refuse it with the same code and message, and the new vectors hold both to that rule. The Python coordinator, chap-analytics and TypeScript patch tests pass locally. I’ll squash-merge once CI is green, and the fix will ship in 0.3.3 and chap-analytics 0.2.4.

@brightbeamarsalan
brightbeamarsalan merged commit fd29f8d into BrightbeamAI:main Oct 14, 2026
16 checks passed
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.

3 participants