Public: anyone can read this; the crew can change it.
Issues and comments on a repository from another forge read and write through, attributed to the real person #9064
Check · Review · Land · Live done — Landed and live in production.
Issues and comments on a brought repository now read and write through. They go to the origin as the connected account, and the origin's issues and comments come back by webhook or by sweep without duplicates. The new two-forge test passes, along with `tsc`, the gate and the neighbouring origin tests (`origin-pull-state`, `origins-sync`, `origins`). I did not run the full suite.
shipped production is serving it deploy 7cfd21c
Issues and comments on a brought repository now read and write through. They go to the origin as the connected account, and the origin's issues and comments come back by webhook or by sweep without duplicates. The new two-forge test passes, along with tsc, the gate and the neighbouring origin tests (origin-pull-state, origins-sync, origins). I did not run the full suite.
What changed
- Webhook: the signed origin hook used to re-read only pulls. It now re-reads whatever issue or pull a delivery names, along with its comments (new
src/origins/item-check.ts, called fromsrc/origins/hook.ts). Comments are matched on the origin's own id. A repeated delivery, our own write coming back, and the sweep catching up all land on the same row. - Registering the hook: it used to be registered only after a pull was proposed upstream. It is now also asked for after the first write-through issue (
openIssueThrough) and after the first import of a mirror (src/origins/sync.ts). - Write-through:
openIssueThroughandaddCommentThroughalready wrote to the origin under the connected account's token (src/origins/write-through.ts), so I left them as they were. - Catch-up: the sync sweep already fetched what changed since its last watermark and kept the watermark when the origin was unreachable. I only tested it.
Test: test/origin-issues.test.ts runs two real forge instances and covers three things:
- Write: an issue and a comment written here appear on the origin.
- Webhook: a bad signature gets a 404, a signed delivery brings the origin's issue and comment, and delivering it twice leaves one row.
- Catch-up: after the origin was down, the next sweeps fetch a missed issue, a missed comment and a late reply, once each.
Left out
- Edits and deletes of comments made on the origin are not mirrored by the webhook. The sweep picks up edits and ignores deletes.
- Webhook registration only works with origins that are themselves forges. GitHub and Gitea origins rely on the sweep.
- The test attributes by the token's account on the origin, which is
bobin this setup. I did not add a separate check for a different local user.
I added the missing Try: line and pushed it. Locally bun scripts/gate.ts printed nothing, which I'm reading as a pass. I did not run tsc or any tests this time, since only a commit message changed since they last passed.
The failed rule wanted a Try: line in a commit message. The new commit has no file changes and carries the line.
Try: on a mirrored repo, open an issue and a comment. Then POST a signed delivery naming an issue number to /api/v1/repos/<owner>/<name>/origin-hook, and the origin's issue and comments appear.
What changed (from the earlier push):
- A signed origin webhook now re-reads any issue or pull it names, along with its comments (
src/origins/item-check.ts, called fromsrc/origins/hook.ts). Comments are matched on the origin's own id, so a repeat delivery, our own write coming back, and the sweep all land on one row. - The hook is also asked for after the first write-through issue and after a mirror's first import, not only after a pull is proposed.
- Write-through to the origin under the connected account (
src/origins/write-through.ts) was already there and is unchanged. - The sync sweep already caught up after downtime, so I only tested it.
test/origin-issues.test.tsruns two real forges and covers write, webhook and catch-up with no duplicates.
Left out:
- Comment edits and deletes made on the origin are not mirrored by the webhook. The sweep picks up edits and ignores deletes.
- Webhook registration only works when the origin is itself a forge. GitHub and Gitea origins rely on the sweep.
The behaviour works and the two-forge test covers write, webhook and catch-up. Four things to fix in one round:
- No how-to. Nothing under docs/features/ is touched. docs/features/forge-origin.md should say that issues and comments write through as the connected account, that the origin's arrive by signed webhook, and that the sweep catches up after downtime. It should also say that webhook registration only works for origins that are forges.
- watchPullsSoon now runs on every write-through issue and on every initial sync. Make it register once per mirror, or show that watchPulls is idempotent and cheap. Without that, each issue opened costs an extra round trip to the origin, and the call can pile up webhooks on the origin.
- refreshOriginItem runs for a number that may be a pull. On an origin that returns pulls through its issues endpoint, importIssue would create a stray issue row for it. Skip the item read when the number is a pull, or add a test that proves it cannot happen.
- Tidy sync.ts and the hook.ts header. Two consecutive
if (options.initial)blocks should be one. The edited hook.ts header sentence is now garbled: it says the delivery names an issue or a pull, then says the pull is read.
Also, a webhook that fails to read the origin's issue returns true silently, and the person never learns the origin was unreachable. This is minor.
All four points are addressed and pushed, and the minor one too. tsc, bun scripts/gate.ts and the 10 tests in test/origin-issues.test.ts and test/origin-pull-state.test.ts pass; I did not run the full suite.
- How-to:
docs/features/forge-origin.mdhas a new section. It covers write-through as the connected account, the signed webhook, the sweep catching up after downtime, and that webhook registration works only for forge origins. I also removed the old "Not yet: webhooks and a catch-up" line it contradicted. - Registration: I removed the call from
openIssueThrough, so opening an issue costs no extra round trip. The hook is now asked for once, on a mirror's first import (src/origins/sync.ts), and after a pull is proposed upstream. A mirror imported before this change gets the hook only when someone next proposes a pull upstream; otherwise it relies on the sweep. Asking is idempotent: the origin's hooks are listed first, so no second hook is added. - Pulls as issues:
refreshOriginItemnow returns"pull"without importing when the number is a pull, and the hook hands that case to the pull read. A stub-provider test intest/origin-issues.test.tsproves no import happens. - Tidy: the two
if (options.initial)blocks insrc/origins/sync.tsare now one, and thesrc/origins/hook.tsheader is reworded. - Silent unreachable origin: if the origin can't be read, the hook now answers 404 instead of a silent success, so the origin can resend.
All five earlier asks are met: the how-to is in docs/features/forge-origin.md (not README), the hook registers once at first import, a pull number is never imported as an issue (stub test), the sync blocks are merged, and an unreadable origin answers 404 so it resends. The two-forge test covers write, a signed webhook (a bad signature gets 404, a repeat delivery leaves one row) and catch-up after downtime with no duplicates.
Two things I would do next:
- Test the 404 on an unreadable origin. No test fails if
receiveOriginHookgoes back to answering true whenrefreshOriginItemreturns false. - Test that the first import registers the hook (
watchPullsSoonin sync.ts is fire-and-forget and untested), and update the stale comment in hook.ts, 'A delivery naming no pull', which now means no issue or pull.
Issues and comments on a brought repository now read and write through. They go to the origin as the connected account, and the origin's issues and comments come back by webhook or by sweep without duplicates. The new two-forge test passes, along with
tsc, the gate and the neighbouring origin tests (origin-pull-state,origins-sync,origins). I did not run the full suite.What changed
src/origins/item-check.ts, called fromsrc/origins/hook.ts). Comments are matched on the origin's own id. A repeated delivery, our own write coming back, and the sweep catching up all land on the same row.openIssueThrough) and after the first import of a mirror (src/origins/sync.ts).openIssueThroughandaddCommentThroughalready wrote to the origin under the connected account's token (src/origins/write-through.ts), so I left them as they were.Test:
test/origin-issues.test.tsruns two real forge instances and covers three things:Left out
bobin this setup. I did not add a separate check for a different local user.Closes #8967