Repository navigation
fix(query): a pseudo-attribute reads the nearest earlier pattern with its entity (#594) - #602
Merged
Merged
Conversation
… its entity (#594) :db/valid-from, :db/valid-to, :db/tx-count and :db/tx-id read the fact of the last pattern the planner ran on the same entity (per-entity __f* keys), so the answer depended on the plan, and with no such pattern matched every fact of the entity. Now parser::resolve_pseudo_sources names each one's source at parse time: the nearest earlier pattern in the text with the same entity term and a stored attribute, in its own clause list, then before each enclosing not/or (not-join/or-join only for a listed variable, which gains the hidden variable), never past a rule body; PRS-081 when there is none. The source binds a hidden variable ?#<n>.<attr> (Pattern::meta); the pseudo-attribute pattern binds or checks against it (Pattern::pseudo_source, substituted to the metadata in not bodies) and never scans. The optimizer places it after its source; not/or bodies that read metadata take the per-row paths (or-join gains one). The engine's scans fill the hidden variable's column; the per-row Step::Pseudo choice and the [e _ _] fallback for per-fact attributes are gone. :db/valid-at is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HyLscCUuJ1GQpdAeX79EdR
This was referenced Oct 15, 2026
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This was referenced Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 2 of 3 for #594 / #596 (plan:
docs/superpowers/plans/2026-10-15-594-596-plan-independence.md, spec:docs/superpowers/specs/2026-10-15-plan-independent-answers-design.md§3.1–3.6, §4.1–4.4). #594 is closed by hand after merge.What changes (breaking for v3.0.0)
parser::resolve_pseudo_sourcesruns on every query:where, rule body and prepared template. A per-fact pseudo-attribute (:db/valid-from,:db/valid-to,:db/tx-count,:db/tx-id) reads the fact of its source: the nearest earlier pattern in the text with the same entity term and a stored attribute. The search covers its own clause list, then the clauses before each enclosingnot/or. It crossesnot-join/or-joinonly for a listed entity variable, which gains the hidden variable as a join variable, and it never goes past a rule body or into a siblingorbranch. Terms match as spec §2 describes: variables and$slots by name, constants by entity id. With no source, the query, rule or prepared query fails with the new PRS-081 (src/error.rs,docs/ERROR_REFERENCE.md).?#<n>.<attr>(Pattern::meta), and the pseudo-attribute pattern reads it (Pattern::pseudo_source). It binds its value term or checks it against the metadata, and never scans facts. The per-entity__fvf_/__fvt_/__ftc_/__fti_keys, the matcher's fast path and the "every fact of the entity" fallback are all gone, as are the__ffilters innot.optimizer::planplaces a pseudo-attribute pattern after its source, and after the pattern that binds its value variable, rather than by selectivity. Anotwhose body reads a source now waits for that source.not/or. A body or branch holding a pseudo-attribute takes the per-row paths: no exclusion set fornot/not-join, and seeded branches foror.or-joingains a per-row path seeded with its join variables only.Step::Pseudo { col, value }binds or checks against it.pseudo_forms, the per-rowpickand the[e _ _]form for per-fact attributes are removed;:db/valid-atkeeps its[e _ _]join. Queries that used to stay on the executor because of plan-dependent pseudo-attributes now either run on the engine or are PRS-081.Deviations from the spec/plan
Pattern::pseudo_sourceisOption<EdnValue>rather thanOption<String>: a symbol, or the metadata integer once anotbody or another substituting path has put it in. As a result,not_body_matches,evaluate_not_joinandnot_clause_violated_via_rulesget the outer metadata throughsubstitute_patternwithout any seeding code. Plan decision 9 had proposed seeding the match; the per-row fallbacks it describes are kept.Tests
tests/pseudo_source_test.rs(12 tests): every PR 2 item in spec §7. The successful$slotcase is checked at prepare time only, because of the separate bug below.pseudo_attributes_without_a_source_are_prs_081).executeand a cursor).temporal_metadata_test::pseudo_attr_as_sole_pattern: now expects PRS-081, then shows the[:alice ?a _]form.executortest_execute_query_with_rules_valid_at_timestamp: the INT-026 query gains a source.queries(): five no-source shapes gained[?e ?b _]sources.other_queries_stay_on_the_executor: split into the executor-only shapes and the new PRS-081 test.Local:
cargo test1532 (1522 passed, 10 ignored);cargo test --features test-oracleand--features wasmpass. Clippy is clean with--all-features, default,wasm,test-oracle, wasm32browserand wasm32-wasip1;cargo checkinfuzz/passes. Codex review: no findings.Found while testing (not fixed here)
prepared::substitutecallsquery.as_of.take()/query.valid_at.take()before matching a slot, so a literal:as-of,:valid-ator:any-valid-timein a prepared query is silently dropped. For example,prepare("[:find ?v :as-of 1 …]")returns the current rows. It has been there since 0ef578e (2026-04-04) and is onmaintoo. Raised separately.Measurement
#394 envelope A/B on the bench machine (
origin/v3vs this branch, 100K and 1M): see the comment below.Docs
CHANGELOG (Breaking changes entry, the #545 engine entry updated, a Tests entry), CLAUDE.md, README, TEST_COVERAGE, ERROR_REFERENCE. Docs site: project-minigraf/minigraf-docs PR (pseudo-attributes
@since/@until v3.0.0, architecture streaming-engine bullet).🤖 Generated with Claude Code
https://claude.ai/code/session_01HyLscCUuJ1GQpdAeX79EdR