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

JSON output

Every command that works on a project accepts --json. The output is a public, versioned contract — version 1 — that the skills rely on, and that you can use in scripts and CI.

Guarantees

  • One document. stdout carries exactly one JSON document, pretty-printed, including when the command fails. Nothing else is printed to stdout.
  • A status array. Every document has a status array, empty when all went well, listing warnings and errors otherwise.
  • The same shape on failure. A failed command returns the shape of its successful output, with empty or null fields, plus the error in status. A consumer parses both cases with the same code.
  • Stable names. Field names are camelCase. Codes are stable and meant to be tested; messages are meant to be read and may be reworded.
  • Portable paths. Every path, absolute or relative, uses / separators on every OS — on Windows, C:/work/acme-app. Paths inside findings are relative to the project root.
  • Additive evolution. Within version 1, fields may be added, never renamed or removed. Some optional fields are omitted when empty, as noted below.

The exit status follows the same rules as in human mode: 0 on success, 1 on failure, and for codev validate, 1 when there are errors (or any finding with --strict).

The status array

{
  "status": [
    {
      "level": "error",
      "code": "ambiguous_change",
      "message": "several active changes: add-audit-log, rework — specify which one"
    }
  ]
}
FieldValues
levelerror or warning
codeA stable identifier, such as no_codev_root
messageHuman-readable text

Common error codes:

CodeMeaning
no_codev_rootNo _codev/ in the current directory or its parents
no_active_changeThe project has no active change
ambiguous_changeSeveral active changes and no --change
unknown_changeThe named change does not exist
invalid_change_idA change name that is not kebab-case, or the reserved archive; the message names the rule it breaks
change_existsnew change with a name already taken
schema_not_foundUnknown schema name
schema_invalid, schema_unreadableA schema file that codev refuses: a structural defect, or invalid YAML or an unknown key
unknown_artifactinstructions with an artifact that the change’s schema does not have
no_artifact_readyinstructions without an artifact, and none is ready
template_not_foundA schema references a missing template
validation_failedsync or archive refused a change whose validation reports errors
review_blockedarchive refused a change whose review record is missing for a reviewed step, holds a high-severity finding still open or disputed, or comes from a newer codev; the message names each reason
invalid_review_modereview.mode in _codev/config.yaml or a change’s change.yaml is neither inline nor isolated; the message names the file and the allowed values
invalidAn invalid file, or a delta that cannot be merged into its main spec
unreadableA file could not be read
write_failedA file could not be written

Warnings reuse the codes of the condition they report, for example inherit_unresolved, git_source_unlocked, unknown_workflow or artifact_skipped.

Documents by command

init and update

codev init --json and codev update --json:

{
  "root": "/home/you/acme-app",
  "created": ["…"],
  "updated": ["…"],
  "untouched": ["…"],
  "preserved": ["/home/you/acme-app/.claude/skills/codev-apply/SKILL.md"],
  "skills": ["codev-propose", "codev-explore", "…"],
  "integration": [
    {
      "step": "claude_md",
      "action": "appended",
      "path": "CLAUDE.md",
      "reason": null
    },
    {
      "step": "ci",
      "action": "skipped",
      "path": ".github/workflows/codev.yml",
      "reason": ".github/workflows/ci.yml already runs codev validate"
    },
    {
      "step": "pr_template",
      "action": "created",
      "path": ".github/PULL_REQUEST_TEMPLATE.md",
      "reason": null
    }
  ],
  "status": []
}

preserved lists the skills left in place because they were edited by hand. integration has one entry per repository integration step: step is claude_md, ci or pr_template; action is created, appended, refreshed, unchanged or skipped; path is relative to the project, or null; reason explains a skipped step, or is null. codev update only lists the managed files it found. The codev doctor hint and the GitLab snippet are human output only.

Warnings specific to init: inherit_not_written (--inherit with an existing config.yaml), inherit_pin_failed (a git source could not be pinned) and managed_section_malformed (unbalanced codev markers; the file is left untouched). An invalid --inherit value fails with invalid_inherit.

doctor

codev doctor --json:

{
  "root": "/home/you/acme-app",
  "checks": [
    {
      "id": "config.rules",
      "group": "configuration",
      "status": "missing",
      "code": "rules_missing",
      "message": "no rules: in _codev/config.yaml",
      "fix": "/codev-configure"
    },
    {
      "id": "config.language",
      "group": "configuration",
      "status": "ok",
      "code": "ok",
      "message": "language: is set",
      "fix": null
    },
    "…"
  ],
  "summary": {
    "total": 14,
    "missing": 2,
    "warnings": 1
  },
  "status": []
}

Each check has a stable id, its group, a status (ok, missing or warning), a stable code (ok when the check passes), a message, and a fix — the command or skill to run — or null when ok. Checks come in the order of the table below; pr_template is only present for GitHub repositories. summary.missing drives the exit status of --strict.

Check idgroupCodes when not ok
config.rulesconfigurationrules_missing
config.languageconfigurationlanguage_missing
config.keysconfigurationunknown_config_key (warning)
config.rolesconfigurationroles_invalid (warning); when ok, message lists the active roles, the roles active on domain with their domains, and the disabled ones
skillsconfigurationskills_missing, skills_outdated, skills_review_mode
sourcesconfigurationsource_unpinned, lock_stale (warning)
codev_versionconfigurationcodev_version_unsatisfied, update_available (warning)
specsplanningno_main_spec
decisionsplanningno_accepted_decision (warning)
validateplanningvalidate_errors, validate_warnings (warning)
claude_mdrepositoryclaude_md_section_missing
cirepositoryci_validate_missing
pr_templaterepositorypr_template_section_missing
changelogrepositorychangelog_missing (warning), changelog_outdated (warning)

changelog

codev changelog --format json (the --json of this command):

{
  "root": "/home/you/acme-app",
  "since": "v1.3.0",
  "until": null,
  "release": null,
  "entries": [
    {
      "category": "Added",
      "text": "`--format json` on `codev export`.",
      "breaking": false,
      "change": "2026-10-02-export-json",
      "archivePath": "_codev/changes/archive/2026-10-02-export-json",
      "source": "release_note"
    }
  ],
  "markdown": "## [Unreleased]\n\n### Added\n\n- `--format json` on `codev export`.\n",
  "written": null,
  "status": []
}
  • since — the ref the range starts after, explicit or the default tag; null when every archived change is in range. until — null for the working tree.
  • release — { "version", "date" } with --release, else null.
  • entries — in display order. text is the item as written, **BREAKING** marker included. source is release_note, or what_changes for a change archived without a release note.
  • markdown — the entries rendered as Keep a Changelog.
  • written — with --write, { "path", "action" }, action being created, updated or unchanged; else null.

Warnings in status: release_note_missing, release_note_invalid, release_note_inconsistent, not_a_git_repository, archived_change_unreadable. Error codes: unknown_ref, not_a_git_repository (with --since or --until outside git), invalid_release_version, invalid_date, invalid_argument, changelog_malformed.

new change

{
  "changeName": "add-audit-log",
  "schemaName": "spec-driven",
  "changeRoot": "/home/you/acme-app/_codev/changes/add-audit-log",
  "created": [
    "/home/you/acme-app/_codev/changes/add-audit-log/change.yaml"
  ],
  "status": []
}

list

{
  "changes": ["add-audit-log"],
  "root": "/home/you/acme-app",
  "status": []
}

With --specs, the array is named specs and lists capability paths.

codev search --json:

{
  "root": "/home/you/acme-app",
  "query": "decision seal",
  "language": "en",
  "results": [
    {
      "kind": "requirement",
      "id": "decisions::Body seal recorded by the CLI",
      "capability": "decisions",
      "requirement": "Body seal recorded by the CLI",
      "excerpt": "The CLI SHALL maintain a `_codev/decisions/seal.yaml` file…"
    },
    {
      "kind": "decision",
      "id": "project/0003",
      "decision": "0003",
      "title": "The planning folder is hidden",
      "origin": "project",
      "status": "superseded",
      "inEffect": false,
      "supersededBy": "project/0009",
      "excerpt": "…"
    }
  ],
  "archived": [
    { "change": "2026-09-09-enforce-decision-immutability", "excerpt": "…" }
  ],
  "status": []
}

language is the language the words were stemmed in. A result carries the fields of its kind only; archived is present only with --archive. Inside a result, status is the decision’s status, not the root array of errors.

show

codev show --json:

{
  "root": "/home/you/acme-app",
  "capability": "decisions",
  "requirement": {
    "name": "Creating a decision through the CLI",
    "text": "### Requirement: Creating a decision through the CLI\n\n…"
  },
  "status": []
}

codev show <capability> carries requirements, the requirement names, and purpose instead of requirement. A failure carries capability: null and its code in status.

status

{
  "changeName": "add-audit-log",
  "schemaName": "spec-driven",
  "planningHome": "/home/you/acme-app",
  "changeRoot": "/home/you/acme-app/_codev/changes/add-audit-log",
  "applyRequires": ["tasks"],
  "isPlanningComplete": false,
  "artifacts": [
    {
      "id": "proposal",
      "outputPath": "proposal.md",
      "status": "ready",
      "requires": [],
      "missingDeps": []
    },
    {
      "id": "specs",
      "outputPath": "specs/**/*.md",
      "status": "blocked",
      "requires": ["proposal"],
      "missingDeps": ["proposal"]
    }
  ],
  "update": {
    "current": "0.5.0",
    "latest": "0.6.0",
    "url": "https://github.com/mairistem/codev/releases/tag/v0.6.0",
    "available": true,
    "snoozed": false
  },
  "versionRequirement": {
    "requirement": ">=0.5",
    "satisfied": true
  },
  "status": []
}

An artifact’s status is done, ready, blocked or skipped. planningHome is the project root; skills use it rather than assuming the current directory.

update is present when the latest codev release is known: current is the running version, latest the latest release, url its release page, available whether latest is newer, and snoozed whether codev self-update --snooze silenced it. It is absent when the check is disabled (CI, CODEV_NO_UPDATE_CHECK=1) or has never succeeded. versionRequirement is present when _codev/config.yaml sets codev_version: requirement is its value, satisfied whether the running version matches it. Both objects are additive: a consumer that ignores them reads the document as before.

self-update

codev self-update --json, which implies --yes:

{
  "current": "0.5.0",
  "latest": "0.6.0",
  "url": "https://github.com/mairistem/codev/releases/tag/v0.6.0",
  "action": "updated",
  "installMethod": "standalone",
  "path": "/home/you/.local/bin/codev",
  "hint": null,
  "projectUpdated": true,
  "status": []
}

action is updated, up_to_date, available (with --check), snoozed, nothing_to_snooze or managed_elsewhere. installMethod is standalone, cargo, build or package_manager; for the last three, hint holds the command that upgrades codev. latest and url are null when unknown. Failures keep this shape and carry their code in status — see the codes.

instructions

{
  "changeName": "add-dark-mode",
  "schemaName": "spec-driven",
  "artifact": "design",
  "description": "Technical approach and decisions",
  "resolvedOutputPath": "/home/you/acme-app/_codev/changes/add-dark-mode/design.md",
  "instruction": "Write the document that explains HOW to implement the change.\n…",
  "template": "# Design: <change title>\n…",
  "language": "en",
  "context": [
    { "origin": "path:~/src/acme-standards", "text": "All services log in JSON." },
    { "origin": "project", "text": "TypeScript monorepo." }
  ],
  "rules": [
    { "origin": "project", "text": "Cite the decisions that constrain the approach." }
  ],
  "dependencies": [
    { "id": "proposal", "path": "/home/you/acme-app/_codev/changes/add-dark-mode/proposal.md", "done": true }
  ],
  "unlocks": ["tasks"],
  "decisions": [
    {
      "id": "0003",
      "qualifiedId": "project/0003",
      "title": "Use SQLite for persistence",
      "status": "accepted",
      "tags": [],
      "path": "_codev/decisions/0003-use-sqlite-for-persistence.md",
      "origin": "project"
    }
  ],
  "related": [
    {
      "capability": "decisions",
      "requirement": "Creating a decision through the CLI",
      "excerpt": "`codev decision new <title>` SHALL create a new ADR in the project…"
    }
  ],
  "skipped": false,
  "role": {
    "id": "architect",
    "name": "Architect",
    "kind": "artifact",
    "at": "plan.write",
    "steps": ["plan.write"],
    "artifact": "design",
    "domains": [],
    "question": "How will it be built?",
    "when": "always",
    "whenOrigin": "default",
    "activatedBy": "always",
    "disableable": false,
    "knowledge": null,
    "doneWhen": [
      { "origin": "schema:spec-driven", "text": "each decision names the alternative it rejects and why" },
      "…"
    ]
  },
  "status": []
}
FieldMeaning
resolvedOutputPathWhere to write; may be a glob such as …/specs/**/*.md
instruction, templateFrom the schema; null when the schema defines none
languageThe artifact language, en by default
relatedThe existing requirements the change likely touches, ranked against the proposal’s Why and What Changes, at most 10, each with capability, requirement and excerpt — for the artifacts that require the proposal directly (specs, design), once it is written; an empty array otherwise
context, rulesBlocks ordered from the most general source to the project, each with its origin (project, path:<path> or git:<url>)
dependenciesThe artifacts this one requires, with their path and whether they are done
unlocksArtifacts that become writable once this one exists
decisionsThe decisions in effect — filled for design, empty for other artifacts
skippedtrue when skip_specs disables this artifact: it must not be written
roleThe role that owns the artifact, in the shape of codev roles; null when the schema declares none

A failure keeps this shape, with language still set — the configured language, or en when the configuration cannot be read — and the other fields null or empty.

validate

{
  "root": "/home/you/acme-app",
  "items": [
    {
      "kind": "change",
      "name": "add-audit-log",
      "path": "_codev/changes/add-audit-log",
      "findings": [
        {
          "path": "_codev/changes/add-audit-log/change.yaml",
          "line": 1,
          "severity": "error",
          "code": "zero_delta_without_marker",
          "message": "no delta file under `specs/` and `skip_specs: true` is not declared; add a delta or set `skip_specs: true` in change.yaml"
        }
      ]
    }
  ],
  "hasWarnings": false,
  "status": []
}

kind is change, spec, decisions or config — the last one only when _codev/config.yaml holds keys this codev does not know (see Unknown keys). Findings are reported in items, not in status: status only carries a failure of the command itself. See Validation codes.

sync and archive

{
  "changeName": "add-dark-mode",
  "root": "/home/you/acme-app",
  "updated": [],
  "created": ["/home/you/acme-app/_codev/specs/ui/theme/spec.md"],
  "unchanged": [],
  "deleted": [],
  "movedTo": "/home/you/acme-app/_codev/changes/archive/2026-09-30-add-dark-mode",
  "status": []
}

codev sync --json returns the same document without movedTo. deleted lists main specs removed by a change that retires a capability.

review

{
  "changeName": "add-sessions",
  "path": "_codev/changes/add-sessions/review.md",
  "exists": true,
  "format": 1,
  "steps": [
    {
      "step": "plan.challenge",
      "recorded": true,
      "activeRoles": ["skeptic", "security"],
      "roles": [
        { "role": "skeptic", "state": "no-finding", "fallback": null, "credited": ["R-security-1"] },
        { "role": "security", "state": "findings", "fallback": null, "credited": [] }
      ],
      "mode": "isolated",
      "summarized": []
    },
    {
      "step": "code.review",
      "recorded": false,
      "activeRoles": ["newcomer", "security"],
      "roles": [],
      "mode": null,
      "summarized": []
    }
  ],
  "findings": [
    {
      "id": "R-security-1",
      "title": "Session tokens are stored in clear",
      "role": "security",
      "step": "plan.challenge",
      "line": 13,
      "severity": "high",
      "confidence": "high",
      "reported": true,
      "location": "design.md#Decision: Sessions live in the database",
      "evidence": "The session token is stored in the `sessions` table.\n",
      "recommendation": "Store a SHA-256 of the token and compare digests.",
      "status": "disputed",
      "awaiting": "human",
      "history": [
        { "status": "open", "by": "security", "note": null, "ref": null, "to": null },
        { "status": "disputed", "by": "architect", "note": "Tokens expire after 5 minutes.", "ref": null, "to": null },
        { "status": "disputed", "by": "security", "note": "Five minutes is enough to replay a stolen token.", "ref": null, "to": null }
      ],
      "raisedBy": ["security", "skeptic"]
    }
  ],
  "issues": [],
  "archive": {
    "ready": false,
    "blockers": [
      { "code": "review_step_missing", "message": "the review record has no `## code.review` section, while roles were active at it" },
      { "code": "review_unresolved", "message": "R-security-1 (Session tokens are stored in clear) is high and still disputed; fix it, or a human accepts the risk or defers it" }
    ]
  },
  "status": []
}
FieldMeaning
existsfalse without review.md; format, findings are then empty or null
steps[].activeRolesThe roles active at the step for the change, as codev roles --at <step> --change <name> returns them
steps[].roles[].statefindings, no-finding, disabled, empty (a section with nothing usable), or missing (an active role without a section, in a recorded step)
steps[].roles[].fallbackThe role’s Fallback: line — why it reviewed inline in an isolated review — or null
steps[].roles[].creditedThe ids of other roles’ findings of the step whose raised_by names this role
steps[].modeThe step’s Mode: line, inline or isolated, or null when not written
steps[].summarizedThe step’s Summarized: lines, in file order
findings[].raisedByEvery role that raised the finding, its own first: its raised_by, or its role alone
findings[].reportedtrue when the confidence is high: the skills present only those
findings[].statusThe last history entry’s status
findings[].awaitingWho acts next: author (open), reviewer (a fix to verify, a dispute to answer), human (a dispute the reviewer maintained), or null
issuesThe record’s defects, in the shape of validate’s findings, with the review_* codes
archive.blockers[].codereview_record_missing, review_step_missing, review_unresolved or review_format_unsupported

The exit status is 1 only when issues holds an error. Fields the file leaves out or gets wrong are null.

roles

codev roles --at plan.challenge --change add-dark-mode --json:

{
  "changeName": "add-dark-mode",
  "schemaName": "spec-driven",
  "at": "plan.challenge",
  "language": "en",
  "stacks": { "detected": ["TypeScript", "JavaScript"], "filter": "detected" },
  "diff": {
    "available": true,
    "reason": null,
    "base": "origin/main",
    "files": 14,
    "scanned": 11,
    "skipped": { "lockfile": 1, "prose": 2 },
    "truncated": false
  },
  "suggestions": [
    {
      "domain": "user-interface",
      "state": "ticked",
      "justification": null,
      "matchCount": 6,
      "matches": [
        { "source": "accessibility", "kind": "paths", "trigger": "**/*.{html,htm,css,scss,sass,less,jsx,tsx,vue,svelte,astro}", "path": "web/src/theme.css", "line": null },
        "…"
      ]
    },
    {
      "domain": "performance",
      "state": "justified",
      "justification": "the cache is the browser's, not ours",
      "matchCount": 1,
      "matches": [
        { "source": "performance", "kind": "patterns", "trigger": "(?i)\\b(cache|caching|memoi[sz]e|lru)\\b", "path": "web/src/theme.ts", "line": 12 }
      ]
    }
  ],
  "roles": [
    {
      "id": "skeptic",
      "name": "Skeptic",
      "kind": "step",
      "at": "plan.challenge",
      "steps": ["plan.challenge"],
      "artifact": null,
      "domains": [],
      "question": "Would the plan survive a reviewer who did not write it?",
      "when": "always",
      "whenOrigin": "project",
      "activatedBy": "project",
      "disableable": true,
      "knowledge": "## Signals\n\nPlanning smells — each one is a question to raise, not a verdict.\n…",
      "doneWhen": [
        { "origin": "built-in", "text": "**Need** — does the change solve the Why? Is there a simpler solution, or one without code?" },
        "…",
        { "origin": "project", "text": "The rollback plan is stated" }
      ]
    },
    {
      "id": "accessibility",
      "name": "Accessibility",
      "kind": "step",
      "at": "plan.challenge",
      "steps": ["plan.challenge", "code.review"],
      "artifact": null,
      "domains": ["user-interface"],
      "question": "Can everyone use what this change adds, with a keyboard or assistive technology?",
      "when": "on_domain",
      "whenOrigin": "default",
      "activatedBy": "domain:user-interface",
      "disableable": true,
      "knowledge": "## Scope\n\nLook at the user interface the change adds or modifies…",
      "doneWhen": [
        { "origin": "built-in", "text": "Every interactive element is reachable and operable with the keyboard alone, in a logical focus order, with a visible focus" },
        "…"
      ]
    }
  ],
  "disabled": [{ "id": "newcomer", "name": "Newcomer", "whenOrigin": "project" }],
  "review": {
    "mode": "isolated",
    "origin": "project",
    "material": {
      "limitBytes": 163840,
      "totalBytes": 18342,
      "summarized": false,
      "files": [
        { "path": "_codev/changes/add-dark-mode/design.md", "bytes": 6120, "treatment": "full" },
        "…"
      ]
    }
  },
  "status": []
}
FieldMeaning
changeNameThe change given with --change; null without it
schemaNameThe schema the artifact roles come from
atThe step given with --at; null lists every role, active or not
stacksThe languages detected from the project’s dependency manifests (detected: Rust, TypeScript, JavaScript, Python, Go, Java) and the filter applied to the knowledge’s By stack part: detected, none-detected (everything kept) or all-stacks (--all-stacks)
diffWith --change, the change’s diff: available, reason when it is not, the default branch base, the files changed, the files scanned by the patterns, the files skipped per category (planning, vendored, lockfile, generated, binary, prose, large), and truncated when a cap was reached; null without --change
suggestionsWith --change, the domains the diff suggests, in the order of the domains list: state is ticked, justified (with its justification) or unticked; matchCount counts the matches and matches lists the first five, each with its source (the role, or project), kind (paths, manifests, patterns), trigger as written, path, and line for a pattern; empty without --change
roles[].nameThe display name in the configured language, English when the role has none in it
roles[].kindstep, or artifact with the artifact’s id in artifact
roles[].atThe step given with --at, otherwise the role’s first step; plan.write for every artifact role
roles[].stepsEvery step the role serves, in workflow order: an expert serves plan.challenge and code.review
roles[].domainsThe domains that activate an expert, such as ["personal-data"]; empty for the other roles
roles[].when, roles[].whenOriginalways, never or on_domain, set by default, project or change
roles[].activatedByWhy the role is active: always, project, change, or domain:<name> for an expert whose domain the proposal ticks; null when it is inactive
roles[].disableablefalse for the artifact roles, the analyst and the developer
roles[].knowledgeThe role’s knowledge, as markdown in the sections Scope, Signals, By stack, References and Reporting, for an active role that has some, its By stack part filtered as stacks.filter says; null otherwise
roles[].doneWhenThe role’s criteria, then the project’s, each with its origin: built-in, schema:<name> or project
disabledWith --at, the roles serving the step that the configuration turned off — their activation, once the project’s then the change’s settings are applied, is never — each with id, name and whenOrigin (project or change, the file whose setting won). The skills write Disabled by configuration. for them in the review record without a second call. Empty without --at; an expert whose domain is not ticked is inactive, not disabled
review.mode, review.originThe review mode in effect, inline or isolated, set by default, project or change
review.materialWith --change at plan.trace, plan.challenge or code.review, what the reviewers read: the change’s planning files but change.yaml and review.md at the plan steps, the diff’s files at code.review — no planning, vendored, generated, lock or binary file, no symbolic link; a deleted file counts 0 bytes. Each file has its bytes and its treatment: full, or summarize once the files taken from the smallest stop fitting in limitBytes (160 KiB). null otherwise, and at code.review without a diff. A file left out because its size cannot be read or its name holds a control character is named by a review_material_incomplete warning in status

With --at, roles holds only the active roles, possibly none: the step is then skipped. Without --change, no proposal is read, so the experts are inactive. A failure — unknown_step, unknown_role, invalid_role_activation, role_not_disableable — keeps this shape, with roles, disabled and suggestions empty, stacks, diff and review null, and language set as for instructions; an invalid review mode fails with invalid_review_mode. A project trigger that cannot be used is left out with an invalid_trigger warning in status.

schemas

{
  "schemas": [
    { "name": "spec-driven", "origin": "built-in", "flow": ["proposal", "specs", "design", "tasks"] }
  ],
  "root": "/home/you/acme-app",
  "status": []
}

origin is project or built-in. A project schema that cannot be used is left out of schemas, with a warning schema_unusable in status. A project schema whose name: differs from its folder is listed under its folder name, with a warning schema_name_mismatch.

decision

codev decision list --json returns { "root", "decisions": [...], "status" }, and codev decision show --json returns { "root", "decision", "content", "status" }, where content is the full Markdown of the file. A decision is:

{
  "id": "0001",
  "qualifiedId": "project/0001",
  "title": "Use PostgreSQL for persistence",
  "status": "superseded",
  "date": "2026-09-30",
  "tags": [],
  "supersedes": [],
  "deviatesFrom": [],
  "path": "…/_codev/decisions/0001-use-postgresql-for-persistence.md",
  "origin": "project",
  "inEffect": false,
  "supersededBy": "project/0003"
}

deviatedBy is added for an inherited decision set aside by a local deviation. origin is project, path:<path> or git:<url>. path is relative to the project for a decision inside it, absolute otherwise.

The commands that create or seal decisions return:

CommandFields besides root and status
decision newdecision, path, bodySha256 (only with --status accepted)
decision acceptdecision, path, bodySha256, superseded
decision supersedenewDecision, newPath, oldId, oldQualifiedId, oldPath
decision sealseal (id, bodySha256, sealedAt), wasNoop
decision deviatedecision, path, targetQualifiedId
decision promotedecision, path, sourceChange, designPath

bodySha256 is present only when the command sealed the decision: decision supersede, decision deviate and decision promote create a proposed decision and seal nothing. For decision supersede, oldId, oldQualifiedId and oldPath name the decision that accepting the new one will supersede; it is not modified. superseded lists the predecessors that decision accept set to superseded — each with its id, qualifiedId and path — and is empty when the decision supersedes nothing.

For example, codev decision accept 0002 --json for a decision created by codev decision supersede 0001 "Use SQLite":

{
  "root": "/home/you/acme-app",
  "decision": {
    "id": "0002",
    "qualifiedId": "project/0002",
    "title": "Use SQLite",
    "status": "accepted",
    "date": "2026-09-30",
    "tags": [],
    "supersedes": [
      "0001"
    ],
    "deviatesFrom": [],
    "path": "_codev/decisions/0002-use-sqlite.md",
    "origin": "project",
    "inEffect": true,
    "supersededBy": null
  },
  "path": "/home/you/acme-app/_codev/decisions/0002-use-sqlite.md",
  "bodySha256": "sha256:c8873b83c178dbf8434045f43a722911b9d9913ee9f13915ccda893539b788c1",
  "superseded": [
    {
      "id": "0001",
      "qualifiedId": "project/0001",
      "path": "/home/you/acme-app/_codev/decisions/0001-use-postgresql.md"
    }
  ],
  "status": []
}

A refused acceptance keeps the same shape, with decision and path set to null, superseded empty and the code in status: unknown_decision_id, cannot_accept_inherited, decision_not_proposed, cannot_supersede_inherited or predecessor_not_accepted.

sources

codev sources list --json returns { "root", "sources": [...], "status" }, where a source is:

{
  "type": "git",
  "address": "git@github.com:acme/codev-standards.git",
  "state": "locked",
  "gitRef": "main",
  "sha": "12958274cbed0eb66985f57d3272c6d3134b672d",
  "resolvedPath": "/home/you/.cache/codev/content/12958274cbed0eb66985f57d3272c6d3134b672d"
}

type is path or git; state is one of resolved, unreadable, unlocked, locked, needs_update. gitRef, subpath, sha and resolvedPath are omitted when they do not apply.

codev sources show --json returns { "source", "filesExposed", "root", "status" }. codev sources update --json returns { "root", "changes", "lockWritten", "status" }, where each change has a kind (added, moved or unchanged), url, gitRef, from (for moved) and to.