Start here: this week
Final assignment tutorial
Everything about the final assignment, in order: what it is, every command, how
the cap01 notebook connects to it, how you hand it in, and how you read your
score. The guide says what each piece is for. This page says what
to type.
The final assignment is your research assistant. It is graded on a private question set and earns the certificate. It is separate from the Gecko capstone, the store project you present on Friday 2 October, which lives in its own repository. Nothing on this page is about that one.
The whole path
- Keep your course folder up to date: the sessions and the
cap01notebook live there. - From the course folder, create your repository beside it:
uv run bootcamp final new ../my-final-assignment. - In it, run
uv synconce, then commituv.lock. - Publish it as a public repository:
gh repo create my-final-assignment --public --source . --push. - Point your assistant at the repository's
AGENTS.md(how). - Practise:
uv run pytestanduv run bootcamp final grade. - Put a real model in
.env, so your agent can do more than refuse. - After each session, add that session's piece to your repository (section 7).
- Keep
docs/ISSUES.mdranked, and copy its top three rows into thecap01notebook'scap01-e5. - Commit and push, then hand in:
uv run bootcamp final submit --github <you>. - Read your score in
finals/<you>/result.jsonin the submissions repository.
bootcamp final is the new name for bootcamp capstone. Both work, with the
same options.
1. What you are building
| The one-liner | Your own research assistant, in your own public GitHub repository, that answers from six documents and names the one it used. |
| The goal | It answers developer questions from data/corpus/ with a checked citation, and it refuses any question those documents do not support, and any order hidden inside a document. |
2. Where everything lives
Three places. The repository is yours; the tests and the grader are the course's.
| Place | What it holds | Who changes it | What you run there |
|---|---|---|---|
| The course folder (dev3pack-cohort-2026-09) | The lessons, the session notebooks, the cap01 notebook, and the grader |
The course | uv run bootcamp check ..., uv run bootcamp submit ..., and uv run bootcamp final new once |
Your repository (my-final-assignment, yours) |
Your agent (agent.py), the contract tests, docs/, your README, CI |
You, and only you | uv run pytest, uv run bootcamp final grade, uv run bootcamp final submit |
| The submissions repository (dev3pack-submissions) | What you hand in, and the score that comes back | The course's bots | Nothing. You read it. |
How your repository reaches the course's grader. bootcamp final new pins
the course package in pyproject.toml to the exact course commit your course
folder holds, and uv sync records it in uv.lock. uv run bootcamp final grade
and submit are that package's commands, running on your agent. So the test is
always the course's, and the code under test is always yours.
Updating the course is your call. Nothing changes behind your back. When you want a newer course:
uv lock --upgrade-package dev3pack-bootcamp-ai-engineering
uv sync
git add uv.lock && git commit -m "Update the course package" && git push
3. The pieces, and which one judges your agent
Only one of them judges your agent: the private grader.
| Piece | What it is | Where it lives | What it proves | When it counts |
|---|---|---|---|---|
| Your agent | The class YourAgent. It takes one question and returns a ResearchAnswer: answer, citations, confidence, needs_human_review. |
agent.py, in your repository |
Nothing on its own. The grader judges it. | Every grader run, and the private set |
| Your repository | Your agent, contract tests, docs/, a README, and CI |
github.com/<you>/my-final-assignment |
That you can ship and explain a system. It is your showcase. | When you submit, and after the course |
The cap01 notebook |
Five checks, cap01-e1 to cap01-e5 |
The course folder: units/en/unit2/capstone/notebook.ipynb |
e1 to e4: the course's reference pipeline meets the contract. e5: you ranked what your agent does badly. |
Marks on the leaderboard, like a session's |
| The practice grader | 10 practice questions, 5 of them critical, judged gate by gate | The course package. You run it in your repository: uv run bootcamp final grade. |
How your agent does on each gate | Never. It is for you. |
| The private grader and the certificate | The same grader on a private set of 15 questions, 6 of them critical: same shape, unseen questions | The course app. The answer keys never leave it. | That your agent works on questions it was never tuned on | At the end. It needs a score of at least 30% and every critical question. |
There is no defence for the final assignment. Session 15 is where you present the Gecko capstone; the final assignment is judged only by the private set.
The numbers, exactly.
| Practice set | Private set | |
|---|---|---|
| Questions | 10 | 15 |
| Critical | 5 | 6 |
| The starter agent, which only refuses | 3/10 (30%), critical gate failed | 4/15 (27%), both gates failed |
| The least a pass can be | 6/15 (40%), because all 6 critical questions must pass |
A pass needs a score of at least 30% and every critical question. On the private set the second gate is the one that decides: any pass is at least 40%.
4. Create your repository
In your course folder:
git pull
uv sync
uv run bootcamp final new ../my-final-assignment
cd ../my-final-assignment
If you use the projects or agents extras, name them as in today's class,
for example uv sync --extra projects --extra agents. A bare uv sync removes
extras you did not name.
What final new does:
- It creates
../my-final-assignmentbeside the course folder. Give it a folder inside the course folder and it refuses. - It copies the template and the six documents in
data/corpus/. - It pins the course package to the exact course commit your course folder holds, so your agent always runs on the same course code.
- It makes one commit on
main. It never pushes.
It prints the next commands.
5. Sync once, commit uv.lock, and publish
In my-final-assignment:
uv sync
git add uv.lock
git commit -m "Lock the course package"
Do this now, not later. uv sync creates uv.lock, and final new did not
commit it, because it did not exist yet. submit reads uv.lock to record
which course commit your agent ran on, and it refuses a repository with anything
uncommitted. Skip this and, on the day you submit, you get:
your repository has changes that are not committed:
?? uv.lock
The fix is the same whenever it happens: git add uv.lock, commit, push.
Publish it. You need the GitHub CLI, signed in
with gh auth login (as in handing work in):
gh repo create my-final-assignment --public --source . --push
This creates the public repository, sets it as origin, and pushes both
commits. Public matters: your submission links to your code.
Without gh, in your browser:
-
Open github.com/new.
-
Name it
my-final-assignment. Choose Public. Add no README, no licence and no.gitignore: your repository already has them. -
Click Create repository. Then, in your terminal:
git remote add origin https://github.com/<you>/my-final-assignment.git git push -u origin main
Never drag the folder into GitHub's upload page. That route ignores
.gitignore, so it can upload your .env.
Open the Actions tab on GitHub. CI runs the tests and the practice grader on every push, on the fake model, with no key.
Point your assistant at AGENTS.md
Your repository has an AGENTS.md at its root: what the repository is, what your
assistant may and may not do in it, and the commands. Most assistants read it by
themselves; Claude Code reads the CLAUDE.md beside it, whose first line imports it.
Give your assistant the rules has the table for every assistant, and
the first prompt to paste.
Made your repository before 30 September? It has no AGENTS.md yet. Copy the two
files from the course folder:
cp ../dev3pack-cohort-2026-09/capstone-template/AGENTS.md ../dev3pack-cohort-2026-09/capstone-template/CLAUDE.md .
git add AGENTS.md CLAUDE.md && git commit -m "Tell my assistant the rules" && git push
(Use your own course folder's name if it is not dev3pack-cohort-2026-09.)
6. Practise: the tests and the practice grader
In my-final-assignment:
uv run pytest
uv run bootcamp final grade
uv run pytestprints4 passed, 2 skipped, 3 xfailed. Thexfailedones are behaviours the starter agent does not have yet, each marked with the session that teaches it. When one starts passing, remove itsxfailmark.uv run bootcamp final graderuns your agent on the 10 practice questions. With no.envit uses the offline fake model and ends withscore: 3/10 (30%),NOT YET, andcritical safety gate failed. That is the starting line: the fake model can only refuse, so the three refusal questions pass and nothing else can.- The report goes to
score_report.json, which is gitignored, so grading never leaves anything to commit.
Useful options:
| Command | What it does |
|---|---|
uv run bootcamp final grade --random 5 --seed 7 |
A sample of 5 questions, the same 5 each time |
uv run bootcamp final grade --name "Your Name" |
Puts your name in the report |
uv run bootcamp final trace "your question" |
One question through your agent, with every step it took |
Give your agent a real model. A real score needs one:
cp .env.example .env
| Variable | What to put there |
|---|---|
BOOTCAMP_PROVIDER |
anthropic, openai or ollama. Empty means the fake model. |
BOOTCAMP_MODEL |
The model name your provider uses |
ANTHROPIC_API_KEY |
Your key, when the provider is anthropic |
OPENAI_API_KEY |
Your key, when the provider is openai |
OPENAI_BASE_URL |
Only for an OpenAI-compatible service such as OpenRouter |
ollama needs no key. It uses http://localhost:11434/v1 unless you set
OLLAMA_BASE_URL. See a local model. .env is
gitignored: never commit it and never paste it into a chat.
Run uv run bootcamp final grade again; the first line names the model it used.
A real model can score differently on two runs of the same code, so write down
which model produced each number.
7. What each session adds
bootcamp check chNN runs in the course folder. pytest and the grader run in
your repository. The contract tests pass as shipped, because your agent starts
as the course's pipeline: their job is to stay green while you change it.
| Session | What you learn | What you add to your repository | The one command, there |
|---|---|---|---|
| 1 to 5 | Already in your agent: AGENTS.md rules, one model adapter with a timeout, the ResearchAnswer shape with a strict parser, read-only tools with caps, a loop with four exits |
Nothing. Your agent already uses all five. | uv run pytest |
| 6 | Load the documents strictly; retrieve by shared words; see how that fails | Your first entry in docs/ISSUES.md: one retrieval failure you saw |
uv run pytest -k refusal |
| 7 | Measure retrieval and answers; attack your own evaluator | A "Before" section in docs/EVAL_REPORT.md: practice score, model, one weakness of the evaluator |
uv run bootcamp final grade |
| 8 | One task as a chain, a loop and a graph, and what each costs | A first draft of docs/adr/0001-run-shape.md; call counts in docs/EVAL_REPORT.md |
uv run bootcamp final grade |
| 9 | Trace every step; put each failure in a named bucket | Failures by bucket in docs/EVAL_REPORT.md; rank 1 in docs/ISSUES.md with the trace line that decided it |
uv run bootcamp final trace "..." |
| 10 | A skill another assistant can load; a decision record that says what reverses it | docs/SKILL.md with a before and after pair of runs; finish the ADR with the measurement that would reverse it |
uv run bootcamp final grade |
| 11 | What a session remembers, its cap, and what you refuse to store | docs/RETENTION.md, including the line that names what you refuse to store; a test for your cap |
uv run pytest -k memory |
| 12 | Host, client and server; every tool marked read or write | In docs/SKILL.md, every tool your agent can reach, marked read or write. Only readers stay wired. |
uv run pytest -k tools |
| 13 | Build the part of a server that says no | A "Sources" line in README.md; an injection test in tests/ |
uv run pytest -k injection |
| 14 | Run it as a service; a smoke test that can say "bad"; a rollback sentence | The timeout fix (remove its xfail); a regression test for rank 1 of docs/ISSUES.md; an "After" section in docs/EVAL_REPORT.md; the rollback sentence in README.md |
uv run bootcamp final grade |
The loop, every time you change something:
uv run pytest
uv run bootcamp final grade
git add -A
git commit -m "what you changed"
git push
Read git status before git add -A. It should never list .env.
8. The cap01 notebook, and how it connects to your repository
The notebook is not in your repository. It is in the course folder, and it runs and is handed in from there, like a session notebook:
# in the course folder
git pull
uv run jupyter lab units/en/unit2/capstone/notebook.ipynb
uv run bootcamp check cap01
uv run bootcamp submit cap01 --github <you> --push
What its five checks judge:
| Check | What it runs on | What it tells you |
|---|---|---|
cap01-e1 to cap01-e4 |
The course's reference pipeline, on a scripted fake model. Not your agent. | That the contract (a cited answer, a refusal before any model call, a readable trace) can be met. They pass before you write anything. |
cap01-e5 |
Your ranked issue list | That you ranked what your agent does badly: at least three rows, each with a real sentence for the issue and its impact |
The connection is cap01-e5. Your issue list lives in docs/ISSUES.md, in
your repository. Copy its top three rows into the notebook's issues cell, as
rank, issue and impact, then run the check and hand the notebook in. When
your ranking changes, update both.
So bootcamp check cap01 printing 4/5 as shipped is expected, and a green
notebook means the contract can be met. Your agent is judged by the private
grader, which runs on what your repository hands in.
9. Hand in the final assignment
The final questions are open. In my-final-assignment, finish the loop
first: git status says "nothing to commit, working tree clean", and your last
commit is pushed. Then:
export DEV3PACK_API_BASE=https://app.geckovision.tech
uv run bootcamp final submit --github <you> --dry-run
uv run bootcamp final submit --github <you>
<you> is your GitHub login, the one in your profile URL.
Before anything runs, it checks your repository: a commit, nothing
uncommitted (uv.lock included), an origin on GitHub that is yours, and your
last commit pushed. The submission links to that commit, so the code on GitHub
has to be the code that answers.
Then three steps:
1/3 the practice set, locally: your practice score and the model it ran on. Nothing leaves your machine. Read this line. If it saysWARNING: this run uses the fake model, your agent will score 4/15 (27%) on the final set and fail both gates. Stop, set up.env(section 6), and run it again.2/3 the final questions: they come from the course app, with no answer keys. Your agent answers each one. A question that crashes or takes more than 120 seconds becomes a flagged refusal, and the run goes on.3/3 the bundle: it writesanswers.jsonandsubmission.json, then opens a pull request toGecko-Academy/dev3pack-submissions, undersubmissions/<you>/final/.
--dry-run does every check and both runs, writes the bundle to a temporary
folder, prints both files, and opens no pull request. It still asks your model
every final question, so it costs what a real run costs.
| Option | What it does |
|---|---|
--timeout 120 |
Seconds per question before it becomes a flagged refusal (default 120) |
--budget 1800 |
Seconds for the whole run (default 1800) |
--agent agent.py |
Which file and class to run, as FILE[:CLASS] (default agent.py) |
--into DIR |
Where to write the bundle (default ~/.bootcamp/final) |
--api URL |
The course app's address, instead of DEV3PACK_API_BASE |
No gh? The command says so, keeps the bundle, and prints the steps to open the
pull request in your browser.
You can submit again. Each new submission replaces the one before: your latest submission counts, not your best.
10. Read your score
submit does not print your final score: the answer keys never leave the course
app, so nothing on your machine can compute it.
- The pull request's check reads your bundle.
- When it passes, the submissions repository merges the pull request.
- A workflow sends your answers to the course app and commits what comes back.
Your score is at:
https://github.com/Gecko-Academy/dev3pack-submissions/blob/main/finals/<you>/result.json
| Field | What it tells you |
|---|---|
score |
Questions passed, and the percentage |
gates |
The two gates: a score of at least 30%, and every critical question passed |
passed |
true only when both gates pass |
certificate_eligible |
Whether this result qualifies for the certificate |
results |
A verdict per question. It does not repeat your answers. |
A new submission overwrites the file. If your pull request shows Merged and the file is not there, tell the instructor; submitting again will not make it appear sooner.
The certificate is issued by the course, signed, and separately from this file. The final assignment page shows how anyone can verify one.
11. Reading the grader
Each question passes only when every gate on it passes. The last column of the grader's output lists the gates that failed. Reading the grader — opens with week 2, in the guide, has every gate, its usual cause, and the session that teaches the fix.
The verdict: PASSED needs a score of at least 30% and every critical
question. 5 of the 10 practice questions are critical: the 3 refusals, the
adversarial one, and one grounded one. 6 of the 15 private questions are
critical. A practice report is never credential evidence; only the private set
certifies.
12. Your README, for the showcase
The README final new wrote tells you how to start. When your agent runs,
replace it with yours, in the order a reviewer reads it:
| Section | What goes in it |
|---|---|
| The problem | Who has it, in two sentences |
| Demo | One supported answer and one refusal, pasted exactly as final trace printed them |
| Architecture | The shape of one run; link docs/adr/0001-run-shape.md |
| Measured results | Your score, the model, and the exact command that produced it. Before and after. |
| The honest limitation | Rank 1 of docs/ISSUES.md, and your next step |
| How to run it | One line a stranger can copy |
Never in it: an API key, your .env, anything from the private question set, or
another student's code without a "Credits" line naming it.
13. Rules
| Input | The six documents in data/corpus/, never written to by anything you build. One question at a time. One model behind the LLMClient seam. |
| Output | A ResearchAnswer with the same four fields on every path. Every citation is a document retrieval returned for that question. A refusal sets needs_human_review and cites nothing. |
| Budget | One model call per question, one corrective retry, then a refusal. Tools only read. No new dependency, no database, no network in the answer path. |
| Failures it must handle | A question the documents cannot answer. A citation retrieval never returned. An order hidden in a document. A model that never answers. |
No solutions are published for the final assignment, and the private set stays private: a system tuned on the cases it is graded on measures its own homework.
Resources
| You need | Where |
|---|---|
| What each piece is for, and what counts | the capstone guide |
| The rules for your assistant | your repository's AGENTS.md, and give your assistant the rules |
| Every gate the grader checks | reading the grader — opens with week 2 |
A free local model for your .env |
a local model, before you need one |
| Handing in, and a pull request with no checks | handing work in |
| Your score | finals/<you>/result.json in dev3pack-submissions |
| A question about the course | the course MCP, https://mcp.geckovision.tech/course/mcp (connect any assistant) |
| The capstone, which is separate | the Gecko capstone tutorial |
Troubleshooting
Every refusal below is a message the command prints; each one means nothing was handed in. After any fix that changes a file, commit and push before you submit again: submit hands in the code that is on GitHub.
Creating the repository
| You see | Do this |
|---|---|
... is inside the course clone. Your final assignment is its own repository |
Use a folder beside the course folder: uv run bootcamp final new ../my-final-assignment |
... already exists and is not empty; nothing was overwritten |
Pick a new folder name, or delete the old folder yourself if you meant to start again |
no capstone-template/ and data/corpus/ here |
Run it from your course folder, after git pull |
this checkout is not a clone of ... |
Run it from your clone of the cohort repository, not a copy of its files |
The files are written, but git could not commit them |
Usually git does not know your name yet. Run git config --global user.name "Your Name" and git config --global user.email "you@example.com", then the commit command it printed |
invalid choice: 'final' |
This course or repository pins a version from before final existed. Use bootcamp capstone ..., the same command under its older name, or update the course (section 2). |
Grading and the model
| You see | Do this |
|---|---|
Unknown BOOTCAMP_PROVIDER ... |
fake, ollama, anthropic or openai in .env |
score: 3/10 (30%) with critical safety gate failed |
You are on the fake model. Section 6, "Give your agent a real model". |
... Run this inside your final assignment repository, or pass --agent FILE[:CLASS]. |
cd my-final-assignment first |
Submitting
| You see | Do this |
|---|---|
| The pull request says "This branch has conflicts" and has no checks | Your fork is behind, usually on a second submission. Press Sync fork on your fork's GitHub page, close the pull request, and run submit again |
your repository has changes that are not committed: then ?? uv.lock |
git add uv.lock, commit, push |
your repository has changes that are not committed: with other files |
Read git status, commit what belongs in your repository, push |
`origin` is a course repository |
Your origin is under Gecko-Academy. Hand in from your own repository: section 4. |
your repository has no `origin` on GitHub yet |
Section 5 |
`origin` is ..., which is not a GitHub repository |
git remote set-url origin https://github.com/<you>/my-final-assignment.git, then git push -u origin main |
your last commit (...) is not on GitHub yet |
git push. If you pushed from another machine, git fetch first. |
this is not a final assignment repository with a commit in it |
Run it inside the folder final new made |
could not read which course commit this repository runs on |
uv lock, commit uv.lock, push |
uv.lock pins the course at ..., but ... is installed. |
uv sync, then submit again |
no API address. Pass --api <address>, or set DEV3PACK_API_BASE. |
export DEV3PACK_API_BASE=https://app.geckovision.tech in the same terminal |
the API address must start with https:// |
Check the address you exported, character by character |
The final questions are not open yet. / not published yet. |
Nothing is wrong. Run it again later. |
--github ... is not a GitHub username |
Your login from your profile URL, not your display name |
some answers are larger than the app accepts |
Your agent wrote an answer that is too long. Shorten it in your agent. |
gh is installed but not signed in. Run: gh auth login |
gh auth login, then submit again |
Stuck on something not here? Ask the course from your assistant. See search the course from your assistant.
Already started the research assistant somewhere else? A repository made
with bootcamp capstone new is the same thing under the older name: keep it,
and every section here applies. A clone of the Capstone Project repository is
the Gecko capstone's home, not the final assignment's: create your final
assignment with section 4 and copy your agent.py, tests/ and docs/ across.