Skip to content

Records

A record is one piece of project knowledge: a decision, a fact, a command, an open question. Records are what a brief is made of. Most records belong to exactly one project. Lessons and preferences can also live in your personal store. Every record is stored privately on your disk.

Terminal window
devtwin record add --pilot "$PILOT" --scope example --project example.atlas \
--kind decision \
--title "Use server-side search" \
--body "Filter on the server because the list can grow." \
--source file:README.md \
--request-id req_atlas_search_01 --apply
PartWhat it isLimits
KindWhat sort of knowledge this is. See kinds.One of twelve values
StatusHow much weight it carries. See statuses.Defaults to observed
TitleA one-line summary.1 to 200 characters, no line breaks
BodyThe detail, as plain text or Markdown.Up to 32,768 bytes
SourcesEvidence the record is based on. See sources.Up to 20
Request IDYour name for this write, used to make it repeatable.req_ plus 8 to 64 of a-z, 0-9, _, -

devtwin adds the rest: a record ID derived from the scope, project and request ID, hashes of the record and body, a creation time, and who recorded it.

KindUse it for
briefA short description of the project. Onboarding writes these from the README.
constraintSomething that must stay true, such as “the repository’s own AGENTS.md governs”.
decisionA choice that was made, and why.
mapWhere things live in the codebase.
commandHow to build, test or run something.
environmentA named environment, such as staging. Names only, never credentials.
factSomething observed to be true.
experimentSomething tried, and what happened.
handoffWhere a session stopped and what comes next. See Handoffs.
unknownAn open question that someone still needs to answer.
lessonAdvice that applies to some files, packages or topics. See Lessons and hooks.
preferenceHow you like to work. It follows you across projects, so it never lives in a project store.

Briefs list records in this order of kind, so constraints and decisions come before commands and facts, and open questions come last.

New records use schema 2, which adds three fields. Readers accept schema 1 and 2, and a schema 1 record is never rewritten.

FieldValuesRule
classfact or lessonA fact stays in its project. A lesson record always has class lesson.
reachpersonal, project or orgA fact has project reach. A preference never has project reach.
matchpaths, packages and topicsAt most 20, 20 and 10 entries. Only a lesson or preference with a match block reaches an agent through the hook.

Each reach has its own home:

ReachHomeHolds
projectThe project’s recordsEvery kind except preference
personalYour personal store, data/personal/records/lesson and preference. It is part of every scope’s lesson index.
orgThe scope’s org home, data/scopes/<scope>/org/records/lesson. No command writes here yet: for a scope with a hub, the hub holds the organization’s copy.
StatusMeaningIn the brief?
observedSomeone saw or decided this. The default.Yes
confirmedThe owner reviewed and confirmed it. Needs --reviewer.Yes, ahead of other records of the same kind
disputedSomeone disagrees; treat with care.Yes, and listed in disputedIds
proposedA draft awaiting review.No, only its ID and title under review

To confirm a record, pass a reviewer reference:

Terminal window
devtwin record add --pilot "$PILOT" --scope example --project example.atlas \
--kind constraint --status confirmed --reviewer github:your-handle \
--title "Node 22 or later" --body "The service runs on Node 22 LTS." \
--request-id req_atlas_node_01 --apply

Without --reviewer, the command fails with DEVTWIN_APPROVAL_REQUIRED. Reviewer references take the form email:, github:, slack:, linear: or handle: followed by an identifier.

Sources say where a record’s evidence came from. Pass --source once per source.

FormExample
file:<path>file:README.md, a path relative to the repository root
commit:<sha>commit:1791ed0 (7 to 64 hex characters)
record:<id>record:rec_94315938d3e27ab174b5665d
issue:<KEY-123>issue:ATLAS-42
user:<ref>user:github:your-handle
url:<https URL>url:https://example.com/adr/12, with no query string, fragment or credentials

File sources drive the freshness label in a brief. Files that onboarding read are stored with a hash, so the brief can say current while the file is unchanged and needs-revalidation once it changes. A file: source you add by hand carries no hash, so the brief marks it needs-revalidation. A file that onboarding did not read shows as unbound-source. See freshness.

Once written, a record never changes. When something changes, write a successor that supersedes it. You need the old record’s ID and its current hash, both of which appear in the brief:

Terminal window
devtwin record supersede --pilot "$PILOT" --scope example --project example.atlas \
--target rec_94315938d3e27ab174b5665d \
--expect-hash sha256:b411bd1f8c215be10b51c23b52ecac6614a9c3a1d627502e237f5f78e5988423 \
--kind decision --title "Use server-side search with paging" \
--body "Filter on the server and page results at 50 items." \
--request-id req_atlas_search_02 --apply

The old record drops out of briefs; the new one takes its place. Both stay on disk, so the history is never lost. The hash check makes sure you are replacing the version you think you are: if someone already superseded it, the command fails with DEVTWIN_STALE_VERSION.

You can pass --target and --expect-hash more than once to replace several records with one.

The record ID comes from the scope, project and request ID. That means:

  • Running the same command twice returns the existing record with "created": false.
  • Reusing a request ID with different content fails with DEVTWIN_CONFLICT.

So pick a new request ID for each new record, and reuse it only to retry.

Sometimes you want a record reviewed before it counts, for example when an agent drafts it. record propose writes a private proposal file instead of a record:

Terminal window
devtwin record propose --pilot "$PILOT" --scope example --project example.atlas \
--kind fact --title "Tests use node:test" \
--body "package.json declares node --test." --source file:package.json \
--request-id req_atlas_tests_01 --apply
{"ok":true,"command":"record propose","result":{"proposalId":"prop_ff03889979eda8c380fdaea8","path":"/private/tmp/devtwin-onboard.vgS21y/data/scopes/example/projects/example.atlas/proposals/prop_ff03889979eda8c380fdaea8.json"}}

A proposal does not appear in briefs. When you have reviewed it, publish it:

Terminal window
devtwin record publish --pilot "$PILOT" --scope example --project example.atlas \
--proposal prop_ff03889979eda8c380fdaea8 --reviewer github:your-handle --apply

Publishing turns the proposal into an observed record. The bundled devtwin-record skill works this way: the agent proposes and you publish. See Capture decisions.

devtwin scans every title and body for text that looks like a secret, such as private keys, authorization headers, key-and-token assignments, JWTs, and GitHub, AWS or Slack tokens. A match fails with DEVTWIN_SENSITIVE_CONTENT and nothing is written:

{"ok":false,"command":"record add","error":{"code":"DEVTWIN_SENSITIVE_CONTENT","message":"Sensitive content refused","details":{"types":["credential-assignment"]}}}

This check is a tripwire, not a guarantee. Never store credentials in records; keep them in your password manager or credential store.