Skip to content

runtime: the pre-flight resolves the edge the join wrote (ACE-133) - #319

Open
sandeep-agami wants to merge 1 commit into
ACE-132-classifier-otherfrom
ACE-133-fan-trap-written-edge
Open

runtime: the pre-flight resolves the edge the join wrote (ACE-133)#319
sandeep-agami wants to merge 1 commit into
ACE-132-classifier-otherfrom
ACE-133-fan-trap-written-edge

Conversation

@sandeep-agami

Copy link
Copy Markdown
Collaborator

Spec: ACE-133 (agami-sdlc projects/agami-core-extension/specs/reconcile-evidence/ACE-133-fan-trap-written-edge.md)

Stacked on #318 (ACE-132). Round 9 of the reconcile stack, from the 51-row run.

The finding

"Pre-flight reports fan_trap on a join it elsewhere reports as one-to-one." A subclass view joined to its base table on the key the model declares one_to_one was reported fan_trap on two aggregates by sm prepare, while sm receipt on the identical statement called the join one_to_one. Clearing the finding forced a UNION ALL restructure of a correct statement.

_one_side_facing_many collected every multiplying relationship between the two tables by table pair and never read from_column, to_column or the written ON; co-presence in scope was the whole test. Not "first found": every edge between the pair is unioned, so declaration order cannot help. The receipt's joins section matches by column pairs (_declared_pairs(rel) <= js.pairs), a different algorithm with no shared helper, which is how one receipt contradicted itself. No test declared two usable edges between one pair; test_ace059_join_adherence.py rules the shape out on purpose.

The change

runtime.py

  • _written_join_pairs(tree, scope_map): this SELECT's own joins as the column pairs each wrote, keyed by the unordered table pair, under _joined_table_pairs's pinning rule (an ON that names its own join's two relations; an unresolved qualifier contributes nothing).
  • _edges_as_written(rels, tree, scope_map, dialect): for a table pair the statement joined with a readable key, when at least one declared edge between them matches that key, only the matching edges stay in the list; otherwise every edge stays. _declared_pairs's None is "does not match", never a wildcard.
  • Applied once per arm in _aggregate_reports, before the chasm and fan detectors, so both read one list. _one_side_facing_many and _many_side_facing_one are unchanged.

A join on a key the model does not declare, or two tables in scope with no join between them (CROSS JOIN, a chain through a third table), keep every edge and today's verdict: over-reporting a fan says more than it had to; clearing one on an undeclared key would say something false.

Tests

tests/test_ace060_trap_free_aggregates.py, a new synthetic two-edge fixture (widget, widget_premium_v; one_to_one on id and many_to_one on parent_id, the sibling declared first so a reorder could not pass): the identity join is not_multiplied and the joins section of the same receipt reads declared, one_to_one (red before the change); the sibling join fans and names the join; a join on an undeclared key and a CROSS JOIN keep today's verdict. 507 passed across the trap, receipt, adherence, filter, probes and reconcile end-to-end suites.

🤖 Generated with Claude Code

The fan and chasm detectors matched a declared edge to a join by table pair and never read the
columns the join wrote, so a subclass view joined to its base table on the key the model declares
one-to-one was reported as a fan trap whenever a sibling many-to-one between the same pair was also
declared, while the joins section of the same receipt, which reads the written key, called the join
one-to-one. The edge list each arm's detectors read is now narrowed to the edges whose declared
columns the written join matches; a join on a key the model does not declare, or two tables in scope
with no join between them, keep every edge and today's verdict.

Spec: ACE-133

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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