Skip to content

Generator refuses joins and pins the demonstrations can't tell apart - #1682

Open
abrichr wants to merge 4 commits into
mainfrom
fix/generator-coincidental-joins
Open

abrichr wants to merge 4 commits into
mainfrom
fix/generator-coincidental-joins

Conversation

@abrichr

@abrichr abrichr commented Oct 31, 2026

Copy link
Copy Markdown
Member

Item 4 of #1677: the generator took ids that happened to be equal for joins, and pinned values one demonstration happened to store. Setup on the held-out apps fixed these by hand.

What changed

Joins (generate/demo_analysis.py, generate/verify_contract.py):

  • A row's own primary key is never a key to another table, and a new row's own key never links it to its record, unless a declared foreign key says so. On a fresh install a new row's id often equals an unrelated row's id in every demonstration (a time entry and its customer, an order line and its order).
  • A value is never placed in the column a path arrived by. A child row's link column holds the record's key by construction, so a value equal to it there tells nothing.
  • A list item's link is the column that holds the record's key in every item row of every demonstration. When two columns always agree and no declared foreign key picks one, the list is refused until a demonstration tells them apart. Before, it took the first column that matched.
  • When the best place goes through a key only equal values support (no declared foreign key, no column named after its table), and a place whose keys are all supported fits every demonstration too, the generator asks (locations.<kind>) instead of picking. Elimination by a claimed column no longer drops a longer path through it, which had left only the coincidental place.

Pins:

  • A pin needs two rows that agree. A value seen beside one write only (a field set one demonstration gives, a row only one demonstration added) is pinned only when every row of that table the kind's demonstrations wrote holds it. Otherwise the bookkeeping question lists it as one the demonstrations can't tell, the proposed answer leaves it out, and the kind has no contract until a person names it under unchecked or records another demonstration.
  • A field set whose own demonstrations leave such a column open, while the kind's full set pins it, gets no contract (Unpinned, listed under rejected). Before, the column was silently unchecked for that set.
  • Child rows now see the acting user, so a row's user column is the actor rather than a copy of an unrelated column that held the same number. A row the app adds beside an update may hold the target's key; that is a link to the record, never a pinned id from one demonstration.

Docs: GENERATE_CONTRACTS.md, CAPABILITIES.md.

Tests

  • tests/test_generate_coincidences.py (new, real PostgreSQL, no declared foreign keys, seeded so ids coincide in every demonstration):
    • A time entry's own id equalled its customer's id. The customer is placed behind the project. A correct entry on another project is CONFIRMED, and an entry on the wrong project is stopped.
    • A ticket's ext_id equalled its customer's id twice, beside the project's customer. The generator asks, with both places as options. The person's pick gives a CONFIRMED correct write.
    • An order line's own id equalled its order's id. The line links by order_id, so a correct order with two lines is CONFIRMED.
    • An order's currency stored by one write only: listed as "seen in one demonstration only", left out of the proposal, the kind refused until named. Named, an order in another currency is CONFIRMED.
  • Before the fix (main, same file): the customer was placed at timesheets.id -> customers.name, the ticket's customer was picked with no question, the currency was pinned, and the order fixture couldn't be answered. All 4 tests fail.
  • tests/test_generate_verify_contract.py: the tracker test's custom field id was 1, the bot's id. With the actor now seen on child rows, that equality was a real ambiguity, so the fixture uses 4 and adds a second demonstration with a custom field.
  • Generator, attack (rounds 1 to 3) and ensure-contract suites pass locally.

Real-app evidence

Running on held-out apps (instance 1) once the shared VM has room; this section will hold the commands and outputs.

Equal ids on a fresh install were taken as joins, and a value one write
happened to store was pinned:

- a row's own primary key is never a key to another table, and a new row's
  own key never links it to its record, without a declared foreign key;
- a value is never placed in the column a path arrived by;
- a list item's link must hold the record's key in every item row, and two
  that always agree are refused unless a declared foreign key picks one;
- a place through a key only equal values support, with a supported rival
  that fits every demonstration, becomes a locations question;
- a pin needs two rows that agree; one row is listed in the bookkeeping
  question, left out of the proposal, and a field set that leaves such a
  column open gets no contract;
- child rows see the acting user, and a row beside an update may link to
  the target instead of pinning a demonstration's id.

This branch has not been deployed

No deployments
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