agentleFS
randommoniclerandommonicle/claude-skills/safe-smokes/SKILL.md

safe-smokes

Skill27 starsChanged 18 days agoApache-2.0View on GitHub

Never run destructive operations against shared or live data in tests, smokes, or seeds. Flip-and-restore with try/finally instead of delete; seed in beforeAll and tear down in afterAll; restore shared state exactly as found. Triggers when writing or editing any test, smoke, or seed script that connects to a shared or live database, Supabase or otherwise. Does not trigger on tests that run only against mocks, fixtures, or an in-memory database.

  • Reads credentials

The file

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 4 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Union-of-anchors tear-down: Delete by the union of every anchor a test could have created, not just the happy-path id.

    line 24
  2. Distinctive:

    Idempotent and self-cleaning: Do not assume a clean database.

    line 25
  3. Very specific:

    If an assertion can be expressed as SQL-level RLS checks, prefer pgTAP with BEGIN ...

    line 31
  4. Very specific:

    Use localhost, not 127.0.0.1.

    line 43
  5. Distinctive:

    Scope assertions on shared data: Never assert a global or firm-wide exact count on a shared database: floor globals, exact-match only rows …

    line 61
  6. Distinctive:

    A fixture shortcut is a finding: When a test exploits a gap to build its fixtures, that gap is an attacker's path, not a convenience — …

    line 69

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Safe smokes against shared data

A smoke must leave shared state exactly as it found it. On 2026-05-18, before this discipline was written, a smoke deleted ASH's real provider row. The rules below exist so that never recurs.

When this applies

Any test, smoke, or seed that connects to a shared or live database. In PropOS this is the Supabase smoke-db project running against real firm data. The risk is highest for the regulated c1-* smokes and anything touching firm, provider, ledger, demand, or transaction rows.

Confirm the target before you run

More than one project exists per environment, and the active credentials or the Dashboard can point at the wrong one. Before running any smoke that can write, confirm which project the run targets and that it is the one you intend, against the project ref recorded in the repo's own instructions rather than one memorised here. The 2026-05-18 incident combined a destructive delete with an unconfirmed target: flip-and-restore removes the first risk, and confirming the target removes the second.

Core rules

  1. Never .delete() shared real-firm state. Capture the current value, mutate it, assert, then restore. The test owns only the rows it created.
  2. Flip-and-restore in try/finally. The restore runs in finally so it happens even when the assertion throws. A restore that only runs on the happy path is not a restore.
  3. Seed in beforeAll, tear down in afterAll. Set-up creates the test's own rows; tear-down removes exactly those and nothing else.
  4. NULL foreign keys before deleting parents. For example, NULL invoices.transaction_id before deleting transactions, and widen the capture to the direct FK (payment_authorisations.transaction_id) so nothing is orphaned or blocked.
  5. Union-of-anchors tear-down. Delete by the union of every anchor a test could have created, not just the happy-path id. A test that branches creates different rows on different paths.
  6. Idempotent and self-cleaning. Do not assume a clean database. Clean up after yourself; never truncate or reset shared tables to force a known state.

Why flip-and-restore, not transaction rollback

App-level smokes open their own client connections. A BEGIN ... ROLLBACK in the test process does not wrap the connection the application code uses, so it does not isolate the writes. Rollback isolation only works when the test and the code under test share the same database connection and transaction, as with in-database pgTAP tests. For live Supabase smokes, flip-and-restore is the correct pattern, not a workaround.

If an assertion can be expressed as SQL-level RLS checks, prefer pgTAP with BEGIN ... ROLLBACK: it leaves zero residue by construction. Use flip-and-restore for everything that must run through the app's client.

Concurrent runs

Flip-and-restore is not safe under parallel runs that touch the same shared row: two runs can interleave so one restores the other's value, not the original. Serialise smokes that mutate shared rows, or give each run its own row keyed to the run, so no two runs flip the same state at once.

Service-role smokes

The regulated c1-* smokes need SUPABASE_SERVICE_ROLE_KEY in app/.env.local. Absent the key, they SKIP rather than fail. State the skip explicitly in the run summary. Never let a skip read as a pass: a skipped regulated smoke is unverified, not green.

Windows

Use localhost, not 127.0.0.1. Do not background a curl-poll loop to wait for the dev server; grep the server's own ready signal in its log instead.

What this skill does not do

It does not replace catalog verification, which proves the schema, and it does not choose the test framework. It governs how any test touches shared data, so a passing suite never costs real firm state.

Why

Shared-database tests run against data other people rely on. One .delete() on the wrong row is a regulatory and trust incident, and it has happened once. Flip-and-restore makes the destructive path impossible to take by accident: the test never holds a delete on state it did not create.

Additions from cross-repo lessons (ratified 2026-07-23)

  • Teardown must prove itself. A .delete() under RLS returns success with zero rows — on any no-DELETE-policy table an authenticated teardown is a silent leak (the same bug sat in 12 files; one instance leaked 1,148 rows). Assert teardown row counts; use service-role teardown for no-DELETE tables, and never "fix" the no-op by adding a DELETE policy to an audit table. When one instance is found, grep the whole tree for the verb-on-table.
  • Prove zero residue with a prefix-scoped post-run count, not with the absence of errors.
  • Scope assertions on shared data. Never assert a global or firm-wide exact count on a shared database: floor globals, exact-match only rows the test owns. A read-only money smoke needs a stability gate on the rows it reads.
  • Isolation conversions are not mechanical. Moving a smoke to throwaway-fixture isolation requires checking HOW each written table is made append-only (an RLS deny is teardown-able by service role; a trigger is not), which gates a fresh fixture trips that the shared fixture had already satisfied, FK-ordered teardown, and self-cleaning on partial seed failure.
  • A fixture shortcut is a finding. When a test exploits a gap to build its fixtures, that gap is an attacker's path, not a convenience — report it.

Note on hub routing: prove-it-can-fail routes here for any shared-data test. The route adds a path in; it does not narrow this skill's own trigger.

Additions from the misses-log sweep (2026-10-22)

  • A test never waits on a person. Patch every modal or prompt in the test harness to fail with its title, so an unexpected dialog is a failure, not a hang. (RimModManager L-005.)
  • A live check stays off the user's desktop. A check that opens windows, takes focus or starts apps on the user's own machine needs their go-ahead and a quiet moment; otherwise hand them the exact steps. (RimModManager L-010.)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.