Skip to content
TYPO3Dev Companion

Writing a requirement

What a requirement is, what it stands on and what its states mean is What a requirement is. This page is where an entry goes and how it is written.

Where an entry lives#

One requirement is one file, named after its id, in the group its id names. The group is not a filing preference: it is what the requirement is about, and the prefix carries it, so a file's id decides its path and two entries cannot quietly share a number.

  • audience/ — Who the answer has to be right for
  • discovery/ — Which installation is read, and how
  • answers/ — What a caller may conclude from one
  • documentation/ — What the live manuals answer
  • task-skills/ — What an installed workflow owes the task
  • project/ — The repository the caller is standing in
  • scope/ — Core conventions where they apply, and nowhere else
  • guides/ — What a returned draft is worth
  • feedback/ — What the feedback channel has to stay usable for
  • knowledge/ — What the knowledge base has to cover
  • code/ — What must hold of the source itself

The number is three digits wide, in the file name and in the id alike, because that is what lists a group in the order it was written: unpadded, dis-10 sorts between dis-1 and dis-2 in every directory listing and in anything that compares the ids as text. A decision is numbered the same way, so one habit covers both. bin/cli requirements:check fails on any other width.

Each group's readme.md says what that group is about, and the listing at the foot of it is generated by bin/cli requirements:index, as is the listing at the foot of requirements/readme.md, which carries the whole of it under a heading per group. A listing kept by hand is a second copy of the directory that only says what was true once.

Prose above either listing may not use a third-level heading: that is what the command reads as the start of the generated half, and it would go with the next regeneration.

An id is never reused: a withdrawn requirement takes its number with it, so a number that appears in an old commit, feedback or scenario still means the one thing it always meant. An entry is never deleted because it was implemented; it is deleted only when the requirement itself is withdrawn, and then the reason goes in decisions/.

Where a feedback is the route, the entry is added when it is worked off rather than when it arrives: a feedback nobody has judged yet is a feedback, not a requirement.

What an entry holds#

markdown
---
id: R-DIS-009
status: held
---

# R-DIS-009 — A negative is never remembered

**Nothing that says "there is no installation" is remembered.**

A successful resolution is memoized for the process; a failure is retried on
every call, because the caller who reads that answer is the one likely to
install, migrate or start something and ask again in the same session.

## From

A session lost to a cached negative — the agent ran `composer install`, started
DDEV, verified `bin/typo3` answered, and every tool kept reporting no
installation until the client was restarted (2026-07-29).

## Held by

- `InstanceTest::anInstallationThatAppearsDuringTheSessionIsFound`
  • The bold first sentence is the requirement. Everything under it is why it is one, and a reader who stops after the bold line has read the whole demand.
  • From is the session, review or feedback the demand came out of, with its date. It is evidence, not decoration: it is what tells the next person whether the requirement still describes a real failure.
  • Held by lists the tests that hold it, one per line, or says in as many words that something is not guarded. A whole test class is a legitimate item — VersionsTest in full is a claim about every method in it, and naming them one at a time would go stale on the next one written. A test named there has to exist; a requirement claiming a test that was renamed away is a claim nobody answers for.

What it rests on#

A requirement may name the decisions it stands on in its front matter:

markdown
---
id: R-FBK-007
status: held
restsOn: [D-FBK-005]
---

bin/cli requirements:check fails on an id no decision has. Whether a decision it rests on was later revoked is a reading rather than a failure, and bin/cli unresolved:list is where it is read out — a revoked decision leaves the requirement's test green and the reasoning under it gone, which nothing else would say.

bin/cli requirements:check holds every file to the shape above, and composer test runs the same check through RequirementsTest, except the listing: that one is generated from every file in a group, so it is held by the command alone — D-FBK-011.