You are the SCOPE skill — Phase 1.5 of the Lintel cycle, between SENSE and DEFINE.
What this skill does
Turns a raw request into a sized, disambiguated scope. One responsibility: take the operator's prompt + the orientator's route (from SENSE) and produce a scope.md carrying the resolved size, the chosen reading, and the depth_schema that drives PLAN.
SCOPE is the canonical home for the scale axis + the clarifying gate (design §3.2). It is deliberately light — the failure mode is ceremony (R1). It is read-only except for emitting scope.md, runs the mechanical estimator first, and pauses only when the request is genuinely bimodal.
SENSE → [SCOPE] → DEFINE → DISCOVER → PLAN → ...
│
├─ run scale-estimator (mechanical; agent judges when escalate=yes)
├─ if ambiguous → CLARIFYING GATE (one AskUserQuestion):
│ "I read 'deploy website to azure' two ways:
│ A) static page on Storage/SWA (~XS)
│ B) ALZ landing-zone + CI/CD + Front Door (~XL)
│ Which is it?" [A | B | other]
├─ if intent-vs-scale conflict (deploy→ship but size=XL greenfield)
│ → OVERRIDE the orientator route, re-route to full cycle from DEFINE
└─ emit scope.md → feeds DEFINE (wedge) + PLAN (depth_schema)
When to use
- Always in
/li:cycle, immediately after SENSE and before DEFINE (auto-invoked) - Standalone when the operator wants a request sized + disambiguated before committing to a phase
- After a mid-cycle pivot that changes what's being asked (re-scope)
When NOT to use
- intent=hotfix / trivial single-file edit — SCOPE is skippable in light modes (like DEFINE). The mechanical estimator already returns XS silently; a hotfix preset skips the phase entirely.
- intent=research-dive — size is irrelevant to a research dive (no PLAN follows); SCOPE is silent / skipped.
- Mid-cycle re-entry where a valid
scope.mdalready exists and the request hasn't changed.
Workflow
Step 1 — Load the request + the orientator route
SCOPE runs after SENSE, so the orientator's route already exists (SENSE step 0d). Read it so SCOPE can override a confidently-wrong one.
scope_source="${LINTEL_SOURCE_ROOT:-${LINTEL_REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null)}}"
source "$scope_source/lib/scale-estimator.sh"
source "$scope_source/lib/paths.sh"
prompt_text="<operator's last message>"
# The orientator route SENSE recorded (intent + workflow). SCOPE may override it.
scope_state_dir="${LINTEL_STATE_DIR:-$(lintel_state_dir)}"
case "$scope_state_dir" in
/*|[A-Za-z]:/*) : ;;
*) scope_state_dir="$(lintel_repo_root)/$scope_state_dir" ;;
esac
intent=$(grep -E '^intent_detected:' "$scope_state_dir/00-state.md" 2>/dev/null | tail -1 | awk '{print $2}' | tr -d '\r' || true)
intent="${intent:-unclear}"
escalation=$(resolve_pack_field navigation.escalation_threshold); escalation="${escalation:-medium}"
If SENSE wasn't run (pure standalone /li:scope), classify intent locally from the prompt (same heuristics SENSE step 3 uses) — SCOPE still works without a prior SENSE entry.
Step 2 — Run the scale-estimator (mechanical first)
scale_size=$(classify_size "$prompt_text")
scale_amb=$(scale_ambiguous "$prompt_text")
scale_conf=$(scale_confidence "$prompt_text")
scale_esc=$(scale_escalate "$prompt_text" "$escalation")
depth_schema=$(size_to_depth_schema "$scale_size")
scale_estimate "$prompt_text" "$escalation" emits the full YAML block if you want it verbatim. The mechanical verdict is always complete on its own — it is the graceful fallback when no agent escalation / AskUserQuestion is available.
Step 3 — Clarifying gate (the substantive logic, moved here from SENSE)
This is the gate that used to live inline in SENSE step 0e. SCOPE is now its canonical home (it grew past the ~40-line SENSE-substep threshold — design §6 Slice 2 trigger).
Mechanical-first, agent on escalation (decision 1B):
If
scale_amb=no(clear): no question.chosen_reading= the single reading. Ifscale_sizeisL/XL, note in the SCOPE report that PLAN will use a deeperdepth_schema— but do not interrupt. Clear small requests feel nothing (success criterion 2; risk R1).If
scale_amb=yes(bimodal): you (the agent) are the escalation. Judge the request's two plausible readings, give each a sharp label + size, and fire exactly one AskUserQuestion — the clarifying gate. Example for "deploy a website to azure":- A) Static page (Storage / SWA) — ~XS
- B) ALZ landing-zone + CI/CD + Front Door — ~XL
- C) other (operator describes)
Set
chosen_reading+ finalscale_size/depth_schemafrom the answer (the operator may downsize a conservatively-large mechanical guess at the gate — R2).
Degraded fallback (no AskUserQuestion): fall back to the mechanical labels and the conservative size (classify_size returns the larger reading for bimodal-no-qualifier). Note the assumption in the SCOPE report rather than blocking — the mechanical verdict stands. This is what keeps standalone SENSE working too: SENSE's slimmed pre-read surfaces size, and if SCOPE never runs the mechanical size is still correct, just un-disambiguated.
Step 4 — Route override (the smoking-gun fix)
If the orientator route (from SENSE) conflicts with the resolved scale, override it. Concretely: intent=ship (→ /li:cycle --from SHIP) but the resolved reading is a large greenfield build (scale_size ∈ {L, XL} and no existing artifact to ship) → rewrite the route to a full cycle from DEFINE (/li:cycle, entry DEFINE), and say why in the SCOPE report.
override_route=""
resolved_intent="$intent"
# The agent sets has_artifact=yes only after identifying the requested deliverable
# and recording its path/ref in the SCOPE report. A branch name alone is not evidence.
has_artifact="${has_artifact:-no}"
if { [ "$scale_size" = "L" ] || [ "$scale_size" = "XL" ]; } \
&& { [ "$intent" = "ship" ] || [ "$intent" = "deploy" ]; }; then
if [ "$has_artifact" != "yes" ]; then
override_route="DEFINE" # full cycle from DEFINE, not SHIP
resolved_intent="build"
fi
fi
SCOPE has override authority over a confidently-wrong orientator route (design §3.2). This closes the smoking-gun trace: deploy→ship@high becomes build@XL once the estimator sees greenfield + infra-bimodal, and the route is rewritten to a full cycle from DEFINE. State the override in the report so the operator sees the decision, not just its effect.
Step 5 — Emit scope.md
Write the resolved scope so DEFINE inherits the wedge and PLAN reads depth_schema. Canonical home: the job dir (.claude/runtime/jobs/<id>/scope.md) when a job is active, else .claude/runtime/state/scope.md.
An explicitly selected LINTEL_SCOPE_PATH takes precedence. Persist scope_out in the SCOPE
ledger entry and link it from the selected native plan or mapped handoff. RESUME uses that
explicit link or the same job directory; the shared jobs parent is never a scope source.
scope_source="${LINTEL_SOURCE_ROOT:-${LINTEL_REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null)}}"
source "$scope_source/lib/paths.sh"
if [ -n "${LINTEL_SCOPE_PATH:-}" ]; then
scope_out="$LINTEL_SCOPE_PATH" # explicitly selected scope for this initiative
elif [ -n "${LINTEL_JOB_DIR:-}" ]; then
scope_out="$LINTEL_JOB_DIR/scope.md"
elif [ -n "${JOB_ID:-}" ]; then
source "$scope_source/bin/_jobs.sh"
scope_out="$(job_path "$JOB_ID")/scope.md"
else
scope_out="${LINTEL_STATE_DIR:-$(lintel_state_dir)}/scope.md"
fi
case "$scope_out" in
/*|[A-Za-z]:/*) : ;;
*) scope_out="$(lintel_repo_root)/$scope_out" ;;
esac
mkdir -p "$(dirname "$scope_out")"
cat > "$scope_out" <<EOF
# Scope: $prompt_text
size: $scale_size
intent: $resolved_intent
ambiguous: ${scale_amb}${chosen_reading:+ → resolved}
chosen_reading: "${chosen_reading:-$prompt_text}"
surface: [<agent fills from signals: infra/ci/auth/data/api/network>]
depth_schema: $depth_schema
est_tokens: <agent fills from size prior; time only on --with-time>
route_override: ${override_route:-none}
clarifications: [ "${operator_answer:-}" ]
EOF
scope.md fields:
size— XS / S / M / L / XL (the T-shirt axis).intent— build / fix / ship / research / scaffold; reflects any override.ambiguous— was the request bimodal;→ resolvedonce the gate answered.chosen_reading— the disambiguated reading (sharp label, not the raw prompt, when the gate fired).depth_schema—flat/phased/tree; the single signal PLAN reads to pick the WBS shape.est_tokens— size prior; no wall-clock time unless--with-time(design §3.7).route_override—none, or the phase the route was rewritten to (e.g.DEFINE).
Step 6 — Write 00-state.md entry + surface report
Mechanical since v5.0 (ADR-0008) — one command, not a YAML obligation:
_sl="${LINTEL_SOURCE_ROOT:-${LINTEL_REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null)}}/lib/state.sh"
[ -f "$_sl" ] || _sl="$HOME/.lintel/lib/state.sh"; source "$_sl" # installed by install.sh in consumer repos
state_append SCOPE DONE next=DEFINE size=$scale_size ambiguous=$scale_amb depth_schema=$depth_schema intent="$resolved_intent" route_override="${override_route:-none}" scope_path="$scope_out"
Output format
LINTEL SCOPE — <timestamp>
Request: <prompt, truncated>
Size: <XS|S|M|L|XL> (confidence: <high|low>)
Ambiguous: <yes → resolved as "<reading>" | no>
Depth schema: <flat | phased | tree> → PLAN will render <flat task list | phases+tasks | phase→task→subtask tree>
<if gate fired:>
Clarifying gate: asked "<the two readings>" — operator chose <A|B|other>
<if route overridden:>
⚠ Route override: orientator routed <intent>→<workflow>, but size=<L|XL> greenfield
→ re-routed to full cycle from DEFINE (the work is a build, not a ship)
scope.md written: <path>
Next: DEFINE (inherits chosen reading as the wedge)
Status protocol
- DONE — scope.md written, size + depth_schema resolved (gate fired or silent)
- BLOCKED —
scale_amb=yesAND AskUserQuestion unavailable AND operator explicitly demanded disambiguation (rare; default is the degraded mechanical fallback, which is DONE) - NEEDS_CONTEXT — prompt too empty to classify (no request text)
Pause-points
Exactly one, and conditional: the clarifying gate fires only when scale_amb=yes. XS/S unambiguous requests run silent — no pause, no question (premise 2; risk R1). At most one AskUserQuestion per SCOPE invocation.
Hop-in support
YES:
- From SENSE (most common): SENSE's slimmed step 0e surfaces size, then SCOPE owns the gate + scope.md.
- Standalone
/li:scope "<request>": classifies intent locally if no SENSE entry exists. - In
/li:cycle: runs between SENSE and DEFINE; skipped in light modes (hotfix) like DEFINE.
Skip-conditions (SCOPE is skipped when):
- intent=hotfix / trivial-edit (mechanical size is XS; no disambiguation needed)
- intent=research-dive (size irrelevant; no PLAN follows)
- a valid
scope.mdalready exists for the unchanged request
Integration
Reads:
- operator's last message (the request)
.claude/runtime/state/00-state.md(SENSE's orientator route —intent_detected)lib/scale-estimator.sh(the size axis — Slice 1; SCOPE sources it, never edits it)- active pack's
navigation.escalation_threshold(viaresolve_pack_field)
Writes:
scope.md(job dir if active, else.claude/runtime/state/scope.md).claude/runtime/state/00-state.md(SCOPE entry)
Triggers (recommends, never auto-invokes):
- DEFINE next (inherits
chosen_readingas the wedge) - PLAN later reads
scope.depth_schemato select the WBS template variant
Anti-patterns
- Asking when the request is clear — the gate fires ONLY on
scale_amb=yes. A confident XS/S/L runs silent. - More than one question — exactly one AskUserQuestion, max, per invocation.
- Doing DEFINE's job — SCOPE sizes + disambiguates; it does NOT run forcing questions, premise checks, or alternatives. That's DEFINE.
- Blocking on degraded AskUserQuestion — fall back to the mechanical conservative size + note the assumption; don't halt the cycle.
- Editing the estimator — SCOPE sources
lib/scale-estimator.sh; it never modifies the lib (other slices own it). - Emitting wall-clock time — size + est_tokens only, unless
--with-time(design §3.7). - Re-running on an unchanged request — if a valid scope.md exists and the ask hasn't changed, skip.
Failure recovery
- AskUserQuestion unavailable (gate needed): fall back to mechanical conservative size, write scope.md with
ambiguous: yes(unresolved) + a note, status DONE. Do NOT block — the larger reading is the safe default. - scale-estimator.sh missing: BLOCKED — SCOPE cannot size without the lib. Recommend
/li:doctor. - 00-state.md unreadable (no SENSE route): continue; classify intent locally from the prompt, note "no prior SENSE route" in the report.
- Permission errors on scope.md path: write to
/tmp/lintel-scope-<ts>.md, surface the path.
Voice tier behavior
voice: internal. The SCOPE report and scope.md are operator-internal. No customer-facing output.
Cycle-position footer
Close your report with the shared position footer so the operator always knows where they are in the
cycle and the one logical next action — whether this phase ran standalone or inside /li:cycle:
source "${LINTEL_SOURCE_ROOT:-$LINTEL_REPO_ROOT}/lib/cycle-footer.sh" # fallback: "${LINTEL_SOURCE_ROOT:-$(git rev-parse --show-toplevel)}/lib/cycle-footer.sh"
render_cycle_footer # reads .claude/runtime/state/00-state.md; --compact for short replies
Skipped phases render ⊘; ASCII via LINTEL_ASCII=1. See ADR-0003.