Project templates and stage gates¶
Status: v1 implemented (internal/gates + the stagegate slice: template list|show|add, stage, stage advance; project add --template attaches). Deviations from this spec, recorded rather than smoothed over:
- Manifests are sectioned markdown, not nested YAML (
## stage: <name>+cone:+ predicate bullets). The flat frontmatter dialect is a deliberate parser boundary, and the nestedstages:block below was unimplementable inside it.dacli template show standardprints the real format. project_sections:replaces thedoc:predicate for v1 — gates check project.md's own structural sections (Goal, Out of scope, …) rather than generated satellite documents, because doc generation hasn't landed. Filled-not-present (§ 5.1) is implemented in full: empty, placeholder-bearing (TBD/TODO/{{), too-short, or major-severity-ambiguous content fails the gate with the reason named.- Execution predicates (
shortcut:,lint: clean) are deferred — they run real commands and belong with gate-cost caching (§ 9.1). - Templates attach per project (
project add --template standard); solo is the default by absence. Passing a gate updates the project's conestage:, so estimates genuinely narrow on advance. - The Viewgraph Engineering detector (§ 2) is still owed to
doctor.
A template defines what kind of project this is: which documents must exist, which stages the work passes through, what must be true to leave each stage, which roles staff it, and what "done" means.
1. The idea¶
dacli init --template <name> seeds a workspace with a process, not just empty folders. The template carries:
- Stages with entry and exit conditions — the "controlled steps"
- Required documents, generated from doc templates with placeholders
- A role roster and its runtime routing
- Shortcuts the project needs
- A definition of done that every task inherits
- Optional GitHub Project mapping (GITHUB.md)
The point is that an agent starting work in a templated workspace cannot skip the parts that are load-bearing, because the gate will not open.
2. The overhead warning, first¶
Stage gates are bureaucracy. Bureaucracy is sometimes correct and usually not, and a tool that makes it free to add will accumulate it.
The SPM model-selection rule is explicit: small and simple → Waterfall; large, complex, change-prone → UP or Agile; never default to the most advanced, because the overhead may exceed the value. Two of the group anti-patterns are directly about this failure — Analysis Paralysis (stuck producing requirements) and Viewgraph Engineering** (time spent on documents rather than the thing).
So:
solois the default template: one stage, two required docs, no gates. Most work should use it.- Heavier templates are opt-in and their cost is stated in the template's own description.
dacli doctorflags a project that has spent more agent turns on gate documents than on tasks. That is Viewgraph Engineering, it is now mechanically detectable, and a tool that ships stage gates without that detector is selling the disease with the cure.
3. Template layout¶
.dacli/templates/<name>/
template.md # the manifest
docs/ # document templates with {{placeholders}}
vision.md
architecture.md
roles/ # seeded roles
shortcuts/ # seeded shortcuts
backlog.md # optional seed tasks
Templates resolve from three places, nearest wins: the workspace, ~/.dacli/templates/, then the built-ins. A project can vendor and edit a template without touching the global one.
4. The manifest¶
---
id: tpl-research-paper
kind: template
name: research-paper
process: unified-process # informational: which model this encodes
summary: theory paper with proof obligations and an experiment protocol
cost: "~4 gate documents; use for work intended for external review"
roles: [theorist, experimentalist, reviewer]
definition_of_done:
- Acceptance criteria all checked
- "`dacli run test` passes"
- "`dacli lint` clean"
- A finding or decision note recorded
# The DoD binds at `dacli task done`, which VERIFIES rather than records:
# checkable items re-run, and an unmet one is a refusal (exit 3) naming the
# criterion. A definition of done that nothing enforces is a poster on a wall.
stages:
- name: inception
cone: definition # which Cone-of-Uncertainty stage this maps to
exit:
- doc: vision.md
sections: [Vision, Goal, Out of scope]
- glossary: {min_terms: 5}
- risks: {min_rank1_with_action: all}
- name: elaboration
exit:
- doc: architecture.md
sections: [Approach, Rejected alternatives]
- decisions: {min: 1}
- tasks: {all_have_acceptance: true, all_have_estimate: true}
- name: construction
exit:
- tasks: {priority: must, status: done}
- shortcut: test
- name: transition
exit:
- doc: results.md
- retro: required
---
Why this template exists and when NOT to use it. If a project does not
need external review, use `solo` instead — these four gates cost more
than they return on work nobody else will read.
process: is informational. It records which model the template encodes (Waterfall, V-Model, Spiral, Unified Process, Scrum, XP, Lean, Kanban) so that a human reading it knows the lineage, but dacli does not behave differently based on the name. The behavior lives entirely in the stages.
Stage names are template-local; each stage declares cone: to say which Cone-of-Uncertainty stage (definition | elicitation | approach | design) it corresponds to, and passing the gate updates the project's stage: field, narrowing how every estimate inside it is reported — the honest version of "we know more now." An earlier draft said the stages "map onto" the cone stages and left the mapping implied, which was a bug in this spec: inception is not the string definition, and an implementer would have had to guess. The mapping is now explicit, and cone: is required on every stage.
5. Gate predicates¶
The predicate vocabulary is deliberately small and entirely checkable. Most predicates inspect documents and run no code.
Two predicates do reach outside the markdown: artifact: and command:. This revises the earlier rule that no predicate may execute. That rule's reasoning — a scriptable gate becomes a place people hide logic, and stops being auditable — is sound, but it was answered by the wrong mechanism: with document-only predicates, a stage could be "passed" with a broken build and a failing test suite, because the gates certified the paperwork rather than the software. Auditability is preserved differently: the command is written verbatim in the template manifest, so it is reviewed along with the template instead of hidden in code, and its exit status and failing output appear in dacli stage status like any other check. A gate that cannot say "the tests pass" is not a quality gate.
| Predicate | Checks |
|---|---|
doc: <name> |
The document exists |
doc: <name> + sections: [...] |
Those headings exist and are filled (§ 5.1) |
glossary: {min_terms: N} |
At least N defined terms |
risks: {min_rank1_with_action: all} |
Every rank-1 risk has an action plan |
decisions: {min: N} |
At least N decision notes with Rejected filled |
tasks: {all_have_acceptance: true} |
Every task has at least one acceptance box |
tasks: {all_have_estimate: true} |
Every task has a three-point estimate |
tasks: {priority: must, status: done} |
Every must task is done |
shortcut: <name> |
That shortcut exits zero |
lint: clean |
dacli lint reports nothing above the threshold |
retro: required |
A retro note exists for this stage |
artifact: <path> |
That file or directory exists, relative to the workspace root. \|-separate several. Traversal (../) and absolute paths are rejected. |
command: <shell> |
That command exits 0, run at the workspace root under a 10-minute deadline. On failure the last lines of its output are reported as the reason. |
Shipped predicate kinds in this build: project_sections, glossary, decisions, tasks, risks, retro, artifact, command.
## stage: implementation
cone: construction
- tasks: musts_done
- artifact: go.mod | internal
- command: go build ./... && go test ./...
5.1 Placeholder detection is the whole game¶
The obvious way to defeat a documentation gate is to generate the document and fill it with "TBD". An agent under budget pressure will do this, not from malice but because the gate asked for a heading and a heading is what it produced.
So a sections: check verifies the section is filled, not present:
- Not empty, and not only whitespace
- Not still containing template placeholders (
{{...}},TODO,TBD,FIXME,...) - Above a minimum length
- Not failing the ambiguity linter at major severity — a Vision section reading "make the thing better and handle the edge cases properly" is exactly as empty as "TBD", and the ambiguity scanner already detects that
That last check is where the SPM layer earns its place in the template system: it is the difference between a gate that checks for documents and a gate that checks for content.
6. Document templates¶
---
kind: doc
template_for: vision.md
---
# {{project_title}}
## Vision
{{vision}}
## Goal
{{goal}}
## Out of scope
- {{out_of_scope_1}}
Generated docs land in the project folder as ordinary markdown with frontmatter and wikilinks, so they open in Obsidian as vault notes with no export step. Placeholders that survive generation are what § 5.1 detects.
Doc templates should carry prompts, not just blanks. A section whose placeholder reads {{what was rejected, and why the rejection still holds}} produces better content than one reading {{rejected}}, because the agent filling it is reading the placeholder as an instruction. This is the cheapest quality intervention in the whole template system.
7. Shipped templates¶
| Template | Stages | Use when |
|---|---|---|
solo |
1, no gates | Default. One person or one agent tree, no external review |
standard |
3 | A team, a real backlog, work others depend on |
research-paper |
4 | Output is a paper: proof obligations, experiment protocol, reproducibility |
service |
4 | Deployed software: runbook, rollback plan, on-call notes required |
regulated |
5 | Audit trail matters; every decision needs a recorded rejection |
dacli template list prints their stated costs. dacli template add vendors one into the workspace for editing.
8. Commands¶
| Command | Purpose |
|---|---|
dacli init --template <name> |
Seed a workspace from a template |
dacli template list\|show\|add |
Manage templates |
dacli stage |
Current stage and unmet exit conditions |
dacli stage advance |
Advance if the gate opens; otherwise list what is missing |
dacli stage advance --force |
Override, recording who and why as a decision note |
--force exists because a gate that cannot be overridden gets worked around, and a documented override is better than a fake document. The override is recorded as a decision note with Rejected: waiting for the gate and reasoning, so it lands in every future brief in scope — the next agent learns the gate was skipped and why.
9. Open questions¶
- Gate checks cost tokens. Placeholder and ambiguity checks are cheap (local, no model call), but
shortcut: testruns a suite andlint: cleanmay not be free at scale. Gates evaluated on everydacli stagecall could get expensive; caching against content hashes is the likely answer. - Templates encode opinion, and stale opinion is worse than none. A vendored template drifts from the global one with no signal. Some form of
dacli template diffis needed. - Stage-gated work fights the small-task model. Small, well-scoped tasks want to flow continuously; stages want batch checkpoints. Kanban's continuous flow and UP's phase gates are genuinely different philosophies and this design currently ships both without saying which wins when they conflict.