Duck Server — Autonomous Design & Verification Schedule
Instructions for the Claude running on the server. Everything needed is in this repo.
Schedule
| Cadence | Task | Prompt file |
|---|---|---|
| Every 5 hours (00:00, 05:00, 10:00, 15:00, 20:00) | Design + verification cycle | automation/dev_cycle.md |
| Weekly — Sunday 02:00 | OpenLane synthesis / GDS check | automation/weekly_openlane.md |
Setup (run once on the server)
The server's system Python is externally-managed and has no python3-venv
package installed, with no passwordless sudo available to add it. Docs
tooling therefore lives in an in-repo venv (.venv-docs/, gitignored)
bootstrapped without relying on the missing ensurepip:
cd /path/to/duck_server
mkdir -p automation/logs
python3 -m venv --without-pip .venv-docs
.venv-docs/bin/python3 <(curl -sS https://bootstrap.pypa.io/get-pip.py)
.venv-docs/bin/pip install mkdocs mkdocs-material pymdown-extensions
crontab -e # add:
0 0,5,10,15,20 * * * cd /path/to/duck_server && automation/guard.sh dev_cycle automation/dev_cycle.md
0 2 * * 0 cd /path/to/duck_server && automation/guard.sh weekly_openlane automation/weekly_openlane.md
# build the site once so there's something to serve before the first cron run:
.venv-docs/bin/python3 scripts/gen_status.py && .venv-docs/bin/mkdocs build
If python3-venv/pip become available system-wide later, mkdocs/python3
can be used directly in place of .venv-docs/bin/... everywhere above.
Serving the site
The site is served by the duck-server-docs container in this server's main
~/docker/docker-compose.yml (alongside wiki, the home-server nav/index
site, and everything else) — not by a manually-started process, so it comes
up and stays up the same way every other service on the box does:
duck-server-docs:
image: python:3-slim
container_name: duck-server-docs
restart: unless-stopped
ports:
- "8000:8000"
volumes:
- ~/projects/ikar_github/duck_server:/app
working_dir: /app
command: python3 scripts/serve_site.py --port 8000
scripts/serve_site.py is stdlib-only, so the container needs no image build
— it bind-mounts the live repo and serves whatever site/ currently
contains. The mkdocs build still happens on the host (via the venv above,
run at setup and again at the end of every dev_cycle/weekly_openlane
run) — the container only serves the result.
cd ~/docker && docker compose up -d duck-server-docs # first start
cd ~/docker && docker compose restart duck-server-docs # after changing the compose service itself
Site is then at http://<server>:8000 — includes Status (current state + next runs) and Next Run (task queue with checkboxes). It's also listed as a tile in ~/navigation (this server's main home-page/index), so it comes up whenever that's up.
automation/guard.sh wraps each claude -p cron invocation with the weekly
run cap defined in automation/usage_budget.conf — see that file for why
(the CLI exposes no plan/session usage percentage to gate on directly). It
logs GUARD:START/GUARD:END/GUARD:SKIP lines to each job's log and
auto-resets the cap every Monday.
How the queue works
docs/next_run.md holds the task queue. Checked = eligible; the topmost checked item runs next. The user can uncheck items (in the file, or via checkboxes on the site's Status page) to override what runs. Each run removes the completed item, preserves the user's unchecked holds, and appends new suggestions (checked by default).
The Status page also has a free-text box (POST /api/queue/add in scripts/serve_site.py) so the user can type a new feature/implementation/strategy request directly on the site — it's appended to the end of the queue, checked by default, and picked up by whichever run reaches it (FIFO among checked items, unless reordered by hand in next_run.md).
Roadblocks: the Unsolved log
Not every queue item resolves in one 5-hour cycle. When a run can't get a
task to all-tests-passing, it doesn't keep retrying it unattended forever
and it doesn't merge a partial fix — it reverts to the last known-good
state and logs the roadblock in docs/unsolved/ (one file per problem, via
docs/unsolved/TEMPLATE.md): why the item was picked up, what was
encountered, and confirmation that main was left clean. The item is then
pulled out of docs/next_run.md's queue so it stops being retried — it
stays parked in Unsolved until a human reviews it, fixes it on a separate
branch, verifies the full regression is clean with no new failures, and
merges on approval. See docs/unsolved/README.md for the full workflow.
Future dev cycles check that log before picking a queue item, so they don't
blind-retry something already flagged for a human.
Rules for every automated run
- One roadmap item per run; small fully-verified increments over large unverified ones.
- Never mark work done with failing tests; revert RTL and file a
docs/issues/entry instead. - If a task turns into a genuine roadblock (not just "needs another iteration"), revert and log it in
docs/unsolved/per the workflow above instead of leaving it silently unfinished — seedocs/unsolved/README.md. - Always finish by regenerating the site:
python3 scripts/gen_status.py && mkdocs build. - Commit locally (
git add -A && git commit), do not push.