Skip to content

feat(session): thread correlationId onto SessionSummary#405

Open
ljagiello wants to merge 1 commit into
mainfrom
feat/session-correlation-id
Open

feat(session): thread correlationId onto SessionSummary#405
ljagiello wants to merge 1 commit into
mainfrom
feat/session-correlation-id

Conversation

@ljagiello
Copy link
Copy Markdown
Contributor

📖 Six-word memoir

Receive correlation id. Now use it.

Summary

Closes the gap PR #402's `run-job-dialog` had to work around: nowait gives the caller a `correlationId` in the 202, but there was no API that accepted it as input — so the dialog snapshotted sessions pre-POST and picked "newest non-snapshot for this jobName" post-POST, which races when two tabs (or cron + a manual click) fire the same job at the same time.

This makes correlationId a first-class attribute of a session, end to end:

  • `SessionSummarySchema` / `SessionViewSchema` get an optional `correlationId` field (`@atlas/core`)
  • `SessionStartEventSchema` gets the same field, and the reducer surfaces it on the live `SessionView` so active sessions (whose summary isn't persisted yet) are also discoverable
  • Runtime threads correlationId from `TriggerSignalOpts` → `WorkspaceRuntimeSignal` → emitted `session:start` event → conditionally-spread `summaryV2` at finalization
  • atlasd cascade consumer passes `envelope.correlationId` into `triggerWorkspaceSignal` (mirroring how `parentSessionId` is already plumbed). The envelope already carries correlationId from the JetStream publish path; this PR just makes the consumer forward it instead of dropping it.
  • `GET /api/sessions` accepts a new optional `?correlationId=…` query that filters the merged active+completed list

`run-job-dialog` is rewritten to use it: read `correlationId` from the 202 body, poll `/api/sessions?workspaceId=…&correlationId=…` with that filter, navigate when a match appears. Snapshot-then-diff is gone (~50 lines deleted). Deterministic match means two tabs firing the same job can no longer mis-attribute each other's runs — each tab's trigger gets its own server-generated correlationId.

Relationship to #402

Same dialog file. This PR's dialog supersedes #402's snapshot-diff approach. Suggested merge order: close #402 in favor of this, OR merge #402 first and then this PR's rebase deletes the snapshot code added by #402. Either works.

Test plan

Verified end-to-end via curl against `deno task dev:playground`:

```
$ curl -sk -X POST https://localhost:8080/api/workspaces/braised_oregano/signals/count-and-sleep?nowait=true \
-H 'Content-Type: application/json' -d '{"payload":{}}'
{"message":"Signal accepted","status":"accepted","workspaceId":"braised_oregano","signalId":"count-and-sleep","correlationId":"738f65c0-b819-47a8-9238-049d74aeb0d4"}

$ curl -sk https://localhost:8080/api/sessions?correlationId=738f65c0-b819-47a8-9238-049d74aeb0d4
{"sessions":[{"sessionId":"0d151072-…","workspaceId":"braised_oregano","jobName":"count-and-sleep","status":"active","correlationId":"738f65c0-b819-47a8-9238-049d74aeb0d4",…}]}

$ curl -sk https://localhost:8080/api/sessions?correlationId=nonexistent
{"sessions":[]}
```

Closes the gap PR #402's `run-job-dialog` had to work around: nowait
gives the caller a `correlationId` in the 202, but there was no API
that accepted it as input — so the dialog snapshotted sessions
pre-POST and picked "newest non-snapshot for this jobName" post-POST,
which races when two tabs (or cron + a manual click) fire the same
job at the same time.

Make correlationId a first-class attribute of a session, end-to-end:

1. `SessionSummarySchema` / `SessionViewSchema` get an optional
   `correlationId` field (@atlas/core).
2. `SessionStartEventSchema` gets the same field, and the reducer
   surfaces it on the live `SessionView` so active sessions (whose
   summary hasn't been persisted yet) are also discoverable.
3. The runtime threads correlationId from `TriggerSignalOpts` →
   `WorkspaceRuntimeSignal` → emitted `session:start` event →
   conditionally-spread `summaryV2` at finalization.
4. atlasd's cascade consumer passes `envelope.correlationId` into
   `triggerWorkspaceSignal` (mirroring how `parentSessionId` is
   already plumbed). The envelope already carries correlationId from
   the JetStream publish path; this PR just makes the consumer
   forward it instead of dropping it.
5. `GET /api/sessions` accepts a new optional `?correlationId=…`
   query that filters the merged active+completed list.

Run-job-dialog is rewritten to use the new filter: read
`correlationId` from the 202 body, poll `/api/sessions` with that
filter, navigate when a match appears. Snapshot-then-diff is gone
(~50 lines deleted). Deterministic match means two tabs firing the
same job can no longer mis-attribute each other's runs — each tab's
trigger gets its own server-generated correlationId.

Verified end-to-end via curl against `dev:playground`:
- POST `?nowait=true` → 202 with `correlationId`
- GET `/api/sessions?correlationId=<that>` → returns the spawned
  session with `correlationId` populated and `status: "active"`
- Filter returns `{sessions: []}` for unknown correlationIds

Typecheck: 0 errors (33 pre-existing warnings unchanged).

Overlaps with PR #402 — same dialog file. This PR's dialog version
supersedes #402's snapshot-diff approach; #402 should be closed or
merge-then-rebased once this lands.
@ljagiello ljagiello requested a review from Vpr99 as a code owner May 20, 2026 04:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant