handlers: audit + sharpen every agent_action string per uniform contract (U3)#42
Merged
Merged
Conversation
…act (U3)
Establishes a single source of truth for every agent_action string the API
returns (internal/handlers/agent_action.go) and enforces a four-requirement
contract in tests (TestAgentActionContract).
The U3 contract every agent_action MUST satisfy:
1. Open with "Tell the user" — the LLM agent re-articulates verbatim.
2. Name the specific reason rejected (tier, limit, policy, resource).
3. Name the exact next action (Upgrade, Claim, Provision twin, Contact
support) — never "try again later" without context.
4. Include a full https://instanode.dev/ URL — no relative paths.
5. Under 280 chars so LLMs reproduce verbatim instead of summarizing.
Audited 25 strings across the handlers + middleware packages. Every one now
meets all four requirements: builders (newAgentAction*) for tier/env/limit
interpolation, static constants for fixed walls, and the existing
codeToAgentAction registry sharpened in lockstep.
Call sites refactored to consume the named constants/builders so reviewers
audit prose in one file (agent_action.go) rather than scattered handlers.
Tests:
- TestAgentActionContract covers every string (static + builder + registry)
- TestAgentActionContract_RegistryCoverage guards expected codes present
- Existing TestRespondError_* assertions updated to match sharpened copy
- All handlers + middleware unit tests green (pre-existing internal/plans
failure unrelated to this PR)
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
Establishes a single source of truth for every
agent_actionstring the API returns (internal/handlers/agent_action.go) and enforces a four-requirement contract viaTestAgentActionContract.Today's strings worked but were inconsistent — some imperative, some passive; some named the tier, some said "their plan"; some included the full URL, some only
/pricing. This PR audits all 25 strings acrosshandlers/+middleware/, sharpens every one to a uniform contract, and extracts each to a named constant or builder so reviewers audit prose in one place.The U3 contract — every
agent_actionMUST:https://instanode.dev/URL — no relative/pricing.The full contract block is committed at the top of
internal/handlers/agent_action.go.Audit counts
Example before/after
deployment_limit_reached(deploy.go):"Tell the user they've hit the %s tier deployment cap (%d apps). Upgrade them to Pro for 10 medium deploys: https://instanode.dev/start?t=..."(broken placeholder URL)"Tell the user they've hit the %s tier deployment cap (%d apps). Upgrade to Pro for 10 deployments at https://instanode.dev/pricing — takes 30 seconds, no card for upgrade preview."upgrade_required(helpers.go registry):"Tell the user this feature requires a higher plan. Have them upgrade at https://instanode.dev/pricing."(vague — "higher plan" gives the LLM nothing concrete)"Tell the user this feature requires the Pro plan or higher. Upgrade at https://instanode.dev/pricing — takes 30 seconds."missing_image_ref(stack.go promote):"This stack predates the image-ref persistence migration. Redeploy the source stack first so its image is cached, then promote."(no "Tell the user" prefix, no URL)"Tell the user this stack predates the image-ref persistence migration, so promote has nothing to redeploy. Redeploy the source stack first at https://instanode.dev/app/stacks, then retry the promote."Pushback / scope notes
internal/plans/TestAll_ReturnsAllPlansfails on master and remains failing — pre-existing, unrelated. Filed as known-issue rather than fixed here.provision_failed,list_failed,stream_failed, etc.) deliberately omitagent_actionpercodeToAgentActioncuration comments — there's no action the user can take. The one exception (resource_binding_lookup_failed) keeps anagent_actionbecause the actionable advice is concrete: "retry in ~10 seconds, check status page".Coordination with P1, U1, U2
P1, U1, U2 add new
agent_actionstrings — those tracks should land their strings as named constants/builders ininternal/handlers/agent_action.goso the audit + the contract-test stay one-stop. The contract block at the top of that file is the canonical reference.Test plan
go build ./...+go vet ./...cleanTestAgentActionContract(new) — every static const + every builder + every registry entry passes all 4 contract checksTestAgentActionContract_RegistryCoverage— all expected error codes registeredTestRespondError_*— existing tests updated to match sharpened registry strings, all greeninternal/handlers/full suite green (25.9s)internal/middleware/full suite green (after one flaky-Redis retry — same flake exists on master)internal/plansfailure (unrelated)🤖 Generated with Claude Code