Skip to content

fix: a probe invents the name it must be able to clean up - #123

Merged
ShocOne merged 1 commit into
mainfrom
fix/a-probe-invents-the-name-it-must-be-able-to-clean-up
Aug 26, 2026
Merged

fix: a probe invents the name it must be able to clean up#123
ShocOne merged 1 commit into
mainfrom
fix/a-probe-invents-the-name-it-must-be-able-to-clean-up

Conversation

@ShocOne

@ShocOne ShocOne commented Aug 26, 2026

Copy link
Copy Markdown
Member

The defect

Both synthesis paths — synth.value in internal/audit/plan and synthValue in internal/audit/run — order Example ahead of the name-bearing token. Only the type fallback ever reaches nameToken, so any name-bearing field whose schema declares an example is probed with the document's example.

Two things follow, and the pilot shows both:

The API refuses it. account_group sent {"accountGroupName":"My testing account group"} — the document's example — and got:

400 "There were some errors in your request, please correct them before trying
     again. An Account Group with that name already exists."

Cleanup cannot see what it created. cleanup.go matches live objects by audit.name_prefix. An object created under a document example carries no prefix, so the prefix pass cannot match it and it stays in the tenant permanently.

handoff.md files account_group under "blockers that are not code" and says the leaked object needs org-admin to remove. It is code: the leak exists because the probe created the object under a name cleanup was never going to match.

The fix

In both paths, a name-bearing field with no enum, format or pattern takes the invented token rather than the declared example. A field carrying one of those constraints is left on the ordinary priority — the token satisfies none of those shapes, so such a field is not one to invent a name for.

Both paths change together deliberately. Run unconditionally strategizes when the document and config are present, so the live path synthesises from strategize; changing the plan alone would leave the plan and the run disagreeing about what they sent.

nameBearing was duplicated in both packages. It is now exported once from internal/audit/plan, which internal/audit/run already imports.

Verification

make check (90.7% total; audit/plan 93.2%, audit/run 90.5%). Both new tests were confirmed to fail with the rule disabled.

One test expectation had to be corrected while writing it, and the correction is a fact worth recording: a declared example already outranks an enum in the existing priority, so a name-bearing field carrying an enum falls through to the example rather than to the enum's first member. The comments say that rather than the tidier thing I first wrote.

What this does not do

It takes a probe re-run to show. No acceptance test changes from this PR alone — the recorded request bodies on disk were captured under the old rule. The already-leaked account_group object still holds its name and still needs org-admin; this stops the next one.

Note for CI

Two lint findings (QF1001 in strategy_test.go, unparam on reduceMaximal) are pre-existing on main and are fixed in #121. They are untouched here to avoid a conflicting duplicate fix.

🤖 Generated with Claude Code

A name must be unique per run and must carry the prefix cleanup matches on,
so the invented token now beats a declared example in both synthesis paths.
An example is a value the API accepted once: an API that requires a name to
be unique refuses it every run after, and the object the first run created
under it carries no prefix, so the cleanup pass cannot see it and it stays in
somebody's tenant for good.

A field an enum, a format or a pattern constrains is left on the ordinary
priority — the token satisfies none of those shapes.

The plan and the strategized run change together: the live path synthesises
from strategize, and a rule applied to one alone would mean the plan and the
run disagreed about what they sent. nameBearing loses its duplicate with it,
exported from the plan package the run already imports.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ShocOne
ShocOne merged commit 95347f7 into main Aug 26, 2026
2 checks passed
@ShocOne
ShocOne deleted the fix/a-probe-invents-the-name-it-must-be-able-to-clean-up branch August 26, 2026 14:17
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