You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 8a4e5ea
Browse filesBrowse the repository at this point in the historyBrowse files
Address Copilot round 9 on F2d: one deadline per PR operation, adopt on sight
- Each PR operation (open, lookup, refresh, draft change) runs under one
deadline for all of its commands and poll waits (60 s by default),
combined with the caller's signal, instead of 30 s per command.
- An abandoned opening's PR is adopted as soon as publish sees it,
under the state-version guard and without changing the task status,
so a non-clear check no longer leaves the PR unrecorded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/implementation/pull-request-opening.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -52,7 +52,7 @@ Only one publish runs per task at a time; a second one is refused. A publish who
52
52
53
53
1.**Recover.** If an opening is still `opening`, look for the open PR from the task branch, with the markers of all the task's openings for that branch. If it has this opening's marker, record it as opened; if this was a draft opening, this publish is a draft publish too, and the PR has since been made ready, turn it back into a draft first (a failure keeps the opening owned). The recovered opening ends this publish only when it is this publish's own work: same task state version and same draft mode. Otherwise the PR is recorded and publishing continues, so the main path pushes the current head and brings the PR into the current mode. If it has another opening's marker, this opening created nothing (one open PR per branch): mark it `abandoned` and continue. If none exists, the request may still be in flight or not yet visible. So publish stops with `OpeningUnsettled` until the opening is 10 minutes old (`settleMs`). After that, it marks the opening `abandoned` and continues.
54
54
2.**Status and no changes.** Refuse unless the task is running (or in needs human, for a draft). If the task head is its base, open nothing. A running task moves to needs human.
55
-
3.**Find the branch PR.** Ask GitHub for the branch's open PR every time, with the markers of the task's opened and abandoned openings for that branch (none if the task has no PR yet). An open PR that carries none of them was not opened by codeboost: publish refuses before anything is pushed. A PR with an opened record's marker but another number is refused too. When the PR's draft state on GitHub differs from the record, the record is corrected here (for example after a draft change whose record was lost), under the task's state-version guard. This comes before the check: an abandoned opening's PR links the issue and has no recorded number, so only its marker shows it is the task's own, and its number is added to the own PRs for the check.
55
+
3.**Find the branch PR.** Ask GitHub for the branch's open PR every time, with the markers of the task's opened and abandoned openings for that branch (none if the task has no PR yet). An open PR that carries none of them was not opened by codeboost: publish refuses before anything is pushed. A PR with an opened record's marker but another number is refused too. An abandoned opening whose PR is now visible is adopted here (number, URL and draft state recorded, task status unchanged), whatever the check then says, so the record names every PR the task has on GitHub. When the PR's draft state on GitHub differs from the record, the record is corrected here (for example after a draft change whose record was lost), under the task's state-version guard. This comes before the check: an abandoned opening's PR links the issue and has no recorded number, so only its marker shows it is the task's own, and its number is added to the own PRs for the check.
56
56
4.**Check.** Run the check. Record the result, and any status change, in one transaction. That transaction refuses if the task changed during the check.
57
57
5.**Push.** When the task already has an open PR, the refresh is recorded first (the push moves that PR's head), then the push runs; otherwise the push runs straight after the check's transaction with no await in between. Push the task head to `codeboost/issue-<issue>-<task slug>-<hash>`. The slug is readable but can collide. The hash is 16 hex characters of SHA-256 over the exact task identity, so two tasks never share a branch.
58
58
6.**Re-read.** Right before the GitHub call, the Store confirms that the latest check is clear and that nothing changed since that check: same task state version, same review version (approvals, choices and notes), same snapshot, same head.
@@ -73,6 +73,8 @@ When the check matches (or is unknown) and the task's earlier PR is open and rea
73
73
74
74
The adapter refuses any answer for a PR that is not open. A PR closed between the lookup and the update is never recorded as the task's review PR.
75
75
76
+
**Deadlines.** Each PR operation (open, lookup, refresh, draft change) has one deadline for all of its commands and poll waits together: 60 seconds by default (`operationMs`), combined with the caller's signal.
77
+
76
78
**Transport.** The PR title and description go to `gh api --input -` as a JSON body on stdin (`github/run-with-input.ts`), never as arguments: Linux limits one argument to 128 KiB, and a 60,000-character description of multibyte text is larger. The runner settles only after `gh` has exited, including on a timeout or abort: it sends SIGTERM, then SIGKILL after 5 seconds if `gh` is still running, and if a process `gh` started keeps the output pipes open after `gh` exits, it closes them after 1 second. The already-fixed check uses the same runner, so its 12-second deadline always holds. The already-fixed check also waits for both of its reads to settle before it returns.
77
79
78
80
**Environment.** Each `gh` process gets only an allowlist of variables: the path, home and locale; GitHub tokens, host and configuration directories; the D-Bus session bus that Linux keyring sign-in uses; and proxy and CA settings (`github/gh-env.ts`). Prompts, the pager and update checks are turned off.
if(this.#task(key).state_version!==expectedStateVersion)thrownewGuardRefusal('Stale task state. Reload before writing.');
1077
+
if(this.#run("UPDATE task_pull_requests SET state='opened', number=?, url=?, draft=?, updated_at=? WHERE plan_key=? AND opening_id=? AND state='abandoned'",
1078
+
pr.number,pr.url,pr.draft ? 1 : 0,newDate().toISOString(),key,openingId).changes!==1)thrownewGuardRefusal('No abandoned opening with this ID.');
1079
+
this.#touch(key);
1080
+
returnthis.#task(key).state_versionasnumber;
1081
+
});
1082
+
}
1067
1083
/** The task, its review and its head are exactly as a publish read them before its last await. */
0 commit comments