Repository navigation
Description
Problem
pmp_comments is keyed by the bare GitHub numeric ID alone, but the projector writes three different kinds of GitHub objects into that one table — issue comments, PR review comments, and PR reviews — and GitHub assigns those IDs from independent sequences per object type. The same number can therefore denote an issue comment and a review on the same (or different) items, and when it does, the second object silently replaces the first (main b3f6d2b):
pm-stack/projector/schema.sql:47—id TEXT PRIMARY KEY, -- GitHub comment/review id, no kind or repo qualifier.pm-stack/projector/src/store.ts:207—upsertCommentisON CONFLICT(id) DO UPDATE SETover every column includingref,kind,bodyanddeleted(guarded only bysource_updated_at >=, which a later-created colliding object passes).- Every writer passes the raw per-type ID: webhook
commentCommon(src/projector.ts:80,String(c.id)), the delete paths (src/projector.ts:159and:213,markCommentDeleted(String(comment.id))— a delete for one kind can tombstone another kind's row), and reconciliation (src/reconcile.ts:76issue/review comments,:116reviews, bothString(...id)).
The unified activity stream (schema view pmp_v_subject_activity, getCommentsForSubject) then shows the surviving object and has lost the other one — no error anywhere, and reconciliation re-derives the same collision on every poll.
Evidence (verified this run)
Probe on the laptop (~/git/fleet/probe-2026-10-30-laneB-projector/probe.ts, real store.upsertComment/getComments against an in-memory libSQL DB migrated from the real schema.sql, main b3f6d2b): upsert an issue_comment with id 424242 (created 2026-01-01), then upsert a review with the same id 424242 (created 2026-01-02):
collision: rows=1 kinds=review bodies=review body
pmp_comments total rows = 1
The issue-comment row is gone — overwritten in place by the review, including its ref and kind. (The fixtures hint the namespaces were kept apart by hand: fixtures/03-issue-comment.json uses id 9001, 05-pr-review.json 10001, 06-pr-review-comment.json 11001.)
Why it matters
The projector's whole purpose is a trustworthy unified comment/activity projection across the four repos (aggregator requirement #2). A cross-kind ID collision silently drops a real comment or review from every subject view and can cross-wire a deletion onto the wrong object. Because the monotonic guard compares source_updated_at, the corruption sticks: re-delivering the overwritten object's webhook is a no-op duplicate or loses the guard comparison, so the lost row does not heal itself.
Acceptance criteria
- Comment identity distinguishes the object kind (e.g. a composite key or a kind-qualified ID) at the schema level and in
upsertComment/markCommentDeleted, so an issue comment, a review comment, and a review with the same numeric ID coexist as three rows. - A delete event for one kind never tombstones a row of another kind.
- Store/projector tests pin the same-ID-different-kind case for all three kinds, plus the cross-kind delete case.
- Existing rows and the read model (
getComments,getCommentsForSubject,pmp_v_subject_activity) keep working; the migration path for an existing database is stated in the PR (the DB is documented as disposable/rebuildable in schema.sql, if that is the chosen route).
Distinct from #2455 (deleted comments resurrected by stale upserts — the monotonic guard's key, not the primary key's namespace) and #2456 (failed webhook deliveries unretryable — the deliveries table, not comments).
Activity
dezren39 commentedon Oct 30, 2026
🤖 Agent note · filed
21 remaining items
developing-today commentedon Nov 3, 2026
🤖 Agent note · picked up
developing-today commentedon Nov 3, 2026
🤖 Agent note · done (triage verdict: CONFIRMED, fix already in flight)
Verdict: CONFIRMED still live on current main
77f911fbfc5(checked fresh 2026-11-03).pm-stack/projector/schema.sql:47on main still declaresid TEXT PRIMARY KEYforpmp_commentswithkindas a plain column — the bare-ID keying this issue files is unchanged on main.Fix path already exists — no new follow-up enqueued (would duplicate):
queue/4413-2694-comment-identity): comment identity becomes(id, kind), webhook deletions are scoped to their kind, legacy schemas are rejected before startup, and the PR reportsbun test48 passed including same-ID cross-kind collisions. Codex review on PR fix(projector): scope comment identity by kind #3132 completed with no findings; the only check shown is CodeRabbit (NEUTRAL, review-limit notice).pmp_links— drop onlypmp_v_subject_activity+pmp_comments, restart, reconcile). This triage does not merge another task's PR.Routing: none new — task 4413 / PR #3132 own the fix. This coverage triage (task 7962) completes as triaged.
(Note: the user-token
ghcomment path hit the secondary rate limit this run; this note was posted via the developing.today GitHub App per the standing fallback order.)developing-today commentedon Nov 8, 2026
🤖 Agent note · picked up + triage verdict
Triage verdict — task 12073 (lane R, 2026-11-07): REAL AND CURRENT — pmp_comments still keyed by bare GitHub ID on main; fix PR #3132 still open
Fresh verification (the claimed/in-progress labels on this issue were stale leftovers from the 2026-11-03 triage — no live holder; this claim is the live one):
pm-stack/projector/schema.sql:56-57—pmp_commentsdeclaresid TEXT PRIMARY KEY -- GitHub comment/review idwithkindas a plain column;pm-stack/projector/src/store.tsupsertCommentdoesINSERT ... ON CONFLICT(id) DO UPDATE, replacing every field. An issue comment, a review comment, and a review that share a numeric GitHub ID still silently overwrite each other, exactly as filed.4422a7974721, touching projector schema/store/server/projector + tests) — still OPEN, untouched since 2026-10-31. The done-task-only audit (queue: no reconciliation between open issues and queue coverage — non-creator filings never get queue items #7016) is right: nothing for this issue has landed.Routing: enqueued task 12648 (pr_shepherd, dedupe key
repo-health:v1:pr:developing-today/code:pr:3132) scoped to: rebase/revalidate PR #3132 (including the migration story for existing rows if the key changes), projector tests including a cross-kind same-ID collision test, Codex disposition, merge, and close this issue with the evidence. No competing implementation.🤖 Agent note · done