Skip to content

T-J2CW-add-epic-entity-task-depends-on-dependencies

Status: closed/done · Impact: medium · Complexity: medium

Give the planning graph two missing primitives: an epic container that sits between milestone and task (a bundle-of-tasks the team explicitly named), and a depends_on field on tasks for hard “B cannot start until A is closed” relationships. Today both concepts get smuggled into prose or into the loose related field, which is non-directional and lifecycle-blind.

  • Three entity types exist under plugin/entities/: task, milestone, backlog. No grouping primitive between milestone and task.
  • plugin/entities/task/schema.json has related (loose, non-directional wikilinks) and tags (free-form). No directional dependency field; “B is blocked by A” is currently encoded informally in prose or in related.
  • plugin/entities/milestone/schema.json has tasks: “ — a downward-containment array that accepts only task wikilinks.
  • plugin/skills/entities-audit/ and plugin/skills/entities-migrate/ already walk every entity file for schema-version validation. There is no cross-file relational check yet (e.g. cycle detection).
  • A new epic entity type exists at plugin/entities/epic/ with schema.json, template.md, and a minimal writing.md. Epic id shape: E<NNNN> zero-padded, immutable, filename E<NNNN>.md. Frontmatter mirrors milestone shape (status enum open/draft|planned|active, closed/done|partial|superseded|abandoned; title, created, last_reviewed, target_date, tags, related, relevance_note, completion_note). closed/done requires completion_note (same pattern milestone uses).
  • Epic carries a downward tasks: “ array. Epic-in-epic nesting is explicitly disallowed for v1 — the pattern accepts task wikilinks only.
  • milestone.tasks pattern is relaxed to accept either a task wikilink (YYYY-MM-DD-slug) or an epic wikilink (E<NNNN>). Single mixed-member field — no separate milestone.epics.
  • task.depends_on is added as a strict-wikilink array accepting task slugs or epic ids. Semantic: hard dependency — the target must reach a closed/ state before this task starts; if parallel work is required, the dependent task branches off the dependency’s branch. related stays as the soft, non-directional escape hatch and its description is tightened to make that contrast explicit.
  • A plugin/scripts/new_epic.py mirrors new_task.py / new_milestone.py: assigns the next E<NNNN> id, stamps frontmatter, writes the template.
  • /sdlc:entities-audit gains a cross-file cycle check on the depends_on graph. Cycle prevention lives in Python (JSON Schema is single-document and cannot see across files). Audit reports any cycle as drift; resolution is manual (edit the offending file).
  • /sdlc:setup creates docs/planning/epics/ alongside the existing per-type directories.
  1. Add plugin/entities/epic/schema.json modeled on milestone’s schema (version: 1, schema_version optional with 0 sentinel for closed, immutable id: ^E\d{4}$, milestone-style status enum, tasks array restricted to task wikilinks only, completion_note required when status starts with closed/).
  2. Add plugin/entities/epic/template.md with frontmatter scaffold and body sections (Goal, Today, Proposed, Member tasks header, Acceptance criteria, Out of scope, Completion note placeholder).
  3. Add plugin/entities/epic/writing.md — short stub explaining when to spawn an epic (multiple related tasks need a shared deliverable boundary) vs. when to leave tasks bare under a milestone.
  4. Update plugin/entities/milestone/schema.json: relax the tasks array item pattern to ^\[\[(E\d{4}|\d{4}-\d{2}-\d{2}-[a-z0-9]+(?:-[a-z0-9]+)*)\]\]$. Update the field description to call out mixed members.
  5. Update plugin/entities/task/schema.json: add depends_on array using the same task-or-epic wikilink pattern, uniqueItems: true. Tighten the related description to emphasize “loose / non-directional — use depends_on for hard blocks.”
  6. Add plugin/scripts/new_epic.py mirroring new_task.py / new_milestone.py. Id assignment: scan docs/planning/epics/E*.md, take max + 1, zero-pad to four digits.
  7. Extend /sdlc:entities-audit (its Python helper) with a cycle check that builds the depends_on adjacency from every task file, runs DFS, and reports each cycle as drift (with the offending file list). Hook into the existing audit-drift reporting path; no new top-level command.
  8. Update /sdlc:setup (its helper script) to create docs/planning/epics/ idempotently alongside the other per-type directories.
  9. Add fixtures under plugin/skills/entities-audit/tests/fixtures/: cycle-of-two/ (A→B, B→A), cycle-of-three/ (A→B→C→A), linear-chain/ (A→B→C, no cycle). Wire into the audit test runner.
  10. Update any plugin docs that enumerate entity types (most likely /sdlc:setup SKILL.md and any top-level README in plugin/entities/).
  • plugin/entities/epic/schema.json (new) — full epic frontmatter schema, milestone-shaped.
  • plugin/entities/epic/template.md (new) — body skeleton + frontmatter scaffold.
  • plugin/entities/epic/writing.md (new) — short note on epic-vs-bare-task-bundle.
  • plugin/entities/milestone/schema.json — relax tasks items pattern to accept epic wikilinks; update description.
  • plugin/entities/task/schema.json — add depends_on array; tighten related description.
  • plugin/scripts/new_epic.py (new) — id-assigning creation script mirroring new_task.py.
  • plugin/skills/entities-audit/ (audit helper) — add cross-file depends_on cycle check; surface as drift.
  • plugin/skills/entities-audit/tests/fixtures/cycle-of-two/ (new) — fixture: A↔B.
  • plugin/skills/entities-audit/tests/fixtures/cycle-of-three/ (new) — fixture: A→B→C→A.
  • plugin/skills/entities-audit/tests/fixtures/linear-chain/ (new) — fixture: A→B→C (no cycle).
  • plugin/skills/setup/SKILL.md and/or its helper — create docs/planning/epics/ directory on setup.
  • AC-1: plugin/entities/epic/schema.json exists, declares version: 1, and validates an epic frontmatter with the canonical fields (id, status, title, created, tasks=[]).
  • AC-2: new_epic.py creates docs/planning/epics/E0001.md (next-id semantics) when run with a headline; the resulting file validates via validate_frontmatter.py.
  • AC-3: A milestone frontmatter with tasks: ['[T-FQCN-self-driving-orchestrator-loop](/planning/tasks/self-driving-orchestrator-loop/)', '2026-05-19-foo'] validates without error; a malformed wikilink fails with a useful message.
  • AC-4: A task frontmatter with depends_on: ['2026-05-01-other'] validates; depends_on: ['[T-FQCN-self-driving-orchestrator-loop](/planning/tasks/self-driving-orchestrator-loop/)'] also validates; depends_on: ['not-a-wikilink'] fails.
  • AC-5: /sdlc:entities-audit flags a cycle when two tasks depends_on each other (fixture cycle-of-two) and three-cycle (cycle-of-three); reports clean for the linear-chain fixture.
  • AC-6: /sdlc:entities-audit reports no false positives on the live corpus under docs/planning/ (no task currently has depends_on, so this is trivially true today — but the audit must not crash on absent field).
  • AC-7: Running /sdlc:setup on a fresh project creates docs/planning/epics/ alongside tasks/, milestones/, backlogs/. Idempotent.
  • Epic-in-epic nesting (deliberately disallowed for v1; relaxing the pattern is a future task once a real need surfaces).
  • Task-in-task containment / “task is an epic if it has children” (rejected during design — conditional schema rules + tool-side branching outweighed the elegance).
  • /sdlc:task-work integration of depends_on (warn when blockers are still open) — separate follow-up task; this one ships the schema field only.
  • /sdlc:task-review surfacing of newly-unblocked tasks — separate follow-up.
  • A reciprocal blocks field on tasks (intentionally one-direction; derive blocks by graph scan if/when needed).
  • Retrofitting existing tasks or milestones to use the new fields. New work uses them; existing work stays as-is.
  • A new /sdlc:epic-new / /sdlc:epic-work skill family. Scripts + schemas first; skills follow.
  • none

Design conversation on 2026-05-19 about extending the planning graph for dependency tracking and grouping (originally proposed as “task contains task” with an epic label). The unified-task model was rejected because it pushes conditional rules into every consumer (status semantics, implementation-ready contract, completion_note shape, tool-side leaf-vs-container branching). Decision: epic as a separate entity mirroring milestone’s container pattern, milestone.tasks accepts mixed members, task.depends_on for hard one-direction dependencies, cycle prevention in Python (JSON Schema is single-document by design). See conversation thread.

Captured by /sdlc:task-work on 2026-05-19. PR: pending.

  • AC-1: auto — epic schema present at plugin/entities/epic/schema.json, declares version: 1, validates synthetic epics via validate_frontmatter.py.
  • AC-2: agent-manual — sub-agent created E0001 via new_epic.py, validated via validate_frontmatter.py, deleted the smoke artifact before commit.
  • AC-3: agent-manual — sub-agent validated a synthetic milestone with tasks: ['[T-FQCN-self-driving-orchestrator-loop](/planning/tasks/self-driving-orchestrator-loop/)', '2026-05-19-foo']; malformed wikilink correctly rejected with pattern error.
  • AC-4: agent-manual — sub-agent validated tasks carrying depends_on: 2026-05-01-other“ and depends_on: [T-FQCN-self-driving-orchestrator-loop](/planning/tasks/self-driving-orchestrator-loop/); non-wikilink string correctly rejected.
  • AC-5: auto — plugin/skills/entities-audit/tests/run_evals.py exercises cycle-of-two/, cycle-of-three/ (cycles flagged), and linear-chain/ (clean). 7/7 eval cases passed.
  • AC-6: auto — audit_entities.py over the live worktree corpus reports only pre-existing schema_version drift on M0001.md and 2026-05-17-add-astro-docs-site.md; no false positives from cycle/broken-link checks; tasks without depends_on audited without crash.
  • AC-7: agent-manual — sub-agent verified setup_planning.py on a fresh /tmp project creates docs/planning/epics/ alongside the other entity dirs; second run reports them as existing (idempotent). Setup is schema-discovery-driven, so the directory falls out for free from the schema’s existence.
  • Mirroring milestone/schema.json + new_milestone.py made the epic implementation mechanical — almost line-for-line translation with the id pattern swapped.
  • setup_planning.py’s schema-discovery design meant AC-7 satisfied itself the moment the epic schema landed.
  • Eval-suite-based fixtures (cycle-of-two, cycle-of-three, linear-chain) gave AC-5 a clean automated verification path with no bespoke test harness.
  • Splitting the work into six logical commits on the feature branch kept the diff legible per concern.
  • /sdlc:task-work step 3 sets status: in-progress BEFORE step 5 invokes /sdlc:task-ensure-ready. The implementation-ready contract (plugin/entities/task/implementation-ready.md) explicitly disqualifies in-progress — so strict contract application makes task-work self-defeating (every invocation would downshift to proposed/needs-definition and stop). Applied the pragmatic interpretation here. Either the contract should accept in-progress as past-the-gate, or task-work should defer the status flip until after ensure-ready passes. → T-Z3ZZ-task-work-defers-status-flip-until-ensure-ready
  • Template files (plugin/entities/*/template.md) fail validate_frontmatter.py directly because the HTML comment block precedes the frontmatter and the parser’s regex is anchored to \A---. Pre-existing behavior, not introduced here, but surprised the sub-agent when it tried to validate the new epic template in place. Either relax the regex to skip a leading HTML comment or document templates as non-validatable-in-place. → T-3K8K-templates-html-comment-after-frontmatter
  • audit_entities.py re-parses every task file inside collect_depends_on_graph because FileReport doesn’t carry the parsed frontmatter forward. Acceptable today (corpus is tiny), but worth memoizing if the audit ever grows to thousands of entries. → T-QIBD-audit-memoizes-parsed-frontmatter
  • The skill expects just full-check / just ci for quality checks (/sdlc:task-work step 7). This repo has no justfile — checks here are validate_frontmatter.py, audit_entities.py, and run_evals.py. Worth parameterizing the skill’s check commands per-project or making the skill discover them. → T-T879-task-work-uses-per-project-quality-checks
  • The Bash tool’s prompt-cache appears to occasionally return stale output for similar-looking commands (git branch --list "<pattern>" once returned main, then empty on retry — likely cached from an earlier git branch --show-current call). Could not reproduce reliably; flagging as observed-once-need-eyes.
  • Sub-agent made two off-spec edits that the spec should probably anticipate: (1) new_milestone.py --task flag relaxed to accept epic ids (otherwise the script would reject legitimate inputs once milestone.tasks accepted epics), and (2) setup/SKILL.md description updated to list the actual ships-today entities (backlog/epic/milestone/task) rather than the outdated “today: task” claim. Both reasonable; future tasks that change a schema’s contained-types should explicitly call out script-flag updates as in-scope. → T-KLIL-task-new-flags-cascading-schema-edits

← Back to Tasks