A full-stack TypeScript study app for the GitHub Certified: Agentic AI Developer (GH-600 beta) certification. It uses the OpenAI API to generate realistic, scenario-driven practice exams, lets you take them in timed Exam or self-paced Practice mode, and exports any exam to a human-readable PDF.
npm install
npx playwright install chromium # needed for PDF exportCopy the example and fill in your values:
cp .env.example .envRequired:
OPENAI_API_KEY=sk-...Optional (defaults shown):
OPENAI_MODEL=gpt-5.5
OPENAI_REASONING_EFFORT=medium
OPENAI_REVIEW_REASONING_EFFORT=high
PORT=3000OPENAI_API_KEY is server-side only and never sent to the browser.
| Command | What it does |
|---|---|
npm run dev |
Start server in dev mode at http://localhost:3000 β generation UI and publish toggle are visible |
npm start |
Start in production mode β only published exams are shown, no generation UI |
npm test |
Run Vitest unit tests |
npm run build |
TypeScript compile check (tsc) |
npm run export-pdf |
CLI: export a specific exam to PDF |
In dev mode the sidebar shows a Generate New Exam panel. Enter a question count and choose a mode (Exam or Practice), then click Generate Exam. The server:
- Builds a domain/item-type blueprint (
src/blueprint.ts) that distributes questions across the six GH-600 domains with weights close to the official exam distribution. - Calls OpenAI in batches (
src/generation.ts) β each batch targets a specific domain and question type, passing previously generated question stems to avoid duplicates. - For case-study batches, already-used narrative themes are passed in the prompt so the generator avoids repeating them.
- Assembles and persists the finished
ExamSettodata/exams/exams.json.
The UI shows live batch-by-batch progress via Server-Sent Events.
From the home screen, choose any available exam and a mode:
- π― Exam β timed (
question_count Γ 1.2minutes), answers hidden until you submit. - π Practice β untimed; correct answers are revealed immediately after each question so you can learn as you go.
After submitting an exam you see a score breakdown by domain and can review every question with full explanations (why the correct answer is right, why each distractor is wrong).
| Type | Description |
|---|---|
single_choice |
One correct answer from four options |
multi_select |
Two or three correct answers |
sequence_order |
Drag items into the correct order |
matching_magnet |
Match left-column items to right-column items |
code_or_config_artifact |
Scenario with an embedded YAML/JSON/log artifact |
case_study_child |
Question that references a shared case-study scenario |
Questions are spread across the six official GH-600 exam domains:
| Domain | Topic | Target weight |
|---|---|---|
| A | Agent architecture and SDLC processes | ~20% |
| B | MCP / tool use and environment permissions | ~22% |
| C | Memory, state, and execution | ~12% |
| D | Evaluation, telemetry, error analysis, and tuning | ~18% |
| E | Multi-agent orchestration and incident response | ~18% |
| F | Guardrails, safety, accountability, and governance | ~10% |
Every generated exam is checked for answer-position bias (src/antiBias.ts): correct answers should be roughly equally distributed across positions AβD. The generation prompt also enforces that the shortest/second-shortest option is correct β₯50% of the time and the longest option is correct β€10% of the time β countering the LLM tendency to make longer answers correct.
Each exam row in dev mode has a π Unpublished / β
Published toggle. Clicking it updates data/published.json, which controls what production users see. In production (npm start) only published exams are returned by the API.
data/published.json format:
{ "examIds": ["<uuid>", "..."] }Each exam row has two buttons:
- π Generate PDF β calls
POST /api/exams/:id/pdf, which uses Playwright/Chromium to render the full exam (with answers and explanations) as a print-quality PDF. - β¬ Download PDF β appears once the PDF exists; links directly to the file.
PDFs are saved to data/exams/ with a human-readable name:
gh-600-YYYY-MM-DD-<count>q-<id8>.pdf
# e.g. gh-600-2026-05-22-100q-a1ac29d0.pdf
You can also generate a PDF from the command line:
npm run export-pdf -- <exam-id>src/
server.ts Express server β all API routes and SSE generation stream
generation.ts OpenAI batch generation, blueprint, assemble
blueprint.ts Domain/item-type distribution logic
pdfExport.ts Playwright HTMLβPDF renderer (shared by server and CLI)
persistence.ts Read/write exams and attempts to data/
scoring.ts Score an attempt, domain breakdowns
studyLoops.ts Weak-domain drills, mistake replay helpers
antiBias.ts Answer-position and length-rank bias checks
types.ts All TypeScript types (ExamSet, PracticeQuestion, Attempt, β¦)
validators.ts Zod schemas for API request validation
config.ts Runtime config (model, effort, dev/prod flags)
public/
index.html Single-page app shell
app.js All frontend logic (exam list, question rendering, timer, review)
styles.css UI styles
prompts/
blueprint.prompt.md
generate-batch.prompt.md Main generation prompt β includes domain boundary rules,
anti-repetition instructions, and case-study theme avoidance
generate-case-study.prompt.md
validate-question.prompt.md
anti-bias-review.prompt.md
assemble-exam.prompt.md
weakness-drill.prompt.md
knowledge/
domain-A.md β¦ domain-F.md Per-domain knowledge injected at generation time
gh-600-study-guide.md
scripts/
export-exam-pdf.ts CLI wrapper around src/pdfExport.ts
generate.ts Standalone generation script (no server)
fetch-knowledge.ts Utility to refresh knowledge files
data/
exams/exams.json All generated exams (questions, case studies, anti-bias stats)
exams/*.pdf Generated PDFs (human-readable filenames)
attempts/ One JSON file per exam attempt
published.json IDs of exams visible in production
tests/
schema.test.ts Type/schema validation
scoring.test.ts Scoring logic
blueprint.test.ts Blueprint generation
studyLoops.test.ts Study loop helpers
antiBias.test.ts Anti-bias checks
fullMockOps.test.ts Full generate β score round-trip (mocked OpenAI)
e2e/exam.spec.ts Playwright end-to-end tests (run separately with Playwright)
All JSON endpoints. IS_DEV endpoints return 403 in production.
| Method | Path | Notes |
|---|---|---|
GET |
/api/config |
{ isDev, hasApiKey, examCount } |
GET |
/api/exams |
List exams. Dev: all with isPublished flag. Prod: published only. |
GET |
/api/exams/:id |
Full exam (questions + case studies) |
GET |
/api/exams/generate |
SSE stream β generates exam in batches |
GET |
/api/exams/:id/pdf-status |
{ exists, url, filename } |
POST |
/api/exams/:id/pdf |
Generate PDF (IS_DEV) |
POST |
/api/exams/:id/publish |
Toggle publish { published: bool } (IS_DEV) |
POST |
/api/exams/blueprint |
Create generation plan |
POST |
/api/questions/generate-batch |
Generate one batch |
POST |
/api/questions/validate-batch |
Validate a batch |
POST |
/api/exams/assemble |
Assemble questions into an ExamSet |
POST |
/api/attempts |
Submit an attempt; returns scored result |
GET |
/api/attempts/:id |
Fetch a stored attempt |
GET |
/healthz |
Health check |
Every push triggers .github/workflows/ci.yml, which runs three jobs:
| Job | Trigger | What it does |
|---|---|---|
| Test | every push / PR | npm ci && npm test (Vitest, 13 unit tests) |
| TypeScript build | every push / PR | npm ci && npm run build β full tsc compile check |
| Deploy to Cloud Run | push to main only |
Builds Docker image β pushes to Artifact Registry β deploys to Cloud Run β verifies /healthz |
The deploy job uses Workload Identity Federation (no long-lived SA keys checked in). It only runs after both Test and Build pass.
A one-shot setup script is provided. Run it once from any machine with gcloud and gh authenticated:
gcloud auth login
gcloud auth application-default login
gh auth login
bash scripts/gcp-setup.shThe script creates everything needed and sets all four GitHub Actions secrets automatically:
| What | Detail |
|---|---|
| Artifact Registry repo | gh-600-prep in us-central1 |
| GCS bucket | gh-600-prep-pdfs β public-read, for generated PDFs |
| Service account | gh-600-prep-deploy@exam-prep-600.iam.gserviceaccount.com |
| WIF pool / provider | github-actions / github scoped to this repo |
| Secret Manager | openai-api-key read from local .env |
Set in Settings β Secrets and variables β Actions (the setup script does this automatically):
| Secret | Value |
|---|---|
GCP_PROJECT_ID |
exam-prep-600 |
GCP_WORKLOAD_IDENTITY_PROVIDER |
Full WIF provider resource name |
GCP_SERVICE_ACCOUNT |
Deploy SA email |
GCS_BUCKET |
GCS bucket name for PDFs |
git push origin main
ββ CI: test β build β deploy
ββ docker build + push to Artifact Registry
ββ gcloud run deploy (zero-downtime rolling update)
ββ curl /healthz β must return {"ok":true}
In production (NODE_ENV=production):
- Only published exams appear (controlled by
data/published.json+ publish toggle in dev mode) - No generation panel, publish toggle, PDF generate, or PDF download buttons
- Exams and PDFs are baked into the Docker image at build time; PDFs are served from GCS
To publish an exam to production: toggle it in dev mode β commit data/published.json + data/exams/exams.json β push to main. The CI deploy bakes the new data into the image automatically.
See .github/CLOUD_RUN_DEPLOYMENT_RUNBOOK.md for operational details (key rotation, rollback, verification).
Use only these verified GitHub/Microsoft repositories as primary material. Avoid braindump sites.
| Priority | Repository | Why it matters |
|---|---|---|
| 1 | github-samples/agents-in-sdlc | Copilot Agent Mode, coding agent, SDLC collaboration β Domains A & E |
| 2 | skills/integrate-mcp-with-copilot | MCP server integration, Agent Mode, issues β PR β Domain B |
| 3 | github/github-mcp-server | Authoritative MCP server: tools, toolsets, permissions β Domain B |
| 4 | github/awesome-copilot | Custom agents, instructions, hooks, MCP references (illustrative) |
| 5 | github-samples/copilot-in-a-box | DevRel hub: samples, walkthroughs, MCP exercise |
| 6 | github/copilot-sdk | Programmable agent workflows, tool invocation, hooks β Domains A, B, D |
| 7 | skills/getting-started-with-github-copilot | Copilot basics β foundational coverage |
| 8 | github-samples/pets-workshop | Actions, Codespaces, GHAS, secure workflows |
| 9 | microsoft/mcp-for-beginners | MCP server/client patterns and fundamentals |
| 10 | microsoft/ai-agents-for-beginners | Agent design patterns: planning, tool use, multi-agent |