Found during review of #41. Design/hygiene follow-up.
Problem
ConversationRouter.route() is documented as a query ("Decide where an incoming message goes") but mutates TTL state.
src/mcpl/conversation-router.ts:157:
/** ... Pure read except for refreshing lastActivity ... */
route(facts) {
...
existing.lastActivity = now; // command hiding in a query
Impact
Command-query separation violation: you cannot preview a routing decision without resetting the idle clock. The test 'expired() respects lastActivity refresh from route()' is load-bearing on the side effect. A future diagnostic caller of route() would silently keep dying engagements alive.
Suggested fix
Split the side effect out — route() decides; an explicit touch(channelId) (or a refresh on the framework's delivery path) advances lastActivity.
Found during review of #41. Design/hygiene follow-up.
Problem
ConversationRouter.route()is documented as a query ("Decide where an incoming message goes") but mutates TTL state.src/mcpl/conversation-router.ts:157:Impact
Command-query separation violation: you cannot preview a routing decision without resetting the idle clock. The test
'expired() respects lastActivity refresh from route()'is load-bearing on the side effect. A future diagnostic caller ofroute()would silently keep dying engagements alive.Suggested fix
Split the side effect out —
route()decides; an explicittouch(channelId)(or a refresh on the framework's delivery path) advanceslastActivity.