From 23b6c293f5e9db410f3d7bb85541de266baf5d39 Mon Sep 17 00:00:00 2001 From: George Diab Date: Mon, 2 Mar 2026 22:09:57 -0800 Subject: [PATCH 1/2] fix: remove stale milestone labels, inline duplicated helper, add 404 test - Remove M2/M3 milestone labels from README, rewrite enrichment line to present tense (closes #10 context) - Delete duplicated `isRedditPageUrl` helper, inline via `isRedditHost` (closes #10) - Add 404 non-retryable test for RedditAdapter (closes #11) - Document limit=8 vs MAX_COMMENTS=5 intent (closes #12) Co-Authored-By: Claude Opus 4.6 --- README.md | 4 ++-- src/adapters/reddit-adapter.ts | 16 ++++++---------- test/unit/reddit-adapter.test.ts | 26 ++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2af9527..08efeb1 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ node dist/cli/index.js --help - `linkledger index-rebuild [--json]` - `linkledger worker [--limit N] [--max-attempts N] [--base-backoff-ms N] [--json]` -## Ingestion adapters (M2) +## Ingestion adapters - `article`: HTML extraction and chunking. - `x`: oEmbed-based extraction with fallback to article adapter. @@ -47,7 +47,7 @@ node dist/cli/index.js --help Retryable adapter failures are requeued with exponential backoff in `worker`. -In M3, successful ingest also creates enrichment artifacts and moves items to `enriched`. +Successful ingest also creates enrichment artifacts (summary, key claims) and moves items to `enriched` status. ## Database path diff --git a/src/adapters/reddit-adapter.ts b/src/adapters/reddit-adapter.ts index 1f213cb..0f95a88 100644 --- a/src/adapters/reddit-adapter.ts +++ b/src/adapters/reddit-adapter.ts @@ -7,6 +7,9 @@ import type { AdapterParseResult, SourceAdapter } from './source-adapter.js'; const MAX_POST_CHARS = 1800; const MAX_COMMENT_CHARS = 900; +// limit=8 in the fetch URL over-fetches to buffer for non-comment children +// (e.g. kind:'more' stubs) that get filtered out, ensuring we still collect +// up to MAX_COMMENTS actual t1 comments. const MAX_COMMENTS = 5; interface RedditListingChild { @@ -59,15 +62,6 @@ const toPublishedAt = (value: unknown): string | undefined => { return new Date(value * 1000).toISOString(); }; -const isRedditPageUrl = (value: string): boolean => { - try { - const parsed = new URL(value); - return isRedditHost(parsed.hostname.toLowerCase()); - } catch { - return false; - } -}; - export class RedditAdapter implements SourceAdapter { supports(url: string): boolean { return this.detectType(url) === 'reddit'; @@ -109,13 +103,15 @@ export class RedditAdapter implements SourceAdapter { const subreddit = compact(toText(postData.subreddit)); const selfText = compact(toText(postData.selftext)); const linkedUrl = compact(toText(postData.url)); + let isLinkedReddit = false; + try { isLinkedReddit = isRedditHost(new URL(linkedUrl).hostname.toLowerCase()); } catch { /* malformed URL */ } const postParts = [ title ? `Title: ${title}` : '', subreddit ? `Subreddit: r/${subreddit}` : '', author ? `Author: u/${author}` : '', selfText ? `Body:\n${selfText}` : '', - linkedUrl && !isRedditPageUrl(linkedUrl) ? `Linked URL: ${linkedUrl}` : '' + linkedUrl && !isLinkedReddit ? `Linked URL: ${linkedUrl}` : '' ].filter(Boolean); const chunks: Array<{ text: string; tokenCount: number }> = []; diff --git a/test/unit/reddit-adapter.test.ts b/test/unit/reddit-adapter.test.ts index aaff48b..c81ef74 100644 --- a/test/unit/reddit-adapter.test.ts +++ b/test/unit/reddit-adapter.test.ts @@ -2,6 +2,7 @@ import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import path from 'node:path'; import test from 'node:test'; +import { AppError } from '../../src/lib/errors.js'; import { RedditAdapter } from '../../src/adapters/reddit-adapter.js'; const fixture = (name: string): string => @@ -57,3 +58,28 @@ test('RedditAdapter marks upstream failures as retryable for 5xx', async () => { globalThis.fetch = originalFetch; } }); + +test('RedditAdapter marks 404 as non-retryable', async () => { + const adapter = new RedditAdapter(); + const originalFetch = globalThis.fetch; + + globalThis.fetch = async () => + new Response('not found', { + status: 404, + headers: { 'content-type': 'text/plain' } + }); + + try { + await assert.rejects( + () => adapter.fetchAndParse({ url: 'https://www.reddit.com/comments/abc123' }), + (error: unknown) => { + assert.ok(error instanceof AppError); + assert.equal(error.code, 'FETCH_FAILED'); + assert.equal(error.retryable, false); + return true; + } + ); + } finally { + globalThis.fetch = originalFetch; + } +}); From 135748fcecbd0739aa2185fc0f0637a28a30666e Mon Sep 17 00:00:00 2001 From: George Diab Date: Mon, 2 Mar 2026 22:31:02 -0800 Subject: [PATCH 2/2] ci: fix Claude review by adding tool permissions and guardrails The review agent was denied 49 tool calls because it lacked explicit permission to post inline comments and run gh pr comment. Add --allowedTools for the inline comment MCP tool and gh CLI commands. Also add --max-turns 10, timeout-minutes 15, and synchronize trigger. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/claude-review.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index d55d585..7bdde86 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -2,7 +2,7 @@ name: Claude Code Review on: pull_request: - types: [opened, ready_for_review] + types: [opened, synchronize, ready_for_review] issue_comment: types: [created] @@ -16,6 +16,7 @@ jobs: review-pr: if: github.event_name == 'pull_request' runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Checkout @@ -25,6 +26,10 @@ jobs: uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + claude_args: >- + --allowedTools + "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)" + --max-turns 10 prompt: | REPO: ${{ github.repository }} PR NUMBER: ${{ github.event.pull_request.number }} @@ -39,6 +44,7 @@ jobs: github.event.issue.pull_request && contains(github.event.comment.body, '@claude') runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Checkout @@ -48,3 +54,7 @@ jobs: uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + claude_args: >- + --allowedTools + "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)" + --max-turns 10