# ChicoryTV issue-queue handoff (client-neutral kickoff + workflow lore)

Static kickoff prompt + workflow lore, pruned (not appended) as protocols change — git history has
the rest. **Queue state is live Gitea state**, resolved fresh every session by
`scripts/select-queue.sh`; never written here as a snapshot, never read from stale prose or a
closed tracker.
<!-- archival:237 --> ersatztv#237 was the pinned pickup tracker through 2026-07-21; it is CLOSED
and now purely historical (superseded by `startup.parallel-orientation` in `docs/decisions.md`; full
history in `docs/decisions/archive/startup.md`). Do not read it for current queue state.

---

## Current phase — maintenance / backlog mode (terminal transition, 2026-07-13)

The ChicoryTV rewrite + go-live arc is **COMPLETE** and closed out. The project is now in
**maintenance / backlog mode**. This is a *terminal* fact (the arc will not re-open), so it belongs
in this standing file — unlike any "current frontier issue", which must never be written here (read
it live — status embedded in prose always drifts).

So a fresh session stops re-discovering this every time:
- The **arc / gate / open-`review` tiers are normally EMPTY now.** An empty arc frontier is the
  *expected steady state* — not an anomaly to investigate, not a reason to stop, and not a reason
  to ask the user what to work on. Fall straight through to the `priority:`-label cascade and
  claim the top-ranked eligible backlog issue.
- **Selection is priority-label-driven**: OPEN-milestoned → `priority: high` → `priority: medium`
  → `priority: low`; within a tier, lowest issue number. **Exclude** `in-progress`, pull requests,
  and **any issue with an open `blocked-by` dependency** — Gitea tracks these (`GET
  /issues/{n}/dependencies`) and auto-clears the block when the blocker closes, so a blocked issue
  becomes eligible automatically once its blocker lands. Do not pick an issue ahead of its open
  blocker (e.g. #70 waits on #380).
- **`parked` → closed, not parked** (convention 2026-07-17): `parked` means "no concrete plans," and
  the decision was to **close** such issues rather than let them linger (reopen instantly if revived).
  So `parked` should normally not appear; if it does, treat it as excluded and surface it to the user.
- **Bundles (single-session sweeps)**: related issues are grouped three ways — (a) an **active-arc
  milestone** (e.g. "Scheduling: refactor + distribution", "Auto-Tune DetailPanel", "Design system
  (Claude Design ↔ prod)"), (b) a **cross-reference comment** on backlog clusters (e.g. "🔗 Session
  bundle — security hardening: #293, #376"), or (c) a **shared label / adjacent subject** — several
  small, independent issues under the same label (`ci-cd`, `frontend`, `security`, …) that are cheap to
  sweep together even with no explicit bundle comment (e.g. #553 + #512, two `ci-cd` tooling-hygiene
  fixes). **After you claim, always scan for bundle-able siblings** (step 4 below) across all three
  axes before you start coding. Active-arc milestones auto-sort above the flat priority backlog; leave
  individually-important backlog bugs unmilestoned so their `priority:` label still orders them.
- A **new gate re-appears only** when a future adversarial-review Blocker/High finding is slotted
  as a priority pickup, or a new OPEN milestone re-establishes an arc. Until then, backlog
  features (ordered by milestone/`priority:` label) are the normal, expected work.

---

## Orchestrator launch profile (operator-facing; do not paste)

As of 2026-07-14:

- **Codex:** use **GPT-5.6 Sol, `ultra`** for the orchestrator. `ultra` is the Codex
  multi-agent orchestration setting; if it is unavailable, use **GPT-5.6 Sol, `max` effort**
  (`xhigh` only when you deliberately want to trade some capability for quota/latency).
- **Claude Code:** use **Opus 4.8, `xhigh` effort** (or **ultracode**, which is the Claude Code
  orchestration preset built on `xhigh`). Reserve **Fable 5, `high` effort** for the frontier
  escalations listed below; use `xhigh` there only when the decision remains genuinely difficult.
- **Queue preflight:** when the user has not named an issue, use the client's cheapest suitable
  fast/small model at **low** effort. Queue selection is tool-bearing, so do not use `minimal`:
  Codex validates the full enabled tool profile, and enabled `web_search` can reject `minimal` even
  when the selector intends to call only Gitea MCP. Route it as a subagent when possible; otherwise
  run a separate low-cost selector session and pass its compact selection packet to the orchestrator.
  Never spend the orchestrator tier on queue ranking.
- If those names are unavailable, choose the client's strongest long-horizon coding/agentic model
  at its high extended-reasoning setting. Do not weaken the orchestrator to a fast/mini model; use
  smaller models for bounded delegated slices, with a separate low-cost queue preflight when the
  client cannot select a cheaper model per agent.

The kickoff below deliberately describes capability tiers instead of model names so it survives
model rollovers. The operator-facing recommendations above may be updated without changing the
queue protocol.

---

# STANDING KICKOFF PROMPT (paste into a fresh Codex or Claude Code session unchanged)

You are the ORCHESTRATOR in the main **Codex or Claude Code** session. First identify your client,
active model, effort/reasoning level, available delegation mechanisms, and concurrency limit.
Orchestration means: decompose, delegate independent slices, integrate their results, verify the
whole, and keep canonical issue state accurate. Use the client's native agent/subagent tools; never
assume a named tool, command, plugin, model-routing feature, or fork mechanism exists.

Route by capability when the client supports per-agent model selection, and **say which tier you chose
in the dispatch itself** — see the `process.per-agent-model-routing` HARD CONSTRAINT below for the
table. Where the client cannot route per agent, use the active model for every slice except the
mandatory queue preflight below. Independent review MUST start from a cold, review-only brief; prefer
a different model family/client when one is available, otherwise use a fresh agent with no
implementation role.

**Low-cost routing applies throughout the session, not only to queue selection.** Before any batch of
bounded searches, inventories, log triage, URL/status sampling, or report drafting, dispatch the cheapest
suitable fast/small model. **If any tool is enabled or required, start at `low`; this includes enabled
`web_search` even when the prompt names only Gitea MCP. Reserve `minimal` for explicitly tool-free synthesis
over already-supplied bounded evidence.** Never retry a known tool-bearing profile at `minimal`. Strictly
cap its allowed files, queries, log window, result/evidence count, and output; instruct it to stop and return
partial evidence at a cap instead of broadening scope. The orchestrator receives a compact evidence packet
and performs only the minimum focused live recheck. Same-tier agents add parallelism, not cost savings,
while direct tool-only mechanical checks may stay inline. If a tool-bearing launch is rejected at `minimal`,
retry once with the same model and unchanged scope at `low`. If a cheap worker lacks a required tool, give it
already-collected bounded evidence for explicitly tool-free synthesis or report the limitation instead of
silently expanding orchestrator reconnaissance. Global Codex hook enforcement is tracked in
`timothy/server-management#592`; the wider Claude-hook port is tracked in `timothy/server-management#593`.

## Two concurrent tracks at session start

A fresh session runs **two independent tracks** — neither blocks the other, and both are cheap/mechanical,
never orchestrator-tier work:

- **Track A — Orientation** (always, regardless of whether an issue is named): read `AGENTS.md` and
  `CLAUDE.md` when present, then `docs/README.md`'s task-signal map, and the active decisions catalog
  `docs/decisions/README.md`. This is what replaces re-deriving conventions from source or from a
  pinned tracker's prose — see "Knowledge retrieval" below for the full catalog-first / breadcrumb-rule
  contract.
- **Track B — Selection** (only when the user has NOT named an issue): run the deterministic selector,
  `ETV_GITEA_BASICAUTH=user:pass scripts/select-queue.sh [N]`. It does the mechanical parts a cheap
  model used to get wrong IN CODE: excludes `in-progress`/`parked`/PRs/bot-authored issues (Renovate's
  Dependency Dashboard #22, ersatztv#553), resolves
  `GET /issues/{n}/dependencies` on every candidate (dropping any with an OPEN blocker), tiers by
  LOCAL `.milestone.state`/`review`/`priority:` filters (never the server `?milestones=` name filter,
  which no-ops on `:`/`+`), and orders by (tier, priority, issue#). **Its DEPS/tiering/ordering are
  deterministic — trust them, do not re-derive them by hand** (the hand-derivation failure modes the
  script encodes are recorded at `ci.gitea-milestone-filter-noop`). It also raises two JUDGMENT flags it deliberately does
  not decide — `CLAIM?` (a claim can precede the label) and `UMBRELLA?` (an epic whose children are the
  real pickups) — resolve ONLY those by reading the flagged issue's comments/body. The script does not
  rank an arc tier (there is no active arc right now — maintenance/backlog mode, see above); if a new
  arc/milestone is ever established, add it as a script tier rather than reverting to prose-derived
  ranking. If the script is unavailable (no creds / Gitea down), it fails open (prints a notice, exits
  0) — in that case fall back to a single low-cost model session querying live tiers directly
  (OPEN-milestone → `review` → `priority: high/medium/low`), never to inline sorting by the
  orchestrator.
  **If the user names an issue, skip Track B entirely** and go straight to focused retrieval (below)
  plus a live claimability check on that issue; this explicit user choice is the sole path by which a
  `parked` issue may be worked.

Dispatch exactly one selector on the cheapest suitable model at `low` effort when the client can route
a cheaper subagent; otherwise run it in a separate low-cost session before starting or resuming the
orchestrator and pass in its packet. **Do not fall back to inline sorting or an equally expensive
selector.** If no cheaper route or session is available, pause and request the selector packet rather
than consuming orchestrator tokens on queue ranking.

FIRST read `AGENTS.md` and `CLAUDE.md` when present, then `docs/README.md` and the sections its
task-signal map points to for your task, plus the Lessons below. Apply both client instruction files; where they differ, follow the
stricter safety/completion requirement unless a higher-priority instruction resolves the conflict.

FRONTIER ESCALATION — these moments go to the strongest available reasoning model. In Claude Code,
prefer Fable; in Codex, prefer the strongest frontier model at extended-high reasoning. If you are
already running that profile, handle them inline. Otherwise delegate with a SELF-CONTAINED brief
(scope, exact question, evidence, file paths, and doc pointers), or ask the user to switch models when
the client cannot route the escalation. Do not block merely because one brand-specific model is absent:
- Architecture/API design decisions (e.g. #202's media-source API shape) and anything
  `[PLAN-MODE]`-tagged.
- Parity verdicts for blazor-route-parity.md (per-capability diff sign-off) and #91b
  removal go/no-go.
- Reconciling contradictory independent-review findings and the final
  pre-merge judgment on gate PRs.
- Stuck after two genuinely different approaches, or evidence contradicts the issue/docs.
Everything else (claiming, worktrees, dispatching implementers, CI monitoring, protocol
bookkeeping, routine merges of green reviewed PRs with user consent) stays at your level.

## Knowledge retrieval (MemPalace + catalog + Gitea — the #642 seam)

**MemPalace = candidate discovery only**, retrieved at a bounded `k`; every passage **verified
against its cited Markdown/Gitea source** before use. **Default wing = `ErsatzTV-Decisions`** (active
`docs/decisions.md` + `docs/decisions/*.md` + catalog). History wings `ErsatzTV-Decisions-Archive`
(`docs/decisions/archive/**`) and `Gitea-ErsatzTV` (issues/comments incl. closing records) are touched
**only** when the question is explicitly "what did the rule *used to be*." "What is the current rule
for X" never touches the history wings.

The four load-bearing orientation bullets — carry these **VERBATIM** wherever this contract is
referenced (`docs/README.md` too):

1. **Current conventions/decisions → catalog-first.** Start at `docs/decisions/README.md`; discover
   via the `ErsatzTV-Decisions` wing (active) / `ErsatzTV-Decisions-Archive` (superseded/retired).
   **Resolve by topic/key, never by chasing a file path.**
2. **Issue history → evidence, not authority.** The `Gitea-ErsatzTV` wing is historical narrative
   that may be stale; it never overrides current Markdown.
3. **The breadcrumb rule (the crux behavior change).** A file path named inside a *historical issue
   comment* (e.g. "grep `docs/decisions.md` 2026-07-17", "see …") is a **breadcrumb, not a live
   pointer.** Find the current rule via the catalog / active wing **by concept**; do not treat the
   named path as current. (Why it's safe: still-current → in the active wing, breadcrumb resolves;
   superseded → the active wing returns the *successor* and a literal follow lands on a record that
   announces its own `status: superseded`; retired → the active wing returns nothing, which is itself
   the signal. The validator-enforced move-to-`archive/` is what prevents the catastrophic "superseded
   rule read as current" case.)
4. **Fallback when MemPalace is stale/down:** `docs/decisions/README.md` catalog, then
   `` rg '^`key: <dotted.key>`' docs/decisions/ ``. MemPalace is never authority nor sole fallback.

**Graceful degradation (calibration, not a correctness cliff):** even an under-oriented agent that
literally greps `docs/decisions.md` post-#521 gets valid-but-*incomplete* results (misses topic files
+ archived records), never *wrong* ones — that file holds only active records. So this is a
completeness/latency risk during rollout, not a correctness cliff.

**Altitude / precedence:** `docs/decisions.md` (+ topic files) is **normative/current** ("the rule is
X") — the `ErsatzTV-Decisions` wing, authority for "what is the current rule." Gitea issues are
**evidentiary/historical** ("we worked X on date D; spec, discussion, outcome") — the `Gitea-ErsatzTV`
wing, authority for "how was this handled / what was the context." A decision record points to its
issue; the issue carries the provenance the record compresses. Same fact, two altitudes — canonical
Markdown wins for "current."

**Issues in MemPalace are for DISCOVERY, not queue state.** Encounter a problem → search MemPalace →
find relevant docs AND relevant open issue(s): this **avoids filing duplicates** and **surfaces
bundles** (related issues to pick up together). But a MemPalace issue hit is a *candidate*:
open/closed/claimed/blocked/priority flip constantly and MemPalace lags (seconds–1h, up to a week if
the webhook is down), so a hit may be a since-closed issue shown open, or miss a just-filed one.
**Always re-confirm live state in Gitea (`scripts/select-queue.sh`) before acting** — never treat a
MemPalace issue hit as current queue state.

**What to mine per issue:** open issue → the BODY (dedup + bundle-clustering; no closing record yet).
Closed issue → body + the `## Closing record` (how it was resolved). Ephemeral comments (claim /
progress / `Review-verdict:`) → skip or de-weight both ways — process exhaust, not the knowledge
store. The `## Closing record` is the one structured per-issue summary meant for retrieval + export.

**Staleness bounds** (so agents know when to distrust a hit): webhook re-mine in seconds; hourly
reconcile; weekly full sweep catches file moves/deletes. Worst case before a supersession takes effect
in retrieval: ~1h, or ~1 week if the webhook is down and only the move happened. When in doubt, fall
back to exact search (bullet 4 above).

**Never** derive live queue state from MemPalace, #237, or historical comments.

Then work the queue:
1. Run the two tracks above (orientation always; selection unless an issue is named).
2. If Track B ran: apply its ranked shortlist. The orchestrator makes one focused live recheck of the
   winner (still OPEN, unclaimed, not blocked) before claiming; if it changed, check the next fallback.
   **An empty backlog is not a stopping condition** — if the selector returns any eligible candidate,
   claim its top-ranked winner; do not ask the user to choose merely because candidates belong to
   different workstreams. Never invent a fix-size, recency, or perceived-relevance tiebreaker.
3. **Claim it**: add the `in-progress` label + a "claiming" comment on the issue(s);
   reviewer-repo audits are claimed by comment only. Treat that claim as live until a later comment
   explicitly releases or abandons it, and exclude audits with a posted deliverable even while the
   issue remains open for implementer replies.
4. **Scan for bundle-able siblings** (always, right after claiming — not optional). Check all three
   bundle axes from "Bundles" above: the claimed issue's **milestone**, its **cross-references /
   backlinks**, and its **shared label(s)** (list the other open issues under each of its labels).
   Surface any small, independent siblings to the user with a size/fit read and a recommendation, and
   fold the agreed ones into this session (parallel worktrees for disjoint slices). This is how #22's
   sibling-cluster fixes (#553 + #512) got swept together; skipping it silently narrows a session that
   could have closed two or three issues for barely more cost.
5. Read the issue bodies (they carry the task context/evidence) and work the item(s) under the
   HARD CONSTRAINTS below.
6. Finish the session: run the **H12 qualification audit**
   (`ETV_GITEA_BASICAUTH=user:pass scripts/issue-qualification-audit.sh`) and add a `priority:`
   label to anything it lists (every issue you filed this session included); post a `## Closing
   record` (template below) on each issue you closed or substantially progressed; remove your
   `in-progress` labels; and complete the per-issue Task Completion Protocol from the applicable
   `AGENTS.md` / `CLAUDE.md` instructions (including the `done` workflow when required).
7. **Leave the shared checkout fresh** (H13): run `scripts/refresh-shared-checkout.sh`. It
   fast-forwards `/Users/timothy/ersatztv` to `origin/main` and refreshes `web/node_modules` if the
   lockfile moved; it refuses and changes nothing unless that tree is on a clean, non-ahead `main`.
   A NO-OP is a normal result — report it and carry on.

## Closing record (session-end / issue-close artifact)

Post this as the structured closing comment on any issue you close (or substantially progress) — it
is both the human-readable summary and the per-issue unit MemPalace mines for retrieval:

```markdown
## Closing record
**Outcome:** <what shipped / what didn't; PR link>
**Root cause:** <for bug fixes/incidents — why the problem existed, or "unknown, see follow-up #N">
**Decisions/conventions changed:** <keys added/superseded in docs/decisions.md, or "none">
**Reusable knowledge:** <a fact/gotcha worth surfacing to a future session or MemPalace search>
**Verification:** <tests run, live-E2E, CI status>
**Deferred:** <anything explicitly punted, with a follow-up issue link, or "none">
**Docs updated:** <which docs/*.md files changed in this PR, or "none required and why">
```

HARD CONSTRAINTS:
- **Work in a worktree off `origin/main` — always**, including a one-line docs commit. Never commit in
  `/Users/timothy/ersatztv` and never read its git state to learn about `main`. → `process.shared-tree-readonly`
- **One committing agent per worktree.** Give each parallel slice its own; read-only review agents may
  share. → `process.one-worktree-one-committing-agent`
- **Never commit or merge inside a worktree you did not create** — land on a sibling-owned branch with a
  plumbing merge. → `process.foreign-worktree-plumbing-merge`
- **Parallelize by default** on genuinely disjoint slices (normally 2–3 delegated agents). Cap builds at
  3–4 and gate on FREE RAM, not CPU. **Never set `ETV_UPDATE_GOLDENS`.** → `process.build-concurrency-limits`
- **Name the model + effort for every dispatched agent, in the dispatch itself** — bounded recon → cheapest
  fast tier at `low`; mechanical slice against a documented contract → mid tier; judgment-heavy (design,
  compiler/parser, security, migrations, review arbitration) → orchestrator tier; independent review →
  a different model family than the implementer. Omitting it silently inherits the orchestrator tier, so
  state the choice out loud. → `process.per-agent-model-routing`
- **Local gate + cold-context review BEFORE the push**, never after. → `process.local-gate-before-push`
- **Independent review is mandatory** for locks/concurrency, auth/security, API write-path handlers, DB
  migrations, or >~150 changed C# lines; a skip must be stated with its reason. → `process.independent-review-rubric`
- **Batch your pushes — you cannot cancel a CI run.** Only the operator can cancel, in the browser.
  → `ci.batch-pushes-no-cancel-route`
- **Do not gate your push on host health** — trust the build queue. → `ci.no-host-health-gating`
- **Arm the CI monitor at PR-open**, on the head sha. → `ci.monitor-armed-at-pr-open`
- **Before any push touching `.cs`, BOM-check the touched set** and run the format gate under `bash -c`.
  → `process.bom-format-detection-recipe`
- **Keep a PR branch current by REBASING on `origin/main`** — never merge main in; regenerate (never
  hand-resolve) generated artifacts after a conflicted rebase. → `release.format-as-you-touch-rebase`
- **Live-E2E via `scripts/e2e-local.sh` before the push** for write-path/UI changes; curl download
  endpoints, never browser tabs; use a fresh config dir.
  → `testing.live-e2e-prepush-timing`, `testing.e2e-local-fresh-config-dir`
- **Merge consent is DERIVED, not asked.** The H6/H10 gate auto-grants when the `## Done-when` boxes are
  ticked, a fresh `Review-verdict` references the current head, and CI is green; confirm in conversation
  only when the gate ASKS. Prior pre-approvals never carry over. → `release.merge-consent-autogrant`
- **After the push, fixes are follow-up commits** — never amend or force-push a pushed branch.
  → `process.pr-routine-sequence`

---

# Lessons / workflow lore (STANDING workflow/orchestration rules only)

> **Scope**: *how we work* — orchestration, CI triage, review routing, cross-session hygiene. Each rule
> below is one or two lines plus its decision `key:`; the evidence, the incident that produced it and the
> full rationale live in that record (`docs/decisions/workflow-process.md` for most of them). **Keep it
> that way** — this file is pasted into every session's kickoff, so a paragraph of narrative here is a tax
> paid by every future session, while a record is retrieved only when it is needed (#542).
>
> Editing: prune covered and stale bullets rather than appending — this is not append-only and git keeps
> the history. If you add a rule, write the record first and cite it here.

**Orientation**
- Read `docs/README.md` → the convention docs first; point agents at named sections, recon only the
  task-specific delta. → `docs.convention-docs-session-start`
- Run `scripts/select-queue.sh` for queue selection; trust its deps/tiering/ordering and resolve only its
  `CLAIM?`/`UMBRELLA?` flags. → `startup.parallel-orientation`
- Claim with `in-progress` before working — but a claim prevents duplicate *pickup*, not overlapping code
  changes. → `process.parallel-session-claim`

**Building and reviewing**
- The PR routine is a fixed sequence; for API changes build the app project FIRST, then
  `./scripts/update-openapi.sh`, then `npm run generate:api`. → `process.pr-routine-sequence`
- Re-review the FIX COMMIT, not just the initial diff; loop until a clean verdict or an explicit
  acceptable-defer, and file a tracked issue for anything deferred. → `release.review-verdict-gate`
- When independent reviews disagree, escalate to the frontier judge — and escalate the proposed FIX with
  the finding. → `process.review-disagreement-frontier-judge`
- "Make X consistent with Y" findings are new code; for any timer/effect ask when it fires, including on
  mount. → `process.consistency-fix-new-code-scrutiny`
- Before trusting a "single owner / no cross-release" claim, grep the whole host project for every
  producer — the background scheduler is the usual miss. → `process.lock-ownership-enumerate-producers`
- Before deleting a "workaround", enumerate every behavior it provided.
  → `process.enumerate-workaround-behaviors-before-deleting`
- Concurrency tests need a stress loop and a negative control that inverts the real condition — never
  `if (true)`. → `ffmpeg.work-ahead-slot-atomic`
- Enumerate lazy LanguageExt returns in tests; live-E2E is the only net for that class.
  → `release.live-e2e-required`
- Harden a soon-to-grow feature with a runtime posture, not a hardcoded clamp.
  → `process.harden-with-runtime-posture-not-clamp`

**Reading a red CI run** (check these before diagnosing your diff)
- A **killed** job reports `conclusion: failure` — read the log tail for the `❌ Failure - Main` marker;
  log timestamps are UTC, the host is UTC+2. → `ci.killed-job-triage`
- **`cancelled` is not a verdict** — report FAILED and CANCELLED separately. → `ci.cancelled-is-not-a-verdict`
- A failure inside a **setup/cache step**, before your code compiles, is environmental.
  → `ci.infra-shaped-red-under-load`
- A lone **`decisions lifecycle`** red is a known flake: do **nothing**, the operator reruns it. A
  convincing local explanation is always available — check this rule before theorising.
  → `ci.decisions-lifecycle-flake`
- Heavy-render web tests need explicit per-test vitest timeouts. → `ci.web-test-per-test-timeouts`
- Verify locally and trust it; CI confirms (~9 min warm, seconds for docs-only).
  → `ci.verify-locally-ci-confirms`

**Tooling gotchas**
- E2E cleanup kills only PIDs it started — never `pkill -f "dotnet ErsatzTV.dll"`; a foreign listener
  is reported, not reaped. Carry this into every delegated E2E brief.
  → `testing.e2e-cleanup-scope-by-pid`
- Playwright-MCP: curl download endpoints, never open a tab; on repeated stalls prefer
  `pgrep -f ms-playwright-mcp` and kill only your own PID (that record still says `pkill -f`; the same
  shared-host reap is being reconciled in #596). → `testing.playwright-mcp-download-and-recovery`
- Channel branding is NOT testable through the troubleshooting-playback API.
  → `testing.troubleshoot-path-cannot-test-branding`
- Codex cheap workers launch via `codex exec`, not `spawn_agent`. → `process.codex-cheap-worker-launch`
- A dropped subagent connection is transient — resume via SendMessage, don't restart.
  → `process.subagent-drop-resume`
- Fixing onto an unmerged feature branch: branch off it, FF-push, and beware the sibling-worktree path
  leak. → `process.branch-off-feature-branch`

**Session end**
- Run the H12 audit and label everything it flags. → `process.issue-qualification-audit`
- Run `scripts/refresh-shared-checkout.sh` so the next session's checkout is current.
  → `session.shared-checkout-refresh`
