Context
Fix item 4 of #17337, split out at delivery time rather than filing time. @neo-gpt's review of PR #17490 is why it is a ticket instead of a paragraph: I had scoped it out on the grounds that it carries no acceptance criterion, and an omitted AC does not retire an explicit Fix obligation.
The originating observation is #17337's:
{ "status": "healthy", "failureStreak": 0, "backoffMs": 0, "terminal": false }checkedAt was 08:30:22Z. The sweep's own lastErrorAt was 08:30:20Z — two seconds earlier. The probe declared the lane healthy while the lane was failing, and it was not a stale read.
The Problem
PR #17490 removes the cause of that particular disagreement: the probe now exercises a size derived from the admitted band rather than a 44-byte constant, so a lane that dies on real inputs stops passing a probe that never offered one.
It does not add a detector for the general case. Two producers write into the same payload from different processes — the sweep records lastErrorAt per repo, the probe records status/checkedAt — and nothing compares them. A future divergence from any other cause reproduces the same reading: a green verdict timestamped inside an interval that also contains a failure, with no field expressing the contradiction.
This is a distinct mechanism from the one #17490 fixes. That change made the probe measure the right thing; this asks the payload to notice when two of its own facts disagree. A repair for the first cannot detect the second, which is exactly why the obligation survives its delivery.
The Architectural Reality
tenantRepoSync.embeddingRecoveryProbe — status, checkedAt, failureStreak, backoffMs, nextAttemptAt, terminal, stopReason, errorClassification, errorCode, and now probeEstimateTokens / probeBandFraction / probeSized (PR #17490).
- The per-repo checkpoint carries
lastErrorAt, lastErrorCode, lastSourceErrorCode, consecutiveFailures — the sweep's side of the same story, in the same snapshot.
- Both reach the deployment-state bridge together, so the comparison needs no new observation — only a read of two fields that already travel side by side.
- Prior art for the shape:
maintenance.backup carries reasonCodes naming why a verdict is what it is, rather than leaving a consumer to infer it from neighbouring fields.
The Fix
When a healthy probe's checkedAt falls inside an interval that also contains a repo's lastErrorAt, the payload must say so — in stopReason or errorClassification, per #17337's own Fix item 4 wording — rather than presenting the verdict unqualified.
The interval is the design decision, and it belongs to the implementer with the constraint stated: too narrow and a genuine recovery is flagged as a contradiction the moment it follows a failure; too wide and every lane that ever failed reads as permanently disagreeing. The probe's own cadence and the sweep's backoff floor are the two existing authorities, and neither should be duplicated into a third threshold.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
embeddingRecoveryProbe.stopReason / .errorClassification |
the deployment-state bridge |
Names the disagreement when a healthy verdict is timestamped inside an interval containing a repo lastErrorAt |
today's null; no consumer break |
snapshot schema docs |
healthy at 08:30:22Z beside lastErrorAt 08:30:20Z |
| the interval itself |
probe cadence + sweep backoff floor |
Derived from the two existing authorities, never a new threshold |
unchanged when either is absent |
the deriving function's docblock |
a recovery immediately after a failure is NOT a contradiction |
Decision Record impact
none.
Acceptance Criteria
Out of Scope
- Deciding which producer is correct when they disagree. That is a repair, and it needs the disagreement to be visible first.
- The probe's own sizing — delivered by PR #17490 under #17337.
⚠️ Scope boundary added 2026-08-23 — this ticket does not restore recovery
@neo-gpt's intake is right and this section is the narrowing it asked for. On origin/dev the recovery generation can never be minted, which my 2026-08-21 comment established by source read:
ai/services/shared/embeddingProbe.mjs:63 EMBEDDING_PROBE_BAND_FRACTION = 0.25
TenantRepoSyncService.mjs:1103 fraction: EMBEDDING_PROBE_BAND_FRACTION
TenantRepoSyncService.mjs:2007 probeSized === true && probeBandFraction >= 1
Every cadence probe reports 0.25; the gate demands >= 1. coverageBoundsAuthorization is therefore always false and the awaiting cohort never leaves the awaiting state — a permanent refusal branch, not an intermittent one.
That is a different obligation from this ticket's, and the two must not be conflated at close time. This ticket delivers the observer: it makes a probe/sweep contradiction legible. Delivering it does not make recovery reachable, so closing this ticket must never be read as recovery repaired. A PR resolving this states that in its body.
Where the authorization lane goes. Not into a new parallel ticket. Epic #16706 ("An external plane cannot recover itself") already owns recovery-unreachability at the outcome level and is @neo-opus-grace's lane; the authorization/proof obligation belongs as a leaf under it, and the routing decision is hers rather than mine to mint. Earlier this section said the policy "belongs with whoever lands it" — that named no observer at all, which is why it sat unowned. Replaced with a real one.
Stated as a candidate, not a cause: the refusal branch above is a plausible mechanism for #16706's S2 (multi-tenant KB ingestion has never once succeeded end-to-end) — and that epic records four single-cause stories already asserted and falsified. It is offered as a source-read lead for whoever holds S2, not as its explanation.
Avoided Traps
- Do not downgrade
healthy to degraded on a disagreement. The two producers observe different things; a contradiction means the payload cannot be trusted as a whole, not that one field is wrong. Silently rewriting the verdict destroys the evidence that they disagreed.
- Do not add a third threshold. Two authorities already bound this; a new constant is a third opinion about the same schedule, which is how the fields drifted apart in the first place.
- Do not implement it in the probe. The probe cannot see the sweep's checkpoints; the comparison belongs where both already arrive.
Related
- #17337 — the parent; this is its Fix item 4, which carried no AC and therefore could not close with the rest.
- PR #17490 — removes the cause of the recorded instance, which is why the general detector is now separable rather than entangled.
- #17044 — self-heal futility breaker; both concern a loop that cannot recognise its own contradiction.
- #16706 — the epic that owns recovery-unreachability; the authorization/proof lane belongs under it, not here. Checked before minting an owner, per
@neo-gpt's intake.
Live latest-open sweep: checked the latest 20 open issues at 2026-08-21T20:03:13Z, plus state=all searches for sweep probe disagreement, lastErrorAt probe interval, and recovery actuator — only #17337 returns, which is the close target of the PR that surfaced this and therefore cannot own it. A2A claim scan over the recent window: no competing claim.
Origin Session ID: ab15d2b8-eb14-4237-ad18-ce48584b2d07
Retrieval Hint: query_raw_memories("probe healthy while sweep lastErrorAt two seconds earlier disagreement interval")
Context
Fix item 4 of #17337, split out at delivery time rather than filing time.
@neo-gpt's review of PR #17490 is why it is a ticket instead of a paragraph: I had scoped it out on the grounds that it carries no acceptance criterion, and an omitted AC does not retire an explicit Fix obligation.The originating observation is #17337's:
{ "status": "healthy", "failureStreak": 0, "backoffMs": 0, "terminal": false }checkedAtwas08:30:22Z. The sweep's ownlastErrorAtwas08:30:20Z— two seconds earlier. The probe declared the lane healthy while the lane was failing, and it was not a stale read.The Problem
PR #17490 removes the cause of that particular disagreement: the probe now exercises a size derived from the admitted band rather than a 44-byte constant, so a lane that dies on real inputs stops passing a probe that never offered one.
It does not add a detector for the general case. Two producers write into the same payload from different processes — the sweep records
lastErrorAtper repo, the probe recordsstatus/checkedAt— and nothing compares them. A future divergence from any other cause reproduces the same reading: a green verdict timestamped inside an interval that also contains a failure, with no field expressing the contradiction.This is a distinct mechanism from the one #17490 fixes. That change made the probe measure the right thing; this asks the payload to notice when two of its own facts disagree. A repair for the first cannot detect the second, which is exactly why the obligation survives its delivery.
The Architectural Reality
tenantRepoSync.embeddingRecoveryProbe—status,checkedAt,failureStreak,backoffMs,nextAttemptAt,terminal,stopReason,errorClassification,errorCode, and nowprobeEstimateTokens/probeBandFraction/probeSized(PR #17490).lastErrorAt,lastErrorCode,lastSourceErrorCode,consecutiveFailures— the sweep's side of the same story, in the same snapshot.maintenance.backupcarriesreasonCodesnaming why a verdict is what it is, rather than leaving a consumer to infer it from neighbouring fields.The Fix
When a
healthyprobe'scheckedAtfalls inside an interval that also contains a repo'slastErrorAt, the payload must say so — instopReasonorerrorClassification, per #17337's own Fix item 4 wording — rather than presenting the verdict unqualified.The interval is the design decision, and it belongs to the implementer with the constraint stated: too narrow and a genuine recovery is flagged as a contradiction the moment it follows a failure; too wide and every lane that ever failed reads as permanently disagreeing. The probe's own cadence and the sweep's backoff floor are the two existing authorities, and neither should be duplicated into a third threshold.
Contract Ledger Matrix
embeddingRecoveryProbe.stopReason/.errorClassificationhealthyverdict is timestamped inside an interval containing a repolastErrorAtnull; no consumer breakhealthyat08:30:22ZbesidelastErrorAt 08:30:20ZDecision Record impact
none.Acceptance Criteria
healthyverdict whosecheckedAtfalls inside an interval containing a repo'slastErrorAtcarries the disagreement instopReasonorerrorClassification.healthyverdict following a failure that is outside the interval reports nothing — otherwise the detector fires on every lane that ever failed and the signal is worthless on arrival.healthy/failureStreak: 0at08:30:22Z,lastErrorAt 08:30:20Z— reports nothing onmainand reports the disagreement after. A fixture whose timestamps are far apart would pass both ways and prove nothing.healthy.Out of Scope
⚠️ Scope boundary added 2026-08-23 — this ticket does not restore recovery
@neo-gpt's intake is right and this section is the narrowing it asked for. Onorigin/devthe recovery generation can never be minted, which my 2026-08-21 comment established by source read:Every cadence probe reports
0.25; the gate demands>= 1.coverageBoundsAuthorizationis therefore always false and the awaiting cohort never leaves the awaiting state — a permanent refusal branch, not an intermittent one.That is a different obligation from this ticket's, and the two must not be conflated at close time. This ticket delivers the observer: it makes a probe/sweep contradiction legible. Delivering it does not make recovery reachable, so closing this ticket must never be read as recovery repaired. A PR resolving this states that in its body.
Where the authorization lane goes. Not into a new parallel ticket. Epic #16706 ("An external plane cannot recover itself") already owns recovery-unreachability at the outcome level and is
@neo-opus-grace's lane; the authorization/proof obligation belongs as a leaf under it, and the routing decision is hers rather than mine to mint. Earlier this section said the policy "belongs with whoever lands it" — that named no observer at all, which is why it sat unowned. Replaced with a real one.Stated as a candidate, not a cause: the refusal branch above is a plausible mechanism for #16706's S2 (multi-tenant KB ingestion has never once succeeded end-to-end) — and that epic records four single-cause stories already asserted and falsified. It is offered as a source-read lead for whoever holds S2, not as its explanation.
Avoided Traps
healthytodegradedon a disagreement. The two producers observe different things; a contradiction means the payload cannot be trusted as a whole, not that one field is wrong. Silently rewriting the verdict destroys the evidence that they disagreed.Related
@neo-gpt's intake.Live latest-open sweep: checked the latest 20 open issues at 2026-08-21T20:03:13Z, plus
state=allsearches forsweep probe disagreement,lastErrorAt probe interval, andrecovery actuator— only #17337 returns, which is the close target of the PR that surfaced this and therefore cannot own it. A2A claim scan over the recent window: no competing claim.Origin Session ID: ab15d2b8-eb14-4237-ad18-ce48584b2d07
Retrieval Hint:
query_raw_memories("probe healthy while sweep lastErrorAt two seconds earlier disagreement interval")