Files
Quince 9893cdf889 Bake identity into the image; volume holds only runtime state
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>
2026-06-10 10:00:58 +02:00

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 (and bypassPermissions in 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 an allowedTools allowlist in .claude/settings.json if 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 point API_URL at a service on the host, switch the container to network_mode: host (see comments in docker-compose.yml).
  • Changing 09:00: edit WAKE_TIME in .env and docker compose up -d.