Cost Governance PolicyΒΆ
Subscription: GitHub Team ($4/user/mo) + Copilot Pro Plus ($39/mo) Budget: 3,000 Linux-equivalent Actions minutes/month Β· 2 GB artifact storage Policy version: 1.0 | Effective: 2026-03-14 | Owner: @mbaetiong OKR: OBJ-001 (POC: 2026-03-22 Β· Production: 2026-04-01)
1 β Why This Policy ExistsΒΆ
GitHub Team provides 3,000 Actions minutes per month at no extra cost for Linux runners. Minutes beyond that, non-standard runners (macOS = 10Γ, Windows = 2Γ, ubuntu-latest-m = 2Γ), and GHCR data-transfer are billed directly. Without a gate, a single matrix build on a medium runner can consume 120+ effective minutes β 4% of the monthly budget in one run.
This policy installs a lightweight approval gate that: - Makes cost visible before a job runs - Blocks high-cost jobs until a stakeholder explicitly approves - Auto-approves low-cost jobs with zero friction - Keeps the repository within its subscription without requiring an Enterprise upgrade
2 β Runner Minute MultipliersΒΆ
| Runner | Cores | Effective minutes multiplier | Notes |
|---|---|---|---|
ubuntu-latest |
2 | 1Γ | Default β included in 3,000 min |
ubuntu-latest-m |
4 | 2Γ | Medium runner β costs 2 min per real min |
ubuntu-latest-l |
8 | 4Γ | Large runner β 4 min per real min |
ubuntu-latest-xl |
16 | 8Γ | XL β not recommended on Team plan |
windows-latest |
2 | 2Γ | Billed at 2Γ Linux rate |
macos-latest |
3 | 10Γ | Most expensive β use sparingly |
self-hosted |
any | 0Γ | No Actions minutes billed |
Source: GitHub Actions billing
3 β Cost TiersΒΆ
Effective minutes = timeout_minutes Γ runner_multiplier Γ matrix_job_count
GREEN β < 30 effective min, no GHCR push β Auto-approved
YELLOW β 30β90 effective min β Warning posted; auto-proceeds after 60 s
RED β > 90 effective min OR GHCR push β Blocked until stakeholder checkbox ticked
Budget impact per tier (worst case, per run)ΒΆ
| Tier | Max effective min | % of monthly budget |
|---|---|---|
| GREEN | 30 | 1.0% |
| YELLOW | 90 | 3.0% |
| RED | 180+ | 6.0%+ |
4 β Covered WorkflowsΒΆ
| Workflow | Tier | Reason | Added |
|---|---|---|---|
build-preview-image.yml |
π΄ RED | ubuntu-latest-m Γ 30 min Γ 2 matrix + GHCR push = 120 eff-min + transfer cost |
PR #3575 |
data-quality-suite.yml |
π΄ RED | 3 jobs Γ 60 min = 180 eff-min | PR #3575 |
scheduled-archival.yml |
π΄ RED | 3 jobs Γ 60 min = 180 eff-min, runs on schedule | PR #3575 |
rust_swarm_ci.yml |
π΄ RED | 3 jobs Γ 60 min = 180 eff-min | PR #3575 |
embedding-index-rebuild.yml |
β οΈ YELLOW | 15 min, scheduled (frequent trigger risk) | PR #3575 |
Workflows intentionally not gated (GREEN tier, < 30 effective min):
| Workflow | Effective min | Reason not gated |
|---|---|---|
deferral-language-gate.yml |
~3 min | Lightweight Python script |
agent-auth-delegation.yml |
~15 min active | Most of 120 min is idle approval wait |
pre-merge-validation.yml |
~10 min | Fast pytest subset |
auto-fix-pr-check.yml |
~8 min | Script only |
5 β Approval FlowΒΆ
flowchart TD
PR["PR opened / push to branch"]
COST["π° cost-gate job\ncost_estimator.py calculates tier"]
GREEN["β
GREEN\nAuto-approved\nJob proceeds immediately"]
YELLOW["β οΈ YELLOW\nWarning comment posted\nAuto-proceeds after 60 s"]
RED["π΄ RED\nBlocking comment posted\nPolls PR body every 60 s"]
CHECKBOX["Stakeholder ticks\n- [x] π° Cost Proposal Approved\nin PR description"]
DISPATCH["Owner triggers via\nworkflow_dispatch\n(bypass)"]
APPROVED["β
Gate passed\nExpensive job unblocked"]
TIMEOUT["β Gate timed out\n(10 min with no approval)\nJob fails β re-run after ticking"]
PR --> COST
COST --> GREEN --> APPROVED
COST --> YELLOW --> APPROVED
COST --> RED
RED -->|"checkbox detected"| APPROVED
RED -->|"workflow_dispatch"| APPROVED
DISPATCHER[Owner] --> DISPATCH --> APPROVED
STAKEHOLDER[Stakeholder] --> CHECKBOX --> APPROVED
RED -->|"10 min timeout"| TIMEOUT
style GREEN fill:#98fb98
style YELLOW fill:#ffd700
style RED fill:#ff6b6b
style APPROVED fill:#98fb98
style TIMEOUT fill:#ff6b6b
6 β Stakeholder Approval InstructionsΒΆ
When the Cost Gate posts a π΄ RED comment on your PR:
- Read the comment β it shows the estimated effective minutes and cost tier reason
- Decide β is the spend justified for this PR?
- Approve β in the PR description, find the Cost Governance section and change: to:
- Wait β the gate polls every 60 seconds and unblocks within ~60 s of your tick
- Alternative β trigger the workflow manually via
Actions β Run workflowto bypass the PR gate
7 β Subscription Boundaries (Do Not Cross)ΒΆ
| Limit | Value | Action if approaching |
|---|---|---|
| Actions minutes | 3,000 min/mo | Alert at 2,500 min (83%); defer non-critical scheduled jobs |
| Artifact storage | 2 GB | Reduce retention-days to 7 for non-critical artifacts |
| GHCR transfer | 10 GB/mo free | Limit image push to main branch merges only |
| Copilot premium requests | 1,500/mo | Reserve for coding agent; use base model for completions |
| GHAS (CodeQL on branches) | Not purchased | CodeQL on feature branches = expected failure, not a bug |
To increase limits without upgrading plan:
- Reduce matrix job count from 3 β 2 where test isolation allows
- Move scheduled workflows from schedule: to workflow_dispatch: only
- Set cancel-in-progress: true on all PR-triggered workflows
8 β Adding a New Workflow to the GateΒΆ
When adding a new workflow that may incur cost, add a cost-gate job as the first entry
in jobs: using the reusable workflow:
jobs:
cost-gate:
name: "π° Cost Gate"
uses: ./.github/workflows/cost-gate.yml
with:
workflow_name: "My Expensive Workflow"
runner: "ubuntu-latest" # adjust to actual runner
timeout_minutes: 60 # adjust to job timeout
matrix_count: 2 # set to number of parallel jobs
pushes_to_ghcr: false # true if job pushes to GHCR
permissions:
contents: read
pull-requests: write
my-expensive-job:
needs: cost-gate # gate must pass first
...
9 β RoadmapΒΆ
| Item | OKR Task | Due | Status |
|---|---|---|---|
Unit tests for cost_estimator.py |
OBJ-001 T-001 | 2026-03-20 | β¬ |
Branch protection: cost-gate as required check |
OBJ-001 T-003 | 2026-03-28 | β¬ (admin) |
| Monthly usage NDJSON logger | OBJ-001 T-004 | 2026-03-28 | β¬ |
| 2,500-min budget alert in Self-Healing CI | OBJ-001 T-005 | 2026-03-30 | β¬ |
Gate docker-build-push.yml (consistency) |
OBJ-001 T-006 | 2026-03-30 | β¬ |
| Production sign-off @mbaetiong | OBJ-001 T-007 | 2026-04-01 | β¬ |
Policy: docs/ops/COST_GOVERNANCE.md | v1.0 | 2026-03-14 Session 24 PR #3575 Subscription: GitHub Team + Copilot Pro Plus | OBJ-001