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") + }) +})