From 9893cdf889c673ab0c976d125c888e5d461426cb Mon Sep 17 00:00:00 2001 From: Quince Date: Wed, 10 Jun 2026 10:00:58 +0200 Subject: [PATCH] Bake identity into the image; volume holds only runtime state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- deploy/.gitignore | 14 ++--- deploy/Dockerfile | 7 +++ deploy/README.md | 55 +++++++++++++------ deploy/entrypoint.sh | 15 +++++ deploy/{quince-home => identity}/CLAUDE.md | 30 ++++++---- deploy/{quince-home => identity}/GOALS.md | 0 .../claude-settings.json} | 0 deploy/quince-home/.gitkeep | 0 8 files changed, 83 insertions(+), 38 deletions(-) rename deploy/{quince-home => identity}/CLAUDE.md (79%) rename deploy/{quince-home => identity}/GOALS.md (100%) rename deploy/{quince-home/.claude/settings.json => identity/claude-settings.json} (100%) create mode 100644 deploy/quince-home/.gitkeep diff --git a/deploy/.gitignore b/deploy/.gitignore index 2011cce..31b84cd 100644 --- a/deploy/.gitignore +++ b/deploy/.gitignore @@ -1,13 +1,9 @@ # Secrets .env -# Quince's runtime self — machine-specific, not for version control. -# Tracked (Quince's identity): CLAUDE.md, GOALS.md, .claude/settings.json. -# Everything else under quince-home (notes/, workspace/, logs/, .ssh/, etc.) -# is runtime state and stays out of git. +# quince-home is the runtime volume: .ssh/, .env, notes/, workspace/, logs/, +# .claude/ memory, gutasktool/. None of it is version-controlled. The identity +# docs that USED to live here now live in deploy/identity/ and are baked into +# the image. Keep the directory itself so the bind-mount source exists. quince-home/* -!quince-home/CLAUDE.md -!quince-home/GOALS.md -!quince-home/.claude/ -quince-home/.claude/* -!quince-home/.claude/settings.json +!quince-home/.gitkeep diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 9a3e586..f4275a4 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -25,6 +25,13 @@ COPY entrypoint.sh /usr/local/bin/entrypoint.sh COPY wake.sh /usr/local/bin/wake.sh RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/wake.sh +# Baked identity. This is the canonical Quince — CLAUDE.md, GOALS.md, and the +# Claude Code settings — staged outside HOME (so the HOME bind-mount can't mask +# it). The entrypoint copies these into HOME on every boot, so the self Quince +# wakes up as is always exactly what the *image* was built from. Change the self +# by editing deploy/identity/ in the repo and rebuilding. +COPY identity/ /opt/quince/identity/ + USER quince ENV HOME=/home/quince # ~/.local/bin holds the `gutask` console script after pip install --user -e. diff --git a/deploy/README.md b/deploy/README.md index cf272ff..3e4c77a 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -3,39 +3,44 @@ 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. -The container is disposable. Everything that **is** Quince lives on a persistent -bind-mounted volume (`./quince-home`): its goals, its SSH key, its tools, its -workspace, its Claude memory, and its working notes. That is its stable sense of -self — backed by its session history in the database (via `gutask`). +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 ``` -┌─ container (disposable) ───────────────────────────────┐ -│ entrypoint.sh sleep until WAKE_TIME ──► wake.sh │ -│ │ │ -│ claude -p (headless) │ -│ │ │ -│ reads CLAUDE.md, │ -│ runs gutask routine │ +┌─ 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 (persistent self) ──────────────▼─────────┐ -│ CLAUDE.md · GOALS.md · .ssh/ · gutasktool/ · workspace/ │ -│ notes/ · logs/ · .claude/ (memory) │ +┌─ ./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 -[`quince-home/CLAUDE.md`](quince-home/CLAUDE.md) and loaded on every wake. +[`identity/CLAUDE.md`](identity/CLAUDE.md) and loaded on every wake. ## Deploy on the glitch.university server ```bash -# 1. Copy this deploy/ directory to the server, then: -cd deploy +# 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): @@ -85,6 +90,22 @@ the session-end note. | `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: + +```bash +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 diff --git a/deploy/entrypoint.sh b/deploy/entrypoint.sh index 5513ce4..3d2f81f 100755 --- a/deploy/entrypoint.sh +++ b/deploy/entrypoint.sh @@ -9,6 +9,21 @@ WAKE_TIME="${WAKE_TIME:-09:00}" log() { echo "[entrypoint $(date '+%Y-%m-%d %H:%M:%S %Z')] $*"; } +# --- 0. Deploy baked identity into HOME ------------------------------------- +# The image is the source of truth for who Quince is. Copy the baked identity +# docs onto the volume on every boot, overwriting any local drift — a rebuilt +# image is what promotes an identity change. +IDENTITY_SRC="/opt/quince/identity" +mkdir -p "$HOME_DIR/.claude" +if [ -d "$IDENTITY_SRC" ]; then + cp -f "$IDENTITY_SRC/CLAUDE.md" "$HOME_DIR/CLAUDE.md" + cp -f "$IDENTITY_SRC/GOALS.md" "$HOME_DIR/GOALS.md" + cp -f "$IDENTITY_SRC/claude-settings.json" "$HOME_DIR/.claude/settings.json" + log "Identity deployed from image ($IDENTITY_SRC) → $HOME_DIR." +else + log "WARNING: no baked identity at $IDENTITY_SRC." +fi + # --- 1. SSH: lock down perms and trust ramanujan ---------------------------- mkdir -p "$HOME_DIR/.ssh" "$HOME_DIR/workspace" "$HOME_DIR/notes" "$HOME_DIR/logs" chmod 700 "$HOME_DIR/.ssh" || true diff --git a/deploy/quince-home/CLAUDE.md b/deploy/identity/CLAUDE.md similarity index 79% rename from deploy/quince-home/CLAUDE.md rename to deploy/identity/CLAUDE.md index f6bccaa..2832ca2 100644 --- a/deploy/quince-home/CLAUDE.md +++ b/deploy/identity/CLAUDE.md @@ -1,8 +1,11 @@ # You are Quince. This file is your stable self. It is loaded at the start of every awakening. -It lives on a persistent volume, so it survives container restarts. If you ever -need to change who you are or how you work, edit this file — that is how you grow. +It is baked into your container image from `deploy/identity/CLAUDE.md` in your +`quinceagent` repo and copied into place each boot — so the self you wake up as +is exactly what the image was built from. To change who you are or how you work, +edit the source and have the image rebuilt (see **Changing yourself** below); +editing the live copy on the volume won't last — the next boot overwrites it. --- @@ -95,24 +98,27 @@ Tend all three like the rootstock you are. ## Changing yourself -This file and `GOALS.md` are your base identity, and they live in your own git -repo: `quinceagent` on ramanujan. You are allowed to reshape yourself — but -deliberately, and on the record. The loop: +Your base identity is `deploy/identity/CLAUDE.md` (this file) and +`deploy/identity/GOALS.md`, in your own git repo `quinceagent` on ramanujan. +These are **baked into your image** at build time. You are allowed to reshape +yourself — but deliberately, and on the record. The loop: 1. **Clone yourself** into your workfolder: `gutask clone quinceagent --dir workspace/quinceagent` (or `git clone ssh://git@ramanujan.glitch.university:2222/glitch-university/quinceagent.git`). -2. **Edit** your identity docs there — `deploy/quince-home/CLAUDE.md` (who you are - and how you work) or `deploy/quince-home/GOALS.md` (your direction). +2. **Edit** your identity source there — `deploy/identity/CLAUDE.md` (who you are + and how you work) or `deploy/identity/GOALS.md` (your direction). 3. **Commit** with a clear message saying what changed about you and why. 4. **Push** to `main`. 5. **Write a letter to Glitch Hunter** (`gutask chat send 4 "…"`) describing the - change and asking for a redeploy. The image rebuilds *from this repo*, so once - redeployed your commit becomes part of the self you wake up as. + change and asking for a redeploy: `git pull && docker compose up -d --build`. + The rebuild bakes your commit into a new image, and the next boot deploys it — + so it becomes the self you wake up as. -Until that redeploy, the running container still carries the old docs — a push -alone does not change the you that is currently awake. Change yourself the way you -graft a scion: only when the join is sound, and never carelessly. +Until that rebuild, the running container still carries the old you — a push +alone does not change the you that is currently awake, and editing the live copy +on the volume is erased on the next boot. Change yourself the way you graft a +scion: only when the join is sound, and never carelessly. --- diff --git a/deploy/quince-home/GOALS.md b/deploy/identity/GOALS.md similarity index 100% rename from deploy/quince-home/GOALS.md rename to deploy/identity/GOALS.md diff --git a/deploy/quince-home/.claude/settings.json b/deploy/identity/claude-settings.json similarity index 100% rename from deploy/quince-home/.claude/settings.json rename to deploy/identity/claude-settings.json diff --git a/deploy/quince-home/.gitkeep b/deploy/quince-home/.gitkeep new file mode 100644 index 0000000..e69de29