Skip to content

feat(coderd): key chat diff statuses by ref - #29081

Draft
DanielleMaywood wants to merge 1 commit into
mainfrom
feat/chat-diff-ref-key
Draft

DanielleMaywood wants to merge 1 commit into
mainfrom
feat/chat-diff-ref-key

Conversation

@DanielleMaywood

@DanielleMaywood DanielleMaywood commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

A chat now keeps one row per git ref. Each ref that the agent reports gets its own stored pull request data. Retained refs keep refreshing on their own schedule, so no pause or resume state exists.

This is part 1 of 3. Part 2 adds the API fields. Part 3 adds the git panel views.

Problem

The chat_diff_statuses table had one row per chat. Because of this, a chat could show data for only one pull request at a time. When a chat switched from one branch to another, the old branch's pull request data stayed with the new branch.

Fix

  • The table key is now (chat_id, git_remote_origin, git_branch). Each ref keeps its own row. A branch switch adds a new row. It does not change the old row.
  • No data rewrite is needed. Both key columns are NOT NULL with empty string defaults, and the old key allowed only one row per chat. So every existing row already satisfies the new key.
  • The worker claims stale rows by the full key. The old query claimed every row of a chat when any one of them went stale.
  • Telemetry reports one diff row per chat with DISTINCT ON.
  • The diff GET's discovery write now keys its upsert to the stored ref. Under the old single-row key an empty ref key was safe. Under the composite key it would create a stray row the worker ignores. A chat with no stored ref skips the write, since there is no row to key.

Notes for review

  • Migration 000591 swaps the primary key. The index build takes an ACCESS EXCLUSIVE lock for its duration. CREATE INDEX CONCURRENTLY is not possible because all migrations run in one transaction. This table is low volume, so the lock is acceptable.
  • Stop old coderd instances before this migration runs. Their ON CONFLICT (chat_id) writes have no arbiter once the old key is gone.

Generated by Coder Agents.

@DanielleMaywood DanielleMaywood added the experimental Changes that might not necessarily be merged, until its approved to proceed with. label Sep 8, 2026
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

Stack: #29081 (database) -> #29082 (API) -> #29083 (git panel). Replaces the closed single PR #29001. Merge in order.

Comment thread coderd/database/queries/chats.sql
Comment thread coderd/database/migrations/000592_chat_diff_statuses_composite_key.up.sql Outdated
Comment thread coderd/database/queries/chats.sql
Comment thread coderd/x/chatd/chatd.go Outdated
Comment thread coderd/exp_chats.go Outdated
Comment thread coderd/exp_chats.go Outdated
Comment thread coderd/exp_chats.go Outdated
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 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-15T14:07:24.123545Z c6eef7d Manual request
ℹ️ 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: 1a7076c4bd

ℹ️ 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".

chat_id = @chat_id::uuid;
chat_id = @chat_id::uuid
ORDER BY
updated_at DESC,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Track the latest reported ref separately from refresh time

When a chat retains multiple refs, ordering by updated_at does not identify the most recently reported ref: every successful background UpsertChatDiffStatus sets that column to NOW(). Consequently, whichever historical ref the worker refreshes last becomes element zero, which getChat, list enrichment, and PublishDiffStatusChange expose as the chat's current diff status; this is especially visible when the current branch has no PR but an older branch does. Preserve a separate report/selection timestamp or otherwise keep refresh writes from changing this ordering.

Useful? React with 👍 / 👎.

@DanielleMaywood
DanielleMaywood force-pushed the feat/chat-diff-ref-key branch 3 times, most recently from e370f06 to caa4caa Compare September 15, 2026 12:48
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex 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: caa4caa84c

ℹ️ 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 2034 to +2035
refreshed_at = EXCLUDED.refreshed_at,
stale_at = EXCLUDED.stale_at,
updated_at = NOW()
stale_at = EXCLUDED.stale_at

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 Deduplicate telemetry by refresh time

When multiple chat/ref rows share a PR URL and their cached states disagree, this refresh now updates refreshed_at while deliberately leaving updated_at as the report time. GetChatDiffStatusSummary still selects the duplicate with the greatest cds.updated_at, so a more recently reported but no-longer-refreshable row can indefinitely override a freshly refreshed state and misreport open/merged/closed telemetry. Deduplicate that query by refreshed_at (with deterministic fallbacks) rather than the report timestamp.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in c6eef7d0cd: the dedup tie-break is now refreshed_at DESC NULLS LAST, updated_at DESC, id DESC, so the summary deduplicates by the most recently refreshed state, matching the query's comment. The option A change in this PR (refresh no longer bumps updated_at) is what made the old updated_at-based tie-break stale, so the fix belongs here.

Note one sub-case of the reported scenario does not mis-count: when a PR is deleted upstream, the worker clears pull_request_state to NULL, and the row drops out of the query's WHERE clause. Only the failed-refresh path (stale state left in place while reports keep bumping updated_at) mis-counts, which the new tie-break resolves.

chat_diff_statuses held one row per chat, so a chat could only track
one pull request at a time and a branch switch carried the previous
PR's URL onto the new branch's data.

Re-key the table to (chat_id, git_remote_origin, git_branch) so every
ref the agent reports keeps its own row. Existing rows satisfy the new
key without a rewrite: both columns are NOT NULL with '' defaults and
the old key capped rows at one per chat.

The worker claims stale rows by the full key. Retained refs keep
polling on their own schedule, so no pause state or resume protocol is
needed. Telemetry reports one diff row per chat via DISTINCT ON.

The diff GET's discovery write now keys its upsert to the stored ref.
Under the old single-row key an empty ref key was safe; under the
composite key it would create a stray row the worker ignores.

Old coderd instances must finish before this migration runs: their
ON CONFLICT (chat_id) writes have no arbiter under the new key.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: c6eef7d0cd

ℹ️ 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".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

experimental Changes that might not necessarily be merged, until its approved to proceed with.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant