destructive.md is generated from a hardcoded string, so it cannot track the surface it describes #570

Open
opened 2026-09-21 21:11:48 +00:00 by hexajon · 0 comments
Owner

Part of #563. Split out of #569, which covers classifying the operations. This one covers the mechanism that let the page go stale unnoticed.

The defect

docs/reference/destructive.md carries the standard banner:

<!-- Code generated by 'go run ./cmd/madtea-dev docs generate'. DO NOT EDIT. -->

The banner is accurate about the file and misleading about the content. The whole page body is one string literal, the Intro field at internal/docs/domain.go:187-193. That domain entry declares no CLIPrefixes and no MCPPrefixes on purpose, so it matches zero commands and zero tools, and generate.go:90 carries a special case to render it anyway.

Every other reference page lists operations gathered from the live command and tool sets, so it moves when the surface moves. This page derives from nothing. Regenerating cannot change a word of it. Only retyping the literal can.

The page is also the one that makes a claim about the whole surface rather than about one domain, so it is the single page where the gap matters most.

What it cost

The literal says the irreversible forge-data deletes are "repository, issue, release, tag, wiki page". Two irreversible deletes ship outside that list: release delete-asset (internal/mcp/tools_releases.go:103, internal/cmd/release/release_assets.go:108) and packages delete (internal/mcp/tools_packages.go:44-45,111). Neither refuses, and the release-delete refusal in the same package names a release's attached assets as exactly the unrecoverable thing it will not touch.

The page also never mentions the no-force rule for worktree removal, which is the case that produced ADR 0029.

Nothing in the build could catch either gap. internal/parity/destructive_remote_pin_test.go pins MCP operations against the older gated policy and never reads the doc.

Options

  1. Derive the page. Mark each refusing command and each omitted operation in its own definition, and have the generator gather them the way every other page gathers its ops. The refusal stubs already share one typed error (internal/cmdutil/deleterefusal.go), so the set is discoverable in code today.
  2. Keep the prose, add a test. Assert that the set of operations the page names equals the set the code actually refuses, and fail the gate on divergence.
  3. Drop the banner. If the page stays hand-written, stop labelling it generated, so a reader knows it is a claim rather than a derivation.

My lean is option 1 with option 2 behind it, because a derived page plus a test that the derivation is complete is the only combination where the page cannot lie. Option 3 alone leaves the drift and only stops it being disguised.

Acceptance criteria

  • The page's list of omitted operations is derived from the code, or a test fails when the page and the shipped refusals disagree.
  • A new destructive operation added to either surface cannot reach main with the page unchanged.
  • The banner matches how the content is actually produced.
  • The worktree force-removal rule appears on the page.
Part of #563. Split out of #569, which covers classifying the operations. This one covers the mechanism that let the page go stale unnoticed. ## The defect `docs/reference/destructive.md` carries the standard banner: ``` <!-- Code generated by 'go run ./cmd/madtea-dev docs generate'. DO NOT EDIT. --> ``` The banner is accurate about the file and misleading about the content. The whole page body is one string literal, the `Intro` field at `internal/docs/domain.go:187-193`. That domain entry declares no `CLIPrefixes` and no `MCPPrefixes` on purpose, so it matches zero commands and zero tools, and `generate.go:90` carries a special case to render it anyway. Every other reference page lists operations gathered from the live command and tool sets, so it moves when the surface moves. This page derives from nothing. Regenerating cannot change a word of it. Only retyping the literal can. The page is also the one that makes a claim about the whole surface rather than about one domain, so it is the single page where the gap matters most. ## What it cost The literal says the irreversible forge-data deletes are "repository, issue, release, tag, wiki page". Two irreversible deletes ship outside that list: `release delete-asset` (`internal/mcp/tools_releases.go:103`, `internal/cmd/release/release_assets.go:108`) and `packages delete` (`internal/mcp/tools_packages.go:44-45,111`). Neither refuses, and the release-delete refusal in the same package names a release's attached assets as exactly the unrecoverable thing it will not touch. The page also never mentions the no-force rule for worktree removal, which is the case that produced ADR 0029. Nothing in the build could catch either gap. `internal/parity/destructive_remote_pin_test.go` pins MCP operations against the older gated policy and never reads the doc. ## Options 1. Derive the page. Mark each refusing command and each omitted operation in its own definition, and have the generator gather them the way every other page gathers its ops. The refusal stubs already share one typed error (`internal/cmdutil/deleterefusal.go`), so the set is discoverable in code today. 2. Keep the prose, add a test. Assert that the set of operations the page names equals the set the code actually refuses, and fail the gate on divergence. 3. Drop the banner. If the page stays hand-written, stop labelling it generated, so a reader knows it is a claim rather than a derivation. My lean is option 1 with option 2 behind it, because a derived page plus a test that the derivation is complete is the only combination where the page cannot lie. Option 3 alone leaves the drift and only stops it being disguised. ## Acceptance criteria - The page's list of omitted operations is derived from the code, or a test fails when the page and the shipped refusals disagree. - A new destructive operation added to either surface cannot reach main with the page unchanged. - The banner matches how the content is actually produced. - The worktree force-removal rule appears on the page.
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#570
No description provided.