Skip to content

feat(api): link adoption ledger for a deterministic noise guard (#709) - #712

Merged
Zach Dunn (zachdunn) merged 1 commit into
mainfrom
claude/709-adoption-ledger
Aug 18, 2026
Merged

feat(api): link adoption ledger for a deterministic noise guard (#709)#712
Zach Dunn (zachdunn) merged 1 commit into
mainfrom
claude/709-adoption-ledger

Conversation

@zachdunn

Copy link
Copy Markdown
Member

Follow-up to #701/#707. Adds a per-target D1 ledger (github_adopted_links,
mirroring github-ingest-ledger.ts's pattern) so the link-adoption noise
guard is deterministic across passes and supports un-adoption, closing #709.

What changed

  • New ledger (apps/api/src/github-link-adopt-ledger.ts,
    apps/api/migrations/20260818200000_github_adopted_links.sql): one row per
    (repo, kind, num, source_key) — the resolved workspace key a pasted
    uploads.sh link pointed to, scoped to the PR/issue it was adopted into.
    detached_at NULL means currently referenced by source (the body or the
    specific comment it was last seen in).
  • Noise guard now counts from the ledger: shouldSyncAfterAdopt reads the
    TOTAL non-detached rows for the target, not the current pass's copy count,
    so two links pasted in two separate comments deterministically trip the
    ≥2 threshold on the second one.
  • Idempotent, cheaply: a rescan (redelivery, comment edit) that finds a
    key already ledgered and not detached skips re-attaching entirely — no
    re-copy, no metadata rewrite.
  • Un-adoption parity with ingest's detach: adoptLinkedFiles now
    reconciles per-source (body or one comment) — a ledgered key no longer
    referenced when that exact source is rescanned gets marked detached and
    the managed comment resyncs. The copy is never deleted, only hidden from
    the render via a gh.detached metadata flag; gatherAttachments
    (github-comment.ts) now filters on it unconditionally. A re-pasted link
    un-detaches without a re-copy.

Pinning test

Before touching the guard, I wrote a test pinning the issue's own scenario
(two links pasted in two separate comments, one at a time) against the
pre-ledger code. It already synced correctly on the second comment — the
first comment's copy landed in R2 regardless of whether the guard fired,
so gatherCommentBody's pre-adoption listing saw it as a "pre-existing
attachment" and tripped the guard by coincidence. Kept as regression
coverage (github-link-adopt.test.ts, "PIN (issue #709)") now that the
guard reads the ledger directly instead of relying on that side effect.

Tests

apps/api/src/github-link-adopt.test.ts — ledger-backed idempotency, the
pinning test, and the detach/re-adopt round-trip.
apps/api/test/github-link-adopt-ledger-sqlite.test.ts — real-SQL-semantics
coverage for the ledger, mirroring github-ingest-ledger-sqlite.test.ts.
apps/api/src/github-comment.test.ts — updated for the unconditional
gh.detached filter read.

pnpm test (4536 tests) and pnpm -r typecheck both green.

No changeset — this is server-only (apps/api), no published-package
(@buildinternet/uploads) behavior changed.

Adds a per-target D1 ledger (github_adopted_links, mirroring the ingest
ledger pattern) recording which resolved source keys have been adopted
into which PR/issue. The noise guard now counts TOTAL currently-adopted
files from the ledger rather than the current pass's copy count, so two
links pasted in two separate comments deterministically trip the >=2
threshold on the second one regardless of ordering.

Idempotent: a rescan of the same source cheaply skips an already-ledgered,
non-detached key (no re-copy). Un-adoption parity with ingest's detach: a
rescan of one exact source (body or a single comment) that no longer
references a previously-adopted key marks it detached and re-syncs the
managed comment; the copy itself is never deleted, only hidden from the
render via a gh.detached metadata flag (gatherAttachments now filters on
it unconditionally). A re-pasted link un-detaches without a re-copy.

A pinning test confirms the two-separate-comments scenario from the issue
already synced correctly pre-ledger (the pre-adoption copy already counted
as a "pre-existing attachment" via the prefix listing) — kept as
regression coverage now that the guard reads the ledger instead.
@changeset-bot

changeset-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: eb51ef5

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (2)
  • coderabbit:review
  • review
🚫 Excluded labels (none allowed) (1)
  • wip

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b58929af-a788-4993-9c94-e589e54f55ce

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@zachdunn
Zach Dunn (zachdunn) merged commit 5858ff6 into main Aug 18, 2026
4 checks passed
@zachdunn
Zach Dunn (zachdunn) deleted the claude/709-adoption-ledger branch August 18, 2026 23:58
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