Skip to content

A role word is recorded against the chunk that says it, so a re-push can say something new - #1428

Open
gedaiu wants to merge 1 commit into
deeplethe:devfrom
re-cinq:feat/role-words-per-chunk
Open

gedaiu wants to merge 1 commit into
deeplethe:devfrom
re-cinq:feat/role-words-per-chunk

Conversation

@gedaiu

@gedaiu gedaiu commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Part of #1267, PR 1 of 3. Thanks for the clear answers on the issue, they made this one easy to cut.

This builds decision 1 of ADR 0070. statement_qualifiers gets a chunk_id (NOT NULL, ON DELETE CASCADE, like fact_evidence), and its key becomes (fact_id, role, chunk_id). add_statement_qualifier now records a role word against the chunk that says it, so a re-push writes a new row for its new chunk and leaves the old one alone. The new edge is guarded the same way as the others under ADR 0048. The trigger checks that the chunk is in the fact's base, the export preflight scans it (squalifier.chunk), and the catalog guard and ADR 0048's table list it. materialize only learns to copy the new column. The liveness readers come in PR 2 and the re-sync in PR 3.

Reads are unchanged. Migration 0121 copies each existing role word onto every evidence chunk of its fact, superseded chunks included, and statement_qualifiers_for collapses the copies with DISTINCT, so every read returns what it returned before. A typed or implied row takes a role word only on the chunk of the source statement that said it, so a value one source said never sits on another source's chunk; a typed role no source carries keeps today's value on the row's evidence. Role words on facts with no evidence, the invalidated rows that documents::purge leaves behind, are dropped, and the migration reports how many with a NOTICE. If a live fact has role words and no evidence, the migration stops, the same way 0070's audit does. The ADR's status moves to In progress.

Tests: a role word written on two chunks makes two rows and the same chunk again adds nothing; a role word on another base's chunk is refused by the trigger and reported by the export preflight; and two scratch-database tests run migration 0121 itself, one checking the backfill and that reads still return one value, the other that a live fact with role words and no evidence stops it. Each fails without its change. I ran the store, server and cli suites against Postgres with UTOPIA_TEST_REQUIRE_DB=1, the migrations twice, and fmt and clippy with -D warnings.

Size: it adds 508 lines, a little over 500. The migration and the test that runs it are most of that and have to ship together, so I don't see a useful split.

…can say something new

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
@gedaiu
gedaiu force-pushed the feat/role-words-per-chunk branch from 8df2c62 to d49d2dc Compare October 26, 2026 10:27
@gedaiu

gedaiu commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

One fix before review: the backfill now puts a typed row's role word only on the chunk of the source statement that said it, not on every evidence chunk of the row. Otherwise a value from statement A could keep speaking through statement B's chunk after A is re-pushed. There's a new test for a typed row with two sources, and the description says so.

@WaylandYang WaylandYang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks, the per-chunk key, the cascade, the trigger and preflight coverage, and the abort and drop handling all match what we agreed. One thing before merge: backfill step 2 copies each source's own value onto the typed row. A typed row whose two sources gave different values (S1 lines 40-52 on c1, S2 58-70 on c2, row holding 40-52) then reads back two values, so reads change, and decision 1 says they mustn't in PR 1. Please match source rows on the typed row's own value and entity (s.value IS NOT DISTINCT FROM q.value AND s.entity_id IS NOT DISTINCT FROM q.entity_id), let the rest fall through to step 3, and add a test with two sources that disagree. PR 3 will bring S2's value across. Small nit: ADR 0048 still counts 39 edges, 13 by trigger; they're now 40 and 14.

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