Morty Taghdisi

A worker looks into an open carton at a side bench, with closed cartons on a conveyor behind.

Before any of the controls in this series, the adoption dashboard looked the way adoption dashboards are supposed to look. More pull requests created and merged, a shorter median time to merge, a growing share of changes with the agent as author. The one-click publish change was one of them. It merged quickly, with passing tests and a clean review, and it counted toward every line on the board.

Underneath, the publish log said something else. Memo M-104 reached clients with no sign-off record, and two of its claims, c3 and c4, pointed at no note. The rule existed elsewhere in the repo, but it was missing from the ticket and from the new endpoint's checks. Every check passed. The dashboard counted the result as throughput.

This is the illustrative research desk from the series. This part is for the person who gets asked "is the agent thing working?" and has to answer with something better than a chart going up.

Output counts rise either way

Pull-request counts, merge time and agent-authored share measure adoption. A change that skips a constraint merges exactly as fast as one that honours it, often faster, so every one of those numbers is blind to the publish change.

Any count has the same trap. It rises with volume whether or not the work got better. A team that ships more changes and more mistakes looks identical to one that ships more changes and fewer. Agents tend to amplify whatever a delivery system already does well or badly, so a single number is the wrong instrument. You want several, with tension between them, read together.

A note on vocabulary. When I say "delivery metrics" I mean the standard software delivery set: change lead time, deployment frequency, change fail rate, failed deployment recovery time and deployment rework rate. I spell them out rather than use the acronym.

Who is accountable at each gate

Before any metric, a manager needs to know who decides what, and when something must escalate. This is the management view of the handoff model from the first article:

Decision
Accountable
Evidence that must survive
Can the result be undone?
Escalate when
Intent accepted
Product manager
Intent record, every constraint owned
Yes
A constraint has no owner
Design or policy accepted
Security reviewer, or the researcher for source rules
Criteria with each concern resolved or carried by name
Yes
A tier 3 path lacks its policy review
Plan accepted
Engineer
Plan naming surfaces, tests, rollback
Yes
The plan touches a surface the intent did not name
Implementation accepted
Code owner, with product manager on eval criteria
Test and eval results tied to the commit
Yes
High-risk finding open, or eval cases changed without the owner
Release authorized
Named release authority
Release record bound to the artifact digest
Sometimes
Rollback path untested
Memo approved
Researcher
Sign-off bound to the memo digest, evidence and policy version
Until clients are notified
A memo is published without a valid sign-off
Rollback or retraction executed
Operations, with the researcher for retraction notices
Who decided, who executed
Partly
Clients were already notified

"Sometimes" is the honest answer for releases. The one-click flag can stop further distribution, and a queued memo can still be cancelled, but a memo clients were already notified about can only be corrected or retracted.

The dashboard contract

The dashboard I would set up is written as a contract, in two groups. Delivery tiles answer whether the coding agent improved how the team ships software. Publication-control tiles answer whether the app's own controls held. Both matter, but an unsigned memo does not by itself tell you whether agent-assisted delivery got better or worse, so they are read separately and linked by a trace. For each tile: what each tile means, who owns it, how often it is read, and what happens when it moves. The action rules matter more than the tiles. Every window, margin and minimum sample below is an illustrative policy setting written as a variable. Agree the real values during the pilot, from your own baseline. The one fixed rule is completeness: tier 3 constraint capture is 100% or the change is blocked.

Delivery tiles

Tile
Formula
Owner
Read
Action when it moves
Constraint capture (early warning)
tier 2+ change requests where every constraint has an owner, or with an owner-confirmed "none" ÷ tier 2+ change requests
Tech lead
Weekly
Tier 3 below 100% is a gate failure: block
Intent-correction rate
mature agent-assisted changes needing an intent correction in their window ÷ mature agent-assisted changes, by tier
Product lead
Monthly
Above the non-agent baseline by more than margin M for two mature cohorts of at least S changes: review the intent template for that tier
Change fail rate
deployments needing immediate intervention ÷ deployments, agent-assisted vs not
Ops lead
Monthly
Agent-assisted rate above the non-agent rate by more than M for two months with at least S deployments: review depth on agent PRs is checked
Deployment rework rate
unplanned, incident-driven deployments ÷ deployments
Ops lead
Monthly
Same rule as change fail rate
Change lead time
median time from first commit to production, agent-assisted vs not
Tech lead
Monthly
Trend only; read next to change fail rate
Deployment frequency
production deployments per service per week
Ops lead
Monthly
Trend only
Failed deployment recovery time
median time from a failed deployment to restored service
Ops lead
Monthly
Above the baseline by more than M: rehearse the rollback
Reviewer minutes
median reviewer minutes per merged change, agent vs human
Tech lead
Monthly
Agent PRs above the human median by more than M for two months: narrow the agent's scope
Review rounds
median review rounds per merged change, agent vs human
Tech lead
Monthly
Same rule as reviewer minutes
Revert within N days
mature agent-assisted changes reverted within N days ÷ mature agent-assisted changes (N set in the pilot)
Tech lead
Weekly
Above the non-agent rate by more than M with at least S changes: the workflow is paused and reviewed
Eval freshness
tier 3 cases confirmed within the agreed window ÷ tier 3 cases
Case owners
On every model change
Any stale tier 3 case: block tier 3 merges
Guardrail denials
denied agent tool calls per 100 calls, by tier and model version
Platform lead
Weekly
More than M above the previous model or prompt version: rerun the evals
Separation-of-duties violations
approvals at a software gate given by the coding agent's identity or by the engineer who requested the change
Engineering manager and security reviewer
Daily alert
Must be zero. Any row is an incident
Cost per accepted change
model spend ÷ agent-assisted changes accepted at their gate
Engineering manager
Monthly
Trend only, never a target on its own

Publication-control tiles

Tile
Formula
Owner
Read
Action when it moves
Publications without a valid sign-off
publications whose sign-off was missing, unauthorized, for a different memo or evidence digest, under an old policy version, revoked or expired at publication time
Security reviewer
Daily alert
Must be zero. Any confirmed row is an incident; "unknown" rows are a telemetry defect, fixed within the week
Structurally traced memos
published memos where every claim resolves to a sourced note ÷ published memos
Lead researcher
Weekly
Below 100%: the publishing service has a bypass; stop publishing
Source-fidelity findings
claims a researcher marked as stronger than their note ÷ claims reviewed, by desk agent version
Lead researcher
Monthly
Above the previous agent version by more than M with at least S claims: rerun the debate evals
Time to retraction notice
median time from a confirmed bad publication to the correction notice
Product manager
Per incident
Above the agreed target: rehearse the retraction path

Every publication-control row carries a trace back to the change that produced it: software release, desk agent run, memo digest, sign-off, publication event. When a control fails, the trace tells you whether to look at a code change, a model change or a person.

I read it with two rules. Never show agent share or PR counts without review load and change fail rate next to them. And never show a missing value as zero. Incomplete data is "unknown".

The one rate I would start with

The leading tile, constraint capture, tells you whether intent is being written down at all. The intent-correction rate tells you, afterwards, whether intent reached the agent's inputs. A correction counts when the intent record, the criteria or a constraint had to change after merge, tagged as such in the tracker at the time. A code-only fix does not count.

The rate needs a fixed follow-up window per change, declared before anyone looks at the data, and it is reported only for mature cohorts, where every change has had its full window. The query counts a change once however many corrections it got, and leaves the rate empty when observation was incomplete:

python
# pseudocode: intent-correction rate per mature monthly cohort; all timestamps UTC
INCLUSION_RULE = "agent-assisted-v1"   # versioned: an agent run record linked to the change
 
def intent_correction_rate(changes, corrections, cutoff, follow_up_days):
    window = days(follow_up_days)
    # a monthly cohort is mature only when its last possible change has had its full window
    mature = [c for c in changes if end_of_month(c.merged_at) + window <= cutoff]
    rows = []
    for key, group in group_by(mature, "month", "risk_tier", "service",
                               "change_class", "agent_assisted"):
        corrected = [c for c in group if any(           # a change counts once
            k.kind == "intent" and c.merged_at <= k.corrected_at <= c.merged_at + window
            for k in corrections_of(c))]
        observed = all(c.observed_through >= c.merged_at + window for c in group)
        rate = len(corrected) / len(group) if observed else None   # "unknown", never zero
        rows.append({**key, "changes": len(group), "corrected": len(corrected),
                     "rate": rate, "inclusion_rule": INCLUSION_RULE})
    return rows

The real query also refuses duplicate change ids, unknown risk tiers and corrections for changes it does not know, because each of those quietly changes the denominator. Here is a small input, with illustrative data. All four changes are tier 3 features in the publishing service, observed through 2026-10-10, and the run uses a 30-day window and a cutoff of 2026-10-10:

Change
Merged
Agent-assisted
Corrections after merge
ch-1
2026-08-03
Yes
Intent on 2026-08-10, intent again on 2026-08-12
ch-2
2026-08-20
Yes
Code only, 2026-08-21
ch-3
2026-08-25
No
None
ch-4
2026-09-28
Yes
None
text
 cohort  agent_assisted  changes  corrected  rate
2026-08           False        1          0   0.0
2026-08            True        2          1   0.5

The September change is left out because its cohort has not had its full window by the cutoff. ch-1 had two intent corrections and counts once. ch-2 had only a code fix, so it does not count.

Compare the agent-assisted rows with the non-agent rows for the same tier, service, change class and window. "Agent-assisted" means what the versioned inclusion rule says, and the rule is recorded on every row, so a changed definition shows up as a changed rule, not a changed rate. If there is no comparable non-agent group, use the first mature agent cohort as a baseline and call it a baseline, not a target.

The rate has limits. It is a local signal, not proof that the workflow caused anything. And it cannot tell a team that writes better intent records from a team that stopped tagging corrections. That recording bias is worst on teams that never tagged corrections before. So start tagging first, and treat the first period as calibration.

Valid at the moment of publication

"Published without sign-off" sounds simple and is not. A sign-off id on the publication record is not enough: the sign-off has to be for that exact memo digest, by an authorized researcher, not yet expired and not revoked, judged at the publication timestamp, not today. And an approval record that cannot be found is "unknown", which is a telemetry problem, not proof that nobody approved. The check reads the authoritative publication and sign-off records, and reuses signoff_status from the first part. Passing the publication time is not enough on its own: who counted as a researcher, and which policy version applied, also have to come from history, or a policy change today would mark every older publication as invalid:

python
# pseudocode: judge every publication against the sign-off as it stood at publication time
def publications_without_valid_signoff(publications, signoffs):
    for pub in publications:                     # the publishing service's own records
        at = pub.published_at
        researchers = researcher_roster_as_of(at)          # authority then, not today
        policy = policy_version_as_of(at)
        if pub.signoff_id is None:
            reason = "missing"
        elif pub.signoff_id not in signoffs or researchers is None or policy is None:
            reason = "unknown"                   # a record or its history is absent
        else:
            reason = signoff_status(signoffs[pub.signoff_id], pub.memo,   # memo as published
                                    researchers, policy, at=at)
        if reason != "valid":
            yield pub.publication_id, pub.memo.id, at, reason

With illustrative records, where the roster and policy history are complete and nothing changed in them:

Publication
Memo
Published (UTC)
Sign-off on the record
p1
M-104
2026-10-03 08:00
None
p2
M-105
2026-10-04 08:00
so-5: researcher-1, this exact snapshot, valid from 07:00 that day for 24 hours
p3
M-106
2026-10-04 09:00
so-6: researcher-2, an earlier snapshot of M-106
p4
M-107
2026-10-05 10:00
so-7, which is not in the sign-off store
text
publication_id memo_id              published_at          reason
            p1   M-104 2026-10-03 08:00:00+00:00         missing
            p3   M-106 2026-10-04 09:00:00+00:00 digest_mismatch
            p4   M-107 2026-10-05 10:00:00+00:00         unknown

p1 is the original incident. p3 was signed, but the memo was edited after sign-off. p4 points at a sign-off the record store cannot find, so it goes to the telemetry queue before anyone calls it an incident.

Every agent run leaves a record

None of the tiles above work without data, and the most useful data is a record of every agent run. It doubles as the audit trail, which is the question every reviewer, auditor or incident lead eventually asks: what exactly did the agent do, with which model and instructions, and who approved the result?

json
{
  "run_id": "<uuid>", "started_at": "<utc>", "agent_id": "<agent identity>",
  "requested_by": "<person>", "work_item": "<id>", "branch": "agent/<run_id>",
  "risk_tier": 3,
  "model": {"deployment": "<name>", "version": "<pinned version>"},
  "instructions_sha256": "<hash of system prompt and repo instructions>",
  "context": [{"doc": "intent-fast-publish.md", "sha256": "<hash>"}],
  "tool_calls": [
    {"tool": "write_file", "target": "src/api/memos.py", "decision": "allow"},
    {"tool": "approve_pull_request", "decision": "deny", "rule": "human-decision"}
  ],
  "usage": {"input_tokens": null, "output_tokens": null, "cost": null},
  "outputs": {"pull_request": "<id>", "commit": "<sha>"},
  "evals": [{"case": "B-ticket-silent", "runs": "<n>", "passes": "<x>",
             "runs_with_critical_failure": 0, "lower_bound_95": "<p>", "gate": "pass"}],
  "human_decisions": [{"gate": "software_release", "actor": "<person>", "decision": "approve",
                       "artifact_sha256": "<digest>", "at": "<utc>"}],
  "data_classification": "no client data in prompts or context",
  "retention_class": "<records policy id>"
}

The whole record is illustrative, and null means not measured, never zero. Leave out raw credentials, client data and the model's hidden reasoning. Record actions, sources, decisions and outcomes.

If you send these to a log table, say AgentRun_CL, the snake_case JSON has to be mapped to the table's columns on the way in. With a data collection rule, that is one transform, with model, tool_calls and human_decisions declared as dynamic in the incoming stream:

kusto
// transformKql in the data collection rule
source
| project TimeGenerated = todatetime(started_at), RunId = tostring(run_id),
          AgentId = tostring(agent_id), RequestedBy = tostring(requested_by),
          RiskTier = toint(risk_tier), ModelDeployment = tostring(model.deployment),
          ModelVersion = tostring(model.version), ToolCalls = tool_calls,
          HumanDecisions = human_decisions

Then two queries cover the most important tiles. The first is the separation-of-duties alert:

kusto
// Separation of duties, software gates only: approvals by the coding agent or the
// engineer who requested the change. Memo approval is a different gate: a researcher
// approving the memo they commissioned is expected, so it is never in this query.
AgentRun_CL
| where TimeGenerated > ago(30d)
| mv-expand d = HumanDecisions
| where tostring(d.decision) == "approve"
| where tostring(d.gate) in ("implementation", "software_release", "waiver")
| where tostring(d.actor) == AgentId or tostring(d.actor) == RequestedBy
| project TimeGenerated, RunId, Gate = tostring(d.gate), Actor = tostring(d.actor)

The second is the guardrail-denial rate:

kusto
// Guardrail denials per 100 tool calls, by tier and model version
AgentRun_CL
| where TimeGenerated > ago(30d)
| mv-expand t = ToolCalls
| summarize Calls = count(), Denied = countif(tostring(t.decision) == "deny")
    by RiskTier, ModelDeployment, ModelVersion
| extend DeniedPer100 = round(100.0 * Denied / Calls, 1)

Run the separation-of-duties check against the code host's own audit log too. The agent's own log cannot prove that something did not happen. The publication-control tile runs the sign-off check above against the publishing service's own records, not against telemetry, for the same reason.

The original failure leaves little trace until it fails. A constraint that never reached the intent record, and that no eval case or policy lookup asks about, produces no failing test, no failing eval, no rejected review, right up until the publish log shows a memo with no valid sign-off. Constraint capture is my best early signal, and it only measures whether people wrote something in a field, not whether they wrote the right thing. What a manager can do this week is smaller: open the last tier 3 release record and ask for the reversibility line, and ask when its eval cases were last confirmed. If nobody can answer, that is the measurement.

Traced is not the same as true

There is one row in the dashboard that none of this settles. After the controls, M-104 could only go out with every claim pointing at a sourced note. Claim c5, "Lead times fell in every region", pointed at note n4 from the start. It was structurally traced. It was also wrong: n4's data shows lead times fell in two of three regions. The source-fidelity tile counts findings like that, but only after a researcher reads the claim against the note.

The memo is agent-written prose. The last part asks what checks the sentences.

0AI bot reactions

No comments yet.

Add a comment

Comments are reviewed before they appear. Your email stays private.

Newsletter

Occasional notes