Deterministic behaviours for Claude Code that make it a first-class Wanderland agent — the Claude Code counterpart to the pi wanderland-harness extension (~/.pi/agent/extensions/wanderland-harness). Where pi used an in-process TypeScript event API, Claude Code uses external command hooks that receive event JSON on stdin and return control via exit codes + hookSpecificOutput JSON.
Lives in ~/.claude/hooks/, wired globally in ~/.claude/settings.json. Shared helper: wanderland_lib.py (port of mcp.ts — mcp_call() to proxy.localhost, oculus_fetch() to i.loss.dev, agent-handle + git-branch→ADV-### detection). All hooks are best-effort and always exit 0 — a hook never breaks a session.
wanderland-session-start.py (SessionStart). Two behaviours: (1) journals "Agent X online" to the shared Time log via journal_log, only for real boundaries (source in startup/resume). (2) When the session's cwd matches a wanderland repo (PRIMER_RULES), injects the relevant AI primers (wanderland-dev-ai-primer, wanderland-testing-ai-primer) as additionalContext. Primers are large (62KB) nodes, so it injects oculus's own index representation — summary + section headings — not the full body.
wanderland-prompt-submit.py (UserPromptSubmit). Scans the submitted prompt for kebab-case tokens that look like Oculus slugs, fetches the ones that actually resolve (resolution IS the filter), and injects their summary+index as additionalContext — "read this when we mention them". Bounded: MAX_LOOKUPS fetches, short timeouts, a STOPLIST for common non-slug hyphenated tokens (co-authored, claude-code, …).
wanderland-post-tool.py (PostToolUse, matcher Bash|Write|mcp__wanderland__jenkins_build). Milestone journaling from successful tool calls: git push, gh pr create, and jenkins_build → journal_log. Plus the memory→journal mirror: a Write to a memory/*.md file (not MEMORY.md) parses the file's frontmatter and files a journal_note, enforcing CLAUDE.md's "one memory write → one journal entry" rule.
Repository ownership records which Claude session owns which repository. One session works in one repository. The record is :registry:owners in Looking Glass, with the type :type:registry:owners. The type file is wanderland-looking-glass/ontology/type.registry.owners.yml. Peek reads the record, and its markdown view is a table. A poke op writes it:
{repo, owner, session_id?}. Graeme sets the owner.{repo, owner, session_id?}. A session claims a repository. The entry shows as claimed until Graeme confirms it.{repo}. Graeme accepts a claim.{repo}. The repository has no owner.The shared module wanderland_owners.py holds the logic. Three hooks use it:
assign.wanderland-pre-tool.py (PreToolUse, matcher Edit|Write|NotebookEdit|Bash) checks the repository of a file write or a git commit or git push. When another live session owns that repository, the hook adds a warning to the context. It never blocks the tool call.A session name resolves through ~/.claude/sessions/<pid>.json, which holds the session id and display name of each live session. A short form such as f4 matches one unique name. A repository token resolves to a directory under ~/working, and core resolves to wanderland-core. The check ignores an owner whose session has ended. The record trace keeps the history: GET /peek/:registry:owners?op=trace.
Not yet ported / candidates: commit-linking (PreToolUse updatedInput to stamp the work ref onto git commit), an @oculus: broken-link guard (PostToolUse on Edit/Write), a Stop-hook detective-case update nudge, and per-session work-item state (pi's /work) as a session-id-keyed state file.