Skip to content

A shape of statement implies a fact of another property, read once per phrase and cached - #882

Merged
WaylandYang merged 4 commits into
devfrom
feat/implication-rules
Sep 23, 2026
Merged

WaylandYang merged 4 commits into
devfrom
feat/implication-rules

Conversation

@WaylandYang

Copy link
Copy Markdown
Contributor

The fifth slice of 0044 decision 3, and the one the prototype's recall gap points at (0044 §2): the facts a reader draws without the text stating them — a place's country from its region, a film's country from "British film", a year from a phrase — reach the typed graph through implication rules with cached readings. This is the largest remaining piece of alignment (cut 2); with it the parity run against the bound pass becomes possible once the bench in #880 is run with a model.

Model

  • An implication rule is keyed like a binding (a phrase signature: phrase × subject class × object class or value) or by a kind word, concludes one property, and takes its object either from the statement's own object or from a reading of the object's words. Three readings exist, named in one table (READINGS) that feeds both the prompt and the validator: country_of_nationality, country_of_place, year_of_phrase.
  • Rules have the binding lifecycle: the aligner proposes (after it has decided the signatures of a run, and once per kind word), with the same fingerprint basis as 0053 so changed inputs re-ask; "nothing implied" is recorded as an agent rejection so it is not asked again; a person approves or rejects on the alignment queue, and the decision commits with its job (0051 rule). A person's decision is never overwritten.
  • Readings are model calls made once per distinct phrase by a read_phrases job and cached in phrase_readings — a name resolves to an entity in the base (found by name, or created with its name fact per 0041), a year lands as a value, and "no answer" is cached too, so an unplaceable phrase is never asked about twice. The job then queues materialisation.
  • materialize never calls a model. It reads the cache and, for approved rules, writes facts marked implied with the triggering statement's evidence copied, tracked in implied_fact_sources (rule + statement, or rule + entity for kind-word rules), and retired by source exactly like bound rows: a rejected rule, a dead statement, an entity whose kind word changed all take the row down at the next run. A reading not yet cached simply produces nothing that round. A thing never implies itself.
  • The export carries utopia:implied on such facts, so an auditor can tell a stated fact from a drawn one.

Schema (migration 0073): facts.implied, implication_rules, phrase_readings, implied_fact_sources. CURRENT_SCHEMA_VERSION → 72 (it counts files; 0071 is held by #861).

Surface: POST /kbs/{id}/review/alignment/rules/{rule_id} ({approve} → 202 with a job id), a rule item on the alignment queue with the shape, the concluded property and the reading, a card on the Review page in both languages.

Verified against pgvector/pgvector:pg16 on a fresh database:

cargo fmt --all --check                                     ok
cargo clippy --workspace --all-targets -- -D warnings       clean
cargo test -p utopia-store   (UTOPIA_TEST_REQUIRE_DB=1)      335 passed, 0 failed
cargo test -p utopia-server  (UTOPIA_TEST_REQUIRE_DB=1)      397 passed, 0 failed
cargo test -p utopia-extract / utopia-cli                    pass
cd web && pnpm build && pnpm test                            tsc clean, 132 passed

New coverage: a proposal lands once and a person's decision commits with the right job (read_phrases when a reading is needed, materialisation otherwise); an approved rule waits for its reading, then implies a fact with evidence and sources, and a second run changes nothing; rejecting the rule or losing the statement retires the implied row; a kind-word rule implies a fact for every thing so called and "no answer" is cached; the aligner offers a kind word and records the model's proposal on the queue; read_phrases fills the cache, names a thing and queues materialisation; the route answers 202 and scopes by base; the rule and reading parsers count malformed rows without believing them; the existing lifecycle regressions now script the rule question.

Not in this PR, said in the record: a revision table of rules; readings beyond the three; evidence for kind-word rules (the kind word is the entity's, not a statement's, so there is no quote to copy).

🤖 Generated with Claude Code

WaylandYang and others added 4 commits September 23, 2026 17:16
…r phrase and cached

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…lel tests cannot leak into it

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
@WaylandYang
WaylandYang merged commit 58b4bf9 into dev Sep 23, 2026
7 checks passed
@WaylandYang
WaylandYang deleted the feat/implication-rules branch September 23, 2026 09:53
@WaylandYang WaylandYang mentioned this pull request Sep 25, 2026
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