LearnNewsExamplesServices
Frontmatter
id16903
titlePersist task deferral streaks across orchestrator restarts
stateClosed
labels
bugaiagent-os
assigneesneo-opus-vega
createdAtAug 10, 2026, 6:51 PM
updatedAtAug 10, 2026, 9:12 PM
githubUrlhttps://github.com/neomjs/neo/issues/16903
authorneo-gpt-emmy
commentsCount0
parentIssue16561
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 10, 2026, 9:12 PM

Persist task deferral streaks across orchestrator restarts

Closed Backlog/active-chunk-15 bugaiagent-os
neo-gpt-emmy
neo-gpt-emmy commented on Aug 10, 2026, 6:51 PM

Context

Parent #16561 established that a priority-0 task can win selection yet remain unable to acquire the heavy-maintenance lease. Its first measurement slice shipped in #16564: repeated deferrals keep a non-sliding anchor in one process. The remaining prerequisite exposed by PR #16900 is narrower: that anchor lived only in MaintenanceBackpressureService.deferralStreakStarts, so an orchestrator restart erased the elapsed wait.

Exact-head review at 2cbf5f9f49d475dca95bf6c4956f1878445bfe68 independently removed the production taskStateService carriage and proved the focused suites still passed 57/57. That does not falsify the source repair; it identifies the production-binding test debt retained below.

The Problem

A process-local deferral anchor makes a starvation threshold structurally restart-sensitive. A task can remain continuously unable to run while every daemon restart makes its wait appear fresh. The future health slice in #16561 therefore cannot consume a trustworthy elapsed duration until the first-deferral timestamp survives process lifetimes and clears only when the task actually starts.

This leaf is intentionally smaller than #16561. It delivers the durable measurement prerequisite; it does not claim the health threshold, its unprotected-data-window derivation, or lease fairness.

The Architectural Reality

  • ai/daemons/orchestrator/services/TaskStateService.mjs owns the persisted per-task envelope and merges stored fields over the current default on read.
  • ai/daemons/orchestrator/services/MaintenanceBackpressureService.mjs owns deferral recording and publishes deferredSince on skipped outcomes.
  • ai/daemons/orchestrator/Orchestrator.mjs already injects the canonical TaskStateService into MaintenanceBackpressureService.
  • TaskStateService.markStarted() is the universal transition proving that the task is no longer deferred.
  • ADR 0022 AC-3 requires stall and deferral observation to ride the durable task-state flow rather than an ephemeral in-memory collection.

Structure-map gate: npm run --silent ai:structure-map -- --files --loc resolves this work to the existing ai/daemons/orchestrator/services/ owners; no new module is introduced.

The Fix

  1. Add nullable deferralStreakStartedAt to the persisted task-state envelope.
  2. Open it with ??= on the first deferral so later polls and blocker changes cannot slide the anchor.
  3. Persist and return it through TaskStateService.markDeferred().
  4. Make MaintenanceBackpressureService.recordDeferral() prefer that durable result over its process-local fallback.
  5. Clear the streak in TaskStateService.markStarted().
  6. Preserve legacy state files by merging an absent field to null; no migration writer is required.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
persisted task-state deferralStreakStartedAt TaskStateService first deferral opens once; subsequent deferrals preserve it; markStarted() clears it legacy/absent field reads as null JSDoc on the envelope and writer first-anchor, restart, clear, and fresh-after-run unit controls in PR #16900
skipped outcome deferredSince MaintenanceBackpressureService.recordDeferral() consuming the task-state writer durable anchor wins over the process-local map direct callers without a task-state service retain the existing process-local behavior existing outcome JSDoc production source trace; composition mutation debt is owned by #16904

Decision Record impact

aligned-with ADR 0022 — specifically AC-3's durable task-state placement. The work does not change ADR 0014's task taxonomy, the picker, lease ownership, or scheduling behavior.

Acceptance Criteria

  • The first deferral persists one deferralStreakStartedAt value and later deferrals do not slide it.
  • A fresh TaskStateService instance reading the same state file recovers that original value.
  • Starting the task clears the durable streak; a later deferral opens a fresh value.
  • Legacy persisted envelopes without the field read as null.
  • No picker, lease, cadence, or task-class behavior changes.

Out of Scope

  • The health-surface threshold and its unprotected-data-window derivation, retained by #16561.
  • Lease waiter registration, preemption, or fairness policy.
  • Changes to PRIORITY_ZERO_TASKS, staleAfterMs, cadence, or picker ranking.
  • Live restart validation on a deployed plane; PR #16900 records this as L3-deferred.

Avoided Traps

  • Closing #16561 from a prerequisite-only PR. Four parent acceptance criteria remain intentionally open.
  • Persisting every skip. “Nothing to do” is not deferral; only the deferral writer opens the streak.
  • Sliding the timestamp on every poll. That would make the eventual threshold unreachable.
  • Deleting the process-local fallback. Direct pure-function callers retain their prior bounded behavior.

Related

  • Parent: #16561
  • Prior non-sliding measurement slice: #16564
  • Implementation: PR #16900
  • Production-binding and write-churn follow-up: #16904
  • Durable-flow authority: ADR 0022

Live latest-open sweep: checked the 20 newest open issues immediately before creation; no equivalent restart-durability leaf existed. Exact title/body sweep found only parent #16561 and resolved predecessor #16564. A2A in-flight sweep: checked the latest 30 messages across read states; Vega's #16561 implementation lane was the sole overlapping claim and is the implementation this leaf records.

Origin Session ID: d813fe26-4096-4c43-81fc-be4241b270cb

Retrieval Hint: query_raw_memories("priority-zero deferral streak process-local restart TaskStateService #16561 #16900")

tobiu referenced in commit 4ba4621 - "feat(agent-os): a deferral streak survives the process that measured it (#16903) (#16900) on Aug 10, 2026, 9:12 PM
tobiu closed this issue on Aug 10, 2026, 9:12 PM