The intent model

kind × work → TDD posture, planning depth, model profile and verification rigor. A spike is not tested like a payments API.

Uniform rigor is wrong in both directions: it over-tests throwaway spikes and under-tests payment paths. DevFlow’s intent model derives the right posture from two declarations.

The two axes

Project kinds

api app library ui-lib cli plugin

Work types

feature port refactor foundation bugfix prototype spike

kind is declared once, on PROJECT.md frontmatter. It says what sort of software this is.

work is declared per objective, on OBJECTIVE.md frontmatter, or inherited from PROJECT.md’s default_work. It says what sort of change this objective is.

What gets derived

The planner looks up the (kind, work) cell in the defaults table and reads nine fields:

FieldValuesWhat it controls
tddprose postureHow tests relate to implementation
depthquick / standard / comprehensiveHow much planning the objective gets
model_profilequality / balanced / budgetWhich model tier the agents run at
verificationproseWhat the verifier must demonstrate
security_isolationmulti_tenant_required / single_tenant / n/aWhether tenant-isolation assertions are required
back_compatapi_parity, ui_parity, behavioral, …Parity target for ports and refactors
tdd_defaultstrict / auto / skipDefault posture absent a user playbook
test_list_firstrequired / optionalWhether the planner emits a behavior-cases checklist
fixture_strategygenerators / cassettes / inline / n/aTest data approach
outside_inbooleanWhether tests must be ordered E2E → integration → unit

An (api, feature) cell demands strict outside-in TDD, comprehensive planning, the quality profile, and tenant-isolation assertions. An (api, spike) cell asks for none of it.

Resolution chain

Five levels. Highest wins.

  1. TRD frontmatter — explicit override on the job spec (type: tdd, confidence: high)
  2. OBJECTIVE.md overrides blocktdd, depth, model_profile
  3. CLAUDE.md user playbook — the planner reads ~/.claude/CLAUDE.md and ./CLAUDE.md for headings matching ^##.*TDD, ^##.*Test, ^##.*Quality, ^##.*Scope and applies what it finds
  4. The (kind, work) defaults table
  5. Built-in fallback — preserves pre-intent-model planner behaviour

Level 3 is the interesting one: a “TDD & Quality” section in your CLAUDE.md is not decoration. The planner reads it and it outranks the defaults table.

Inspecting a resolution

df-tools intent resolve --objective 4

Every resolved field carries provenance, so you can see exactly which level supplied it. Two parallel maps come back:

provenance — the effective source per field after all overrides:

ValueMeaning
tablethe defaults cell supplied it
user_playbookpromoted by a CLAUDE.md playbook section
objective_overrideset in OBJECTIVE.md
trd_overrideset in TRD frontmatter

cell_provenance — which tier of the table the cell came from:

ValueMeaning
project_table.planning/defaults-table.md
org_table~/.claude/devflow/defaults-table.md
bundled_tablethe plugin’s own reference copy

Read together: if provenance.tdd is trd_override and cell_provenance.tdd is project_table, your TRD won — but had it not, your project’s own table would have supplied the value.

Overriding the table

You do not need to fork the plugin. The loader merges cell-by-cell across three tiers (project beats org beats bundled), so an override file only needs the cells you actually want to change.

# Org-wide, applies to every project on this machine
df-tools defaults-table init --scope=org      # → ~/.claude/devflow/defaults-table.md

# Just this project
df-tools defaults-table init --scope=project  # → .planning/defaults-table.md

Omitted cells fall through to the next tier.

Migrating older projects

Projects created before the intent model have no kind and no work. Run /devflow:status check; if it reports missing intent frontmatter it offers a migration, which always backs up to .planning/.migrate-backup-{timestamp}/ first.