Skip to content

feat(gfql): surface column-stat fact decisions in gfql_explain - #1860

Merged
lmeyerov merged 2 commits into
masterfrom
feat/gfql-colstats-trace
Aug 9, 2026
Merged

feat(gfql): surface column-stat fact decisions in gfql_explain#1860
lmeyerov merged 2 commits into
masterfrom
feat/gfql-colstats-trace

Conversation

@lmeyerov

@lmeyerov lmeyerov commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1859 (base is perf/gfql-colstats-bytype-flag, not master).

Closes the gap behind three bugs in this stack. Facts are a pure accelerator, so a dead one is invisible: the query still returns the right answer via the scan, every value test stays green, and you pay the build for nothing. That is exactly how the boolean-label rejection, the shared-pass fingerprint regression, and the col_stats_by_type no-op all survived a green suite.

Each consult now records op: col_stats with an outcome whose cases need different fixes:

outcome meaning fix
absent no fact for this key build them
stale fact exists, frame rebound since rebuild after the rebind
insufficient live, but cannot prove what the plan needs what a whole-frame fact always is against a typed pattern
served scan skipped

Immediately diagnostic — this is the structural limitation that took a full investigation to find, now one line:

no facts     -> nodes/absent        edges/absent        edges/absent
whole-frame  -> nodes/absent        edges/insufficient  edges/insufficient
per-type     -> nodes/served        edges/served        edges/served

Gated by the same _trace_active() check the adjacency decisions use, so it costs nothing outside index_trace()/gfql_explain; pinned both ways.

It also makes engagement testable against a public surface instead of monkeypatching private callees. That matters concretely: patching a name another module imported directly does not intercept the call, and I produced exactly that false negative while auditing — it read like a dead fast path.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi

lmeyerov added a commit that referenced this pull request Aug 9, 2026
…ic surface

Perf work is contracted 'same answer, faster', so a DEAD optimization is
invisible: the scan fallback returns the right result and every value test stays
green. Measured in test_lowering.py: 665 value assertions vs 42 engagement ones,
and three bugs in this stack lived in that gap.

The usual way to write engagement pins -- patch a private callee and count calls
-- FAILS OPEN. If another module did a direct 'from .gfql_fast_paths import X',
it holds its own reference and patching the definition site never intercepts, so
the pin cannot fail and the silence reads like a dead code path. I produced that
false negative while auditing. These helpers read the public gfql_explain trace
instead, so they observe the decision where it is made.

assert_col_stats asserts served/not-served and per-(role, column) outcomes, and
refuses to pass vacuously when no consult was reached. The existing trace pins
now dogfood it, and a self-test asserts the helper FAILS when the optimization
did not fire -- an engagement pin that cannot fail is worse than none.

Stacked on #1860.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
lmeyerov and others added 2 commits August 9, 2026 15:39
Facts are a pure accelerator, so a dead one is invisible: the query still returns
the right answer via the scan, every value test stays green, and you pay the
build for nothing. That is the failure mode behind three bugs in this stack --
the boolean label form never engaging, the shared-pass fingerprint making every
fact look stale, and col_stats_by_type silently no-oping.

Each consult now records op=col_stats with an outcome whose cases need different
fixes: absent / stale / insufficient / served. 'insufficient' is what a
whole-frame fact always is against a typed pattern -- the structural limitation
that took a full investigation to diagnose is now one trace line.

Gated by the same _trace_active() check the adjacency decisions use, so it costs
nothing outside index_trace()/gfql_explain; pinned both ways.

It also makes engagement testable against a PUBLIC surface rather than by
patching private callees -- which matters: patching a name that another module
imported directly does not intercept, and produces a false negative that reads
like a dead optimization.

Stacked on #1859.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
CI mypy (3.12/3.13) rejected the new trace records: IndexTraceStep is a TypedDict
with a closed key set and Literal-constrained path/decision_code, so the added
role/column/type_column/type_value keys and the col_stats_* codes were type
errors. Local mypy passed only because I checked a file subset.

Declared properly rather than cast around: the keys join IndexTraceStep, 'facts'
joins IndexPath, the four col_stats_* codes join IndexDecisionCode, and outcome
-> code is an explicit checked mapping so adding one side without the other is a
type error instead of a string that silently never matches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
@lmeyerov
lmeyerov force-pushed the feat/gfql-colstats-trace branch from 547590f to 00c57e0 Compare August 9, 2026 22:39
@lmeyerov
lmeyerov changed the base branch from perf/gfql-colstats-bytype-flag to master August 9, 2026 22:39
lmeyerov added a commit that referenced this pull request Aug 9, 2026
…ic surface

Perf work is contracted 'same answer, faster', so a DEAD optimization is
invisible: the scan fallback returns the right result and every value test stays
green. Measured in test_lowering.py: 665 value assertions vs 42 engagement ones,
and three bugs in this stack lived in that gap.

The usual way to write engagement pins -- patch a private callee and count calls
-- FAILS OPEN. If another module did a direct 'from .gfql_fast_paths import X',
it holds its own reference and patching the definition site never intercepts, so
the pin cannot fail and the silence reads like a dead code path. I produced that
false negative while auditing. These helpers read the public gfql_explain trace
instead, so they observe the decision where it is made.

assert_col_stats asserts served/not-served and per-(role, column) outcomes, and
refuses to pass vacuously when no consult was reached. The existing trace pins
now dogfood it, and a self-test asserts the helper FAILS when the optimization
did not fire -- an engagement pin that cannot fail is worse than none.

Stacked on #1860.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
@lmeyerov
lmeyerov merged commit 0704eed into master Aug 9, 2026
69 of 101 checks passed
lmeyerov added a commit that referenced this pull request Aug 9, 2026
…ic surface

Perf work is contracted 'same answer, faster', so a DEAD optimization is
invisible: the scan fallback returns the right result and every value test stays
green. Measured in test_lowering.py: 665 value assertions vs 42 engagement ones,
and three bugs in this stack lived in that gap.

The usual way to write engagement pins -- patch a private callee and count calls
-- FAILS OPEN. If another module did a direct 'from .gfql_fast_paths import X',
it holds its own reference and patching the definition site never intercepts, so
the pin cannot fail and the silence reads like a dead code path. I produced that
false negative while auditing. These helpers read the public gfql_explain trace
instead, so they observe the decision where it is made.

assert_col_stats asserts served/not-served and per-(role, column) outcomes, and
refuses to pass vacuously when no consult was reached. The existing trace pins
now dogfood it, and a self-test asserts the helper FAILS when the optimization
did not fire -- an engagement pin that cannot fail is worse than none.

Stacked on #1860.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
lmeyerov added a commit that referenced this pull request Aug 11, 2026
Extends the #1860 col-stats trace to the fast paths themselves, which is what
the engagement audit needs. Fast paths are contracted 'same answer, faster', so
a DEAD one is invisible: the fallback returns the right result and every value
test passes. Ratio in test_lowering.py: 665 value assertions vs 42 engagement.

Recorded at the CALL SITE in gfql_unified, not via shims or in-body hooks: it is
one place, it is where the decision is consumed, and it cannot be bypassed the
way patching a directly-imported name is -- gfql_unified imports these names
directly, which already produced one false negative during an audit.

assert_fast_path(g, query, path, served=) makes engagement a one-liner against a
PUBLIC surface. Pinned: both paths engage on their shapes; a shape neither serves
declines both with a correct answer; SHORT-CIRCUIT is distinguishable from
DECLINE (when grouped-agg serves, two_hop_count is absent from the map rather
than present-and-False -- conflating those lets a dead path read as 'declined for
a good reason'); and the helper FAILS when the path did not fire.

Registered in POLARS_TEST_FILES. Local: 0 non-cudf failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
lmeyerov added a commit that referenced this pull request Aug 11, 2026
Extends the #1860 col-stats trace to the fast paths themselves, which is what
the engagement audit needs. Fast paths are contracted 'same answer, faster', so
a DEAD one is invisible: the fallback returns the right result and every value
test passes. Ratio in test_lowering.py: 665 value assertions vs 42 engagement.

Recorded at the CALL SITE in gfql_unified, not via shims or in-body hooks: it is
one place, it is where the decision is consumed, and it cannot be bypassed the
way patching a directly-imported name is -- gfql_unified imports these names
directly, which already produced one false negative during an audit.

assert_fast_path(g, query, path, served=) makes engagement a one-liner against a
PUBLIC surface. Pinned: both paths engage on their shapes; a shape neither serves
declines both with a correct answer; SHORT-CIRCUIT is distinguishable from
DECLINE (when grouped-agg serves, two_hop_count is absent from the map rather
than present-and-False -- conflating those lets a dead path read as 'declined for
a good reason'); and the helper FAILS when the path did not fire.

Registered in POLARS_TEST_FILES. Local: 0 non-cudf failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
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