LearnNewsExamplesServices
Frontmatter
id16904
titleBind deferral persistence at the production backpressure seam
stateClosed
labels
enhancementaitestingagent-os
assigneesneo-gpt
createdAtAug 10, 2026, 6:53 PM
updatedAtAug 11, 2026, 5:14 PM
githubUrlhttps://github.com/neomjs/neo/issues/16904
authorneo-gpt-emmy
commentsCount0
parentIssue16561
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 11, 2026, 5:14 PM

Bind deferral persistence at the production backpressure seam

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

Context

PR #16900 makes the per-task deferral anchor durable under #16903. Its exact-head source path is correct, but independent review found that the committed tests exercise TaskStateService.markDeferred() directly and never bind the production delegation from MaintenanceBackpressureService.recordDeferral().

A disposable exact-head mutation removed only taskStateService: this.taskStateService from the production wrapper. Both focused suites still passed 57/57. The same source audit found that every unchanged deferral poll rewrites the complete task-state file after the first anchor is already durable.

The Problem

The persisted primitive is guarded, while the composition that makes runtime deferrals use it is not. A future refactor can disconnect the injected service and silently fall back to the process-local map without a red test—the exact restart-reset shape #16903 repairs.

Separately, markDeferred() performs synchronous whole-envelope persistence on every poll even though the ??= contract means only the first deferral changes state. Correctness is preserved, but repeated blocked polls create avoidable write churn in the orchestrator hot path.

The Architectural Reality

  • TaskStateService owns persistence and the non-sliding deferralStreakStartedAt field.
  • MaintenanceBackpressureService.recordDeferral() is the production composition boundary that must invoke markDeferred() and publish its returned anchor.
  • The in-memory map is an intentional compatibility fallback for direct callers; the test must not delete it.
  • ADR 0022 AC-3 requires the live deferral flow—not merely its isolated persistence helper—to remain durable across process lifetimes.

Structure-map gate: npm run --silent ai:structure-map -- --files --loc places both changes in the existing ai/daemons/orchestrator/services/ owner; no new module is needed.

The Fix

  1. Add a production-bound MaintenanceBackpressureService test that supplies a task-state writer, proves markDeferred() is called, and proves its durable anchor wins over the local map.
  2. Recreate the task-state service over the same persisted file and verify a changed blocker still publishes the original anchor.
  3. Verify the actual markStarted() transition clears the streak and the next deferral opens a new anchor.
  4. Avoid a whole-file rewrite when markDeferred() observes an already-open, unchanged streak, without weakening first-write durability or the synchronous-before-outcome ordering.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
backpressure → task-state delegation MaintenanceBackpressureService.recordDeferral() every production deferral consumes TaskStateService.markDeferred(); returned durable value wins process-local map remains only when no task-state service is supplied existing service JSDoc composition mutation must turn the new test red
task-state persistence writes TaskStateService.markDeferred() persist synchronously on first open; avoid rewriting an unchanged anchor persistence failure remains loud under the existing writer contract method JSDoc first-open write count + repeated-deferral no-op control

Decision Record impact

aligned-with ADR 0022 — strengthens AC-3's durable task-state composition and does not alter scheduling policy.

Acceptance Criteria

  • Removing the production MaintenanceBackpressureService → TaskStateService.markDeferred() carriage turns a named test red.
  • The production composition publishes the durable anchor across service recreation and blocker change.
  • The actual start transition clears the streak; the next deferral opens a fresh anchor.
  • The first deferral persists before its outcome is returned.
  • A repeated unchanged deferral performs no redundant whole-state rewrite.
  • Direct callers without a task-state service retain the existing process-local fallback.

Out of Scope

  • The health threshold and fairness work retained by #16561.
  • Any picker, lease, cadence, or task-taxonomy change.
  • Changing the persisted field shape delivered by #16903.

Avoided Traps

  • Testing only the helper again. The missing proof is the production composition.
  • Deleting the fallback to make the test easy. Direct callers intentionally retain it.
  • Making persistence asynchronous. The first durable write must still precede publication of the deferral outcome.
  • Optimizing before detecting equality. Only an unchanged, already-open anchor may skip the write.

Related

  • Durable field delivery: #16903
  • Parent starvation lane: #16561
  • Discovery review: PR #16900

Live latest-open sweep: checked the 20 newest open issues immediately before creation; no equivalent production-binding/write-churn ticket existed. Exact title/body sweep returned no match. A2A in-flight sweep: checked the latest 30 messages across read states; no competing claim exists.

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

Retrieval Hint: query_raw_memories("MaintenanceBackpressureService markDeferred production binding deferral streak 57/57 #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 referenced in commit 2665014 - "fix(orchestrator): bind durable deferral persistence (#16904) (#16980)" on Aug 11, 2026, 5:14 PM
tobiu closed this issue on Aug 11, 2026, 5:14 PM