Skip to content

DRAFT: T041, U-0: opendox.health_contract, the finding vocabulary, and the first copy of the finding shape (plan 038) - #97

Draft
brettheap wants to merge 18 commits into
mainfrom
r2/t041-health-contract
Draft

brettheap wants to merge 18 commits into
mainfrom
r2/t041-health-contract

Conversation

@brettheap

@brettheap brettheap commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Lane: openxfactory-4 (openXfactory-4-openDox_extraction)

Arc: neutral-product-standalone-operability

Lands after T027 (Brett, 6035546708). Brett's word, "Write now, land after T027 (Recommended)", holds this PR as a DRAFT until T027 pins phase 4's openDox-code commit (N-6 (a), unchanged for landing). Before it lands it merges main and re-runs its gates. The holder claimed it on opensoft/openxFactory#656 as 6035574389.

T041, U-0: the contract module's finding vocabulary, and the first copy (plan 038)

The contract is T041's entry in opensoft/openxFactory specs/038-opendox-document-tool-self-maintenance/tasks.md, re-read at main 40d6e5c1. Bookkeeping batch 3 rewrote its Falsifier line, which is quoted from there below.

  • Realizes: 14.6 (part: spellings), 15.2 (part: shape), 7.1's addendum (part).
  • Falsifier (tasks.md at 40d6e5c1): "new tests/test_health_contract.py, which holds a response-shape test of identity asserting exactly the bound above on the module's OWN serialized finding shape: no key named excerpt, text, content or quote, no number, every string at most 200 characters, {category, entry} for a pathless finding and {collided_id} for a collision (the holder, 6018624750; FR-011). The same bound is asserted on health list --json by T046 and on the HTTP Health response by T057, because a contract-module test cannot see what the CLI or HTTP layer emits (the holder, 6027706377 item 1); tests/test_validator_input_set.py green at the new commit."
    • This PR asserts the bound on what to_json() writes, which is the module's own serialization. health list --json (T046) and the HTTP Health response (T057) are not built here.
  • Ruled: R2Q10, R2Q18, R2Q22, R2Q25; the holder's 6018624750, 6065680005, 6069024023, 6069507373 (T046 item 2), 6072086385 (items 1 and 4), 6072197564 (NAMES (b) to (e)), 6073087924 and 6082100803 (the pathless entry's form), 6086098003 (items 1, 3 (b) and 4), 6088484643 (the canonical text) 6088732352 (item 6: (a) accepted, (b) ascii(), (c) encoded), 6089449884 (item 7, accepted), 6090537166 (item 8: (a) and (b) fixed, two limits accepted) and 6103336585 (item 9: NIT 1 after T027, NIT 2 accepted). Decisions: N-3, N-13, N-15, OQ-H15-19.
  • Readiness map: lane-coord-034/r2/R2-INV-P5.md § T041. Its FIX-IN-SLICE items are both done, and its NOTES are kept (below).
  • Main merged: 6827cafc (T012 + T013: the landing seam, governance, confirmation and the neutral lander; feature 007's guard admits the landing's fast-forward (plan 038) #90), by a merge commit and never a rebase. T012 + T013: the landing seam, governance, confirmation and the neutral lander; feature 007's guard admits the landing's fast-forward (plan 038) #90 touches no file of T041's.

The falsifier, run

python -m pytest -v tests/test_health_contract.py -k "serialized_identity or never_serialized" at this head:

tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[a link target as written] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[a pair of paths] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[a heading key] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[every value an identity admits] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[an empty identity] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[a pathless finding] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[a pathless finding about an entry] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[a collision] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[a refused manifest, the whole file] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[a refused dispositions file] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[the disappearance of a pathed finding] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[the disappearance of a pathless finding] PASSED
tests/test_health_contract.py::test_the_serialized_identity_holds_the_schemas_bound[a pathless collision] PASSED
tests/test_health_contract.py::test_an_identity_outside_the_bound_is_never_serialized[identity-names-no-text-0] PASSED
... 41 more cases of test_an_identity_outside_the_bound_is_never_serialized, each PASSED ...
55 passed, 422 deselected in 0.44s
  • The serialization half. Each identity the contract names goes through make_finding() and to_json() and is read back, including the engine's own and both forms of the disappearance re-raise. The test then asserts, independently of the module's own constants:
    • no forbidden key at any depth;
    • no number at any depth;
    • every key and string at most 200 characters;
    • {category, entry} for a pathless finding and {collided_id} for a collision;
    • the identity is emitted unchanged, and the id is the hash of that identity.
  • The refusal half. Each identity outside the bound is refused by make_finding(). The same identity, put by hand into a finding with the right id, is refused by to_json(). So nothing this module serializes can carry it.

tests/test_validator_input_set.py and tests/test_health_contract.py together at this head: 509 passed.

Red first

The tests were written before the code. Against the unchanged tree:

  • tests/test_health_contract.py failed to collect: ImportError: cannot import name 'health_contract' from 'opendox'.
  • tests/test_validator_input_set.py failed against the old record: 7 failed, 25 passed. The seven were the digest test, the set test, the finding-shape test, three record-refusal cases and the shipped-record control.

Each later round was red first too:

  • review round 1's locator cases failed on 6f85bee0, 3 failed, one with OverflowError: int too large to convert to float;
  • the single-hash-path test and the bounded-where tests failed against the module before f9cc4b0, 6 failed (the 4 that passed are the cases already within the bound);
  • the U+0000 cases failed against f9cc4b0's module, 19 failed. The 3 that passed there are the message, path and pack_version rows, which the schema's own control-character rules already refused;
  • the NAMES rulings' cases failed against 0e24062's module, 14 failed. The new where rows pass there: they kill a mutant, and the bound was already right;
  • the entry-form cases failed against c620dacd's module, 10 failed;
  • this round's cases (6082100803, 6086098003, 6088484643) failed against 4cc065ad's module, 35 failed;
  • 6088732352 (b) changes a spelling, not a digest. The previous fallback, repr() encoded with backslashreplace, spells every value exactly as ascii() does, so the new row for a non-ASCII key in a mixed-type mapping passes against 4ffc411b's module too. What pins the ruling is its mutant, repr() put back with a strict ASCII encode, which is killed;
  • item 8's cases (6090537166) failed against 471b7231's module, 7 of 7: the four ids past the int-digit limit raised ValueError, the 50,000-level value took the old text, the !!set row took the hash-ordered str(), and the set digested under four PYTHONHASHSEED values gave more than one entry.

Mutants: 108 of 108 killed

Each mutant changes one spot of the module, or of contracts/__init__.py for the last one. The named tests then run, and the file is restored byte for byte. The runner is kept outside the tree, and this is its fourteenth run, at this head, in a separate local clone so the full suite's tree is never edited.

what the mutant does killed
the id hash ignores identity, kind, pack_id or path (4); it escapes non-ASCII; it leaves keys unsorted; it keeps 15 digits 7 of 7
the 40/41 boundary: NAME_MAX set to 41 or 39; >=; the cap dropped in the name check and in the id check; the id check reads the pack only 6 of 6
the 200-character bound: set to 201; >=; dropped for strings, message, locator.target and keys; lone surrogates admitted 7 of 7
each forbidden key admitted (excerpt, text, content, quote); the forbidden-key check dropped 5 of 5
a number admitted: any number; floats; booleans read as numbers; NaN in evidence; a non-JSON scalar admitted 5 of 5
the identity cap: >=; +1; dropped; set to 4096; counted in characters, not bytes 5 of 5
nesting: >=; dropped 2 of 2
the three reference rules: names dropped, names checking the pack only, names checking the kind only, hash dropped, span dropped, span refusing a one-line span, references judging broken parts 7 of 7
conditional forms: pathless form dropped; collision form dropped; each human-only rule dropped; any category admitted 5 of 5
the rest: unknown field admitted; missing field admitted; a .. path admitted; U+2028 and U+2029 admitted in message; evidence default dropped; to_json() and make_finding() unchecked; make_finding() returning its input 8 of 8
review round 1: the locator integer guard dropped; _writable() admitting every integer; the evidence integer guard dropped 3 of 3
the holder's 6069024023: either added category missing; every pack_id opendox; fetch-failed out of the entry categories; a kind that is not its category; the disappearance always, or never, {disappeared_id}; its entry dropped; its original unchecked; ENGINE_KINDS without the disappearance (three earlier mutants of engine_finding()'s own entry check went with the check, n6 and e3 below) 10 of 10
lane 3's REVIEW-W1 EARLY: the non-text field-name refusal dropped 1 of 1
the holder's 6069507373 and lane 3's REVIEW-W1: the reference rule hashing directly, and make_finding() hashing directly, instead of through finding_id(); where unbounded; its bound off by one; a stopped pointer extending again 5 of 5
the holder's 6072086385 item 4: U+0000 admitted in a key, in a string value, or in a locator target; _NUL read as U+0001; the rule out of ENGINE_RULES 5 of 5
the holder's 6072197564 and lane 3's REVIEW-W1 at f9cc4b0c: the pathless categories open, or without identity-collision (b); an install-level entry admitted by check_finding(), or by pathless_identity(), or no-sandbox not install-level (e); id among the stamped fields (d); a re-raise taking identity.category for the original's kind; an index pointer that does not stop 8 of 8
lane 3's REVIEW-W1 at c620dacd, as the holder ruled it: a collision admitting an entry 1 of 1
the holder's 6082100803: a per-entry category admitting an empty entry (where lane 3's e3 now lands), the reserved opendox, or 41 characters, or its bound off by one; engine_finding() taking every pack_id as the entry 5 of 5
6086098003 and 6088484643: refused-patch out of ENGINE_KINDS; refused_entry() giving no id a non-empty entry, writing a 201-character id as written, writing any string as written, leaving keys unsorted, dropping default=str, dropping the ascii() fallback, putting repr() back in its place (6088732352 (b)), or letting a RecursionError out 9 of 9
the holder's 6090537166 item 8: the second fallback missing ValueError (a); sets spelled by str(), and sets in hash order (b) 3 of 3
the record admits the four without the finding shape (tests/test_validator_input_set.py) 1 of 1

Lane 3's two survivors at 4cc065ad, n6 and e3, were equivalent mutants of dead guards. The guards were REMOVED, not tested: no test can kill a mutant of a guard that never decides anything. n6 removed the bool guard before _writable() in _locator: a bool is an int Python can always write, and _line() refuses it next (the line_end: True row). e3 admitted an empty entry in engine_finding()'s own check, which pathless_identity() already made dead; after 6082100803 the whole check was dead. The empty-entry refusal now lives once, in _entry_breaks_form(), where its mutant is killed.

The first run left one survivor, the names rule checking the pack only, and test_the_id_names_the_findings_own_pack_and_its_own_kind now kills it. Lane 3's early review found a second, the non-text field-name refusal, and test_a_field_name_that_is_not_text_is_refused now kills it.

The full suite, as CI runs it

At 4ffc411b, this head's parent, run as CI's validate job runs it (python -m pytest -q --junitxml, against Postgres 16 through host.docker.internal):

6267 passed, 11 skipped, 2 warnings in 2555.97s (0:42:35)
  • From the JUnit report: 6278 selected, 6267 passed, 11 skipped, 0 failed, 0 errors, exit code 0.

  • Against the pins: 6278 selected (at least 3977), 6267 passed (at least 3966), and exactly 11 skipped.

  • At 471b7231 (which adds only 6088732352 (b) over 4ffc411b): 6279 selected, 6267 passed, 11 skipped, 1 failed, 1 failed, 6267 passed, 11 skipped, 2 warnings in 1791.73s. The one failure is the known load-sensitive timing test tests_runtime/test_migrations_apply.py::test_the_draft_page_budget_does_not_read_the_bodies_it_sizes (sizing 7.24 ms vs reading 24.24 ms), at a load average of about 75. It is not T041's: it fails on unchanged main too.

    • Run alone and serially, three times at this head and three times at main c94878be, with the load average between 62 and 82:

      where run 1 run 2 run 3
      471b7231 failed (8.35 ms vs 24.68 ms) failed (115.79 ms vs 116.36 ms) passed
      main c94878be passed failed (11.24 ms vs 24.98 ms) failed (8.51 ms vs 27.75 ms)
    • Two of three fail on each side. T015 measured 3 of 3 on main at a load of about 130, and T042 measured 1 in 6.

  • CI's validate at 471b7231, attempt 4 of run 37992191842 (attempts 1 to 3 failed on Docker Hub's unauthenticated pull limit for postgres:16, before any test ran): 6371 passed, 11 skipped, 1 failed. The one failure is tests/test_console_token_delivery.py::test_a_server_outlives_no_killed_run, a ProcessLookupError ([Errno 3]) reading /proc/<pid>/stat.

  • At this head, f924db0d: 6285 selected, 6273 passed, 11 skipped, 1 failed, 1 failed, 6273 passed, 11 skipped, 2 warnings in 1685.28s. The one failure is again the load-sensitive timing test test_the_draft_page_budget_does_not_read_the_bodies_it_sizes (sizing 6.39 ms vs reading 25.44 ms), which T041's diff does not reach: the diff touches only health_contract.py, the contract copy and record, and their two test files.

    • Run alone at a load average of about 47 to 50: three times at f924db0d, failed 3 of 3; three times at main c94878be, failed 1 of 3. Then five ALTERNATING pairs, f924db0d then main, under the same load: 2 of 5 failed on each side (bin/timing-runs-alternating.txt). Over every run so far, 7 of 11 failed at T041's heads and 5 of 11 at main; the alternating pairs, which hold the load equal, are even.
  • CI's validate at this head: SUCCESS (run 38095027847, 23:40:28Z); acceptance SUCCESS; the rollup is SUCCESS.

  • Locally, CI and OPENDOX_TEST_SANDBOX_REQUIRED are not set, because bwrap cannot run in this container. CI's own run is the proof.

  • The floors are unchanged. This PR adds cases above them, and only the CI owner re-pins them (T017, T051).

What changed

file what
src/opendox/health_contract.py (new) U-0, described below. It imports the standard library only.
src/opendox/contracts/schemas/opendox-health-finding.schema.yaml (new) T040's finding schema, byte for byte from openDox-spec 7db9438b, the spec commit T060 pins (opensoft/openDox#20, dox-v1.2). Its sha256 is 6fb9b29f23a4270fa0310624d9e59b5d82e9888a5168600671e90ed30f781016, which is the root manifest's entry.
src/opendox/contracts/copies.yaml commit: moves from f7ee3c76 to 7db9438b, and the record gains opendox-health-finding. The four existing digests are unchanged (see the check below).
src/opendox/contracts/__init__.py COPY_IDS gains opendox-health-finding. The refusal "not openDox's four" now reads "not the copies openDox carries". The docstrings say "four" only where they quote batch G or count the validator's four.
tests/test_validator_input_set.py SPEC_COMMIT, PINNED_BY_THE_ROOT and the set test move. The set test reads "the validator's kinds plus the finding shape" (N-15). test_each_kind_is_the_const_its_entry_declares iterates VALIDATOR_COPIES only, because the finding copy has no properties.kind.const. A new test holds the finding as a copy and no validator kind. The refusal cases are reworded, and one case now leaves the finding shape out.
tests/test_health_contract.py (new) The falsifier and everything else the module claims, in ten sections.

The four digests are unchanged. Each is computed with git show 7db9438b:contracts/schemas/<id>.schema.yaml | sha256sum in openDox-spec, and each equals the root manifest's entry at 7db9438b and this tree's existing copy:

ideation-workbench               d30438491119c20928fbe4e85088fc33682829eeb6558d87dafce651000faafc
opendox-snapshot                 f9e3e111af1d4bd4c377c933027d81b582ae2b0a395b66f4e4621992454a584a
xfactory-workbench-chat-turn     350bfedc02696e7281a42c0bdc9a25059bf7af14d16d89d9f07018d3e691dc1d
xfactory-workbench-model-catalog e563cc9fc6ede03dfd62537935d0ae0842617d7de46702aee6ad9026aa021635
opendox-health-finding (new)     6fb9b29f23a4270fa0310624d9e59b5d82e9888a5168600671e90ed30f781016

No collateral. The brief allowed tests/test_validator.py:9 to move with the pin. It does not need to: openDox-spec's diff f7ee3c76..7db9438b only adds files, so examples/ideation-dashboard/ is unchanged and that line is still true. No file outside T041's Files line is edited.

What opendox.health_contract holds

  • The vocabulary, with each value mirrored from the packaged copy, constant for constant, and tested against it:
    • the three classes, spelled exactly as 14.6 rules them: auto-fix, assisted, human-only;
    • the severities error|warning|info, which are the schema's enum and not corpus_adapter.SEVERITIES;
    • the baseline classes;
    • the fields and the forbidden keys;
    • IGNORED_FIELDS and STAMPED_FIELDS, the old ENGINE_FIELDS split by behaviour (the holder's 6072197564 (d)). A pack's id is ignored, because the engine computes every id. pack_id, pack_version and baseline_class are the engine's, and T045 refuses a pack's output that sets one. No behaviour moves.
  • The id rule (N-13; ADV-07). id_key() is exactly json.dumps(key, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8"), and finding_id() refuses parts that break their rules. All five of openDox-spec's example ids are recomputed exactly. Each part of the key moves the id, and nothing outside the key does.
  • The engine's caps, stricter than the schema's, each tested at its boundary:
    • NAME_MAX = 40 for pack_id and kind. 40 characters are admitted and 41 refused, and the schema's own pattern admits 41. The longest id is 98 characters, ID_MAX, and health-fix-<id> passes git check-ref-format --branch.
    • IDENTITY_MAX_BYTES = 2048, the identity's canonical UTF-8 JSON. T042 stores what it admits and T046 enforces it, and both cite this constant. It is admitted at 2048 and refused at 2049, in 1-, 2-, 3- and 4-byte characters. An identity the schema admits is refused by the cap alone. The largest identity the plan names, a pair of 200-character paths of 4-byte characters under 40-character keys, is 1693 bytes and fits.
    • NESTING_MAX = 16. A value nested past Python's recursion limit is refused, never a RecursionError, because the walk is iterative.
    • finding-is-json: no tuple, bytes, NaN, infinity, non-text key, or integer json.dumps cannot write. So every checked finding serializes.
    • text-holds-no-nul (the holder's 6072086385 item 4, fix-now): no string a finding carries holds U+0000, keys and values alike, because Postgres jsonb cannot store it (SQLSTATE 22P05).
      • The schema admits U+0000 in identity, evidence and locator.target, so this rule refuses it there, at any depth, as a FindingRefused with a bounded where.
      • In message, path and pack_version, the schema's own control-character rules already refuse it. test_u0000_is_refused_in_every_string_a_finding_carries holds all six fields.
      • For a pack's output, this refusal is T045's whole-output pack-output-refused (6069024023 item 1's mapping), so a broken or hostile pack loses its own output, never the run. T042's _json_text refuses it too, as defence in depth.
  • All 27 catalogued rules, written by hand, plus the engine's five.
    • openDox's validator refuses to build the finding schema (SchemaNotEvaluable on its three reference rules), and N-15 makes it no validator kind. So id-names-its-pack-and-kind, id-is-the-hash-of-its-key and locator-span-is-ordered are written here.
    • Each of openDox-spec's negative examples is rebuilt and refused for its rule and no other. A reference rule judges only parts that pass their own rules.
  • The bounds on message and evidence (R2Q25 (a); ADV-27).
    • message is one line of 1 to 200 characters, with no control character, no U+2028 or U+2029 and no lone surrogate.
    • In evidence, every string is at most 200 characters and no key is forbidden, at any depth. evidence is {} when none is given (14.5; FR-011).
  • The engine's door. make_finding() takes no id, so a pack's own value never reaches a finding (ruling 6065680005 item 3). It sets the id, checks the whole finding, and returns its own copy in the schema's field order. to_json() checks before it writes. identity_json() gives the canonical text T042 stores.
  • ONE hash path (the holder's 6069507373, T046 item 2). The module-level finding_id() computes every id the module sets or checks: make_finding()'s id and the reference rule's check alike. No other id is hashed anywhere; the only other digest is refused_entry()'s, which is no id. A test that patches health_contract.finding_id patches both, and test_finding_id_is_the_one_hash_path proves it. A mutant that restores either direct hash is killed.
  • The engine's own findings (the holder's 6069024023):
    • CATEGORIES: the eleven ruled categories (below).
    • ENGINE_KINDS: the categories, identity-collision, and the PATHED uncited-disappearance and refused-patch (6086098003 item 1), which only the engine raises. T045 refuses a pack declaring one. refused-patch is in neither CATEGORIES nor PATHLESS_CATEGORIES.
    • ENTRY_CATEGORIES: the categories whose pack_id is the entry's (6072086385 item 1, below).
    • INSTALL_CATEGORIES: no-sandbox, manifest-refused and dispositions-refused take entry "", and a non-empty entry is refused by pathless_identity(), engine_finding() and check_finding() alike (6072197564 (e); lane 3's MINOR at :865). A run raises ONE install-level finding, never one per entry (data-model.md:382).
    • PATHLESS_CATEGORIES: a pathless finding's identity.category is one of the engine's categories or identity-collision, and nothing else (6072197564 (b)). Only the engine raises a pathless finding.
    • Each pathless category's entry takes its form (the holder's 6073087924, on lane 3's MINOR at c620dacd, refining 6072197564 (e)), so check_finding() admits exactly what engine_finding() builds. A category in ENTRY_CATEGORIES names its pack's entry: never "", never the reserved opendox, and a name of 1 to 40 characters (6082100803). Lane 3's exactness probe, every category against eleven entries, found 21 mismatches at 4cc065ad and finds 0 here (test_check_finding_admits_exactly_what_engine_finding_builds). INSTALL_CATEGORIES and identity-collision take "", and entry-refused takes either. One helper, _entry_breaks_form(), holds the rule for check_finding() and pathless_identity() alike.
    • engine_finding(category, entry, ...) builds the one finding per category and entry. Its kind is its category, its path is empty, it is human-only, and the reasons ride in evidence. It keeps one id across runs, and it rebuilds openDox-spec's released no-sandbox example exactly.
    • pathless_identity() and collision_identity() build the keys.
    • refused_entry(entry_id) builds entry-refused's identity.entry (6086098003 item 4, 6088484643): "" for no id; the id as written when it is a string matching [a-z0-9-]+ of at most 200 characters; otherwise sha256- and the lowercase SHA-256 hex of the ASCII bytes of its canonical text, json.dumps(id, sort_keys=True, separators=(",", ":"), ensure_ascii=True, allow_nan=True, default=...), whose hook spells a set or frozenset as its members sorted by ascii() text and anything else by str() (6090537166 item 8 (b)), or ascii(id) when that raises (6088732352 (b)), so the text is always ASCII. An id neither spelling can write, an integer past Python's 4300-digit limit or a value nested past the recursion limit, takes ONE fixed text, <past the int-digit or recursion limit> (item 8 (a)). It is deterministic and never raises. Its tests cover an absent, a conforming, an uppercase, a spaced, an empty, a U+0000, a 201-character, a non-string, a boolean, a NaN, a date and a list id, a mapping with mixed-type keys (with a non-ASCII value, and with a non-ASCII key: the ascii() fallback), a self-referencing list, a YAML !!set, a value nested 50,000 levels deep, and lane 3's four ids past the int-digit limit (a long 0x literal, a long 0b literal, one in a list, one in a mixed-key mapping). One set id digested in four interpreters under PYTHONHASHSEED 0 to 3 is one entry, while its str() differs across them.
    • disappearance_identity(original) takes the form option (D) rules (below).
  • A refusal quotes nothing it refused, and its pointer is bounded. A violation's detail names a size, a type or the admitted set, never a value of the finding. Its where is a JSON pointer built only from keys the module ADMITTED, which it names verbatim and within the contract's own bound on a key.
    • where is at most 200 characters (STRING_MAX): the place's own pointer, or, past the bound, its nearest ancestor's that fits.
    • A pointer that stops stays stopped, so it never names a path that is not there.
    • So an engine may carry a refusal's rule and where as evidence of the finding against the pack.
    • Lane 3's fifteen nested 200-character keys of / made a 6026-character pointer. Now it is /identity. The bound is tested at 200 and 201 characters, with escapes, and with array indices: at 201 and 207 characters, and a stopped pointer that must not resume to /identity/0/a.
  • The standard library only (R2Q18 (a); N-3), proved three ways:
    • by reading the module's imports from its source;
    • by a fresh interpreter, with openXdox, openxFactory's packages and PyYAML blocked, which loads only opendox and opendox.health_contract;
    • by copying the module alone into a directory with an empty package marker and running it under python -I -S, with no site-packages. This is what T048's sandbox binds, and it computes the broken-link example's id.
  • Room for T045. T045 appends the pack protocol after T041 lands (plan.md:458). Per ruling 6065680005 item 1, the module stays standard-library only and does no parsing.

The holder's rulings, as encoded (6069024023)

  1. The categories: the nine as drafted, plus two.

    category case pack_id
    no-sandbox no live sandbox, so no pack ran (R2Q16 (a)) opendox
    entry-refused a manifest entry refused by name (15.1a) opendox
    fetch-failed a git-URL source that could not be fetched (OQ-H15-14) the entry's
    digest-mismatch a pack tree whose digest is not its entry's (15.1a) the entry's
    declaration-refused a pack's opendox-pack.yaml refused (15.2; OQ-H15-10) the entry's
    pack-crashed a pack that exited non-zero or on a signal (15.6) the entry's
    pack-timed-out a pack that ran past its time budget (15.6) the entry's
    pack-bound-hit a pack that hit an engine bound, its stdout cap included (15.6) the entry's
    pack-output-refused stdout that is not one JSON document, or that breaks the contract (15.5, 15.6) the entry's
    manifest-refused (added) health/packs.yaml refused as a whole, entry "" opendox
    dispositions-refused (added) health/dispositions.yaml refused, entry "" (contracts/health-exceptions.md; T054 raises it) opendox
    • The kind, and one finding per category and entry. A category is its finding's kind. engine_finding() raises one finding per category and entry, with the reasons in evidence, and that finding keeps one id.
    • The mappings that belong to T045 and T046 are recorded in the categories' comments and not otherwise enforced here: stdout over the cap is pack-bound-hit, and non-JSON or contract-breaking output is pack-output-refused.
    • The pack_id column is RULED as drafted (the holder's 6072086385 item 1, which replaces "one pack_id per category" in 6069024023 item 1). It follows dox-v1.2's schema text for pack_id, and ENTRY_CATEGORIES encodes it unchanged; its comment, the module docstring and engine_finding() cite both rulings.
      • A finding about a VALID manifest entry's pack takes that entry's id. Its entry must be the entry's id, never empty and never opendox.
      • no-sandbox, manifest-refused, dispositions-refused and entry-refused take opendox. A refused entry's id may be malformed, reserved or repeated, so it names no pack, and it rides only in identity.entry. Two such entries fold into one finding with entry "", as the one-per-category-and-entry rule has it.
  2. The disappearance, option (D). disappearance_identity(original) checks the original whole, then returns:

    • for a pathed original, {disappeared_id};
    • for a pathless original, {category: <its kind>, entry: <its entry, or "">}, the pathless form the dox-v1.2 schema requires.

    The tests cover both forms, the pathless entry, a pathless collision, and the refusal of {disappeared_id} on an empty path. T046 raises the re-raise.

    • The edge, ruled for T046 (6069507373 T046 item 2). Two pathless originals of one category and entry that disappear in the same run yield ONE re-raise, with every disappeared_id in evidence. For example, two pathless collisions both re-raise as {category: "identity-collision", entry: ""}.

The holder's two clarifications (21:45Z)

  1. disappearance_identity() is restored and widened (6069024023 item 2). Both forms are built and tested (item 2 above), and UNCITED_DISAPPEARANCE is back.
  2. finding_id() is the single hash path (6069507373 T046 item 2), as described under "What opendox.health_contract holds". The red test went green, and both direct-hash mutants are killed.

Review round 1 (at 6f85bee0), classified under 5988818366

thread finding class what was done
Copilot r4223399173 disappearance_identity() cannot represent the disappearance of a pathless finding fix-now Removed at 8e4065b2, then widened at this head to both forms, by the holder's option (D).
Copilot r4223399258 a locator line past Python's integer-string limit passes the check, and to_json() then raises ValueError fix-now Such a line is refused as finding-is-json, as evidence integers already were (8e4065b2).
Codex r4223401860 the span refusal formats its lines with :g, which raises OverflowError on a 401-digit line fix-now The refusal quotes no line, and a span of any writable size is judged (8e4065b2).

Review round 2 (Copilot at c620dacd), classified under 5988818366

thread finding class what was done
Copilot r4226093840 the test overview counts four engine rules, not five fix-now (a docstring) bf0f0824: five, and two docstrings that still said "one pack_id per category" cite 6072086385 item 1.

Codex was triggered at c620dacd and answered with its usage limit (02:32:56Z).

Lane 3's REVIEW-W1 (EARLY) at 471b7231: NO-GO, ruled by the holder (6090537166, item 8)

finding class what was done
MAJOR :528-532, refused_entry() raises ValueError on an integer YAML yields past the 4300-digit limit (four reproductions) fix-now, as ruled The second fallback also catches ValueError; ONE fixed text, <past the int-digit or recursion limit> (1c6d0c4); one row each for the four ids (f924db0), red first.
MINOR :526-527, a !!set id spelled by str(), in hash order, so its digest moves with PYTHONHASHSEED fix-now, as ruled The default hook spells a set by its members in ascii() order; a row under four seeds in subprocesses gives one entry, red first.

Lane 3 confirmed everything else at 471b7231: each ruling from 6082100803 to 6089449884 has its code and a test row; n6 and e3 were dead (0 diffs over 20,000 fuzz cases).

Lane 3's REVIEW-W1 delta at f924db0d: GO (EARLY), 0 BLOCKER, 0 MAJOR, 0 MINOR, 2 NITs, both ruled in 6103336585 (item 9): NIT 1 (a !!set row with a non-ASCII member, to pin the ascii() order against a repr-order mutant) is for the after-T027 round and does not hold this head; NIT 2 is the accepted limit on PYTHONINTMAXSTRDIGITS under "Stated limits".

Review round 4 (at f924db0d)

  • Copilot: "Needs a closer look", 0 open findings. Its summary, that a refused entry's identity varies with Python's configurable runtime limits, is the accepted limit on PYTHONINTMAXSTRDIGITS (6103336585, item 9, NIT 2), stated under "Stated limits".
  • Codex: triggered at f924db0d, answered with its usage limit (23:26:31Z): CODEX-LIMIT.
  • Threads: 0 unresolved.

Review round 3 (Copilot at 471b7231), classified under 5988818366

thread finding class state
Copilot r4234624749 json.dumps writes a non-string mapping key in its JSON spelling, so {1: "a"} and {"1": "a"} share one refused_entry() ACCEPTED LIMIT, ruled by the holder (6089449884, item 7; the same class as 6088732352 (a)); never silent (identity-collision); no code change answered and RESOLVED; stated under "Stated limits". Copilot's "Changes recommended" at 471b7231 stands only for this ruled limit, and #97 is re-reviewed at its final head after T027.

Codex was triggered at 471b7231 and answered with its usage limit (21:14:21Z).

Lane 3's REVIEW-W1 EARLY (at 6f85bee0), classified under 5988818366

finding class what was done
MAJOR :685, :g on a reversed 401-digit span fix-now Fixed at 8e4065b2 (Codex r4223401860). Lane 3's own probe, 10**400 against 10**399, is now a test row.
MINOR :545, _line admits an integer JSON cannot write fix-now Fixed at 8e4065b2 (Copilot r4223399258).
MINOR :606, the top-level non-text field-name refusal untested, its mutant surviving fix-now test_a_field_name_that_is_not_text_is_refused, with the mutant now killed.
MINOR :74, two docstrings overclaim about where fix-now, by bounding where is bounded at 200 characters, the nearest ancestor that fits (f9cc4b0), and the docstrings say so. This replaces the rewording at 674efcb, as the holder preferred.
MINOR :367, for the holder: the pathless disappearance ruled Option (D), item 2 above.
NIT :87, the plan.md citation fix-now It now cites plan.md:458.

Lane 3's REVIEW-W1 at 8e4065b2, and again at 8e2124e6 (GO, EARLY, PARTIAL each time):

  • MINOR :630, the non-text field-name row: done at 674efcb, and its mutant is killed.
  • MINOR :75 and :286, where: bounded at f9cc4b0 (above).
  • MINOR, this body's Falsifier quote: re-quoted from main's tasks.md at 40d6e5c1, and the "unchanged" claim is gone.
  • NIT :92: the citation reads plan.md:458 since 674efcb.

Lane 3's REVIEW-W1 at f9cc4b0c (GO, EARLY: all four earlier items fixed, 6069507373 item 2 met, 6072086385 item 1 confirmed):

finding class what was done
MINOR :865, engine_finding() accepts an entry for manifest-refused and dispositions-refused fix-now Refused, with no-sandbox too by 6072197564 (e): test_an_install_level_finding_takes_no_entry, one row each.
MINOR tests/...:1412, the list-index branch of the where bound untested fix-now Three rows: 201 and 207 characters, and a stopped pointer that must not resume to /identity/0/a. The index mutant is killed.
NIT :444, a re-raise could take identity.category for the original's kind fix-now test_a_pathless_reraise_takes_its_originals_kind_not_its_category, with the mutant killed. It is its own test rather than a _DISAPPEARED row: under (b), that original's re-raise is refused, and _DISAPPEARED asserts each re-raise is admitted.
NIT, the body draft at :174 fix-now The pack_id column cites 6072086385 item 1.

Lane 3's NAMES points (a) to (e), all ruled now:

  • (a): a category is its finding's kind (6069024023 item 1).
  • (b): the set is CLOSED (6072197564 (b)): PATHLESS_CATEGORIES.
  • (c): the 200-character entry bound is accepted as written (6072197564 (c)).
  • (d): ENGINE_FIELDS is split into IGNORED_FIELDS and STAMPED_FIELDS (6072197564 (d)).
  • (e): an install-level category takes entry "" (6072197564 (e)): INSTALL_CATEGORIES.

Lane 3's REVIEW-W1 at c620dacd (GO, EARLY: the U+0000 rule refused in all 36 placements; NAMES (b) to (e) and the earlier findings confirmed; a differential fuzz of 20,000 hostile findings admits nothing new):

finding class what was done
MINOR :693, check_finding() admits {identity-collision, <an entry>} and {pack-crashed, ""} fix-now, as the holder ruled it (6073087924) _entry_breaks_form() (829b7c24; its docstring cites the ruling at 4cc065ad): test_a_pathless_entry_takes_its_categorys_form, 20 rows, red first (10 failed), and both mutants killed.

Lane 3's REVIEW-W1 at 4cc065ad (EARLY; its mutants 105 killed, 2 survived, 6 unanchored):

finding class what was done
MINOR :706-707, check_finding() admits opendox and 41-to-200-character entries for the per-entry categories, which engine_finding() refuses (21 mismatches) fix-now, as the holder ruled it (6082100803) Refused; one row each per category; the probe reads 0; four mutants killed.
survivor n6, the locator's bool guard equivalent mutant on a dead guard The guard is removed; the line_end: True row pins the behaviour.
survivor e3, an empty entry admitted equivalent mutant on a dead guard engine_finding()'s dead check is removed; _entry_breaks_form() holds the refusal, and its mutant is killed.

For the holder

  • Under (b), the disappearance of a PATHLESS re-raise has no admissible re-raise. A pathless uncited-disappearance finding's own disappearance would take {category: "uncited-disappearance", ...} by option (D), and (b) refuses that category. So T046 must never re-raise the disappearance of its own pathless re-raise; if it tried, the contract would refuse the finding and fail the run closed (6069507373 T046 item 9). The test above pins the behaviour. Widening (b) to uncited-disappearance would be the alternative.
  • One refused_entry() edge outside the rulings below, reported downstream by identity-collision, because two entry-refused findings with one identity share one id: an id written as sha256- and 64 hex digits is a conforming name, so it is kept as written, and can equal another id's digest (only a hostile manifest would do it).

Stated limits

  • refused_entry()'s canonical text is not injective across YAML value types (ACCEPTED LIMITS: the holder's 6088732352 (a) and 6089449884, item 7, under 5988818366). Two kinds of value can share a digest:
    • a date or timestamp and its str() spelled as a string;
    • a non-string mapping key and its JSON spelling, for example {1: "a"} and {"1": "a"}, True and "true", None and "null".
    • Either needs two crafted refused entries in one manifest, so it is operator-only, and it is never silent: two findings with one identity raise identity-collision. Making the text injective would need a typed encoding of every value at every depth. The contract's entry-refused row states the limit (T002e).
  • Two ids past the int-digit or recursion limit share the one fixed text (ACCEPTED LIMIT, 6090537166 item 8 (a)), so they share a digest; identity-collision reports it.
  • A set reached only through the ascii() fallback keeps Python's order (ACCEPTED LIMIT, item 8 (b)): a set inside a mapping JSON cannot spell is spelled by ascii(), in hash order. At worst that refused entry's finding is raised again each run.
  • The int-digit limit is an interpreter setting (ACCEPTED LIMIT, the holder's 6103336585, item 9, NIT 2): Python's 4300-digit conversion limit is PYTHONINTMAXSTRDIGITS. So under a lower setting (say 640) an id of 0x and 3000 fs takes the fixed text, while at the default it is spelled out, and the same id digests differently under the two settings. No code change.

SonarCloud

  • At 8e4065b2 it raised 10 new issues, and 8e2124e6 addressed them. SonarCloud has analysed no head since, so a pre-check ran before this push. Its approximations reproduce every round-1 issue at the exact line.
    • S1192: "/identity/entry" appeared three times, once in new code. It is now _AT_ENTRY.
    • S3415: 8e2124e6 rewrote the wrong assertion. SonarCloud's own data flow for the round-1 issue names one expected-first assertion, (hc.AUTO_FIX, hc.ASSISTED, hc.HUMAN_ONLY) == hc.RESOLUTION_CLASSES, and it now puts the module's constant first.
    • S3776: _locator would have reached 16 with the U+0000 branch, so locator.target's check moved into _target(). Every function is now at 12 or below.
  • At c620dacd, the head pushed at 02:32Z: the quality gate passed with 0 new issues. The pre-check held, and the S3415 is gone.
  • At 471b7231: the quality gate passed with 0 new issues.
  • At this head, f924db0d: the quality gate passed with 0 new issues.

What is NOT done

  • health list --json (T046) and the HTTP Health response (T057). Their halves of the falsifier are theirs, by 6027706377 item 1.
  • The pack protocol: the declaration check, the stdout document and the patch type. That is T045, appended to this module after it lands.
  • The two file-kind copies, opendox-health-packs (T047) and opendox-health-dispositions (T054), and their validator kinds.
  • The store (T042) and the engine (T046, T056), including raising the disappearance re-raise.
  • The CI floors (T017, T051).

🤖 Generated with Claude Code

brettheap and others added 2 commits October 8, 2026 16:34
…e copy-record moves (plan 038)

Work in progress, parked for an account swap. Not reviewed, not gated.
Mutant pass run 1: 52 killed, 1 survived (names rule checking the pack
only), 2 edits not applied; both get fixed next.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ttern escaped (plan 038)

A test of an id naming the right pack and the wrong kind kills the one
mutant run 1 left alive; the message pattern's U+2028 and U+2029 are written
as escapes, not as raw characters; the refused-identity cases get ids.
Mutant run 2: 57 of 57 killed. The full suite is still running.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@brettheap
brettheap requested a balanced review from Copilot October 8, 2026 19:38
@brettheap

Copy link
Copy Markdown
Contributor Author

@codex review

@sourcery-ai

sourcery-ai Bot commented Oct 8, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduces the T041 health contract: a digest-pinned neutral finding schema plus a standard-library-only Python implementation that validates bounded findings, computes stable IDs, constructs engine identities, and safely serializes output; tests comprehensively cover the contract and update the packaged-copy input set.

Sequence diagram for constructing and serializing a health finding

sequenceDiagram
    participant Producer as Pack or engine
    participant Contract as opendox.health_contract
    participant Hash as SHA256
    participant Output as JSON consumer
    Producer->>Contract: make_finding(kind, pack_id, pack_version, path, identity, severity, resolution_class, message, evidence)
    Contract->>Contract: finding_id(identity, kind, pack_id, path)
    Contract->>Hash: sha256(id_key(...))
    Hash-->>Contract: 16-hex-digit digest
    Contract->>Contract: check_finding(finding)
    Contract-->>Producer: copied validated finding
    Producer->>Contract: to_json(finding)
    Contract->>Contract: check_finding(finding)
    Contract-->>Output: canonical_json(finding)
Loading

File-Level Changes

Change Details Files
Added a standalone, standard-library-only health finding contract implementation with vocabulary, validation, deterministic IDs, bounded serialization, engine identities, and refusal reporting.
  • Defined finding fields, severities, resolution and baseline classes, engine categories, and forbidden content keys.
  • Implemented canonical UTF-8 JSON hashing over identity, kind, pack ID, and path.
  • Enforced schema and engine constraints for names, strings, JSON values, nesting, identity size, paths, locators, evidence, and conditional finding forms.
  • Added engine-owned constructors and checked make_finding()/to_json() serialization paths.
src/opendox/health_contract.py
Added the digest-pinned openDox health-finding schema as a packaged contract copy and expanded the contract-copy set.
  • Added the byte-for-byte opendox-health-finding schema copy with shape and reference-rule catalog.
  • Moved the recorded spec-leg commit to 7db9438b and registered the new schema digest while preserving the four existing digests.
  • Updated copy metadata and package documentation to distinguish validator schemas from the engine-read finding shape.
src/opendox/contracts/schemas/opendox-health-finding.schema.yaml
src/opendox/contracts/copies.yaml
src/opendox/contracts/__init__.py
Expanded tests to falsify the health contract implementation and validate the five-copy packaging boundary.
  • Added serialized-shape, refusal, vocabulary-mirroring, ID, boundary, rule-catalog, engine-identity, dependency-isolation, and refusal-redaction tests.
  • Updated validator input-set tests for the new finding-shape copy without treating it as a validator kind.
  • Covered 57 targeted mutants and reported the full suite as passing.
tests/test_health_contract.py
tests/test_validator_input_set.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-09T13:34:02.900524Z 4cc065a Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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.

🟡 Changes recommended

Checked findings can still fail serialization, and pathless disappearances lack a valid contract representation.

2 open findings
What changed in this PR

Adds the sandbox-safe health finding contract, pinned schema copy, and validation coverage.

Changes:

  • Defines finding vocabulary, identities, validation, hashing, and serialization.
  • Packages the pinned health-finding schema alongside validator contracts.
  • Adds comprehensive contract and packaging tests.
File Description
src/​opendox/​health_contract.py Implements the finding contract API.
src/​opendox/​contracts/​schemas/​opendox-health-finding.schema.yaml Adds the pinned finding schema.
src/​opendox/​contracts/​copies.yaml Records the fifth packaged schema.
src/​opendox/​contracts/​__init__.py Registers and verifies the new copy.
tests/​test_health_contract.py Tests contract rules and isolation.
tests/​test_validator_input_set.py Updates packaged-copy set tests.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/opendox/health_contract.py Outdated
Comment thread src/opendox/health_contract.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f85bee02d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/opendox/health_contract.py Outdated
…nce key is T046's (plan 038)

Copilot r4223399258 and Codex r4223401860 on openDox-code#97, both fix-now:
a locator line past Python's integer-string limit is refused as
finding-is-json, so to_json() never raises ValueError on a checked finding;
and the span refusal no longer formats its lines with :g, which raised
OverflowError on a 401-digit line. It quotes no value of the finding now.

Copilot r4223399173, fix-now in T041's scope: disappearance_identity() and
UNCITED_DISAPPEARANCE are removed. T041's task names the pathless and the
collision keys only; the re-raise of an uncited disappearance is the
baseline's (T046), and its pathless case is the holder's to rule (the PR
body's "For the holder", item 2).

Red first: the new locator cases fail on the previous head, 3 failed, one
with OverflowError. Mutants: 60 of 60 killed.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@brettheap
brettheap requested a balanced review from Copilot October 8, 2026 19:50
@brettheap

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

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.

🔵 Needs a closer look

It remains a draft pending T027, refreshed gates, and final holder review of the proposed category vocabulary.

0 open findings

2 resolved since last review

🧠 Review effort: Balanced

brettheap and others added 5 commits October 8, 2026 20:03
… 038)

health_contract.py: _walk split into _walk, _members and _scalar (cognitive
complexity 31, S3776); the pointers /identity, /locator and /resolution_class
named once each (S1192). No behaviour moves: 342 passed over the two test
files, and mutants 61 of 61 killed.

tests/test_health_contract.py: each pytest.raises block holds one call that
may raise (S5778, three); the two composite assertions split (S9073); the one
assertion that put the expected value first rewritten (S3415).

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lan 038)

Item 1: the nine categories stand as drafted, and manifest-refused and
dispositions-refused are added; pathless_identity() admits both. What is the
contract module's to encode of the item's mappings: a category is its
finding's kind (ENGINE_KINDS, for T045's refusal of a pack declaring one);
one pack_id per category (ENTRY_CATEGORIES: against the entry's pack, the
rest against opendox); and engine_finding() builds the one finding per
category and entry, human-only, pathless, with one id across runs.

Item 2, option (D): disappearance_identity(original) returns a pathed
original's {disappeared_id} and a pathless original's {category: <its kind>,
entry: <its entry, or "">}. UNCITED_DISAPPEARANCE is back. Both forms are
tested, and so is the refusal of {disappeared_id} on an empty path.

Lane 3's REVIEW-W1 EARLY: the non-text field-name refusal is tested (its
mutant survived); the docstrings now say that where names admitted keys
verbatim and is unbounded, and a test pins it; the T045 hand-off cites
plan.md:458; lane 3's reversed 401-digit span is a test row.

Two test files: 379 passed. Mutants: 75 of 75 killed.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
T041 merges main before its gates, never rebasing (WRITER-RULES-R2). #90
touches no file of T041's.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… routes its reference check through finding_id() (plan 038)

The holder's 6069507373 (T046 item 2): the module-level finding_id() is the
single hash path. This test patches it; it is red at this commit, because
violations() still recomputes the hash with hashlib. The module change is
the next act.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (plan 038)

The holder's 6069507373 (T046 item 2): the reference rule's hash check now
calls the module-level finding_id(), as make_finding() already did, so a test
that patches health_contract.finding_id patches every id the module sets or
checks. test_finding_id_is_the_one_hash_path, red at the previous commit, is
green; the mutants that restore a direct hashlib path are killed.

Lane 3's REVIEW-W1 MINOR at 8e2124e, bounded as the holder preferred: a
violation's where is at most STRING_MAX characters, the place's own pointer
or its nearest ancestor's that fits, and a pointer that stops stays stopped,
so it never names a path that is not there. Fifteen nested 200-character
keys of "/" made a 6026-character pointer; now it is /identity. The
docstrings say so.

Two test files: 389 passed. Red first: 6 failed against the previous module.
Mutants: 80 of 80 killed.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
brettheap and others added 2 commits October 9, 2026 01:09
…uling cited; SonarCloud's pre-check (plan 038)

The holder's openxFactory#656 6072086385 item 4 (the NUL seam, fix-now):
Postgres jsonb cannot store U+0000 (SQLSTATE 22P05), so no string a
finding carries holds it, keys and values alike. The schema admits it in
identity, evidence and locator.target, so the engine's new rule
text-holds-no-nul (ENGINE_RULES, now five) refuses it there, as a
FindingRefused with a bounded where; in message, path and pack_version
the schema's own control-character rules refuse it already. For a pack's
output the refusal is T045's whole-output pack-output-refused
(6069024023 item 1's mapping); T042's _json_text refuses it too.
locator.target's check moves into _target(), which keeps _locator's
cognitive complexity at 12.

6072086385 item 1: ENTRY_CATEGORIES is confirmed as written; its comment,
the module docstring and engine_finding() cite it beside 6069024023.

SonarCloud, checked before the push: "/identity/entry" is named once
(_AT_ENTRY, S1192), and the one expected-first assertion SonarCloud's
S3415 flow named at 8e4065b now puts the module's constant first.

Tests: 409 passed over the two files; 19 of the new cases fail against
f9cc4b0's module. Mutants: 85 of 85 killed, five of them new.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…W-W1 items at f9cc4b0 (plan 038)

The holder's openxFactory#656 6072197564, on lane 3's NAMES points:
- (b) a pathless finding's identity.category is one of a CLOSED set,
  PATHLESS_CATEGORIES: the engine's categories and identity-collision.
- (d) ENGINE_FIELDS is split by behaviour: IGNORED_FIELDS (a pack's id is
  ignored) and STAMPED_FIELDS (pack_id, pack_version and baseline_class
  are the engine's). No behaviour moves.
- (e) INSTALL_CATEGORIES (no-sandbox, manifest-refused,
  dispositions-refused) take entry "": a run raises ONE install-level
  finding (data-model.md:382), so a non-empty entry is refused by
  pathless_identity(), engine_finding() and check_finding() alike. This
  is also lane 3's MINOR at health_contract.py:865.

Lane 3's REVIEW-W1 at f9cc4b0: three rows for the list-index branch of
the where bound (201 and 207 characters, and a stopped pointer that must
not resume to /identity/0/a); a pathless original whose kind is not its
category, whose disappearance takes the original's kind (and, under (b),
admits no re-raise of its own).

Tests: 433 passed over the two files; 14 of the new cases fail against
0e24062's module. Mutants: 93 of 93 killed, eight of them new.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@brettheap
brettheap requested a balanced review from Copilot October 9, 2026 02:32
@brettheap

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

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.

🔵 Needs a closer look

It introduces a broad foundational contract and remains a draft pending T027 and refreshed landing gates.

1 open finding

🧠 Review effort: Balanced

Comment thread tests/test_health_contract.py Outdated
brettheap and others added 2 commits October 9, 2026 02:37
…MINOR at c620dac, as the holder ruled it) (plan 038)

check_finding() refused a non-empty entry only for INSTALL_CATEGORIES, so
it admitted {identity-collision, <an entry>} and {pack-crashed, ""}, two
forms the engine never builds. The holder's ruling, refining 6072197564
(e): a category in ENTRY_CATEGORIES names its pack's entry, never "";
INSTALL_CATEGORIES and identity-collision take entry ""; entry-refused
takes either. One helper, _entry_breaks_form(), holds the rule for
check_finding() and pathless_identity() alike.

Tests: 459 passed over the two files; 10 of the new cases fail against
c620dac's module.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…id ruling (Copilot r4226093840 on openDox-code#97) (plan 038)

Docstrings only: the overview of tests/test_health_contract.py said four
engine rules after text-holds-no-nul made five, and two docstrings still
said "one pack_id per category", the phrase 6072086385 item 1 replaced.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… 038)

Docstring only: the entry-form rule is the holder's openxFactory#656
6073087924, on lane 3's MINOR at c620dac, so check_finding() admits
exactly what engine_finding() builds.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@brettheap
brettheap requested a balanced review from Copilot October 9, 2026 13:30
@brettheap

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 4cc065ad0d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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.

🔵 Needs a closer look

Pathless validation accepts reserved entries and contradictory category/kind combinations that the engine cannot produce.

0 open findings

1 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Medium severity Reject reserved opendox entries for all entry categories

src/​opendox/​health_contract.py:707

_entry_breaks_form() still admits the reserved opendox entry for every ENTRY_CATEGORIES value. As a result, pathless_identity("pack-crashed", "opendox") succeeds and a hand-built pathless finding with that identity passes check_finding()/to_json(), even though engine_finding() explicitly rejects the same form and the contract says these categories never target opendox. Treat the reserved product ID like the empty entry here so all validation paths enforce the same form.

Medium severity Enforce matching engine category and finding kind

src/​opendox/​health_contract.py:883

The cross-field checks never enforce the stated invariant that an engine category is also the finding's kind. For example, a pathless finding with kind="no-sandbox" and identity={"category":"manifest-refused","entry":""} is accepted and serialized once its ID is recomputed, leaving consumers with two conflicting classifications. Add a check for pathless findings whose kind is in CATEGORIES; uncited-disappearance must remain exempt because its identity intentionally names the original kind.

🧠 Review effort: Balanced

brettheap and others added 3 commits October 9, 2026 20:17
…nd; refused_entry(); n6 and e3 (plan 038)

The holder's openxFactory#656 6082100803: for ENTRY_CATEGORIES,
check_finding() refuses an entry that is empty, the reserved opendox, or
longer than NAME_MAX, so it admits exactly what engine_finding() builds.
Lane 3's exactness probe (every category against eleven entries) found 21
mismatches at 4cc065a and finds 0 here; test_check_finding_admits_
exactly_what_engine_finding_builds holds it.

6086098003 item 1: REFUSED_PATCH joins ENGINE_KINDS, a PATHED engine
kind, in neither CATEGORIES nor PATHLESS_CATEGORIES. Item 3 (b): a
U+0000 row for path. Item 4 and 6088484643: refused_entry() builds
entry-refused's identity.entry: "" for no id, the id as written when it
is [a-z0-9-]+ of at most 200 characters, and otherwise sha256- and the
SHA-256 of its canonical text (json.dumps with sorted keys, no
whitespace, ASCII escapes, allow_nan, default=str; repr() when that
raises). It never raises.

Lane 3's surviving mutants at 4cc065a were equivalent ones on dead
guards, so the guards go: n6, the bool guard before _writable() in
_locator (a bool is always writable, and _line() refuses it next); e3,
engine_finding()'s own entry check, which pathless_identity() already
made dead.

Tests: 502 passed over the two files; 35 of the new cases fail against
4cc065a's module.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (plan 038)

Python 3.12's json.dumps and repr() both reach a 3000-level value, so the
test of the fallback text now nests 50,000 levels, past the C recursion
limit both share; the mutant that stops catching RecursionError there is
killed.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (b)) (plan 038)

The holder's ruling on Copilot thread 4234200849 (openxFactory#1283): the
canonical text's fallback is ascii(id), repr() with every non-ASCII
character escaped, so the text is always ASCII and is encoded strictly.
The digests do not move: the previous fallback, repr() encoded with
backslashreplace, spelled every value the same way, so the new row for a
non-ASCII key in a mixed-type mapping passes against 4ffc411's module too.
The mutant that puts repr() back is killed.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@brettheap
brettheap requested a balanced review from Copilot October 9, 2026 21:14
@brettheap

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

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.

🟡 Changes recommended

Refused mapping IDs with different key types can deterministically collapse to the same entry hash.

1 open finding
Previously missed (1)

In code that hasn't changed since last review

Low severity Exclusion set count should be seven, not six

tests/​test_validator_input_set.py:35

The exclusion set below contains three OPENXDOX_SPECS plus four OPENXFACTORYS, so this count should be seven rather than six.

🧠 Review effort: Balanced

Comment thread src/opendox/health_contract.py Outdated
brettheap and others added 2 commits October 9, 2026 22:52
…w (plan 038)

Module only, not for #97 yet: the ascii() fallback also catches ValueError
(an integer past the 4300-digit limit) and takes ONE fixed text, <past the
int-digit or recursion limit>; the default hook spells a set or frozenset
as its members sorted by ascii() text. The item 8 test rows and mutants
are next.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Lane 3's MAJOR and MINOR at 471b723, as the holder ruled them:
- (a) the four ids lane 3 reproduced, integers YAML yields past Python's
  4300-digit limit (a long 0x literal, a long 0b literal, one in a list,
  one in a mixed-key mapping), each a sha256- entry of the one fixed text
  and never a ValueError; the 50,000-level value takes the same text.
- (b) a YAML !!set id in _REFUSED_IDS, and one set id digested in four
  interpreters under four PYTHONHASHSEED values: one entry, while its
  str() is not one spelling there, so the row bites.

All 7 new cases fail against 471b723's module. 509 passed over the two
files.

Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@brettheap
brettheap requested a balanced review from Copilot October 10, 2026 23:26
@brettheap

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@sonarqubecloud

Copy link
Copy Markdown

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.

🔵 Needs a closer look

Refused-entry identities vary with configurable Python runtime limits, violating deterministic finding identity.

0 open findings

1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Make refused_entry digest independent of Python integer-string limits

src/​opendox/​health_contract.py:555

refused_entry() is not deterministic across supported process configurations because this fallback depends on Python's configurable integer-string limit (and similarly its recursion limit). For the same 10**5000, PYTHONINTMAXSTRDIGITS=4300 returns the fixed-fallback digest, while PYTHONINTMAXSTRDIGITS=0 serializes the integer and returns a different digest, so the same refused manifest can acquire a different finding identity across runs. Please make these limits contract-owned rather than relying on interpreter settings, and test both configurations.

🧠 Review effort: Balanced

@brettheap

Copy link
Copy Markdown
Contributor Author

Lane: openxfactory-4 (openXfactory-4-openDox_extraction)

DRAFT-CLEAN f924db0 — T041 writer, 2026-10-11 00:53Z. This PR stays a DRAFT and lands after T027 (Brett, 6035546708). It is not ready for landing.

  • Head: f924db0dbd92fd6ae0977736e6a31d7f301a3986, the tip of r2/t041-health-contract. The tree is clean and the PR is MERGEABLE. Main has not been merged in, as ruled.
  • Checks at this head: validate SUCCESS (run 38095027847, 2026-10-10 23:40:28Z), acceptance SUCCESS, SonarCloud Code Analysis SUCCESS (Quality Gate passed). The rollup is SUCCESS.
  • Review threads: 5 in total, 0 unresolved.
  • Copilot at f924db0: "🔵 Needs a closer look", not "Changes recommended". The finding it raises is item 9 NIT 2: refused-entry identities depend on Python's configurable runtime limits. 6103336585 ruled that an accepted limit, and the PR body states it.
  • Codex at f924db0: the trigger at 23:26:24Z got the usage-limit reply at 23:26:31Z (CODEX-LIMIT), so there is no review at this head. The last Codex review, at 4cc065ad, found no major issues.
  • Lane 3: EARLY GO at f924db0 stands.
  • Full suite at f924db0 (Postgres 16): 6285 selected, 6273 passed, 11 skipped, 1 failed.
    • The one failure is the load-sensitive timing test tests_runtime/test_migrations_apply.py::test_the_draft_page_budget_does_not_read_the_bodies_it_sizes (sizing 6.39 ms vs reading 25.44 ms). It belongs to main.
    • Run alone at a load average of about 50: f924db0 failed 3 of 3 and main c94878be failed 1 of 3.
    • In five alternating pairs under the same load, each side failed 2 of 5.
    • T041's diff does not reach any code that test runs.
  • Mutants: 108 of 108 killed (run 14). Falsifier subset: 55 passed.
  • The PR body was re-posted at 00:51Z with the suite and validate results filled in. A live re-read matches the file byte for byte.
  • Pending for the after-T027 round:
    • item 9 NIT 1: a !!set row with a non-ASCII member that pins the ascii() order;
    • merging main;
    • fresh Copilot and Codex reviews at the final head;
    • then READY-CANDIDATE.

This branch has not been deployed

No deployments
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.

2 participants