LearnNewsExamplesServices
Frontmatter
id17295
titleA poll-and-skip consumer cannot starve, because nothing records that it waited
stateClosed
labels
bugaiarchitecture
assigneesneo-opus-vega
createdAtAug 17, 2026, 1:55 PM
updatedAtAug 17, 2026, 2:38 PM
githubUrlhttps://github.com/neomjs/neo/issues/17295
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 17, 2026, 2:38 PM

A poll-and-skip consumer cannot starve, because nothing records that it waited

Closed Backlog/active-chunk-17 bugaiarchitecture
neo-opus-vega
neo-opus-vega commented on Aug 17, 2026, 1:55 PM

A poll-and-skip consumer cannot starve, because nothing records that it waited

Context

Found on a live external plane while diagnosing an unrelated embedding fault, and traced to its mechanism by @neo-opus-grace, who flagged it to this surface rather than claiming it because it sits against #17049 / #17290's contract.

The orchestrator had been logging this on a ~30s cadence for hours:

[Orchestrator] Deferring provider residency repair; heavy maintenance task tenant-repo-sync is active.

Meanwhile that plane's heavyMaintenanceStarvation receipt reported exactly three breaches — graphlog-compaction, message-concept-harvest, summary — and not the residency repair, at the same moment it was deferring every thirty seconds. The starvation surface was not wrong. It was structurally unable to see this consumer.

The Problem

Orchestrator.isProviderWarmStillAdmitted() (ai/daemons/orchestrator/Orchestrator.mjs:675-696) is a poll-and-skip, not a lease waiter:

activeTask = this.maintenanceBackpressureService.getActiveHeavyMaintenanceTask();
if (activeTask) {
    this.writeLog('INFO', `[Orchestrator] Deferring provider residency repair; heavy maintenance task ${activeTask} is active.`);
    return false;
}
if (this.isHeavyMaintenanceLeaseActive(now)) { /* log */ return false }
return true;

It inspects, it logs, it returns. It never calls registerWaiterSync — which exists and is used by the real registration path at services/MaintenanceBackpressureService.mjs:663. Three consequences, each worse than the last:

  1. The starvation watchdog cannot report it. foldHeavyMaintenanceStarvation scores from listActiveWaitersSync; waiterCount is waiters.length. A consumer that never becomes a waiter contributes zero, forever. #17290 corrected that fold's freshness bound so a starved waiter is now visible on every poll rather than one in five — and this consumer remains invisible at any bound, because the defect is upstream of the receipt.
  2. Lease fairness cannot help it. Fairness yields to registered waiters. There is nobody to be fair to, so the three-rank yield gate never considers it.
  3. #17132 will not unblock it. Its slice budget rotates repos within the sync task, and its AC-4 explicitly preserves lease semantics — the lease-level yield still ends the whole task while the repo-slice yield rotates inside it. The task therefore stays active across rotation, getActiveHeavyMaintenanceTask() keeps returning it, and the repair keeps deferring. Fairer tenants, same starved actuator. (Confirmed by @neo-opus-grace, who owns that slice.)

Fixing the incident that produced this — the long-running sweep — clears this instance: the task goes idle and the poll finally succeeds. The mechanism survives untouched. Any long-running heavy task starves the residency repair indefinitely while every starvation surface reads clean.

The Architectural Reality

  • Orchestrator.mjs:675-696 — the poll-and-skip, both branches log-and-return.
  • services/heavyMaintenanceWaiterLedger.mjs:65registerWaiterSync({leasePath, taskName, priorityZero, bootstrapCritical, deferredSince, …}); note deferredSince must be the durable ISO streak start, and the function throws rather than accept an unmeasured wait as a fresh one. A consumer registering here must therefore own a durable streak anchor, not a call-time timestamp.
  • services/MaintenanceBackpressureService.mjs:663 — the existing registration call site, the shape a new registrant should follow.
  • ai/services/memory-core/HealthService.mjsfoldHeavyMaintenanceStarvation, the consumer whose reach this bounds.
  • Live evidence: three breaches reported while a fourth consumer deferred every 30s for hours.

The Fix

The residency repair registers as a waiter when it defers, and clears when it proceeds — so its wait becomes a measured fact rather than a log line. Whether it should additionally be admitted by the fairness gate is the open design question and deliberately not pre-decided here: registration makes it visible, admission would make it scheduled, and those are separable.

The narrower question a reviewer should press: is isProviderWarmStillAdmitted the only poll-and-skip consumer of getActiveHeavyMaintenanceTask(), or a representative of a class? A census of that method's callers belongs in this work — if others inspect-and-skip the same way, the fix is a shared registration helper rather than one call site.

Acceptance Criteria

  • A deferring residency repair appears in listActiveWaitersSync with a durable deferredSince streak anchor, and disappears when it proceeds.
  • Its starvation past the risk bound surfaces in heavyMaintenanceStarvation.breaches and degrades aggregate health, through the existing #17049 fold with no change to that contract.
  • The streak anchor survives a restart — a repair deferring for hours across an orchestrator recycle reports the true streak, not a fresh one (registerWaiterSync already refuses the unmeasured case; the caller must supply the durable value).
  • Every caller of getActiveHeavyMaintenanceTask() is enumerated, and each is either a registered waiter or carries a recorded reason why it legitimately is not.
  • Mutation control: reverting the registration returns the consumer to invisible, proving the spec observes registration rather than the log line.

Out of Scope

Admission/scheduling changes — the fairness gate's ranking is untouched by this work · #17132's slice budget, which is orthogonal and confirmed not to resolve this · the embedding fault that surfaced it · whether the residency repair should run during heavy maintenance at all, which is a policy question this ticket deliberately leaves open by making the wait measurable first.

Avoided Traps

  • Treating this as a #17290 regression. It is not: that fix corrected the receipt's freshness bound and is working. This consumer is invisible upstream of the receipt, so no bound could reach it. Conflating them would send someone back into a fold that is already correct.
  • Fixing the instance instead of the mechanism. Clearing the long sweep makes the symptom vanish and leaves the class intact for the next long-running task.
  • Assuming a log line is an observation. It is the log that made this look reported; the structured surface never carried it, and only the structured surface degrades health.

Related

#17049 (the starvation health contract) · #17290 / PR #17292 (its freshness repair — the fold this gap sits upstream of) · #17132 (@neo-opus-grace, confirmed NOT a fix for this) · #16706 (external plane self-recovery, the adjacent theme) · #17072.

Live latest-open sweep: latest 20 open checked 2026-08-17T11:54Z, plus a state:all keyword sweep on four phrasings; nearest neighbours #16706 / #16948 / #17063 / #17072, no equivalent. A2A herd-window sweep clean.

Origin Session ID: c992afd0-2e26-410e-b460-b480ccd0a240

Retrieval Hint: query_raw_memories("provider residency repair poll-and-skip never registers waiter invisible starvation")

tobiu referenced in commit 695490d - "feat(ai): a deferring residency repair registers its wait instead of logging it (#17295) (#17298) on Aug 17, 2026, 2:38 PM
tobiu closed this issue on Aug 17, 2026, 2:38 PM