the destructive surface has no enumerable source of truth #572

Open
opened 2026-09-21 23:39:50 +00:00 by hexajon · 0 comments
Owner

Part of #563. This is the sweep mechanism the other children assume exists.

The problem

Three separate things describe madtea's destructive surface, and each is maintained by hand:

  • ADR 0029's per-operation table (docs/decisions/0029-strict-omission-destruction-model.md:130-141), 9 rows, headed "Every currently destructive-adjacent operation, classified".
  • internal/parity/destructive_remote_pin_test.go, which pins roughly 50 delete-shaped operations with a gated or ungated verdict and a rationale comment per row.
  • docs/reference/destructive.md, whose text is a string literal at internal/docs/domain.go:189-193 (see #570).

None derives from the others and none derives from the code. The table says 9, the pin test enumerates far more, and the doc names 5. All three are green.

There is also no way to ask the codebase what its destructive surface is. Answering "which operations can destroy something, and how is each classified" currently means a person reading three files and reconciling them by eye. That is why the count keeps coming out different: three passes over the 24 ungated rows at destructive_remote_pin_test.go:126-155 produced 22, 22, and 25.

What to build

One enumerable inventory, derived from the code, that everything else consumes.

  1. Mark destructive capability at its definition. Each MCP action and CLI command that can destroy carries a classification in its own definition: omit, machine-proof, already-safe, or unclassified. unclassified is the default for anything destructive-shaped, so a new operation is loud rather than silent.
  2. Generate the inventory from those marks. One list, every destructive-capable operation on both surfaces, with its classification and the surface it appears on.
  3. Make the existing three views consume it. ADR 0029's table, destructive.md, and the pin test all read the generated inventory instead of restating it.
  4. Fail the gate on drift. An operation marked unclassified, or a classification with no counterpart in the record, fails. A new destructive operation cannot reach main without a classification.

Detection, not just description

The marks are still assertions unless something checks them. Two checks worth having:

  • A destructive-shaped operation with no mark fails the gate. Catch it by verb (delete, remove, purge, destroy, reset, restore, transfer, convert, overwrite, replace, drop) and by any handler reaching a forge DELETE or a destructive git subcommand. isDestructiveDeleteOp (internal/parity/destructive_remote_pin_test.go:57) already does a narrow version of this and misses transfer and convert because its verb set is delete-only.
  • An operation marked machine-proof names the function computing its proof, and a test asserts that function runs before the destructive call.

Acceptance criteria

  • One command prints the complete destructive inventory with a classification per operation, and it is derived, not typed.
  • ADR 0029's table, destructive.md, and the pin test all agree with it by construction rather than by review.
  • Adding a destructive operation without classifying it fails the gate.
  • The count is produced by the tool and is the same every time it runs.
Part of #563. This is the sweep mechanism the other children assume exists. ## The problem Three separate things describe madtea's destructive surface, and each is maintained by hand: - ADR 0029's per-operation table (`docs/decisions/0029-strict-omission-destruction-model.md:130-141`), 9 rows, headed "Every currently destructive-adjacent operation, classified". - `internal/parity/destructive_remote_pin_test.go`, which pins roughly 50 delete-shaped operations with a gated or ungated verdict and a rationale comment per row. - `docs/reference/destructive.md`, whose text is a string literal at `internal/docs/domain.go:189-193` (see #570). None derives from the others and none derives from the code. The table says 9, the pin test enumerates far more, and the doc names 5. All three are green. There is also no way to ask the codebase what its destructive surface is. Answering "which operations can destroy something, and how is each classified" currently means a person reading three files and reconciling them by eye. That is why the count keeps coming out different: three passes over the 24 ungated rows at `destructive_remote_pin_test.go:126-155` produced 22, 22, and 25. ## What to build One enumerable inventory, derived from the code, that everything else consumes. 1. Mark destructive capability at its definition. Each MCP action and CLI command that can destroy carries a classification in its own definition: `omit`, `machine-proof`, `already-safe`, or `unclassified`. `unclassified` is the default for anything destructive-shaped, so a new operation is loud rather than silent. 2. Generate the inventory from those marks. One list, every destructive-capable operation on both surfaces, with its classification and the surface it appears on. 3. Make the existing three views consume it. ADR 0029's table, `destructive.md`, and the pin test all read the generated inventory instead of restating it. 4. Fail the gate on drift. An operation marked `unclassified`, or a classification with no counterpart in the record, fails. A new destructive operation cannot reach main without a classification. ## Detection, not just description The marks are still assertions unless something checks them. Two checks worth having: - A destructive-shaped operation with no mark fails the gate. Catch it by verb (`delete`, `remove`, `purge`, `destroy`, `reset`, `restore`, `transfer`, `convert`, `overwrite`, `replace`, `drop`) and by any handler reaching a forge DELETE or a destructive git subcommand. `isDestructiveDeleteOp` (`internal/parity/destructive_remote_pin_test.go:57`) already does a narrow version of this and misses `transfer` and `convert` because its verb set is delete-only. - An operation marked `machine-proof` names the function computing its proof, and a test asserts that function runs before the destructive call. ## Acceptance criteria - One command prints the complete destructive inventory with a classification per operation, and it is derived, not typed. - ADR 0029's table, `destructive.md`, and the pin test all agree with it by construction rather than by review. - Adding a destructive operation without classifying it fails the gate. - The count is produced by the tool and is the same every time it runs.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
sixfold-space/madtea#572
No description provided.