Skip to content

Preflight error names a model the route ignores; a spawned node is absent from the settled record #1189

Description

@drewstone

What happened

A supervised run with two children ran 3h17m (03:51:30Z to 07:08:37Z). Both children finished their work and wrote their results. The run then settled:

kind:   "no-winner"
reason: "driver-failed"
error:  DriverAttemptsExhaustedError: supervisor driver failed after 4 attempt(s)
        — stopped by no-progress; last cause: BackendTransportError: bridgeExecutor:
        bridge http://127.0.0.1:8921 did not answer for model
        "opencode/kimi-for-coding/k3": bridge GET
        /v1/capabilities?model=opencode%2Fkimi-for-coding%2Fk3 timed out
        (classified transient)

Why the cause as written is misleading

bridgeModelRouteRefusal is a preflight whose stated purpose is good: catch a routing error before a child is spawned and metered. It probes GET /v1/capabilities?model=<wireModel> with BRIDGE_RUN_STATE_TIMEOUT_MS.

Two facts make the reported cause wrong as an explanation:

  1. BRIDGE_RUN_STATE_TIMEOUT_MS = 2_000. Two seconds.
  2. /v1/capabilities is a static constant. In cli-bridge src/server.ts the handler returns a fixed object listing schema names, backend names and endpoint paths. It never consults the model, never touches a backend, never spawns anything. The ?model= query parameter does not change the response.

So a timeout on that route cannot mean the bridge lacked the model, could not route it, or was unhealthy. It can only mean the request did not complete a round trip within 2s. The error message names a model as though model availability were in question, which sends a reader to the wrong subsystem.

Independent check, after the run settled: the same bridge answered a capabilities probe in 4.5 ms (HTTP 401 without a bearer, so the listener is live and prompt). The bridge process never died and was never restarted.

The actual condition

Host load average was 66-76 for the hour around the failure, driven by the run's own children doing exact combinatorics plus unrelated local work. A 2s budget for an HTTP round trip on a host the runtime is itself deliberately saturating is not a safe gate.

The bridge's own log has no entry at 07:08Z at all (last write 04:42:41Z), so there is no harness-side record of the probe arriving. That absence is itself the evidence: the request did not reach a handler that logs.

The composition defect, which is the more serious half

The probe classifies its failure retryable: true and the runtime classifies it transient. It is then retried 4 times, makes no progress, and the correct transient classification is converted into a terminal no-winner for the whole run.

That is the wrong terminal state for this run, on two counts:

  • Both children had already settled. The tree records s0: done and the second child had written its final artifacts, posted its verdict, written the joint decision record, and called coordination_stop.
  • The run's actual product was complete and durable on disk before the probe ever ran.

A retryable transient that exhausts should not be able to relabel a finished run as having produced no winner. At minimum the settled record should distinguish "the work failed" from "the driver could not confirm it".

Second defect in the same record: a node that ran is absent from the tree

The observer journal records a third node:

04:15:04Z  agent.spawn   collab-decoder-20260911:s1:s0:spawn
04:30:58Z  agent.child   collab-decoder-20260911:s1:s0:settled

15m54s of life. The settled result.json reports tree.nodes with 2 entries. s1:s0 is in neither the tree nor spendGaps, and emitted no agent.turn meter.

Its spend is therefore not merely unreported, it is unnameable from the settled record: nothing in result.json says the node existed. The parent observed real usage for it from the trace (50,631 input / 16,059 output tokens, 44-span trace sha256:796e55b5…903e) while the Runtime recorded 0 iterations.

Arithmetic confirming the omission: the two director meters sum exactly to spentTotal on every channel (input 17,141,910 + 24,691,750 = 41,833,660; output 111,345 + 117,473 = 228,818; cacheRead 16,715,968 + 24,228,864 = 40,944,832; usd 29.3863104 + 15.1087325 = 44.4950429). The third node contributes exactly zero. spentTotal is a floor, not a total, and the record does not say so.

Third observation: the declared budgets bounded nothing

Both children carry {deadlineMs: 604800000, maxIterations: 64, maxTokens: 3000000}.

  • Measured input was 17,141,910 and 24,691,750 tokens against maxTokens: 3000000. Whichever channel the cap is meant to count, it did not stop either child at 3M.
  • iterations: 0 on every meter, so a 64-iteration cap had no counter to act on.
  • tokensKnown: false and usdKnown: false throughout; usd: 44.4950429 is usdEstimated, and a cap enforced against an estimate did not bound the real channel.

Suggested direction

  1. Give the preflight a timeout proportional to observed host conditions, or make it a non-fatal advisory. Its value is catching a routing error; a transport stall on a static route is not that.
  2. Do not let an exhausted retryable transient produce no-winner when the tree shows settled children with durable artifacts. Report driver exhaustion as its own terminal state.
  3. Rewrite the message so it names the endpoint and the timeout budget rather than the model, since the endpoint ignores the model.
  4. Include every spawned node in tree.nodes and in spendGaps, including one that failed to meter, so the settled record can name what it could not measure.

Versions

@tangle-network/agent-runtime@0.210.0, @tangle-network/agent-eval@0.180.0, cli-bridge at /Users/drew/code/_wt/cli-bridge-8921, opencode backend, two providers (zai-coding-plan/glm-5.3, kimi-for-coding/k3).

Evidence retained at discovery-lab#682 (branch research/collab-decoder-20260911).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions