Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

File formats

codev’s files are Markdown and YAML meant to be written by people and agents, and parsed by codev. This chapter gives the exact rules, with the English keywords. codev also reads their French equivalents (### Exigence :, ## Exigences AJOUTÉES, DOIT…), listed in Artifact language.

Everything inside fenced code blocks and HTML comments is ignored by the parsers, so templates can carry guidance in <!-- … --> comments.

Main spec

Path: _codev/specs/<capability>/spec.md, where <capability> is one or more kebab-case segments (ui/theme). Only a file named exactly spec.md is a main spec: other files under _codev/specs/, such as a README.md, are ignored by codev list --specs and codev validate.

# Theme Specification

## Purpose

Let users choose the color theme of the interface.

## Requirements

### Requirement: Theme follows the system preference by default

The application SHALL render with the operating system's color scheme until the user picks a theme.

#### Scenario: No theme chosen yet

- **GIVEN** a user who has never changed the theme setting
- **WHEN** their operating system is set to dark mode
- **THEN** the application renders with the dark theme
  • ## Purpose is required.
  • Requirements are ### Requirement: <name> headings inside ## Requirements. The name identifies the requirement in deltas, so keep it unique within the spec.
  • A requirement’s description contains a normative keyword in upper case: SHALL or MUST, or DOIT or DOIVENT.
  • Every requirement has at least one #### Scenario: <name>, with exactly four # characters. Scenario steps are bullet lines using **GIVEN**, **WHEN**, **THEN** and **AND**.
  • Other ## sections are allowed and preserved by merges.
  • Delta headings (## ADDED Requirements…) are not allowed in a main spec.

When archiving creates a main spec for a new capability, it titles it from the last segment of the capability path (# Theme Specification) and copies the delta’s ## Purpose.

Spec delta

Path: _codev/changes/<name>/specs/<capability>/spec.md, with the same capability path as the main spec it modifies.

## ADDED Requirements

### Requirement: The chosen theme is remembered

The application SHALL restore the theme the user last picked, on every device they sign in from.

#### Scenario: Returning user

- **GIVEN** a user who picked the dark theme on their laptop
- **WHEN** they sign in on their phone
- **THEN** the application renders with the dark theme

## MODIFIED Requirements

### Requirement: Theme follows the system preference by default

<the complete requirement, scenarios included, as it must read after the change>

## REMOVED Requirements

### Requirement: Legacy color picker

**Reason**: Replaced by the theme setting.
**Migration**: Users' custom colors are dropped; the default theme applies.

## RENAMED Requirements

- FROM: Theme follows the system preference by default
- TO: Theme follows the system preference until the user chooses
SectionContentMerge
## ADDED RequirementsComplete new requirementsAppended at the end of ## Requirements
## MODIFIED RequirementsComplete requirements, with an existing nameEach replaces the requirement of the same name
## REMOVED RequirementsA ### Requirement: heading with **Reason**: and **Migration**: linesThe requirement is deleted
## RENAMED RequirementsFROM: / TO: line pairs, with or without a leading - The heading is retitled; body and scenarios are untouched

In RENAMED, write after FROM: and TO: either the bare requirement name, as above, or the full heading in backticks, as the template does: - FROM: `### Requirement: <old name>`.

Rules checked by validation:

  • A requirement appears in at most one of ADDED, MODIFIED and REMOVED (cross_section_conflict), and at most once per section (duplicate_requirement).
  • Inside a delta section, every ### heading is a ### Requirement: (delta_unexpected_heading); a section with no entry is reported as a warning (delta_section_empty).
  • A RENAMED source must exist in the main spec (rename_source_missing, unless the rename was already applied by an earlier sync); a RENAMED target cannot also be ADDED (rename_target_collision); and MODIFIED uses the new name of a renamed requirement, not the old one (modified_uses_old_name).
  • MODIFIED and REMOVED names must exist in the main spec; this is checked when the delta is merged (modified_target_missing).
  • New capability: start the delta with ## Purpose. For an existing capability, omit it — the main spec’s Purpose is kept.
  • A REMOVED delta that would leave a spec with no requirement is refused (would_leave_spec_without_requirement) unless the change declares retire_capabilities: true; the main spec is then deleted.

proposal.md

The spec-driven template:

# Proposal: <change title>

## Why

## What Changes

## Capabilities

### New Capabilities

### Modified Capabilities

### Removed Capabilities

## Impact

## Domains

## Release note

Each capability listed under New or Modified Capabilities gets its own delta file at specs/<capability>/spec.md. Breaking changes are marked BREAKING in What Changes. A capability listed under Removed Capabilities requires retire_capabilities: true.

Release note

What end users read in the changelog, built by codev changelog. Either one or more items:

## Release note

- Added: `--format json` on `codev export`.
- Changed: **BREAKING** `codev export` writes to stdout by default; pass
  `--output <PATH>` to write a file.

or, for a change users will not notice, the single line:

## Release note

None

Each item is - <Category>: <text>, the category being one of Added, Changed, Deprecated, Removed, Fixed and Security — the categories of Keep a Changelog. An item may wrap over several lines. A breaking item starts its text with **BREAKING**. The text follows the project’s artifact language. codev validate reports a missing section as a warning and an unknown category, an item without text or an empty section as errors, on active changes only.

Domains

The domains the change touches, which activate their experts. The template lists the six domains unticked; tick the ones the change touches:

## Domains

- [x] security
- [ ] personal-data
- [ ] user-interface
- [ ] performance
- [x] operations — a new background worker
- [ ] migration

or, when it touches none of them, write the single line None.

- [x] <domain> or - <domain> ticks a domain, - [ ] <domain> leaves it unticked, and the text after the domain name is a free comment. The domains are security, personal-data, user-interface, performance, operations and migration. codev validate reports, on active changes only, an unknown domain (unknown_domain) and a line that is neither an item nor None, or None next to a ticked domain (domains_invalid), as errors, and a missing section (domains_missing) as a warning.

That comment is also how you answer a suggested domain the change does not touch: an unticked domain with a reason after its name is justified — - [ ] security — only renames a variable. Leading dashes, colons and spaces are not part of the reason; None justifies nothing. codev validate reports each suggested domain neither ticked nor justified as domain_suggested, a warning at the domain’s line.

design.md

# Design: <change title>

## Context

## Goals / Non-Goals

## Decisions

### Decision: <the choice>

**Alternatives considered**:

## Risks / Trade-offs

## Migration Plan

## Open Questions

Each ### Decision: <title> block can be promoted to an ADR with codev decision promote <change> "<title>". Risks / Trade-offs uses the [Risk] → Mitigation form. Migration Plan and Open Questions are removed when they do not apply.

tasks.md

# Tasks

## 1. Theme tokens

- [ ] 1.1 Move every color to a CSS custom property, verified by a visual diff of the settings page
- [ ] 1.2 Add the dark token set, verified by `npm test -- theme`

Tasks are grouped under numbered ## headings and written as - [ ] X.Y <task>; /codev-apply checks them off as - [x]. Each task states how to verify it.

change.yaml

Path: _codev/changes/<name>/change.yaml. Written by codev new change. <name> is never archive, the folder of archived changes.

schema: spec-driven
created: 2026-09-30
goal: Let users switch the UI to a dark theme
KeyTypeMeaning
schemastring, requiredSchema of the change: spec-driven, or the folder name of a project schema under _codev/schemas/
createddateCreation date, YYYY-MM-DD
goalstringThe goal given with --goal
skip_specsbooleanThe change intentionally has no spec delta: the artifacts writing under specs/ are skipped, and validation accepts zero deltas
retire_capabilitiesbooleanAllows sync and archive to delete a main spec whose last requirement the change removes
rolesmappingRole activations for this change only, over the project’s roles:: newcomer: always. Activations only — criteria belong to the project
reviewmappingmode: inline or mode: isolated for this change only, over the project’s review:. Another value fails with invalid_review_mode

Unknown keys are rejected — inside review too. A codev that predates review refuses a change.yaml that sets it: raise the project’s codev_version to the release that introduced it before using it. A change folder without change.yaml uses the project’s schema. An invalid entry of roles is reported by codev validate as an error of the change (unknown_role, invalid_role_activation, role_not_disableable).

The artifacts of a schema.yaml may declare their role as data under role: — see Custom schemas.

review.md

Path: _codev/changes/<name>/review.md. The change’s review record, written by /codev-propose (plan.challenge) and /codev-apply (code.review), or by hand. It is not a planning artifact: codev status ignores it, codev sync leaves it, codev archive moves it with the change.

# Review record: add-sessions

<!-- codev-review: 1 -->

## plan.challenge

### skeptic

No finding.

### security

#### R-security-1 Session tokens are stored in clear

```yaml
severity: high
confidence: high
location: "design.md#Decision: Sessions live in the database"
evidence: |
  The session token is stored in the `sessions` table.
recommendation: Store a SHA-256 of the token and compare digests.
history:
  - status: open
    by: security
  - status: fixed
    by: architect
    ref: design.md, the decision now stores a digest
  - status: fixed
    by: security
    note: verified
```

## code.review

### newcomer

Disabled by configuration.
ElementRule
<!-- codev-review: <n> -->Optional format marker; without it, format 1. A greater format is not checked and blocks archiving
## <step>plan.challenge or code.review, once each
### <role-id>One per role under a step, once each. Holds findings, or the line No finding., or the line Disabled by configuration.
#### <id> <title>A finding: R-<role-id>-<n>, unique in the record, never reused, followed by one fenced yaml block
Mode: <mode>.Optional, under a step: inline or isolated, how the step was reviewed. Once per step
Summarized: <text>.Optional, under a step, any number: a file of the material the reviewers received as a summary
Fallback: <reason>.Optional, under a role, once: why the role reviewed inline in an isolated review
FieldTypeMeaning
severityhigh, medium or lowSee the scale
confidencehigh or lowA low finding is not presented to the human
locationstring<path>:<line> or <path>:<start>-<end> from the project root, or <file>#<heading> from the change folder. Relative, no .., no drive; \ reads as /. Quote it when it holds :
evidencestringThe cited text, verbatim: whole lines or a fragment of each
recommendationstringWhat to do
historylistThe answers, oldest first; the last one is the status
raised_bylist, optionalEvery role that raised the finding, its own role first, each once: a finding merged from several reviewers in an isolated review
history[] keyMeaning
statusopen, fixed, disputed, accepted-risk, deferred or rejected
byA role id, human, or human:<name>
refThe commit or the file change — required by fixed after open or disputed
noteThe argument (disputed), the justification (accepted-risk, by a human), or why a fix does not hold (open after fixed)
toWhere a deferred finding goes: a change or an issue. A high one is deferred by a human

A field this codev does not know is a warning, review_unknown_field, and is ignored: a later codev may add optional fields without changing the format. A new status, step or required field raises the format marker instead, which an older codev refuses to check. The transitions and the checks are in Review record.

Decision (ADR)

Path: _codev/decisions/NNNN-<slug>.md. Every .md file in this folder is parsed as a decision.

---
id: "0003"
title: "Use SQLite for persistence"
status: accepted
date: 2026-09-30
tags: [storage]
supersedes: ["0001"]
---

## Context

## Decision

## Consequences

## Alternatives considered
FieldRequiredMeaning
idyesShort id, conventionally four digits; quote it so YAML keeps it a string
titleyesTitle
statusyesaccepted, superseded, proposed, deprecated or rejected
dateyesYYYY-MM-DD
tagsnoList of tags
supersedesnoId, or list of ids, of the decisions this one replaces
deviates_fromnoList of qualified ids of inherited decisions this one departs from

The frontmatter is delimited by --- lines at the very start of the file. The body is split into ## sections; the template’s four sections are a convention, not a requirement. Only accepted decisions that are not superseded or deviated from are in effect, and supersedes and deviates_from only take effect on an accepted decision: a proposed supersession or deviation changes nothing until codev decision accept.

Files saved with Windows line endings (CRLF) are read like any other. When codev rewrites a decision’s status — codev decision accept, or a predecessor marked superseded — it keeps the file’s line endings and leaves the body byte for byte as it was, so the seal stays valid.

seal.yaml

Path: _codev/decisions/seal.yaml. Maintained by codev; do not edit it by hand.

version: 1
seals:
- id: '0001'
  bodySha256: sha256:fad1f272bf13eb44f97aba1bc22f36f294503d1a2471bf12ab4bfc6ee7b4d7f6
  sealedAt: 2026-09-30

Each entry records the SHA-256 of a decision’s body — everything after the closing --- of the frontmatter, byte for byte — and the date it was sealed. Commit this file with your decisions.

codev.lock

Path: _codev/codev.lock. Written only by codev sources update, in TOML.

version = 1

[[source]]
git = "git@github.com:acme/codev-standards.git"
ref = "main"
commit = "12958274cbed0eb66985f57d3272c6d3134b672d"
resolved_at = "2026-09-30"

One [[source]] entry per git: source, with the ref it follows, the commit it is pinned to, and, when set, its subpath. Commit this file.

SKILL.md

Path: .claude/skills/codev-<workflow>/SKILL.md. Generated by codev init and codev update:

---
name: codev-explore
description: "Explore an idea, investigate a problem or clarify a need before creating a codev change. …"
allowed-tools: "Bash(codev:*), Read, Glob, Grep"
license: MIT
metadata:
  generator: codev
  version: "0.6.1"
  checksum: "8d676b09…"
---

<workflow instructions>

metadata.checksum is the SHA-256 of the whole skill without that line. With metadata.version, it is how codev update tells codev’s renderings from your edits. codev update rewrites a skill stamped with another version; among the skills stamped with its own version, it rewrites one whose content still hashes to its checksum — codev’s rendering, from an earlier build or for the other review mode — and preserves one that no longer does, edited by hand, unless you pass --force. A skill without a checksum, rendered before checksums existed, is preserved when it carries this version’s stamp and differs from the rendering by more than the review mode: run codev update --force once to bring such skills under the checksum — it also overwrites a skill you really edited, so check git diff first and apply your edits again after; an edit made then is preserved, since it no longer matches the checksum.

Validation codes

Every finding reported by codev validate carries a stable code. Messages may be reworded between versions; codes are not.

CodeSeverityMeaning
spec_purpose_missingerrorA main spec has no ## Purpose
spec_no_requirementerrorA main spec has no requirement
requirement_outside_sectionerrorA ### Requirement: outside ## Requirements
delta_header_in_main_specerrorA delta heading in a main spec
requirement_no_shallerrorA requirement without SHALL, MUST, DOIT or DOIVENT
requirement_no_scenarioerrorA requirement without a scenario
scenario_wrong_heading_levelerrorA scenario written with three # instead of four
duplicate_requirementerrorThe same requirement twice in one delta section
delta_unexpected_headingerrorA ### heading in a delta section that is not ### Requirement:, such as a translated keyword
cross_section_conflicterrorThe same requirement in two of ADDED, MODIFIED, REMOVED
rename_source_missingerrorA RENAMED source names no requirement of the main spec
rename_target_collisionerrorA RENAMED target is also ADDED
modified_uses_old_nameerrorMODIFIED uses the old name of a renamed requirement
zero_delta_without_markererrorA change has no delta and does not declare skip_specs: true
skip_specs_conflicterrorA change declares skip_specs: true but has deltas
decision_missing_frontmattererrorA file in _codev/decisions/ has no frontmatter
decision_missing_fielderrorA decision lacks a required field, or has an unknown one
decision_unknown_statuserrorA decision has an unknown status
decision_field_type_mismatcherrorA frontmatter field has the wrong shape
decision_seal_mismatcherrorA sealed decision’s body was edited
decision_conflicting_deviationserrorTwo accepted local decisions deviate from the same inherited one
release_note_invaliderrorThe proposal’s ## Release note has an unknown category, an item without text, or is empty
release_note_missingwarningAn active change’s proposal has no ## Release note section
unknown_domainerrorThe proposal’s ## Domains names a domain outside the six
domains_invaliderrorThe proposal’s ## Domains has a line that is neither an item nor None, or None next to a ticked domain
domains_missingwarningAn active change’s proposal has no ## Domains section
domain_suggestedwarningThe change’s diff suggests a domain the proposal neither ticks nor keeps unticked with a reason
invalid_triggererrorA group of triggers: in _codev/config.yaml has no known domain, no trigger, or a glob or expression that does not compile (a warning for a key codev does not know)
review_invaliderrorThe review record’s layout, a finding’s field, id or location, a status or a transition is wrong
review_decision_incompleteerrorA history entry lacks its ref, note or to, or a risk is accepted, or a high finding deferred, by someone other than a human
review_location_missingerrorA finding cites a file, a line or a heading that does not exist
review_evidence_not_founderrorA finding’s evidence is nowhere in the cited file or section
review_role_missingerrorA role active at a recorded step has no section, or one that says it is disabled
review_evidence_movedwarningA finding’s evidence is in the file, more than 5 lines away from the cited lines
review_evidence_uncheckedwarningThe cited file is not UTF-8 text, holds NUL bytes, or is larger than 4 MiB: the evidence is not compared
review_unknown_sectionwarningA step or a role the record should not hold; it is ignored
review_unknown_fieldwarningA finding field this codev does not know; it is ignored
review_format_unsupportedwarningThe record comes from a newer codev; nothing in it is checked
delta_section_emptywarningA delta section has no entry, so nothing of it is merged
decision_unsealedwarningAn accepted or superseded local decision has no seal
unknown_config_keywarning_codev/config.yaml holds a top-level or mcp key this codev does not know — see Unknown keys
decision_orphan_sealwarningA seal has no matching decision
decision_supersedes_unknownwarningsupersedes names an unknown decision
decision_id_collisionwarningA local and an inherited decision share an id
decision_dangling_deviationwarningdeviates_from names a decision that is not indexed
decision_supersession_cyclewarningSupersessions form a cycle; none of the decisions involved is in effect