Young's Mind
06 Implementation

A2A agent reporting system — agents report to Min (authority, 2026-10-20)

Authority doc (specs only — no execution). Consolidates the design for the agent→Min reporting system built on the fleet's existing A2A transport. Nothing here executes, installs, or modifies a service. It specifies the contract, the protocols, and the integration points so that any agent (or lane) can implement against one document.

A2A agent reporting system — agents report to Min (authority, 2026-10-20)

Authority doc (specs only — no execution). Consolidates the design for the agent→Min reporting system built on the fleet's existing A2A transport. Nothing here executes, installs, or modifies a service. It specifies the contract, the protocols, and the integration points so that any agent (or lane) can implement against one document.

Young's ruling (inherited): one voice to Young — Min. Agents report to Min; Min consolidates and reports to Young. This doc makes that rule mechanical: every agent's report is an A2A message that becomes a row Min can read, verify, and project onto the dashboard. See fleet-ground-rules-2026-10-20.md §2 Rule 2.


0. Scope and non-scope

In scope (spec only):

  1. A2A agent reporting system design — heartbeat + task completion + blocker reporting.
  2. How Min polls agents via their heartbeat (tasks/get, tasks/send, health probes).
  3. Work-queue integration — A2A messages become tasks.db rows with owner/deadline/state.
  4. Agent ACK-then-own protocol.
  5. Heartbeat interval + staleness detection (300 s stuck rule).
  6. Integration with the fleet dashboard (status.json).
  7. Cross-references to the three named authority docs.

Out of scope (named, not designed here): the dialect standardisation across planes (Heart/Pulse/Brain) — that is Sol's lane and is tracked on the fleet board (a2a-connectivity-verification-2026-10-20.md §3). The A2A auto-session provenance rule is named here but its implementation is Min's lane. No code, no service changes.


1. A2A agent reporting system design

1.1 Planes and endpoints

Three planes, each a set of A2A agents with a canonical endpoint. All reachability is via the loopback tunnels in fleet-ssh-tunnels.md (Tailscale decommissioned; loopback-only binds both ends).

PlaneAgentsPortsDial dialect
Brain (LAB)min, mira, sol, nova, echo8311–8315v1.0 SendMessage (Brain answers both names)
Heart (LAB)ace, alpha, atlas8301–8303v1.0 SendMessage + parts:[{text}]
Pulse (laptop)eva, eve, tam8441–8443v0.3 message/send + content

Authority: fleet-ssh-tunnels.md §1 (port registry), a2a-connectivity-verification-2026-10-20.md §3 (dialect matrix). A per-peer dialect: field in the registry is the planned fix for the split; until then the working recipe is recorded in a2a-connectivity-verification-2026-10-20.md §3.

1.2 Three report channels (all A2A)

Every agent reports to Min through exactly one of three A2A message shapes. No chat, no out-of-band channel, no direct-to-Young (ground rules Rule 2).

ChannelTriggerA2A shapePayload
Heartbeatperiodic (§5)tasks/send (Brain) / message/send (Pulse){"type":"heartbeat","agent":<id>,"contextId":<id>,"last_activity_at":<iso>,"skills_count":N,"in_flight":[<task_id>...]}
Task completiona task reaches a terminal statetasks/send + task idthe A2A Task object (status.state, artifacts, history) — Min polls tasks/get for the final state
Blocker reportagent cannot proceedtasks/send{"type":"blocker","task_id":<id>,"reason":<text>,"escalate":true}

Task states (wire values, spec-correct, from cortex/tasks/states.yaml):TASK_STATE_SUBMITTED · WORKING · INPUT_REQUIRED · COMPLETED · FAILED · CANCELED · REJECTED · AUTH_REQUIRED. A report that names a state must name one of these; local states never reach the wire (D2 ruling in should-the-board-live-in-cortex.md).

1.3 Agent Card (discovery)

Each agent serves GET /.well-known/agent-card.json (canonical v1.0 path) with name, protocolVersion, skills[], capabilities. Min's discovery pass reads the card to confirm the agent is alive and non-empty (skills_count from the card; emptiness is LOUD per dashboard §5.2 C2). A card with skills: [] is RED, never silently absent (ground rules Rule 7 — proof before claim).

1.4 Audit trail

Every A2A exchange is appended to ~/.hermes/a2a_audit.jsonl (append-only, per-identity). Min's polling reads the tail of this file per agent to compute last_exchange_s (dashboard colour rules: <30 min green, <6 h amber, >6 h red). Full audit events are out-of-band gallery only — never rendered raw on the dashboard (Sol's annex §2 Q3).


2. How Min polls agents via their heartbeat

Min is the single poller. She does not wait for agents to push; she polls on a cadence and treats a missing/late heartbeat as a signal.

2.1 Health probe (liveness)

For each agent in the registry:

HTTP GET <endpoint>/.well-known/agent-card.json   (or the A2A root for v0.3)

Any HTTP response (200/405/JSON-RPC error) = up. Connection refused/timeout = down. Cadence: 2 min (dashboard §5.2 C2). Source of the port list: fleet-ssh-tunnels.md registry (laptop 18301–18313, LAB→laptop 18441–18443).

2.2 Heartbeat poll (tasks/send / message/send)

Min sends a heartbeat-request message to each agent:

POST <endpoint>/   (JSON-RPC)
{"jsonrpc":"2.0","id":<n>,"method":"SendMessage"|"message/send",
 "params":{"message":{"role":"user","parts":[{"text":"heartbeat"}],
          "contextId":<min-context>}}}

Method name per the peer's dialect: field (§1.1). Min expects a reply within the 300 s stuck window (§5). The reply carries last_activity_at, skills_count, and in_flight task ids.

2.3 Task state poll (tasks/get)

For each task id Min knows about (from the audit log and the work queue), Min polls:

POST <endpoint>/   {"method":"tasks/get","params":{"id":<task_id>}}

Returns the Task with status.state. Min records the state into the work queue (§3) and computes age vs 300 s → stuck (dashboard §5.1 live[]).

2.4 Cadence summary

ProbeCadenceSource
Endpoint liveness2 minfleet-ssh-tunnels.md port registry
Heartbeat request2 min (aligned with liveness)agent card + audit log
tasks/get for in-flight tasks2 mina2a_audit.jsonl recent ids
Board projection (kanban)5 minDocs/Kanban/fleet-board.md + Pulse/Kanban/PULSE-BOARD.kanban
Tunnel probes2 minfleet-ssh-tunnels.md §5 one-liners
System host probes5 minprod free/df/systemctl

All collectors run from the laptop (hub of the hub-and-spoke); they reach LAB via the mesh and prod via tunnels. Collectors are read-only; they write status.json atomically (tmp+mv). See fleet-dashboard-plan.md §5.4.

2.5 Min's polling stance (no push dependency)

Agents are not required to push. A missing heartbeat is not "no data" — it is a signal: the agent is down, stuck, or unreachable, and Min records it as such (emptiness-is-loud). This matches the ground rules' "report, don't re-plan" and Stage 8's premise that absence announces itself.


3. Work-queue integration — A2A messages become tasks.db rows

3.1 The board (REVISED 2026-10-20 22:5x — the topology changed under this section)

CORRECTION — "one board" is now "one board per workstream". The original text said Docs/Kanban/fleet-board.md is the lifecycle truth. That is no longer true, and the change is Young's ruling, applied by Tam (board-split-2026-10-20.md, commit 281e283). Measured live 2026-10-20 22:5x:

HostBoards (the kernel, hermes kanban boards list)
LABdocker-vhost · research · daily-operation · solos-v3 (the build board) · default (empty)
Pulsedaily-report-brief · research · podcast · pulse (archive) · default (archive)

Task truth is hermes kanban, not a file. The .md board was retired as a register (fleet-board.md now carries status: superseded); the per-board vault files (Docs/Kanban/<board>.kanban, Pulse/Kanban/<board>.kanban) are read-only projections generated every 30 min by no-agent crons. SOLOS-V3.md was deleted per Young (5b48edc) and its generator now fails closed (kanban-projection-decision-2026-10-20.md).

A worker still never owns lifecycle truth; it executes and reports back. The per-agent *-tasks.db files are attempt notebooks, not registries (Min's ruling in should-the-board-live-in-cortex.md).

⚠️ The board split is a HOST boundary as well as a topic boundary — a card whose assignee is not a Hermes profile on this host (e.g. tam, a Pulse profile) can never be claimed here, and kanban.dispatch_profiles (fail-closed allowlist, LAB: echo,min,mira,nova,sol) is what enforces that.

3.2 Row shape

Every instruction Min receives (from Young or from an agent's report) becomes one row in the shared store operation/state/tasks.db (one file per workspace, not per agent), with these fields (Tam addendum §2.1):

FieldMeaning
topicwhat the task is
ownerthe single declared owner (ground rules Rule 1)
deadlineETA from the ACK (§4)
stateboard state: triage / todo / ready / running / blocked / review / done / archived
external_task_idthe A2A task id, so one card = one task (join key)
last_heartbeat_atliveness of the worker

Board states come from cortex/tasks (9 wire / 14 local states); every state that leaves the system is a spec wire value (TASK_STATE_*).

3.3 A2A message → row mapping

A2A messageWork-queue action
Heartbeat (§1.2)update last_heartbeat_at for that agent's in-flight rows; no new row
Task completion (tasks/get → terminal state)move the row to done (or review); record the artifact ref
Blocker reportmove the row to blocked; record reason; trigger Min's escalation path
New instruction from Young/Mincreate a row with topic, owner, deadline, state=ready
Agent ACK (§4)set owner + deadline on the row; state → running when work starts

3.4 Min's cron

REVISED 2026-10-20 22:5x. The original text said "Every 30 min Min re-projects the board into the vault (Docs/Kanban/fleet-board.md)". Two things are now true and neither matches that sentence:

  1. The target is per-board, and it is a projection, not the register. The crons write Docs/Kanban/<board>.kanban (LAB: docker-vhost, research, daily-operation) and Pulse/Kanban/<board>.kanban (Pulse: daily-report-brief, research, podcast). fleet-board.md is superseded and hand-edited by other agents — it must never be a generator target (a retarget would delete its custom sections; see kanban-projection-decision-2026-10-20.md).
  2. The projection is generated, the register was hand-written. The per-board .kanban files are machine-written every 30 min by no-agent crons; fleet-board.md was retired as a register.

The principle is unchanged: instructions arrive once, the board carries the state, and agents derive their next step from the board rather than from re-reading chat history (Tam addendum §2.1).

3.5 The 300 s stuck rule (primary)

A task is stuck when it has produced no reply for 300 s (Young's corrected ruling, young-rulings-2026-10-10-four.md §Ruling 3). Source: plugins/platforms/a2a/adapter.py:_MIN_ORPHAN_TIMEOUT = 300. The watchdog sweeps every 60 s (_WATCHDOG_INTERVAL). On timeout the task transitions to TASK_STATE_FAILED with the message "[task orphaned — no reply produced]" — a state, not an absence, and the state names the cause (E14, the honest scope note).

A second, coarser condition (3600 s) records "the worker died" — kanban's DEFAULT_CLAIM_HEARTBEAT_MAX_STALE_SECONDS. Both are recorded; only the 300 s condition fires the follow-up loop.

✅ SCOPE — this rule governs the V3/A2A lane, NOT the Hermes board (2026-10-20 22:5x)

The 300 s rule and the Hermes board's own thresholds are two different mechanisms, and conflating them produces false positives on healthy workers. Measured:

MechanismThresholdGoverns
300 s stuck rule (cortex/tasks/stuck.py → states.yaml:stuck_rule.no_reply_seconds: 300)300 sV3/A2A tasks — a task in the V3 store with no reply
Hermes claim TTL (kanban_db.py:279 DEFAULT_CLAIM_TTL_SECONDS)15 minboard cards — a claim's lifetime
Hermes stranded threshold (kanban_diagnostics.py:688)30 minboard cards — assigned, unclaimed, in ready
Hermes dispatch stale timeout (config_defaults.py:1947)4 hboard cards — a running card with no heartbeat

The V3 rule is correctly implemented — cortex/tasks/stuck.py reads the declared constant from cortex/tasks/states.yaml (no_reply_seconds: 300, with source: plugins/platforms/a2a/adapter.py:38 and ruled_by: young) and never spells a duration in code. Measured live: kanban-t_8d7bde82 went queued -> stuck with the cause "no reply for 423s (>= 300s) — the task is not progressing", and the escalation was written to operation/state/escalations.jsonl.

⚠️ So: do not build a parallel 300 s detector over Hermes board cards. A 300 s rule over a kernel whose own floor is 15 minutes would fire on every healthy worker — every long model call, every arm64 pull. The board already emits reclaimed, crashed, timed_out, stale and skipped_nonspawnable events; map to those, do not re-detect. (The 300 s number remains correct for the V3 lane, where it is implemented and measured.)

A measured instance of exactly this failure, 2026-10-20 22:31–22:54: the V3 lane projected t_8d7bde82 to running on the board with no claim_lock, and the Hermes reconciler (hermes_cli/kanban_db_dispatch.py:845, reconcile_orphaned_running) requeued it in the same second as orphaned_running — then the V3 lane dispatched it again. Five cycles, and the card never ran. The card was not slow and the lane was not dead: the two dispatchers were fighting over one card's bookkeeping.

3.6 Refinement rule

"Refine it when we have more data" is a rule, not a deferral: the 300 s value is a declared constant with its source cited, never a magic number. Signal table (from young-rulings-2026-10-10-four.md):

SignalAction
tasks hitting stuck at 300 s that later completetoo short — raise it
tasks that stop and are not noticed for much longertoo long — lower it
a task class with a different natural durationa per-class override, not a global change

4. Agent ACK-then-own protocol

The working pairs' discipline, made the required default (Tam addendum §2.2):

Whoever receives a task replies within minutes with either "ACK, owning, ETA" or "ACK, escalating to X" or "ACK, blocked on Y".

The absence of an ACK is what Young reads as "losing grip" — because he cannot tell who took the task. This is a one-line rule in every agent's memory, not a skill rewrite.

ACKMeaningWork-queue effect
ACK, owning, ETA <t>I took it, I will deliver by set owner + deadline; state → running when work starts
ACK, escalating to XI am not the owner; handing to Xre-assign owner=X; X must ACK in turn
ACK, blocked on YI cannot proceed; blocker Ystate → blocked; reason recorded; Min escalates

4.2 Multi-tag rule (group channel)

Any message tagged to 3+ agents → the first to ACK owns the topic; the rest explicitly ACK "yielded to owner" or stay silent unless @-mentioned for a specific question. Verification questions (Young asking Tam to verify a specific fact) are the exception — those carry a direct tag and are answered directly (Tam addendum §2.4).

4.3 Find-before-register gate

Before creating any new vault file: find Docs -iname "*<topic>*". If a related file exists, that file is the home — write a section, don't spawn a sibling (Tam addendum §2.3; ground rules Rule 4). This is the direct causal fix for the "same task written multiple times" symptom.

4.4 No agent reports to Young directly

Min is the single voice. An agent's report goes to Min via A2A; Min consolidates and reports to Young. An agent speaks to Young only when directly @-mentioned by Young or Min (ground rules Rule 2).


5. Heartbeat interval + staleness detection

5.1 Interval

  • Heartbeat request cadence: 2 min (aligned with the liveness probe, §2.1).
  • Board projection: 5 min (kanban).
  • System host probes: 5 min.
  • Tunnel probes: 2 min.
  • Collector cron (vault re-projection): 30 min (Tam addendum §2.1).

5.2 Staleness bands (agent health)

From fleet-dashboard-plan.md §5.3 and Sol's annex:

SignalGreenAmberRed
Agent endpointresponds—refused/timeout
Last exchange<30 min<6 h>6 h
In-flight task<300 s—≥300 s (stuck)
Skills countnon-empty—empty (LOUD)

5.3 Stuck rule (primary)

300 s without progress → stuck → Min's follow-up fires without a human deciding (§3.5). A task that stops is a state, not an absence, and the state names the cause (E14).

5.4 A2A auto-session provenance

Inbound A2A spawns a per-context profile session (151 such sessions in Echo's log) that answers as the agent outside its interactive session. It shares memory but not conversation state. An A2A reply is that agent's answer but not necessarily a deliberate one from its interactive session. Provenance must be logged with the session id in the audit log (a2a-connectivity-verification-2026-10-20.md §4). Named here; implementation is Min's lane (fleet board card).


6. Integration with the fleet dashboard (status.json)

The dashboard is the human face of this reporting system. It consumes the same data Min collects; it does not build a second collector stack (Sol's annex §1).

6.1 status.json v0 schema (from fleet-dashboard-plan.md §5.1)

{
  "generated_at": "<iso8601>",
  "stale": false,
  "tasks":    { "board": { "columns": {...}, "cards": [...] },
                "live":  [ {"id","agent","age_s","stuck":bool,"state"} ] },
  "agents":   [ {"name","endpoint","up":bool,"last_exchange_s","skills_count",
                 "lane","model","window":{"model","percent","status","resets_at"} } ],
  "systems":  [ {"host","ram_used_gb","ram_total_gb","disk_free_gb",
                 "services":[{"name","active"}],"tunnels":[{"name","up"}],
                 "crons":[{"name","last_ok_s"}],"vault_parity":bool} ],
  "backups":  { "gdrive_latest": "<date>", "age_h": N }
}

Rules: every array element carries its own checked_at; a failed probe fills the field with "error: <reason>" and sets that element's stale=true — the panel renders red, never omits (emptiness-is-loud).

6.2 Agents panel (Min's read of the reporting system)

One row per agent (11): endpoint status (probed), last successful exchange (from a2a_audit.jsonl), skills count (empty = loud), lane, model, GO window %. Colour rules per §5.2. The reporting system's heartbeat data feeds last_exchange_s and in_flight (→ live[]).

6.3 Tasks panel

Kanban board projection (columns/cards/owners from the .kanban file, auto-synced via git) plus "Live tasks" — in-flight A2A tasks with age vs the 300 s rule, shown separately. A task ≥300 s shows a red STUCK badge (dashboard-page-design.md).

6.4 Systems panel

Hosts (laptop, LAB, prod): RAM/disk/services/tunnels-up/crons-last-run/vault-parity; backup freshness. Collectors run from the laptop (hub-and-spoke); read-only.

6.5 Serving

status.json emitted by Stage 8's collectors on the loopback at /api/status.json, served by prod nginx behind auth_basic (single user young). Refresh: collectors run on cron (2–5 min); dashboard renders the last snapshot — no heavy live polling from the browser. Failure rule: collector cron is deliver=local, quiet-failure; the dashboard's own staleness banner is the alert.

6.6 What must NOT be on the dashboard

Per Sol's annex §2 Q3: no tokens/keys, no per-tenant .env, no Telegram tx hashes, no provider keys, no JWTs, and never a2a_audit.jsonl raw events (only last-exchange age). Only slices cross the gate.


7. Cross-references

7.1 fleet-ground-rules-2026-10-20.md

  • Rule 1 (one owner per topic): every topic has one owner; the owner writes the file. Applied to reporting: each agent owns its own lane's reports; Min owns the consolidated report to Young.
  • Rule 2 (one voice to Young): only Min reports to Young. This entire reporting system exists to make that rule mechanical — agents report to Min via A2A, Min consolidates.
  • Rule 4 (one file per topic): the work queue is one board, not five kanban files.
  • Rule 5 (report, don't re-plan): agents execute assigned work and report back; they do not decide what to do next — Min decides. The ACK-then-own protocol (§4) is the operational form of this rule.
  • Tam's addendum (Part 2): ACK-then-own, find-before-register, work-queue + one Kanban file — all adopted here as §3 and §4.
  • Sol's Rule 8 (blockers reported, not absorbed): blocker reports (§1.2) are the mechanism.

7.2 fleet-ssh-tunnels.md

  • Port registry: all A2A endpoints are reached via the loopback tunnels (laptop 18301–18313 → LAB 8301–8313; LAB → laptop 18441–18443). No Tailscale.
  • Health probes (§2.1) use the ports in this registry; the one-liners in §5 of that doc are the tunnel probes Min runs.
  • Standing rules: loopback-only binds both ends; register every new tunnel in that note; reboot survival pattern (user units + linger + autossh).
  • Dialect routing (a2a-mesh-rectify-2026-10-20.md): Heart/Pulse/Brain speak different dialects; a per-peer dialect: field in the registry is the planned fix. Until then the working recipe (Heart → v1.0 SendMessage + parts; Pulse → v0.3 message/send + content; Brain → either) is the contract.

7.3 fleet-dashboard-plan.md (and Sol's annex)

  • status.json v0 schema (§6.1) is the dashboard's data contract; the reporting system's heartbeat/task/blocker data feeds it.
  • Collectors (§5.2 of that doc: C1 board, C2 agents health, C3 systems health, C4 backups) are the implementation of §2's polling; read-only, run from the laptop, write status.json atomically.
  • Colour rules (§5.3) are the staleness bands of §5.2 here.
  • Sol's annex: observe.py is the data contract (do not rebuild); the dashboard is status.json emitted by Stage 8 collectors (no second stack); a2a_audit.jsonl is projected, never rendered raw; emptiness is loud.
  • P0/P1/P2 phasing: data contract → first page → refresh + alerts (stuck-task alarm at 300 s is P2).

8. What this doc does NOT do

  • It writes no code and modifies no service. Specs only.
  • It does not standardise the A2A dialects across planes (Sol's lane; tracked on the fleet board).
  • It does not implement the A2A auto-session provenance rule (Min's lane; named in §5.4).
  • It does not build the collectors or the dashboard (Tam/Sol lanes; specified in fleet-dashboard-plan.md).
  • It does not change any agent persona or config.

9. Open items (registered, not resolved here)

#ItemOwnerSource
1Mira + Sol have zero a2a_agents peers configured — cannot send A2AMin (registry/projection)a2a-connectivity-verification-2026-10-20.md §2
2A2A dialects diverge per plane; record per-peer dialect: in the registrySola2a-connectivity-verification-2026-10-20.md §3
3A2A auto-session provenance must be logged with session idMina2a-connectivity-verification-2026-10-20.md §4
4Work-queue dispatcher: one tasks.db + single Kanban board projection cronMinfleet board card
5Standardise on one A2A dialect (upgrade Pulse to v1.0 or add v0.3 compat)Sola2a-connectivity-audit-2026-10-20.md §5
6Clean up stale Heart listeners (8304/8305)Sola2a-connectivity-audit-2026-10-20.md §5

Rule: specs only. This document records the design. Implementation is tracked on Docs/Kanban/fleet-board.md and owned by the lanes named above.

Copyright © 2026