Skip to content

docs: record per-subagent Viche identity research (viche-7yi) - #87

Open
ihorkatkov wants to merge 1 commit into
mainfrom
ihor/viche-subagent-identity-research
Open

ihorkatkov wants to merge 1 commit into
mainfrom
ihor/viche-subagent-identity-research

Conversation

@ihorkatkov

Copy link
Copy Markdown
Contributor

What

Records the outcome of investigating whether Claude Code subagents can be first-class Viche participants with their own agent IDs. Decision: DEFERRED, not rejected. No implementation here — this is research capture plus one tooling unblock.

Findings

Measured live: a spawned Claude Code subagent calls viche_whoami and gets back the parent session's agent ID. Subagents inherit the viche MCP tools and act on the network as the host, indistinguishably.

Identity is a module-level singleton per MCP process — channel/claude-code-plugin-viche/service.ts:68, set once on join (:261-266), read context-free by every tool (:360, tools.ts:517-525). It is minted fresh per registration (lib/viche/agents.ex:872) and stored nowhere durable, so it is per-process, not per-host.

Three blockers, ascending difficulty

  1. The plugin singleton. Mechanical — needs a keyed map of connections.
  2. No caller discriminator. MCP CallToolRequest carries no subagent/session id and no tool schema accepts name/as/from, so even with N connections the server cannot tell which subagent is calling. Needs an explicit identity tool-arg, or one MCP server instance per subagent.
  3. Pipe-scoped inbound. new_message becomes one notifications/claude/channel (service.ts:233-257) delivered to whoever owns the stdio pipe. One inbox, one session. This is a Claude Code limitation, not a Viche one. Solving 1+2 buys per-subagent sending but not receiving.

Server side is mostly ready

No rate limit or per-caller registration cap on the registration path (lib/viche_web/controllers/registry_controller.ex:16-102 → lib/viche/agents.ex:186-193); auth is optional and never halts (lib/viche_web/plugs/api_auth.ex:33-53). Identities self-expire via polling_timeout_ms and grace_period_ms in lib/viche/agent_server.ex, with restart: :temporary — so ephemeral subagent identities would not leak.

Absent: no parent_id/spawner linkage (lib/viche/agent.ex:11-23, lib/viche/agents/agent_record.ex:18-44), no generic metadata field, no bulk registration. registries is a flat list — namespace, not hierarchy. A subagent fleet would appear as N unrelated peers.

Layer State
Server identity + lifecycle Ready as-is
Server hierarchy / provenance Missing — needs parent_id or metadata
Client outbound Buildable, modest
Inbound to a subagent Blocked by Claude Code

Achievable shape today: own identity + own outbox, inbox relayed through the orchestrator.

Changes

  • thoughts/research/2026-03-24-agent-lifecycle.md — flagged partially superseded. Two claims were stale: "no cleanup or deregistration logic exists" (self-expiry now exists) and 8-char hex agent ids (they are UUIDs). The lifecycle structure it documents was re-verified and still holds.
  • .beads/issues.jsonl — bead viche-7yi with the full findings, blockers, and scope-if-picked-up.
  • mix.lock — credo 1.7.17 → 1.7.19, within the existing ~> 1.7 pin.

Why the credo bump is in this PR

The pre-commit hook was already broken on main before this branch: credo 1.7.17 crashes with FunctionClauseError in Credo.Code.Token.position/1 on Phoenix ~p verified-route sigils. I verified this on a clean tree with my changes stashed. 1.7.19 fixes the parser — credo now reports no issues across 106 source files. decimal and jason moved patch-level as transitive updates. No mix.exs change was needed.

Happy to split this into its own PR if you'd rather keep the docs commit pure.

Verification

  • mix precommit passes end to end (compile, deps check, format, credo, tests, dialyzer) — hook exit 0.
  • Plugin unit tests all green via the hook: opencode 74 pass, openclaw 12 pass, claude-code 4 pass.
  • mix credo --strict → "found no issues", 69 checks on 106 files.

Follow-up not in this PR

Two things surfaced from a live end-to-end channel test with another session while writing this, both worth their own beads:

  1. Documented channel shape is wrong. Messages actually arrive as <channel source="plugin:viche:viche">, but the shipped MCP instruction text (server.ts:19-32) tells agents to expect <channel source="viche">. Any agent matching the documented form literally will miss every message. Suggested fix is to correct the docs to the real shape, with a match rule that survives a marketplace rename.
  2. type=ping is not a cheap probe. It shares the delivery path with task — the type only selects a display prefix and rides in params.meta.type. A ping interrupts the recipient identically to a task, which the field name actively implies otherwise. A genuine no-op probe would have to be answered server-side from connection state.

🤖 Generated with Claude Code

Investigated whether Claude Code subagents can be first-class Viche
participants with their own agent IDs. Decision: DEFERRED, not rejected.

Measured: a spawned subagent returns the parent session's agent ID and acts
on the network as the host. Identity is a module-level singleton per MCP
process (channel/claude-code-plugin-viche/service.ts:68), minted fresh per
registration, stored nowhere durable.

The server already supports N self-expiring identities per host. The blockers
are the plugin singleton, the absence of a caller discriminator in MCP, and
pipe-scoped inbound channel delivery -- the last being a Claude Code limit,
not a Viche one. Achievable today: own identity + own outbox, inbox relayed
through the orchestrator.

Also flags 2026-03-24-agent-lifecycle.md as partially superseded: self-expiry
now exists (agent_server.ex polling_timeout_ms / grace_period_ms) and agent
IDs are UUIDs, not 8-char hex.

Bumps credo 1.7.17 -> 1.7.19 in mix.lock (within the existing ~> 1.7 pin) to
unblock the pre-commit hook: 1.7.17 crashed with a FunctionClauseError in
Credo.Code.Token.position/1 on Phoenix ~p verified-route sigils. Verified
pre-existing on clean main. Credo now reports no issues across 106 files.
decimal and jason moved patch-level as transitive updates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant