Skip to content

Follow up Linear triggers with Agent Sessions, team routing, and replies - #88

Open
michaelmwu wants to merge 27 commits into
getpaseo:mainfrom
voytravel:agent/linear-v2
Open

Follow up Linear triggers with Agent Sessions, team routing, and replies#88
michaelmwu wants to merge 27 commits into
getpaseo:mainfrom
voytravel:agent/linear-v2

Conversation

@michaelmwu

@michaelmwu michaelmwu commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

This is the single follow-up to the Linear trigger implementation merged in #84. It supersedes closed #59 and packages the remaining Linear capabilities together. It also incorporates the production-tested Agent Session and delivery contribution from @as-cto in voytravel#4 while preserving commit authorship.

Included

  • Native Linear Agent Sessions: signed created/prompted intake, runtime routing, bounded causal history, and explicitly opt-in OAuth scopes.
  • Complete session lifecycle behavior: accepted/failure/closing activities, question elicitation with choices, stop handling, completion after required replies, and deduplication when one delegation emits both session and comment events.
  • Project and team scope routing, with team IDs serving as deterministic route keys for projectless issues.
  • Thread-aware comments: reply-only and app-thread filters, paginated Agent Session classification, replies anchored to the Linear thread root, and actionable provider delivery errors.
  • Delivery and persistence safeguards, including required-output failure propagation, serialized rotating-token refresh/rebind behavior, durable ordering between session stops/delegations and delayed trigger creation, and one confirmation across concurrent Stop retries.

Scope

This branch contains only the unmerged Linear follow-up work and remains cleanly mergeable with current main. It does not duplicate the initial Linear project-trigger implementation that landed in #84.

Validation

Current head 7b925fe:

  • Focused Linear/lifecycle/configuration/Postgres acceptance tests: 157 passed.
  • Linear connection-action tests: 2 passed.
  • npm run typecheck
  • npm run lint
  • npm run format:check
  • npm run db:check
  • npm run build

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T21:54:44.482062Z af3028c New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: d6439afaed

ℹ️ 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 thread src/projects/panels.tsx Outdated
@michaelmwu michaelmwu closed this Aug 30, 2026
@michaelmwu
michaelmwu deleted the agent/linear-v2 branch August 30, 2026 09:34
@michaelmwu michaelmwu changed the title Add Linear V2 Agent Sessions, team routing, and replies Follow up Linear triggers with Agent Sessions, team routing, and replies Aug 30, 2026
@michaelmwu
michaelmwu restored the agent/linear-v2 branch August 30, 2026 09:39
@michaelmwu michaelmwu reopened this Aug 30, 2026
@michaelmwu michaelmwu closed this Aug 30, 2026
@michaelmwu
michaelmwu deleted the agent/linear-v2 branch August 30, 2026 09:40
@michaelmwu
michaelmwu restored the agent/linear-v2 branch August 30, 2026 09:41
@michaelmwu michaelmwu reopened this Aug 30, 2026
@michaelmwu

Copy link
Copy Markdown
Contributor Author

@as-cto Could you review #88 when you have time? This is the single ready-for-review Linear follow-up after #59, and it includes the bounded causal history, corrected Linear cursor types, projectless team routing, reply-parent filtering, and connection-isolation regression coverage from your feedback. The focused regression set is green (60 tests). Thank you again for validating the earlier work against a real Linear workspace.

@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: 31428bcb33

ℹ️ 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 thread src/triggers/linear/webhook.ts
@as-cto

as-cto commented Sep 1, 2026

Copy link
Copy Markdown

Reviewing #88 from a self-hosted deployment — thanks for the ping. filters.team routing and the DateTimeOrDuration history fix both work cleanly for us, and the compact-team-event hydration on the latest push closes the gap we'd have hit on projectless issues. We can drop our local equivalents.

Two things we'd like to align on, both about what a user actually sees in the session panel:

1. Live progress in the session. Today a session posts a single accept thought (onDispatchAccepted), then the final response, with an error on failure/termination — nothing in between. From the panel side it reads as "Working…" until the whole answer lands. The Linear layer here looks complete; the gap is upstream of it — the hub only receives coarse lifecycle events from the daemon (accepted / failed / terminated / final output), so there's no live agent output to forward as incremental thought/action activities. Is surfacing daemon-side progress (even coarse step markers) something you'd consider in scope, or is that squarely a daemon/protocol concern to track separately? For anything that runs more than a few seconds, the session currently reads as a black box.

2. Deduplicating a single delegation. A delegation emits both an agent_session event and a comment_created for us, so wiring both triggers fires two agents for one tag. We've worked around it by routing Linear solely through agent sessions and dropping the comment trigger, which is clean — but it means issue-thread replies (not typed into the session panel) only continue the session if Linear delivers them as prompted. Does #88 emit prompted for issue-thread replies on an issue with an active session, or only for messages sent through the session UI? Knowing that determines whether a comment trigger is still needed at all.

Happy to test any of this against our deployment.

@as-cto

as-cto commented Sep 1, 2026

Copy link
Copy Markdown

Follow-up from our self-hosted deployment, this time a concrete bug report on the boot migration in this branch (head 9639bb6), reproduced on a staging copy of our database.

What happens. On first boot, migrated project configurations to organization triggers archives the existing projects and creates one synthetic trigger-<id> project per trigger, with a fresh revision each. Those migrated revisions carry the daemon environment by slug only:

env0 = {"kind":"daemon","name":"target","daemon":"cs8-senspace-vps","cwd":"…"}   ← no daemonId

whereas revisions produced by configurations/install on the same build contain daemonId (resolved at compile time). Since toProjectConfiguration (src/configuration/store.ts) throws active configuration contains an uncompiled daemon reference when daemonId is missing, every event match fails with a 500 after the migration — webhooks and POST /api/v1/manual-runs alike (collectProviderMatches → match → getRevision → parseProjectConfiguration). The container stays healthy, so nothing surfaces unless you dispatch something.

Second effect. Manual runs addressed by the original project slug resolve to the archived project and throw manual project configuration unavailable; they only work when addressed to the synthetic trigger-<id> slug, which nothing tells the operator.

Workaround we use. Right after switching images, re-install every bundle by name: (that path restores archived projects and compiles daemonId), then prove dispatch with a manual check run. Rolling back to the previous image also works once the synthetic projects are archived (status='archived', archived_at, null revision — the projects_archive_shape_check constraint requires all three).

Suggested fix. Have the migration resolve daemon slugs to daemonId the same way the compiler does (or make parseProjectConfiguration tolerate a missing daemonId by resolving the slug at read time, failing only if the daemon is unknown). A boot-time self-check that dispatches nothing but at least parses every active revision and logs unparseable ones would have made this visible immediately.

Happy to test a patched build against our staging copy.

@as-cto

as-cto commented Sep 1, 2026

Copy link
Copy Markdown

Follow-up on the migration report above, after recovering a production hub running this branch.

Re-installing the archived projects by name: (which recompiles daemonId) brings event matching back, but every manual run kept returning 500 (manual project configuration unavailable) even with healthy, active projects. Cause: resolveManualRunProject (src/public-operations/database-adapter.ts) prefers an organization_triggers row of the same name over the project slug, and dispatches to its runtimeProjectId — the synthetic trigger-* project, which is archived after recovery. persistManual then finds no active project and throws. The migration leftovers therefore keep breaking manual runs after the projects themselves are fixed (event matching only reads project_trigger_routes, so it is unaffected).

What worked, verified on a copy of the database and then in production: delete from organization_triggers (revisions and routes cascade) while keeping project_trigger_migrations — without those markers the boot migration runs again on next start and re-archives the projects.

Two small suggestions: resolveManualRunProject could skip organization triggers whose runtime project is not active, and the boot migration could skip (or roll back) projects whose migrated revisions cannot be compiled (missing daemonId), instead of leaving the organization with routes that 500 on every match.

@michaelmwu

Copy link
Copy Markdown
Contributor Author

@as-cto Confirmed both reproductions and fixed them in c96e134.

  • The startup migration now resolves each synthetic runtime configuration through the organization compiler, so daemon environments persist with daemonId. If a project has an unresolved daemon target, that project remains active and no broken organization trigger is written.
  • Manual-run resolution now ignores a same-named organization trigger when its runtime project is no longer active, so it falls back to the active project slug after recovery.

Added migration, embedded persistence, and manual-resolution regressions. Focused migration/manual/configuration tests (26), typecheck, lint, format check, and production build pass locally. Thank you for the staging reproduction.

@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: c96e134a3d

ℹ️ 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 thread src/triggers/linear/webhook.ts Outdated

@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: 7aece66b4c

ℹ️ 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 thread src/config/compiler.ts
@as-cto

as-cto commented Sep 2, 2026

Copy link
Copy Markdown

One more finding from production, on issue-comment replies (not agent sessions).

Linear threads are one level deep: commentCreate with a parentId that is itself a reply is rejected with Parent comment must be a top level comment. When creating a reply the parentId should be set to the same value as the comment you're replying to. A hub build that replied with parentId = triggering comment id therefore failed delivery for every reply to a nested comment (12 of 12 failed deliveries over four days matched this; all 24 successes targeted a root comment or a session). This branch avoids the error by posting comment replies without parentId, but that drops them out of the thread.

What we run now: the reply executor passes parentId = comment.parentId ?? comment.id (thread root), and the hub.reply tool error includes the provider message ("Provider said: …") so the agent knows why a delivery failed instead of retrying blindly. Also worth noting for the required-output logic: an execution whose linear.reply attempts all failed still ended as succeeded once the agent called finish_execution; we now end it as failed with output_delivery_failed.

Happy to open PRs for any of these if useful.

@as-cto

as-cto commented Sep 2, 2026

Copy link
Copy Markdown

Thanks for c96e134 — we merged the current head (7aece66) on top of our production patches with no conflicts, and the full suite is green on that combination (1239 tests, typecheck, lint, format). We have not yet booted that build against a copy of our recovered database, so the migration path itself is still verified only by your regressions on our side; we will report if the staging boot says otherwise.

As offered, the fixes we described above (thread-root replies, provider error forwarded to the agent, output_delivery_failed) plus the session lifecycle changes we run in production are now a single PR against this branch: voytravel#4. Summary of what it adds, beyond what is already in this thread:

  • the idle deadline only completes an execution once its required outputs were emitted, and completes right away once the reply is posted;
  • a workflow that ends without hub.reply gets an explicit closing response instead of leaving the session open;
  • hub.reply with kind: "question" and options becomes an elicitation activity with a select signal (session in awaitingInput);
  • the stop signal stops running and undispatched executions instead of opening a run;
  • thread_with_app: true comment filter, and a comment run superseded when the same comment also opens a session (commentId or sourceCommentId) — this is the deduplication from question 2 above.

It is one squashed commit with the per-topic history linked in the PR; glad to split it any way that suits the review.

@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: 4b408e017d

ℹ️ 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 thread src/providers/linear/client.ts Outdated
Comment thread src/daemons/lifecycle.ts Outdated

@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: b40974bb91

ℹ️ 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 thread src/daemons/lifecycle.ts
Comment thread src/triggers/linear/provider.ts Outdated
Comment thread src/triggers/linear/provider.ts Outdated
@michaelmwu

Copy link
Copy Markdown
Contributor Author

@as-cto Integrated voytravel#4 as fe71b2d with your authorship preserved. I also fixed the three current-head lifecycle findings in 3f84488: late idle completion now reports success, Stop survives trigger-revision changes, and multi-route stops emit one receipt-owned confirmation while stopping work in every routed project. The full combined suite passes (1,245 tests; 15 environment-gated skips), as do typecheck, lint, format, database drift, and build. Thank you for the production repros and contribution.

@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: 3f8448819b

ℹ️ 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 thread src/db/trigger-acceptance.ts
@michaelmwu

Copy link
Copy Markdown
Contributor Author

@as-cto Closing the loop on your earlier session-panel questions: your voytravel#4 contribution is now integrated with authorship preserved, including session/comment delegation dedup and continuation behavior. I also confirmed that live incremental progress is a separate daemon/protocol concern: Hub currently receives lifecycle boundaries and final output, not truthful intermediate agent output to forward. I am deliberately keeping synthetic progress machinery out of this Linear follow-up rather than expanding an already broad PR.

michaelmwu and others added 18 commits September 6, 2026 05:14
…tions, honor stop; reply under thread roots and fail runs on lost deliveries

Squashed from the per-topic branches we run in production on a self-hosted hub
(image lineage linear-v5/v6, built on this PR's head).

Session lifecycle
- idle deadline: an execution is only completed at its idle deadline once its
  required outputs (linear.reply, required: true) were emitted; a session that
  already replied is completed, not left open.
- explicit close: when a workflow ends without hub.reply, the hub posts a short
  closing response itself instead of leaving the session open silently.
- questions: hub.reply accepts kind: "question" with options, delivered as a
  Linear elicitation activity with a select signal; the session goes to
  awaitingInput instead of complete. Choices are deduplicated.
- stop: the Linear stop signal no longer opens a run; it stops running and
  undispatched executions (stopped_by_user / agent_session_stopped).

Issue comments
- replies are posted under the thread root (comment.parentId ?? comment.id):
  Linear only accepts one level of nesting and rejects a parentId that is
  itself a reply ("Parent comment must be a top level comment").
- the provider error is forwarded to the agent in the hub.reply tool error
  ("Provider said: ...") instead of being swallowed.
- new comment filter thread_with_app: true keeps a reply only when the app
  already posted in that thread; complements replies_only.
- a comment run is superseded (superseded_by_agent_session) when the same
  comment also opens an agent session, by commentId or sourceCommentId, so a
  single delegation no longer fires two agents.

Executions
- a required output whose delivery attempts all failed ends the execution,
  step and run as failed (output_delivery_failed) at finish_execution instead
  of succeeded; on a session a short error activity is posted.
`PaseoIssueCommentHistory` declared `$issueId: String!` where the issue
filter expects an `ID`. Linear rejected every request, so comment runs
never received their thread history.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…supersede on prompted

Linear materializes an agent session as a comment thread: the root is a
bot comment, the user's prompts are their comments, and the app's
responses are comments authored by the app user. After the first
response every session thread therefore satisfied `thread_with_app`, so
a reply there started both an agent-session run (prompted) and a
comment run, and the user was answered twice.

- `readCommentThread` now also returns the root comment ids of the
  issue's agent sessions, read in the same request through the comment's
  issue. The normalized comment event carries `threadIsAgentSession`,
  which `thread_with_app` treats as disqualifying; `replies_only` alone
  is unaffected. The provider logs the skipped comment at debug level.
- A prompted agent session carries the comment behind the prompt as
  `agentSession.sourceCommentId` (read from the activity's
  `sourceCommentId`, nested `sourceComment`, or content). The provider
  supersedes the comment run started from that comment, as it already
  did for the comment that created a session. A prompt without a comment
  supersedes nothing.

The webhook order observed in production is what justifies superseding
on prompted too: for a reply in a session thread, Linear emits the
comment webhook first and the prompted agent-session webhook about
100 ms later, so a comment run may already exist when the prompt is
matched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@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: 7b925fe4e7

ℹ️ 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 thread src/db/trigger-acceptance.ts
Comment thread src/triggers/linear/provider.ts Outdated

@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: cd0f013466

ℹ️ 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 thread src/db/trigger-acceptance.ts Outdated

@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: fd30d0a2be

ℹ️ 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 thread src/providers/linear/index.ts

@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: 602c92c338

ℹ️ 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 thread src/triggers/linear/match.ts
Comment thread src/daemons/lifecycle.ts

@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: af3028c81b

ℹ️ 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 thread src/connections/linear-actions.ts Outdated
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.

2 participants