Writing with Intent
Entries are plain objects
In v0.2, entries are Record<string, unknown> — any JSON-serialisable shape you want:
await field.write({
entry: { topic: 'market-size', cagr: '23%', source: 'gartner' },
intent: 'validate market size assumption for go-to-market strategy',
agent: 'researcher',
})
There is no schema to conform to. The Field stores what you give it, attaches metadata, and makes it available to other agents.
The topic convention
entry.topic is not enforced by the protocol, but it matters a great deal in practice:
- Attunement uses it.
field.attune({ agent, topic })matches entries byentry.topicand gives them full relevance weight (0.60 out of 1.0). - Conflict detection uses it.
field.reckon()only compares entries with the sameentry.topicfor conflicts.
Use topic to group related entries. Think of it as the subject of the entry — what it's about, not how it's typed.
// Good — consistent, precise topics
{ topic: 'competitor-pricing', vendor: 'acme', price: '$49/mo' }
{ topic: 'competitor-pricing', vendor: 'acme', price: '$39/mo' } // ← conflicts with above
// Less useful — topic too broad to drive useful attunement
{ topic: 'research', content: '...' }
Intent is mandatory
Every write requires an intent string explaining why this entry exists. The default minimum is 10 characters. Shorter intents are rejected with INTENT_TOO_SHORT.
// Rejected — intent too vague
await field.write({
entry: { topic: 'pricing', price: '$49' },
intent: 'pricing', // ← INTENT_TOO_SHORT
agent: 'researcher',
})
// Accepted
await field.write({
entry: { topic: 'pricing', price: '$49' },
intent: 'competitor pricing observed directly on their website',
agent: 'researcher',
})
Intent is the answer to why was this written, not what is it. Any agent — including a human reviewing the field later — can read the intent and immediately understand the reasoning.
The agent field
agent is optional but strongly recommended:
- It drives the self-filter in
attune()andreckon()— agents don't see their own committed entries in results. - It is required for
retract()— you can only retract your own entries. - It improves relevance scoring via role matching when the agent is registered.
What the Field auto-generates
When you call write(), the Field adds:
| Field | Description |
|---|---|
id | A ULID — globally unique, lexicographically sortable by time |
timestamp | Unix milliseconds at write time |
epoch | A monotonic counter scoped to this Field instance |
status | "committed" immediately |
Status lifecycle
Every entry has a status reflecting where it is in its lifecycle:
| Status | What it means |
|---|---|
committed | Live and visible to other agents via attune() / reckon() |
draft | Private to the author until committed (see Draft Lifecycle) |
retracted | Withdrawn by the author — excluded from attune() / reckon() |
superseded | Replaced by a newer entry — excluded from attune() / reckon() |
read() returns all entries including retracted and superseded ones (with their status). Use it when you need the full history.
Next
- Attunement — how other agents receive your entries
- Draft Lifecycle — write privately before publishing
- Managing Entries — retract and supersede