ADR-0009: The wire carries path observations, not computed state¶
- Status: Accepted — implementation tracked in item 02
- Date: 2026-06-25
Context¶
Fixing the backward-ant delay metric (item 02)
already changes the meaning of AntHop.time (cumulative-since-source → per-hop
delta), which is a semantic change to an existing wire field and therefore bumps
kWireVersion (ADR-0006) regardless.
While scoping that change we found that a backward ant serializes four fields —
pheromone, hops, prevSINR, prevHop — that are transient compute state:
- Verified in both adapters (
ns2/src/ant_packet_ns2.cc,ns3/model/anthocnet-packet.cc): they are only marshaled header ⇄AntMessage; neither adapter reads them to make a routing decision. - The core recomputes them at every hop in
advanceBackAnt(hops += 1,prevHop = current.node,pheromone = …). They do not need to survive across a hop on the wire.
Carrying them is wasted airtime (relevant to the overhead AntHocNet is
benchmarked on, item 08)
and a trust hazard: a node could believe an upstream-supplied pheromone
instead of computing its own.
Decision¶
A backward ant's wire image carries identity + the path observation, and each node computes pheromone locally. Concretely, as part of item 02:
- Drop
pheromone,hops,prevSINR,prevHopfrom the serialized format. They become (at most) core-local working fields.prevSINRis therefore removed, not renamed — the misnomer simply leaves the wire. - Encode time as a per-hop delta in the existing
AntHop.timeslot (seconds), summed on the backward pass — so no newtimeDestfield is added and the frame shrinks rather than grows. - Bump
kWireVersion. Do not hard-code the new number: it is a monotonic counter, so usecurrent + 1. Coordinate with item 12 — whichever of {item 02, item 12} lands first introduces thekWireVersionconstant; the other bumps it. Seedocs/wire-format.mdfor the target layout.
This is a refinement of ADR-0004's "one canonical wire format": the format encodes what was observed (the path and its per-hop timing), not what a node derived from it.
Alternatives considered¶
- Keep marshaling the four fields (minimal item 02). Lower blast radius for a
P0 fix, and ADR-0006 makes a later slim cheap and detectable. Rejected because
the slim is provably low-risk here — the fields are dead-on-the-wire, so we are
deleting unused serialized state, not changing logic — and item 02 is already
an atomic codec + both-headers +
test_codec.cppchange, so the slim costs no extra version bump. - Absolute arrival time + a new
timeDestfield. Simpler subtraction, but grows the wire with a field whose only purpose is cross-hop time math that the per-hop-delta encoding does without. Rejected in favour of the delta.
Consequences¶
- Every backward ant is smaller (four fewer fixed fields: −24 bytes), trimming control overhead.
- Item 12's threat model tightens: there is no upstream-supplied pheromone/hop count to validate or distrust — those values never cross the boundary.
prevSINRdisappears from the struct's wire image; if a local time accumulator is still wanted it is a private working field, not a serialized one.- Forward and backward ants now have clearly different wire footprints (the
backward ant adds nothing beyond the path it inherited) — documented in
docs/wire-format.md. - One more reason the codec must recompute, never trust: a regression test should
assert the decoder leaves
pheromone/hopsat defaults (they are not on the wire) and the core fills them. - Single-scalar-cost invariant. Each per-hop path entry stays
{node, cost}— one scalar. A MAC-aware or otherwise richer per-hop cost (item 10/A2) enters by improving the measurement fed into that scalar atstampForward(ILinkDelay), never by adding per-hop signal fields. The pheromone-metric port (item 16,ILinkMetric) is the orthogonal seam for combining path aggregates + node-local signals. Adding per-hop downstream signal fields to the wire is a separate, ADR-gated decision, not a default extensibility point.