@henryqw/pi-pr 6.2.13 → 9.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -13
- package/docs/pr-routing.svg +137 -88
- package/extensions/pr-ci.ts +49 -14
- package/extensions/pr-command.ts +76 -24
- package/extensions/pr-comment-sweep.ts +172 -34
- package/extensions/pr-create.ts +6 -0
- package/extensions/pr-github.ts +10 -3
- package/extensions/pr-merge.ts +4 -1
- package/extensions/pr-publish-work.ts +37 -18
- package/extensions/pr-routing.ts +20 -22
- package/extensions/pr-run.ts +107 -0
- package/extensions/pr-sync.ts +92 -0
- package/extensions/pr-ui.ts +9 -1
- package/extensions/pr-update-branch.ts +84 -23
- package/extensions/pr.ts +132 -56
- package/package.json +4 -4
- package/skills/pi-pr-comment-sweep/SKILL.md +6 -4
- package/skills/pi-pr-comment-sweep/references/recovery.md +7 -1
- package/skills/pi-pr-fix-ci/SKILL.md +13 -2
- package/skills/pi-pr-publish-work/SKILL.md +4 -2
- package/skills/pi-pr-update-branch/SKILL.md +4 -4
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ See the current branch pull request in the Pi footer. Run `/pr` to create, updat
|
|
|
8
8
|
pi install npm:@henryqw/pi-pr
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Requires an authenticated GitHub CLI session (`gh auth login`) and a GitHub.com or GitHub Enterprise checkout. Verify authentication with `gh auth status`.
|
|
11
|
+
Requires Pi 1.0.0 or newer, an authenticated GitHub CLI session (`gh auth login`) and a GitHub.com or GitHub Enterprise checkout. Verify authentication with `gh auth status`. CI repair requires a GitHub CLI whose `gh api --help` lists `--allow-escape-sequences`; upgrade `gh` if that flag is unavailable.
|
|
12
12
|
|
|
13
13
|
## Works with
|
|
14
14
|
|
|
@@ -22,7 +22,7 @@ The required packages install with `pi-pr`; Herdr itself is optional.
|
|
|
22
22
|
|
|
23
23
|
## Use
|
|
24
24
|
|
|
25
|
-
Run `/pr` in a GitHub checkout. It reads fresh local and GitHub state, takes the next safe route, and continues until it needs external input, encounters a blocker, waits for CI or review, or merges. It works outside Herdr. A stopped workflow can be retried with `/pr` after the blocker is addressed.
|
|
25
|
+
Run `/pr` in a GitHub checkout. It reads fresh local and GitHub state, takes the next safe route, and continues until it needs external input, encounters a blocker, waits for CI or review, or merges. It works outside Herdr. A stopped workflow can be retried with `/pr` after the blocker is addressed. While Pi is busy, internal workflow guidance continues through hidden session context at settlement, not editable user steering or follow-up queues. Cancelling does not restore that guidance to the editor.
|
|
26
26
|
|
|
27
27
|
| Surface | Type | Purpose |
|
|
28
28
|
| --- | --- | --- |
|
|
@@ -46,26 +46,35 @@ The `pi_pr_*` tools are model-only and run one action at a time. Codemode script
|
|
|
46
46
|
|
|
47
47
|
## Flow
|
|
48
48
|
|
|
49
|
-

|
|
50
50
|
|
|
51
51
|
### Routes
|
|
52
52
|
|
|
53
53
|
| Current condition | `/pr` action |
|
|
54
54
|
| --- | --- |
|
|
55
55
|
| No PR and a commit ahead or ordinary pending work | Create a PR, provided the destination is unambiguous and safe. |
|
|
56
|
-
| One matching published PR without a configured upstream | Link its exact remote ref, then rediscover
|
|
57
|
-
|
|
|
58
|
-
|
|
|
56
|
+
| One matching published open PR without a configured upstream, including a draft | Link its exact remote ref, then rediscover. A draft stops after linking; a non-draft continues through ordinary routing. |
|
|
57
|
+
| Local HEAD behind the PR head, or clean local commits that diverged from it | Fast-forward, or rebase the local commits onto the PR head, then continue. Uncommitted work that overlaps the update or a rebase conflict stops with the original HEAD kept. |
|
|
58
|
+
| Intended uncommitted or ahead local work on an open PR | Inspect and publish only owned paths after validation. On a diverged branch, commit the owned paths first, sync onto the PR head, then validate and publish. Stop if ownership is ambiguous or work cannot be separated. |
|
|
59
|
+
| GitHub is still computing mergeability | Re-read fresh state up to three times, five seconds apart, then stop and ask you to rerun. |
|
|
60
|
+
| Confirmed merge conflict, or a base GitHub reports behind | Rebase onto the pinned base and push with an exact lease; stop for unclear conflict resolutions. |
|
|
59
61
|
| Failed GitHub Actions job | Inspect failure evidence, make a scoped fix, validate, and publish. Other failed checks block without an automatic fix. |
|
|
60
62
|
| Changes requested, unresolved threads, or new feedback | Triage and address actionable feedback; leave blocked threads open. |
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
+
| CI running and nothing else blocks | Wait inside the same `/pr`, re-reading GitHub every `ciPollSeconds` for up to `ciWaitMinutes`, then route the fresh state. |
|
|
64
|
+
| Draft, closed, merged, or review still required | Report the state rather than mutate. |
|
|
65
|
+
| Merge-ready | Recheck fresh state and merge with the configured `mergeMethod` (default squash). |
|
|
63
66
|
|
|
64
67
|
Creation chooses the base from `branch.<branch>.gh-merge-base` or the validated `origin` default branch. It requires a commit ahead or ordinary pending work, including untracked files. It stays silent on the base branch itself. The head may be in a fork on the same GitHub host; other fork relationships block creation. A published ref without a PR, multiple candidate remotes or PRs, and unsafe push configuration also block it.
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
An inferred open PR is linked before the draft check. Fresh discovery must confirm the configured PR identity, destination, and head before routing continues. A configured draft reports its state; it does not sync, publish, rebase, fix CI, sweep, or merge.
|
|
67
70
|
|
|
68
|
-
For
|
|
71
|
+
For a configured, open, non-draft PR, local state is normalized before any remote condition is acted on, except when `/pr` finds verified, matching-branch recovery for a branch update or feedback sweep; those workflows resume first. A local HEAD behind the PR head is fast-forwarded, and clean local commits that diverged from it are rebased onto it; neither step pushes, stashes, or resets. Uncommitted work on a diverged branch is committed through the same ownership review as any local work, then the committed branch is synced and published within the same `/pr`. GitHub reports a behind base only when repository policy requires an up-to-date branch, so `/pr` treats it like a conflict and rebases onto the pinned base. Running CI prevents merging, not a safe earlier route; when CI is the only remaining blocker, `/pr` waits within its configured budget and then re-reads GitHub, so comments or conflicts that arrived during the run route to their workflows instead of a merge. Merging happens only inside an explicit `/pr` invocation; GitHub auto-merge is never enabled. New or edited standalone comments and review bodies are assessed before merge or waiting; GitHub does not provide a resolution control for these, so they are triaged rather than marked resolved. A sweep makes scoped fixes without a second approval, but new feedback after publication waits for a later `/pr` cycle. Its guarded `commit` action stages only changed owned paths; checks run on the clean committed HEAD before publication. Unrelated changes block the commit, and interrupted commits must be reconciled through `resume` before continuing. A sweep with no edits creates no empty commit. For each addressed review thread, it replies with only the full fixing commit hash; for a non-actionable thread, it replies with a one-sentence rebuttal. It verifies the reply and resolves the thread, even when the fix moved the lines or made the thread outdated. New or edited comment content still blocks resolution.
|
|
72
|
+
|
|
73
|
+
For an identified PR, the footer shows a linked `PR #number` and a text status such as `N unresolved`, `CI failed`, `merge conflict`, `local behind`, or `merge-ready`. Blocked discovery and unavailable status show generic `PR` text without a number or link. The widget shows a route hint without repeating the footer. Status text remains meaningful without color. When `/pr` begins, the widget shows `⠋ Checking pull request…` until route selection; errors use `✗`, warnings `!`, success `✓`, and neutral routes `●`.
|
|
74
|
+
|
|
75
|
+
### CI evidence
|
|
76
|
+
|
|
77
|
+
CI repair reads each failed job's log once, retaining at most a 20 KiB UTF-8 tail. Only job-log reads use `gh api --allow-escape-sequences`; JSON API reads keep the CLI's default escape guard. The collector strips terminal escape sequences and remaining control characters (including incomplete escapes) from retained output before returning evidence or command-failure diagnostics, preserving UTF-8 text, tabs, and line breaks. Evidence may be shortened further to fit the serialized limits of 20 KiB per failure and 256 KiB across failures. Logs remain untrusted text, not instructions.
|
|
69
78
|
|
|
70
79
|
### Refresh and Herdr
|
|
71
80
|
|
|
@@ -77,12 +86,32 @@ After creating a PR, the extension prefixes the Herdr workspace name with `#<num
|
|
|
77
86
|
|
|
78
87
|
The Pi session stores the configured PR URL, number, host, head identity, and target identity, but not mutable CI or review state. Discovery revalidates this identity against GitHub; no repository cache is created.
|
|
79
88
|
|
|
80
|
-
In-progress feedback sweeps keep private recovery under `<agent-dir>/config/pi-pr/sweep/<worktree-id>/state.json`; branch updates use `<agent-dir>/config/pi-pr/update-branch/`. These files protect in-progress decisions and remote mutation attempts. A fresh `/pr` resumes only matching, verified recovery. If you manually committed and pushed a recorded sweep's fixes, `/pr` can recognize that publication when the clean local HEAD equals the live PR head, descends from the original commit, changes only recorded owned paths, and has no uncertain mutation. It preserves the ledger and refreshes feedback before replying or resolving; it does not push again. Other mismatched or malformed records remain unchanged and block routing; do not delete them or replay an uncertain mutation to force continuation.
|
|
89
|
+
In-progress feedback sweeps keep private recovery under `<agent-dir>/config/pi-pr/sweep/<worktree-id>/state.json`; branch updates use `<agent-dir>/config/pi-pr/update-branch/`. These files protect in-progress decisions and remote mutation attempts. A fresh `/pr` resumes only matching, verified recovery. Movement of the same base branch's tip does not block planning, scoped commits/publication, or recovery; PR identity, base repository/ref, head/lease, destination, and ownership must still match. The sweep keeps its frozen feedback and plan until the post-publish `refresh` binds the current base; replies and resolution require that fresh snapshot. If you manually committed and pushed a recorded sweep's fixes, `/pr` can recognize that publication when the clean local HEAD equals the live PR head, descends from the original commit, changes only recorded owned paths, and has no uncertain mutation. It preserves the ledger and refreshes feedback before replying or resolving; it does not push again. Other mismatched or malformed records remain unchanged and block routing; do not delete them or replay an uncertain mutation to force continuation.
|
|
90
|
+
|
|
91
|
+
### Automatic sweep scope repair
|
|
92
|
+
|
|
93
|
+
Version 8 requires the guarded `validate` action before publishing changed sweep HEADs. Custom callers must move post-publication checks to this step; v3 `finalize` takes `checks: []`. Existing recovery migrates as described below.
|
|
94
|
+
|
|
95
|
+
A matching sweep resumes before local publication. If clean, already-committed work extends its recorded scope, resume reconciles prior attempts and returns `pendingScope` with the exact HEAD and unexpected paths. The agent reviews the complete committed diff and commits against your requested PR work. It adopts **all** intended paths through guarded `adopt`, without asking you for a path list. Being on the branch, or appearing in PR feedback, does not establish ownership. Unrelated or unclear work stops without publishing any of it.
|
|
96
|
+
|
|
97
|
+
Pending scope blocks commit, validation, and publication. Adoption rechecks the exact reviewed HEAD, complete outside-path set, clean worktree, ancestry, PR identity, and original remote lease. It extends only the owned paths, preserving the ledger, feedback generation, original head/lease, and mutation history. Dirty outside work, uncertain mutations, changed authority, and already-published scope cannot be adopted.
|
|
98
|
+
|
|
99
|
+
Before publishing changed sweep HEADs, `validate` runs mandatory `git diff --check` plus selected non-destructive checks, then verifies that HEAD and worktree stayed fixed. The saved validation must match at publication. Check adequacy and ownership still require agent judgment. New v3 sweeps run no new checks after publication; finalization only verifies feedback and cleans up.
|
|
100
|
+
|
|
101
|
+
Unpublished v1/v2 recovery migrates to v3 with no validation. Published legacy recovery retains its saved checks and attempts. Failed legacy checks cannot be replaced or dropped; unknown results stay blocked. A later `/pr` can retry unknown local checks only after you explicitly confirm they are safe to repeat on the published HEAD. This confirmation never authorizes replaying uncertain GitHub or push mutations.
|
|
102
|
+
|
|
103
|
+
### Invocation limits
|
|
104
|
+
|
|
105
|
+
Optional integer settings live in `<agent-dir>/config/pi-pr/config.json`: `maxPublicationCycles` and `maxRepairAttempts` (default `3`), `ciPollSeconds` (default `30`, between `1` and `2147483`), and `ciWaitMinutes` (default `10`; `0` disables waiting for running CI). The string setting `mergeMethod` selects `squash` (default), `merge`, or `rebase` for the direct merge. Missing configuration uses defaults; malformed configuration is preserved and blocks the invocation. Configuration is read once per explicit `/pr`.
|
|
106
|
+
|
|
107
|
+
Limits, frozen route check sets, executed HEADs, and completed route/head keys survive helper resumes and automatic continuations. A check set runs once per HEAD per invocation, including failures; repairing code must change HEAD without dropping checks. Failed or unknown check sets spend repair attempts. Confirmed PR-head movements spend publication cycles once; duplicate observations, no-ops, and failed pushes do not. Exhaustion prevents another push, not published-sweep cleanup or readiness/merge. Repeating a completed route on the same entry head stops. A later explicit `/pr` or reload starts fresh limits. The CI wait budget is shared across every wait in one invocation, including a second CI run after a fix or sweep publishes; when it is spent, `/pr` stops with `still waiting for CI` and a later explicit `/pr` waits again. Waiting for human review never polls.
|
|
81
108
|
|
|
82
109
|
## Limits and recovery
|
|
83
110
|
|
|
84
111
|
- `/pr` does not open a browser, run `/done` or `/sweep`, enable auto-merge, or use a merge queue. It only starts workflows when explicitly invoked.
|
|
112
|
+
- If rebase preflight, fresh sweep startup, or initial CI preflight proves that clean local HEAD advanced from the frozen PR head before any rebase, sweep recovery write, or CI evidence read, `/pr` cancels that obsolete run and automatically rediscovers the next safe route in the same invocation. Rebase preflight also reroutes when the same base ref's OID changes or both the conflict and required base update clear. A behind-base update remains eligible while GitHub still requires it. It can adopt an already-published fast-forward only when clean local HEAD, the fresh PR head, and the remote lease agree, and Git proves the frozen head is an ancestor. The cancellation reports which of these changes was observed; the old updater never rebases or pushes under its obsolete lease. Initial CI preflight can also cancel when only the same base ref's tip changed, with clean equal local HEAD and unchanged PR identity, destination, and exact remote lease. Base movement during or after evidence collection still blocks CI repair. An existing unpublished local merge goes through inspection and validation before publication, not another rebase; fresh discovery then selects CI repair, feedback triage, waiting, or merging against the published head. No stale CI evidence or consumed collect action is reused.
|
|
113
|
+
- Automatic handoff reserves a fresh run ID before settlement; expired IDs are never revived. Rediscovery is limited to two attempts across the invocation without resetting publication or repair budgets. It requires the same PR identity and destination, and pins the remote head to the validated cancellation snapshot (including a proven published fast-forward). Another head change during rediscovery stops the handoff. Rerouting never bypasses unreconciled rebase or sweep recovery. Dirty or divergent state, changed branches or destinations, aborts, and uncertain mutations still stop; no mutation is blindly retried.
|
|
85
114
|
- Pushes use a saved exact remote OID lease and revalidate the destination. Concurrent updates block publication instead of being overwritten. Creation does not change the base branch, and a configured push target does not change upstream settings.
|
|
86
|
-
- Direct merge requires a clean, safe local Git state and fresh matching head; it
|
|
87
|
-
- A Git operation in progress blocks direct merge. An unverified conflict rebase needs manual recovery; a verified rebase can resume through `/pr`. Rebasing leaves other local branch refs unchanged, even with `rebase.updateRefs=true`. Branches with merge commits since the fork point require manual rebase resolution.
|
|
115
|
+
- Direct merge requires a clean, safe local Git state and fresh matching head; it uses the configured `mergeMethod`. GitHub repository policy may still reject a method the repository does not allow. No branch or worktree is deleted.
|
|
116
|
+
- A Git operation in progress blocks direct merge. A paused conflict rebase keeps checking the recorded branch's PR authority even while Git detaches HEAD. An unverified conflict rebase interrupted by a restart needs manual recovery; a verified rebase can resume through `/pr`. Rebasing leaves other local branch refs unchanged, even with `rebase.updateRefs=true`. Branches with merge commits since the fork point require manual rebase resolution.
|
|
88
117
|
- Only authenticated GitHub.com and GitHub Enterprise repositories are supported. Resolve ambiguous remotes, unrelated local changes, or GitHub blockers before retrying `/pr`.
|
package/docs/pr-routing.svg
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720" role="img" aria-labelledby="pr-routing-title pr-routing-desc">
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720" role="img" aria-labelledby="pr-routing-title pr-routing-desc" id="pr-routing-root">
|
|
3
3
|
<title id="pr-routing-title">How /pr picks each next route</title>
|
|
4
|
-
<desc id="pr-routing-desc">The /pr command creates a pull request when needed,
|
|
4
|
+
<desc id="pr-routing-desc">The /pr command creates a pull request when needed, links an inferred open PR before draft handling and repeats discovery, stops on a configured draft, then for non-draft open PRs syncs local state, publishes scoped work, re-reads pending mergeability, rebases conflicts or behind bases, fixes CI, sweeps feedback, waits within its CI budget, and merges only when fresh state is ready.</desc>
|
|
5
5
|
<defs>
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
<style>@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&family=Noto+Serif:ital@0;1&family=Noto+Sans+KR:wght@400;500;600&family=Noto+Serif+KR:wght@400&family=Noto+Sans+TC:wght@400;500;600&family=Noto+Serif+TC:wght@400&display=swap');
|
|
7
|
+
#pr-routing-root .diagram-container { overflow-x: auto; }</style>
|
|
8
|
+
<marker id="pr-routing-pr-routing-arrow" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
8
9
|
<polygon points="0 0, 8 4, 0 8" fill="#4c5665"/>
|
|
9
10
|
</marker>
|
|
10
|
-
<marker id="pr-routing-arrow-accent" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
11
|
+
<marker id="pr-routing-pr-routing-arrow-accent" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
11
12
|
<polygon points="0 0, 8 4, 0 8" fill="#1d4ed8"/>
|
|
12
13
|
</marker>
|
|
13
|
-
<marker id="pr-routing-arrow-link" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
14
|
+
<marker id="pr-routing-pr-routing-arrow-link" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
14
15
|
<polygon points="0 0, 8 4, 0 8" fill="#1d4ed8"/>
|
|
15
16
|
</marker>
|
|
16
17
|
</defs>
|
|
@@ -19,109 +20,157 @@
|
|
|
19
20
|
|
|
20
21
|
<!-- Header -->
|
|
21
22
|
<text x="80" y="68" fill="#101828" font-size="28" font-weight="400" font-family="'Instrument Serif', serif">How /pr picks each next route</text>
|
|
22
|
-
<text x="80" y="88" fill="#4c5665" font-size="8" font-family="'Geist', sans-serif">
|
|
23
|
+
<text x="80" y="88" fill="#4c5665" font-size="8" font-family="'Geist', sans-serif">Ordinary route order; verified branch-update or sweep recovery resumes first on configured, open, non-draft PRs.</text>
|
|
23
24
|
<text x="1200" y="48" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.12em">FIRST MATCHING CONDITION WINS</text>
|
|
24
25
|
<text x="1200" y="68" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.12em">FRESH AFTER EACH ROUTE</text>
|
|
25
26
|
|
|
26
27
|
<!-- Continuation arrows, drawn before nodes -->
|
|
27
|
-
<g fill="none" stroke="#4c5665" stroke-width="1.2" marker-end="url(#pr-routing-arrow)">
|
|
28
|
-
<line x1="640" y1="
|
|
29
|
-
<line x1="640" y1="
|
|
30
|
-
<line x1="640" y1="
|
|
31
|
-
<line x1="640" y1="
|
|
32
|
-
<line x1="640" y1="
|
|
33
|
-
<line x1="640" y1="
|
|
34
|
-
<line x1="640" y1="
|
|
28
|
+
<g fill="none" stroke="#4c5665" stroke-width="1.2" marker-end="url(#pr-routing-pr-routing-arrow)">
|
|
29
|
+
<line x1="640" y1="136" x2="640" y2="152"/>
|
|
30
|
+
<line x1="640" y1="188" x2="640" y2="196"/>
|
|
31
|
+
<line x1="640" y1="232" x2="640" y2="240"/>
|
|
32
|
+
<line x1="640" y1="276" x2="640" y2="284"/>
|
|
33
|
+
<line x1="640" y1="320" x2="640" y2="328"/>
|
|
34
|
+
<line x1="640" y1="364" x2="640" y2="372"/>
|
|
35
|
+
<line x1="640" y1="408" x2="640" y2="416"/>
|
|
36
|
+
<line x1="640" y1="452" x2="640" y2="460"/>
|
|
37
|
+
<line x1="640" y1="496" x2="640" y2="504"/>
|
|
38
|
+
<line x1="640" y1="540" x2="640" y2="548"/>
|
|
39
|
+
<line x1="640" y1="584" x2="640" y2="592"/>
|
|
35
40
|
</g>
|
|
36
41
|
|
|
37
42
|
<!-- Input node -->
|
|
38
|
-
<rect x="240" y="96" width="800" height="
|
|
39
|
-
<text x="640" y="
|
|
40
|
-
<text x="640" y="
|
|
43
|
+
<rect x="240" y="96" width="800" height="40" rx="8" fill="#1d4ed8" fill-opacity="0.08" stroke="#1d4ed8" stroke-width="1.2"/>
|
|
44
|
+
<text x="640" y="114" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">/pr reads fresh state</text>
|
|
45
|
+
<text x="640" y="129" fill="#1d4ed8" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">GITHUB + LOCAL STATE</text>
|
|
41
46
|
|
|
42
47
|
<!-- 1 -->
|
|
43
|
-
<rect x="120" y="
|
|
44
|
-
<line x1="176" y1="
|
|
45
|
-
<line x1="704" y1="
|
|
46
|
-
<text x="148" y="
|
|
47
|
-
<text x="200" y="
|
|
48
|
-
<text x="200" y="
|
|
49
|
-
<text x="728" y="
|
|
50
|
-
<text x="728" y="
|
|
51
|
-
<text x="1136" y="
|
|
48
|
+
<rect x="120" y="152" width="1040" height="36" rx="6" fill="#ffffff" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
49
|
+
<line x1="176" y1="156" x2="176" y2="184" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
50
|
+
<line x1="704" y1="156" x2="704" y2="184" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
51
|
+
<text x="148" y="175" fill="#4c5665" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">1</text>
|
|
52
|
+
<text x="200" y="163" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
53
|
+
<text x="200" y="179" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Safe PR absence</text>
|
|
54
|
+
<text x="728" y="163" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
55
|
+
<text x="728" y="179" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Create pull request</text>
|
|
56
|
+
<text x="1136" y="163" fill="#4c5665" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">pi-pr-create</text>
|
|
52
57
|
|
|
53
58
|
<!-- 2 -->
|
|
54
|
-
<rect x="120" y="
|
|
55
|
-
<line x1="176" y1="
|
|
56
|
-
<line x1="704" y1="
|
|
57
|
-
<text x="148" y="
|
|
58
|
-
<text x="200" y="
|
|
59
|
-
<text x="200" y="
|
|
60
|
-
<text x="728" y="
|
|
61
|
-
<text x="728" y="
|
|
62
|
-
<text x="1136" y="
|
|
59
|
+
<rect x="120" y="196" width="1040" height="36" rx="6" fill="#ffffff" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
60
|
+
<line x1="176" y1="200" x2="176" y2="228" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
61
|
+
<line x1="704" y1="200" x2="704" y2="228" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
62
|
+
<text x="148" y="219" fill="#4c5665" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">2</text>
|
|
63
|
+
<text x="200" y="207" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
64
|
+
<text x="200" y="223" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Inferred open PR (including draft)</text>
|
|
65
|
+
<text x="728" y="207" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
66
|
+
<text x="728" y="223" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Link exact remote ref; rediscover</text>
|
|
67
|
+
<text x="1136" y="207" fill="#4c5665" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">link-branch; draft stops after discovery</text>
|
|
63
68
|
|
|
64
69
|
<!-- 3 -->
|
|
65
|
-
<rect x="120" y="
|
|
66
|
-
<line x1="176" y1="
|
|
67
|
-
<line x1="704" y1="
|
|
68
|
-
<text x="148" y="
|
|
69
|
-
<text x="200" y="
|
|
70
|
-
<text x="200" y="
|
|
71
|
-
<text x="728" y="
|
|
72
|
-
<text x="728" y="
|
|
73
|
-
<text x="1136" y="
|
|
70
|
+
<rect x="120" y="240" width="1040" height="36" rx="6" fill="#ffffff" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
71
|
+
<line x1="176" y1="244" x2="176" y2="272" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
72
|
+
<line x1="704" y1="244" x2="704" y2="272" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
73
|
+
<text x="148" y="263" fill="#4c5665" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">3</text>
|
|
74
|
+
<text x="200" y="251" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
75
|
+
<text x="200" y="267" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Merged, closed, or configured draft</text>
|
|
76
|
+
<text x="728" y="251" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
77
|
+
<text x="728" y="267" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">No action</text>
|
|
78
|
+
<text x="1136" y="251" fill="#4c5665" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">report current state</text>
|
|
74
79
|
|
|
75
80
|
<!-- 4 -->
|
|
76
|
-
<rect x="120" y="
|
|
77
|
-
<line x1="176" y1="
|
|
78
|
-
<line x1="704" y1="
|
|
79
|
-
<text x="148" y="
|
|
80
|
-
<text x="200" y="
|
|
81
|
-
<text x="200" y="
|
|
82
|
-
<text x="728" y="
|
|
83
|
-
<text x="728" y="
|
|
84
|
-
<text x="1136" y="
|
|
81
|
+
<rect x="120" y="284" width="1040" height="36" rx="6" fill="#ffffff" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
82
|
+
<line x1="176" y1="288" x2="176" y2="316" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
83
|
+
<line x1="704" y1="288" x2="704" y2="316" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
84
|
+
<text x="148" y="307" fill="#4c5665" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">4</text>
|
|
85
|
+
<text x="200" y="295" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
86
|
+
<text x="200" y="311" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Local HEAD behind or diverged (clean)</text>
|
|
87
|
+
<text x="728" y="295" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
88
|
+
<text x="728" y="311" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Sync local branch</text>
|
|
89
|
+
<text x="1136" y="295" fill="#4c5665" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">ff-only or rebase onto PR head; never pushes</text>
|
|
85
90
|
|
|
86
91
|
<!-- 5 -->
|
|
87
|
-
<rect x="120" y="
|
|
88
|
-
<line x1="176" y1="
|
|
89
|
-
<line x1="704" y1="
|
|
90
|
-
<text x="148" y="
|
|
91
|
-
<text x="200" y="
|
|
92
|
-
<text x="200" y="
|
|
93
|
-
<text x="728" y="
|
|
94
|
-
<text x="728" y="
|
|
95
|
-
<text x="1136" y="
|
|
92
|
+
<rect x="120" y="328" width="1040" height="36" rx="6" fill="#ffffff" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
93
|
+
<line x1="176" y1="332" x2="176" y2="360" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
94
|
+
<line x1="704" y1="332" x2="704" y2="360" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
95
|
+
<text x="148" y="351" fill="#4c5665" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">5</text>
|
|
96
|
+
<text x="200" y="339" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
97
|
+
<text x="200" y="355" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Dirty or ahead local work (diverged commits first)</text>
|
|
98
|
+
<text x="728" y="339" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
99
|
+
<text x="728" y="355" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Publish scoped work</text>
|
|
100
|
+
<text x="1136" y="339" fill="#4c5665" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">pi-pr-publish-work</text>
|
|
96
101
|
|
|
97
102
|
<!-- 6 -->
|
|
98
|
-
<rect x="120" y="
|
|
99
|
-
<line x1="176" y1="
|
|
100
|
-
<line x1="704" y1="
|
|
101
|
-
<text x="148" y="
|
|
102
|
-
<text x="200" y="
|
|
103
|
-
<text x="200" y="
|
|
104
|
-
<text x="728" y="
|
|
105
|
-
<text x="728" y="
|
|
106
|
-
<text x="1136" y="
|
|
103
|
+
<rect x="120" y="372" width="1040" height="36" rx="6" fill="#ffffff" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
104
|
+
<line x1="176" y1="376" x2="176" y2="404" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
105
|
+
<line x1="704" y1="376" x2="704" y2="404" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
106
|
+
<text x="148" y="395" fill="#4c5665" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">6</text>
|
|
107
|
+
<text x="200" y="383" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
108
|
+
<text x="200" y="399" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">GitHub mergeability still computing</text>
|
|
109
|
+
<text x="728" y="383" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
110
|
+
<text x="728" y="399" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Re-read fresh state</text>
|
|
111
|
+
<text x="1136" y="383" fill="#4c5665" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">up to 3 reads, 5 s apart</text>
|
|
107
112
|
|
|
108
113
|
<!-- 7 -->
|
|
109
|
-
<rect x="120" y="
|
|
110
|
-
<line x1="176" y1="
|
|
111
|
-
<line x1="704" y1="
|
|
112
|
-
<text x="148" y="
|
|
113
|
-
<text x="200" y="
|
|
114
|
-
<text x="200" y="
|
|
115
|
-
<text x="728" y="
|
|
116
|
-
<text x="728" y="
|
|
117
|
-
<text x="1136" y="
|
|
114
|
+
<rect x="120" y="416" width="1040" height="36" rx="6" fill="#ffffff" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
115
|
+
<line x1="176" y1="420" x2="176" y2="448" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
116
|
+
<line x1="704" y1="420" x2="704" y2="448" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
117
|
+
<text x="148" y="439" fill="#4c5665" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">7</text>
|
|
118
|
+
<text x="200" y="427" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
119
|
+
<text x="200" y="443" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Confirmed conflict or behind base</text>
|
|
120
|
+
<text x="728" y="427" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
121
|
+
<text x="728" y="443" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Rebase onto pinned base</text>
|
|
122
|
+
<text x="1136" y="427" fill="#4c5665" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">pi-pr-update-branch; exact lease push</text>
|
|
123
|
+
|
|
124
|
+
<!-- 8 -->
|
|
125
|
+
<rect x="120" y="460" width="1040" height="36" rx="6" fill="#ffffff" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
126
|
+
<line x1="176" y1="464" x2="176" y2="492" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
127
|
+
<line x1="704" y1="464" x2="704" y2="492" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
128
|
+
<text x="148" y="483" fill="#4c5665" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">8</text>
|
|
129
|
+
<text x="200" y="471" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
130
|
+
<text x="200" y="487" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">CI failed</text>
|
|
131
|
+
<text x="728" y="471" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
132
|
+
<text x="728" y="487" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Fix CI</text>
|
|
133
|
+
<text x="1136" y="471" fill="#4c5665" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">pi-pr-fix-ci; other failed checks block</text>
|
|
134
|
+
|
|
135
|
+
<!-- 9 -->
|
|
136
|
+
<rect x="120" y="504" width="1040" height="36" rx="6" fill="#ffffff" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
137
|
+
<line x1="176" y1="508" x2="176" y2="536" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
138
|
+
<line x1="704" y1="508" x2="704" y2="536" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
139
|
+
<text x="148" y="527" fill="#4c5665" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">9</text>
|
|
140
|
+
<text x="200" y="515" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
141
|
+
<text x="200" y="531" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Changes requested or new feedback</text>
|
|
142
|
+
<text x="728" y="515" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
143
|
+
<text x="728" y="531" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Sweep</text>
|
|
144
|
+
<text x="1136" y="515" fill="#4c5665" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">pi-pr-comment-sweep</text>
|
|
145
|
+
|
|
146
|
+
<!-- 10 -->
|
|
147
|
+
<rect x="120" y="548" width="1040" height="36" rx="6" fill="#ffffff" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
148
|
+
<line x1="176" y1="552" x2="176" y2="580" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
149
|
+
<line x1="704" y1="552" x2="704" y2="580" stroke="#101828" stroke-opacity="0.141" stroke-width="1"/>
|
|
150
|
+
<text x="148" y="571" fill="#4c5665" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">10</text>
|
|
151
|
+
<text x="200" y="559" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
152
|
+
<text x="200" y="575" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">CI running, nothing else blocking</text>
|
|
153
|
+
<text x="728" y="559" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
154
|
+
<text x="728" y="575" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Wait, then re-read</text>
|
|
155
|
+
<text x="1136" y="559" fill="#4c5665" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">poll every ciPollSeconds, up to ciWaitMinutes</text>
|
|
156
|
+
|
|
157
|
+
<!-- 11 -->
|
|
158
|
+
<rect x="120" y="592" width="1040" height="36" rx="6" fill="#1d4ed8" fill-opacity="0.08" stroke="#1d4ed8" stroke-width="1.2"/>
|
|
159
|
+
<line x1="176" y1="596" x2="176" y2="624" stroke="#1d4ed8" stroke-opacity="0.32" stroke-width="1"/>
|
|
160
|
+
<line x1="704" y1="596" x2="704" y2="624" stroke="#1d4ed8" stroke-opacity="0.32" stroke-width="1"/>
|
|
161
|
+
<text x="148" y="615" fill="#1d4ed8" font-size="12" font-family="'Geist Mono', monospace" text-anchor="middle">11</text>
|
|
162
|
+
<text x="200" y="603" fill="#1d4ed8" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONDITION</text>
|
|
163
|
+
<text x="200" y="619" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Merge-ready</text>
|
|
164
|
+
<text x="728" y="603" fill="#1d4ed8" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">ROUTE</text>
|
|
165
|
+
<text x="728" y="619" fill="#101828" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Recheck, merge</text>
|
|
166
|
+
<text x="1136" y="603" fill="#1d4ed8" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">configured mergeMethod; expected-head mutation</text>
|
|
118
167
|
|
|
119
168
|
<!-- Legend strip -->
|
|
120
|
-
<line x1="44" y1="
|
|
121
|
-
<line x1="80" y1="
|
|
122
|
-
<text x="104" y="
|
|
123
|
-
<text x="104" y="
|
|
124
|
-
<text x="668" y="
|
|
125
|
-
<text x="724" y="
|
|
126
|
-
<text x="724" y="
|
|
169
|
+
<line x1="44" y1="640" x2="1236" y2="640" stroke="#101828" stroke-opacity="0.141" stroke-width="0.8"/>
|
|
170
|
+
<line x1="80" y1="650" x2="80" y2="674" stroke="#4c5665" stroke-width="1.2" marker-end="url(#pr-routing-pr-routing-arrow)"/>
|
|
171
|
+
<text x="104" y="654" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">DOWNWARD CONNECTOR</text>
|
|
172
|
+
<text x="104" y="674" fill="#101828" font-size="8" font-family="'Geist', sans-serif">Otherwise, continue to the next condition.</text>
|
|
173
|
+
<text x="668" y="672" fill="#101828" font-size="28" font-weight="400" font-family="'Instrument Serif', serif">↻</text>
|
|
174
|
+
<text x="724" y="654" fill="#4c5665" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">CONTINUATION RULE</text>
|
|
175
|
+
<text x="724" y="674" fill="#101828" font-size="8" font-family="'Geist', sans-serif">The first match runs; link, sync, re-read, and completed routes return to fresh discovery.</text>
|
|
127
176
|
</svg>
|