Skip to content

Project structure

Every GenLabZ project is a directory containing a spec.yaml and a set of code files. The layout is type-first: code is grouped by what it does (solution, evaluation, cleanup), not by which objective it belongs to.

Directory layout

{project_id}/
├── spec.yaml # Project definition — the single source of truth
├── content/ # Student-facing markdown content (optional)
│ ├── overview.md # Overrides spec.yaml overview field if present
│ ├── recap.md # Overrides spec.yaml recap field if present
│ ├── {objective_id}.md # Overrides objective description if present
│ └── {objective_id}_{step_id}.md # Overrides step instructions if present
├── images/ # Image assets referenced from content
├── solution/
│ └── boto3/
│ └── {objective_id}_{step_id}.py # One file per step
├── evaluation/
│ └── boto3/
│ └── {objective_id}_{step_id}.py # One file per step
└── cleanup/
└── boto3/
└── {objective_id}.py # One file per objective

What each directory contains

spec.yaml is the authoritative definition of the project — its metadata, objectives, steps, handle declarations, and platform configuration. Everything else on disk is derived from or validated against it.

content/ holds optional Markdown files that override the corresponding text fields in spec.yaml. Content files are entirely optional — a project works fine with just spec fields. They become useful when you want richer formatting, longer narrative, or images that don’t fit comfortably in a YAML string.

  • overview.md — overrides the top-level overview field; shown to students before they begin
  • recap.md — overrides the top-level recap field; shown after the student completes the project
  • {objective_id}.md — overrides that objective’s description field; provides conceptual grounding before the student starts the objective
  • {objective_id}_{step_id}.md — overrides that step’s instructions field; provides background and context for a specific step

When a content file exists, it takes precedence over the corresponding spec field. See Content files for the full authoring guide.

images/ holds image assets referenced from content files using relative paths, e.g. ![diagram](../images/architecture.png).

solution/boto3/ contains one Python file per step, named {objective_id}_{step_id}.py. Each file implements the solution for that step — creating the AWS resources the student is expected to create.

evaluation/boto3/ contains one Python file per step, also named {objective_id}_{step_id}.py. Each file checks that the resources created by the solution actually meet the project’s requirements.

cleanup/boto3/ contains one Python file per objective, named {objective_id}.py. Each file tears down all resources created by that objective’s steps, in reverse dependency order.

Why type-first layout

The layout groups files by their role (solution, evaluation, cleanup) rather than by objective. This makes it straightforward to run all solution code, all evaluation code, or all cleanup code as a batch — which is exactly what the test runner does. It also makes it easy to see at a glance whether every step has both a solution and an evaluation file.

Variations are inferred, not declared

The boto3/ subdirectory represents a code variation — the implementation approach used for that code type. Variations are inferred from the filesystem rather than declared in spec.yaml. This means you can add a new variation (e.g. cdk/) by creating the directory and files; no spec changes are needed.

Currently, evaluation is boto3 only. CLI and console-based solutions can’t be evaluated automatically, so only evaluation/boto3/ is supported.

File naming

Code files use zero-padded two-digit IDs matching the spec:

  • 01_01.py — objective "01", step "01"
  • 01_02.py — objective "01", step "02"
  • 02_01.py — objective "02", step "01"

Cleanup files use only the objective ID: 01.py, 02.py.

IDs in filenames are strings, not integers — "01" not "1".