symfonic.capabilities.memory.nap_stage¶
nap_stage ¶
The handler behind the nap stage: fire the due cycle, do not hold the answer.
Three things happen here that used to be spread across the engine, and each of them was a defect where it was:
- The trigger is the turn, not the agent.
_maybe_schedule_quick_napcounted turns on the agent, so two tenants served by one process shared a cadence and either consolidated on the other's turns. The coordinator counts per scope, and this stage tells it about the scope this turn actually ran for. - The nap runs after the flush. Legacy fired it once the turn was over;
this sits after
memory.lifecyclein the same phase, so the roster reads a store that already contains what the turn produced rather than one that is about to. - The task has an owner, and it is not the run.
asyncio.create_taskput the cycle in a process-wide set nobody waited for and nobody reported; the coordinator holds it and drains it at shutdown. Not the run's background registry, which was the other candidate: that bounds the cycle to the run's deadline and cancels it at that run's teardown, so a consumer hanging up would abandon maintenance for memories already published -- and because draining a run's events runs its teardown,Agent.runwould wait for the very nap the deployment asked to run in the background.
A nap never fails the turn. Every stage result here is applied or
no_change: the answer is delivered, the memories are already published, and
a maintenance pass that could not run is a line in a log, not a broken turn.
nap_handler ¶
Advance this scope's cadence, and run the cycle it makes due.