[EPIC] feat: Context-Aware Part Subscription System - Server Infra #125
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context-Aware Part Subscription System — Server Infrastructure
Repository:
siloType: Feature
Depends on: None (foundation layer)
Summary
Implement server-side infrastructure for a subscription system where users subscribe to parts, project tags, or categories and receive automatic file updates driven by editing context transitions. When a user finishes editing a part (exits a Body, leaves assembly editing), the client pushes a checkpoint and releases its edit session lock. The server propagates updates to subscribers via SSE.
Critically, this system also implements edit session locking with interference detection. When a user enters an editing context, the client acquires a server-side lock. The server classifies the request against all active sessions as no-interference, soft interference, or hard interference — and the client enforces the result before allowing the user into the editing mode.
1. Module Registration
Register
subscriptionsas an optional module perMODULES.md:subscriptionstrueauth2. Database Schema
Migration:
workstationsMigration:
subscriptionsMigration:
sync_stateMigration:
checkpointsMigration:
edit_sessionsThe unique index on
(item_id, context_level, object_id)is the hard interference lock — only one session per object per context level. Soft interference is detected by overlappingdependency_conearrays across different objects.3. Edit Session Locking
3.1 Concept
Edit sessions are the server-side representation of a user's active editing context. They map directly to the
EditingContextResolverstates in Create:context_levelobject_idsketchSketch001partdesignBodyor specific featureassemblyA session is acquired when the user enters an editing context and released when they exit (upward context transition → checkpoint push).
3.2 Acquire Endpoint
Auth: editor
Request:
dependency_coneis optional. If the DAG has been synced for this item, the client can compute and send it. If omitted, the server computes it from the stored DAG (if available). If no DAG exists, interference detection falls back to context-level-only comparison.Server processing:
Check for existing sessions on the same item.
Classify interference for each active session:
Hard interference — same
item_id+ samecontext_level+ sameobject_idas an existing session. Response:409 Conflict.Soft interference — different
object_idbut overlappingdependency_cone. Response:200 OKwithinterference: "soft"and details of the conflicting session.No interference — no overlap at all. Response:
200 OKwithinterference: "none".If not hard-blocked: insert into
edit_sessions, broadcastedit.session_acquiredSSE event.Response (success):
Response (soft interference):
Response (hard interference):
3.3 Release Endpoint
Called explicitly on upward context transition (via checkpoint push), or implicitly by the heartbeat sweeper.
Server processing:
edit.session_releasedSSE event.3.4 Heartbeat
Edit sessions are kept alive by SSE connection heartbeat. The existing
/api/eventsSSE endpoint already maintains connection state. Extend it:workstation_id.edit_sessionsrow wherelast_heartbeat < now() - session_timeout_minutesis auto-released.UPDATE edit_sessions SET last_heartbeat = now() WHERE workstation_id = $1.This handles client crashes, network drops, and "walked away" scenarios without orphaned locks.
3.5 Query Active Sessions
Auth: viewer
Returns all active edit sessions for an item. Used by clients to show presence indicators before entering edit mode.
3.6 Handoff and Admin Override
Per MULTI_USER_EDITS.md §6.2–6.3:
Sends an SSE notification to the holder. They can release, defer, or negotiate.
Auth: admin. Force-releases with mandatory audit trail entry. The holder's in-progress work is preserved as a checkpoint.
3.7 SSE Events for Edit Sessions
edit.session_acquired{item_id, part_number, user, context_level, object_id, interference}edit.session_released{item_id, part_number, user, context_level, object_id}edit.interference_resolved{item_id, session_id, resolved_conflict}edit.handoff_requested{item_id, session_id, requester, message}edit.force_released{item_id, session_id, admin, reason}These are broadcast to all connections viewing the affected item.
4. Checkpoint System
4.1 Concept
A checkpoint is a lightweight working snapshot pushed on upward context transitions. It is distinct from a formal revision and serves as both the distribution mechanism for subscriptions and the session-close signal for edit locking.
Origin_Commitlatest_releasedsubsreleasedlatestsubsitems/{pn}/rev{N}.extitems/{pn}/checkpoints/{user_id}/{timestamp}4.2 Checkpoint Endpoint
Auth: editor
Request:
Server processing:
checkpointstable.dagpresent: upsert DAG, mark dirty nodes, emitdag.updated.session_idpresent: release that edit session (delete fromedit_sessions, emitedit.session_released).checkpoint_max_per_itemper user.context:sketch_close→ no subscription events (too granular, session may still be active at Body level)body_close,assembly_close,manual→ emit tolatestpolicy subscribersautosave→ store only, no eventsResponse:
The
session_idfield makes checkpoint + session release atomic. One round-trip from the client handles: file upload, DAG sync, edit lock release, and subscriber notification.4.3 Context-to-Action Mapping
sketch_closebody_closelatestsubsassembly_closelatestsubsmanuallatestsubsautosave4.4 Cleanup
4.5 Retrieval
GET/api/items/{pn}/checkpoint/latestGET/api/items/{pn}/checkpointsDELETE/api/items/{pn}/checkpoints/{id}5. Subscription Endpoints
Workstation Registration
POST/api/workstationsGET/api/workstationsDELETE/api/workstations/{id}Subscription CRUD
GET/api/subscriptions?workstation_id=POST/api/subscriptionsPUT/api/subscriptions/{id}DELETE/api/subscriptions/{id}Resolution
GET/api/subscriptions/{id}/itemsGET/api/subscriptions/resolvePolicy resolution:
latest_released→ newestreleasedrevision. Checkpoints ignored.latest→ newest of (latest revision, latest checkpoint).pinned→ exact revision. Checkpoints ignored.Sync
POST/api/sync/reportGET/api/sync/diffPOST/api/sync/acksync/diffresponse includesis_checkpoint,checkpoint_context, andcheckpoint_userwhen the resolved target is a checkpoint.6. SSE Events — Complete List
Edit Sessions
edit.session_acquirededit.session_releasededit.interference_resolvededit.handoff_requestededit.force_releasedSubscriptions
subscription.revisionsubscription.checkpointsubscription.updatesubscription.sync_requiredPer-Connection Filtering
SSE connections include
workstation_id. The broker:edit.*events to all connections viewing the affected item.subscription.*events only to connections whose workstation has matching subscriptions.event_debounce_ms) collapses bulk operations.7. Acceptance Criteria
Edit Sessions
POST /edit-sessionreturns200withinterference: "none"when no conflictsPOST /edit-sessionreturns200withinterference: "soft"and conflict details when dependency cones overlapPOST /edit-sessionreturns409when same item + context_level + object_id already heldDELETE /edit-sessionremoves session and broadcasts release SSEGET /edit-sessionsreturns all active sessions for an itemCheckpoints
POST /checkpointwithsession_idatomically stores checkpoint and releases sessionsketch_closedoes NOT emit subscription eventsbody_close/assembly_close/manualemit tolatestsubscribersautosavestores without eventsSubscriptions
latest_releasedignores checkpointslatestreturns checkpoint when newerpinnedreturns exact revisionsync/diffincludes checkpoint metadataSSE