Lab Notes Ledger Write Contract
This standard defines how any writer (Web Admin UI, CLI, API clients, AI agents, imports) must create and update Lab Notes so the system remains deterministic and non-haunted.
Core Model (v2)β
lab_notes = identity + metadata + pointers
lab_note_revisions = append-only truth for content + canonical frontmatter
Key invariantsβ
- Content truth lives in
lab_note_revisions. lab_notes.current_revision_idpoints at the latest draft content.lab_notes.published_revision_idpoints at the published content snapshot.- Publishing is explicit; drafts do not silently become public.
Identity Rulesβ
Two identities, one truthβ
- Public identity:
(slug, locale) - DB identity:
id
Required uniquenessβ
(slug, locale)must be unique (enforced by DB index).
Client state keyβ
- Use
${slug}:${locale}for editor caches. - After first save, store
idand use it for subsequent updates.
Write Contract: What a βSaveβ Must Doβ
A writer performing a save MUST:
- Create exactly one new revision in
lab_note_revisions. - Update exactly one pointer in
lab_notes:current_revision_id = <new revision id>
- Never modify
published_revision_idas part of a normal save. - Always bump
lab_notes.updated_at.
Required revision fields (minimum meaningful set)β
lab_note_revisions must include:
id(uuid)note_id(lab_notes.id)revision_num=MAX(revision_num)+1per notesupersedes_revision_id= previouslab_notes.current_revision_id(orNULLfor first)frontmatter_json(stringified JSON; canonicalized frontmatter)content_markdown(markdown body)content_hash(sha256 of canonical representation)schema_version(e.g."0.1")source(see Source Semantics)intent,intent_version(see Intent Semantics)auth_type,scopes_json(see Auth Semantics)created_at(db default ok)
Canonical hash inputβ
To ensure stable dedupe and diffs, content_hash should be computed from:
frontmatter_json(stringified JSON)- delimiter line
\n---\n content_markdown
Publish Contractβ
Publishing is the only operation that changes published pointers.
A writer performing a publish MUST:
- Set:
status = 'published'published_revision_id = current_revision_id
- Ensure
published_atis set (auto-fill if missing). - Bump
updated_at.
Unpublish Contractβ
A writer performing an unpublish MUST:
- Set
status='draft' - Clear
published_revision_id - Clear
published_at - Bump
updated_at
Source Semanticsβ
source describes the channel of origin:
cliwebapiimport
Human vs AI is represented via audit events, not source.
Auth Semanticsβ
auth_type:
human_sessionlab_token
Scopes should always be recorded.
Intent Semanticsβ
Use stable, action-shaped intents:
admin_save,admin_publishcli_save_draft,cli_publishapi_saveseed_marker_note
Audit Semanticsβ
Every revision SHOULD emit a lab_events row with:
actor_type:human | ai | systemactor_idrevision_id,note_idintent,auth_type,scopes_json
Regression Testsβ
- Draft nulls publish metadata
- Publish pins content
- Slug+locale uniqueness
Definition of Doneβ
When this contract is followed:
- Drafts are safe
- Published content is stable
- Audits are complete
- The database becomes boring (highest compliment)