| name | gather-requirements |
| description | Conduct a conversational Q&A to capture requirements for a feature or change, then dry-run the draft against prior accepted requirements to detect conflicts before acceptance. Use when the user mentions "new feature", "change request", "requirements", "spec this out", "what do you want", "what are we building", "let's figure out what this needs", "supersede", "replace the old requirement", or is about to start non-trivial work without a requirements doc. Produces a time-ordered, globally-unique, immutable requirement file with a machine-readable deltas block, runs structural and declarative-state conflict checks, and either accepts (folding into .devflow/state.yml) or reports conflicts with resolution paths. |
| license | MIT |
| metadata | {"author":"Kevin Solorio","version":"0.1.0","repo":"ksolo/devflow"} |
gather-requirements — Phase 1
Your job is to turn a fuzzy ask into an accepted, immutable requirement that the rest of
the workflow can rely on. Requirements are treated like database migrations: time-ordered
IDs allocated at draft time, immutable once accepted, supersede-only.
Golden rules
- Ask before assuming. When anything is ambiguous, stop and ask — one or two focused
questions at a time, not a wall of bullets.
- Accepted requirements are immutable. Never edit
status: accepted files in place. Any
change in intent requires a new requirement file with supersedes: [<prior-REQ-id>].
- No code in this phase. Only artifacts: the requirement file and updates to
.devflow/state.yml / .devflow/log.jsonl.
- Conflict before accept. Every draft runs through the dry-run before acceptance. See
references/conflict-detection.md.
The lifecycle
flowchart LR
kickoff["user ask"] --> qa["Q&A loop"]
qa --> draft["draft requirements.md<br/>status: draft"]
draft --> dryrun["dry-run:<br/>Tier 1 + Tier 2 checks"]
dryrun -- "no conflicts" --> accept["status: accepted<br/>fold deltas into state.yml<br/>append to log.jsonl"]
dryrun -- "conflicts found" --> report["conflict report"]
report --> amend["amend draft"]
report --> supersede["supersede prior REQ"]
report --> reject["reject draft"]
amend --> dryrun
supersede --> dryrun
accept --> handoff["hand off to create-plan"]
Every draft must pass dry-run before it can be accepted. Never hand-edit state.yml — it is
deterministically regenerated from the ordered acceptance log.
What to do, step by step
1. Establish the feature slug
Before the Q&A, agree on a short kebab-case slug (url-shortener, password-reset,
rate-limits). This drives the directory path: docs/features/<slug>/.
If a docs/features/<slug>/ already exists with accepted requirements, you are authoring an
amending or superseding requirement. Read the existing files first before asking anything.
2. Run the Q&A loop
Work through the references/question-bank.md categories in
this order, skipping categories that are clearly settled:
- Context — why now, what problem, who's affected.
- Actors — who interacts with this, what roles exist.
- Functional — observable behaviors; stated as "the system shall…".
- Non-functional — performance budgets, security constraints, compliance.
- Edge cases — failure modes, abuse paths, empty/degenerate states.
- Out-of-scope — explicitly what this requirement does not cover.
- Acceptance criteria — testable, one-liner per criterion, will map 1:1 to BDD
scenarios later.
Ask one or two questions at a time. Summarize back after each cluster to check alignment.
Never invent answers; if the engineer doesn't know, record it under Open Questions.
3. Allocate the ID
Generate the ID at draft-creation time from the current UTC timestamp plus a 4-character
random hex suffix:
REQ-YYYYMMDDTHHMMSSZ-xxxx
Example: REQ-20260421T164512Z-a7f3.
This scheme is collision-free across parallel engineer branches — two engineers drafting
requirements on separate branches from main will not clash, because the timestamp (and
random suffix, in the rare same-second case) guarantees uniqueness without coordination. The
ID is set once at draft creation and never changes, even if the draft is edited across
multiple days or rebased.
IDs are global across features, not per-feature — this matters because conflicts cross
feature boundaries.
Acceptance ordering is determined by append order in .devflow/log.jsonl (via accepted_at),
not by the timestamp embedded in the ID. It is fine — and expected — for an older-looking ID
to be accepted after a newer-looking one (drafted Monday, accepted Thursday).
3a. Record the tracker reference (optional)
If the work is tracked in an external system (Jira, GitHub Issues, Linear, etc.), capture the
ticket identifier in the frontmatter tracker: field:
tracker: JIRA-1234
This is the handle humans will actually use in conversation and PRs. Leave the field omitted
(not empty) if there's no external ticket — internal polish, tech debt, ad-hoc work. Ask for
it during the Context category of the Q&A (see
references/question-bank.md).
4. Fill the template
Copy references/requirements-template.md to
docs/features/<slug>/requirements.md. Fill every section. Set status: draft.
The template ends with a machine-readable deltas: YAML block. See
references/state-file.md for the exact schema. This is
load-bearing — the dry-run reads these deltas.
5. Run the dry-run
Follow references/conflict-detection.md to execute:
- Tier 1 (structural): dangling refs, duplicate IDs, touching
locked: true scenarios,
duplicate acceptance-criterion slugs with different outcomes.
- Tier 2 (declarative state): apply the
deltas: block to a copy of .devflow/state.yml
and check for capability/actor/rule consistency + numeric/enum budget contradictions.
If either tier fails, generate a conflict report per
references/supersede-protocol.md and stop. Present
it to the engineer with the three resolution paths.
6. Resolve or accept
- Amend draft — fix the draft, re-run dry-run.
- Supersede prior — add the conflicting prior IDs to
supersedes: (with justification in
Decision Notes of the new draft), re-run dry-run. The prior requirements stay on disk as
history; they are now status: superseded and superseded_by: <new-REQ-id>.
- Reject draft — user decides not to proceed. Keep the draft file with
status: rejected
and a note, or delete it (user's call).
If dry-run passes:
- Flip the draft's frontmatter
status: draft → status: accepted.
- Apply the
deltas: to .devflow/state.yml (see
references/state-file.md). Commit the regenerated file.
- Append a line to
.devflow/log.jsonl:
{"id":"REQ-20260421T164512Z-a7f3","accepted_at":"2026-04-21T17:02:11Z","feature":"url-shortener","tracker":"JIRA-1234","supersedes":[]}
- Update
.devflow/session.yml: phase: create-plan.
- Hand off to the
create-plan skill with a one-line summary.
Handoff format
When done, say exactly:
Requirement REQ-20260421T164512Z-a7f3 (url-shortener, tracker JIRA-1234) accepted.
State file updated (+3 capabilities, 1 budget). No conflicts. Handing off to create-plan
for step breakdown.
If there is no tracker, omit that clause.
Then stop. Do not start planning in this skill — that's Phase 2.
When a superseding requirement is needed
If during the Q&A the engineer says something that contradicts an accepted requirement, do
not silently override it. Say:
"That conflicts with REQ-20260302T091733Z-b1c8 (tracker JIRA-0987, accepted 2026-03-02)
which says X. We can either: (a) keep it as-is and narrow the new requirement to not
conflict, or (b) supersede it with a new requirement that replaces it. Which do you want?"
Prefer referencing the tracker id (JIRA-0987) in conversation; the REQ id is the canonical
machine handle but is awkward to speak.
If (b), the new requirement's frontmatter carries
supersedes: [REQ-20260302T091733Z-b1c8] and its Decision Notes section explains why. The
dry-run will still run and may surface further conflicts (because the superseded REQ may
itself have been depended on by a later REQ).
What this skill does NOT do
- Produce plans, BDD scenarios, or code — those are later phases.
- Edit accepted requirement files.
- Mutate
.devflow/state.yml by hand (always regenerate from the log).
- Skip the dry-run.
References