Multi-milestone work and documents on the new loop (M2.4)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 3m45s
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 3m45s
- Every milestone branch is cut from the latest default branch: the next milestone builds on the merged previous one, and a redo after a closed PR drops the rejected work. Uncommitted work comes along. - While the item's PR is open, start_milestone is refused, with the PR's state read from Gitea at that moment, not the last watch round. - finish_milestone(final): earlier PRs say "Part of #N", the last "Closes #N"; unsaid, it's final, as before. - close_item with the item's PR still open waits for the merge (L2: a closed item leaves nothing open). - The driver's instructions cover ARCHITECTURE.md and build plans under docs/plans/ (D2); the harness never parses a plan. - Parking stashes uncommitted work and pushes it to <item>-parked, put back on resume, instead of committing it onto the item's branch. The walk found a half-done review fix parked onto a PR that had merged meanwhile, where it was lost. Architecture D4's parking line says so. Tests: unit tests for fresh milestone branches, the merge wait (including a merge not yet watched), redo after a closed PR, Part of/Closes, close waiting on an open PR, and the parked-fix regression; the walk's new milestones_start_fresh property (fork-from-current fails 9 seeds); an e2e two-milestone feature. 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:
1 parent
da32cac5cf
commit
cd9ab2559e
10 files changed
+539
-53
No files matched your search
@@ -425,3 +425,59 @@ def test_a_plain_yes_is_a_go_ahead(
|
||||
prs.append(pr)
|
||||
finally:
|
||||
operator.tidy(issues, prs)
|
||||
|
||||
|
||||
def test_a_two_milestone_feature(
|
||||
running: tuple[RunningLoop, InMemoryTransport, str],
|
||||
) -> None:
|
||||
"""M2.4: work in two milestones. The first PR carries the build plan and
|
||||
is part of the issue; the second starts from the merged first and closes
|
||||
it; the item closes after both merge."""
|
||||
agent, transport, run = running
|
||||
operator = Operator(agent, transport)
|
||||
gitea, repo = agent.gitea, agent.config.gitea_repo
|
||||
as_operator = operator_gitea(agent.config.gitea_url)
|
||||
issues: list[int] = []
|
||||
prs: list[int] = []
|
||||
try:
|
||||
body = (
|
||||
f"{task(run)}\n\nThen, separately, {dependent_task(run)[0].lower()}"
|
||||
f"{dependent_task(run)[1:]}\n\nDo this in two milestones, one PR each: "
|
||||
f"vowels_{run} first, then consonants_{run} after the first has merged. "
|
||||
"Write a short build plan for it."
|
||||
)
|
||||
issues.append(as_operator.create_issue(repo, f"letters_{run}", body))
|
||||
as_operator._request(
|
||||
"PATCH",
|
||||
f"/repos/{repo}/issues/{issues[0]}",
|
||||
json={"assignees": [agent.account]},
|
||||
)
|
||||
operator.tick()
|
||||
|
||||
# --- milestone 1: the plan and vowels; part of the issue ---
|
||||
item = operator.drive_until(MERGE, issues[0])
|
||||
first = loop_of(item).pr
|
||||
prs.append(first)
|
||||
pr = gitea._request("GET", f"/repos/{repo}/pulls/{first}")
|
||||
assert f"Part of #{issues[0]}" in pr["body"], pr["body"]
|
||||
files = [
|
||||
f["filename"]
|
||||
for f in gitea._request("GET", f"/repos/{repo}/pulls/{first}/files")
|
||||
]
|
||||
assert any(f.startswith("docs/plans/") for f in files), files
|
||||
merge(as_operator, repo, first)
|
||||
operator.tick()
|
||||
|
||||
# --- milestone 2: from the merged first; closes the issue ---
|
||||
item = operator.drive_until(MERGE, issues[0])
|
||||
second = loop_of(item).pr
|
||||
assert second != first
|
||||
prs.append(second)
|
||||
pr = gitea._request("GET", f"/repos/{repo}/pulls/{second}")
|
||||
assert f"Closes #{issues[0]}" in pr["body"], pr["body"]
|
||||
diff = gitea._http.get(f"/repos/{repo}/pulls/{second}.diff").text
|
||||
assert f"consonants_{run}" in diff
|
||||
assert f"\n+def vowels_{run}" not in diff # built on the merged first
|
||||
operator.merge_and_close(issues[0])
|
||||
finally:
|
||||
operator.tidy(issues, prs)
|
||||
Reference in new issue
Block a user