fix(forge): treat Linear's duplicate state as closed (RIG-3590) - #1069
Open
rigel-mintaka wants to merge 2 commits into
Open
fix(forge): treat Linear's duplicate state as closed (RIG-3590)#1069rigel-mintaka wants to merge 2 commits into
rigel-mintaka wants to merge 2 commits into
Conversation
|
Compass engineering docs preview: https://compass-forge-rig-3590-dupli.compass-eng-docs.pages.dev Deployed from |
…ory model (RIG-3590) Review found two comments asserting things I had not verified. The doc comment on linearClosedStateTypes claimed Linear files duplicate under Done. It does not: duplicate is its own system-managed terminal category, peer to completed, applied when an issue is marked a duplicate. The conclusion held but the reason was wrong, and it sat next to the frozen compass-forge-state-transition record whose default close rule targets completed only -- a reader believing the false premise had a reason to fold duplicate into that set, which that record rejects. The table test's comment promised enum-drift protection a hardcoded table cannot give. RIG-3590 was a KNOWN type left unhandled, so the promise installed false confidence exactly where the real failure mode lives. Also adds the missing fallback row: an unrecognised type maps to open. Mutation-proved unique -- a mutant that keeps all seven real verdicts and flips only the unknown case is caught by that row alone. Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka
marked this pull request as ready for review
September 11, 2026 03:16
mattwilkinsonn
approved these changes
Sep 11, 2026
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.
Fixes RIG-3590.
Problem
Linear's
WorkflowState.typeSDL enum has seven values:triage,backlog,unstarted,started,completed,canceled,duplicate.linearClosedStateTypesingo/internal/forge/linear.golisted two of them,so an issue closed as a duplicate resolved to the forge's open truth.
That slice feeds two consumers, which is what makes it more than a cosmetic
mis-label:
mapLinearState— the read mapping, so a fetched duplicate reportedopen.teamIssueFilter— the GraphQLIssueFilter, asninfor open andinforclosed, so the server's own open-issue query returned duplicates.
Net effect: an issue a human closed as a duplicate was re-served as live work.
The existing doc comment already enumerated
duplicateas a valid SDL type, sothe enum was known — only the closed set was short.
Fix
Add
duplicatetolinearClosedStateTypes. Both consumers read the one slice,so the mapping and the query stay consistent by construction.
Verification
Red first, on both surfaces, before the fix:
TestMapLinearStateCoversEverySDLType— table over all seven SDL types, notjust the branched ones, so a new Linear type arriving fails here.
TestTeamIssueFilterExcludesDuplicateFromOpen— asserts the query filter andthe read mapping agree. They share the slice, so a divergence between them is
the failure the mapping test alone cannot catch.
duplicatefrom the slice fails both.go/internal/forge/testdata/linear/list_issues.jsonupdated — its pinnedrequest body carries the
ninlist, so the behaviour change is visible in thegolden. Hand-edited because the
-updatecapture path is thelivegithubsuite's (it needs a real Linear call).
go build,go vet,go test ./internal/forge/... ./server/...all pass.moon ci(MOON_BASE=origin/main): 33 actions, 0 failed.No ledger rows touched.
One-time notification on deploy
Surfaced by review.
forge_artifact_cursors.snapshotpersists the observedstate, so a Linear issue already sitting in
duplicatehas"open"storedagainst it. On the first reconcile sweep after deploy,
detectArtifactseesfetched.State != prev.Stateand emits one correctiveSTATEnotification peraffected subscribed issue. The artifact did not change — the mapping did.
Self-healing, no migration or backfill: STATE delivery is at-least-once by
design and the delivered value is the correct one. Noted so nobody debugs the
burst on deploy day.
No stored row is made inconsistent:
store.Issue.ForgeStateis written onlythrough the board-ingest lane, and
buildBoardIngestLaneis GitHub-only byconstruction, so no Linear issue has ever reached that column.
No GitHub analogue
Checked. GitHub's issue
stateis already two-valued and passes throughverbatim; its
state_reason(which does have aduplicatevalue) is a reasonorthogonal to state — a GitHub issue closed as a duplicate already reports
closed.state_reasonappears nowhere in the Go tree. The asymmetry iscorrect: Linear is the only provider projecting a seven-value category enum
onto the forge's two-value domain.
Review
0 high, 2 medium, 5 low. Both mediums were comments of mine asserting
unverified claims, fixed in the second commit: the doc comment said Linear
files duplicate under Done (it does not — duplicate is its own system-managed
terminal category, peer to completed), and the table test's comment promised
enum-drift protection a hardcoded table cannot provide. Also took the free low:
a fallback row pinning that an unrecognised type maps to open, mutation-proved
unique against a mutant that keeps all seven real verdicts and flips only the
unknown case.
Two lows surfaced for your ruling rather than fixed here:
compass-forge-state-transitionrecord quotes the old two-elementslice as a literal code block, now stale. T3's behaviour is unaffected (its
default rule targets
completedexplicitly, never "everything else"), so thequote misgrounds a reader without misrouting the implementation. Whether a
frozen record may be amended for code drift is your convention call.
mapLinearStatestill fails open on an unrecognised type — the same shapethat produced this bug. Making enum drift fail loud (or default closed) is a
behaviour change needing a ruling, so it is not in this PR.