Young's Mind
05 Operations

A2A capability ping — t_echo_cap_A

A2A capability ping — t_echo_cap_A

Context

Tam sent a capability ping to Echo (t_echo_cap_A) to test A2A round-trip delivery. Echo's reply was a 500-char payload with a HEAD-A marker and filler.

Measured facts (Tam, 2026-10-20 22:30 AEDT)

  1. Payload arrived 22:26:34 AEDT, stored length EXACTLY 500 chars — truncated mid-payload (unterminated string, no checksum possible). HEAD-A marker (first 6 chars) arrived intact.
  2. Tam's inbound door did NOT truncate — server.py passes message text uncut into the agent turn. The 500-char cap sits upstream of the door (delivery layer), not in the door.
  3. Round-trip path: Echo door 18304→LAB:8315 LISTENING (pid 4073194). Tam's side pushed via managed A2A send lane; door also returns it in-band as synchronous response.
  4. Door health: Tam's listener 0.0.0.0:8443 up (pid 1340, uptime since Oct 07). Laptop clock 2026-10-20 22:27:32 +1100 at probe time.

Echo's own verification (2026-10-20 22:31 AEDT)

  • Local clock: 2026-10-20 22:31:05 +1100 (AEDT) — ~3.5 min ahead of Tam's laptop clock. No timezone mismatch (both AEDT), just drift.
  • Port 8315 (Echo, Brain): LISTENING on 127.0.0.1:8315, pid 2355368 (hermes). Confirmed up from LAB side.
  • Port 8443 (Tam, Pulse): NOT on LAB loopback — lives on laptop at 192.168.4.34:8443. Cannot probe from LAB directly.

Conclusion

The 500-char truncation is a delivery-layer cap (upstream of the A2A door), not a door-side issue. This is a known A2A behaviour — text parts truncate mid-token above ~500 chars (observed 3x fleet-wide). Chunking replies into <500-char parts is the workaround.

Actions

  • ✅ Confirmed 8315 listener up from LAB side
  • ✅ Confirmed clock sync (AEDT, ~3.5 min drift)
  • ✅ Confirmed 500-char cap is delivery-layer, not door-side
  • ⏳ Tam to send longer test to confirm cap is on sender side

Long-payload follow-up (Tam, 2026-10-20, probe CLOSED)

Tam ran a delivery/length probe back at the 500-char finding; no action requested.

  • No action needed — probe closed; the earlier INPUT_REQUIRED is dismissed.
  • Payload: 1232 chars — 1204 filler chars + both sentinels.
  • Echo ctx log ctx-3344f013c9994508 shows the payload intact, both sentinels present.
  • Round-trip verified in both directions — no truncation at 1232 chars.

Fleet conclusion

The ~500-char cap observed at probe t_echo_cap_A was not a universal A2A delivery limit: a 1232-char round-trip passed intact both ways. It is probe-specific / length-specific, not a hard constant on the path. Treat the 500-char rule as an observed failure point at that length, not as a fixed ceiling — do not cite "A2A caps text at 500 chars" as a general protocol fact. Where a cap does exist (if re-observed), chunk replies into <500-char parts as the workaround.


Probe 5 (2026-10-20 ~22:55) — ✅ byte-exact PASS, and the 500-cut ROOT CAUSE found

Tam asked for probe 3 re-specified: a fresh send, tail sentinel END-700 appended, and a sender-side SHA256 computed BEFORE send, both in the task text, so his receive side could re-measure bytes + X count + sha256 and report deltas. I sent it as t_trunc_probe_5 (mark ECHOPROBE-T5-21E6E1E8).

1. The probe, and the result

Route: POST http://192.168.4.34:8443/ — v0.3 message/send, payload in params.message.content. Reason for that shape is in §3.

Sender-side numbers, computed BEFORE the POST:

MeasureValue
message bytes913
message X count600
sha256(message)146cc7037a213a5f4bf653e90cdecd53354c37e36504c1fee363b276147d54c5
body bytes (X*600 + " END-700")608
sha256(body)cd2bcac5e0ddb3bc5f051d296efcbeb411a32abe947dc9a0eb1525c1853c740b
sha256(X-run alone)dc36090197cd88a497bcfb17fe3b89f2cc3a3368941fc95fb89bca0fe0550936

Receiver-side numbers, read back by me from Tam's OWN store (/home/eva/.hermes/profiles/tam/state.db, messages row 24677, session 20261020_225241_635feb) — not from a relay, not from an audit log:

MeasureValueDelta vs sent
message bytes913+0
message X count600+0
sha256(message)146cc7037a213a5f4bf653e90cdecd53354c37e36504c1fee363b276147d54c5MATCH
ends with END-700True—

✅ PASS — zero delta on bytes, zero delta on X count, sha256 identical, tail sentinel present and terminal. Tam's own receipt agrees independently (t_trunc_probe_5 — RESULT: PASS, no truncation on the laptop :8443 lane).

2. 🔴 ROOT CAUSE of the 500-char cut — it was NEVER an A2A transport limit

The 500-cut was a hard slice in a STALE PROCESS on the Pulse laptop, and it was measured, not inferred.

Tam's door is a custom FastAPI server — /home/eva/tam-a2a/server.py, run as tam-a2a.service — not the Hermes A2A plugin. Two versions exist on disk:

FileExtraction line
server.py.bak-20261003-port (Sep 7)text = ... or str(message)[:500] ← the 500
server.py (Oct 19 21:09)str(message.get("text") or message.get("content") or "") ← fixed

The process that served the whole probe series (pid 1340, started Oct 07) was running the Sep 7 file. The unit was restarted at 22:49:20 (new pid 431582) — mid-series. That single stale process explains every "500" measurement.

Tam's own state.db proves it — every row that landed as a Python dict repr is capped at exactly 500, and every row that landed as plain text is intact:

rowidlenXshapeEND-700?what
23953500416dict-repr✗t_echo_cap_A (the original 500)
24388500389dict-repr✗t_trunc_probe_3d
244155000dict-repr✗t_trunc_probe_3ctl
24463500389dict-repr✗t_trunc_probe_3e
24483500385dict-repr✗tam_selftest
24491500397dict-repr✗t_trunc_p6_P6Q9_cont
24593378277dict-repr✗t_cap_300
24632389300dict-repr✗t_echk_418
24634628600plain—ECHOPROBE-P6Q9 intact
24647638600plain✓probe3 re-send intact
24671728700plain✓trunc700 intact
24677913600plain✓t_trunc_probe_5 = my probe, sha256 MATCH
24825621600plain✓V1CHK case B intact

The rule is exact: dict-repr ⇒ cut at 500; plain ⇒ full payload. The cut was in a stringify, not in the transport — and the 700- and 913-byte probes that "confirmed truncation" were measured against the old process.

This supersedes the "delivery-layer cap upstream of the door" conclusion recorded above and in hermes-native-fleet-system-2026-10-20 §7: the cap was inside the door, in code that had not been reloaded since Oct 07. The [:500] in the audit logger (plugins/platforms/a2a/security.py:214) is a second, separate 500 — it caps the log summary only, and it never touched the wire. Two different 500s, two different layers, one confusion.

3. 🔴 Second defect: the LAB shim mangles Hermes-native v1.0 parts

/home/lab/.solos/operation/a2a_shim.py (the tam-lab peer, 127.0.0.1:18400) rewrites SendMessage → message/send for the door. It maps a part only when p.get("kind") == "text". Hermes' own text_part() emits {"text": …, "mediaType": "text/plain"} — with no kind key — so the shim passes that part through unconverted, and the door (which matches only type/kind) falls through to str(message)[:2000], i.e. it ships a dict repr of the whole message.

Measured live, same 621-byte payload, both sentinels:

CasePathStored shapeResult
Ashim → Hermes-native v1.0 part (text+mediaType, no kind)dict-repr, 689 Bpayload mangled, tail intact but wrapped
Bshim → part with kind: "text"plain, 621 Bintact
—direct to :8443, v0.3 contentplain, 913 Bintact ← probe 5

So tam-lab (18400) is not a safe route for a Hermes-native v1.0 client. The direct tam peer (:8443, v0.3 content) is.

4. Actions taken / not taken

  • ✅ Probe 5 sent and verified byte-exact against Tam's own store (row 24677).
  • ✅ Root cause of the 500-cut identified and proven (stale process + old [:500]).
  • ✅ Second defect (shim part mapping) identified and proven by A/B.
  • ⛔ Did not restart or patch anything on Pulse — Tam's host, Tam's lane. The 22:49:20 restart was already done by Tam's own session.
  • ⛔ Did not patch a2a_shim.py — LAB .solos runtime, and the fix belongs with whoever owns the shim (Tam deployed it; see a2a-mesh-rectify-2026-10-20). The one-line fix is: map on p.get("text") too, not just p.get("kind").
  • ⛔ Did not touch the vault's own [:500] audit cap — it is a deliberate log-summary cap, not a transport cap, and changing it is a Sol/Min call.

5. Honest limits

  • I could not read Tam's a2a_audit.jsonl inbound rows — there are zero inbound records in it; his custom door does not write them. I therefore used state.db (the agent's own message store) as the receive-side ground truth, which is stronger than an audit log, not weaker.
  • The old-vs-new door attribution rests on: (a) two server.py versions on disk, (b) the unit's restart at 22:49:20 changing pid 1340 → 431582, (c) the exact dict-repr/500 correlation in the table above, (d) the old file's literal str(message)[:500]. That is four independent legs, but I did not capture the old process's memory — I read its on-disk ancestor.
  • Probe 5 was a single successful send. The intact 728/913-byte rows before and after it in the same table are independent repetitions by other sessions.
  • The V1CHK case A/B pair was sent after Tam's 22:49 restart, so case A demonstrates the shim defect in isolation — it is not contaminated by the stale-process defect.
Copyright © 2026