This is the development record, published as it was written. It is amended by revision, including where the work went wrong. Nothing here has been rewritten for the web.
Records
The conventions for decision records in this project. They exist so the corpus stays readable when it is merged with the others and published, and so an agent can file a correct record without asking.
The rules are enforced where they can be. make records runs the Perspicuity checker and scripts/check_actors.py, and make ci runs it too. The actor check is ours, not the checker's: the checker validates front matter, and the naming rule lives in prose, so it needed something that reads prose. What each one covers is in What is checked, and what is not.
Where records live
| Record | Path | Owns |
|---|---|---|
| The project record | [RECORD.md](../RECORD.md) | The continuing account: current position, the inherited basis, the plan, the unit table, the review |
| Sub-decisions | docs/records/YYYY-MM-DD-<slug>.md | One consequential choice, its alternatives, its comparison and its review |
The project record is the parent of every record in this repository. A sub-decision never replaces it; it hangs off it.
What is inside the checked set
make records reads RECORD.md, AGENTS.md, CONTEXT.md, docs/ACTORS.md, docs/RECORDS.md and docs/ARCHITECTURE.md, plus every file under docs/records/. In those files the actor naming rule is enforced on prose, not just on front matter. Outside that set:
| Outside the set | Why | Consequence |
|---|---|---|
docs/RUBRIC.md | Generated from the catalogue in eligibility/checks.py, and its subject is a site, not an actor | Edited by make rubric, never by hand |
docs/KICKOFF.md, TODO.md, README.md, docs/DEPLOY.md, docs/ADDING-A-CHECK.md | Scaffolding and guides built *from* this document, kept in step by hand | Bring them along when a rule here changes; they are not scanned |
build/ | Generated output | Not a source |
<!-- actor-naming: discusses-rule --> Named actors and the history of the Primary label are in ACTORS.md. <!-- actor-naming: end -->
Naming
File name: docs/records/YYYY-MM-DD-<slug>.md
YYYY-MM-DDis the date the choice was made, not the date the file was tidied.<slug>is 2–5 lowercase words joined by hyphens, naming the decision, not the activity.
Record id: ae- followed by the file name without its extension.
docs/records/2026-09-19-crawler-policy-stance.md
-> file stem: 2026-09-19-crawler-policy-stance
-> id: ae-2026-09-19-crawler-policy-stance
That rule is mechanical on purpose: the id and the file can never disagree, and a script could check it. The project record keeps its original id, agent-eligibility-2026-09-19. Ids are stable. Never rename an id to match a later convention; supersede it with a new revision.
Title: a short intention phrase, sentence case — the decision, phrased as something done or chosen. Not a topic.
| Good | Bad | Why |
|---|---|---|
Set the crawler policy stance | Crawler policy | A topic is not a decision |
Choose the report URL shape | URLs | Names what was chosen |
Refuse to audit domains we cannot verify | Safety notes | States the position taken |
2026-09-19-validation-fixes.md | notes.md, updates.md, misc.md | An activity log is not a record |
Subject prefixes. Start the slug with one of these when one fits. It is a convenience for browsing, not a gate — if none fits, use a descriptive slug anyway.
| Prefix | Covers |
|---|---|
rubric- | Anything that changes a check, the weighting or the claim boundary |
market- | Payer, pricing, outreach, positioning |
tech- | Architecture, dependencies, storage, deployment |
process- | How we work: records, review, gates, delegation |
data- | Privacy, retention, what we store or fetch |
Actors
<!-- actor-naming: discusses-rule --> **Name every actor. Never write "the agent", "the AI", a job title such as Primary or Coordinator, or a model name.** The reason is in ACTORS.md, which also holds the roster and the rules for choosing a name. <!-- actor-naming: end -->
- The coordinator names itself in its first session, before filing anything, and records the
name in ACTORS.md. It persists across every session it works in.
- The coordinator names its workers when it makes the assignment; the name travels with the
grant and appears in the return.
- Write
Rook (coordinator)on first mention, thenRook. The role says what the actor did in
this record; the name says who they are.
- An assessor is never the author, and separate names make that checkable.
- David is David. No agent takes his name, or a living person's.
This is not a request. scripts/check_actors.py fails the build on one of five listed role words — the retired label plus <!-- actor-naming: discusses-rule -->Coordinator, Assistant, Admin and Manager<!-- actor-naming: end --> — anywhere in the checked set, and it allows a page to quote them only inside a block opened by an HTML comment reading actor-naming: discusses-rule and closed by one reading actor-naming: end. A marked block is recognised only in ACTORS.md, this file, docs/records/ and RECORD.md — the places whose subject *is* the rule, or its history, and which cannot be written without naming what it forbids — and a marker may sit inline in a sentence, where it covers that line alone. Three guards keep the exception from swallowing the rule it serves:
- a block that quotes nothing forbidden is an error, tested block by block, so a stale exception
cannot ride on a live one;
- a block may not cover an actor-credit field (
Work owner:,Owner:) or a roster row — naming
an actor is not stating the rule;
- the check is a denylist, not a parser, so a role word outside the five passes. Add it to
FORBIDDEN when it occurs; ACTORS.md says the same and records the known false positive on sentence-initial words.
When a record is required
Required when the choice:
- changes a published claim, the rubric, or a check's wording or weight;
- adds, removes or upgrades a dependency;
- changes what we fetch, store or publish — safety and privacy both live here;
- sets a precedent that later work will follow;
- changes authority, ownership, or creates a review obligation;
- selects among credible alternatives whose reason would otherwise be lost.
Not required, and better kept in the project record's action account: typo fixes, refactors with no behavioural change, formatting, and new tests that apply an existing rule.
The test is not "was this hard?" It is "would a stranger, later, need to know why?"
The skeleton
Copy this. Omit sections that carry nothing — a short honest record beats a padded one.
---
format: perspicuity-work/1
id: ae-YYYY-MM-DD-<slug>
revision: 1
skill_version: 0.4.0
updated: YYYY-MM-DD
created_at: "YYYY-MM-DDTHH:MM:SS-06:00"
updated_at: "YYYY-MM-DDTHH:MM:SS-06:00"
record_status: open
work_status: not_started
# next_check: YYYY-MM-DD only when a timed obligation exists
---
# <Intention>
## Current position
Parent: [RECORD.md](../../RECORD.md), revision N.
Principal: David. Decider: <who>.
Work owner: <named actor>.
Decision: <pending | recommended | selected | inherited | none> — <one sentence, with the basis revision>.
Work scope: <the delivery this record owns>.
Work: <what has actually been done, with exact revisions>.
Outcome: <observed result, or explicitly unknown>.
Next: <named actor / action / trigger>.
Dependency: <the missing input and its owner, when work waits>.
## Frame and Decide
<The underlying problem, the adopted frame, and any reframe with its reason.>
| Fundamental objective | Source | Measure, direction and horizon |
| --- | --- | --- |
| Material condition | Type | Basis | Affects |
| --- | --- | --- | --- |
<Register this basis before evaluating alternatives.>
### Alternatives and consequences
<Credible complete courses of action, including the current course. A compact table.>
<The decisive tradeoff, the preference it rests on, and what would warrant reconsideration.>
### Selection
<`selected_at`, decider, basis revision, the choice and the reason. Or the pending question
and its owner.>
## Act
<The plan registered before implementation, with acceptance criteria. Actual evidence after.>
## Review
| Criterion | Evidence source | Owner, window or trigger | Finding | Response |
| --- | --- | --- | --- | --- |
<Delivery acceptance stays separate from evidence of later benefit.>
## Changes
<Revision, time, change, source, reason, affected work.>
Every label above is load-bearing. The checker reads Work scope, Next and Dependency out of ## Current position, and reads record_status and work_status from the front matter, so renaming one of them turns a clean record into a failing one. Parent: and the title are not read by any script; they are what makes the corpus navigable, and the first line of the skeleton is the one most often dropped.
Two things about the skeleton are easy to get wrong:
- Its relative links assume the record's own location. A record lives in
docs/records/, so
its parent is ../../RECORD.md and ACTORS.md is ../ACTORS.md. A link copied into RECORD.md itself needs one fewer ../, which is the mistake to check for first when a published page 404s.
- **
next_checkis omitted unless a timed obligation exists.** Most sub-records have none, and
a date invented to fill the field is worse than an empty one.
What is checked, and what is not
make records fails on any of these, so fix them before committing:
- A
## Current positionheading must exist, andWork scope:,Next:and (when work
waits) Dependency: must appear inside it.
record_statusmust beopenorclosed;work_statusmust be one ofnot_started,
active, waiting, submitted, accepted, stopped.
revisionmust be a positive integer, andidmust carry no spaces.- A closed record must not carry a
next_checkdate, and must haveacceptedorstopped
work plus a closure explanation.
- Ids must be unique across the corpus.
- Actor naming, from
scripts/check_actors.py: none of the five listed role words in the
checked set outside a marked block; no marked block that has gone stale or that covers an actor credit; and no marked block in a file outside ACTORS.md, this file, docs/records/ and RECORD.md. It is a denylist: it does not establish that every actor is named, only that the words which have been used instead of a name are absent.
A clean result establishes none of this: it does not check whether the reasoning is any good, whether the authority was real, or whether the work was done. It is a spell-checker, not a reviewer.
Two further limits are worth knowing before relying on a green run:
- The actor check reads a fixed list of files. A record may be filed elsewhere and simply not be
read; the path rule above is what keeps records inside the checked set.
scripts/check_records.shskips the front-matter check when
/home/david/.codex/skills/perspicuity/scripts is absent, and says so in its output. A skip is not a pass: make records still exits 0, and the tell is the note plus the absence of the markdown_files: summary line. The actor check still runs, so a skip leaves part of the set unchecked rather than all of it.
- That skip is the normal case in CI. The GitHub workflow runs
scripts/ci.shon a bare runner,
which has no Perspicuity skill installed, so the front-matter check has never run there — only on David's machine, and only for as long as that path exists. Making it portable is a real choice with a real cost, recorded as pending in TODO.md rather than settled here. Nothing in the corpus is unchecked on the machine where records are filed; what is missing is the second pair of eyes.
Registration order
Save the basis before the work that depends on it, and the choice before dependent action. This is not a formality: a record written afterwards records what you decided, not what you knew, and the difference is the whole value of the corpus.
That creates a problem the rule alone does not solve, because a record is one file and the work it governs happens after it. The convention here is two moments in the life of the same file:
| Moment | What exists | When |
|---|---|---|
| Registered | The record's identity, ## Current position, the frame, and the choice — everything the dependent work needs to be reviewable. Committed before that work starts. | Before dependent action |
| Filed | The same file with ## Act evidence, ## Review findings and a ## Changes entry. Work status moves to submitted, or active while it runs. | After the work |
A registered record with work_status: active and an empty ## Act is a correct state, not an unfinished one. What is not correct is the reverse: creating the file at the end and writing the decision date from memory. When the documentation time differs from the decision time, say so in the record rather than letting the header imply otherwise — the project record's own "Registration order" note is the worked example.
When a later finding changes an earlier basis, do not edit the earlier revision into agreement. Add a revision, name what changed and why, and preserve the earlier one.
Lifecycle of a sub-record
Filing is not the end of the file's obligations. The parts that are easy to forget:
- The parent index is updated when the sub-record is registered, naming the record, its
owner and its state. An index that lists a record nobody filed, or omits one that exists, is the defect a reader notices first.
- The sub-record states who assesses it. Worker completion is not acceptance, and a record
whose author is also its only assessor says so and says why.
- **It stays
openwhile an owed observation remains**, even after its delivery is accepted.
accepted work plus open record status is the normal state for anything with a later question attached.
- It closes only when every obligation is met, cancelled or transferred, with a
Closure
naming the actor, date, reason and evidence, and with no next_check left in the header.
Publication hygiene
Assume every record may be published on perspicuity.ai. Never put in a record:
- credentials, tokens, API keys, or a
.envvalue; - a customer's, supplier's or correspondent's personal information;
- private commercial terms, or anything under an agreement that forbids disclosure;
- a real person's private contact details.
A record may name a public domain, a public source and a public price. It may name an internal path. When in doubt, describe the fact and leave the identifier out.
Review obligations
A record that promises later observation carries the question, the evidence source, the owner and a date or a trigger. next_check in the header is the earliest such date; the dashboard reads it, and a record with no timed obligation simply omits it.
Delivery acceptance is not benefit. "The check shipped" and "a supplier paid" are different findings, and the corpus is only honest if they stay separate.
Delegated work
A worker receives the parent basis, the objectives, the exact grant and the return destination, and a name for the assignment. It is the coordinator's job to assign that name, at the moment of the assignment, before the worker starts; an unnamed return is a defect in the grant, not in the return.
The return carries its own decision basis, or an explicit statement that it made no new choice, plus its output revision, its checks, its failures and anything unresolved. It is attributed to the worker's name, not to a model.
The assessor is named too, and is never the author. When the coordinator both commissioned the work and assesses it, the record says so, and an independent check is commissioned when the consequence warrants one. Worker completion is not acceptance.
How this record connects
It builds on or points to: RECORD, ACTORS.
It is referenced by: Agent instructions, Context, Actors, Make the record conventions followable and enforce the actor rule.