About the event contract
Every line in the Log is one event, and every event follows one contract. A single schema defines every valid line. Each line is checked against it before it is written, and again before it is sent or read back. A line that does not fit is never written.
The contract is pure: building and checking a line touches no file, network or process. It ships in the @attalabs/ package, with one schema per family, one for the header, and one for the whole line.
This page states the rules. For every field of every event, see Events.
How it works
Each line has three parts:
- The header (
metaandsubject): who wrote the line, when, where, and about what. The harness builds it. A caller passes only the event itself and can never set its own header. - The family and event (
kindandevent): what happened. - The family's own fields: the details of that event.
Every line reads, in order: meta, subject, kind, event, duration_ms (optional), payload, then the family's fields. payload is always an empty object {}; families carry their content in named fields.
Families
kind is one of ten families. It is a closed list. event is one of a fixed set within each family.
| Family | Events | Has a producer |
|---|---|---|
dispatch | dispatched, outcome_received, dispatch_failed | Yes |
dev_review_loop | loop_started, round_started, gate_result_read, verdicts_read, findings_, stop_, paused, resumed, cancelled, unpushed_, round_ended, journal_, infrastructure_, driver_heartbeat, driver_exited | Yes |
forge_write | validated, refused, written | No, kept in the schema only |
gate | summary, checked | Yes |
operation | completed | Yes |
usage | observed | Yes |
role_attempt | attempted | Yes |
handoff | raised, resolved | Yes |
effect | verified; attempted and observed in the schema only | verified only |
custom | recorded | Yes |
The old families command and tokens are refused by name: command became operation, and tokens became usage.
The header
The header has a version, meta.schema. Each version adds fields to the one before.
| Version | Adds |
|---|---|
1 | ts, run_id, seq, repo, vinaya, doctrine, host, machine |
2 | event_id, process_id, actor_id, lineage { run, attempt, parent }, input_, provenance |
3 | work { ref, repo, change, revision }, flow { id, version }, runtime, source |
subject always holds issue (null when none) and role; optionally pr, sha, round and objectives_.
- 1
New lines are always schema 3
The harness writes
schema: 3on every new line. Versions1and2exist only so old lines keep parsing. - 2
Missing data is null
Data the harness cannot find is
null, never invented. The one default isflow.id, which readsvinayawhen unset. - 3
Foreign values pass through unchanged
subject.roleis one of the known roles, orunattributed.meta.actor_id,work.refandflow.idare opaque, so an actor from another tool, a work reference that is not an Issue, and a flow that is not Vinaya are written and read back as they are. - 4
Unsafe run ids are refused
run_idmust match^[A-. Any other value is refused when the line is written.Za- z0- 9_ . - ]{1,128}$
Strictness
- No extra keys. Every object is strict. An extra key anywhere, including inside
payload, fails the check. - A failing line is dropped. It is not written, and the harness warns once per process.
- Closed lists. Every list of values is closed, except where the schema keeps a free string on purpose:
severity_scale,paused.(a code token),reason_ code operation,usage.sourceandrole_.attempt. actor - Bounded strings.
| Value | Limit |
|---|---|
confidence_ | 280 characters |
Finding description | 1000 characters |
| Code tokens | ^[A- |
| Custom event text | 500 characters |
Compatibility
The schema only grows, so a line written today can still be read tomorrow, and a line written long ago can still be read today.
Compatibility is proven by tests that replay saved example lines of every version.
Roll out a schema change
The Log server is strict about unknown keys: a line carrying a field the server does not know is stored as an unknown-version record. So the order matters.
- 1
Deploy the server's schema first
Redeploy the Log server with the new schema: run
bunx wrangler deployfromapps/log-server. - 2
Then release the CLI
Release the CLI version that writes the new fields only after the server knows them.
- 3
Check that they match
Run
vinaya log selftest, orvinaya doctorwithlogs.readHeadersset. Both compare the server's schema digest with the CLI's. A mismatch or a missing digest is awarnin doctor and aFAILin selftest, and the message names the redeploy command.
Configuration
There is nothing to configure. The contract is fixed. The only way to add event names is logs.events, which adds names inside the custom family; see Custom events.