Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: lint

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
markdownlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DavidAnson/markdownlint-cli2-action@v17
with:
globs: "**/*.md"

frontmatter:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Verify SKILL.md frontmatter
run: |
set -e
required=(name description allowed-tools)
for field in "${required[@]}"; do
if ! grep -q "^${field}:" SKILL.md; then
echo "::error file=SKILL.md::Missing required field: ${field}"
exit 1
fi
done
echo "SKILL.md frontmatter: required fields present."
- name: Verify agent files have no frontmatter
run: |
set -e
for f in agents/*.md; do
if head -n 1 "$f" | grep -q "^---$"; then
echo "::error file=$f::Agent files are prompt templates and must not have YAML frontmatter (see README architecture note)."
exit 1
fi
done
echo "agents/: no frontmatter, as expected."
14 changes: 14 additions & 0 deletions .markdownlint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"default": true,
"MD013": false,
"MD022": false,
"MD024": { "siblings_only": true },
"MD028": false,
"MD031": false,
"MD032": false,
"MD033": { "allowed_elements": ["br", "details", "summary", "sub", "sup"] },
"MD040": false,
"MD041": false,
"MD046": { "style": "fenced" },
"MD060": false
}
47 changes: 47 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Changelog

All notable changes to `deep-recon` are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- `--pdfs` flag for Explorer PDF collection — Explorer downloads relevant PDFs to `<output_dir>/PDFs/` during web search.
- `--plain` flag — Synthesizer produces CommonMark-only output (no `[[wikilinks]]`, no `> [!callouts]`) for non-Obsidian environments.
- Per-agent model overrides: `--explorer-model`, `--associator-model`, `--critic-model`, `--synthesizer-model`. Each agent's Task dispatch now passes the resolved model parameter; defaults are tabulated in SKILL.md's "Agent Model Selection" section.
- `--budget <tokens>` flag — hard cap on total token spend. The orchestrator reads `_metrics.md` between rounds and aborts gracefully (writing the best-available draft) before exceeding the cap.
- `argument-hint` field in SKILL.md frontmatter — surfaces the skill's flag set at invocation time.
- `examples/` directory with two illustrative recon outputs (Explore mode, Focus mode) plus a README explaining their illustrative status.
- `docs/TUNING.md` — guidance for forkers on customizing the Synthesizer's voice. Documents the hardcoded `_resources/Kazys Varnelis – Personal Writing Style Guide.md` reference and three remediation paths (replace, remove, parameterize).
- `CONTRIBUTING.md` — PR norms, prompt-edit conventions, local testing, style expectations.
- `tests/check_recon_structure.py` and `tests/run_smoke_tests.sh` — structural validator for recon output documents (frontmatter fields, required sections, Territory cardinality, Obsidian/plain flavoring), with self-test against the example files.
- `tests/test_validator_contract.py` — negative tests for the validator itself: feeds it 7 malformed stubs (missing section, framings under/over cardinality, missing frontmatter field, plain-mode wikilinks, plain-mode callouts, Obsidian mode with no wikilinks) and asserts the expected errors fire. Catches regressions that would silently weaken the validator. Wired into `run_smoke_tests.sh`.
- `.github/workflows/lint.yml` — CI runs markdownlint-cli2 plus a SKILL.md frontmatter check and a "no frontmatter in agent files" check.
- README **Troubleshooting** section covering missing-document recovery, determinism expectations, partial-round failure handling, generic-output fixes, metrics after compaction, and cost controls.
- README **Documentation** section pointing to CHANGELOG, TUNING, and examples.

### Changed
- Orchestrator now handles partial-round failures gracefully — see the new **Failure Handling** section in SKILL.md. One agent failing no longer aborts the round; Synthesizer write failures retry once then fall back to orchestrator-written stub; `_metrics.md` failures degrade non-fatally.
- README architecture section updated to be Claude-Code-version-agnostic (the prior "experimental in Claude Code 4.6" reference is replaced with a forward-compatible framing).
- README modes table includes `--plain`.

## [1.0.0] — 2026-02-19

Initial public release.

### Added
- Four-agent recon workflow: Explorer (divergent), Associator (lateral), Critic (adversarial), Synthesizer (integrative).
- 2–3 round parallel dispatch via Claude Code's Task tool, with orchestrator cross-pollination between rounds.
- Interactive mode (Socratic — checks in between rounds) and Autonomous mode (end-to-end run).
- Explore intention (divergent — opens possibility space, ends with open questions) and Focus intention (convergent — narrows to a thesis, ends with action plan).
- `--vault-only` flag — skip web search, vault content only.
- `--output <path>` flag — explicit output directory override.
- Anti-hallucination guardrails in the Synthesizer: epistemic honesty rules, observation-vs-invention discipline, ground-every-claim requirement.
- Disk-persisted Synthesizer write — the final document is written directly to its output path by the Synthesizer agent, surviving orchestrator crashes.
- Metrics tracking — per-agent token counts and elapsed times persisted to `_metrics.md` after each round, surviving context compaction.
- Architecture rationale: subagents over agent teams, with the orchestrator as deliberate interpretive layer.
- Obsidian-native output formatting — `[[wikilinks]]`, `> [!callout]` blocks, footnotes, YAML frontmatter, Process Log.

[Unreleased]: https://github.com/kvarnelis/deep-recon/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/kvarnelis/deep-recon/releases/tag/v1.0.0
71 changes: 71 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Contributing to deep-recon

This is a small skill — five markdown files, one orchestrator prompt, four agent prompts, one output template. Changes ripple through the whole behavior of the skill quickly, so the bar for PRs is higher than the surface area suggests.

## Before you start

Different kinds of changes have different paths:

- **Bug in the orchestrator or an agent prompt** (the skill misbehaves on a defined input): open an issue with a minimal reproduction, then PR the fix. Small fixes can skip the issue.
- **Feature addition** (new flag, new mode, new agent): open an issue first. Most feature additions break the existing prompt economy in non-obvious ways, and a 5-line discussion ahead of time is cheaper than a 200-line revert.
- **Voice or personal-style change** to the Synthesizer's prompt: don't PR these upstream — fork. The Synthesizer's voice is calibrated for one specific user, and the right way to use it for someone else is to retune in your fork. See [`docs/TUNING.md`](docs/TUNING.md).
- **Architectural change** (subagents → agent teams, structured handoff, persistence layer): open an issue and discuss the design before any code. These are the kinds of changes that need consensus, not review.
- **Documentation, examples, troubleshooting**: PR directly. Light review, fast merge.

## Prompt-edit conventions

The agent prompt files (`agents/*.md`) and `SKILL.md` are the load-bearing parts of this repo. Edits here change recon behavior in ways that are hard to predict from a diff.

**Atomic changes.** One prompt-section change per PR. If you're rewriting the Synthesizer's Voice section *and* the Critic's Strongest Objections format, that's two PRs.

**Justify additions.** New paragraphs in agent prompts add tokens and instruction-load. If you're adding a section, the PR description should explain what failure mode it addresses and why a smaller change wouldn't fix it.

**Show before/after.** If your change is non-trivial, run a recon on a topic both before and after the edit and quote a paragraph from each in the PR description. If the after-version isn't clearly better, the change probably isn't ready.

**Never edit `SKILL.md` and an agent file in the same PR** unless the edits are coupled by design. The orchestrator's responsibilities and an agent's responsibilities should be reviewed independently.

**Don't loosen anti-hallucination guardrails.** The Synthesizer's Epistemic Honesty section and the Critic's "WITHOUT rebuttal" rule are load-bearing for output quality. PRs that weaken these rules need an exceptional justification.

## Testing changes locally

Before submitting a PR:

1. **Run a recon** on a topic you've used the skill on before. Read the output against the previous version. Look for register drift, structural regressions, missing sections, off-tone prose.
2. **Run the structural test script** (when goldens land — see `tests/golden/`). A passing script means the output skeleton is intact. It does not mean the prose is good.
3. **Run `markdownlint-cli2` locally** if you can: `npx markdownlint-cli2 "**/*.md"`. The CI runs this on every push.
4. **Verify frontmatter**: `SKILL.md` must keep `name`, `description`, `allowed-tools` fields. Agent files must NOT have YAML frontmatter (see the README's "A note on the agents/ directory").

If you can't run the skill locally (no Claude Code access, no Obsidian vault), say so in the PR description. We'll smoke-test for you before merging.

## Style

Match the existing repo voice. The README and SKILL.md are deliberately opinionated and direct. PRs that introduce hedging, generic consultancy-speak, or LLM tells ("delve," "leverage," "robust," "comprehensive") will get bounced.

**Concrete.** Prefer "the orchestrator reads `recon/rN-explorer.md` from disk after each round" over "the orchestrator processes agent outputs."

**Declarative.** Prefer "this is the right call until agent teams reach a stable form" over "one might consider exploring agent teams when they become more stable."

**Cite specifics.** When recommending a change, point to a file and line. PR descriptions and commit messages benefit from the same.

## Commit messages

Match the repo's existing pattern: imperative subject line, no conventional-commit prefix, optional body explaining the *why*. Examples:

- `Add --plain output flag for non-Obsidian forks`
- `Fix Synthesizer voice-guide read on missing path`
- `Document architecture: subagents over agent teams`

If the change is part of a larger plan, append the plan's item ID in parens: `Add Troubleshooting section (T1.1)`.

## Issue templates

We don't have formal issue templates yet. A useful issue includes:

- **What you tried** — the exact `/deep-recon` invocation
- **What you expected** — the output structure or content you anticipated
- **What happened** — the actual output, or the missing output
- **Vault context** — rough size, whether the topic had relevant existing notes, mode flags used

## License

By contributing, you agree your contributions are released under the same MIT license as the rest of the repo.
75 changes: 69 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,21 @@ rounds.

This is deliberate. The orchestrator's role between rounds — digesting the Synthesizer's
analysis, compiling settled claims, crafting tailored prompts for each agent — is an
interpretive step that shapes the next round's quality. Agent teams (experimental in Claude
Code 4.6) offer direct inter-agent messaging, but at the cost of deterministic control
over round structure and dispatch.
interpretive step that shapes the next round's quality. Direct inter-agent messaging
(via agent teams in newer Claude Code releases) erases that interpretive layer.

When agent teams exit experimental status, a hybrid approach — orchestrator-controlled
rounds with inter-agent dialogue within each round — could improve the Critic↔Explorer
and Synthesizer→all-agents communication flows.
If agent teams reach a stable form with deterministic dispatch guarantees, a hybrid is
plausible: orchestrator-controlled rounds with inter-agent dialogue *within* each round —
especially Critic↔Explorer for real-time stress-testing, and Synthesizer→all-agents for
mid-round redirect. Until then, the subagent + orchestrator pattern is the right call.

### A note on the `agents/` directory

The four files in `agents/` are not Claude Code agent definitions (no frontmatter,
no `subagent_type`). They are **prompt templates** — read at runtime by the orchestrator
(`SKILL.md`) and inserted into `Task` calls dispatched as `subagent_type:
"general-purpose"`. This keeps the prompts version-controllable without coupling them to
Claude Code's agent-definition spec, which has been moving fast.

## Modes

Expand All @@ -54,6 +62,7 @@ and Synthesizer→all-agents communication flows.
| `--focus` | Focus | Convergent — narrows to one argument, ends with action plan |
| `--vault-only` | Vault-only | Skips web search, uses only vault content |
| `--pdfs` | PDF collection | Explorer downloads relevant PDFs to `<output_dir>/PDFs/` |
| `--plain` | Plain markdown | Output is CommonMark-only — no `[[wikilinks]]`, no `> [!callouts]`. Use for non-Obsidian environments. |

## Installation

Expand Down Expand Up @@ -121,6 +130,60 @@ The skill produces an Obsidian-native markdown document saved to a `recon/` subd

Individual agent reports are saved alongside as reference material.

## Troubleshooting

### "I don't see my recon document"

The Synthesizer writes the final document directly to disk — it should appear at `<output_dir>/YYYY-MM-DD-<topic-slug>.md`. If it's missing:

- Check the per-agent reports in the same folder (`r1-explorer.md`, `r1-critic.md`, etc.). If those exist but the final doc doesn't, the Synthesizer write failed mid-flight — re-run the skill with the same topic. Agent reports survive across attempts.
- If the agent reports also don't exist, the orchestrator failed before any agent dispatched. Confirm `--output` resolves to a writable directory and that the vault root is in scope.

### "Two runs of the same topic produce different outputs"

This is by design. Web search results vary day-to-day, vault state evolves, and model sampling is non-deterministic. The skill is built for divergent exploration, not reproducibility. If you need repeatable runs, use `--vault-only` and treat the agent reports (`rN-*.md`) as the canonical record — those at least come from a fixed input set.

### "An agent timed out, or one round is missing a report"

The orchestrator handles partial-round failures gracefully — see the **Failure Handling** section in `SKILL.md`. The summary:

- **One agent fails:** the round proceeds with N–1 reports. The Process Log notes the failure.
- **All agents fail in a round:** the orchestrator skips to the final Synthesizer with whatever earlier rounds produced.
- **Synthesizer's final write fails:** the orchestrator retries, and on a second failure writes a stub document pointing the user at the per-agent reports on disk.

Practical recovery as a user:

- Inspect the recon directory for whatever reports landed.
- Read the Process Log in the final document — it tells you which rounds and agents failed.
- Re-run the skill with the same topic if you want a fresh attempt; the orchestrator overwrites per-round files.
- If web search is the consistent failure, add `--vault-only`.

### "The output sounds generic, not like my voice"

The Synthesizer reads existing notes in your vault to match register. If output feels generic:

- Make sure your vault has notes the Synthesizer can find via Grep on the topic's terms.
- If you forked this skill, see [`docs/TUNING.md`](docs/TUNING.md) — the Synthesizer references a personal style guide by default and will need adjustment for your voice.

### "The Process Log shows wrong token counts after a context compaction"

The Process Log reads from `_metrics.md`, which is updated after every round expressly to survive context compaction. If the numbers in the final document look off, check `_metrics.md` directly — it's the source of truth. The orchestrator recovers from compaction by re-reading this file.

### "How much does a typical run cost?"

Token spend depends on vault size, web search depth, and whether you run 2 or 3 rounds. Per-round cost data is recorded in `_metrics.md` — review it after a few runs to calibrate. The Synthesizer (Opus) is the highest single-agent contributor in most runs.

If cost matters, you have two levers:

- **Hard cap:** `--budget <tokens>` — the orchestrator aborts gracefully (writing the best-available draft) before exceeding the cap. See SKILL.md's "Budget Check" section.
- **Cheaper Explorer:** `--explorer-model haiku` is the safest single substitution for cost-sensitive runs. The Synthesizer should remain on Opus — Haiku-on-Synthesizer significantly degrades final-document quality. See SKILL.md's "Agent Model Selection" section for full guidance.

## Documentation

- [`CHANGELOG.md`](CHANGELOG.md) — version history.
- [`docs/TUNING.md`](docs/TUNING.md) — adjusting the Synthesizer's voice for your fork.
- [`examples/`](examples/) — sample recon outputs.

## License

MIT
Loading