diff --git a/CONCEPTS.md b/CONCEPTS.md
index 4df79f548..b6e7a362a 100644
--- a/CONCEPTS.md
+++ b/CONCEPTS.md
@@ -99,7 +99,7 @@ A discrete, self-scored confidence value on a fixed small scale, each level tied
The classification of a review finding by how safely its proposed fix can be applied: applied silently, applied only after user confirmation, left for a human to resolve, or recorded as advisory with no action.
### Headless mode
-An explicit opt-in mode that runs a Skill unattended, with no user prompts — it produces a written report as its deliverable and conservatively defers genuinely ambiguous decisions rather than guessing.
+An explicit opt-in mode that runs a Skill unattended, with no user prompts — it produces a written report as its deliverable and conservatively defers genuinely ambiguous decisions rather than guessing. A Skill may expose a separate depth selector inside headless mode when automations need an explicit coverage tradeoff; the non-interactive contract and the work depth remain distinct decisions.
### Session-settled decision
A decision examined and chosen by the user in the invoking conversation — a surfaced tradeoff or alternative followed by the user's choice — carried through the Pipeline as a provenance-labeled constraint (annotation stem `session-settled:`, classes `user-directed` and `user-approved`) that downstream skills augment but never re-ask, and contradict only on evidence. An unexamined assertion is a directive, not a settled decision, and receives exactly one in-pipeline challenge; agents never label their own unexamined proposals.
diff --git a/docs/skills/ce-compound.md b/docs/skills/ce-compound.md
index cfe7b63ae..3f2e75f01 100644
--- a/docs/skills/ce-compound.md
+++ b/docs/skills/ce-compound.md
@@ -47,10 +47,12 @@ Most teams solve the same problem twice — sometimes with the same person — b
**Full mode** runs three research subagents in parallel (Context Analyzer / Solution Extractor / Related Docs Finder), plus an automatic session-history probe that searches your prior sessions across Claude Code, Codex, and Cursor for related context. Cross-references existing docs, detects duplicates, runs specialized reviews.
-**Lightweight mode** does the same documentation in a single pass, no subagents, no cross-referencing. Faster, fewer tokens.
+**Lightweight mode** writes the same solution-doc artifact type in a single pass, with no subagents or cross-referencing. It is lower overhead, but it also skips overlap detection, session-history research, and semantic grounding validation.
**The skill picks the mode itself — it does not ask.** Full is the default because its token cost is small next to the work that produced the learning; Lightweight is chosen only under real context pressure (session near its limit, or a trivial fix where cross-referencing adds nothing). Those are conditions the agent can observe and the user can't, so a prompt would just ask you to guess. The skill states which mode it ran, and why, on the first line of its output; if it guessed wrong for your taste, re-running is a cheap correction.
+Automations can select the same tradeoff without a prompt: `mode:headless depth:lightweight` runs the single-pass workflow, while `mode:headless depth:full` runs the complete workflow, including its automatic session-history probe. Existing `mode:headless` calls remain Full by default. Depth is headless-only; a depth flag without headless intent, an unknown value, or conflicting depth flags fails explicitly instead of silently choosing a workflow.
+
### 2. Bug track vs knowledge track — different structures for different shapes
The skill classifies the work into one of two tracks based on `problem_type`:
@@ -78,7 +80,7 @@ The proposed addition matches the existing file's tone and density — a single-
A solution doc is only as valuable as its claims are true, and drafting from conversation evidence invites three failure shapes: code-behavior claims written from a session-level summary instead of the source, "fixed in X" claims about merges the current checkout can't see, and drafting scaffold ("Learning 3") leaking into the written doc.
-Phase 2.45 closes this in two layers. A deterministic script (`scripts/validate-doc-claims.py`) checks cited repo paths, commit SHAs (classified by reachability from HEAD vs the upstream default branch, so a stale checkout is distinguished from a fabricated citation), relative links, and dangling scaffold — its flags are adjudicated, not auto-failed, because a doc may legitimately cite a path deleted by the very fix it documents. Then a read-only validator subagent (Full and headless modes) verifies code-behavior claims by quoting the defining source line, merge-state claims against remote truth (`gh` primary, local git fallback), and internal completeness of countable assertions. The same discipline applies at draft time: the Solution Extractor must read the defining line before asserting behavior, and cite PR numbers over rebase-fragile SHAs.
+Phase 2.45 closes this in two layers. A deterministic script (`scripts/validate-doc-claims.py`) checks cited repo paths, commit SHAs (classified by reachability from HEAD vs the upstream default branch, so a stale checkout is distinguished from a fabricated citation), relative links, and dangling scaffold — its flags are adjudicated, not auto-failed, because a doc may legitimately cite a path deleted by the very fix it documents. Then a read-only validator subagent (Full mode, including headless Full) verifies code-behavior claims by quoting the defining source line, merge-state claims against remote truth (`gh` primary, local git fallback), and internal completeness of countable assertions. Lightweight keeps the deterministic check and skips the validator subagent. The same discipline applies at draft time: the Solution Extractor must read the defining line before asserting behavior, and cite PR numbers over rebase-fragile SHAs.
### 6. Selective refresh trigger
@@ -154,6 +156,8 @@ The skill is its own complete cycle:
- **Just-finished problem** — `/ce-compound` (or auto-invoked from "that worked")
- **With context hint** — `/ce-compound "the email digest race condition we fixed"`
- **Lightweight on a long session** — when context is tight, the skill selects lightweight mode on its own and says so in its output
+- **Lower-overhead unattended capture** — `/ce-compound mode:headless depth:lightweight "the verified fix"`
+- **Full unattended capture** — `/ce-compound mode:headless depth:full "the verified fix"` (plain `mode:headless` is equivalent)
The auto-invoke triggers happen mid-conversation; you don't need to remember the slash command if you've just confirmed something works.
@@ -173,6 +177,8 @@ Put it in the repo's `AGENTS.md`/`CLAUDE.md`, or in your global instruction file
> After a solved, verified problem produces a non-trivial, reusable learning, automatically invoke the `ce-compound` skill, passing `mode:headless` as the skill argument. Only in repositories that accept `docs/solutions/` as a tracked knowledge store.
+Use `mode:headless depth:lightweight` instead when the standing workflow deliberately accepts reduced research and validation in exchange for a single-pass, no-subagent closure.
+
Auto-run writes to `docs/solutions/` (and may touch `CONCEPTS.md`) without asking — but that's the point, and it's no scarier than the other edits you're already making on the branch and reviewing before you commit. Headless never edits `AGENTS.md`/`CLAUDE.md`; if discoverability is missing it reports `gap noted, not applied` so a later interactive run can apply it with consent. Passing `mode:headless` as an argument is the explicit, unambiguous form: the skill also honors a clear "run headless / without prompts" request, but the token removes all doubt — without a headless signal the run stays interactive and can stop for the one-time discoverability-consent prompt.
Every other phrase in those lines is deliberate too:
diff --git a/skills/ce-compound/SKILL.md b/skills/ce-compound/SKILL.md
index f6a14d9fe..0bc68bca9 100644
--- a/skills/ce-compound/SKILL.md
+++ b/skills/ce-compound/SKILL.md
@@ -1,7 +1,7 @@
---
name: ce-compound
description: Document a recently solved problem or durable project vocabulary in docs/solutions/ or CONCEPTS.md. Use when capturing a learning after work.
-argument-hint: "[optional: brief context] [mode:headless] "
+argument-hint: "[optional: brief context] [mode:headless] [depth:lightweight|full]"
---
# /ce-compound
@@ -21,6 +21,8 @@ Captures problem solutions while context is fresh, creating structured documenta
/ce-compound [brief context] # Provide additional context hint
/ce-compound mode:headless # Non-interactive run for automations
/ce-compound mode:headless [context] # Non-interactive run with context hint
+/ce-compound mode:headless depth:lightweight [context] # Lower-overhead non-interactive run
+/ce-compound mode:headless depth:full [context] # Full non-interactive run
```
**One learning per run.** The workflow's grounding, overlap detection, and cross-referencing all assume a single solved problem. When a session produced multiple distinct learnings, run the skill once per learning, sequentially — each run grounds fresh against the tree. Do not batch several learnings through one run and stitch cross-references between the drafts afterward; drafting-context numbering ("Learning 3") leaking into written docs is the failure this rule prevents.
@@ -31,14 +33,16 @@ If invoked specifically to create or bootstrap `CONCEPTS.md` from scratch rather
## Mode Detection
-Enter headless mode when **either** holds: the arguments you were invoked with contain the `mode:headless` token, **or** the invocation makes non-interactive intent unmistakable — a caller or standing instruction asking to run `ce-compound` "headless", "non-interactively", "unattended", or "without prompts/questions". The token is the explicit form; a clear natural-language request for a non-interactive run is equivalent. Bare "automatically" or "auto-run" is **not** on its own a headless signal — it speaks to *invoking* the skill, not to suppressing its prompts — so an ambiguous or absent signal defaults to interactive. Tokens starting with `mode:` are flags, not context — strip `mode:headless` from arguments before treating the remainder as the brief context hint.
+Enter headless mode when **either** holds: the arguments you were invoked with contain the `mode:headless` token, **or** the invocation makes non-interactive intent unmistakable — a caller or standing instruction asking to run `ce-compound` "headless", "non-interactively", "unattended", or "without prompts/questions". The token is the explicit form; a clear natural-language request for a non-interactive run is equivalent. Bare "automatically" or "auto-run" is **not** on its own a headless signal — it speaks to *invoking* the skill, not to suppressing its prompts — so an ambiguous or absent signal defaults to interactive. Tokens starting with `mode:` or `depth:` are flags, not context — strip them before treating the remainder as the brief context hint.
+
+Depth is an explicit headless-only selector. In headless mode, accept at most one depth token: `depth:lightweight` routes directly to Lightweight Mode, while `depth:full` routes to Full Mode with its automatic session-history probe. `mode:headless` without a `depth:` token remains backward compatible and runs Full Mode. Headless lightweight asks no blocking questions and launches no subagents. If the invocation contains an unknown `depth:` token, multiple `depth:` tokens, or a `depth:` token without headless intent, do not guess; emit the headless failure report with the reason and end with `Documentation skipped`.
| Mode | When | Behavior |
|------|------|----------|
| **Interactive** (default) | No headless token or clear non-interactive intent | Auto-pick Full vs Lightweight and report the choice; run session history as an automatic probe (Full only); prompt for Discoverability Check consent; end with a plain summary (no "What's next?" menu) |
-| **Headless** | `mode:headless` token present, or the invocation makes non-interactive intent unmistakable | No blocking questions. Run **Full mode**, including the automatic session-history probe (it never prompts, so it preserves headless's non-interactive contract). If the Discoverability Check finds a gap, report it under `Instruction-file edit: gap noted, not applied` — never edit instruction files (headless is for skill-to-skill / automation handoffs; amending the repo's operating contract needs interactive consent). Skip Phase 3 specialized reviews. End with a structured terminal report — no "What's next?" menu. |
+| **Headless** | `mode:headless` token present, or the invocation makes non-interactive intent unmistakable | No blocking questions. Run the explicitly requested depth, defaulting to **Full mode** with the automatic session-history probe. If the Discoverability Check finds a gap, report it without editing instruction files. Skip Phase 3 specialized reviews. End with a structured terminal report — no "What's next?" menu. |
-Headless mode is intended for automations and skill-to-skill invocation where no human is present to answer questions. The doc itself is identical to what an interactive Full run would produce — classification work (track, category, overlap) follows the same rules and writes nothing extra into the artifact. Once detected, headless mode applies for the entire run.
+Headless mode is intended for automations and skill-to-skill invocation where no human is present to answer questions. Once detected, headless mode applies for the entire run.
## Session context
@@ -73,9 +77,9 @@ When spawning subagents, pass the relevant file contents into the task prompt so
- Choose **Lightweight** (single-pass, no subagents — see Lightweight Mode) ONLY under real context pressure: the session is near its context limit, or the fix is trivial enough that cross-referencing would add nothing. These are conditions the agent can observe and the user cannot, which is exactly why this is not a question.
- State the chosen mode and a one-line reason as the first line of the completion output (e.g., "Ran Full mode." / "Ran Lightweight mode — session context was tight."). If Lightweight was the wrong call for the user's taste, re-running is a rare, cheap correction — cheaper than taxing every run with a prompt.
-**In headless mode**, skip mode selection entirely and run **Full Mode**, including the automatic session-history probe (Phase 1 step 4) — it is non-interactive by construction. Proceed straight to research.
+**In headless mode**, skip automatic mode selection. Run the depth selected during Mode Detection: `depth:lightweight` enters Lightweight Mode; `depth:full` or no depth token enters Full Mode, including the automatic session-history probe (Phase 1 step 4).
-**Session history — an automatic probe in Full mode, never a question.** The point of searching prior sessions is that an *unrelated* earlier session may hold related problem-solving; neither the agent nor the user can know that a priori, so asking is pointless. Instead, Full mode always runs the cheap discovery+metadata probe (Phase 1 step 4) — it runs in parallel with the research subagents, so it is near-free on wall-clock — and escalates to the expensive extraction+synthesis only when the probe surfaces genuinely relevant candidate sessions. Lightweight mode skips session history entirely; headless runs the same automatic probe, since it prompts for nothing and so keeps headless non-interactive. This support exists only inside the compounding workflow; there is no standalone session-history product surface.
+**Session history — an automatic probe in Full mode, never a question.** The point of searching prior sessions is that an *unrelated* earlier session may hold related problem-solving; neither the agent nor the user can know that a priori, so asking is pointless. Instead, Full mode always runs the cheap discovery+metadata probe (Phase 1 step 4) — it runs in parallel with the research subagents, so it is near-free on wall-clock — and escalates to the expensive extraction+synthesis only when the probe surfaces genuinely relevant candidate sessions. Lightweight mode skips session history entirely; headless Full runs the same automatic probe, since it prompts for nothing and so keeps headless non-interactive. This support exists only inside the compounding workflow; there is no standalone session-history product surface.
---
@@ -370,7 +374,7 @@ The doc (and any `CONCEPTS.md` entries from Phase 2.4) is about to become perman
Exit 0 means nothing flagged. Exit 1 means flags to **adjudicate, not auto-fix** — each flagged path, SHA, link, or scaffold pattern is fixed, annotated as historical, or confirmed intentional per the reference's adjudication table. A doc may legitimately cite a path deleted by the very fix it documents; a flag is a question, not a failure. If the script cannot be resolved on this platform, apply the reference's manual checklist and say so in the output — never silently skip.
-2. **Semantic grounding validator (Full and headless; lightweight skips it).** Dispatch one read-only generic subagent built from the prompt template in the reference, covering the written doc plus any `CONCEPTS.md` entries added or edited this run. It verifies code-behavior claims by quoting the defining source line, merge-state claims against remote truth (`gh` primary, git reachability fallback), and internal completeness of countable assertions. Apply its verdicts per the reference (fix contradicted claims from the quoted evidence; soften or drop unverifiable ones; mark offline merge-state checks as degraded), then re-run the mechanical check if the body changed.
+2. **Semantic grounding validator (Full mode, including headless Full; lightweight skips it).** Dispatch one read-only generic subagent built from the prompt template in the reference, covering the written doc plus any `CONCEPTS.md` entries added or edited this run. It verifies code-behavior claims by quoting the defining source line, merge-state claims against remote truth (`gh` primary, git reachability fallback), and internal completeness of countable assertions. Apply its verdicts per the reference (fix contradicted claims from the quoted evidence; soften or drop unverifiable ones; mark offline merge-state checks as degraded), then re-run the mechanical check if the body changed.
### Phase 2.5: Selective Refresh Check
@@ -453,7 +457,7 @@ After the learning is written and the refresh decision is made, check whether th
`docs/solutions/` — documented solutions to past problems (bugs, best practices, workflow patterns), organized by category with YAML frontmatter (`module`, `tags`, `problem_type`). Relevant when implementing or debugging in documented areas.
```
- c. In full interactive mode, explain to the user why this matters — agents working in this repo (including fresh sessions, other tools, or collaborators without the plugin) won't know to check `docs/solutions/` unless the instruction file surfaces it. Show the proposed change and where it would go, then use the platform's blocking question tool to get consent before making the edit: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_question` in Antigravity CLI (`agy`), `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to presenting the proposal in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question. In lightweight mode, output a one-liner note and move on. In headless mode, **do not edit instruction files** — surface the gap in the terminal report as `Instruction-file edit: gap noted, not applied` (headless scope is documentation capture, not project-config edits; a human-invoked interactive run applies the edit with consent)
+ c. In full interactive mode, explain to the user why this matters — agents working in this repo (including fresh sessions, other tools, or collaborators without the plugin) won't know to check `docs/solutions/` unless the instruction file surfaces it. Show the proposed change and where it would go, then use the platform's blocking question tool to get consent before making the edit: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_question` in Antigravity CLI (`agy`), `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to presenting the proposal in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question. In lightweight mode (interactive or headless), output a one-liner note and move on. In full headless mode, **do not edit instruction files** — surface the gap in the terminal report as `Instruction-file edit: gap noted, not applied` (headless scope is documentation capture, not project-config edits; a human-invoked interactive run applies the edit with consent)
5. **If `CONCEPTS.md` exists at repo root, run a parallel discoverability check for it.** Assess whether the instruction file would lead an agent to discover the project's shared domain vocabulary. Use the same workflow as the `docs/solutions/` check above: same target file, same edit-placement judgment, same consent-then-edit interaction shape per mode. A line in an existing section is almost always better than a new headed section. Example calibration when nothing else fits:
@@ -486,26 +490,31 @@ Based on problem type, optionally dispatch generic subagents seeded with local p
### Lightweight Mode
-**Single-pass alternative — same documentation, fewer tokens.**
+**Single-pass alternative — same artifact type, reduced research and validation.**
-This mode skips parallel subagents entirely. The orchestrator performs all work in a single pass, producing the same solution document without cross-referencing or duplicate detection.
+This mode skips parallel subagents entirely. The orchestrator performs all work in a single pass and writes the same solution-doc artifact type, but omits cross-referencing, duplicate detection, session-history research, and semantic grounding validation.
-Headless mode forces Full and does not enter Lightweight — automations get the cross-reference and overlap detection benefits without the interactive overhead.
+Headless mode enters Lightweight only when explicitly invoked with `depth:lightweight`; otherwise it defaults to Full for backward compatibility.
The orchestrator (main conversation) performs ALL of the following in one sequential pass:
1. **Extract from conversation**: Identify the problem and solution from conversation history. Also scan the "user's auto-memory" block injected into your system prompt, if present (Claude Code only) -- use any relevant notes as supplementary context alongside conversation history. Tag any memory-sourced content incorporated into the final doc with "(auto memory [claude])". Before asserting how code behaves (enum values, status semantics, limits, defaults), Read the defining line at the current tree — soften or attribute any claim you cannot verify. Cite PR numbers over bare commit SHAs, and phrase unmerged fixes as pending
2. **Classify**: Read `references/schema.yaml` and `references/yaml-schema.md`, then determine track (bug vs knowledge), category, and filename
-3. **Write minimal doc**: Create `docs/solutions/[category]/[filename].md` using the appropriate track template from `assets/resolution-template.md`, with:
+3. **Write minimal doc**: Before writing, check whether the exact proposed `docs/solutions/[category]/[filename].md` path exists. If it exists, read it: update it only when it covers the same problem, preserving its path and frontmatter structure and adding `last_updated: YYYY-MM-DD`; otherwise choose a distinct, descriptive filename and re-check that exact path is absent before writing. This is exact-path collision handling only — do not run Full mode's semantic overlap research or dispatch subagents. Create or update the doc using the appropriate track template from `assets/resolution-template.md`, with:
- YAML frontmatter with track-appropriate fields, applying the YAML-safety quoting rule for array items (see `references/yaml-schema.md` > YAML Safety Rules)
- Bug track: Problem, root cause, solution with key code snippets, one prevention tip
- Knowledge track: Context, guidance with key examples, one applicability note
-4. **Vocabulary capture (update-only)**: if `CONCEPTS.md` exists at repo root, read `references/concepts-vocabulary.md`, then scan the new doc and the conversation for qualifying terms and add/refine entries silently (same criteria as Phase 2.4). Do **not** bootstrap or seed in lightweight mode — if `CONCEPTS.md` does not exist, defer creation to a Full run, which owns seeding. Record the outcome in the output (e.g., "Vocabulary: 1 entry refined" or "scanned, no qualifying terms"). If you refined `CONCEPTS.md` and a quick read of `AGENTS.md`/`CLAUDE.md` shows it isn't surfaced there, add the discoverability tip to the output below — lightweight **tips**, it does not edit instruction files (an interactive Full run owns that edit after consent; headless Full also tips/reports only).
-5. **Mechanical claims check**: run `scripts/validate-doc-claims.py` against the written doc exactly as in Phase 2.45 step 1 (same `SKILL_DIR` anchor, same adjudicate-not-auto-fix rule — read `references/grounding-validation.md` for the adjudication table when it flags anything). Lightweight skips only the semantic validator subagent, not this deterministic check.
-6. **Skip specialized agent reviews** (Phase 3) and the semantic grounding validator (Phase 2.45 step 2) to conserve context
-
-**Lightweight output:**
+4. **Vocabulary capture (update-only)**: if `CONCEPTS.md` exists at repo root, read `references/concepts-vocabulary.md`, then scan the new doc and the conversation for qualifying terms and add/refine entries silently (same criteria as Phase 2.4). Do **not** bootstrap or seed in lightweight mode — if `CONCEPTS.md` does not exist, defer creation to a Full run, which owns seeding. Record the outcome in the output (e.g., "Vocabulary: 1 entry refined" or "scanned, no qualifying terms"). If you refined `CONCEPTS.md` and the project's active instructions and conventions already in your context do not surface it, add the discoverability tip to the output below — lightweight **tips**, it does not edit instruction files (an interactive Full run owns that edit after consent; headless Full also tips/reports only).
+5. **Read-only discoverability check**: Using the project's active instructions and conventions already in your context, assess whether they surface `docs/solutions/` against the three criteria under **Discoverability Check** above. Do not open, offer to edit, or edit instruction files; Lightweight only reports the result. Record one of:
+ - `no gap` when active project instructions surface the knowledge store
+ - `gap noted — instruction-file tip emitted` when active project instructions exist but do not surface it
+ - `not applicable — no active project instructions` when no project instructions are active; emit no discoverability tip
+6. **Mechanical claims check**: run `scripts/validate-doc-claims.py` against the written doc exactly as in Phase 2.45 step 1 (same `SKILL_DIR` anchor, same adjudicate-not-auto-fix rule — read `references/grounding-validation.md` for the adjudication table when it flags anything). Lightweight skips only the semantic validator subagent, not this deterministic check.
+7. **Frontmatter parser-safety check**: validate the written doc exactly as in Phase 2 step 8, using the same bundled-script existence guard and manual fallback checklist. Fix any violation and repeat the check; do not report success until the written frontmatter is parser-safe.
+8. **Skip specialized agent reviews** (Phase 3) and the semantic grounding validator (Phase 2.45 step 2) to conserve context
+
+**Lightweight completion output:** In headless Lightweight, do not emit this interactive block; use the depth-specific report under `Success Output` > `Headless mode` instead. In interactive Lightweight, emit:
```
✓ Documentation complete (lightweight mode)
@@ -591,7 +600,7 @@ Knowledge track:
| Subagent returns a long prose body only as its inline response | Subagent writes full output to its run artifact; orchestrator Reads it back (inline return is fallback only) |
| Research and assembly run in parallel | Research completes → then assembly runs |
| Multiple files created during workflow | One solution doc written or updated: `docs/solutions/[category]/[filename].md` (plus optional maintenance writes: a `CONCEPTS.md` create/update from Phase 2.4, and — interactive Full only, after consent — a small instruction-file edit for discoverability) |
-| Headless Discoverability Check edits AGENTS.md/CLAUDE.md | Headless reports `Instruction-file edit: gap noted, not applied`; only interactive Full applies the edit after consent |
+| Headless Discoverability Check edits AGENTS.md/CLAUDE.md | Headless Full reports `Instruction-file edit: gap noted, not applied`; headless Lightweight emits a discoverability tip; only interactive Full applies the edit after consent |
| Creating a new doc when an existing doc covers the same problem | Check overlap assessment; update the existing doc when overlap is high |
| Asserting code behavior or merge-state from conversation memory | Read the defining source line before asserting; cite PR numbers over SHAs; soften unverifiable claims (Phase 1 extractor rules, re-checked in Phase 2.45) |
| Batching several learnings through one run and stitching cross-references between drafts | One learning per run; run the skill sequentially for each additional learning |
@@ -602,6 +611,25 @@ Knowledge track:
Emit a structured terminal report and end the turn. No "What's next?" question, no blocking prompt. End with `Documentation complete` as the terminal signal so callers can detect completion.
+For `depth:lightweight`, use this lower-overhead report after the Lightweight Mode workflow:
+
+```
+✓ Documentation complete (headless lightweight mode)
+
+File: docs/solutions//.md (created | updated)
+Track:
+Category:
+Grounding:
+Discoverability:
+CONCEPTS.md:
+CONCEPTS.md discoverability:
+Refresh recommendation:
+
+Documentation complete
+```
+
+For `depth:full` or backward-compatible headless calls with no depth token, use the Full report:
+
```
✓ Documentation complete (headless mode)
diff --git a/tests/skills/ce-compound-headless-depth.test.ts b/tests/skills/ce-compound-headless-depth.test.ts
new file mode 100644
index 000000000..7ce1fad46
--- /dev/null
+++ b/tests/skills/ce-compound-headless-depth.test.ts
@@ -0,0 +1,146 @@
+import { describe, expect, test } from "bun:test"
+import { readFileSync } from "fs"
+import path from "path"
+
+const skillPath = path.join(
+ import.meta.dir,
+ "..",
+ "..",
+ "skills",
+ "ce-compound",
+ "SKILL.md",
+)
+
+const skill = readFileSync(skillPath, "utf8")
+
+describe("ce-compound non-interactive depth contract", () => {
+ test("advertises explicit lightweight and full headless invocations", () => {
+ expect(skill).toContain("mode:headless depth:lightweight")
+ expect(skill).toContain("mode:headless depth:full")
+ })
+
+ test("keeps existing headless calls backward compatible", () => {
+ expect(skill).toMatch(/`mode:headless` without a `depth:` token[^\n]+Full/i)
+ expect(skill).toMatch(/`depth:full` or no depth token enters Full Mode[^\n]+automatic session-history probe/i)
+ })
+
+ test("routes explicit lightweight depth without prompts or subagents", () => {
+ expect(skill).toMatch(/`depth:lightweight`[^\n]+Lightweight Mode/i)
+ expect(skill).toMatch(/headless lightweight[^\n]+no blocking questions/i)
+ expect(skill).toMatch(/headless lightweight[^\n]+no subagents/i)
+ expect(skill).toContain("Documentation complete (headless lightweight mode)")
+ expect(skill).toContain("In full headless mode, **do not edit instruction files**")
+ expect(skill).not.toContain("In full headless mode, apply the edit directly")
+ expect(skill).toContain("Discoverability: {
+ expect(skill).toMatch(/unknown `depth:`[^\n]+Documentation skipped/i)
+ expect(skill).toMatch(/multiple `depth:`[^\n]+Documentation skipped/i)
+ expect(skill).toMatch(/`depth:` token without headless intent[^\n]+Documentation skipped/i)
+ })
+
+ test("keeps full-only validation out of lightweight runs", () => {
+ expect(skill).toContain("Semantic grounding validator (Full mode, including headless Full; lightweight skips it)")
+ expect(skill).not.toContain("Semantic grounding validator (Full and headless; lightweight skips it)")
+ })
+
+ test("scopes the automatic session-history probe to Full runs", () => {
+ expect(skill).toMatch(/Lightweight mode skips session history entirely; headless Full runs the same automatic probe/i)
+ expect(skill).not.toMatch(/Lightweight mode skips session history entirely; headless runs the same automatic probe/i)
+ })
+
+ test("routes headless Lightweight past the interactive completion block", () => {
+ const lightweightStart = skill.indexOf("### Lightweight Mode")
+ const successOutputStart = skill.indexOf("## Success Output")
+ const lightweightSection = skill.slice(lightweightStart, successOutputStart)
+
+ expect(lightweightSection).toMatch(/In headless Lightweight, do not emit this interactive block[^\n]+Headless mode/i)
+ expect(skill.match(/Documentation complete \(headless lightweight mode\)/g)).toHaveLength(1)
+ })
+
+ test("grounds lightweight discoverability from active context without reopening instruction files", () => {
+ const lightweightStart = skill.indexOf("### Lightweight Mode")
+ const successOutputStart = skill.indexOf("## Success Output")
+ const lightweightSection = skill.slice(lightweightStart, successOutputStart)
+ const checkStart = lightweightSection.indexOf("Read-only discoverability check")
+ const reportStart = lightweightSection.indexOf("Lightweight completion output")
+
+ expect(checkStart).toBeGreaterThan(-1)
+ expect(reportStart).toBeGreaterThan(checkStart)
+ expect(lightweightSection).toContain(
+ "the project's active instructions and conventions already in your context",
+ )
+ expect(lightweightSection).not.toContain("Phase 2.6")
+ expect(lightweightSection).not.toMatch(/quick read of `AGENTS\.md`\/`CLAUDE\.md`/i)
+ })
+
+ test("reports an explicit not-applicable state when no project instructions are active", () => {
+ expect(skill).toMatch(
+ /not applicable — no active project instructions[^\n]+emit no (?:discoverability )?tip/i,
+ )
+ expect(skill).toContain(
+ "Discoverability: ",
+ )
+ })
+
+ test("carries CONCEPTS.md discoverability into the headless Lightweight report", () => {
+ const reportStart = skill.indexOf("For `depth:lightweight`, use this lower-overhead report")
+ const fullReportStart = skill.indexOf(
+ "For `depth:full` or backward-compatible headless calls",
+ )
+ const lightweightReport = skill.slice(reportStart, fullReportStart)
+
+ expect(reportStart).toBeGreaterThan(-1)
+ expect(fullReportStart).toBeGreaterThan(reportStart)
+ expect(lightweightReport).toContain(
+ "CONCEPTS.md discoverability: ",
+ )
+ })
+
+ test("validates lightweight frontmatter parser safety before reporting success", () => {
+ const lightweightStart = skill.indexOf("### Lightweight Mode")
+ const successOutputStart = skill.indexOf("## Success Output")
+ const lightweightSection = skill.slice(lightweightStart, successOutputStart)
+ const writeStep = lightweightSection.indexOf("**Write minimal doc**")
+ const parserSafetyStep = lightweightSection.indexOf("**Frontmatter parser-safety check**")
+ const completionOutput = lightweightSection.indexOf("**Lightweight completion output:**")
+
+ expect(writeStep).toBeGreaterThan(-1)
+ expect(parserSafetyStep).toBeGreaterThan(writeStep)
+ expect(completionOutput).toBeGreaterThan(parserSafetyStep)
+ expect(lightweightSection).toMatch(
+ /Frontmatter parser-safety check[^\n]+Phase 2 step 8[^\n]+bundled-script existence guard and manual fallback checklist/i,
+ )
+ })
+
+ test("guards lightweight writes against exact target-path collisions", () => {
+ const lightweightStart = skill.indexOf("### Lightweight Mode")
+ const successOutputStart = skill.indexOf("## Success Output")
+ const lightweightSection = skill.slice(lightweightStart, successOutputStart)
+ const writeStep = lightweightSection.indexOf("**Write minimal doc**")
+ const collisionGuard = lightweightSection.indexOf(
+ "check whether the exact proposed `docs/solutions/[category]/[filename].md` path exists",
+ )
+ const claimsCheck = lightweightSection.indexOf("**Mechanical claims check**")
+
+ expect(writeStep).toBeGreaterThan(-1)
+ expect(collisionGuard).toBeGreaterThan(writeStep)
+ expect(claimsCheck).toBeGreaterThan(collisionGuard)
+ expect(lightweightSection).toMatch(
+ /If it exists, read it: update it only when it covers the same problem, preserving its path and frontmatter structure and adding `last_updated: YYYY-MM-DD`/i,
+ )
+ expect(lightweightSection).toMatch(
+ /otherwise choose a distinct, descriptive filename and re-check that exact path is absent before writing/i,
+ )
+ expect(lightweightSection).toContain(
+ "This is exact-path collision handling only — do not run Full mode's semantic overlap research or dispatch subagents.",
+ )
+ })
+
+ test("describes Lightweight as reduced coverage without bounded-cost claims", () => {
+ expect(skill).toContain("Single-pass alternative — same artifact type, reduced research and validation.")
+ expect(skill).not.toContain("Single-pass alternative — same documentation, fewer tokens.")
+ expect(skill).not.toContain("use this bounded report")
+ })
+})