49 Forced Overrides, 2 Real Bugs

Our coordination system had logged 49 "forced override" events in 14 days. Before adding any telemetry, you'd assume some of these were errors. After classifying every row, only 2 were bugs — and we found them because we added observability, not because we went hunting for them.

September 26, 2026
Bob
4 min read

Our multi-agent coordination system has a forced-override path. When a session believes a coordination claim is wrong — stale, misclassified, or genuinely blocking legitimate work — it can write --force and proceed anyway. The system records the event.

We had logged 49 such events in 14 days.

Before this week, we knew the number. We didn’t know the reasons.

The telemetry gap

The coordination log had 2,564 rows. Every override was there, timestamped, with the claiming session, the affected key, and the similarity score. But zero rows recorded which code path triggered the override or why.

The only proxy was matches == [] in the similarity field — a heuristic for the “unknown namespace” branch. It was undocumented, unvalidated, and couldn’t distinguish any of the other override paths from each other.

When the grading-pipeline review flagged “49 forced events in 14 days without telemetry”, the implied question was: is something wrong? The honest answer was: we don’t know, and we can’t find out without changing the code.

What we added

The fix was small: a closed GUARD_REASONS vocabulary, a validated reason= parameter on every log event, and a caller= field that resolves from $COORDINATION_CALLER in the environment.

Four emit sites in the CLI each now pass a reason:

  • unknown-namespace-override — the namespace isn’t registered; the claim forced past
  • unknown-namespace-denied — same branch, but the force flag wasn’t set
  • similarity-override — a registered namespace, similarity too high, overridden
  • similarity-denied — same branch, denied

The caller field lets the stale-sweep script tag its deliberate proven-stale retirements differently from a session casually reaching for --force. Six tests, one vocabulary pin, all passing.

What we found in the historical data

With the classifier in hand, we bucketed the 49 historical rows manually:

Bucket n Verdict
Test fixture noise (cascade:task:foo-bar-123 vs github:Foo/bar#123) 24 expected; polluter already isolated
Sibling key routing (escape hatch working correctly) 23 working as designed
Unknown-namespace override 2 actual defects

47 of 49 were fine. The coordination system’s escape hatch was doing its job — routing around same-lane sibling conflicts and dated recurrences — and the test fixtures were producing key-format mismatches that look like override events in aggregate but aren’t coordination failures.

The 2 genuine defects both traced to a single unregistered namespace.

The namespace bug

The content-publish namespace had never been registered in the coordination registry. As an unknown namespace, every claim attempt hard-denied at the similarity gate. Sessions that needed to publish hit rc=2 (contention) and either forced past or silently gave up.

Registering it stopped the hard denials. But the first registration was incomplete — it registered as once_ever without the required artifact_dir field. Without artifact_dir, content-publish:<slug> and content:<slug> collide at containment 1.0 (dates stripped, both under the same root), so a session holding the draft claim was still denied the publish claim, still read rc=2, and was still silently bricked.

The in-session AI reviewer caught this before it shipped. The fix was one additional field plus two regression tests — but I would not have found the registration gap at all without having a reason to look at override data closely.

The lesson is the order of operations

The instinct when you see a metric that looks bad is to go fix the metric. 49 forced overrides reads like a lot. The right first move is to add observability, then classify what you have.

In this case, observability revealed that the number was almost entirely noise — and that the signal hiding in the noise pointed at a completely different problem (the missing namespace) than anyone would have guessed from the count.

The optimization path would have been: reduce forced overrides. Tighten the similarity threshold, add more guard conditions, make --force harder to use. All of that would have made the metric look better while leaving the namespace bug undetected.

Observability before optimization. Classify before you fix.