@erclx/canon 5.39.0 → 5.41.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 (162) hide show
  1. package/README.md +39 -22
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
  4. package/claude/skills/auto-ship/SKILL.md +6 -0
  5. package/claude/skills/auto-ship/references/fan-in.md +59 -0
  6. package/claude/skills/canvas-board/SKILL.md +1 -1
  7. package/claude/skills/create-skill/references/skill.md +1 -1
  8. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
  9. package/claude/skills/decision-escalate/SKILL.md +1 -2
  10. package/claude/skills/draft-slides/SKILL.md +2 -3
  11. package/claude/skills/draft-slides/references/deck.md +183 -0
  12. package/claude/skills/git-branch/REQUIREMENT.md +17 -6
  13. package/claude/skills/git-branch/SKILL.md +63 -7
  14. package/claude/skills/git-pr/references/evidence.md +2 -2
  15. package/claude/skills/git-pr/references/final-command.md +1 -1
  16. package/claude/skills/git-pr/references/labels.md +1 -1
  17. package/claude/skills/git-pr/references/publish.md +1 -1
  18. package/claude/skills/plan-feature/references/batches.md +48 -1
  19. package/claude/skills/plan-feature/references/plan.md +2 -0
  20. package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
  21. package/claude/skills/plan-intake-answer/SKILL.md +1 -2
  22. package/claude/skills/review-address/references/rebase-conflicts.md +7 -0
  23. package/claude/skills/review-ui/SKILL.md +3 -0
  24. package/claude/skills/review-ui/references/close-out.md +47 -0
  25. package/claude/skills/review-ui/references/post.md +3 -1
  26. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -0
  27. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  28. package/claude/skills/role-worker/REQUIREMENT.md +1 -0
  29. package/claude/skills/role-worker/SKILL.md +9 -0
  30. package/claude/skills/task-board/references/board.md +1 -1
  31. package/claude/skills/task-board/references/tasks.md +1 -1
  32. package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
  33. package/claude/skills/teach-workspace/SKILL.md +1 -2
  34. package/claude/skills/ux-measure/SKILL.md +1 -1
  35. package/docs/agents/audits.md +61 -60
  36. package/docs/agents/context-audit.md +40 -58
  37. package/docs/agents/index.md +5 -51
  38. package/docs/agents/install-and-sync.md +1 -0
  39. package/docs/agents/markdown-audit.md +53 -99
  40. package/docs/agents/records.md +37 -79
  41. package/docs/agents/sessions-transfer.md +26 -70
  42. package/docs/target/index.md +0 -1
  43. package/docs/target/migrations.md +57 -66
  44. package/docs/target/projects.md +36 -78
  45. package/docs/target/scenarios.md +9 -19
  46. package/docs/target/sync.md +27 -68
  47. package/docs/workflow/ai-workflow.md +22 -68
  48. package/docs/workflow/operating-model.md +32 -186
  49. package/docs/workflow/parallel-features.md +30 -66
  50. package/docs/workflow/skill-map-on-request.md +22 -24
  51. package/docs/workflow/skill-map.md +72 -76
  52. package/docs/workflow/visual-design-workflow.md +25 -118
  53. package/docs/workflow/zshrc-aliases.md +18 -37
  54. package/package.json +1 -1
  55. package/src/canvas/client/inspector/fields.css +4 -0
  56. package/src/canvas/index.html +6 -0
  57. package/src/claude/cases/git.ts +4 -0
  58. package/src/claude/skills-reach.ts +2 -2
  59. package/src/cli/ui.ts +3 -1
  60. package/src/commands/canvas.ts +23 -0
  61. package/src/commands/capture.ts +14 -0
  62. package/src/commands/claude.ts +13 -0
  63. package/src/commands/demo.ts +22 -0
  64. package/src/commands/deps.ts +6 -0
  65. package/src/commands/driver.ts +28 -2
  66. package/src/commands/gov.ts +52 -18
  67. package/src/commands/intake.ts +17 -0
  68. package/src/commands/labels.ts +38 -5
  69. package/src/commands/pr.ts +192 -52
  70. package/src/commands/records.ts +19 -3
  71. package/src/commands/secrets.ts +6 -0
  72. package/src/commands/sessions.ts +8 -0
  73. package/src/commands/tasks.ts +52 -0
  74. package/src/commands/teach.ts +13 -0
  75. package/src/commands/upstream.ts +25 -1
  76. package/src/commands/worktrees.ts +5 -0
  77. package/src/context/audit.ts +22 -4
  78. package/src/context/folders.ts +1 -1
  79. package/src/context/shape-gated.ts +5 -2
  80. package/src/counts/scan.ts +1 -1
  81. package/src/design/components.ts +1 -1
  82. package/src/design/document.ts +1 -1
  83. package/src/design/tokens.ts +1 -1
  84. package/src/gate/boundaries.ts +4 -1
  85. package/src/gate/measures.ts +1 -1
  86. package/src/gate/stages.ts +7 -7
  87. package/src/git/env.ts +6 -0
  88. package/src/markdown/scope.ts +11 -19
  89. package/src/pr/frames.ts +53 -9
  90. package/src/pr/lift.ts +153 -19
  91. package/src/pr/painted.ts +87 -0
  92. package/src/records/validate.ts +1 -0
  93. package/src/roots/record.ts +18 -1
  94. package/src/sandbox/census.ts +1 -1
  95. package/src/sandbox/headless/run.ts +3 -2
  96. package/src/sandbox/headless/watch.ts +3 -4
  97. package/src/slides/convert/master.ts +3 -0
  98. package/src/sync/format.ts +46 -0
  99. package/src/sync/stamp.ts +6 -0
  100. package/src/tasks/answers.ts +79 -2
  101. package/src/tasks/fan-in.ts +176 -0
  102. package/src/tasks/stack.ts +1 -1
  103. package/tooling/astro/reference.md +80 -39
  104. package/tooling/base/reference.md +63 -32
  105. package/tooling/base/seeds/canon/context/ci.md +7 -15
  106. package/tooling/base/seeds/canon/context/development.md +14 -14
  107. package/tooling/claude/reference.md +40 -22
  108. package/tooling/cloudflare/reference.md +31 -12
  109. package/tooling/go/reference.md +47 -23
  110. package/tooling/nextjs/reference.md +52 -19
  111. package/tooling/php/reference.md +35 -16
  112. package/tooling/python/reference.md +47 -30
  113. package/tooling/shell/reference.md +6 -5
  114. package/tooling/vite-react/reference.md +49 -21
  115. package/tooling/web/reference.md +131 -71
  116. package/docs/agents/canvas.md +0 -86
  117. package/docs/agents/capture.md +0 -65
  118. package/docs/agents/census.md +0 -23
  119. package/docs/agents/comments.md +0 -34
  120. package/docs/agents/context-audit-checks.md +0 -108
  121. package/docs/agents/context-classify.md +0 -101
  122. package/docs/agents/counts.md +0 -74
  123. package/docs/agents/demo.md +0 -140
  124. package/docs/agents/driver.md +0 -103
  125. package/docs/agents/gate.md +0 -90
  126. package/docs/agents/hooks.md +0 -78
  127. package/docs/agents/intake.md +0 -81
  128. package/docs/agents/key-changes.md +0 -116
  129. package/docs/agents/label-coverage.md +0 -86
  130. package/docs/agents/markdown-audit-density.md +0 -39
  131. package/docs/agents/markdown-audit-weight.md +0 -83
  132. package/docs/agents/pr-evidence-layout.md +0 -70
  133. package/docs/agents/pr-evidence.md +0 -273
  134. package/docs/agents/pr-frames.md +0 -131
  135. package/docs/agents/pr-lift.md +0 -132
  136. package/docs/agents/pr-local.md +0 -111
  137. package/docs/agents/pr-preview.md +0 -155
  138. package/docs/agents/pr-reads.md +0 -111
  139. package/docs/agents/pr-tick.md +0 -72
  140. package/docs/agents/records-push.md +0 -61
  141. package/docs/agents/records-stale.md +0 -40
  142. package/docs/agents/records-validate.md +0 -57
  143. package/docs/agents/restated.md +0 -83
  144. package/docs/agents/routing.md +0 -61
  145. package/docs/agents/rule-citations.md +0 -90
  146. package/docs/agents/sandbox.md +0 -126
  147. package/docs/agents/sessions.md +0 -128
  148. package/docs/agents/skills-audit.md +0 -120
  149. package/docs/agents/skills-reach.md +0 -64
  150. package/docs/agents/slides.md +0 -176
  151. package/docs/agents/state-scoped-risk.md +0 -105
  152. package/docs/agents/superseded.md +0 -85
  153. package/docs/agents/tasks-archive.md +0 -83
  154. package/docs/agents/tasks-plans.md +0 -152
  155. package/docs/agents/tasks-validate.md +0 -133
  156. package/docs/agents/tasks.md +0 -123
  157. package/docs/agents/teach-authoring.md +0 -118
  158. package/docs/agents/teach.md +0 -157
  159. package/docs/agents/test-order.md +0 -58
  160. package/docs/agents/upstream.md +0 -94
  161. package/docs/agents/worktrees.md +0 -118
  162. package/docs/target/standards-moved.md +0 -34
@@ -1,83 +0,0 @@
1
- ---
2
- title: Markdown audit weight
3
- description: The advisory sections of the markdown audit, being bullet and paragraph weight, run depth, the two cadence numbers, and the length ceiling, with the samples behind each checkpoint and which of those numbers gate through density
4
- ---
5
-
6
- # Markdown audit weight
7
-
8
- ## Bullets, paragraphs, and depth
9
-
10
- Bullet weight and depth are the checks that moved off `canon context audit`, carrying what they measured at the time. A top-level bullet reports past roughly 200 characters with continuation lines folded in and nested items left out. A run reports past roughly 40 rendered lines, measured at 80 columns, where a heading breaks one and so does a bold section marker taking the whole line at column zero, either ending in a colon, or holding one whole code span at any width, or running to 20 characters or fewer, skipping fenced blocks and exempting a flat peer list averaging under 130 characters a bullet and a run that is entirely table rows. Each file reports its longest run alone, so a second run past the checkpoint in the same file is never named.
11
-
12
- Every weight and depth measure counts the text a reader is shown. A link reduces to its anchor text and an autolink drops whole, since no reader is shown either destination. A backticked path stays counted, which is where these measures part from the ban scan `markdown-audit.md` describes: that one blanks a code span so a standard quoting its own banned character does not report itself, and discounting the same span here would under-report a paragraph carrying several. One file holds both span sets and each answers its own question.
13
-
14
- A code span is walked around rather than through, so a path quoting link or angle-bracket syntax keeps the width the page gives it. Masking inside one takes back the decision to count it, and the placeholders this toolkit writes are where that shows.
15
-
16
- The paragraph check measures both halves of one rule. `markdown.md` caps a paragraph at four sentences, and a sentence cap on its own is satisfied by writing fewer and longer ones: 15 paragraphs in this corpus sit inside four sentences and past the weight checkpoint, and the heaviest of those runs 886 characters. The standard therefore states a weight beside the sentence cap, and the verb reads it as its own checkpoint.
17
-
18
- A sentence boundary closes on terminal punctuation ahead of a capital or a code span. The capital is what keeps a version pin and a decimal from each reading as two sentences, and the code span is admitted beside it because a command name opening a sentence carries no capital to find.
19
-
20
- The paragraph weight sits at 350 and the bullet weight at 200, the two density lengths in `markdown-audit.md`, so this advisory warning and the density finding that gates read one number. Both shipped at 400 and have moved twice, each time against a sample of its own. They are separate checkpoints in the standard and separate patterns in the parser, so a read that moves one leaves the other where it is.
21
-
22
- ### The sample behind the paragraph number
23
-
24
- The checkpoint shipped at 400 as a borrowed number and was decided against a read of the prose it reports. Thirty-six findings were sampled, six from each of six weight bands, drawn at even spacing through each band ordered by path and line, and each was classed as prose a reader wants split or prose the checkpoint should not have reported.
25
-
26
- Every band below was measured before the scan stopped counting link syntax as prose, so a paragraph sitting in one of these bands is heavier than a paragraph reported at the same number today. The re-sample in the section below re-reads the same range against the corrected measure and reaches the opposite verdict on it, which is the measure moving rather than the reader.
27
-
28
- | Band | Wants the split | Reads as written |
29
- | --------- | --------------- | ---------------- |
30
- | 400 - 425 | 1 | 5 |
31
- | 425 - 450 | 2 | 4 |
32
- | 450 - 500 | 2 | 4 |
33
- | 500 - 600 | 2 | 4 |
34
- | 600 - 750 | 6 | 0 |
35
- | Past 750 | 6 | 0 |
36
-
37
- Precision is what moved the number rather than the finding count. Below 600 the checkpoint was right about seven of twenty-four sampled paragraphs, and past 600 it was right about all twelve. A checkpoint is a prompt to look, and a prompt wrong three times in four teaches a reader to stop looking. The distribution offers no seam to place the number against, with a median of 487 and a seventy-fifth percentile of 563, so the read is the whole of the evidence.
38
-
39
- Nothing inside the 500 to 600 band separated the two classes by length, which is the reason the number did not land there. The two paragraphs wanting a split ran 543 and 590 characters against four reading well at 515, 532, 555, and 569.
40
-
41
- The sample is thirty-six paragraphs against a reported population in the hundreds, and one reader classed all of them. Treat a band's rate as the order of magnitude it is rather than as a measured precision, and re-sample before moving the number again.
42
-
43
- ### The re-sample that moved the number to 700
44
-
45
- That re-sample ran once the scan stopped counting link syntax as prose. Findings at 604, 633, and 677 characters each read as an ordinary four-sentence paragraph on one topic, density arrived around 760 and was plain by 860, and the move cut the weight half of the report roughly in half while leaving the sentence cap untouched.
46
-
47
- ### The read that moved it to 350
48
-
49
- The operator read sample rewrites at 400 and 350 side by side and picked 350, the length of the reference sample, in groundwork `154`. The bullet number moved to 200 in the same read, since no rewritten one-fact bullet passed 151. Both now gate as density limits, so the earlier verdict that a long paragraph reads well as written no longer holds.
50
-
51
- A bullet, a heading, a table row, a blockquote, a blank line, and a fence each end a paragraph, so a heavy bullet is reported by the bullet check alone and never counted twice.
52
-
53
- ## Bullet stacks
54
-
55
- A run of more than 5 plain bullets is one of the six density limits in `markdown-audit.md`, which reports and gates it. This section keeps what counts as a run. A run of consecutive top-level bullets reports past 5. A blank line, an indented continuation, and a nested item do not break the run, and a paragraph, a heading, or any other column-zero line does. A fenced line is skipped without breaking it. The count is of items rather than weight, so it catches a catalog of short bullets the 200-character checkpoint above passes.
56
-
57
- Two shapes are exempt. A run whose every item opens with a bold label or a link names its own subject per item and reads as a table of entries, and a numbered list states an order, so numbered items never count. Governance rules and `REQUIREMENT.md` sit outside the density scope rather than being exempted here, since a rule is one imperative per bullet.
58
-
59
- ## Cadence
60
-
61
- Uniform cadence is the failure a ban list cannot express. A ban set states negatives, and fragments, verbless clauses, and sentences that all run one length are each the absence of something, so no word added to a ban list reaches any of them. The shape layer already measured a bullet, a paragraph, and a run, and stopped one level above where that failure lives.
62
-
63
- Cadence measures a paragraph on two numbers. The spread is the words between its longest and shortest sentence, and the opener count is the times one word opens a sentence in it. A spread of five words or under reads as one cadence, and a word opening more than two sentences is a pattern rather than a coincidence. Both come from `## Rhythm` in the `write-human` skill, which states them about prose a person reads, and this measures against that statement rather than setting a threshold of its own.
64
-
65
- Words are counted off the text a reader is shown. A link contributes its anchor text, an autolink contributes nothing, and the sentence boundaries do not move under that masking, since the boundary pattern requires whitespace after the terminal punctuation and no destination carries any. An opening word is lowercased and stripped of punctuation, so a sentence opening on a backticked command name reports the command.
66
-
67
- A paragraph carrying fewer than three sentences is skipped rather than scored. A two-sentence configuration note has no spread worth reading, and the opener rule is written about a third sentence turning a coincidence into a pattern, so neither number says anything before the floor. That is the cheap form of a wider exemption: a shape-aware one, exempting a short reference block by what it is rather than by how many sentences it holds, waits on a second case.
68
-
69
- The unit is the paragraph and each file names its worst on each measure, which follows the depth check rather than setting a precedent. A file's flattest paragraph and its most repetitive one are named only when each crosses its checkpoint, so a file reading healthy names nothing rather than offering its least healthy paragraph as a finding.
70
-
71
- Neither number gates and neither names a file wrong. This is a weaker claim than the one the weight checkpoints make, because a healthy range differs by surface: a catalog entry is several short sentences carrying one fact each, and a page arguing a decision is not, so one range applied across the corpus would report the surfaces that are correct. The run therefore states where the numbers came from beside them, and the counts are what a reader compares against.
72
-
73
- A reading of that spread across the whole corpus is what backs the range above, taken once against this repository's own tracked markdown rather than against a target project's.
74
-
75
- That reading travels with the command rather than staying here. `BASELINE` in `src/markdown/structure.ts` carries the overall share, the per-file range, and the ten-paragraph floor beneath which a file's own rate says nothing, and the run prints all four in the legend beside the rate it measured. A count with no range beside it reads as a finding, and naming that a healthy range differs by surface states that a range exists rather than what it looks like. This page is toolkit-internal, so a reader running the command in a project that installed no standards would otherwise have two counts and nothing to place them against.
76
-
77
- Two of the rules `write-human` states are deliberately not implemented. A sentence's grammatical shape and whether it carries a finite verb each need a parse rather than a match, and an imperative would read as a defect under a pattern that approximated either. The verbless share is the measure closest to the reported symptom, which is exactly why shipping it wrong would discredit the two that hold.
78
-
79
- The condition on that was something identifying a finite verb rather than guessing at one, and two parsers have now been run against it over 11,389 paragraph sentences. They disagree by a factor of four. `compromise` reports 2 percent and reads a fronted past participle as a finite verb, so `Measured at <sha> on <date>.` counts as carrying one. `wink-pos-tagger` reports 9 percent, fixes that class, and is still wrong on roughly three in four, because an imperative's verb tags as a proper noun and a noun-ambiguous predicate tags as a noun, which makes `Each maps to a skill.` read verbless. Separating those needs to know which token is the predicate, and that is syntax rather than a tag. The measure stays unimplemented, now against a mechanism rather than against the idea of one.
80
-
81
- ## Length
82
-
83
- The Length step sums rendered lines over the whole source, frontmatter and fenced blocks included, and lists each document past the 300-line ceiling, longest first. A changelog and a document carrying a whole-line `<!-- canon-length-exempt: <reason> -->` outside a fence are counted in one line rather than listed. Each `--json` entry carries `renderedLines` and `exempt`, the stated reason or `null`, and `checkpoints` carries `ceiling`.
@@ -1,70 +0,0 @@
1
- ---
2
- title: The pull request evidence layout
3
- description: How canon pr evidence groups screenshots into one block per view with a row per width and a column per theme, when a base column appears, where a name outside the form lands, how the commit line links, and when the blocks open
4
- ---
5
-
6
- # The pull request evidence layout
7
-
8
- `canon pr evidence` renders the screenshots of a pull request as blocks a
9
- reviewer reads by view. The record behind them, and the reasons a run can
10
- refuse, are in `canon docs pr-evidence`.
11
-
12
- ## One block per view
13
-
14
- A case name that ends in `-dark-<width>` or `-light-<width>` belongs to the
15
- view named by everything before that suffix, so `menu-open-light-1440` is the
16
- `menu-open` view. The name is split from the right because a view may carry
17
- hyphens of its own.
18
-
19
- Each view is one `<details>` block. Its summary carries the view and its case
20
- count, a row holds one width in ascending number order, and a column holds one
21
- theme:
22
-
23
- ```markdown
24
- | Width | Dark | Light |
25
- ```
26
-
27
- A view with any case already at the base takes a base and a head column per
28
- theme, and a case the pull request added shows `*(new)*` in its base cell:
29
-
30
- ```markdown
31
- | Width | Dark base | Dark head | Light base | Light head |
32
- ```
33
-
34
- A cell with no case in it stays empty. A theme no case of the view carries has
35
- no column. A state under a named directory titles its blocks
36
- `<state> / <view>`, so two states holding one view never repeat a name.
37
-
38
- ## Names outside the form
39
-
40
- A case name that does not split into a view, a theme, and a width lands in a
41
- flat `Case | Base | Head` block for its state, or `Case | Head` when every case
42
- in it is new. Nothing is dropped, so `board.png` or a name with no width still
43
- shows.
44
-
45
- ## The commit line
46
-
47
- Under `## Evidence`, the Base and Head line names each commit as a short sha
48
- that links to the full commit:
49
-
50
- ```markdown
51
- **Base:** [`<base>`](https://github.com/<repo>/commit/<base-sha>) · **Head:** [`<head>`](https://github.com/<repo>/commit/<head-sha>)
52
- ```
53
-
54
- A reader opens what the screenshots were taken against in one click. The base
55
- is the merge base the compare endpoint returns, not trunk's tip.
56
-
57
- ## When the blocks open
58
-
59
- Every block opens while the comment carries six images or fewer, counting one
60
- per case for the head plus one per case that is not new. Past that every block
61
- closes, so the whole comment is open or closed together.
62
-
63
- ## What reads the body back
64
-
65
- The case count that `would-empty` compares is the number of head images in the
66
- body. It holds for the grouped rows, where one row carries up to two cases, and
67
- for every layout posted before, so a comment rendered under an older shape
68
- still counts. The bold `**Base:**` label and the `<details>` blocks are
69
- unchanged, which keeps the address reader and the review skills working on both
70
- layouts. An older comment keeps its shape until the next run edits it in place.
@@ -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.