The queue: parking, the operator's order, blocked items (M2.2)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 3m26s

Several work items on the new loop (L1, D4):
- Switching parks the item on the workspace: its work is committed and its
  branch pushed; resuming checks the item's branch back out. Branches of
  open items are never set aside by a fresh workspace.
- Next item: the operator's "do #N next" first, otherwise the oldest.
- Issues with open Gitea dependencies are blocked until they close, checked
  at intake and on every round; status shows blocked items and the line.

Tests: queue unit tests, the property walk over several issues with
dependencies and a new parked_work_safe property (it catches parking
disabled), and an end-to-end test with two issues where B depends on A.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
This commit is contained in:
dongarandClaude Opus 5.5 committed 2026-10-04 13:34:09 +00:00
1 parent d504588a57
commit cceb30c729
11 files changed
+657 -85

No files matched your search

+35 -10
View File
@@ -58,11 +58,19 @@ class Forge:
self.notes: dict[int, list[ReviewNote]] = {}
self.posted: list[tuple[int, str]] = []
self.closed_issues: list[int] = []
self.issue = Issue(14, "Add count_words", "count words", "don", ())
self.issues = [Issue(14, "Add count_words", "count words", "don", ())]
self.deps: dict[int, list[int]] = {} # issue -> the issues it depends on
# intake
def assigned_issues(self, repo: str, assignee: str) -> list[Issue]:
return [] if self.issue.number in self.closed_issues else [self.issue]
return [i for i in self.issues if i.number not in self.closed_issues]
def open_dependencies(self, repo: str, index: int) -> list[str]:
return [
f"{repo}#{n}"
for n in self.deps.get(index, [])
if n not in self.closed_issues
]
def assigned_by(self, repo: str, index: int, assignee: str) -> str:
return "don"
@@ -109,14 +117,31 @@ class Forge:
# what the operator does in Gitea
def merge(self, n: int) -> None:
head = self.prs[n]["head"]
git(
"--git-dir",
str(self.bare),
"update-ref",
"refs/heads/main",
f"refs/heads/{head}",
)
head = f"refs/heads/{self.prs[n]['head']}"
bare = ["--git-dir", str(self.bare)]
merged = head
if subprocess.run(
["git", *bare, "merge-base", "--is-ancestor", "refs/heads/main", head],
check=False,
).returncode:
# Main moved on (another item's PR): a real merge commit.
tree = git(*bare, "merge-tree", "--write-tree", "refs/heads/main", head)
merged = git(
*bare,
"-c",
"user.name=g",
"-c",
"user.email=g@t",
"commit-tree",
tree,
"-p",
"refs/heads/main",
"-p",
head,
"-m",
f"Merge PR {n}",
)
git(*bare, "update-ref", "refs/heads/main", merged)
self.prs[n].update(state="closed", merged=True)
def files(self, n: int) -> list[str]: