# ADR-007: Planning Layer, Task Hierarchy, and Story-Gated Deployment **Status:** Draft **Date:** 2026-03-19 **Context:** Design discussion exploring the integration of Claudomator with Doot and a richer task hierarchy model. --- ## Context Claudomator currently operates as a flat queue of tasks, each with optional subtasks (`parent_task_id`). There is no concept of grouping tasks into shippable units, no deploy automation, and no integration with personal planning tools. Separately, Doot is a personal dashboard that aggregates tasks, meals, calendar events, and bugs from third-party services (Todoist, Trello, PlanToEat, Google Calendar) into a unified `Atom` model. The goal of this ADR is to capture a design direction that: 1. Integrates Claudomator into Doot as a first-class data source 2. Introduces a four-level task hierarchy (Epic → Story → Task → Subtask) 3. Defines a branching and execution model for stories 4. Establishes stories as the unit that gates deployment --- ## Decision ### 1. Claudomator as an Atom Source in Doot Doot already normalizes heterogeneous data sources into a unified `Atom` model (see `internal/models/atom.go`). Claudomator tasks are a natural peer to Todoist and Trello — they are their own source of truth (SQLite, full execution history) and should be surfaced in Doot's aggregation views without duplication elsewhere. **Design:** - Add `SourceClaudomator AtomSource = "claudomator"` to Doot's atom model - Implement a Claudomator API client in `internal/api/claudomator.go` (analogous to `todoist.go`, `trello.go`) - Map Claudomator tasks to `Atom` with appropriate priority, status, and source icon - Individual subtasks are **not** surfaced in the Doot timeline — they are execution-level details, not planning-level items **Rationale:** Claudomator is a peer to other task sources, not subordinate to them. Users should not need a Todoist card to track agent work — Claudomator is the source of truth for that domain. --- ### 2. Four-Level Task Hierarchy The current flat model (task + optional subtask) is insufficient for feature-scale work. The following hierarchy is adopted: | Level | Name | Description | |---|---|---| | 4 | **Epic** | Large design initiative requiring back-and-forth, resulting in a set of stories. Lives primarily in the planning layer (Doot). Not an execution unit. | | 3 | **Story** | A shippable slice of work. Independent and deployable on its own. Groups tasks that together constitute a releasable change. The unit that gates deployment. | | 2 | **Task** | A feature- or bug-level unit of work. Individually buildable, but may not make sense to ship alone. Belongs to a story. | | 1 | **Subtask** | A discrete, ordered agent action. The actual Claudomator execution unit. Belongs to a task. Performed in sequence. | **Key properties:** - Stories are independently shippable — deployment is gated at this level - Tasks are individually buildable but do not gate deployment alone - Subtasks are the agent execution primitive — what `ContainerRunner` actually runs - Epics are planning artifacts; they live in Doot or a future planning layer, not in Claudomator's execution model - Scheduling prefers picking up subtasks from **already-started stories** before beginning new ones (WIP limiting) **Claudomator data model changes required:** - Add `stories` table with deploy configuration and status - Add `story_id` to tasks (foreign key to stories) - `repository_url` moves from individual tasks to stories (all tasks in a story operate on the same repo) - Story status is derived: all tasks completed → story is shippable --- ### 3. Story-Level Branching Model Each story has a dedicated Git branch. Subtasks execute sequentially, each cloning the repository at the story branch's current HEAD, making commits, and pushing back before the next subtask begins. **Model:** One branch per story. Fresh clone + container per subtask. Subtasks commit to the story branch in sequence. **Properties:** - **Each subtask sees all prior subtask work** — it clones the story branch at HEAD, which includes all previous subtask commits - **Clean environment per subtask** — no filesystem state leaks between subtasks; the container is ephemeral - **Ordered execution enforced** — subtasks run strictly in order; each depends on the previous commit - **Reviewable history** — the story branch accumulates one commit per subtask, giving a clean, auditable record before merge - **Clear recovery points** — if subtask N fails, roll back to subtask N-1's commit, fix the subtask definition, rerun **Tradeoffs accepted:** - Clone and container creation cost is paid per subtask (not amortized across the story). Acceptable at current usage scale. - No parallelism within a story — subtasks are strictly sequential by design - Concurrency lock required at the story level to prevent two subtasks running simultaneously (e.g., on retry races) **Rejected alternatives:** *Isolated commit model (fresh clone per subtask, independent branches):* Clean but subtasks cannot build on each other's work. Requires careful branch ordering and merging to assemble a story. *Persistent workspace per story (one container, one clone for the life of the story):* More efficient, natural continuity, but a bad subtask can corrupt the workspace for subsequent subtasks. Recovery is harder. Loses the discipline of enforced commit points. ### Sequential Subtask Execution Subtasks within a story execute sequentially. This is enforced via `depends_on` links set automatically at task creation time — each subtask added to a story gets `depends_on: [previous_subtask_id]`, forming a linear chain. The existing pool dependency mechanism handles the rest. **Rejected alternative — pool-level story concurrency lock:** Would require the executor to become story-aware, lock state would be in-memory (fragile across restarts), and the ordering would be invisible in the data model. The `depends_on` approach is durable, inspectable, and reuses existing infrastructure. The 5-second polling delay between subtasks is an accepted tradeoff. --- ### 4. Story-Gated Deployment Deployment is triggered at the story level, not the task or subtask level. **Trigger:** All tasks belonging to a story reach `COMPLETED` status → story transitions to `SHIPPABLE` → deploy is triggered. **Deploy configuration:** Stored on the story (e.g., target environment, deploy script/command, repo). This replaces per-task `repository_url` which moves up to the story level. **Failure recovery policy (TBD):** If a subtask fails mid-story, the options are: - Roll back to the previous subtask's commit and retry the failed subtask - Pause the story and require human review before resuming Policy is not decided here — this is flagged for a future decision once the branching model is implemented and failure patterns are observed. --- ## Consequences **Claudomator changes:** - New `stories` table: `id, name, branch_name, repository_url, deploy_config, status` - `tasks.story_id` FK; `repository_url` removed from tasks (inherited from story) - Sequential subtask ordering via auto-wired `depends_on` at task creation time - Post-task-completion check: all story tasks COMPLETED → story transitions to SHIPPABLE → deploy trigger - `ContainerRunner`: clone at story branch HEAD; push back to story branch after each subtask - Deployment status check moves from task level to story level - Remove `Agent.RepositoryURL`, `Agent.ProjectDir` legacy fields, `skip_planning`, `fallbackGitInit()` - Remove duplicate changestats extraction (keep pool-side, remove API server-side) **Doot changes:** - New `SourceClaudomator` atom source - Claudomator API client (`internal/api/claudomator.go`) - Story → Atom mapper (title = story name, description = task progress e.g. "3/5 tasks done", priority from story config, deploy status) - Task → Atom mapper (optional, feature-level visibility) - Individual subtasks explicitly excluded from all views **Doot removals (dead code / superseded):** - `bugs` table, `BugToAtom`, `SourceBug`, `TypeBug` — bug reporting becomes a thin UI shim that submits to Claudomator; nothing stored in Doot's data model - `notes` table and all Obsidian skeleton code — never wired up - `AddMealToPlanner()` stub — never called - `UpdateCard()` stub — never called - All bug handlers, templates, and store methods **Planning layer (future):** - Epics live here, not in Claudomator - Story creation via elaboration + validation flow (see below) - WIP-limiting scheduler that prefers subtasks from started stories ### Story Creation: Elaboration and Validation Flow Story creation is driven by a beefed-up version of Claudomator's existing elaboration and validation pipeline, not a YAML file or form. **Flow:** 1. User describes the story goal (rough, high-level) in the UI 2. Elaboration agent runs against a **local working copy** of the project (read-only mount, no clone) — reads the codebase, understands current state, produces story + task + subtask breakdown with `depends_on` chain wired 3. Validation agent checks the structure: tasks are independently buildable, subtasks properly scoped, story has a clear shippable definition, no dependency cycles 4. User reviews and approves in the UI 5. On approval: story branch created (`git checkout -b story/xxx origin/main`, pushed to remote); subtasks queued **Responsiveness:** - Elaboration uses a local working copy — no clone cost, near-instant container start - A `git fetch` (not pull) at elaboration start updates remote refs without touching the working tree - Branch creation is deferred to approval — elaboration agent is purely read-only - Execution clones use `git clone --reference /local/path ` — reuses local object store, fetches only the delta; significantly faster than cold clone ### Project Registry The local working copy model requires a formal project concept. A `projects` table replaces the current ad-hoc `repository_url` + `working_dir` fields: | Field | Purpose | |---|---| | `id` | UUID | | `name` | Human-readable label | | `remote_url` | Git remote (clone target for execution) | | `local_path` | Local working copy path (read cache for elaboration, object store for `--reference` clones) | `repository_url` on stories becomes a FK to `projects`. The existing `project` string field on tasks (currently just a label) is replaced by `project_id`. `Agent.RepositoryURL`, `Agent.ProjectDir`, and `Task.RepositoryURL` are all removed — project is the single source of truth for repo location. --- ## Out of Scope - Voice interface (noted as a future exploration, not an architectural requirement) - Epic management tooling - Failure recovery policy for mid-story subtask failures - Parallelism within stories