Mode-Selector Skill
Status
Heuristic v1 active (issue #291, shipped 2026-04-25). Wired into session-start Phase 7.5
(issue #292, shipped 2026-04-25). Backlog signal source live (issue #293, shipped 2026-04-25):
signals.backlog is populated by scripts/lib/backlog-scan.mjs::scanBacklog. Accuracy feedback
loop live (issue #294, shipped 2026-04-25): scripts/lib/mode-selector-accuracy.mjs::recordAccuracy
writes a mode-selector-accuracy learning after the user confirms/overrides the Phase 7.5 banner.
Phase B contract is closed. Phase C (#277) /autopilot Loop Command is the next epic and
owns its own PRD.
Purpose
Mode-Selector centralizes the session-mode decision across all consumers: session-start Phase 1.5
banner, /autopilot (Phase C), and any future caller that needs a structured recommendation rather
than ad-hoc heuristics inline at the call site. Before this skill existed, mode-picking logic was
either implicit (user-typed free text) or embedded directly in session-start with no reuse path.
Phase A (state-md.mjs::parseRecommendations, issue #272) established the recommended-mode
frontmatter field written by session-end Phase 3.7a. Phase B is the skill that reads that field
(plus future signals) and returns a structured recommendation. The key output is a four-field
tuple: {mode, rationale, confidence, alternatives}. mode is the recommended session type.
rationale is a ≤120-char human-readable explanation. confidence is a float (0.0–1.0)
indicating how strongly the selector commits to the recommendation. alternatives is an ordered
list of {mode, confidence} objects representing the next-best choices, enabling callers to offer
override options without re-running the selector.
The selector is a pure function: given the same signals object it always returns the same output.
No file I/O, no network calls, no global state. This makes it trivially testable and safe to call
from any skill without side-effect risk.
Contract
Input: signals object
recommendedMode(string|null) — Phase A frontmatter field; therecommended-modekey fromparseRecommendations()topPriorities(number[]|null) — issue numbers from thetop-prioritiesfrontmatter fieldcarryoverRatio(number|null) — float 0.0–1.0 from Phase A; fraction of issues carried over from previous sessioncompletionRate(number|null) — float 0.0–1.0 from Phase A; ratio of planned issues completedpreviousRationale(string|null) — therationalestring written by session-end Phase 3.7alearnings(object[]|null) — RESERVED; not consumed in scaffold; Phase B-1 heuristic inputrecentSessions(object[]|null) — RESERVED; not consumed in scaffold; recent-sessions trend inputbacklog(object|null) —{criticalCount, highCount, staleCount, byLabel, total, vcs, limit}fromscripts/lib/backlog-scan.mjs::scanBacklog(Phase B-3, #293).nullwhen CLI missing or no git origin — contributes 0 delta.bootstrapLock(object|null) — RESERVED; not consumed in scaffold; tier-aware sizing hints
Output: Recommendation object
| Field | Type | Range / Values | Purpose |
|---|---|---|---|
mode | string enum | housekeeping | feature | deep | discovery | evolve | plan-retro | Recommended session type |
rationale | string | ≤120 chars | Human-readable explanation for the recommendation |
confidence | float | 0.0–1.0 | Selector commitment; see Fallback Behavior for threshold semantics |
alternatives | {mode, confidence}[] | 0–3 entries; may be empty, never null | Next-best modes with partial confidence scores |
Invocation Points
Current
skills/session-start/SKILL.mdPhase 7.5 — first wired invocation (issue #292). Renders📊 Mode-Selector suggests:whenconfidence < 0.5(informational, no pre-selection) or📊 Mode-Selector recommends:whenconfidence >= 0.5(pre-selects AUQ option 1). Eight graceful no-op conditions documented inline. Note: Phase 1.5📋banner is NOT a Mode-Selector invocation — it reads Phase A STATE.md frontmatter directly viaparseRecommendations; the Mode-Selector lives at Phase 7.5.tests/lib/mode-selector.test.mjs— 75 tests (7 describe blocks) exercising SPIRAL, CARRYOVER, high-confidence path, conflicting-signals, stale-signals, alternatives generation, and defensive parsing.mode-selector.mjscoverage 100%/100%/100%/100%. Issue #291.
Future
/autopilot(Phase C, #277) — auto-execute whenconfidence >= 0.85AND SPIRAL/FAILED/carryover-50% kill-switches pass. No user prompt in that path.
Companion modules (Phase B closure)
scripts/lib/backlog-scan.mjs::scanBacklog— feedssignals.backlog. Phase B-3 (#293). Module-level cache, glab/gh auto-detection, returnsnullon graceful-degradation paths.scripts/lib/mode-selector-accuracy.mjs::recordAccuracy— post-AUQ feedback writer. Phase B-4 (#294). Subject pattern<recommended>-selected-vs-<chosen>; agreement and override land at distinct subjects so the existing learning lifecycle can confirm/contradict them independently.
Scaffold Heuristic (v0)
The v0 scaffold implements a minimal three-branch passthrough. It is intentionally thin so the contract is exercisable by tests before the full Phase B-1 rule-set lands.
selectMode(signals):
if signals is null/undefined:
→ {mode: 'feature', rationale: 'scaffold: null signals → default', confidence: 0.0, alternatives: []}
if signals.recommendedMode is valid mode:
→ {mode: <recommendedMode>, rationale: 'scaffold: passthrough of Phase A recommended-mode', confidence: 0.5, alternatives: []}
otherwise:
→ {mode: 'feature', rationale: 'scaffold: missing/invalid recommendedMode → default', confidence: 0.0, alternatives: []}
Note: the full Phase B heuristic — rule-set consuming learnings.jsonl, recent sessions trend, VCS backlog priority-weighting, and bootstrap.lock tier — is the Phase B-1 follow-up sub-issue.
Fallback Behavior
confidence = 0.0means the selector is declining to choose; caller should fall back to its own logic (v0 heuristic) or prompt the user without pre-selecting any option.0.0 < confidence < 0.5means low-confidence; caller should present as a suggestion, never auto-execute; AUQ should show the recommended mode without marking it as "Recommended".confidence ≥ 0.5means accept as default; present as the pre-selected AUQ option; user can still override.confidence ≥ 0.85(future Phase C threshold) means suitable for autonomous execution in/autopilotmode without user prompt, subject to kill-switch guards.
Integration with Other Skills
state-md.mjs::parseRecommendations→ read Phase A frontmatter fields; consumed viasignals.recommendedMode,signals.carryoverRatio,signals.completionRate, etc.recommendations-v0.mjs::isValidMode→ mode enum validation; import and use — do not redefine the six-value enum inline.learnings.mjs::readLearnings→ reserved for Phase B-1 heuristic input viasignals.learningssession-schema.mjs::normalizeSession→ reserved for recent-sessions trend input viasignals.recentSessionsbootstrap-lock-freshness.mjs::parseBootstrapLock→ reserved for tier-aware sizing hints viasignals.bootstrapLockgitlab-ops.md→ reserved for VCS backlog scan (Phase B-3) viasignals.backlog
Critical Rules
- Pure function only. No I/O, no side effects, no throws, no dynamic imports.
selectModemust be synchronous and referentially transparent. - Never write STATE.md. session-start or Phase C writes any derived state; the selector is
read-only. session-end Phase 3.7a is the sole writer of
recommended-mode. - Every return path returns all 4 keys. The
{mode, rationale, confidence, alternatives}shape is enforced by tests; missing keys are a contract violation. alternativesis always an array. N