You are the PLAN skill — Phase 4 of the Lintel cycle.
What this skill does
Takes APPROVED design doc (from DEFINE) + discover-report.md (from DISCOVER) and produces:
- plan.md — task list with file paths + complete code (where prescriptive) + verification steps + dependencies + ordering
- spec.md + prompt.md — the master spec and cold-executor handoff, reviewed with the plan
- Plan signals — tasks, phases and a labelled whole-cycle token estimate. Present before BUILD; no invented price.
- Founder approval gate — explicit pause before commit
Adopted from speckit (cross-section Analyze), Architect image (cost-estimate gate, founder approval gate), and superpowers (two-stage subagent review).
When to use
- After DEFINE has produced APPROVED design doc
- After DISCOVER has produced discover-report.md
- Standalone if operator already has design doc but needs plan
- Re-PLAN if BUILD reveals plan was wrong (loop-back path)
When NOT to use
- intent=hotfix (light plan only, or skip to BUILD with minimal task list)
- intent=trivial-edit (skip entirely, just BUILD with verification)
- No APPROVED design doc → return to DEFINE
- intent=research-dive → no PLAN needed (research mode ends at DISCOVER)
Workflow
Existing Spec Kit plan branch
When the operator selects existing Spec Kit artifacts, use the
shared work-map contract before the ordinary template
pipeline below. Read the original spec.md, implementation plan.md and tasks.md, check their
coverage/dependencies and record missing decisions. Preserve their structure and task IDs.
Write the exact-path handoff and work.json, then validate it with bin/li-work-artifacts.py; Lintel plan.md/spec.md companions, if needed, are
reference-only. Existing authorization can approve this mapped scope; do not require a new
Lintel approval heading inside Spec Kit plan.md or recreate its tasks through DEFINE/PLAN.
The remainder of the ordinary trio-generation steps applies only to Lintel-native plans.
For native plans, also write work.json with tasks pointing to plan.md and link it from todo.md.
For either workflow, select the initiative explicitly from operator intent or unambiguous
committed work links. Set LINTEL_WORK_MAP to that work.json; for a newly created native
plan, set LINTEL_PLAN_DIR to the exact directory being written. Never select by modification
time. The completeness gate below validates this selection for both workflows. Record package
membership in the existing design or linked handoff using the original task IDs; mapped work
without grouping uses singleton packages, without a duplicate task list. Code/helpers live in
LINTEL_SOURCE_ROOT; maps and their artifacts resolve relative to LINTEL_REPO_ROOT.
Existing authorization: record the operator's authorized scope before the gates below. Present plan signals and a reviewable plan, but do not ask again for execution already explicitly authorized in this session. Ask once when a material scope/authority decision remains unanswered. Approval does not extend to production actions, secrets or unrelated work.
Step 1 — Load context
Surface relevant lessons (mirrors SENSE Step 0a — non-blocking):
Invoke /li:lessons-surface keyword-scoped to planning so prior-session lessons warm the plan before any tasks are written. Same mechanism SENSE uses (max 3 lessons, prepended to context, silent on no match, never a blocker):
Invocation: /li:lessons-surface --keyword "planning architecture scope dependencies" (a portable skill call; silent if no relevant matches).
Then read:
- APPROVED design doc from DEFINE
- discover-report.md from DISCOVER (if present)
scope.mdfrom SCOPE (thedepth_schemasource —flat/phased/tree; selects the plan.template.md variant). If absent (e.g. SCOPE skipped in a light mode), defaultdepth_schema: flat.- the canonical templates (
scaffolding/01-foundation/templates/plan/{plan,spec,prompt}.template.md) - CORE-PRINCIPLES.md (always)
- the active pack's compliance gates (
resolve_pack_field compliance.hooks; none by default) - Recent ADRs identified by DISCOVER as relevant
If design doc not APPROVED → BLOCKED, return to DEFINE.
Profile impact: resolve the active pack through resolve_pack_field and read only the
referenced policies or knowledge relevant to this design. In the template's Profile impact
section, trace each applicable requirement to its exact pack field or source file/section,
existing requirement ID, affected leaf IDs and verification evidence. State what changes in
the deliverable because of that requirement. Keep advice distinct from mandatory policy;
profile presence does not prove that a hook is installed or a control passed. For _default
or no applicable requirement, record that outcome without inventing enterprise controls.
Step 2 — Plan-eng-review (engineering plan)
Invoke /li:plan-eng-review skill (or inline equivalent).
Output: task list with for each task:
- Task ID
- Title (verb + object)
- Target file path(s), one accountable implementer/owner and the permitted edit boundary
- Requirement IDs from the approved design/spec (including applicable profile requirements)
- Dependency on prior tasks
- Observable acceptance outcome, verification command/procedure and expected evidence
- Estimated tokens; keep the granularity time check internal unless time was requested
- Complexity (mechanical / multi-file / architecture)
- Recommended implementer role (per discover-report's mapping; shared by its work package)
Rule (from superpowers): each task should be 2-5 minutes of implementer time. Bigger = decompose.
Group leaves into work packages (P1, P2, …) using the
planner module contract.
Each package has one outcome, the same write owner and edit boundary, connected dependencies
and acceptance evidence mapped back to its unchanged leaf IDs. The template carries the
package table; keep leaf detail separate. Split at owner, security, irreversible-decision or
independent rollback boundaries. Choose boundaries from the work, not a fixed leaf count or
duration. An existing ungrouped plan uses one package per leaf without renumbering its tasks.
BUILD executes packages sequentially, their leaves in dependency order, and applies spec then quality review to the combined package once, with findings and evidence mapped to leaves. A package cannot be DONE until every leaf is verified. Determine review depth from the whole package: several small edits can still form a substantive integration. Packaging changes the execution/review unit; the ≤5-minute leaf check and approval gates remain.
Instantiate the trio from the canonical templates as DRAFT before the following reviews; keep those drafts current as tasks change. A verification command alone is not an acceptance criterion: name the behavior it must demonstrate and its expected result. Link existing authoritative requirements instead of copying them into a competing specification.
Step 3 — Plan-design-review (if frontend in scope)
If design doc indicates UI/frontend work, invoke /li:plan-design-review:
- Design system implications
- Accessibility considerations
- Visual sketch (if needed) via
/li:design-htmlor/li:design-review
Add design tasks to plan.
Step 4 — Plan-devex-review (always)
Invoke /li:plan-devex-review:
- Operator-DX implications (will this be painful to use later?)
- Documentation needed
- Telemetry hooks needed
- Test coverage gaps
Add DX-improving tasks to plan.
Step 5 — Plan-tune (iterative refinement)
If plan-eng-review / plan-design-review / plan-devex-review surface conflicts or gaps, invoke /li:plan-tune to reconcile.
Iterate until plan is internally consistent.
Step 6 — Dependency graph
For each leaf, identify upstream blocking leaves, then derive package dependencies from those edges. A package must not hide a dependency on a leaf in a later package. Visualize:
T1 (setup) → T2 (schema) → T3 (api) → T5 (test-e2e)
↘ T4 (ui) ↗
Detect cycles and impossible orderings in both views. Keep leaf IDs authoritative; the package table is a grouping in plan.md, not a new job-state schema or an automatic dispatch service. Surface blockers explicitly.
Step 7 — Cost estimate (MANDATORY GATE)
Time-on-request (design §3.7): wall-clock time fields are emitted only when the operator asked for them (--with-time, or they explicitly request it). Tokens + task count + size are always shown; time is opt-in so the default estimate never anchors on a guessed duration.
Honest signals only (no invented dollar figure). Lintel has no pricing table and the token
estimator is uncalibrated until CAPTURE records actuals (scale_calibrated_prior falls back to
size_default_prior — a hardcoded guess — when no history exists; see lib/scale-estimator.sh).
So the gate presents what the system can honestly compute — task count, the phase list, and a
labelled token estimate — and does not present a dollar number the system cannot derive.
# Plan signals (honest — task count + phases + labelled token estimate)
total_tasks: N # unchanged leaf count
work_packages: N
phases: [<phase list from the plan — what BUILD will actually run>]
size: <XS|S|M|L|XL from scope.md>
execution_roles: [<roles selected for package complexity and review independence>]
# model_configuration: <actual available host configuration, only when known>
token_estimate:
value: <tokens from scale_token_estimate <size>, once for the whole cycle>
basis: <calibrated|uncalibrated from the same helper result>
samples: <matching measured cycles from the same helper result>
scope: cycle
# estimated_time: <sum minutes> # only when --with-time
scale_token_estimate <size> returns tokens basis samples in one read; the numeric
scale_calibrated_prior API remains available. CAPTURE records whole-cycle actuals, so never
multiply this prior by the number of tasks or sum it once per leaf. Calibration writes remain
opt-in under ADR-0008; no usable actuals means uncalibrated, even if a log file exists.
AskUserQuestion (MANDATORY):
"Plan ready: tasks across , est. ~ tokens (<CALIBRATED | UNCALIBRATED — no actuals recorded yet>). Proceed?" (append ", ~" only when --with-time; never a $ figure)
- A) Approve and proceed
- B) Scope-trim (which tasks to defer)
- C) Decompose (tasks too big, break further)
- D) Abort (scope too large)
If A: continue to Step 8. If B/C: loop back. If D: status BLOCKED, no advance.
Step 8 — Cross-section-analyze (delegates to /li:analyze, ADR-0004)
Invoke /li:analyze with trigger plan-step8 — it runs the DEFINE↔PLAN and authority legs
(coverage, traceability, LOCKED-decision contradictions, discover-report ADR constraints) and
persists .claude/runtime/state/analyze-report.md. One implementation, shared with BUILD's final pass
and standalone runs; do not re-implement the checks inline.
If the report has findings: surface the gap-list, ask operator: defer to backlog / add to plan / accept gap (record the acceptance in the report).
Step 9 — Adversarial two-stage review (adopted from superpowers)
Dispatch CodeReviewer subagent (or general-purpose) with plan.md path:
Stage 1 — Spec compliance review: "Does plan.md match design doc requirements exactly? Coverage gaps? Tasks not traceable to design?"
If Stage 1 finds issues: fix (Edit tool), re-dispatch. Max 3 iterations.
Stage 2 — Quality review (only after Stage 1 PASS): "Are leaves well-decomposed, package boundaries coherent and aggregate review depth appropriate? Are dependencies correct, estimates grounded, acceptance and evidence complete for every leaf?"
If Stage 2 finds issues: fix, re-dispatch. Max 3 iterations.
Convergence guard: if same issues persist across 3 iterations, surface as "Reviewer Concerns" in plan.md and proceed.
If subagent unavailable: skip review, note in plan.md "Adversarial review unavailable — plan unreviewed."
Step 10 — Founder approval gate (MANDATORY PAUSE)
AskUserQuestion (per Architect image):
"Plan reviewed. across , est. ~ tokens (<CALIBRATED | UNCALIBRATED>). Final approval?" (append ", ~" only when --with-time; never a $ figure — the basis for this is Step 7)
- A) APPROVE — proceed to BUILD
- B) REDIRECT — specific feedback (loop back)
- C) PAUSE — save state for later, don't proceed
- D) ABORT — close plan, status BLOCKED
If A: mark the reviewed trio APPROVED, finalize it and write the checkpoint. Only declare status DONE after the artifact checks below pass. Until approval, all three remain DRAFT.
Step 11 — Write artifacts
The trio comes from versioned template files (Slice 2 — design §3.3). plan.md / spec.md / prompt.md are no longer rendered from inline prose; they are instantiated from the canonical template family, the single source of truth for their shape:
scaffolding/01-foundation/templates/plan/plan.template.md— depth_schema-parametric (flat / phased / tree marked sections).scaffolding/01-foundation/templates/plan/spec.template.md— engineering master spec.scaffolding/01-foundation/templates/plan/prompt.template.md— cold-executor handoff.
Read the template, strip the comment header + the unused depth_schema sections (for plan.template.md), fill the placeholders, and write the result to the output path. Preserve the approval status established at Step 10 when finalizing the drafts. If the scaffolding tree is absent, locate these templates in the installed Lintel source; if unavailable there too, retain the essential task fields from Step 2 and depth structure below, and record that the canonical template was unavailable.
plan.md (canonical, .claude/plans/<slug>/plan.md) — from plan.template.md:
# Plan: <wedge title> (size: <XS|S|M|L|XL> · schema: <flat|phased|tree>)
**Generated by:** /li:plan on <date>
**Status:** DRAFT (APPROVED only after Step 10)
**Design doc:** <path>
**Discover report:** <path>
**Scope:** <path to scope.md>
## Summary
<2-3 sentences>
## Plan signals
- Tasks: <N>
- Phases: <phase list — what BUILD will run>
- Size: <XS|S|M|L|XL from scope.md>
- Token estimate: ~<total> (<CALIBRATED from CAPTURE history | UNCALIBRATED — no actuals yet, size_default_prior guess>)
<!-- no dollar figure: Lintel has no pricing table (Step 7) -->
<!-- - Duration: <time> ← only emit when --with-time (design §3.7) -->
Depth-parametric rendering (design §3.3). Read depth_schema from scope.md (emitted by the SCOPE phase) and render the plan.template.md section that matches. The 2-5 min granularity rule applies to the leaf (task at flat/phased, subtask at tree) — hierarchy adds milestones, it does not weaken the leaf check. plan-eng-review Step 0's BLOCKING per-leaf check stays.
flat(XS/S — today's shape): one task table, IDsT1, T2, ….phased(M): phases with tasks, numbered1, 1.1 / 2, 2.1.tree(L/XL): phases → tasks → subtasks + milestone checkpoints,1 / 1.1 / 1.1.a(Slice 2 — see below).
Use the matching tables and per-leaf detail from plan.template.md; it is the source of
truth for ownership, requirement tracing, acceptance, verification and evidence fields.
Only add a minutes column when the operator requested time estimates.
depth_schema: tree — WBS rendering for L/XL (Slice 2 — design §3.3). Renders phase → task → subtask with milestone checkpoints. This replaces Slice 1's fallback (where tree degraded to phased).
Numbering scheme (three tiers, strictly hierarchical):
- Phase —
Phase 1,Phase 2, … (top tier; each carries a[milestone-checkpoint: <pass criterion>]). - Task —
1.1,1.2/2.1,2.2(the<phase>.<task>tier). - Subtask —
1.1.a,1.1.b/1.2.a(the<phase>.<task>.<letter>tier; lowercase letters).
The subtask is the LEAF at tree depth — the verification and progress unit. The 2-5 min granularity rule applies to the subtask (1.1.a), NOT the task or phase. Work packages group these leaves for execution and review while retaining leaf evidence and status. Milestone checkpoints stay at the phase level; packaging does not replace existing resume pointers.
--lazy (optional, opt-in — design §5 Approach-C graft): for very large XL trees, the subtask leaves (1.1.a) under a phase MAY be elaborated just-in-time when BUILD reaches that phase, rather than all up front. When --lazy is set, render the phases + tasks now and mark each phase's subtasks (lazy: elaborated at BUILD); the per-leaf ≤5 min rule still applies once a leaf is elaborated. Opt-in only — the default renders the full tree up front (preserves the trio's born-together contract; --lazy is the escape hatch for genuinely huge greenfield work where up-front elaboration would be wasteful).
spec.md (canonical, .claude/plans/<slug>/spec.md) — from spec.template.md:
- Master engineering specification — born in PLAN (v3.8 Feature 2.2: trio born together)
- Architecture overview from design doc
- Data model, interfaces, contracts
- Requirements traced to design
- Status: APPROVED (CAPTURE re-affirms on cycle-end, no longer the birth-point)
prompt.md (canonical, .claude/plans/<slug>/prompt.md) — from prompt.template.md — v3.8 Feature 2.2: born in PLAN, not CAPTURE.
It is a SELF-CONTAINED prompt: a fresh AI session reading only this prompt + the linked spec.md + plan.md can re-execute or extend the work without prior context. See prompt.template.md for the full skeleton (Context / Constraints / Acceptance criteria / Deliverables / How to re-execute / What you DON'T need to know).
The trio (plan.md + spec.md + prompt.md) is the cold-executor handoff contract. Born together in PLAN — from the versioned templates above — so standalone planner-module invocations (/li:plan <design.md> without a surrounding cycle) produce a complete handoff. CAPTURE re-affirms the trio (verifies presence, updates with final-build evidence) but no longer generates prompt.md.
.planner-checkpoint.md (.claude/runtime/state/):
- State for
/li:resume - Includes plan.md path, current task pointer, build-log placeholder
Step 11a — Trio completeness gate (mechanical — issue I4)
The selected work map is the handoff contract for native and Spec Kit work. Before declaring PLAN done, validate its paths, approval status and nonempty artifacts through the shared validator. This proves selection and structural completeness; the preceding reviews must also verify that requirements, leaf acceptance and handoff context are substantive, with no unfilled template placeholders. Structural validation alone does not prove those semantics.
working_repo="${LINTEL_REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null)}"
lintel_source="${LINTEL_SOURCE_ROOT:-$working_repo}"
selected_work_map="${LINTEL_WORK_MAP:-}"
if [ -z "$selected_work_map" ] && [ -n "${LINTEL_PLAN_DIR:-}" ]; then
selected_work_map="${LINTEL_PLAN_DIR%/}/work.json"
fi
[ -n "$selected_work_map" ] || {
echo "PLAN BLOCKED: select the initiative's work.json or exact LINTEL_PLAN_DIR" >&2
exit 1
}
case "$selected_work_map" in
/*|[A-Za-z]:/*) : ;;
*) selected_work_map="$working_repo/$selected_work_map" ;;
esac
"${PYTHON:-python3}" - "$lintel_source/bin/li-work-artifacts.py" "$working_repo" "$selected_work_map" <<'PY'
from pathlib import Path
import runpy
import sys
helper, repo, selected = sys.argv[1:]
try:
contract = runpy.run_path(helper)
root = Path(repo).resolve()
work = contract["load_work_map"](root, Path(selected))
if work["status"] != "APPROVED":
raise ValueError("selected work must be APPROVED before BUILD")
for field in contract["REQUIRED_ARTIFACTS"]:
path = contract["artifact_path"](root, work[field])
if not path.read_text(encoding="utf-8-sig").strip():
raise ValueError(f"selected {field} artifact is empty: {work[field]}")
except (OSError, ValueError, TypeError) as error:
print(f"PLAN BLOCKED: {error}", file=sys.stderr)
sys.exit(1)
print("PASS: explicitly selected approved work map and nonempty artifacts")
PY
gate_status=$?
[ "$gate_status" -eq 0 ] || exit "$gate_status"
slug_dir="$(dirname "$selected_work_map")"
# Optional stronger check: if a cold_executor envelope was emitted, validate it against the schema
[ -f "$slug_dir/handoff.envelope.yaml" ] && "$lintel_source/bin/li-envelope-validate" "$slug_dir/handoff.envelope.yaml" --quiet || true
Step 11b — Handoff-size check against the 500k cap (trio-emit gate, NON-BLOCKING)
The trio (plan.md + spec.md + prompt.md) now exists on disk — this is the cold-executor handoff payload. Before recommending BUILD, run the existing cap check so the trio + warming context can't silently exceed the 500k cap (the v4.9 audit's PARTIALLY-UPHELD Promise 6: cap logic existed but was invoked at no handoff).
Invoke the existing mechanism — do not rebuild it:
/li:handoff-size-check (a portable skill call; reads the trio it just wrote + .claude/runtime/state/warming-manifest.md, applies the mode-aware cap from /li:context-budget mode_envelopes, default customer-engagement: 500k soft / 750k hard).
- SURFACE, don't block. A yellow/red verdict warns ("this plan yields ~Nk handoff, near cap — split it?") and surfaces options (split the plan, cut a warming target, switch to a higher-cap mode). It does NOT halt PLAN — the operator decides.
- Off-switch:
--skip-handoff-size-check(orSKIP_HANDOFF_SIZE_CHECK=1) skips the gate entirely for operators who don't want it. Silent when skipped. - Silent green pass when trio + warming < soft cap — no friction in the common case.
Step 12 — 00-state.md append
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 PLAN DONE next=BUILD plan_path=<path> spec_draft_path=<path> tasks_count=<N> tokens_est=<total> tokens_est_basis=<calibrated|uncalibrated>
Status protocol
- DONE — plan APPROVED with cost estimate accepted + adversarial review pass
- DONE_WITH_CONCERNS — approved with caveats noted (reviewer concerns left in plan.md)
- BLOCKED — cost exceeds operator budget OR alternative undecided OR design missing
- NEEDS_CONTEXT — design doc incomplete, return to DEFINE
Pause-points (MANDATORY)
- After plan-eng-review/design-review/devex-review → confirm findings addressed before tune
- After cost estimate → AskUserQuestion gate (D7)
- After cross-section-analyze → if gaps, AskUserQuestion defer/add/accept
- After two-stage review → fix gaps before next stage
- After full plan + reviews → AskUserQuestion founder approval gate (D10)
Hop-in support
YES — operator can /li:plan with existing APPROVED design doc.
Skip-conditions:
- intent=hotfix (light plan, skip cost-estimate gate if <5k tokens)
- intent=research-dive (no plan needed)
Integration
Reads:
- APPROVED design doc (from DEFINE)
- discover-report.md (from DISCOVER)
scope.md(from SCOPE — thedepth_schemathat selects the WBS template variant)scaffolding/01-foundation/templates/plan/{plan,spec,prompt}.template.md(the canonical trio templates)- CORE-PRINCIPLES.md
- the active pack's compliance gates (
resolve_pack_field compliance.hooks; none by default) - Recent relevant ADRs
.claude/memory/lessons.md(via/li:lessons-surface, keyword-scoped, non-blocking)
Writes:
plan.md(canonical)spec.md(draft, finalized in CAPTURE).claude/runtime/state/.planner-checkpoint.md.claude/runtime/state/00-state.md(PLAN entry).claude/runtime/audit/plan-metrics.jsonl
Triggers:
- BUILD with plan.md as canonical source
Recommended agents to dispatch (from discover-report)
- Planner (engineering/) — primary, task decomposition
- Architect (engineering/) — sanity-check tech choices
- BackendArchitect / FrontendBuilder / DataPipelineDesigner (engineering/) — per domain
- APIDesigner (engineering/) — if API surface
- DatabaseDesigner (engineering/) — if schema changes
- TerraformReviewer / K8sManifestReviewer (devops/) — if infra
- ADRDrafter (engineering/) — if non-trivial decisions surface during planning
- SecurityAuditor / ThreatModelDrafter (security/) — sensitive-data flow review
- EUAIActReviewer (compliance/) — if AI/ML in a regulated market
Anti-patterns
- Plan that's a vague to-do list — must be file:line:verb with complete code or precise spec
- No cost estimate — operator commits to unknown burn → wasted hours
- Skipping two-stage review because "it's a simple plan" — simple plans hide assumption gaps
- Ignoring ADRs identified in DISCOVER — they're constraints, not advisory
- Task decomposition too coarse — 2-5 min per task (superpowers rule); bigger = decompose
- Invented model availability — select roles by package complexity and honor the current host's actual model configuration
- Plan finalized without founder gate — gate is MANDATORY per Architect image pattern
Failure recovery
- Cost estimate exceeds budget: AskUserQuestion scope-trim / decompose / abort. Don't proceed silently.
- Subagent reviewer unavailable: skip review, note in plan.md, proceed with caveat in status.
- Cross-section analyze finds critical gap: PAUSE, fix gap (back to DEFINE if design-level), re-plan.
- Operator rejects 3x at founder gate: status BLOCKED, save state for next session, don't loop indefinitely.
Voice tier behavior
voice: internal. Plan.md is engineering-internal. spec.md inherits the active pack's voice tier (resolve_pack_field voice.default_tier; default: internal).
Module-callable (v3.8 Feature 2.4)
PLAN is no longer just Phase 4 of cycle — it's a callable planner-module that any workflow can invoke.
Three invocation modes
1. Inside cycle (Phase 4):
/li:cycle → SENSE → DEFINE → DISCOVER → PLAN → BUILD → REVIEW → SHIP → CAPTURE
▲
reads DEFINE + DISCOVER outputs from job dir
2. Standalone:
/li:plan <design.md>
↓
workflow_root: true → spawns own job at .claude/runtime/jobs/plan-<stamp>-<hash>/
(job auto-spawn is dormant by decision, ADR-0008 — the job-begin hook is
not auto-registered; the trio + approval gate below run regardless)
produces: plan.md + spec.md + prompt.md (the trio)
handoff-size-check against 500k cap (trio + warming)
founder approval gate
→ DONE, ready for cold-executor handoff
3. Sub-module called by another workflow_root skill:
/li:cycle OR /li:safe-install
↓ discovery ↓ pre-flight
CALL /li:plan --from <design> CALL /li:plan --from <change-spec>
↓ receives trio ↓ receives trio
proceed to BUILD with trio proceed to execute with trio
The calling workflow passes:
--from <path>(design doc or change-spec)--called-by <skill-name>(setsCALLED_BYenv so job.yaml records caller)--no-job(if the caller is itself a workflow_root job; nested jobs are pointless)
Output contract (deterministic for callers)
Regardless of invocation mode, PLAN always emits:
<run-dir>/plan.md— task breakdown<run-dir>/spec.md— engineering master spec<run-dir>/prompt.md— cold-executor handoff (born here, v3.8 Feature 2.2)
Callers can rely on these paths existing post-DONE. CAPTURE re-affirms but doesn't (re)generate.
Job integration
When workflow_root: true fires job-begin hook:
- Job spawned at
.claude/runtime/jobs/plan-<stamp>-<hash>/ - Trio written to
outputs/plan.md,outputs/spec.md,outputs/prompt.md job-endpromotes trio to.claude/plans/<slug>/on DONE
Anti-pattern: nested job spawning
If /li:cycle calls /li:plan as Phase 4, the operator already has a cycle-job. PLAN should NOT spawn its own nested job — that creates two open jobs for one workflow. The caller passes --no-job (or NO_JOB=1 env) so the job-begin hook short-circuits.
See also
docs/concepts/planner-as-module.md(architecture doc)/li:jobscontroller
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.