Skip to content

Every agent call names the agent that made it - #144

Merged
czpython merged 2 commits into
mainfrom
agent-attribution-is-not-optional
Jul 28, 2026
Merged

Every agent call names the agent that made it#144
czpython merged 2 commits into
mainfrom
agent-attribution-is-not-optional

Conversation

@czpython

Copy link
Copy Markdown
Owner

AgentCall.agent was nullable, AgentCallResponse.agent optional, and the
response's label fell back to the string "Agent" when it was missing.
Nothing could produce that row.

The one production writer is AgentCall.start(..., agent=self.id)
(agents.py), where self is an Agent. An
explicit id registers in __post_init__; otherwise __set_name__ stamps it
from the attribute name. Even seed_call takes agent as a required
positional. So the | None, the nullable column and the "Agent" fallback
modelled an absence with no producer — and #142 turning label into a
computed field made that dead branch read like a deliberate rule rather than
a copied default.

  • agent: Mapped[str], and AgentCall.start(agent: str)
  • AgentCallResponse.agent: str; label is get_display_label(self.agent)
  • AgentCallSummary.agent: string on the wire mirror
  • migration a4e1f60c2b95

The migration adds the constraint with no backfill. A null here would mean a
writer nobody knows about, and inventing an attribution would put it in the
cost table — the constraint should find it instead.

The blank id the constraint would have taken

Agent.id defaults to "" so __set_name__ can fill it, and an Agent
built loose — a local, never assigned to an Extension, never given an
explicit id= — keeps that default. __set_name__ never runs, so it never
registers, but nothing stopped it running: it would have written agent="",
which satisfies NOT NULL and displays as a blank timeline label.

An agent with no id has no durable key — its settings overrides, its registry
entry and its calls all key on nothing — so __call__ refuses it, beside the
check that it is inside a workflow.

Not changing

SubjectStatus.agent stays str | None. Its None means "no live agent call"
— the driving run is parked, or has none yet — not "an unattributed call".

czpython added 2 commits July 28, 2026 19:27
The column was nullable and the response optional, with the label falling
back to "Agent" — but an agent is what makes a call. The one writer is
AgentCall.start(agent=self.id), and an Agent's id is either explicit or
stamped from its attribute name, so nothing can produce a row without one.

The migration adds the constraint with no backfill: a null here would mean
a writer nobody knows about, and inventing an attribution would put it in
the cost table.
An Agent built loose — never assigned to an Extension, never given an
explicit id — kept the empty default, and nothing stopped it running. Its
settings, its registry entry and the call it records all key on nothing,
and the not-null column would have taken the blank as a name.
@czpython
czpython merged commit ef89f77 into main Jul 28, 2026
2 checks passed
@czpython
czpython deleted the agent-attribution-is-not-optional branch July 28, 2026 17:42
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.

1 participant