Skip to content

feat(linear): agent-session fetch + append-only guards raw GraphQL (chat@4.31/#151 — L5/5)#173

Merged
patrick-chinchill merged 2 commits into
mainfrom
feat/4.31-linear-agent-sessions-l5-fetch
Jun 20, 2026
Merged

feat(linear): agent-session fetch + append-only guards raw GraphQL (chat@4.31/#151 — L5/5)#173
patrick-chinchill merged 2 commits into
mainfrom
feat/4.31-linear-agent-sessions-l5-fetch

Conversation

@patrick-chinchill

Copy link
Copy Markdown
Collaborator

Last of the 5-PR Wave D (#151). Builds on L1–L4 (all on main). Ports the Linear agent-session FETCH/read path from upstream adapter-linear/src/index.ts (chat@4.31.0).

The Python adapter has no @linear/sdk, so upstream's linear.agentSession(id) + linear.comments({filter}) calls (fetchAgentSessionMessages, index.ts:1771) are ported as raw GraphQL queries over the existing _graphql_query helper, schema-hardened field-by-field against Linear's published schema.graphql.

Scope (read path only — emit path + comment-path fetch left byte-identical)

  • fetch_messages: FIRST branch dispatches agent-session threads to the new _fetch_agent_session_messages; the comment_id / issue branches are unchanged below it.
  • _fetch_agent_session_messages (new): agentSession(id) query → issueId/root-comment raises → direction-driven children pagination → per-comment thread-id parse → next_cursor.
  • edit_message / delete_message: append-only guards raising the exact upstream AdapterError strings for session threads, before any network call.
  • fetch_thread: agentSessionId added to metadata.

CRITICAL schema-hardening

AgentSession has NO scalar issueId field in the published schema (it exposes only the issue: Issue relation alongside comment/sourceComment/id). Upstream's agentSession.issueId works because @linear/sdk's model derives it; in raw GraphQL, requesting a non-existent issueId would server-reject the whole query (the L4 blocking-bug class). So the issue id is read off issue { id } — equivalent to upstream's agentSession.issueId ?? thread.issueId (the same issueId ?? issue?.id fallback upstream uses at index.ts:959).

Other hazards preserved: nullish (issue.id ?? thread, endCursor ?? undefined) → is not None (NOT or); pagination forward → first / otherwise last; per-comment thread_id (linear:{issue}:c:{comment}:s:{session}, NOT a fixed shared id) via reused L4 author logic; is_mention=True.

Published-schema confirmation (per field)

agentSession(id: String!): AgentSession! ✓ · AgentSession.{id, comment, issue} ✓ (no issueId ✓) · comments(filter: CommentFilter, first/last/after): CommentConnection! ✓ · CommentFilter.parent → NullableCommentFilter.id → IDComparator.eq ✓ · Comment.{id,body,parentId,createdAt,updatedAt,url,user,botActor} ✓ · User.{id,displayName,name,email,avatarUrl} ✓ · ActorBot.{id,name,userDisplayName,avatarUrl} ✓ · PageInfo.{hasNextPage,endCursor} ✓.

Tests — tests/test_linear_agent_session_fetch.py (20)

Happy path (root+children → per-comment thread-ids, author resolution), issueId fallback + missing-issueId/missing-root-comment raises, forward-vs-backward (first/last) pagination, next_cursor by hasNextPage, append-only edit/delete raises, and comment-path-unchanged regressions. Each fails on a plausible mutation (forward/backward swap, per-comment-id collapse, cursor-logic flip, dropped guard, dropped issueId fallback) — all six verified.

Gauntlet

ruff check ✓ · ruff format ✓ · audit_test_quality (0 hard failures) ✓ · pyrefly (0 src + 0 test) ✓ · verify_test_fidelity --strict (732/732, 100%) ✓ · pytest (5242 passed, 4 skipped) ✓.

Live-tenant verification pending (documented in UPSTREAM_SYNC.md): query/field names confirmed against the published schema but not yet exercised against a live Linear agent-session tenant.

Wave D (#151) is now complete.

…hat@4.31/#151 — L5/5)

Port the Linear agent-session FETCH/read path (last of the 5-PR Wave D,
#151), building on L1–L4 already on main. The Python adapter has no
@linear/sdk, so upstream's linear.agentSession(id) + linear.comments({filter})
calls (fetchAgentSessionMessages, index.ts:1771) are ported as raw GraphQL
queries over the existing _graphql_query helper, schema-hardened field-by-field
against Linear's published schema.graphql.

Surface (strictly the read path; the L4 emit methods and the comment-path
fetch are left byte-identical):
- fetch_messages: FIRST branch dispatches agent-session threads to the new
  _fetch_agent_session_messages; comment/issue branches unchanged.
- _fetch_agent_session_messages: agentSession(id) query (selecting issue { id }
  + the root comment relation), issueId/root-comment raises, direction-driven
  children pagination (forward->first, otherwise last), per-comment thread_id
  (linear:{issue}:c:{comment}:s:{session}) via reused L4 author logic, and
  next_cursor = endCursor if hasNextPage else None.
- edit_message / delete_message: append-only guards raising the exact upstream
  AdapterError strings for session threads before any network call.
- fetch_thread: agentSessionId added to metadata.

CRITICAL schema-hardening: AgentSession has NO scalar issueId field in the
published schema (only the issue: Issue relation), so the issue id is read off
issue { id } — equivalent to upstream's agentSession.issueId ?? thread.issueId.
Requesting a non-existent issueId would server-reject the whole query (the L4
blocking-bug class). Nullish (issue.id ?? thread, endCursor ?? undefined) uses
is not None, not or.

Tests: tests/test_linear_agent_session_fetch.py (20 tests) — happy path,
issueId fallback + missing-issueId/missing-root-comment raises, forward-vs-
backward (first/last) pagination, next_cursor by hasNextPage, append-only
edit/delete raises, and comment-path-unchanged regressions. Each fails on a
plausible mutation (forward/backward swap, per-comment-id collapse, cursor-
logic flip, dropped guard, dropped issueId fallback) — all six verified.

Doc: UPSTREAM_SYNC L5 divergence row + Wave D marked complete.

Wave D (#151) is now complete.
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6b6ab2be-e01d-4b8a-b7c7-a9ff23bb41a8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request completes the Linear agent-session support (Wave L5) by implementing the agent-session fetch path. Specifically, it introduces raw GraphQL queries to fetch agent sessions and their child comments, adds append-only guards to edit_message and delete_message to prevent modification of session activities, and implements _fetch_agent_session_messages to handle pagination and parsing of comments into messages. Additionally, it updates the fetch_thread metadata to include agentSessionId and adds a comprehensive test suite in tests/test_linear_agent_session_fetch.py. As there are no review comments, I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

…lish on agent-session fetch (L5)

- Drop the inbound `after: options.cursor` forwarding from
  `_fetch_agent_session_messages` and the `$after: String` param from
  `_AGENT_SESSION_CHILDREN_QUERY`. Upstream `fetchAgentSessionMessages`
  (index.ts:1793-1804) passes ONLY `first`/`last` and never reads
  `options.cursor`; the sibling `_fetch_issue_comments`/`_fetch_comment_thread`
  paths also forward no cursor. `next_cursor` is still returned off
  `pageInfo.endCursor`. Removes the undocumented divergence; updates the L5
  UPSTREAM_SYNC.md row accordingly.
- Fix the misleading null-session guard message: when the raw-GraphQL
  `agentSession(id)` resolves to null (port-only branch — the SDK throws its
  own not-found upstream), raise "... not found" instead of "... is missing
  issueId". The separate downstream missing-issueId raise is unchanged.
- Add tests pinning: comment-session thread id dispatches to the
  agent-session fetch (branch-swap mutation); empty-string session issue.id is
  kept per nullish (`??`/`is not None`) not truthiness (`or`); null-session
  not-found message; `endCursor` only returned when both hasNextPage and
  endCursor present. Each new test fails on the corresponding mutation
  (verified by injection).
@patrick-chinchill patrick-chinchill marked this pull request as ready for review June 20, 2026 08:16
@patrick-chinchill patrick-chinchill merged commit c2eac43 into main Jun 20, 2026
8 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 754a58b7f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1498 to +1502
if decoded.agent_session_id:
raise AdapterError(
"Linear agent session activities are append-only and cannot be edited",
"linear",
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Move the session edit guard before token refresh

When client-credentials auth is configured and the cached token is expired, this branch is reached only after _ensure_valid_token() refreshes the token. If that refresh fails or the token endpoint is unavailable, edit_message() on an agent-session thread raises an auth/network error and performs an unnecessary network request instead of the append-only AdapterError; delete_message() already decodes and guards before auth. Move the decode/session check ahead of token validation for the session case.

Useful? React with 👍 / 👎.

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