🌐 US-Proxy
class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
Skip to content

feat: add GET /organizations/{org}/groups/ai/spend - #27123

Merged
ssncferreira merged 5 commits into
mainfrom
ssncf/aigov-org-groups-spend
Jul 20, 2026
Merged

feat: add GET /organizations/{org}/groups/ai/spend#27123
ssncferreira merged 5 commits into
mainfrom
ssncf/aigov-org-groups-spend

Conversation

@ssncferreira

@ssncferreira ssncferreira commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Description

Adds GET /api/v2/organizations/{org}/groups/ai/spend?group_ids=... to return per-group AI spend and configured limits for a set of groups in an organization.

In the UI, this endpoint is used alongside the existing /api/v2/organizations/{org}/groups endpoint. AI spend data is kept separate from that endpoint so that:

  • Different concepts stay on different endpoints: identity (groups) vs. cost control (spend). Cost control is an additional feature layered on top of groups/orgs.
  • Callers that don't need spend information don't pay for its computation.

UI flow:

  1. Request /api/v2/organizations/{org}/groups → returns the organization's groups.
  2. Request /api/v2/organizations/{org}/groups/ai/spend?group_ids=... with the IDs from step 1.

The groups endpoint from 1) is currently not paginated, but if pagination is added later, this design keeps the two responses in sync. This spend endpoint intentionally takes group_ids rather than paginating on its own, since it depends on the group set from step 1. Pagination could be added in the future, especially for Cost Control-focused pages.

image

Changes

  • Add codersdk.OrganizationGroupsAISpend and OrganizationGroupAISpend types, plus a shared AISpendPeriodWindow embedded in the spend response.
  • Add GetOrganizationGroupsAISpend SQL query with a dbauthz per-row filter that mirrors GET /organizations/{org}/groups.
  • Add handler and route under /organizations/{organization}/groups/ai/spend with a required group_ids query param (cap 100). Callers with more than 100 groups are expected to batch across multiple requests.
  • Add codersdk client method.
  • Tests: dbauthz, raw SQL, endpoint, and role-access.

Closes https://linear.app/codercom/issue/AIGOV-466/backend-organization-groups-endpoint-with-groups-spend

Note

Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Docs preview

📖 View docs preview for docs/reference/api/enterprise.md

ssncferreira commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@ssncferreira
ssncferreira force-pushed the ssncf/aigov-org-groups-spend branch from bd0fc88 to ca91968 Compare July 9, 2026 16:12
@ssncferreira
ssncferreira force-pushed the ssncf/aibridge-budget-period branch from db14366 to a06d471 Compare July 13, 2026 12:05
@ssncferreira
ssncferreira force-pushed the ssncf/aigov-org-groups-spend branch from ca91968 to 802751b Compare July 13, 2026 12:05
@ssncferreira
ssncferreira force-pushed the ssncf/aibridge-budget-period branch from a06d471 to a35ad30 Compare July 13, 2026 12:21
@ssncferreira
ssncferreira force-pushed the ssncf/aigov-org-groups-spend branch from 802751b to 2b21c5f Compare July 13, 2026 12:21
@linear-code

linear-code Bot commented Jul 13, 2026

Copy link
Copy Markdown

AIGOV-466

@ssncferreira
ssncferreira changed the base branch from ssncf/aibridge-budget-period to graphite-base/27123 July 13, 2026 16:52
@ssncferreira
ssncferreira force-pushed the ssncf/aigov-org-groups-spend branch from 2b21c5f to b6c2225 Compare July 13, 2026 16:53
@ssncferreira
ssncferreira force-pushed the graphite-base/27123 branch from a35ad30 to 6580cdc Compare July 13, 2026 16:53
@graphite-app
graphite-app Bot changed the base branch from graphite-base/27123 to main July 13, 2026 16:53
@ssncferreira
ssncferreira force-pushed the ssncf/aigov-org-groups-spend branch 3 times, most recently from c27116f to 98e74d3 Compare July 14, 2026 16:32
return g.Group.RBACObject()
}

func (g GetOrganizationGroupsAISpendRow) RBACObject() rbac.Object {

@ssncferreira ssncferreira Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIU, for full parity with /api/v2/organizations/{org}/groups, we need this for regular org members. These members see all groups in GET /organizations/{org}/groups, but without this they would get empty rows from this new endpoint.

Note that regular members already see the group's configured spend limit via the existing group budget endpoint (/api/v2/groups/{group}/ai/budget), so the only new property here is the aggregate current spend.

As a result, this comes down to: should regular members have visibility into their groups' current AI spend? Let me know what you think.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 [CRF-5] Answering your question: as written, group AI spend inherits group-read authz, so on a default deployment every org member can read every group's aggregate spend, and a human needs to decide whether that is intended (Pariston P2, Kurapika/Knov P3)

GetOrganizationGroupsAISpendRow.RBACObject delegates to Group.RBACObject, so visibility equals GET /organizations/{org}/groups. A plain org member gets org-wide ResourceGroup read only when org.ShareableWorkspaceOwners == everyone (roles.go:1168), which is the column default; the passing Member case (a user not in the group) confirms it. Meruem's nuance: the budget limit is not new exposure (GetGroupAIBudget already authorizes member read), so the only incremental disclosure here is each group's aggregate current_spend_micros, summed across members the caller may not be able to enumerate individually. This is deliberate parity and not a code defect, but cost-control spend is a different sensitivity class from group identity. Per our review policy an agent can't accept this gap as permanent: either confirm members should see group spend (and keep group-read authz), or gate spend behind an admin-level action/resource. Flagging so it's decided rather than defaulted.

🤖

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine as-is.

// URL length limits and match expected UI batch sizes.
const maxOrganizationGroupsAISpendGroupIDs = 100

// @Summary Get organization groups AI spend

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other alternatives could be POST or ideally QUERY. Since QUERY is still a draft HTTP method and not widely used, and since similar endpoints like /api/v2/groups?group_ids=... use GET with the same comma-separated batch pattern, going with GET for consistency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On GET vs POST/QUERY: the panel agrees GET is the right call. 100 UUIDs is roughly 3,700 characters of query string, well within URL limits; GET keeps the endpoint idempotent and cacheable and matches /api/v2/groups?group_ids=...; the cap is enforced on the parsed UUID count and ANY(@group_ids) dedups in SQL, so duplicates cannot fan out. No change needed.

🤖

@ssncferreira
ssncferreira force-pushed the ssncf/aigov-org-groups-spend branch 2 times, most recently from eb5f19c to 0830e8f Compare July 14, 2026 17:41
@ssncferreira

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Chat: Review posted | View chat
Requested: 2026-07-15 16:53 UTC by @ssncferreira
Spend: $42.92 / $100.00

Review history
  • R1 (2026-07-14), 1 Note, 1 P2, COMMENT. Review
  • R2 (2026-07-15): 21 reviewers, 1 Nit, 4 Note, 1 P1, 3 P2, 4 P3, REQUEST_CHANGES. Review
  • R3 (2026-07-15), 1 Nit, 4 Note, 1 P1, 3 P2, 4 P3, COMMENT. Review

deep-review v0.9.0 | Round 3 | 304f127..f402a88

Last posted: Round 3, 13 findings (1 P1, 3 P2, 4 P3, 1 Nit, 4 Note), COMMENT. Review

Finding inventory

Finding inventory - PR #27123

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P2 Author fixed (4b4aa4d) coderd/database/modelmethods.go:461 GetOrganizationGroupsAISpendRow.RBACObject duplicates Group.RBACObject verbatim instead of delegating R1 Netero Yes
CRF-2 Note Author fixed (f402a88), unverified (BLOCKED round) enterprise/coderd/aibridge_test.go:3476 Role-access test sets WantGroupInResponse:true for all cases, so the empty-response branch is dead R1 Netero Yes
CRF-3 P1 Author fixed (f402a88), unverified (BLOCKED round) enterprise/coderd/aibridge_test.go:3480 OtherOrgMember case fails deterministically (404, not empty 200); CI red and CRF-2's empty branch still dead R2 Bisky P0, Mafuuu/Pariston/Kurapika/Knov P1 Yes
CRF-4 P2 Author fixed (f402a88), unverified (BLOCKED round) enterprise/coderd/aibridge_test.go:3212 RequiresLicenseFeature/RequiresExperiment tests short-circuit at parent templateRBACEnabledMW, so FeatureAIBridge + experiment gates are untested R2 Ryosuke P2, Kite P2/P3, Chopper P3 Yes
CRF-5 P2 Author accepted R3 (johnstcn: fine as-is; incremental exposure is aggregate spend only) coderd/database/modelmethods.go:461 Group AI spend (financial data) inherits group-read authz; on default deployments any org member reads every group's aggregate spend. Human decision needed R2 Pariston P2, Kurapika/Knov P3 Yes
CRF-6 P3 Author fixed (f402a88, documented), unverified (BLOCKED round) enterprise/coderd/aibridge.go:971 100 group_ids cap collides with the unbounded/unpaginated groups endpoint it pairs with; SDK client does not chunk R2 Luffy P3, Knov P4 Yes
CRF-7 P3 Open (silent R3, blocks) enterprise/coderd/coderd.go:607 Sibling /groups/{group}/ai/budget route lacks the ExperimentAIGatewayCostControl gate every other cost-control route carries (outside diff) R2 Razor P3, Meruem P3, Zoro Note Yes
CRF-8 Nit Author fixed (f402a88), unverified (BLOCKED round) coderd/database/queries/aicostcontrol.sql:120 Inline comment restates the WHERE clause and duplicates the header doc R2 Gon P2 Yes
CRF-9 P3 Author fixed (f402a88), unverified (BLOCKED round) enterprise/coderd/aibridge.go:981 Budget-period computation copied verbatim from userAISpendStatus handler instead of a shared helper R2 Robin P3, Zoro Note Yes
CRF-10 P3 Author fixed (f402a88), unverified (BLOCKED round) enterprise/coderd/aibridge.go:952 100-id cap is undocumented in the @PARAM annotation and the exact 100 boundary is untested R2 Leorio P3, Bisky Note Yes
CRF-11 Note Author fixed (f402a88), unverified (BLOCKED round) coderd/database/queries/aicostcontrol.sql:123 Query has no ORDER BY; row order unspecified, latent flake if positional/pagination use is added R2 Komugi, Meruem Yes
CRF-12 Note Open (silent R3, blocks) coderd/database/queries/aicostcontrol.sql:107 Comment says spend aggregated until NOW but query has no upper bound; correct today, matches sibling R2 Pariston, Leorio, Hisoka, Knuckle Yes
CRF-13 Note Author fixed (f402a88, documented), unverified (BLOCKED round) enterprise/coderd/aibridge.go:942 Unknown/cross-org/unreadable group_ids silently dropped with no per-id signal; matches design R2 Mafuuu, Kite, Chopper, Ryosuke Yes
CRF-14 Note Dropped by orchestrator (codebase-wide convention, 1093 call sites, not this PR) enterprise/coderd/aibridge_test.go:3400 ptr.Ref redundant vs new() builtin on go1.26 R2 Ging-go No
CRF-15 Note Dropped by orchestrator (no existing helper; codersdk-wide extraction is broader than this PR) codersdk/aibridge.go:485 UUID-slice to comma-joined group_ids is the 6th inline copy of the idiom R2 Robin, Zoro No

Contested and acknowledged

None this round.

Round log

Round 3 update

BLOCKED (churn guard). New commit f402a88 ("chore: address review comments"). 9 addressed (CRF-2/3/4/6/8/9/10/11/13, all author-claimed, unverified since panel deferred), 1 acknowledged (CRF-5, human johnstcn accepted as-is), 2 silent (CRF-7 P3 and CRF-12 Note: no code change, no author response). No panel spawned. Review blocked pending an author response (fix, ticket, or reasoned won't-fix) on CRF-7 and CRF-12. Reviewed against 304f127..f402a88.

Round 2

Panel (first panel round), 20 reviewers + Netero. Churn guard PROCEED (CRF-1, CRF-2 claimed fixed). Panel disproved the CRF-2 closure: orchestrator ran TestOrganizationGroupsAISpendRoleAccess on head 4b4aa4d, OtherOrgMember FAILs with 404 while the other five pass (CRF-3, reopens CRF-2). Orchestrator also verified CRF-4: route nests under templateRBACEnabledMW (= RequireFeatureMW(FeatureTemplateRBAC)); both gate tests omit FeatureTemplateRBAC so they 403 at the parent before the route's own gates, asserting only StatusForbidden. New: 1 P1, 2 P2, 5 P3, 4 Note. 2 dropped (scope). Netero round 2: no new findings, CRF-1/CRF-2 code present. Reviewed against 304f127..4b4aa4d.

Round 1

Netero-only first-pass (P2 present, pre-panel gate). 1 P2, 1 Note. Reviewed against 61e5253..0830e8f. Orchestrator verified both findings against the head worktree: RBACObject reconstructs the ACL that Group.RBACObject builds (modelmethods.go:446-455 vs 461-469); role-access test cases are all WantGroupInResponse:true so the else/require.Empty branch never runs.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First-pass review (Netero only). This is a mechanical first pass, so the full review panel has not yet looked at this PR. The panel will review once these findings are addressed. Posting these early so they don't cost parallel review time later.

The change is well-tested: 76% test density, and the SQL query has genuine coverage for period normalization, cross-org exclusion, and cross-user aggregation (NormalizesNonUTCPeriodStart and ExcludesSpendBeforePeriodStart would fail without the query's date handling). The shared AISpendPeriodWindow reuse across UserAISpendStatus and the new response type is clean, and the route/handler/SDK/generated code all line up.

Severity count: 1 P2, 1 Note.

The P2 is the one to weigh before the panel round. As Netero put it: "This is authorization logic, so the duplication is a drift hazard." The new row's RBACObject rebuilds the same ACL that Group.RBACObject already produces, so a future change to group read authorization would land on GET /organizations/{org}/groups but silently miss this endpoint. Sibling rows (GetGroupsRow) avoid this by delegating.

The Note is a design question your own inline comment already raises: the role-access test asserts every role (including a plain Member) sees the group, so it never exercises the filtered-out path. Worth deciding whether a role should be excluded and testing that.

🤖 This review was automatically generated with Coder Agents.

Comment thread coderd/database/modelmethods.go
Comment thread enterprise/coderd/aibridge_test.go Outdated
@ssncferreira
ssncferreira force-pushed the ssncf/aigov-org-groups-spend branch from 0830e8f to 4b4aa4d Compare July 15, 2026 09:35
@ssncferreira

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@ssncferreira
ssncferreira force-pushed the ssncf/aigov-org-groups-spend branch from 4b4aa4d to 8ef0651 Compare July 15, 2026 09:54

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Panel round (first full-panel pass). Strong PR overall: the SQL aggregate is correct by construction (the LEFT JOIN on group_ai_budgets can't fan out the spend SUM because the budget PK is group_id), cross-org isolation is enforced at both the SQL WHERE clause and the dbauthz post-filter, and the raw-SQL test layer genuinely hunts edges (cross-org exclusion, id-not-in-list, period boundary, cross-user aggregation, non-UTC normalization). The AISpendPeriodWindow extraction is a clean, minimal refactor with no wire-format change, and CRF-1 was fixed correctly by delegating to Group.RBACObject.

But the CRF-2 fix regressed. The OtherOrgMember case added to revive the dead empty-response branch fails deterministically: I ran TestOrganizationGroupsAISpendRoleAccess on head 4b4aa4d and OtherOrgMember returns 404 (blocked at the org-membership middleware, before the handler), while the other five cases pass. So CI is red and the branch CRF-2 targeted is still dead. That is a merge blocker.

Two P2s follow. The license/experiment gate tests don't test their gates: the route nests under templateRBACEnabledMW, so both negative tests 403 at the parent FeatureTemplateRBAC check before reaching the FeatureAIBridge/experiment gates they name (Ryosuke proved it by deleting each gate, the tests stayed green). And your own open question about member visibility is now a concrete disclosure decision a human must make: on a default deployment any org member can read every group's aggregate AI spend.

Severity count: 1 P1, 2 P2, 5 P3, 1 Nit, 4 Note.

Hisoka, who found nothing to break, put the shape of it well: "I came looking for a fight. I fought the join, the clock, the routing, the embedding, and the authz filter. None of them broke." The engine is sound. The instrument panel (the two gate tests) reads green while measuring nothing, and one committed test is red.


enterprise/coderd/coderd.go:607

P3 [CRF-7] The sibling /groups/{group}/ai/budget route lacks the ExperimentAIGatewayCostControl gate that every other cost-control route carries (Razor P3, Meruem P3)

The new spend route and the user AI routes gate on both the experiment and FeatureAIBridge (with the TODO(AIGOV-443) kill-switch comment). The group budget CRUD routes at coderd.go:607-613 gate on FeatureAIBridge only. Consequence: on a licensed deployment with the experiment off, operators can still create/read/delete group budgets while reading spend returns 403, an incomplete kill-switch. This route is outside this PR's diff, but you're actively extending this surface, so you're the right person to reconcile it. The class fix is to hang the experiment gate on the cost-control boundary once rather than per leaf route. A human should decide whether the divergence is intended.

🤖

🤖 This review was automatically generated with Coder Agents.

Comment thread enterprise/coderd/aibridge_test.go
Comment thread enterprise/coderd/aibridge.go
Comment thread coderd/database/queries/aicostcontrol.sql Outdated
Comment thread enterprise/coderd/aibridge.go Outdated
Comment thread enterprise/coderd/aibridge.go Outdated
Comment thread coderd/database/queries/aicostcontrol.sql Outdated
Comment thread coderd/database/queries/aicostcontrol.sql
Comment thread enterprise/coderd/aibridge.go
// @Param group_ids query string true "Comma-separated list of group IDs (maximum 100)"
// @Success 200 {object} codersdk.OrganizationGroupsAISpend
// @Router /api/v2/organizations/{organization}/groups/ai/spend [get]
func (api *API) organizationGroupsAISpend(rw http.ResponseWriter, r *http.Request) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The endpoint GET /api/v2/organizations/{org}/groups is not paginated and currently has no limit on the number of groups returned. However, for this new endpoint we are establishing a limit of 100 group_ids as input, meaning that if the former returns more than 100 groups, it is the caller's responsibility (e.g. the UI) to batch across multiple requests. AFAIK the UI has a default pagination of 25 (DEFAULT_RECORDS_PER_PAGE), so maybe this number is more appropriate for this new endpoint? A possible follow-up would be to add pagination to the org groups endpoint. Let me know what you think.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine as-is, although we should clearly document the limitation for callers in the API doc.

@ssncferreira
ssncferreira marked this pull request as ready for review July 15, 2026 11:20
LEFT JOIN group_ai_budgets budget ON budget.group_id = groups.id
LEFT JOIN ai_user_daily_spend spend
ON spend.effective_group_id = groups.id
AND spend.day >= ((@period_start::timestamptz) AT TIME ZONE 'UTC')::date

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we introduce a @period_end argument as well here so we're aligned with codersdk.AISpendPeriodWindow?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good point 👍 It works today because this is currently only used for the current time period, so we effectively only need the spend up until NOW. But it definitely makes sense to apply the period end so it can also be used in the future for calls that require previous periods. I will address this in a follow-up PR, since we also apply this logic in GetUserAISpendSince.

return g.Group.RBACObject()
}

func (g GetOrganizationGroupsAISpendRow) RBACObject() rbac.Object {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine as-is.

// @Param group_ids query string true "Comma-separated list of group IDs (maximum 100)"
// @Success 200 {object} codersdk.OrganizationGroupsAISpend
// @Router /api/v2/organizations/{organization}/groups/ai/spend [get]
func (api *API) organizationGroupsAISpend(rw http.ResponseWriter, r *http.Request) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine as-is, although we should clearly document the limitation for callers in the API doc.

@ssncferreira

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review paused (churn guard blocked). Solid progress on f402a88: of the 12 open findings, 9 have code fixes and 1 (CRF-5, member visibility) was accepted by @johnstcn as fine as-is. The panel hasn't re-verified those fixes yet, because two findings are still silent (no code change and no response), and the review process holds the panel until every finding gets at least a reasoned answer.

Two items need a word from you before the panel re-runs:

  • CRF-7 (P3) - the sibling /groups/{group}/ai/budget route (coderd.go:607-613) gates on FeatureAIBridge only, without the ExperimentAIGatewayCostControl gate that this PR's spend route and the user AI routes carry. It's outside this PR's diff, so a fix here is optional, but it needs a disposition: fix it, file a follow-up ticket, or say why the divergence is intended.

  • CRF-12 (Note) - the aicostcontrol.sql comment says spend is aggregated "until NOW" while the query has no upper bound. It's correct today and mirrors GetUserAISpendSince, so a won't-fix is entirely reasonable; it just needs to be stated (a one-line "acknowledged, matches sibling, leaving as-is" clears it).

Neither requires a code change. A short reply on each thread (or a ticket link for CRF-7) unblocks the next round, at which point the panel will verify the nine fixes and re-check CRF-3/CRF-4 against the code.

🤖 This review was automatically generated with Coder Agents.

@ssncferreira
ssncferreira force-pushed the ssncf/aigov-org-groups-spend branch from f402a88 to 312fa43 Compare July 16, 2026 09:52
@ssncferreira
ssncferreira force-pushed the ssncf/aigov-org-groups-spend branch from ca72acc to 50a654d Compare July 20, 2026 11:41

ssncferreira commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Merge activity

  • Jul 20, 11:54 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 20, 11:54 AM UTC: @ssncferreira merged this pull request with Graphite.

@ssncferreira
ssncferreira merged commit 2adc8f5 into main Jul 20, 2026
33 of 34 checks passed
@ssncferreira
ssncferreira deleted the ssncf/aigov-org-groups-spend branch July 20, 2026 11:54
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants