Module 6: Finops For Tokens

6. Hands-on: a volume change that triggers the budget

Description

Lessons 4 and 5 left a complete gate: bedrock-budget.rego requires the correct tag, and the new cost-check step requires a projected cost within budget. This lesson tests the second half of that promise with real evidence: a deliberately disproportionate monthly volume assumption, proposed as if it were a real change to GENAI-COST-PROFILE.md, stopped by lesson 4's step before it ever reaches an apply — never after a bill has already surprised someone. You're going to see it pass once, with the current volume, and fail once, with the disproportionate volume, both with real act evidence.

Connection to the module

This is the same discipline finops-and-cost-guardrails-guide Module 3, lesson 7 already applied to its own hand-built gate: a dedicated test workflow, run twice, with real Job succeeded and Job failed — never a design promise with no evidence. The difference here is mechanism, not discipline: instead of two pairs of prepared JSON files, this lesson changes a single number — --monthly-requests — between the two runs.


Analogy: the fire drill, done on purpose to confirm the alarm works

A building with smoke detectors doesn't wait for a real fire to find out if the alarm sounds — it runs scheduled drills, with controlled smoke, to confirm the whole system reacts before the real event happens. This lesson is exactly that drill: nobody waits for Andes Cargo to really receive a hundred thousand free-text manifests in a single month to discover whether the token budget cuts off in time. Instead, this lesson deliberately triggers the scenario, in a controlled, isolated environment (a workflow_dispatch workflow, never connected to a real Pull Request), exactly to confirm the alarm — lesson 4's step — really sounds before the bill exists.


Step 1 — The real tension: two documents that have to change together

Lesson 4 already warned about this: --monthly-requests 5000 in ci.yml and GENAI-COST-PROFILE.md's "Stress" row (section 4) have to stay synchronized by hand, because no automatic mechanism ties them together. Imagine the real scenario that triggers this lesson: someone at Andes Cargo, seeing that a large logistics partner announced it's permanently switching to free-text manifests (the same scenario ADR-001 already named as the trigger to reconsider the deterministic parser), proposes updating GENAI-COST-PROFILE.md with a much larger new volume assumption:

  | Scenario | Escalated manifests/month | Rationale |
  |---|---:|---|
  | **Realistic** | 40 (10% of the 400/month total) | A conservative starting estimate... |
- | **Stress** | 5,000 | Deliberately pessimistic: an order-of-magnitude larger... |
+ | **Stress** | 100,000 | A large partner permanently switches to free-text format,
+ | | | at a scale nobody validated against the deterministic parser's coverage |

This change, on its own, is just text in a document — it triggers nothing automatically. Lesson 4's CI step also has to be updated, in the same Pull Request, so the volume the gate checks matches the volume the document declares.


Step 2 — A dedicated test workflow, the same technique from cicd-and-gitops-on-aws-guide/finops-and-cost-guardrails-guide

Exactly the same reason finops-and-cost-guardrails-guide Module 3, lesson 7 already explained for cost-gate-demo.yml: this step of "change a number and see what happens" should never live inside the real ci.yml — no genuine Pull Request should ever have its real volume replaced by a test number. That's why this demonstration lives in its own file, triggered only by hand:

# .github/workflows/token-budget-demo.yml
name: token-budget-demo

on: workflow_dispatch

jobs:
  token-budget-check-demo:
    runs-on: ubuntu-latest
    steps:
      - name: Check out andes-cargo-infra
        uses: actions/checkout@v4

      - name: Enforce the token budget (scripts/bedrock_cost_estimate.py)
        run: |
          python3 scripts/bedrock_cost_estimate.py \
            --model amazon.nova-lite-v1:0 \
            --input-tokens 800 \
            --output-tokens 150 \
            --monthly-requests 5000 \
            --monthly-budget 5.00

No Infracost token, no second checkout with an explicit ref: — the two requirements finops-and-cost-guardrails-guide Module 3, lesson 6 already documented as unavailable under local act. Just a simple checkout and a direct call to lesson 4's script, without modifying it by a single line. The only thing that changes between this lesson's two runs is the --monthly-requests value inside this file — the same kind of "edit the file, run, revert" you already saw in cicd-and-gitops-on-aws-guide Module 6, lesson 7.


Step 3 — First run: the current volume (5,000), should pass

act workflow_dispatch -j token-budget-check-demo -W .github/workflows/token-budget-demo.yml

What to expect (literal — executed to write this lesson, with real Docker):

[token-budget-demo/token-budget-check-demo] ⭐ Run Set up job
[token-budget-demo/token-budget-check-demo]   ✅  Success - Set up job
[token-budget-demo/token-budget-check-demo] ⭐ Run Main Check out andes-cargo-infra
[token-budget-demo/token-budget-check-demo]   ✅  Success - Main Check out andes-cargo-infra [11.726486417s]
[token-budget-demo/token-budget-check-demo] ⭐ Run Main Enforce the token budget (scripts/bedrock_cost_estimate.py)
[token-budget-demo/token-budget-check-demo]   | Model                          amazon.nova-lite-v1:0
[token-budget-demo/token-budget-check-demo]   | TOTAL MONTHLY COST                           $0.42
[token-budget-demo/token-budget-check-demo]   ✅  Success - Main Enforce the token budget (scripts/bedrock_cost_estimate.py) [150.438625ms]
[token-budget-demo/token-budget-check-demo] ⭐ Run Complete job
[token-budget-demo/token-budget-check-demo]   ✅  Success - Complete job
[token-budget-demo/token-budget-check-demo] 🏁  Job succeeded

Job succeeded, $0.42 well under $5.00 — the same result lesson 4 already confirmed by hand, now run inside an ephemeral Docker container, with Python installed from scratch on every run, exactly the same double confirmation every gate in this ecosystem has already demonstrated.


Step 4 — Second run: the disproportionate volume (100,000), should fail

Change the file's one number, exactly as Step 1 proposed:

sed -i.bak 's/--monthly-requests 5000/--monthly-requests 100000/' .github/workflows/token-budget-demo.yml
grep "monthly-requests" .github/workflows/token-budget-demo.yml
act workflow_dispatch -j token-budget-check-demo -W .github/workflows/token-budget-demo.yml

What to expect (literal — executed to write this lesson, the same run, against the already-edited file):

[token-budget-demo/token-budget-check-demo] ⭐ Run Set up job
[token-budget-demo/token-budget-check-demo]   ✅  Success - Set up job
[token-budget-demo/token-budget-check-demo] ⭐ Run Main Check out andes-cargo-infra
[token-budget-demo/token-budget-check-demo]   ✅  Success - Main Check out andes-cargo-infra [10.297275542s]
[token-budget-demo/token-budget-check-demo] ⭐ Run Main Enforce the token budget (scripts/bedrock_cost_estimate.py)
[token-budget-demo/token-budget-check-demo]   ❗  ::error::token-budget-check failed: projected monthly cost $8.40 exceeds the $5.00 budget declared for this volume assumption (100,000 requests/month).
[token-budget-demo/token-budget-check-demo]   | Model                          amazon.nova-lite-v1:0
[token-budget-demo/token-budget-check-demo]   | TOTAL MONTHLY COST                           $8.40
[token-budget-demo/token-budget-check-demo]   ❌  Failure - Main Enforce the token budget (scripts/bedrock_cost_estimate.py) [217.134542ms]
[token-budget-demo/token-budget-check-demo] exitcode '1': failure
[token-budget-demo/token-budget-check-demo] ⭐ Run Complete job
[token-budget-demo/token-budget-check-demo]   ✅  Success - Complete job
[token-budget-demo/token-budget-check-demo] 🏁  Job failed

Job failed, exitcode '1', $8.40 above $5.00. The workflow's only step that does real work — Enforce the token budget — ended in failure, and that exit code propagated up to the whole job. In a real Pull Request, with this same step wired into cost-check as lesson 4 shows, this result stops the merge — the disproportionate volume assumption never reaches apply, exactly this module's lesson 1's promise: the gate cuts off before apply, not after the bill.


Before and after, side by side

Run 1 (Step 3)Run 2 (Step 4)
Declared --monthly-requests5,000100,000
TOTAL MONTHLY COST$0.42$8.40
Step result✅ Success❌ Failure
Job result🏁 Job succeeded🏁 Job failed
Would this change reach apply in a real pipeline?YesNo

The only difference between the two runs is the number that declares the volume — same script, same budget, same workflow. The gate doesn't know the intent of whoever proposed the change, only the number they declared: exactly the same principle finops-and-cost-guardrails-guide Module 3, lesson 7 already established for its own dollar gate.


Returning the lab to a healthy state

rm .github/workflows/token-budget-demo.yml.bak
sed -i 's/--monthly-requests 100000/--monthly-requests 5000/' .github/workflows/token-budget-demo.yml
grep "monthly-requests" .github/workflows/token-budget-demo.yml
git status

What to expect (literal):

--monthly-requests 5000 \
On branch main
nothing to commit, working tree clean

nothing to commit confirms the file went back, byte for byte, to a healthy state — the same hygiene habit cicd-and-gitops-on-aws-guide Module 6, lesson 7 and this guide's Module 5, lesson 3 already practiced.


Common mistakes

Leaving --monthly-requests at 100000 "for next time," and carrying the failure into later lessons (project hygiene). What happens: someone finishes Step 4, sees the expected Job failed, and moves on without reverting the file. How to spot it: act workflow_dispatch -j token-budget-check-demo keeps failing in later runs that should pass. How to fix it: always revert the volume to its healthy state (5000) before closing this lesson — the exact same habit this module's lesson 3 already practiced with bedrock.tf.

Changing only GENAI-COST-PROFILE.md, or only token-budget-demo.yml/ci.yml, without the other, and not noticing the divergence (the exact scenario this lesson exists to prevent). What happens: someone updates only the document, or only the YAML, thinking one of the two is enough. How to spot it: if the gate keeps passing (or keeps failing) with a result that doesn't match what the document declares. How to fix it: the two have to change together, in the same Pull Request — the exact discipline this lesson's Step 1 demonstrates with the proposed diff over GENAI-COST-PROFILE.md, followed by the corresponding change in the gate's step.

Confusing token-budget-demo.yml with the real ci.yml, and leaving it triggering on every Pull Request. What happens: someone changes token-budget-demo.yml's trigger from workflow_dispatch to pull_request, thinking this "tests the gate on every real change." How to spot it: the demo workflow starts running automatically, with a hardcoded volume that reflects no real Pull Request. How to fix it: token-budget-demo.yml exists exclusively to validate the step's logic, with a controlled value — ci.yml (lesson 4), with the real step inside cost-check, is the only workflow that should trigger on every Pull Request, always against the volume genuinely declared in GENAI-COST-PROFILE.md.


Exercises

Exercise 1 — Calculate the exact invocations/month volume at which the projected cost first crosses the $5.00 budget, with this lesson's same token sizes (800 input / 150 output). Verify your calculation by running the script with that exact volume.

See solution

The cost per invocation is (800 × $0.06 + 150 × $0.24) / 1,000,000 = $0.000084. To cross $5.00: $5.00 / $0.000084 ≈ 59,524 invocations/month. Running python3 bedrock_cost_estimate.py --model amazon.nova-lite-v1:0 --input-tokens 800 --output-tokens 150 --monthly-requests 59524 confirms a total of $5.00 (the exact limit, which still passes because the comparison is >, not >= — Module 6, lesson 4); 59525 would already exceed it by a fraction of a cent.

Exercise 2 — Explain why this lesson chose 100,000 invocations/month, and not a number closer to Exercise 1's crossing point, for the FAIL scenario. What pedagogical advantage does a number well above the crossing point have over one just barely above it?

See solution

A number well above the crossing point (100,000 versus ~59,524) leaves the result unambiguous — $8.40 versus $5.00 is a clear difference, easy to read at a glance, with no need to check decimals closely. A number just barely above the crossing point (59,525, for example) would also fail, correctly, but the error message would show a fraction-of-a-cent difference, harder to distinguish from a rounding error than from a real, meaningful budget violation. 100,000 also has a second narrative advantage: it's 25 times the original stress scenario (5,000) and 2,500 times the realistic scenario (40) — a scale jump that credibly corresponds to the real business scenario Step 1 describes (a large partner permanently migrating to free text).

Exercise 3 — Design, in prose, a third token-budget-demo.yml scenario where the volume GOES UP but the gate still passes. What condition would need to hold for that to happen, given the same $5.00 budget?

See solution

Any volume between 5,000 (the current healthy state) and 59,524 (the crossing point calculated in Exercise 1) would raise the declared volume without triggering the FAIL — for example, --monthly-requests 20000 would produce a cost of $0.06 × 4 = ... (scaling linearly, Module 2, lesson 7, Exercise 1) $1.68, still under $5.00. The general condition: as long as the projected cost, at this token size and this model, stays below $5.00, the gate passes regardless of whether the volume rose compared to the previous state — the gate never reacts to the change itself, only to the projected absolute value, the same property check-cost-threshold.sh (which does compare a delta) deliberately does NOT share with this step (which compares an absolute total, not a difference).


Summary and next step

This lesson built token-budget-demo.yml, a dedicated test workflow, triggered only by workflow_dispatch, that runs lesson 4's step — with no modification — against two --monthly-requests values. The first run, with the current volume (5,000), produced Job succeeded, $0.42. The second, with a deliberately disproportionate volume (100,000, a large partner permanently migrating to free text), produced Job failed, exitcode '1', $8.40 — real evidence, executed with act against Docker, that the budget cuts off before apply, not after the bill.

Before moving on you should be able to: explain why this demonstration lives in a separate workflow, never inside ci.yml; calculate the exact budget crossing point for any token size and model; and describe what would happen with a real Pull Request that proposed this same volume change without lesson 4's step in cost-check.

Lesson 7 steps away from code for a moment and develops, with real numbers verified at this very moment, when Provisioned Throughput — not on-demand — would be the right decision for Andes Cargo.

Resources

  1. finops-and-cost-guardrails-guide, Module 3, lesson 7 (07-hands-on-proving-the-gate.md) — the exact precedent for a dedicated test workflow with two real runs, PASS and FAIL.
  2. GitHub Actions — workflow_dispatch — official reference for the manual trigger used in token-budget-demo.yml.
  3. ADR-001-llm-as-escalation-path.md (Module 1, lesson 8 of this guide) — the source of the business scenario (a large partner migrating to free text) that motivates this lesson's disproportionate volume.
  4. This module, lesson 4 — the origin of the step and the $5.00 budget this lesson tests in both directions.