Version: post-0.6.0, latest dev build 81483a0
2026-09-27 (60 commits after v0.6.0)
Applies to: projects using Git, Markdown, deterministic
tools, and AI agents.
This is a model, not a framework. Adapt it to the project. Do not create artifacts, directories, metadata, or process merely to comply with this document.
ChadKit exists for projects where important project knowledge and state should live in Git alongside the work, be usable by humans and AI agents, and remain reviewable through small deterministic changes.
It is meant as much for cleaning up an existing repository as for starting a new one. Most projects that need ChadKit already have the material - decisions argued in pull requests, feedback in a chat log, conventions living in one person's head - scattered where neither a human nor an agent can find it. Adoption is largely an act of consolidation, and the model is shaped around that: nothing is scaffolded ahead of need, existing artifacts are adopted in place rather than migrated, and dated records carry the date the project learned a thing rather than the date somebody got around to writing it down.
Its focus is durable project truth and agent-safe project state: goals, tasks, evidence, terminology, decisions, and reusable workflows. It does not require that every feature begin with a formal specification or that projects adopt one prescribed development lifecycle.
ChadKit is intentionally composable. A project may use it alongside a specification system, coding methodology, Agent Skill library, issue tracker, or documentation framework. If another maintained system solves a ChadKit concern better with equal or lower complexity, prefer adopting, integrating, or delegating to that system over maintaining a parallel invention.
Use ChadKit when several of these are true:
ChadKit may be unnecessary when the project has almost no durable non-code state, or when another system is intentionally authoritative and duplicating it in Git would create more problems than it solves.
| System | Primary concern | Prefer/use it when | Relationship to ChadKit |
|---|---|---|---|
| ChadKit | Git-native durable project truth and agent-safe state | You need lightweight goals, tasks, evidence, terminology, decisions, and workflows across human/AI work | Base information layer |
| GitHub Spec Kit | Extensible intent/spec-driven process harness | You want structured specification, planning, task generation, implementation workflows, presets, extensions, or broader SDLC automation | Major upstream idea source; can coexist with ChadKit |
| OpenSpec | Lightweight behavior/change specification for AI coding | The main problem is agreeing on what to build before implementation, including brownfield change work | Complementary specification/change layer |
| Superpowers | Composable agent software-development methodology/skills | You want strong reusable coding workflows such as planning, debugging, TDD, verification, and review | Reuse skills/workflows rather than copying them into ChadKit |
| BMAD Method | Broad AI-driven development lifecycle with specialized agents and workflows | You want a more comprehensive, opinionated methodology | Usually heavier than ChadKit; mine useful ideas selectively |
| Agent Skills / Claude Skills | Portable packaging of reusable agent instructions, scripts, references, and assets | You need a reusable agent capability | Mechanism ChadKit should use, not replace |
| MADR | Architectural and other consequential decision records | You need durable rationale for a choice among alternatives | ChadKit's decision-record foundation |
| Diátaxis | User-facing documentation organization | You need to organize tutorials, how-to guides, reference, and explanation | Useful documentation lens, not ChadKit's project-state taxonomy |
The existence of overlap is intentional. ChadKit should own only the concerns for which a lightweight Git-native information layer provides distinct value.
Two paths, depending on which of the two operating modes applies. Either way the goal is a working minimum, not a complete directory.
Tooled. Install the CLI, then from the project root:
chadkit init # writes the config, README.md, and thoughts.md - nothing else
chadkit init --full # ...or also language.md, one adoption spike task, and a known-installs entry
chadkit task add "First real task" # creates tasks.md
chadkit log add --type t_lesson --summary "Why X works this way" # creates log.md if it does not exist
chadkit validate
Doc-only. Put this document wherever the project
keeps reference material and create chadkit/config.yaml if
paths or overrides differ from the defaults. Write the first task, log
record, or term into its artifact when you actually have one.
Either way, no artifact exists until something is written to it. That is deliberate: read Bootstrapping and migrating an existing project before adding anything, and the useful work turns out to be deciding what the project already has, not producing files.
This document works with or without tooling. Know which mode you are in.
| Mode | Condition | Behavior |
|---|---|---|
| Tooled | chadkit CLI installed and
chadkit/config.yaml exists |
Defaults come from ChadKit; deviations and project overrides come
from the config. Structured mutations go through the tool.
doctor may report drift. |
| Doc-only | This file was handed to an agent in a project with no CLI installed | Conventions only. Follow the formats below, make surgical edits by hand, skip command references. A config is optional but recommended when paths or overrides differ from the defaults. |
Doc-only is a first-class mode, not a degraded fallback - it is how this model reaches projects that will never install anything. Nothing below may depend on the tool existing.
When instructions conflict, use this precedence:
chadkit/config.yaml when present.Do not silently override a higher-authority rule with a lower-authority one.
If a project already has a good equivalent for an artifact described here, reuse it rather than creating a duplicate, and declare it in the config. Prefer migration by accretion: improve the project as work touches it instead of reorganizing the repository.
Preserve the intent even when adapting the structure.
CLAUDE.md and equivalents point to project truth; they
never duplicate it.Keep ChadKit-owned project state together rather than scattering kit files through the repository root.
ChadKit state lives in a single chadkit/ directory at
the project root. In prose this is written
<project-root>/chadkit/, where the project root is
the repository root, or the root of the subtree a project owns inside a
larger repository. Paths elsewhere in this document are relative to
it.
README.md # normal project entry point; not ChadKit-owned
CLAUDE.md # optional agent adapter; not project truth
chadkit/
├── README.md # what this directory holds; no rules, no project truth
├── config.yaml # required in tooled mode; optional in doc-only
├── goals.md
├── tasks.md
├── tasks-archive.md # create only when first needed
├── log.md
├── language.md
├── thoughts.md # human-only scratch; created by init
├── decisions/ # create only when needed
└── workflows/ # create only for repeatable procedures
The chadkit CLI may be installed externally or
implemented locally. Its executable location is not part of the project
information model.
Do not create empty directories or placeholder
documents merely because they appear above. The one exception is
thoughts.md, which init creates: it is
hand-edited only, so no command would ever materialise it, and an
existing scratch file is what invites the first jotted idea.
init --full also writes language.md's
bootstrap rows and one adoption task; those carry content the project
uses at once, so they are records, not placeholders.
Only the artifacts below are ChadKit-owned. The project's root
README.md and any agent adapter are outside that boundary:
ChadKit neither creates nor edits them.
| Artifact | Purpose | Default shape |
|---|---|---|
chadkit/README.md |
Orientation for the directory: version, one line per artifact, who writes what, link to this model | Short; states where things are, never what the rules are |
chadkit/config.yaml |
ChadKit version plus project-specific deviations/overrides | Required in tooled mode; optional in doc-only |
chadkit/goals.md |
Direction and outcomes worth pursuing | One file |
chadkit/tasks.md |
Current actionable project work | One file until sharding is justified |
chadkit/tasks-archive.md |
Completed project tasks removed from the active container | Create only when archiving is first needed |
chadkit/log.md |
Typed chronological evidence | Append-oriented |
chadkit/language.md |
Ubiquitous Language | One file; may be very small |
chadkit/thoughts.md |
Unstructured human scratch | One file, hand-edited only; created by init |
chadkit/decisions/ |
Consequential choices | One record per decision |
chadkit/workflows/ |
Repeatable procedures | One file per workflow |
Not every file is tool-owned. The boundary is structured record mutation, not file ownership:
chadkit when installed:
allocating a PT- or PLR- ID, completing a
task, changing a task's bracket fields, appending a log record. These
need ID allocation and validation, and are the operations agents
repeat.README.md. These are prose. Git diff discipline applies; the tool
does not.thoughts.md - init only seeds it; see Thoughts, optional.In doc-only mode the first category is hand-edited too, under the same discipline.
README.md remains uppercase because that filename is a
widely recognized repository convention. ChadKit-owned files use
lowercase consistently; thoughts.md has no special
ecosystem convention that justifies all-caps naming.
PG-001 Project Goal
PT-001 Project Task
PLR-20260827-01 Project Log Record
PD-001 Project Decision (MADR-based, project-local)
All four prefixes are self-describing and grep-safe. Decision records
use MADR 4.0 as their content model but not its bare 0007
numbering, because a decision ID is referenced from task lines, log
records, and prose where the containing path is not present to supply
the namespace - and grep 0007 matches dates, versions, and
hashes. A container that already uses a different convention keeps it;
see Decisions.
Every record value is a single line. A task line is
one line; a log record is a heading plus one line per field. No value
may contain a newline or a control character, because the line break is
what separates one record from the next: a newline inside a value does
not corrupt its own line, it fabricates the ones after it. Values
written into brackets additionally may not contain [,
], or ,, which delimit the fields. Tools
reject such values; a human writing by hand should not produce them.
Long context belongs in a linked artifact, not inside a record.
Three separable layers allow projects to adapt without needless churn or breaking common tooling.
| Layer | Project rule |
|---|---|
| Record format - base shape and semantics of a task line or log record | Shared base is stable; projects may extend it but should not remove or reinterpret shared semantics |
| Container layout - how many files, their names, how records group | May vary freely, declared in the config when one exists |
| Addressing - how a record is referenced | Stable and unambiguous; IDs remain project-global where defined that way |
Project-specific extensions are legitimate:
- [ ] PT-017 [PG-002] [#auth] [owner:client] Resolve callback failure.
Here [owner:client] is the extension;
[PG-002] and [#auth] are shared base fields
defined under Tasks.
Reinterpreting shared syntax is not:
[x] means "blocked" in this project
If a project genuinely requires incompatible semantics, declare that artifact nonstandard in the config rather than silently changing the contract.
A larger project may shard tasks.md into:
chadkit/tasks/
├── auth.md
└── billing.md
This is a container change, not an identity change. Project
task IDs remain project-global. PT-017 stays
PT-017 if it moves from auth.md to
billing.md.
Before sharding, try tags such as [F1] or
[#auth]. Do not encode the task's current feature,
container, or file location into its stable identity
(F1T-017), because classification can change while identity
should not.
chadkit/config.yaml is the project-local discovery point
for ChadKit. Its fixed location lets tooling and agents find
project-specific deviations without scattering configuration through
agent-specific files.
In tooled mode, the config is required. In doc-only mode, it is optional when the project uses ChadKit defaults, but recommended whenever artifact paths or important overrides differ.
Do not edit this model per project. Project-specific deviations belong in the config or in higher-authority project instructions.
Do not vendor this model into the project. A copied
CHADKIT-MODEL.md goes stale, produces large diffs on every
ChadKit change, and invites the per-project editing forbidden above. In
tooled mode the config's chadkit-version is the reference
and chadkit model names where to read the matching text; in
doc-only mode the document is present because a human put it there
deliberately.
The config should declare deviations, not restate every default:
# chadkit/config.yaml
chadkit-version: 0.6.0 # written by init; matches the installed CLI
# Omit paths that use ChadKit defaults.
paths:
tasks: BACKLOG.md
task_archive: archive/completed-project-tasks.md
# Only when more than one decision container exists; see the Decisions section.
decisions:
- path: docs/adr/
schema: docs/adr/README.md
scope: architecture
prefix: ADR
- path: chadkit/decisions/
review:
trigger: "after each full pipeline pass" # optional; only when useful
workflows: # settings by workflow name; see below
qa-gate:
checks: scripts/gate-checks.sh
paths:
- src/parser.py
since: main
overrides:
- Existing BACKLOG.md remains authoritative for project tasks.
- Domain term is Customer, not Member.
Default paths are:
goals: chadkit/goals.md
tasks: chadkit/tasks.md
task_archive: chadkit/tasks-archive.md
log: chadkit/log.md
language: chadkit/language.md
decisions: chadkit/decisions/ # shorthand; see Decisions for several
workflows: chadkit/workflows/
thoughts: chadkit/thoughts.md # human scratch; created by init
workflows holds settings by workflow name and is an
extension namespace: any workflow may keep keys under its own name, and
the CLI checks only the entries it knows. It knows qa-gate
(see Workflows): checks names the
script the gate runs after a review, paths the paths whose
commits the gate must clear, and since where the audit
starts, a date, a commit, or a branch name, which audits only the
commits not yet merged there and so suits a squash-merge workflow. What
doctor checks against these is under Deterministic tools.
Omitting an optional artifact such as workflows/ is
legitimate. Ubiquitous Language is a default invariant: keep
language.md unless an explicit project override explains
why terminology governance provides no useful value.
In doc-only mode, existing project instructions and current human direction serve as project-specific overrides. Do not create a config merely to repeat defaults. Do create one when non-default paths would otherwise be ambiguous to humans or agents.
The same name and grammar serve the project and the user, following the usual configuration convention:
| Level | Path | Holds |
|---|---|---|
| Project | chadkit/config.yaml |
Declarations: version, paths, containers, review trigger, overrides, workflow settings |
| User | $XDG_CONFIG_HOME/chadkit/config.yaml, default
~/.config/chadkit/config.yaml |
init (what a bare chadkit init does:
minimal or full) and
known-installs, the projects this user works in, each with
how much of it a cross-repo sweep may read (chadkit or
all) |
Precedence is not a merge: the project file declares, and the user
file only supplies defaults for what a project has not declared. A user
file can never override a project declaration. A machine level, if one
is ever needed, takes the same name and the same rule.
chadkit config owns the user file (init,
show, add-install,
remove-install). Before 0.6 the project file was
chadkit/manifest.yaml; that name is still read for one
minor release and chadkit upgrade renames it.
Goals may be directional, outcome-oriented, or exploratory. A goal does not need a fake metric to look rigorous.
# Goals
## PG-001 - Make AI a first-class project collaborator
Explore where AI improves speed, quality, consistency, or breadth, and adapt workflows when
evidence supports it.
## Review
Optional project-specific questions worth revisiting:
1. Is the fixed output schema producing cross-project insight, or fighting the domains?
PG-NNN
(PG-001, …). Keep the active set small.PG- distinguishes project goals from generic
G- identifiers and makes cross-references and grep
safer.## Review section may keep project-specific
double-loop questions near the goals they interrogate; omit it when it
adds no value.All ChadKit project-task IDs use the PT-NNN form.
# Tasks
- [ ] PT-001 [PG-001] Add terminology validation to ChadKit tooling.
- [ ] PT-002 [PG-002] [#onboarding] Investigate failure reported in PLR-20260827-02.
- [x] PT-003 [PG-001] Prototype agent-driven QA. Completed: 2026-08-27
- [ ] PT-004 [PG-001,PG-003] [#auth,#billing] [needs:PT-002,PT-003] Resolve callback failure.
- [-] PT-005 [PG-002] Build combined exporter. Resolved: 2026-08-30 no longer relevant per-format exporters cover it
- [?] PT-006 [PG-002] Add the exporter CLI. Confirm the flag names in chadkit/work/PT-006.md?
The mark is the task's state, one of five:
| Mark | State | Written by |
|---|---|---|
[ ] |
Open, untouched | task add |
[?] |
Waiting on a human: the task cannot proceed until someone answers. The question lives in the task's work file, optionally in the task text | task wait; task resume returns it to
[ ] |
[/] |
In progress | Hand-written; reserved, no verb yet |
[x] |
Completed, with a Completed: trailer |
task complete |
[-] |
Resolved, with a Resolved: trailer |
task resolve |
The first three are the open marks: a waiting or in-progress task
still takes dependencies, is still scanned by
language check, and completes or resolves directly without
passing through [ ]. [?] and [/]
follow the Minimal checklist convention; GitHub renders both as literal
text rather than a checkbox. A [?] task is the one to
review first on returning to a project:
chadkit task list --waiting lists them (doctor
too; see Deterministic tools).
The shared base fields follow the task ID, in this order, each optional:
| Field | Form | Meaning |
|---|---|---|
| Goals | [PG-001,PG-003] |
Goals this task serves |
| Tags | [#auth,#billing] |
Free classification; the # sigil is repeated on every
tag. Recording each tag in Ubiquitous
Language is recommended |
| Dependencies | [needs:PT-002,PT-003] |
Tasks that must complete first |
| Priority | [priority:high] |
Optional low/med/high; an
absent bracket means med, and tools write only the
deviations. high is reserved for important features or work
an important feature needs |
The field order above is canonical for a line written whole. It is not a validity condition, and nothing may reorder the fields of an existing line to achieve it - the search recipes in the retrieval contract are position-independent by design, so reordering costs diff size and buys nothing.
A reader treats repeated same-kind brackets
([PG-001] [PG-002]) as the comma-joined union in file
order: non-canonical, but unambiguous, and hand-writing them is natural
in doc-only mode. Tools never write that shape, and a mutation that
touches such a field may collapse it into a single bracket - that
collapse is the minimal lossless way to express the change, not
gratuitous normalisation.
Multi-valued fields are comma-separated inside one bracket, never
repeated as separate brackets. The # sigil repeats so that
a bare search for #billing matches in any position. The
retrieval contract under Agent adapters,
skills, and context loading holds the matching search recipes, which
must be used instead of grep '\[PG-003\]' - that pattern
silently returns nothing when the goal is not the only value.
PT- distinguishes project tasks from other identifiers
and makes repository-wide grep safer than a generic T-
prefix.needs expresses ordering between existing tasks. It
must reference real task IDs and must not form a cycle; both are
validation targets. A task with unmet dependencies is not thereby
blocked from being worked, and completion does not cascade.[-] and the line gains
Resolved: YYYY-MM-DD plus why -
split into PT-..., duplicate of PT-NNN, or
no longer relevant with optional prose. The text keeps
saying what was intended; rewriting it to match what happened falsifies
the record, and successor tasks carry the remainder.needs pointing at
one is stale: repoint it at the named successors or the duplicate's
target - a validation target. A resolution of
no longer relevant names nothing to repoint at, so a
dependent needs a human decision; that is health reporting, not
validation.chadkit/work/PT-017.md, linked from the task rather than
inlined: hand-edited prose that evolves with the task, and where a human
looks first. Record the why - rejected options, gates, reasons - not
only the what.needs:, the
feature's priority in its priority bracket, and everything that evolves
- scope, notes, why the priority is what it is - in its work file. A log
record only for a turning point worth a dated, append-only trail.chadkit/tasks-archive.md. Create it only when the first
archive occurs.paths.task_archive in the config.PT-NNN; use file location and tags for classification
rather than identity.tasks.md is the actionable backlog. The log is
not a second backlog.
One append-oriented log holding evidence and history. Records are never rewritten.
Project log record IDs use PLR-YYYYMMDD-NN; the
PLR- namespace is intentionally explicit and grep-safe,
matching the self-describing PG- and PT-
namespaces.
Date is when the project learned it,
not when the thing happened. For most record types these coincide - a
t_qa observation, a t_lesson, a
t_experiment's result and a t_review are all
learned as they occur. Where they differ, the knowledge date is the one
worth recording; the reason is under Why insertion order is worth
preserving below.
Three dates can be in play, and conflating them is what makes a log misleading:
| Date | Where it lives |
|---|---|
| When the project learned it | Date - the required field, and the date the ID
stamps |
| When it was written down | Recorded, optional, present only when it differs from
Date |
| When the underlying thing happened | The summary prose, when it is significant at all |
A client mentioning something a fortnight ago that reaches you today is dated today: today is when the project could first act on it, so a report written last week cannot fairly be faulted for missing it. Put "Shanna said this on the 15th" in the summary if the timing matters.
Fields are written in one order: Date, then the type's
required fields as listed below, then whichever optional fields apply. A
record's body is its field lines: any top-level
- Word: value line under a heading is a field claim and is
validated as one; prose belongs in field values, not free bullets. A
record is readable either way, but a fixed order keeps generated and
hand-written records from differing for no reason.
| Type | Meaning | Required fields | Optional |
|---|---|---|---|
t_feedback |
Input from a client, user, or stakeholder | Date, Source, Summary | Related, Action |
t_qa |
Observed behavior, test result, defect evidence | Date, Summary | Related, Action, Severity |
t_lesson |
Durable knowledge learned while working | Date, Summary | Related |
t_experiment |
A bounded trial, especially of AI/tool/workflow behavior | Date, Hypothesis, Result | Related, Follow-up |
t_review |
Optional double-loop reassessment - see Double-loop review | Date, Summary | Trigger, Outcomes, Goals, Shape, Cost, Decision, Related |
t_release |
A release of this project was cut; the note says what changed | Date, Version, Summary | Related |
t_other |
An observation worth dating that fits no other type; promote it or
leave it. The agent-writable counterpart of thoughts.md,
which agents never write to |
Date, Summary | Related |
The t_ sigil is part of the type's name, on the same
reasoning as the # on tags: t_release is
grep-distinct from a #release tag and from the word
"release" in prose, where the bare word is useless as a search key.
Every type also accepts an optional Recorded field: the
date the record was written, when that is later than Date.
Omit it when they match, which is the normal case. It exists for the two
cases where they genuinely differ - adopting ChadKit into a project with
existing history, and writing up something learned a while ago - and it
must never be earlier than Date, since nothing is written
before it is known.
Every type likewise accepts an optional Tags field: free
classification, comma-separated with the # sigil repeated
(- Tags: #cli,#regression), under the same recommendation
to record each tag in Ubiquitous
Language. Tags on a record classify the observation; tags on a task
classify work.
Add types only for a real need (t_risk);
t_other exists so that need is felt before a type is
invented, and a t_other that keeps recurring with one shape
is the case for a new type.
The heading line is
### PLR-YYYYMMDD-NN t_type [IDs] Title, so one grep hit on
an ID carries the record's id, type, links, and title, and the delimiter
recipes of the retrieval contract work on the log too. The bracket is
derived, never hand-maintained: it holds the union of
ID-shaped tokens appearing in the record's field values - IDs stay in
prose where they belong, and a bare ID in Related adds a
link with no prose. An id in backticks is quoted verbatim - a foreign
project's id, an example - and is neither linked nor reference-checked.
Declared decision container prefixes join the link grammar
(ADR-0006, PD-004), resolved against the
container's files; path-qualified decision references stay prose. A
bracket that does not equal that union is a validation failure, since
the tool cannot produce one; omit the bracket when no field names an ID,
except that a title itself beginning with [ is preceded by
an explicit empty bracket ([]) so it cannot read as the
links.
### PLR-20260827-01 t_feedback [PG-002,PT-014] Onboarding terminology is confusing
- Date: 2026-08-27
- Source: client
- Summary: Client read "workspace" as an organization rather than a project container.
- Related: PG-002
- Action: PT-014
PLR-YYYYMMDD-NN. NN is two
digits, so one date addresses at most 99 records. Reaching that is a
signal the log is being used as something else; tools refuse rather than
widen the field, because the width is part of the shared format.log.md ends with one blank line, so every append is a
pure tail-addition.chadkit/log/2026.md) only when the file becomes
genuinely unwieldy, and declare the new path in the config. Do not split
preemptively.Why insertion order is worth preserving. It records
what was knowable when. A review that judges last quarter's work against
today's log is committing hindsight bias - faulting people for missing
information the project did not yet have. Because Date is
the knowledge date, the log answers that directly: everything above a
record was known before it.
Because Date is the knowledge date, ID order and file
order normally agree, and a record whose ID precedes its neighbours is
the exception rather than the norm. When one appears it means the
project learned something and wrote it down later - bulk adoption of
existing history, most often - and its Recorded field says
so explicitly rather than leaving a reader to infer it from
position.
Experiments vs decisions. An experiment is a bounded trial with no commitment attached. A decision commits. An experiment whose result changes what you do gets promoted to a decision record - it does not stay in the log as the authority.
Do not split types into separate files merely because their semantics differ. Split only when lifecycle diverges materially - for example, QA acquires status, severity, ownership, and closure workflows, or a single-loop calibration record accumulates enough volume to warrant its own file. When that happens, keep the shared record format unless the changed lifecycle creates a real need to extend or replace it.
Use MADR 4.0 as the base model for consequential technical and non-technical choices. A project may use an established customized MADR template, but the depth of a record should be proportional to the consequence and uncertainty of the decision.
Create a decision record when a future reader is likely to ask: why this instead of the plausible alternatives? Do not create one merely because a choice occurred.
In a container ChadKit creates, records are PD-NNN, one
per file, named PD-001-short-title.md. This diverges from
MADR's 0001-short-title.md filenames deliberately, for the
addressing reason given under Format, container, and
addressing.
A container that already exists keeps its own filenames and its own
id field. Nothing is renamed.
For routine-but-consequential decisions, the useful minimum is:
Use additional fields from the project's customized MADR schema - such as reversibility, validation, review date, outcome grading, source drift, or claim validation - only when they materially improve the decision or its later review. Empty ceremony is worse than an omitted field.
status and date go in YAML frontmatter, as
MADR 4.0 places them. The retrieval contract greps for
status: accepted across a container, so a status buried in
prose is invisible to it:
---
status: accepted
date: 2026-08-28
---
# PD-002 - Multi-valued task fields are comma-separated
Related: PG-001
## Context and problem
...
Status values follow MADR: proposed,
accepted, rejected, deprecated,
superseded by <id>.
If a container declares a decision schema - by default
chadkit/decisions/README.md - that schema is authoritative
for records in that container. ChadKit uses MADR 4.0 as the common basis
and permits established project extensions. Extensions may add useful
fields; they should not redefine the meaning of the shared lightweight
fields.
Related: PG-001)
when one applies.A project may already keep architecture decisions somewhere else,
often under a heavier customized MADR template scoped to architecture
alone. Keep it. Do not migrate those records into chadkit/
(directive 16 under Default
directives), and do not force lightweight decisions into an
architecture-scoped schema.
Declare each container in the config, with its schema and what it is scoped to:
decisions:
- path: docs/adr/ # files stay 0001-use-madr-with-source-drift-tracking.md
schema: docs/adr/README.md
scope: architecture
prefix: ADR # reference form only: ADR-0001
- path: chadkit/decisions/ # everything the existing container is not scoped to
prefix governs how a decision is referenced, not
what its file is called. Declaring prefix: ADR
above means a task line or log record cites ADR-0001; the
file remains
docs/adr/0001-use-madr-with-source-drift-tracking.md and
its id: 0001 field is untouched. Renaming existing records
to carry a prefix is a migration, and migrations need the justification
described under Bootstrapping
and migrating an existing project.
The decisions: list supersedes
paths.decisions when present; use the shorthand for the
ordinary single-container case. Rules when more than one container
exists:
prefix - ADR-0001 for the adopted container,
PD-001 for ChadKit's. Where no prefix is declared,
references must be path-qualified (docs/adr/0001). Bare
0001 is never a valid cross-container reference.overrides and do not mirror its records into Git - see When to deviate.Use Ubiquitous Language in every project, even small ones, because AI agents are especially prone to terminology drift: using multiple words for the same concept, or reusing similar words for concepts that are actually different.
The implementation may be tiny. chadkit/language.md can
begin with only the terms that have already shown ambiguity or are
central to the project.
Scope values are themselves project vocabulary: give each a row with
Scope: scope, so the column's possible values are listed in
the one file a human edits. Only tag is interpreted by
tooling; every other scope value is the project's to define.
Tags belong here too, as rows with
Scope: tag - task tags and log record tags are one
namespace. Recording a tag when you invent it is recommended, not
required - nothing rejects an undefined tag, because a tag has to stay
cheap enough that trying one out costs nothing. But an undefined tag is
how a project ends up with #migration,
#adoption, and #brownfield splitting one
category three ways, and the moment that risk is highest is the moment
the tag is created, not later. Writing one line then is the whole
safeguard.
| Term | Meaning | Allowed aliases | Avoid | Scope |
|------|---------|-----------------|-------|-------|
| Member | Registered person using the product | - | customer, account-holder | domain |
| Single-loop learning | Improve execution while current goals and assumptions remain fixed | - | fast-loop | project-management |
| Double-loop review | Reassess the goals, assumptions, constraints, or shape of the work itself | - | slow-loop, direction review | project-management |
| `domain` | Terms of the project's own subject matter | - | - | scope |
| `project-management` | General methodology terms the project has adopted | - | - | scope |
| `scope` | Values of the Scope column itself; each value used gets a row like these | - | - | scope |
Rules:
Enforcement is warnings: language check never fails, and
validate does not check terminology use. A hard failure
would need a check with near-zero false positives, which none has earned
yet.
The checker distinguishes project-authored terminology from literal or externally owned text. Do not require terminology rewrites inside:
Markdown syntax alone is not a blanket exemption in principle, but the current checker skips fenced and inline code: code is where third-party literals live, and checking project-owned identifiers inside code is deliberately deferred until observed drift demands it. Blockquotes are exempt only when they are actually preserving quoted/external wording.
The checker reports the discouraged or ambiguous term, canonical term or conflict, file, and location. It does not automatically rewrite semantic terminology unless the change is unambiguous.
init creates chadkit/thoughts.md for
thoughts, half-baked ideas, and anything with nowhere else to go yet. It
is the top of the chain described under Promotion, and the only artifact with no
structure.
Rules - these are exemptions, and they are binding:
validate, language check, diff discipline, or
promotion pressure. Nothing may report on its contents.chadkit thoughts command.Keeping thoughts.md out of default context loading also
prevents an agent treating a discarded half-idea as a plan. Projects
that do not need a human scratch file may delete it; init
never recreates it.
Use the lightest artifact that preserves useful information. Everything starts in the transient layer; promotion routes it by what the information is:
From thoughts.md / conversation / transient note:
actionable work -> task
durable evidence or learning -> typed log record
changed direction -> goal edit
repeated procedure -> workflow or tool
consequential choice -> decision record
canonical meaning -> language.md / other truth document
And onward between artifacts as information hardens:
log record -> task when the evidence becomes actionable
task -> decision record when a consequential choice surfaces
t_review -> goal edit when the reassessment moves direction
anything -> language.md when a term needs pinning
Possible transitions, not a mandatory lifecycle.
A spike is promotion done as scheduled work: a task,
tagged #spike by convention, whose deliverable is knowledge
or direction rather than a change to materials. While open, its work
file holds options, evidence, and the direction stakeholders gave; it
closes by promoting - tasks for what became actionable, a log record for
what was learned or decided (t_feedback when the yield is
mostly the direction given), a decision record for a consequential
choice, a goal edit if direction moved. No time-box and no required
output shape: an exploration that yields only a lesson closed well.
Once agents repeatedly mutate structured state, prefer a tool. In
tooled mode, artifact paths come from chadkit/config.yaml;
do not hardcode movable artifact paths.
chadkit init [--full|--minimal] # config, orientation README, thoughts.md; --full adds language.md, an adoption spike, a known-installs entry
chadkit task add "<text>" [--goal PG-001] [--tag auth] [--needs PT-002]
chadkit task complete PT-017 [--date YYYY-MM-DD]
chadkit task mod PT-004 --add-tag billing --remove-goal PG-001
chadkit task resolve PT-005 --split-into PT-006,PT-007 # or --duplicate-of PT-002, --not-relevant [--reason "..."]
chadkit task wait PT-006 # [?]: cannot proceed until a human answers; no date
chadkit task resume PT-006 # [?] back to [ ]
chadkit task list [--all | --waiting] [--priority high] [--sort-by state,priority] [--width 80]
chadkit task info PT-006 [--history] # the task's part of a gate brief; --history: the timeline alone
chadkit task stats # counts per state, high priority alone, most recently updated
chadkit log add --type t_feedback [--date YYYY-MM-DD] # the bare type is accepted and normalised
chadkit log add --type t_review
chadkit language init # language.md from the template: the scope rows plus #spike and #migration
chadkit language check [paths...] [--all] # Avoid-column warnings; never fails; defaults scan chadkit artifacts only
chadkit language check --fix [paths...] # rewrite confident matches for git-diff review; never combines with --all
chadkit validate
chadkit doctor [--all] # health/drift reporting; --all sweeps every known install in one report
chadkit goals | chadkit tags # one line per goal or tag with its meaning; also shown in --goal and --tag help
chadkit goals qa # every goal in full with the gate's standing question, what the judge reads
chadkit model # where CHADKIT-MODEL.md is: the checkout's copy, this version's URL, latest
chadkit help --all # every command and subcommand's help in one dump, for adapters to point at
chadkit upgrade # config chadkit-version -> installed CLI; prints how to refresh the CLI
chadkit config init | show # the user-level config.yaml; add-install [path] [--read all], remove-install [path]
Later: goal add, task archive,
decision new, log rotate.
chadkit init writes chadkit/config.yaml,
chadkit/README.md, and chadkit/thoughts.md -
the last being the placeholder rule's one exception - and stops. A
re-run on an initialised project refuses; --force rewrites
the config alone, and a deleted README or thoughts.md stays
deleted. Every other artifact is created by the first command that
writes a record into it - task add creates
tasks.md, log add creates log.md
- in a new or existing repository alike. Scaffolding a full directory
would contradict both the placeholder rule under Minimal information model and the
inspect-first sequence under Bootstrapping
and migrating an existing project, and it would guess at answers
that only inspecting the project can give. A missing artifact is a valid
state, not a finding: validate must not report one.
Multi-valued options accept either repetition or a comma list
(--goal PG-001 --goal PG-003,
--goal PG-001,PG-003) and always write the canonical
single-bracket form defined under Tasks.
--date exists for adoption and migration: a task
completed before today, or an imported log record whose
PLR-YYYYMMDD-NN ID must reflect when the evidence arrived
rather than when it was recorded.
chadkit task mod changes only the bracket fields on a
task line, using explicit --add-* and
--remove-* verbs so a bulk pass cannot silently drop a
field. It does not rewrite task text; that is a human edit, and
requirement 6 below applies.
chadkit task archive writes to
chadkit/tasks-archive.md by default (or the config
override) and makes only the minimum necessary source/archive edits.
Requirements:
Until a tool exists, agents edit directly under the same discipline. When the same structured edit recurs, add the command rather than repeatedly teaching agents to edit Markdown.
validate grows in response to real
failure modes: duplicate or malformed IDs, references to nonexistent
IDs, needs dependency cycles, invalid log record types,
fields that are missing or that do not apply for a record's type,
malformed language.md, config-declared invariants. Do not
build a schema system before the project needs one. Discouraged
terminology is language check's job, and it warns rather
than fails.
doctor reports project health rather
than correctness. Keep it lightweight and add checks only for failure
modes the project actually cares about - for example ChadKit version
drift, overdue decision reviews, pending unverified claims, or a
configured review trigger that appears to have been skipped. Two checks
earn their place from the start. Because artifacts are created lazily,
doctor reports a project that has accumulated real work
with no language.md, so that the Ubiquitous Language
invariant is dropped by explicit override rather than by never getting
around to it. And because recording a tag is recommended rather than
required, doctor reports tags in use with no row in
language.md - a note, never a failure, which is the right
weight for a recommendation. Where a project declares a QA gate,
doctor reports a check script that is missing or not
executable, and names commits touching a gate path with no
QA-Gate: trailer since the declared start (up to ten, then
a count), so a skipped gate is noticed rather than remembered. Where a
task is waiting on a human, doctor lists it, as it does an
open task that cites no goal, since validation has nothing to judge it
against. If a project has no review trigger, gate, or enhanced decision
fields, doctor should not invent them.
A workflow is a repeatable way of doing work, not project state. Create one when the procedure repeats, a missed step has caused problems, an agent benefits materially from explicit sequencing, tools must be called in order, or the process should improve over time. Do not document one-off activities.
Assume workflows can improve. When an agent or tool demonstrates a
better procedure: run a bounded experiment if evidence is uncertain,
record the result as a t_experiment or
t_lesson, update the workflow if evidence supports it, move
mechanical steps into tools, remove obsolete human-only steps rather
than preserving ceremony, and keep human review gates where judgment,
risk, or accountability warrant them.
The goal is not to make AI imitate the human process. It is the most effective reliable process using humans, agents, and deterministic software for the parts each does best.
The QA gate is one project's workflow, the ChadKit repository's own, offered as the shape a gate can take: an independent adversarial examination a body of work takes before it lands, and the record it leaves.
workflows.qa-gate.checks in the project config, so the gate
runs one command rather than a list it is trusted to remember.t_qa record tagged #qa-gate,
related to the task, with the confirmed, refuted, and declined findings
and why, appended before the commit so the record lands in the commit it
stamps. A clean run appends nothing.QA-Gate: PLR-YYYYMMDD-NN naming that record or
QA-Gate: clean; doctor audits it (see Deterministic tools).#qa-gate task that makes it deterministic: a
test, a validate rule, a script.A project adopts the gate by writing its own
chadkit/workflows/qa-gate.md with these parts and its
specifics, declaring workflows.qa-gate in the project config, and adding the
QA gate term and #qa-gate tag rows to its
language.md. Which commits must take it is the project's
rule, stated in its workflow.
CLAUDE.md, Claude Skills, Codex instructions, and
equivalents are adapters. They point at project truth; they do not copy
it.
When asked to review, QA, or gate a change:
1. Follow `chadkit/workflows/qa-gate.md`.
2. Record a run that confirmed a finding: `chadkit log add --type t_qa --tag qa-gate --related PT-NNN --summary "..."`.
3. Follow terminology in `chadkit/language.md`.
4. Do not duplicate project truth in this file.
An adapter names commands, not their surface:
chadkit help --all is the whole surface in one dump,
chadkit task list, chadkit goals, and
chadkit tags are the state a session needs on arrival,
chadkit model is where the model is, and each stays current
without an edit to the adapter.
Adapters and skill descriptions must state when to consult an artifact, not merely where the file lives. Passive references are easy for agents to under-trigger on. When missing an artifact could materially reduce correctness, prefer explicit and somewhat assertive trigger language.
At minimum:
language.md whenever naming or renaming domain
concepts, project-owned APIs, data fields, goals, tasks, user-facing
concepts, or other terminology with semantic meaning.Descriptions should optimize for reliable triggering, not elegance. A short adapter that clearly says when to load project truth is better than a passive catalog of paths.
Before creating a ChadKit-specific skill or workflow for a common development activity, check whether an existing maintained Agent Skill already solves the problem adequately. Prefer composition and integration over copying a generic methodology into ChadKit.
Relevant overlap includes:
skill-creator - use it to
create, evaluate, and improve Claude Skills rather than inventing a
separate ChadKit skill-development methodology.claude-md-management -
useful for maintaining thin CLAUDE.md adapters, but durable
project knowledge should still be promoted into ChadKit truth instead of
accumulating in CLAUDE.md.project-artifact - useful as
a presentation/status output layer. It may summarize ChadKit truth; it
should not become a competing source of truth.A ChadKit workflow should add project-specific orchestration around reusable skills, terminology, truth, and deterministic state mutations.
Agents do not load every document. Default order:
chadkit/config.yaml when present (especially for
non-default paths or overrides);Targeted retrieval is grep-shaped:
grep '^- \[[ ?/]\] PT-' chadkit/tasks.md # open tasks, any open mark
grep '^- \[?\] PT-' chadkit/tasks.md # waiting on a human
grep '^- \[ \] PT-' chadkit/tasks.md # not started
grep -A12 'PLR-20260827-01' chadkit/log.md
grep -l 'status: accepted' chadkit/decisions/*.md
Task fields are comma-separated lists (see Tasks), so match on the delimiters rather than the
whole bracket. These patterns are exact at any position in the list and
need no word-boundary escape, which is not portable across
grep implementations:
grep -E '[[,]PG-003[],]' chadkit/tasks.md # tasks serving a goal
grep -E '[[,]#auth[],]' chadkit/tasks.md # tasks carrying a tag
grep -E '[:,]PT-002[],]' chadkit/tasks.md # tasks blocked on a dependency
grep '\[PG-003\]' is wrong: it silently returns nothing
whenever the goal is not the only value.
Never ingest the whole log or every decision record by default. Never
read chadkit/thoughts.md by default.
Large diffs are a project-quality problem, not merely a review inconvenience. Before finishing, inspect the diff and ask: did I change only what actually changed; did I rewrite or reformat unrelated documentation; did I duplicate information authoritative elsewhere; did I create a durable record for transient session context; could a tool make this mutation smaller next time?
Avoid: automatic session-summary dumps; repeated project descriptions across files; generated timestamps on untouched records; sorting or reflowing whole files; updating related documents "just in case"; copying workflow or policy text into agent adapters; storing raw AI reasoning or implementation narration.
An optional mechanism, but an important concept - a project may skip the record and still owe itself the question.
Single-loop learning improves execution while the
current goals and assumptions remain fixed: workflows adapt, tools
improve, and t_experiment and t_lesson records
accumulate.
Double-loop review asks a different question: are the goals, assumptions, constraints, and shape of the work themselves still right? Without that question, a project can become highly efficient at pursuing the wrong target. The terminology derives from Argyris and Schön.
A project may record a double-loop review as a t_review
log record when doing so adds value. There is no mandatory cadence,
trigger, or checklist.
Useful questions include:
If the project has a natural review trigger, it may declare one in the config. If not, omit it. A review that changes a goal edits the goal; a review that changes a consequential commitment may produce a decision record. The review record itself is evidence of the reassessment, not a second source of truth.
Use the terms single-loop learning and
double-loop review rather than fast-loop
and slow-loop; terminology consistency here is itself an
example of the Ubiquitous Language rule.
chadkit/config.yaml; in doc-only
mode, create it only when non-default paths or overrides would otherwise
be ambiguous.Adoption starts at the config alone. After a little real use, a project may have grown only this far, and that is a complete and valid state:
chadkit/
├── config.yaml # required tooled; optional doc-only
├── README.md
├── tasks.md # appeared when the first task was recorded
├── log.md # appeared when the first record was appended
└── language.md # appeared when the first term needed pinning down
Goals, decisions, and workflows appear the same way - when they
provide value or map to a useful existing project artifact, never in
advance of one. thoughts.md is the exception:
init seeds it so the first jotted idea has somewhere to go,
and a project that does not want it deletes it.
Migration here means consolidating a project's existing history into
ChadKit records - what the --date and Recorded
mechanics exist for. Migrating records between ChadKit
versions is the other sense: see ChadKit versioning while
experimental, where chadkit upgrade moves the config
and validate names each record still in an old format.
Deviation is expected when it improves the project. Good reasons: the repo has a clear equivalent; the project is too small for an artifact; an artifact's lifecycle warrants splitting; an external system is intentionally authoritative; a different structure produces smaller diffs or better agent reliability; experiments show a better approach.
Bad reasons: an agent prefers its habitual layout; a template says a file "should" exist; extra structure makes the repo look complete; duplication is convenient for one prompt.
Record recurring deviations in the config overrides or
as a decision record. Prefer extending shared formats to redefining
them; if incompatibility is necessary, make it explicit.
For now, use one ChadKit version for the shared
contract: the information model, shared record semantics, and tooling
expected to operate on them. Do not separately version
MODEL.md, the CLI, and schemas until a real
release-management need requires that separation.
ChadKit's first released version was 0.1.0, cut when
it was placed in its own repository and applied to the first real
project. Projects in tooled mode declare the version they follow in the
config (see The project config);
doctor reports when the installed CLI has moved past it,
and chadkit upgrade brings the config up to the installed
version. The reverse - a config ahead of the installed CLI - fails
validate, because a checker older than the records it is
judging passes blind. Refreshing the CLI itself is the user's action;
upgrade reads how the CLI was installed (PEP 610
direct_url.json) and names the reinstall command rather
than running it - or says none is needed, for an editable install that
runs live from its checkout.
ChadKit remains intentionally pre-1.0. We want permission to change our minds cheaply while real projects reveal which ideas work.
Guidelines:
Changes to ChadKit should be justified by observed or strongly anticipated failure modes, not by a desire to complete a hypothetical platform.
ChadKit borrows from existing work rather than claiming these ideas as novel.
Maintained external systems are ongoing sources of research and proven patterns. When GitHub Spec Kit, OpenSpec, Agent Skills, Superpowers, MADR, DDD practice, or another relevant model introduces an idea that could improve ChadKit, treat it as a candidate rather than assuming ChadKit's current design is superior.
Adopt, integrate, delegate to, or replace existing ChadKit behavior when an alternative solves the same problem more effectively without violating ChadKit's core constraints. Do not add an idea merely because another framework has it.
Evaluate candidate changes with these questions:
If a substantially better model emerges, ChadKit should be willing to shrink, integrate with it, replace part of itself, or stop owning a concern entirely.