feat(bg): B-0442.2 — merged-PR fetch via gh CLI (10 tests pass)#3014
Conversation
…tion)
Slice 2 of B-0442. The missed-substrate cascade detector now fetches
recent merged PRs via `gh pr list --state merged --search "merged:>{iso}"`
and reports the candidate set within a configurable lookback window
(default 30min).
Key design choices:
- spawnSync (execFile-style, no shell) + sonarjs lint suppression
with rationale (matches B-0440.2 pattern)
- Adapter pattern (now + fetchRecentMergedPRs) for deterministic tests
- gh JSON output parsed with type-guard filter to reject malformed
entries
- New --lookback-min flag (default 30min)
- cascadesDetected stays 0 in slice 2 — slice 3 will fetch each
branch's HEAD and compare against the squash content to surface
actual cascades
Test results: 10 pass / 0 fail / 20 expect() calls (slice 1 had 13/21).
Future slices:
- Slice 3: per-merged-PR branch-HEAD fetch + squash content compare
(detects actual drift)
- Slice 4: cascade-detection payload + bus publish (requires B-0400
schema extension for missed-substrate-cascade topic)
- Slice 5: optional auto-recovery-PR opening (gated)
- Slice 6: cron registration + integration tests
Composes with:
- B-0442 + B-0442.1 (PR #3008 — skeleton this extends)
- B-0440.2 + B-0441.2 (PR #3011 + #3012 — companion services with
real detection)
- B-0400 (bus protocol — slice 4)
- PR #2997 (the Otto-section-missed-PR-2980-by-3min recovery — the
canonical operational example this service mechanizes)
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6229a2c836
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR advances the B-0442 missed-substrate detector from a slice-1 skeleton to slice 2 by adding a gh pr list --state merged-based fetch of recently merged PRs and reporting the size of the candidate window via the detector’s JSON output.
Changes:
- Add
ghCLI integration (viaspawnSync) to fetch merged PRs within a configurable lookback window. - Extend config/CLI parsing with
--lookback-min(default 30) and includecandidatesScannedin the poll result. - Update tests to use injected adapters for deterministic
pollOncebehavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tools/bg/missed-substrate-detector.ts | Implements merged-PR fetching via gh, adds lookback config/flag, and expands poll result shape. |
| tools/bg/missed-substrate-detector.test.ts | Updates tests for slice 2, exercising adapter-injected pollOnce and new CLI flag parsing. |
Comments suppressed due to low confidence (2)
tools/bg/missed-substrate-detector.ts:75
- The
ghcall drops stderr and on any failure (non-zero exit, missing stdout, JSON parse/type-guard failure) returns an empty list. That makespollOncereport "no merged PRs" even whenghis missing/auth-failed/output-changed. Please surface fetch failures distinctly (e.g., return an error from the adapter and reflect it innote, and/or capture stderr) so operational issues aren’t misreported as an empty candidate set.
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] },
);
if (result.status !== 0 || !result.stdout) return [];
try {
tools/bg/missed-substrate-detector.ts:110
notetreatsmerged.length === 0as "no merged PRs", butmergedcan also be empty due toghfailures/parse errors. Once fetch failures are surfaced, consider distinguishing these cases in the note so the JSON output is diagnostic in cron/daemon logs.
note: merged.length === 0
? `no merged PRs in last ${config.lookbackMin}min lookback window`
: `${merged.length} merged PR(s) in last ${config.lookbackMin}min; slice 3 will compare each branch HEAD against squash content`,
};
…ion, fetchLimit configurable, header accuracy)
1. P1: gh-failure no longer silently treated as zero merged PRs.
FetchResult is now a discriminated union of {status: "ok", prs,
truncated} | {status: "gh-error", reason}. PollResult.fetchStatus
surfaces the distinction in the note.
2. P2: Hard-coded 50-item cap replaced with configurable fetchLimit
(default 100, settable via --fetch-limit). When results hit the
cap, fetchTruncated:true is set + warning included in note so
caller can raise --fetch-limit or shorten --lookback-min.
3. Header comment corrected: removed inaccurate claim that the
slice checks branch-exists-in-remote-tracking-refs. Slice 2 only
fetches merged-PR metadata; the actual branch HEAD comparison
lands in slice 3.
4. Duplicate of #2 (same 50-cap concern from a second reviewer
perspective) resolved by the same fix.
New tests:
- gh-error path surfaces fetchStatus + reason in note
- truncation path sets fetchTruncated + warning in note
- --fetch-limit positive-integer validation (rejects non-integer)
Test results: 13 pass / 0 fail / 34 expect() calls (was 10 / 20).
Co-Authored-By: Claude <noreply@anthropic.com>
…not real cascade detection) (#3023) Slice 4 of B-0442. Wires the bus-publish path for the missed-substrate cascade detector. Slice 3 (branch-HEAD vs squash-content compare) is NOT yet implemented; the detectCascade adapter is a stub that always returns null. SUBSTRATE-HONEST FRAMING (per Riven's P0 catch on the analogous B-0440 cascade — envelope 6c689634-...): This PR ships: - Bus-publish path (try/catch wrapped, structured publish-error surfacing, missed-substrate-cascade topic from B-0400) - Adapter abstraction (detectCascade injectable for tests; real slice-3 comparator plugs in later) - CascadeFinding payload schema (prNumber, branchName, missingCommits, urgency) - CLI flags (--no-publish, --agent, --to, --fetch-limit) This PR does NOT ship: - Real branch-vs-squash comparator (slice 3) - Auto-recovery-PR opening (slice 5) - Cron registration (slice 6) In production right now, this service WILL fetch merged PRs but WILL NOT detect any cascades (stub returns null). The reactive loop is wired but inert until slice 3 lands. Riven's P0 warning preserved: do NOT frame this as "missed- substrate cascade detection is operational." The framing is "bus- publish wiring complete; slice-3 detector stub awaiting real compare logic." Key design choices: - Adapter pattern (now / fetchRecentMergedPRs / detectCascade / publishCascade) for full test injectability - spawnSync (execFile-style) for gh CLI invocation - Canonical SENDER_IDS / AGENT_IDS reuse (Riven/Vera/Copilot cross-PR finding) - try/catch on publishCascade (daemon survives bus IO failures) Tests: 11 pass / 0 fail / 42 expect() calls. Composes with: - B-0442.2 (PR #3014 — merged-PR fetch this extends) - B-0440.4 (PR #3017 — same bus-publish pattern; first reactive loop closed) - B-0441.4 (PR #3020 — proactive companion; same try/catch pattern) - B-0400 schema extension (PR #3016 — missed-substrate-cascade topic) - Riven adversarial review (envelopes 6c689634 + e8174b34) Co-authored-by: Claude <noreply@anthropic.com>
Summary
Slice 2 of B-0442. The missed-substrate cascade detector now fetches recent merged PRs via `gh pr list --state merged --search "merged:>{iso}"` and reports the candidate set within a configurable lookback window (default 30min).
What lands
Tests
```
bun test
10 pass
0 fail
20 expect() calls
```
Three-service mechanization status
🤖 Generated with Claude Code