Implements the explicit-project-identity directive at the route layer: a
request's store is resolved from request projectId -> the daemon's registered
launch project id -> only for unregistered launch directories, the raw
launch-dir store (one-time warn). Resolution funnels through a single seam
(routes/context.ts resolveRequestProjectId + resolveStoreForProjectId); the
server.ts realtime resolveScopedStore delegates to the same function instead
of mirroring it. Scattered 'projectId ? getOrCreateProjectStore : store'
ternaries in todo/goals/mission/insights/research/evals routes now use the
shared seam.
Code-review fixes folded in (multi-agent ce-code-review, 10 reviewers):
- mission interview drafts list/discard resolve the same project id the
start endpoint stamps (write/read no longer split namespaces)
- chat stream-attach guard treats legacy null-projectId sessions as
launch-owned instead of 404ing; planner-chat dedup retries unscoped to
reuse legacy sessions instead of duplicating them
- getProjectIdFromRequest trims and rejects whitespace-only ids
- evals/research middleware forwards store-resolution failures to Express
(previously rethrew inside a detached promise chain and hung the request)
- one-time launch-dir fallback warning routes through runtimeLogger
- seam + delegation + whitespace + engine-fallthrough covered in
routes-context-project-identity.test.ts (10 cases)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>