Zum Inhalt springen

Creating and accepting your first requirement

Create a requirement, bind it to code, check it, and accept it, in a few minutes.

A typical case: your client has been promised that users can reset their own password. Until now, this sentence lives in a set of minutes. After this, it lives in the repository, is bound to the code, and is checked on every pull request.

What this is about and when it pays off is described under Requirements at a glance.

1. Create the file

The corpus lives under .codecharter/req in the repo root. Create the directory:

mkdir -p .codecharter/req

Inside it, create REQ-12.req.md. The file name follows the id, otherwise the check reports a warning:

---
id: REQ-12
title: Users can reset their password themselves
priority: must
contractual: true
status: in-progress
kind: functional
section: Account management
source: Workshop notes 2026-08-14, section 3.2
---
A user who has forgotten their password requests a reset link from the
sign-in page and sets a new password through it. The link is single-use and
expires after 30 minutes.

## Out of Scope

Signing in through an external identity provider is not covered by this
requirement.

The six keys id, title, priority, contractual, status, and kind are required, everything else is optional. The body is the text a reviewer or an auditor reads later. Write full sentences there, not bullet points.

2. Check the corpus

codecharter req check

The command does not open a solution and does not need the .NET SDK, which is why it is fast enough for every pull request. On the first run you get a warning, because the requirement does not yet have an anchor. That is expected: at status: in-progress, a missing anchor is a warning; at status: implemented it would be an error.

3. Bind the requirement to code

Once the code exists, an anchor names the place that fulfills the promise. Extend the frontmatter:

anchors:
  - symbol_id: MyApp.Auth.PasswordResetService

Alternatively, mark the implementing place directly in the code. For that you need the attribute package:

dotnet add package CodeCharter.Annotations
[Satisfies("REQ-12")]
public async Task SendResetLinkAsync(string email, CancellationToken ct)

Both paths bind the requirement to code. The attribute path has the advantage that the binding is visible in the diff and feeds into requirements coverage.

4. Make the promise checkable

A requirement without an acceptance check is a statement of intent. Add a criterion with exactly one carrier, here a named test:

criteria:
  - id: AC-1
    test: MyApp.Tests.Auth.PasswordReset_SendsSingleUseLink

After that you can set the status to implemented. Now also check against the code:

codecharter req check --with-code

This run analyzes the solution and reports, among other things, when an anchored symbol does not exist or the bound test cannot be found.

5. Accept

Acceptance is a human act and deliberately not an MCP tool, so that no AI agent approves its own work:

codecharter req accept REQ-12 AC-1 --by alice

This writes the approver, the date, and the evidence the approval rests on into the criterion. If the accepted test later changes, the pin no longer matches, and the requirement renders as Stale instead of silently continuing to count as satisfied. Then you deliberately accept it again.

Without --by, the command looks for an identity in CODECHARTER_REQ_APPROVER and then in git config user.name. If it finds none, it aborts instead of guessing.

6. Hook it into CI

codecharter req check returns a CI-friendly exit code and is fast, because it does not rely on a code model:

codecharter req check --output json

codecharter req coverage measures the state across the whole corpus. The command is initially just a report and only becomes a gate once you make it one in .codecharter/config.yml.

Where to go next