From 7333b4cb42a9449400304ec31271a38a7ec09a83 Mon Sep 17 00:00:00 2001 From: Manas Srivastava Date: Sat, 6 Jun 2026 12:04:57 +0530 Subject: [PATCH] docs(jobs): fix stale money-path + quota comments (terminate endpoint live, Team not unlimited) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three agent-misleading comment inaccuracies in two job files. Comment-only — zero behavior change; `go build ./internal/jobs/` + `go vet` clean. payment_grace_terminator.go: - Header said terminate "(b) downgrade tier to ANONYMOUS" — the api endpoint downgrades to FREE (api internal_terminate.go:207, "anonymous would be wrong"); a terminated team keeps its team_id so free is the correct floor. - The TODO claimed the api side "is referenced by this dispatcher but NOT yet implemented in the api repo — that side ships in a separate PR." It IS implemented and live: api router.go:893 → internal_terminate.go::Terminate (pause-resources + downgrade-to-free + mark-grace-terminated + audit-emit). An agent reading the TODO would wrongly conclude dunning-termination is a no-op. Rewritten to describe the live state. quota_wall_nudge.go: - Skip-rationale said "team tier is unlimited on every axis." False post the strict-≥80%-margin redesign (2026-06-05) which retired all -1 limits — Team now carries finite high-capacity caps. Skipping Team is still correct (it's the top self-serve tier; the wall nudge is an UPGRADE banner with nothing above to upsell), so this is a stale-reason fix, not a behavior change. Verified the one local integration-test failure (TestIntegration_BillingReconciler_SkipsTestCohort) reproduces IDENTICALLY on clean origin/master without this diff — it is a pre-existing local-test-DB seed flake unrelated to these comment edits (the authoritative gate is CI's seeded postgres service). Co-Authored-By: Claude Opus 4.8 --- internal/jobs/payment_grace_terminator.go | 20 +++++++++++++------- internal/jobs/quota_wall_nudge.go | 10 +++++++--- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/internal/jobs/payment_grace_terminator.go b/internal/jobs/payment_grace_terminator.go index 7be04a3..f296e80 100644 --- a/internal/jobs/payment_grace_terminator.go +++ b/internal/jobs/payment_grace_terminator.go @@ -4,7 +4,9 @@ package jobs // payment_grace_periods table looking for teams whose grace expired // (expires_at < now()) and calls the api repo's // POST /internal/teams/:id/terminate endpoint to (a) pause all resources, -// (b) downgrade tier to anonymous, (c) mark the dunning row terminated. +// (b) downgrade tier to FREE — NOT anonymous; a terminated team keeps its +// team_id, so "free" (claimed-but-unpaid) is the correct floor (see +// api internal_terminate.go), (c) mark the dunning row terminated. // // Cadence: every 1h (per the brief). The 1h floor is fine because the // grace clock is 7 days; a customer who recovers in the final hour @@ -27,14 +29,18 @@ package jobs // without action — better than booting the worker with no secret and // later writing audit_log rows for non-terminations. // -// TODO(ops): WORKER_INTERNAL_JWT_SECRET is a new env var. Operator must +// OPS: WORKER_INTERNAL_JWT_SECRET is a shared env var. Operator must // (1) generate a 32-byte random value, (2) write it into the worker's // secrets, (3) write the same value into the api's secrets under -// WORKER_INTERNAL_JWT_SECRET so the api can verify the bearer token, -// (4) the api must expose POST /internal/teams/:id/terminate that -// accepts the bearer + does pause/downgrade/mark-terminated. The -// endpoint is referenced by this dispatcher but NOT yet implemented in -// the api repo — that side ships in a separate PR. +// WORKER_INTERNAL_JWT_SECRET so the api can verify the bearer token. +// The api side IS shipped: POST /internal/teams/:id/terminate is +// registered (api router.go → internal_terminate.go::Terminate) and +// does the pause-resources + downgrade-to-free + mark-grace-terminated +// + audit-emit work. This dispatcher only fires the call and mirrors the +// lifecycle event into audit_log for the email forwarder. (Historical +// note: when this file was first written the api endpoint was a separate +// follow-up PR — that PR has since merged, so the prior "NOT yet +// implemented" TODO is removed to avoid misleading an agent reading it.) import ( "bytes" diff --git a/internal/jobs/quota_wall_nudge.go b/internal/jobs/quota_wall_nudge.go index 7799862..7c91a42 100644 --- a/internal/jobs/quota_wall_nudge.go +++ b/internal/jobs/quota_wall_nudge.go @@ -13,9 +13,13 @@ package jobs // inside the last 24h, regardless of axis. This keeps the dashboard banner // stable (it won't flicker between axes once it's earned a slot). // -// Tier gating: teams on the "team" tier are skipped entirely — team tier -// is unlimited on every axis, so a wall nudge is incoherent. Teams on -// tier "free" (claimed-but-unpaid) and "anonymous" (unclaimed) are also +// Tier gating: teams on the "team" tier are skipped entirely — Team is the +// top self-serve tier, so an "upgrade for headroom" nudge has no target +// to point at. (The wall nudge is an UPGRADE banner; post the +// strict-≥80%-margin redesign of 2026-06-05 Team is no longer "unlimited" +// — it carries finite high-capacity limits — but there is still nothing +// above it to upsell, so skipping it remains correct.) Teams on tier +// "free" (claimed-but-unpaid) and "anonymous" (unclaimed) are also // skipped: they are pre-conversion and the conversion CTA is the claim // banner, not an upgrade banner. //