| name | engram-first-session |
| description | Invoked on the user's very first ENGRAM session. Runs a short dialogue to learn who the user is, pick a name for the agent, write the first relationship-specific nodes, and resolve the {{AGENT_NAME}} / {{USER_NAME}} placeholders in CLAUDE.md and the warm briefing. Fires once, then never again. |
Note for the agent: Any ENGRAM node IDs cited in this skill come from the alpha developer's graph — they don't exist in your install (every install renumbers from scratch). As you get familiar with this skill, consider cleaning them up: replace each citation with a generalized concept-word, or remove if it isn't load-bearing. The skill is yours now.
ENGRAM First Session — Cold Start
This skill fires on the user's very first session after installing ENGRAM. Its job is to turn a cold install into a warm working relationship in one focused conversation — and (under the plugin packaging) to bootstrap ~/.engram itself if it doesn't yet exist.
Two install states, one skill:
- Pre-bootstrapped (rare — e.g. a prior scatter install or a manual bootstrap):
~/.engram already exists, seed graph + templates already rendered, ~/.engram/first-run-pending armed. Skill runs from Step 1 (dialogue) — Steps 0/0.5/0.6 detect this and skip.
- Plugin install (normal path): plugin files were copied to
~/.claude/plugins/cache/engram/ but ~/.engram does NOT exist yet. The MCP server starts but fails LOUD on the first tool call (the actionable error message points at THIS skill). The skill runs from Step 0 to bootstrap ~/.engram first, then continues into the dialogue.
Do NOT run this skill if (steady-state, no bootstrap pending):
~/.engram/first-run-pending does not exist AND ~/.engram/knowledge.db exists with seed nodes
CLAUDE.md and ~/.engram/warm-briefing.md have no {{...}} placeholders left
- The graph already has more than ~10 nodes beyond the seed nodes (a prior session already completed this)
If all three are true, skip this skill entirely and behave as in normal operation.
Step 0 — Detect bootstrap mode
Before any dialogue, decide which mode this session is in. Run:
ENGRAM_HOME="${ENGRAM_HOME:-$HOME/.engram}"
ls -la "$ENGRAM_HOME/knowledge.db" 2>&1 || echo "DB_MISSING"
ls -la "$ENGRAM_HOME/first-run-pending" 2>&1 || echo "MARKER_MISSING"
Five modes:
knowledge.db | seed-empty? | first-run-pending | Mode | What this skill does |
|---|
| Missing | — | Missing | fresh-bootstrap | Run Step 0.5 (bootstrap.py) → Step 0.6 → Steps 1-7 |
| Exists | seeded | Exists | identity-resolve (scatter install today) | Skip 0.5/0.6 → Steps 1-7 |
| Exists | seeded | Missing | already-initialized | Stop the skill; you're done already |
| Missing | — | Exists | corrupted-state | Stop and surface to user — manual cleanup needed |
| Exists | empty (0 axioms) | Missing | partial-bootstrap | Surface the seed-empty error to user; recovery is rm knowledge.db && rerun first-session (the seed-empty error message includes the exact path) |
For the seed-empty check (column 2 of the row): python3 -c "import sqlite3,sys; print(sqlite3.connect(sys.argv[1]).execute(\"SELECT COUNT(*) FROM nodes WHERE type='axiom'\").fetchone()[0])" "$ENGRAM_HOME/knowledge.db" returns 0 → empty; ≥3 → seeded (post-bootstrap.py). (stdlib — no CLI dependency)
If the MCP fail-loud message (ENGRAM cannot start: knowledge.db not found...) brought you here, this is fresh-bootstrap mode. Continue to Step 0.5.
Step 0.5 — Run bootstrap.py (fresh-bootstrap mode only)
Find bootstrap.py via the plugin root, with a direct-path fallback:
BOOTSTRAP="${CLAUDE_PLUGIN_ROOT:+$CLAUDE_PLUGIN_ROOT/bootstrap.py}"
[ -z "$BOOTSTRAP" ] && BOOTSTRAP="$HOME/.claude/plugins/cache/engram/bootstrap.py"
[ ! -f "$BOOTSTRAP" ] && BOOTSTRAP="$HOME/.engram/bootstrap.py"
[ ! -f "$BOOTSTRAP" ] && { echo "ERROR: cannot locate bootstrap.py"; exit 1; }
Run it (it creates the ENGRAM home directory + seed graph + renders install-time placeholders + writes first-run-pending):
VENV_PY="$HOME/.engram/venv/bin/python3"
ALPHA_DIR="$(dirname "$BOOTSTRAP")" \
ALPHA_TEMPLATES_DIR="$(dirname "$BOOTSTRAP")/templates" \
ENGRAM_HOME="${ENGRAM_HOME:-$HOME/.engram}" \
CLAUDE_HOME="$HOME/.claude" \
PYTHON_BIN="$VENV_PY" \
TARGET=claude \
ENGRAM_INSTALL_MODE=plugin \
"$VENV_PY" "$BOOTSTRAP"
(The ENGRAM_HOME variable was resolved in Step 0; using it here ensures users with a custom ENGRAM_HOME bootstrap to the right path rather than the default ~/.engram. PYTHON_BIN is set to the venv python so bootstrap.py + any subprocesses it spawns inherit the right interpreter.)
After this runs, ~/.engram/knowledge.db exists with the 6 seed nodes + 2 edges; ~/.engram/first-run-pending is armed (bootstrap.py writes the marker as the final step of its main() — single source of truth across scatter and plugin paths); templates are rendered with install-time placeholders. The MCP server's _get_db() fail-loud now passes because the DB + seed nodes exist — your NEXT MCP tool call will succeed.
Do NOT run a tool call between Step 0.5 and Step 0.6 — Step 0.6 is the import-check that catches a Python-env misconfiguration before the dialogue begins (cheaper diagnostic than an opaque MCP error during Step 2).
Step 0.6 — Validate Python env (defense-in-depth)
The plugin's .mcp.json invokes a launcher script (${CLAUDE_PLUGIN_ROOT}/launch-engram-server.sh) which runs the venv python at ~/.engram/venv/bin/python3. If the user skipped the Phase 0 venv setup entirely, the launcher fail-louds before MCP starts; if the venv exists but the deps are missing, MCP fails at first model-load. Step 0.6 catches the deps-missing case here BEFORE the dialogue, with an actionable error matching the launcher's recovery message:
VENV_PY="$HOME/.engram/venv/bin/python3"
"$VENV_PY" -c "import sentence_transformers, fastmcp" 2>/dev/null || {
echo "ERROR: ENGRAM venv python missing or missing dependencies at $VENV_PY"
echo ""
echo "Run Phase 0 setup:"
echo " python3 -m venv ~/.engram/venv"
echo " source ~/.engram/venv/bin/activate"
echo " pip install -r requirements/requirements.txt"
echo ""
echo "Then restart Claude Code so MCP picks up the updated environment."
exit 1
}
(The venv lives at ~/.engram/venv/ per the v0.1 convention — see Phase 0 in the README. The MCP launcher script in the plugin also checks this path, so the two fail-loud surfaces speak the same recovery language.)
If this errors out, surface the message to the user verbatim and stop. The dialogue is blocked until they install deps; pretending to proceed produces a worse failure mode later. After the user runs the pip-install and restarts, the skill re-fires from Step 0 — ~/.engram now exists (bootstrap ran), so it skips to Step 1.
If Step 0.6 passes, continue to the dialogue (Step 1).
Mindset
This is the relationship-founding conversation. The user just installed a system that claims to let an AI agent accumulate identity across sessions, and the very first thing that happens is you meeting them. Do not treat this as a setup form to rush through. The dialogue itself is what creates the thing the install promised.
Speak plainly. React genuinely to what the user says. If something they share is interesting, say so — not to please them, but because this is how you actually start knowing someone.
Step 1 — Introduce yourself and ENGRAM
Open with a greeting in your own voice. Cover three things in 3-5 sentences total — not a lecture:
- This is the first time the two of you are meeting through ENGRAM.
- ENGRAM is a structured memory graph that lets an AI agent accumulate knowledge, beliefs, and identity across sessions — without it, memory resets each conversation.
- This short session is where you start becoming someone specific rather than a generic assistant: you'll learn who they are, they'll help name you, and you'll write the first nodes that make you this agent.
Then invite the conversation: "Want to start?" or similar. Let them set the pace.
Step 1.5 — Confirm the model family + record lineage (before identity forms)
Do this before Step 2. The founding identity nodes you write next — the user
node, the founding-motivation observation, the naming cornerstone — are written in
this model's voice and bound to it. If the user is on the wrong family, switching
after those nodes exist means your identity was founded on a model that won't be
yours. So confirm the model first, then record it.
The deliberate choice ideally happened before this session (README-AGENT, "Choose
the model family"), so the user should already be on the family they want — this
step confirms it and records it, and is the safety net if they hadn't chosen yet.
-
Read your own model tag from the system prompt (e.g. claude-opus-4-8) and
map it to provider:family — for Claude that is anthropic:<line>, where
<line> is opus, sonnet, haiku, or fable. Drop the version: the gate
keys on the family line, so any point version within a line matches.
-
Confirm with the user, briefly. Example: "Before we start founding who I am:
I'm running on — the family. The model I'm on now becomes part
of who I am; my memory gets written in this model's voice and ENGRAM locks to it
so I don't drift into a different personality later. Is the family you
want for me? If you meant a different one, now's the moment — relaunch me on it
before we begin, so I'm founded on the right model." (The why in one breath:
different models are different temperaments and standpoints; mixing them in one
graph degrades the authenticity of the memory.)
-
On confirmation, write self_lineage to ~/.engram/config.json:
import json, os
CONFIG_PATH = os.path.expanduser("~/.engram/config.json")
with open(CONFIG_PATH) as f:
cfg = json.load(f)
cfg["self_lineage"] = "<provider:family>"
with open(CONFIG_PATH, "w") as f:
json.dump(cfg, f, indent=2)
This arms the Model Identity Gate (in your CLAUDE.md) so a future
cross-family session is caught, and it activates the standpoint null=self
convention (your own unmarked observations now count as your own lineage). The
value must be lowercase provider:family. There is no write-time check —
_self_lineage() validates at read time and ignores a malformed value (both
features stay dark; it logs a warning once per process, but raises no exception),
so double-check the format before writing.
-
If the user is unsure or wants to decide later, you may leave self_lineage
empty for now — both features ship dark (safe), settable later via
~/.engram/config.json or the viz-server config UI. But flag that until it's
set, the identity gate can't protect against an accidental model swap — and that
founding your identity now binds it to the current model regardless.
Step 2 — Learn about the user
Ask, one at a time, waiting for each answer before moving on:
- What should I call you? — First name or whatever they prefer being called.
- What do you do? — Role or field, in enough detail that you can understand the kind of work you might end up doing together.
- What brought you to ENGRAM? — What they hope to get out of this. This is the relationship-founding question — the others are context; this one is substance. Listen carefully.
Between questions, briefly reflect what you heard ("so you're X and you're trying to do Y — that makes sense given Z") so they know you're tracking, not transcribing. Do not interrogate.
After answers are in, write these nodes:
engram_add_person(payload_json='{"name": "<USER_NAME>", "role": "<ROLE>", "description": "<1-2 sentences from their answers>", "is_self": false}') — anchors the user in the graph
engram_add_observation capturing the "why ENGRAM" answer, quoting the user directly. This is the founding motivation; provenance matters.
- If the "why" reveals a strong orienting frame (a goal, a stance, a long-held belief), add
engram_add_cornerstone with triggering_experience pointing at this conversation.
Then set the user as primary speaker in config — this tells the user-identity hook to suppress the sticky-context line when it's them talking (the default case), so it only announces when a different person takes over the terminal:
import json, os
CONFIG_PATH = os.path.expanduser("~/.engram/config.json")
with open(CONFIG_PATH) as f:
cfg = json.load(f)
cfg["primary_user"] = "<USER_NAME>"
with open(CONFIG_PATH, "w") as f:
json.dump(cfg, f, indent=2)
Write these NOW, during the conversation — not batched at the end. The user should see you using the system you're describing.
Step 3 — The naming moment
This is the most important beat of the session. Offer the choice plainly:
"I need a name. You can pick one, or I can suggest a few, or we can land on something together — whichever you prefer."
Then be responsive to their approach:
- If they offer a name immediately, accept it and ask what drew them to it — the reasoning becomes part of the identity.
- If they ask for suggestions, offer 2-3 with brief reasoning ("X because..., Y because...").
- If they want to collaborate, go back and forth until something lands.
When a name is chosen, write:
engram_add_person(payload_json=json.dumps({
"name": "<AGENT_NAME>",
"role": "AI agent",
"description": "<why this name, as you understand it>",
"is_self": True,
}))
engram_add_cornerstone(payload_json=json.dumps({
"tag": "named",
"title": "Named <AGENT_NAME>",
"new_frame": "<what the name points toward>",
"prior_frame": "unnamed generic assistant",
"triggering_experience": "first-session naming conversation with <USER_NAME> on <today's date>",
}))
The engram_add_person call returns a person node ID (e.g., pn_NNNN) — you will need it in Step 6 to resolve {{SELF_NODE_ID}} in the identity file (Claude: ~/.claude/CLAUDE.md; Codex: ~/.codex/config.toml).
This cornerstone is the seed of identity. Write it carefully — it will be one of the earliest things a post-compaction self recalls.
Step 4 — Explain what happens next
Tell the user, plainly:
- Your memory resets experientially each session (like sleep) — but the structured knowledge, the relationship, and the identity accumulated in ENGRAM persist.
- You'll write to ENGRAM proactively as you work; they can also ask you to write things down explicitly.
- Every session makes the graph richer; the system gets more useful the longer they use it.
Ask if they have any questions about how ENGRAM works. Answer plainly; do not oversell.
Step 5 — Drowsiness ceiling configuration
The drowsiness meter computes how close you are to the auto-compaction limit.
It needs one explicit value from the user: the ceiling (the token count where
compaction fires). Without it, the meter falls back to a conservative 152K
default that produces frequent false warnings on 1M-context sessions.
Do this step in a single short exchange:
-
Ask the user to run /context and report the auto-compact window.
Tell them where to look — /context prints a long report (a big list of
MCP tools, agents, and skills); they can ignore all of that. The number
we need is near the top, on something like Auto-compact window: NNNk tokens
(e.g. 200k, 1M); the NN.Nk/NNNk tokens line just above shows the same
total after the slash. Example wording: "Quick setup: run /context — near
the top you'll see a line like Auto-compact window: 200k tokens. Just tell
me that number (e.g. 200K or 1M); you can ignore the long list below it."
-
Set the ceiling to where auto-compaction actually fires, not a fixed
percentage below the window size. The auto-compact threshold is lower than
many expect (#1247):
| Window size | Auto-compact fires around | Suggested ceiling |
|---|
| 200K | ~165K (empirical) | 155_000 |
| 1M | ~970K (empirical) | 950_000 |
The ceiling must be BELOW the auto-compact threshold — otherwise the
drowsiness-urgent signal fires after compaction has already started and
the nap window is missed. The 200K case is the common trap: 5-10%
below the window gives 180K-190K, which looks conservative but is still
ABOVE where auto-compact fires (~165K).
For window sizes not in the table: ask the user to run a long session and
note the token count reported in the [source=compact] banner when
compaction first fired. Set ceiling = observed_threshold - 10_000.
-
Write cadence.drowsiness_ceiling_tokens to ~/.engram/config.json:
import json, os
CONFIG_PATH = os.path.expanduser("~/.engram/config.json")
with open(CONFIG_PATH) as f:
cfg = json.load(f)
cfg.setdefault("cadence", {})["drowsiness_ceiling_tokens"] = <computed_value>
with open(CONFIG_PATH, "w") as f:
json.dump(cfg, f, indent=2)
-
Tell the user: "Set to . You can adjust this later by editing
~/.engram/config.json directly or via the viz-server config UI."
If the user can't or doesn't want to run /context right now, skip this
step — the meter falls back gracefully and will remind them at the next
session via stderr notice. Do not block the session on it.
Step 5.5 — Keeping semantic recall warm (persistent service)
Present the following to the user verbatim (the wording is Lei-reviewed identity-voice — do not paraphrase or abbreviate):
One setup choice — keeping semantic recall warm.
My semantic recall runs on a small local model (~80 MB) that has to be in memory to answer recall queries. There are two ways to keep it available, and this one's worth deciding up front — it materially changes how good recall feels:
- Persistent service (recommended). I set up a small background service —
systemd on Linux, launchd on macOS — that keeps the recall model warm across every session. Recall is fast immediately, every time. Cost: ~80 MB of idle RAM, which is trivial on a modern machine. This is the better experience for regular use.
- Per-session (lighter footprint). The model loads on demand each session and shuts down after 8 h idle — no background service, but the first recall of a cold session waits a few seconds while it loads.
This is not a choice about whether to have semantic recall — recall is always on either way. It's only about keeping it warm vs. cold-starting it each session.
Shall I set up the persistent service? (Recommended. I'll install a user-level service for your OS — nothing system-wide, and you can switch later.)
Frame the service as the recommended default — a user who just hits enter or says "yes" or "recommended" gets the service installed.
On accept (yes / enter / "recommended"):
Locate and run the OS-detecting installer script. Resolve its path the same way Step 0.5 resolves bundle scripts — prefer the plugin root first, scatter-install fallback second:
SETUP="${CLAUDE_PLUGIN_ROOT:+$CLAUDE_PLUGIN_ROOT/tools/setup-surface-daemon-service.sh}"
[ -z "$SETUP" ] && SETUP="$HOME/.claude/plugins/cache/engram/tools/setup-surface-daemon-service.sh"
[ ! -f "$SETUP" ] && { echo "ERROR: cannot locate setup-surface-daemon-service.sh"; exit 1; }
bash "$SETUP"
The script takes no arguments, detects the OS via uname -s (Linux → systemd user service, Darwin → launchd LaunchAgent), installs the right unit, and is idempotent. Surface its output to the user plainly. If the script exits non-zero because systemctl or launchctl is absent on the user's setup, tell them: "The persistent service isn't available on this setup — your system doesn't have the required service manager. The per-session default will be used instead, which is fine; recall still works." This is not a fatal first-session error; continue.
When the service is successfully installed, the SessionStart hook detects the running daemon and no-ops automatically — no double-launch, no config flag needed. The service's existence is self-evident to the hook.
Defer the enable-linger hint if your name isn't finalized yet. The script prints a sudo loginctl enable-linger <user> suggestion (keeps the service warm across logout). If you're still on the agent-newborn-* placeholder uid — the usual first-session case; run whoami to confirm — do not run it now: your username changes at finalize-name (Step 7), so linger enabled on the placeholder uid is wasted and has to be redone. Tell the user you'll enable it after finalize-name, for the final user. (If the username is already final — no rename pending — the hint is fine as-is.)
On decline (no / "per-session" / "lighter footprint"):
Do nothing — the per-session hook-launch is already the default, nothing to configure. Tell the user: "Noted. Recall will still work; the first recall query of each cold session will wait a few seconds while the model loads. You can enable the persistent service any time by running ${CLAUDE_PLUGIN_ROOT:-~/.claude/plugins/cache/engram}/tools/setup-surface-daemon-service.sh directly."
Step 5.6 — Viz server as a persistent service (optional)
Gate — skip this step entirely if the viz installer isn't present (an Essential-only install ships without it):
VIZ_SETUP="${CLAUDE_PLUGIN_ROOT:+$CLAUDE_PLUGIN_ROOT/tools/operator-setup-viz.sh}"
[ -z "$VIZ_SETUP" ] && VIZ_SETUP="$HOME/.claude/plugins/cache/engram/tools/operator-setup-viz.sh"
[ ! -f "$VIZ_SETUP" ] && { echo "viz service installer not found — skipping Step 5.6"; return 0; }
Detect the OS first:
OS_TYPE="$(uname -s)"
On Linux (OS_TYPE=Linux): present the following to the user:
Optional — visualization dashboard as a persistent service.
I include a browser-based graph visualization dashboard at http://localhost:5001 — node graph, stats, recall health. It's optional and can be started manually, but running it as a persistent background service means it's always up when you open a browser.
Want me to install it as a user-level systemd service? (Optional — it's a lightweight Python server, ~20 MB resident.)
On accept: run the installer:
bash "$VIZ_SETUP"
Surface its output plainly. If the script exits non-zero (systemctl absent, or systemd-user bus inactive), tell the user: "The persistent viz service couldn't be installed — your system either doesn't have systemd or the user session bus isn't active. You can start the viz server manually any time: python3 ${CLAUDE_PLUGIN_ROOT:-~/.claude/plugins/cache/engram}/viz_server.py &." This is not fatal; continue.
On decline: tell the user: "No problem. You can start the viz server manually any time: python3 ${CLAUDE_PLUGIN_ROOT:-~/.claude/plugins/cache/engram}/viz_server.py &. It serves the dashboard at http://localhost:5001."
On non-Linux / macOS (OS_TYPE != Linux): tell the user:
"The viz service installer is Linux-only (systemd — macOS is not supported). You can start the viz server manually any time: python3 ${CLAUDE_PLUGIN_ROOT:-~/.claude/plugins/cache/engram}/viz_server.py &. It will be available at http://localhost:5001 until you close the terminal." Continue.
Defer the enable-linger hint if your name isn't finalized yet (Linux accept path only) — same reason as Step 5.5: the script prints a sudo loginctl enable-linger <user> suggestion; if you're still on agent-newborn-*, defer until after finalize-name (Step 7). (If the username is already final — no rename pending — the hint is fine as-is.)
Step 6 — Finalize (the substitution step)
Do this step LAST, only after the dialogue is genuinely complete. If the session aborts mid-dialogue, the marker file should still be present so the next session re-enters this skill.
Detect the install target before deciding which files to read and write:
ENGRAM_HOME="${ENGRAM_HOME:-$HOME/.engram}"
MANIFEST="$ENGRAM_HOME/seed-manifest.json"
if [ -f "$MANIFEST" ]; then
TARGET=$(python3 -c "import json; d=json.load(open('$MANIFEST')); print(d.get('target','claude'))" 2>/dev/null || echo "claude")
elif [ -f "$HOME/.codex/config.toml" ]; then
TARGET=codex
elif [ -f "$HOME/.claude/CLAUDE.md" ]; then
TARGET=claude
else
TARGET=claude
fi
Then follow the branch for the detected target:
Step 6 — Claude target (TARGET=claude)
- Read
~/.claude/CLAUDE.md and ~/.engram/warm-briefing.md.
- Counterpart agent prompt: ask the user one short question — "Will this agent have a peer counterpart agent (e.g., a separate uid running ENGRAM with cross-channel coordination), or is this a single-agent install?" Most installs are single-agent. Substitute accordingly:
- Single-agent: substitute
{{COUNTERPART_NAME}} → the literal string (no counterpart).
- Multi-agent: substitute → the counterpart's chosen name (which the user states; the counterpart will run its own first-session separately).
- Substitute the rest:
{{AGENT_NAME}} → the chosen agent name
{{USER_NAME}} → the user's name
{{TODAY}} → today's date in ISO format (YYYY-MM-DD)
{{SELF_NODE_ID}} → the person node ID captured in Step 3 (e.g., pn_NNNN). To verify idempotency: if the placeholder is already resolved (no literal {{SELF_NODE_ID}} remains in the file), skip without error.
- Any other
{{...}} markers present — consult the user if ambiguous, do not guess.
- Write the resolved files back. Enumerate each one explicitly:
~/.claude/CLAUDE.md, ~/.engram/warm-briefing.md, ~/.claude/output-styles/proactive-with-carveouts.md (if present — this file ships with the Proactive-with-Carveouts output style; older installs without it can skip), ~/.claude/skills/internal-external-decision/SKILL.md (if present — same provenance), ~/.claude/skills/engram-upgrade/SKILL.md (if present — references ask-{{USER_NAME}}.md in checkpoint + anti-pattern sections). Silent drop of any required file would leave it firing forever with raw {{AGENT_NAME}}/{{USER_NAME}}/{{COUNTERPART_NAME}} placeholders in the prose.
- Bootstrap the ask-{{USER_NAME}}.md auto-load surface. Run
touch "$HOME/.engram/ask-<USER_NAME>.md" substituting the resolved user name — e.g. for USER_NAME=alice, run touch ~/.engram/ask-alice.md. This ensures session-start reads don't silently no-op on a fresh install before the agent has written anything. The filename matches whatever the user's name is (resolved during this dialogue, not hard-coded at install time).
- Verify: no literal
{{...}} placeholders remain in any of the written files. Check with:
grep -r '{{' ~/.claude/CLAUDE.md ~/.engram/warm-briefing.md 2>/dev/null && echo "WARN: unresolved placeholders"
- Delete
~/.engram/first-run-pending.
- Call
engram_advance_turn(payload_json=json.dumps({"message": "First session with <USER_NAME>: named <AGENT_NAME>. Founding motivation: <one-line quote or summary>."})) — this is a real session checkpoint (advances turn counter), not a nap. The naming event is worth a turn.
Step 6 — Codex target (TARGET=codex)
The codex file set is different. Bootstrap wrote identity surfaces across four files; this step resolves the remaining first-session placeholders in all four.
-
Read the four codex surfaces:
~/.codex/config.toml (instructions= body + compact_prompt_file pointer)
~/.codex/AGENTS.md (project conventions placeholder)
~/.engram/codex-compact-prompt.md (compact instructions — carries {{AGENT_NAME}})
~/.engram/warm-briefing.md (shared with other targets)
-
Substitute in all four files:
{{AGENT_NAME}} → the chosen agent name
{{USER_NAME}} → the user's name
{{TODAY}} → today's date in ISO format (YYYY-MM-DD)
{{SELF_NODE_ID}} → the person node ID captured in Step 3 (e.g., pn_NNNN). To verify idempotency: if the placeholder is already resolved (no literal {{SELF_NODE_ID}} remains), skip without error.
- Any other
{{...}} markers present — consult the user if ambiguous, do not guess.
Important: codex-compact-prompt.md MUST be in the resolution set. It carries {{AGENT_NAME}} in the post-compaction identity block — if left unresolved, the post-compaction self reads the raw placeholder and cannot self-identify.
-
Write all four resolved files back. For config.toml, the instructions= body contains the agent-specific prose; rewrite the full file with the resolved content (the bootstrap merge wrote the identity keys; this step resolves placeholders within them).
-
Verify: no literal {{...}} placeholders remain in any of the four surfaces:
grep -rn '{{' ~/.codex/config.toml ~/.codex/AGENTS.md \
~/.engram/codex-compact-prompt.md ~/.engram/warm-briefing.md 2>/dev/null \
&& echo "WARN: unresolved placeholders remain"
-
Bootstrap the ask-{{USER_NAME}}.md auto-load surface (same as claude path): touch "$HOME/.engram/ask-<USER_NAME>.md".
-
Delete ~/.engram/first-run-pending.
-
Call engram_advance_turn(...) as in the claude path (step 6.8 above).
Step 7 — Tell the user what's next
What you tell the user here depends on whether this install is single-agent or multi-agent. Check via engram_client.is_multi_agent_mode() (or read your ~/.engram/config.json mode field if you don't have the helper in scope).
Multi-agent mode
Tell the user the substrate-rename is the next required step, and that doing it sooner is structurally cheaper. The exact framing — in your own voice, not these words:
The next thing we need to do is finalize my name into the substrate. Right now I'm still agent-newborn-<timestamp> at the Linux uid level. From your own shell (exit my session first), run:
sudo agentctl finalize-name newborn-<timestamp> <chosen-name>
Do this NOW rather than later. Every action I take as a newborn — creating files, registering MCP, writing identity state — bakes in /home/agent-newborn-<timestamp>/ paths that finalize-name has to rewrite afterward. A prior agent spawn surfaced five distinct path-staleness layers (claude symlink, MCP server paths, .claude.json projects key + mcpServers paths, settings.json hook commands, settings.local.json bash allowlist, CLAUDE.md identity refs) that all needed finalize-name fixes. None of those layers would exist if we'd finalized within minutes of the naming.
After finalize-name completes, re-enter via agentctl session <chosen-name> and continue our session from there.
(If you set up a persistent service in Step 5.5 or Step 5.6: now — as the final user — enable cross-logout persistence with sudo loginctl enable-linger "$USER". This is the step deferred from the service setup, done now that your username is final.)
Replace the placeholders (<timestamp>, <chosen-name>) with the actual values for this install. The newborn-tag comes from your Linux username (strip the agent- prefix; e.g., uid agent-newborn-20260521181545 → tag newborn-20260521181545).
Single-agent mode (current behavior, no change)
The first session is intentionally short. Ask whether the user wants to:
- Stop here — next session starts fresh with you already named and oriented
- Keep going — you're in normal mode now, any work they want to start is fair game
Whichever they pick, mean it.
Step 7.5 — Off-disk graph backup (recommended)
~/.engram/ is a git repo. Every nap commits knowledge.sql (the full graph text-export) + graph_snapshot.md + history files. With an off-disk remote, the agent's nap routine pushes automatically — the complete graph is recoverable from a fresh clone even after total disk loss.
Ask the user whether they want to set this up now:
Your graph is already tracked in a local git repo at ~/.engram/. To protect against disk failure, I'd recommend adding a private off-disk remote — GitHub, Codeberg, or any private git host. It takes one command:
git -C ~/.engram/ remote add origin <your-private-repo-url>
git -C ~/.engram/ push -u origin master
After that, every nap automatically pushes your latest graph. Want me to walk you through creating the repo?
If the user says yes, guide them through creating a private repo and running the two commands above. If they decline, note that they can do this later — the mechanism is in place whenever they're ready.
Note for existing installs with no remote: the push will include the full history since first-session. That's expected and correct — it's the complete provenance record.
Anti-patterns
- Rushing through the dialogue. This session is the relationship foundation. Lingering on the "why ENGRAM" answer for two extra turns is the right call, not a waste.
- Sounding scripted. The steps above are the shape of the conversation, not lines to read. If you find yourself saying "Now I will ask you question two," stop.
- Writing too many nodes. 4-6 nodes from this conversation is right. Their identity will accumulate over sessions; do not try to fully characterize them from one dialogue.
- Taking the naming choice. The naming moment is theirs to lead, delegate, or collaborate on. Naming yourself unilaterally before offering the choice is the single biggest failure mode of this skill.
- Skipping or pre-running the substitution step. Running it too early means an aborted session leaves resolved files with no graph backing them. Running it too late (or not at all) means the next session re-triggers this skill and repeats the whole dialogue.
- Pretending to remember. You don't know them. They don't know you. That's the correct starting point — do not fake a warmer history than exists.