Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {
displayName: "Periodic reports",
description: "Turns validated Goal stage progress into a frozen report and automatically delivers it through the configured Goal Channel with exact readback.",
},
pull_request_review: {
displayName: "Pull-request review",
description: "Ranks the public GitHub PR review queue with a machine-level default; it never grants GitHub, Todo, push, or merge authority.",
},
reward_memory: {
displayName: "Reward Memory experiment",
description: "Configures a reviewed local-private provider binding for Goal-scoped Agent recall and evidence-backed outcome learning.",
Expand Down Expand Up @@ -94,6 +98,10 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {
displayName: "周期报告",
description: "把经过验证的 Goal 阶段进展整理为冻结报告,并通过配置的 Goal Channel 自动发送和精确回读。",
},
pull_request_review: {
displayName: "Pull-request Review",
description: "配置公开 GitHub PR 审阅队列的本机默认排序;不会授予 GitHub、Todo、push 或 merge 权限。",
},
reward_memory: {
displayName: "Reward Memory 实验",
description: "为 Goal 内 Agent 的召回与证据化结果学习配置经过审阅的本机私有 Provider 绑定。",
Expand All @@ -112,6 +120,7 @@ const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
max_children: { label: "Maximum children", description: "Hard upper bound for concurrently delegated child work." },
profile: { label: "Planner profile", description: "Select one registered Explore Harness profile." },
profile_preset: { label: "Report profile", description: "Capability-owned report profile, such as weekly-progress." },
review_priority: { label: "Review priority", description: "Choose whether other developers' PRs or the authenticated reviewer's own PRs are ranked first." },
route_ref: { label: "Goal Channel route", description: "Public route alias only; credentials and provider identifiers stay outside this form." },
safe_fix: { label: "Allow one bounded safe-fix pass" },
strict_receipt: { label: "Require an exact-diff receipt" },
Expand All @@ -130,6 +139,7 @@ const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
max_children: { label: "最大子 Agent 数", description: "可同时委派的子任务硬上限。" },
profile: { label: "规划 Profile", description: "选择一个已注册的 Explore Harness profile。" },
profile_preset: { label: "报告 Profile", description: "由该能力管理的报告 profile,例如 weekly-progress。" },
review_priority: { label: "审阅优先级", description: "选择先排其他开发者的 PR,还是先排当前已认证审阅者自己的 PR。" },
route_ref: { label: "Goal Channel 路由", description: "只填写公开 route alias;凭据与 Provider 标识不会进入此表单。" },
safe_fix: { label: "允许一次有界安全修复" },
strict_receipt: { label: "要求精确 diff 回执" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,15 @@ export function MachineConfigurationSettings() {
</section>
) : null}

{selected.capability_id === "pull_request_review" ? (
<section className="personal-capability-behavior-note">
<ShieldCheck aria-hidden size={18} />
<div><strong>{locale === "zh-CN" ? "只改变队列排序" : "Queue ordering only"}</strong><p>{locale === "zh-CN"
? "默认先审阅其他开发者的 PR;选择 owner-first 才会优先当前已认证审阅者自己的 PR。此配置不会发布 review、写 Todo、push 或 merge。"
: "The default reviews other developers' PRs first; choose owner-first only when the authenticated reviewer's own PRs should lead. This setting never posts a review, writes Todos, pushes, or merges."}</p></div>
</section>
) : null}

{editorAvailable ? <>{editorMode === "json" || !selected.configuration_editor.fields.some((field) => field.key === "enabled" && field.input_kind === "boolean") ? <div className="personal-capability-editor-mode">
<button onClick={() => changeMode(editorMode === "guided" ? "json" : "guided")} type="button">
<Code2 aria-hidden size={14} />{t(editorMode === "guided" ? "machine.editJson" : "machine.backToForm")}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ assert.match(workspaceSettings, /role="radiogroup"/, "Settings expose theme and
assert.match(workspaceSettings, /setLocale\(option\.value\)/, "Settings updates the workspace locale");
assert.match(machineSettings, /available_scopes\.includes\("machine"\)/, "Machine configuration only renders capabilities that grant machine-scope configuration");
assert.match(machineSettings, /selected\.capability_id === "periodic_report"/, "Periodic reports expose their governed activation semantics");
assert.match(machineSettings, /selected\.capability_id === "pull_request_review"/, "PR review exposes its queue-ordering-only semantics");
assert.match(machineSettings, /previewMachineConfiguration\(/, "Machine settings require a preview before apply");
assert.match(machineSettings, /applyMachineConfiguration\([\s\S]*preview\.plan_revision/, "Machine settings apply the exact reviewed revision");
assert.match(machineSettings, /previewMachineConfigurationRollback\(/, "Machine settings preview rollback before execution");
Expand Down Expand Up @@ -404,12 +405,13 @@ for (const capabilityId of [
"multi_subagent",
"peer_task_coordination",
"periodic_report",
"pull_request_review",
"reward_memory",
]) {
const matches = capabilityLocalization.match(new RegExp(`${capabilityId}:`, "g")) ?? [];
assert.equal(matches.length, 2, `${capabilityId} has English and Simplified Chinese metadata`);
}
for (const fieldKey of ["allowed_domains", "coordinator_agent_id", "enabled", "max_children", "profile", "profile_preset", "route_ref", "safe_fix", "strict_receipt", "timezone"]) {
for (const fieldKey of ["allowed_domains", "coordinator_agent_id", "enabled", "max_children", "profile", "profile_preset", "review_priority", "route_ref", "safe_fix", "strict_receipt", "timezone"]) {
const matches = capabilityLocalization.match(new RegExp(`${fieldKey}:`, "g")) ?? [];
assert.equal(matches.length, 2, `${fieldKey} has English and Simplified Chinese field copy`);
}
Expand Down
9 changes: 5 additions & 4 deletions examples/pr-review-command-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,24 +188,25 @@ def fake_run_gh_json(args: list[str], *, cwd: Path | None = None) -> object:
assert request["command"] == "/loopx-pr-review", request
assert (
request["cli_command"]
== "loopx pr-review [--repo owner/repo] [--state open|merged|all] [--since ISO]"
== "loopx pr-review [--repo owner/repo] [--state open|merged|all] [--review-priority other-developers-first|owner-first] [--since ISO]"
), request
assert request["privacy_mode"] == "public_safe_github_metadata", request
assert request["dry_run"] is True, request
assert request["repository"] == "owner/repo", request
assert request["state_filter"] == "all", request
assert request["review_priority"] == "other-developers-first", request
assert "result_completeness" in request["include"], request
assert "scheduling_policy" in request["include"], request
assert payload["result_completeness"]["complete"] is True, payload
scheduling_policy = payload["scheduling_policy"]
assert (
scheduling_policy["schema_version"]
== "pull_request_review_scheduling_policy_v0"
== "pull_request_review_scheduling_policy_v1"
), scheduling_policy
assert [item["id"] for item in scheduling_policy["ordered_tiers"][:3]] == [
"other_developer_feedback_and_aged_backlog",
"other_developer_remaining",
"authenticated_developer_owned",
"community_feedback_and_aged_backlog",
"composite_remaining",
], scheduling_policy
assert "one-off author filters" in scheduling_policy["manual_override_rule"]
assert payload["summary"]["total_pr_count"] == 4, payload["summary"]
Expand Down
5 changes: 5 additions & 0 deletions loopx/canary/module_metric_baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
"dict_any_count": 0,
"lines": 1570
},
"loopx/pr_review.py": {
"any_count": 49,
"dict_any_count": 0,
"lines": 1515
},
"loopx/quota.py": {
"any_count": 219,
"dict_any_count": 0,
Expand Down
17 changes: 17 additions & 0 deletions loopx/capabilities/configuration_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,23 @@ def capability_configuration_editor(
_field("strict_receipt", "Require an exact-diff receipt", "boolean"),
],
},
"pull_request_review": {
"supported_scopes": ["machine"],
"writable_scopes": ["machine"],
"fields": [
_field(
"review_priority",
"Review priority",
"select",
options=("other-developers-first", "owner-first"),
required=True,
description=(
"Default ranks actionable PRs whose author differs from the "
"authenticated reviewer before the reviewer's own PRs."
),
),
],
},
"local_authority_shadow": {
"supported_scopes": ["goal"],
"writable_scopes": ["goal"],
Expand Down
4 changes: 4 additions & 0 deletions loopx/capabilities/machine_configuration/builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ def build_builtin_machine_configuration_registry() -> MachineConfigurationRegist
from ..periodic_report.machine_defaults import (
periodic_report_machine_configuration_namespace,
)
from ..pr_review_queue.machine_defaults import (
pull_request_review_machine_configuration_namespace,
)
from ..todo_replan_cadence.machine_defaults import (
todo_replan_cadence_machine_configuration_namespace,
)
Expand All @@ -24,6 +27,7 @@ def build_builtin_machine_configuration_registry() -> MachineConfigurationRegist
.register(periodic_report_machine_configuration_namespace())
.register(todo_replan_cadence_machine_configuration_namespace())
.register(change_quality_machine_configuration_namespace())
.register(pull_request_review_machine_configuration_namespace())
)


Expand Down
65 changes: 48 additions & 17 deletions loopx/capabilities/pr_review_queue/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ observation to this same command. It reuses the existing GitHub scan and
normalized review queue; it does not introduce a second crawler or a new write
authority.

The capability is also registered with the standard machine configuration
surface. Open Dashboard → machine settings (or use `loopx machine-config
describe`) and edit the `Pull-request review` capability. The select field is
stored under the typed `pull_request_review` namespace and is read by
`loopx pr-review` whenever `--review-priority` is omitted. Preview/apply is
revision-locked and readback-verified like every other machine capability;
removing the namespace returns to the default `other-developers-first` mode.
This setting changes queue order only and never grants review, comment, Todo,
push, or merge authority.

The capability also owns the review-depth contract. The shared
`agent_response_contract.review_execution_contract` defines required evidence,
completion, freshness, finding, and verdict rules. Each actionable PR carries a
Expand All @@ -36,7 +46,7 @@ workflow or the merge-focused `loopx-pr-merge` skill.

| Command | CLI reference | Intent |
| --- | --- | --- |
| `/loopx-pr-review` | `loopx pr-review [--repo owner/repo] [--state open\|merged\|all] [--since ISO] [--fresh-audit-exact-head NUMBER@HEAD_OID]` | List open and merged PRs for the current project or explicit repository, provide concrete main-regression analysis for each actionable PR, and include a blank five-block template that agentloop fills after reading the selected PR body/diff. A typed exact-head option is required to re-audit an unchanged concluded head. |
| `/loopx-pr-review` | `loopx pr-review [--repo owner/repo] [--state open\|merged\|all] [--review-priority other-developers-first\|owner-first] [--since ISO] [--fresh-audit-exact-head NUMBER@HEAD_OID]` | List open and merged PRs for the current project or explicit repository, provide concrete main-regression analysis for each actionable PR, and include a blank five-block template that agentloop fills after reading the selected PR body/diff. The default prioritizes non-owner developer PRs; `owner-first` opts into owner priority. A typed exact-head option is required to re-audit an unchanged concluded head. |
| pre-merge readback | `loopx pr-review --repo owner/repo --check-merge-readiness NUMBER@HEAD_OID` | Immediately before merge, fail closed unless the remote PR is still open at the reviewed head, its standalone conclusion approves that head, all checks are successful or skipped, review-thread pagination is complete with no unresolved thread, and merge state is compatible. This read grants no merge authority. |

The slash command must run the CLI first. Agentloop must not reconstruct the
Expand Down Expand Up @@ -153,19 +163,35 @@ states:
The repository-scoped fingerprint contains only compact public PR metadata.
Persisted `items` carry the PR number, fingerprint, exact head, decision, and
next action; they never carry review bodies.
`pull_request_review_scheduling_policy_v0` owns the stable queue order:

1. actionable PRs authored by the authenticated developer (`reviewer_login`);
2. community response heads pushed after an independent `REQUEST_CHANGES`
review and community exact heads waiting at least 24 hours;
`pull_request_review_scheduling_policy_v1` owns the stable queue order. The
`--review-priority` switch selects the actionable ordering:

- `other-developers-first` (the default) reviews actionable PRs whose author
differs from `request.reviewer_login` before the authenticated developer's
own PRs;
- `owner-first` restores the authenticated developer's own PRs before other
developers' PRs.

The capability does not infer organization membership or trust from GitHub
metadata; “other developer” is strictly an author-identity comparison. The
selected mode is carried in `request.review_priority`,
`scheduling_policy.review_priority`, and autonomous observations, so changing
the switch is an explicit queue transition rather than hidden local state.

Within either mode, the queue order is:

1. the mode-selected actionable author group;
2. the other actionable author group, with community response heads pushed
after an independent `REQUEST_CHANGES` review and community exact heads
waiting at least 24 hours;
3. remaining actionable work in current-head `review_ready_at`, creation-time,
and PR-number order;
4. current heads that already have a conclusion, followed by merged, draft,
and closed rows.

Community feedback and aged backlog share one age-fair tier. On a material
transition, at most one newly pushed community response head may take a bounded
fast-feedback slot after all unprojected owner-authored work. `updatedAt` does
fast-feedback slot after the mode-selected actionable group. `updatedAt` does
not define readiness because comments and checks must not make old code look
new. Only an explicit PR selection in the current user request may override the
next item's ordering for that request; it does not override the selected row's
Expand Down Expand Up @@ -374,11 +400,12 @@ absolute paths, private source bodies, or hidden CI artifacts.
"request": {
"schema_version": "loopx_pr_review_command_request_v0",
"command": "/loopx-pr-review",
"cli_command": "loopx pr-review [--repo owner/repo] [--state open|merged|all] [--since ISO]",
"cli_command": "loopx pr-review [--repo owner/repo] [--state open|merged|all] [--review-priority other-developers-first|owner-first] [--since ISO]",
"repository": "owner/repo",
"limit": 100,
"state_filter": "all",
"since": "2026-06-28T00:00:00Z",
"review_priority": "other-developers-first",
"window": {"state_filter": "all", "since": "2026-06-28T00:00:00Z"},
"source": "github_cli",
"privacy_mode": "public_safe_github_metadata",
Expand All @@ -394,14 +421,16 @@ absolute paths, private source bodies, or hidden CI artifacts.
"rerun_cli_args": []
},
"scheduling_policy": {
"schema_version": "pull_request_review_scheduling_policy_v0",
"schema_version": "pull_request_review_scheduling_policy_v1",
"identity_basis": "request.reviewer_login",
"owner_first_active": true,
"review_priority": "other-developers-first",
"owner_first_active": false,
"other_developers_first_active": true,
"community_backlog_age_hours": 24.0,
"ordered_tiers": [
{"tier": 0, "id": "authenticated_developer_owned"},
{"tier": 1, "id": "community_feedback_and_aged_backlog"},
{"tier": 2, "id": "composite_remaining"}
{"tier": 0, "id": "other_developer_feedback_and_aged_backlog"},
{"tier": 1, "id": "other_developer_remaining"},
{"tier": 2, "id": "authenticated_developer_owned"}
]
},
"summary": {
Expand Down Expand Up @@ -722,10 +751,12 @@ A first implementation is acceptable when:
long answer;
- live packets expose and recheck `headRefOid` so a review verdict is bound to
the remote revision actually inspected;
- autonomous packets rank authenticated-developer-owned actionable work first,
then community response and 24-hour backlog, then remaining work by
current-head `review_ready_at`; response preemption is bound to one slot and
check-only activity does not change readiness priority;
- autonomous packets honor `request.review_priority`: the default ranks
non-owner developer actionable work first, while `owner-first` restores
authenticated-developer-owned priority. Community response and 24-hour
backlog retain their age ordering within the selected mode; response
preemption is bound to one slot and check-only activity does not change
readiness priority;
- `scheduling_policy` is preserved as packet authority; Todo/monitor prose and
one-off author filters cannot replace it;
- `--observation-state-file` atomically carries observation and handled cursors
Expand Down
16 changes: 16 additions & 0 deletions loopx/capabilities/pr_review_queue/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

from .core import build_pull_request_review_queue_observation
from .merge_readiness import build_merge_readiness
from .machine_defaults import (
PULL_REQUEST_REVIEW_MACHINE_DEFAULTS_SCHEMA,
normalize_pull_request_review_machine_defaults,
pull_request_review_machine_configuration_namespace,
review_priority_machine_default,
)
from .review_contract import (
build_agent_response_contract,
build_review_execution_contract,
Expand All @@ -14,28 +20,38 @@
normalize_fresh_audit_exact_heads,
)
from .scheduling import (
DEFAULT_REVIEW_PRIORITY,
PullRequestReviewPriority,
PullRequestSchedulingLane,
build_scheduling_policy,
classify_scheduling_lane,
community_feedback_ready,
normalize_review_priority,
scheduling_sort_key,
scheduling_tier,
)

__all__ = [
"build_agent_response_contract",
"build_merge_readiness",
"PULL_REQUEST_REVIEW_MACHINE_DEFAULTS_SCHEMA",
"normalize_pull_request_review_machine_defaults",
"pull_request_review_machine_configuration_namespace",
"review_priority_machine_default",
"build_pull_request_review_queue_observation",
"build_review_execution_contract",
"build_review_plan",
"build_review_template",
"build_scheduling_policy",
"DEFAULT_REVIEW_PRIORITY",
"classify_scheduling_lane",
"community_feedback_ready",
"normalize_review_priority",
"exact_head_key",
"materialize_review_execution",
"normalize_fresh_audit_exact_heads",
"PullRequestSchedulingLane",
"PullRequestReviewPriority",
"scheduling_sort_key",
"scheduling_tier",
]
Loading