47f3e4137e
Adds a router middleware that extracts the X-Session-Tag header from each request and decorates the request context via project_events.WithSessionTag. Service- and model-layer publishers then read it back via project_events.SessionTagFromContext and attach it to outgoing CardMoved / CardLinked / CardUnlinked events. The originating browser tab compares the incoming session_tag to its own and skips the echo, avoiding double-application of the optimistic local update. Other tabs see no tag match and apply the event normally. Wired into both the web router chain (before Contexter so the base context inherits the tag) and the API router chain (before APIContexter for the same reason).