CB-597: bridged.example.yaml documents a schema five sections out of date, including the one that makes replies durable #85

Closed
opened 2026-08-16 17:04:02 +02:00 by ltms · 2 comments
Owner

Found on 2026-08-16 during release-1 close-out review. Sibling of #81 (CB-595) but a different surface: that one is the feature catalogue, this one is the config schema.

Why this file carries more weight than it looks

bridged/bridged.yaml is gitignored. A worker never sees it, CI never checks it, and a new host has nothing to copy. So bridged.example.yaml is the only committed description of the config schema. If it is wrong, there is no second source.

The drift

Top-level keys, example against live:

example:  bind  herdrSocket  profiles  placement  fleet  guard
live:     bind  profiles  quarantineCooldownSeconds  placement  configReload
          fleet  health  lifecycle  guard  broker

Missing from the example entirely:

Section What an operator loses
broker: the durable reply inbox. Absent → in-memory, and a reply does not survive a daemon bounce. This is CB-307 Stage 2, the whole point of the AMQP work
lifecycle: idleTtlSeconds, contextCap, drainTimeoutSeconds — CB-303. A member is reaped on a timer the operator cannot find documented
health: fleet health detection. The daemon logs fleet health: detection-only (no notification sink configured) at every boot and the example never mentions the key that fixes it
configReload: which keys are hot and which need a restart
quarantineCooldownSeconds: CB-578 credential quarantine

The file is 464 lines — longer than the live 424-line config. So it does not look neglected. It looks complete and is silently a schema or two behind, which is worse than an obviously short file.

Also nested and undocumented: fleet.leaders, fleet.architects, gitTokenEnv, credentialId, subscription, tabPrefix, scanIntervalSeconds, paneProbeIntervalSeconds, workingSuspectAfterSeconds, instances.

Corroboration

docs/CB-307-Reliable-Delivery.md still says "do not add a broker: config block (that arrives with Stage 2 landing)". Stage 2 landed at 2bc5f3a. The example was written against the pre-Stage-2 schema and never revisited. Spotted independently by the CB-527/528 worker while working PR #83.

The good news

The example does not carry the retired primary: / terminal: keys that CB-579 now rejects at load, so it should still parse. This is an omission problem, not a broken-file problem. Confirm that rather than assume it.

Acceptance criteria

  1. Every top-level and nested config key the code actually reads appears in bridged.example.yaml, with a comment saying what it does and what the default is.
  2. broker: is documented with a working example URI and an explicit statement of what changes when it is absent: replies are held in memory and do not survive a restart.
  3. health: documents the notification sink, so fleet health: detection-only at boot has a fix an operator can find.
  4. A test fails when a config key exists in the code but not in the example. This is the criterion that matters — without it the file drifts again the moment someone adds a key, which is exactly how it got here. If a full schema-reflection test is too much, a narrower one over the top-level sections is acceptable; say which you built and why.
  5. bridged.example.yaml loads cleanly through BridgedConfig.load, proved by a test rather than by inspection.
  6. Fix the stale line in docs/CB-307-Reliable-Delivery.md that tells the reader not to add a broker: block.

Not in scope

  • bridged.yaml itself. It is the operator's gitignored runtime config; changing it is the lead's job at merge time, not a worker's.
  • Any secret value. The example carries env var names only.
Found on 2026-08-16 during release-1 close-out review. Sibling of #81 (CB-595) but a different surface: that one is the feature catalogue, this one is the config schema. ## Why this file carries more weight than it looks `bridged/bridged.yaml` is **gitignored**. A worker never sees it, CI never checks it, and a new host has nothing to copy. So `bridged.example.yaml` is the **only** committed description of the config schema. If it is wrong, there is no second source. ## The drift Top-level keys, example against live: ``` example: bind herdrSocket profiles placement fleet guard live: bind profiles quarantineCooldownSeconds placement configReload fleet health lifecycle guard broker ``` **Missing from the example entirely:** | Section | What an operator loses | |---|---| | `broker:` | **the durable reply inbox.** Absent → in-memory, and a reply does not survive a daemon bounce. This is CB-307 Stage 2, the whole point of the AMQP work | | `lifecycle:` | `idleTtlSeconds`, `contextCap`, `drainTimeoutSeconds` — CB-303. A member is reaped on a timer the operator cannot find documented | | `health:` | fleet health detection. The daemon logs `fleet health: detection-only (no notification sink configured)` at every boot and the example never mentions the key that fixes it | | `configReload:` | which keys are hot and which need a restart | | `quarantineCooldownSeconds:` | CB-578 credential quarantine | The file is **464 lines** — *longer* than the live 424-line config. So it does not look neglected. It looks complete and is silently a schema or two behind, which is worse than an obviously short file. Also nested and undocumented: `fleet.leaders`, `fleet.architects`, `gitTokenEnv`, `credentialId`, `subscription`, `tabPrefix`, `scanIntervalSeconds`, `paneProbeIntervalSeconds`, `workingSuspectAfterSeconds`, `instances`. ## Corroboration `docs/CB-307-Reliable-Delivery.md` still says *"do not add a `broker:` config block (that arrives with Stage 2 landing)"*. Stage 2 landed at `2bc5f3a`. The example was written against the pre-Stage-2 schema and never revisited. Spotted independently by the CB-527/528 worker while working PR #83. ## The good news The example does **not** carry the retired `primary:` / `terminal:` keys that CB-579 now rejects at load, so it should still parse. This is an omission problem, not a broken-file problem. Confirm that rather than assume it. ## Acceptance criteria 1. Every top-level and nested config key the code actually reads appears in `bridged.example.yaml`, with a comment saying what it does and what the default is. 2. `broker:` is documented with a **working** example URI and an explicit statement of what changes when it is absent: replies are held in memory and do not survive a restart. 3. `health:` documents the notification sink, so `fleet health: detection-only` at boot has a fix an operator can find. 4. A test fails when a config key exists in the code but not in the example. **This is the criterion that matters** — without it the file drifts again the moment someone adds a key, which is exactly how it got here. If a full schema-reflection test is too much, a narrower one over the top-level sections is acceptable; say which you built and why. 5. `bridged.example.yaml` loads cleanly through `BridgedConfig.load`, proved by a test rather than by inspection. 6. Fix the stale line in `docs/CB-307-Reliable-Delivery.md` that tells the reader not to add a `broker:` block. ## Not in scope - `bridged.yaml` itself. It is the operator's gitignored runtime config; changing it is the lead's job at merge time, not a worker's. - Any secret value. The example carries env var **names** only.
ltms added this to the 1.1 — single-host close-out milestone 2026-08-16 17:04:02 +02:00
Author
Owner

Delivered and merged as PR #92 — but the premise of this ticket was wrong, and that is worth recording.

I claimed five whole sections were missing from bridged.example.yaml, including broker:. Nothing was missing. My comparison script matched only uncommented lines, so every section documented as a commented-out example — which is most of the file — looked absent. Earlier tickets (CB-573, CB-566, CB-559, CB-579, CB-527/528) had each updated the example alongside their feature. The process worked; my check did not.

The worker read the parser instead of trusting my list, and found real problems I had not asked about:

  • health.workingSuspectAfterSeconds and paneProbeIntervalSeconds documented enforced minimums that do not exist. I verified this myself: both names appear only in the Health record declaration and are read by nothing. Only intervalSeconds is clamped, and it is silently raised to 15 rather than rejected.
  • notifications.mode: webhook only changes what bridge_list reports as healthCoverage. No webhook is ever sent — "webhook" appears in a single configured() boolean and there is no delivery code anywhere in the repo.
  • lifecycle.clearAfterTurn was undocumented, and is a no-op for any peer kind other than claude-code.
  • The reload documentation claimed the whole fleet: block is hot. fleet.leaders is built once at startup and never rebuilt, so a change is silently accepted, reported as reloaded, and does nothing until a restart.
  • The fleet.leaders demotion consequence now sits next to the block itself: a pane that matches no tab: is silently an ordinary worker, every orchestration call it makes is refused, and there is no startup error.

Two of those tell an operator a knob does nothing. That is more valuable than the sections I thought were missing, and it is the opposite of what a "documentation catch-up" ticket normally produces.

Verified by the lead: parses cleanly under the project's own snakeyaml 1.30; both dead-knob claims confirmed by grep against src/main rather than taken on trust.

One thing left alone as out of scope: primary: is documented twice, once near the top and once near the bottom. Cosmetic.

Delivered and merged as PR #92 — but **the premise of this ticket was wrong, and that is worth recording.** I claimed five whole sections were missing from `bridged.example.yaml`, including `broker:`. Nothing was missing. My comparison script matched only uncommented lines, so every section documented as a commented-out example — which is most of the file — looked absent. Earlier tickets (CB-573, CB-566, CB-559, CB-579, CB-527/528) had each updated the example alongside their feature. The process worked; my check did not. The worker read the parser instead of trusting my list, and found real problems I had not asked about: - `health.workingSuspectAfterSeconds` and `paneProbeIntervalSeconds` documented **enforced minimums that do not exist**. I verified this myself: both names appear only in the `Health` record declaration and are read by nothing. Only `intervalSeconds` is clamped, and it is silently raised to 15 rather than rejected. - `notifications.mode: webhook` only changes what `bridge_list` reports as `healthCoverage`. **No webhook is ever sent** — "webhook" appears in a single `configured()` boolean and there is no delivery code anywhere in the repo. - `lifecycle.clearAfterTurn` was undocumented, and is a no-op for any peer kind other than claude-code. - The reload documentation claimed the whole `fleet:` block is hot. `fleet.leaders` is built once at startup and never rebuilt, so a change is silently accepted, reported as reloaded, and does nothing until a restart. - The `fleet.leaders` demotion consequence now sits next to the block itself: a pane that matches no `tab:` is silently an ordinary worker, every orchestration call it makes is refused, and there is no startup error. Two of those tell an operator a knob does nothing. That is more valuable than the sections I thought were missing, and it is the opposite of what a "documentation catch-up" ticket normally produces. Verified by the lead: parses cleanly under the project's own snakeyaml 1.30; both dead-knob claims confirmed by grep against `src/main` rather than taken on trust. One thing left alone as out of scope: `primary:` is documented twice, once near the top and once near the bottom. Cosmetic.
ltms closed this issue 2026-08-16 17:59:58 +02:00
Author
Owner

Correction on the close: I closed this having delivered four of six criteria, not all six. Being precise about which:

  • 1, 2, 3 (every key documented, broker: with its absent-behaviour, health: notification sink) — satisfied, though as noted above they were largely already satisfied before this ticket.
  • 5 (the example loads through BridgedConfig.load, proved by a test) — already satisfied before the ticket by BridgedConfigTest.shippedExampleConfigParses. I did not check for it when writing the criteria.
  • 6 (the stale "do not add a broker: block" line in docs/CB-307-Reliable-Delivery.md) — already fixed on main; grep finds nothing. Also already true when I wrote it.
  • 4 (a test that fails when a config key exists in the code but not the example) — not delivered. Split out as #96 (CB-602).

That last one is the one I described as "the criterion that matters", so leaving it as a line in a closed ticket would have buried it. #96 carries the full reasoning, including why the check must scan the example as text rather than parsed YAML — reading it as YAML is precisely the mistake that produced this ticket's wrong premise.

Staying closed. The schema-accuracy work is done; the drift guard is its own piece of work.

Correction on the close: I closed this having delivered **four of six** criteria, not all six. Being precise about which: - **1, 2, 3** (every key documented, `broker:` with its absent-behaviour, `health:` notification sink) — satisfied, though as noted above they were largely already satisfied before this ticket. - **5** (the example loads through `BridgedConfig.load`, proved by a test) — already satisfied before the ticket by `BridgedConfigTest.shippedExampleConfigParses`. I did not check for it when writing the criteria. - **6** (the stale "do not add a `broker:` block" line in `docs/CB-307-Reliable-Delivery.md`) — already fixed on `main`; grep finds nothing. Also already true when I wrote it. - **4** (a test that fails when a config key exists in the code but not the example) — **not delivered.** Split out as #96 (CB-602). That last one is the one I described as "the criterion that matters", so leaving it as a line in a closed ticket would have buried it. #96 carries the full reasoning, including why the check must scan the example as **text** rather than parsed YAML — reading it as YAML is precisely the mistake that produced this ticket's wrong premise. Staying closed. The schema-accuracy work is done; the drift guard is its own piece of work.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: fleet/fleetd#85