Skip to content

feat(oc_recording): require triple identity to invalidate secondary (ENG-72)#82

Open
henokteixeira wants to merge 3 commits into
mainfrom
henok/eng-72-secondary-classification-triple
Open

feat(oc_recording): require triple identity to invalidate secondary (ENG-72)#82
henokteixeira wants to merge 3 commits into
mainfrom
henok/eng-72-secondary-classification-triple

Conversation

@henokteixeira

Copy link
Copy Markdown
Contributor

Summary

🤖 Generated with Nori

  • The secondary classification rule changes from single-field (secondary_genre_id != genre_id) to triple-equality ((register, genre, subcategory) of secondary must differ from primary by at least one field).
  • Shared secondary_equals_primary(...) helper in app/models/oc_recording.py backs RecordingCreate/RecordingUpdate model validators, update_recording service (with patch-vs-existing merge), and a new defense-in-depth check in split_service.request_split for segment effective primary triple vs parent secondary triple.
  • Companion to the oral-collector client PR feat(classification): require triple identity to invalidate secondary (ENG-72) oral-collector#60. Without this PR, the client allows the new rule but uploads/updates fail with 422 from the old validator.

Linear: ENG-72

Test Plan

  • pytest tests/ — 627 tests pass.
  • ruff check, ruff format — clean.
  • mypy app/ — clean.
  • After both PRs land, manually verify in the client: pick primary (Formal, Narrative, Myth) + secondary (Ceremonial, Narrative, Myth), save, sync, confirm no 422 from server.
  • Manually verify split path: parent with secondary set, request split with segment override that would collapse to parent's secondary triple → expect 422.

Share Nori with your team: https://www.npmjs.com/package/nori-skillsets

henokteixeira and others added 3 commits May 29, 2026 20:39
…ENG-72)

The secondary classification rule changes from "secondary genre must differ
from primary genre" (single field) to "the (register, genre, subcategory)
triple of the secondary must differ from the primary by at least one field".
The invalid state is only when all three are identical.

Shared helper `secondary_equals_primary(...)` backs every callsite:
- `RecordingCreate` / `RecordingUpdate` model validators (Pydantic).
- `update_recording` service: merges patch with existing recording before
  evaluating the triple, so partial updates are caught accurately.
- `split_service.request_split` adds a defense-in-depth check that each
  segment's effective primary triple (override-overlaid-on-inherit) is not
  identical to the parent's secondary triple — which would force the child
  row into primary == secondary.

Companion to the oral-collector client PR #60. Together they restore the
ability to set a secondary classification that shares the genre with the
primary as long as register or subcategory differs.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
Per review: business rules belong in the service layer, not in the Pydantic
DTO. RecordingCreate/RecordingUpdate are now plain schemas; the
`secondary_equals_primary` helper lives in `recording_service` and is invoked
at the start of both `create_recording` and `update_recording` (the latter
already had the merged-triple check). `split_service.request_split` imports
the same helper from `recording_service` instead of from the model.

Tests rewritten: the previously Pydantic-level rejection tests are now
service-level tests that call `create_recording` and assert `GenreConflictError`.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
Adds a dedicated exception type for the split-segment triple-collision case
following the existing GenreConflictError pattern, replacing the inline string
passed to ValidationError. Also drops the prose comments added in this PR
(naming carries the meaning) and updates GenreConflictError's message to
reflect the new triple-equality rule.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
@henokteixeira henokteixeira self-assigned this May 30, 2026
@henokteixeira henokteixeira requested a review from joaocarvoli May 30, 2026 00:10
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.

1 participant