Pick by the failure you actually have
Choose a spec framework by naming the failure your team keeps hitting, then take the first framework built to answer it. Ask first whether your specs should live or expire, then four questions in order: the first yes that answer allows wins, and "none" is a real answer. Compare the failure you hit in Exercise 2 with the failure each question names.
Start from the failure, not the feature list
Side by side, the three feature lists blur, as Lessons 1 and 2 showed: each gives you a spec file, a command that moves it forward, and somewhere for it to go after the code ships. Failures do not.
Write one sentence about the last change that went wrong for a reason that was not a bug: the diff sent back for touching the networking layer, the module nobody knew another screen depended on, the ticket three people read three ways. That sentence usually names one of three failures; question four covers one that names none. Answer the live-or-expire question (its section follows question four) first. Its answer overrides the four, not just ties: if your specs expire, a yes to question one does not get you LID; if they live, a yes to question three does not get you BMAD. Then ask these in order and stop at the first yes it allows.
Question one: does your team need to say which requirement a line of code serves?
If the recurring pain is "we changed this and did not know what depended on it", the failure
is traceability, and LID is built for it. Every requirement gets a semantic ID that reads as its
path through the design tree, flat as FEATURE-NNN and one segment longer per level.
LID's format marks each requirement [x] implemented, [ ] a gap, or
[D] deferred. The Change C checkpoint, LK-PERIOD-001 to
LK-PERIOD-022, uses none: every spec is marked (no test yet), so if
you ran the LID track in Exercise 2, check which form your run used. The link into code is a line comment on the topmost function that
owns the behavior:
// @spec LK-PERIOD-007, LK-PERIOD-013
public static func periods(for transactions: [Transaction], cycle: StatementCycle) -> [StatementPeriod]
// @spec LK-PERIOD-007, LK-PERIOD-013
fun periods(transactions: List<Transaction>, cycle: StatementCycle): List<StatementPeriod>
Search for the ID and you reach the requirement and the code that serves it. The price is daily: someone writes the annotation on every change, and on the build machine the two LID plugins at commit 831c1959 added about 1,090 tokens to every session before anything was typed. If your team would not write the annotations, answer no.
Question two: is the codebase already large, already shipping, and changing in small increments?
If every spec attempt turns into a document about the whole system, the failure is scope,
and OpenSpec's unit answers it: a change folder whose delta spec says only what the change adds, modifies, or
removes, under ## ADDED Requirements, ## MODIFIED Requirements, and
## REMOVED Requirements. Archiving applies the delta to the live specs and moves the
folder into changes/archive/ under a date-prefixed name; Exercise 3 runs that path.
A ticket that reworks one screen's pagination
owes nobody a description of the app.
Question three: is what is missing the planning, not the traceability?
If work starts before anyone has decided scope, sequence, or who reviews it, the failure is
planning roles, and BMAD ships that structure: a spec broken into an ordered story set, planned
before anything is built. Adopt the shape, not the command names. BMAD needs uv and
permission to run shell commands; if your environment blocks either, BMAD does not start, and
Lesson 5 covers what survives a locked-down network.
Question four: none of the above
With no yes, two answers are honest. One is OpenSpec for its light footprint: on the build
machine, choosing Claude Code, OpenSpec 1.13.1's init wrote fifteen files where BMAD
6.12.0's installer wrote 235, and each change stays in one folder until you archive it. The other,
if specs expire, is no framework: the Day 1 spec template,
../day-1-from-chat-to-spec/starter/specs/TEMPLATE.md (the starter's, not the
lane's own specs/TEMPLATE.md), in your team's specs/ folder; the
.claude/settings.json from Day 2, committed so it applies to everyone in the project;
and Day 2's run-spec runner skill, shared with the team. Most such teams should
start with a month of no framework, to learn which failure recurs.
The cross-axis: do you want your specs to live or expire?
One preference overrides all four questions: whether a spec
lives on after its change ships or expires with it. If your team will re-read a requirement in
six months, when an auditor asks about a declined attempt on a statement, you need a
living spec tree: OpenSpec's archive-and-fold into openspec/specs/, or LID's design
tree.
If nobody will re-read it, a per-change document is fine and the lightest option that answers your failure wins: BMAD's spec folder when planning is what is missing, OpenSpec or no framework otherwise. Böckeler calls the two positions spec-anchored, a spec kept and evolved beside the code, and spec-first, a spec that serves the change in front of it.
Write the decision down where it survives you
Whatever you pick, put the decision in a committed file: the date, the failure it was chosen to fix, and the signal Lessons 1 and 2 asked you to pick. Exercise 3's comparison card is this file.
A choice held only in someone's head gets relitigated by the next person who reads a blog post. A useful norm: whoever proposes switching frameworks brings that signal, counted.
When the tree gives the wrong answer
The tree assumes you already run Days 1 and 2's habits. If your acceptance criteria still
cannot fail a command, every branch is wrong: LID gives each unfalsifiable criterion an ID,
OpenSpec archives it, and BMAD marks it ready-for-dev. The fix is upstream, in
Day 1's criteria; no framework applies it.
The tree also assumes one team. A framework chosen for five people and imposed on fifty is a different decision, which this course does not cover. Take your card to your team, not to your organization.
And for a one-line fix, a throwaway script, or an exploratory spike, skip any framework and any spec: the change is done before a spec would pay for itself.
Quick check
Two engineers on the same team complain about the same module. One says "every spec attempt turns into a document about the whole system." The other says "we changed this and had no idea what depended on it." A delta-shaped spec unit answers one of them. Which, and why does it do nothing for the other?
The first. A delta says only what this change adds, modifies, or removes, so the part of the system that is not changing never has to be re-described. The second is a traceability failure: a delta records what changed, not which requirement a given line of code serves. That one wants semantic IDs on requirements and annotations in the code.
Two teams give the same answers to all four questions but pick differently and both are right. What did they disagree about?
The cross-axis: whether their specs are meant to live or to expire. The team that will re-read its requirements in six months chooses between OpenSpec's archive-and-fold and LID's tree; the team that will not takes the lightest option that answers its failure: BMAD when planning is missing, otherwise OpenSpec or no framework.
Sources
Framework documentation accessed 2026-09-22. Versions pinned for Day 3:
openspec-04, OpenSpec 1.13.1, released 2026-09-17, from
the
OpenSpec releases; lid-obs-01, LID commit 831c1959, still the tip of its main
branch; bmad-obs-01, BMAD 6.12.0, the npm
package's latest tag. Entries marked observed were recorded on the build machine on
2026-09-22 in a scratch copy of the Swift lane.
lid-11— the// @specline comment, placed on the topmost function or module that owns the behavior, from Phase 6 of the LID README.lid-12,lid-13— the[x],[ ]and[D]status markers, and the semantic ID shape as a root-to-leaf path through the design tree, from the LID repository'sAGENTS.md.lid-obs-04— observed: with both LID plugins installed at commit 831c1959, about 531 and 559 always-on tokens added to every session before any command ran, read fromclaude plugin detailsfor each.openspec-13,openspec-14,openspec-15,openspec-20— the delta headers and the date-prefixed folder underchanges/archive/that an archived change moves to, with the delta applied to the live specs, from OpenSpec concepts.openspec-06—openspec/specs/as the folderopenspec initscaffolds for the live specs, from OpenSpec setup.openspec-obs-02— observed:openspec initchoosing Claude Code wrote fifteen files and modified none.bmad-06,bmad-09,bmad-11— BMAD'suvrequirement, from the BMAD changelog; theuvhalt is also in the pinned snapshot'sbmad-buildskill understarter/vendored/bmad-6.12.0/.bmad-12,bmad-13— a spec broken into an ordered story set, and the status values that includeready-for-dev, from BMAD autonomous development loops.bmad-obs-05,bmad-obs-10,bmad-obs-11— observed: a default BMAD 6.12.0 install choosing Claude Code wrote 235 files, a/bmad-buildsession without permission to run shell commands halted before planning, and a/bmad-buildpass on Change C marked its specready-for-dev.practitioners-05— spec-first, spec-anchored and spec-as-source, from Birgitta Böckeler's article on spec-driven development tools (2025-10-15).enterprise-11—.claude/settings.jsonas the committed file shared with everyone in the project, from Settings.