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
- Requirements: all fields, carrier kinds, and anchor kinds
- Scope and effort: what the corpus does not cover
codecharter req: the full reference for check, accept, and coverage- Requirements over MCP: read the same corpus from an AI coding tool