Session Start Skill

SkillFiles & storage

Use this skill when initializing a session for any project repo. Autonomously analyzes git state, VCS issues, SSOT files, branches, environment, and cross-repo status. Then presents structured findings with recommendations for user alignment before creating a wave plan. Triggered by /session [housekeeping|feature|deep] command.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Session Start Skill skill

What this skill tells your AI

The instructions your AI receives, as published by kanevry/session-orchestrator in skills/session-start/SKILL.md and read by ahel’s review.

Project-instruction file resolution: CLAUDE.md and AGENTS.md (Codex CLI) are transparent aliases — see skills/_shared/instruction-file-resolution.md. All references to CLAUDE.md in 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.

Then set the output level. Read ~/.config/session-orchestrator/owner.yaml and take efficiency.output-level (lite | full | ultra), efficiency.preamble, and tone.style. If the file is missing, unreadable, or a key is absent, use the defaults full / minimal / neutral. Apply the matching ### output-level: <value> block from soul.md § Output Levels for the whole session — its line budgets are binding, not advisory, and § "Never traded for brevity" names what they may never cut.

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: false in 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 1
  • PASS_THROUGH with a non-empty advisory array (GH#67) → a discovered peer with lockSuperseded: true never fires the Promotion AUQ (it stays visible, per the #1085 advisory-lock contract — it is not filtered). Print ONE advisory line per entry: parallel-aware: registry entry <sessionId> (last heartbeat <N> min ago) is superseded by this root's live lock <lockOwnerId> — likely a finished task on a platform without SessionEnd (GH#67); still counted for PSA-001 awareness, then continue to Phase 1. It remains PSA-002-relevant if the same id also shows up in STATE.md (source: 'state-md' is handled by Phase 1.2.1 unchanged).
  • EXCLUSIVE_BLOCKED → exit Phase 0 cleanly per the AUQ outcome (Warten / Andere Session beenden / Abbrechen — all three return without initializing STATE.md)
  • PROMOTION_OFFER with user picking "Worktree anlegen + starten" → call enterWorktree({ basePath, sessionId, branch, repoRoot, rawSessionId, reason: 'worktree-promotion' }) from scripts/lib/autopilot/worktree-pipeline.mjs — since #1170 this ONE call does both jobs: it creates the destination worktree AND, because rawSessionId is supplied, releases the source root internally (see below), so no separate leaveSourceRoot call is made at this site. Compute params: basePath = path.dirname(repoRoot), sessionId from resolveSemanticSessionId() for the worktree-name attribution label only, branch from current HEAD, repoRoot = process.cwd(), rawSessionId from readLock({ repoRoot }).session_id. sessionId (the semantic label) is not a lock/registry ownership key; the new worktree's Phase 1.2 obtains its own physical raw session_id. Because branch is the current HEAD it is normally checked out by repoRoot already, so enterWorktree treats it as a start point only and lands the promoted worktree on a fresh so/<sessionId> branch, returning { branch: 'so/<sessionId>', promotedFrom: '<branch>' } (#1067) — the new worktree's STATE.md branch MUST record so/<sessionId> and note promoted from <branch>@<repoRoot>, never the source branch alone. rawSessionId is the RAW physical session_id read from this root's .orchestrator/session.lock via readLock({ repoRoot }) — never the semantic label, and never the id in current-session.json, which may describe a peer session (#863); a wrong id aborts the internal leaveSourceRoot() teardown with left.ok: false, reason: 'lock-session-mismatch:<owner>' and removes nothing. The promotion is a PROCESS BOUNDARY, not a live migration (#1069): the old root is deregistered and its session.lock released BEFORE the new worktree's own Phase 1.2 acquires, so the two roots never both own a live claim at once. enterWorktree()'s return value carries the outcome as left: { ok, steps, reason? }; leaveSourceRoot() never throws, so on left.ok !== true enterWorktree itself emits the stderr WARN enterWorktree: leaveSourceRoot: <reason> and the promotion continues regardless — the destination worktree already exists by the time this runs, so aborting here would leave exactly the two-live-roots state the call prevents. Then 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 (WorktreeBoundaryError or git worktree add non-zero exit), emit stderr WARN parallel-aware: enterWorktree failed: <err>; falling back to Manuell and proceed via the Manuell path.
  • PROMOTION_OFFER with user picking "Manuell — in-place daneben" → append Deviation, continue to Phase 1
  • PROMOTION_OFFER with 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.05: Skill-Invocation Self-Report (#1199)

Emit an L1 skill-invocation record for session-start itself. The PreToolUse Skill-matcher hook only captures skills dispatched via the Skill tool — a prose-invoked skill like this one is invisible to it (verified gap: external users show 0/20 sessions with a session-start row in skill-invocations.jsonl, vs. 93/93 for the operator). This self-report closes that gap so L2/L3 skill-health has a session-start selection signal. Best-effort, try/catch-silent — it never blocks Phase 1.1.

try {
  const { appendSkillInvocation, DEFAULT_SKILL_INVOCATIONS_PATH } =
    await import('${PLUGIN_ROOT}/scripts/lib/skill-invocations-schema.mjs');
  const nodePath = await import('node:path');
  await appendSkillInvocation(nodePath.join(process.cwd(), DEFAULT_SKILL_INVOCATIONS_PATH), {
    timestamp: new Date().toISOString(),
    event: 'selected',
    skill: 'session-orchestrator:session-start',
    session_id: null,   // no session.lock is bound yet at Phase 1 — the raw id is acquired at Phase 1.2 (#1199)
    phase: 'session-start',
  });
} catch { /* self-report is advisory — never blocks Phase 1.1 */ }

Phase 1.1: Dispatcher-Autonomy Migration Capture (one-time, per-repo)

Runs after Phase 1, before Phase 1.2. Fires exactly once per repo — only when the committed ## Dispatcher Autonomy H2 is ABSENT from CLAUDE.md (raw presence check via isDispatcherAutonomyBlockPresent, never the resolved value); skip silently when no committed CLAUDE.md exists. One AUQ, then the committed block is written and never re-asked. Full procedure: references/phase-1-1-dispatcher-autonomy-capture.md.

Phase 1.2: Session Lock Acquire (#330)

Skip when persistence: false. Confirmatory since Epic #583 — hooks/on-session-start.mjs writes .orchestrator/session.lock mechanically; this phase verifies it via readLock({ repoRoot }) and re-calls acquire() only when the lock is null or its raw session_id does not match. Active / stale / fs-error decision flow, forceAcquire() on user consent, and cross-host behaviour: references/phase-1-2-session-lock.md.

Phase 1.2.1: Peer-Guard (Epic #583 defense-in-depth)

Skip when persistence: false. After 1.2, findPeers(repoRoot, { mySessionId }) re-checks the STATE.md surface for a live peer the lock missed; a source: 'state-md' peer fires the Worktree-Promotion AUQ instead of overwriting STATE.md. SOFT-GATE (operator may override), fail-open on read errors. Full decision flow: references/phase-1-2-session-lock.md § Phase 1.2.1.

Phase 1.5: Session Continuity

Skip when persistence: false. Reads <state-dir>/STATE.md (stale when its branch ≠ current HEAD) and branches on status:active/paused → resume AUQ + Snapshot Recovery; completed → Recommendations Banner, then Idle Reset. Every surfaced prior-session record MUST carry the #621 HISTORICAL guard banner (SSOT scripts/lib/historical-guard.mjs). Full procedure incl. Recommendations Banner, Idle Reset (which PRESERVES ## What Not To Retry), Snapshot Recovery (#196) and the Current-Task Banner (#184): references/phase-1-5-session-continuity.md. The same file carries the STATE.md-init rule for the ultradeep alias (setSessionProfilesession-type stays deep; absence is the contract for every other argument).

Phase 1.6: Metrics Initialization

Skip if persistence config is false.

  1. Ensure '.orchestrator/metrics/' directory exists in the project root (create if missing). For backward compatibility with pre-v2.0 sessions, also check the platform's legacy metrics directory (<state-dir>/metrics/ where <state-dir> is .claude/, .codex/, or .cursor/ per platform).
  2. If '.orchestrator/metrics/sessions.jsonl' exists, count lines to determine number of previous sessions. If not found, check <state-dir>/metrics/sessions.jsonl as a platform-specific legacy fallback.
  3. Store the count for display in Phase 7 — this feeds the Historical Trends section

Phase 1.7: Vault Live-Status Board (#674)

Skip silently unless vault-integration.enabled: true in Session Config. Marks THIS repo in-progress on <vault-dir>/01-projects/_active-sessions.md via sweepBoard() (scripts/lib/vault-status/board-writer.mjs) and force-closes crashed rows host-wide via enumerateCandidates(); generator-marked, idempotent, never touches _overview.md, non-blocking (falls back to single-repo mirrorBoard()). Full procedure: references/phase-1-7-vault-status-board.md.

Phase 2: Git Analysis (parallel)

Run these checks as ONE parallel Bash block — background the independent git ops with & and wait:

# Refresh remote-tracking refs BEFORE reading them. Without this, `origin/main`
# is a snapshot from the last fetch or clone, and every ahead/behind derivation
# below silently compares against stale data — a repo can read "in sync" while
# the real remote is many commits ahead. Best-effort and non-blocking: connect
# timeouts are bounded (no `timeout(1)` — it is absent on macOS by default) and
# any failure (offline, no remote, auth prompt) falls through to `|| true`,
# leaving the previous behaviour of reading whatever refs are on disk.
GIT_SSH_COMMAND='ssh -o ConnectTimeout=5 -o BatchMode=yes' \
  git -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=5 \
  fetch --quiet --prune 2>/dev/null || true

# Independent ops — launch in parallel, collect output via tmpfiles
git branch -a > /tmp/so-branches.$$ &
git log --oneline -N > /tmp/so-commits.$$ &        # N from Session Config `recent-commits` (default 20)
git status --short > /tmp/so-status.$$ &
# `--left-right --count A...B` emits "<behind>\t<ahead>": commits reachable only
# from origin/main, then only from HEAD. The older `git log origin/main..HEAD`
# form could express ahead ONLY, so "behind" was structurally unreportable.
git rev-list --left-right --count origin/main...HEAD > /tmp/so-divergence.$$ 2>/dev/null &
wait
# Then read the 4 tmpfiles in a single step and derive: branch state, recent commits,
# unpushed/uncommitted, open branches. Clean up tmpfiles once derivations are done:
rm -f /tmp/so-branches.$$ /tmp/so-commits.$$ /tmp/so-status.$$ /tmp/so-divergence.$$

Checks to run (derived from the collected output):

  1. Branch state: current branch (from branch -a), ahead/behind origin (from the divergence tmpfile — field 1 is behind, field 2 is ahead). Report BOTH directions. A non-zero behind count means the local branch is missing remote work: surface it, because agents reading repo instructions from a stale checkout will follow superseded guidance. An empty divergence tmpfile means no origin/main ref resolved (no remote, or a differently-named default branch) — report that as unknown, never as zero.
  2. Recent commits: parse commits tmpfile — identify last session's work by commit patterns
  3. Unpushed/uncommitted: status tmpfile + the ahead field of the divergence tmpfile combined
  4. Open branches: parse branch -a tmpfile, identify which are mergeable to develop/main
  5. Stale branches: run AFTER the parallel block — requires iterating over branches (depends on branch -a output). Use git log -1 --format=%ct <branch> per branch; flag those with no commits in more than stale-branch-days (default: 7) days.

Rationale: The 4 independent ops are I/O-bound — running them in parallel cuts Phase 2 wall-clock from ~500ms to ~150ms. The stale-branches check depends on the branch list, so it runs after wait.

Phase 2.5: Docs Planning (Docs-Orchestrator Integration)

Skip this phase if docs-orchestrator.enabled config is not true (default: false).

Reads the docs-orchestrator config fields, auto-detects which audiences (user/dev/vault) are affected by the current scope using signals from Phases 2–5, confirms the selection with the user via AskUserQuestion, and emits a ### Docs Planning Result (Phase 2.5) block into the conversation context. That block is the MANDATORY contract consumed by session-plan Step 1.8 to seed Docs-role tasks. Audience → file-pattern mapping is the authoritative source at skills/docs-orchestrator/audience-mapping.md. Contains non-overlap discipline rules (paths owned by vault-mirror and daily are off-limits).

See phase-2-5-docs-planning.md for full details.

Phase 2.6: Steering Docs Loading

Skip this phase silently when .orchestrator/steering/ does not exist in the project root. This mirrors Phase 2.5's silent-no-op pattern — backward compatibility with repos that have not yet scaffolded steering docs.

Check for the steering directory and load all three docs if present:

STEERING_DIR=".orchestrator/steering"
if [ -d "$STEERING_DIR" ]; then
  PRODUCT_MD=""
  TECH_MD=""
  STRUCTURE_MD=""
  [ -f "$STEERING_DIR/product.md" ]   && PRODUCT_MD=$(cat "$STEERING_DIR/product.md")
  [ -f "$STEERING_DIR/tech.md" ]      && TECH_MD=$(cat "$STEERING_DIR/tech.md")
  [ -f "$STEERING_DIR/structure.md" ] && STRUCTURE_MD=$(cat "$STEERING_DIR/structure.md")
fi

When at least one file is non-empty, inject the following Steering Context banner into the conversation context before Phase 3. This gives Phase 3 (VCS Deep Dive) and subsequent phases stable product/tech/structure facts without re-reading CLAUDE.md:

--- Steering Context ---
[product.md contents — mission, target users, in-scope, out-of-scope]
[tech.md contents — stack, commands, constraints]
[structure.md contents — directory map, inventory, key skills]
--- End Steering Context ---

If .orchestrator/steering/ is absent or all three files are empty, proceed directly to Phase 3 with no banner and no warning. Do not treat missing steering docs as an error.

See .orchestrator/steering/{product,tech,structure}.md for file contents.

Phase 2.7: GitLab Portfolio Snapshot (#41)

Skip silently unless gitlab-portfolio.enabled: true AND vault-integration.enabled: true with a non-empty vault-dir AND gitlab-portfolio.mode != off. Dry-run only — renders a compact portfolio health banner via scripts/lib/gitlab-portfolio/cli.mjs --dry-run inside an 8s budget, writes no file and never blocks session-start (the write path belongs to /portfolio). Full procedure incl. banner rendering, failure behaviour and performance budget: references/phase-2-7-portfolio-snapshot.md.

Phase 3: VCS Deep Dive (parallel)

VCS Reference: Detect the VCS platform per the "VCS Auto-Detection" section of the gitlab-ops skill. Use CLI commands per the "Common CLI Commands" section. For cross-project queries, see "Dynamic Project Resolution."

Using the detected VCS CLI, query (reading issue-limit from Session Config, default: 50):

  1. Open issues — categorize by priority and status labels
  2. Recently closed — what was done since last session
  3. Milestones — active sprint status
  4. Open MRs/PRs — anything waiting for review/merge
  5. Pipeline/CI status — is CI green?

Group issues by:

  • priority::critical / priority::high — must-address
  • status:ready — ready to work on
  • Session-type relevance (housekeeping tasks vs feature tasks vs deep-work tasks)

Phase 4: SSOT & Environment Check

Always runs; every finding is a NON-BLOCKING banner in the Session Overview (never a gate — the Full Gate is the Quality wave's job). Covers SSOT freshness, the Baseline quality commands (resolved .orchestrator/policy/quality-gates.json → Session Config → defaults, each availability-checked with command -v), Pencil design status, plugin + bootstrap.lock freshness, and the banner-probe family registered in scripts/lib/session-start-probes.mjs (vault-staleness, telemetry-flush-health, ci-status, qg-command-drift, peer-cards, loop-readiness, instruction-budget, reconcile-nudge, sessions-staleness, sessions-integrity, owner-config, moc-staleness, context-coverage, claude-md-budget-lint, tests:src-ratio, project-hygiene, mirror-issues, git-config-drift). Per-probe module path, return contract and exact banner wording: references/phase-4-ssot-environment-check.md.

Phase 4.5: Resource Health (v3.1.0)

Skip this phase if resource-awareness: false in Session Config.

Reads .orchestrator/host.json and runs a live resource snapshot via resource-probe.mjs. Computes a green/warn/critical verdict against configurable thresholds (RAM, CPU, concurrent Claude processes, SSH). On warn/critical, presents an AskUserQuestion prompt to apply the recommended agents-per-wave cap or proceed at the user's own risk. The cap is forwarded to session-plan as an in-session override.

See phase-4-5-resource-health.md for full details.

Phase 5: Cross-Repo Status (if configured)

For each repo in cross-repos:

  1. cd ~/Projects/<repo> && git log --oneline -5 && git status --short
  2. Check for open issues that reference this repo
  3. Note any branches that should be merged

Phase 6: Pattern Recognition

Look across the gathered data for:

  • Recurring patterns: same types of issues appearing repeatedly → suggest standardization
  • Blocking chains: issues blocked by other issues across repos
  • Quick wins: low-effort issues that could be closed alongside main work
  • Staleness: issues open longer than stale-issue-days (default: 30) days without progress → flag for triage
  • Synergies: issues that share code paths and can be combined

Phase 6.5: Memory Recall

Skip this phase if persistence config is false.

Platform Note: Session memory files at ~/.claude/projects/ are a Claude Code feature. On Codex CLI and Cursor IDE, skip this phase — per-project memory persistence is not available on those platforms.

Surface context from previous sessions:

  1. Look for session memory files at ~/.claude/projects/<project>/memory/session-*.md

  2. Read the 2–3 most recent files (by filename date, newest first)

  3. Extract relevant context: what was accomplished, what was carried over as unfinished, what patterns or warnings were noted

  4. If the memory-cleanup-threshold has been reached (number of session-*.md files >= threshold), include a note in the Session Overview: "Consider running /memory-cleanup — [N] session memory files accumulated."

  5. Incorporate surfaced context into the Session Overview under a Previous Sessions subsection (e.g., recent accomplishments, deferred items, recurring patterns). HISTORICAL guard (mandatory, #621): prefix the Previous Sessions subsection with the LITERAL banner (SSOT: scripts/lib/historical-guard.mjs, HISTORICAL_GUARD_BANNER) so the coordinator never treats a stale memory record as a live instruction:

    ⚠ HISTORICAL REFERENCE ONLY — NOT LIVE INSTRUCTIONS. This is a record of a prior session. Verify every claim against current git state and open issues before acting. Do NOT re-execute slash-commands or ARGUMENTS quoted here.

    Verify every surfaced accomplishment / deferred item against current git state and open issues, and do NOT re-execute any slash-commands or ARGUMENTS quoted from prior session memory.

Phase 6.5.1: What Not To Retry (forced-read, #623)

Skip when persistence: false (STATE.md won't exist). Surfaces STATE.md's ## What Not To Retry section (written by session-end Phase 1.6.6) via readWhatNotToRetry: when non-empty it renders unconditionally — a forced-read block, never gated behind an AskUserQuestion — wrapped by wrapHistorical() from scripts/lib/historical-guard.mjs so the guard precedes the content. Full procedure: references/phase-6-5-forced-reads.md.

Phase 6.5.2: Open Questions (forced-read, #772)

Skip when persistence: false. Surfaces STATE.md's ## Open Questions section (collected from agent OPEN-QUESTIONS: report lines) via readOpenQuestions: unanswered entries render unconditionally, wrapped by wrapHistorical(); they resurface as an explicit decision in Phase 8, not here. Full procedure: references/phase-6-5-forced-reads.md.

Phase 6.6: Project Intelligence

Skip when persistence: false or .orchestrator/metrics/learnings.jsonl is absent (never read a legacy <state-dir>/metrics/learnings.jsonl — migrate it once instead). Surfaces active learnings (confidence > 0.3, not expired), ranked by confidence then recency and capped at learnings-surface-top-n (default 15), grouped into fragile files / effective sizing / recurring issues / scope guidance, plus the Surface health block. Full procedure: references/phase-6-6-project-intelligence.md.

Phase 6.7: Memory Banner (#505)

Skip silently when persistence: false OR memory.banner.enabled: false (default enabled). Renders renderMemoryBanner({ repoRoot, config }) from scripts/lib/memory-banner.mjs to user-facing stdout — a compact summary of what session-start loaded from persistent memory. Full procedure incl. behaviour summary and implementation notes: references/phase-6-7-memory-banner-telemetry-consent.md.

Phase 6.8: Telemetry Consent (one-time, #845)

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
50
Forks
7
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
session-start-kanevry
Source
github.com/kanevry/session-orchestrator