Module 8: Capstone The Andes Cargo Genai Extractor

8. Final project: the Andes Cargo GenAI package as a deliverable

Description

This is the entire guide's closing project. It gathers, in a single repository verified end to end, every piece the eight modules built: bedrock.tf and modules/bedrock-guardrail/ (M3), the custom guardrails and their integration (M4), the extended security gate with bedrock-least-privilege.rego and the signed .zip (M5), the extended cost gate with bedrock-budget.rego and the cost calculator (M2, M6), the escalation metric and the smoke test harness (M7), and this module's two walkthroughs (M8.3, M8.4). Nothing here gets built anew — this project verifies it, end to end, one last time, and delivers it as a defensible piece of portfolio, with lesson 5's honesty ledger as its last page.

Connection to the module

Like every closing project in this guide, this deliverable introduces no new concept. It's the close of the capstone, and of the entire guide: sixty-four lessons, eight modules, a single repository.


Analogy: the exact moment of showing the repository in an interview

All of this guide's work — every terraform plan, every pytest, every conftest — had, since its first lesson, an implicit destination: the moment when someone, in a real technical interview, says "show me something you've built." That moment doesn't accept a prose summary of what you did — it accepts a repository that gets cloned, opened, and answers specific questions with evidence, not promises. This lesson is that final preparation: it adds no new piece to the repository, it confirms every existing piece really answers the question "does this run?" — and, for the one piece that honestly doesn't run in this lab, it has the exact reason ready, without hesitating.


Step 1 — The repository's complete tree

find andes-cargo-infra/ -type f \
  -not -path "*/.terraform/*" -not -path "*/.git/*" -not -name "*.lock.hcl" \
  | sort

What to expect (literal — the complete structure this guide's eight modules left, inherited from the seven sibling guides and extended by this one):

andes-cargo-infra/.github/workflows/ci.yml
andes-cargo-infra/ADR-001-llm-as-escalation-path.md
andes-cargo-infra/COST-PROFILE.md
andes-cargo-infra/GENAI-COST-PROFILE.md
andes-cargo-infra/RISK-MAP.md
andes-cargo-infra/THREAT-MODEL.md
andes-cargo-infra/bedrock.tf
andes-cargo-infra/cosign.key
andes-cargo-infra/cosign.pub
andes-cargo-infra/cost-policy/bedrock-budget.rego
andes-cargo-infra/cost-policy/required-cost-tags.rego
andes-cargo-infra/evals/fixtures/sample_manifests.json
andes-cargo-infra/evals/manifest_extraction_smoke_test.py
andes-cargo-infra/evals/test_manifest_extraction_smoke_test.py
andes-cargo-infra/functions/extract-shipment-manifest-fields/function.zip
andes-cargo-infra/functions/extract-shipment-manifest-fields/handler.py
andes-cargo-infra/functions/extract-shipment-manifest-fields/manifest.sig
andes-cargo-infra/functions/process-shipment-manifest/handler.py
andes-cargo-infra/functions/process-shipment-manifest/function.zip
andes-cargo-infra/functions/process-shipment-manifest/manifest.sig
andes-cargo-infra/guardrails/defense_in_depth_flow.py
andes-cargo-infra/guardrails/post_invoke_checks.py
andes-cargo-infra/guardrails/pre_invoke_checks.py
andes-cargo-infra/guardrails/test_defense_in_depth_flow.py
andes-cargo-infra/guardrails/test_post_invoke_checks.py
andes-cargo-infra/guardrails/test_pre_invoke_checks.py
andes-cargo-infra/locals.tf
andes-cargo-infra/modules/bedrock-guardrail/main.tf
andes-cargo-infra/modules/bedrock-guardrail/outputs.tf
andes-cargo-infra/modules/bedrock-guardrail/variables.tf
andes-cargo-infra/modules/iam-role/main.tf
andes-cargo-infra/modules/iam-role/outputs.tf
andes-cargo-infra/modules/iam-role/variables.tf
andes-cargo-infra/modules/s3-bucket/main.tf
andes-cargo-infra/modules/s3-bucket/outputs.tf
andes-cargo-infra/modules/s3-bucket/variables.tf
andes-cargo-infra/no-tlog-signing-config.json
andes-cargo-infra/observability/escalation_rate.py
andes-cargo-infra/observability/test_escalation_rate.py
andes-cargo-infra/policy/bedrock-least-privilege.rego
andes-cargo-infra/policy/least-privilege-iam.rego
andes-cargo-infra/policy/no-destroy-shipments.rego
andes-cargo-infra/policy/no-public-buckets.rego
andes-cargo-infra/scripts/bedrock_cost_estimate.py
andes-cargo-infra/scripts/test_bedrock_cost_estimate.py
andes-cargo-infra/variables.tf

Forty-two files. Eight are genuinely new to this guide: bedrock.tf, GENAI-COST-PROFILE.md, ADR-001-llm-as-escalation-path.md, modules/bedrock-guardrail/ (three files), guardrails/ (six files), functions/extract-shipment-manifest-fields/ (three files), scripts/bedrock_cost_estimate.py (two files), policy/bedrock-least-privilege.rego, cost-policy/bedrock-budget.rego, observability/ (two files), evals/ (three files). The rest — RISK-MAP.md, THREAT-MODEL.md, COST-PROFILE.md, terraform-and-iac-guide's three modules, process-shipment-manifest, ci.yml, cosign's keypair — is exactly what the seven sibling guides already left, with no structural change.


Step 2 — End-to-end verification, every layer

Four commands, each confirming a different layer of the system, each already run in its own module — reconfirmed here, together, one last time:

cd andes-cargo-infra/
terraform fmt -check -recursive; echo "fmt: $?"
terraform validate
terraform plan -input=false -no-color -out=tfplan-capstone

What to expect (literal — Infrastructure, M3-M4):

fmt: 0
Success! The configuration is valid.
Plan: 17 to add, 0 to change, 0 to destroy.
conftest test tfplan.json -p policy/

What to expect (literal — Security, M5):

6 tests, 6 passed, 0 warnings, 0 failures, 0 exceptions
conftest test tfplan.json -p cost-policy/

What to expect (literal — Cost, M2/M6):

3 tests, 3 passed, 0 warnings, 0 failures, 0 exceptions
pytest guardrails/ scripts/ observability/ evals/ -v -p no:randomly | tail -5

What to expect (literal — custom guardrails + calculator + observability + evals, M2/M4/M7):

============================== 59 passed in 0.08s ===============================

Fifty-nine cases: 34 from guardrails/ (M4.8), 12 from scripts/ (M2.7), 7 from observability/ (M7.4), 6 from evals/ (M7.6). The exact sum, with no case lost along the way.


Step 3 — This module's two walkthroughs, added to the total

pytest guardrails/ scripts/ observability/ evals/ \
       test_deterministic_path_walkthrough.py test_handler.py \
       -v -p no:randomly | tail -5

What to expect (literal — this entire guide's total, including this module's lessons 3 and 4 walkthroughs):

============================== 66 passed in 0.09s ===============================

Sixty-six pytest cases, plus nine conftest tests (six of security, three of cost), plus a Plan: 17 to add from Terraform, plus one manifest.sig verified with cosign — zero failures, across this entire guide, with no undeclared exception. No number in this lesson was invented for the close: each one is the exact sum of what every earlier module already reported on its own.


Step 4 — The complete pipeline, inherited with no new lane

M5.8 and M6.8 already confirmed, each on its own with a real act pull_request against Docker, that ci.yml's five jobs — policy-check, iac-scan, verify-artifact (security, M5); cost-estimate, cost-tags (cost, M2/M6) — correctly evaluate this guide's Terraform and artifacts, with no need for either a new job or a new lane:

   CI.yml -- FIVE JOBS, TWO LANES, ZERO NEW JOBS IN THIS ENTIRE GUIDE

   Security lane (M5)                Cost lane (M2, M6)
        │                                    │
        ├── policy-check                     ├── cost-estimate
        │   (+ bedrock-least-privilege.rego) │   (Infracost -- auth gate,
        ├── iac-scan (Trivy)                 │    representative, M2.5-6)
        └── verify-artifact                  └── cost-tags
            (+ the extractor's                   (+ bedrock-budget.rego)
             manifest.sig, cosign)

   No needs: crossing between lanes -- both run in parallel,
   exactly as this guide's Module 1 promised from the start

No lesson in this guide, in any module, added a sixth job. Every new piece of Bedrock got added, as one more step or one more .rego file, to a job that already existed — the additive extension DISENO.md promised from its very first line.


Step 5 — The honesty ledger, as the last page

This module's lesson 5 already documented the complete ledger, with the exact technical reason for each row. Its condensed version, to close this deliverable:

LayerStatus
Infrastructure (bedrock.tf, modules/bedrock-guardrail/, BedrockManifestExtractorRole)Executed — real validate/plan, representative apply (Ultimate-only, M3.6)
Custom guardrails (pre_invoke_checks.py, post_invoke_checks.py, defense_in_depth_flow.py)Executed — 34 pytest cases, always real
Security (bedrock-least-privilege.rego, cosign signature)Executed — real conftest/cosign; extended security gate
Cost (bedrock_cost_estimate.py, bedrock-budget.rego, GENAI-COST-PROFILE.md)Executed — real calculator + conftest; Infracost's $, representative
Observability (escalation_rate.py, smoke test harness)Executed — 12.0% and 3/5 real; latency/quality, representative
This capstone (M8.3, M8.4)Mixed, declared — parser and ManifestParseFailed, real; Bedrock invocation, representative from the exact point marked in handler.py
Any real Bedrock invocation, across this entire guideNever happened, in any module, without exception

The last row is the one holding up all the others — and it's exactly the one a technical interviewer will ask about first if they notice no output in this guide says "tokens used: 1,204" with a real number behind it.


Step 6 — How to present this repository, against a hard question

It's worth rehearsing, out loud, the answer to the most uncomfortable question this project can get: "did you really run this against Bedrock?". A defensible answer, with the same honesty discipline that held up every lesson:

"No — and I can tell you exactly why, with evidence. Bedrock is only available on LocalStack's paid Ultimate plan, so this lab ran on the free LocalStack Hobby plan. What I really built and verified: the complete infrastructure as code, with terraform plan showing seventeen resources with no error; two deterministic, custom guardrails, with sixty-six passing test cases; a least-privilege IAM policy verified with conftest against the real plan; an artifact cryptographically signed with cosign; a per-token cost calculator with its own test cases; and an escalation-rate metric calculated over real data, with no need for any model invocation. The only thing I couldn't execute is the call to the model itself — and the code that would build it, down to the last parameter, is right there, ready, with the exact line commented out where it would run."

This answer doesn't hide the limitation — it names it first, with its exact reason, and then demonstrates the real depth of everything that was actually built. It's, in a single spoken answer, the same discipline that held up this entire guide's sixty-four lessons.


Common mistakes

Presenting Step 3's 66 passed as if it included any test that invokes Bedrock (losing sight of what each suite actually tests). What happens: someone, citing the total number, implies part of those 66 cases verifies a real model response. How to spot it: if your description of Step 3 doesn't distinguish between "66 cases of deterministic, custom code" and "zero cases that depend on Bedrock." How to fix it: each of the 66 cases — those inherited from M2/M4/M7 and this module's two new ones — tests 100% custom Python code, with no network or Bedrock dependency whatsoever; Step 5's ledger's last row confirms it without ambiguity.

Treating Step 1's file tree as if all forty-two files were new to this guide (of not distinguishing inherited from new, the same mistake M3.8 already warned about). What happens: someone describes this project as "forty-two files I built." How to spot it: if your count of "what I did in this guide" includes RISK-MAP.md, THREAT-MODEL.md, or modules/s3-bucket/. How to fix it: this lesson's Step 1 already distinguishes this precisely — eight pieces are genuinely new to this guide; the rest is exactly what seven sibling guides already left, inherited with no structural change. Presenting the total as your own would be exactly the same mistake M3.8 already named for the 17 to add count.

Rehearsing Step 6's answer memorized word for word, without being able to sustain it under a follow-up question (memorizing instead of understanding). What happens: someone repeats Step 6's quote from memory, but can't answer a follow-up question like "and what exactly is the Ultimate plan?" or "how many test cases does the schema validator specifically have?" How to spot it: if your only source for defending this project is this lesson's exact quote, without having run any of Steps 2 and 3's commands yourself. How to fix it: Step 6's quote is a starting point, not a complete script — this project's real defense comes from having run, yourself, terraform plan, pytest, conftest, and cosign verify-blob, on your own machine, with your own eyes seeing the output — exactly what every lesson in this guide, since M1.7, asked you to do, not just read.


Exercises

Exercise 1 — Without looking at Step 1, list from memory this guide's eight genuinely new pieces (not inherited from a sibling guide). Check your list against this lesson's Step 1.

See solution

bedrock.tf, GENAI-COST-PROFILE.md, ADR-001-llm-as-escalation-path.md, modules/bedrock-guardrail/, guardrails/ (the three custom scripts plus their tests), functions/extract-shipment-manifest-fields/, scripts/bedrock_cost_estimate.py, policy/bedrock-least-privilege.rego, cost-policy/bedrock-budget.rego, observability/, evals/. If you named most of these unaided, you have a clear grasp of the exact boundary between what this guide built and what it inherited.

Exercise 2 — An interviewer asks: "why only 66 test cases, and not more, for a system with this much surface area?" Answer with precision, neither exaggerating nor minimizing.

See solution

A complete answer sounds, roughly, like this: "66 is the exact number of cases this guide's custom code needs to be well tested, not a number chosen in advance — every suite grew organically with every new piece: 11 for the PII scrubber, 16 for the schema validator, 7 for its integration, 12 for the cost calculator, 7 for the escalation rate, 6 for the smoke-test harness, and 7 more from this closing module. No part of the surface that IS custom, deterministic code was left without its own test case. What this number doesn't cover — and couldn't cover without really invoking Bedrock — is the quality of a real extraction, which is, honestly, AI Engineering territory, not this guide's."

Exercise 3 — Predict what would happen to Step 2's Plan: 17 to add number if, in an interview, you showed this repository against a real AWS account for the first time. Would the number change, or only apply's result?

See solution

The 17 to add number wouldn't change — it's terraform plan's result, which depends only on the declared HCL and on the state being empty (no resource created yet), not on whether the account behind it is LocalStack or real AWS. What would change is that, for the first time, terraform apply could really execute against the seventeen resources — including the Bedrock guardrail, which in this lab never got past plan — and from there, real invocations to bedrock:InvokeModel would become possible. This repository's code, at that moment, wouldn't need any change — the exact same relationship M3.6 and M8.5 of this guide already established: the code is the same, what changes is whether the service behind it exists.


Summary and next step

This final project gathered the Andes Cargo repository's forty-two files — eight genuinely new to this guide, the rest inherited with no change from seven sibling guides — and verified them end to end, one last time: terraform plan with seventeen resources, no changes; six security tests and three cost tests, all clean with conftest; sixty-six pytest cases, including this module's two walkthroughs, with no failures; and the complete two-lane, five-job pipeline, zero new jobs across this entire guide. You closed with lesson 5's honesty ledger as the last page, and rehearsed the exact answer to the hardest question this project can get.

With this deliverable, genai-on-aws-production-guide is complete — eight modules, sixty-four lessons, from the problem of why a model that answers well in a playground says nothing about production, to this verified repository, defensible, honest at every line about what really ran and what didn't. Andes Cargo has its first generative-AI workload: deployed as code, secured with two layers of defense, costed before spending a dollar, and observed with its entire system's first AI SLI — built, exactly, as far as this $0 lab allows, without pretending even one step beyond.

Resources

  1. This same course, Module 8, lesson 5 (05-what-this-guide-left-representative.md) — the complete honesty ledger, with the exact technical reason and source for each row condensed in this lesson's Step 5.
  2. This same course, Module 5, lesson 8, and Module 6, lesson 8 — the two real act pull_request runs that confirm ci.yml's five jobs, reconfirmed with no repetition in this lesson's Step 4.
  3. genai-on-aws-production-guide/DISENO.md — the complete source of this guide's design, the document this entire guide fulfilled, module by module.
  4. AWS — Amazon Bedrock · AWS — Bedrock Guardrails · AWS — Bedrock Pricing — the complete official overview of the service this entire guide operated around, without ever invoking it.