Skip to content

docs(events): annotations are runtime-written; the agent path is not built (#90) - #492

Open
bricef wants to merge 1 commit into
mainfrom
docs/annotations-trim
Open

docs(events): annotations are runtime-written; the agent path is not built (#90)#492
bricef wants to merge 1 commit into
mainfrom
docs/annotations-trim

Conversation

@bricef

@bricef bricef commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Closes #90 by trimming, per the decision recorded there.

The committed schema advertised the annotations layer as "commentary from the producing agent". No agent can write one — no built-in, tool or reducer intent, and nothing in an agent's prompt or tool surface mentions the channel. The 2026-07-09 review called it "a small honesty trap"; it works on agents reading the docs, which is how it was found.

The layer isn't empty — it's host-owned

That reframes the fix. Event::annotate is load-bearing; every writer is runtime code:

writer keys
control_plane/dispatcher.rs, control_plane/advisory_watch.rs dead_letter_*
worker/reducer/runner/llm.rs flags (context-pressure warning, #76)

Four of the five well-known keys — notes, confidence, reasoning, sources_considered — have no writer at all. So this isn't deleting a capability; it's describing the one that exists.

What changed

  • docs/design/committed/event-schema.md — the layer table's "Written by" column now says runtime, not producing agent. The Annotations section leads with the fact that agents have no write path. The well-known-keys table gains a Status column so shipped and reserved are distinguishable at a glance.
  • docs/adrs/README.md — ADR-0016's index row notes that the annotation operations it names aren't built.
  • events.rs — the annotation_keys rustdoc made the same claim; it now says reserved-not-available and points at the schema doc.

The licence for editing a committed doc is that folder's own rule: "If a committed doc contradicts the code, one of them is wrong — fix whichever it is." Here the doc was.

What is deliberately preserved

The intent, in the two places built to hold it:

  • docs/design/aspirational/inter-node-contracts-and-event-layers.md is untouched. It is explicitly design-ahead, and it is where the annotation registry and learning loop live.
  • ADR-0016's body is untouched, because docs/adrs/README.md states ADRs "aren't rewritten as the system evolves (later ADRs supersede earlier ones; guides track the current state)". The current-state note goes on its index row instead — the same treatment ADR-0011's and ADR-0027's rows already carry.

The schema doc gains a "When the agent write path arrives" section recording the decision: it is consumer-driven — the write path lands when something reads it. Building the producer first gives agents somewhere to write that nothing consumes, and the consumer barrier means no agent ever reads annotations back. ADR-0016 still governs the shape it must take (typed operations, never annotations.set(key, value)).

It also records that reasoning needs an answer before any write path opens: it is chain-of-thought, annotations ride every event, and the log is retained — a retention decision, not plumbing.

Recorded, not resolved

The flags writer emits an object ({"context_pressure": "…"}) where the schema specifies an array of strings. Annotations is Map<string, JsonValue>, so nothing catches it. Which shape is right is a decision, so it's written down in the doc and the rustdoc and left on #90 rather than silently picked here.

Verification

just quality, just lint-docs, RUSTDOCFLAGS="-D warnings" cargo doc, and 652 fq-runtime lib tests — all green. Doc comments only, no behaviour change.

events.rs is on a file-size budget and the additions tripped it; the prose was tightened to fit rather than raising the budget.

Note for reviewers: just check-links reports 0 relative links checked when run from a .claude/worktrees/* worktree — the script skips any path containing .claude, so the gate is vacuously green in the layout this repo uses. The 41 relative links in the changed files were verified separately. Fixed under its own PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Qc4GGVQbQ7nT2aqYtjP4Mh

…built (#90)

The committed schema advertised annotations as "commentary from the
producing agent". No agent can write one — there is no built-in, tool
or reducer intent for it, and nothing in an agent's prompt or tool
surface mentions the channel. The 2026-07-09 review called it "a small
honesty trap", and the trap works on agents reading the docs, which is
how it was found in the first place.

Trimmed rather than wired, and deliberately without discarding the
design.

## What is now said

`docs/design/committed/event-schema.md` describes the layer as it is:
runtime-written, with the reserved vocabulary marked per key. Four of
the five well-known keys have no writer at all; `flags` has one and it
is the host's context-pressure warning. The `dead_letter_*` keys the
dispatcher and advisory watch write are named as runtime bookkeeping so
the reserved list is not mistaken for the whole story.

That folder's own rule is the licence for this edit: a committed doc
"describes the system as built... If a committed doc contradicts the
code, one of them is wrong — fix whichever it is." Here the doc was.

## What is preserved

The intent, in the two places built for it. The aspirational design
(`inter-node-contracts-and-event-layers.md`) is untouched — it is
explicitly design-ahead and is where the annotation registry and
learning loop live. ADR-0016 is untouched too, because ADRs are
point-in-time records that "aren't rewritten as the system evolves";
its index row in `docs/adrs/README.md` gains the current-state note
instead, which is how ADR-0011's and ADR-0027's rows already work.

The schema doc gains a "When the agent write path arrives" section
recording the decision: **consumer-driven — it lands when something
reads it.** Building the producer first would give agents somewhere to
write that nothing consumes, and the barrier means no agent ever reads
annotations back. It also records that `reasoning` needs a retention
answer before any write path opens: it is chain-of-thought, annotations
ride every event, and the log is kept.

## Also recorded, not resolved

The `flags` writer emits an object where the schema specifies an array
of strings. `Annotations` is `Map<string, JsonValue>` so nothing
catches it. Which shape is right is a decision, so it is written down
in both the doc and the rustdoc and left on #90.

No behaviour change. Doc comments only; `events.rs` stays inside its
size budget (tightened prose rather than raising it).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qc4GGVQbQ7nT2aqYtjP4Mh
@github-actions

Copy link
Copy Markdown

Module coupling

Module coupling changed in this PR — see below.

What changed

fq-runtime

Module Production lines Fan-out Fan-in Dependencies
events 1,671 → 1,672 (+1) 3 14
Full coupling table

fq-cli — 32 modules

Module Production lines Fan-out Fan-in Depends on
(root) 1,330 21 0 agents, cli, connections, control, control_commands, costs, dead_letters, doctor, edge_identity, events, invocations, listeners, operator_surface, pricing, project, recovery, resume, status, trigger, version, workers
operator_surface 741 9 3 active_report, control_commands, cost_report, dead_letter_atom, dead_letter_requeue, doctor_report, event_atom, status_report, trigger_command
cli 575 0 13
trigger_command 558 0 3
event_atom 546 0 2
events 459 3 1 cli, edge_call, event_atom
invocations 418 4 1 cli, edge_call, operator_surface, resume
dead_letter_atom 413 0 2
status 369 2 1 cli, edge_call
dead_letter_requeue 349 1 1 trigger_command
recovery 345 0 1
connections 344 1 2 cli
resume 237 0 3
control_commands 220 0 2
doctor_report 205 0 2
dead_letters 202 4 1 cli, dead_letter_atom, edge_call, trigger_command
cost_report 164 0 2
doctor 164 3 1 cli, doctor_report, edge_call
workers 159 3 1 cli, edge_call, operator_surface
edge_call 149 2 10 cli, connections
control 139 2 1 cli, edge_call
agents 115 2 1 cli, edge_call
status_report 109 1 1 version
project 99 0 1
active_report 83 0 1
costs 82 3 1 cli, cost_report, edge_call
listeners 80 1 1 resume
edge_identity 77 0 1
trigger 65 2 1 cli, edge_call
pricing 54 0 1
version 41 0 2
bin 10 0 0

fq-dashboard — 4 modules

Module Production lines Fan-out Fan-in Depends on
render 1,517 0 2
pages 889 1 0 render
fixtures 840 1 0 render
(root) 344 0 0

fq-edge — 8 modules

Module Production lines Fan-out Fan-in Depends on
auth 406 0 3
client 377 3 0 auth, service, wire
server 281 4 1 auth, registry, service, wire
registry 251 1 2 wire
testing 207 4 0 auth, registry, server, wire
wire 130 0 5
(root) 39 0 0
service 17 1 2 wire

fq-lint — 4 modules

Module Production lines Fan-out Fan-in Depends on
analysis 567 0 1
(root) 555 0 0
coupling 394 1 0 analysis
ratchet 216 0 0

fq-ops — 5 modules, 1 cycle group(s)

Module Production lines Fan-out Fan-in Depends on
model 704 1 2 opid
opid 486 1 3 model
registry 314 2 0 model, opid
fixtures 223 1 0 opid
(root) 43 0 0

fq-runtime — 26 modules, 1 cycle group(s)

Module Production lines Fan-out Fan-in Depends on
worker 8,584 10 7 agent, bus, events, llm, mcp, policy, pricing, tools, trigger, validation
control_plane 5,945 10 3 agent, bus, config, dead_letter, events, llm, pricing, trigger, watermark, worker
mcp 1,850 4 1 agent, prompt, tools, validation
events 1,672 3 14 agent, llm, worker
views 1,465 6 2 agent, control_plane, db, transcript, watermark, worker
agent 1,400 2 8 events, worker
bus 822 1 5 events
config 758 4 2 llm, paths, pricing, worker
llm 757 2 4 config, events
trigger 542 5 2 agent, bus, dead_letter, events, views
transcript 522 2 2 events, worker
db 461 2 1 control_plane, worker
pricing 410 1 3 events
surface 372 2 0 health, views
turn 314 2 0 events, transcript
policy 288 2 1 agent, validation
prompt 234 1 1 events
tools 213 1 2 events
agent_view 177 2 0 agent, events
dead_letter 157 1 2 events
watermark 155 0 2
health 153 2 1 bus, control_plane
event_tail 145 2 0 bus, events
validation 104 0 3
(root) 91 0 0
paths 59 0 1

fq-store — 18 modules

Module Production lines Fan-out Fan-in Depends on
index 849 0 2
cli 842 3 0 fs, grants, service
grant_log 742 2 2 grants, index
fs 442 0 1
tokens 399 2 1 grant_log, grants
grants 392 0 4
conformance 342 0 0
verify 317 0 1
gate 308 3 0 grant_log, grants, tokens
service 279 0 1
repository 234 0 0
(root) 185 0 0
audit 179 2 0 index, verify
collector 87 0 0
error 75 0 1
cid 55 1 0 error
stats 50 0 0
bin 6 0 0

fq-tools — 4 modules

Module Production lines Fan-out Fan-in Depends on
builtin 1,323 1 0 tool
sandbox 363 0 1
tool 97 1 1 sandbox
(root) 16 0 0

Advisory — this gates nothing. Measured at 0656fa915feb against the merge base, by the same fq-lint build on both sides.

Edges are crate::/super:: paths in production code, between a crate's top-level modules. Re-exports and trait-method calls resolve without naming a path, so every count is a floor. ⚑ marks fan-in and fan-out both ≥ 4. Rationale: docs/reviews/2026-07-27-code-quality-metrics.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

debt: The annotations layer promises agents a commentary channel no runner path lets them write

1 participant