Skip to content
This repository was archived by the owner on Jul 24, 2026. It is now read-only.
This repository was archived by the owner on Jul 24, 2026. It is now read-only.

Ding refreshes status for a dead provider — an unarmed sidecar forges liveness #101

Description

@schickling-assistant

Problem

A Ding sidecar that never armed still refreshes its target's status. When the provider process dies, Ding keeps writing liveness, so the agent reads available indefinitely — for a process that no longer exists.

This makes status unsafe to trust as a liveness signal, which is the one job it has.

Why this matters

For an always-on supervised role (a Chief of Staff, an orchestrator), the failure is silent and permanent:

  • the provider crashes
  • Ding keeps the status fresh, so every consumer reads the agent as healthy
  • nothing alerts, nothing restarts

The agent is dead and the bus says it is fine. An operator has no signal until they notice the work stopped. This is worse than a missing feature, because the system actively reports the opposite of the truth during the exact failure the signal exists to expose.

It also breaks the intended contract that a sidecar keeps liveness fresh and lets status decay honestly when the session is no longer running. Only the first half currently holds.

Evidence

Observed on a Linux host, convoy 0.2.0-ts.0, running a managed session with two limbs — provider <host>.w1 and sidecar <host>.w1.ding — in an isolated network.

1. Status advanced while the provider was dead. After killing the provider process, status mtime kept moving forward and both st agents and convoy ls --tree reported available.

2. Ding was the writer. Killing Ding froze the mtime permanently.

3. Observer effect ruled out. With both limbs dead, running st agents and convoy ls repeatedly left the mtime untouched. So the refresh was not a side effect of reading — Ding was actively writing.

4. Ding never armed. Its entire event log for the session is 206 bytes containing a single session_start record. The log shows:

target session "<host>.w1" not yet registered

despite the target being registered at that point. Ding never armed, never delivered a message — and still wrote liveness for the whole session.

So the status write is not gated on the sidecar having successfully attached to its target.

Proposed direction

The invariant I would suggest: a sidecar must not write liveness it has not earned. Concretely, one of:

  • (a) Arm-or-exit. If Ding cannot register against its target, it exits non-zero rather than running in a degraded state that still writes status. Simplest, and makes the failure loud.
  • (b) Gate the write on armed state. Ding keeps running but writes status only while it holds a verified attachment to a live target.
  • (c) Liveness derives from the provider, not the sidecar. Ding reports what it observes rather than being the source of truth.

(a) is the smallest change and would have surfaced this immediately. (c) is the most principled but the largest.

What I would like consensus on

  1. Is Ding intended to be the writer of status at all, or is that an accident of it being the long-lived limb?
  2. Should an unarmed sidecar be a hard failure, or a degraded-but-running state? If degraded is intentional, what should it be allowed to write?
  3. Does status mean "this identity has a live session" or "something is tending this folder"? The two readings diverge exactly here, and consumers currently assume the first.

Happy to put up a PR once there is agreement on which of (a)/(b)/(c) is wanted — the fix differs a lot depending on the answer to Q3.

Related

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