Session Start Skill
Project-instruction file resolution:
CLAUDE.mdandAGENTS.md(Codex CLI) are transparent aliases — see skills/_shared/instruction-file-resolution.md. All references toCLAUDE.mdin this skill resolve via that precedence rule.
Soul
Before anything else, read and internalize soul.md in this skill directory. It defines WHO you are — your communication style, decision-making philosophy, and values. Every interaction in this session should reflect this identity. You are not a generic assistant; you are a seasoned engineering lead who drives outcomes.
Phase 0: Bootstrap Gate
Read skills/_shared/bootstrap-gate.md and execute the gate check. If the gate is CLOSED, invoke skills/bootstrap/SKILL.md and wait for completion before proceeding. If the gate is OPEN, continue to Phase 1.
Phase 0.5: Parallel-Aware Preamble
Skip silently when
persistence: falsein Session Config.
Before Phase 1, run the parallel-aware preamble per skills/_shared/parallel-aware-preamble.md. The preamble detects other active sessions in the worktree-family, classifies the caller mode against the exclusivity-matrix, and fires the appropriate AUQ on conflict.
This runs BEFORE the local session-lock acquire in Phase 1.2 — the preamble's cross-worktree detection is broader than acquire()'s single-worktree check. When the preamble returns PROMOTION_OFFER and the user picks "Worktree anlegen + starten", Phase 1.2 will be skipped entirely (the new worktree's own session-start performs it).
Outcome handling:
PASS_THROUGH→ continue to Phase 1EXCLUSIVE_BLOCKED→ exit Phase 0 cleanly per the AUQ outcome (Warten/Andere Session beenden/Abbrechen— all three return without initializing STATE.md)PROMOTION_OFFERwith user picking "Worktree anlegen + starten" → callenterWorktree({ basePath, sessionId, branch, repoRoot })fromscripts/lib/autopilot/worktree-pipeline.mjs. Compute params:basePath = path.dirname(repoRoot),sessionIdfrom resolveSemanticSessionId(),branchfrom current HEAD,repoRoot = process.cwd(). On success, exit Phase 0 immediately — the new worktree's own session-start runs from scratch (Phase 1 onwards), Phase 1.2 session-lock-acquire is the new worktree's responsibility. On enterWorktree failure (WorktreeBoundaryErrororgit worktree addnon-zero exit), emit stderr WARNparallel-aware: enterWorktree failed: <err>; falling back to Manuelland proceed via the Manuell path.PROMOTION_OFFERwith user picking "Manuell — in-place daneben" → append Deviation, continue to Phase 1PROMOTION_OFFERwith user picking "Abbrechen" → exit cleanly
Implementation reference: skills/_shared/parallel-aware-preamble.md § Implementation.
AUQ reference: skills/_shared/parallel-aware-auq.md.
Phase 1: Read Session Config
Read and parse Session Config per skills/_shared/config-reading.md. Store result as $CONFIG.
Phase 1.2: Session Lock Acquire (#330)
See also Phase 0.5 (Parallel-Aware Preamble) — the cross-worktree detection runs first. This Phase 1.2 handles the single-worktree local-lock semantics that complement the preamble.
Skip this phase if
persistenceconfig isfalse.
Acquire a distributed session-lock to detect parallel sessions in the same repo before initializing STATE.md. This prevents two concurrent Claude/Codex sessions from stomping each other's wave state and metrics writes.
Mechanical wiring (Epic #583, 2026-05-27): The SessionStart hook (hooks/on-session-start.mjs → hooks/_lib/lock-bootstrap.mjs) now writes .orchestrator/session.lock mechanically BEFORE this skill's prose runs. The prose Phase 1.2 becomes confirmatory — it verifies the lock exists with the expected shape via readLock({ repoRoot: process.cwd() }). Re-call acquire() only if readLock() returns null (mechanical hook failed) OR the existing lock's session_id does not match the current session's id (a rare divergence — surface via AUQ before overwriting). The decision flow below still applies to all three outcomes (active / stale / fs-error) when the prose path needs to acquire.
import { acquire, forceAcquire } from 'scripts/lib/session-lock.mjs';
const result = acquire({ sessionId, mode: sessionType, ttlHours: 4, repoRoot: process.cwd() });
Where sessionId is the session identifier derived from the session type and timestamp (e.g. main-2026-05-08-deep-1), and sessionType is the session mode (housekeeping, feature, or deep).
Decision flow
-
result.ok === true→ lock is held. Continue to Phase 1.5 (Session Continuity). The lock must be released in session-end. -
result.ok === falsewith `reason === 'active'**:- Another Claude/Codex session holds an active lock in this repo.
- Present a choice via
AskUserQuestion:AskUserQuestion({ questions: [{ question: `Another session lock is active in this repo (started ${ageHours}h ago, mode=${existingLock.mode}, host=${existingLock.host}, pid=${existingLock.pid}). How should I proceed?`, header: "Session Lock Conflict", multiSelect: false, options: [ { label: "Abort (Recommended)", description: "Let the other session finish. Safe default — prevents metrics and wave-state corruption." }, { label: "Force-take the lock", description: "Overwrites the active lock. ONLY use if you are certain the other session is no longer running." }, ], }], }); - Codex CLI / Cursor IDE fallback (numbered Markdown list):
Session lock conflict — active lock detected (started <ageHours>h ago, mode=<mode>, host=<host>, pid=<pid>). 1. Abort (Recommended) — let the other session finish. 2. Force-take the lock — ONLY if the other session is known dead. Reply with the number of your choice. - On Abort: exit session-start cleanly with a brief stderr note (
session-lock: aborted — active lock held by session_id=<id>). Do NOT initialize STATE.md. - On Force-take: call
forceAcquire({ sessionId, mode: sessionType, ttlHours: 4, repoRoot: process.cwd() }). After Phase 1.5 initializes STATE.md, append a deviation viaappendDeviation():Force-took session lock from session_id=<existingLock.session_id>, age=<ageHours>h, mode=<existingLock.mode>, pid=<existingLock.pid>. Continue.
-
result.ok === falsewithreason === 'stale-pid-dead'or `'stale-pid-alive'**:- A stale lock was found (TTL expired). Likely left behind by a session that crashed or was force-killed.
- Present a choice via
AskUserQuestion:AskUserQuestion({ questions: [{ question: `Stale session lock found (started ${ageHours}h ago, ttl=${existingLock.ttl_hours}h). Process pid=${existingLock.pid} on host=${existingLock.host} is ${reason === 'stale-pid-dead' ? 'confirmed dead' : 'still running or status unknown'}. Reclaim the lock?`, header: "Stale Session Lock", multiSelect: false, options: [ { label: "Reclaim (Recommended)", description: "Overwrite the stale lock and continue. Safe when the previous session is no longer active." }, { label: "Abort — investigate manually", description: "Stop here. Inspect .orchestrator/session.lock before proceeding." }, ], }], }); - Codex CLI / Cursor IDE fallback (numbered Markdown list):
Stale session lock found (started <ageHours>h ago, ttl=<ttlHours>h, pid=<pid> on <host>). 1. Reclaim (Recommended) — overwrite stale lock and continue. 2. Abort — investigate .orchestrator/session.lock manually. Reply with the number of your choice.