agentleFS
onelastcommitonelastcommit/noctra/.claude/skills/auto-iterate/SKILL.md

auto-iterate

Skill4 starsChanged 29 days agoMITView on GitHub

Use when changing the PR watcher (AUTO_ITERATE_PRS) — how Noctra finds its own PRs, classifies review feedback and CI failures, advances cursors, re-engages the agent, or replies to and resolves review threads.

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Only PRs Noctra authored: the noctra/<id> branch prefix (repo.BranchName) plus a body marker (github.IsNoctraAuthoredBody, matching the …

    line 20
  2. Distinctive:

    Keep creation and watching in sync — same prefix, same marker — or the watcher silently never finds its own PRs.

    line 22
  3. Very specific:

    Every failure path in iteratePR must record the iteration before returning, or the cursor never advances and the same feedback loops …

    line 40
  4. Very specific:

    Fallback: no parseable per-finding block (older logs, a non-compliant backend) → one conversation comment with the run summary …

    line 51

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Auto-iterate on PR feedback

With AUTO_ITERATE_PRS=true a second poll loop runs beside the Linear one, on the same WaitGroup, so shutdown drains in-flight iterations. Off by default.

PR poll loop → github.ListNoctraPRs → github.GetPR (comments+reviews+statusCheckRollup)
  → watch.Scan (diff vs state cursor: new feedback OR failing CI on a new head SHA)
  → pipeline.iteratePR (bounded, shares the worker pool + active-set)
  → repo.ResumeWorktreeWithBranch(PR head branch) → [github.CheckLogs for CI] → agent.BuildFixPrompt → agent.Run
  → commit/push (same branch) → state.Update (advance comment/review/CI cursor + bump iteration count)

Which PRs it claims

Only PRs Noctra authored: the noctra/<id> branch prefix (repo.BranchName) plus a body marker (github.IsNoctraAuthoredBody, matching the hidden github.NoctraPRBodyMarker that every PR body builder embeds, or the legacy "by [Noctra]" footer), plus the author (--author @me in token mode, --author app/<slug> in App mode).

Keep creation and watching in sync — same prefix, same marker — or the watcher silently never finds its own PRs. Sweep PRs were once missed exactly this way: their footer lacked the old marker. The naming skill owns every one of these strings.

What counts as feedback

  • Captured: conversation comments, review summaries (CHANGES_REQUESTED / non-empty COMMENTED), and inline review-thread comments (fetched separately via gh api, non-fatal on failure). APPROVED, DISMISSED and empty COMMENTED advance the cursor without acting.
  • Trusted-reviewer rule (watch.actionable): humans are always actionable; bots only when their login is in TRUSTED_REVIEWERS. CI is not gated by this. Logins are compared through watch.botLoginKey (lower-cased, [bot] stripped): GraphQL reports a bot as chatgpt-codex-connector, REST inline comments as chatgpt-codex-connector[bot], and matching the raw login silently ignored every trusted bot's inline findings (regulars#89). Two non-actionable exceptions, cursor still advancing:
    1. a comment whose whole body is a bot-directed command (@codex review, @gemini, /review) — it targets another tool. A comment mixing a command with real feedback still acts.
    2. Noctra's own replies, identified by the hidden github.NoctraReplyMarker. Under a personal GitHub account those replies look human; without the marker they get re-read as feedback → re-engage → reply → loop until MAX_PR_ITERATIONS (observed before the marker existed).
  • CI failures are a second trigger into the same iteratePR: when every check on the head commit (statusCheckRollup) has completed and ≥1 failed, watch.diff sets PRChanges.CIFailure; iteratePR fetches failed-step logs (gh run view --log-failed, truncated, best-effort) and folds them into the same fix prompt. Pending review feedback and CI are handled in one re-engagement. If the token can't read the rollup (GitHub refuses the whole gh pr view with "Resource not accessible by integration" when, say, the App lacks Commit statuses), GetPR retries without it and warns: feedback still works, the CI trigger is off for that PR, and the CI cursor (only advanced on a failure) is untouched.

Triage before engaging

With PR_TRIAGE=true (default) and a backend that agent.TriageSupported (Claude, Codex, Copilot), iteratePR first asks the agent whether the new activity needs a change or a reply (Pipeline.triageFeedback, agent.BuildTriagePrompt), before the 👀 reaction, the Telegram message or the worktree. It runs in an empty temp dir with RunOptions{NoTools: true, Model: agent.TriageModel(backend)} (Haiku on Claude, the default model elsewhere), no plugins, a 3-minute timeout, and answers {"act", "reason"} between agent.TriageStartMarker/EndMarker. A clear act: false advances the cursor and stops: no reply, no notification, no iteration counted. Anything else (error, timeout, no verdict) engages as before. CI failures skip triage and always engage. Like the per-finding respond flag, it judges content, not authors; the prompt treats item text as untrusted and says "when unsure, act".

Cursors and guards

  • Comment/review cursors are timestamps (naturally ordered; conversation and inline comments share the comment cursor). The CI cursor is the head commit SHA (LastCISHA) — acted on once per commit, since a fix changes the SHA and makes a fresh failure eligible again.
  • MAX_PR_ITERATIONS is per PR and shared across review and CI re-engagements. Timeouts and rate limits don't count.
  • Every failure path in iteratePR must record the iteration before returning, or the cursor never advances and the same feedback loops forever (architecture Invariant 7 has the exceptions).
  • pipeline.active dedupes, so a ticket can't be freshly dispatched and iterated at once.

Replying per finding

The fix prompt asks the agent for a JSON array — one {finding, addressed, reply} per numbered review finding — wrapped in agent.FindingsStartMarker/EndMarker and parsed by agent.ExtractFindingReplies.

  • The prompt's ### N) findings map 1:1 to watch.PRChanges.Events[N-1], and an inline finding's Event.ThreadID equals its thread's FirstCommentDatabaseID (REST id == GraphQL databaseId). ThreadID is the comment's in_reply_to_id when it is a reply inside a thread, else its own id; keying on CommentID silently dropped every reply to a human who answered inside an existing thread (noctra-site#66). A reply event also carries its parent (Event.ReplyTo), which the fix prompt quotes so the agent knows what "let's fix this" refers to, and the prompt treats a human reviewer's request as an instruction rather than a suggestion. So pipeline.postIterationReplies routes each reply to the exact thread it came from via github.ReplyToThreadsByComment, carrying NoctraReplyMarker.
  • A thread is resolved only when its finding is addressed. A finding Noctra pushed back on stays open; a thread with no matching finding gets neither reply nor resolution. One summary is never broadcast to every thread — that was the bug behind PR #246/#250, where a single gofmt-fix reply landed on several unrelated Gemini findings.
  • The reply prefixes "Addressed in <sha>." when HEAD advanced. Key it on HEAD movement, not branch-ahead-of-remote, because the agent sometimes self-pushes.
  • The agent may stay silent. A finding with "respond": false (and not addressed) gets no thread reply and doesn't count towards a conversation comment. The prompt asks for it on anything that isn't feedback: a bot's status notice, a summary pointing at findings listed separately, an acknowledgement. When no code changed, CI wasn't involved and every finding is silent, nothingToRespondTo ends the run quietly: no comment, no notification, cursor advanced via advanceCursor, and no iteration counted. Noctra deliberately has no per-bot wording filters; the agent judges by content (regulars#89, where Codex's "review in progress" comment drew a reply and burned an iteration).
  • Fallback: no parseable per-finding block (older logs, a non-compliant backend) → one conversation comment with the run summary (agent.ExtractSummary), every thread left open. Conversation-level feedback always gets one comment, so a no-diff review isn't silent.
  • The summary is persisted to state.PRState.LastReasoning and fed into the next fix prompt (agent.FixPromptInput.PriorReasoning) so re-engagements don't re-litigate settled feedback. It also notifies via Telegram/Linear.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.