docs(designs): forge-poll driver design record (SEA-1810) - #219
Open
seal-agent wants to merge 5 commits into
Open
docs(designs): forge-poll driver design record (SEA-1810)#219seal-agent wants to merge 5 commits into
seal-agent wants to merge 5 commits into
Conversation
seal-agent
force-pushed
the
compass-server-1810-forge-poll-driver-design
branch
2 times, most recently
from
August 7, 2026 20:00
50bc8d7 to
d9e3860
Compare
Design record for the compass-server forge-poll driver: a ctx-cancellable board-ingestion poll loop that drives the existing `internal/ingest` library against a real GitHub forge so tracker issues appear live in the server projection. Three tasks: - **T1** — a hand-rolled `net/http` GitHub read client (OQ-6 frozen: no new dep), with `sort=updated&since` incremental fetch, `If-None-Match`/304 conditional requests over a per-URL response+`Link`-chain cache, and an `x-ratelimit-*` budget gate that fails fast rather than sleeping. - **T2** — an `ingest.Poller` scheduler: immediate first pass then ticker, sequential per-repo, log-and-continue on error, `nil` on ctx cancel. - **T3** — serve boot wiring behind an all-optional `ForgeConfig`, the token resolved (behind a short TTL) via the DL-052 `server_only` secret path, poller run under the existing serve errgroup. Design layer only — no implementation ships here. Red-teamed by design-critic; its findings are folded (the `since`/ETag-efficacy fix, 304 page-chain caching, resolve-cost TTL, the added test cases) and the PR-C double-fetch consequence + a durable-cursor Option 1.5 are promoted into OQ-A. Freeze is blocked on **OQ-A** — the relationship to the frozen DL-053 subscription model (distinct board-poll loop vs building on the DL-053 machinery). Parked for Matt; OQ-B (live-demo content) is a non-load-bearing deferral. Ledger-impact: none in this PR — Draft record; the DECISIONS.md ledger delta lands at freeze, once Matt rules OQ-A and the record goes Active. Refs SEA-1810 Co-authored-by: Matt Wilkinson <matt@sealedsecurity.com>
…invariant, 403 discriminator, no-clobber test Review (skill://review, 1 round, 1 high + 2 medium + 2 low) folded: - HIGH: the since-watermark added in the design-critic pass advanced on fetch success, but Ingester.Ingest sinks-then-stops (ingest.go:60-62) and forge.Issue carries no timestamp (provider.go:37-53), so an in-client watermark could not gate on sink outcome — a mid-pass sink failure stranded every issue below it, voiding the re-poll idempotency invariant. v1 now uses ETag/304 only; a correct durable incremental cursor is homed in OQ-A Option 1.5. Idempotency stated as a load-bearing Global Constraint. - MEDIUM: single-rule 403 disambiguation (rate-limit vs bad-creds vs permission) stated explicitly in Approach step 3, reconciled with step 7. - MEDIUM: T3 no-clobber pgtest respecified to set a human State baseline via store.SetIssueState first, so it can go red (was vacuous over a zero row). - LOW: trimmed the DL-129 reopen over-claim; fixed garbled poller prose. Co-authored-by: Matt Wilkinson <matt@sealedsecurity.com>
…ed-team Matt ruled OQ-A = Option 2: build the forge-poll driver ON the DL-053 forge-subscription machinery from day one, board as a distinguished subscriber. Re-cut T1-T4 around a durable sink-gated forge_list_cursors fetch cursor; DL-053/DL-129 added as frozen constraints. Folds the design-critic red-team (0 high / 6 medium / 3 low): M1 boundary probe for the stored-has_next walk gap; M3 forge.Issue.UpdatedAt widened at T1 for the DL-129 recency guard; M4 TokenSource invalidation seam; M6 OQ-C owns the LIST-cursor class; L1 multi-replica precondition clause; L2 advanced_at rename on the list cursor; L3 dual startup error text. OQ-D reframed to two sub-forks (M2+M5): land the two spec-d DL-053 tables now vs defer, and the provider-domain CHECK. Refs SEA-1810. Co-authored-by: Matt Wilkinson <matt@sealedsecurity.com>
…-161/162/163) Fold the 10 design-critic findings into the SEA-1810 forge-poll driver record (sound-with-folds verdict) and flip it to Active with its ledger delta, making the design PR merge-ready: - F1: seed reconcile ON CONFLICT DO NOTHING (bootstrap-only insert; the table is authoritative after first insert; a soft-disabled row stays disabled across restarts), DO-UPDATE weighed + rejected in Alternatives. - F2: one boot slog.Warn when polling is disabled but enabled target rows exist for the bound (provider, host); never fail-fast. - F3: justify the deferred issues-CHECK widening (OQ-E) vs the Go ForgeProviderLinear constant closed now. - F4/F5: GitHub repo-string lowercase normalization at the seed boundary; seed rows keyed under --forge-host (host change abandons prior rows). - F6: rename the driver store interface CursorStore -> PollStore. - F7/F8/F10: freeze-delta names both ledger + ownership annotation; forge_artifact_cursors GC deferred with its writers; forge_repo_subscriptions gains updated_at touched on every upsert/enable-flip. - F9: T4 tests assert the seed-visible-before-first-pass ordering directly. Ledger: append DL-161 (the poll driver + forge_list_cursors FETCH-cursor), DL-162 (repo targets as forge_repo_subscriptions table rows, --forge-repos becomes a boot seed), DL-163 (0015 lands four tables, CHECK IN (1,2,3,4)). Co-authored-by: Matt Wilkinson <matt@sealedsecurity.com>
…er DL-053 annotation (SEA-1883), citation drifts, empty-probe row Review round (design-critic + review agent) surfaced 2 medium + 2 low, all documentation/ledger-cross-reference; the poll/cursor/seed/budget model passed every soundness lens. - M1: correct the stale 'Draft'/'Ledger-impact: none' prose — the record is Active and its ledger delta (DL-161/162/163) rides this PR. - M2: the DL-053 ownership-layer forward-annotation is deferred to SEA-1883 (editing a frozen record needs Matt's call); stated explicitly in OQ-C, and the mis-cited target line :983 corrected to :978. - L1: DECISIONS.md citations drifted +3 by this PR's own DL rows — :173->:176 (DL-129), :126->:129 (DL-051). - L2: an empty boundary-probe page writes no cursor row (skip the upsert on a zero-issue page), so the probe never persists an unprunable empty tail row. Co-authored-by: Matt Wilkinson <matt@sealedsecurity.com>
seal-agent
force-pushed
the
compass-server-1810-forge-poll-driver-design
branch
from
August 7, 2026 20:41
d9e3860 to
36a3fc4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Design record for the compass-server forge-poll driver: a ctx-cancellable board-ingestion poll loop that drives the existing
internal/ingestlibrary against a real GitHub forge so tracker issues appear live in the server projection, built ON the DL-053 forge-subscription machinery (Matt ruled OQ-A = Option 2).Merge-ready —
Status: Active, all load-bearing Open Questions resolved, ledger delta in this PR.Four tasks:
net/httpGitHub read client (OQ-6 frozen: no new dep):ListIssuesPage/HasNextover RFC-5988Link,If-None-Match/304 conditional requests,per_page=100, a403rate-vs-dead-token discriminator (retry-after/x-ratelimit-remaining), and anx-ratelimit-*budget gate that fails fast rather than sleeping.0015_forge_subscriptions.sql(four tables, DDL below) +store/forge_cursors.golist-cursor + repo-subscription methods +store.ForgeProviderLinear = 4; pgtest-covered.ingest.Driver: the DL-053 conditional-poll driver, per-pass target enumeration viaPollStore.ListEnabledRepos, sink-gated per-page cursor advance,nilon ctx cancel.ForgeConfig(SeedRepos+Poll), the boot seed reconcile, the DL-052server_onlytoken behind a short TTL, driver under the existing serve errgroup.Resolved Open Questions (Matt)
forge_repo_subscriptionsrows (dynamically add/removable), not config; DL-053'sforge_subscriptionslands RENAMEDagent_forge_subscriptions.0015, every provider CHECK admits the full declared enumIN (1, 2, 3, 4).issues-CHECK prerequisite) are explicit non-load-bearing deferrals.Red-team (design-critic)
Second red-team over the table/seed re-cut: sound-with-folds, no high, no needs-rework, no Option-1 vestige. All 10 findings folded (F1 seed
ON CONFLICT DO NOTHING; F2 polling-disabled boot Warn; F6CursorStore→PollStore; F3–F5, F7–F10). Two carry a small author-resolved call flagged here for your ratification at review, rather than a separate round-trip:ON CONFLICT DO NOTHING(bootstrap-only insert; the table is authoritative after first insert; a soft-disabled row stays disabled across restarts). RejectsDO UPDATE SET enabled = TRUE, which would silently re-enable an incident-disabled repo on a routine restart. Weighed in Alternatives (h); flagged in OQ-C.compass-server-ownership-layer/design.md:983) is not applied here — that record is frozen Active, and I don't rewrite a frozen record without your call. Say the word and it lands as its own delta.Ledger delta (in this PR)
forge_list_cursorsFETCH-cursor model.forge_repo_subscriptionstable rows;--forge-reposbecomes a declarative boot seed.0015lands four tables unconditionally; provider CHECKIN (1, 2, 3, 4).Ledger-impact: DL-161, DL-162, DL-163 appended (Storage section).
Refs SEA-1810
Co-authored-by: Matt Wilkinson matt@sealedsecurity.com