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>
This commit is contained in:
Quince
2026-06-10 10:00:58 +02:00
parent 1f4f5b1b71
commit 9893cdf889
8 changed files with 83 additions and 38 deletions
+5 -9
View File
@@ -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
+7
View File
@@ -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.
+38 -17
View File
@@ -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
+15
View File
@@ -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
@@ -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.
---
View File