feat: add per-license Products section with Coder Agents price gates - #28051
Conversation
…ldens with generators make fmt/go (gci) regroups the imports in smtp_internal_test.go, and make gen/golden-files re-renders the AI budget notification goldens with HTML-escaped apostrophes. Both were committed out of sync with the repo's pinned tools, so every local pre-commit run failed its unstaged check. No behavior change.
…ents The usage generator writes hb_agent_runtime_v1 rows with created_at at the UTC hourly bucket start and exactly one row per bucket, but nothing in the schema enforced either invariant. A duplicate bucket row under a different id would be double-counted by any consumer summing runtime_ms, and a misaligned created_at would skew which usage period a bucket is attributed to. Replace the non-unique partial index idx_usage_events_agent_runtime with a unique index of the same shape and add an hour-alignment CHECK constraint. generateBucket treats a unique violation on the bucket index as another replica having won the race, mirroring the existing ON CONFLICT (id) no-op for committed rows.
…able runtime hours claims Two coupled changes to the license/entitlements layer. Measurement diagnostics: extract a measureUsage helper that owns the usage-query failure policy. A query failure now publishes the stable codersdk.LicenseManagedAgentUsageUnavailableErrorText on the entitlements Errors channel (keeping the alertable coderd_license_errors gauge counting) while the raw cause goes to the coderd log through the new FeatureArguments.Logger. A nil usage closure becomes a hard developer error, and a failure with a dead context still aborts the whole refresh. The managed agent count closure switches from AsSystemRestricted to the narrower AsUsagePublisher subject. The dashboard recognizes diagnostic texts and renders them muted, without the exceedance heading or a sales link. Claim tolerance: unusable agent_runtime_hours_* claim combinations no longer reject the whole license. decodeAgentRuntimeHours drops the unusable claims, reports them in a stable LicenseAgentRuntimeHoursClaimsIgnoredWarningText warning (deduplicated across licenses), and logs the affected license and claims; validateAgentRuntimeHours and its license-invalidating errors are removed. Rejecting a signed license over a cosmetic threshold claim would drop the deployment to unlicensed. Part of the stack for #27796.
Populate FeatureAgentRuntimeHours.Actual on every entitlements refresh for licenses that grant the feature. A new GetTotalUsageHBAgentRuntimeV1 query sums runtime_ms over the license's usage period, reading usage_events directly: hb_agent_runtime_v1 is exactly one row per hourly bucket deployment-wide, enforced by the unique partial index idx_usage_events_agent_runtime. The measurement reuses the shared measureUsage policy through a new AgentRuntimeMsFn closure (usage publisher subject): failures publish the stable LicenseAgentRuntimeUsageUnavailableErrorText and log the cause. Usage is floored to whole hours, matching the unit of the agent_runtime_hours_* claims, and at most one warning is emitted: reaching the allocation supersedes the advisory soft limit. The dashboard renders the soft-limit advisory muted without a sales link and treats the runtime usage-unavailable text as a diagnostic. Closes CODAGT-852.
…mplate goldens with generators" This reverts commit ed531d9d365720b4e394f4b422ea9776886c56a3. The gen/fmt drift it patched over came from main commit 07f79af (fix: markdown rendering improvements) having landed out of sync with its generators; main has since reverted that commit entirely in 8c2f7ad (#27979), so the sync is obsolete and conflicts with the regenerated goldens on latest main.
…s unlimited An agent_runtime_hours_allocation claim of exactly -1 now grants the agent_runtime_hours feature enabled with a nil Limit, which the entitlements API serves as an omitted "limit" field, the shape the UI already renders as Unlimited. Threshold claims alongside it are dropped with the existing claims-ignored warning, and any other negative allocation remains unusable.
An unlimited (-1) allocation decodes to an enabled agent_runtime_hours feature with a nil Limit, so the warning write-back now guards the allocation dereference: no thresholds can exist for an unlimited license, so no runtime hours warning is ever emitted. Actual usage is still measured and published. Feature.Compare is unchanged; the metered-beats-unlimited-on-exact-tie edge is pinned by a test and documented on decodeAgentRuntimeHours.
The expanded arbiter-scoping commentary is not needed on the insert query; the duplicate-bucket behavior is documented on the migration and in generateBucket where it is handled.
…ments Resolves LicenseBannerView.stories.tsx by adopting the base branch's convention: data-variant assertions are dropped (visual snapshots cover mutedness), so the runtime hour stories keep only their text and sales-link assertions.
…000567 Main landed its own 000565 and 000566 (oauth2 client type constraint and auth method backfill) after this branch was cut, making the migration number a duplicate that fails migrate setup.
…ments Adopts the base branch's trimmed comment style for the shared license warning constants and diagnostics tests, keeping the runtime-hours constants and the soft-limit prefix-coupling note; typesGenerated.ts is regenerated.
…ating story assertions
A nil Limit on a usage period feature means unlimited, so Feature.Compare now ranks it above any set limit when issued-at and end dates tie. Also types the LicenseBanner map callbacks as LicenseBannerMessage instead of using as const assertions.
… per-license Products section with Coder Agents price gates Each license card now always expands to a Products section: a Coder Workspaces box with active seat usage, and (on Premium licenses) a Coder Agents box driven by the agent_runtime_hours_* license claims and merged agent_runtime_hours entitlement. The Coder Agents box renders five states: no allocation (dashed purple upgrade CTA), unlimited (-1 sentinel), normal usage, allocation exceeded (red border, red 'Agent hours exceeded' status, chats stay Unlimited), and hard limit exceeded (red 'Hard limit exceeded' status, chats capped at 5, mirroring the backend's maxConcurrentRootAgents). Usage and overage indicators follow the AI Governance winning-license pattern via a generalized isLicenseApplicableForFeatureUsage helper. The header gains a Type column (Trial/Standard) and the left label now shows the feature set only (Premium/Enterprise).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ea1ef4f51a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ime claims The backend decodes agent_runtime_hours_* claims for every license regardless of feature set, so an Enterprise license carrying an allocation now renders the Coder Agents product instead of being hidden by the Premium-only condition. Feature.Compare tie-breaks equal issued-at values on the usage period end, so the winning-license predicate now matches the complete merged usage period (iat/nbf/exp) plus the validated soft and hard thresholds, not issued-at alone. The hard-limit claim validation moved into the effective threshold computation shared by both checks.
|
@codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Documentation CheckThis PR surfaces new user-facing licensing UI by default (no experiment flag): each license card now expands to a Products section showing Coder Workspaces seat usage and, for Premium licenses, a Coder Agents box that tracks Updates Needed
New Documentation Needed
Note Update: the backing entitlement PR #27985 has merged and this PR now targets Automated review via Coder Agents |
ibetitsmike
left a comment
There was a problem hiding this comment.
Mux reviewed this pull request on Mike's behalf.
Reviewed exact head 9a1e2c46eb52f4b5b12547302237c50db9887781 against the PR merge-base with runtime-hours-entitlements.
Code review
Standards: PASS. The applicability helper, card decomposition, naming, types, comments, and primitive reuse follow repository conventions. I found no new standards or code-smell findings.
Spec: PASS. I traced winning-license selection across iat, nbf, and exp, plus allocation and threshold matching. Positive, unlimited, no-allocation, exceeded, and hard-limit states render consistently with the merged entitlement. Fractional usage display and destructive boundaries use the same floored tenths value, AI Governance behavior is preserved, and the Workspaces card uses active-seat data. I found no new actionable findings.
Frontend review
FE1 PASS
FE2 PASS
FE3 PASS
FE4 PASS
FE5 PASS
FE6 PASS
FE7 PASS
FE8 PASS
FE9 PASS
FE10 PASS
Storybook interactions cover keyboard access, tooltip focus and dismissal, collapsible expansion, license applicability, Enterprise claims, and all Coder Agents price-gate states. All prior review findings are addressed at this head.
ibetitsmike
left a comment
There was a problem hiding this comment.
Mux approved this pull request on Mike's behalf.
Approved after code review and FE1 through FE10 frontend review found no actionable issues at this exact head.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5ad5e7d0b4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ir story queries broken by design updates
…rt product card states semantically in stories
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1863dc2286
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…t usage at the allocation boundary as exceeded
|
@codex review |
|
Codex Review: Didn't find any major issues. Another round soon, please! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Each license card now always expands to a Products section: a Coder Workspaces box showing active seat usage, and, on Premium licenses, a Coder Agents box driven by the
agent_runtime_hours_*license claims and the mergedagent_runtime_hoursentitlement. The card header gains a Type column (Trial/Standard), and the left header label now shows the feature set only (Premium/Enterprise).The Coder Agents box renders five states: no allocation (dashed purple upgrade CTA), unlimited allocation (
-1sentinel), normal usage, allocation exceeded (red border and red "Agent hours exceeded" status; concurrent chats stay Unlimited), and hard limit exceeded (red "Hard limit exceeded" status; concurrent chats capped at 5, mirroring the backend'smaxConcurrentRootAgents, which is not exposed via the API). Usage and overage indicators only render on the license whose allocation matches the merged entitlement and which is currently effective, following the existing AI Governance winning-license pattern via a generalizedisLicenseApplicableForFeatureUsagehelper; AI Governance add-on behavior is unchanged.Stacked on #27985 (base branch
runtime-hours-entitlements); do not merge before it.Notes for review:
feat: report agent runtime hours usage in entitlements #27985 now grandfathers claim-less Premium licenses into a zero-hour
agent_runtime_hoursallocation, so the merged entitlement (disabled,limit: 0) and its measuredactualare always present for Premium deployments. The upgrade card's "Agent hours used" row therefore renders universally; thePremiumstory pins that state.Agent hours usage now renders with exactly one decimal (e.g.
16,264.3,42.0), derived from feat: report agent runtime hours usage in entitlements #27985's newactual_msfield and floored to tenths with integer math. The same floored value drives the exceeded checks, so the displayed number and the red state flip at the same instant; a fraction past the allocation now trips "Agent hours exceeded" (20,000.1 > 20,000), pinned by thePremiumWithAgentHoursExceededByFractionstory. The allocation denominator stays whole (it comes from the whole-hour license claim).