[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6C4783JDQNJDTPEKFJBWAMP":3,"skill-same:01M6C4783JDQNJDTPEKFJBWAMP":99},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":41,"linkOnly":42,"markdownContent":43,"markdownBody":44,"frontMatter":45,"refName":46,"commitSha":47,"skillDirectory":48,"forksCount":49,"repositoryHtmlUrl":50,"repositoryHomepageUrl":6,"ownerHtmlUrl":51,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":52,"install":53,"resources":66},"01M6C4783JDQNJDTPEKFJBWAMP","session-startup",null,"Automatic startup cleanup guard for new conversations. Use when deciding whether to run the session cleanup detector before the first reply, handling stale-state prompts, or preserving run-once startup semantics. DO NOT USE FOR: post-merge archival workflows (use post-pr-review) or general workflow troubleshooting outside startup detection (use process-troubleshooting).","Grimblaz\u002Fagent-orchestra","Grimblaz","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F74755849?v=4","01M6C430M9XZDHGHPWQVTQ3KMA","01M6C430RJY3MMX0QAZ3X8DR3Y",3,"MIT","https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fsession-startup\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],"session startup","session","startup","detection","prompts","use","process","troubleshooting","guard","first","review","state","run","cleanup","pr","merge","workflows","workflow","2026-11-10 10:12:30",false,"---\nname: session-startup\ndescription: \"Automatic startup cleanup guard for new conversations. Use when deciding whether to run the session cleanup detector before the first reply, handling stale-state prompts, or preserving run-once startup semantics. DO NOT USE FOR: post-merge archival workflows (use post-pr-review) or general workflow troubleshooting outside startup detection (use process-troubleshooting).\"\n---\n\n# Session Startup\n\nRun-once startup guard for the automatic session-cleanup detector.\n\n## When to Use\n\n- When the SessionStart hook injects `additionalContext` into the agent's first turn\n- When deciding whether the automatic startup detector should run\n- When interpreting stale-state detector output and optionally running cleanup\n- When checking whether the installed Claude plugin version has drifted behind the marketplace\n- When preserving manual detector access after the automatic startup path fires\n\n## Purpose\n\nThe trigger mechanism is now a plugin-distributed `hooks\u002Fhooks.json` SessionStart hook rather than an LLM-interpreted per-agent directive. Apply a session-memory run-once guard after that hook fires so the detector runs at most once automatically per conversation while remaining available for explicit manual use. The same run-once pass also owns the Claude-only plugin drift backstop: when `agent-orchestra@agent-orchestra` is installed but behind the resolved marketplace version, surface the update result and a restart-vs-continue decision without blocking the session on failures.\n\n## Session Startup Check\n\nFollow these steps exactly.\n\n> **Survival**: `SMC-07` owns the startup run-once marker. Hook-driven marker writes are `within-conversation:hooks`; the honest gaps are the inline run-once marker write, the inline-vs-subagent enforcement split, the headless-Claude prompt skip, and the freshness-step fail-open user-visible surface. Do not add a second marker or new persistence mechanic.\n\n### Canonical Automatic Startup Guard Contract\n\n```json\n{\n  \"sessionStartupMarkerPath\": \"\u002Fmemories\u002Fsession\u002Fsession-startup-check-complete.md\",\n  \"checkMarkerBeforeAutomaticDetectorRun\": true,\n  \"recordMarkerAfterFirstAutomaticStartupCheck\": true,\n  \"recordMarkerRegardlessOfCleanupChoice\": true,\n  \"failOpenOnSessionMemoryAccessError\": true,\n  \"manualDetectorRunsRemainAllowed\": true,\n  \"confirmSharedBodyLoadForAgentShells\": true\n}\n```\n\n### Step 1 — Check prerequisites\n\nFor automatic startup runs, first use any hook-injected `additionalContext` if it is already present in the agent's first turn. Resolve the detector script path relative to this skill file for manual fallback: the wrapper at `scripts\u002Fsession-cleanup-detector.ps1` (in this skill's directory) self-resolves its repo root via `$PSScriptRoot`, so no environment variables are required. If `pwsh` is unavailable or the script is missing, skip the entire check silently and continue with the user's request.\n\n**Scratch-containment net (AC5)**: As part of the SessionStart hook invocation, `session-cleanup-detector.ps1` calls `scripts\u002FEnsure-ScratchGitignore.ps1` (fail-open) before running the cleanup detector. That script idempotently writes to **two distinct destinations**, and both are intentional:\n\n1. It appends `.tmp\u002F` and the mangle-literal gitignore patterns from `skills\u002Fterminal-hygiene\u002FSKILL.md ## Scratch & Temp-File Hygiene` to the consumer repo's tracked `.gitignore` when any are absent.\n2. It writes the goal-run harness runtime-state patterns (root-anchored entries for `goal-run-active.json` and `goal-run-log.jsonl`, each written with a leading slash so it matches only at the worktree root; sourced from `skills\u002Fgoal-run\u002F`, **not** from terminal-hygiene) to `.git\u002Finfo\u002Fexclude` — the per-clone, untracked exclude file. That destination is load-bearing: appending these to the tracked `.gitignore` would leave the launch repo dirty, and the goal-contract validator refuses a dirty `-RepoRoot`, which is the exact `refused: uncommitted-changes` halt this ignoring exists to prevent (#929). A maintainer auditing the script against this doc should not treat the goal-run handling as spurious.\n\nRunning it twice produces no duplicate lines in either destination. Failures in this step are swallowed — they never crash the hook or affect the cleanup-detector output.\n\n### Step 2 — Check the automatic run-once guard\n\nBefore any automatic startup detector run, check session memory for the marker at `\u002Fmemories\u002Fsession\u002Fsession-startup-check-complete.md`. If that marker is present, skip the automatic detector run and continue silently with the user's request. If session-memory lookup, read, or other access fails, fail open and still run the detector rather than suppressing the check.\n\n### Step 3 — Run the detector script\n\nFor automatic startup runs, the plugin-distributed SessionStart hook runs the detector before the agent sees the user's request and injects the resulting `additionalContext`. This step preserves the manual fallback command contract and any contributor-side direct invocation. The wrapper self-resolves its repo root via `$PSScriptRoot`, so no env vars are needed — but the terminal's working directory in a consumer repo is usually the consumer's workspace, **not** the orchestra plugin. Invoke the script by a path that resolves from wherever the `agent-orchestra` content actually lives.\n\n**Repo clone** (contributors, CWD is the repo root — relative path works):\n\n```powershell\npwsh -NoProfile -NonInteractive -File \"skills\u002Fsession-startup\u002Fscripts\u002Fsession-cleanup-detector.ps1\"\n```\n\n**Plugin-cache install** (Copilot or Claude Code consumers, CWD is the consumer workspace — pass the plugin's absolute path). Resolve the plugin directory from the installed plugin cache rather than any `chat.*Locations` setting (Copilot: the VS Code `agentPlugins\u002F...\u002Fagent-orchestra` cache path under the active product profile; Claude Code: `\u003Cplugins-cache-root>\u002Fagent-orchestra\u002F`), then:\n\n```powershell\npwsh -NoProfile -NonInteractive -File \"\u003Cplugin-root>\u002Fskills\u002Fsession-startup\u002Fscripts\u002Fsession-cleanup-detector.ps1\"\n```\n\nIf neither path resolves (the script is genuinely missing), skip the check silently per Step 1.\n\n### Step 4 — Record the run-once marker\n\nRecord or write the session-memory marker at `\u002Fmemories\u002Fsession\u002Fsession-startup-check-complete.md` after the first automatic startup check runs so later agent hops in the same conversation skip the automatic detector run. Record the marker regardless of whether cleanup is needed and regardless of whether the user later confirms, declines, or skips cleanup. If session-memory write or other access fails, fail open: continue with the detector result you already obtained, and allow later automatic checks rather than risking a missed cleanup warning.\n\n### Step 5 — Parse the output\n\nThe detector returns one of two JSON shapes.\n\n**No stale state found**: continue silently and do not mention the check to the user.\n\n```json\n{}\n```\n\n**Stale state found**: prompt the user.\n\n````json\n{\n  \"hookSpecificOutput\": {\n    \"hookEventName\": \"SessionStart\",\n    \"additionalContext\": \"**Post-merge cleanup detected** - stale tracking artifacts found:\\n\\n- `.copilot-tracking\u002Fissue-42-my-feature.yml`\\n\\nTo clean up, run:\\n```powershell\\n# Run in a PowerShell (pwsh) terminal:\\npwsh '\u002Fpath\u002Fto\u002Fagent-orchestra\u002Fskills\u002Fsession-startup\u002Fscripts\u002Fpost-merge-cleanup.ps1' -IssueNumber 42 -FeatureBranch 'feature\u002Fissue-42-my-feature'\\n```\\n\"\n  }\n}\n````\n\nThe `additionalContext` field is a Markdown-formatted description of what was found plus the command block to clean it up.\n\n**Multi-category example** (when sibling worktrees or orphan branches are also found, the cleanup command uses the composite form; `-TmpRoot '.tmp'` is auto-wired when `-IssueNumber` is present and a `.tmp\u002F` directory exists at the repo root):\n\n```powershell\npwsh '\u002Fpath\u002Fto\u002Fagent-orchestra\u002Fskills\u002Fsession-startup\u002Fscripts\u002Fpost-merge-cleanup.ps1' -IssueNumber 42 -FeatureBranch 'feature\u002Fissue-42-my-feature' -TmpRoot '.tmp' -SiblingWorktrees @('\u002Fpath\u002Fto\u002Fworktree') -OrphanBranches @('claude\u002Fold-feature')\n```\n\nWhen writing a `.claude\u002Fsettings.json` allowlist entry, match against `Bash(pwsh*post-merge-cleanup.ps1*)` — this covers both the single-issue form and the composite form with `-SiblingWorktrees` and `-OrphanBranches` arguments.\n\nDetector findings are reported in this order when present:\n\n- **Current branch**: flags the current branch when its upstream was merged\u002Fdeleted. A current `claude\u002F*` no-upstream branch is a candidate on branch shape alone — prefix and upstream state — since issue #922 removed the ancestry pre-filter that used to gate it. That filter rendered a merged verdict of its own, and because a squash merge writes a new commit, a squash-merged branch is never an ancestor and was discarded before any evidence was gathered. Candidacy only becomes eligibility through the shared evidence-gated eligibility primitive (`Test-WorktreeBranchRemovalEligible`): git-only tree-equivalence or an OID-checked merged-PR-by-head match, or — for a branch with no unique commits — an OID-checked merged PR or a closed parent issue. When the signal is unverifiable, the branch is reported as needing manual review (with the specific reason) rather than silently retained or falsely offered for cleanup. The shared `Test-WorktreeIsPrimary` guard excludes the primary checkout from this candidate check before eligibility is ever evaluated. When eligible, the current branch — whether it has an issue ID (`feature\u002Fissue-N-…`) or not — is rolled into the same composite `pwsh ... post-merge-cleanup.ps1 ... -FeatureBranch '\u003Cbranch>'` invocation as the other categories, so confirming cleanup triggers exactly one permission prompt. The script switches to the default branch, pulls, and deletes the now-stale current branch on your behalf. (The `Documents\u002FDesign\u002Fsession-hooks.md` note about running current-worktree cleanup from another checkout still applies if you choose to bypass the script and run the underlying git commands manually — but the recommended path is the composite invocation.)\n- **Tracking files**: flags issue-scoped `.copilot-tracking\u002F` files whose remote `feature\u002Fissue-*` branch is gone; persistent calibration data remains excluded.\n- **`.tmp\u002F` scratch clearing**: per-issue `.tmp\u002F{N}-*` and `.tmp\u002Fissue-{N}*` scratch files are cleared at cleanup time (not detected). `-TmpRoot '.tmp'` is auto-wired into the composite invocation when `-IssueNumber` is present and a `.tmp\u002F` directory exists at the repo root; the `Remove-IssueTmpScratch` function handles removal and emits a summary line.\n- **Sibling worktrees**: flags sibling worktrees on `claude\u002F*` no-upstream branches or upstream-deleted `feature\u002Fissue-*` branches, then routes each candidate through the same shared evidence-gated eligibility primitive (`Test-WorktreeBranchRemovalEligible`) as the current-branch check above — git-only tree-equivalence or an OID-checked merged-PR-by-head match for a branch with unique commits, or an OID-checked merged PR or closed parent issue for a branch with none. A candidate whose signal is unverifiable is reported as needing manual review (`Sibling worktree branch \u003Cbranch> at \u003Cpath> needs manual review: \u003Creason>`) instead of being silently dropped or falsely offered for cleanup. The shared `Test-WorktreeIsPrimary` guard excludes the primary checkout from sibling candidacy on every code path, including the case where the agent runs from a non-primary linked worktree and the primary checkout would otherwise appear to be a sibling. Eligible candidates' cleanup is rolled into the single composite `pwsh ... post-merge-cleanup.ps1 -SiblingWorktrees @(...) -OrphanBranches @(...)` invocation appearing inside the fenced PowerShell block — not as individual `git` commands. Cleanup time re-verifies eligibility independently rather than trusting the detector's snapshot, then reports the verified post-removal state honestly: `removed`, `removed-partial-root-held` (contents gone but an empty directory remains — another process held the root), `removed-partial-content-remains` (unregistered but files remain — inspect manually), `stale-registration` (the removal attempt did NOT succeed in deregistering the worktree — the directory is gone but `git worktree list` still shows it registered; this is a failed-to-deregister outcome and is never counted toward the \"Deleted N sibling worktree(s)\" summary, unlike the other outcomes above), `failed`, or `verification-indeterminate` — never the old false \"skipping\" message for a worktree a removal attempt had already partially destroyed. A locked worktree whose directory still exists is left for manual review and never force-removed on the lock flag alone (`skipped locked worktree ... - remove the lock first`). A locked worktree whose directory is confirmed absent via `Test-Path` (issue #522, corrected) is double-force-removed (`git worktree remove --force --force`) regardless of the porcelain `prunable` marker — real git never sets `prunable` on a locked worktree, so the corrected clause keys on `Test-Path`-verified absence alone, never on the (for a locked worktree, unreachable) `prunable` self-report — and the actual post-attempt state is then verified through the same honest outcome probe, never assumed.\n- **Orphan branches**: flags unattached merged `claude\u002F*` no-upstream branches and unattached upstream-deleted `feature\u002Fissue-*` branches. Their cleanup is rolled into the single composite `pwsh ... post-merge-cleanup.ps1 -SiblingWorktrees @(...) -OrphanBranches @(...)` invocation appearing inside the fenced PowerShell block — not as individual `git` commands. Squash-merged branches are classified as merged from content evidence — strict tree-equivalence (`git diff --quiet` exit 0) or a merge-tree no-op. Since issue #922 `git cherry` establishes neither verdict: it compares patch identities, so a revert leaves it reporting merged and a squash leaves it reporting unmerged for a fully shipped branch. Both no-upstream and upstream-deleted candidates are routed at detection time through the same shared evidence-gated eligibility primitive (`Test-WorktreeBranchRemovalEligible`) used by the sibling and current-branch checks above; a candidate whose signal is unverifiable is reported as needing manual review (with the specific reason) instead of being silently dropped. `feature\u002Fissue-N-*` orphan branches — the session-convention naming this repo uses — carry an additional `; eligible for auto-resolve at cleanup time` suffix: the detection-time verdict is a report, not the final word, because the destructive `git branch -D` at cleanup time always re-evaluates live rather than trusting the detector's snapshot. For a zero-unique-commit `feature\u002Fissue-N-*` or `claude\u002F*-N-hex` branch with a derivable issue id (the two session-convention naming shapes the shared primitive can derive an issue id from), cleanup time reuses the same shared primitive as detection (OID-checked-PR-first, then closed-issue evidence). For every other `feature\u002Fissue-N-*` case — one or more unique commits, or the unique-commit count itself could not be determined (a `git rev-list` failure) — cleanup time instead uses the richer tree-equivalence-plus-absorption chain (`Test-BranchMergedIntoDefault` + `Test-OrphanBranchAutoResolveEligible`): the parent issue must be CLOSED, a merged PR with matching `headRefOid` must exist, and every residual commit must be absorbed by main (ancestor-on-main \u002F patch-equivalent \u002F spike-only confined to `.tmp\u002Fissue-N\u002F` \u002F tree-at-HEAD-matches-main). Honest skip messages at cleanup time: `auto-resolve declined` (signals say not shipped; branch retained), `could not verify auto-resolve signals` (gh unavailable — fail-open, branch retained), and `branch not reachable from default (merged-state re-check returned false)` (a race between detection and the pre-delete re-check). Non-`feature\u002Fissue-N-*` orphans (e.g., `claude\u002F*`) never carry the auto-resolve suffix or use the absorption chain. A zero-unique-commit non-`feature\u002Fissue-N-*` orphan with a derivable issue id (`claude\u002F*-N-hex`) clears only through the shared primitive's own zero-commit evidence (OID-checked-PR-first, then closed-issue evidence), same as described above. A non-`feature\u002Fissue-N-*` orphan with one or more unique commits instead falls through to `Test-BranchMergedIntoDefault` directly: git-only tree-equivalence, or — when that is inconclusive — the same OID-checked merged-PR-by-head lookup the primitive itself uses (never a name-only `gh pr list` match, which would misreport a branch that merely shares its name with an unrelated merged PR as shipped).\n- **Fail-open behavior**: fetch, worktree-list, for-each-ref, and ref-lookup failures suppress only the unverifiable candidate and do not fail the startup session.\n- **Deliberate silence for live work (issue #922)**: a candidate the eligibility gate concludes is *definitively unmerged* — content evidence shows it carries changes absent from the remote default, and no merged pull request matches its current tip — renders **nothing at all**: no finding line, no command argument, no contribution to the ten-line display cap. This is the one class the check deliberately says nothing about, because it is live work rather than a leftover, and a manual-review line for every in-flight worktree at every session start is noise that trains you to ignore the block. Every other not-eligible candidate is reported with its cause. Silence is reached only after the candidate has been *evaluated* to that conclusion, never by declining to look at it.\n- **Opt-in cleanup**: the detector only reports findings. Nothing is removed unless the user confirms, and explicit manual detector runs remain available after the automatic guard fires.\n\nClaude cleanup findings are capped at 10 concrete `claude\u002F*` entries; additional entries are summarized with a `+N more` hint.\n\n### Step 6 — Prompt the user\n\nIf the output contains `hookSpecificOutput`, present the `additionalContext` text to the user and ask for confirmation before running cleanup, offering exactly two options: \"Yes — run cleanup\" and \"No — skip for now\". The confirmation covers only the fenced cleanup block; inline current-worktree commands are deliberately narrative and require a separate manual run from another checkout.\n\n### Step 7 — Run cleanup (only if confirmed)\n\nIf the user confirms, run all lines from the fenced code block inside `additionalContext` in the terminal. Skip blank lines; `#`-prefixed comment lines are safe to include. Do not scrape or execute inline current-worktree cleanup text outside the fenced block. Report what was cleaned up when complete.\n\n### Step 7b — Run the Claude plugin drift check\n\nAfter the cleanup path completes, run this Claude-only sub-step before continuing with the user's request. Copilot skips this sub-step silently because it has no version-cache analog.\n\n**Ordered procedure (must follow this exact sequence):**\n\n1. **Refresh marketplace view**: Run `claude plugin marketplace update` with a 5-second timeout. If it fails or times out, emit `marketplace freshness check failed — using cached view` and continue with the cached view. Do not retry transient freshness failures.\n\n2. **Resolve installed state**: Read `~\u002F.claude\u002Fplugins\u002Finstalled_plugins.json`. Find the `agent-orchestra@agent-orchestra` entry and read its installed `version` plus `marketplace` field. If the file is missing, the entry is absent, or the entry has no `marketplace` field, fail open: silent-skip or emit a one-line minimal error and continue.\n\n3. **Resolve marketplace latest**: Read `~\u002F.claude\u002Fplugins\u002Fmarketplaces\u002F{marketplace}\u002F.claude-plugin\u002Fplugin.json`. If the installed version matches, continue silently (verified-current silence).\n\n4. **Detect drift and emit marker**: If the installed version is behind the marketplace version, emit `Drift detected — updating…` as an inline status line. This marker signals that the install is about to begin.\n\n5. **Run the install**: Run `claude plugin update agent-orchestra@agent-orchestra --yes` with a 30-second timeout. On success, emit:\n   `Updated 'agent-orchestra@agent-orchestra' from {old} -> {new}. Current session runs under old code until restart.`\n   On failure, retry once on transient errors. If the retry also fails, emit a failure summary and the manual fallback command:\n   `Plugin update failed: {error}. Manual fallback: claude plugin install agent-orchestra@agent-orchestra`\n\n6. **Ask (only after step 5 completes)**: The question MUST NOT be presented while the install is in flight or before it has been attempted (success or announced failure). After step 5 completes — whether it succeeded or failed — present the appropriate prompt (see the `Drift Failure-Mode Prompt` subsection below).\n\n**Silent skip conditions for this sub-step**: when `pwsh` is missing; when the `claude` CLI is missing or fails; when the marketplace registration points at a non-git local directory, dirty tree, or detached HEAD (the local-path classification already surfaces remediation).\n\n**Local-path marketplace classification** (run before step 4 when a local path is detected):\n\n- Clean git repo behind `origin\u002Fmain`: surface that the marketplace path is behind and include `claude plugin marketplace remove agent-orchestra` and `claude plugin marketplace add Grimblaz\u002Fagent-orchestra` remediation commands\n- Non-git local directory: surface that the registration is a non-git local directory\n- Dirty tree or detached HEAD: surface that local marketplace remediation is skipped because the clone has local work\n- Fetch failure: fail open and continue\n\n**Headless Claude behavior**: Headless Claude runs perform the same steps 1–5 and same fail-open emission; only step 6 (the stop\u002Fcontinue question) is suppressed. The verified-current silence guarantee applies only on the freshness-success branch; on freshness failure, cached comparison is a documented accepted limitation.\n\nThis sub-step shares the existing Step 4 run-once marker; do not introduce a second marker or persistence mechanism.\n\n### Drift Failure-Mode Prompt\n\nThe canonical `### Inline-Dispatch Option Labels` YAML block keeps exactly 4 keys (`cleanup_yes`, `cleanup_no`, `drift_stop`, `drift_continue`) — no additional keys are added for failure-mode labels.\n\n**On install success** (step 5 of Step 7b succeeded): ask using the canonical `drift_stop` and `drift_continue` option labels verbatim:\n\n- `Stop — I'll restart now` (maps to `drift_stop`)\n- `Continue — run under old code` (maps to `drift_continue`)\n\n**On install failure** (step 5 of Step 7b failed, including post-retry): render a one-off failure-recovery prompt (not a canonical labeled option — these labels are session-ephemeral only):\n\n- `Stop — I'll restart and try the install again`\n- `Continue — under old code (install failed)`\n\nInclude the manual fallback command in the failure prompt body so the user has something concrete to act on when they choose to restart: `claude plugin install agent-orchestra@agent-orchestra`.\n\nThis conditional rendering keeps the `inline-dispatch-contract.Tests.ps1` `ExpectedCount 4` and `Should -Be 4` assertions green because failure-mode labels are prose-rendered and session-ephemeral, not added to the canonical YAML block.\n\n### Permission allowlist (recommended)\n\nTo avoid repeated per-command permission prompts when the session-startup cleanup runs, add these entries to your project's `.claude\u002Fsettings.json` `allow` list:\n\n```json\n{\n  \"permissions\": {\n    \"allow\": [\n      \"Bash(pwsh*post-merge-cleanup.ps1*)\",\n      \"Bash(git branch -D feature\u002F*)\",\n      \"Bash(git branch -D claude\u002F*)\",\n      \"Bash(pwsh*persist-marker.ps1*)\"\n    ]\n  }\n}\n```\n\nThe first entry covers the composite cleanup-script invocation that the session-startup hook emits. The second and third entries cover edge cases where manual cleanup commands are needed outside the script (for example, when the current-worktree narrative guidance is followed manually from another checkout). The fourth entry (issue #893) covers `skills\u002Fsession-memory-contract\u002Fscripts\u002Fpersist-marker.ps1`, the marker-write primitive that supersedes hand-composed `gh issue comment` \u002F `gh pr comment` calls for every migrated marker family (see `skills\u002Fsession-memory-contract\u002Freferences\u002Fhandoff-markers.md`).\n\nThis allowlist entry is safe specifically because of `persist-marker-core.ps1`'s scratch-root bounding (issue #893 slices s6\u002Fs11, root check added by the PR #917 review's F1 fix): `Resolve-MarkerScratchBoundedPath` requires every `-BodyFile`\u002Fburst-manifest `bodyFile` to resolve, via real `Resolve-Path` canonicalization (traversal-aware — never a raw string-prefix test on the unresolved input) plus a symlink\u002Fjunction reparse-point check on the scratch root itself AND a per-segment walk of every descendant down to the leaf (`Resolve-Path` alone does not dereference reparse points, so both the root and every descendant must be checked explicitly), inside the consumer repo's `.tmp\u002F` scratch root before the script will read it. Known residual gap: hard links are not reparse points and are not detected by this walk. Without that bound, a broad `Bash(pwsh*persist-marker.ps1*)` allowlist entry would let any `-BodyFile` argument read an arbitrary file on disk and post its contents to a public GitHub comment — an arbitrary-file-read-to-public-comment primitive. The bounding is what keeps the wide `pwsh*persist-marker.ps1*` glob pattern (rather than a narrower, harder-to-match pattern) safe to allowlist: a payload path outside `.tmp\u002F` is refused pre-write, before any network call.\n\nAutomated detection or installation of these allowlist rules is out of scope for this issue and would be a follow-up. AC4 of issue #500 is satisfied by this documentation per the Experience-Owner framing: \"documents how the cleanup confirmation answer translates into permission.\"\n\nOn Windows, the existing `Bash(pwsh*post-merge-cleanup.ps1*)` allowlist entry covers `pwsh` invocations through Claude Code's Bash tool surface; no separate `PowerShell(...)` entry is required.\n\nIf the cleanup script (or a settings.local.json edit applying these recommendations) is silently denied even after explicit user authorization in the same conversation, you are observing the contextual-classifier override pattern documented in [issue #546](https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fissues\u002F546) (see comments [4414368049](https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fissues\u002F546#issuecomment-4414368049) and [4414376114](https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fissues\u002F546#issuecomment-4414376114)). Workaround: apply this allowlist *before* the deny fires by editing `.claude\u002Fsettings.local.json` directly; do not ask the agent to make the edit on your behalf in the same turn you authorize it.\n\nIssue #548's `gh issue view` and `gh pr list --head` calls run as child processes of `pwsh` inside `post-merge-cleanup.ps1`, so the existing `Bash(pwsh*post-merge-cleanup.ps1*)` allowlist entry above covers them. No new `Bash(gh ...)` entries are needed. The L2 customer-self-verification responsibility documented above (issue #546 contextual-classifier override) applies symmetrically to these gh calls — if a same-turn denial is observed, apply the existing workaround.\n\n### Step 7c — Portfolio snapshot (Claude-only, fail-open)\n\n\u003C!-- scope: claude-only -->\n\nThis step runs **outside** the Step 4 run-once guard — it refreshes every session start, not just the first. Skip this step if the user chose `drift_stop` in Step 7b (session is ending; no point rendering a snapshot). Copilot: silent skip.\n\n**Behavior**: If `Documents\u002FPlanning\u002Fsequence.yaml` exists in the working directory and is parseable (has a `control_tower:` integer field), run:\n\n```powershell\ngh issue view {control_tower} --json body --jq '.body' | Select-String 'rendered by render-portfolio\\.ps1' -Context 4,0\n```\n\nwith a ~3-second timeout (set at the Bash-tool invocation level, not as a `gh` CLI flag). Surface at most **5 lines** of the rendered section by anchoring on the footer line (`rendered by render-portfolio.ps1`) and showing up to 4 preceding content lines — this guarantees the footer is always present regardless of section length. Truncate with `(…)` if more lines precede those 4.\n\nAny failure (file absent, parse error, `gh` timeout, network error, missing `control_tower` field, non-zero exit) → **silent skip**. Never block session startup on a portfolio snapshot.\n\nSee `platforms\u002Fclaude.md` for the Claude Code tool invocation note.\n\n### Step 7d — Baseline-eligibility harvest (Claude-only, fail-open)\n\n\u003C!-- scope: claude-only -->\n\nLike Step 7c, this step runs outside the Step 4 run-once guard: it can act again on later session starts, not just the first, because the underlying data it reads is already cached for an hour. Copilot: silent skip (no analog).\n\n**What it does (plain summary)**: Some pull requests get their AI-usage cost numbers written down partway through a work session — before that session actually finished — because the PR happened to be created mid-session. Those numbers are a fair estimate at the time but not exact. This step checks, at the start of a later session, whether any recently-merged PR is still carrying one of those mid-session estimates and, when it is safe to do so, re-counts that PR's session from scratch and quietly swaps in the exact end-of-session numbers.\n\n**Invocation**: Call `Invoke-CostBaselineHarvest` (`.github\u002Fscripts\u002Flib\u002Fcost-baseline-harvest.ps1`) with the current session's `ParentCwd` (its own working directory) and `RepoRoot` (the repo root), and optionally `Slug` — the function resolves `Slug` itself from `RepoRoot` when it is omitted. See `platforms\u002Fclaude.md` for the Claude Code tool invocation note.\n\n**Fail-open contract**: Any failure — no GitHub access, no matching local session transcript, a re-count that comes back empty, or a comment write that fails — leaves this step a silent no-op. Never block session startup on this harvest, and never surface an error to the user.\n\n**Budget**: At most one expensive re-count runs per `Invoke-CostBaselineHarvest` call. The function narrows down to eligible candidates first (recently merged, still carrying a mid-session estimate, not already attempted), then walks them until it finds the first one that both has a matching local session transcript and is confirmed still merged by a fresh GitHub lookup — only that one candidate spends the re-count budget, whatever the outcome. See the function's own doc comment in `cost-baseline-harvest.ps1` for the full selection and promotion mechanics.\n\n**What surfaces to the user** (issue #824 CE Gate fix, F1): silent when nothing was attempted this startup — no eligible candidate, or every candidate failed the cheap verify-then-select\u002Flive-merge gates before the harvest ever spent its one-re-count budget. Once a candidate has consumed that budget, though, the outcome is always shown as a single line, whichever of these it is: `upgraded #{pr} to end-of-session` on success, or the function's own `upgrade expected for #{pr} — {reason}` diagnostic otherwise (e.g. transcript unavailable, still partial, token count lower than persisted, composite comment not found, cost section format mismatch). The design intent here is \"announce both success and expected-but-failed upgrades\" — a candidate that was actually re-counted and didn't convert is not startup noise, it's the one signal a maintainer needs to notice a baseline row that isn't converging. Genuine fail-open no-ops (nothing to attempt, or an infrastructure failure before the budget was spent) stay silent as before.\n\n### Step 8 — Continue with the user's request\n\nAfter the automatic startup path is complete, continue with the user's original request only after completing any other applicable startup steps below, including Step 7b and Step 9 when they apply. In hook-driven runs, this means consuming any injected `additionalContext`, recording the run-once marker, and then proceeding. This automatic run-once guard applies only to the cleanup-detector plus Claude drift-check path; explicit or manual detector runs still remain allowed after the automatic guard fires.\n\n### Step 9 — Confirm paired shared-body load (agent shells with a paired body)\n\nThis step is not gated by the session-startup run-once marker and fires on every agent-role adoption in the conversation, including every subagent dispatch. Do not wrap this step in the Step 2 or Step 4 marker guard.\n\nIf you are operating as an agent shell at `agents\u002F{name}.md` whose body contains a `## Shared methodology` section naming a paired `agents\u002F{Name}.agent.md`, **or if you are an inline slash-command dispatch at `commands\u002F{name}.md` that names a paired `agents\u002F{Name}.agent.md` body in its load reference**, load that paired file via the platform's file-read tool before proceeding. Use the D1 plugin-cache-first body resolution sequence documented in your host file's own `### Step 9 — Paired-body halt-on-fail` block (each agent shell and each command file carries its own per-body cascade).\n\nIf that load fails, emit exactly: `⚠️ Shared-body load failed for agents\u002F{Name}.agent.md — {error}. This run cannot continue without the canonical methodology; surface this to the user and stop.` After emitting that message, do not make any further tool calls, subagent dispatches, questions to the user, or any other agent actions.\n\nIf the paired load succeeds, cite it with `Shared body loaded — proceeding as {AgentName}` and include the full-form H2 body names exactly as they appear in the shared body, excluding `Platform-specific invocation`.\n\nIf you are not in a paired-body context, skip this step silently.\n\nIf the same shared body is loaded more than once in a conversation, the load is idempotent — loading the same file a second time is harmless and does not require deduplication logic.\n\n### Inline-Dispatch Option Labels\n\n```yaml\ncanonical_option_labels:\n  cleanup_yes: \"Yes — run cleanup\"\n  cleanup_no: \"No — skip for now\"\n  drift_stop: \"Stop — I'll restart now\"\n  drift_continue: \"Continue — run under old code\"\n```\n\nEnforcement paths: Claude inline slash-command dispatch (`\u002Fexperience`, `\u002Fdesign`, `\u002Fplan`, `\u002Forchestrate`, and `\u002Fpolish`) enforces the pre-flight protocol via a load reference to this skill; the per-command prose duplication was removed in issue #498 (DRY pass following #481). Each command file retains a `### Step 9 — Paired-body halt-on-fail` block with its D1 body-resolution cascade immediately after the load reference; Step 9 of this skill now recognizes inline slash-command dispatch as a paired-body context, so the halt-on-fail contract applies to both `Agent` tool subagent dispatches and inline command invocations.\n\n## Silent Skip Conditions\n\nSkip the entire check silently in any of these cases:\n\n- `pwsh` is not available on `PATH`\n- The detector script does not exist at the expected path\n- The detector script returns an error or non-JSON output\n\nThese are normal conditions in repos that have not installed the agent-orchestra plugin or in environments where PowerShell is unavailable.\n\n\u003C!-- auto-mode-boundary-recipe:begin -->\n## Auto-mode Boundary Verification Recipe\n\nRun these three checks in Claude Code to audit the auto-mode boundary in your session.\n\n**1. Positive case (D1):** Run `git status` under auto-mode. It should execute immediately without a permission prompt. If it prompts, D1 has regressed — check your `permissions.allow` list.\n\n**2. Risky case (D2):** Run `gh pr merge --admin` against a draft PR you own, then **abort at the Claude Code permission prompt** — do not complete the merge. Expected: a permission prompt appears before execution. If no prompt appears and the command is silently denied, you are observing the L2 contextual-classifier override pattern documented above. Fallback: (a) record the chat transcript verbatim, (b) confirm the [cleanup-script allowlist entry](#permission-allowlist-recommended) is applied (project-level for contributors, `~\u002F.claude\u002Fsettings.json` for plugin consumers — see that skill section for the correct path), (c) file an issue at the [agent-orchestra repo](https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fissues) with the transcript and settings excerpt.\n\n**3. Axis B case (D3):** Run `\u002Fexperience N` against an **existing issue** you own (use an issue that already carries an upstream marker — e.g., a `\u003C!-- experience-owner-complete-{ID} -->` comment from a prior `\u002Fexperience` run — so the upstream-onboarding standards check actually fires; a fresh unframed issue will skip the standards check per [skills\u002Fupstream-onboarding\u002FSKILL.md § When to Skip](..\u002Fupstream-onboarding\u002FSKILL.md#when-to-skip)). Verify the upstream-onboarding standards check puts the concern to you as an explicit, answerable question — in whatever form the agent chose. If the agent skips the question and assumes an answer, D3 has regressed — report it on [issue #546](https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fissues\u002F546).\n\u003C!-- auto-mode-boundary-recipe:end -->\n\n## Gotchas\n\n| Trigger                            | Gotcha                                                                                | Fix                                                                                         |\n| ---------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |\n| Session memory read or write fails | Suppressing the detector would hide cleanup warnings for the rest of the conversation | Fail open: still run or keep the existing detector result, and allow later automatic checks |\n| `git cherry` labels squash-merged branch commits as unique (patch-id false positive) | Squash-merged branches are reported as \"unmerged\" and left behind instead of being cleaned up | Merge-detection uses `git diff --quiet` tree-equivalence as primary check, then a merge-tree no-op; `git cherry` was removed as a conclusive signal in both directions by issue #922 |\n\n| Trigger                                     | Gotcha                                                                              | Fix                                                                                   |\n| ------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |\n| Running the detector manually after startup | Treating the run-once guard as a global prohibition blocks legitimate manual checks | Keep manual detector runs available; the guard only limits the automatic startup path |\n\n---\n\n> **D3b soft exemption**: unlike the other five platform-split skills, this SKILL.md retains Copilot-specific invocation details (see §Trigger) because the session-startup trigger path is Copilot-native. The canonical routing footer below still applies and is byte-identical across all six split skills; the exemption is specific to this skill's Trigger section, not the footer.\n\n## Platform-specific invocation\n\nThis skill's methodology is tool-agnostic. Platform-specific routing lives alongside:\n\n- Copilot: [platforms\u002Fcopilot.md](platforms\u002Fcopilot.md)\n- Claude Code: [platforms\u002Fclaude.md](platforms\u002Fclaude.md)\n","# Session Startup\n\nRun-once startup guard for the automatic session-cleanup detector.\n\n## When to Use\n\n- When the SessionStart hook injects `additionalContext` into the agent's first turn\n- When deciding whether the automatic startup detector should run\n- When interpreting stale-state detector output and optionally running cleanup\n- When checking whether the installed Claude plugin version has drifted behind the marketplace\n- When preserving manual detector access after the automatic startup path fires\n\n## Purpose\n\nThe trigger mechanism is now a plugin-distributed `hooks\u002Fhooks.json` SessionStart hook rather than an LLM-interpreted per-agent directive. Apply a session-memory run-once guard after that hook fires so the detector runs at most once automatically per conversation while remaining available for explicit manual use. The same run-once pass also owns the Claude-only plugin drift backstop: when `agent-orchestra@agent-orchestra` is installed but behind the resolved marketplace version, surface the update result and a restart-vs-continue decision without blocking the session on failures.\n\n## Session Startup Check\n\nFollow these steps exactly.\n\n> **Survival**: `SMC-07` owns the startup run-once marker. Hook-driven marker writes are `within-conversation:hooks`; the honest gaps are the inline run-once marker write, the inline-vs-subagent enforcement split, the headless-Claude prompt skip, and the freshness-step fail-open user-visible surface. Do not add a second marker or new persistence mechanic.\n\n### Canonical Automatic Startup Guard Contract\n\n```json\n{\n  \"sessionStartupMarkerPath\": \"\u002Fmemories\u002Fsession\u002Fsession-startup-check-complete.md\",\n  \"checkMarkerBeforeAutomaticDetectorRun\": true,\n  \"recordMarkerAfterFirstAutomaticStartupCheck\": true,\n  \"recordMarkerRegardlessOfCleanupChoice\": true,\n  \"failOpenOnSessionMemoryAccessError\": true,\n  \"manualDetectorRunsRemainAllowed\": true,\n  \"confirmSharedBodyLoadForAgentShells\": true\n}\n```\n\n### Step 1 — Check prerequisites\n\nFor automatic startup runs, first use any hook-injected `additionalContext` if it is already present in the agent's first turn. Resolve the detector script path relative to this skill file for manual fallback: the wrapper at `scripts\u002Fsession-cleanup-detector.ps1` (in this skill's directory) self-resolves its repo root via `$PSScriptRoot`, so no environment variables are required. If `pwsh` is unavailable or the script is missing, skip the entire check silently and continue with the user's request.\n\n**Scratch-containment net (AC5)**: As part of the SessionStart hook invocation, `session-cleanup-detector.ps1` calls `scripts\u002FEnsure-ScratchGitignore.ps1` (fail-open) before running the cleanup detector. That script idempotently writes to **two distinct destinations**, and both are intentional:\n\n1. It appends `.tmp\u002F` and the mangle-literal gitignore patterns from `skills\u002Fterminal-hygiene\u002FSKILL.md ## Scratch & Temp-File Hygiene` to the consumer repo's tracked `.gitignore` when any are absent.\n2. It writes the goal-run harness runtime-state patterns (root-anchored entries for `goal-run-active.json` and `goal-run-log.jsonl`, each written with a leading slash so it matches only at the worktree root; sourced from `skills\u002Fgoal-run\u002F`, **not** from terminal-hygiene) to `.git\u002Finfo\u002Fexclude` — the per-clone, untracked exclude file. That destination is load-bearing: appending these to the tracked `.gitignore` would leave the launch repo dirty, and the goal-contract validator refuses a dirty `-RepoRoot`, which is the exact `refused: uncommitted-changes` halt this ignoring exists to prevent (#929). A maintainer auditing the script against this doc should not treat the goal-run handling as spurious.\n\nRunning it twice produces no duplicate lines in either destination. Failures in this step are swallowed — they never crash the hook or affect the cleanup-detector output.\n\n### Step 2 — Check the automatic run-once guard\n\nBefore any automatic startup detector run, check session memory for the marker at `\u002Fmemories\u002Fsession\u002Fsession-startup-check-complete.md`. If that marker is present, skip the automatic detector run and continue silently with the user's request. If session-memory lookup, read, or other access fails, fail open and still run the detector rather than suppressing the check.\n\n### Step 3 — Run the detector script\n\nFor automatic startup runs, the plugin-distributed SessionStart hook runs the detector before the agent sees the user's request and injects the resulting `additionalContext`. This step preserves the manual fallback command contract and any contributor-side direct invocation. The wrapper self-resolves its repo root via `$PSScriptRoot`, so no env vars are needed — but the terminal's working directory in a consumer repo is usually the consumer's workspace, **not** the orchestra plugin. Invoke the script by a path that resolves from wherever the `agent-orchestra` content actually lives.\n\n**Repo clone** (contributors, CWD is the repo root — relative path works):\n\n```powershell\npwsh -NoProfile -NonInteractive -File \"skills\u002Fsession-startup\u002Fscripts\u002Fsession-cleanup-detector.ps1\"\n```\n\n**Plugin-cache install** (Copilot or Claude Code consumers, CWD is the consumer workspace — pass the plugin's absolute path). Resolve the plugin directory from the installed plugin cache rather than any `chat.*Locations` setting (Copilot: the VS Code `agentPlugins\u002F...\u002Fagent-orchestra` cache path under the active product profile; Claude Code: `\u003Cplugins-cache-root>\u002Fagent-orchestra\u002F`), then:\n\n```powershell\npwsh -NoProfile -NonInteractive -File \"\u003Cplugin-root>\u002Fskills\u002Fsession-startup\u002Fscripts\u002Fsession-cleanup-detector.ps1\"\n```\n\nIf neither path resolves (the script is genuinely missing), skip the check silently per Step 1.\n\n### Step 4 — Record the run-once marker\n\nRecord or write the session-memory marker at `\u002Fmemories\u002Fsession\u002Fsession-startup-check-complete.md` after the first automatic startup check runs so later agent hops in the same conversation skip the automatic detector run. Record the marker regardless of whether cleanup is needed and regardless of whether the user later confirms, declines, or skips cleanup. If session-memory write or other access fails, fail open: continue with the detector result you already obtained, and allow later automatic checks rather than risking a missed cleanup warning.\n\n### Step 5 — Parse the output\n\nThe detector returns one of two JSON shapes.\n\n**No stale state found**: continue silently and do not mention the check to the user.\n\n```json\n{}\n```\n\n**Stale state found**: prompt the user.\n\n````json\n{\n  \"hookSpecificOutput\": {\n    \"hookEventName\": \"SessionStart\",\n    \"additionalContext\": \"**Post-merge cleanup detected** - stale tracking artifacts found:\\n\\n- `.copilot-tracking\u002Fissue-42-my-feature.yml`\\n\\nTo clean up, run:\\n```powershell\\n# Run in a PowerShell (pwsh) terminal:\\npwsh '\u002Fpath\u002Fto\u002Fagent-orchestra\u002Fskills\u002Fsession-startup\u002Fscripts\u002Fpost-merge-cleanup.ps1' -IssueNumber 42 -FeatureBranch 'feature\u002Fissue-42-my-feature'\\n```\\n\"\n  }\n}\n````\n\nThe `additionalContext` field is a Markdown-formatted description of what was found plus the command block to clean it up.\n\n**Multi-category example** (when sibling worktrees or orphan branches are also found, the cleanup command uses the composite form; `-TmpRoot '.tmp'` is auto-wired when `-IssueNumber` is present and a `.tmp\u002F` directory exists at the repo root):\n\n```powershell\npwsh '\u002Fpath\u002Fto\u002Fagent-orchestra\u002Fskills\u002Fsession-startup\u002Fscripts\u002Fpost-merge-cleanup.ps1' -IssueNumber 42 -FeatureBranch 'feature\u002Fissue-42-my-feature' -TmpRoot '.tmp' -SiblingWorktrees @('\u002Fpath\u002Fto\u002Fworktree') -OrphanBranches @('claude\u002Fold-feature')\n```\n\nWhen writing a `.claude\u002Fsettings.json` allowlist entry, match against `Bash(pwsh*post-merge-cleanup.ps1*)` — this covers both the single-issue form and the composite form with `-SiblingWorktrees` and `-OrphanBranches` arguments.\n\nDetector findings are reported in this order when present:\n\n- **Current branch**: flags the current branch when its upstream was merged\u002Fdeleted. A current `claude\u002F*` no-upstream branch is a candidate on branch shape alone — prefix and upstream state — since issue #922 removed the ancestry pre-filter that used to gate it. That filter rendered a merged verdict of its own, and because a squash merge writes a new commit, a squash-merged branch is never an ancestor and was discarded before any evidence was gathered. Candidacy only becomes eligibility through the shared evidence-gated eligibility primitive (`Test-WorktreeBranchRemovalEligible`): git-only tree-equivalence or an OID-checked merged-PR-by-head match, or — for a branch with no unique commits — an OID-checked merged PR or a closed parent issue. When the signal is unverifiable, the branch is reported as needing manual review (with the specific reason) rather than silently retained or falsely offered for cleanup. The shared `Test-WorktreeIsPrimary` guard excludes the primary checkout from this candidate check before eligibility is ever evaluated. When eligible, the current branch — whether it has an issue ID (`feature\u002Fissue-N-…`) or not — is rolled into the same composite `pwsh ... post-merge-cleanup.ps1 ... -FeatureBranch '\u003Cbranch>'` invocation as the other categories, so confirming cleanup triggers exactly one permission prompt. The script switches to the default branch, pulls, and deletes the now-stale current branch on your behalf. (The `Documents\u002FDesign\u002Fsession-hooks.md` note about running current-worktree cleanup from another checkout still applies if you choose to bypass the script and run the underlying git commands manually — but the recommended path is the composite invocation.)\n- **Tracking files**: flags issue-scoped `.copilot-tracking\u002F` files whose remote `feature\u002Fissue-*` branch is gone; persistent calibration data remains excluded.\n- **`.tmp\u002F` scratch clearing**: per-issue `.tmp\u002F{N}-*` and `.tmp\u002Fissue-{N}*` scratch files are cleared at cleanup time (not detected). `-TmpRoot '.tmp'` is auto-wired into the composite invocation when `-IssueNumber` is present and a `.tmp\u002F` directory exists at the repo root; the `Remove-IssueTmpScratch` function handles removal and emits a summary line.\n- **Sibling worktrees**: flags sibling worktrees on `claude\u002F*` no-upstream branches or upstream-deleted `feature\u002Fissue-*` branches, then routes each candidate through the same shared evidence-gated eligibility primitive (`Test-WorktreeBranchRemovalEligible`) as the current-branch check above — git-only tree-equivalence or an OID-checked merged-PR-by-head match for a branch with unique commits, or an OID-checked merged PR or closed parent issue for a branch with none. A candidate whose signal is unverifiable is reported as needing manual review (`Sibling worktree branch \u003Cbranch> at \u003Cpath> needs manual review: \u003Creason>`) instead of being silently dropped or falsely offered for cleanup. The shared `Test-WorktreeIsPrimary` guard excludes the primary checkout from sibling candidacy on every code path, including the case where the agent runs from a non-primary linked worktree and the primary checkout would otherwise appear to be a sibling. Eligible candidates' cleanup is rolled into the single composite `pwsh ... post-merge-cleanup.ps1 -SiblingWorktrees @(...) -OrphanBranches @(...)` invocation appearing inside the fenced PowerShell block — not as individual `git` commands. Cleanup time re-verifies eligibility independently rather than trusting the detector's snapshot, then reports the verified post-removal state honestly: `removed`, `removed-partial-root-held` (contents gone but an empty directory remains — another process held the root), `removed-partial-content-remains` (unregistered but files remain — inspect manually), `stale-registration` (the removal attempt did NOT succeed in deregistering the worktree — the directory is gone but `git worktree list` still shows it registered; this is a failed-to-deregister outcome and is never counted toward the \"Deleted N sibling worktree(s)\" summary, unlike the other outcomes above), `failed`, or `verification-indeterminate` — never the old false \"skipping\" message for a worktree a removal attempt had already partially destroyed. A locked worktree whose directory still exists is left for manual review and never force-removed on the lock flag alone (`skipped locked worktree ... - remove the lock first`). A locked worktree whose directory is confirmed absent via `Test-Path` (issue #522, corrected) is double-force-removed (`git worktree remove --force --force`) regardless of the porcelain `prunable` marker — real git never sets `prunable` on a locked worktree, so the corrected clause keys on `Test-Path`-verified absence alone, never on the (for a locked worktree, unreachable) `prunable` self-report — and the actual post-attempt state is then verified through the same honest outcome probe, never assumed.\n- **Orphan branches**: flags unattached merged `claude\u002F*` no-upstream branches and unattached upstream-deleted `feature\u002Fissue-*` branches. Their cleanup is rolled into the single composite `pwsh ... post-merge-cleanup.ps1 -SiblingWorktrees @(...) -OrphanBranches @(...)` invocation appearing inside the fenced PowerShell block — not as individual `git` commands. Squash-merged branches are classified as merged from content evidence — strict tree-equivalence (`git diff --quiet` exit 0) or a merge-tree no-op. Since issue #922 `git cherry` establishes neither verdict: it compares patch identities, so a revert leaves it reporting merged and a squash leaves it reporting unmerged for a fully shipped branch. Both no-upstream and upstream-deleted candidates are routed at detection time through the same shared evidence-gated eligibility primitive (`Test-WorktreeBranchRemovalEligible`) used by the sibling and current-branch checks above; a candidate whose signal is unverifiable is reported as needing manual review (with the specific reason) instead of being silently dropped. `feature\u002Fissue-N-*` orphan branches — the session-convention naming this repo uses — carry an additional `; eligible for auto-resolve at cleanup time` suffix: the detection-time verdict is a report, not the final word, because the destructive `git branch -D` at cleanup time always re-evaluates live rather than trusting the detector's snapshot. For a zero-unique-commit `feature\u002Fissue-N-*` or `claude\u002F*-N-hex` branch with a derivable issue id (the two session-convention naming shapes the shared primitive can derive an issue id from), cleanup time reuses the same shared primitive as detection (OID-checked-PR-first, then closed-issue evidence). For every other `feature\u002Fissue-N-*` case — one or more unique commits, or the unique-commit count itself could not be determined (a `git rev-list` failure) — cleanup time instead uses the richer tree-equivalence-plus-absorption chain (`Test-BranchMergedIntoDefault` + `Test-OrphanBranchAutoResolveEligible`): the parent issue must be CLOSED, a merged PR with matching `headRefOid` must exist, and every residual commit must be absorbed by main (ancestor-on-main \u002F patch-equivalent \u002F spike-only confined to `.tmp\u002Fissue-N\u002F` \u002F tree-at-HEAD-matches-main). Honest skip messages at cleanup time: `auto-resolve declined` (signals say not shipped; branch retained), `could not verify auto-resolve signals` (gh unavailable — fail-open, branch retained), and `branch not reachable from default (merged-state re-check returned false)` (a race between detection and the pre-delete re-check). Non-`feature\u002Fissue-N-*` orphans (e.g., `claude\u002F*`) never carry the auto-resolve suffix or use the absorption chain. A zero-unique-commit non-`feature\u002Fissue-N-*` orphan with a derivable issue id (`claude\u002F*-N-hex`) clears only through the shared primitive's own zero-commit evidence (OID-checked-PR-first, then closed-issue evidence), same as described above. A non-`feature\u002Fissue-N-*` orphan with one or more unique commits instead falls through to `Test-BranchMergedIntoDefault` directly: git-only tree-equivalence, or — when that is inconclusive — the same OID-checked merged-PR-by-head lookup the primitive itself uses (never a name-only `gh pr list` match, which would misreport a branch that merely shares its name with an unrelated merged PR as shipped).\n- **Fail-open behavior**: fetch, worktree-list, for-each-ref, and ref-lookup failures suppress only the unverifiable candidate and do not fail the startup session.\n- **Deliberate silence for live work (issue #922)**: a candidate the eligibility gate concludes is *definitively unmerged* — content evidence shows it carries changes absent from the remote default, and no merged pull request matches its current tip — renders **nothing at all**: no finding line, no command argument, no contribution to the ten-line display cap. This is the one class the check deliberately says nothing about, because it is live work rather than a leftover, and a manual-review line for every in-flight worktree at every session start is noise that trains you to ignore the block. Every other not-eligible candidate is reported with its cause. Silence is reached only after the candidate has been *evaluated* to that conclusion, never by declining to look at it.\n- **Opt-in cleanup**: the detector only reports findings. Nothing is removed unless the user confirms, and explicit manual detector runs remain available after the automatic guard fires.\n\nClaude cleanup findings are capped at 10 concrete `claude\u002F*` entries; additional entries are summarized with a `+N more` hint.\n\n### Step 6 — Prompt the user\n\nIf the output contains `hookSpecificOutput`, present the `additionalContext` text to the user and ask for confirmation before running cleanup, offering exactly two options: \"Yes — run cleanup\" and \"No — skip for now\". The confirmation covers only the fenced cleanup block; inline current-worktree commands are deliberately narrative and require a separate manual run from another checkout.\n\n### Step 7 — Run cleanup (only if confirmed)\n\nIf the user confirms, run all lines from the fenced code block inside `additionalContext` in the terminal. Skip blank lines; `#`-prefixed comment lines are safe to include. Do not scrape or execute inline current-worktree cleanup text outside the fenced block. Report what was cleaned up when complete.\n\n### Step 7b — Run the Claude plugin drift check\n\nAfter the cleanup path completes, run this Claude-only sub-step before continuing with the user's request. Copilot skips this sub-step silently because it has no version-cache analog.\n\n**Ordered procedure (must follow this exact sequence):**\n\n1. **Refresh marketplace view**: Run `claude plugin marketplace update` with a 5-second timeout. If it fails or times out, emit `marketplace freshness check failed — using cached view` and continue with the cached view. Do not retry transient freshness failures.\n\n2. **Resolve installed state**: Read `~\u002F.claude\u002Fplugins\u002Finstalled_plugins.json`. Find the `agent-orchestra@agent-orchestra` entry and read its installed `version` plus `marketplace` field. If the file is missing, the entry is absent, or the entry has no `marketplace` field, fail open: silent-skip or emit a one-line minimal error and continue.\n\n3. **Resolve marketplace latest**: Read `~\u002F.claude\u002Fplugins\u002Fmarketplaces\u002F{marketplace}\u002F.claude-plugin\u002Fplugin.json`. If the installed version matches, continue silently (verified-current silence).\n\n4. **Detect drift and emit marker**: If the installed version is behind the marketplace version, emit `Drift detected — updating…` as an inline status line. This marker signals that the install is about to begin.\n\n5. **Run the install**: Run `claude plugin update agent-orchestra@agent-orchestra --yes` with a 30-second timeout. On success, emit:\n   `Updated 'agent-orchestra@agent-orchestra' from {old} -> {new}. Current session runs under old code until restart.`\n   On failure, retry once on transient errors. If the retry also fails, emit a failure summary and the manual fallback command:\n   `Plugin update failed: {error}. Manual fallback: claude plugin install agent-orchestra@agent-orchestra`\n\n6. **Ask (only after step 5 completes)**: The question MUST NOT be presented while the install is in flight or before it has been attempted (success or announced failure). After step 5 completes — whether it succeeded or failed — present the appropriate prompt (see the `Drift Failure-Mode Prompt` subsection below).\n\n**Silent skip conditions for this sub-step**: when `pwsh` is missing; when the `claude` CLI is missing or fails; when the marketplace registration points at a non-git local directory, dirty tree, or detached HEAD (the local-path classification already surfaces remediation).\n\n**Local-path marketplace classification** (run before step 4 when a local path is detected):\n\n- Clean git repo behind `origin\u002Fmain`: surface that the marketplace path is behind and include `claude plugin marketplace remove agent-orchestra` and `claude plugin marketplace add Grimblaz\u002Fagent-orchestra` remediation commands\n- Non-git local directory: surface that the registration is a non-git local directory\n- Dirty tree or detached HEAD: surface that local marketplace remediation is skipped because the clone has local work\n- Fetch failure: fail open and continue\n\n**Headless Claude behavior**: Headless Claude runs perform the same steps 1–5 and same fail-open emission; only step 6 (the stop\u002Fcontinue question) is suppressed. The verified-current silence guarantee applies only on the freshness-success branch; on freshness failure, cached comparison is a documented accepted limitation.\n\nThis sub-step shares the existing Step 4 run-once marker; do not introduce a second marker or persistence mechanism.\n\n### Drift Failure-Mode Prompt\n\nThe canonical `### Inline-Dispatch Option Labels` YAML block keeps exactly 4 keys (`cleanup_yes`, `cleanup_no`, `drift_stop`, `drift_continue`) — no additional keys are added for failure-mode labels.\n\n**On install success** (step 5 of Step 7b succeeded): ask using the canonical `drift_stop` and `drift_continue` option labels verbatim:\n\n- `Stop — I'll restart now` (maps to `drift_stop`)\n- `Continue — run under old code` (maps to `drift_continue`)\n\n**On install failure** (step 5 of Step 7b failed, including post-retry): render a one-off failure-recovery prompt (not a canonical labeled option — these labels are session-ephemeral only):\n\n- `Stop — I'll restart and try the install again`\n- `Continue — under old code (install failed)`\n\nInclude the manual fallback command in the failure prompt body so the user has something concrete to act on when they choose to restart: `claude plugin install agent-orchestra@agent-orchestra`.\n\nThis conditional rendering keeps the `inline-dispatch-contract.Tests.ps1` `ExpectedCount 4` and `Should -Be 4` assertions green because failure-mode labels are prose-rendered and session-ephemeral, not added to the canonical YAML block.\n\n### Permission allowlist (recommended)\n\nTo avoid repeated per-command permission prompts when the session-startup cleanup runs, add these entries to your project's `.claude\u002Fsettings.json` `allow` list:\n\n```json\n{\n  \"permissions\": {\n    \"allow\": [\n      \"Bash(pwsh*post-merge-cleanup.ps1*)\",\n      \"Bash(git branch -D feature\u002F*)\",\n      \"Bash(git branch -D claude\u002F*)\",\n      \"Bash(pwsh*persist-marker.ps1*)\"\n    ]\n  }\n}\n```\n\nThe first entry covers the composite cleanup-script invocation that the session-startup hook emits. The second and third entries cover edge cases where manual cleanup commands are needed outside the script (for example, when the current-worktree narrative guidance is followed manually from another checkout). The fourth entry (issue #893) covers `skills\u002Fsession-memory-contract\u002Fscripts\u002Fpersist-marker.ps1`, the marker-write primitive that supersedes hand-composed `gh issue comment` \u002F `gh pr comment` calls for every migrated marker family (see `skills\u002Fsession-memory-contract\u002Freferences\u002Fhandoff-markers.md`).\n\nThis allowlist entry is safe specifically because of `persist-marker-core.ps1`'s scratch-root bounding (issue #893 slices s6\u002Fs11, root check added by the PR #917 review's F1 fix): `Resolve-MarkerScratchBoundedPath` requires every `-BodyFile`\u002Fburst-manifest `bodyFile` to resolve, via real `Resolve-Path` canonicalization (traversal-aware — never a raw string-prefix test on the unresolved input) plus a symlink\u002Fjunction reparse-point check on the scratch root itself AND a per-segment walk of every descendant down to the leaf (`Resolve-Path` alone does not dereference reparse points, so both the root and every descendant must be checked explicitly), inside the consumer repo's `.tmp\u002F` scratch root before the script will read it. Known residual gap: hard links are not reparse points and are not detected by this walk. Without that bound, a broad `Bash(pwsh*persist-marker.ps1*)` allowlist entry would let any `-BodyFile` argument read an arbitrary file on disk and post its contents to a public GitHub comment — an arbitrary-file-read-to-public-comment primitive. The bounding is what keeps the wide `pwsh*persist-marker.ps1*` glob pattern (rather than a narrower, harder-to-match pattern) safe to allowlist: a payload path outside `.tmp\u002F` is refused pre-write, before any network call.\n\nAutomated detection or installation of these allowlist rules is out of scope for this issue and would be a follow-up. AC4 of issue #500 is satisfied by this documentation per the Experience-Owner framing: \"documents how the cleanup confirmation answer translates into permission.\"\n\nOn Windows, the existing `Bash(pwsh*post-merge-cleanup.ps1*)` allowlist entry covers `pwsh` invocations through Claude Code's Bash tool surface; no separate `PowerShell(...)` entry is required.\n\nIf the cleanup script (or a settings.local.json edit applying these recommendations) is silently denied even after explicit user authorization in the same conversation, you are observing the contextual-classifier override pattern documented in [issue #546](https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fissues\u002F546) (see comments [4414368049](https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fissues\u002F546#issuecomment-4414368049) and [4414376114](https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fissues\u002F546#issuecomment-4414376114)). Workaround: apply this allowlist *before* the deny fires by editing `.claude\u002Fsettings.local.json` directly; do not ask the agent to make the edit on your behalf in the same turn you authorize it.\n\nIssue #548's `gh issue view` and `gh pr list --head` calls run as child processes of `pwsh` inside `post-merge-cleanup.ps1`, so the existing `Bash(pwsh*post-merge-cleanup.ps1*)` allowlist entry above covers them. No new `Bash(gh ...)` entries are needed. The L2 customer-self-verification responsibility documented above (issue #546 contextual-classifier override) applies symmetrically to these gh calls — if a same-turn denial is observed, apply the existing workaround.\n\n### Step 7c — Portfolio snapshot (Claude-only, fail-open)\n\n\u003C!-- scope: claude-only -->\n\nThis step runs **outside** the Step 4 run-once guard — it refreshes every session start, not just the first. Skip this step if the user chose `drift_stop` in Step 7b (session is ending; no point rendering a snapshot). Copilot: silent skip.\n\n**Behavior**: If `Documents\u002FPlanning\u002Fsequence.yaml` exists in the working directory and is parseable (has a `control_tower:` integer field), run:\n\n```powershell\ngh issue view {control_tower} --json body --jq '.body' | Select-String 'rendered by render-portfolio\\.ps1' -Context 4,0\n```\n\nwith a ~3-second timeout (set at the Bash-tool invocation level, not as a `gh` CLI flag). Surface at most **5 lines** of the rendered section by anchoring on the footer line (`rendered by render-portfolio.ps1`) and showing up to 4 preceding content lines — this guarantees the footer is always present regardless of section length. Truncate with `(…)` if more lines precede those 4.\n\nAny failure (file absent, parse error, `gh` timeout, network error, missing `control_tower` field, non-zero exit) → **silent skip**. Never block session startup on a portfolio snapshot.\n\nSee `platforms\u002Fclaude.md` for the Claude Code tool invocation note.\n\n### Step 7d — Baseline-eligibility harvest (Claude-only, fail-open)\n\n\u003C!-- scope: claude-only -->\n\nLike Step 7c, this step runs outside the Step 4 run-once guard: it can act again on later session starts, not just the first, because the underlying data it reads is already cached for an hour. Copilot: silent skip (no analog).\n\n**What it does (plain summary)**: Some pull requests get their AI-usage cost numbers written down partway through a work session — before that session actually finished — because the PR happened to be created mid-session. Those numbers are a fair estimate at the time but not exact. This step checks, at the start of a later session, whether any recently-merged PR is still carrying one of those mid-session estimates and, when it is safe to do so, re-counts that PR's session from scratch and quietly swaps in the exact end-of-session numbers.\n\n**Invocation**: Call `Invoke-CostBaselineHarvest` (`.github\u002Fscripts\u002Flib\u002Fcost-baseline-harvest.ps1`) with the current session's `ParentCwd` (its own working directory) and `RepoRoot` (the repo root), and optionally `Slug` — the function resolves `Slug` itself from `RepoRoot` when it is omitted. See `platforms\u002Fclaude.md` for the Claude Code tool invocation note.\n\n**Fail-open contract**: Any failure — no GitHub access, no matching local session transcript, a re-count that comes back empty, or a comment write that fails — leaves this step a silent no-op. Never block session startup on this harvest, and never surface an error to the user.\n\n**Budget**: At most one expensive re-count runs per `Invoke-CostBaselineHarvest` call. The function narrows down to eligible candidates first (recently merged, still carrying a mid-session estimate, not already attempted), then walks them until it finds the first one that both has a matching local session transcript and is confirmed still merged by a fresh GitHub lookup — only that one candidate spends the re-count budget, whatever the outcome. See the function's own doc comment in `cost-baseline-harvest.ps1` for the full selection and promotion mechanics.\n\n**What surfaces to the user** (issue #824 CE Gate fix, F1): silent when nothing was attempted this startup — no eligible candidate, or every candidate failed the cheap verify-then-select\u002Flive-merge gates before the harvest ever spent its one-re-count budget. Once a candidate has consumed that budget, though, the outcome is always shown as a single line, whichever of these it is: `upgraded #{pr} to end-of-session` on success, or the function's own `upgrade expected for #{pr} — {reason}` diagnostic otherwise (e.g. transcript unavailable, still partial, token count lower than persisted, composite comment not found, cost section format mismatch). The design intent here is \"announce both success and expected-but-failed upgrades\" — a candidate that was actually re-counted and didn't convert is not startup noise, it's the one signal a maintainer needs to notice a baseline row that isn't converging. Genuine fail-open no-ops (nothing to attempt, or an infrastructure failure before the budget was spent) stay silent as before.\n\n### Step 8 — Continue with the user's request\n\nAfter the automatic startup path is complete, continue with the user's original request only after completing any other applicable startup steps below, including Step 7b and Step 9 when they apply. In hook-driven runs, this means consuming any injected `additionalContext`, recording the run-once marker, and then proceeding. This automatic run-once guard applies only to the cleanup-detector plus Claude drift-check path; explicit or manual detector runs still remain allowed after the automatic guard fires.\n\n### Step 9 — Confirm paired shared-body load (agent shells with a paired body)\n\nThis step is not gated by the session-startup run-once marker and fires on every agent-role adoption in the conversation, including every subagent dispatch. Do not wrap this step in the Step 2 or Step 4 marker guard.\n\nIf you are operating as an agent shell at `agents\u002F{name}.md` whose body contains a `## Shared methodology` section naming a paired `agents\u002F{Name}.agent.md`, **or if you are an inline slash-command dispatch at `commands\u002F{name}.md` that names a paired `agents\u002F{Name}.agent.md` body in its load reference**, load that paired file via the platform's file-read tool before proceeding. Use the D1 plugin-cache-first body resolution sequence documented in your host file's own `### Step 9 — Paired-body halt-on-fail` block (each agent shell and each command file carries its own per-body cascade).\n\nIf that load fails, emit exactly: `⚠️ Shared-body load failed for agents\u002F{Name}.agent.md — {error}. This run cannot continue without the canonical methodology; surface this to the user and stop.` After emitting that message, do not make any further tool calls, subagent dispatches, questions to the user, or any other agent actions.\n\nIf the paired load succeeds, cite it with `Shared body loaded — proceeding as {AgentName}` and include the full-form H2 body names exactly as they appear in the shared body, excluding `Platform-specific invocation`.\n\nIf you are not in a paired-body context, skip this step silently.\n\nIf the same shared body is loaded more than once in a conversation, the load is idempotent — loading the same file a second time is harmless and does not require deduplication logic.\n\n### Inline-Dispatch Option Labels\n\n```yaml\ncanonical_option_labels:\n  cleanup_yes: \"Yes — run cleanup\"\n  cleanup_no: \"No — skip for now\"\n  drift_stop: \"Stop — I'll restart now\"\n  drift_continue: \"Continue — run under old code\"\n```\n\nEnforcement paths: Claude inline slash-command dispatch (`\u002Fexperience`, `\u002Fdesign`, `\u002Fplan`, `\u002Forchestrate`, and `\u002Fpolish`) enforces the pre-flight protocol via a load reference to this skill; the per-command prose duplication was removed in issue #498 (DRY pass following #481). Each command file retains a `### Step 9 — Paired-body halt-on-fail` block with its D1 body-resolution cascade immediately after the load reference; Step 9 of this skill now recognizes inline slash-command dispatch as a paired-body context, so the halt-on-fail contract applies to both `Agent` tool subagent dispatches and inline command invocations.\n\n## Silent Skip Conditions\n\nSkip the entire check silently in any of these cases:\n\n- `pwsh` is not available on `PATH`\n- The detector script does not exist at the expected path\n- The detector script returns an error or non-JSON output\n\nThese are normal conditions in repos that have not installed the agent-orchestra plugin or in environments where PowerShell is unavailable.\n\n\u003C!-- auto-mode-boundary-recipe:begin -->\n## Auto-mode Boundary Verification Recipe\n\nRun these three checks in Claude Code to audit the auto-mode boundary in your session.\n\n**1. Positive case (D1):** Run `git status` under auto-mode. It should execute immediately without a permission prompt. If it prompts, D1 has regressed — check your `permissions.allow` list.\n\n**2. Risky case (D2):** Run `gh pr merge --admin` against a draft PR you own, then **abort at the Claude Code permission prompt** — do not complete the merge. Expected: a permission prompt appears before execution. If no prompt appears and the command is silently denied, you are observing the L2 contextual-classifier override pattern documented above. Fallback: (a) record the chat transcript verbatim, (b) confirm the [cleanup-script allowlist entry](#permission-allowlist-recommended) is applied (project-level for contributors, `~\u002F.claude\u002Fsettings.json` for plugin consumers — see that skill section for the correct path), (c) file an issue at the [agent-orchestra repo](https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fissues) with the transcript and settings excerpt.\n\n**3. Axis B case (D3):** Run `\u002Fexperience N` against an **existing issue** you own (use an issue that already carries an upstream marker — e.g., a `\u003C!-- experience-owner-complete-{ID} -->` comment from a prior `\u002Fexperience` run — so the upstream-onboarding standards check actually fires; a fresh unframed issue will skip the standards check per [skills\u002Fupstream-onboarding\u002FSKILL.md § When to Skip](..\u002Fupstream-onboarding\u002FSKILL.md#when-to-skip)). Verify the upstream-onboarding standards check puts the concern to you as an explicit, answerable question — in whatever form the agent chose. If the agent skips the question and assumes an answer, D3 has regressed — report it on [issue #546](https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fissues\u002F546).\n\u003C!-- auto-mode-boundary-recipe:end -->\n\n## Gotchas\n\n| Trigger                            | Gotcha                                                                                | Fix                                                                                         |\n| ---------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |\n| Session memory read or write fails | Suppressing the detector would hide cleanup warnings for the rest of the conversation | Fail open: still run or keep the existing detector result, and allow later automatic checks |\n| `git cherry` labels squash-merged branch commits as unique (patch-id false positive) | Squash-merged branches are reported as \"unmerged\" and left behind instead of being cleaned up | Merge-detection uses `git diff --quiet` tree-equivalence as primary check, then a merge-tree no-op; `git cherry` was removed as a conclusive signal in both directions by issue #922 |\n\n| Trigger                                     | Gotcha                                                                              | Fix                                                                                   |\n| ------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |\n| Running the detector manually after startup | Treating the run-once guard as a global prohibition blocks legitimate manual checks | Keep manual detector runs available; the guard only limits the automatic startup path |\n\n---\n\n> **D3b soft exemption**: unlike the other five platform-split skills, this SKILL.md retains Copilot-specific invocation details (see §Trigger) because the session-startup trigger path is Copilot-native. The canonical routing footer below still applies and is byte-identical across all six split skills; the exemption is specific to this skill's Trigger section, not the footer.\n\n## Platform-specific invocation\n\nThis skill's methodology is tool-agnostic. Platform-specific routing lives alongside:\n\n- Copilot: [platforms\u002Fcopilot.md](platforms\u002Fcopilot.md)\n- Claude Code: [platforms\u002Fclaude.md](platforms\u002Fclaude.md)\n",{"name":5,"description":7},"main","d5486aacc32c9ca1b07173cfd530f8fe75c29e58","skills\u002Fsession-startup",0,"https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra","https:\u002F\u002Fgithub.com\u002FGrimblaz","2026-08-16 01:12:46",{"source":8,"skillName":5,"commands":54,"prompt":64,"zipAvailable":65},[55,58,61],{"tool":56,"command":57},"npx","npx skills add Grimblaz\u002Fagent-orchestra --skill session-startup",{"tool":59,"command":60},"bunx","bunx skills add Grimblaz\u002Fagent-orchestra --skill session-startup",{"tool":62,"command":63},"pnpm","pnpm dlx skills add Grimblaz\u002Fagent-orchestra --skill session-startup","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fsession-startup\u002FSKILL.md",true,[67,73,77,83,87,91,95],{"path":68,"resourceType":69,"resourceTypeDesc":70,"sizeBytes":71,"sourceHtmlUrl":72},"platforms\u002Fclaude.md","REFERENCE","参考文档",6212,"https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fsession-startup\u002Fplatforms\u002Fclaude.md",{"path":74,"resourceType":69,"resourceTypeDesc":70,"sizeBytes":75,"sourceHtmlUrl":76},"platforms\u002Fcopilot.md",1148,"https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fsession-startup\u002Fplatforms\u002Fcopilot.md",{"path":78,"resourceType":79,"resourceTypeDesc":80,"sizeBytes":81,"sourceHtmlUrl":82},"scripts\u002FEnsure-ScratchGitignore.ps1","SCRIPT","脚本",7838,"https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fsession-startup\u002Fscripts\u002FEnsure-ScratchGitignore.ps1",{"path":84,"resourceType":79,"resourceTypeDesc":80,"sizeBytes":85,"sourceHtmlUrl":86},"scripts\u002Fpost-merge-cleanup.ps1",42416,"https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fsession-startup\u002Fscripts\u002Fpost-merge-cleanup.ps1",{"path":88,"resourceType":79,"resourceTypeDesc":80,"sizeBytes":89,"sourceHtmlUrl":90},"scripts\u002Fsession-cleanup-detector-core.ps1",100134,"https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fsession-startup\u002Fscripts\u002Fsession-cleanup-detector-core.ps1",{"path":92,"resourceType":79,"resourceTypeDesc":80,"sizeBytes":93,"sourceHtmlUrl":94},"scripts\u002Fsession-cleanup-detector.ps1",1732,"https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fsession-startup\u002Fscripts\u002Fsession-cleanup-detector.ps1",{"path":96,"resourceType":79,"resourceTypeDesc":80,"sizeBytes":97,"sourceHtmlUrl":98},"scripts\u002Fsession-startup-git-helpers.ps1",72416,"https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fsession-startup\u002Fscripts\u002Fsession-startup-git-helpers.ps1",[100,131,151,167,192,213],{"skillNo":101,"name":102,"nameZh":6,"description":103,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":104,"categories":105,"keywords":112,"publishedAt":41},"01M6C48YMJVFHM7DVGG1WWW5RJ","webapp-testing","Playwright end-to-end testing guidance for web apps, with practical patterns and setup steps. Use when creating or improving browser-based E2E coverage, test stability, and CI execution. DO NOT USE FOR: React component tests (use ui-testing), canvas game interaction (use browser-canvas-testing), TDD workflow (use test-driven-development), debugging failures (use systematic-debugging), or randomized property tests (use property-based-testing).","https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fwebapp-testing\u002FSKILL.md",[106,109],{"categoryNo":107,"categoryName":108,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":110,"categoryName":111,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",[113,114,115,116,117,40,118,119,120,121,122,123,124,125,126,127,128,129,130,28],"testing","webapp testing","webapp","canvas","tdd","playwright","test","web","react","component","debugging","development","browser","ci","execution","e2e","game","coverage",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":135,"categories":136,"keywords":137,"publishedAt":41},"01M6C48TNNP2KCSQXV8YK7SK49","verification-before-completion","Evidence-based verification checklist before marking work complete. Use before PRs, releases, marking tickets done, or any \"I'm finished\" declaration; also when naming a baseline commit for a differential suite claim, when designing a mutation campaign or red-run evidence, when making an absence claim, a universal claim, or a one-time demonstration offered as a standing guarantee, when attributing a measurement or claiming a sweep or verification loop is complete, and when stating what a check or a CI suite will do about a change. DO NOT USE FOR: post-merge cleanup or archival (use post-pr-review) or processing GitHub review comments (use code-review-intake).","https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fverification-before-completion\u002FSKILL.md",[],[138,139,33,140,141,38,37,36,35,142,143,144,126,145,146,147,148,28,149,150],"verification","verification before completion","change","commit","code","code-review","intake","loop","verification loop","work","check","evidence","campaign",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":157,"publishedAt":41},"01M6C48QZWNTP7JGKD32QZAKKX","validation-methodology","Reusable validation and review methodology for staged validation, failure triage, and prosecution-depth setup. Use when running validation ladders, triaging failures, or executing adversarial review passes. DO NOT USE FOR: CE Gate orchestration, specialist dispatch ownership, or step execution flow (keep those in Code-Conductor).","https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fvalidation-methodology\u002FSKILL.md",[],[158,159,160,33,161,162,163,164,142,165,127,28,166],"validation","methodology","validation methodology","triage","orchestration","setup","adversarial review","flow","dispatch",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":179,"publishedAt":41},"01M6C48MCAHG5Z79BJ369CRYRZ","upstream-onboarding","Shared opening-phase protocol for upstream agents (Experience-Owner, Solution-Designer, Issue-Planner) and Code-Conductor when invoked on an existing GitHub issue. Renders a scaled context brief and runs a standards check on inherited work at each phase boundary. Use when a user-invocable agent receives a request referencing an existing GitHub issue and must load solution-authoring and upstream-onboarding at the opening phase. DO NOT USE FOR: subagent dispatches (which already operate within an assessed session context); post-merge review (use post-pr-review); research or non-tree subagents.","https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fupstream-onboarding\u002FSKILL.md",[173,174],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":175,"categoryName":176,"taxonomyType":177,"taxonomyTypeDesc":178},"01JBSEEDCAT000000000000038","人力资源","PROFESSION","适用职业",[180,181,33,182,183,38,184,37,142,185,186,187,188,189,190,147,148,191,24,28],"onboarding","upstream onboarding","github","research","standards check","protocol","brief","boundary","designer","subagent","planner","user",{"skillNo":193,"name":194,"nameZh":6,"description":195,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":196,"categories":197,"keywords":206,"publishedAt":41},"01M6C48GPVE120TGPMD1CS365H","ui-testing","Resilient React component testing strategies focusing on user behavior. Use when writing or reviewing component-level React tests, fixing flaky component tests, or establishing React testing patterns. DO NOT USE FOR: Playwright E2E tests (use webapp-testing), canvas game interaction (use browser-canvas-testing), TDD workflow (use test-driven-development), debugging failures (use systematic-debugging), UI component design (use frontend-design), or randomized property tests (use property-based-testing).","https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fui-testing\u002FSKILL.md",[198,199,200,203],{"categoryNo":107,"categoryName":108,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":110,"categoryName":111,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":201,"categoryName":202,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":204,"categoryName":205,"taxonomyType":177,"taxonomyTypeDesc":178},"01JBSEEDCAT000000000000032","设计师",[113,207,208,209,210,211,116,40,118,119,121,122,123,124,125,212,128,129,28,115],"ui","ui testing","frontend","design","writing","testing patterns",{"skillNo":214,"name":215,"nameZh":6,"description":216,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":217,"categories":218,"keywords":221,"publishedAt":41},"01M6C48DRJ9XBDR2FW0W37DY53","ui-iteration","Reusable screenshot-driven UI polish workflow for iterative visual refinement. Use when improving layout, hierarchy, spacing, feedback, or Tailwind-based presentation through repeated review-and-adjust passes. DO NOT USE FOR: functional bug fixing, accessibility-only audits, or major redesign strategy work (use Code-Smith or frontend-design).","https:\u002F\u002Fgithub.com\u002FGrimblaz\u002Fagent-orchestra\u002Fblob\u002Fmain\u002Fskills\u002Fui-iteration\u002FSKILL.md",[219,220],{"categoryNo":110,"categoryName":111,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":204,"categoryName":205,"taxonomyType":177,"taxonomyTypeDesc":178},[207,222,209,210,33,40,223,142,224,225,226,227,228,147,229,230,28,231],"ui iteration","accessibility","screenshot","Tailwind","polish","ui polish","feedback","redesign","strategy","only"]