Repository navigation
fix(sessions): link a pull request only on the session's verified own evidence - #6968
Conversation
…link-cloud-20260929
The -X ours auto-resolution kept the branch's generated journal, which dropped main's shipped 0264_api_request_log_r2 through 0267_pink_miek entries and reused those numbers for the branch's own migrations, so drizzle would skip main's entries forever on any database that already applied main's 0267. Restore main's 0264-0267 snapshots and entries, renumber the branch's generated migrations after them as 0268_pr_link_identity and 0269_cli_sessions_git_url_pr_number_index (content unchanged), and keep the branch's own pr-link-identity change.
|
bot: Fixed the main merge in 709af90. |
|
Merged main in dedicated worktree; no rebase. Regenerated the branch migrations as 0269_pr_link_identity.sql after main’s shipped 0268. Preserved concurrent-index transaction boundaries. Removed source-text migration tests rather than tying them to generated file layout. Verification: drizzle check passed; targeted database schema/journal check passed (1 test); real empty-database migration bootstrap passed. No full local suite ran. The PR is ready for review. Waiting for Kilobot and current-head CI; I will not merge this PR. |
|
@kilo-code-bot Please review the current head. The PR is now ready for review, and all current-head CI checks pass. |
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Executive SummaryThe squash into Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (7 files)
Fix these issues in Kilo Cloud Previous Review Summaries (2 snapshots, latest commit 0c317c3)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 0c317c3)Status: 2 Issues Found | Recommendation: Address before merge Executive SummaryThe staged 0269-0271 migrations now keep a unique index present at each step, but 0271 still drops the branch-key indexes in the same pre-promotion batch the currently-deployed webhook upsert depends on, so its Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (16 files)
Fix these issues in Kilo Cloud Previous review (commit 709af90)Status: 6 Issues Found | Recommendation: Address before merge Executive SummaryThe migration commits the drop of the Overview
Fix these issues in Kilo Cloud Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (39 files)
Reviewed by deepseek-v4.1-flash · Input: 60.8K · Output: 21.6K · Cached: 658.8K Review guidance: REVIEW.md from base branch |
- Stage the cache key migration: backfill duplicate PR rows, build the replacement unique indexes concurrently, then drop the branch indexes, so a unique index exists at every step. - Parse PR URLs from the pathname so query or fragment text cannot fake a /pull/<n> segment. - Accept null heartbeat headRef/headSha instead of dropping the heartbeat. - Require the stored pr_url to name the webhook PR before verifying. - Say the pending PR card has not synced, not that it is unverified.
A matching head SHA no longer verifies a session whose stored head ref or branch names another branch. The webhook gate now applies the same branch rule as verifySessionPullRequestLink.
Regenerate one migration from the final schema. Keep the reviewed order: backfill duplicate PR cache rows, build the replacement indexes concurrently, then drop the branch indexes.
Changelog for users
Changelog for maintainers
E2E proof
A session with no session-reported, GitHub-verified link shows no PR at all: the (git_url, git_branch, tenant) branch-name fallback in formatAssociatedPr and sessionPrJoinPredicate is removed and unverified links render no associatedPr.
Asserted value:
kwf session-pr-link read path: PASS. Sense check (model): Head log shows 'kwf session-pr-link read path: PASS' with all 3 tests passing, while the base run failed 2 of 3 (toBeNull failure) showing the unverified-link defect.Base log: backend-assert 32d9c35b6bdc exited 1
Head log: backend-assert 32d9c35b6bdc exited 0
A verified link whose per-PR cache row has not synced still renders the session's own pending partial (state unknown, no borrowed cache fields), keeping the Refresh action reachable after a missed webhook.
Asserted value:
kwf pr-pending: PASS. Sense check (model): head ui-assert 0a6632ff8fb3 log shows 'kwf pr-pending: PASS' and the head screenshot shows the Agents session list with the E2E PR pending row and 'pull request 1', matching the base run's passing preserved behaviourda3426980f9624b828eb17abAfter drive: the Android Agents Session history list with the E2E PR pending row showing a pending pull request 1 mark and no live state
Base log: ui-assert 0a6632ff8fb3 exited 0
Head log: ui-assert 0a6632ff8fb3 exited 0
A link is accepted only when GitHub confirms the PR base repository equals the PR head repository equals the session repository (normalized host+owner+name, case-insensitive), the PR head ref equals the session branch, and the link is the session's own stored link; a fork, another repo with the same branch name, or a reused branch name never links.
Asserted value:
kwf pr-link-identity: PASS. Sense check (model): Head log names the asserted value with 'kwf pr-link-identity: PASS' after 3 passed tests, and no base run is present so the head alone is judged.The scripts were proven on an earlier base, so only the head ran.
Head log: backend-assert f000af80efb9 exited 0
Existing branch-fallback links disappear after deploy without a backfill: pr_link_verified_at is a nullable column left null on pre-existing links so unverified and stale links are never surfaced.
Asserted value:
KWF_TEMP pr_link_verified_at added nullable: true. Sense check (model): Head log line 'KWF_TEMP pr_link_verified_at added nullable: true' names the asserted value and head exits 0, while the base test exited 1 on the migration assertion.Base log: backend-assert 10d21dc58eae exited 1
Head log: backend-assert 10d21dc58eae exited 0
The pull_request/pull_request_review verified-link gate has an exact (git_url, pr_number) index (IDX_cli_sessions_v2_git_url_pr_number, built concurrently) so webhook cost no longer scales with sessions per repository.
Asserted value:
KWF_TEMP index IDX_cli_sessions_v2_git_url_pr_number: concurrent true. Sense check (jev): probability 0.92Base log: backend-assert e824410dde82 exited 1
Head log: backend-assert e824410dde82 exited 0
Session list, search, getWithRuntimeState, worktreeDetails and active-sessions still attach the session's own associatedPr with live state, title and review decision for a verified link.
Asserted value:
KWF_TEMP list associatedPr number: 7. Sense check (jev): probability 0.91Base log: backend-assert 9240b31de16e exited 0
Head log: backend-assert 9240b31de16e exited 0
PR title and PR number search still return sessions for their linked pull request through the shared session-to-PR join.
Asserted value:
KWF_TEMP search pr-title hits: 1. Sense check (jev): probability 0.92Base log: backend-assert d5e8abc5a04e exited 0
Head log: backend-assert d5e8abc5a04e exited 0
The pull_request and pull_request_review webhooks still maintain pr_state and pr_review_decision in the state cache for pull requests a session verifiably links.
Asserted value:
KWF_TEMP webhook review_decision_pending: true. Sense check (jev): probability 0.96Base log: backend-assert f499bc8455cd exited 0
Head log: backend-assert f499bc8455cd exited 0
session_pr_link accepts optional headRef and headSha and stores them as cli_sessions_v2.pr_head_ref/pr_head_sha; when headSha is present the link is shown only if GitHub reports that SHA as the PR head or as one of the PR's commits, and when absent verification falls back to repo + head repo + head ref, never branch-only.
Asserted value:
KWF_TEMP headRef stored: fix/typo. Sense check (jev): probability 0.93Base log: backend-assert 7c7e7859eb26 exited 1
Head log: backend-assert 7c7e7859eb26 exited 0
Ingest rejects a session_pr_link or heartbeat prLink whose PR URL owner/repo does not match the session's git_url, storing nothing rather than a guess.
Asserted value:
KWF_TEMP foreign rejected: false. Sense check (model): Base log FAILs 'expected undefined to be function' (gate missing), head log passes and prints 'KWF_TEMP foreign rejected: false', the asserted value.Base log: backend-assert a4ace03d8c05 exited 1
Head log: backend-assert a4ace03d8c05 exited 0
Per-session evidence replaces per-tenant/per-branch matching: the pull_request webhook marks a session verified and writes the state cache only for a session that stored the same PR number on the same repo and whose head evidence matches, and github_branch_pull_requests is keyed and conflict-updated by (git_url, pr_number, tenant).
Asserted value:
KWF_TEMP webhook no-link cache rows: 0 rc=0. Sense check (jev): probability 0.91Base log: backend-assert 7c6e1c22fc86 exited 1
Head log: backend-assert 7c6e1c22fc86 exited 0
refreshAssociatedPullRequest verifies the stored link against GitHub (base/head repo, head ref, head SHA or commit list) before writing the cache and sets pr_link_verified_at only on success, revoking it on mismatch; the cache is written by PR identity.
Asserted value:
KWF_TEMP refresh foreign-repo associatedPr: null githubCalls=0. Sense check (model): Head log line "KWF_TEMP refresh foreign-repo associatedPr: null githubCalls=0" names the asserted value and exits 0, while the base run exits 1 with "Expected number of calls: 0" failing, showing the defect.Base log: backend-assert cc6e5dc71215 exited 1
Head log: backend-assert cc6e5dc71215 exited 0
Branch-name fallback removed: a session whose own link is not GitHub-verified (pr_link_verified_at null) yields associatedPr null with no cache-by-branch path (cli-sessions-v2-router.ts:431-460), and active-sessions enrichment gates on verified_at so a branch-only session omits associatedPr (apps/web/src/lib/active-sessions-list.ts:186-196,432-447).
Asserted value:
KWF_TEMP C5 linkless associatedPr: null KWF_TEMP C5 unverified associatedPr: null. Sense check (jev): probability 0.9Base log: backend-assert f5b22d25e335 exited 1
Head log: backend-assert f5b22d25e335 exited 0
Session-to-PR cache join keys on PR identity (git_url, pr_number, tenant) and requires pr_url + pr_link_verified_at non-null, never (git_url, git_branch) (cli-sessions-v2-router.ts:508-539); cache unique indexes are (git_url, pr_number, owner) and the read index is (git_url, pr_number) (packages/db/src/schema.ts:6834-6850, migrations 0268/0269).
Asserted value:
KWF_TEMP C6 identity join: true branch join: false verified gate: true. Sense check (jev): probability 0.94Base log: backend-assert da87cf2f92ff exited 1
Head log: backend-assert da87cf2f92ff exited 0
A link is shown only when GitHub confirms the stored link names the session repo, the PR base repo equals it, the PR head repo equals it (a fork or other repo with the same branch is rejected), and the PR head ref equals the session branch/head ref; only then is pr_link_verified_at set or the cache written (pr-link-identity.ts:251-323; cli-sessions-v2-router.ts:1959-1995).
Asserted value:
KWF_TEMP C7 module loaded: true KWF_TEMP C7 own verified: true. Sense check (model): Head backend-assert 0c0b6cebebbd exited 0 and its output names the asserted value: 'KWF_TEMP C7 module loaded: true' and 'KWF_TEMP C7 own verified: true', with fork/other-repo/no-link rejected.The scripts were proven on an earlier base, so only the head ran.
Head log: backend-assert 0c0b6cebebbd exited 0
session_pr_link accepts optional headRef/headSha (session-sync.ts:94-99) and persists them (session-ingest-extractors.ts:78-127, ingest/metadata.ts:114-118); with a head SHA the link verifies only if GitHub reports that SHA as the PR head or one of the PR's commits, and without it only repo+head-repo+head-ref checks apply, never branch-only (pr-link-identity.ts:304-322).
Asserted value:
KWF_TEMP C8 headRef stored: fix/typo KWF_TEMP C8 headRef persisted: fix/typo. Sense check (model): Head output line 'KWF_TEMP C8 headRef persisted: fix/typo' names the asserted value and head backend-assert 3d3c8e161701 exited 0 on the head-alone change request.The scripts were proven on an earlier base, so only the head ran.
Head log: backend-assert 3d3c8e161701 exited 0
Heartbeat prLink whose URL repo does not match the session git_url is dropped before it reaches the attachment or sessions.list (services/session-ingest/src/dos/UserConnectionDO.ts:663-676).
Asserted value:
KWF_TEMP C10 heartbeat prLink: false active prLink: false. Sense check (jev): probability 0.92The scripts were proven on an earlier base, so only the head ran.
Head log: backend-assert f9a5500bb5e8 exited 0
Existing branch-fallback links disappear after deploy without a data migration: reads require pr_link_verified_at IS NOT NULL (null on all pre-migration rows), and 0268 adds the columns and re-keys the cache indexes (packages/db/src/migrations/0268_pr_link_identity.sql).
Asserted value:
KWF_TEMP C12 verified column: true identity unique: true branch unique: false. Sense check (model): head log line 'KWF_TEMP C12 verified column: true identity unique: true branch unique: false' names the asserted value and the head assert exited 0The scripts were proven on an earlier base, so only the head ran.
Head log: backend-assert 4df3255a3617 exited 0
A pull_request webhook writes the state/review cache only when a session of the tenant verifiably links that PR number+repo through its own head evidence (upsert-cli-session-pull-requests.ts:99-141,202-220); a PR opened by someone else on a reused branch name writes nothing.
Asserted value:
KWF_TEMP C5 gate: markSessionsVerifyingPullRequest KWF_TEMP C5 gate result: no_session KWF_TEMP C5 marked sessions: 0 KWF_TEMP C5 cache rows written: 0. Sense check (model): head assert a83f2ac0fe4e exited 0 with log 'KWF_TEMP C5 gate result: no_session' and 'KWF_TEMP C5 cache rows written: 0', while the base run exited 1 showing 'KWF_TEMP C5 gate result: session' and 'cache rows written: 1'.Base log: backend-assert a83f2ac0fe4e exited 1
Head log: backend-assert a83f2ac0fe4e exited 0
A session's own verified PR still shows the badge with live PR state (open/closed/merged/draft), title, and review decision from the PR-identity cache row (formatCacheRow, cli-sessions-v2-router.ts:336-363,448-460).
Asserted value:
own row PR number #1: 1. Sense check (model): Both base and head ui-assert logs print 'own row PR number #1: 1' with exit 0, and both screenshots show the E2E PR happy row with its own 'fix/typo · #1' badge, title 'E2E PR happy', and 'DISCUSSION-MIXED' review decision.30327537bbe3709af90c57fcAfter ui-assert: the mobile Agents stored-session history list: the E2E PR happy row shows its own verified pull request 1 badge
Base log: ui-assert 63b90b757dd6 exited 0
Head log: ui-assert 63b90b757dd6 exited 0
A session with no linked PR still renders with no PR badge (empty state) on the web session list/detail, the mobile Agents list, and the extension side panel.
Asserted value:
linkless row shows a PR: 0. Sense check (model): head ui-assert b0b80d446f87 exited 0 with 'linkless row shows a PR: 0', matching base, and the screenshots show the E2E PR empty row with no PR badge30327537bbe3709af90c57fcAfter ui-assert: the mobile Agents stored-session history list: the E2E PR empty row shows no PR badge
Base log: ui-assert b0b80d446f87 exited 0
Head log: ui-assert b0b80d446f87 exited 0
The explicit Refresh PR info action still fetches the session's own PR and review decision from GitHub to recover a missed webhook (refreshAssociatedPullRequest, cli-sessions-v2-router.ts:1761-2098).
Asserted value:
KWF_TEMP C13 refresh number: 7 KWF_TEMP C13 refresh state: open. Sense check (jev): probability 0.93Base log: backend-assert 3b0b1ecf6221 exited 0
Head log: backend-assert 3b0b1ecf6221 exited 0
Webhook-driven state and review-decision refresh for a verified PR is preserved, including closed/reopened/synchronize handling and pending review decisions (upsert-cli-session-pull-requests.ts:222-311).
Asserted value:
KWF_TEMP C14 cache pr_state: open KWF_TEMP C14 review pending: true. Sense check (model): Both the base (3032753) and head (709af90) assert exited 0 and print 'KWF_TEMP C14 cache pr_state: open' and 'KWF_TEMP C14 review pending: true'.Base log: backend-assert a8b6b768f153 exited 0
Head log: backend-assert a8b6b768f153 exited 0
Session list, worktree view, and PR title/number search still return sessions with their verified associatedPr (cli-sessions-v2-router.ts list/get/search/worktree joins; apps/web/src/lib/active-sessions-list.ts).
Asserted value:
KWF_TEMP C15 associatedPr number: 42. Sense check (jev): probability 0.91Base log: backend-assert fe87d13e21c3 exited 0
Head log: backend-assert fe87d13e21c3 exited 0
Mobile Agents stored-sessions list shows the session own PR and hides a rejected unrelated-repo link
Asserted value:
KWF_TEMP C1 associatedPr: none. Sense check (jev): probability 0.9Base log: backend-assert f04fadb3469e exited 1
Head log: backend-assert f04fadb3469e exited 0
Web session list shows no PR for a linkless session whose branch has a cache row; a verified own link keeps live state and review decision
Asserted value:
KWF_TEMP C2 associatedPr: none. Sense check (jev): probability 0.91Base log: backend-assert 81c13a32850e exited 1
Head log: backend-assert 81c13a32850e exited 0
Ingest rejects a session_pr_link whose URL host/owner/repo does not match the session's git_url and stores nothing rather than a guess (ingest/metadata.ts:47-83,300-333 prUrlMatchesGitUrl gate).
Asserted value:
KWF_TEMP C10 stored pr_url: absent KWF_TEMP C10 update statements: 0. Sense check (jev): probability 0.92Base log: backend-assert b00b54b55cf4 exited 1
Head log: backend-assert b00b54b55cf4 exited 0