fix(api): deploy_ttl anon walls emit claim_required, not upgrade_required (B7-P1-7)#206
Merged
mastermanas805 merged 2 commits intoMay 30, 2026
Merged
Conversation
…ired (B7-P1-7)
POST /api/v1/deployments/:id/{make-permanent,ttl} returned 402 with
`error: "upgrade_required"` on anonymous-tier callers. The agent_action
sentence correctly said "claim the account (free)", but agents that
branch on the `error` keyword route on `upgrade_required` to the paid
pricing page (codeToAgentAction["upgrade_required"].UpgradeURL =
https://instanode.dev/pricing). The actual remediation is a FREE claim,
not a paid upgrade — wrong destination for the strict code-switching
caller.
Rule 17 coverage block:
Symptom: JSON body `error: "upgrade_required"` on anon /make-permanent + /ttl
Enumeration: rg -F '"upgrade_required"' internal/handlers/deploy_ttl.go
Sites found: 2 (deploy_ttl.go:65, deploy_ttl.go:139)
Sites touched: 2 (both flipped to "claim_required")
Coverage test: TestDeployTTL_AnonymousArmsEmitClaimRequired (iterates a
route table — a third arm that emits upgrade_required
fails the matching row) + TestDeployTTL_NoUpgradeRequiredInSource
(source-grep belt+braces)
Live verified: pending — awaiting prod deploy + healthz commit_id gate
Rule 22 surface check (contract change touches all surfaces in one PR):
- internal/handlers/deploy_ttl.go (both anon walls)
- internal/handlers/helpers.go (codeToAgentAction["claim_required"]
registered with UpgradeURL: https://instanode.dev/claim)
- internal/handlers/openapi.go (402 description on both routes)
- openapi.snapshot.json (regenerated via `make openapi-snapshot`)
- internal/handlers/agent_action_contract_test.go (expectedCodes
registry — claim_required is now a guaranteed-present entry)
Wire shape preserved:
- Status: 402 (unchanged)
- agent_action: still points at https://instanode.dev/claim with the
same "Claim the account" copy (AgentActionDeployMakePermanentAnonymous)
- upgrade_url: changes from https://api.instanode.dev/start (a 302 to
/claim) to https://instanode.dev/claim directly — fewer hops for
the calling agent
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
POST /api/v1/deployments/:id/{make-permanent,ttl}returned 402 witherror: "upgrade_required"for anonymous-tier callers. The remediationis a free claim, not a paid upgrade — so the keyword was wrong.
Agents that branch on the
errorcode (instead of reading the proseagent_action) routed users tohttps://instanode.dev/pricing(paid checkout) when a 30-second free claim would have cleared the wall.
Closes BugBash 2026-05-20 finding B7-P1-7.
Wire shape preserved (status 402, message, agent_action sentence), only
the machine-readable
errorkeyword +upgrade_urlchange:error:upgrade_required->claim_requiredupgrade_url:https://api.instanode.dev/start(302 alias) ->https://instanode.dev/claim(canonical)Rule 17 coverage block
Rule 22 surface check
Contract changes touched in this single PR:
internal/handlers/deploy_ttl.go(both anon walls)internal/handlers/helpers.go(codeToAgentAction["claim_required"]registered withUpgradeURL: https://instanode.dev/claimand a claim-not-upgradeagent_action)internal/handlers/openapi.go(402 description on both routes)openapi.snapshot.json(regenerated viamake openapi-snapshot- cross-stack contract gate)internal/handlers/agent_action_contract_test.go(expectedCodesregistry now listsclaim_requiredso a future drop is a contract regression)Other handlers that legitimately use
upgrade_requiredfor paid-plan walls (db.go, vector.go, nosql.go, queue.go, cache.go, backup.go, stack.go, resource.go, etc.) are untouched.Test plan
go test ./internal/handlers/ -run 'TestDeployTTL_AnonymousArmsEmitClaimRequired|TestDeployTTL_NoUpgradeRequiredInSource' -count=1 -short->okgo test ./internal/handlers/ -run 'TestAgentActionContract|TestMakePermanent|TestSetTTL|TestDeployTTL|TestCodeToAgentAction' -count=1 -short->okgo build ./...cleango vet ./internal/handlers/cleanmake openapi-snapshotregenerated; diff = exactly 2 line changes (the two 402 descriptions)error: "claim_required"+upgrade_url: "https://instanode.dev/claim"