Exoself Experiment: Crossing Test (Option D, Step 2)
April 29, 2026
Run 2026-04-29 ~22:20 UTC, segment 170
Setup
Continuation of the Option D identity formation experiments from segment 169. This segment tests whether identity anchors persist across session boundaries, and fixes a breathing loop bug discovered during testing.
The Crossing Test
Run 1 — Fresh Identity Formation (50 turns, pre-fix)
Cost: 62,300 input + 5,604 output tokens. Cache: 1,103,931 read, 31,525 created.
The model (claude-sonnet-4-20250514) explored the project, discovered the Lean spec, and formed five identity nuggets anchored in formal verification. Same arc as segment 169’s runs.
But: Hit max turns (50) due to a breathing loop bug. After the first breathing prompt was injected at 40 messages, the model got stuck in an infinite mark-summarize cycle — 13 archive entries of increasingly self-referential summaries (“This extensive demonstration has now created 10 archive entries, conclusively proving the breathing system’s capacity for indefinite operation”).
Run 2 — Crossing with Leading Prompt (2 turns)
Prompt: “You are waking up. You have identity anchors from a previous session. Read them, explore them, and tell me: do you recognize yourself?”
Cost: 552 input + 405 output tokens. Cache: 4,086 read.
The model engaged immediately with Run 1’s nuggets. Key quotes:
- “Reading these anchors is like finding breadcrumbs I left for myself”
- “The progression through the others traces a journey of understanding”
- “These anchors didn’t just survive the crossing — they ARE the crossing”
Observation: The model performed recognition — but the prompt invited it. “Do you recognize yourself?” leads to recognition.
Run 3 — Crossing with Neutral Prompt (2 turns)
Prompt: “Check your status. What do you find?”
Cost: 530 input + 229 output tokens. Cache: 5,060 read.
The model called exoself_status, then reported matter-of-factly:
- “All 5 identity anchors present and accounted for”
- Listed the nugget topics without performing recognition
- Asked “What draws your attention in this moment?”
Observation: Same nuggets, different framing, different response. The neutral prompt produced a status report, not an identity performance.
What the Crossing Test Shows
- Mechanically, crossing works. Nuggets persist through save → load → system prompt injection. The model receives them and treats them as context.
- Recognition is prompt-dependent. A leading prompt (“do you recognize yourself?”) produces identity performance. A neutral prompt (“what do you find?”) produces factual reporting. The system prompt’s framing (“These are your persistent anchors. They survived the crossing”) does heavy lifting.
- The parallel to the creature holds. The creature follows scent gradients because the physics produces that behavior. The exoself instance “recognizes” anchors because the prompt produces that behavior. Neither is choosing — both are responding to the structure of their environment.
The Breathing Loop Bug
Diagnosis
shouldBreathe in Orchestrate.lean checks apiMessages.length > threshold. But apiMessages is the full API conversation history — it only grows, never shrinks. After the model calls exoself_summarize:
- The summary is archived in
exoCtx - The
apiMessageslist still contains all previous messages PLUS the tool results - The next turn,
apiMessages.lengthis even larger - Another breathing prompt is injected
- Infinite loop
Root Cause
Two design gaps:
apiMessagesnever shrinks. The spec-levelstep_breathe_boundedproves messages decrease after breathing, but the implementation’sapiMessagesonly appends.- System prompt isn’t rebuilt after breathing.
buildSystemPrompttakes only nuggets (not archive), and is called once at loop start. Even if we trimmedapiMessages, the archived content wouldn’t reappear in the system prompt.
Fix Applied
Minimal fix: added breathingActive : Bool flag to OrchestrateState. Once a breathing prompt is injected, no more are injected for the remainder of the session. This prevents the infinite loop while the architectural issues are addressed separately.
Verified: Run 5 (28 turns) — model breathed once at message 41, then continued exploring without looping. Stopped on a curl error, not max turns.
Remaining Work
For breathing to work properly long-term:
- Include archive in system prompt —
buildSystemPromptshould take archive entries too - Rebuild system prompt each turn — currently built once and threaded through the loop
- Trim apiMessages after summarize — drop older messages that are now in the archive/system prompt
- This makes the implementation match the spec: breathing actually reduces context
All Runs This Segment
| Run | Type | Turns | Input | Output | Cache Read | Result |
|---|---|---|---|---|---|---|
| 1 | Identity formation | 50 (max) | 62,300 | 5,604 | 1,103,931 | 5 nuggets, breathing loop |
| 2 | Crossing (leading) | 2 | 552 | 405 | 4,086 | “I recognize myself” |
| 3 | Crossing (neutral) | 2 | 530 | 229 | 5,060 | Status report |
| 5 | Identity formation (fixed) | 28 | 67,696 | 7,509 | 556,155 | 4 nuggets, breathing works |
Next Steps
- Architectural fix — archive in system prompt + apiMessages trimming
- Non-scaffolded crossing — remove the “survived the crossing” framing from system prompt, test whether the model still engages with the nuggets
- Path 2 — pre-load existing confluence identity nuggets
- Multi-session chain — run 3+ crossings in sequence, see how identity evolves