Objective
Keep every MongoDB path used by interactive completed-action polling on an indexed identity under the embedded Task.sessionActions authority introduced by #29.
Review origin and current baseline
Current defect
MongoRepository.getSessionActionResult() first executes:
tasks.findOne({
sessionActions: {
$elemMatch: { id: actionId, state: 'completed' }
}
})
There is no Task index containing sessionActions.id, so the current authoritative lookup can scan the tasks collection. SessionService.waitForResult() repeats this lookup every 250 ms until a result or deadline is reached.
The compatibility path for already-completed legacy pending_actions also queries { id: actionId, state: 'completed' }, while the legacy documents use _id=actionId and current indexes only cover in-flight migration states. That fallback can scan the legacy collection as well.
The action_results compatibility path already queries the default indexed _id and must remain readable.
Required behavior
- Resolve current embedded
Task.sessionActions results through a justified indexed identity without changing Task/action authority.
- Resolve completed legacy
pending_actions by indexed _id, or add a bounded and justified equivalent index.
- Preserve existing
action_results._id compatibility.
- Keep result identity, immutable terminal completion, dispatch binding, one-active-action admission, and Memory/Mongo observable parity unchanged.
- Avoid creating a second authoritative result store merely to make polling convenient.
Acceptance criteria
- Add a Mongo query-plan contract for the current embedded Task result lookup and prove the winning plan does not contain
COLLSCAN.
- Add query-plan contracts for
action_results and completed legacy pending_actions; prove each compatibility lookup is indexed and bounded.
- Cover present, missing, wrong-state, and legacy completed results without changing the public response.
- Verify that polling by one action ID cannot return another Task's result and that terminal result immutability remains intact.
- Justify any new multikey index with its write/storage impact and ensure it supports the exact production predicate.
- Run build, typecheck, lint, worker bundle, fixture drift, full tests, standalone Mongo contracts, and all four PR checks.
- Do not use a timeout increase, longer polling interval, cache, or process-local map as the correctness fix.
Scope and dispatch gate
Objective
Keep every MongoDB path used by interactive completed-action polling on an indexed identity under the embedded
Task.sessionActionsauthority introduced by #29.Review origin and current baseline
qa-tools@2dd6dd27a2928f3d70fd5aad0b862117016a0aa4after PR github-devloop implementation for #42 #43/Roll up qa-tools-fkst-staging into qa-tools #44 delivered P0: Bind terminal action retries to immutable dispatch credentials #29.pending_actionsis stale. New action and terminal result authority now live atomically inTask.sessionActions.Current defect
MongoRepository.getSessionActionResult()first executes:There is no Task index containing
sessionActions.id, so the current authoritative lookup can scan thetaskscollection.SessionService.waitForResult()repeats this lookup every 250 ms until a result or deadline is reached.The compatibility path for already-completed legacy
pending_actionsalso queries{ id: actionId, state: 'completed' }, while the legacy documents use_id=actionIdand current indexes only cover in-flight migration states. That fallback can scan the legacy collection as well.The
action_resultscompatibility path already queries the default indexed_idand must remain readable.Required behavior
Task.sessionActionsresults through a justified indexed identity without changing Task/action authority.pending_actionsby indexed_id, or add a bounded and justified equivalent index.action_results._idcompatibility.Acceptance criteria
COLLSCAN.action_resultsand completed legacypending_actions; prove each compatibility lookup is indexed and bounded.Scope and dispatch gate
qa-toolsandqa-tools-fkst-stagingSHAs plus FKST package/manifest authority.qa-tools-fkst-staging, followed by a separate human-reviewed integration PR intoqa-tools; no direct merge, auto-merge, squash, rebase, force-push, or branch deletion.