@erclx/canon 5.39.0 → 5.40.0

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.
Files changed (131) hide show
  1. package/README.md +5 -5
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/canvas-board/SKILL.md +1 -1
  4. package/claude/skills/draft-slides/SKILL.md +2 -3
  5. package/claude/skills/draft-slides/references/deck.md +183 -0
  6. package/claude/skills/git-pr/references/evidence.md +2 -2
  7. package/claude/skills/git-pr/references/final-command.md +1 -1
  8. package/claude/skills/git-pr/references/labels.md +1 -1
  9. package/claude/skills/git-pr/references/publish.md +1 -1
  10. package/claude/skills/review-ui/SKILL.md +1 -0
  11. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  12. package/claude/skills/task-board/references/board.md +1 -1
  13. package/claude/skills/task-board/references/tasks.md +1 -1
  14. package/claude/skills/ux-measure/SKILL.md +1 -1
  15. package/docs/agents/audits.md +61 -60
  16. package/docs/agents/context-audit.md +40 -58
  17. package/docs/agents/index.md +5 -51
  18. package/docs/agents/install-and-sync.md +1 -0
  19. package/docs/agents/markdown-audit.md +53 -99
  20. package/docs/agents/records.md +37 -79
  21. package/docs/agents/sessions-transfer.md +26 -70
  22. package/docs/target/index.md +0 -1
  23. package/docs/target/migrations.md +57 -66
  24. package/docs/target/projects.md +36 -78
  25. package/docs/target/scenarios.md +9 -19
  26. package/docs/target/sync.md +27 -68
  27. package/docs/workflow/ai-workflow.md +22 -68
  28. package/docs/workflow/operating-model.md +32 -186
  29. package/docs/workflow/parallel-features.md +29 -66
  30. package/docs/workflow/skill-map-on-request.md +22 -24
  31. package/docs/workflow/skill-map.md +72 -76
  32. package/docs/workflow/visual-design-workflow.md +25 -118
  33. package/docs/workflow/zshrc-aliases.md +18 -37
  34. package/package.json +1 -1
  35. package/src/canvas/client/inspector/fields.css +4 -0
  36. package/src/canvas/index.html +6 -0
  37. package/src/claude/skills-reach.ts +2 -2
  38. package/src/cli/ui.ts +3 -1
  39. package/src/commands/canvas.ts +23 -0
  40. package/src/commands/capture.ts +14 -0
  41. package/src/commands/claude.ts +13 -0
  42. package/src/commands/demo.ts +22 -0
  43. package/src/commands/deps.ts +6 -0
  44. package/src/commands/driver.ts +28 -2
  45. package/src/commands/gov.ts +52 -18
  46. package/src/commands/intake.ts +17 -0
  47. package/src/commands/labels.ts +38 -5
  48. package/src/commands/pr.ts +160 -43
  49. package/src/commands/records.ts +8 -0
  50. package/src/commands/secrets.ts +6 -0
  51. package/src/commands/sessions.ts +8 -0
  52. package/src/commands/tasks.ts +52 -0
  53. package/src/commands/teach.ts +13 -0
  54. package/src/commands/upstream.ts +25 -1
  55. package/src/commands/worktrees.ts +5 -0
  56. package/src/context/audit.ts +22 -4
  57. package/src/context/folders.ts +1 -1
  58. package/src/context/shape-gated.ts +4 -2
  59. package/src/counts/scan.ts +1 -1
  60. package/src/design/components.ts +1 -1
  61. package/src/design/document.ts +1 -1
  62. package/src/design/tokens.ts +1 -1
  63. package/src/gate/boundaries.ts +4 -1
  64. package/src/gate/measures.ts +1 -1
  65. package/src/gate/stages.ts +7 -7
  66. package/src/git/env.ts +6 -0
  67. package/src/markdown/scope.ts +5 -17
  68. package/src/pr/lift.ts +153 -19
  69. package/src/pr/painted.ts +87 -0
  70. package/src/roots/record.ts +18 -1
  71. package/src/sandbox/census.ts +1 -1
  72. package/src/sandbox/headless/run.ts +3 -2
  73. package/src/sandbox/headless/watch.ts +3 -4
  74. package/src/slides/convert/master.ts +3 -0
  75. package/src/sync/format.ts +46 -0
  76. package/src/sync/stamp.ts +6 -0
  77. package/tooling/astro/reference.md +2 -2
  78. package/tooling/base/reference.md +1 -1
  79. package/tooling/go/reference.md +1 -1
  80. package/tooling/nextjs/reference.md +1 -1
  81. package/tooling/php/reference.md +1 -1
  82. package/tooling/python/reference.md +1 -1
  83. package/tooling/vite-react/reference.md +1 -1
  84. package/tooling/web/reference.md +1 -1
  85. package/docs/agents/canvas.md +0 -86
  86. package/docs/agents/capture.md +0 -65
  87. package/docs/agents/census.md +0 -23
  88. package/docs/agents/comments.md +0 -34
  89. package/docs/agents/context-audit-checks.md +0 -108
  90. package/docs/agents/context-classify.md +0 -101
  91. package/docs/agents/counts.md +0 -74
  92. package/docs/agents/demo.md +0 -140
  93. package/docs/agents/driver.md +0 -103
  94. package/docs/agents/gate.md +0 -90
  95. package/docs/agents/hooks.md +0 -78
  96. package/docs/agents/intake.md +0 -81
  97. package/docs/agents/key-changes.md +0 -116
  98. package/docs/agents/label-coverage.md +0 -86
  99. package/docs/agents/markdown-audit-density.md +0 -39
  100. package/docs/agents/markdown-audit-weight.md +0 -83
  101. package/docs/agents/pr-evidence-layout.md +0 -70
  102. package/docs/agents/pr-evidence.md +0 -273
  103. package/docs/agents/pr-frames.md +0 -131
  104. package/docs/agents/pr-lift.md +0 -132
  105. package/docs/agents/pr-local.md +0 -111
  106. package/docs/agents/pr-preview.md +0 -155
  107. package/docs/agents/pr-reads.md +0 -111
  108. package/docs/agents/pr-tick.md +0 -72
  109. package/docs/agents/records-push.md +0 -61
  110. package/docs/agents/records-stale.md +0 -40
  111. package/docs/agents/records-validate.md +0 -57
  112. package/docs/agents/restated.md +0 -83
  113. package/docs/agents/routing.md +0 -61
  114. package/docs/agents/rule-citations.md +0 -90
  115. package/docs/agents/sandbox.md +0 -126
  116. package/docs/agents/sessions.md +0 -128
  117. package/docs/agents/skills-audit.md +0 -120
  118. package/docs/agents/skills-reach.md +0 -64
  119. package/docs/agents/slides.md +0 -176
  120. package/docs/agents/state-scoped-risk.md +0 -105
  121. package/docs/agents/superseded.md +0 -85
  122. package/docs/agents/tasks-archive.md +0 -83
  123. package/docs/agents/tasks-plans.md +0 -152
  124. package/docs/agents/tasks-validate.md +0 -133
  125. package/docs/agents/tasks.md +0 -123
  126. package/docs/agents/teach-authoring.md +0 -118
  127. package/docs/agents/teach.md +0 -157
  128. package/docs/agents/test-order.md +0 -58
  129. package/docs/agents/upstream.md +0 -94
  130. package/docs/agents/worktrees.md +0 -118
  131. package/docs/target/standards-moved.md +0 -34
@@ -1,273 +0,0 @@
1
- ---
2
- title: The pull request evidence comparison
3
- description: What canon pr evidence compares, the marker that lets it edit its own comment rather than duplicate it, the refusal reasons it names, what the --check read reports as owed, and why the comparison anchors at the merge base rather than the previous push
4
- ---
5
-
6
- # The pull request evidence comparison
7
-
8
- `canon pr evidence` renders one comment naming every changed image under an
9
- `evidence/` path segment, comparing each against the pull request's merge base.
10
- `git-pr` posts it when a pull request opens or is edited, and `git-followup`
11
- posts it again after every later push, so a reviewer never has to open Files
12
- Changed to see what a case looked like before and after.
13
-
14
- ```bash
15
- canon pr evidence
16
- canon pr evidence 1341 --json
17
- ```
18
-
19
- ## Why the comparison anchors at the merge base
20
-
21
- The comparison is always base against the current head, never the previous
22
- push against the new one. A follow-up only ever moves the after side of the
23
- comment, so what the comment claims can only grow to match what the branch
24
- actually shows, and a reviewer who opens it midway through a review round
25
- never reads a stale before image describing an intermediate commit nobody is
26
- looking at anymore.
27
-
28
- ## What counts as evidence
29
-
30
- A changed path qualifies when one of its segments is literally `evidence` and
31
- its filename carries an image extension (`png`, `jpg`, `jpeg`, `gif`, `webp`,
32
- `avif`, `svg`). The second half of that test exists because an `evidence/`
33
- folder holds whatever else a project keeps beside its captures. This
34
- repository's own tree carries eight `.md` files, two `.sh` scripts, a `.tsv`,
35
- a `.json`, and an `.html` file against a single `.png`, and every one of them
36
- would render as a broken image embed without the extension filter.
37
-
38
- ## What the record carries
39
-
40
- The record groups every evidence path by state, the remainder of its
41
- directory under the `evidence/` segment, and within a state by filename stem,
42
- so a `before/hero.png` and an `after/hero.png` naming the same case group as
43
- one entry, not two files. Each entry carries whether it existed at the base
44
- commit, so the comment shows a base image or marks it new. Each `<img>` takes
45
- its PNG width, at most 1000. `canon docs pr-evidence-layout` covers how the body
46
- groups those entries by view.
47
-
48
- `reason` on the record is what a caller branches on, not the exit code:
49
-
50
- | Reason | What it means |
51
- | ---------------------- | --------------------------------------------------------------------------------------- |
52
- | `ok` | A body was rendered. `commentId` is set when a marked comment already exists. |
53
- | `no-evidence` | No `evidence/` segment in the diff and no `--checklist` or `--preview`. A silent no-op. |
54
- | `unreadable-checklist` | `--checklist` named a path that could not be read, or one holding nothing. |
55
- | `gh-missing` | `gh` is not on the path, so no pull request could be resolved. |
56
- | `gh-failed` | `gh` could not answer for this repository or branch, or read its comments. |
57
- | `no-branch` | The pull request carries no head branch name. |
58
- | `no-object-head` | The pull request object reported no head commit. |
59
- | `no-base` | GitHub reported no merge base for the pull request. |
60
- | `unreadable-changes` | GitHub could not list what the pull request changed. |
61
- | `would-empty` | The render holds no cases and the marked comment holds some. No body. |
62
- | `check-writes` | `--check` was passed with `--preview`, `--local`, or `--checklist`. |
63
-
64
- `no-evidence` is not a refusal a caller reports. A project on a stack that
65
- carries no evidence path, such as `base` or `python`, hits this reason on
66
- every pull request and posts nothing, which is the correct behavior rather
67
- than a gap.
68
-
69
- Both `ok` and `no-evidence` also report what the marked comment already
70
- shows a reviewer, each field present only when that comment holds it:
71
-
72
- | Field | What it carries |
73
- | ----------- | ----------------------------------------------------------------- |
74
- | `preview` | The hosted `**Preview:**` address on the comment's address line. |
75
- | `local` | The `**Local preview:**` address beside it. |
76
- | `checklist` | The checklist between its delimiters, with any ticked boxes kept. |
77
- | `boxes` | Each box's `number`, `text`, tick, `stamp`, and `isTaste`. |
78
-
79
- These come from the comment already posted, never from the flags the call
80
- passed, so a skill driving a review reads which address and which checklist a
81
- reviewer has without parsing the comment itself. A checklist posted raw, which
82
- only the fallback for a refused render does now, carries no marker, so the
83
- record reports none of the three for it. Reading
84
- them means the thread is read before the `no-evidence` answer too, so an
85
- unreadable thread refuses as `gh-failed` on both reasons rather than
86
- reporting the fields absent.
87
-
88
- `ok` also carries `states`, the comparison the body renders as data: each
89
- state's items with `path`, `stem`, `added`, and `width` and `baseWidth` when
90
- read. `review-ui` matches a frame against it so it publishes no image the
91
- worker's evidence already shows. `canon docs pr-frames` covers that store.
92
- `widthUnread` lists a failed read as `head:<path>` or `base:<path>`.
93
-
94
- ## What `[number]` selects, and what it does not
95
-
96
- Naming a number picks the pull request the whole record describes: its head
97
- commit, its merge base, its changed set, and the marked comment to edit in
98
- place. The changed paths and whether each was added or modified come from the
99
- pull request's files endpoint, paged to the end, and the merge base comes from
100
- the compare endpoint, so the record is the same from any checkout. A run from
101
- the main worktree against a branch built elsewhere renders that branch's
102
- comparison.
103
-
104
- The trade is that the verb no longer sees evidence that is uncommitted or
105
- unpushed, which the comment's head-pinned image links could not show anyway.
106
- A path the pull request renamed or copied counts as added, since the new path
107
- has no counterpart at the merge base, so a rename loses its before image and
108
- reads as new. A path the pull request removed is dropped, since it has no
109
- head image to show. A failed files read refuses as `unreadable-changes` rather
110
- than rendering a short set, and a missing merge base refuses as `no-base`.
111
-
112
- ## A render never empties a comment that carries cases
113
-
114
- When the render holds no cases and the marked comment holds some, the verb
115
- refuses `would-empty` and prints no body. A render with no cases cannot tell a
116
- case removed on purpose from a short read, so the destructive direction needs a
117
- person: they edit the comment directly. The record keeps `commentId` and the
118
- carried fields, so the caller can still find the comment. A comment holding
119
- only a preview or a checklist has no cases to lose and still re-renders `ok`.
120
- `no-evidence` without a flag returns before any render, so the guard touches
121
- only `--preview`, `--local`, and `--checklist` calls.
122
-
123
- ## One comment, found by its own marker
124
-
125
- Every body this verb renders ends with a trailing marker naming the head it
126
- describes:
127
-
128
- ```markdown
129
- <!-- pr-evidence: head=<sha> -->
130
- ```
131
-
132
- `canon pr evidence` reads that marker back off every comment on the pull
133
- request, the same way `canon pr review-state` reads its own marker off a
134
- review body, and reports the REST id of whichever comment carries it as
135
- `commentId`. A caller with no `commentId` posts a new comment. A caller
136
- holding one edits that comment in place with a `PATCH` rather than posting a
137
- second one.
138
-
139
- Putting the lookup here, in the one place both `git-pr` and `git-followup`
140
- call, is what keeps two close-out comments from landing beside each other the
141
- way `review-pr` once posted, before that skill's own guard existed. It is
142
- also why this verb renders the whole comment body rather than handing each
143
- skill a record to format on its own: a fix to the table shape or the
144
- details wrapper lands once, not twice.
145
-
146
- ## The preview line
147
-
148
- Every body opens with `## Evidence`. Under it sits one address line carrying
149
- whichever previews apply, joined by a middle dot:
150
-
151
- ```plaintext
152
- ## Evidence
153
-
154
- **Preview:** <url> · **Local preview:** <url>
155
-
156
- **Base:** [`<base>`](<base-commit-url>) · **Head:** [`<head>`](<head-commit-url>)
157
- ```
158
-
159
- The Base and Head line follows only when the comment carries screenshots.
160
-
161
- `--preview <url>` puts the branch's hosted address on that line, and a run
162
- without it carries forward the address the marked comment already holds. With
163
- no evidence in the diff and an address in hand, the body is the heading, the
164
- address line, and the marker alone, reported as `ok`. `canon docs pr-preview`
165
- covers where the address comes from.
166
-
167
- A carried hosted address is dropped when a deploy workflow resolves and its
168
- push path filter matches no path the pull request changed, since the site it
169
- links shows none of the change. An explicit `--preview` is always kept.
170
-
171
- The carry-forward reads every line above the commit line, the checklist, and
172
- the comparison for each prefix, wherever it sits. A comment posted before the
173
- heading opened the body, with each address on a line of its own at the top,
174
- reads the same way, so an open pull request keeps its links on its first
175
- re-render.
176
-
177
- ## The checklist line
178
-
179
- `--checklist <path>` closes the body with a visual checklist, below the
180
- comparison it annotates, so one comment carries the preview address, the
181
- screenshots, and what a reviewer has to look at. The file is what
182
- `canon:ui-checklist` writes to `.canon/tmp/handoff/ui-checklist/<slug>.md`, and
183
- `git-pr` passes it here rather than posting it as a second comment.
184
-
185
- A run without the option carries forward whatever checklist the marked comment
186
- already holds, the same way the preview address is carried. That is what keeps
187
- `git-followup`'s re-render after a push from dropping the checklist, since
188
- `git-pr` deletes the handoff once the first post reports success and no later
189
- call has a file to pass. `canon docs pr-tick` covers what a tick carries.
190
-
191
- The option decides `no-evidence` on its own. A branch with a checklist and no
192
- changed evidence image renders `ok` with a marked body holding the checklist,
193
- so the checklist always lands in the one comment a later call finds and edits.
194
- The raw post survives only as the caller's fallback when the render is refused.
195
- A path that cannot be read, or one holding nothing, refuses as
196
- `unreadable-checklist` rather than rendering without it: the caller deletes the
197
- handoff once a post succeeds, so a silently dropped checklist is the only copy
198
- gone.
199
-
200
- ## The local preview line
201
-
202
- `--local <url>` adds `**Local preview:** <url>` to the address line, after the
203
- hosted segment when there is one and alone when there is not. A run without the
204
- option carries forward the local address the marked comment already holds, the
205
- same as the other two.
206
-
207
- The option does not turn `no-evidence` into `ok` on its own. A branch with a
208
- dev server running and no rendered change would otherwise get a comment holding
209
- a link and nothing else. Together with `--checklist` the link and the
210
- checklist land in one comment.
211
- `canon docs pr-local` covers where the address comes from and how the line is
212
- removed at close.
213
-
214
- ## What a pull request is still owed
215
-
216
- `--check` reads the same changed set and the same thread, renders no body, and
217
- reports which steps of the ship chain that run after the pull request opens
218
- never landed on it. It reads the pull request rather than any session's account
219
- of what it ran, so a session that stopped after opening the pull request leaves
220
- a branch the check can tell apart from one that needed neither step.
221
-
222
- ```bash
223
- canon pr evidence 1341 --check --json
224
- ```
225
-
226
- | Reason | What it means |
227
- | --------- | ------------------------------------------------------------------ |
228
- | `settled` | Nothing is owed. `owed` is empty. |
229
- | `owed` | `owed` lists `evidence`, `preview`, or both, always in that order. |
230
-
231
- - `evidence` is owed when the changed set carries an evidence image and no
232
- comment carries the marker.
233
- - `preview` is owed when the pull request has a marked comment or an evidence
234
- change, a deploy workflow resolves in the checkout the verb runs from, its
235
- push path filter matches a path the pull request changed, and the marked
236
- comment carries no `**Preview:**` address. A `**Local preview:**` address
237
- does not satisfy it. A project deploying nothing is never owed a preview,
238
- and neither is a branch its deploy does not build from.
239
-
240
- The record carries `owed`, `commentId` when a marked comment exists, and the
241
- same `preview`, `local`, and `checklist` fields the other reasons carry, so a
242
- caller reads one shape. It never carries `body`, so a writer branching on `ok`
243
- and posting `body` cannot act on a check record. Passing `--preview`, `--local`,
244
- or `--checklist` with it refuses as `check-writes` before any `gh` call, since
245
- those ask a read to write.
246
-
247
- The thread cannot separate a skipped preview step from a failed one. A pull
248
- request whose deploy failed or timed out reads as owing a preview, and the
249
- session that ran it holds the cause. A checklist-only branch posts a marked
250
- comment, so it reads as owing a preview once that comment exists and a deploy
251
- workflow resolves. The deploy workflow is read from the checkout rather than
252
- the pull request's head, so one adding or removing its own reads trunk's.
253
-
254
- An unreadable thread refuses as `gh-failed` rather than reporting `settled` off
255
- a thread it never read. The check skips the merge base read, which only the
256
- render needs, so it never refuses `no-base`. The orchestrator's draft lift is
257
- the caller, and lifts only on `settled`, so a refusal holds it the same as
258
- `owed` does.
259
-
260
- ## When the comment opens
261
-
262
- The comment opens every block while it carries six images or fewer and closes
263
- every block past that. A line under `## Evidence` names the compared base and
264
- head as linked short shas, which tells a reader whether the images match the
265
- latest push. `canon docs pr-evidence-layout` covers the count and the blocks.
266
-
267
- ## What the comment still leaves to GitHub
268
-
269
- GitHub already draws its own before-and-after comparison on the Files Changed
270
- tab for any tracked image that changed. This comment does not make that view
271
- redundant. The diff view shows what moved between two commits a reader has
272
- already opened, and the comment is what lets a reader see the same comparison
273
- without opening it at all.
@@ -1,131 +0,0 @@
1
- ---
2
- title: The pull request frame store
3
- description: How canon pr frames pushes a UI review frame to a never-merged branch through the GitHub API and returns a branch-path link, which ref it writes, the pass segment that keeps an earlier image fixed, the refusal reasons it names, and how --drop and --prune rewrite the branch so a dropped frame leaves history
4
- ---
5
-
6
- # The pull request frame store
7
-
8
- `canon pr frames` gives a review comment an image it can embed. `review-ui`
9
- drives a pull request's checklist and captures a frame per box, and a frame
10
- on the reviewer's disk resolves for nobody else. The verb pushes one frame to
11
- the `canon/frames` branch and returns a link to its path there.
12
-
13
- ```bash
14
- canon pr frames 1341 --add frames/3.png --box 3 --head <sha> --pass 20261002T154450Z --json
15
- canon pr frames 1341 --add frames/3-dark.png --box 3 --theme dark --head <sha> --pass 20261002T154450Z --json
16
- canon pr frames 1341 --drop --json
17
- canon pr frames --prune 30 --json
18
- ```
19
-
20
- ## What it writes
21
-
22
- An add writes `refs/heads/canon/frames` and no other ref. Every call goes
23
- through the GitHub git data API, being blobs, trees, commits, and that one ref,
24
- so nothing is checked out, no worktree is entered, and neither `main` nor any
25
- branch a pull request holds is touched. The branch never merges.
26
-
27
- A frame lands at `pr-<number>/<short-head>/<pass>/box-<n>.png`, or at
28
- `box-<n>-<theme>.png` when `--theme` names `dark` or `light`, so two shots of
29
- one box in one pass never take the same path. A frame without `--theme` keeps
30
- the plain form. `--head` names
31
- the commit the pass drove, 7 to 40 lowercase hex characters, and defaults to
32
- the head the pull request object reports. `--pass` is the instant the pass
33
- started, in compact UTC as `date -u +%Y%m%dT%H%M%SZ` prints it, which
34
- `review-ui` records once at its first step. A second pass at the same head and
35
- box therefore writes a new file rather than replacing the image an earlier
36
- comment shows, which matters when a preview is minted again at one head.
37
-
38
- `link` uses the same `blob/<ref>/<path>?raw=true` form as the evidence comment,
39
- naming the `canon/frames` branch rather than a commit. Every drop rewrites the
40
- branch, and a link pinned to a commit would stop resolving for every other pull
41
- request's frames once GitHub collects the commit it names. A path link lives
42
- exactly as long as its file, so it survives every rewrite that drops another
43
- pull request and ends only when its own is dropped.
44
-
45
- The first `--add` creates the branch with a root commit holding the
46
- frame, so a project needs no setup. An add only fast-forwards the ref. When
47
- another writer moved it between the read and the move, the add rebuilds on the
48
- new tip, up to three times, before refusing as `ref-conflict`.
49
-
50
- Only a frame of the address the pass drove belongs here. The branch is as
51
- public as the repository, so a frame of a private or pre-release state would
52
- be published with it.
53
-
54
- ## What the record carries
55
-
56
- `reason` on the record is what a caller branches on, not the exit code:
57
-
58
- | Reason | What it means |
59
- | ------------------ | -------------------------------------------------------------------------------- |
60
- | `ok` | An add pushed, or a drop or prune finished, including one that removed nothing. |
61
- | `bad-mode` | Not exactly one of `--add`, `--drop`, `--prune`, or a number with `--prune`. |
62
- | `no-number` | `--add` or `--drop` named no pull request. |
63
- | `bad-box` | `--add` carried no positive `--box`. |
64
- | `bad-head` | `--head`, or the head the pull request reported, is not a 7 to 40 character sha. |
65
- | `bad-pass` | `--add` carried no `--pass` stamp in compact UTC. |
66
- | `bad-theme` | `--theme` is neither `dark` nor `light`, or came without `--add`. |
67
- | `bad-days` | `--prune` carried no positive number of days. |
68
- | `unreadable-frame` | The `--add` file is missing, empty, or not a PNG. Nothing is called. |
69
- | `read-only` | GitHub refused a write with 403 or 404, which is what a fork's token gets. |
70
- | `unreadable-tip` | The branch exists and could not be read. |
71
- | `push-failed` | A write failed partway and the ref did not move. |
72
- | `ref-conflict` | Another writer kept moving the ref through every attempt. |
73
- | `no-object-head` | No `--head` and the pull request reported none. |
74
- | `gh-missing` | `gh` is not on the path. |
75
- | `gh-failed` | `gh` could not name the repository or read the pull request. |
76
-
77
- An add's `ok` record carries `link`, `commit`, `path`, `box`, `theme` when one was passed, `head`, `pass`,
78
- and `created`, which is true when the add made the branch. A drop's carries
79
- `removed`, the pull requests whose folders went, plus `commit` for the rewrite
80
- or `deleted` when it emptied the branch. A prune adds `kept` and `unread`.
81
-
82
- `review-ui` reads every reason but `ok` as a cue to describe the frame in words
83
- and name the reason, so a refused push never fails the pass.
84
-
85
- ## Dropping frames
86
-
87
- A plain delete commit would keep every image reachable through history, so
88
- `--drop` and `--prune` write one root commit holding everything else and
89
- force-move the ref to it. A rewrite that leaves the branch empty deletes it,
90
- and the next add recreates it. Dropping a pull request with no frames, or on a
91
- missing branch, reports `ok` with nothing removed, so a scheduled run never
92
- fails on an empty branch.
93
-
94
- `--prune <days>` reads every `pr-<n>/` folder, asks GitHub for each pull
95
- request's state, and drops every one reading closed with a close date more
96
- than that many days ago, in a single rewrite. It keeps one still open, one
97
- reopened after closing, one closed exactly the given days ago, and one whose
98
- state could not be read, listing that last kind under `unread`.
99
-
100
- ## The legacy branch
101
-
102
- Frames lived on `canon-frames` before the rename to `canon/frames`. An add
103
- never writes it. `--drop` and `--prune` also run against it after the
104
- current branch, remove only, and delete it once empty. `--prune` drains it
105
- as its pull requests expire, and `--drop` clears one pull request at once.
106
- The record carries `legacy: true` when that
107
- pass changed the ref, and its `removed` list folds both branches in, naming
108
- a pull request once. A missing or unreadable legacy ref never fails a run
109
- the current branch finished.
110
-
111
- A link minted before the rename points at `blob/canon-frames/...` and keeps
112
- resolving until its pull request has been closed 30 days or `--drop` runs.
113
-
114
- This repository runs `--prune 30` daily from
115
- `.github/workflows/pr-frames-cleanup.yml`. A frame's file stays on the branch
116
- until its own pull request has been closed for 30 days, so a link in a review
117
- comment keeps resolving for about a month after its pull request closes,
118
- whatever other pull requests the branch drops in between. A target project
119
- carries no such workflow yet and keeps its frames until someone runs `--drop`.
120
-
121
- ## What a rewrite does not do
122
-
123
- A force-move leaves the old commits unreachable without purging them. GitHub
124
- serves an unreachable commit by sha until it collects it, so a dropped frame
125
- stays reachable to anyone holding a sha from an earlier record, and a frame
126
- holding sensitive content needs a purge request to GitHub. Its path link stops
127
- resolving at the drop, since the branch no longer carries the file.
128
-
129
- A drop racing an add can lose the add's frame, since GitHub's ref update has no
130
- compare-and-swap for a force-move. The drop builds from the tip it reads, and
131
- the window is the length of one rewrite.
@@ -1,132 +0,0 @@
1
- ---
2
- title: The pull request draft lift
3
- description: How canon pr lift removes the draft mark once the reviews, evidence, standing, and CI all read clear at the head, the gate order and what each reads, the wait on a pending run, the re-read before the write, the refusal reasons it names, and what --check reports
4
- ---
5
-
6
- # The pull request draft lift
7
-
8
- `canon pr lift` takes a pull request out of draft. It reads the gate itself, so
9
- any session that closes a review can run it, and nobody has to judge whether a
10
- message asking for the lift came from someone entitled to ask.
11
-
12
- ```bash
13
- canon pr lift 1341 --json
14
- canon pr lift 1341 --check --json
15
- ```
16
-
17
- With no number it resolves the one open pull request on the checked-out branch.
18
- The ship chain marks every pull request a draft, and this is the verb that
19
- undoes it.
20
-
21
- ## What it reads
22
-
23
- Five gates, read in this order. The first four need no wait, so they all run
24
- before the check runs are read.
25
-
26
- | Gate | Clear when |
27
- | ---------- | --------------------------------------------------------------------------------------------- |
28
- | `review` | The newest `## Review` pass is closed, and its marker names the head. |
29
- | `ui` | The newest `## UI review` verdict is closed at the head. Binds only where a checklist exists. |
30
- | `evidence` | `canon pr evidence --check` reads `settled`, meaning no post-pull-request step is owed. |
31
- | `standing` | The head merges into the freshly fetched base, and the base moved no path the branch writes. |
32
- | `ci` | Every check run on the branch tip concluded and none failed. |
33
-
34
- The head is the branch tip read from the remote, not the pull request object's
35
- head, which trails a push by up to a minute. A review marker names the commit
36
- the reviewer read, and it covers the head when it is that commit or a prefix of
37
- it.
38
-
39
- The UI gate binds only when `canon pr evidence --check --json` carries a
40
- `checklist` field. The verb branches on that field and not on the exit, so a
41
- branch with no rendered surface owes no UI review.
42
-
43
- A review pass with no read-time marker refuses as `review-unmarked`. Its covered
44
- commit would be the stamp GitHub wrote at submission, which the marker exists to
45
- replace, and lifting on it reintroduces the defect `canon pr review-state`
46
- documents.
47
-
48
- ## The wait
49
-
50
- A pending CI run is waited on rather than refused. `--wait <seconds>` bounds it,
51
- defaulting to 1200, and the check runs are read every 30 seconds until one of
52
- four things happens:
53
-
54
- - every run passed, and the verb goes on to the write
55
- - a run failed, which refuses `ci-failing` at once
56
- - the branch conflicts with its base, which refuses `ci-conflicted` at once,
57
- since no run starts for a conflicted branch and the wait could not end
58
- - the deadline passed, which refuses `ci-timeout`
59
-
60
- A refusal on any of the first four gates comes before the wait starts. A code
61
- reviewer whose UI partner is still driving refuses `ui-open` immediately
62
- instead of holding a turn through a CI run.
63
-
64
- ## The re-read
65
-
66
- A push during the wait moves the head off the commit every earlier gate
67
- cleared. After the wait, and immediately before the write, the verb reads the
68
- pull request, the branch tip, and the reviews again. A tip that moved refuses
69
- `head-moved`, and a review that opened refuses with its own reason. The
70
- standing is read again as well, so a sibling merging during the wait refuses
71
- `standing-stale` instead of lifting a branch that read fresh before it. A pull
72
- request already out of draft by then reports `already-ready`.
73
-
74
- Two reviewers closing within seconds of each other can each read the other's
75
- review as still open and both refuse. The re-read narrows that window and does
76
- not close it, so a draft whose reviews both read closed is the controlling
77
- session's to lift.
78
-
79
- ## The write
80
-
81
- The lift is the `markPullRequestReadyForReview` GraphQL mutation, because the
82
- REST pull request update carries no draft field. It is the one GraphQL call the
83
- verb makes, and every read stays on REST. The verb reads `isDraft` back from the
84
- mutation's answer, and a response that does not say `false` refuses `gh-failed`
85
- rather than reporting a lift.
86
-
87
- A cloud session's proxy refuses GraphQL, so a reviewer running there reaches
88
- `gh-failed` on the write and hands the lift to the controlling session.
89
-
90
- ## What it reports
91
-
92
- `reason` on the record is what a caller branches on. Exit 0 covers `lifted` and
93
- `already-ready`, so the second of two reviewers to close owes nobody a message.
94
- Every other reason exits 1.
95
-
96
- | Reason | What it means |
97
- | ------------------- | ---------------------------------------------------------------------- |
98
- | `lifted` | The mark came off. |
99
- | `already-ready` | The pull request was not a draft. Nothing is written. |
100
- | `clear` | Under `--check`, every gate reads clear. |
101
- | `pull-closed` | The pull request is closed. |
102
- | `review-missing` | The thread carries no code review. |
103
- | `review-open` | The newest code review is open. |
104
- | `review-unmarked` | The newest code review carries no marker. |
105
- | `review-behind` | The newest code review closed on an earlier commit than the head. |
106
- | `ui-missing` | A checklist exists and no UI review has been posted. |
107
- | `ui-open` | The newest UI review is open. |
108
- | `ui-unmarked` | The newest UI review carries no head marker. |
109
- | `ui-behind` | The newest UI review closed on an earlier commit than the head. |
110
- | `evidence-owed` | The evidence check reads `owed`. |
111
- | `standing-stale` | The base moved under a path the branch writes. |
112
- | `standing-conflict` | The head does not merge cleanly into the base. |
113
- | `standing-unknown` | The head or base did not resolve, which a fork or deleted head leaves. |
114
- | `ci-pending` | Under `--check`, a run has yet to conclude. |
115
- | `ci-failing` | A check run failed. |
116
- | `ci-conflicted` | The branch conflicts, so no run will start. |
117
- | `ci-timeout` | A run was still pending when `--wait` ran out. |
118
- | `head-moved` | A push landed during the wait. |
119
- | `gh-failed` | A read or the write was refused, so nothing is reported as lifted. |
120
-
121
- `standing-unknown` waits on a person, matching the orchestrator poll's own rule
122
- for a head it cannot resolve.
123
-
124
- ## `--check`
125
-
126
- `--check` reports every gate and writes nothing. It reads the check runs once
127
- and does not wait, so a pending run reports `ci-pending`. The record carries a
128
- `gates` array with each gate's name, whether it is clear, and its reason, so a
129
- caller sees every gate that blocks and not the first alone.
130
-
131
- The orchestrator's poll and a session deciding whether to run the verb read the
132
- same answer this way, rather than each restating the gate.
@@ -1,111 +0,0 @@
1
- ---
2
- title: The pull request local preview
3
- description: How canon pr local finds the server a worktree is running, why a server in another worktree is never reported, the refusal reasons it names, and how a close workflow drops the address line with --remove
4
- ---
5
-
6
- # The pull request local preview
7
-
8
- `canon pr local` reports the `localhost` address of the dev server the current
9
- worktree is running. `git-pr` calls it ahead of the evidence comparison and
10
- passes the address through `canon pr evidence --local`, so a reviewer on the
11
- same machine gets the running branch to click into beside the screenshots and
12
- the checklist. A workflow on the pull request's `closed` event calls it again
13
- with `--remove`, which drops the address line and leaves nothing in its place.
14
-
15
- ```bash
16
- canon pr local --json
17
- canon pr local 1341 --remove --json
18
- ```
19
-
20
- ## How it finds the server
21
-
22
- The verb reads every TCP socket in `LISTEN` with the process that owns it. It
23
- keeps a socket when that process's working directory sits inside the
24
- worktree's toplevel, including a subfolder, since some tools change into one
25
- before they listen. It then requests `/` on each kept port and reports the
26
- lowest one serving an HTML page.
27
-
28
- The HTML test is there because a working tree often holds more than one
29
- listener. A test runner's own server, or a dev server's reload socket, answers
30
- HTTP too, and a reviewer opening either gets a bare 404 rather than the branch.
31
-
32
- Sockets are read from `/proc/net/tcp` and `/proc/net/tcp6` wherever they exist,
33
- matched against each process's `fd` table, and through `lsof` on a machine
34
- without them. Linux reads `/proc` even with `lsof` installed, since `lsof` 4.95
35
- drops a process whose truncated name holds an unmatched `(`, which is what
36
- Next's `next-server (vX.Y.Z)` title is cut down to. A machine with neither
37
- refuses as `no-listener-reader`.
38
-
39
- The port is never derived from the worktree. A port helper needs a per-stack
40
- base port the toolkit cannot know, and a server started by hand or by an
41
- earlier session would not match it anyway.
42
-
43
- ## Why another worktree's server is never reported
44
-
45
- A server running in a different worktree shows a different branch, and a link
46
- to it on this pull request would show a reviewer the wrong code.
47
-
48
- - A sibling linked worktree sits outside this one's toplevel, so its process
49
- never matches.
50
- - The main checkout's server, read from a linked worktree, sits outside the
51
- linked worktree's toplevel for the same reason.
52
- - A linked worktree's server, read from the main checkout, sits inside the main
53
- toplevel. The verb excludes everything under `.claude/worktrees/` for that
54
- case alone.
55
-
56
- ## What the record carries
57
-
58
- `reason` on the record is what a caller branches on, not the exit code:
59
-
60
- | Reason | What it means |
61
- | -------------------- | --------------------------------------------------------------------- |
62
- | `ok` | A server answered. `url` carries the address and `port` the port. |
63
- | `no-server` | Nothing inside this worktree served an HTML page. |
64
- | `no-listener-reader` | Neither `lsof` nor `/proc` is available, so no socket could be read. |
65
- | `removed` | `--remove` dropped the address line from the evidence comment. |
66
- | `no-comment` | `--remove` found no comment carrying the evidence marker. |
67
- | `no-line` | `--remove` found the comment but no address line on it. |
68
- | `gh-missing` | `--remove` needs `gh` and it is not on the path. |
69
- | `gh-failed` | `--remove` could not read the comments or could not edit the comment. |
70
-
71
- The exit is 0 on `ok` and on every `--remove` outcome that finished, including
72
- the two no-ops, and 1 on every refusal. A caller on an older binary gets an
73
- unknown-subcommand error instead of a record, which `git-pr` reads as a skip.
74
-
75
- ## Removing the link at close
76
-
77
- The link goes dead once the branch's server stops, and nothing on the pull
78
- request says so. `--remove` runs on the pull request's `closed` event, merged or
79
- abandoned, from a workflow seeded into every target:
80
-
81
- ```yaml
82
- on:
83
- pull_request:
84
- types: [closed]
85
- ```
86
-
87
- It reads the comment carrying the `pr-evidence` marker, drops the address line
88
- under `## Evidence`, the hosted `**Preview:**` segment and the
89
- `**Local preview:**` segment together, and edits the comment itself through
90
- `gh api`. The hosted link goes too because the deploy workflow deletes the
91
- branch's preview deployments on the same close. No note replaces the line, and
92
- the blank line it sat on goes with it so no gap doubles under the heading. A
93
- comment in the older layout, with each address on its own line at the top,
94
- loses both lines the same way.
95
-
96
- The verb writes here rather than handing a body back, because its caller is a
97
- workflow with no session to post one. Every other byte of the comment stays as
98
- it was, which keeps the marker, the checklist delimiters, and any ticked box
99
- intact. A second run finds no line and reports `no-line`.
100
-
101
- A fork pull request gets a read-only token, so the workflow skips the edit with
102
- a notice rather than failing the close.
103
-
104
- ## What this does not cover
105
-
106
- The link can still go dead while the pull request is open, such as when the
107
- session holding the server retires. Only the close is covered, and a re-render
108
- after a push carries the line forward whether the server still answers or not.
109
-
110
- `canon docs pr-preview` covers how the hosted address is minted and when it is
111
- refused.