The event log¶
The ways binary appends one JSON object per line to $XDG_STATE_HOME/agent-ways/events.jsonl (usually ~/.local/state/agent-ways/events.jsonl; ways events-log-path prints it). Nothing reads the file while a session runs. ways tune stats, ways tune precision, ways session and ways agent cost read it afterwards.
Every value is a string, numbers included. Every line has ts (UTC, ISO 8601) and event. A field is left out when it has no value; an empty string means the value was computed and was empty.
A write that finds the file past 32 MiB compacts it to its most recent 24 MiB, cut at a line boundary. judge_call lines from the dropped part are carried forward, so judge spend history survives. When the carried lines leave too little to free, the compaction is skipped until the file has grown further, so the file can sit above 32 MiB for a while.
Common fields¶
| Field | Meaning |
|---|---|
way |
Way id, its path under the ways root: softwaredev/delivery/commits |
domain |
First segment of the way id |
trigger |
The channel that matched; see Trigger values |
scope |
Who received it: agent, subagent, teammate |
project |
Project directory |
session |
Claude Code session id. Subagent hooks report the parent's session. |
agent_id |
The agent the event concerns: main, or the subagent's id |
model |
Model id the receiving agent ran, read from its transcript. unknown when none was resolved. |
hook |
The hook event that ran the gate: UserPromptSubmit |
Events¶
session_start¶
The SessionStart hook ran. Fields: project, session.
way_fired¶
A way was shown to the agent for the first time in its session.
| Field | Meaning |
|---|---|
way, domain, trigger, scope, project, session |
Common fields |
token_position |
Session token count at the fire |
model, agent_id |
Common fields |
fire_score |
Semantic fires only: the score that decided the fire (the share for a late-interaction fire, the calibrated g(s) for a single-vector one; see hooks-and-ways.md) |
surface |
Semantic fires only: a snippet of the text that was matched |
matched_span |
Keyword, command and file fires only: the text the pattern matched |
parent, tree_depth, epoch_distance |
Ways inside a tree: the parent id, depth, and epochs since the parent fired |
team |
Teammate sessions: the team name |
A way delivered to a subagent at dispatch logs way_fired with way, domain, trigger, scope, project, session and team, with no position or model.
way_redisclosed¶
A way fired again after its refire: cadence let it. Same fields as way_fired, except matched_span, which is only on first fires.
way_suppressed¶
A way or check matched and was not shown.
| Field | Meaning |
|---|---|
kind |
way or check |
reason |
refire: the cadence is still holding it back (logged once per way per fire window). context_cap: the per-invocation context budget was full. |
way, domain, trigger, scope, project, session, agent_id |
Common fields |
way_nearmiss¶
A semantic score landed within near_miss_margin under the fire threshold (see stats.md for the setting). These are the likely false silences.
| Field | Meaning |
|---|---|
way, corpus_id, domain, trigger, scope, project, session |
corpus_id is the way's id in the corpus, prefixed for project ways |
prob_en, prob_multi |
Calibrated probability from the English and multilingual models; empty when a model did not score |
tau_s |
The semantic fire threshold the scores were measured against |
margin |
How far under tau_s the best score landed |
query_tokens |
Approximate size of the matched text |
Rows written before calibration carry score_en, score_multi, thr_en and thr_multi instead.
way_keyword_gated¶
A pattern: matched, but the way's semantic score was under the keyword floor on every model, so it did not fire (ADR-155). pattern_strict ways skip the floor and never log this.
| Field | Meaning |
|---|---|
matched_span |
The text the pattern matched |
prob_en, prob_multi |
Calibrated probabilities |
floor |
The keyword floor |
token_position |
Session token count |
way, corpus_id, domain, trigger, scope, project, session |
As in way_nearmiss |
check_fired¶
A check under a way was shown.
| Field | Meaning |
|---|---|
check |
Check id |
domain, trigger, scope, project, session |
Common fields |
epoch, way_epoch, distance |
Current epoch, the epoch the parent way last fired, and the distance between them |
fire_count |
Times this check has fired in the session |
match_score, effective_score |
The raw match score and the score after distance decay |
anchored |
true when the check's ## anchor section was included, which happens five or more epochs after the parent way fired |
injection_suppressed¶
The subagent switch withheld ways from a dispatched agent. Logged once per Task dispatch and once per agent.
| Field | Meaning |
|---|---|
reason |
subagents_off |
switch |
session (the per-session switch) or config (subagents: false) |
lane |
The hook lane that was suppressed: task, subagent_start, prompt, state, command, file, post_tool, queued |
agent |
The agent id, when known |
scope, project, session |
scope is subagent |
way_judged¶
The relevance gate (ADR-196) asked the judge about a way that matched on the prompt lane. One line per way judged.
| Field | Meaning |
|---|---|
way |
The judged way |
p_yes |
The judge's probability that the way is relevant |
threshold |
The profile's threshold |
verdict |
pass; block in enforce mode; would_block in shadow mode |
mode, engine, model |
Gate mode, engine profile and judge model |
judge_ms, gate_ms, candidates |
Judge latency, whole-gate latency, ways in the request |
reason, ancestor |
On a way blocked because an ancestor was blocked: reason is ancestor and ancestor names it. These lines carry no latency or candidate count. |
hook, scope, project, session |
Common fields |
judge_call¶
One provider call made by the judge. ways agent cost sums these.
| Field | Meaning |
|---|---|
outcome |
judged, or fallback when the call ended in a fallback |
reason |
The fallback reason, on fallback |
engine, provider, model, candidates |
The call |
input_tokens, output_tokens, cache_read_tokens, cache_write_tokens |
Usage, when the provider reported it |
cost_usd |
Cost, when known. Left out rather than written as zero when unknown. |
cost_source |
provider, price_table or unknown |
hook, scope, project, session |
Common fields |
gate_fallback¶
The gate could not judge, so nothing was blocked. The gate fails open.
| Field | Meaning |
|---|---|
reason |
Why: a timeout, a transport or provider error, an agent error, or config: … when the gate settings do not parse. A config: line carries no latency or candidate count. |
gate_ms, candidates |
Gate latency and ways that went unjudged |
hook, scope, project, session |
Common fields |
gate_capped¶
More ways matched than the profile's max_candidates (default 8). The rest went unjudged and fire unless an ancestor was blocked.
| Field | Meaning |
|---|---|
judged, unjudged |
Counts |
ways |
The unjudged way ids, comma-separated |
hook, scope, project, session |
Common fields |
Trigger values¶
trigger |
Channel |
|---|---|
keyword |
A pattern: matched the prompt |
semantic:embedding:en, semantic:embedding:multi |
Prompt matched by the single-vector semantic matcher, English or multilingual model |
semantic:late-interaction:en |
Prompt matched by the late-interaction matcher (ADR-160); always recorded with :en |
semantic:bash:en, semantic:bash:multi |
A shell command's text matched a way's description semantically |
bash |
A commands: pattern matched a shell command |
file |
A files: pattern matched a file being edited |
state |
A trigger: condition: session-start, context-threshold, file-exists |
task |
The Task lane, scanning a subagent's prompt |
prompt |
Near-miss and keyword-gate rows from the prompt lane; subagent dispatch fires with no recorded channel |
check-pull |
A check fired before its parent way, so the parent was shown with it |
postcheck |
A way requested by a post-tool check script |
attend:<signal> |
An attend signal handler |
unknown |
A way shown by ways show way with no --trigger |
ways tune stats groups these into lanes: keyword and the prompt semantic triggers are the prompt lane, semantic:bash:* joins bash, and the rest group by the text before the first colon.
Reading it¶
# Ways the judge blocked
jq -c 'select(.event=="way_judged" and .verdict=="block") | {ts, way, p_yes}' \
"$(ways events-log-path)"
# Fires by team
jq -r 'select(.event=="way_fired" and .team) | .team' "$(ways events-log-path)" | sort | uniq -c
See stats.md for the summary reports built on this log.