Docker task images for Park Bench.
This repo contains composable layers and Dockerfiles for the curated Park Bench
task catalog. Each task image is assembled from reusable layers -- base content,
extensions, harness configs, and skills -- combined via a Dockerfile under
tasks/.
park-bench-task-images/
├── layers/
│ ├── base/ # core task content
│ │ ├── make-me-a-playlist/
│ │ ├── oas-generation/
│ │ └── trellette/
│ ├── extensions/ # pi agent extensions
│ │ ├── lint-guard-both-languages/
│ │ ├── lint-guard-only-python/
│ │ └── pied-pi/
│ ├── harness/ # phase config + phase skills
│ │ ├── exercise-2phase/
│ │ └── fullstack-4phase/
│ └── skills/ # domain knowledge libraries
│ ├── litestar/
│ ├── pi-extension-lint-guard-python-only/
│ ├── pi-extension-pied-pi-4-phase/
│ ├── playwright-bdd/
│ ├── react-frontend/
│ └── recommender/
├── tasks/
│ ├── oas-generation/
│ │ ├── Dockerfile
│ │ └── task-manifest.json
│ ├── make-me-a-playlist/
│ │ ├── Dockerfile
│ │ └── task-manifest.json
│ ├── trellette/
│ │ ├── Dockerfile
│ │ └── task-manifest.json
│ └── trellette-bare/
│ ├── Dockerfile
│ └── task-manifest.json
├── scripts/
├── .github/workflows/build-task-images.yml
└── Makefile
Layers live under layers/ and are organized into four categories:
Source code, tests, docs, schemas, and prompts that define a task. Each
subdirectory is a self-contained project (e.g. backend + UI + feature specs).
Copied into /task/ in the final image. The runtime task-manifest.json lives
next to each task's Dockerfile under tasks/<name>/, not inside the base layer,
so variants can share a base while specifying their own manifest (different
prompt, resources, scoring, etc.) without duplicating base content.
Runtime extensions for the pi agent. Currently includes:
- pied-pi -- the pied-pi runtime extension
- lint-guard-both-languages -- linting enforcement for Python and TypeScript
- lint-guard-only-python -- linting enforcement for Python only
Copied into /task/.pi/extensions/<name>/.
Phase configuration (harness.json) and per-phase skill definitions that
control how the harness orchestrates work. Examples:
- exercise-2phase -- planning + implementation phases
- fullstack-4phase -- planning, test-creation, implementation, documentation
Copied into /task/.pied-pi/.
Reference documentation that the agent can consult during execution. Each skill
directory contains a SKILL.md entry point and supporting docs. Examples:
- litestar -- Litestar framework patterns (DI, ORM, testing, OpenAPI)
- react-frontend -- React component patterns, TanStack, Zustand
- playwright-bdd -- Playwright BDD testing
- recommender -- recommender system patterns
- pi-extension-lint-guard-python-only -- lint-guard extension documentation
- pi-extension-pied-pi-4-phase -- pied-pi harness documentation
Copied into /task/.pi/skills/<name>/.
Every published task image must:
- inherit from the unified Park Bench iteration base image
- contain a
/taskdirectory - contain
/task/task-manifest.json-- a valid JSON file matching theTaskManifestschema withmanifest_version: 1 - keep task contents self-contained
- contain no symlinks under
task/ - define the label
io.parkbench.task-name
The manifest is the authoritative runtime contract. Park Bench resolves the
catalog image_ref, validates the manifest, extracts /task into the metadata
cache, and executes the resolved image digest.
Task images are published by GitHub Actions from tags shaped like:
task/<task-name>/<version>
Examples:
git tag task/oas-generation/v1
git push origin task/oas-generation/v1
git tag task/make-me-a-playlist/v1
git push origin task/make-me-a-playlist/v1
git tag task/trellette/v1
git push origin task/trellette/v1
git tag task/trellette-bare/v1
git push origin task/trellette-bare/v1The workflow publishes images named:
ghcr.io/<owner>/park-bench-task-oas-generation:<version>ghcr.io/<owner>/park-bench-task-make-me-a-playlist:<version>ghcr.io/<owner>/park-bench-task-trellette:<version>ghcr.io/<owner>/park-bench-task-trellette-bare:<version>
Park Bench resolves task images by pulling them through Docker and then using a repo digest, so the end-to-end local flow should push to a local registry, not just build an unpushed local image.
make registry-up
make registry-checkThis uses 127.0.0.1:5000 so Docker can pull from the local registry without a
custom TLS setup.
make build-oas TAG=dev
make smoke-oas TAG=dev
make push-oas TAG=devOr for the other tasks:
make build-playlist TAG=dev
make smoke-playlist TAG=dev
make push-playlist TAG=dev
make build-trellette TAG=dev
make smoke-trellette TAG=dev
make push-trellette TAG=dev
make build-trellette-bare TAG=dev
make smoke-trellette-bare TAG=dev
make push-trellette-bare TAG=devBuilds use docker build -f tasks/<name>/Dockerfile with the repo root as the
build context, so layer paths in COPY directives resolve correctly.
In the Park Bench repo, update the seeded catalog row to the local-registry ref. For example:
docker compose exec server python - <<'PY'
import sqlite3
conn = sqlite3.connect('/data/results.db')
conn.execute(
"""
UPDATE task_images
SET image_ref = ?, updated_at = datetime('now')
WHERE name = ?
""",
('127.0.0.1:5000/park-bench-task-oas-generation:dev', 'oas-generation'),
)
conn.commit()
conn.close()
PYThen trigger a benchmark or suite run through Park Bench normally.
To create a new task variant, add a directory under tasks/<name>/ with:
Dockerfilethat composes the layers you need. The build context is always the repo root, so COPY paths start withlayers/ortasks/.task-manifest.jsondescribing the runtime execution contract (prompt, container resources, workspace rules, scoring paths, and persistence mode). The Dockerfile must copy it to/task/task-manifest.json.
For example, a stripped-down playlist variant without skills or lint-guard:
# tasks/make-me-a-playlist-no-skills/Dockerfile
FROM ghcr.io/danballance/park-bench-iteration:0.13.0
LABEL io.parkbench.task-name="make-me-a-playlist-no-skills"
COPY layers/base/make-me-a-playlist/ /task/
COPY layers/extensions/pied-pi/ /task/.pi/extensions/pied-pi/
COPY layers/harness/fullstack-4phase/ /task/.pied-pi/
COPY tasks/make-me-a-playlist-no-skills/task-manifest.json /task/task-manifest.jsonThen add corresponding build-*, smoke-*, and push-* targets to the
Makefile following the same pattern as the existing tasks.
All task Dockerfiles use the unified Park Bench iteration base image:
ghcr.io/danballance/park-bench-iteration:0.13.0
If you need to test against local, unreleased Park Bench base-image changes, build that base image locally with the same tag before building task images.