feat(tool-delegate): record routing-matrix provenance on spawn telemetry - #349
Conversation
`delegate:agent_spawned` recorded WHICH provider_preferences a delegation resolved to, but not WHICH matrix file produced them. A user file in ~/.amplifier/routing/ silently outranks the bundle's own same-named matrix, so a surprising resolution in the event stream was indistinguishable from a shadowed matrix, a shipped-matrix change, or no routing at all. Adds an optional `routing_matrix` key (matrix_name / matrix_path / matrix_source / shadowed_paths) READ FROM the model_role_resolver capability's published attributes -- hooks-routing publishes them, and its own docstring names "a spawn-time telemetry payload" as the intended consumer. Nothing here re-derives matrix precedence; a second implementation of that precedence is the drift this avoids. Captured at the one site that actually consults the resolver (execute()), threaded to the emit site as a keyword-only arg, so the recorded identity is the strategy that produced THESE preferences and cannot drift from it. Additive and omitted when unknown: - Consumers ignoring the field are unaffected: no existing key's name, type or value changes. - Absent means UNKNOWN, never "no shadowing". Every capture on disk today lacks the key; an analyzer treating absence as a negative assertion would silently clear exactly the shadowed sessions this exists to catch. - No model_role, an explicit provider_preferences pin, an agent-level default, no routing bundle, or a resolver that reports no source all leave the payload byte-identical to before. Same key added to `delegate:model_role_unresolved`, where "which matrix failed to serve this role" is the first question asked. 17 new tests; verified non-vacuous (4 fail with the injection removed).
bfb6a65 to
41fbef8
Compare
Merge-queue verification — lane wokFresh scratch clone ( Conflict check: no textual conflict.
All gates pass, including the required non-vacuity check on gate (b) (the headline shadowing test). Merging with |
What
delegate:agent_spawnedrecords which routing matrix file produced adelegation's
provider_preferences, not just the preferences themselves.Why
A user file in
~/.amplifier/routing/silently outranks the bundle's ownsame-named matrix. Until now a surprising resolution in the event stream —
e.g. a
fast-role delegate served ongpt-5.6-sol@xhigh— wasindistinguishable from a shadowed matrix, a shipped-matrix change, or no
routing at all. Two prior investigations read the shipped file, reasoned about
a matrix that was not in effect, and reached confidently wrong mechanisms.
Consumed, not re-derived
matrix_path/matrix_source/shadowed_pathsare published byhooks-routing on the
model_role_resolvercapability object this toolalready holds. That module's own docstring names "a spawn-time telemetry
payload" as the intended consumer. Nothing here re-implements matrix
precedence — a second implementation of that precedence is exactly the drift
this reads published state to avoid.
Verified against the real
MatrixModelRoleResolverobject (not a mock) inshadowed / clean / no-origin configurations. Foundation does not and must not
depend on a routing bundle — the capability is duck-typed by design — so the
committed tests use a stand-in mirroring the published attribute contract
exactly.
Where
_matrix_provenance(),modules/tool-delegate/.../__init__.pyexecute(), at the one site that consults the resolver_spawn_new_session()→delegate:agent_spawneddelegate:model_role_unresolvedCaptured at the resolver-lookup site rather than re-fetched at the emit site,
so the recorded identity is the strategy that produced these preferences and
cannot drift from it if the capability is swapped mid-session.
Backward compatibility
Additive, and omitted when unknown.
type, or value changes.
payload.get("routing_matrix"). Absent means UNKNOWN, never "noshadowing." Every capture on disk today lacks the key; an analyzer that
treats absence as a negative assertion would silently clear exactly the
shadowed sessions this field exists to catch. Pinned by a test that reads an
old-shape and a new-shape payload side by side.
model_role;an explicit
provider_preferencespin; an agent-level default; no routingbundle installed; a resolver that reports no source (alternate strategy, or
an older routing bundle). Pinned against a literal set of the pre-existing
keys, so a rename or drop fails loudly.
matrix — the resolver was never asked.
provider_routingdict returned to themodel is deliberately unchanged: filesystem paths in model-facing context are
token cost and leakage, and the event stream is the forensic record.
Tests
17 new (
test_delegate_spawn_matrix_provenance.py). Verified non-vacuous:removing the payload injection fails 4 of them, including the headline
shadowing case.
strinshadowed_pathsis not iteratedcharacter-wise; non-
strentries dropped, not coercedFull suite: 1857 passed, 1 skipped. One pre-existing unrelated failure
(
test_grpc_adapter_main.py::test_non_isinstance_object_with_mount_passes,Python 3.12
@runtime_checkable) — confirmed failing on a clean tree.