Context
#13755 tracks orchestrator heavy-maintenance regression hardening after the reboot-stability push. During exact-head review of #13764, a separate accounting gap showed up in the Dream typed-outcome path: a no-op skipped result is currently recorded through the same task-state success path as a completed run.
This matters because the Agent OS scheduler now relies on durable task-state freshness for heavy-maintenance fairness. A skipped/deferred run should be observable as a skip, not as a successful maintenance completion.
Problem
ai/daemons/orchestrator/scheduling/pipeline.mjs:476-486 handles outcome.status === "skipped" by calling services.taskStateService.markCompleted(taskName).
ai/daemons/orchestrator/services/TaskStateService.mjs:190-196 documents and implements markCompleted() as a successful completion and writes lastSuccessAt.
The same service already has markSkipped() at ai/daemons/orchestrator/services/TaskStateService.mjs:211-221, documented as marking a task skipped "without treating the no-op as a successful run". The scheduler is not using that contract for typed Dream skips.
Net effect: a deferred/no-op Dream cycle can look like a successful run in durable task state, which can mask stale work and pollute the freshness signals used by the heavy-maintenance scheduler.
Architectural Reality
pipeline.mjs already distinguishes typed outcomes: completed, skipped, and failed. The bug is not lack of typed outcome support; it is the task-state write chosen for skipped.
TaskStateService already exposes the intended non-success skip primitive. This is a wiring/accounting fix, not a new state-model invention.
- ADR 0022 defines the heavy-maintenance fairness model around freshness/staleness signals, including
(now - lastRunAt) / cadenceMs ranking for lease-competing maintenance tasks. Skipped work must not be collapsed into success accounting.
Fix Direction
Route typed skipped outcomes through a non-success task-state path, preferably the existing TaskStateService.markSkipped(taskName), while preserving the current health outcome record of skipped plus skipReason.
If implementation proves lastRunAt start-time semantics also need adjustment for this exact path, document that separately in the PR body and keep the leaf bounded to typed skip accounting. Do not redesign the whole scheduler freshness model in this ticket.
Contract Ledger Matrix
| Contract |
Current behavior |
Required behavior |
Typed completed outcome |
markCompleted() + health completed |
unchanged |
Typed skipped outcome |
markCompleted() + health skipped |
non-success task-state write + health skipped |
lastSuccessAt |
updated for skipped outcomes via markCompleted() |
must not be updated by skipped outcomes |
skipReason health payload |
recorded |
preserved |
Acceptance Criteria
runDreamTask() / scheduler typed skipped handling no longer calls the successful-completion task-state path.
- A
skipped outcome does not update lastSuccessAt.
- Health-service reporting still records
skipped and preserves skipReason.
- Focused unit coverage proves completed, skipped, and failed outcomes keep distinct task-state semantics.
- PR body states whether
lastRunAt start-time semantics were inspected and whether they remain intentionally out of scope.
Out of Scope
- Replacing the ADR 0022 fairness model.
- Changing lease acquisition, lease watchdog, inherited-token handling, or wrapper crash safety. Those are covered by sibling lanes under
#13755.
- Broad task-state schema migration unless the focused test demonstrates it is necessary for this typed-outcome bug.
Avoided Traps
- Do not treat health
skipped as sufficient while task-state still says success. Both surfaces are consumed differently.
- Do not paper over this with warning text only; the durable accounting must stop marking skips successful.
- Do not close or supersede
#13755; this is one leaf under that stability parent.
Evidence / Duplicate Sweep
- Live code anchor checked on 2026-06-21:
pipeline.mjs:481-486 routes skipped through markCompleted().
- Live code anchor checked on 2026-06-21:
TaskStateService.mjs:211-221 already provides markSkipped().
- Live open-issue sweep checked on 2026-06-21 for
#13755, deferred lastRunAt, lastRunAt skipped, markSkipped dream, and runDreamTask skipped; no equivalent open leaf found.
- A2A recent-message sweep checked on 2026-06-21; no competing recent claim for this deferred-run accounting lane found.
- Memory Core semantic prior-art sweep attempted, but query tools are currently degraded: embedding write canary timed out after 5000ms.
Related
- Parent stability epic:
#13755
- Orchestrator stability root:
#13624
- Serialization/revert companion:
#13762
- Lease-status companion:
#13764
- Hung-lease watchdog companion:
#13766
- Authority:
learn/agentos/decisions/0022-heavy-maintenance-scheduling-fairness.md
Handoff Retrieval Hints
Search anchors: runDreamTask skipped markCompleted, TaskStateService markSkipped, lastSuccessAt skipped outcome, ADR 0022 staleness-ratio.
Context
#13755tracks orchestrator heavy-maintenance regression hardening after the reboot-stability push. During exact-head review of#13764, a separate accounting gap showed up in the Dream typed-outcome path: a no-opskippedresult is currently recorded through the same task-state success path as a completed run.This matters because the Agent OS scheduler now relies on durable task-state freshness for heavy-maintenance fairness. A skipped/deferred run should be observable as a skip, not as a successful maintenance completion.
Problem
ai/daemons/orchestrator/scheduling/pipeline.mjs:476-486handlesoutcome.status === "skipped"by callingservices.taskStateService.markCompleted(taskName).ai/daemons/orchestrator/services/TaskStateService.mjs:190-196documents and implementsmarkCompleted()as a successful completion and writeslastSuccessAt.The same service already has
markSkipped()atai/daemons/orchestrator/services/TaskStateService.mjs:211-221, documented as marking a task skipped "without treating the no-op as a successful run". The scheduler is not using that contract for typed Dream skips.Net effect: a deferred/no-op Dream cycle can look like a successful run in durable task state, which can mask stale work and pollute the freshness signals used by the heavy-maintenance scheduler.
Architectural Reality
pipeline.mjsalready distinguishes typed outcomes:completed,skipped, andfailed. The bug is not lack of typed outcome support; it is the task-state write chosen forskipped.TaskStateServicealready exposes the intended non-success skip primitive. This is a wiring/accounting fix, not a new state-model invention.(now - lastRunAt) / cadenceMsranking for lease-competing maintenance tasks. Skipped work must not be collapsed into success accounting.Fix Direction
Route typed
skippedoutcomes through a non-success task-state path, preferably the existingTaskStateService.markSkipped(taskName), while preserving the current health outcome record ofskippedplusskipReason.If implementation proves
lastRunAtstart-time semantics also need adjustment for this exact path, document that separately in the PR body and keep the leaf bounded to typed skip accounting. Do not redesign the whole scheduler freshness model in this ticket.Contract Ledger Matrix
completedoutcomemarkCompleted()+ healthcompletedskippedoutcomemarkCompleted()+ healthskippedskippedlastSuccessAtmarkCompleted()skipReasonhealth payloadAcceptance Criteria
runDreamTask()/ scheduler typedskippedhandling no longer calls the successful-completion task-state path.skippedoutcome does not updatelastSuccessAt.skippedand preservesskipReason.lastRunAtstart-time semantics were inspected and whether they remain intentionally out of scope.Out of Scope
#13755.Avoided Traps
skippedas sufficient while task-state still says success. Both surfaces are consumed differently.#13755; this is one leaf under that stability parent.Evidence / Duplicate Sweep
pipeline.mjs:481-486routesskippedthroughmarkCompleted().TaskStateService.mjs:211-221already providesmarkSkipped().#13755,deferred lastRunAt,lastRunAt skipped,markSkipped dream, andrunDreamTask skipped; no equivalent open leaf found.Related
#13755#13624#13762#13764#13766learn/agentos/decisions/0022-heavy-maintenance-scheduling-fairness.mdHandoff Retrieval Hints
Search anchors:
runDreamTask skipped markCompleted,TaskStateService markSkipped,lastSuccessAt skipped outcome,ADR 0022 staleness-ratio.