Skip to content

Keep the fleet's policy in one place instead of on every machine - #58

Merged
kristofferR merged 39 commits into
mainfrom
feat/fleet-config
Jul 28, 2026
Merged

Keep the fleet's policy in one place instead of on every machine#58
kristofferR merged 39 commits into
mainfrom
feat/fleet-config

Conversation

@kristofferR

@kristofferR kristofferR commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Per-host configuration files diverge the moment somebody edits one, and nothing says so: each host is behaving correctly according to what it can see.

That happened today. A repository was excluded on this host and still reviewed by the Mac, because "which repositories crq covers" was an environment variable on two machines rather than a decision the fleet had made.

The rule this follows

internal/state/state.go already argues it, on the per-repository reviewer map:

a daemon and an agent reading different configurations while writing one shared state ref is a new class of divergence. Both already read this ref, so both cannot disagree about it.

This applies the same reasoning one level up, to the settings that say what the fleet does at all.

The split

Fleet — in the state ref, via crq config: scope, repos, exclude, required-bots, min-interval, rate-limit-fallback, calibrate-ttl, settle, skip-marker.

Per host — stays in the environment, three kinds and no more:

  • where the state lives (CRQ_REPO, CRQ_ISSUE, CRQ_STATE_REF) — a host cannot read fleet policy until it knows where to look;
  • credentials;
  • what the machine can physically do (CRQ_DISPATCH_CMD, CRQ_WORKSPACE, CRQ_DISPATCH_CONCURRENCY) — genuinely not the same answer everywhere.

Adoption

Recorded wins over the environment, so this goes in one machine at a time: until something is recorded, every host keeps using its own. crq config seed writes this host's current answers for whatever the fleet has not decided yet.

Seeding never overwrites a recorded value — otherwise the last machine to run it wins, which is the divergence this is meant to end.

Failure modes

  • A value this binary cannot read is refused at crq config set. The ref is shared, so a value only some hosts can parse breaks the fleet from the inside.
  • One arriving anyway from a newer crq leaves the host on its own value rather than acting on half a policy, and says so once rather than every pass.
  • crq doctor reports both that and any variable still set locally that the fleet overrides — naming the variable, since the remedy is to delete it.
  • Stored as a flat map, so an older binary round-trips settings it does not know instead of dropping them.

Ref #42

Summary by CodeRabbit

  • New Features
    • Added crq config with show, set, unset, and seed to manage fleet policy in shared state.
    • Added/expanded crq reviewers commands for selecting bots and required reviewers.
  • Bug Fixes
    • Improved fleet-consistent behavior across enqueue/watch/drain/quota/feedback, including safer dry-run previews and stronger rechecks at claim/apply time.
    • Fleet-scoped timing and fallback rules are now applied more reliably.
  • Documentation
    • Updated persisted state compatibility to schema v5, including CRQ_STATE_REF schema version and fencing notes.

The reviewers were a property of the process: one CRQ_COBOTS for every
repository the fleet touches. A repo that only wants Codex, or one where
Bugbot should gate, had no way to say so — and the answer to "which bots
do you want here" was to edit a global env file and restart a daemon.

crq reviewers <repo> now reports who runs there and what each one costs;
crq reviewers set chooses them; crq reviewers clear goes back to the
fleet default. Each reviewer reports its budget, which is the only
property the queue cares about: account is serialized against the shared
CodeRabbit allowance, none runs immediately and waits for nobody.

The configuration lives in the shared state ref rather than a .crq.yaml
the repository carries. The daemon has no checkout of the repositories it
reviews, so an in-repo file would be invisible to it or cost a REST fetch
per PR — and a daemon and an agent reading different configurations while
writing one state ref is a class of bug worth not having.

The override reaches the derived views, not just the stored list: after
applying it, Reviewers, RequiredBots and FeedbackBots are rebuilt exactly
as LoadConfig builds them, so crq cannot gate on or surface findings from
a bot the repository excluded. "Chosen to be none" is kept distinct from
"not chosen".

The primary stays fleet-wide. Its markers and command are compiled into
the dialect classifiers when the Service is built, so a per-repo primary
means per-repo classifiers.
Eight review findings, two of them P1.

Choices resolved against the fleet's enabled list, so the feature could
only ever subtract: with CRQ_COBOTS=codex, asking a project for bugbot
was rejected as unknown though it is a registered reviewer. They resolve
against the registry now, and a bot the fleet does not enable is built
from its registry entry. Naming a bot in --required enables it too, the
rule fleet parsing already follows — one that gates but is never
triggered waits forever.

A primary that is itself a registry bot keeps its silenced entry through
an override. That entry carries its wording and check-run hooks, so
dropping it costs the PRIMARY its evidence and the round waits out a
clean result crq can no longer read. Same defect as the fleet path had,
one layer up.

A second call naming one half no longer wipes the other: the existing
override is loaded and merged, which is what the nil-versus-empty
contract promised. An empty --required is refused, because gating on
nobody makes Feedback converge before any chosen reviewer runs, and
clear takes no mutation flags — ignoring them turned a malformed call
into a silent wipe.

The Codex-only degrade is gated on Codex being a reviewer here, or an
unsolicited auto-review would release the head during an account block
on behalf of a bot the project excluded.

And sharing a ref only stops an old binary ERASING the field; it does not
make it act on one. State now records which hosts write it and what they
understand, and `crq reviewers` names any host driving the queue that
predates overrides, instead of implying agreement.
Nine review findings, four of them P1, and they converge on one mistake:
the override changed who has to answer without changing who gets asked.

`--required bugbot` with no `--bots` set the required half only, so
nothing enabled Bugbot and it became an unknown reviewer with no command.
Required now implies enabled whichever half was named, the rule fleet
parsing already follows. A retained co-reviewer that becomes required is
promoted out of a never trigger for the same reason — Codex's fleet entry
is never while it is optional, so keeping that entry and flipping only the
Required bit left the engine waiting for evidence no command was posted
for. Only a never trigger is promoted, so a deliberate selfheal survives.

Autoreview decided from the primary alone, so a repo requiring Codex whose
head CodeRabbit had already reviewed was never enqueued and Codex was
never asked. It evaluates the repository's own required set now.

And changing the requirements on a head with a completed round stranded
the PR outright: convergence reported the new reviewer pending while
enqueue kept skipping the head, because a completed round IS the dedup
marker. Those rounds are reopened when the required set actually changes.

Three more. The two halves are merged inside the CAS closure, or two
hosts setting different halves both derive from one snapshot and the later
write drops the other's. An explicit CRQ_FEEDBACK_BOTS survives an
override, since it is the one list an operator may widen beyond who
reviews. And the lagging-host check now matches the leader's "host=x
pid=n" form against the host key capabilities are recorded under —
otherwise every current-version daemon read as needing an upgrade.

The docs no longer call the Codex example Codex-only: an override chooses
the co-reviewers, so the primary is still triggered and still spends
quota. Leaving it out of --required means the round does not wait for it.
Three of four findings; the fourth is answered rather than fixed.

`crq reviewers owner/repo --bots codex` is a set command missing its
verb. It showed the configuration and exited 0, which tells automation
the mutation worked when nothing changed. It is refused now, naming the
verb.

`crq reviewers clear owner-repo` cleared a key nothing uses and exited 0,
so a typo left the real override in force while the caller believed it
had restored the fleet default. Clear validates the shape set already
validates.

On the fourth: reopening a completed round can re-ask a primary that
finished with a completion reply and no Review object, because
DecideFire's already-reviewed gate reads submitted reviews. Marking the
round co-only does not help — that flag records what a fire DID, it does
not gate one — and the alternatives all need an observation the CAS
closure cannot make. So the trade is stated in the code: one duplicate
review in an uncommon case, against a PR stranded silently and
permanently. The loud failure is the better one, and the gate is where a
real fix belongs.
Four P1s, and the first retires a trade-off I had argued for instead of
fixing.

Reopening a completed round could re-ask a primary that answered with a
completion reply and no Review object, because the already-reviewed gate
read only obs.Reviews. The reviewer's point was that the gate is where
the fix belongs, and they were right: it now also counts a completion
reply paired to that round's own command. Reopening no longer risks a
duplicate review, so the round can be requeued when the required set
changes without the cost I had written into a comment.

A repository enabling a bot the fleet disabled was built from registry
defaults, silently discarding the operator's CRQ_COBOT_<NAME>_CMD,
TRIGGER and GRACE — a different bot than the one configured. Every
registry co-reviewer is now resolved with the environment applied,
enabled or not, and an override picks from that.

Capabilities are keyed per PROCESS rather than per host. A new CLI and an
old daemon on one machine is the ordinary upgrade, and a per-host key let
the CLI's write vouch for the daemon that had not been upgraded — hiding
exactly the case the signal exists to catch.

And applyFire revalidates the override before acting. Deciding and
writing are two steps, so an operator removing a co-reviewer between them
would otherwise have crq post that bot's trigger on the authority of a
configuration that no longer exists.
Ten findings across both reviewers; two are answered rather than fixed.

Changing a repository's required set requeued every completed round it
had. Rounds are never deleted, so a repository's merged and closed PRs
stay behind as completed dedup markers: on a busy repo that hands Pump
hundreds of dead rounds to observe and drop, one per tick, ahead of every
real one. A stranded PR is by definition an open one, so the requeue asks
GitHub which PRs are open first and touches only those.

Reopening also stamped LastAttemptAt, which is the adoption floor for a
FAILED attempt. Raising it discarded a newly required co-reviewer's own
unanswered trigger as too old to adopt, so crq posted that bot a second
request for the very round the reopen exists to let it answer.

The reopened round's already-reviewed gate read the ADOPTION helper,
which deliberately omits Completion's prior-review requirement and its
failed-summary guard — it answers "was this command spoken for", a
weaker question. A completion reply nothing backs would have written a
completed marker that every later same-head check skips, forever.

Capabilities are keyed per process, but a round recorded its bare
hostname, which no writer entry can match. Every `crq reviewers` call
during a fire therefore named the current process in lagging_hosts and
told the operator to upgrade a binary that already understands
overrides. The round now records the writer id; the dashboard still
prints the machine name.

applyFire re-read the state on EVERY verdict to revalidate the override,
including the FireNo no-op that is most of what a pump does — an extra
four API calls per tick against the git-backed store. Only the verdicts
that post a configuration-chosen command pay for it now.

Setting reviewers with neither half named wrote a fresh UpdatedAt
anyway, and that timestamp IS the override's identity, so a no-op call
discarded every in-flight fire decision for the repository. Reading a
repository now applies the shape check set and clear apply, so
`crq reviewers show` fails instead of reporting the fleet default for a
repository nobody has.

The help conflated budget with requiredness ("none ... waits for
nobody"), required --bots for an update the handler accepts with
--required alone, and offered an empty value for a flag that refuses
one.

Not fixed: feedback.go reading isConfiguredBot from the fleet config
rather than the repo's is identical by construction — the primary is not
overridable, so ForRepo never touches Config.Bot. And SetReviewers'
store.Update is an operator's explicit mutation, not the apply phase of
a review decision: Enqueue and Cancel write outside it too, and DryRun
means "request no reviews", not "silently ignore a configuration
command".
A reviewer change requeues a repository's completed rounds, but three
paths let one slip through and leave the PR waiting on a bot crq never
asks.

A closed PR's round was skipped, and closed is not final: reopened at
the same head, its completed round is the dedup marker that hides the
requirement added while it was shut. Mark those rounds instead of
requeueing them — requeueing dead work would put it ahead of every live
round — and let whichever enqueue path next finds the PR alive reopen it.

FireDedupe was exempt from the apply-time revalidation because it posts
nothing. But its completed round asserts that everyone gating the head
has answered, and an override landing after the decision finds the round
still queued, so nothing requeues it. Revalidate dedupe too, and report
the refusal as the lost race it is rather than as a dedupe.

`crq reviewers <repo>` accepted "owner/", "/name" and "owner/name/extra".
The read path never contacts GitHub, so a typo printed the fleet default
and exited 0, reading as a report about a project crq follows.
Two P1/P2s from Codex, both about a decision outliving the state it was
made from.

Convergence and slot release were the same question. A repository whose
required set omits the primary — required Codex only — converges the
moment Codex answers, so Progress completed the round and released the
fire slot while the `@coderabbitai review` it had posted was still
unacknowledged. The next PR could then spend the account allowance
alongside it, which is the one thing the queue exists to prevent, and a
late account-block reply landed on a completed round nothing progresses.
A round that spent the quota now stays fired until the primary reacts or
replies, or until its in-flight timeout gives up on it; at that timeout a
converged round completes rather than buying a second metered review no
configured reviewer asked for. A co-only round posted no command of its
own and still completes at once.

The override revalidation ran in applyFire, one read before the effect's
own write. SetReviewers committing in that window left the effect
applying a decision the new configuration would not have made — for
FireDedupe permanently, since dedupe posts nothing, the round is still
queued when the override lands (requeuing a queued round is a no-op), and
the marker it writes is what stops the newly required reviewer from ever
being asked. The check now runs inside each effect's own CAS mutation:
the dedupe write, the slot reservation, and the co-reviewer trigger
claims. That is the commit point in every case — nothing is posted before
it — so the window is gone, and the separate Load with it.
Progress now keeps the slot until the primary acknowledges the command a
round fired, but the loop releases that slot too: completeWaitRound ends
the wait by completing the round, and the two convergence callers reach
it on exactly the case the engine rule was written for — a required set
that omits the primary, converged on a co-reviewer alone while the
`@coderabbitai review` this round posted is still unanswered.

Ask the engine the same question there (PrimaryAckPending, exported for
it) and leave such a round fired. Its head is already the dedup marker,
so nothing re-fires it, and any Pump finishes it on the acknowledgement
or on the in-flight timeout. The timed-out and never-fired waits still
end unconditionally: neither is a claim that the round is done.
Four gaps review found in the reviewer override, each one a decision made
from a configuration that no longer holds by the time it is written.

Completion is the one that cannot be undone: reopenForChangedReviewers
leaves an in-flight round alone because it is already going to answer, so
a required reviewer added between Progress deciding and applyTransition
writing is caught by neither — the completed marker dedupes the head
under the set that no longer gates it. Revalidate the override inside
that write, as the fire paths already do. The self-heal sweep needs the
same: its claim is what authorizes the post, and it was the one trigger
path that could still post for a bot `crq reviewers set` had removed.

A repository may also name the primary when the fleet configuration
carried no entry for it — not enabled, not fleet-required — and skipping
it as "not a co-reviewer here" costs the PRIMARY the wording and
check-run hooks its own evidence is read through, so a check-only clean
result is never fetched. Add the silenced entry the fleet parse would
have had. And promoteTrigger can now tell a `never` that fell out of the
registry defaults from one CRQ_COBOT_<NAME>_TRIGGER named: the fleet
parse lets the operator's value win over the required trigger, and a
per-repo requirement must not switch a command back on behind them.

Finally, writer identity: host and pid do not name a process over time. A
container daemon restarts as pid 1 and pids are reused, so an old binary
could inherit a capable predecessor's recorded capabilities and vouch for
itself while ignoring every override. Stamp a per-run id, and let the
autoreview leader use WriterID rather than spelling the format twice.
Progress and the loop both keep a converged round `fired` when the primary
has not acknowledged the command it posted, so the slot is not handed to
the next pull request while the account allowance is still in flight. But
staying `fired` holds the slot only for as long as the round exists, and
converging is exactly what tells the agent to push: `Enqueue` supersedes
the round, `Normalize` drops a slot no round holds, and the next `Pump`
buys a second metered review while the first is unanswered.

The obligation belongs to the command, not to the head it was posted at.
The loop now stamps `FireSlot.HoldUntil` in the same write that declines
to complete the round, bounded by that command's in-flight window — the
deadline crq would have given up at anyway, so an unanswered command
cannot wedge the queue for longer than waiting for it would have. The
fire gates ask `SlotHeld` rather than "is a round holding it", and
`Normalize` keeps a slot the hold still covers.
Per-host configuration files diverge the moment somebody edits one, and nothing
says so: each host is behaving correctly according to what it can see. Today one
repository was excluded here and still reviewed by the Mac, because "which
repositories crq covers" was an environment variable on two machines rather than
a decision the fleet had made.

This repository already argues the point, on the per-repository reviewer map: a
daemon and an agent reading different configurations while writing one shared
state ref is a new class of divergence, and both already read that ref. The same
reasoning applies one level up, to the settings that say what the fleet does at
all.

So scope, repos, exclude, required-bots, min-interval, rate-limit-fallback,
calibrate-ttl, settle and skip-marker move into the state ref, reachable through
crq config. Three kinds of setting deliberately stay local: where the state
lives — a host cannot read fleet policy until it knows where to look —
credentials, and what a machine can physically do, which is genuinely not the
same answer everywhere.

Recorded wins over the environment, so adoption is one machine at a time: until
something is recorded every host keeps its own, and crq config seed writes this
host's answers for whatever the fleet has not decided. Seeding never overwrites
a recorded value, or the last machine to run it would win — the divergence this
is meant to end.

A value this binary cannot read is refused where it is set, and one arriving
from a newer crq leaves the host on its own value rather than acting on half a
policy; crq doctor reports that, and any variable still set locally that the
fleet overrides. Stored as a flat map so an older binary round-trips settings it
does not know rather than dropping them.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 891ff314-f31c-46b9-bcd6-83431b7bccb9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Fleet policy is persisted in schema v5, applied to queue and reviewer workflows, reconciled against existing rounds, rendered in dashboards, and exposed through configuration and reviewer CLI commands. Drain installation, quota handling, watch passes, and documentation now use fleet-aware settings.

Changes

Fleet policy rollout

Layer / File(s) Summary
Persisted fleet state and dashboard rendering
internal/state/*, internal/crq/state.go, internal/crq/init.go, AGENTS.md
Schema v5 adds persisted fleet configuration and capability fencing; dashboard rendering, status output, initialization, and migration tests use effective fleet settings.
Fleet policy registry and reconciliation
internal/crq/fleetconfig.go, internal/crq/fleetcmd.go, internal/crq/reviewers.go, internal/crq/config.go, internal/crq/*fleetconfig_test.go
Fleet settings are parsed, validated, overlaid onto host configuration, mutated through set/unset/seed operations, checked for divergence, and reconciled against reviewer rounds.
Fleet-aware queue and reviewer execution
internal/crq/{auto,watch,service,feedback,next,cliquota,repoconfig,drainswitch,threads,tidy}.go, internal/crq/*_test.go
Queueing, dispatch, quota, feedback timing, reviewer overrides, thread detection, tidying, drain settings, and self-healing use state-derived policy and reject stale fleet revisions or excluded repositories.
CLI controls and drain installation
cmd/crq/main.go, internal/crq/drain.go, README.md, llms.txt, skills/coderabbit-queue/SKILL.md
Adds crq config, fleet-aware reviewer controls, divergence reporting, host-only drain previews, effective-policy drain rendering, help text, and configuration documentation.

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

Possibly related PRs

Poem

A rabbit found policies tucked in the state,
And taught every queue round to patiently wait.
Fleet bots chose reviewers, dashboards shone bright,
Stale plans lost races and yielded the right.
“Hop,” said the rabbit, “schema five is the way!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.44% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s main shift from per-host policy to shared fleet state.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fleet-config

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

# Conflicts:
#	AGENTS.md
#	README.md
#	cmd/crq/main.go
#	internal/crq/auto.go
#	internal/crq/config.go
#	internal/crq/feedback.go
#	internal/crq/reviewers.go
#	internal/crq/service.go
#	internal/crq/state.go
#	internal/state/dashboard.go
#	internal/state/state.go
#	llms.txt
#	skills/coderabbit-queue/SKILL.md
@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: b1b7da34d6

ℹ️ 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/fleetconfig.go
Comment thread internal/crq/fleetcmd.go Outdated
Comment thread internal/crq/fleetconfig.go Outdated
Comment thread internal/crq/fleetconfig.go Outdated
Comment thread internal/crq/fleetconfig.go
Comment thread internal/crq/fleetcmd.go Outdated
Comment thread internal/crq/fleetcmd.go Outdated
Comment thread internal/crq/reviewers.go
Comment thread internal/crq/fleetconfig.go
Comment thread internal/crq/reviewers.go
@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: 4e5f21e7ff

ℹ️ 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/fleetconfig.go
Comment thread internal/crq/fleetconfig.go Outdated
Comment thread internal/crq/fleetcmd.go Outdated
Comment thread internal/crq/fleetconfig.go Outdated
Comment thread internal/crq/cliquota.go
Comment thread internal/crq/fleetcmd.go Outdated
Comment thread internal/crq/fleetconfig.go Outdated
Comment thread internal/crq/fleetconfig.go Outdated
@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: 342671178e

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

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 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: 5

🤖 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 `@cmd/crq/main.go`:
- Around line 80-105: Rework the dry-run flow around parseDrainArgs, LoadConfig,
NewGitHub, and GitStateStore.Load so the normal authenticated path proceeds
through the common install logic first without performing a discarded probe.
Only when that path cannot load GitHub fleet state should it construct and print
the host-only DrainPlan with PolicySource "host" and the existing warning; avoid
repeating argument parsing, configuration loading, GitHub creation, or state
reads.
- Around line 1139-1142: Update the fleet settings enumeration in the crq config
help text to include feedback-bots alongside the other fleet-owned settings.
Keep the existing ordering and wording unchanged for all other entries, and use
the registered feedbackBotsKey name.
- Around line 709-712: Remove one of the duplicate three-line `crq reviewers`
command blocks from `usage()` in `cmd/crq/main.go`, leaving a single complete
reviewers/help section and preserving the surrounding usage text.

In `@internal/crq/fleetconfig.go`:
- Around line 62-63: Memoize the static registry returned by fleetSettings using
a read-only cache such as sync.OnceValue, ensuring the map is initialized once
from the dialect registry and subsequently shared safely. Update callers
including FleetKeys, fleetCfg, applyFleet, ValidateFleetSetting, and FleetConfig
to avoid rebuilding or mutating the cached map while preserving existing
behavior.

In `@README.md`:
- Around line 516-519: Update README.md lines 516-519 to describe the primary
reviewer as fleet-wide rather than a per-host setting, while preserving its
association with CRQ_BOT, CRQ_REVIEW_CMD, and the related markers. Make no
changes to llms.txt lines 222-228 or skills/coderabbit-queue/SKILL.md lines
200-206.
🪄 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 Plus

Run ID: 17bcd4a7-0553-4a31-bd3a-66ee06fa83f2

📥 Commits

Reviewing files that changed from the base of the PR and between 00c377a and 3426711.

📒 Files selected for processing (40)
  • AGENTS.md
  • README.md
  • cmd/crq/main.go
  • internal/crq/auto.go
  • internal/crq/cliquota.go
  • internal/crq/cliquota_test.go
  • internal/crq/config.go
  • internal/crq/drain.go
  • internal/crq/drain_test.go
  • internal/crq/drainswitch.go
  • internal/crq/drainswitch_test.go
  • internal/crq/feedback.go
  • internal/crq/feedback_test.go
  • internal/crq/fleetcmd.go
  • internal/crq/fleetconfig.go
  • internal/crq/fleetconfig_test.go
  • internal/crq/init.go
  • internal/crq/init_test.go
  • internal/crq/next.go
  • internal/crq/replay_test.go
  • internal/crq/repoconfig.go
  • internal/crq/repoconfig_test.go
  • internal/crq/reviewers.go
  • internal/crq/service.go
  • internal/crq/service_test.go
  • internal/crq/state.go
  • internal/crq/watch.go
  • internal/crq/watch_test.go
  • internal/state/dashboard.go
  • internal/state/dashboard_sync_test.go
  • internal/state/dashboard_test.go
  • internal/state/fleet.go
  • internal/state/state.go
  • internal/state/statusline.go
  • internal/state/store.go
  • internal/state/store_version_test.go
  • internal/state/tolerant.go
  • internal/state/writers_test.go
  • llms.txt
  • skills/coderabbit-queue/SKILL.md
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
internal/state/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/state/**/*.go: Persist one Round per PR and never delete rounds; use transitions or archival when superseded, preserving reviewed-head deduplication records.
Preserve legacy Codex fields through dual writes and normalization, and round-trip unknown JSON members for forward compatibility; schema v5 remains an intentional compatibility fence.

Files:

  • internal/state/tolerant.go
  • internal/state/statusline.go
  • internal/state/dashboard_test.go
  • internal/state/dashboard.go
  • internal/state/fleet.go
  • internal/state/writers_test.go
  • internal/state/store.go
  • internal/state/dashboard_sync_test.go
  • internal/state/store_version_test.go
  • internal/state/state.go
internal/crq/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/crq/**/*.go: Keep internal/crq orchestration-only: observe, decide, and apply effects through the service; it must not contain decision rules or bot-specific wording.
service.go is the only effects executor for CAS state writes and PostIssueComment; DryRun must report without writing.

Files:

  • internal/crq/init_test.go
  • internal/crq/init.go
  • internal/crq/replay_test.go
  • internal/crq/next.go
  • internal/crq/cliquota_test.go
  • internal/crq/reviewers.go
  • internal/crq/drainswitch_test.go
  • internal/crq/config.go
  • internal/crq/feedback.go
  • internal/crq/drainswitch.go
  • internal/crq/repoconfig.go
  • internal/crq/feedback_test.go
  • internal/crq/auto.go
  • internal/crq/fleetconfig.go
  • internal/crq/fleetcmd.go
  • internal/crq/cliquota.go
  • internal/crq/state.go
  • internal/crq/watch_test.go
  • internal/crq/fleetconfig_test.go
  • internal/crq/drain_test.go
  • internal/crq/repoconfig_test.go
  • internal/crq/watch.go
  • internal/crq/service_test.go
  • internal/crq/service.go
  • internal/crq/drain.go
internal/crq/config.go

📄 CodeRabbit inference engine (AGENTS.md)

A new co-reviewer that should ship enabled by default must be added to parseCoBots.

Files:

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

Timestamp: 2026-07-28T00:12:23.246Z
Learning: Maintain the dependency direction `dialect ← engine ← crq`, `state ← crq`, `gh ← {state, crq}`, and `workspace ← crq`, with no dependency cycles.
Learnt from: CR
Repo: kristofferR/coderabbit-queue

Timestamp: 2026-07-28T00:12:23.246Z
Learning: Use the distinct vocabulary consistently: GitHub REST quota is `Throttle`; CodeRabbit account quota is `AccountQuota` or `account blocked`; literal `rate limit` belongs only in `gh` and `dialect`.
Learnt from: CR
Repo: kristofferR/coderabbit-queue

Timestamp: 2026-07-28T00:12:23.246Z
Learning: The loop exit-code contract is frozen: 0 for converged/skipped/held, 10 for findings, and 2 for timeout; a hold is terminal and must not be reported as timeout.
📚 Learning: 2026-07-27T01:11:18.244Z
Learnt from: kristofferR
Repo: kristofferR/coderabbit-queue PR: 57
File: internal/crq/feedback.go:91-92
Timestamp: 2026-07-27T01:11:18.244Z
Learning: When reviewing code in `internal/crq` that deals with bot configuration, keep the separation of concerns intact: `Config.isConfiguredBot(login)` should only be evaluated against the fleet-wide primary `Config.Bot`. Do not override or reinterpret `Config.Bot` based on repo-specific reviewer/co-reviewer data. If repo-added co-reviewers are involved, they must be handled via repo-derived bot sets (e.g., `Config.evidenceBots()`), and not via `isConfiguredBot`. `Config.ForRepo(...)` should adjust co-reviewer-derived fields like `CoBots`, `RequiredBots`, `Reviewers`, and `FeedbackBots`, but intentionally not change the meaning of `Config.Bot` used by `isConfiguredBot`.

Applied to files:

  • internal/crq/init_test.go
  • internal/crq/init.go
  • internal/crq/replay_test.go
  • internal/crq/next.go
  • internal/crq/cliquota_test.go
  • internal/crq/reviewers.go
  • internal/crq/drainswitch_test.go
  • internal/crq/config.go
  • internal/crq/feedback.go
  • internal/crq/drainswitch.go
  • internal/crq/repoconfig.go
  • internal/crq/feedback_test.go
  • internal/crq/auto.go
  • internal/crq/fleetconfig.go
  • internal/crq/fleetcmd.go
  • internal/crq/cliquota.go
  • internal/crq/state.go
  • internal/crq/watch_test.go
  • internal/crq/fleetconfig_test.go
  • internal/crq/drain_test.go
  • internal/crq/repoconfig_test.go
  • internal/crq/watch.go
  • internal/crq/service_test.go
  • internal/crq/service.go
  • internal/crq/drain.go
📚 Learning: 2026-07-27T01:11:20.071Z
Learnt from: kristofferR
Repo: kristofferR/coderabbit-queue PR: 57
File: internal/crq/repoconfig.go:163-183
Timestamp: 2026-07-27T01:11:20.071Z
Learning: In internal/crq, treat the “observe → decide → apply” effects restriction as applying only to effects produced by review decisions. Explicit operator mutation commands (e.g., Service.SetReviewers, Service.ClearReviewers, enqueue/cancel flows) are allowed to perform state mutations outside the standard apply path, so review logic should not incorrectly require them to follow observe/decide/apply.

For CRQ_DRY_RUN: it should suppress review requests and fire-record writes, but it must not make explicit configuration/mutation commands (e.g., `crq reviewers set`) silently succeed without persisting the requested changes.

Ensure Service.applyFire revalidates repository reviewer overrides just before posting, to protect against stale/changed overrides since earlier reads.

Applied to files:

  • internal/crq/init_test.go
  • internal/crq/init.go
  • internal/crq/replay_test.go
  • internal/crq/next.go
  • internal/crq/cliquota_test.go
  • internal/crq/reviewers.go
  • internal/crq/drainswitch_test.go
  • internal/crq/config.go
  • internal/crq/feedback.go
  • internal/crq/drainswitch.go
  • internal/crq/repoconfig.go
  • internal/crq/feedback_test.go
  • internal/crq/auto.go
  • internal/crq/fleetconfig.go
  • internal/crq/fleetcmd.go
  • internal/crq/cliquota.go
  • internal/crq/state.go
  • internal/crq/watch_test.go
  • internal/crq/fleetconfig_test.go
  • internal/crq/drain_test.go
  • internal/crq/repoconfig_test.go
  • internal/crq/watch.go
  • internal/crq/service_test.go
  • internal/crq/service.go
  • internal/crq/drain.go
🪛 LanguageTool
skills/coderabbit-queue/SKILL.md

[style] ~200-~200: To strengthen your wording, consider replacing the phrasal verb “leave out”.
Context: ... override chooses the co-reviewers. Leaving the primary out of --required means the round does not w...

(OMIT_EXCLUDE)

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

419-430: LGTM!

Also applies to: 484-497, 535-579

internal/state/fleet.go (1)

24-76: LGTM!

internal/crq/init.go (1)

41-52: LGTM!

internal/crq/init_test.go (1)

14-43: LGTM!

internal/crq/fleetconfig.go (1)

550-613: LGTM!

internal/crq/fleetcmd.go (2)

43-82: LGTM!

Also applies to: 87-158, 168-240


457-482: 🩺 Stability & Availability

No change needed. EndRound only archives the matching round, deletes its st.Rounds entry, and never inserts a new key during iteration.

internal/crq/fleetconfig_test.go (1)

18-1181: LGTM!

internal/crq/reviewers.go (1)

324-378: LGTM!

Also applies to: 389-392

cmd/crq/main.go (2)

539-600: LGTM!


1907-1922: 🩺 Stability & Availability

No change needed. GitStateStore.logf nil-checks before calling s.log.Printf, so passing a nil logger via NewGitStateStore(..., nil) is safe here.

internal/crq/repoconfig.go (1)

166-184: LGTM!

Also applies to: 208-213

internal/state/store.go (2)

84-85: LGTM!

Also applies to: 140-203, 254-256, 383-383


101-133: 🎯 Functional Correctness

No change needed for SyncDashboard rendering config.

internal/state/dashboard.go (1)

259-260: LGTM!

Also applies to: 414-415

internal/state/statusline.go (1)

18-19: LGTM!

internal/state/dashboard_sync_test.go (1)

92-248: LGTM!

internal/state/dashboard_test.go (1)

936-948: LGTM!

internal/state/store_version_test.go (1)

79-129: LGTM!

internal/crq/next.go (1)

177-183: LGTM!

Also applies to: 253-264

internal/crq/replay_test.go (1)

238-256: LGTM!

internal/crq/drain.go (1)

27-45: LGTM!

Also applies to: 55-84, 176-265, 377-536

internal/crq/drain_test.go (1)

15-15: LGTM!

Also applies to: 73-148, 254-338

internal/state/tolerant.go (1)

21-22: LGTM!

internal/state/writers_test.go (1)

8-13: LGTM!

Also applies to: 102-126

internal/crq/state.go (1)

33-34: LGTM!

Also applies to: 53-56, 102-113

AGENTS.md (1)

33-33: LGTM!

Also applies to: 42-44

internal/crq/config.go (1)

86-96: LGTM!

Also applies to: 274-281

README.md (1)

400-402: LGTM!

Also applies to: 491-515, 520-528

llms.txt (1)

214-220: LGTM!

skills/coderabbit-queue/SKILL.md (1)

185-199: LGTM!

internal/crq/drainswitch.go (1)

84-91: LGTM!

Also applies to: 104-130

internal/crq/drainswitch_test.go (1)

58-78: LGTM!

internal/crq/auto.go (1)

6-6: LGTM!

Also applies to: 224-253, 254-277, 317-317

internal/crq/watch.go (2)

180-253: LGTM!

Also applies to: 267-269, 809-823


299-337: 🚀 Performance & Scalability

No change needed. GitHub REST GET calls are already retried with cached ETags, so unchanged ref/commit/tree/blob URLs are revalidated without consuming quota.

			> Likely an incorrect or invalid review comment.
internal/crq/service.go (2)

51-86: LGTM!

Also applies to: 228-291, 305-352, 593-625, 743-789, 1049-1073, 1529-1612, 1841-1886, 1909-2013, 2052-2118, 2252-2259


857-878: 🗄️ Data Integrity & Integration

No change needed. overrideChanged already rejects committed OutRetry transitions when st.FleetRevision differs by comparing the full effective FleetRevision, so fleet timing/rate-limit changes are covered.

internal/crq/cliquota.go (1)

65-75: LGTM!

Also applies to: 84-84, 94-98, 110-130

internal/crq/feedback.go (1)

705-705: LGTM!

Also applies to: 867-875, 910-915

internal/crq/repoconfig_test.go (1)

70-101: LGTM!

Also applies to: 697-697, 868-918, 1000-1030, 1036-1069

internal/crq/service_test.go (1)

76-76: LGTM!

Also applies to: 268-273, 425-425, 461-461, 630-710, 721-756, 758-872, 2504-2598

internal/crq/watch_test.go (1)

692-695: LGTM!

Also applies to: 811-839, 1098-1111, 1127-1275

internal/crq/cliquota_test.go (1)

6-6: LGTM!

Also applies to: 102-148

internal/crq/feedback_test.go (1)

103-141: LGTM!

Also applies to: 2404-2442

Comment thread cmd/crq/main.go Outdated
Comment thread cmd/crq/main.go Outdated
Comment thread cmd/crq/main.go Outdated
Comment thread internal/crq/fleetconfig.go
Comment thread README.md
An authenticated `crq drain install --dry-run` probed the state ref only to
decide it should fall through, then paid for the same read again inside
InstallDrain. Move the host-only fallback to where the state is actually read,
so the preview costs one read: main now answers only the host that cannot get
that far — no credentials, or no CRQ_REPO to look in.

Also drop the duplicate `crq reviewers` block from `crq help`, and name
feedback-bots among the fleet settings `crq config` enumerates.
@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: ca3f36ad17

ℹ️ 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/watch.go
Comment thread internal/crq/reviewers.go
Comment thread internal/crq/fleetconfig.go
Comment thread internal/crq/service.go
Comment thread internal/crq/drain.go Outdated
Comment thread internal/crq/autofix.go
Comment thread internal/crq/fleetcmd.go
Comment thread internal/crq/init.go
Comment thread internal/crq/fleetconfig.go
Six places still decided from this host's startup configuration while the
fleet's was in hand:

- an observed account block was discarded whenever ANY fleet setting had
  moved since the observation. Update reports ErrNoChange as success, so
  the caller was told the window had been recorded while the account
  stayed open, and the next fire went out inside it. Only the scope says
  whose account a notice is about; the window is now recomputed under the
  current policy, inside the write.
- `crq threads` classified authors with this host's reviewer set, so a
  login only the fleet names came back as a human — disagreeing with
  `crq feedback` over the same thread.
- `Tidy` compared trigger comments against commands this host knows, so a
  co-reviewer command the fleet configured read as edited by hand and its
  spent comments stayed on the PR.
- `crq init` applied the fleet's scope to its own copy and then printed
  the host's, installing a fallback that scans another account.
- the installed service froze a DERIVED feedback-bots list as an explicit
  one, and dropped the per-bot settings of every co-reviewer this host has
  switched off — the fallbacks a repository override or a fleet `cobots`
  change picks that bot up with.

And `scope` now validates GitHub owner logins rather than repository-name
characters: `-team` could be recorded fleet-wide, where it fails every
autoreview pass on every host.
@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

@kristofferR

Copy link
Copy Markdown
Owner Author

bugbot run

@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: b9a2f23932

ℹ️ 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/fleetconfig.go Outdated
Comment thread internal/crq/fleetcmd.go Outdated
Comment thread internal/crq/service.go
Comment thread internal/crq/service.go Outdated
Four places drew a conclusion from a policy or a slot and then wrote it
somewhere it no longer belonged:

- releasing the fire slot went by key alone, so an orphaned hold — the slot
  kept for a metered command whose round was superseded by the push its
  success invited — was dropped by whatever retired the QUEUED replacement at
  that key: a fleet exclusion, a cancel, a closed PR. The hold belongs to the
  command, so the slot is now released only by the round that took it, matched
  on its token.
- a CLI-reported account block was refused because ANY fleet setting had moved
  between preflight's read and the write. Only the scope says whose account the
  block is about, and cliOrgMatches already asks that against the current
  policy; an edit to settle or repos left the shared quota open and the caller
  told to run preflight again.
- a calibration reply adopted under the TTL the read started with was committed
  with CheckedAt=now even when the fleet had shortened calibrate-ttl under it,
  presenting a reading the new window calls stale as fresh for a whole TTL.
  readQuota now returns when the reply it adopted was written, and the write
  revalidates that age.
- applyFleet applied the recorded settings alphabetically, so `cobots`
  resolved through ForRepo against THIS HOST's required list: a fleet recording
  `required-bots=coderabbitai[bot]` with `cobots=""` had the host's locally
  required co-reviewer put straight back, and the rebuild preserved it.
  required-bots is now applied first, giving both keys one baseline.
@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: 74860c60a5

ℹ️ 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/fleetcmd.go
kristofferR added a commit that referenced this pull request Jul 28, 2026
CRQ_COBOTS defaulted to every co-reviewer in the registry, so a fresh install
asked Bugbot and Macroscope on every round of every repository — posting a
trigger comment, waiting out a grace period, and getting nothing, because
nobody had an account for either. A co-reviewer is opt-in now. Anything named
in CRQ_REQUIRED_BOTS is still enabled, since requiring a reviewer crq never
asks would hang the round forever.

The repository page marks a bot crq has never seen work there, and sorts the
proven ones first. It still offers it: a bot cannot produce evidence until it
is asked, so hiding the unproven ones would make the first one impossible to
turn on. But "enabled" and "works" are different claims and the page now says
which it means.

Two bugs fell out of this.

Narrowing a reviewer set reopened finished rounds. Removing two co-reviewers
from a fleet default proposed re-reviewing SEVENTEEN completed rounds — that
many metered reviews, against a shared allowance, to reach the answer already
on record. Only ADDING a reviewer can invalidate a round: one that converged
did so with more evidence than the new configuration asks for. Narrowing is
free now, and the impact preview asks the same question so it stops warning
about work that will not happen.

And crq recorded a co-reviewer's answer only on the reviewing sweep, so a round
that never fires — because the account is blocked, or the primary does not run
there — was observed on the fire path and nowhere else. A co-reviewer could
review it every time and crq would never notice. That is exactly what made a
working Codex read as "never answered": it reviewed #58 twice today while the
Bots page called it unverified.
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 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: 2

Caution

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

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

36-47: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Apply fleet policy before creating the calibration PR.

ensureCalibrationPR runs before the shared state is read and applyFleet is called. If the host leaves CalibrationPR unset while fleet state supplies one, Init can create a host-local calibration PR before replacing cfg with the fleet value. Move the state read/application before this external call and add a regression test for fleet calibration precedence. The fleet-policy contract requires recorded fleet values to override host values.

Suggested ordering
+	state, err := store.Update(ctx, func(st *State) error { return nil })
+	if err != nil {
+		return InitResult{}, err
+	}
+	cfg = applyFleet(cfg, state.FleetConfig, nil)
+
 	if cfg.CalibrationPR <= 0 {
 		pr, err := ensureCalibrationPR(ctx, cfg, gh)
 		...
 	}
-	state, err := store.Update(ctx, func(st *State) error { return nil })
-	if err != nil {
-		return InitResult{}, err
-	}
-	cfg = applyFleet(cfg, state.FleetConfig, nil)
🤖 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/init.go` around lines 36 - 47, In Init, read shared state and
apply applyFleet before calling ensureCalibrationPR, so fleet-provided
CalibrationPR values take precedence over host configuration and prevent
unnecessary PR creation. Preserve the existing state-update error handling, and
add a regression test covering an unset host CalibrationPR with a recorded fleet
CalibrationPR.
🤖 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/drainswitch.go`:
- Around line 109-129: Update validOwnerLogin to reject GitHub-invalid owner
logins by enforcing the 39-character maximum and rejecting any login containing
consecutive hyphens, while preserving the existing character and
leading/trailing hyphen checks.

In `@internal/crq/service.go`:
- Around line 2113-2116: The calibration probe and pruning logic in readQuota
should use the fleet configuration passed through cfg rather than the host-level
s.cfg.RateLimitCommand. Update the relevant command references in readQuota so
both writes follow cfg’s adopted calibration TTL and policy while preserving the
existing quota calculation behavior.

---

Outside diff comments:
In `@internal/crq/init.go`:
- Around line 36-47: In Init, read shared state and apply applyFleet before
calling ensureCalibrationPR, so fleet-provided CalibrationPR values take
precedence over host configuration and prevent unnecessary PR creation. Preserve
the existing state-update error handling, and add a regression test covering an
unset host CalibrationPR with a recorded fleet CalibrationPR.
🪄 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 Plus

Run ID: ea7b2393-21f6-4a6c-90c9-6d8e18970083

📥 Commits

Reviewing files that changed from the base of the PR and between 3426711 and 74860c6.

📒 Files selected for processing (17)
  • cmd/crq/main.go
  • internal/crq/cliquota_test.go
  • internal/crq/drain.go
  • internal/crq/drain_test.go
  • internal/crq/drainswitch.go
  • internal/crq/feedback.go
  • internal/crq/fleetcmd.go
  • internal/crq/fleetconfig.go
  • internal/crq/fleetconfig_test.go
  • internal/crq/init.go
  • internal/crq/init_test.go
  • internal/crq/service.go
  • internal/crq/service_test.go
  • internal/crq/threads.go
  • internal/crq/threads_test.go
  • internal/crq/tidy.go
  • internal/crq/tidy_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
internal/crq/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/crq/**/*.go: Keep internal/crq orchestration-only; it wires packages and executes effects, while observation is built once, decisions come from internal/engine, and state writes plus comment posting occur only in the apply layer.
Keep bot wording out of orchestration code; consume classified events and registry data instead of adding per-bot event kinds or verdicts.

Files:

  • internal/crq/tidy.go
  • internal/crq/threads_test.go
  • internal/crq/init.go
  • internal/crq/init_test.go
  • internal/crq/feedback.go
  • internal/crq/cliquota_test.go
  • internal/crq/drainswitch.go
  • internal/crq/threads.go
  • internal/crq/tidy_test.go
  • internal/crq/drain_test.go
  • internal/crq/service_test.go
  • internal/crq/fleetconfig.go
  • internal/crq/fleetconfig_test.go
  • internal/crq/fleetcmd.go
  • internal/crq/service.go
  • internal/crq/drain.go
internal/{engine,state,crq}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use AccountQuota or “account blocked” for CodeRabbit account quota; reserve Throttle for GitHub REST quota and do not mix the concepts.

Files:

  • internal/crq/tidy.go
  • internal/crq/threads_test.go
  • internal/crq/init.go
  • internal/crq/init_test.go
  • internal/crq/feedback.go
  • internal/crq/cliquota_test.go
  • internal/crq/drainswitch.go
  • internal/crq/threads.go
  • internal/crq/tidy_test.go
  • internal/crq/drain_test.go
  • internal/crq/service_test.go
  • internal/crq/fleetconfig.go
  • internal/crq/fleetconfig_test.go
  • internal/crq/fleetcmd.go
  • internal/crq/service.go
  • internal/crq/drain.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 (3)
📓 Common learnings
Learnt from: CR
Repo: kristofferR/coderabbit-queue

Timestamp: 2026-07-28T05:52:07.035Z
Learning: The dependency direction must remain acyclic: `dialect ← engine ← crq`, `state ← crq`, `gh ← {state, crq}`, and `workspace ← crq`.
Learnt from: CR
Repo: kristofferR/coderabbit-queue

Timestamp: 2026-07-28T05:52:07.035Z
Learning: The global `FireSlot` permits at most one concurrent primary fire fleet-wide; use CAS coordination and release the slot only according to the defined acknowledgment and hold semantics.
Learnt from: CR
Repo: kristofferR/coderabbit-queue

Timestamp: 2026-07-28T05:52:07.035Z
Learning: `DryRun` must short-circuit application into reporting with no writes, and loop exit codes remain frozen: 0 for converged/skipped/held, 10 for findings, and 2 for timeout.
📚 Learning: 2026-07-27T01:11:18.244Z
Learnt from: kristofferR
Repo: kristofferR/coderabbit-queue PR: 57
File: internal/crq/feedback.go:91-92
Timestamp: 2026-07-27T01:11:18.244Z
Learning: When reviewing code in `internal/crq` that deals with bot configuration, keep the separation of concerns intact: `Config.isConfiguredBot(login)` should only be evaluated against the fleet-wide primary `Config.Bot`. Do not override or reinterpret `Config.Bot` based on repo-specific reviewer/co-reviewer data. If repo-added co-reviewers are involved, they must be handled via repo-derived bot sets (e.g., `Config.evidenceBots()`), and not via `isConfiguredBot`. `Config.ForRepo(...)` should adjust co-reviewer-derived fields like `CoBots`, `RequiredBots`, `Reviewers`, and `FeedbackBots`, but intentionally not change the meaning of `Config.Bot` used by `isConfiguredBot`.

Applied to files:

  • internal/crq/tidy.go
  • internal/crq/threads_test.go
  • internal/crq/init.go
  • internal/crq/init_test.go
  • internal/crq/feedback.go
  • internal/crq/cliquota_test.go
  • internal/crq/drainswitch.go
  • internal/crq/threads.go
  • internal/crq/tidy_test.go
  • internal/crq/drain_test.go
  • internal/crq/service_test.go
  • internal/crq/fleetconfig.go
  • internal/crq/fleetconfig_test.go
  • internal/crq/fleetcmd.go
  • internal/crq/service.go
  • internal/crq/drain.go
📚 Learning: 2026-07-27T01:11:20.071Z
Learnt from: kristofferR
Repo: kristofferR/coderabbit-queue PR: 57
File: internal/crq/repoconfig.go:163-183
Timestamp: 2026-07-27T01:11:20.071Z
Learning: In internal/crq, treat the “observe → decide → apply” effects restriction as applying only to effects produced by review decisions. Explicit operator mutation commands (e.g., Service.SetReviewers, Service.ClearReviewers, enqueue/cancel flows) are allowed to perform state mutations outside the standard apply path, so review logic should not incorrectly require them to follow observe/decide/apply.

For CRQ_DRY_RUN: it should suppress review requests and fire-record writes, but it must not make explicit configuration/mutation commands (e.g., `crq reviewers set`) silently succeed without persisting the requested changes.

Ensure Service.applyFire revalidates repository reviewer overrides just before posting, to protect against stale/changed overrides since earlier reads.

Applied to files:

  • internal/crq/tidy.go
  • internal/crq/threads_test.go
  • internal/crq/init.go
  • internal/crq/init_test.go
  • internal/crq/feedback.go
  • internal/crq/cliquota_test.go
  • internal/crq/drainswitch.go
  • internal/crq/threads.go
  • internal/crq/tidy_test.go
  • internal/crq/drain_test.go
  • internal/crq/service_test.go
  • internal/crq/fleetconfig.go
  • internal/crq/fleetconfig_test.go
  • internal/crq/fleetcmd.go
  • internal/crq/service.go
  • internal/crq/drain.go
🔇 Additional comments (20)
internal/crq/init.go (1)

17-22: LGTM!

Also applies to: 58-66

internal/crq/init_test.go (1)

1-43: LGTM!

Also applies to: 45-77

internal/crq/fleetconfig.go (1)

94-94: LGTM!

Also applies to: 196-196, 442-449, 464-466, 538-546, 568-608, 610-632

internal/crq/fleetcmd.go (1)

459-487: LGTM!

cmd/crq/main.go (1)

58-73: LGTM!

Also applies to: 84-104, 126-126, 334-346, 537-598, 728-729, 879-907, 1029-1029, 1117-1155, 1926-1938

internal/crq/feedback.go (1)

910-915: LGTM!

Also applies to: 927-927

internal/crq/threads.go (1)

39-49: LGTM!

Also applies to: 65-65

internal/crq/drain.go (1)

25-29: LGTM!

Also applies to: 60-87, 400-496

internal/crq/drain_test.go (2)

150-172: LGTM!

Also applies to: 338-420


174-180: 🎯 Functional Correctness

No duplicate unreadableStore declaration present.

The file declares unreadableStore exactly once, so no compile-time redeclaration issue remains.

internal/crq/fleetconfig_test.go (1)

94-104: LGTM!

Also applies to: 251-284, 486-519

internal/crq/service.go (4)

599-643: LGTM!

Also applies to: 778-798


888-932: LGTM!

Also applies to: 1146-1146, 1338-1338, 1969-2012, 2155-2178


933-957: 🩺 Stability & Availability

No slot-release issue remains.

Progress assigns KeepWaiting directly only while the round is still waiting; the other outcomes handled here are written to the slot-holding round, and OutAbandon already releases the slot before returning.


1937-1943: 🩺 Stability & Availability

No change needed. crq cancel is documented as abandoning the current round, and the auto-clear timeout is the stated recovery for an “in flight” PR when cancellation does not apply; no tests imply cancel must unstick an already-gone round.

			> Likely an incorrect or invalid review comment.
internal/crq/threads_test.go (1)

92-129: LGTM!

internal/crq/tidy.go (1)

69-75: LGTM!

internal/crq/service_test.go (1)

2577-2618: LGTM!

Also applies to: 3392-3442

internal/crq/cliquota_test.go (1)

149-178: LGTM!

internal/crq/tidy_test.go (1)

396-406: 🎯 Functional Correctness

Verify the fleet-derived bugbot trigger under the bugbot key.

cursor[bot] and bugbot share the same registry aliases (bugbot run / cursor review), so this does not currently show a missing fleet lookup. Check that the test uses a command that is not shared with cursor review, and assert the command is materialized in result.Deleted rather than only checking the absence of the edited message.

Comment thread internal/crq/autofixswitch.go
Comment thread internal/crq/service.go
validOwnerLogin already rejects the characters and the leading or trailing
hyphen no GitHub login may hold, but let through two logins GitHub also
cannot issue: one longer than 39 characters, and one with consecutive
hyphens. Recorded fleet-wide, either fails every scan on every host at once
— the reach that makes catching it at `crq config set` worth more than the
per-host check.
@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: dc2ba51bdd

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

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 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

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 65bfa835fc

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

@kristofferR
kristofferR merged commit c1cebad into main Jul 28, 2026
2 checks passed
@kristofferR
kristofferR deleted the feat/fleet-config branch July 28, 2026 10:27
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