Skip to content

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 — see docs/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.