DESIGN.md@docs · git:20260928.d3d3d0b · 2026-09-28 · sha256 1b1bf81efd942142
DESIGN.md@docs git:20260928.d3d3d0bA
Immutable. This exact content never changes and is served at /api/v1/blob/1b1bf81efd942142.
# Design Decisions Why this repository is laid out the way it is. If you're about to refactor something to "modernize" or "simplify" it, read the relevant section first — most of these choices have a reason that isn't obvious from the files, and reversing them breaks either discovery, the ship boundary, or the methodology itself. This document is for AI agents and human contributors who need the *why*, not just the *what*. The conventions themselves are in [`AGENTS.md`](../AGENTS.md). The behavior is in [`skills/deepworkplan/SKILL.md`](../skills/deepworkplan/SKILL.md) and the normative standard under [`skills/deepworkplan/spec/`](../skills/deepworkplan/spec/README.md). --- ## 1. Router meta-skill + sub-skills (not one big SKILL.md) We ship **one discoverable skill** (`deepworkplan`) that routes to six internal sub-skills (`onboard`, `create`, `execute`, `refine`, `resume`, `status`). An alternative would have been a single multi-thousand-line `SKILL.md` covering everything. We chose the router approach because: - **Description-based discovery works better with focused descriptions.** When an agent decides "should I activate this skill?", it reads the `description` field. A focused description like *"Resume an interrupted plan from its recorded progress"* triggers more accurately than a generic *"Do anything with plans"*. - **Different invocation policies per capability.** Each sub-skill is its own `/deepworkplan-*` slash command with its own frontmatter, so users can invoke exactly the verb they want. - **Independent symlinks via `setup.sh`.** Users get `~/.claude/skills/deepworkplan-create` etc. as standalone commands. This requires each sub-skill to live in its own folder. - **Easier to evolve one capability without touching others.** A change to `execute/SKILL.md` doesn't ripple into `onboard/`. Trade-off: more files. We accept that — for a public skill people will audit, focused files beat one giant one. ## 2. Everything under `skills/deepworkplan/` (not `SKILL.md` at repo root) skills.sh discovers skills in two patterns: a `SKILL.md` at the repo root (for single-skill repos), or `skills/<name>/SKILL.md` (for single or multi-skill packs). We use the second pattern even though we ship one logical skill because: - It lets us keep dev infrastructure at the repo root (`AGENTS.md`, `tests/`, `scripts/`, `docs/`, `.github/`) without ambiguity. With the root pattern, every root file becomes part of the "skill" semantically. - The boundary "what ships at runtime" is unambiguous: **anything inside `skills/deepworkplan/` ships, anything outside doesn't.** No `.skillignore`-style exclusion list to maintain. The CI ship-boundary discipline (and `AGENTS.md` rule #2) depend on this holding. ## 3. `shared/`, `spec/`, `guide/`, `examples/`, `addons/` live **inside** the pack `context.sh`, `dwp-paths.md`, `adaptation.md`, the RFC-2119 spec, the methodology guide, the templates, and the addons are all under `skills/deepworkplan/` even though they're "reference material," because: - skills.sh and `setup.sh` install the **pack directory**. If `spec/` or `shared/` were siblings (e.g. `skills/_shared/`), an installed `deepworkplan/SKILL.md` referencing `../shared/context.sh` wouldn't resolve on the user's machine. - Every install must be self-contained. A sub-skill may read `../shared/`, `../spec/`, or `../examples/`, but it must never reach *out of* `skills/deepworkplan/`. ## 4. `setup.sh` exists alongside `npx skills add` There are two install paths: `npx skills add DailybotHQ/deepworkplan-skill` (skills.sh CLI, the canonical cross-agent installer) and `git clone && ./setup.sh` (symlinks the pack and each sub-skill). `setup.sh` is not redundant: - It works without Node.js / npm. Users in minimal containers, or who prefer not to add an npm dependency, get a pure-bash path. - It makes the layout obvious — the script is small and readable and shows exactly which path the skill installs to per agent. - It's the path you use while hacking on the skill locally: clone, edit, re-run setup, and your symlinked install picks up edits live. ## 5. `documentation_url` (not `homepage`) in frontmatter Older Agent Skill examples used `homepage:` for the project URL. We use `documentation_url:` because some agent harnesses interpret `homepage` as "fetch this URL to refresh the skill content" — a remote-load semantics we explicitly don't want. A single DNS/CDN issue could otherwise push modified instructions to every installed user. `documentation_url` is unambiguous: a *reference link*, not a *re-fetch source*. `scripts/validate-frontmatter.py` rejects any new `homepage:` entries. ## 6. Kebab-case `name:` in frontmatter (not snake_case) skills.sh URLs and flags use kebab-case (`deepworkplan-create`), and `setup.sh` symlinks are kebab (`~/.claude/skills/deepworkplan-create`). Snake_case in frontmatter plus kebab in symlinks is an inconsistency that confuses users and breaks discovery for some agents. The validator rejects `deepworkplan_*` names. ## 7. The `.dwp/` output convention (gitignored working state) All Deep Work Plan output lives in a gitignored `.dwp/` directory at the repo root (`.dwp/plans/PLAN_<id>_<slug>/` for new plans; old unnumbered names are preserved), resolved by [`shared/context.sh`](../skills/deepworkplan/shared/context.sh) and overridable via `DWP_DIR`. We chose a single conventional, gitignored directory because: - **Plan artifacts are working state, not committed source.** They're how an agent and a developer coordinate a long-running effort — valuable during the work, noise in the git history afterward. - **One predictable location** lets every sub-skill find prior plans without configuration, and lets onboarding add one line to `.gitignore` instead of scattering output across the tree. - For orchestrator hubs, child plans nest under `repositories/<repo>/.dwp/plans/…` — same convention, applied per managed repo. New plans receive a monotonic ID with a three-digit minimum (`001`, `002`, `100`). The flat tree remains stable while a local `.next-plan-id` file prevents reuse after deletion. Selection by ID and `latest` uses numeric order, not filesystem modification time; unique slugs also resolve. Old unnumbered plans are never renamed because their basename appears in state, contracts, prompts, and evidence pointers. This is a forward-only naming policy, so no plan migration runs on adoption. This is a public contract (see `AGENTS.md` rule #5): changing the `.dwp/` layout or the `DWP_DIR`/`DWP_AGENT_TOOL` env vars is a breaking change. ## 8. Reasoning over copy-paste (common baseline, reasoned remainder) The skill is **markdown-first**: its "code" is instructions an agent reads and then *reasons about*, not templates it blindly stamps out. As [`shared/adaptation.md`](../skills/deepworkplan/shared/adaptation.md) puts it, most of any generated artifact follows a fixed baseline shape, and the remainder is reasoned from the target repo's actual stack, validation commands, and module layout. The split is an observation from an audit of six repositories, not a measured constant, and is not published as a ratio. We designed it this way because a skill that copy-pastes a fixed `AGENTS.md` produces plausible-but-wrong docs for every repo that isn't the one it was templated from. The onboarding presets under [`onboard/presets/`](../skills/deepworkplan/onboard/presets/README.md) are *reasoning starting points*, not files to copy verbatim. ## 9. Two archetypes (individual repo vs orchestrator hub) Onboarding and plan execution branch on two repo archetypes, classified by a heuristic in [`spec/ARCHETYPES.md`](../skills/deepworkplan/spec/ARCHETYPES.md): a normal **individual repo** (the common case) and an **orchestrator hub** that coordinates work across many managed repos. We encode this explicitly rather than pretend one shape fits all because the orchestrator case needs child `.dwp/` nesting, an `ORCHESTRATOR_MANIFEST.md`, and integration-checkpoint tasks that would be dead weight in a single repo. ## 10. A normative RFC-2119 spec, shipped with the skill The standard itself — retained v5 base documents and current v6 extensions under [`spec/`](../skills/deepworkplan/spec/README.md) — ships inside the pack, not just on the website. The skill *implements* a standard, and an agent benefits from reading the MUST/SHOULD/MAY contract directly when it needs to resolve an ambiguity. Keeping the spec versioned alongside the sub-skills also means the standard and its implementation can never silently drift apart. ## 11. Addons are opt-in and non-blocking (one declared exception) The `devcontainer` and `dailybot` addons under [`addons/`](../skills/deepworkplan/addons/README.md) are opt-in, with one declared exception — the AI Diff Reviewer **local** review is part of the 2.3.0 baseline (`spec/ADDONS.md` §6.5); its CI surface stays opt-in. Core DeepWorkPlan has **zero** dependency on either — the `dailybot` addon, for instance, only wires optional progress reporting *if* the developer already has Dailybot and consents, and it must never block plan execution. We keep addons vendor-neutral and deferred so the core methodology stays useful to everyone and the skill never becomes a covert install vector for a vendor's tooling. ## 12. Markdown-first: no network, no CLI, no auth, no consent prompts Unlike skills that call a vendor API, DeepWorkPlan makes **no network calls, ships no CLI, and needs no authentication**. The only executable code is two bash helpers that read local git/filesystem state and emit JSON. This is a deliberate security posture (see [`SECURITY.md`](../SECURITY.md)): the smaller the runtime surface, the easier the skill is to audit and approve. It's also why this repo has no consent/secret-scan flows — there's no outbound action to gate. ## 13. Versioning is automatic (conventional commits → auto-release) `.github/workflows/auto-release.yml` reads the version from the router `SKILL.md`, inspects conventional commits since the last tag, bumps every `SKILL.md` in sync, prepends to `CHANGELOG.md`, tags, and creates a GitHub Release on every merge to `main`. Contributors never hand-edit `version:` fields, `CHANGELOG.md`, or tags. We automate this because a markdown skill spread across a router plus nine sub-skills plus addons is exactly the kind of thing where hand-bumping versions drifts out of sync — and a wrong version is what users see first. ## 14. Lite first, Full by promotion (not a second planning product) Every new request materializes one executable **Lite** plan directory. Lite keeps the durable DWP contract — README, manifest, state, progress, prompts, analysis results, task records, gates and Final Review — while storing bounded task records inline instead of creating a large file per task. It is not a draft and it never runs product work during creation. The create skill recommends Lite or Full from scope, risk, dependencies and expected horizon. `lite` and `full` are explicit overrides; `trust` changes only whether a human reviews the recommendation. A Lite plan can be promoted safely to Full before execution or between tasks, preserving task IDs and evidence. The reverse conversion is deliberately absent: collapsing a detailed plan would make state recovery and audit history ambiguous. This lets DWP structure a small change without making long-horizon rigor optional. ## Vendored agent skills — three dogfood copies under `.agents/skills/` This repo vendors **three** agent skills under `.agents/skills/`, all tracked in git and pinned via [`skills-lock.json`](../skills-lock.json). They give any AI agent that clones this repo the toolchain the DWP methodology recommends — but they are managed differently on purpose: | Vendored skill | Upstream | Release auto-refresh | Purpose in this repo | |----------------|----------|----------------------|----------------------| | `.agents/skills/deepworkplan/` | this repo (`skills/deepworkplan/`) | **No** | Contributor dogfood, kept **byte-identical** to `skills/deepworkplan/` (verified by checksum on every sync). It is excluded from release auto-refresh because that would pull the last published tag instead of this working revision. Sync with `bash scripts/refresh-dogfood-skill.sh`. | | `.agents/skills/dailybot/` | [`DailybotHQ/agent-skill`](https://github.com/DailybotHQ/agent-skill) | **Yes** | Powers Dailybot standup reporting for plan lifecycle events (see the Dailybot addon) | | `.agents/skills/ai-diff-reviewer/` | [`DailybotHQ/ai-diff-reviewer`](https://github.com/DailybotHQ/ai-diff-reviewer) | **Yes** | Powers the local code review (same `prompt.md` used by optional downstream CI integrations) | **Why deepworkplan is excluded.** Blind `npx skills add --force` of this repo's own skill into `.agents/skills/deepworkplan/` would overwrite the dogfood copy. The release workflow still **smoke-tests** that the published tag installs (into a temp directory); it does not commit that install back into the tree. When the shipped pack under `skills/deepworkplan/` changes and the dogfood copy should follow, run `bash scripts/refresh-dogfood-skill.sh`, review, and commit. **How addon refresh works.** [`.github/workflows/auto-release.yml`](../.github/workflows/auto-release.yml) runs on every merge to `main` and, after cutting the release for this repo, resolves the latest published tags of `agent-skill` and `ai-diff-reviewer` (via `gh release view`), compares to the vendored `SKILL.md` `version:`, and if either moved runs: ```bash npx --yes skills add <repo>@<tag> --skill <name> --force -y ``` Both `--yes` (npm's proceed prompt) AND `-y` (the skills CLI's agent-picker prompt) are required in a non-TTY runner — dropping either hangs the workflow indefinitely. After each install the workflow asserts that the vendored `SKILL.md`'s `version:` equals the requested tag; a mismatch fails the release. If any file changes, the workflow commits `chore(release): dogfood vendored <skill> to v<tag> [skip release]` and pushes. The `[skip release]` marker prevents an infinite auto-release loop. **Editing policy.** - **Do not** hand-edit `.agents/skills/dailybot/` or `.agents/skills/ai-diff-reviewer/` — the next release will overwrite those. Contribute upstream, land a release there, then this repo's auto-release picks them up. - **Do** treat `.agents/skills/deepworkplan/` as a generated mirror: refresh it only via `scripts/refresh-dogfood-skill.sh` (or an explicit reviewed edit), never via release dogfood. **Pinned install commands are NOT auto-refreshed — and are CI-gated.** Release dogfood updates the *vendored copy* under `.agents/skills/`. It does **not** rewrite the install commands the shipped pack **teaches** under `skills/deepworkplan/`, because those live in prose (`npx --yes skills add DailybotHQ/ai-diff-reviewer@vX.Y.Z …`). The two drifted apart once — the pack taught `@v2.0.0` while the vendored addon was already `2.0.1`, so every repository onboarded from it installed a stale reviewer. The invariant is now a CI gate: **every exact `ai-diff-reviewer@vX.Y.Z` pin in `skills/deepworkplan/` must equal the `version:` of the vendored `.agents/skills/ai-diff-reviewer/SKILL.md`** (`tests/agents-dogfood.bats`). When a release moves the vendored addon, update the documented pins in the same PR — `grep -rn 'ai-diff-reviewer@v[0-9]' skills/` finds them all. `DailybotHQ/ai-diff-reviewer@v2` (no patch) is a different thing: the **GitHub Action's floating major tag**, deliberately left floating so patch fixes flow automatically. The gate ignores it. Do not pin it to a patch.