feat(ontology): promote temporal primary Voice history onto main (ADR 0252) - #761
Conversation
Expand voc_type from five to twelve post types (Supplier, Employee, Business, Regulator, Investor, Society, Process added) grounded in stakeholder identification theory and quality-engineering practice, and mirror six new counterparty relationship codes so any post class can type each named organization. Ontology SKOS concepts/object properties, idempotent migration 0222, classifier vocabulary + prompt, round-trip and parser tests stay in sync.
…y' into feat/voice-of-x-combinations
# Conflicts: # backend/tests/test_api.py
Three-way promote feat/voice-primary-history (ADR 0252). Main already carried 0237's effective_from column and queries voice.effective_to in main.py and ingestion, but shipped neither the 0243 migration (effective_to + exclusive GiST primary-period range + synchronize trigger) nor the dataclass/export support, leaving runtime queries against a missing column. Grafted the missing layer onto main's newer files: - migrations/0243_source_post_voice_history.sql (effective_to, interval check, partial unique index, primary-period exclusion constraint, and the synchronize_source_post_primary_voice trigger with least(created_at, change_at) effective_from) - ontology_neighborhood.py VoiceTuple gains required effective_from and optional effective_to with offset-aware + ordered-interval validation - neighborhood ingestion SELECT returns effective_from/effective_to, cutoff predicate collapses to coalesce($2,$3), and the payload and JSON-LD projections carry valid_from/valid_to - tests aligned to the coalesce predicate (branch versions are strict supersets of main's, zero main-only cases dropped) - ADR 0252 and voice-combination requirements doc Suite: 54 neighborhood/voice tests + 23 schema + 20 replay green.
|
Warning Review limit reachedNext included review available in 39 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (12)
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. Comment |
| | [`voice-combination-technical-requirements.md`](../voice-combination-technical-requirements.md) | [0246](0246-expanded-voice-of-x-post-taxonomy.md), [0251](0251-evidence-bearing-voice-combinations.md), [0252](0252-temporal-primary-voice-history.md) | | ||
| | [`lineage-bi-research-notes.md`](../lineage-bi-research-notes.md) | [0084](0084-lineage-research-grounding.md), [0062](0062-semantic-unit-embedding.md), [0064](0064-lineage-evidence-and-tree-assembly.md), [0024](0024-rankweave-fusion-fail-closed.md), [0165](0165-quantity-script-display.md), [0167](0167-rankweave-ranking-channel-evidence.md), [0169](0169-ask-batched-lineage-graph.md), [0172](0172-event-lineage-channel-evidence.md), [0202](0202-ask-event-time-filter.md), [0223](0223-explicit-semantic-content-unit-kinds.md) | |
There was a problem hiding this comment.
🟡 Duplicate research-notes row in ADR map
The supporting-document map now lists lineage-bi-research-notes.md twice. The added copy duplicates the existing row but drops the 0238 reference, leaving two conflicting entries for one document.
| | [`voice-combination-technical-requirements.md`](../voice-combination-technical-requirements.md) | [0246](0246-expanded-voice-of-x-post-taxonomy.md), [0251](0251-evidence-bearing-voice-combinations.md), [0252](0252-temporal-primary-voice-history.md) | | |
| | [`lineage-bi-research-notes.md`](../lineage-bi-research-notes.md) | [0084](0084-lineage-research-grounding.md), [0062](0062-semantic-unit-embedding.md), [0064](0064-lineage-evidence-and-tree-assembly.md), [0024](0024-rankweave-fusion-fail-closed.md), [0165](0165-quantity-script-display.md), [0167](0167-rankweave-ranking-channel-evidence.md), [0169](0169-ask-batched-lineage-graph.md), [0172](0172-event-lineage-channel-evidence.md), [0202](0202-ask-event-time-filter.md), [0223](0223-explicit-semantic-content-unit-kinds.md) | | |
| | [`voice-combination-technical-requirements.md`](../voice-combination-technical-requirements.md) | [0246](0246-expanded-voice-of-x-post-taxonomy.md), [0251](0251-evidence-bearing-voice-combinations.md), [0252](0252-temporal-primary-voice-history.md) | |
Was this helpful? React with 👍 or 👎 to provide feedback.
| and voice.effective_from <= coalesce($2::timestamptz, $3::timestamptz) | ||
| and ( | ||
| voice.effective_to is null | ||
| or coalesce($2::timestamptz, $3::timestamptz) < voice.effective_to | ||
| ) |
There was a problem hiding this comment.
📝 Info: No-cutoff read keyed to snapshot_at
When knowledge_cutoff is null the predicate now keys off snapshot_at rather than effective_to is null. For a fresh live read the two agree, but a voice with effective_from after the frozen snapshot is now dropped. This matches the ADR's snapshot-consistency goal for paged continuation, and half-open intervals still yield exactly one primary.
Was this helpful? React with 👍 or 👎 to provide feedback.
| create unique index if not exists source_post_voice_current_pair_idx | ||
| on source_post_voice (post_id, voice_type_code) | ||
| where effective_to is null; |
There was a problem hiding this comment.
📝 Info: Duplicate current-pair unique index
The new migration re-adds effective_to and a unique index on (post_id, voice_type_code) where effective_to is null, which source_post_voice_current_type_idx in the earlier migration already provides. The result is two identical unique indexes under different names, adding redundant write-time cost.
Was this helpful? React with 👍 or 👎 to provide feedback.
| update source_post_voice | ||
| set effective_to = change_at | ||
| where post_id = new.post_id | ||
| and effective_to is null | ||
| and (is_primary or voice_type_code = new.voc_type_code); | ||
|
|
||
| insert into source_post_voice | ||
| (post_id, voice_type_code, is_primary, truth_status_code, | ||
| effective_from, recorded_at) | ||
| values ( | ||
| new.post_id, | ||
| new.voc_type_code, | ||
| true, | ||
| 'truth_observed', | ||
| case when tg_op = 'INSERT' then least(new.created_at, change_at) else change_at end, | ||
| change_at | ||
| ); |
There was a problem hiding this comment.
📝 Info: 0243 trigger replaces 0237's upsert with close-then-insert
Sorted migration order makes 0243's synchronize_source_post_primary_voice the effective definition, dropping the ON CONFLICT upsert. It closes every current primary and any current row matching the incoming voice before a plain insert, which keeps both the current-pair unique index and the primary exclusion constraint satisfied and allows A to B to A.
Was this helpful? React with 👍 or 👎 to provide feedback.
Three-way merge of feat/voice-primary-history onto protected main, completing the half-shipped Voice temporal layer.
Main already carried 0237's effective_from column and main.py/ingestion queried voice.effective_to, but neither migration 0243 nor the dataclass/export support were present, leaving runtime queries against a missing column. Grafts the missing layer onto main's newer files:
Local: 1597 unit/feature tests + 12 skip (backend api excluded only by a stale local stack DB; test_schema throwaway-DB fixture applies 0237+0243 cleanly).