Skip to content

Replace the patchwork core with a round state machine, bot dialects, and a pure decision engine#30

Merged
kristofferR merged 20 commits into
mainfrom
refactor/extract-dialect
Jul 18, 2026
Merged

Replace the patchwork core with a round state machine, bot dialects, and a pure decision engine#30
kristofferR merged 20 commits into
mainfrom
refactor/extract-dialect

Conversation

@kristofferR

@kristofferR kristofferR commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Why

On 2026-07-16 crq posted @coderabbitai review ~19 times in one day on kristofferR/ha-adjustable-bed#448 (10+ on a single head), and the same pattern hit another repo. The daemon log showed the mechanism: a rate-limited requeue deleted the "already fired at this head" marker, so every 60s pass re-concluded "no bot review at head" and fired again. That was one instance of a general disease after ~40 consecutive one-off fixes: fire-safety emerged from six scattered guards, "is the round done?" had two divergent implementations, state was five parallel maps, and every bot-message heuristic was an inline string match.

What

A phased rewrite of the core around two ideas (see AGENTS.md for the full map):

  • One never-deleted Round per PR (internal/state, schema v3): phases queued → reserved → fired → reviewing → awaiting_retry/completed/abandoned, with transitions as methods that error on illegal edges. Forgetting "already requested at this head" now requires destroying a record no transition destroys; the rate-limit cooldown (RetryAt) lives on the round and survives every requeue.
  • observe → decide → apply: internal/dialect turns bot text into typed events (all CodeRabbit/Codex wording lives there, pinned by a golden corpus in testdata/); internal/engine is the pure decision core — DecideFire (single fire owner), Progress, Completion (the one round-done answer) — shared by the daemon and crq loop; internal/crq orchestrates with a single observe() fetch point and one effects boundary (dry-run = decide without apply, the mirrored dry-run branch is gone). internal/gh is the transport with GitHub REST quota renamed Throttle so it can never again be conflated with CodeRabbit's account quota.

internal/crq/replay_test.go re-enacts the incident end-to-end with an injected clock: rate-limit bounce over a full 40-minute window fires exactly once; the instant "Review finished" ack cannot converge a first review; the in-progress summary releases the slot without closing the round; force-push supersedes without stale command adoption; the full day-sequence produces exactly 3 commands where v2 produced ~19.

Notes

  • External contract frozen: CLI verbs, loop exit codes 0/10/2, findings JSON, stdout-JSON-only, drain-first/hold-the-head. State schema is v3 on a new default ref crq-state-v3 with loud auto-reinit on unknown payloads — no migration, crq is pre-release.
  • Deliberate divergences from v2: retries always carry a cooldown (v2's timeout requeue had none); the in-progress/failed summary states now gate the daemon path too; a reviewing round's wall-clock deadline no longer daemon-retries (the loop owns its timeout); enqueue supersedes on head change immediately.
  • Local CodeRabbit preflight ran clean of regressions; its four findings were verified: one false positive (pre-move layout), two intentional v2-identical behaviors, and two pre-existing-in-v2 hardening ideas (throttle-aware force-push cutoff, calibration probe cutoff) worth follow-ups rather than blockers.
  • Net −1.2k lines. After merge: rebuild, launchctl bootout + bootstrap the daemon, watch one autoreview pass, run one real crq loop to convergence.

Summary by CodeRabbit

  • New Features
    • Rollout of the v3 round/slot scheduling model with retries, force-push superseding, and stronger command deduplication.
    • Improved CodeRabbit/Codex handling, including configurable Codex trigger (CRQ_CODEX_CMD) and richer dialect parsing for review findings.
    • Updated the queue dashboard and persisted state tracking.
  • Bug Fixes
    • Throttling-based backoff now matches edited “available in” notices; reduced premature/duplicate firings (incl. instant acknowledgements and stale signals).
    • CLI debug refresh now reports the account value instead of blocked state.
  • Documentation
    • Updated architecture/config docs and Codex loop guidance.
  • Tests
    • Expanded golden fixture + deterministic incident replay coverage, plus engine/state suite updates.

Added in review: Codex firing + auto-detection + dynamic gating

crq now drives Codex too (2c4aefb): with Codex in CRQ_REQUIRED_BOTS it posts CRQ_CODEX_CMD (default @codex review) in the same fire step — never when it detects Codex auto-review on the PR (any Codex review no command preceded). A Codex that joins a round uninvited gates that round's convergence dynamically; its usage-limit notice releases the dynamic gate. End-to-end scenarios in codex_replay_test.go cover all three modes.

All CodeRabbit/Codex message heuristics — completion phrases, rate-limit
notices, in-progress/failure summaries, findings markup parsers, SHA
conventions — move to a dependency-free internal/dialect package. Config
markers are injected as CodeRabbit struct fields instead of read from cfg
inside classifiers.

A golden corpus (internal/dialect/testdata) now pins one file per known
message format; existing crq call sites and tests are kept stable through
thin aliases in dialect_aliases.go that die with the engine extraction.
github.go and its tests move verbatim to a gh package. The GitHub REST
quota vocabulary is renamed to Throttle (IsThrottled/ThrottleWait) so it
can no longer be confused with CodeRabbit's account-quota rate limits,
which stay a dialect concern. The 2500-comment cap check — calibration
policy, not transport — hoists into crq, and the User-Agent version is
injected at init since gh cannot import crq.

crq keeps compiling unchanged through type/func aliases in gh_aliases.go,
deleted once the engine extraction rewrites the callers.
internal/state is schema v3: one Round per PR with owned transitions
(queued/reserved/fired/reviewing/awaiting_retry/completed/abandoned), a
global FireSlot, and AccountQuota for CodeRabbit's quota. Rounds are
never deleted — only transitioned or archived on supersede — so the #448
failure (a requeue deleting the fired marker, re-firing the same head
every pass) is structurally unrepresentable, and illegal transitions are
errors.

internal/engine is the pure decision core both the daemon and crq loop
will drive: DecideFire (the single fire owner), Progress (round
transitions; ports inflightStatus plus requeueInflight's same-comment
rate-limit window logic), and Completion (the ONE round-done answer;
ports reviewedByForRound, the completion-reply pairing fold with its
prior-review/nonterminal/failed gates, and codexInactiveOrThumbed).
Deliberate fixes over v2: the in-progress/failed summary states now gate
the daemon path too, and every retry carries a RetryAt cooldown.

dialect gains Classifier/BotEvent: issue comments become typed events at
the observation boundary, so the engine never touches message text.
Old v2 code is untouched and still runs; cutover comes next.
Stage 4a of the refactor: persistence and the daemon/pump paths now run on
the schema-v3 round state machine instead of the five parallel v2 maps.

- Move the git-ref CAS store and dashboard rendering into internal/state
  (store.go, dashboard.go), operating on state.State. A payload whose schema
  version isn't 3 (or won't parse) is logged loudly and auto-reinitialized —
  no migration, crq is pre-release. The default ref is now crq-state-v3, so a
  stale binary writing the old ref is harmless.
- Rewrite Pump as observe -> engine -> apply: observe.go is the one place that
  fetches a PR's GitHub facts (head/open, reviews, classified comment events,
  reactions, adoptable commands with the v2 adoption cutoffs) and builds an
  engine.Observation. Pump progresses the slot round via engine.Progress,
  sweeps one reviewing round toward completion, then fires the next eligible
  round via engine.DecideFire. The mirrored dry-run branch is gone: apply()
  simply writes and posts nothing under DryRun. PumpResult action strings are
  preserved.
- Rewrite Enqueue/enqueueBatch/Cancel/Status and autoreview needsReview on
  rounds (NewRound/Supersede/EndRound; needsReview skips only when a round
  already tracks the current head, any phase).
- Port the calibration/quota code to AccountQuota (renamed from Blocked).
- Keep feedback.go/Loop/Wait compiling on v3 with minimal shims (waitView,
  roundAnchor, firedMarker, accountBlockedUntil, wait-lifecycle helpers over
  round transitions); stage 4b rewrites them.
- Add a Round.Dedupe transition (bot already reviewed the head) and drop the
  reviewing-round wall-clock deadline retry from engine.Progress (the loop
  owns its own wait timeout; the daemon waits for real bot evidence).
- Migrate service_test.go and state_test.go to v3; delete tests that only
  exercised deleted v2 internals (requeueInflight, inflightStatus, the wait
  sweep/prune, FiredMax trimming, cooldowns) and add the missing edge cases as
  engine/state tests.
Completes the flag-day cutover: Loop, Wait, and Feedback now run on the
v3 round state machine and the pure engine, and the transitional v2 shim
and alias layers are gone.

- Feedback keeps its findings-extraction pipeline (review bodies, threads,
  review comments, issue comments) but derives convergence from
  engine.Completion driven off observe(). observe() now carries the raw
  reviews/comments Feedback parses, so one fetch serves both the daemon
  (DecideFire/Progress) and the loop (Completion + findings) with no second
  path. ReviewedBy comes from CompletionStatus.
- Loop/waitToFire and Wait run on rounds directly: the wait IS the round
  (fired/reviewing with a WaitDeadline), drain-first uses
  engine.BlockingFindings, Wait uses engine.FindingsOnHead. Exit codes
  0/10/2 and the internal 3 dedupe are unchanged; throttle riding, the
  >=60s pump cadence, and account-block deadline extension are preserved.
- Deleted the completion-fallback chain and its Service classifier
  forwarders (feedbackCompletionContext, applyCompletionReplyFallback,
  completionReplyForFiredCommand, reviewCommandReplies, codexInactiveOrThumbed,
  codexThumbedUp, hasNonterminal/hasFailedReviewState, is{RateLimited,
  ReviewsPaused,ReviewAlreadyDone,CompletionReply,AutoReply}) plus the
  FeedbackWait shim struct and waitView/roundAnchor/ensureFeedbackWait/
  extendFeedbackWaitDeadline/clearFeedbackWait. The adoption completion-reply
  guard moved to engine.CommandHasCompletionReply.
- Deleted the dialect and gh alias layers; call sites use dialect.X/ghapi.X
  directly. The crq-local Finding/FeedbackReport JSON output is unchanged.
- Vocabulary: "rate limit" as literal text now lives only in gh (Throttle)
  and dialect; engine/state/crq use Throttle (GitHub) or AccountQuota/
  "account blocked" (CodeRabbit). The requeue reason="rate limited" log
  string is preserved via dialect.ReasonRateLimited.
- Migrated the Loop/Feedback/Wait tests onto the native round API; ported
  the markReviewed suffix and completion-reply-adoption edges to
  engine_test.go. Added an AGENTS.md architecture map (CLAUDE.md symlink).
Re-enact the 2026-07-16 ha-adjustable-bed#448 incident (crq posted
`@coderabbitai review` ~19x in one day, 10+ on a single head) end to end
against the fakeGitHub + MemoryStore harness, proving the v3 round-state
architecture makes that spam class structurally impossible.

Inject a controllable clock into Service (s.clock) for the scheduling
decisions in the pump/enqueue/sweep/wait paths, and let the fake GitHub
timestamp posted comments off the same clock, so a whole day replays
deterministically with no sleeps and no wall-clock dependence. Production
leaves the clock nil and reads time.Now; logging/jitter uses are untouched.

Five scenarios, each asserting at most one command per (head, retry window):
- a rate-limit bounce (40m window, comment edited in place every few passes)
  fires once, parks at a fixed RetryAt for the whole window, then fires one
  retry — never more;
- an instant "Review finished" ack on a first-ever command keeps the round
  reviewing (not converged, not completed, no double-fire) and the real
  review's findings surface via Loop (exit 10);
- an in-progress top summary releases the fire slot (a second PR fires) while
  the round stays open and never re-fires;
- a force-push mid-round supersedes the round and fires a fresh command for
  the new head without adopting the stale one;
- the full incident sequence (fire, 15m-fallback rate limit, retry, parseable
  40m rate limit, head move mid-window, block-gated refire, real review)
  posts exactly three commands and completes.

No production bug surfaced: the architecture already holds the invariant, and
the suite pins it against regression.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR introduces CRQ v3: dialect-based bot parsing, persistent round state, pure fire/completion decisions, coordinated orchestration, Codex gating, observation-backed feedback, and deterministic replay coverage.

Changes

CRQ v3 queue architecture

Layer / File(s) Summary
Bot dialect and finding extraction
internal/dialect/*, internal/dialect/testdata/*
Adds CodeRabbit and Codex classifiers, quota parsers, finding models, reply-verdict classifiers, normalization helpers, golden tests, and message fixtures.
Round state and persistence
internal/state/*, internal/crq/state.go
Adds schema v3 rounds, lifecycle transitions, fire-slot handling, CAS-backed Git storage, memory storage, archives, and dashboard rendering.
Pure decisions and Codex gating
internal/engine/*
Adds observation, fire, progress, completion, finding-convergence, and Codex participation logic with focused tests.
Transport and observation wiring
internal/gh/*, internal/crq/observe.go, internal/crq/config.go, internal/crq/init.go, cmd/crq/main.go
Exports GitHub transport types and throttling APIs, centralizes observation, adds Codex configuration, and rewires initialization.
Queue orchestration and feedback
internal/crq/service.go, internal/crq/auto.go, internal/crq/feedback.go, internal/crq/*_test.go
Reworks enqueueing, pumping, quota calibration, command adoption, firing, waiting, convergence, finding deduplication, and replay validation.
Architecture documentation
AGENTS.md, CLAUDE.md, README.md, llms.txt
Documents CRQ invariants, Codex firing and gating, configuration, and extension procedures.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHub
  participant Service
  participant Observation
  participant Engine
  participant StateStore
  GitHub->>Observation: fetch pull, reviews, comments, and reactions
  Observation->>Engine: build normalized observation
  Engine->>Service: return fire or progress decision
  Service->>StateStore: apply round transition with CAS
  Service->>GitHub: post or adopt review commands
  GitHub->>Observation: expose bot events and findings
  Observation->>Engine: evaluate completion
  Engine->>Service: return convergence transition
  Service->>StateStore: complete round and release slot
Loading

Possibly related PRs

Poem

A bunny watched the rounds take flight,
With Codex hopping left and right.
Slots stayed calm, findings aligned,
Old heads slept while new ones shined.
Through every retry, states held true—
A carrot-powered queue came through.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.28% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing the core with a round state machine, dialect layer, and pure engine.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/extract-dialect

Comment @coderabbitai help to get the list of available commands.

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
internal/engine/engine_test.go (1)

46-369: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Table-test every engine rule and add the missing finding cases.

The current one-off scenarios do not lock down guard precedence, and BlockingFindings, FindingsOnHead, and Converged have no direct coverage. Add table rows for fire ordering, progress/completion variants, SHA lengths, and finding convergence.

As per coding guidelines, every engine rule—including fire, progress, completion, finding, and convergence behavior—must be table-tested.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/engine/engine_test.go` around lines 46 - 369, The engine tests
currently use one-off cases and omit direct coverage for finding and convergence
helpers. Convert the rule coverage around DecideFire, Progress, Completion,
BlockingFindings, FindingsOnHead, and Converged into table-driven tests, adding
rows for fire-guard precedence, progress/completion variants, valid and invalid
SHA lengths, and finding-based convergence outcomes while preserving each
existing expected behavior.

Source: Coding guidelines

internal/crq/feedback.go (3)

353-365: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep the round reviewing when findings arrive before required reviewers finish.

Line 360 completes the round unconditionally, even when allReviewed is false. This prematurely creates a completed dedupe marker and stops daemon progress for the pending reviewer.

-			s.completeWaitRound(ctx, repo, pr, head)
+			if allReviewed(report.ReviewedBy) {
+				s.completeWaitRound(ctx, repo, pr, head)
+			}
 			return report, 10, nil

As per coding guidelines, completion must be centralized in Completion, and rounds stay in progress until completion is established.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/crq/feedback.go` around lines 353 - 365, The findings path in the
feedback round currently calls completeWaitRound before all required reviewers
have finished. Update the surrounding report handling to leave the round in
progress when allReviewed(report.ReviewedBy) is false, and only complete it once
completion is established through the centralized Completion flow; preserve the
existing status, reason, and return behavior.

Source: Coding guidelines


882-912: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Move finding deduplication and suppression out of orchestration.

dedupeFindings decides which findings remain actionable and therefore directly affects convergence. Put normalization/identity in dialect and convergence-oriented suppression/deduplication in engine; crq should only wire the result.

As per coding guidelines, internal/crq is orchestration-only and finding convergence logic belongs in internal/engine.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/crq/feedback.go` around lines 882 - 912, Move dedupeFindings out of
internal/crq: put finding normalization and identity generation in the dialect
layer, and move actionable filtering plus structured-location and prompt
suppression/deduplication into the engine layer. Update the CRQ orchestration to
call the engine convergence helper and only wire its result, preserving the
current suppression behavior and generated finding IDs.

Source: Coding guidelines


124-145: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Move all PR-state reads into observe.go.

Feedback combines the observation with later reviewThreads, review-comment, and commit reads. Completion and findings can therefore be derived from different GitHub snapshots, causing inconsistent convergence decisions.

Extend the raw observation with threads, fallback review comments, and the head commit timestamp, then parse only that snapshot here.

As per coding guidelines, observe.go must be the single place querying GitHub for PR state and must build one observation per decision.

Also applies to: 179-185

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/crq/feedback.go` around lines 124 - 145, Move all GitHub PR-state
reads out of Feedback and into observe.go, including reviewThreads, fallback
review comments, and the head commit timestamp query. Extend the raw observation
to carry these values, build one observation per decision, and have Feedback
parse only that snapshot for findings and completion. Preserve throttle/error
propagation while ensuring all derived state comes from the same GitHub
snapshot.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@AGENTS.md`:
- Line 38: Update the state-diagram code fence in AGENTS.md to specify the text
language by using the text fence marker, preserving the diagram content
unchanged.

In `@internal/crq/observe.go`:
- Around line 34-43: Update Pump’s decision flow to remove the pullHead
preflight and call observe as the single PR-state read for each decision. Use
the resulting observation’s o.eng.Open and o.eng.Head values when evaluating
fire decisions, ensuring each decision builds exactly one observation and avoids
duplicate pull-state queries.
- Around line 187-188: Update adoptableCommands and the headForcePushCutoff
lookup so a GraphQL failure is not converted to a zero cutoff. Propagate the
lookup error or make adoptableCommands return no adoption candidates when the
force-push guard is unavailable; retain normal cutoff comparison only when the
lookup succeeds.

In `@internal/crq/replay_test.go`:
- Around line 67-84: Replace the duplicated bot-message literals in the replay
harness constants replayCompletionReply, replayInProgress, and
replayUnparseableRateLimit, plus the template used by replayFairUsage, with
content loaded from the corresponding files in the CodeRabbit dialect corpus.
Reuse the existing corpus-loading helpers or add the minimal loader needed so
replay and golden-classification tests share those files, while preserving the
current message variants and rate-limit parameterization.

In `@internal/crq/service_test.go`:
- Line 218: Remove the duplicate retryNoChangeStore type declaration in
internal/crq/service_test.go, keeping only one definition with its existing cfg
Config field so the redeclaration compile error is resolved.

In `@internal/crq/service.go`:
- Around line 461-475: Require each CAS mutation in abandonRound,
supersedeRound, and both reservation branches of fireRound to verify that the
currently stored round matches the observed round’s identity, including Seq and
Head, before mutating or reserving it. Return ErrNoChange when the round is
missing or either identity field differs, preventing stale decisions from
affecting a replacement round.
- Around line 733-745: Use the injectable s.clock() consistently for all
scheduling decisions in the calibration flow: replace wall-clock calls used by
quota freshness checks, wait-timeout calculations, feedback cadence, and
lastFeedbackCheck with the service clock, preserving UTC/elapsed-time semantics
as applicable. Update both freshness checks around the quota read and the
related logic near the wait and feedback handling paths.
- Around line 226-230: Update the quota refresh flow in Pump to avoid calling
RefreshQuota when DryRun is enabled; retain the already-loaded quota snapshot
for dry-run execution, while preserving the existing RefreshQuota behavior for
normal runs. Ensure dry-run processing remains entirely write-free.
- Around line 687-689: Update the detached retry context in the retry flow
around record to derive it with context.WithoutCancel(ctx) instead of
context.Background(), preserving parent context values while ignoring
cancellation before applying the 30-second timeout.

In `@internal/crq/state.go`:
- Around line 103-160: Move waitingHead, containsActive, and firedMarker from
internal/crq into methods on the shared state.State or state.Round types,
updating callers to use those methods. Move accountBlockedUntil into
internal/engine as the time-dependent blocking decision, preserving its
account-wide and awaiting_retry checks. Remove the corresponding internal/crq
helpers so orchestration remains limited to coordinating shared decisions.

In `@internal/dialect/coderabbit.go`:
- Around line 214-225: Move ParseReviewBodyFindings from coderabbit.go to
common.go, preserving its existing body normalization and aggregation behavior
across ParseDetailedReviewFindings, ParsePromptReviewFindings, and
ParseCodexReviewFindings. Keep CodeRabbit-specific recognition and parsing in
coderabbit.go, Codex-specific parsing in codex.go, and ensure bot-message
recognition remains in the parser appropriate to each dialect.

In `@internal/engine/completion.go`:
- Around line 154-183: Update commandReply and commandReplies to retain the
completion reply timestamp as replyAt, then use reply.replyAt—not
reply.commandAt—when calling stateSince in completionReplyForRound and
CommandHasCompletionReply. Ensure contradictory states are evaluated only from
the reply time onward, while preserving the existing before-reply and
after-reply behavior.

In `@internal/engine/findings.go`:
- Around line 14-20: Replace direct SHA prefix comparisons with
dialect.SHAPrefixMatch across BlockingFindings and the current-head matching
logic in internal/engine/findings.go (lines 14-20 and 27-34),
already-reviewed-head detection in internal/engine/fire.go (lines 69-74),
reviewMatchesRound in internal/engine/progress.go (lines 153-160), and
submitted-review and Codex review evidence in internal/engine/completion.go
(lines 49-55 and 109-120). Preserve each surrounding condition while routing all
SHA comparison semantics through the dialect helper; do not introduce SHA
vocabulary in engine packages.

In `@internal/state/dashboard.go`:
- Around line 121-128: Update the remaining-review display in the dashboard
rendering flow around the remaining variable to use the recorded
st.Account.Remaining value when it is populated, while preserving the
blocked-account message and existing via suffix behavior.
- Around line 4-5: Update hashString to use the SHA-256 implementation instead
of crypto/sha1, replacing the corresponding hash constructor while preserving
the existing hex encoding and State.DashboardSHA behavior. Remove the unused
SHA-1 import and add the SHA-256 import.

In `@internal/state/state.go`:
- Around line 349-369: Update EndRound so abandoned rounds remain in s.Rounds
and are not appended to s.Archive or deleted; retain the PhaseAbandoned
transition for ordinary close, cancel, and repair paths. Move the
archive-and-remove behavior into Supersede before creating the fresh queued
round, ensuring only a new head supersedes and archives the existing round.

---

Outside diff comments:
In `@internal/crq/feedback.go`:
- Around line 353-365: The findings path in the feedback round currently calls
completeWaitRound before all required reviewers have finished. Update the
surrounding report handling to leave the round in progress when
allReviewed(report.ReviewedBy) is false, and only complete it once completion is
established through the centralized Completion flow; preserve the existing
status, reason, and return behavior.
- Around line 882-912: Move dedupeFindings out of internal/crq: put finding
normalization and identity generation in the dialect layer, and move actionable
filtering plus structured-location and prompt suppression/deduplication into the
engine layer. Update the CRQ orchestration to call the engine convergence helper
and only wire its result, preserving the current suppression behavior and
generated finding IDs.
- Around line 124-145: Move all GitHub PR-state reads out of Feedback and into
observe.go, including reviewThreads, fallback review comments, and the head
commit timestamp query. Extend the raw observation to carry these values, build
one observation per decision, and have Feedback parse only that snapshot for
findings and completion. Preserve throttle/error propagation while ensuring all
derived state comes from the same GitHub snapshot.

In `@internal/engine/engine_test.go`:
- Around line 46-369: The engine tests currently use one-off cases and omit
direct coverage for finding and convergence helpers. Convert the rule coverage
around DecideFire, Progress, Completion, BlockingFindings, FindingsOnHead, and
Converged into table-driven tests, adding rows for fire-guard precedence,
progress/completion variants, valid and invalid SHA lengths, and finding-based
convergence outcomes while preserving each existing expected behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 13544132-df76-4dcf-912f-c15a70f4c9bf

📥 Commits

Reviewing files that changed from the base of the PR and between e2aa2f0 and 3edc67c.

📒 Files selected for processing (51)
  • AGENTS.md
  • CLAUDE.md
  • cmd/crq/main.go
  • internal/crq/auto.go
  • internal/crq/config.go
  • internal/crq/feedback.go
  • internal/crq/feedback_test.go
  • internal/crq/init.go
  • internal/crq/observe.go
  • internal/crq/preflight.go
  • internal/crq/replay_test.go
  • internal/crq/service.go
  • internal/crq/service_test.go
  • internal/crq/state.go
  • internal/crq/state_test.go
  • internal/crq/store.go
  • internal/dialect/coderabbit.go
  • internal/dialect/codex.go
  • internal/dialect/common.go
  • internal/dialect/event.go
  • internal/dialect/finding.go
  • internal/dialect/golden_test.go
  • internal/dialect/testdata/coderabbit/already-reviewed.md
  • internal/dialect/testdata/coderabbit/completion-reply.md
  • internal/dialect/testdata/coderabbit/findings-failed-to-post.md
  • internal/dialect/testdata/coderabbit/findings-nested-quotes.md
  • internal/dialect/testdata/coderabbit/findings-outside-diff.md
  • internal/dialect/testdata/coderabbit/findings-prompt-block.md
  • internal/dialect/testdata/coderabbit/no-actionable-comments.md
  • internal/dialect/testdata/coderabbit/rate-limit-bold-window.md
  • internal/dialect/testdata/coderabbit/rate-limit-fair-usage.md
  • internal/dialect/testdata/coderabbit/rate-limit-legacy.md
  • internal/dialect/testdata/coderabbit/review-failed.md
  • internal/dialect/testdata/coderabbit/review-in-progress.md
  • internal/dialect/testdata/coderabbit/reviews-paused.md
  • internal/dialect/testdata/codex/clean-summary-legacy.md
  • internal/dialect/testdata/codex/clean-summary-tada.md
  • internal/dialect/testdata/codex/findings-outside-diff.md
  • internal/dialect/testdata/codex/usage-limit.md
  • internal/engine/completion.go
  • internal/engine/engine.go
  • internal/engine/engine_test.go
  • internal/engine/findings.go
  • internal/engine/fire.go
  • internal/engine/progress.go
  • internal/gh/github.go
  • internal/gh/github_test.go
  • internal/state/dashboard.go
  • internal/state/state.go
  • internal/state/state_test.go
  • internal/state/store.go
💤 Files with no reviewable changes (1)
  • internal/crq/store.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (12)
internal/dialect/testdata/{coderabbit,codex}/**/*

📄 CodeRabbit inference engine (AGENTS.md)

For each new bot-message format, add one corpus file containing the real message under the appropriate dialect testdata directory.

Files:

  • internal/dialect/testdata/coderabbit/completion-reply.md
  • internal/dialect/testdata/codex/clean-summary-legacy.md
  • internal/dialect/testdata/coderabbit/findings-outside-diff.md
  • internal/dialect/testdata/coderabbit/rate-limit-legacy.md
  • internal/dialect/testdata/codex/usage-limit.md
  • internal/dialect/testdata/coderabbit/reviews-paused.md
  • internal/dialect/testdata/coderabbit/review-failed.md
  • internal/dialect/testdata/coderabbit/no-actionable-comments.md
  • internal/dialect/testdata/codex/clean-summary-tada.md
  • internal/dialect/testdata/codex/findings-outside-diff.md
  • internal/dialect/testdata/coderabbit/already-reviewed.md
  • internal/dialect/testdata/coderabbit/rate-limit-fair-usage.md
  • internal/dialect/testdata/coderabbit/review-in-progress.md
  • internal/dialect/testdata/coderabbit/findings-failed-to-post.md
  • internal/dialect/testdata/coderabbit/rate-limit-bold-window.md
  • internal/dialect/testdata/coderabbit/findings-nested-quotes.md
  • internal/dialect/testdata/coderabbit/findings-prompt-block.md
internal/{dialect,engine,crq,state,gh}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Preserve the Go dependency direction: dialect ← engine ← crq, state ← crq, and gh ← {state, crq}; do not introduce dependency cycles.

Files:

  • internal/engine/findings.go
  • internal/dialect/finding.go
  • internal/engine/progress.go
  • internal/gh/github_test.go
  • internal/dialect/golden_test.go
  • internal/engine/engine.go
  • internal/engine/fire.go
  • internal/crq/preflight.go
  • internal/state/dashboard.go
  • internal/dialect/event.go
  • internal/crq/observe.go
  • internal/crq/init.go
  • internal/dialect/codex.go
  • internal/engine/completion.go
  • internal/state/state_test.go
  • internal/crq/config.go
  • internal/state/state.go
  • internal/dialect/common.go
  • internal/state/store.go
  • internal/crq/replay_test.go
  • internal/crq/state_test.go
  • internal/dialect/coderabbit.go
  • internal/engine/engine_test.go
  • internal/crq/auto.go
  • internal/gh/github.go
  • internal/crq/state.go
  • internal/crq/service_test.go
  • internal/crq/feedback.go
  • internal/crq/feedback_test.go
  • internal/crq/service.go
internal/engine/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/engine/**/*.go: Keep internal/engine pure: pass now explicitly and do not use context or GitHub I/O. Centralize fire decisions in DecideFire, progress in Progress, completion in Completion, and finding convergence logic in the engine.
DecideFire must be the sole owner of review firing and consolidate guards in order: open, readable/current head, eligible phase, free slot, account quota, minimum interval, not already reviewed, then adopt/post.

Files:

  • internal/engine/findings.go
  • internal/engine/progress.go
  • internal/engine/engine.go
  • internal/engine/fire.go
  • internal/engine/completion.go
  • internal/engine/engine_test.go
internal/dialect/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Keep all bot-text knowledge, literal bot wording, classifiers, finding parsers, SHA/severity vocabulary, Finding, BotEvent, and Classifier definitions in internal/dialect; this package must have zero dependencies.

Files:

  • internal/dialect/finding.go
  • internal/dialect/golden_test.go
  • internal/dialect/event.go
  • internal/dialect/codex.go
  • internal/dialect/common.go
  • internal/dialect/coderabbit.go
internal/gh/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Represent GitHub REST quota using the Throttle concept and its ThrottleWait/IsThrottled APIs; only gh and dialect may use the literal text rate limit.

Files:

  • internal/gh/github_test.go
  • internal/gh/github.go
internal/dialect/golden_test.go

📄 CodeRabbit inference engine (AGENTS.md)

For each new bot-message format, add one row to TestGoldenClassification; that row defines the classification specification.

Files:

  • internal/dialect/golden_test.go
internal/crq/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/crq/**/*.go: Keep internal/crq orchestration-only: coordinate service operations and wire packages, but do not place pure decision logic or bot wording there.
Use the shared observe → decide → apply flow for daemon and loop behavior; no code outside DecideFire may post the review command.

Files:

  • internal/crq/preflight.go
  • internal/crq/observe.go
  • internal/crq/init.go
  • internal/crq/config.go
  • internal/crq/replay_test.go
  • internal/crq/state_test.go
  • internal/crq/auto.go
  • internal/crq/state.go
  • internal/crq/service_test.go
  • internal/crq/feedback.go
  • internal/crq/feedback_test.go
  • internal/crq/service.go
internal/state/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/state/**/*.go: Maintain persisted schema v3 with one Round per PR, one global FireSlot, the CodeRabbit AccountQuota, and an Archive ring.
Implement round transitions as methods on Round, reject illegal edges, never delete rounds, and archive them only when a new head supersedes them.
Use a global CAS-protected FireSlot to allow at most one concurrent fire fleet-wide; release the slot on bot acknowledgement while keeping the round in progress.

Files:

  • internal/state/dashboard.go
  • internal/state/state_test.go
  • internal/state/state.go
  • internal/state/store.go
internal/crq/observe.go

📄 CodeRabbit inference engine (AGENTS.md)

Make observe.go the single place that queries GitHub for PR state and reduces it to an engine.Observation; build the observation once per decision and retain raw reviews/comments for feedback parsing.

Files:

  • internal/crq/observe.go
internal/dialect/{coderabbit,codex,common}.go

📄 CodeRabbit inference engine (AGENTS.md)

When adding bot-message recognition, modify only the relevant classifier/parser in coderabbit.go, codex.go, or common.go; keep bot wording out of engine, state, and crq.

Files:

  • internal/dialect/codex.go
  • internal/dialect/common.go
  • internal/dialect/coderabbit.go
internal/engine/**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

Table-test every engine rule, including fire decisions, progress, completion, and finding/convergence behavior.

Files:

  • internal/engine/engine_test.go
internal/crq/service.go

📄 CodeRabbit inference engine (AGENTS.md)

Make service.go the only effects executor for CAS state writes and PostIssueComment; DryRun must report without writing.

Files:

  • internal/crq/service.go
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: kristofferR/coderabbit-queue

Timestamp: 2026-07-17T16:24:08.013Z
Learning: Preserve frozen loop exit codes: `0` for converged/skipped, `10` for findings, and `2` for timeout.
🪛 ast-grep (0.44.1)
internal/state/dashboard.go

[warning] 206-206: SHA-1 is a cryptographically broken hash function vulnerable to collision attacks and is unsuitable for security purposes such as signatures, integrity checks, or password hashing. Use a SHA-2 family function (e.g. sha256.New() / sha256.Sum256()) or SHA-3 instead.
Context: sha1.Sum
Note: [CWE-327] Use of a Broken or Risky Cryptographic Algorithm.

(weak-hash-sha1-go)

🪛 golangci-lint (2.12.2)
internal/state/dashboard.go

[medium] 4-4: G505: Blocklisted import crypto/sha1: weak cryptographic primitive

(gosec)


[medium] 207-207: G401: Use of weak cryptographic primitive

(gosec)

internal/state/store.go

[error] 296-296: Error return value of encoding/json.Marshal is not checked: unsafe type time.Time found

(errchkjson)

internal/dialect/coderabbit.go

[error] 187-187: QF1001: could apply De Morgan's law

(staticcheck)


[error] 211-211: S1007: should use raw string (...) with regexp.MustCompile to avoid having to escape twice

(staticcheck)

internal/crq/service.go

[error] 689-689: Non-inherited new context, use function like context.WithXXX instead

(contextcheck)

🪛 markdownlint-cli2 (0.23.0)
internal/dialect/testdata/coderabbit/completion-reply.md

[warning] 2-2: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/codex/clean-summary-legacy.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/findings-outside-diff.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/rate-limit-legacy.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/codex/usage-limit.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/reviews-paused.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/review-failed.md

[warning] 3-3: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/no-actionable-comments.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/codex/clean-summary-tada.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/codex/findings-outside-diff.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/already-reviewed.md

[warning] 2-2: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/rate-limit-fair-usage.md

[warning] 2-2: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/review-in-progress.md

[warning] 3-3: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/findings-failed-to-post.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/rate-limit-bold-window.md

[warning] 3-3: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/findings-nested-quotes.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/findings-prompt-block.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)


[warning] 4-4: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 15-15: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

AGENTS.md

[warning] 38-38: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 OpenGrep (1.25.0)
internal/state/dashboard.go

[WARNING] 207-207: MD5 and SHA1 are cryptographically broken and should not be used for security purposes. Use SHA-256 or stronger.

(coderabbit.crypto.go-weak-hash)

🔇 Additional comments (50)
internal/dialect/coderabbit.go (1)

1-213: LGTM!

Also applies to: 229-336

internal/dialect/event.go (1)

1-114: LGTM!

internal/dialect/testdata/coderabbit/rate-limit-bold-window.md (1)

1-8: LGTM!

internal/dialect/testdata/coderabbit/rate-limit-fair-usage.md (1)

1-2: LGTM!

internal/dialect/testdata/coderabbit/rate-limit-legacy.md (1)

1-1: LGTM!

internal/dialect/testdata/coderabbit/review-failed.md (1)

1-5: LGTM!

internal/dialect/testdata/coderabbit/review-in-progress.md (1)

1-8: LGTM!

internal/dialect/testdata/coderabbit/reviews-paused.md (1)

1-4: LGTM!

internal/dialect/testdata/codex/clean-summary-legacy.md (1)

1-1: LGTM!

internal/crq/preflight.go (1)

18-19: LGTM!

Also applies to: 242-246

internal/dialect/codex.go (1)

1-142: LGTM!

internal/dialect/common.go (1)

1-209: LGTM!

internal/dialect/finding.go (1)

1-51: LGTM!

internal/dialect/golden_test.go (1)

1-181: LGTM!

internal/dialect/testdata/coderabbit/already-reviewed.md (1)

1-2: LGTM!

internal/dialect/testdata/coderabbit/completion-reply.md (1)

1-2: LGTM!

internal/dialect/testdata/coderabbit/findings-failed-to-post.md (1)

1-15: LGTM!

internal/dialect/testdata/coderabbit/findings-nested-quotes.md (1)

1-27: LGTM!

internal/dialect/testdata/coderabbit/findings-outside-diff.md (1)

1-19: LGTM!

internal/dialect/testdata/coderabbit/findings-prompt-block.md (1)

1-16: LGTM!

internal/dialect/testdata/coderabbit/no-actionable-comments.md (1)

1-3: LGTM!

internal/dialect/testdata/codex/clean-summary-tada.md (1)

1-3: LGTM!

internal/dialect/testdata/codex/findings-outside-diff.md (1)

1-10: LGTM!

internal/dialect/testdata/codex/usage-limit.md (1)

1-1: LGTM!

internal/gh/github.go (1)

1-1: LGTM!

Also applies to: 36-39, 348-348, 362-370, 402-423, 524-524, 548-548, 569-569, 591-591, 630-630, 741-747, 1065-1082, 1127-1151

internal/gh/github_test.go (1)

1-1: LGTM!

internal/crq/init.go (1)

8-9: LGTM!

Also applies to: 19-19, 62-62

internal/crq/observe.go (1)

45-186: LGTM!

Also applies to: 190-225, 258-262

cmd/crq/main.go (1)

16-16: LGTM!

Also applies to: 66-72, 245-245

internal/crq/config.go (1)

11-20: LGTM!

Also applies to: 103-109, 261-261, 289-289

CLAUDE.md (1)

1-1: LGTM!

internal/state/state.go (1)

1-348: LGTM!

Also applies to: 370-430

internal/state/dashboard.go (1)

1-3: LGTM!

Also applies to: 6-120, 129-205

internal/state/store.go (1)

1-300: LGTM!

internal/state/state_test.go (1)

1-224: LGTM!

internal/crq/state.go (1)

8-90: LGTM!

internal/crq/state_test.go (1)

4-26: LGTM!

internal/engine/engine.go (1)

1-100: LGTM!

internal/engine/fire.go (1)

1-68: LGTM!

Also applies to: 75-94

internal/engine/progress.go (1)

1-152: LGTM!

Also applies to: 161-161

internal/engine/completion.go (1)

1-48: LGTM!

Also applies to: 56-108, 121-148, 184-214, 285-307

internal/engine/findings.go (1)

1-13: LGTM!

Also applies to: 21-26, 35-41

internal/engine/engine_test.go (1)

1-45: LGTM!

internal/crq/service.go (1)

13-178: LGTM!

Also applies to: 189-225, 231-460, 476-524, 532-556, 579-686, 690-732, 746-1006, 1018-1025, 1034-1074, 1081-1140

internal/crq/auto.go (1)

10-12: LGTM!

Also applies to: 29-30, 55-59, 72-73, 90-90, 112-115, 129-139, 223-223, 232-232, 243-243, 263-268, 277-277, 289-342

internal/crq/service_test.go (1)

12-217: LGTM!

Also applies to: 219-1801

internal/crq/feedback.go (1)

14-123: LGTM!

Also applies to: 146-178, 186-352, 366-881, 913-940

internal/crq/feedback_test.go (1)

8-1876: LGTM!

internal/crq/replay_test.go (1)

1-66: LGTM!

Also applies to: 85-645

AGENTS.md (1)

1-37: LGTM!

Also applies to: 39-97

Comment thread AGENTS.md Outdated
Comment thread internal/crq/observe.go
Comment thread internal/crq/observe.go Outdated
Comment thread internal/crq/replay_test.go Outdated
Comment thread internal/crq/service_test.go
Comment thread internal/engine/completion.go
Comment thread internal/engine/findings.go
Comment thread internal/state/dashboard.go Outdated
Comment thread internal/state/dashboard.go
Comment thread internal/state/state.go
crq now drives Codex instead of only listening for it. When Codex is in
CRQ_REQUIRED_BOTS and does not auto-review the PR, the fire step posts
CRQ_CODEX_CMD (default "@codex review") alongside the CodeRabbit
command and records it on the round (CodexCommandID); a failed Codex
post self-heals on a later pump. Auto-review is detected from evidence —
any Codex review or clean summary that no @codex command preceded — and
suppresses posting entirely.

A Codex that joins a round uninvited (actionable comment, review, or
thumbs-up in the round window, or auto-review on the PR) now gates that
round's completion dynamically, so both bots converge on the same head
even where Codex isn't configured as required. Its usage-limit notice
releases the dynamic gate so an exhausted Codex cannot stall rounds it
volunteered for; explicitly-required Codex stays bounded by the normal
feedback deadline.

The @codex command and the usage-limit notice are classified in dialect
(EvCodexCommand/EvCodexUsageLimit, corpus-pinned); the decisions live in
engine/codex.go (DecideCodexPost + the dynamic gate, table-tested); the
end-to-end scenarios in codex_replay_test.go drive all three modes
through the real pump: required-no-auto posts exactly once and never on
retry, auto-active never posts, and the dynamic gate holds then releases
on usage exhaustion.
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Dogfooding crq v3 on its own PR (crq loop → exit 10, drain-first)
surfaced twelve findings; nine were valid and are fixed here:

- A dry-run Pump no longer calls RefreshQuota — the calibration probe it
  can post is a side effect dry-run promises not to have.
- The detached recordFire retry derives from context.WithoutCancel(ctx)
  instead of context.Background(), keeping parent values.
- ParseReviewBodyFindings moves to dialect/common.go — it dispatches
  across both dialects and never belonged to coderabbit.go.
- engine/findings.go compares commits via dialect.SHAPrefixMatch, so a
  10-char Codex commit matches a 9-char head instead of silently failing
  the one-directional prefix test.
- The dashboard renders the recorded remaining-review count, hashes with
  SHA-256, and AGENTS.md's diagram fence declares a language.
- The round-view helpers (WaitingHead, RoundWaitDeadline, ContainsActive,
  FiredMarker, AccountBlockedUntil) move onto state.State where they
  belong; crq keeps only genuinely crq-shaped adapters.
- The replay suite loads bot messages from the dialect corpus instead of
  duplicating the wording (new corpus file: rate-limit-no-window.md).

Declined with reasons on the PR threads: the duplicate-type claim (the
suite compiles; phantom finding), merging the pullHead preflight into
observe (the cheap head check deliberately gates the expensive fetch
behind quota/pacing), and keeping abandoned rounds in Rounds (archiving
on terminal PR events is the designed semantics; the live
already-reviewed check covers re-fire safety).
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/engine/fire.go (1)

72-81: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not let CodeRabbit deduplication skip a required Codex review.

Line 76 returns FireDedupe before PostCodex is computed. If CodeRabbit already reviewed the head but required Codex has not, the round is deduped without ever requesting Codex. Add a Codex-only posting outcome or equivalent apply-layer signal before final deduplication.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/engine/fire.go` around lines 72 - 81, Update the decision flow
around the review deduplication loop and PostCodex computation so a CodeRabbit
match cannot return FireDedupe before a required Codex request is scheduled.
Compute or preserve the Codex-posting outcome first, then return an equivalent
Codex-only/apply-layer signal when Codex is still required, while retaining
FireDedupe when no Codex action is needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/crq/codex_replay_test.go`:
- Around line 15-29: Move the Codex login, clean-summary, usage-limit, and
review-command literals out of the replay test and use the canonical
fixtures/constants exposed by internal/dialect. Update newCodexReplayFixture and
the related test expectations to reference those dialect-owned symbols,
including the SHA-parameterized clean-summary fixture, so bot wording exists
only in the dialect package.

In `@internal/crq/service.go`:
- Around line 740-776: Update fireCodexReview and the Round state flow to
reserve Codex posting before calling postCodexReviewComment, using a persisted
in-progress/token marker and bounded retry cooldown so concurrent pumps cannot
post duplicates. After the external call, finalize the reservation with the
command ID on success, or release it while retaining the cooldown on
failure/ambiguous results. Update selfHealCodex to honor the reservation and
cooldown when deciding whether to retry.

In `@internal/engine/codex.go`:
- Around line 78-94: Update CodexAutoActive and its command-correlation logic so
automatic reviews or clean summaries are evaluated only against commands
associated with the same head or current observation window, rather than
firstCodexCommand across the entire PR history. Ensure old commands cannot
suppress later-head automatic detection, and early automatic reviews cannot
suppress commands for later windows; preserve the existing Codex bot and
clean-summary checks within the relevant window.
- Around line 11-14: The canonical Codex bot identity is defined in the engine
instead of the dialect layer. Add and export the identity/key from
internal/dialect, remove the local codexBot literal in internal/engine/codex.go,
and consume the dialect-owned symbol there. Update
internal/engine/engine_test.go lines 336-359 to use that dialect symbol for test
policies and observations; ensure the bot literal appears only in
internal/dialect.

---

Outside diff comments:
In `@internal/engine/fire.go`:
- Around line 72-81: Update the decision flow around the review deduplication
loop and PostCodex computation so a CodeRabbit match cannot return FireDedupe
before a required Codex request is scheduled. Compute or preserve the
Codex-posting outcome first, then return an equivalent Codex-only/apply-layer
signal when Codex is still required, while retaining FireDedupe when no Codex
action is needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 28be516b-b552-44bc-bc48-40dd4d4f9b47

📥 Commits

Reviewing files that changed from the base of the PR and between 3edc67c and 2c4aefb.

📒 Files selected for processing (19)
  • README.md
  • internal/crq/codex_replay_test.go
  • internal/crq/config.go
  • internal/crq/observe.go
  • internal/crq/service.go
  • internal/crq/service_test.go
  • internal/crq/state.go
  • internal/dialect/codex.go
  • internal/dialect/common.go
  • internal/dialect/event.go
  • internal/dialect/golden_test.go
  • internal/dialect/testdata/codex/review-command.md
  • internal/engine/codex.go
  • internal/engine/completion.go
  • internal/engine/engine.go
  • internal/engine/engine_test.go
  • internal/engine/fire.go
  • internal/state/state.go
  • llms.txt
📜 Review details
🧰 Additional context used
📓 Path-based instructions (12)
internal/dialect/testdata/{coderabbit,codex}/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Each new bot-message format must include one corpus file containing the real message.

Files:

  • internal/dialect/testdata/codex/review-command.md
internal/{dialect,engine,crq,state,gh}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Enforce the dependency direction dialect � engine � crq, state � crq, and gh � {state, crq}; prevent dependency cycles.

Files:

  • internal/engine/fire.go
  • internal/crq/codex_replay_test.go
  • internal/engine/codex.go
  • internal/dialect/golden_test.go
  • internal/engine/engine.go
  • internal/crq/observe.go
  • internal/engine/completion.go
  • internal/crq/config.go
  • internal/dialect/event.go
  • internal/crq/state.go
  • internal/state/state.go
  • internal/dialect/common.go
  • internal/dialect/codex.go
  • internal/engine/engine_test.go
  • internal/crq/service_test.go
  • internal/crq/service.go
internal/engine/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/engine/**/*.go: Keep the engine pure: decision logic must not perform I/O, use context, or depend on gh; pass now explicitly.
DecideFire is the single owner of review firing and must consolidate guards in order: open, readable/current head, eligible phase, free slot, account quota, minimum interval, not already reviewed, then adopt/post.

Files:

  • internal/engine/fire.go
  • internal/engine/codex.go
  • internal/engine/engine.go
  • internal/engine/completion.go
  • internal/engine/engine_test.go
internal/{engine,state,crq}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use AccountQuota or “account blocked” for the CodeRabbit account quota; do not call it GitHub REST rate limit or mix the two concepts.

Files:

  • internal/engine/fire.go
  • internal/crq/codex_replay_test.go
  • internal/engine/codex.go
  • internal/engine/engine.go
  • internal/crq/observe.go
  • internal/engine/completion.go
  • internal/crq/config.go
  • internal/crq/state.go
  • internal/state/state.go
  • internal/engine/engine_test.go
  • internal/crq/service_test.go
  • internal/crq/service.go
internal/crq/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Keep orchestration in internal/crq; it should wire packages through service, observation, automation, feedback, configuration, calibration, preflight, and initialization rather than implement bot classification or pure decision rules.

Files:

  • internal/crq/codex_replay_test.go
  • internal/crq/observe.go
  • internal/crq/config.go
  • internal/crq/state.go
  • internal/crq/service_test.go
  • internal/crq/service.go
internal/dialect/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Keep all bot-text knowledge in internal/dialect, including CodeRabbit/Codex classifiers, finding parsers, SHA/severity vocabulary, and Finding, BotEvent, and Classifier types. Bot literal wording must appear only there.

Files:

  • internal/dialect/golden_test.go
  • internal/dialect/event.go
  • internal/dialect/common.go
  • internal/dialect/codex.go
internal/dialect/golden_test.go

📄 CodeRabbit inference engine (AGENTS.md)

Each new bot-message corpus file must have one corresponding row in TestGoldenClassification; that row defines the classification specification.

Files:

  • internal/dialect/golden_test.go
internal/crq/observe.go

📄 CodeRabbit inference engine (AGENTS.md)

observe.go is the single place that queries GitHub for PR state and constructs engine.Observation; it must carry raw reviews/comments so Feedback parses findings from the same fetch.

Files:

  • internal/crq/observe.go
internal/state/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/state/**/*.go: Persist schema v3 with one Round per PR, one global FireSlot, the CodeRabbit AccountQuota, and an Archive ring.
A Round must never be deleted. Illegal state transitions must return errors; rounds may only transition or be archived when superseded by a new head.

Files:

  • internal/state/state.go
internal/dialect/{coderabbit,codex,common}.go

📄 CodeRabbit inference engine (AGENTS.md)

When adding recognition for a new bot phrasing, modify only the relevant classifier/parser in internal/dialect; keep bot wording out of engine, state, and crq.

Files:

  • internal/dialect/common.go
  • internal/dialect/codex.go
internal/engine/**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

Table-test every engine rule, including DecideFire, progress, completion, and convergence behavior.

Files:

  • internal/engine/engine_test.go
internal/crq/service.go

📄 CodeRabbit inference engine (AGENTS.md)

service.go is the only effects executor: apply CAS state writes and PostIssueComment; DryRun must report without writing.

Files:

  • internal/crq/service.go
🪛 markdownlint-cli2 (0.23.0)
internal/dialect/testdata/codex/review-command.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🪛 OpenGrep (1.25.0)
internal/crq/codex_replay_test.go

[ERROR] 146-146: Possible credit card number (PAN) detected in source code. Credit card numbers should never be hardcoded or stored in source files. Use a secrets manager or tokenization service instead.

(coderabbit.pii.credit-card-number)

🔇 Additional comments (18)
internal/state/state.go (1)

57-60: LGTM!

internal/crq/state.go (1)

87-87: LGTM!

internal/engine/engine.go (1)

19-19: LGTM!

Also applies to: 67-83

internal/engine/codex.go (1)

16-76: LGTM!

Also applies to: 97-161

internal/engine/completion.go (1)

49-58: LGTM!

Also applies to: 92-99, 126-139

internal/engine/engine_test.go (1)

332-335: LGTM!

Also applies to: 360-404

internal/crq/service_test.go (1)

715-715: LGTM!

Also applies to: 1291-1291

internal/crq/codex_replay_test.go (1)

1-14: LGTM!

Also applies to: 30-225

llms.txt (1)

64-67: LGTM!

README.md (1)

423-423: LGTM!

Also applies to: 446-453

internal/dialect/codex.go (1)

46-52: LGTM!

Also applies to: 99-150

internal/dialect/common.go (1)

123-149: LGTM!

internal/dialect/event.go (1)

18-18: LGTM!

Also applies to: 27-28, 66-72, 86-103

internal/dialect/golden_test.go (1)

29-108: LGTM!

internal/dialect/testdata/codex/review-command.md (1)

1-1: LGTM!

internal/crq/service.go (1)

269-269: LGTM!

Also applies to: 402-402, 438-440, 543-546, 596-598, 653-659, 674-690

internal/crq/config.go (1)

35-43: LGTM!

Also applies to: 111-113

internal/crq/observe.go (1)

67-69: LGTM!

Also applies to: 103-118, 152-199, 202-264

Comment thread internal/crq/codex_replay_test.go Outdated
Comment thread internal/crq/service.go
Comment thread internal/engine/codex.go Outdated
Comment thread internal/engine/codex.go Outdated
Dogfooding on PR #30 surfaced both, one per review round:

1. CodeRabbit appends an "Also applies to: <lines>" trailer to real
   finding bodies. The non-actionable denylist matched the phrase as a
   substring, so all four findings carrying the trailer in round one were
   silently dropped — never surfaced, never resolved. The phrase now only
   marks a comment that IS the trailer (prefix match); corpus files pin
   both directions.

2. CodeRabbit submits empty-bodied COMMENTED review objects as carriers
   for its inline-comment batches, minutes before the real review lands.
   Counting a shell as review evidence converged round two with zero
   findings at 17:26 while the real review was still posting until 17:32.
   observe() now skips body-less COMMENTED reviews; the replay suite pins
   the shell-then-real-review sequence, and review fixtures carry real
   bodies as actual bot reviews do.
… review

- CAS identity guards: every round-mutating CAS closure (abandon, dedupe,
  supersede, both fireRound reservations, recordFire, fireCodexOnly) now
  verifies the stored round still matches the observed Seq+Head via a shared
  sameRound helper, so a concurrent supersede reads as a benign lost race
  instead of mutating a replacement round.
- Canonical Codex login: export dialect.CodexBotLogin and consume it from
  IsCodexBot, engine's codexBot, config's extraFeedbackBots, and the tests;
  the replay suite now loads the clean-summary (SHA-templated) and usage-limit
  texts from the dialect corpus instead of duplicating bot wording.
- Adoption force-push guard: headForcePushCutoff returns (time, error); on any
  lookup failure adoptableCommands skips adoption this pass rather than adopting
  an old-head command blind (worst case is re-posting an existing command).
- Codex auto-review detection scoped to the latest evidence: only the most
  recent Codex review/clean-summary decides auto-active, so an old unprompted
  review no longer suppresses posting once a later commanded review lands.
- Dedupe no longer skips a required Codex: a CodeRabbit review at the head that
  a gating Codex has not yet reviewed yields FireCodexOnly (post only the Codex
  command, wait on Codex alone) when crq may post, FireNo when Codex will still
  produce its own evidence, and plain FireDedupe only when Codex is truly
  unobtainable so the round never wedges.
- Calibration scheduling uses the injectable s.clock() so replay tests can drive
  quota freshness deterministically.
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/crq/service.go (1)

219-234: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep every dry-run path state-write-free. The guard only skips quota refresh: Line 224 can persist an abandonment before it, while Wait still persists an enqueue and can repair a completed round.

  • internal/crq/service.go#L219-L234: return the closed-PR dry-run result without calling abandonRound.
  • internal/crq/service.go#L1118-L1155: simulate enqueue/dedupe decisions in dry-run; do not invoke Enqueue or the poison-marker repair update.

As per coding guidelines, “DryRun must report without writing.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/crq/service.go` around lines 219 - 234, Keep all dry-run paths
state-write-free: in internal/crq/service.go lines 219-234, return the closed-PR
dry-run result without calling abandonRound; in lines 1118-1155, update Wait to
simulate enqueue and dedupe decisions without invoking Enqueue or performing the
poison-marker repair update. Preserve normal write behavior outside DryRun.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/crq/service.go`:
- Around line 219-234: Keep all dry-run paths state-write-free: in
internal/crq/service.go lines 219-234, return the closed-PR dry-run result
without calling abandonRound; in lines 1118-1155, update Wait to simulate
enqueue and dedupe decisions without invoking Enqueue or performing the
poison-marker repair update. Preserve normal write behavior outside DryRun.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 07cfc6c4-2d7d-43ac-9e1d-5b5c3634752e

📥 Commits

Reviewing files that changed from the base of the PR and between 2c4aefb and 2d988ff.

📒 Files selected for processing (15)
  • AGENTS.md
  • internal/crq/codex_replay_test.go
  • internal/crq/feedback.go
  • internal/crq/feedback_test.go
  • internal/crq/replay_test.go
  • internal/crq/service.go
  • internal/crq/service_test.go
  • internal/crq/state.go
  • internal/dialect/coderabbit.go
  • internal/dialect/common.go
  • internal/dialect/golden_test.go
  • internal/dialect/testdata/coderabbit/rate-limit-no-window.md
  • internal/engine/findings.go
  • internal/state/dashboard.go
  • internal/state/state.go
💤 Files with no reviewable changes (2)
  • internal/dialect/coderabbit.go
  • internal/crq/state.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (10)
internal/dialect/testdata/{coderabbit,codex}/**/*

📄 CodeRabbit inference engine (AGENTS.md)

For each new bot-message format, add one corpus file containing the real message.

Files:

  • internal/dialect/testdata/coderabbit/rate-limit-no-window.md
internal/{dialect,engine,crq,state,gh}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Preserve the dependency direction: dialect ← engine ← crq, state ← crq, and gh ← {state, crq}; introduce no dependency cycles.

Files:

  • internal/engine/findings.go
  • internal/dialect/golden_test.go
  • internal/crq/codex_replay_test.go
  • internal/dialect/common.go
  • internal/state/dashboard.go
  • internal/state/state.go
  • internal/crq/replay_test.go
  • internal/crq/feedback.go
  • internal/crq/service_test.go
  • internal/crq/service.go
  • internal/crq/feedback_test.go
internal/engine/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Keep the engine pure: pass now explicitly and do not use context or GitHub I/O. DecideFire is the sole fire owner, while Progress handles round transitions and Completion determines whether a round is done.

Files:

  • internal/engine/findings.go
AGENTS.md

📄 CodeRabbit inference engine (CLAUDE.md)

Follow the instructions defined in AGENTS.md for the applicable project or directory scope.

Files:

  • AGENTS.md
internal/dialect/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Keep all bot-text knowledge in internal/dialect, including CodeRabbit/Codex classifiers, finding parsers, SHA/severity vocabulary, and bot event types; bot literal wording must appear only there.

Files:

  • internal/dialect/golden_test.go
  • internal/dialect/common.go
internal/dialect/golden_test.go

📄 CodeRabbit inference engine (AGENTS.md)

For each new bot-message format, add one TestGoldenClassification row; that row defines the classification specification.

Files:

  • internal/dialect/golden_test.go
internal/crq/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/crq/**/*.go: Keep internal/crq orchestration-only: it coordinates service operations and wires packages but must not contain decision logic or bot wording.
Use the shared observe → decide → apply flow for both daemon and loop operation; no code outside engine.DecideFire may post the review command.

Files:

  • internal/crq/codex_replay_test.go
  • internal/crq/replay_test.go
  • internal/crq/feedback.go
  • internal/crq/service_test.go
  • internal/crq/service.go
  • internal/crq/feedback_test.go
internal/dialect/{coderabbit,codex,common}.go

📄 CodeRabbit inference engine (AGENTS.md)

When adding support for a new bot-message phrasing, modify only the relevant classifier/parser in internal/dialect; keep the wording out of engine, state, and crq.

Files:

  • internal/dialect/common.go
internal/state/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/state/**/*.go: Maintain persisted schema v3 with one Round per PR, one global FireSlot, the CodeRabbit AccountQuota, and an Archive ring.
Rounds must never be deleted; illegal Round transitions must return errors, and superseded rounds may only be archived.
Use FireSlot with CAS to allow at most one concurrent fire fleet-wide; releasing the slot on bot acknowledgement must not close the round.

Files:

  • internal/state/dashboard.go
  • internal/state/state.go
internal/crq/service.go

📄 CodeRabbit inference engine (AGENTS.md)

Make service.go the only effects executor for CAS state writes and PostIssueComment; DryRun must report without writing.

Files:

  • internal/crq/service.go
🪛 markdownlint-cli2 (0.23.0)
internal/dialect/testdata/coderabbit/rate-limit-no-window.md

[warning] 2-2: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (13)
AGENTS.md (1)

38-38: LGTM!

internal/crq/feedback_test.go (1)

8-27: LGTM!

Also applies to: 48-60, 97-116, 200-219, 232-232, 254-273, 306-341, 363-382, 406-420, 430-430, 447-594, 644-649, 669-669, 699-714, 739-763, 787-794, 804-807, 819-819, 870-891, 911-924, 950-963, 983-996, 1019-1035, 1057-1071, 1097-1113, 1139-1155, 1187-1200, 1213-1213, 1226-1226, 1269-1287, 1299-1299, 1336-1379, 1433-1433, 1462-1470, 1500-1512, 1529-1533, 1552-1570, 1607-1625, 1666-1686, 1721-1734, 1765-1782, 1811-1817, 1838-1839, 1856-1876

internal/dialect/common.go (1)

201-214: LGTM!

internal/dialect/golden_test.go (1)

55-56: LGTM!

internal/dialect/testdata/coderabbit/rate-limit-no-window.md (1)

1-3: LGTM!

internal/engine/findings.go (1)

15-15: LGTM!

Also applies to: 28-28

internal/crq/codex_replay_test.go (1)

79-135: LGTM!

internal/crq/feedback.go (1)

243-422: LGTM!

Also applies to: 430-467, 576-617

internal/crq/replay_test.go (1)

69-90: LGTM!

Also applies to: 287-354, 363-419, 427-484, 564-650

internal/crq/service.go (1)

681-780: LGTM!

Also applies to: 959-1033

internal/crq/service_test.go (1)

431-435: LGTM!

Also applies to: 472-476, 635-642, 685-692, 1116-1118, 1627-1634, 1701-1708

internal/state/dashboard.go (1)

4-4: LGTM!

Also applies to: 93-182, 201-212

internal/state/state.go (1)

436-495: LGTM!

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/crq/observe.go`:
- Around line 54-61: Restrict the empty COMMENTED review suppression in the
review-processing loop to reviews authored by the configured CodeRabbit
identity. Update the predicate near review.Body and review.State to also
validate that reviewer, preserving the existing behavior for CodeRabbit shells
while retaining legitimate empty reviews from Codex or other bots.

In `@internal/crq/replay_test.go`:
- Around line 183-187: Move bot-message literals out of tests: in
internal/crq/replay_test.go lines 183-187, load the CodeRabbit review body from
dialect corpus data or use a neutral sentinel; in
internal/crq/codex_replay_test.go lines 64-68, replace the Codex prose with
dialect-owned corpus data or a neutral sentinel; and in
internal/engine/engine_test.go lines 336-343, use an opaque non-bot command
value while preserving the existing blank/nonblank behavior tests.

In `@internal/crq/service.go`:
- Around line 723-741: The failed-post handling in the `PostIssueComment` error
path currently requeues the round immediately; after `ReleaseToQueue` in the
`s.store.Update` callback, set the round’s next eligible time to `now +
RetryBackoff` while still releasing the slot. Preserve the existing token
validation, warning, state persistence, sync, and `PumpResult` behavior.

In `@internal/engine/codex.go`:
- Around line 78-127: Update CodexAutoActive and codexCommandAtOrBefore so
historical commands only suppress the latest Codex evidence when they belong to
that same head/evidence window, rather than any command at or before its
timestamp. Track the preceding Codex review or clean-summary boundary when
evaluating the latest evidence, preserving the commanded classification when a
command precedes that evidence and marking a later unprompted evidence event as
automatic.

In `@internal/engine/engine_test.go`:
- Around line 416-456: Convert TestDecideFireCodexDedupe into a table-driven
test with named cases covering each existing Codex fire-decision scenario and
expected verdict. Define shared fixtures outside the case table where
appropriate, then iterate through the rows and report failures with the case
name. Preserve all current observations, request configurations, and verdict
expectations so future Codex guard-matrix cases can be added systematically.

In `@internal/engine/fire.go`:
- Around line 125-126: The Codex waiting branch in the fire decision logic must
persist a timed waiting state instead of returning an untracked FireNo decision.
Update the path guarded by obs.CodexAutoActive, obs.CodexCommands, or
r.CodexCommandID to adopt the existing command-phase mechanism and set an
appropriate WaitDeadline, or use an existing wait verdict that records the
fired/reviewing phase; ensure subsequent pumps can time out when Codex never
responds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 12982932-fe7a-4ee1-96d7-e7a837d68c43

📥 Commits

Reviewing files that changed from the base of the PR and between 2d988ff and 58f6f86.

📒 Files selected for processing (15)
  • internal/crq/codex_replay_test.go
  • internal/crq/config.go
  • internal/crq/feedback_test.go
  • internal/crq/observe.go
  • internal/crq/replay_test.go
  • internal/crq/service.go
  • internal/crq/service_test.go
  • internal/dialect/codex.go
  • internal/dialect/common.go
  • internal/dialect/golden_test.go
  • internal/dialect/testdata/coderabbit/finding-with-also-applies-trailer.md
  • internal/dialect/testdata/coderabbit/thread-ack-also-applies.md
  • internal/engine/codex.go
  • internal/engine/engine_test.go
  • internal/engine/fire.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (10)
internal/dialect/**/*.{go,md}

📄 CodeRabbit inference engine (AGENTS.md)

When adding a bot-message format, change only its classifier/parser in internal/dialect, one real-message corpus file under internal/dialect/testdata/{coderabbit,codex}/, and one corresponding TestGoldenClassification row.

Files:

  • internal/dialect/testdata/coderabbit/finding-with-also-applies-trailer.md
  • internal/dialect/testdata/coderabbit/thread-ack-also-applies.md
  • internal/dialect/golden_test.go
  • internal/dialect/common.go
  • internal/dialect/codex.go
internal/dialect/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Keep all bot-text knowledge, literal bot wording, completion/rate-limit/paused/in-progress/failed/clean-review classifiers, finding parsers, SHA/severity vocabulary, Finding, BotEvent, and Classifier definitions in internal/dialect; this package must have zero dependencies.

Files:

  • internal/dialect/golden_test.go
  • internal/dialect/common.go
  • internal/dialect/codex.go
internal/{dialect,engine,state,crq}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Do not mix GitHub REST Throttle with CodeRabbit AccountQuota or “account blocked”; use the distinct vocabulary consistently, and keep the literal “rate limit” restricted to internal/gh and internal/dialect.

Files:

  • internal/dialect/golden_test.go
  • internal/engine/codex.go
  • internal/crq/config.go
  • internal/engine/fire.go
  • internal/crq/observe.go
  • internal/crq/replay_test.go
  • internal/crq/codex_replay_test.go
  • internal/dialect/common.go
  • internal/dialect/codex.go
  • internal/engine/engine_test.go
  • internal/crq/service.go
  • internal/crq/service_test.go
  • internal/crq/feedback_test.go
internal/dialect/**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

Use TestGoldenClassification rows as the specification for how each bot corpus message classifies.

Files:

  • internal/dialect/golden_test.go
internal/engine/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/engine/**/*.go: Keep internal/engine pure: decision logic must receive now explicitly and must not use context or GitHub I/O. DecideFire is the single fire owner; Progress, Completion, findings helpers, and Policy belong here.
DecideFire must consolidate every fire guard in order: open, readable head, current head, eligible phase, free slot, account quota, minimum interval, not already reviewed, and adopt/post. No other code may post the review command.

Files:

  • internal/engine/codex.go
  • internal/engine/fire.go
  • internal/engine/engine_test.go
internal/{engine, state, crq}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Keep bot wording out of internal/engine, internal/state, and internal/crq; those packages should consume dialect classifications rather than matching literal bot text.

Files:

  • internal/engine/codex.go
  • internal/engine/fire.go
  • internal/engine/engine_test.go
internal/crq/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/crq/**/*.go: Keep internal/crq orchestration-only: service operations, observation, automation, feedback assembly, configuration, calibration, preflight, initialization, and package wiring belong here; it must not contain bot wording or duplicate engine decision logic.
Use the shared observe → decide → apply flow for daemon and loop behavior: observation is built once, engine decisions remain pure, and orchestration applies the decision through the service.

Files:

  • internal/crq/config.go
  • internal/crq/observe.go
  • internal/crq/replay_test.go
  • internal/crq/codex_replay_test.go
  • internal/crq/service.go
  • internal/crq/service_test.go
  • internal/crq/feedback_test.go
internal/crq/observe.go

📄 CodeRabbit inference engine (AGENTS.md)

Make crq/observe.go the single place that queries GitHub for a PR and builds an engine.Observation, including the head, open state, reviews, classified bot events, adoptable commands, reactions, and raw reviews/comments for feedback parsing.

Files:

  • internal/crq/observe.go
internal/engine/**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

Table-test every engine rule, including fire decisions, progress, completion, and findings/convergence behavior.

Files:

  • internal/engine/engine_test.go
internal/crq/service.go

📄 CodeRabbit inference engine (AGENTS.md)

Make crq/service.go the only effects executor for CAS state writes and PostIssueComment; DryRun must report without writing state or posting effects.

Files:

  • internal/crq/service.go
🪛 golangci-lint (2.12.2)
internal/crq/observe.go

[error] 204-204: error is not nil (line 202) but it returns nil

(nilerr)

🪛 LanguageTool
internal/dialect/testdata/coderabbit/finding-with-also-applies-trailer.md

[grammar] ~1-~1: Use a hyphen to join words.
Context: Guard the cutoff against a zero fired time. A round that never fired ...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.23.0)
internal/dialect/testdata/coderabbit/finding-with-also-applies-trailer.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/thread-ack-also-applies.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (15)
internal/dialect/testdata/coderabbit/finding-with-also-applies-trailer.md (1)

1-5: LGTM!

internal/dialect/testdata/coderabbit/thread-ack-also-applies.md (1)

1-1: LGTM!

internal/dialect/codex.go (1)

1-17: LGTM!

Also applies to: 19-35, 37-49, 51-58, 60-68, 70-82, 84-88, 90-102, 104-156

internal/dialect/common.go (1)

1-7: LGTM!

Also applies to: 8-13, 15-29, 31-44, 46-58, 60-65, 67-81, 83-107, 109-121, 123-156, 158-163, 164-167, 168-173, 175-184, 186-201, 203-205, 207-220, 222-229

internal/dialect/golden_test.go (1)

1-26: LGTM!

Also applies to: 27-62, 63-66, 67-117, 118-200

internal/engine/fire.go (1)

7-7: LGTM!

Also applies to: 21-21, 75-80, 106-124, 127-129

internal/engine/codex.go (1)

11-14: LGTM!

internal/engine/engine_test.go (1)

462-488: LGTM!

Also applies to: 495-525

internal/crq/config.go (1)

251-251: LGTM!

internal/crq/observe.go (1)

197-205: LGTM!

Also applies to: 282-318

internal/crq/service.go (1)

345-352: LGTM!

Also applies to: 448-449, 477-499, 515-515, 544-544, 582-582, 623-623, 688-722, 743-760, 773-773, 902-902, 914-914, 1195-1204, 1294-1294

internal/crq/service_test.go (1)

198-204: LGTM!

Also applies to: 669-669, 948-948, 1075-1075

internal/crq/feedback_test.go (1)

110-110: LGTM!

Also applies to: 215-215, 269-269, 320-320

internal/crq/replay_test.go (1)

653-705: LGTM!

internal/crq/codex_replay_test.go (1)

4-8: LGTM!

Also applies to: 17-35, 185-185, 232-239, 244-289

Comment thread internal/crq/observe.go
Comment thread internal/crq/replay_test.go Outdated
Comment thread internal/crq/service.go
Comment thread internal/engine/codex.go
Comment thread internal/engine/engine_test.go
Comment thread internal/engine/fire.go Outdated
…tion

Round-three self-hosted-loop findings on the new head:

- The empty-COMMENTED review filter (CodeRabbit's inline-comment carriers)
  is scoped to the configured reviewer, so it can never drop another bot's
  empty review that a Codex-gated round waits on.
- Both fire paths (CodeRabbit and Codex-only) park in awaiting_retry with a
  bounded cooldown when a command post fails, instead of re-posting on the
  next pump — the same no-cooldown-requeue shape that fed the #448 spam.
- CodexAutoActive keys on a command in the window since the previous Codex
  evidence, so a stale @codex review from an earlier round no longer masks
  a later unprompted review as commanded and suppresses posting forever.

Declined on the PR: converting TestDecideFireCodexDedupe to a table test
(the cases are already readable; churn), and giving the codex-wait branch a
timed phase (it only fires when Codex evidence is imminent — auto-active,
commanded, or already asked — and the loop's own deadline bounds the
agent-facing wait).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58f6f86ecf

ℹ️ 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".

Comment thread internal/crq/service.go
Comment thread internal/crq/feedback.go Outdated
Comment thread internal/crq/service.go
Comment thread internal/crq/service.go Outdated
Comment thread internal/crq/service.go
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

When an agent declines a finding with 'crq decline --resolve', the bot
replies either conceding ('I'm withdrawing this finding') or contesting
('I'm retaining the finding: ...'). crq dropped resolved threads wholesale,
so a valid rebuttal vanished and the loop converged over an unaddressed
disagreement — exactly how the real #30 review caught a bug I'd wrongly
declined.

Now the feedback pass reads that reply: dialect gains the verdict
classifiers IsReviewFindingWithdrawn/IsReviewFindingRetained (golden-pinned
with CodeRabbit's actual concede/contest messages from PR #30), and
feedback.go re-surfaces a resolved thread whose latest comment is a bot
reply following the agent's own comment and not a clear withdrawal, as a
'review_reply' finding. Ambiguous replies surface too — never bury a
possible rebuttal on a false concession. The loop then holds until the
agent re-addresses it (fix, or decline again convincingly enough that the
bot withdraws).
Codex auto-reviewed PR #30 and caught three real edges:

- readQuota/RefreshQuota wiped a still-active account block when a
  calibration probe was inconclusive (no fresh reply): after CalibrationTTL
  the block vanished and Pump could fire queued reviews inside the original
  blocked window. The refresh now carries a live block forward until a
  conclusive reply lands.
- The loop completed a fired/reviewing round on findings-return even when a
  required (or dynamically-gating) bot was still pending — so a Codex finding
  posted before CodeRabbit's review released the slot and stopped observing
  CodeRabbit's review/rate-limit/timeout, losing evidence. It now leaves the
  round active while any reviewer is pending, matching hold-the-head; the
  co-review wait deadline bounds it.
- sweepReviewing's CAS lacked the sameRound(Seq+Head) identity guard the
  other mutations use, so a supersede between observe and write could apply
  an old head's Progress to a replacement round. Guarded.

TestLoopResumesAwaitingFeedbackWithoutRefiring updated: a round with a
pending dynamically-gating Codex now correctly stays active (not completed),
still without re-firing.
@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f673edee02

ℹ️ 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".

Comment thread internal/engine/fire.go Outdated
Comment thread internal/state/state.go
Comment thread internal/engine/progress.go
Comment thread internal/crq/service.go
Comment thread internal/crq/service.go
Comment thread internal/crq/feedback.go
…t anchors

The rebuttal reader's first live run surfaced Codex's platform boilerplate
as a false rebuttal; its 'create an environment' notice is now classified
non-actionable in dialect (corpus-pinned) and threadRebuttal skips
non-actionable replies generally.

Codex's round-four findings, all real:

- Codex-only resolutions (dedupe, FireCodexOnly, co-review wait) spend no
  CodeRabbit quota, so DecideFire resolves them BEFORE the account-block
  and pacing gates — a block from another PR no longer delays them.
- The self-heal Codex post claims the round under CAS (CodexClaimedAt,
  2-minute TTL) before the network call, so two unserialized sweepers can
  no longer double-post; the claim is released on record or failure.
  Codex flagged this race three times across rounds — it was right that
  the observation-guard alone left a real window.
- A reviewing round that would re-acknowledge now returns KeepWaiting, so
  a silent co-bot wait no longer writes identical state and re-syncs the
  dashboard on every pump.
- AwaitCoReview anchors at the adopted @codex command's time, not the
  observation time, so a SHA-less legacy clean summary posted before the
  pump still counts toward completion.
- A PR closed while parked in awaiting_retry is abandoned on the next
  pump (sweepParkedClosed) instead of surviving its cooldown window.
- threadRebuttal only trusts complete threads: the reviewThreads query
  carries totalCount and a partially-fetched thread is skipped rather
  than judged on a stale mid-thread reply.
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/crq/codex_replay_test.go`:
- Around line 535-537: Strengthen the assertion after f.round(repo, pr) to
verify the closed round is archived rather than deleted: confirm State.Archive
contains this round’s head with PhaseAbandoned. Retain the existing nil-result
check, and use the round’s existing head and archive symbols to validate the
one-round persistence invariant.

In `@internal/crq/feedback.go`:
- Around line 885-890: Move the unknown-to-major severity flooring from the
orchestration code around SeverityOf into internal/dialect. Add or reuse
dialect-owned severity constants or a FloorSeverity helper, then update the
caller to use that API instead of comparing or assigning literal severity
strings; preserve the existing behavior for all other severities.
- Around line 868-875: Update the agentReplied detection loop in the feedback
evaluation flow to require a bot → non-bot → bot sequence, rather than marking
any non-bot comment as an agent reply. Track the preceding and following comment
authors using dialect.InBots, and only set agentReplied when the non-bot comment
is immediately preceded and followed by bot comments before the existing
!agentReplied handling.

In `@internal/crq/service.go`:
- Around line 1437-1468: Update sweepParkedClosed to avoid repeatedly inspecting
only the oldest open awaiting_retry round; rotate the candidate across pumps or
continue scanning parked rounds until a closed PR is found. Preserve the
one-pull-per-pump constraint and abandon the closed round via abandonRound with
the existing skipped outcome.
- Around line 904-916: Update fireCodexReview and its associated finalization
logic to validate the round with sameRound rather than only comparing Head,
preventing stale results from updating a replacement round with a different Seq.
Clear CodexClaimedAt only when codexID indicates a successful post; retain the
claim after failure so codexClaimTTL governs retries.

In `@internal/engine/codex.go`:
- Around line 85-110: Ensure latestCodexEvidence keeps prev strictly earlier
than latest by ignoring evidence timestamps equal to the current latest or prev
when updating the candidates. Add a regression test covering review and
clean-summary evidence with the same timestamp and a command at that timestamp,
verifying CodexAutoActive classifies the review as commanded.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2a4a080f-5fb1-4008-9980-664e1647fd79

📥 Commits

Reviewing files that changed from the base of the PR and between 58f6f86 and 86c0c15.

📒 Files selected for processing (23)
  • AGENTS.md
  • README.md
  • internal/crq/codex_replay_test.go
  • internal/crq/feedback.go
  • internal/crq/feedback_test.go
  • internal/crq/observe.go
  • internal/crq/replay_test.go
  • internal/crq/service.go
  • internal/crq/service_test.go
  • internal/dialect/codex.go
  • internal/dialect/common.go
  • internal/dialect/golden_test.go
  • internal/dialect/reply.go
  • internal/dialect/testdata/coderabbit/reply-retained.md
  • internal/dialect/testdata/coderabbit/reply-withdrawn.md
  • internal/dialect/testdata/codex/environment-notice.md
  • internal/engine/codex.go
  • internal/engine/engine_test.go
  • internal/engine/fire.go
  • internal/engine/progress.go
  • internal/state/state.go
  • internal/state/state_test.go
  • llms.txt
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
internal/dialect/testdata/{coderabbit,codex}/**/*

📄 CodeRabbit inference engine (AGENTS.md)

When adding support for a new bot-message format, add one corpus file containing the real message under the appropriate dialect testdata directory.

Files:

  • internal/dialect/testdata/codex/environment-notice.md
  • internal/dialect/testdata/coderabbit/reply-withdrawn.md
  • internal/dialect/testdata/coderabbit/reply-retained.md
internal/dialect/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Keep all bot-text knowledge, literal bot wording, classifiers, parsers, SHA/severity vocabulary, and bot-event definitions in internal/dialect; do not place bot wording in engine, state, or crq.

Files:

  • internal/dialect/reply.go
  • internal/dialect/golden_test.go
  • internal/dialect/common.go
  • internal/dialect/codex.go
internal/{dialect,engine,state,crq,gh}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Preserve the dependency direction dialect � engine � crq, state � crq, and gh � {state, crq}; do not introduce dependency cycles. The engine must perform no I/O.

Files:

  • internal/dialect/reply.go
  • internal/dialect/golden_test.go
  • internal/engine/fire.go
  • internal/engine/codex.go
  • internal/dialect/common.go
  • internal/engine/progress.go
  • internal/crq/codex_replay_test.go
  • internal/dialect/codex.go
  • internal/crq/replay_test.go
  • internal/crq/observe.go
  • internal/state/state_test.go
  • internal/state/state.go
  • internal/engine/engine_test.go
  • internal/crq/service_test.go
  • internal/crq/feedback.go
  • internal/crq/service.go
  • internal/crq/feedback_test.go
internal/dialect/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

For each new bot-message format, add one row to TestGoldenClassification in golden_test.go; that row is the classification specification.

Files:

  • internal/dialect/golden_test.go
internal/engine/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Keep decision logic pure: pass now explicitly, avoid context and GitHub dependencies, and centralize fire eligibility in DecideFire; use Progress for round transitions and Completion for determining whether a round is done.

Files:

  • internal/engine/fire.go
  • internal/engine/codex.go
  • internal/engine/progress.go
  • internal/engine/engine_test.go
internal/crq/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Keep orchestration in internal/crq: observation belongs in observe.go, effects belong in service.go, and no code outside the apply path may post the review command. DryRun must report without writing.

Files:

  • internal/crq/codex_replay_test.go
  • internal/crq/replay_test.go
  • internal/crq/observe.go
  • internal/crq/service_test.go
  • internal/crq/feedback.go
  • internal/crq/service.go
  • internal/crq/feedback_test.go
internal/state/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/state/**/*.go: Maintain one Round per PR, never delete rounds, and represent lifecycle changes only through validated Round transition methods; archive superseded rounds rather than deleting them.
Preserve the global FireSlot invariant of at most one concurrent fire fleet-wide, and release the slot on bot acknowledgement while keeping the round open in reviewing.

Files:

  • internal/state/state_test.go
  • internal/state/state.go
internal/engine/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

Table-test every convergence and fire rule in the engine test suite, including rules consuming dialect classifications.

Files:

  • internal/engine/engine_test.go
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: kristofferR/coderabbit-queue

Timestamp: 2026-07-17T22:31:52.909Z
Learning: Keep the observe � decide � apply flow intact: observe builds one `engine.Observation`, engine performs pure decisions, and crq applies CAS state writes and GitHub comments.
🪛 markdownlint-cli2 (0.23.0)
internal/dialect/testdata/codex/environment-notice.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/reply-withdrawn.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

internal/dialect/testdata/coderabbit/reply-retained.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (23)
AGENTS.md (1)

1-37: LGTM!

Also applies to: 38-60, 61-83, 84-98

README.md (1)

405-410: LGTM!

Also applies to: 429-429, 452-460

llms.txt (1)

64-67: LGTM!

Also applies to: 95-100

internal/dialect/codex.go (1)

51-56: LGTM!

internal/dialect/common.go (1)

123-156: LGTM!

internal/dialect/golden_test.go (1)

70-72: LGTM!

Also applies to: 205-227

internal/dialect/reply.go (1)

1-39: LGTM!

internal/dialect/testdata/coderabbit/reply-retained.md (1)

1-1: LGTM!

internal/dialect/testdata/coderabbit/reply-withdrawn.md (1)

1-1: LGTM!

internal/dialect/testdata/codex/environment-notice.md (1)

1-1: LGTM!

internal/state/state.go (1)

65-69: LGTM!

Also applies to: 258-286

internal/state/state_test.go (1)

147-190: LGTM!

internal/crq/replay_test.go (1)

186-189: LGTM!

internal/crq/codex_replay_test.go (1)

4-4: LGTM!

Also applies to: 292-505, 540-569

internal/engine/codex.go (1)

126-144: LGTM!

internal/engine/fire.go (1)

17-24: LGTM!

Also applies to: 72-80, 116-133

internal/engine/progress.go (1)

64-74: LGTM!

Also applies to: 85-89, 170-180

internal/engine/engine_test.go (1)

178-218: LGTM!

Also applies to: 445-451, 483-493, 588-617

internal/crq/observe.go (1)

58-62: LGTM!

internal/crq/service.go (1)

214-221: LGTM!

Also applies to: 431-434, 462-463, 662-662, 744-744, 777-835, 1024-1040, 1307-1310, 1390-1394

internal/crq/feedback.go (1)

135-140: LGTM!

Also applies to: 363-369, 741-742, 793-793, 858-863, 891-903

internal/crq/service_test.go (1)

1813-1849: LGTM!

internal/crq/feedback_test.go (1)

1529-1538: LGTM!

Also applies to: 1886-1967

Comment thread internal/crq/codex_replay_test.go
Comment thread internal/crq/feedback.go
Comment thread internal/crq/feedback.go Outdated
Comment thread internal/crq/service.go
Comment thread internal/crq/service.go
Comment thread internal/engine/codex.go
… rotation

All seven findings from the fifth self-hosted round were valid:

- fireCodexReview finalizes under the sameRound guard (a same-head
  replacement round with a new Seq no longer inherits an old post's
  result), and a FAILED post keeps its claim — the claim TTL is the retry
  backoff, so clearing it early bypassed codexClaimTTL.
- threadRebuttal requires the strict bot-finding → agent-reply → bot
  last-word shape: a human-started thread a bot merely answered no longer
  fabricates a contested finding.
- The severity floor moved into dialect (FloorSeverity) — no severity
  literals in orchestration code.
- sweepParkedClosed rotates its inspected candidate across pumps so one
  long-cooldown open PR cannot starve the closed-PR check for every
  parked round behind it.
- latestCodexEvidence keeps prev strictly older than latest, so a review
  and its clean summary in the same second don't collapse the command
  window and misclassify a commanded review as automatic.
- The parked-closed replay asserts the abandoned round lands in the
  Archive (never-delete invariant), not merely leaves Rounds.
- Codex's second boilerplate variant (create account / connect) joins the
  dialect notice corpus.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 86c0c15077

ℹ️ 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".

Comment thread internal/crq/service.go Outdated
Comment thread internal/crq/service.go
Comment thread internal/engine/progress.go
Comment thread internal/crq/service.go
Comment thread internal/crq/service.go
- Pump no longer returns blocked/min_interval before observe+DecideFire:
  the engine resolves quota-free verdicts (dedupe, FireCodexOnly,
  FireCoReviewWait) ahead of those gates, and mapFireNo still reports the
  same actions for real fires. The early return was re-adding the gate the
  engine had deliberately moved.
- The co-review wait anchor falls back to the primary bot's head-review
  time when no @codex command exists, so a pre-pump legacy clean summary
  from auto-review counts instead of hiding until the deadline.
- The adopt path takes the Codex self-heal claim in the same CAS that
  records the fire — the fired state is never visible unclaimed.
- The poisoned-marker repair keeps a completed round whose primary review
  is real but whose co-bot is pending (a deliberate dedupe), instead of
  deleting it into ack-and-dedupe churn.
Bots deliver in waves: Codex auto-reviews a pushed head minutes after the
push, and CodeRabbit's real review can trail its comment shells. The loop
exited 0 on the first converged observation, so a wave landing moments
later was found by a human re-checking the PR instead of by crq — exactly
the failure this tool exists to prevent.

The loop now records when it first observes convergence and keeps polling
for CRQ_SETTLE (default 90s): anything new inside the window flips the
verdict back to the normal findings/pending flow, and only a quiet window
exits 0. The wait deadline is suppressed while settling (the settle is
its own bound). Replay scenarios pin both directions; scenario configs
disable the window where they assert immediate verdicts.
@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f1105bb59

ℹ️ 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".

Comment thread internal/crq/observe.go Outdated
Comment thread internal/crq/service.go
Comment thread internal/crq/config.go
- Update the README and llms.txt setup snippets to the v3 default state ref
  crq-state-v3, so following the docs no longer bypasses the protective default
  and risks a v3 client overwriting a v2 payload on the old crq-state ref.
- Ignore a `@codex review` command Codex already answered with a review when
  gathering adoptable Codex commands: on a regular push whose commit date
  predates that consumed command, the command survives the cutoff, and treating
  it as live suppressed the Codex command the new head still needed.
- Record the adopted Codex command id in the adopt fire's CAS write when crq is
  not posting Codex, so the self-heal scan (anchored on FiredAt) never re-posts a
  Codex command that was posted before the adopted CodeRabbit command.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant