@@ -27,60 +27,60 @@ import (
2727func agentActionContractCases () map [string ]string {
2828 cases := map [string ]string {
2929 // Static constants.
30- "AgentActionMultiEnvUpgradeRequired" : AgentActionMultiEnvUpgradeRequired ,
31- "AgentActionStackPromoteMissingImageRef" : AgentActionStackPromoteMissingImageRef ,
32- "AgentActionBindingFamilyDisabled" : AgentActionBindingFamilyDisabled ,
33- "AgentActionBindingLookupFailed" : AgentActionBindingLookupFailed ,
34- "RecycleGateAgentAction" : RecycleGateAgentAction ,
35- "AgentActionPrivateDeployRequiresPro" : AgentActionPrivateDeployRequiresPro ,
36- "AgentActionPrivateDeployRequiresAllowedIPs" : AgentActionPrivateDeployRequiresAllowedIPs ,
37- "AgentActionAdminRequired" : AgentActionAdminRequired ,
38- "AgentActionPromotionInvalid" : AgentActionPromotionInvalid ,
39- "AgentActionPromotionAlreadyUsed" : AgentActionPromotionAlreadyUsed ,
40- "AgentActionPromotionExpired" : AgentActionPromotionExpired ,
41- "AgentActionPromoteTokenExpired" : AgentActionPromoteTokenExpired ,
42- "AgentActionReadOnlySession" : AgentActionReadOnlySession ,
43- "AgentActionNotifyWebhookInvalid" : AgentActionNotifyWebhookInvalid ,
44- "AgentActionPauseRequiresPro" : AgentActionPauseRequiresPro ,
45- "AgentActionResourceAlreadyPaused" : AgentActionResourceAlreadyPaused ,
46- "AgentActionResourceNotPaused" : AgentActionResourceNotPaused ,
47- "AgentActionBackupRequiresClaim" : AgentActionBackupRequiresClaim ,
48- "AgentActionRestoreRequiresPro" : AgentActionRestoreRequiresPro ,
49- "AgentActionRestoreRequiresHobbyPlus" : AgentActionRestoreRequiresHobbyPlus ,
50- "AgentActionRestoreBackupNotReady" : AgentActionRestoreBackupNotReady ,
51- "AgentActionRestoreInflight" : AgentActionRestoreInflight ,
52- "AgentActionRestoreDestructiveAckRequired" : AgentActionRestoreDestructiveAckRequired ,
53- "AgentActionRestoreTargetCrossTeam" : AgentActionRestoreTargetCrossTeam ,
54- "AgentActionBackupIntegrityFailed" : AgentActionBackupIntegrityFailed ,
55- "AgentActionMetricsRequiresUpgrade" : AgentActionMetricsRequiresUpgrade ,
56- "AgentActionEmailNotVerified" : AgentActionEmailNotVerified ,
30+ "AgentActionMultiEnvUpgradeRequired" : AgentActionMultiEnvUpgradeRequired ,
31+ "AgentActionStackPromoteMissingImageRef" : AgentActionStackPromoteMissingImageRef ,
32+ "AgentActionBindingFamilyDisabled" : AgentActionBindingFamilyDisabled ,
33+ "AgentActionBindingLookupFailed" : AgentActionBindingLookupFailed ,
34+ "RecycleGateAgentAction" : RecycleGateAgentAction ,
35+ "AgentActionPrivateDeployRequiresPro" : AgentActionPrivateDeployRequiresPro ,
36+ "AgentActionPrivateDeployRequiresAllowedIPs" : AgentActionPrivateDeployRequiresAllowedIPs ,
37+ "AgentActionAdminRequired" : AgentActionAdminRequired ,
38+ "AgentActionPromotionInvalid" : AgentActionPromotionInvalid ,
39+ "AgentActionPromotionAlreadyUsed" : AgentActionPromotionAlreadyUsed ,
40+ "AgentActionPromotionExpired" : AgentActionPromotionExpired ,
41+ "AgentActionPromoteTokenExpired" : AgentActionPromoteTokenExpired ,
42+ "AgentActionReadOnlySession" : AgentActionReadOnlySession ,
43+ "AgentActionNotifyWebhookInvalid" : AgentActionNotifyWebhookInvalid ,
44+ "AgentActionPauseRequiresPro" : AgentActionPauseRequiresPro ,
45+ "AgentActionResourceAlreadyPaused" : AgentActionResourceAlreadyPaused ,
46+ "AgentActionResourceNotPaused" : AgentActionResourceNotPaused ,
47+ "AgentActionBackupRequiresClaim" : AgentActionBackupRequiresClaim ,
48+ "AgentActionRestoreRequiresPro" : AgentActionRestoreRequiresPro ,
49+ "AgentActionRestoreRequiresHobbyPlus" : AgentActionRestoreRequiresHobbyPlus ,
50+ "AgentActionRestoreBackupNotReady" : AgentActionRestoreBackupNotReady ,
51+ "AgentActionRestoreInflight" : AgentActionRestoreInflight ,
52+ "AgentActionRestoreDestructiveAckRequired" : AgentActionRestoreDestructiveAckRequired ,
53+ "AgentActionRestoreTargetCrossTeam" : AgentActionRestoreTargetCrossTeam ,
54+ "AgentActionBackupIntegrityFailed" : AgentActionBackupIntegrityFailed ,
55+ "AgentActionMetricsRequiresUpgrade" : AgentActionMetricsRequiresUpgrade ,
56+ "AgentActionEmailNotVerified" : AgentActionEmailNotVerified ,
5757 // Wave FIX-J deploy TTL walls. The long-form success-path
5858 // newAgentActionDeployAutoExpire24h is documented in
5959 // agent_action.go as the canonical exception to the 280-char
6060 // soft target (it has to enumerate THREE next actions), so it
6161 // is intentionally NOT exercised by this contract gate —
6262 // covered instead by deploy_ttl_test.go which spot-checks the
6363 // imperative opening + URL inclusion.
64- "AgentActionDeployMakePermanentAnonymous" : AgentActionDeployMakePermanentAnonymous ,
65- "AgentActionDeployTTLHoursOutOfRange" : AgentActionDeployTTLHoursOutOfRange ,
66- "AgentActionTeamSettingsInvalidTTLPolicy" : AgentActionTeamSettingsInvalidTTLPolicy ,
64+ "AgentActionDeployMakePermanentAnonymous" : AgentActionDeployMakePermanentAnonymous ,
65+ "AgentActionDeployTTLHoursOutOfRange" : AgentActionDeployTTLHoursOutOfRange ,
66+ "AgentActionTeamSettingsInvalidTTLPolicy" : AgentActionTeamSettingsInvalidTTLPolicy ,
6767
6868 // Builders — representative inputs covering tier/env/role/limit
6969 // interpolation.
70- "newAgentActionDeploymentLimitReached(hobby,1)" : newAgentActionDeploymentLimitReached ("hobby" , 1 ),
71- "newAgentActionBackupRateLimited(hobby,1)" : newAgentActionBackupRateLimited ("hobby" , 1 ),
72- "newAgentActionMetricsWindowTooLarge(hobby,1h)" : newAgentActionMetricsWindowTooLarge ("hobby" , "1h" ),
73- "newAgentActionPromoteApprovalSent(prod,email)" : newAgentActionPromoteApprovalSent ("production" , "owner@example.com" ),
74- "newAgentActionStorageLimitReached(hobby,500)" : newAgentActionStorageLimitReached ("hobby" , 500 ),
75- "newAgentActionVaultQuotaExceeded(hobby,50)" : newAgentActionVaultQuotaExceeded ("hobby" , 50 ),
76- "newAgentActionEnvPolicyDenied(prod,deploy)" : newAgentActionEnvPolicyDenied ("production" , "deploy" , "owner" , "developer" ),
77- "newAgentActionOwnerRequired(developer)" : newAgentActionOwnerRequired ("developer" ),
78- "newAgentActionBindingInvalidUUID(KEY)" : newAgentActionBindingInvalidUUID ("DATABASE_URL" , "not-a-uuid" ),
79- "newAgentActionBindingNotFound(KEY)" : newAgentActionBindingNotFound ("DATABASE_URL" ),
80- "newAgentActionBindingCrossTeam(KEY)" : newAgentActionBindingCrossTeam ("DATABASE_URL" ),
81- "newAgentActionBindingNoEnvTwin(uuid,name,env)" : newAgentActionBindingNoEnvTwin ("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" , "owner-db" , "staging" ),
82- "newAgentActionAdminTierChanged(team,pro)" : newAgentActionAdminTierChanged ("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" , "pro" ),
83- "newAgentActionAdminPromoIssued(team,code)" : newAgentActionAdminPromoIssued ("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" , "01H8XGZJ" ),
70+ "newAgentActionDeploymentLimitReached(hobby,1)" : newAgentActionDeploymentLimitReached ("hobby" , 1 ),
71+ "newAgentActionBackupRateLimited(hobby,1)" : newAgentActionBackupRateLimited ("hobby" , 1 ),
72+ "newAgentActionMetricsWindowTooLarge(hobby,1h)" : newAgentActionMetricsWindowTooLarge ("hobby" , "1h" ),
73+ "newAgentActionPromoteApprovalSent(prod,email)" : newAgentActionPromoteApprovalSent ("production" , "owner@example.com" ),
74+ "newAgentActionStorageLimitReached(hobby,500)" : newAgentActionStorageLimitReached ("hobby" , 500 ),
75+ "newAgentActionVaultQuotaExceeded(hobby,50)" : newAgentActionVaultQuotaExceeded ("hobby" , 50 ),
76+ "newAgentActionEnvPolicyDenied(prod,deploy)" : newAgentActionEnvPolicyDenied ("production" , "deploy" , "owner" , "developer" ),
77+ "newAgentActionOwnerRequired(developer)" : newAgentActionOwnerRequired ("developer" ),
78+ "newAgentActionBindingInvalidUUID(KEY)" : newAgentActionBindingInvalidUUID ("DATABASE_URL" , "not-a-uuid" ),
79+ "newAgentActionBindingNotFound(KEY)" : newAgentActionBindingNotFound ("DATABASE_URL" ),
80+ "newAgentActionBindingCrossTeam(KEY)" : newAgentActionBindingCrossTeam ("DATABASE_URL" ),
81+ "newAgentActionBindingNoEnvTwin(uuid,name,env)" : newAgentActionBindingNoEnvTwin ("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" , "owner-db" , "staging" ),
82+ "newAgentActionAdminTierChanged(team,pro)" : newAgentActionAdminTierChanged ("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" , "pro" ),
83+ "newAgentActionAdminPromoIssued(team,code)" : newAgentActionAdminPromoIssued ("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" , "01H8XGZJ" ),
8484 }
8585
8686 // codeToAgentAction registry — every entry must also pass the contract.
@@ -161,8 +161,8 @@ func assertContract(t *testing.T, name, s string) {
161161 "Re-issue" , "re-issue" , // approval-link expired → re-issue from app
162162 "Re-enter" , "re-enter" , // invalid_email → re-enter address
163163 "Refresh" , "refresh" , // stale slug/state → refresh from app
164- "POST " , // multipart endpoints — POST is the action
165- "GET " , // list endpoints — GET is the action
164+ "POST " , // multipart endpoints — POST is the action
165+ "GET " , // list endpoints — GET is the action
166166 "Request" , "request" , // magic-link not found → request new one
167167 "Add " , "add " , // missing_email / missing_env etc → add the field
168168 "Trim " , "trim " , // env_too_large / tarball_too_large
@@ -287,11 +287,14 @@ func TestAgentActionContract_RegistryCoverage(t *testing.T) {
287287 // Quota walls.
288288 "quota_exceeded" , "storage_limit_reached" , "vault_quota_exceeded" ,
289289 "vault_not_available" , "vault_env_not_allowed" , "member_limit" ,
290- // "tier_unavailable" was dropped 2026-05-29 alongside the Team-tier
291- // checkout/change-plan guards (CEO BIZ-1). It was the only code in
292- // this registry that no handler emitted; the orphan-coverage gate
293- // flagged it. If a future feature reintroduces a "tier is genuinely
294- // unavailable" surface, re-add the code + its emitter in one PR.
290+ // "tier_unavailable" was dropped 2026-05-29 (CEO BIZ-1) and
291+ // superseded 2026-06-04 by "tier_not_yet_available" when Team was
292+ // RE-GATED out of self-serve checkout + change-plan (CEO directive:
293+ // Team not rolled out until unlimited-resource delivery is proven
294+ // built). A drop of "tier_not_yet_available" without migrating its
295+ // emitters is a contract regression — agents branching on the code
296+ // would lose the "contact sales" remediation.
297+ "tier_not_yet_available" ,
295298 "upgrade_required" , "rate_limit_exceeded" ,
296299 // B7-P1-7 (BugBash 2026-05-20): `claim_required` is the honest
297300 // 402 for anonymous-tier walls whose remediation is a FREE claim
0 commit comments