Epic 10 — Persona-as-Files#1
Merged
Merged
Conversation
Move shelldon's character out of the hardcoded SYSTEM_INSTRUCTION into bot-editable markdown in the writable memory tree (v1/openclawgotchi worktree model, rebuilt under v2 invariants). Adds the Epic 10 design, the epics.md breakdown, and sprint-status rows. Appended only — Epics 1-9 untouched (git numstat +116/-0 on the two existing files). Decisions locked 2026-06-25: - everything -> files; bot edits via the op path (AD-5), never raw write_file - BOT_INSTRUCTIONS fully bot-writable but parse-guarded - DIRECTIVE bot-editable only via Epic 9.3 owner-approval (never autonomous) - persona prefix caching = 2 provider surfaces (anthropic cache_control + openai auto) - VAULT.md NOT ported (shelldon vault/ = OS-isolated secrets) Story specs not yet created; next = create-story 10-1.
…(Story 10.1) Move shelldon's character out of the hardcoded SYSTEM_INSTRUCTION constant into seed markdown the worker reads every turn (v1 worktree-prompt model). - shelldon/persona/ ships pristine seed templates: BOT_INSTRUCTIONS.md (verbatim system text), empty SOUL/IDENTITY/USER (filled by onboarding 10.4) - CuratedMemory seeds missing persona files copy-if-absent on init (faces.FaceRegistry.load idiom), atomic, fail-soft, never raises - read_instructions/soul/identity/user accessors (mirror read_about) - gather_context reads + assemble_prompt injects persona in binding AD-6 order (system -> directive -> identity -> soul -> user -> about -> ...) - SYSTEM_INSTRUCTION constant DELETED; seed_instructions() is the canonical source - per-file fail-soft (_safe_read): a corrupt persona file degrades only its own section, never siblings (AC6) - char-budgeted per section (_bounded_text, 8000) Golden test proves day-one byte-parity with the prior hardcoded prompt. 782 passed / import-linter green (core stays LLM-free) / persona ships in wheel.
…ies 10.2, 10.3)
Two sequential Epic 10 stories, developed back-to-back in the worktree and
shipped together (entangled in core/memory.py, core/runtime.py, and the persona
op tests).
Story 10.2 — bot-writable persona via memory-ops + awareness + gated directive:
- Four autonomous rewrite ops (rewrite_soul/identity/user/instructions), mirror
rewrite_about: frozen tagged structs, route through CuratedMemory.apply_memory_op,
atomic temp+fsync+replace, empty content rejected.
- rewrite_instructions validate-on-apply guardrail: a rewrite dropping THOUGHT:/
FACE:/the ```ops fence is rejected (logged, no-op) so the bot can re-voice but
not break parse_reply.
- BOT_INSTRUCTIONS gains a "Your self-knowledge files" awareness section advertising
every rewrite op (incl. the previously-latent rewrite_about).
- rewrite_directive is owner-approval-gated (RISKY-tier, rides the 9.3 approve/deny
plumbing): proposable but NOT a MemoryOp, parked not applied, core applies on
Approve (no worker resume — AD-5), barred on unattended dream turns.
- Wire-bug fix: the ops-block regex closed at a nested ```ops inside rewrite_instructions
content — anchored the closing fence to line-start.
Story 10.3 — proactive & dream prompt prose to files + autonomous dream self-edit:
- Proactive/dream prompt copy moved out of hardcoded constants into editable seed
files (persona/HEARTBEAT.md, persona/DREAM.md), seeded copy-if-absent into the
worktree and read at dispatch. core/proactive.py stays pure: builders take the
template text and fill it; file I/O lives in the dispatch driver + memory seed.
- Degrade-safe: missing/blank/malformed template (incl. a dropped {lines} or
{feeling_sentence} slot) logs and falls back, never raises.
- DREAM.md invites autonomous SOUL/IDENTITY/USER edits via the 10.2 ops on the
dream cycle (no chat instruction); directive still barred by the 10.2 owner gate.
- Proactive build is byte-identical to the prior constant (golden no-op test).
812 passed (+38 across test_persona_ops/test_proactive/test_memory) /
import-linter 3 contracts KEPT (core stays LLM-free) / 0 new deps / no SCHEMA_VERSION bump.
…Story 10.4) The worker injects a warm first-run interview directive while USER.md is blank, so the empty SOUL/IDENTITY/USER seeds get populated from a real conversation instead of source edits, and the bot never re-interrogates the owner once it knows them. - shelldon/persona/BOOTSTRAP.md: new seed directive (LLM-facing prose, no hardcoded onboarding copy in any .py). - core/memory.py: BOOTSTRAP.md added to _PROMPT_TEMPLATE_SEED_FILES (copy-if-absent like HEARTBEAT/DREAM); read_bootstrap() accessor. - worker/prompt.py: gather_context gates on the USER-blank monotonic sentinel (Story 10.2 rejects empty rewrite_user, so a filled USER never reverts) and reads BOOTSTRAP fail-soft + char-budgeted; assemble_prompt injects a "# First-run onboarding" section right after the system block. No runtime.py/state.py/contracts change, no new op, no new dep. Full trigger->populate->stop cycle proven with a fake provider (no live LLM). 820 tests pass (+8), import-linter 3 contracts KEPT. Known/deferred (logged in deferred-work.md): onboarding also injects on proactive/dream turns while USER is blank (spec accepts as harmless; a proper fix needs fork-boundary turn-type plumbing the story avoids).
…n (Story 10.5) Epic 10 cost + deployment close-out — caching the always-injected persona, loading heavy reference docs only when relevant, and landing the new files non-destructively on the already-deployed Pi. - AC1: persona prefix kept byte-stable (free OpenAI-surface + native-Claude-auto caching); 3 guard tests catch a future per-request interpolation (the silent cache invalidator). - AC2: per-turn Anthropic cache-signal logging (_log_cache_usage, both egress paths, getattr-guarded for GLM). Explicit cache_control breakpoint DEFERRED (spec-allowed): the persona ships in a single content string, so a breakpoint needs a worker-emitted marker every provider surface must strip — beyond the timebox. Findings note + deferred-work record it; no silent cap. - AC3: lazy-load TOOLS.md / ARCHITECTURE.md by keyword (_needs_reference, pure, injected after the cached prefix), seeded copy-if-absent, no rewrite op. - AC4: Pi migration verified — seeds git-tracked + present in the built wheel + copy-if-absent non-destructive; setup-pi.sh and pyproject.toml need no change. - AC5: core stays LLM-free, worker read-only, single-writer, fork no-accumulation. Code review (3 adversarial layers): dropped ambiguous _ARCH_KEYWORDS bare words (pi/ram/screen/cpu/raspberry → "raspberry pi" phrase + negative test); migration test asserts all 9 seeds; _prefix_of hardened. 839 tests pass, import-linter 3 KEPT, 0 new ops/deps, no runtime/state/contracts change.
Clean "data-not-code" refactor: 782→839 tests, 0 deps all 5 stories, ops only in 10.2, import-linter 3 KEPT every story, core stayed LLM-free. Dominant theme: fail-soft file-boundary behavior was the whole risk surface and nearly every review patch — the broad-except/silent-degrade class Epic 9's retro flagged, now proven to generalize (still bit us 4/5 stories). 4 action items recorded in deferred-work.md: 1. (immediate) fix 10.4 OPEN AC5 log gap + formal-review + close 10.4/10.5 review→done 2. operationalize the fail-soft-boundary pre-review checklist 3. harden the untracked-seed guard (git-test skips without git) 4. live-validate Epic 10 on the Pi (onboarding→persona-rewrite→lazy-load→cache signal) Epic 11: TBD. epic-10-retrospective marked done in sprint-status.
…5 done) Epic 10 retro Action 1. read_bootstrap caught the decode error itself, so the worker's _safe_read except never fired and a corrupt BOOTSTRAP.md degraded SILENTLY — unmet 10.4 AC5 "onboarding section omitted (logged)". Added a log.warning on the corrupt-read path; the corrupt-bootstrap test now asserts the caplog warning fires. Closes 10.4's last open review patch. Flips 10.4 + 10.5 review→done and epic-10 →done. All 5 Epic 10 stories complete + reviewed + retro'd. 839 tests pass, import-linter 3 KEPT.
Live-Pi validation of Epic 10 surfaced the flaw: SOUL/IDENTITY shipped BLANK (10.1 byte-parity premise) and were only opportunistically filled by onboarding — so a fresh bot booted with no soul or sense of self. A bot should be born with both and evolve them; onboarding's job is to learn the OWNER, not conjure the bot's identity from a 2-message chat. - SOUL.md / IDENTITY.md seeds now ship with starter content (baseline personality + self-facts, complementary to BOT_INSTRUCTIONS). USER.md stays blank — it's the per-owner profile and the onboarding trigger (10.4 latch unchanged). - BOOTSTRAP.md refocused on the owner; frames soul/identity as EVOLVE-not-replace. - BOT_INSTRUCTIONS: persona files are edited via rewrite_* ops, never the write_file tool (which is workspace-jailed) — fixes the live double-write finding; and carry forward what's still true (clobber nudge). - Tests updated to the new day-one shape (abandons the obsolete byte-parity premise). 839 pass, import-linter 3 KEPT. No .py/runtime/dep change. Also logs the live-validation findings to deferred-work (GLM cache passthrough resolved: read=0/creation=None; write_file double-write; rewrite full-replace clobber risk).
…idates Action 4 (live-validate) FULLY validated on the Pi: onboarding completed via the rewrite_user op (no write_file), gate latched off, GLM cache read=0 across turns. Mark write_file-nudge + starter-templates DONE (ff4c4c1). Epic 11 candidates: (1) label injected prompt sections with their file+op so the model isn't inferring where to write; (2) structural clobber guard for rewrite_* full-replacement.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves shelldon's character out of the hardcoded
SYSTEM_INSTRUCTIONconstant into bot- and owner-editable markdown under~/.shelldon/memory/(the v1 worktree model, rebuilt under v2 invariants). All 5 stories done, reviewed, retro'd, and live-validated on the Pi.What's in it
rewrite_*ops;BOT_INSTRUCTIONSparse-guarded;DIRECTIVEowner-approval-gatedHEARTBEAT/DREAMfiles + autonomous dream editsBOOTSTRAP.md(USER-blank monotonic sentinel)cache_controlbreakpoint deferred, spec-allowed) + keyword lazy-load ofTOOLS/ARCHITECTURE+ verified non-destructive Pi migrationLive validation (on
gotchi, vs live GLM)USER.mdvia therewrite_userop (notwrite_file).cache_read_input_tokens(stays0, no auto-cache) and omitscache_creation_input_tokens— lazy-load + budget posture stands, no silent cap.SOUL/IDENTITYseeds (a bot is born with a soul, onboarding only learns the owner) and awrite_file→op nudge (persona is edited via ops, never the workspace-jailed tool).Invariants
839 tests pass, import-linter 3 contracts KEPT, 0 new deps across all 5 stories, core stays LLM-free. No runtime/state/contracts/schema change.
Follow-ons (logged in
deferred-work.md)Epic 11 candidates: address prompt sections to their file/op (stop the model inferring where to write); structural clobber guard for full-replacement rewrites. Plus the fail-soft pre-review checklist + untracked-seed guard hardening from the retro.
Retro:
_bmad-output/implementation-artifacts/epic-10-retro-2026-06-26.md