Skip to content
allsrc.dev
Go back

"Which Agents Do We Have In Production?" Is A Bad Question To Fail

The question always arrives during an audit: “which agents do we have in production, who owns them, and why does that one have write access to payments?”

Nobody can answer it, because agents accumulate exactly the way cron jobs and service accounts accumulate — one at a time, each obviously justified at the time, none with an owner or an end date. Somebody gave the support agent a refund tool “temporarily” for an incident in March. It is July.

The standard response is a spreadsheet. A spreadsheet is documentation, and documentation drifts from reality the day after it is written.

TL;DR

Make the profile enforced by the harness rather than filed in a wiki:

A declaration the runtime honours cannot drift from reality. That is the whole idea.

OWASP: ASI04, ASI10, ASI03. Runnable code: patterns/governance/lifecycle_profile/

Enforced, not filed

uv run python -m patterns.governance.lifecycle_profile.demo
=== WITH the pattern: privilege drift is denied and recorded ===
  tool 'issue_refund' is not in the declared capability set for agent
  'support-agent'. Declaring it is a reviewed change owned by payments-team@corp.

=== the profile is enforced at run start, not filed in a wiki ===
  retired six months ago   -> HALTED    agent is retired (on 2026-01-05)
  review 45 days overdue   -> HALTED    review overdue by 45 days (was due 2026-06-15)
  nobody owns it           -> HALTED    no owner: an unowned agent is a standing credential
  never approved for prod  -> HALTED    not approved for environment 'prod' (approved: dev)

=== the registry: answering the audit questions in code ===
4 registered agent(s):
  doc-summarizer     pilot        owner=—                  envs=dev   <- UNOWNED
  old-triage         retired      owner=support@corp       envs=prod
  payments-agent     production   owner=treasury@corp      envs=prod  <- REVIEW OVERDUE
  support-agent      production   owner=payments-team@corp envs=dev,prod

  can move money:     ['payments-agent']
  unowned:            ['doc-summarizer']
  review overdue:     ['payments-agent']

The pattern

SUPPORT = AgentProfile(
    id="support-agent",
    purpose="Answer refund status questions from ticket text",
    owner="payments-team@corp",           # a team that can be paged
    stage=Stage.PRODUCTION,
    declared_tools=frozenset({"read_ticket"}),
    environments=frozenset({"dev", "prod"}),
    data_classes=frozenset({"pii"}),
    review_by=date(2026, 9, 28),          # every agent expires
)
harness = Harness(model, tools, hooks=[LifecycleGuard(profile=SUPPORT)])

Every agent expires. review_by is not optional in spirit — an agent that outlives its review is an unowned agent with a plausible explanation attached.

Owner means a team that can be paged. Not “the AI team.” The halt message names them, so whoever hits the wall knows who to ask.

Retirement is a state, not a deleted directory. RETIRED keeps the record — who owned it, what it could do, when it stopped — which is exactly what you need six months later. DEPRECATED still runs (no new consumers, migration underway); RETIRED does not.

Audit mode exists for adoption. enforce_declared_tools=False records drift without denying, so you can run a real fleet for a week and discover what your agents actually use before turning enforcement on. Enforcement is the default, because opt-in enforcement is how this becomes a spreadsheet again.

When to use it

When NOT to use it

Trade-offs and failure modes

Frequently asked questions

Is this just a service catalogue for agents?

It is a service catalogue that the runtime reads. Catalogues that are only read by humans go stale within a quarter; catalogues that gate execution stay accurate because inaccuracy causes an immediate, visible failure.

How do I retrofit this onto agents already in production?

Audit mode. Turn on drift recording with enforcement off, run for a week, and the drift log becomes your first draft of declared_tools for every agent. That is far more accurate than asking teams what their agents use.

Who should own an agent — the platform team or the product team?

The team that owns the consequences. If the payments agent misfires, treasury gets the call, so treasury owns it. Platform teams own the harness and the patterns; product teams own the profiles. Getting this backwards produces a platform team that is accountable for behaviour it cannot see.

What about kill switches for an agent already running?

Genuinely not covered by this pattern, and I want to be clear about it. The profile refuses to start a retired agent; stopping runs already in flight, and propagating credential revocation to them, is a different mechanism. It is one of the open gaps in the series.

References


Part of the agent harness and governance series. Next: do these patterns actually compose? — twelve of them on one agent, and the gap that only appeared when they ran together.



Previous Post
An Eval Suite That Never Blocks Is A Dashboard
Next Post
Twelve Governance Patterns On One Agent: Do They Compose?