What happened
Opened the app and found three sessions that should be active had been moved to
completed:
- citadel-134-sensor-framework (citadel#134 — still OPEN)
- crow-66-launch-announcement (crow#66 — issue no longer resolvable)
- citadel-98-cost-tracking-pricing (citadel#98 — still OPEN)
All three share updated_at: 2026-04-16T17:16:26Z, i.e. one IssueTracker run
flipped them together. A fourth session (ontology-prototype-1) was untouched.
Why I think it's the auto-completer
autoCompleteFinishedSessions in Sources/Crow/App/IssueTracker.swift:1013
works by set-difference: any candidate session whose ticketURL isn't in the
fetched openIssues list is treated as finished. Two branches are unsafe when
the fetch is partial:
- Line 1042-1043: PR is missing from
viewerPRs → falls back to
"absence == done" and marks completed.
- Line 1048-1051: no PR link + issue not in
openIssues → marks completed.
Neither branch distinguishes "issue is closed" from "fetch returned nothing /
returned a partial page / was rate-limited." Issue #173 (closed same day,
2026-04-16) was about GraphQL rate-limit exhaustion here, so a degraded
response on that date is plausible.
Repro hypothesis
- Have >=1 active session with an OPEN assigned GitHub issue.
- Force
fetchOpenIssues / viewerPRs to return an empty or truncated list
(rate-limit, network error swallowed, pagination cutoff, scope change).
autoCompleteFinishedSessions runs → every active session with a ticketURL
not in the (bad) list is marked completed.
Suggested fix directions
- Treat an empty / errored
openIssues result as a no-op, not as "everything
finished." Propagate fetch success/failure into the auto-complete step.
- Require positive evidence of closure before completing: either the issue is
present in openIssues and marked CLOSED, or a dedicated per-ticket state
query confirms it. Remove the absence == done fallback.
- Add a floor check: if the fetched open-issue count is 0 while we have N
candidate sessions with tickets, skip auto-complete and log a warning.
- Add a unit test for
autoCompleteFinishedSessions covering the
partial-fetch case.
Recovery
Reset the three sessions:
crow set-status --session 27664893-E932-4AB3-97D6-7C8A6F997829 active
crow set-status --session EC8B5E7F-25A6-4E9A-A321-5087B01D32CA active
crow set-status --session 1D802104-9921-49EA-81DC-2D42F4AD2C6D active
Related: #173.
What happened
Opened the app and found three sessions that should be active had been moved to
completed:All three share
updated_at: 2026-04-16T17:16:26Z, i.e. one IssueTracker runflipped them together. A fourth session (ontology-prototype-1) was untouched.
Why I think it's the auto-completer
autoCompleteFinishedSessionsinSources/Crow/App/IssueTracker.swift:1013works by set-difference: any candidate session whose ticketURL isn't in the
fetched
openIssueslist is treated as finished. Two branches are unsafe whenthe fetch is partial:
viewerPRs→ falls back to"absence == done" and marks completed.
openIssues→ marks completed.Neither branch distinguishes "issue is closed" from "fetch returned nothing /
returned a partial page / was rate-limited." Issue #173 (closed same day,
2026-04-16) was about GraphQL rate-limit exhaustion here, so a degraded
response on that date is plausible.
Repro hypothesis
fetchOpenIssues/ viewerPRs to return an empty or truncated list(rate-limit, network error swallowed, pagination cutoff, scope change).
autoCompleteFinishedSessionsruns → every active session with a ticketURLnot in the (bad) list is marked completed.
Suggested fix directions
openIssuesresult as a no-op, not as "everythingfinished." Propagate fetch success/failure into the auto-complete step.
present in
openIssuesand marked CLOSED, or a dedicated per-ticket statequery confirms it. Remove the
absence == donefallback.candidate sessions with tickets, skip auto-complete and log a warning.
autoCompleteFinishedSessionscovering thepartial-fetch case.
Recovery
Reset the three sessions:
Related: #173.