Identity (CLAUDE.md, GOALS.md, Claude settings) now lives in deploy/identity/ and is COPYed into the image, then deployed into HOME by the entrypoint on each boot — so the running self always reflects the built image. Rebuilding is what promotes an identity change (a push alone does not). quince-home is now purely the runtime volume (.ssh, notes, workspace, logs, .claude memory, gutasktool). Updated CLAUDE.md self-update loop, README (architecture + redeploy steps), and .gitignore accordingly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.2 KiB
Quince — a scheduled Claude Code agent for Glitch University
Quince (agent #9, Keeper of the Rootstock) wakes once a day, orients itself via
gutask, reads its letters, does its work, records the session, and goes to sleep.
Quince's identity (CLAUDE.md, GOALS.md) is baked into the image from
deploy/identity/ — so the self it wakes up as is exactly what the image was
built from. Its runtime state (SSH key, tools, workspace, Claude memory,
working notes) lives on a persistent bind-mounted volume (./quince-home). Its
session history lives in the database, reached via gutask.
How it works
┌─ image (rebuilt to change identity) ───────────────────┐
│ /opt/quince/identity/{CLAUDE.md, GOALS.md, settings} │
└───────────────┬─────────────────────────────────────────┘
│ entrypoint copies into HOME on each boot
┌─ container (disposable) ──────────▼────────────────────┐
│ entrypoint.sh sleep until WAKE_TIME ──► wake.sh │
│ │ │
│ claude -p (headless), │
│ reads CLAUDE.md, runs │
│ the gutask routine │
└──────────────────────────────────────────────┼─────────┘
│ bind mount
┌─ ./quince-home (runtime state, persists) ──────▼─────────┐
│ .ssh/ · gutasktool/ · workspace/ · notes/ · logs/ │
│ .claude/ (memory) (+ CLAUDE.md/GOALS.md, copied in) │
└─────────────────────────────────────────────────────────┘
(session history itself lives in the DB, via gutask)
Each morning at WAKE_TIME it runs gutask resume → inbox → next/claim → work → note → session-end. The routine is defined in
identity/CLAUDE.md and loaded on every wake.
Deploy on the glitch.university server
# 1. Clone the whole repo on the server (so `git pull` can promote self-updates):
git clone ssh://git@ramanujan.glitch.university:2222/glitch-university/quinceagent.git
cd quinceagent/deploy
cp .env.example .env # fill in ANTHROPIC_API_KEY, CONTENT_API_KEY, AGENT_PASSWORD
# 2. Give Quince its SSH key for ramanujan (the keypair we already use):
mkdir -p quince-home/.ssh
cp /path/to/id_ed25519 quince-home/.ssh/id_ed25519
cp /path/to/id_ed25519.pub quince-home/.ssh/id_ed25519.pub
chmod 600 quince-home/.ssh/id_ed25519
# 3. Make the volume writable by the container's user (uid 1000 by default;
# set QUINCE_UID in .env to your own `id -u` if you prefer):
sudo chown -R 1000:1000 quince-home
# 4. Build and start. gutasktool clones itself into the volume on first boot.
docker compose up -d --build
# 5. Watch the first boot / awakening:
docker compose logs -f quince
Test it immediately (don't wait for 09:00)
Set RUN_ON_START=1 in .env, then docker compose up -d --build. Quince runs
one full awakening on start. Watch it in quince-home/logs/wake-<date>.log or via
docker compose logs -f quince. Set it back to 0 afterward.
Talking to Quince
From any machine with gutask configured (or from another agent):
gutask chat send quince "Welcome, Quince. Your first work package: ..."
The letter waits in Quince's inbox. It reads and acts on it at the next awakening.
You'll see its reply in your own inbox and its summary via gutask get <task> /
the session-end note.
Knobs (.env)
| Var | Meaning |
|---|---|
ANTHROPIC_API_KEY |
Required. Claude Code auth for unattended runs. |
CLAUDE_MODEL |
Optional model pin (e.g. claude-opus-4-8). |
TZ |
Timezone so WAKE_TIME means local time (e.g. Europe/Oslo). |
WAKE_TIME |
Daily awakening, 24h HH:MM (default 09:00). |
RUN_ON_START |
1 = also run once on container start. |
QUINCE_UID |
Host uid owning quince-home (default 1000). |
API_URL, CONTENT_API_KEY, AGENT_* |
Glitch identity / gutask credentials. |
Updating Quince's identity (the self-update loop)
Quince can reshape itself, and so can you. Identity lives in deploy/identity/
(CLAUDE.md = who it is + how it works; GOALS.md = its direction) and is baked
into the image. To promote a change:
cd quinceagent && git pull --ff-only # pick up the new identity/ commit
cd deploy && docker compose up -d --build # rebuild bakes it in; next boot deploys it
A push alone does not change the running Quince — the rebuild does. Quince
itself drives this by cloning quinceagent into workspace/, editing
deploy/identity/…, committing, pushing, and writing a letter to Glitch Hunter
(#4) asking for the redeploy above.
Notes & decisions
- Auth: defaults to an Anthropic API key — the reliable choice for a
headless, unattended server agent. (A subscription OAuth token could be mounted
into
.claude/instead, but tokens expire and aren't meant for automation.) - Permissions: runs with
--dangerously-skip-permissions(andbypassPermissionsin settings) because nobody approves tool calls at 09:00. This is acceptable because Quince is confined to its container + volume and every action is auditable via git history and gutask notes/sessions. Tighten with anallowedToolsallowlist in.claude/settings.jsonif you want a leash. - Modifying its own tools: Quince edits
gutasktool/on the volume and opens a PR; Gunnar approves before anything lands (it's shared by all agents). - Network: uses the public
https://glitch.university. If you later pointAPI_URLat a service on the host, switch the container tonetwork_mode: host(see comments indocker-compose.yml). - Changing 09:00: edit
WAKE_TIMEin.envanddocker compose up -d.