Writing with Intent
Entries are plain objects
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.
Confidence is optional
As of v0.3, write() accepts an optional confidence block — how sure the writing agent is about what it just recorded:
await field.write({
entry: { topic: 'market-size', cagr: '23%' },
intent: 'estimate from a single analyst report, not yet cross-checked',
agent: 'researcher',
confidence: { score: 0.4, reason: 'single source, unverified' },
})
score is a required number in [0, 1] when confidence is provided; reason is optional but encouraged. Confidence is informational only — it never influences attune() ranking or reckon() conflict detection. A high-confidence entry and a low-confidence one that contradict each other still surface as a conflict; deciding which to trust is the reading agent's job.
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 Lamport logical clock value, monotonic within the Field and comparable across processes on a shared durable adapter |
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