Skip to content

feat: store reviews and capture human feedback#33

Merged
SimonOyaneder merged 6 commits into
mainfrom
feat/review-storage
Feb 26, 2026
Merged

feat: store reviews and capture human feedback#33
SimonOyaneder merged 6 commits into
mainfrom
feat/review-storage

Conversation

@SimonOyaneder

Copy link
Copy Markdown
Owner

Summary

  • Persists every Canon review (with all findings) to PostgreSQL when posted
  • Captures human replies to Canon inline comments and links them to the original finding
  • Adds pull_request_review_comment.created webhook handler for feedback capture

Schema

pr_reviews (1 per review posted)
  └── pr_review_findings (N per review, 1 per finding)
        └── review_feedback (M per finding, 1 per human reply)

Changes

  • Migration 0002_review-storage.sql — 3 new tables with indexes
  • src/db/review-store.tsstoreReview() (transactional) + storeFeedback() (idempotent)
  • src/github/feedback-handler.ts — validates parent is Canon bot comment, stores feedback
  • src/review/commenter.tspostReview returns PostReviewResult with review ID, comment IDs, findings, event
  • src/github/pr-handler.ts — calls storeReview after posting (graceful on failure)
  • src/index.ts — registers new webhook event

Test plan

  • npm run lint passes (tsc --noEmit)
  • npm test — 306 tests across 28 suites pass
  • npm run build compiles cleanly
  • New tests cover: storeReview transaction + rollback, storeFeedback lookup + idempotency, feedback handler skip logic for non-replies/bots/non-Canon comments, pr-handler storeReview integration + error resilience

🤖 Generated with Claude Code

SimonOyaneder and others added 5 commits February 26, 2026 17:51
Aligns the default with the documented value in CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Creates pr_reviews, pr_review_findings, and review_feedback tables
to persist Canon reviews and capture human replies to findings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
storeReview() inserts a review with all findings in a single transaction.
storeFeedback() links a human reply to a stored finding, idempotent via
ON CONFLICT DO NOTHING.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Returns review ID, inline comment IDs, deduped findings, and event type
instead of a boolean. Fetches comment IDs via listCommentsForReview after
posting so they can be linked to stored findings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- pr-handler calls storeReview after posting (graceful on failure)
- feedback-handler captures human replies to Canon inline comments
- index.ts registers pull_request_review_comment.created event

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@canon-review canon-review 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.

Found 3 pattern deviations: ⚪ 3 low

Additional findings

src/github/pr-handler.ts — Style Inconsistency · confidence: 98%

Config is imported as a value import even though it is only used as a type; this deviates from existing type-only import usage.

Suggested fix:

import type { Config } from "../config";

Reference: src/review/reviewer.ts


Comment thread src/db/review-store.ts Outdated
@@ -0,0 +1,105 @@
import { getPool } from "../lib/db-client";
import { Finding } from "../review/reviewer";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Style Inconsistency · low · confidence: 99%

Type import is not using the repo’s type-only import pattern for TypeScript types.

Existing pattern: Types are imported with import type where appropriate.
Reference: src/review/reviewer.ts

Suggested change
import { Finding } from "../review/reviewer";
import type { Finding } from "../review/reviewer";

Comment thread src/github/feedback-handler.ts Outdated
@@ -0,0 +1,63 @@
import { Context } from "probot";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Style Inconsistency · low · confidence: 98%

Type import from Probot is not using type-only import syntax, which is used in the codebase for type-only dependencies.

Existing pattern: Type-only imports are preferred for types/interfaces.
Reference: src/review/reviewer.ts

Suggested change
import { Context } from "probot";
import type { Context } from "probot";

Applies Canon review feedback — Finding, Context, and Config are only
used as types and should use import type syntax.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@SimonOyaneder

Copy link
Copy Markdown
Owner Author

/canon review

@canon-review canon-review 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.

Found 2 pattern deviations: 🟡 1 medium · ⚪ 1 low

Additional findings

tests/github/pr-handler.test.ts — Style Inconsistency · confidence: 98%

Type import style is inconsistent with the established pattern; Config should be imported as type-only.

Suggested fix:

import type { Config } from "../../src/config";

Reference: src/github/pr-handler.ts


Comment thread src/db/review-store.ts
}

await client.query("COMMIT");
} catch (err) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Pattern Violation · medium · confidence: 95%

Catch variable name deviates from the repository’s DB transaction error-handling pattern (error + throw error).

Existing pattern: Transactional DB flows use catch (error) { await client.query("ROLLBACK"); throw error; } for consistency.
Reference: src/db/migrate.ts

Suggested change
} catch (err) {
} catch (error) {
await client.query("ROLLBACK");
throw error;
}

@SimonOyaneder SimonOyaneder merged commit 36e2d7e into main Feb 26, 2026
2 checks passed
@SimonOyaneder SimonOyaneder deleted the feat/review-storage branch February 26, 2026 21:00
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