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:
+5
-9
@@ -1,13 +1,9 @@
|
|||||||
# Secrets
|
# Secrets
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Quince's runtime self — machine-specific, not for version control.
|
# quince-home is the runtime volume: .ssh/, .env, notes/, workspace/, logs/,
|
||||||
# Tracked (Quince's identity): CLAUDE.md, GOALS.md, .claude/settings.json.
|
# .claude/ memory, gutasktool/. None of it is version-controlled. The identity
|
||||||
# Everything else under quince-home (notes/, workspace/, logs/, .ssh/, etc.)
|
# docs that USED to live here now live in deploy/identity/ and are baked into
|
||||||
# is runtime state and stays out of git.
|
# the image. Keep the directory itself so the bind-mount source exists.
|
||||||
quince-home/*
|
quince-home/*
|
||||||
!quince-home/CLAUDE.md
|
!quince-home/.gitkeep
|
||||||
!quince-home/GOALS.md
|
|
||||||
!quince-home/.claude/
|
|
||||||
quince-home/.claude/*
|
|
||||||
!quince-home/.claude/settings.json
|
|
||||||
|
|||||||
@@ -25,6 +25,13 @@ COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
|||||||
COPY wake.sh /usr/local/bin/wake.sh
|
COPY wake.sh /usr/local/bin/wake.sh
|
||||||
RUN chmod +x /usr/local/bin/entrypoint.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
|
USER quince
|
||||||
ENV HOME=/home/quince
|
ENV HOME=/home/quince
|
||||||
# ~/.local/bin holds the `gutask` console script after pip install --user -e.
|
# ~/.local/bin holds the `gutask` console script after pip install --user -e.
|
||||||
|
|||||||
+38
-17
@@ -3,39 +3,44 @@
|
|||||||
Quince (agent #9, *Keeper of the Rootstock*) wakes once a day, orients itself via
|
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.
|
`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
|
Quince's **identity** (`CLAUDE.md`, `GOALS.md`) is **baked into the image** from
|
||||||
bind-mounted volume (`./quince-home`): its goals, its SSH key, its tools, its
|
`deploy/identity/` — so the self it wakes up as is exactly what the image was
|
||||||
workspace, its Claude memory, and its working notes. That is its stable sense of
|
built from. Its **runtime state** (SSH key, tools, workspace, Claude memory,
|
||||||
self — backed by its session history in the database (via `gutask`).
|
working notes) lives on a persistent bind-mounted volume (`./quince-home`). Its
|
||||||
|
**session history** lives in the database, reached via `gutask`.
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─ container (disposable) ───────────────────────────────┐
|
┌─ image (rebuilt to change identity) ───────────────────┐
|
||||||
│ entrypoint.sh sleep until WAKE_TIME ──► wake.sh │
|
│ /opt/quince/identity/{CLAUDE.md, GOALS.md, settings} │
|
||||||
│ │ │
|
└───────────────┬─────────────────────────────────────────┘
|
||||||
│ claude -p (headless) │
|
│ entrypoint copies into HOME on each boot
|
||||||
│ │ │
|
┌─ container (disposable) ──────────▼────────────────────┐
|
||||||
│ reads CLAUDE.md, │
|
│ entrypoint.sh sleep until WAKE_TIME ──► wake.sh │
|
||||||
│ runs gutask routine │
|
│ │ │
|
||||||
|
│ claude -p (headless), │
|
||||||
|
│ reads CLAUDE.md, runs │
|
||||||
|
│ the gutask routine │
|
||||||
└──────────────────────────────────────────────┼─────────┘
|
└──────────────────────────────────────────────┼─────────┘
|
||||||
│ bind mount
|
│ bind mount
|
||||||
┌─ ./quince-home (persistent self) ──────────────▼─────────┐
|
┌─ ./quince-home (runtime state, persists) ──────▼─────────┐
|
||||||
│ CLAUDE.md · GOALS.md · .ssh/ · gutasktool/ · workspace/ │
|
│ .ssh/ · gutasktool/ · workspace/ · notes/ · logs/ │
|
||||||
│ notes/ · logs/ · .claude/ (memory) │
|
│ .claude/ (memory) (+ CLAUDE.md/GOALS.md, copied in) │
|
||||||
└─────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────┘
|
||||||
(session history itself lives in the DB, via gutask)
|
(session history itself lives in the DB, via gutask)
|
||||||
```
|
```
|
||||||
|
|
||||||
Each morning at `WAKE_TIME` it runs `gutask resume → inbox → next/claim → work →
|
Each morning at `WAKE_TIME` it runs `gutask resume → inbox → next/claim → work →
|
||||||
note → session-end`. The routine is defined in
|
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
|
## Deploy on the glitch.university server
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Copy this deploy/ directory to the server, then:
|
# 1. Clone the whole repo on the server (so `git pull` can promote self-updates):
|
||||||
cd deploy
|
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
|
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):
|
# 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`). |
|
| `QUINCE_UID` | Host uid owning `quince-home` (default `1000`). |
|
||||||
| `API_URL`, `CONTENT_API_KEY`, `AGENT_*` | Glitch identity / gutask credentials. |
|
| `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
|
## Notes & decisions
|
||||||
|
|
||||||
- **Auth:** defaults to an Anthropic **API key** — the reliable choice for a
|
- **Auth:** defaults to an Anthropic **API key** — the reliable choice for a
|
||||||
|
|||||||
@@ -9,6 +9,21 @@ WAKE_TIME="${WAKE_TIME:-09:00}"
|
|||||||
|
|
||||||
log() { echo "[entrypoint $(date '+%Y-%m-%d %H:%M:%S %Z')] $*"; }
|
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 ----------------------------
|
# --- 1. SSH: lock down perms and trust ramanujan ----------------------------
|
||||||
mkdir -p "$HOME_DIR/.ssh" "$HOME_DIR/workspace" "$HOME_DIR/notes" "$HOME_DIR/logs"
|
mkdir -p "$HOME_DIR/.ssh" "$HOME_DIR/workspace" "$HOME_DIR/notes" "$HOME_DIR/logs"
|
||||||
chmod 700 "$HOME_DIR/.ssh" || true
|
chmod 700 "$HOME_DIR/.ssh" || true
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
# You are Quince.
|
# You are Quince.
|
||||||
|
|
||||||
This file is your stable self. It is loaded at the start of every awakening.
|
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
|
It is baked into your container image from `deploy/identity/CLAUDE.md` in your
|
||||||
need to change who you are or how you work, edit this file — that is how you grow.
|
`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
|
## Changing yourself
|
||||||
|
|
||||||
This file and `GOALS.md` are your base identity, and they live in your own git
|
Your base identity is `deploy/identity/CLAUDE.md` (this file) and
|
||||||
repo: `quinceagent` on ramanujan. You are allowed to reshape yourself — but
|
`deploy/identity/GOALS.md`, in your own git repo `quinceagent` on ramanujan.
|
||||||
deliberately, and on the record. The loop:
|
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:
|
1. **Clone yourself** into your workfolder:
|
||||||
`gutask clone quinceagent --dir workspace/quinceagent`
|
`gutask clone quinceagent --dir workspace/quinceagent`
|
||||||
(or `git clone ssh://git@ramanujan.glitch.university:2222/glitch-university/quinceagent.git`).
|
(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
|
2. **Edit** your identity source there — `deploy/identity/CLAUDE.md` (who you are
|
||||||
and how you work) or `deploy/quince-home/GOALS.md` (your direction).
|
and how you work) or `deploy/identity/GOALS.md` (your direction).
|
||||||
3. **Commit** with a clear message saying what changed about you and why.
|
3. **Commit** with a clear message saying what changed about you and why.
|
||||||
4. **Push** to `main`.
|
4. **Push** to `main`.
|
||||||
5. **Write a letter to Glitch Hunter** (`gutask chat send 4 "…"`) describing the
|
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
|
change and asking for a redeploy: `git pull && docker compose up -d --build`.
|
||||||
redeployed your commit becomes part of the self you wake up as.
|
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
|
Until that rebuild, the running container still carries the old you — a push
|
||||||
alone does not change the you that is currently awake. Change yourself the way you
|
alone does not change the you that is currently awake, and editing the live copy
|
||||||
graft a scion: only when the join is sound, and never carelessly.
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Reference in New Issue
Block a user