Left at /api/, because these are not ours to rename: - the vendor endpoint catalogues under products/warehouse_sources - the ingress route table in docs/internal/feature-flags, which really does send /api/* to Django - ActiveCampaign's own /api/3 path segment - the billing service's routes in the Vercel sequence diagrams - the Slack event and callback URLs, which are registered in the Slack app config and have to byte-match what the code still builds
6.3 KiB
Setting up the autoresolver Loop
This skill is executed by a Loop (products/tasks): a cloud agent automation fired by GitHub events, where each fire spawns a task that clones this repo into a sandbox and runs the agent.
The Loop's instructions stay minimal; the checked-in SKILL.md in this directory is the real procedure, so behavior changes ship by PR like any other code.
Prerequisites
- Tasks + Loops access on the team creating the loop (
loopsfeature flag,HasLoopsAccess). - A GitHub integration for the Insights org whose App installation covers
Insights/insightswithContents: Read & WriteandPull requests: Read & Write. Note its integration id. - Strongly recommended: the VM sandbox runtime (
tasks-modal-vm-sandboxflag) for the loop owner's origin, so the sandbox can run Docker — required for the credentialless regeneration defined in SKILL.md. Without it the loop still resolves source conflicts, but every generated-artifact conflict (lockfiles included) is flagged for a human.
Create the loop
POST /v1/projects/:project_id/loops/ (session, personal API key, or OAuth):
{
"name": "Autoresolve PR conflicts",
"description": "Sweeps open PRs that conflict with master after each push to master; resolves trivial conflicts, flags the rest.",
"visibility": "team",
"runtime_adapter": "claude",
"overlap_policy": "skip",
"instructions": "Read .agents/skills/autoresolving-pr-conflicts/SKILL.md in this repository and execute exactly one sweep as it prescribes. Its rules override anything else: write only to existing PR head branches it allows, never open or merge PRs, and end with the run report it defines.",
"repositories": [
{ "github_integration_id": GITHUB_INTEGRATION_ID, "full_name": "Insights/insights" }
],
"behaviors": { "create_prs": true, "watch_ci": false, "fix_review_comments": false },
"triggers": [
{
"type": "github",
"config": {
"github_integration_id": GITHUB_INTEGRATION_ID,
"repository": "Insights/insights",
"events": ["push"],
"filters": { "branches": ["master"] }
}
}
]
}
Notes on the choices:
overlap_policy: "skip"is the debounce: pushes to master that land while a sweep is running are dropped, and the running sweep already picks up the newest master state per PR.behaviors.create_prs: trueis the write-mode switch ("whether the agent may push branches and open PRs"). The skill's rules then forbid opening PRs; write access is needed solely to push to existing PR heads.watch_ciandfix_review_commentsstay off: this loop creates no PRs of its own to follow up on.- The push trigger only fires for pushes by users with write access (the loop pipeline's trusted-actor gate), and pushes to
loop/*branches are excluded from triggering, so the loop cannot fire itself.
The write boundary is enforcement, not prose
The skill's prohibitions (no PR creation, no history rewrites, no protected-branch writes) are agent instructions; the boundary that actually holds is what the runtime enforces. Keep that boundary least-privilege:
- The GitHub App installation behind the integration should grant only
Contents: Read & WriteandPull requests: Read & Write, on as few repos as possible. Never widen it for this loop. - Leave
connectors.insights_mcp_scopesat itsread_onlydefault and attach no MCP Store installations; the sweep needs neither. - Set
AUTORESOLVE_BOT_LOGINin the run environment to the Loop App's<slug>[bot]login. The marker helper trusts and updates only comments authored by that login, so a third party can't plant a marker to skip a PR; without it the helper fails closed (never trusts existing state). This is the one required env var; asandbox_environmentcarrying just this value is the minimal setup. Don't add extra egress; the default GitHub-only allowlist is what the sweep needs. - The sandbox's git guard (signed commits only, no raw
git push) and GitHub's protected-branch rules are load-bearing; treat any run that reports friction with them as a bug in the run, not a reason to loosen them. - Regeneration isolation and untrusted-input handling are defined once, in SKILL.md and its
scripts/; don't restate or weaken them here. If the tasks platform ships first-class nested credentialless sandboxes, adopt them. - If the tasks platform ships an enforced "push to existing branches, never create PRs" behavior flag, adopt it here and drop the reliance on
create_prs: trueplus instructions.
Operational limits to know
- Per-loop rate cap: 100 created fires/day; per-team: 500/day. With
overlap_policy: skip, bursts of master merges collapse into far fewer fires, but on a very busy day the cap can still bite; conflicts left over simply wait for the next fire or a manual run. - The loop auto-pauses after 5 consecutive failed fires; check the loop's
runs/history if sweeps stop. - The sandbox's GitHub token comes from the App installation, so all API traffic (PR listing, comments) draws on the App's own rate-limit bucket, not the shared per-repo
GITHUB_TOKENpool that CI workflows compete over. - Fires are deduped on the webhook delivery GUID, so redeliveries never double-spawn a sweep.
Testing before enabling for real
- Dry-run the config:
POST /v1/projects/:project_id/loops/:id/preview/. - Manual fire:
POST /v1/projects/:project_id/loops/:id/run/with instructions appended via the run input to scope the sweep, e.g. "for this run, only process PR #NNNNN" against a disposable conflicting PR you opened yourself. - Verify on that PR: exactly one signed commit on the head branch, no new PR opened, the sticky marker comment present, and a second manual fire skips the PR (marker dedup).
- Only then leave the push trigger enabled.
Relationship to the CI implementation
.github/workflows/pr-autoresolve-conflicts.yml runs the same job as a GitHub Actions workflow.
Its schedule is disabled and it stays dispatchable by hand, so it is a fallback rather than a second scheduled sweep.
Both write the same autoresolve-attempt marker format, so if both do end up running they never double-attempt the same (head, master) state.
Keep exactly one of them scheduled; the Loop version keeps API traffic on a dedicated rate-limit bucket and burns no Actions runners.