본문으로 건너뛰기
cccskills
무료GitHub에 공개

session-end

Use this skill when performing a full session close-out: verifies all planned work against the agreed plan, creates issues for gaps, runs quality gates, commits cleanly, mirrors to GitHub, and produces a session summary. Triggered by /close command.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Kanevry/session-orchestrator스타 52(GitHub 52 + cccskills 0)
연동 도구GitHub

실행 점수 미검증

설치 방법 보기

포함된 파일(19)

  • SKILL.md40.9 KB
  • discovery-scan.md3.3 KB
  • drift-operations.md10.4 KB
  • learning-patterns.md9.8 KB
  • metrics-collection.md20.9 KB
  • phase-3-2-docs-verification.md7.9 KB
  • phase-3-6-tail.md42.2 KB
  • phase-3-7a-recommendations.md8.2 KB
  • plan-verification.md36.8 KB
  • references/phase-2-quality-gate.md11.3 KB
  • references/phase-3-7e-vault-orphans.md4.5 KB
  • references/phase-3-documentation-updates.md25.1 KB
  • references/phase-4a-worktree-cleanup.md10.9 KB
  • references/phase-4b-worktree-orphan-sweep.md5.3 KB
  • references/phase-5-issue-cleanup.md14.0 KB
  • references/session-summary-template.md4.8 KB
  • session-metrics-write.md24.6 KB
  • vault-operations.md4.5 KB
  • verification-checklist.md1.6 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Session End Skill

Platform Note: State files (STATE.md, wave-scope.json) live in the platform's native directory: .claude/ (Claude Code), .codex/ (Codex CLI), .cursor/ (Cursor IDE), or .pi/ (Pi). All references to .claude/ below should use the platform's state directory. Shared metrics live in .orchestrator/metrics/. See skills/_shared/platform-tools.md.

Project-instruction file: 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.

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.

<HARD-GATE> Do NOT proceed past Phase 0 if GATE_CLOSED. There is no bypass. Refer to `skills/_shared/bootstrap-gate.md` for the full HARD-GATE constraints. </HARD-GATE>

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 via findPeers(repoRoot, { mySessionId }), classifies the caller's mode via classifyMode(callerMode) against the exclusivity-matrix, and fires the appropriate AUQ on conflict.

Outcome handling:

  • PASS_THROUGH → continue to Phase 1
  • EXCLUSIVE_BLOCKED → exit Phase 0 cleanly per the AUQ outcome
  • PROMOTION_OFFER → user picks Worktree-Promotion (see parallel-aware-auq.md outcome-handling — calls enterWorktree()), in-place + Deviation, or Abbrechen

For session-end specifically: the preamble is DETECTION-ONLY. The lock-release path in later phases keeps its current behavior — releasing the OWN session's lock requires no matrix consultation.

Implementation reference: skills/_shared/parallel-aware-preamble.md § Implementation. AUQ reference: skills/_shared/parallel-aware-auq.md.

Phase 0.6: Skill-Invocation Self-Report (#724, C4)

Emit an L1 skill-invocation record for session-end 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: zero session-end rows in skill-invocations.jsonl despite many closed sessions). This self-report closes that gap so L2/L3 skill-health has a session-end selection signal. Best-effort, try/catch-silent — it never blocks the close.

try {
  const { pathToFileURL } = await import('node:url');
  const { appendSkillInvocation, DEFAULT_SKILL_INVOCATIONS_PATH } =
    await import(pathToFileURL('${PLUGIN_ROOT}/scripts/lib/skill-invocations-schema.mjs').href);
  const { resolveLedgerPath } =
    await import(pathToFileURL('${PLUGIN_ROOT}/scripts/lib/ledger-path.mjs').href);
  // In a linked worktree the main checkout's ledger when it is gitignored there,
  // so the record survives the worktree (#1722).
  await appendSkillInvocation(resolveLedgerPath(DEFAULT_SKILL_INVOCATIONS_PATH, process.cwd()), {
    timestamp: new Date().toISOString(),
    event: 'selected',
    skill: 'session-orchestrator:session-end',
    session_id: sessionId ?? null,   // from session.lock `session_id`, when available
    phase: null,
  });
} catch { /* self-report is advisory — never block the close */ }

Phase 1: Plan Verification

Always runs, first. Reads back the agreed plan and dispositions every item: 1.1 Done (verify with evidence) + 1.1a File-Level Grounding + 1.1b External Acceptance (open [Abnahme] issues, closed only on oracle evidence), 1.2 / 1.3 / 1.4 which COLLECT carryover candidates and file nothing (#769), 1.5 Discovery Scan, 1.6 Safety Review + 1.6.6 "What Not To Retry", then 1.65 Handover Alignment Gate — the only place [Carryover] filing is authorized to originate, fail-open to "everything carries" — followed by 1.7 Metrics Collection, 1.8 Session Review, and 1.9 / 1.10 Mission-Status classification + breakdown. Full procedure: plan-verification.md.

Phase 2: Quality Gate

Always runs and is BLOCKING — do NOT commit broken code. Runs every check in verification-checklist.md, then Phase 2.0a Echo-Stub Detection (GH #42 — a stubbed: {} entry from gate-full.mjs is a FAILED gate, never a pass), 2.1 Vault Validation, 2.2 CLAUDE.md (or AGENTS.md) Drift Check and 2.3 Vault Staleness Check — each gated by its own Session Config key. Full procedure incl. the per-mode routing matrices: references/phase-2-quality-gate.md.

Phase 2.4: Security-Delta (#1769)

Opt-in via security-delta.mode (off | warn | strict, default off); skip silently when off or absent. Runs scripts/security-delta.mjs on $SESSION_RANGE and routes its JSON: pass → one Session Summary line, warn → findings listed + offered as carryover candidates, block (strict, exit 3) → AUQ like Phase 2.5 hard. Unreadable output counts as degraded, never clean. Full procedure: references/phase-2-quality-gate.md § 2.4.

Phase 2.5: Custom Phases (#637)

Opt-in. Skip this phase entirely if custom-phases in $CONFIG is absent or [] (the default).

Repos declare deterministic close/housekeeping phases as a contract (not the freeform special: convention): each phase runs a command with exit-code gating and Final-Report reporting. The block is parsed by scripts/lib/config/custom-phases.mjs; each record is { name, when, command, mode, review } (already validated — unsafe records were dropped at parse time).

Step 1 — Read + filter by when

Read custom-phases from $CONFIG and the session-type from STATE.md frontmatter (feature | deep | housekeeping | none):

  • If session-type === 'housekeeping': keep phases with when ∈ {housekeeping, both}.
  • Otherwise (feature/deep/any other): keep phases with when ∈ {session-end, both}.

If no phases remain after filtering, skip to Phase 3.

Step 2 — Run each phase in declaration order

For each kept phase:

  • mode === 'off' ⇒ skip silently (do not run the command).
  • Otherwise run command via Bash. Capture the exit code and the last ~10 lines of stdout (these become the report summary — do NOT inline the full output).
  • If review is set, read that file after the command as the review step and note its path in the report.

Step 3 — Route by mode

  • mode === 'warn' (default): record the result (name, exit code, summary) for the Phase 6 Final Report "Custom Phases" line. Never block the close — even on a non-zero exit.
  • mode === 'hard':
    • exit code 0 ⇒ continue; record <name>: pass (mode=hard).
    • exit code ≠ 0 ⇒ BLOCK the close using the same routing pattern as Phase 2.3 strict-mode. mode: hard here is an operator-declared repo contract (the repo deliberately chose mode: hard), so the block semantics are preserved — but the AUQ now ALSO offers a warn + carryover escape hatch. Present the phase name + captured summary and offer:
      • On Claude Code: AskUserQuestion with options:
        1. "Fix and retry Phase 2.5" (Recommended) — exit close, let the user investigate.
        2. "Warn + carryover and close" — file a carryover issue (labels carryover, priority::high) titled [Carryover] custom-phase '<name>' (mode=hard) exited <code> capturing the phase name + captured summary for a follow-up session, log the Deviation entry, then continue the close.
        3. "Override and close" — proceed, log a Deviation entry in STATE.md ## Deviations: - [<ISO timestamp>] Phase 2.5: custom-phase '<name>' (mode=hard) exited <code>, overridden by user. In addition to the Deviation entry, emit an override-ratio event so the override feeds the override_ratio metric (#730/H5): node scripts/emit-event.mjs --type orchestrator.finding.overridden --payload '{"phase":"2.5","kind":"custom-phase-hard","count":N}'.
        4. "Abort close" — exit close without writing.
      • On Codex CLI / Cursor IDE: same options as a numbered Markdown list.

A hard-fail (whether overridden or not) ALWAYS appends its result line to STATE.md ## Deviations; warn-mode results do not.

Step 4 — Surface to closing report

Pass each phase result (name, mode, exitCode, summary, review?) forward to the Phase 6 Final Report "Custom Phases" line (see Phase 6 below).

Phase 2.6: Broken-Window Budget (#730/H5)

Opt-in via broken-window-budget.enabled in Session Config (default false). Skip silently when disabled.

Assemble the in-memory "knowingly-broken shipment" list from THIS session's already-computed results — no new detection logic, only aggregation:

  1. Phase 2.0a stub findings (result.stubbed) that shipped anyway under enforcement: warn.
  2. Phase 2.3 / 2.4 / 2.5 "Override and close" choices (reuse each entry's Deviation-log payload verbatim).
  3. Phase 1.8 MED/LOW findings routed to "Unresolved Review Findings" (#617).
  4. Wave-level reviewer findings overridden without a fix task (## Deviations entries matching reviewer finding overridden — written by wave-executor §5/5a).

For EACH item: file a hard-terminated closure issue via createBrokenWindowIssue() from scripts/lib/spiral-carryover.mjs — labels broken-window + priority::high, due-date = today + broken-window-budget.due-days (default 7; glab native --due-date, gh fallback: Due: <date> as first body line — GitHub has no native due-date field). Idempotent per task-hash — re-running a close never duplicates issues.

Emit ONE event per filed issue (note: event-name segments use underscores, never hyphens):

node scripts/emit-event.mjs --type orchestrator.broken_window.filed --payload \
  "$(node -e "process.stdout.write(JSON.stringify({source:'<2.0a|2.3|2.5|1.8|wave-override>', issue:<IID>, due:'<YYYY-MM-DD>'}))")"

Non-blocking: a filing failure is a WARN, never blocks the close (same fail-open discipline as createSpiralCarryoverIssue).

Phase 3: Documentation Updates

Always runs. Refreshes the session-lock heartbeat at entry (updateHeartbeat, #590-3 — skip when persistence: false) so a long close-out cannot lapse the 4h TTL, then walks 3.0 Defensive Cleanup → 3.1 SSOT files → 3.2 Docs Verification → 3.2a Session Handover → 3.3 Claude-rules freshness → 3.4/3.4a STATE.md write + coordinator-snapshot cleanup → 3.45 Telemetry Flush → 3.5/3.5a/3.6.x session memory, learning extraction and the mechanical skip-plan tail → 3.7/3.7a/3.7b/3.7c/3.7d/3.7e metrics write, recommendations, durable commit (#490), vault board → closed, session-eval, vault orphans named (#1563). Full procedure: references/phase-3-documentation-updates.md.

Phase 3.8: Session Lock Release (#330)

Gate: Only run if persistence is true in Session Config. Skip silently otherwise.

After STATE.md is finalized with status: completed (Phase 3.4) and Recommendations are written (Phase 3.7a), release the distributed session-lock with ONE verifying command:

node scripts/release-session-lock.mjs --session-id "<raw session_id>" --json

--session-id is the PHYSICAL raw value established by session-start Phase 1.2 and stored in .orchestrator/session.lock session_id — never STATE.md session: and never semantic_session_id, both of which are attribution labels.

No --repo-root: the command looks for the checkout whose lock carries that id itself (#1730) — the registry's session_root, the cwd's checkout, its main checkout, a registered linked worktree — and names it in the JSON (repo_root, root_source). Before, it read only the cwd, answered absent with exit 0 when the lock sat in the other checkout, and the lock stayed.

The command releases the lock only when that raw id owns it, emits the terminal orchestrator.session.lock.released breadcrumb, and then RE-READS the lock path: exit 0 means the lock is provably gone. It replaces the hand-executed release() this phase used to prescribe, which deleted the lock silently — the SessionEnd hook then found status: 'absent' and emitted nothing either, so a lock lifecycle ended with no terminal event at all (#1395: 5 lock.acquired against 0 lock.released in this repo). outcome: "absent" is a normal exit-0 result (idempotent re-run; nothing was released, so no event is written). A verified release also leaves .orchestrator/runtime/lock-released/<raw session_id>.json (release_marker: true in the JSON), so a later compact or resume of the same raw session does not re-create the lock (#1651); startup and clear take a lock as before, and the session's next acquire() removes the marker.

A non-zero exit is a WARN, never a blocker — log ⚠ session-lock: <outcome> and continue; the TTL provides automatic expiry for the next session:

  • exit 1 — the lock is owned by a different raw session id, so nothing was touched. That is ambiguous by design: do not retry with an equal semantic_session_id, STATE.md session, or owner proof. Leave that live lock for its TTL/Reaper lifecycle.
  • exit 2 — system error (unreadable/corrupt lock, owner-proof mismatch, the lock still present after release, or the breadcrumb could not be written).

The lock is released here — AFTER all STATE.md writes are complete and BEFORE the commit is staged in Phase 4.1. This ordering ensures a clean handover when the current raw owner releases it: the lock file is absent from the working tree when the commit is assembled, so it is not accidentally staged.

Phase 4: Commit & Push

4.1 Stage Changes

  • Stage files individually: git add <file> — NEVER git add . or git add -A
  • Always stage these session artifacts (if modified):
    • .orchestrator/metrics/sessions.jsonl (session summary from Phase 3.7)
    • .orchestrator/metrics/learnings.jsonl (learnings from Phase 3.6)
    • .orchestrator/metrics/eval.jsonl (eval record from Phase 3.7d, if modified — note: in repos where metrics are gitignored this is a no-op)
    • <state-dir>/STATE.md (session state, if persistence enabled)
    • Any files created or modified by wave agents
  • Review staged changes: git diff --cached — verify every change is from THIS session
  • If you see changes you did NOT make, ask the user (parallel session awareness)

4.2 Commit

Use Conventional Commits format:

type(scope): description

- [bullet points of what changed]
- Closes #IID1, #IID2 (if applicable — never an `[Abnahme]` IID, plan-verification 1.1b)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

For sessions with many changes, prefer ONE commit per logical unit (not one mega-commit).

4.3 Push

git push origin HEAD

4.4 GitHub Mirror (if configured in Session Config)

Three states, three DISTINGUISHABLE outcomes. The predecessor of this block (git remote get-url github 2>/dev/null && git push github HEAD 2>/dev/null || echo "GitHub mirror: not configured") collapsed a failed push into GitHub mirror: not configured and exited 0 — git's real error went to /dev/null, so a broken mirror was indistinguishable from an unconfigured one (.claude/rules/bash-harness-pitfalls.md — "Silence is not success"). That matters more once anything is wired to the mirror (e.g. a Vercel Git deploy): a silently-failing push means the downstream artifact never updates and nobody is told.

Run it verbatim — tests/skills/session-end/github-mirror-push.test.mjs extracts the block between the markers and executes it, so no second copy of this command may exist.

# --- github-mirror-push:begin ---
# The 'github' remote is checked FIRST: without one there is nothing to mirror,
# so a consumer repo (even one without a Session Config) skips with exit 0.
# With a remote, Session Config `mirror` decides (#1034), read through parse-config.mjs:
#   `mirror: none`       → never push, even with a 'github' remote (explicit opt-out)
#   `mirror: github` or key absent (`mirror: null` counts as absent) → push HEAD,
#                          but only after `github-export.mjs --plain-guard` (#1645):
#                          when HEAD or origin's main (ls-remote, fetched if not
#                          local) carries .orchestrator/policy/public-export.json,
#                          the repo publishes curated only — a checkout whose
#                          CLAUDE.md predates `mirror: curated` must not push its
#                          full private history → WARN, exit 1, nothing pushed.
#                          origin unreadable → same refusal (fail closed); no
#                          'origin' remote → refused unless `mirror: github` is
#                          set explicitly (nothing to check the policy against).
#   `mirror: curated`    → publish ONE squash snapshot (scripts/github-export.mjs,
#                          #1631) on top of the verified public github main,
#                          never HEAD. Source anchor: only when HEAD is on branch
#                          `main` AND equals origin's main (`git ls-remote`, after
#                          the 4.3 push) — GitHub main deploys production, so a
#                          feature branch or unpushed/unreviewed local commits
#                          skip (exit 0, nothing pushed); origin unreadable →
#                          WARN, exit 1. Export exit 3 = nothing to publish (no
#                          push), other non-zero → WARN, exit 1. Bound to the
#                          scanned sha (read from the export's stdout only):
#                          push URL must equal fetch URL, then `--verify <sha>`
#                          re-checks it right before the push (one commit on the
#                          ls-remote-verified github main, trailer
#                          `Source-Commit: <origin main>`, no excluded path in its
#                          tree), else WARN, exit 1; the push sends that sha, not
#                          the export ref. Plain push, no lease: the commit's
#                          parent IS the verified sha, and a plain push is
#                          fast-forward-only, so GitHub itself rejects it if its
#                          main moved to anything that is not an ancestor of that
#                          sha — the same anchor a lease would enforce, without a
#                          force flag (blocked by the destructive-command guard).
#   any other value / config unreadable → loud WARN, no push, exit 1
# The plugin root is resolved, not assumed: a fresh /close shell often lacks
# PLUGIN_ROOT. Order per skills/_shared/config-reading.md (native PLUGIN_ROOT,
# then Claude, then Codex), else the repo toplevel when it IS the plugin repo.
# Unresolvable → its own WARN + exit 1 (never "Session Config unreadable").
# State 0: not a git repository at all → loud WARN, exit 1. This state was MISSED
#          in the first version and is the reason it is listed first now: outside
#          a repo, `git remote get-url` fails with "fatal: not a git repository",
#          which is indistinguishable from "no such remote" by exit code alone.
#          The block then announced "no 'github' remote configured — skipping
#          (not an error)" and exited 0 — fail-open, in the very fix written to
#          close a fail-open. Found by an adversarial reviewer, not by the author.
# State 1: no 'github' remote      → informational, exit 0 (legitimate for consumer repos)
# State 2: push succeeded          → confirmation WITH the pushed SHA, exit 0
# State 3: push FAILED             → loud WARN on stderr WITH git's real output, exit 1
if ! git_dir=$(git rev-parse --git-dir 2>&1); then
  echo "WARN GitHub mirror: not a git repository — cannot mirror anything." >&2
  echo "  git said: ${git_dir}" >&2
  exit 1
elif ! mirror_url=$(git remote get-url github 2>&1); then
  echo "GitHub mirror: no 'github' remote configured — skipping (not an error)."
  echo "  git said: ${mirror_url}" >&2
  exit 0
fi
so_root="${PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-}}}"
if [ -z "$so_root" ]; then
  so_root=$(git rev-parse --show-toplevel 2>/dev/null)
  [ -f "${so_root}/scripts/parse-config.mjs" ] || so_root=""
fi
if [ -z "$so_root" ] || [ ! -f "${so_root}/scripts/parse-config.mjs" ]; then
  echo "WARN GitHub mirror: plugin root not resolvable — set PLUGIN_ROOT; mirror push skipped." >&2
  echo "  tried: PLUGIN_ROOT, CLAUDE_PLUGIN_ROOT, CODEX_PLUGIN_ROOT, then the repo toplevel" >&2
  exit 1
elif ! cfg_json=$(node "$so_root/scripts/parse-config.mjs"); then
  echo "WARN GitHub mirror: Session Config unreadable — 'mirror' not evaluated, nothing pushed." >&2
  exit 1
elif ! mirror_cfg=$(printf '%s' "$cfg_json" | node -e 'let s="";process.stdin.on("data",(d)=>{s+=d;}).on("end",()=>{const v=JSON.parse(s).mirror;process.stdout.write(v==null?"":String(v));});'); then
  echo "WARN GitHub mirror: parse-config output is not JSON — nothing pushed." >&2
  exit 1
elif [ "$mirror_cfg" = "none" ]; then
  echo "GitHub mirror: 'mirror: none' in Session Config — skipping (not an error)."
elif [ -n "$mirror_cfg" ] && [ "$mirror_cfg" != "github" ] && [ "$mirror_cfg" != "curated" ]; then
  echo "WARN GitHub mirror: unknown 'mirror: ${mirror_cfg}' (expected none | github | curated) — nothing pushed." >&2
  exit 1
elif [ "$mirror_cfg" = "curated" ]; then
  if ! origin_ls=$(git ls-remote origin refs/heads/main 2>&1); then
    echo "WARN GitHub mirror: cannot read origin's main — curated source not verifiable, nothing pushed." >&2
    echo "  git said: ${origin_ls}" >&2
    exit 1
  fi
  origin_main=$(printf '%s\n' "$origin_ls" | awk '$2 == "refs/heads/main" { print $1 }')
  if [ "$(git symbolic-ref --short -q HEAD)" != "main" ] || [ -z "$origin_main" ] || [ "$(git rev-parse HEAD)" != "$origin_main" ]; then
    echo "GitHub mirror: 'mirror: curated' publishes only a HEAD on main that equals origin's main (HEAD $(git symbolic-ref --short -q HEAD || echo detached) $(git rev-parse --short HEAD), origin main ${origin_main:-none}) — skipping (not an error)."
  else
    # stdout carries ONLY the sha (stderr goes straight to the operator), so the
    # sha pushed below is the one the export scanned — never a ref name that could
    # be moved in between.
    export_sha=$(GIT_NO_REPLACE_OBJECTS=1 node "$so_root/scripts/github-export.mjs" --source "$origin_main")
    export_rc=$?
    if [ "$export_rc" -eq 3 ]; then
      echo "GitHub mirror: curated snapshot of ${origin_main} unchanged — nothing to publish."
    elif [ "$export_rc" -ne 0 ]; then
      echo "WARN GitHub mirror: curated export REFUSED (exit ${export_rc}, reason above) — nothing pushed." >&2
      exit 1
    elif ! printf '%s' "$export_sha" | grep -Eqx '[0-9a-f]{40}([0-9a-f]{24})?'; then
      echo "WARN GitHub mirror: the export printed no commit sha — nothing pushed." >&2
      exit 1
    elif [ "$(git remote get-url github)" != "$(git remote get-url --push github)" ]; then
      echo "WARN GitHub mirror: the push URL of 'github' differs from its fetch URL (pushurl or pushInsteadOf) — the verified main would not describe the push target; nothing pushed." >&2
      exit 1
    elif ! GIT_NO_REPLACE_OBJECTS=1 node "$so_root/scripts/github-export.mjs" --verify "$export_sha" --source "$origin_main"; then
      echo "WARN GitHub mirror: ${export_sha} is not one clean snapshot of ${origin_main} on the verified github main — nothing pushed." >&2
      exit 1
    elif push_out=$(git push github "${export_sha}:refs/heads/main" 2>&1); then
      echo "GitHub mirror: pushed curated snapshot ${export_sha} (source ${origin_main}) -> ${mirror_url}"
    else
      echo "WARN GitHub mirror PUSH FAILED: curated snapshot ${export_sha} is NOT on ${mirror_url}" >&2
      echo "${push_out}" >&2
      echo "WARN Mirror is stale — anything wired to it (site deploy) will not update." >&2
      exit 1
    fi
  fi
elif ! GIT_NO_REPLACE_OBJECTS=1 node "$so_root/scripts/github-export.mjs" --plain-guard --mirror-key "${mirror_cfg:-unset}"; then
  echo "WARN GitHub mirror: plain push of HEAD REFUSED (reason above) — this repo publishes curated only; nothing pushed." >&2
  exit 1
elif push_out=$(git push github HEAD 2>&1); then
  echo "GitHub mirror: pushed $(git rev-parse HEAD) -> ${mirror_url}"
else
  echo "WARN GitHub mirror PUSH FAILED: $(git rev-parse HEAD) is NOT on ${mirror_url}" >&2
  echo "${push_out}" >&2
  echo "WARN Mirror is stale — anything wired to it (site deploy) will not update." >&2
  exit 1
fi
# --- github-mirror-push:end ---

State 3 exits non-zero on purpose: it is the only machine-readable signal that the mirror is behind. Report it to the operator in the session summary; do not retry silently and do not swallow it with || true.

4.5 Only a Pipeline or Release Train Left

Do not wait for a pipeline or release train inside session-end. If the session has no other work left, apply skills/_shared/fleet-protocol.md § Rücklauf an den Navigator (Pflicht) with the wait case zug-<name>: exactly one report in this /close turn, only with fleet integration (flotte: true). That the waiting answer before /close already reported the train is intended — every waiting turn triggered by an input (/close is one) reports once (#1611); turns woken by /loop, a Monitor event, an own background agent or shell, or ScheduleWakeup do not. Write NO handover or train manifest here — a coordinator only acts on one written with the waiting answer before /close (same section, Übergabe und Zug-Manifest) — and never invoke /close or another close yourself. Then continue with Phase 4a; never gate the close on a reply (CSM-004).

Queue marks on the own handover (#1639). With fleet integration — whether or not a train is left — re-check the newest own handover or train manifest in the fleet folder (fleet-protocol § Flotten-Ordner und Session-Name gives <name>, <cwd> and <startedAt>): node "$PLUGIN_ROOT/scripts/handover-queue-check.mjs" --name <name> --fleet-dir <cwd>/.orchestrator/runtime/fleet --since <startedAt> --apply --repo <repo> [--navigator-cli <koordinator-cli>], same <repo> and CLI as fleet-protocol Schritt 1a. It files every open point that still lacks a queue:#/issue:# mark and writes the id into the file; this edits the existing file and is not the forbidden new handover. --since keeps a predecessor's file of the same session name out (names repeat across days). Exit 3 (points left unmarked, e.g. the CLI is unreachable): name Übergabe ohne Queue-IDs: <n> Punkte with the listed points in the session summary. It never blocks the close; without fleet integration or without an own file it is a silent no-op (exit 0).

Phase 4a: Auto-Promoted Worktree Cleanup (#575 P3.2)

Skip when persistence: false; skip silently unless the CURRENT worktree is an auto-promoted sibling — detectAutoPromotedWorktree() from scripts/lib/session-end/worktree-cleanup.mjs (marker .orchestrator/promoted-from.json first, legacy basename match as fallback, #1069). Runs AFTER the Phase 4 commit+push, never before (#490 durableCommit ordering). Clean worktree → auto-remove with WARN; dirty → the 3-option AUQ (Behalten / Löschen / Manuell) before any destructive action, per PSA-003. Full procedure: references/phase-4a-worktree-cleanup.md.

Phase 4b: Worktree-Orphan Sweep (#831/B5)

Skip when persistence: false; skip silently unless worktree-orphans.enabled: true (opt-in, default false). Runs AFTER the Phase 4 commit+push (same #490 invariant as 4a). checkWorktreeOrphans() from scripts/lib/session-end/worktree-orphan-sweep.mjs PROPOSES with a read-only argv set; the coordinator DISPOSES via the AUQ it renders itself. Full procedure: references/phase-4b-worktree-orphan-sweep.md.

Phase 5: Issue Cleanup

Always runs. Closes resolved issues via the single node "$PLUGIN_ROOT/scripts/lib/issue-close-strip-labels.mjs" --close [--vcs gitlab|github] [-R <spec>] <id>... CLI call (strips status:* labels, closes, then re-reads each id; a closed: false in its per-id JSON output is a finding, not done — #308), updates partially-done issues, and in Step 3 FILES the Phase 1.65 gate's carry-list — the deferred createSpiralCarryoverIssue call for SPIRAL/FAILED items and the markOpenQuestionAnsweredOnDisk write live here, not in Phase 1.65 (atomicity). Step 3b folds non-exempt over-cap creations into one [Backlog-Sammel] collector under the issue-budget cap; discovery findings from Phase 1.5 are filed at the end. Full procedure: references/phase-5-issue-cleanup.md.

Phase 6: Final Report

Present to the user the Session Summary: Completed / Abnahme offen (open [Abnahme] issues from Phase 1.1b — never counted as Completed, #1678) / Carried Over / Dropped at Handover Gate (#769) / New Issues Created / Unresolved Review Findings (MED-LOW, #617) / Metrics — including the Docs Health line rendered from the Phase 2.3 result and the Custom Phases line from Phase 2.5 — / Next Session Recommendations. Full template plus the Test-delta and Documentation-Coverage anchors: references/session-summary-template.md.

Sub-File Reference

FilePurpose
plan-verification.mdPhase 1 FULL procedural body (1.1 … 1.10) — SESSION_START_REF accessor, 1.1a File-Level Grounding, the #769 Candidate Record Format, 1.5 Discovery Scan, 1.6 Safety Review + 1.6.6 "What Not To Retry", 1.65 Handover Alignment Gate (routing, AUQ shapes, fail-open, telemetry), 1.7 Metrics, 1.8 Session Review, 1.9/1.10 Mission-Status
verification-checklist.mdPhase 2 quality gate checklist and checks
references/phase-2-quality-gate.mdPhase 2 full procedural body — Phase 2.0a Echo-Stub Detection (GH #42), 2.1 Vault Validation, 2.2 CLAUDE.md/AGENTS.md Drift Check, 2.3 Vault Staleness Check (mode resolution, probe invocation, aggregation + routing, closing-report surface), 2.4 Security-Delta (range, CLI call, verdict routing, strict-block AUQ)
discovery-scan.mdPhase 1.5 embedded discovery dispatch and findings triage
metrics-collection.mdPhase 1.7 JSONL schema and conditional field rules
vault-operations.mdPhase 2.1 validator bash contract and reporting matrix
drift-operations.mdPhase 2.2 drift-checker bash contract and reporting matrix
references/phase-3-documentation-updates.mdPhase 3 full procedural body — final heartbeat (#590-3), 3.0 Defensive Cleanup, 3.1 SSOT files, 3.2/3.2a docs + handover, 3.3 rules freshness, 3.4/3.4a STATE.md write + snapshot cleanup, 3.45 Telemetry Flush, 3.5/3.5a/3.6.x memory + learnings + tail dispatcher, 3.7/3.7a/3.7b/3.7c/3.7d/3.7e metrics, recommendations, durable commit, vault board, session-eval, vault orphans
phase-3-2-docs-verification.mdPhase 3.2 full procedural body — docs-tasks load, SESSION_START_REF, per-task loop, mode-gated report, Documentation Coverage block
learning-patterns.mdPhases 3.5a + 3.6 extraction heuristics, confidence updates, passive decay, and JSONL write procedure
phase-3-6-tail.mdPhase 3.6.x tail — detail procedures for the tail phases: 3.6.3 Memory-Proposals Collection (collectProposals + AUQ multiSelect + promoteAndClear, composing writeApproved + clearProposalsJsonl behind a mechanical write-before-clear guard, #828), 3.6.4 Expired-Learnings Sweep — MECHANICAL since 2026-09-09: after planTailPhases(), call runTailPhases({ repoRoot, plan }) from scripts/lib/session-end/tail-runner.mjs (delegating to runExpiredSweep) and report result['3.6.4'] (ran, scanned, archived); the event orchestrator.learnings.sweep_applied is the proof it ran (Epic #723 B4), 3.6.5 Auto-Dream — RETIRED and 3.6.7 Auto-Dialectic — RETIRED (both replaced by the session-start maintenance-due probe, checkMaintenanceDue in scripts/lib/maintenance-due-banner.mjs; headings kept as two-line stubs because other docs cite them), 3.6.6 Skill-Applied Judge (#645 L3 — two calls, not one: buildSkillEvidence from scripts/lib/skill-evidence-window.mjs renders this session's transcript into the evidence window, then runSkillJudge judges it and the coordinator writes each judgment via appendSkillJudgment; on empty evidence text runSkillJudge returns status: 'no-evidence' and does NOT dispatch, #1399 — the exact call shape is in the sub-file, do not reconstruct it here), 3.6.8 Reconciliation Rule Proposals (#696 FA3 — runReconcile + AUQ + writeApprovedRules). Loaded on demand by the SKILL.md skip-plan dispatcher (#724) — only phases with run: true in the planTailPhases() plan execute
scripts/lib/session-end/phase-skip.mjsPhase 3.6.x tail skip-plan aggregator (#724) — planTailPhases({repoRoot, config, sessionId, platform}) → {plan, skippedReport}; side-effect-free (reconcile/sweep via dry-run — no writes), never-throws (per-phase probe error fail-opens to run: true). Since 2026-09-09 it plans FOUR phases, not six: the 3.6.5 (auto-dream) and 3.6.7 (auto-dialectic) deciders were removed with those phases' retirement. Its APPLY half for 3.6.4 is scripts/lib/session-end/tail-runner.mjs (runTailPhases, runExpiredSweep) — the planner fails OPEN, the runner fails CLOSED
references/phase-3-documentation-updates.md § 3.45Telemetry Flush (advisory, #844; MECHANICAL since #1138 — hooks/on-session-end.mjs calls flush() itself at the end of every teardown and emits an orchestrator.telemetry.flush breadcrumb, so this phase is the DESCRIPTION and the fallback, never the trigger; a coordinator that skips it changes nothing) — flush() from scripts/lib/telemetry/sync.mjs drains the host-local send-queue fire-and-forget; no config key (send-gate is resolveConsent() inside the module, fail-closed); skip when persistence: false; never-throw + ~3s-bounded, offline → bounded oldest-dropped queue, optional Telemetry: sent/queued/gated close-summary line, NEVER an error banner; runs late in the close after Phase 3.7
session-metrics-write.mdPhase 3.7 JSONL append, vault-mirror invocation, durable narrative mirror (mirrorNarrative, #675), and behavior matrix
phase-3-7a-recommendations.mdPhase 3.7a full procedural body — computeV0Recommendation call, STATE.md field write, data source guarantee, error mode
phase-3-7a-recommendations.md § 3.7bPhase 3.7b full procedural body — withDurableCommit invocation for sessions.jsonl + STATE.md (#490 AC2), enabled:false local no-op, autopilot.jsonl exclusion note
references/phase-3-documentation-updates.md § 3.7cVault Board → Closed (#674) — mirrorBoard({ explicitStatus: 'closed' }) transitions this repo's board row to closed; gated on vault-integration.enabled, generator-marked + idempotent, non-blocking, ordered after 3.7b and before 3.7d/3.4/3.8
references/phase-3-documentation-updates.md § 3.7dSession-Eval (opt-in — #803) — node scripts/eval-session.mjs --json scores the just-closed session; gated on eval.enabled + eval.mode != off (parsed by scripts/lib/config/eval.mjs), optional eval-judge dispatch + writeEvalReport, advisory/never-blocks-close, ordered after 3.7 (record must exist) and before 3.4/Phase 4 (record committed with the session). Full flow in skills/eval/SKILL.md
references/phase-3-7e-vault-orphans.mdPhase 3.7e Vault Orphans (#1563) — node scripts/vault-orphans.mjs --since <started_at> --json (findVaultOrphans in scripts/lib/session-end/vault-orphans.mjs, read-only git ls-files --others) names untracked vault files modified since the session start, minus _generator: session-orchestrator- files; candidates only (mtime is no proof), one Open Question via appendOpenQuestionOnDisk, never commits/moves/deletes in the vault; unavailable = "could not measure", never "no orphans"; gated on vault-integration.enabled, ordered after 3.7d and before 3.4
(inline) Phase 3.8Session Lock Release — release() uses the physical raw session_id; raw mismatch/absent is non-fatal but never repaired with semantic labels or proof (live ambiguity remains for TTL/Reaper); fs-errors are non-fatal; runs after STATE.md writes and before Phase 4 commit staging
(inline) Phase 4Commit & Push — stage individually (PSA-004), commit, push to origin, then the github-mirror-push block (4 states: not-a-repo / no github remote / pushed / push failed), then 4.5 (only a pipeline or train left → one fleet Rücklauf zug-<name> in this turn, fleet integration only; no manifest, no self-close)
references/phase-4a-worktree-cleanup.mdPhase 4a full procedural body — auto-promoted-worktree detection (detectAutoPromotedWorktree, marker-keyed since #1069), clean-check, clean auto-remove path, dirty 3-option AUQ (Behalten/Löschen/Manuell), PSA-003 + #490 ordering rationale
references/phase-4b-worktree-orphan-sweep.mdPhase 4b full procedural body — checkWorktreeOrphans() read-only proposal set, the coordinator-rendered AUQ, opt-in worktree-orphans.enabled gate
references/phase-5-issue-cleanup.mdPhase 5 full procedural body — close resolved issues via node "$PLUGIN_ROOT/scripts/lib/issue-close-strip-labels.mjs" --close (strip status:* → close → re-read; closed: false is a finding, not done, #308), Step 3 filing of the Phase 1.65 carry-list incl. the deferred createSpiralCarryoverIssue and markOpenQuestionAnsweredOnDisk, Step 3b [Backlog-Sammel] overflow, discovery-issue creation
references/phase-5-issue-cleanup.md § Step 3b.2 (issue-budget reconcile)Phase 5 issue-budget cross-check — reconcileIssueBudget({ repoRoot, record, sessionId, rawSessionId, config }) from scripts/lib/issue-budget-reconcile.mjs runs on the in-memory session record BEFORE it is appended to sessions.jsonl, emitIssueBudgetReconciled records orchestrator.issue_budget.reconciled, and formatIssueBudgetReconcileWarn(result) prints the verdict (match / no-ledger / escaped / stale-record) in the Final Report. Ordered AFTER the overflow drain (which resets overflow[]) and BEFORE reapStaleBudgetFiles (which must not remove the file being read)
references/session-summary-template.mdPhase 6 Final Report — the full Session Summary template (Completed / Carried Over / Dropped at Handover Gate / New Issues / Unresolved Review Findings / Metrics incl. Docs Health + Custom Phases / Next Session Recommendations) plus the Test-delta and Documentation-Coverage anchors

Anti-Patterns

  • DO NOT commit before running quality gates — a "clean commit" with TypeScript errors is not clean
  • DO NOT mark issues as closed without verifying the implementation actually addresses them
  • DO NOT skip creating tracking issues for unfinished work — "I'll remember for next session" always fails
  • DO NOT use git add . or git add -A — parallel sessions may have uncommitted work in the tree
  • DO NOT push to mirrors before verifying origin push succeeded — broken state propagates

Critical Rules

  • NEVER claim work is done without running verification — evidence before assertions
  • NEVER commit with TypeScript errors — 0 errors is non-negotiable
  • NEVER use git add . — stage files individually to avoid capturing parallel session work
  • NEVER skip issue updates — VCS must reflect reality after every session
  • ALWAYS create issues for unfinished PLANNED work — SPIRAL/FAILED agent carryover and partially-done plan items (Phase 1.2 / 1.6) ALWAYS get a ticket; nothing planned-but-unfinished is "remembered" without one. The issue-budget per-session cap does NOT weaken this: priority::critical, the carryover class ([Carryover], [SPIRAL]/[FAILED], type::carryover, bare carryover) and broken-window closure issues are exempt from the cap by construction (scripts/lib/issue-budget.mjs EXEMPT_RULES). Non-exempt over-cap creations are not dropped either — they are parked and folded into one [Backlog-Sammel] collector in Phase 5 Step 3b.
  • DO NOT auto-file MED/LOW review findings as issues — newly-surfaced reviewer findings (Phase 1.8 / W4 panel) at MED or LOW severity are folded in-session or recorded in the Final Report under "Unresolved Review Findings". Only HIGH+/blocking review findings get an issue. (Issue #617 — stops the self-referential low-priority backlog.)
  • ALWAYS push to origin — local-only work is lost work
  • ALWAYS mirror to GitHub if configured — keep mirrors in sync
  • ALWAYS review git diff --cached before committing — verify only YOUR changes are staged

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Use when the user asks to improve architecture, find refactoring opportunities, surface deepening opportunities, consolidate tightly-coupled modules, or make a codebase more testable and AI-navigable. Surfaces shallow modules and hypothetical seams using a precise vocabulary (Module / Interface / Implementation / Depth / Seam / Adapter / Leverage / Locality from LANGUAGE.md).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Kanevry/session-orchestrator522026년 10월 26일 업데이트

autopilot

무료

Use this skill when running an autonomous session-orchestration loop. Chains session-start → session-plan → wave-executor → session-end for N iterations with all 10 kill-switches (SPIRAL, FAILED wave, carryover > 50%, max-hours, max-sessions, resource-overload, token-budget, stall-timeout, sub-threshold confidence, user-abort). Reads Mode-Selector output (Phase B) to decide auto-execute vs. fallback. Writes one autopilot.jsonl record per loop run. Phase C scaffold (issue #277); implementation lives in scripts/lib/autopilot.mjs (Phase C-1 follow-up).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Kanevry/session-orchestrator522026년 10월 26일 업데이트

bootstrap

무료

Use this skill when scaffolding the minimum repository structure required by session-orchestrator. Invoked automatically by the Bootstrap Gate when CLAUDE.md, Session Config, or bootstrap.lock is missing. Also available as /bootstrap for manual invocation. Three intensity tiers: fast (demos/spikes), standard (MVPs), deep (production/team).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Kanevry/session-orchestrator522026년 10월 26일 업데이트

Use when you have a feature idea but the scope or UX is still ambiguous — runs a lightweight Socratic design dialogue (3-5 AUQ rounds) and writes a spec markdown file. Use BEFORE /plan feature when product intent needs validation; skip to /plan feature when scope is already clear. HARD-GATE prevents any code work until the design is user-approved.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Kanevry/session-orchestrator522026년 10월 26일 업데이트

Use when detecting drift between CLAUDE.md (or AGENTS.md, the Codex CLI alias) / _meta narrative and live repository state. Ten checks: absolute-path resolution, 01-projects/ count claims, issue-reference freshness, session-file existence, command-count sync, session-config-parity (mandatory template keys = error, opt-in gaps = note, unknown local keys = warning), vault-dir-parity (CLAUDE.md vs AGENTS.md), generated-rule-staleness (WARN-only), rule-scoping (paths:/globs: frontmatter defects, dangling rule citations, zero-match globs), and docs-parity (docs/components.md count-claims vs on-disk counts, template-vs-reference config-key parity, stale .claude/metrics/ paths). Full per-check spec in the body table. Invoked as an opt-in session-end phase; mirrors vault-sync's lean JSON+exit-code contract.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Kanevry/session-orchestrator522026년 10월 26일 업데이트

close

무료

End session with verification, commits, and documentation

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Kanevry/session-orchestrator522026년 10월 26일 업데이트

Kanevry의 스킬 모두 보기

이 스킬의 문제 신고