Skip to content

fix(doc): build relationship edge tables once per model, uncharged to the visit budget - #565

Merged
HuiJun merged 2 commits into
developfrom
fix/relationship-tables-uncharged
Sep 24, 2026
Merged

HuiJun merged 2 commits into
developfrom
fix/relationship-tables-uncharged

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

What and why

RelatedElements, WhereRelated and relationship-derived columns read a per-kind edge table that is built, on first use, by scanning every declaration in the workspace. Each declaration scanned was charged to the query's visit budget (default 100,000), so on any model past ~100k declarations every relationship query failed with visit-budget before traversing a single edge. The migrated TMT model has 101,014 declarations; its two requirements-mapping documents need about 200 visits for their rows and were the only two of 103 documents that would not render.

The scan is a fixed cost of the model, not of the query, so it is no longer charged and is memoized across executions:

type Context struct {
	…
	Related *RelationshipTables   // nil: tables live for one Execute
}
  • executor.scanScope no longer calls consumeVisit; relationshipEdges/relatedNeighbors can no longer fail and drop their expression/error plumbing.
  • RelationshipTables.lookup(kind, context) discards entries built against another Index/Model, or against the same Index before an edit (symbols.Index.Generation(), the write counter its layers already bump and ShortNamed/LibraryIdentity memoize on), so a shared table can never answer for the wrong or a stale model.
  • docir.Evaluate and EvaluateLinked give a context without tables its own, so the queries of one document (or a linked set) build each kind once instead of once per query.
  • Traversal is unchanged: traverseRelated still pays one visit per element reached and still raises visit-budget, naming the column for a related column.

Default budgets are untouched.

Specification basis

Not a specification behaviour; the executor's own bounded-workload contract. The RelatedElements and relationship-column rows of docs/project/spec-compliance.md now state that the edge tables are built once per model and uncharged, and name the new tests.

How it was verified

  • The three tests that pinned "table construction is charged" now pin the opposite, exactly: TestExecuteRelatedLeavesTableConstructionUncharged (a source with no edges succeeds with VisitBudget: 1; two subsetters fit a budget of exactly two), TestExecuteRelatedColumnLeavesTableConstructionUncharged (count 0 within one visit; count 2 within two; budget one still fails naming column related), and TestExecuteWhereRelatedChargesTheVisitBudget (a row nothing satisfies is kept with exists = false under one visit). TestExecuteRelatedConsumesTheVisitBudget and the related-column exhausted visit budget case still prove traversal is charged.
  • TestExecuteSharesRelationshipTablesThroughTheContext: two executions under one Context.Related reuse the same table; a context over another index rebuilds it.
  • TestExecuteRebuildsRelationshipTablesAfterAnIndexEdit: one Context.Related follows AddDocument of a further subsetter and its RemoveDocument.
  • TMT (49,060 elements, 101,014 declarations): L1 TMT to L2 APS Requirements Mapping Document and L3 APT to L2 APS Requirement Mapping Document render under the default budget in ~9 s each (previously exceeded its visit budget in column Refine (outgoing)); the Refine cells hold the one real refinement the model states, as Cameo's matrix does.
  • go build ./..., go vet ./..., gofmt -l . (empty), go test ./internal/doc/...; full go test ./... reported in the checks.

Checklist

  • make test and make lint pass locally
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md)
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (compliance rows need nothing: the census is counted at docs build)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

… the visit budget

RelatedElements, WhereRelated and relationship-derived columns build a
kind's edge table by scanning every workspace declaration, and charged
each one to the query's visit budget, so any model past ~100k
declarations failed every relationship query before traversing. The
scan is now memoized in queryexec.Context.Related, shared across the
queries a document evaluates, and left uncharged; traversal still pays
one visit per element reached.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review September 24, 2026 15:42
devin-ai-integration[bot]

This comment was marked as resolved.

…uilt from takes an edit

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@HuiJun
HuiJun merged commit b348f5d into develop Sep 24, 2026
15 checks passed
@HuiJun
HuiJun deleted the fix/relationship-tables-uncharged branch September 24, 2026 18:07
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