Conversation
Signed-off-by: wangzifei <wangzifei@cit.group.hk>
|
Checked this against real data rather than against the description, because the gate rests on one assumption worth testing: that It holds, and I can put a number on it. In a current-schema base here (
That was my one real worry — an entity holding both a proper name and a description would silently stop accepting legitimate aliases — and the data says the two are exclusive in practice, not only by intent. The same query is the argument for the change. The described entities look like this:
The gate is structural, which is the part that matters here. One thing I would change. The refusal is invisible. That runs against how the rest of this codebase treats its own guards: It does not need to block the PR, but it is cheap now and awkward to retrofit once the silence is the established behaviour. |
WaylandYang
left a comment
There was a problem hiding this comment.
The prompt tightening and the intent here both look right, but the structural gate
is placed in names::record, which is shared with a caller that is not extraction:
graph::update_entity — the human rename endpoint — calls it at graph.rs:1677,
right under the comment "人改的名字也是一条名字事实(0041)".
update_entity has no check excluding described entities and does not clear
description, so after this change a person who renames a described thing gets a
new display name and no name fact at all. The entity then can't be found by name.
Verified against Postgres 16 with the same probe on both branches — create a
described entity, rename it through graph::update_entity, then count known_as
facts:
dev |
this branch | |
|---|---|---|
canonical_name |
"Acme Shanghai Co." |
"Acme Shanghai Co." |
known_as fact count |
1 | 0 |
description |
still set | still set |
The predicate itself is sound — create_described is the only production writer of
entities.description, and there is no UPDATE ... SET description anywhere, so
description IS NOT NULL really does mean "described, unnamed". It's the layer
that's wrong: what this PR wants to stop is the model writing a role phrase into
n, which is the extraction path (extraction_open.rs:350 and :741). Gating
there would leave the human decision alone.
Minor, separate: the guard adds a round-trip per name recorded on the ingest hot
path. If it stays in names::record, it could fold into the existing statement
rather than being a separate SELECT EXISTS.
What
OPEN_SYSTEMcontract sonaccepts only a proper name or fixed term. A role or generic phrase whose referent is resolved by the passage stays as a description or qualifier words, even when it points at a listed or already recorded thing.names::record: an entity with a non-nulldescriptionis described rather than named, so it refuses a late alias. The gate checks the entity state and referent relationship, not a vocabulary blacklist; fixed terms such asfree cash flowremain valid names.docs/design/extraction.md.Why
Issue #770 shows a passage-resolved role phrase entering
known_as. That turns a description into a recall bridge and can pull an unrelated thing back under one identity. The prompt-level rule fixes the contract at extraction time, while the store gate gives the described-entity invariant a structural last check.Testing
open::tests::a_passage_resolved_phrase_is_not_another_name; it failed on its first missing phrase assertion before the prompt change and passes afterward.a_described_thing_has_a_description_and_no_name; the new late-alias assertion first observedSome(...)and then passed after the store gate returnedNone.UTOPIA_DATABASE_URL=postgres://utopia:utopia@127.0.0.1:15432/utopiaandUTOPIA_TEST_REQUIRE_DB=1,cargo test --locked -j 1 -p utopia-store --test a_described_thing_is_an_entity_without_a_name -- --test-threads=1passes.cargo fmt --all --checkandcargo clippy --workspace --all-targets -- -D warningspass.memory allocation failed/os error 1455), not by a test assertion.Fixes #770