Skip to content

Add exitdrill mutate to build seeded adversarial derivatives of an export #132

Description

@ChelseaKR

What

exitdrill mutate export.json --attachment-root files/ --out lossy/ --seed 7 --drop-relationships 2 --corrupt-attachment 1 --strip-permissions 1 --drop-audit-events 2 --alter-field 1 builds a deterministic adversarial derivative of any synthetic normalized export — the generalisation of scripts/build_directus_lossy_canary.py, which hard-codes one profile's mutations. It keeps entity row counts unchanged (the "same count, different evidence" attack the README highlights), writes a mutation-statement.json listing exactly what it changed as aggregate counts (never record ids or values), and refuses any input that lacks the synthetic-only marker.

Why it matters

Negative controls are how this project proves its gates can fail: the lossy canary exists because a clean fixture alone proves nothing. Today every new fixture needs a hand-written mutation script, so the negative control is the part most likely to be skipped. A seeded mutator makes "this evaluator would have caught it" a one-line check for any synthetic contract, and it is squarely inside the freeze — a test-tooling verb over the existing normalized contract, adding no connector or data category. It also gives the outside-person walkthrough (#51) a second thing to try.

Scope

  • mutate.py with pure, seeded mutation operators per dimension; output through the same bounded atomic writer; statement document with a schema.
  • validate accepts the derivative; drill on it must report not_structurally_restorable with the dimensions named in the statement failing.
  • scripts/build_directus_lossy_canary.py reimplemented on top of the operators, with its committed statement byte-identical (acceptance test).

Out of scope

  • Mutating baselines or capture bundles; random or unseeded modes; any mutation that changes entity counts.

Done when

  • Same seed and flags → byte-identical derivative and statement; different seed → different derivative, same statement counts.
  • Drilling the derivative fails exactly the dimensions the statement names and no others.
  • An export without the synthetic marker is refused before any file is written.
  • The statement contains no record value from the input (disclosure gate).

Pointers

  • scripts/build_directus_lossy_canary.py (_mutate), src/exitdrill/loader.py, src/exitdrill/models.py, src/exitdrill/paths.py
  • README "Three-minute demo"; docs/decisions/0021-gate-record-value-disclosure-on-the-synthetic-demo.md

Proposed with AI assistance.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestfeatureMajor new capability proposal

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions