Skip to content

feat(quality): add behavior ownership catalogue foundation - #297

Merged
abiorh-claw merged 5 commits into
mainfrom
codex/ws-qual-002-01-catalogue-foundation
Aug 8, 2026
Merged

feat(quality): add behavior ownership catalogue foundation#297
abiorh-claw merged 5 commits into
mainfrom
codex/ws-qual-002-01-catalogue-foundation

Conversation

@Abiorh001

@Abiorh001 Abiorh001 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

PR Trust Bundle: WS-QUAL-002-01

Chunk

WS-QUAL-002-01 — Behavior Ownership Catalogue Foundation.

Goal And Human-Approved Intent

Add one versioned catalogue contract, exact eligible-target partition, and
deterministic read-only generator/validator without activating mutation CI or
changing Workstream product behavior. The human separately approved the narrow
contract correction that assigns the new focused test module to the existing
shared_foundations lane.

What Changed And Why

  • Added the schema separating candidate, reviewed, and strict
    structural_only records.
  • Added the canonical digest-bound partition assigning every eligible target to
    exactly one population group.
  • Added deterministic inventory, non-authoritative candidate generation,
    validation, exact pytest collection, and optional exact owned-test execution.
  • Added fail-closed custody, symlink, path, schema, callable, test, remap,
    carry-forward, structural-side-effect, identity, and effective-owner checks.
  • Added contributor and backend-testing documentation plus a real reviewed
    example.

Design Chosen

The tooling delegates eligibility, safe paths, callable spans, changed-callable
derivation, outcomes, boundaries, and test-node syntax to
backend/scripts/mutation_policy.py. Candidate inference is structurally
non-authoritative. Protected records remain byte-identical or resolve through
exactly one reviewed, evidence-preserving remap. The initial empty catalogue is
reported as incomplete rather than promoted or blocked.

Alternatives Rejected

No wildcard/group inference authority, branch-local partition replacement,
callable-wide mutation activation, inferred reviewed ownership, free-form
structural exemption, parallel AST implementation, or workflow change.

Scope Control And Product Behavior

All files are within the approved contract plus the human-approved single lane
assignment. No .github/workflows/**, backend application module, migration,
coverage floor, timeout, skip, deselection, product review decision,
authorization rule, payment, reputation, or ContributionRecord behavior
changed.

Acceptance Proof And Tests

  • 49 focused tests pass.
  • scripts.behavior_ownership focused coverage is 91.30 percent, above 90.
  • 33 semantic-lane contract tests pass.
  • Ruff passes for all touched backend Python files.
  • Real validation reports 173 unresolved targets and complete: false.
  • Candidate generation remains authoritative: false, emits no empty-callable
    candidate, and separates structural-review targets.
  • Markdown links pass; stale wording passed during product/operations review.

Test Delta And CI Integrity

One focused test module was added and assigned to shared_foundations. No test
was removed, skipped, deselected, weakened, or moved between existing lanes. No
workflow, coverage threshold, package configuration, or required-check behavior
changed.

Reviewer Results

  • Architecture: PASS.
  • Senior engineering: PASS after typed non-reviewed supersession and physical
    group-directory enforcement.
  • QA: PASS after current callable-owner uniqueness.
  • Security: PASS after protected deletion and multiple-effective-owner repairs.
  • Product/operations: PASS.
  • CI integrity: PASS.
  • Documentation: PASS after using a real example and documenting strict
    structural exclusions.
  • Reuse/deduplication: PASS.
  • Test delta: PASS.

External Review

CodeRabbit and exact-head GitHub checks are pending after PR creation. They
supplement, but do not replace, the internal reviews above.

Remaining Risks And Follow-Up

The catalogue is intentionally incomplete until population chunks 03A through
03D merge. Context evidence (02), completeness integration (04), and any
future changed-line mutation reactivation (05) remain separate approved
chunks. Mutation enforcement remains retired.

Human Review Focus

Review candidate-versus-reviewed authority, protected partition bootstrap and
future trusted-base custody, remap carry-forward/effective-owner rules, strict
structural-only behavior, and the single lane assignment.

Human Merge Ownership

  • Intent, scope, and non-goals are explicit.
  • Deterministic local evidence passes.
  • Required internal reviewers pass.
  • External review findings are addressed.
  • Exact-head GitHub checks pass.
  • The user explicitly approves this PR for merge.

Summary by CodeRabbit

  • New Features

    • Added a read-only behavior-ownership catalogue with deterministic grouping and partition records.
    • Added tools to inspect inventory, generate non-authoritative candidates, validate catalogue data, and verify ownership safeguards.
    • Added structured formats for reviewed, candidate, and structural entries.
    • Added an initial catalogue that is explicitly incomplete and non-authoritative.
  • Documentation

    • Documented catalogue usage, validation rules, review statuses, and contribution guidance.
  • Tests

    • Added comprehensive validation coverage and integrity checks.
    • Existing mutation, coverage, lane, skip, and deselection behavior remains unchanged.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bb302152-476d-41d7-a91a-bb7b0fa04438

📥 Commits

Reviewing files that changed from the base of the PR and between e9a2c7d and 321108b.

📒 Files selected for processing (1)
  • backend/scripts/run_test_lanes.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/scripts/run_test_lanes.py

📝 Walkthrough

Walkthrough

The PR adds a behavior-ownership catalogue foundation. It defines a JSON Schema, partition manifest, deterministic tooling, validation rules, candidate generation, reviewed-test execution, comprehensive tests, and repository documentation. Existing mutation and test-lane behavior remains unchanged except for registering the new test module.

Changes

Behavior ownership catalogue

Layer / File(s) Summary
Catalogue contract and partition
.ci/behavior-ownership/*, scripts/behavior-ownership.schema.json, backend/scripts/behavior_ownership.py
Defines reviewed, candidate, and structural-only records. Adds deterministic target discovery, group assignment, and partition generation.
Catalogue validation and custody
backend/scripts/behavior_ownership.py, backend/tests/test_behavior_ownership.py
Validates schema, partition integrity, record semantics, remaps, ownership uniqueness, completeness, filesystem safety, and test-node freshness.
Candidate generation and CLI execution
backend/scripts/behavior_ownership.py, backend/tests/test_behavior_ownership.py, backend/scripts/run_test_lanes.py, .agent-loop/.../chunks/WS-QUAL-002-01-catalogue-foundation.md
Adds inventory, candidate, validation, and partition commands. Runs only reviewed test nodes and registers the new test module in shared_foundations.
Repository guidance and implementation records
CONTRIBUTING.md, docs/operations_backend_testing.md, .agent-loop/...
Documents catalogue use, non-blocking candidate output, active implementation status, review evidence, and preserved existing workflow authority.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant behavior_ownership.py
  participant partition.v1.json
  participant catalogue_records
  participant pytest
  CLI->>behavior_ownership.py: run inventory, candidate, validation, or partition command
  behavior_ownership.py->>partition.v1.json: load and verify assignments
  behavior_ownership.py->>catalogue_records: validate record schema and ownership
  behavior_ownership.py->>pytest: collect or run reviewed test nodes
  pytest-->>behavior_ownership.py: return collection or execution status
  behavior_ownership.py-->>CLI: emit JSON result or validation error
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the goal, scope, design, evidence, tests, reviews, risks, follow-up work, and merge ownership.
Title check ✅ Passed The title clearly identifies the primary change: adding the behavior ownership catalogue foundation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/ws-qual-002-01-catalogue-foundation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 8

🧹 Nitpick comments (4)
backend/scripts/behavior_ownership.py (2)

397-403: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Re-reading the partition creates a second source of truth.

validate_partition already parses and digest-verifies the partition at line 360. Line 397 reads the same file again with no shape or digest check and uses protected_base_commit from that second read. The file can change between the two reads, and a KeyError escapes as an untyped error if the second read returns a JSON object without that key.

Return the protected base commit from validate_partition, or extract a single load_partition helper that both call.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/scripts/behavior_ownership.py` around lines 397 - 403, Update
validate_partition to return the validated protected base commit, then have the
caller near _validate_remaps reuse that returned value instead of calling
_read_json again. Remove the second partition read and preserve the existing
validation and digest-verification path.

98-111: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Domain modules fall into shared because grouping matches directories and a fixed token list. group_for_target recognizes a domain only when the path contains a directory segment such as /authorization/ or /artifacts/, or one of four substrings. A module named after its domain but located elsewhere falls through to shared. The partition is generated output, so both sites change together.

  • backend/scripts/behavior_ownership.py#L98-L111: add a /auth/ directory rule and match a module named artifacts.py or artifact_*.py, so auth adapters and artifact workers and interfaces reach their own group.
  • .ci/behavior-ownership/partition.v1.json#L627-L642: regenerate the file with behavior_ownership partition after the rule change, so authority_digest and the affected assignments stay consistent.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/scripts/behavior_ownership.py` around lines 98 - 111, The domain
grouping logic in group_for_target must recognize additional auth and artifact
module paths. In backend/scripts/behavior_ownership.py#L98-L111, add the /auth/
directory rule and match artifacts.py or artifact_* module names; then
regenerate .ci/behavior-ownership/partition.v1.json#L627-L642 with
behavior_ownership partition so authority_digest and affected assignments remain
consistent.
.ci/behavior-ownership/README.md (1)

30-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the partition subcommand.

_main registers four subcommands: inventory, generate, validate, and partition. The command block lists only three. partition is the command that regenerates partition.v1.json, so a maintainer needs it after any change to group_for_target or to the eligible target set.

📝 Proposed documentation addition
 .venv/bin/python -m scripts.behavior_ownership validate
+.venv/bin/python -m scripts.behavior_ownership partition --base-commit <sha>
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.ci/behavior-ownership/README.md around lines 30 - 36, Update the command
examples in the behavior-ownership README to include the registered partition
subcommand, showing that it regenerates partition.v1.json for maintainers after
changes to group_for_target or the eligible target set. Keep the existing
inventory, generate, and validate examples unchanged.


</details>

<!-- cr-comment:v1:37230b389bfdea47b619148c -->

</blockquote></details>
<details>
<summary>backend/tests/test_behavior_ownership.py (1)</summary><blockquote>

`449-468`: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _💤 Low value_

**Both subprocess fakes replace `run` on the shared stdlib module.** `ownership.subprocess` is the `subprocess` module object, so each `monkeypatch.setattr` call swaps `subprocess.run` process-wide for the duration of the test. `monkeypatch` restores the attribute, so the current suite is safe, but the isolation is weaker than it appears.
- `backend/tests/test_behavior_ownership.py#L449-L468`: patch a module-local indirection in `backend/scripts/behavior_ownership.py` instead of the stdlib attribute.
- `backend/tests/test_behavior_ownership.py#L794-L809`: apply the identical change to `test_collect_only_runner_adds_collection_flag`.

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @backend/tests/test_behavior_ownership.py around lines 449 - 468, Replace the
process-wide subprocess.run patches in test_owned_tests_runs_only_reviewed_nodes
and test_collect_only_runner_adds_collection_flag
(backend/tests/test_behavior_ownership.py:449-468 and :794-809) with patches to
the module-local subprocess runner indirection used by
backend/scripts/behavior_ownership.py, preserving each test’s existing fake
behavior and assertions.


</details>

<!-- cr-comment:v1:b1ae68bc8cc638882cf9dfaa -->

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.ci/behavior-ownership/partition.v1.json:

  • Around line 627-642: Update group_for_target to classify artifact-related
    module filenames such as artifacts.py and artifact_operations.py, not only
    targets within an /artifacts/ directory or the existing fixed token list.
    Preserve the intended artifacts grouping for checkers.py and related modules,
    then regenerate partition.v1.json.

In @backend/scripts/behavior_ownership.py:

  • Around line 241-261: The structural-only validation is missing executable AST
    node types. In backend/scripts/behavior_ownership.py lines 241-261, update
    _is_strictly_structural’s forbidden tuple to include ast.Lambda, ast.IfExp,
    ast.comprehension, and ast.Assert. In backend/tests/test_behavior_ownership.py
    lines 522-540, add one parametrized source case for each newly forbidden node
    type to ensure structural_only rejects them.
  • Around line 301-307: Update the record-loading logic around
    _catalogue_at_revision so any successfully parsed JSON value that is not a dict
    raises BehaviorOwnershipError("invalid_protected_record") instead of being
    skipped. Preserve the existing JSONDecodeError handling and append only
    validated object records, ensuring malformed protected data fails closed.
  • Around line 196-209: Update the custody validation around _git_show_optional
    so a missing trusted revision is distinguished from a missing PARTITION_PATH at
    an existing revision. When trusted_revision is provided but the revision cannot
    be resolved, raise BehaviorOwnershipError("invalid_partition_ancestry") instead
    of falling back to the merge-base check; retain the existing behavior only when
    the trusted revision exists and the file is absent, while trusted_revision=None
    continues to opt out explicitly.

In @backend/tests/test_behavior_ownership.py:

  • Around line 522-540: Extend _is_strictly_structural’s forbidden node tuple to
    reject lambda expressions, conditional expressions, comprehensions, and assert
    statements as runtime behavior. Add each corresponding source case to
    test_structural_only_rejects_runtime_side_effects, preserving the existing
    BehaviorOwnershipError assertion and matching executable_structural_only.
  • Around line 682-709: Extend test_catalogue_reports_owned_test_failure coverage
    in test_behavior_ownership.py by setting up a valid catalogue fixture, patching
    _run_test_nodes to return 0 for collect_only=True and a non-zero status for
    collect_only=False, and invoking validate_catalogue with run_tests=True. Assert
    BehaviorOwnershipError matches "owned_test_failure" and verify calls occurred in
    the order [True, False].
  • Around line 87-107: Extend test_partition_tampering_fails_closed and related
    ownership tests to cover validate_partition’s invalid_partition_assignments,
    invalid_partition_assignment, missing_partition_base_commit,
    invalid_partition_ancestry, and invalid_trusted_partition branches. Recompute
    the partition digest after each payload mutation so validation reaches the
    intended branch, and adjust the _mock_partition_git behavior or targeted mocks
    to exercise missing base commits and merge-base failures while preserving the
    expected BehaviorOwnershipError messages.
  • Around line 41-48: Update
    test_repository_partition_is_exact_deterministic_and_digest_bound to avoid
    requiring unavailable local Git refs: either skip when protected_base_commit or
    origin/main cannot be resolved, or call
    validate_partition(trusted_revision=None). Preserve assertions for
    authority_digest, eligible-target ordering, and deterministic assignment content
    without depending on trusted-revision validation.

Nitpick comments:
In @.ci/behavior-ownership/README.md:

  • Around line 30-36: Update the command examples in the behavior-ownership
    README to include the registered partition subcommand, showing that it
    regenerates partition.v1.json for maintainers after changes to group_for_target
    or the eligible target set. Keep the existing inventory, generate, and validate
    examples unchanged.

In @backend/scripts/behavior_ownership.py:

  • Around line 397-403: Update validate_partition to return the validated
    protected base commit, then have the caller near _validate_remaps reuse that
    returned value instead of calling _read_json again. Remove the second partition
    read and preserve the existing validation and digest-verification path.
  • Around line 98-111: The domain grouping logic in group_for_target must
    recognize additional auth and artifact module paths. In
    backend/scripts/behavior_ownership.py#L98-L111, add the /auth/ directory rule
    and match artifacts.py or artifact_* module names; then regenerate
    .ci/behavior-ownership/partition.v1.json#L627-L642 with behavior_ownership
    partition so authority_digest and affected assignments remain consistent.

In @backend/tests/test_behavior_ownership.py:

  • Around line 449-468: Replace the process-wide subprocess.run patches in
    test_owned_tests_runs_only_reviewed_nodes and
    test_collect_only_runner_adds_collection_flag
    (backend/tests/test_behavior_ownership.py:449-468 and :794-809) with patches to
    the module-local subprocess runner indirection used by
    backend/scripts/behavior_ownership.py, preserving each test’s existing fake
    behavior and assertions.

</details>

<details>
<summary>🪄 Autofix</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro Plus

**Run ID**: `010a9a5d-2609-40b9-afbb-8d499d3f7ce2`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7676ce4347db0c9694962a9b587a20765e16eac6 and e9a2c7d9c5730bf8d8301bee67aba12adfeffed0.

</details>

<details>
<summary>📒 Files selected for processing (12)</summary>

* `.agent-loop/initiatives/WS-QUAL-002-behavior-ownership-catalogue/STATUS.md`
* `.agent-loop/initiatives/WS-QUAL-002-behavior-ownership-catalogue/chunks/WS-QUAL-002-01-catalogue-foundation.md`
* `.agent-loop/initiatives/WS-QUAL-002-behavior-ownership-catalogue/reviews/WS-QUAL-002-01-pr-trust-bundle.md`
* `.ci/behavior-ownership/README.md`
* `.ci/behavior-ownership/examples/reviewed.example.json`
* `.ci/behavior-ownership/partition.v1.json`
* `CONTRIBUTING.md`
* `backend/scripts/behavior_ownership.py`
* `backend/scripts/run_test_lanes.py`
* `backend/tests/test_behavior_ownership.py`
* `docs/operations_backend_testing.md`
* `scripts/behavior-ownership.schema.json`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +627 to +642
{
"group": "shared",
"target": "backend/app/workers/artifacts.py"
},
{
"group": "shared",
"target": "backend/app/workers/async_runner.py"
},
{
"group": "shared",
"target": "backend/app/workers/celery_app.py"
},
{
"group": "artifacts",
"target": "backend/app/workers/checkers.py"
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Artifact modules assigned to shared.

backend/app/workers/artifacts.py is assigned to shared, while backend/app/workers/checkers.py on the adjacent line is assigned to artifacts. The cause is in group_for_target: the rule tests for the directory segment /artifacts/ and for a fixed token list, so a module named artifacts.py outside an artifacts/ directory falls through. backend/app/interfaces/artifacts.py at line 116 and backend/app/interfaces/artifact_operations.py at line 112 are affected in the same way.

Fix the rule in group_for_target, then regenerate this file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.ci/behavior-ownership/partition.v1.json around lines 627 - 642, Update
group_for_target to classify artifact-related module filenames such as
artifacts.py and artifact_operations.py, not only targets within an /artifacts/
directory or the existing fixed token list. Preserve the intended artifacts
grouping for checkers.py and related modules, then regenerate partition.v1.json.

Comment on lines +196 to +209
if trusted_revision is not None:
trusted = _git_show_optional(root, trusted_revision, PARTITION_PATH)
if trusted is not None:
try:
trusted_value = json.loads(trusted)
except json.JSONDecodeError as exc:
raise BehaviorOwnershipError("invalid_trusted_partition") from exc
if trusted_value != value:
raise BehaviorOwnershipError("untrusted_partition_change")
else:
try:
_git(root, "merge-base", "--is-ancestor", protected_base, "HEAD")
except BehaviorOwnershipError as exc:
raise BehaviorOwnershipError("invalid_partition_ancestry") from exc

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Custody check degrades silently when the trusted revision is unavailable.

_git_show_optional returns None for any git show failure. This includes the case where origin/main does not exist in the clone, for example after a shallow fetch or in a fork without the remote ref. Validation then skips the comparison against the protected copy and accepts a branch-local partition that only needs the protected base commit to be an ancestor of HEAD. This is the exact substitution that the README says must fail.

Distinguish "trusted revision does not exist" from "file absent at that revision". Fail closed when the revision itself is missing, unless the caller passes trusted_revision=None explicitly.

🔒 Proposed fail-closed handling
     if trusted_revision is not None:
+        _git(root, "rev-parse", "--verify", f"{trusted_revision}^{{commit}}")
         trusted = _git_show_optional(root, trusted_revision, PARTITION_PATH)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if trusted_revision is not None:
trusted = _git_show_optional(root, trusted_revision, PARTITION_PATH)
if trusted is not None:
try:
trusted_value = json.loads(trusted)
except json.JSONDecodeError as exc:
raise BehaviorOwnershipError("invalid_trusted_partition") from exc
if trusted_value != value:
raise BehaviorOwnershipError("untrusted_partition_change")
else:
try:
_git(root, "merge-base", "--is-ancestor", protected_base, "HEAD")
except BehaviorOwnershipError as exc:
raise BehaviorOwnershipError("invalid_partition_ancestry") from exc
if trusted_revision is not None:
_git(root, "rev-parse", "--verify", f"{trusted_revision}^{{commit}}")
trusted = _git_show_optional(root, trusted_revision, PARTITION_PATH)
if trusted is not None:
try:
trusted_value = json.loads(trusted)
except json.JSONDecodeError as exc:
raise BehaviorOwnershipError("invalid_trusted_partition") from exc
if trusted_value != value:
raise BehaviorOwnershipError("untrusted_partition_change")
else:
try:
_git(root, "merge-base", "--is-ancestor", protected_base, "HEAD")
except BehaviorOwnershipError as exc:
raise BehaviorOwnershipError("invalid_partition_ancestry") from exc
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/scripts/behavior_ownership.py` around lines 196 - 209, Update the
custody validation around _git_show_optional so a missing trusted revision is
distinguished from a missing PARTITION_PATH at an existing revision. When
trusted_revision is provided but the revision cannot be resolved, raise
BehaviorOwnershipError("invalid_partition_ancestry") instead of falling back to
the merge-base check; retain the existing behavior only when the trusted
revision exists and the file is absent, while trusted_revision=None continues to
opt out explicitly.

Comment on lines +241 to +261
forbidden = (
ast.FunctionDef,
ast.AsyncFunctionDef,
ast.Call,
ast.Await,
ast.Yield,
ast.YieldFrom,
ast.If,
ast.For,
ast.AsyncFor,
ast.While,
ast.With,
ast.AsyncWith,
ast.Try,
ast.Raise,
ast.Match,
ast.NamedExpr,
ast.AugAssign,
ast.Delete,
)
return not any(isinstance(node, forbidden) for node in ast.walk(tree))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The forbidden node tuple is incomplete, so structural_only accepts executable modules. _is_strictly_structural omits ast.Lambda, ast.IfExp, ast.comprehension, and ast.Assert. A module-level lambda, conditional expression, comprehension, or assert therefore passes as structural, although the README and the chunk contract state that branches, loops, and raises fail validation. The test parametrization mirrors the same gap, so no test detects it.

  • backend/scripts/behavior_ownership.py#L241-L261: add ast.Lambda, ast.IfExp, ast.comprehension, and ast.Assert to the forbidden tuple.
  • backend/tests/test_behavior_ownership.py#L522-L540: add one parametrized source per added node type, so the gap cannot reopen.
📍 Affects 2 files
  • backend/scripts/behavior_ownership.py#L241-L261 (this comment)
  • backend/tests/test_behavior_ownership.py#L522-L540
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/scripts/behavior_ownership.py` around lines 241 - 261, The
structural-only validation is missing executable AST node types. In
backend/scripts/behavior_ownership.py lines 241-261, update
_is_strictly_structural’s forbidden tuple to include ast.Lambda, ast.IfExp,
ast.comprehension, and ast.Assert. In backend/tests/test_behavior_ownership.py
lines 522-540, add one parametrized source case for each newly forbidden node
type to ensure structural_only rejects them.

Comment on lines +301 to +307
try:
value = json.loads(source)
except json.JSONDecodeError as exc:
raise BehaviorOwnershipError("invalid_protected_record") from exc
if isinstance(value, dict):
records.append(value)
return records

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Non-object protected records are skipped instead of rejected.

Line 305 appends only dict values. If a protected record at the base revision is replaced by a JSON list or scalar, _catalogue_at_revision drops it. The protected owner then disappears from protected_by_id, and the ownership, replacement, and effective-owner checks never run for it. Every other malformed-input path in this module fails closed. Make this one consistent.

🐛 Proposed fail-closed handling
-        if isinstance(value, dict):
-            records.append(value)
+        if not isinstance(value, dict):
+            raise BehaviorOwnershipError("invalid_protected_record")
+        records.append(value)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try:
value = json.loads(source)
except json.JSONDecodeError as exc:
raise BehaviorOwnershipError("invalid_protected_record") from exc
if isinstance(value, dict):
records.append(value)
return records
try:
value = json.loads(source)
except json.JSONDecodeError as exc:
raise BehaviorOwnershipError("invalid_protected_record") from exc
if not isinstance(value, dict):
raise BehaviorOwnershipError("invalid_protected_record")
records.append(value)
return records
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/scripts/behavior_ownership.py` around lines 301 - 307, Update the
record-loading logic around _catalogue_at_revision so any successfully parsed
JSON value that is not a dict raises
BehaviorOwnershipError("invalid_protected_record") instead of being skipped.
Preserve the existing JSONDecodeError handling and append only validated object
records, ensuring malformed protected data fails closed.

Comment on lines +41 to +48
def test_repository_partition_is_exact_deterministic_and_digest_bound() -> None:
value = json.loads((ownership.ROOT / ownership.PARTITION_PATH).read_text())
mapping = ownership.validate_partition()
assert len(mapping) == len(ownership.eligible_targets())
assert list(mapping) == ownership.eligible_targets()
authority = {key: value[key] for key in value if key != "authority_digest"}
assert value["authority_digest"] == hashlib.sha256(ownership._json_bytes(authority)).hexdigest()
assert ownership.build_partition(base_commit=value["protected_base_commit"]) == value

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

This test depends on local git state.

validate_partition() runs with the default trusted_revision="origin/main". It also runs git rev-parse --verify on protected_base_commit at line 170 of backend/scripts/behavior_ownership.py. Both steps fail in a shallow clone and in a clone without the origin/main ref. The test then fails for an environment reason, not a code reason.

Skip the test when the base commit or the trusted ref is unavailable, or pass trusted_revision=None and assert the digest and assignment content only.

💚 Proposed guard
 def test_repository_partition_is_exact_deterministic_and_digest_bound() -> None:
     value = json.loads((ownership.ROOT / ownership.PARTITION_PATH).read_text())
+    if subprocess.run(
+        ["git", "cat-file", "-e", f"{value['protected_base_commit']}^{{commit}}"],
+        cwd=ownership.ROOT,
+        capture_output=True,
+    ).returncode:
+        pytest.skip("protected base commit is not present in this clone")
     mapping = ownership.validate_partition()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_behavior_ownership.py` around lines 41 - 48, Update
test_repository_partition_is_exact_deterministic_and_digest_bound to avoid
requiring unavailable local Git refs: either skip when protected_base_commit or
origin/main cannot be resolved, or call
validate_partition(trusted_revision=None). Preserve assertions for
authority_digest, eligible-target ordering, and deterministic assignment content
without depending on trusted-revision validation.

Comment on lines +87 to +107
@pytest.mark.parametrize(
("mutator", "error"),
[
(lambda value: value.update(schema="wrong"), "unsupported_partition_schema"),
(lambda value: value.update(authority_digest="0" * 64), "partition_digest_mismatch"),
(lambda value: value.update(assignments="wrong"), "partition_digest_mismatch"),
(lambda value: value["assignments"].append(value["assignments"][0]), "partition_digest_mismatch"),
],
)
def test_partition_tampering_fails_closed(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, mutator, error: str
) -> None:
target = "backend/scripts/example.py"
value = _partition([target])
mutator(value)
_write_json(tmp_path / ownership.PARTITION_PATH, value)
monkeypatch.setattr(ownership, "eligible_targets", lambda root=ownership.ROOT: [target])
_mock_partition_git(monkeypatch)
with pytest.raises(ownership.BehaviorOwnershipError, match=error):
ownership.validate_partition(tmp_path, trusted_revision=None)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Several partition failure branches have no test.

The parametrized cases all fail at the digest check or earlier. The following error paths in validate_partition are never reached by this module:

  • invalid_partition_assignments (line 174) — assignments is not a list, with a matching digest.
  • invalid_partition_assignment (line 184) — an assignment with an unknown group, an extra key, or an ineligible target, with a matching digest.
  • missing_partition_base_commit (line 171) — _mock_partition_git always returns the same value that rev-parse is compared against.
  • invalid_partition_ancestry (line 209) — the _git mock never raises for merge-base.
  • invalid_trusted_partition (line 202) — the trusted copy is never malformed JSON.

The chunk contract requires negative tests for malformed records and invalid ancestry. Add cases that recompute the digest after each mutation, so the check under test is the one that fires.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_behavior_ownership.py` around lines 87 - 107, Extend
test_partition_tampering_fails_closed and related ownership tests to cover
validate_partition’s invalid_partition_assignments,
invalid_partition_assignment, missing_partition_base_commit,
invalid_partition_ancestry, and invalid_trusted_partition branches. Recompute
the partition digest after each payload mutation so validation reaches the
intended branch, and adjust the _mock_partition_git behavior or targeted mocks
to exercise missing base commits and merge-base failures while preserving the
expected BehaviorOwnershipError messages.

Comment on lines +522 to +540
@pytest.mark.parametrize(
"source",
[
"open('value.txt')\n",
"if FLAG:\n VALUE = 1\n",
"VALUES = []\nVALUES.append(1)\n",
"for item in VALUES:\n VALUE = item\n",
"raise RuntimeError('side effect')\n",
],
)
def test_structural_only_rejects_runtime_side_effects(tmp_path: Path, source: str) -> None:
target = "backend/scripts/structural.py"
path = tmp_path / target
path.parent.mkdir(parents=True)
path.write_text(source, encoding="utf-8")
with pytest.raises(ownership.BehaviorOwnershipError, match="executable_structural_only"):
ownership._validate_record_semantics(
tmp_path, {"status": "structural_only", "target": target}
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add structural cases for lambdas, conditional expressions, comprehensions, and asserts.

The parametrized sources cover a call, an if, a method call, a for, and a raise. They do not cover the node types that _is_strictly_structural omits from its forbidden tuple. Add these sources after you extend that tuple, so the gap cannot reopen:

  • SCALE = lambda value: value * 2
  • LIMIT = 10 if FLAG else 100
  • NAMES = [item for item in RAW]
  • assert LIMIT > 0
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_behavior_ownership.py` around lines 522 - 540, Extend
_is_strictly_structural’s forbidden node tuple to reject lambda expressions,
conditional expressions, comprehensions, and assert statements as runtime
behavior. Add each corresponding source case to
test_structural_only_rejects_runtime_side_effects, preserving the existing
BehaviorOwnershipError assertion and matching executable_structural_only.

Comment on lines +682 to +709
def test_catalogue_fails_when_exact_test_collection_fails(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
target = "backend/scripts/example.py"
path = tmp_path / target
path.parent.mkdir(parents=True)
path.write_text("def run():\n return 1\n", encoding="utf-8")
test_file = tmp_path / "backend/tests/test_example.py"
test_file.parent.mkdir(parents=True)
test_file.write_text("def test_run():\n pass\n", encoding="utf-8")
(tmp_path / "scripts").mkdir()
(tmp_path / ownership.SCHEMA_PATH).write_text(
(ownership.ROOT / ownership.SCHEMA_PATH).read_text(), encoding="utf-8"
)
_write_json(
tmp_path / ".ci/behavior-ownership/shared/example.json",
_catalogue_record("reviewed", target, "behavior:example"),
)
_write_json(tmp_path / ownership.PARTITION_PATH, _partition([target]))
monkeypatch.setattr(
ownership,
"validate_partition",
lambda root=ownership.ROOT, **kwargs: {target: "shared"},
)
monkeypatch.setattr(ownership, "_validate_remaps", lambda *args, **kwargs: None)
monkeypatch.setattr(ownership, "_run_test_nodes", lambda *args, **kwargs: 1)
with pytest.raises(ownership.BehaviorOwnershipError, match="stale_catalogue_test"):
ownership.validate_catalogue(tmp_path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

No test covers owned_test_failure.

test_catalogue_fails_when_exact_test_collection_fails patches _run_test_nodes to return 1, so the collection branch at line 408 of backend/scripts/behavior_ownership.py raises first. The run_tests=True branch at lines 410-411 is never executed. That branch is the --run-owned-tests behavior named in the chunk contract acceptance criteria.

Add a case where the collect-only call returns 0 and the execution call returns non-zero, then assert owned_test_failure.

💚 Proposed test
def test_catalogue_reports_owned_test_failure(tmp_path, monkeypatch):
    calls = []

    def fake_nodes(root, records, *, collect_only):
        calls.append(collect_only)
        return 0 if collect_only else 3

    monkeypatch.setattr(ownership, "_run_test_nodes", fake_nodes)
    # reuse the fixture setup from test_catalogue_fails_when_exact_test_collection_fails
    with pytest.raises(ownership.BehaviorOwnershipError, match="owned_test_failure"):
        ownership.validate_catalogue(tmp_path, run_tests=True)
    assert calls == [True, False]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_behavior_ownership.py` around lines 682 - 709, Extend
test_catalogue_reports_owned_test_failure coverage in test_behavior_ownership.py
by setting up a valid catalogue fixture, patching _run_test_nodes to return 0
for collect_only=True and a non-zero status for collect_only=False, and invoking
validate_catalogue with run_tests=True. Assert BehaviorOwnershipError matches
"owned_test_failure" and verify calls occurred in the order [True, False].

@abiorh-claw
abiorh-claw self-requested a review August 8, 2026 15:56
@abiorh-claw
abiorh-claw merged commit 6c12a14 into main Aug 8, 2026
9 checks passed
@abiorh-claw
abiorh-claw deleted the codex/ws-qual-002-01-catalogue-foundation branch August 9, 2026 04:18
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