flowchart LR A[README] --> B[Reveal a route] A --> C[Replace the legend] A --> D[Why seeds repeat] A --> E[Public API] B --> F[Quarto field guide] C --> F D --> F E --> F F --> G[Verified deployment]
Unit Challenge: Publish the Puzzle Garden Field Guide
1. Reopen the Puzzle Garden
The Puzzle Garden’s code still works, but its field guide does not. The quick start calls a retired function, the expected path is stale, the public docstring hides an invalid-clue failure, two pages are missing from navigation, and the workflow publishes source instead of the rendered site.
Your goal is to publish a small field guide that lets another player:
- understand what the package reveals;
- prepare the lesson project and reveal one exact path;
- replace the clue legend for a new theme;
- understand why the same seed repeats the same path;
- look up the public call and anticipated failure; and
- trust that the examples, links, pages, and deployment artifact were checked.
The satisfying result is visible. The quick start should reveal:
Each reader path must reach a checked source page, and every page must reach the same rendered field guide artifact.
The package behavior, file names, public signature, initial page headings, and progressive checks are supplied. Your central work is deciding what each reader needs, repairing public promises, and connecting the checked artifact to publication.
2. Inspect the supplied project
Begin with this tree:
puzzle-garden/
├── README.md
├── pyproject.toml
├── src/puzzle_garden/
│ ├── __init__.py
│ └── trail.py
├── tests/
│ ├── test_trail.py
│ └── test_documentation.py
├── docs/
│ ├── _quarto.yml
│ ├── index.qmd
│ ├── first-route.qmd
│ ├── customize-legend.qmd
│ ├── why-seeds-repeat.qmd
│ └── api.qmd
└── .github/workflows/docs.ymlThe public package implementation already passes its ordinary unit tests. Do not redesign its algorithm. Documentation changes may improve the public module/function docstrings, examples, README, QMD pages, checks, and workflow.
The source uses three clue symbols:
| Symbol | Default location |
|---|---|
L |
Lantern lilies |
M |
Moss arch |
B |
Blue gate |
A seed rotates the explicit clue sequence. It does not use global randomness or remember an earlier call.
Acceptance example
Changed legend:
Important failure:
3. Start from the contract
Create src/puzzle_garden/trail.py from this supplied implementation. Complete its docstring; do not change the observable algorithm to make stale prose pass.
from collections.abc import Mapping
DEFAULT_LEGEND = {
"L": "Lantern lilies",
"M": "Moss arch",
"B": "Blue gate",
}
def reveal_path(
clues: str,
*,
seed: int = 0,
legend: Mapping[str, str] = DEFAULT_LEGEND,
) -> tuple[str, ...]:
"""Return decoded clue names rotated by a repeatable seed.
TODO: Document parameter meaning, returned order, empty/unknown clue
failures, and two deterministic examples.
"""
symbols = clues.split()
if not symbols:
raise ValueError("clues cannot be empty")
for symbol in symbols:
if symbol not in legend:
raise ValueError(f"unknown clue: {symbol}")
offset = seed % len(symbols)
ordered = symbols[offset:] + symbols[:offset]
return tuple(legend[symbol] for symbol in ordered)Export the public names in src/puzzle_garden/__init__.py:
The starter README currently contains these defects:
The starter Quarto sidebar lists only index.qmd, first-route.qmd, and api.qmd. The starter workflow ends with publish_dir: docs.
4. Build the field guide in small stages
Stage A — Repair the project entrance
Write a README with:
- a specific purpose and Python 3.10+ context;
- a truthful preparation/install path for the supplied source project;
- a complete public import and seed-1 quick start;
- the exact Moss arch → Blue gate → Lantern lilies result;
- limitations and support sections;
- descriptive relative links to the four deeper reader pages.
Stage B — Give every page one job
Finish these pages:
first-route.qmd: a guided path using supplied clues and seed;customize-legend.qmd: a focused task for replacing every used symbol;why-seeds-repeat.qmd: explanation of rotation over explicit input and no hidden state;api.qmd: exact signature, parameters, return, andValueErrorcases.
Use informative titles, visible prerequisites, expected output, meaningful link text, and recovery close to the failure it repairs.
Stage C — Make examples executable
Complete the public docstring with one ordinary example and the unknown-clue failure. Ensure doctest reports 2 attempted, 0 failed. Keep the custom-legend boundary in pytest where a structured tuple assertion is clearer.
Stage D — Build and connect the site
Repair _quarto.yml so all five QMD pages appear in reader order. Render from a clean output directory and verify all five HTML files. Repair every local source link before publication.
Stage E — Publish only checked output
The workflow should run ordinary tests and doctests, render the complete site, verify the HTML inventory, and publish docs/_site only after success on the accepted branch. Never commit a personal token.
5. Run progressive assertions
Run these stages in order. Keep the expected values unchanged unless you can explain an approved contract change.
Check 1 — Public behavior
from puzzle_garden import reveal_path
assert reveal_path("L M B", seed=1) == (
"Moss arch",
"Blue gate",
"Lantern lilies",
)
assert reveal_path("L M", seed=0) == (
"Lantern lilies",
"Moss arch",
)
try:
reveal_path("L X")
except ValueError as error:
assert str(error) == "unknown clue: X"
else:
raise AssertionError("unknown clue was accepted")Check 2 — Discovered examples
Check 3 — Real README and source pages
from pathlib import Path
root = Path(".").resolve()
readme = (root / "README.md").read_text(encoding="utf-8")
for heading in (
"## Requirements",
"## Installation",
"## Quick start",
"## Limitations",
"## Support",
"## Learn more",
):
assert heading in readme, f"README missing {heading}"
assert "from puzzle_garden import reveal_path" in readme
assert "seed=1" in readme
assert "('Moss arch', 'Blue gate', 'Lantern lilies')" in readme
source_pages = {
"index.qmd",
"first-route.qmd",
"customize-legend.qmd",
"why-seeds-repeat.qmd",
"api.qmd",
}
assert source_pages == {path.name for path in (root / "docs").glob("*.qmd")}Check 4 — Local README targets
Then run the broader source-link checker from Lesson 5 so links inside QMD pages are also resolved relative to their source.
Check 5 — Clean rendered artifact
from pathlib import Path
site = Path("docs/_site")
expected_html = {
"index.html",
"first-route.html",
"customize-legend.html",
"why-seeds-repeat.html",
"api.html",
}
observed_html = {path.name for path in site.glob("*.html")}
assert expected_html <= observed_html, sorted(expected_html - observed_html)Check 6 — Local/CI contract
Confirm the workflow contains the same responsibilities in this order and that its deployment input is docs/_site.
Do not replace file inspection with README_HEADINGS = [...] and then assert the list against itself. The checks must read the artifact the learner will publish.
6. Use the hint ladder only when needed
Hint 1
Anchor the README on the seed-1 route. State the project-root context, import reveal_path, show the exact tuple, and link each later reader question to its own page.
Hint 2
For the docstring, document clues, keyword-only seed, and legend; explain rotation and the returned tuple; include the ordinary call plus a traceback ending in ValueError: unknown clue: X. Verify attempted count as well as failures.
Hint 3
List all five QMD paths in website.sidebar.contents. Render from docs/ or target the docs project from root. Publish docs/_site only after pytest, doctest, render, and page-inventory checks pass.
7. Keep debugging evidence
Preserve one real failing check before repairing it. Use the earliest useful evidence rather than the final symptom.
| Failure | Exact evidence | One hypothesis | Controlled change | Verified rerun |
|---|---|---|---|---|
| What command/check failed? | Expected, observed, file/line/path | Which single cause fits? | What one source changed? | Which unchanged checks now pass? |
Good candidates include:
- zero doctest attempts because the transcript prompt is malformed;
- expected default-order output while the quick start passes
seed=1; customize-legend.qmdmissing from the sidebar;- a README link to retired
guide.qmd; - a clean render missing
api.html; or - workflow
publish_dirpointing at sourcedocsinstead ofdocs/_site.
Do not use “changed everything and it worked” as the record. A reader should be able to connect the evidence to your one repair.
8. Compare with a complete solution path
Reveal after your checks pass or all three hints are exhausted
A complete public docstring can be:
from collections.abc import Mapping
DEFAULT_LEGEND = {
"L": "Lantern lilies",
"M": "Moss arch",
"B": "Blue gate",
}
def reveal_path(
clues: str,
*,
seed: int = 0,
legend: Mapping[str, str] = DEFAULT_LEGEND,
) -> tuple[str, ...]:
"""Return decoded clue names rotated by a repeatable seed.
Args:
clues: Space-separated clue symbols.
seed: Integer offset selecting the first clue in the returned route.
legend: Mapping from clue symbols to visitor-facing location names.
Returns:
Decoded location names rotated by `seed`.
Raises:
ValueError: If `clues` is empty or contains an unknown symbol.
Examples:
>>> reveal_path("L M B", seed=1)
('Moss arch', 'Blue gate', 'Lantern lilies')
>>> reveal_path("L X")
Traceback (most recent call last):
...
ValueError: unknown clue: X
"""
symbols = clues.split()
if not symbols:
raise ValueError("clues cannot be empty")
for symbol in symbols:
if symbol not in legend:
raise ValueError(f"unknown clue: {symbol}")
offset = seed % len(symbols)
ordered = symbols[offset:] + symbols[:offset]
return tuple(legend[symbol] for symbol in ordered)A complete README entry path can be:
# Puzzle Garden Field Guide
Reveal repeatable routes through a tiny symbolic garden puzzle.
## Requirements
Use Python 3.10 or newer and run commands from the project root.
## Installation
For this supplied source project, install in editable mode:
```text
python -m pip install -e .
```
## Quick start
```python
from puzzle_garden import reveal_path
print(reveal_path("L M B", seed=1))
```
Expected result: `('Moss arch', 'Blue gate', 'Lantern lilies')`.
## Limitations
Every clue must exist in the selected legend. The package stores no route state.
## Support
Report the clue string, seed, complete error, and Python version.
## Learn more
- [Reveal your first garden route](docs/first-route.qmd "first route")
- [Replace the clue legend](docs/customize-legend.qmd "customize legend")
- [Understand why seeds repeat](docs/why-seeds-repeat.qmd "seed explanation")
- [Look up the public API](docs/api.qmd "API reference")The complete site configuration can be:
project:
type: website
output-dir: _site
website:
title: Puzzle Garden Field Guide
page-navigation: true
search: true
sidebar:
contents:
- href: index.qmd
text: Start Here
- href: first-route.qmd
text: Reveal a Route
- href: customize-legend.qmd
text: Replace the Legend
- href: why-seeds-repeat.qmd
text: Why Seeds Repeat
- href: api.qmd
text: Public API
format:
html:
toc: trueEach QMD file still needs its own content. A concise complete set includes:
---
title: Reveal Your First Garden Route
---
## Prepare the project
Use Python 3.10+ and the editable installation from the README.
## Reveal the path
Run `reveal_path("L M B", seed=1)` and confirm the exact Moss arch, Blue gate,
Lantern lilies tuple.
## Recover from an unknown clue
Read the symbol named by `ValueError`, then add it to the legend or correct the
clue string.Finally, the workflow’s gate and publication boundary should read clearly:
- name: Test code and documentation
run: pytest --doctest-modules src tests -q
- name: Render field guide
run: quarto render docs
- name: Verify rendered pages
run: python tests/check_rendered_docs.py
- name: Publish verified field guide
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/_siteCompare reader outcomes, public contracts, checks, and artifact paths—not only sentence order. A different site structure is sound when it satisfies the same observable promises.
9. Check the field guide decisions
10. Decide whether the challenge is complete
Use the evidence rather than page length:
| Evidence | Ready to record when |
|---|---|
| Reader entrance | A clean learner can prepare the project and reproduce the seed-1 route using only README. |
| Page purposes | Tutorial, how-to, explanation, and API pages answer distinct questions and link meaningfully. |
| Public API | help()/docstring state inputs, rotation result, and both ValueError boundaries. |
| Executable examples | Exactly two intended doctest attempts pass; structured boundaries remain in pytest. |
| Site artifact | A clean render produces all five named HTML pages with repaired navigation and links. |
| Publication | CI repeats the local gate and publishes only the verified _site artifact. |
| Debugging | One record connects exact evidence to one hypothesis, controlled change, and clean rerun. |
This button stores a self-reported marker only in this browser. It does not submit work, grade it, verify identity, or issue a certificate.
Not yet recorded.
Key points
- Documentation is part of the Puzzle Garden result: it lets another player reveal and understand the route without the author’s hidden state.
- Progressive checks inspect real README, docstring, QMD, HTML, and workflow artifacts rather than stand-in lists.
- A reader-focused page, executable example, resolved link, clean render, and verified deployment each contribute different evidence.
- The strongest solution keeps the public API stable while repairing the promises around it.