Skip to content
Back to skills

Auto Iterate

ASecurity

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 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 4, 2026
ai-agentsrustgogitapidatabasebackend

Works with

  • cursor
  • api

Security analysis

A100/100

Scanned October 5, 2026

npx -y skills add onelastcommit/noctra --skill auto-iterate --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Auto Iterate?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Auto Iterate
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/onelastcommit-auto-iterate/badge)](https://www.skillsdirectory.com/skills/onelastcommit-auto-iterate)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: auto-iterate
description: 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.
---

# 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.ResumeWorktree → [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`](../naming/SKILL.md) 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. 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.

## 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.
- **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.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.