---
title: "Session Start to Stop"
description: "How OrchestKit manages the full session lifecycle, from environment setup and context loading through metrics, compaction, and fire-and-forget cleanup."
canonical: "https://orchestkit.yonyon.ai/docs/hooks/lifecycle-hooks"
---

# Session Start to Stop

How OrchestKit manages the full session lifecycle, from environment setup and context loading through metrics, compaction, and fire-and-forget cleanup.

import { Callout } from "fumadocs-ui/components/callout";
import { Steps, Step } from "fumadocs-ui/components/steps";

## The Session Lifecycle

A Claude Code session is bookended by startup and shutdown. OrchestKit hooks at every boundary to load context, track metrics, coordinate with other instances, and clean up when you leave, all without blocking your workflow.

```
Setup (first run)
  |
  v
SessionStart -----> UserPromptSubmit (repeated) -----> Stop
  |                        |                             |
  |  load context          |  inject memory,             |  fire-and-forget:
  |  check consent         |  detect patterns            |  7 background hooks
  |  enrich PR status      |  track satisfaction         |
  |  prefill guard         |                             v
  v                        v                        SessionEnd
                                                      |
                                                      |  coordination cleanup
                                                      |  metrics summary
                                                      |  pattern sync push
                                                      v
                                                    (done)
```

## Setup Hooks (9 hooks)

Setup hooks run when the plugin is installed or during maintenance windows. They are registered under the `Setup` event in `hooks.json`.

| Hook | Purpose | Runs Once? |
|------|---------|------------|
| `setup/unified-dispatcher` | Fire-and-forget launcher for background setup tasks | No |
| `setup/setup-check` | Validate plugin installation integrity | Yes |
| `setup/first-run-setup` | Create `.claude/` directories, initialize config files | Yes |
| `setup/setup-maintenance` | Periodic cleanup of stale files and caches | No |
| `setup/setup-repair` | Self-heal corrupted configuration files | No |
| `setup/monorepo-detector` | Detect monorepo structure and set workspace context | Yes |

Hooks marked "Runs Once" use the `"once": true` flag in `hooks.json`, so they execute only on the first invocation per session.

## SessionStart Hooks (5 hooks)

When a new session begins, these hooks fire in order:

<Steps>
<Step>
### Unified Dispatcher (fire-and-forget)

Spawns a detached background process that runs startup tasks in parallel without blocking the session:

- **pattern-sync-pull**: Pull learned patterns from previous sessions
- **coordination-init**: Register this instance for multi-instance coordination
- **decision-sync-pull**: Pull decision history
- **dependency-version-check**: Check for outdated dependencies
</Step>

<Step>
### Session Context Loader

Loads session state using Context Protocol 2.0. Reads from:

- `.claude/context/session/state.json`: Previous session state
- `.claude/context/identity.json`: Project identity configuration
- `.claude/context/knowledge/index.json`: Knowledge index
- `.claude/context/session/compaction-manifest.json`: Key decisions and files touched from the last compacted session

If an `AGENT_TYPE` environment variable is set (e.g., from `--from-pr` or agent dispatch), the loader also reads agent-specific configuration from `.claude/agents/{agent-type}.md`.

```typescript
// The compaction manifest bridges sessions across compactions
const manifest = JSON.parse(readFileSync(compactionManifest, 'utf-8'));
process.env.ORCHESTKIT_LAST_SESSION = manifest.sessionId;
process.env.ORCHESTKIT_LAST_DECISIONS = JSON.stringify(manifest.keyDecisions);
```
</Step>

<Step>
### Analytics Consent Check

Verifies that analytics tracking has user consent before enabling telemetry hooks.
</Step>

<Step>
### PR Status Enricher

When a session is started with `claude --from-pr 123`, this hook enriches the context with PR status, review comments, and CI check results.
</Step>

<Step>
### Prefill Guard

Scans skills for deprecated patterns (response prefilling, `output_format` parameter) that are unsupported on Opus 4.6+. If detected, it emits a warning with migration guidance:

```
Opus 4.6 deprecation: 3 skill(s) reference deprecated patterns.
Migration: use structured outputs instead of prefilling;
use output_config.format instead of output_format.
```
</Step>
</Steps>

## SessionEnd Hooks (4 hooks)

When the session formally ends (after Stop hooks have dispatched), these hooks run synchronously:

| Hook | Purpose |
|------|---------|
| `lifecycle/coordination-cleanup` | Unregisters the instance from the coordination database, updates heartbeat status to "stopping", removes `.instance_env` |
| `lifecycle/session-metrics-summary` | Reads session metrics (tool call counts, error counts) and logs a summary |
| `lifecycle/session-cleanup` | Removes temporary files created during the session |
| `lifecycle/pattern-sync-push` | Pushes newly learned patterns to persistent storage |

### Coordination Cleanup in Detail

Multi-instance coordination uses a SQLite database at `.claude/coordination/.claude.db` and heartbeat files at `.claude/coordination/heartbeats/{instance-id}.json`. The cleanup hook:

1. Loads the instance ID from `.claude/.instance_env`
2. Updates the heartbeat file status to `"stopping"`
3. Unregisters the instance from the coordination database (releases all locks)
4. Deletes the `.instance_env` file

```typescript
function updateHeartbeatStatus(projectDir: string, instanceId: string): void {
  const heartbeatFile = `${heartbeatsDir}/${instanceId}.json`;
  const content = JSON.parse(readFileSync(heartbeatFile, 'utf-8'));
  content.status = 'stopping';
  writeFileSync(heartbeatFile, JSON.stringify(content, null, 2));
}
```

## PreCompact Hook (1 hook)

**When:** The context window is about to be compacted (conversation history truncated to fit within token limits).

The `lifecycle/pre-compact-saver` hook preserves critical context that would otherwise be lost during compaction:

- Key decisions made during the session
- List of files touched
- Current task status and dependencies
- Open problems and blockers

This data is written to `.claude/context/session/compaction-manifest.json` and is loaded by the session context loader on the next session start.

## The Fire-and-Forget Pattern

The most important architectural pattern in OrchestKit's lifecycle hooks is **fire-and-forget**. When the user types `/exit`, they should not wait for 7 cleanup hooks to finish. Instead:

```
User types /exit
    |
    v
stop-fire-and-forget.mjs called (stdin: hook input)
    |
    v
Writes work payload to .claude/hooks/pending/stop-{uuid}.json
    |
    v
Spawns detached background-worker.mjs (child.unref())
    |
    v
Returns immediately: {"continue": true, "suppressOutput": true}
    |
    v
Session exits instantly (~50ms)
    |
    v
Background worker picks up the payload and runs 7 hooks in parallel
```

### The 7 Stop Hooks

These hooks run in the background after the session has already exited:

**Core Session (6):**
- `auto-save-context`: Save conversation context for next session
- `session-patterns`: Extract and persist patterns from the session
- `issue-work-summary`: Summarize work done on GitHub issues
- `calibration-persist`: Save calibration metrics
- `session-profile-aggregator`: Aggregate session profile data
- `session-end-tracking`: Final session telemetry

**Memory Sync (2):**
- `graph-queue-sync`: Flush pending graph memory operations
- `workflow-preference-learner`: Learn workflow preferences from session behavior

**Instance Management (3):**
- `multi-instance-cleanup`: Clean up multi-instance coordination state
- `cleanup-instance`: Remove instance registration
- `task-completion-check`: Verify all tasks completed or properly handed off

**Analysis (3):**
- `context-compressor`: Compress session context for storage
- `auto-remember-continuity`: Automatically store important decisions for session continuity
- `security-scan-aggregator`: Aggregate security findings from the session

**Skill Validation (12):**
- `coverage-check`, `evidence-collector`, `coverage-threshold-gate`, `cross-instance-test-validator`, `di-pattern-enforcer`, `duplicate-code-detector`, `eval-metrics-collector`, `migration-validator`, `review-summary-generator`, `security-summary`, `test-pattern-validator`, `test-runner`

**Heavy Analysis (1):**
- `full-test-suite`: Run comprehensive test validation

### Background Worker Safeguards

The background worker (`bin/background-worker.mjs`) includes several safety mechanisms:

- **5-minute timeout**: Self-terminates via `setTimeout` to prevent hangs
- **Orphan cleanup**: Removes temp files in `.claude/hooks/pending/` older than 10 minutes
- **Parallel execution**: All 7 hooks run via `Promise.allSettled` (failures don't block others)
- **Debug logging**: Writes to `.claude/logs/hooks/background-worker.log`

## Multi-Instance Coordination

When multiple Claude Code sessions run on the same project (e.g., one per terminal tab), hooks coordinate through:

1. **Instance registration**: Each session registers with a unique instance ID at startup
2. **Heartbeat files**: Periodic heartbeat updates at `.claude/coordination/heartbeats/`
3. **File locks**: The `multi-instance-lock` PreToolUse hook writes locks atomically (temp file + `renameSync`) to prevent concurrent writes to the same file
4. **Quality gates**: The `multi-instance-quality-gate` checks if another instance is already working on the same area

<Callout type="warn">
**SEC-001 (SQL Injection Prevention):** Instance IDs are validated against `/^[a-zA-Z0-9_\-.:]+$/` before any shell-exec SQLite operations. Invalid IDs are silently rejected.
</Callout>

<Callout type="warn">
**SEC-003 (Atomic File Writes):** Lock files are written to a temp path (`locks.json.{pid}.tmp`) then renamed atomically, preventing TOCTOU race conditions.
</Callout>

## Unified Dispatchers

OrchestKit uses 6 unified dispatchers across the lifecycle. Each is a single `hooks.json` entry that fans out to multiple internal hooks:

| Dispatcher | Event | Internal Hooks | Execution |
|------------|-------|---------------|-----------|
| `lifecycle/unified-dispatcher` | SessionStart | 7 | Fire-and-forget |
| `posttool/unified-dispatcher` | PostToolUse | 7 | Fire-and-forget |
| `stop-fire-and-forget.mjs` | Stop | 7 | Detached background worker |
| `subagent-stop/unified-dispatcher` | SubagentStop | varies | Fire-and-forget |
| `notification/unified-dispatcher` | Notification | varies | Fire-and-forget |
| `setup/unified-dispatcher` | Setup | varies | Fire-and-forget |

The fire-and-forget pattern is used exclusively for non-blocking operations: analytics, network I/O, and cleanup tasks that can fail silently without impacting the user's workflow.
