Skip to content

bug: platform env vars (CLAUDE_CODE_TEAM_NAME, CLAUDE_CODE_AGENT_NAME, CLAUDE_SESSION_ID) are empty — multiple hooks silently disabled #343

@michael-wojcik

Description

@michael-wojcik

Problem

Three environment variables that PACT hooks depend on are consistently empty across all sessions:

  • CLAUDE_CODE_TEAM_NAME — always empty
  • CLAUDE_CODE_AGENT_NAME — always empty
  • CLAUDE_SESSION_ID — always empty

Root Cause (Confirmed)

These variables are PACT inventions — Claude Code was never designed to provide them.

Deep investigation (session pact-1b41631e, 2026-04-02) confirmed:

  1. Claude Code only provides 5 env vars to hooks: CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT, CLAUDE_PLUGIN_DATA, CLAUDE_CODE_REMOTE, CLAUDE_ENV_FILE
  2. Context like session_id, agent_id, agent_type is provided via stdin JSON, not env vars
  3. GitHub issue #9567 confirms Claude Code has "not planned" env var context delivery
  4. PACT hooks were written assuming an API that doesn't exist

What Claude Code Actually Provides

Env vars (official, all work):

Variable Available To Purpose
CLAUDE_PROJECT_DIR All hooks Project root
CLAUDE_PLUGIN_ROOT All hooks Plugin install directory
CLAUDE_PLUGIN_DATA All hooks Plugin's persistent data dir
CLAUDE_CODE_REMOTE All hooks "true" in web environments
CLAUDE_ENV_FILE SessionStart, CwdChanged, FileChanged File path for persisting env vars

Stdin JSON (all hooks receive):

{
  "session_id": "abc123",
  "agent_id": "unique-id",
  "agent_type": "Explore",
  "tool_name": "Edit",
  "cwd": "/current/dir",
  "hook_event_name": "PostToolUse"
}

What PACT Hooks Expect vs. What Exists

PACT Expects (env var) Actually Available Where
CLAUDE_CODE_TEAM_NAME Nowhere — not in env vars or stdin Must self-provision
CLAUDE_CODE_AGENT_NAME agent_id + agent_type Stdin JSON (not the human-readable name)
CLAUDE_SESSION_ID session_id Stdin JSON — already there, hooks read the wrong source

Impact: Silently Dead Features

Multiple PACT features have been non-functional since creation, passing unit tests (which mock the env vars) but never working in production:

Feature Hook Status Since
Environment drift detection file_tracker.py Dead — never tracked any agent edits Creation
Teachback enforcement teachback_check.py Dead — can't identify agents Creation
Peer context injection peer_inject.py Dead — exits early without team name Creation
Per-session file tracking track_files.py Broken — 1196 entries across all sessions in unknown.json Creation
S2 drift self-exclusion s2_drift_check.py (PR #340) Degraded — falls back to "unknown" PR #340

Evidence

# Env vars empty for both main process and teammates
$ echo "CLAUDE_CODE_TEAM_NAME=$CLAUDE_CODE_TEAM_NAME"
CLAUDE_CODE_TEAM_NAME=

# track_files.py has accumulated 1196 entries in unknown.json across ALL sessions
$ wc -l ~/.claude/pact-memory/session-tracking/unknown.json
# Contains entries from every session, proving CLAUDE_SESSION_ID has NEVER been set

# PostToolUse hooks DO fire (track_files.py recorded teammate edits)
# but env-var-dependent hooks exit early with no output

Recommended Fix: CLAUDE_ENV_FILE Self-Provisioning

Claude Code provides CLAUDE_ENV_FILE — an official mechanism where SessionStart hooks can write KEY=VALUE pairs that the platform persists as env vars for all subsequent hooks in the session.

Architecture

SessionStart (session_init.py — already runs, already knows team_name + session_id)
    ├── Reads session_id from stdin JSON ✓ (already does this)
    ├── Computes team_name ✓ (already does this)
    └── NEW: Writes to CLAUDE_ENV_FILE:
        PACT_TEAM_NAME=pact-1b41631e
        PACT_SESSION_ID=1b41631e-66e9-...

All subsequent hooks (PostToolUse, PreToolUse, etc.)
    └── Read PACT_TEAM_NAME and PACT_SESSION_ID from env vars
        (platform loads them from CLAUDE_ENV_FILE automatically)

What This Fixes

Variable Source Fix
Team name CLAUDE_ENV_FILE (written by session_init.py) Rename from CLAUDE_CODE_TEAM_NAME to PACT_TEAM_NAME
Session ID CLAUDE_ENV_FILE OR stdin JSON session_id Rename from CLAUDE_SESSION_ID to PACT_SESSION_ID, also read from stdin
Agent name Stdin JSON agent_id → lookup in team config Map agent_id to name via ~/.claude/teams/{team}/config.json members array

What This Doesn't Fix

Agent name (CLAUDE_CODE_AGENT_NAME): The human-readable agent name (e.g., "backend-coder-1") is not available via env vars or stdin JSON. Stdin provides agent_id (UUID) and agent_type (e.g., "pact-backend-coder"), but not the name parameter passed to the Agent tool.

Options for agent identity:
A) Map agent_id → name via team config — read ~/.claude/teams/{team}/config.json which has a members array with {name, agentId, agentType}
B) Use agent_type as proxy — less specific but always available in stdin
C) Self-provision via hook — SubagentStart hook writes agent name to a per-agent file, subsequent hooks read it

Implementation Scope

  1. session_init.py: Write PACT_TEAM_NAME and PACT_SESSION_ID to CLAUDE_ENV_FILE
  2. All affected hooks: Replace os.environ.get("CLAUDE_CODE_TEAM_NAME") with os.environ.get("PACT_TEAM_NAME") (or read from stdin where available)
  3. All affected hooks: Replace os.environ.get("CLAUDE_SESSION_ID") with os.environ.get("PACT_SESSION_ID") or read from stdin session_id
  4. Agent identity hooks: Implement agent_id → name lookup from team config (option A)
  5. Tests: Update all mocked env vars to match new names

Migration

  • Old env var names → new PACT-prefixed names
  • Hooks that currently read env vars → read stdin JSON first, env var fallback
  • Pattern: input_data.get("session_id") or os.environ.get("PACT_SESSION_ID", "")

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions