Skip to content

English · Español

Solution 01 — just manifest reference

Read only after completing ../lab/01-write-the-justfile.md and committing your attempt. Compare against this reference, not before.

Reference recipe

Append to /Justfile:

# Record a per-experiment manifest. Used by every numeric experiment from Phase 4+.
# Usage: just manifest <topic>
manifest topic:
    #!/usr/bin/env bash
    set -euo pipefail
    safe_topic=$(printf '%s' "{{topic}}" | tr -c '[:alnum:]._-' '-' )
    dir="experiments/$(date +%F)-${safe_topic}"
    mkdir -p "$dir"
    versions=$(uv run python -c "from utils.seeding import log_versions; import json; print(json.dumps(log_versions()))")
    git_sha=$(git rev-parse HEAD 2>/dev/null || echo "no-git")
    if git diff-index --quiet HEAD -- 2>/dev/null; then
      git_dirty=false
    else
      git_dirty=true
    fi
    cpu=$(awk -F: '/^model name/{print $2; exit}' /proc/cpuinfo | sed 's/^ //')
    kernel=$(uname -r)
    os=$(. /etc/os-release && echo "$PRETTY_NAME")
    cat > "$dir/manifest.json" <<JSON
    {
      "id": "$(date +%F)-${safe_topic}",
      "git_sha": "${git_sha}",
      "git_dirty": ${git_dirty},
      "seed": 42,
      "versions": ${versions},
      "hardware": { "cpu": "${cpu}", "kernel": "${kernel}", "os": "${os}" },
      "started_at": "$(date -u +%FT%TZ)"
    }
    JSON
    echo "$dir/manifest.json"

Decisions made in this reference

  • set -euo pipefail — fail loudly on any error, undefined var, or pipeline failure. Without it, a missing git would silently produce git_sha: "".
  • safe_topic — replace anything non-alphanumeric (plus ., _, -) with -. Prevents topic=foo;rm -rf . from injecting commands.
  • #!/usr/bin/env bash — the recipe is a single bash script (just supports shebangs). This is cleaner than chaining commands with &&.
  • /proc/cpuinfo — Fedora-friendly CPU model extraction. Falls back gracefully if the file isn't there (would produce empty cpu, but set -e is on so we'd fail before producing a half-manifest — acceptable).
  • started_at only, no finished_at — this recipe records the start manifest; the experiment script that consumes it updates finished_at and wall_seconds when it's done. Coupling them would force every experiment to invoke just manifest and a finish step, which is awkward.
  • Idempotency: re-running the recipe with the same topic on the same day overwrites the manifest. Defensible: a re-run is a re-run. If you want history, use a different topic.

What I'd flag in a review

  • The versions is captured outside the experiment process — so if your experiment script changes its venv state, the manifest is stale. Real record_manifest in src/utils/ will be called inside the experiment, which is correct. This recipe is a quick ad-hoc tool.
  • No pid, user, host fields. Add them if you collaborate or run on shared hardware.
  • The recipe blocks on git being available. For a no-git checkout (rare), it falls back to "no-git" — acceptable for a curriculum, not for a regulated environment.

Reference test

Create tests/test_justfile.py:

import json
import re
import subprocess
from pathlib import Path

def test_manifest_recipe(tmp_path: Path, monkeypatch) -> None:
    # The recipe writes inside the repo; we'd ideally redirect to tmp_path.
    # For a curriculum exercise, asserting "the file exists and parses" is enough.
    result = subprocess.run(
        ["just", "manifest", "lab-test"],
        capture_output=True,
        text=True,
        check=True,
    )
    path = Path(result.stdout.strip())
    assert path.exists()
    assert path.suffix == ".json"

    data = json.loads(path.read_text())
    assert data["versions"]["python"].startswith("3.11")
    assert re.fullmatch(r"[0-9a-f]{40}", data["git_sha"]) or data["git_sha"] == "no-git"
    assert isinstance(data["git_dirty"], bool)
    assert data["seed"] == 42
    assert data["id"].endswith("lab-test")

Why this test is "good enough" rather than "perfect"

A perfect test would redirect the recipe to write under tmp_path. That requires either: - A --dir parameter on the recipe (over-engineering for the curriculum). - A chdir into tmp_path and a recipe that resolves paths relative to pwd (already the case — try it).

Either is fine. The version above is a smoke test; the second is a unit test. Pick based on how much friction you'll tolerate.

Comparison checklist

When diffing your version against this one, check:

  • Did you quote {{topic}} correctly? An unquoted ${topic} allows path traversal (../../something).
  • Did you mkdir -p (no error if exists) rather than mkdir?
  • Did you set git_dirty as a real boolean in the JSON (not the string "true")?
  • Did you handle the no-git case?
  • Did you embed versions as a nested object, not a stringified JSON?

If any "no" — note it in learners/borja/phase-00/notes/justfile-notes.md and decide whether to fix. Sometimes the answer is "my way is fine for the curriculum" — that's a valid conclusion.