proctolog 0.1.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 (228) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/README.md +178 -0
  4. data/app/controllers/proctolog/acceptances_controller.rb +29 -0
  5. data/app/controllers/proctolog/application_controller.rb +32 -0
  6. data/app/controllers/proctolog/bursts_controller.rb +34 -0
  7. data/app/controllers/proctolog/choices_controller.rb +30 -0
  8. data/app/controllers/proctolog/code_reviews_controller.rb +26 -0
  9. data/app/controllers/proctolog/decisions_controller.rb +33 -0
  10. data/app/controllers/proctolog/entries_controller.rb +14 -0
  11. data/app/controllers/proctolog/evidence_controller.rb +23 -0
  12. data/app/controllers/proctolog/freshness_controller.rb +27 -0
  13. data/app/controllers/proctolog/good_to_go_controller.rb +29 -0
  14. data/app/controllers/proctolog/memberships_controller.rb +20 -0
  15. data/app/controllers/proctolog/mentions_controller.rb +16 -0
  16. data/app/controllers/proctolog/moments_controller.rb +26 -0
  17. data/app/controllers/proctolog/note_params.rb +24 -0
  18. data/app/controllers/proctolog/notes_controller.rb +56 -0
  19. data/app/controllers/proctolog/open_pull_requests_controller.rb +59 -0
  20. data/app/controllers/proctolog/pictures_controller.rb +27 -0
  21. data/app/controllers/proctolog/prompts_controller.rb +28 -0
  22. data/app/controllers/proctolog/pull_request_scoped.rb +21 -0
  23. data/app/controllers/proctolog/pull_requests_controller.rb +34 -0
  24. data/app/controllers/proctolog/put_away_controller.rb +19 -0
  25. data/app/controllers/proctolog/recordings_controller.rb +28 -0
  26. data/app/controllers/proctolog/rendered_pieces_controller.rb +11 -0
  27. data/app/controllers/proctolog/review_data_controller.rb +12 -0
  28. data/app/controllers/proctolog/rules_controller.rb +20 -0
  29. data/app/controllers/proctolog/scenario_notes_controller.rb +26 -0
  30. data/app/controllers/proctolog/scenario_scoped.rb +21 -0
  31. data/app/controllers/proctolog/scenarios_controller.rb +67 -0
  32. data/app/controllers/proctolog/sitting_servers_controller.rb +30 -0
  33. data/app/controllers/proctolog/waiting_controller.rb +14 -0
  34. data/app/controllers/proctolog/with_decisions.rb +28 -0
  35. data/app/helpers/proctolog/bursts_helper.rb +52 -0
  36. data/app/helpers/proctolog/decisions_helper.rb +102 -0
  37. data/app/helpers/proctolog/desk_helper.rb +61 -0
  38. data/app/helpers/proctolog/mentions_helper.rb +27 -0
  39. data/app/helpers/proctolog/open_pull_requests_helper.rb +31 -0
  40. data/app/helpers/proctolog/prompts_helper.rb +21 -0
  41. data/app/helpers/proctolog/pull_requests_helper.rb +67 -0
  42. data/app/helpers/proctolog/recordings_helper.rb +30 -0
  43. data/app/helpers/proctolog/rounds_helper.rb +27 -0
  44. data/app/helpers/proctolog/scenarios_helper.rb +123 -0
  45. data/app/helpers/proctolog/standings_helper.rb +80 -0
  46. data/app/jobs/proctolog/application_job.rb +8 -0
  47. data/app/jobs/proctolog/ask_for_release_job.rb +9 -0
  48. data/app/jobs/proctolog/fetch_pictures_ahead_job.rb +36 -0
  49. data/app/jobs/proctolog/prune_fresh_profiles_job.rb +9 -0
  50. data/app/jobs/proctolog/read_mentions_ahead_job.rb +12 -0
  51. data/app/models/proctolog/acceptance.rb +47 -0
  52. data/app/models/proctolog/app.rb +92 -0
  53. data/app/models/proctolog/browser.rb +42 -0
  54. data/app/models/proctolog/burst.rb +268 -0
  55. data/app/models/proctolog/burst_notices.rb +143 -0
  56. data/app/models/proctolog/chapters.rb +42 -0
  57. data/app/models/proctolog/check.rb +122 -0
  58. data/app/models/proctolog/choices.rb +112 -0
  59. data/app/models/proctolog/code_review.rb +251 -0
  60. data/app/models/proctolog/decision_record.rb +12 -0
  61. data/app/models/proctolog/decisions.rb +199 -0
  62. data/app/models/proctolog/description.rb +165 -0
  63. data/app/models/proctolog/difference.rb +138 -0
  64. data/app/models/proctolog/doctor.rb +127 -0
  65. data/app/models/proctolog/evidence.rb +29 -0
  66. data/app/models/proctolog/failure.rb +7 -0
  67. data/app/models/proctolog/focus.rb +48 -0
  68. data/app/models/proctolog/fresh_sitting.rb +35 -0
  69. data/app/models/proctolog/github.rb +353 -0
  70. data/app/models/proctolog/handover/brief.rb +138 -0
  71. data/app/models/proctolog/handover/layers.rb +128 -0
  72. data/app/models/proctolog/handover/places.rb +98 -0
  73. data/app/models/proctolog/handover/taken.rb +41 -0
  74. data/app/models/proctolog/handover/worklist.rb +35 -0
  75. data/app/models/proctolog/handover.rb +123 -0
  76. data/app/models/proctolog/housekeeping.rb +23 -0
  77. data/app/models/proctolog/kit.rb +36 -0
  78. data/app/models/proctolog/marks.rb +130 -0
  79. data/app/models/proctolog/membership.rb +91 -0
  80. data/app/models/proctolog/mentions.rb +117 -0
  81. data/app/models/proctolog/note.rb +94 -0
  82. data/app/models/proctolog/outside.rb +45 -0
  83. data/app/models/proctolog/overlay/brief.rb +52 -0
  84. data/app/models/proctolog/overlay/written.rb +33 -0
  85. data/app/models/proctolog/overlay.rb +87 -0
  86. data/app/models/proctolog/owner.rb +16 -0
  87. data/app/models/proctolog/pick.rb +47 -0
  88. data/app/models/proctolog/picture.rb +138 -0
  89. data/app/models/proctolog/pieces_rendering.rb +40 -0
  90. data/app/models/proctolog/project.rb +118 -0
  91. data/app/models/proctolog/pull_request.rb +89 -0
  92. data/app/models/proctolog/recording.rb +194 -0
  93. data/app/models/proctolog/release.rb +59 -0
  94. data/app/models/proctolog/rendering.rb +303 -0
  95. data/app/models/proctolog/reports.rb +66 -0
  96. data/app/models/proctolog/response_files.rb +55 -0
  97. data/app/models/proctolog/rounds.rb +305 -0
  98. data/app/models/proctolog/route.rb +151 -0
  99. data/app/models/proctolog/rules.rb +121 -0
  100. data/app/models/proctolog/scenario.rb +126 -0
  101. data/app/models/proctolog/server.rb +273 -0
  102. data/app/models/proctolog/since.rb +108 -0
  103. data/app/models/proctolog/size.rb +30 -0
  104. data/app/models/proctolog/standing.rb +132 -0
  105. data/app/models/proctolog/theme.rb +40 -0
  106. data/app/models/proctolog/unopened/numbers.rb +48 -0
  107. data/app/models/proctolog/unopened/opening.rb +88 -0
  108. data/app/models/proctolog/unopened.rb +203 -0
  109. data/app/models/proctolog/visit.rb +26 -0
  110. data/app/views/layouts/proctolog/application.html.erb +58 -0
  111. data/app/views/proctolog/acceptances/new.html.erb +34 -0
  112. data/app/views/proctolog/bursts/_line.html.erb +28 -0
  113. data/app/views/proctolog/bursts/_merged.html.erb +23 -0
  114. data/app/views/proctolog/bursts/_moved.html.erb +16 -0
  115. data/app/views/proctolog/bursts/_notices.html.erb +39 -0
  116. data/app/views/proctolog/bursts/_put_away.html.erb +11 -0
  117. data/app/views/proctolog/bursts/_rules.html.erb +27 -0
  118. data/app/views/proctolog/bursts/_side.html.erb +49 -0
  119. data/app/views/proctolog/bursts/_side_stop.html.erb +10 -0
  120. data/app/views/proctolog/bursts/show.html.erb +54 -0
  121. data/app/views/proctolog/code_reviews/show.html.erb +33 -0
  122. data/app/views/proctolog/decisions/_alternative.html.erb +5 -0
  123. data/app/views/proctolog/decisions/_beneath.html.erb +21 -0
  124. data/app/views/proctolog/decisions/_box.html.erb +27 -0
  125. data/app/views/proctolog/decisions/_choices.html.erb +9 -0
  126. data/app/views/proctolog/decisions/_draft_tools.html.erb +47 -0
  127. data/app/views/proctolog/decisions/_part.html.erb +61 -0
  128. data/app/views/proctolog/decisions/_round_data.html.erb +1 -0
  129. data/app/views/proctolog/decisions/_theme.html.erb +13 -0
  130. data/app/views/proctolog/decisions/index.html.erb +75 -0
  131. data/app/views/proctolog/notes/_card.html.erb +19 -0
  132. data/app/views/proctolog/notes/_template.html.erb +41 -0
  133. data/app/views/proctolog/open_pull_requests/_side.html.erb +30 -0
  134. data/app/views/proctolog/open_pull_requests/index.html.erb +106 -0
  135. data/app/views/proctolog/prompts/show.html.erb +41 -0
  136. data/app/views/proctolog/pull_requests/_connections.html.erb +65 -0
  137. data/app/views/proctolog/pull_requests/_finish.html.erb +42 -0
  138. data/app/views/proctolog/pull_requests/_margin.html.erb +33 -0
  139. data/app/views/proctolog/pull_requests/_note.html.erb +4 -0
  140. data/app/views/proctolog/pull_requests/_outline.html.erb +26 -0
  141. data/app/views/proctolog/pull_requests/_paragraphs.html.erb +9 -0
  142. data/app/views/proctolog/pull_requests/_scenarios.html.erb +16 -0
  143. data/app/views/proctolog/pull_requests/_since.html.erb +42 -0
  144. data/app/views/proctolog/pull_requests/_standing.html.erb +51 -0
  145. data/app/views/proctolog/pull_requests/_status.html.erb +5 -0
  146. data/app/views/proctolog/pull_requests/_try.html.erb +122 -0
  147. data/app/views/proctolog/pull_requests/show.html.erb +145 -0
  148. data/app/views/proctolog/recordings/_dialog.html.erb +26 -0
  149. data/app/views/proctolog/recordings/_list.html.erb +8 -0
  150. data/app/views/proctolog/recordings/_note.html.erb +22 -0
  151. data/app/views/proctolog/recordings/index.html.erb +33 -0
  152. data/app/views/proctolog/recordings/show.html.erb +118 -0
  153. data/app/views/proctolog/scenarios/_dialog.html.erb +22 -0
  154. data/app/views/proctolog/scenarios/_note.html.erb +18 -0
  155. data/app/views/proctolog/scenarios/_open.html.erb +9 -0
  156. data/app/views/proctolog/scenarios/_scenario.html.erb +48 -0
  157. data/app/views/proctolog/scenarios/_server.html.erb +20 -0
  158. data/app/views/proctolog/scenarios/index.html.erb +62 -0
  159. data/bin/proctolog +330 -0
  160. data/completions/_proctolog +143 -0
  161. data/config/application.rb +94 -0
  162. data/config/database.yml +16 -0
  163. data/config/environment.rb +6 -0
  164. data/config/initializers/grouse.rb +113 -0
  165. data/config/initializers/grouse_notes.rb +9 -0
  166. data/config/keys.yml +6 -0
  167. data/config/routes.rb +71 -0
  168. data/config/storage.yml +4 -0
  169. data/db/migrate/20261019213542_create_grouse_tables.grouse.rb +128 -0
  170. data/db/migrate/20261019213550_create_active_storage_tables.active_storage.rb +58 -0
  171. data/docs/manual/app.md +157 -0
  172. data/docs/manual/bursts.md +129 -0
  173. data/docs/manual/deciding.md +89 -0
  174. data/docs/manual/desk.png +0 -0
  175. data/docs/manual/films.md +20 -0
  176. data/docs/manual/handover.md +93 -0
  177. data/docs/manual/pr-format.md +75 -0
  178. data/docs/manual/reading.md +233 -0
  179. data/docs/manual/unopened.md +80 -0
  180. data/docs/manual/updates.md +55 -0
  181. data/lib/proctolog/installation.rb +125 -0
  182. data/lib/proctolog/version.rb +7 -0
  183. data/public/asking.js +50 -0
  184. data/public/code-review.js +40 -0
  185. data/public/cursor.js +292 -0
  186. data/public/decision-screenshots.js +97 -0
  187. data/public/decisions.css +949 -0
  188. data/public/decisions.js +1289 -0
  189. data/public/desk.css +582 -0
  190. data/public/desk.js +126 -0
  191. data/public/favicon.png +0 -0
  192. data/public/keys.js +228 -0
  193. data/public/mentions.js +389 -0
  194. data/public/messages.js +103 -0
  195. data/public/notes.js +731 -0
  196. data/public/pick.css +99 -0
  197. data/public/plaque.png +0 -0
  198. data/public/pr-number.js +11 -0
  199. data/public/prompt.css +46 -0
  200. data/public/prompt.js +16 -0
  201. data/public/pull_request.css +805 -0
  202. data/public/recording.css +379 -0
  203. data/public/recording.js +394 -0
  204. data/public/release.js +25 -0
  205. data/public/respond.css +260 -0
  206. data/public/respond.js +95 -0
  207. data/public/review-drafts.js +949 -0
  208. data/public/review-rounds.js +1447 -0
  209. data/public/scenarios.css +253 -0
  210. data/public/scenarios.js +36 -0
  211. data/public/schemas/proctolog-response-v1.json +101 -0
  212. data/public/side.js +133 -0
  213. data/public/standing.css +478 -0
  214. data/public/terminal-apl.svg +6 -0
  215. data/public/terminal-cuneiform.svg +3 -0
  216. data/public/terminal-grain-dots.svg +6 -0
  217. data/public/terminal-grain-weave.svg +6 -0
  218. data/public/terminal-grain-wedges.svg +6 -0
  219. data/public/terminal-shades.svg +3 -0
  220. data/public/terminal.css +4426 -0
  221. data/public/toast.js +26 -0
  222. data/public/try.js +124 -0
  223. data/public/viewer.js +808 -0
  224. data/public/words.js +81 -0
  225. data/skills/proctolog-handover/SKILL.md +77 -0
  226. data/skills/proctolog-pr-writing/SKILL.md +228 -0
  227. data/skills/proctolog-pr-writing/references/example-description.md +33 -0
  228. metadata +366 -0
data/public/words.js ADDED
@@ -0,0 +1,81 @@
1
+ // The words of a description, as the desk and the server count them.
2
+ //
3
+ // A section's text is that of GitHub's nodes alone, and a note quotes it with
4
+ // a little before and after. What the desk puts inside a section is marked
5
+ // data-desk and left out here as Rendering leaves it out there: the + beside
6
+ // a block, the form of a note, the notes under a box. So a note quotes the
7
+ // same words whether or not the desk has added anything.
8
+
9
+ const NEAR = 64;
10
+
11
+ // What a note can be pinned to without selecting anything: a paragraph, a
12
+ // list item, a heading, a table row, a code block, a caption, a picture.
13
+ export const BLOCKS =
14
+ "p, li, h3, h4, h5, h6, pre, tr, dt, dd, figcaption, figure.picture";
15
+
16
+ export const elementOf = (node) =>
17
+ node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
18
+
19
+ export const inDesk = (node) => elementOf(node).closest("[data-desk]") !== null;
20
+
21
+ // The block of the description that holds a node, if it is GitHub's.
22
+ export const blockOf = (node) => {
23
+ const block = elementOf(node).closest(BLOCKS);
24
+ return block && !inDesk(block) ? block : null;
25
+ };
26
+
27
+ export const blocksIn = (node) =>
28
+ [...node.querySelectorAll(BLOCKS)].filter((block) => !inDesk(block));
29
+
30
+ // The text of a copy, without the desk's own elements.
31
+ export const measured = (copy) => {
32
+ for (const own of copy.querySelectorAll("[data-desk]")) own.remove();
33
+ return copy.textContent;
34
+ };
35
+
36
+ // A block's own sentence: its words up to the first block inside it, so that
37
+ // a list item with a list under it is noted for what it says itself.
38
+ export const ownWords = (block) => {
39
+ const range = document.createRange();
40
+ range.selectNodeContents(block);
41
+ const [inner] = blocksIn(block);
42
+ if (inner) range.setEndBefore(inner);
43
+ return range;
44
+ };
45
+
46
+ export const hasWords = (block) =>
47
+ measured(ownWords(block).cloneContents()).trim() !== "";
48
+
49
+ // Stops a quote's edges from cutting a character in two.
50
+ const clean = (text) => text.replace(/^[\uDC00-\uDFFF]|[\uD800-\uDBFF]$/g, "");
51
+
52
+ // The words a range covers, as a note holds them: the words themselves, and
53
+ // the text around them in their section. The offset is a count of characters
54
+ // of the section's own text, so it is the same however the page is laid out.
55
+ // Null when the range is not within one section, or touches the desk's own.
56
+ export const quoteOf = (range) => {
57
+ if (inDesk(range.startContainer) || inDesk(range.endContainer)) return null;
58
+ const body = elementOf(range.commonAncestorContainer).closest(
59
+ "[data-quotable]",
60
+ );
61
+ if (!body) return null;
62
+
63
+ const lead = document.createRange();
64
+ lead.selectNodeContents(body);
65
+ lead.setEnd(range.startContainer, range.startOffset);
66
+
67
+ const picked = measured(range.cloneContents());
68
+ const start =
69
+ measured(lead.cloneContents()).length +
70
+ (picked.length - picked.trimStart().length);
71
+ const exact = picked.trim();
72
+ const text = measured(body.cloneNode(true));
73
+ if (!exact || text.slice(start, start + exact.length) !== exact) return null;
74
+
75
+ return {
76
+ exact,
77
+ before: clean(text.slice(Math.max(0, start - NEAR), start)),
78
+ after: clean(text.slice(start + exact.length, start + exact.length + NEAR)),
79
+ section: body.dataset.section,
80
+ };
81
+ };
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: proctolog-handover
3
+ description: Take the owner's review from PRoctolog, the review desk on their Mac, and carry it out - their notes, their choices on the decisions and their code comments, handed over with `proctolog handover` from the checkout under review, even halfway through the review, and answered in the one file the desk reads back. Use when the owner names PRoctolog or its desk - "check my proctolog comments", "check my proctolog notes", "do what I decided in proctolog", "pick up my proctolog review" - when they say "check again" after an earlier pass of this skill, or when they give you a PRoctolog handover folder or its handover.md. Not for review comments on GitHub alone.
4
+ ---
5
+
6
+ # Taking the owner's review from PRoctolog
7
+
8
+ The owner reviews pull requests on PRoctolog, a desk on their Mac. What they
9
+ decide there (a change, a question, a task for later, a rule) waits for an
10
+ agent. You take it yourself; nobody needs to press anything.
11
+
12
+ ## Take a pass
13
+
14
+ From the checkout of the repository under review:
15
+
16
+ ```sh
17
+ proctolog handover
18
+ ```
19
+
20
+ It hands over everything the owner decided that no agent has yet, writes it as
21
+ a folder where the desk keeps handovers, and prints what went in and where the
22
+ brief is. `proctolog burst list` names the bursts when there are several:
23
+ `proctolog handover first-run`. `--part 1055` takes one part of a burst alone,
24
+ when the owner names one.
25
+
26
+ When it says nothing waits, tell the owner so, and stop. When it says the
27
+ checkout has no burst, or is no checkout at all, PRoctolog is not used here:
28
+ say so, and stop.
29
+
30
+ ## Work through it
31
+
32
+ Read the brief, `handover.md`, whole. Its rules are the owner's and bind you,
33
+ beside the repository's own. Then read one pull request's file under `prs/` at
34
+ a time, from the bottom of the stack up. `worklist.json` holds the same
35
+ requests as data.
36
+
37
+ The owner may be halfway through their review, and may go on while you work.
38
+ Change nothing the worklist does not name: not a description's other
39
+ decisions, their keys or their pictures. A decision they have not reached yet
40
+ is no instruction.
41
+
42
+ Writing a description goes by the `proctolog-pr-writing` skill. Every write to
43
+ GitHub waits for the owner's word, as the brief's rules say. A pull request
44
+ nobody has opened yet is described in a file on this Mac, which its layer's
45
+ file names: rewrite that file in place, front matter kept, and open nothing.
46
+
47
+ ## Answer
48
+
49
+ Write the one file the brief names, under the burst's `responses/` folder, in
50
+ the format `proctolog-response/v1`. Write it even when you cannot finish: give
51
+ what you could not do as blocked, and say why. Until an answer comes back, the
52
+ desk holds that pass's requests back from every later one. Write nothing else
53
+ under the desk's folders: the marks and choices are the owner's.
54
+
55
+ To correct an answer, after a restack or a fix, write the same file again with
56
+ the time of writing as its `createdAt`. The desk shows the new version and
57
+ keeps the one before.
58
+
59
+ The desk shows your answers to the owner, who confirms each one or replies.
60
+
61
+ ## Held back
62
+
63
+ The command may say that requests an earlier pass handed over are still with an
64
+ agent that has not answered. They are held back so that two agents never do the
65
+ same work. Take them only when the owner says no agent is at them, or tells you
66
+ to take everything they decided:
67
+
68
+ ```sh
69
+ proctolog handover --again
70
+ ```
71
+
72
+ When you cannot ask, leave them, and say so in your last message.
73
+
74
+ ## Check again
75
+
76
+ When the owner says to check again, run `proctolog handover` once more. It
77
+ carries only what they decided since, and their replies to your answers.
@@ -0,0 +1,228 @@
1
+ ---
2
+ name: proctolog-pr-writing
3
+ description: Write, rewrite or regrade pull request descriptions for PRoctolog, the review tool - decisions for the owner graded blocking or non-blocking, each with a theme, at most five blocking, checked with `proctolog check` before the owner publishes them - including for branches nobody has opened yet, which the desk reviews from a folder on this Mac. Use whenever asked to "write these PRs for PRoctolog", "rewrite this PR's description for PRoctolog", make PRs reviewable in PRoctolog, review work on PRoctolog "without opening PRs" or "before opening PRs", grade a description's decisions or prepare one for review on PRoctolog, or open on GitHub what was reviewed unopened. Every GitHub write waits for the owner's word. Working through the owner's review is the proctolog-handover skill.
4
+ ---
5
+
6
+ # Writing a pull request for PRoctolog
7
+
8
+ The owner reviews this work on PRoctolog, a desk that reads each description,
9
+ lists its decisions, asks the owner about each, and hands the answers back to
10
+ an agent. A description is written for that reading: what was built, what the
11
+ owner must decide, and how to see it. The diff is on GitHub already; say what
12
+ it cannot.
13
+
14
+ Start from compound-engineering's guide to PR descriptions
15
+ (`ce-commit-push-pr/references/pr-description-writing.md`): lead with what is
16
+ now possible or fixed, never with what files moved, and size the description
17
+ by what it costs the owner to decide, not by the size of the diff. What
18
+ follows is what the desk adds to that.
19
+
20
+ A complete example is in `references/example-description.md`.
21
+
22
+ ## The sections
23
+
24
+ Keep the project's own template (Summary, Changes, Verification) and add these,
25
+ with exactly these second-level headings:
26
+
27
+ - `## How to test locally`: a **bold scenario name** over numbered steps, each
28
+ step one line: "Do this. Expect that." The desk opens each scenario from the
29
+ bold name.
30
+ - `## Decisions for the owner`: what the owner decides. Each decision is one
31
+ task item on one line.
32
+
33
+ ## What is a decision
34
+
35
+ Use the feedback ladder (Netlify's):
36
+
37
+ - **Mountains and boulders are blocking.** A choice that loses data, changes
38
+ who can do what, costs money, breaks an agreed rule, or cannot be undone
39
+ cheaply; or one the owner should settle before the work goes. These stay at
40
+ the top of the section.
41
+ - **Pebbles are non-blocking.** A choice a sensible default settles, which the
42
+ owner may still want to see: a wording, a spacing, a fold. They go under a
43
+ `### Non-blocking` heading, after every blocking decision.
44
+ - **Sand and dust are no decisions.** Implementation facts belong in Changes;
45
+ test gaps in Verification under "Not verified"; the rest nowhere.
46
+
47
+ **At most five blocking decisions.** More than five asks too much of one owner
48
+ at once: grade the ones a default can settle non-blocking, split the work, or
49
+ settle what a rule already settles (below). The desk warns past five.
50
+
51
+ The desk asks the blocking decisions one by one. The owner can leave every
52
+ non-blocking decision that waits for nothing as it is, in one gesture, so a
53
+ non-blocking decision must be one the owner could leave and not regret.
54
+
55
+ ## The decisions section
56
+
57
+ ```markdown
58
+ ## Decisions for the owner
59
+
60
+ - [ ] **Verifying can delete an account row.** Why it was built so, and who it affects. See [the row](#screenshot-account-row). Alternative: refuse instead, at the cost of a stuck account. <!-- proctolog:theme accounts -->
61
+
62
+ ### Non-blocking
63
+
64
+ - [ ] **"Skip for now" sits under a 664px fold.** Why. Alternative: tighten the step. <!-- proctolog:theme fit -->
65
+ ```
66
+
67
+ Each decision:
68
+
69
+ - is one line, a top-level task item: `- [ ] ` and nothing indented under it;
70
+ - opens with what was built, in **bold**: a distinct lead, which is how the
71
+ desk knows the decision again after an edit;
72
+ - says why, and who it affects;
73
+ - links the pictures that show it, beside the claim: `[a label](#screenshot-NAME)`,
74
+ where NAME is one picture's exact, unique `alt` text in this description;
75
+ - names the alternative last in its words: `Alternative: …`, with its cost, or
76
+ `Alternative: none.` when there is none worth offering;
77
+ - ends with its theme: `<!-- proctolog:theme NAME -->`.
78
+
79
+ The heading `### Non-blocking` is visible on GitHub on purpose: the grade
80
+ matters to anyone reading the pull request there.
81
+
82
+ ## Themes
83
+
84
+ A theme says what kind of question a decision asks, so the owner can answer one
85
+ kind across the burst at once. Use one of the project's: `proctolog themes`
86
+ prints them with what each means. The desk's own list, which a project may
87
+ replace in `.proctolog/project.yml`:
88
+
89
+ | Theme | What it covers |
90
+ |---|---|
91
+ | `copy` | What the words on a screen say, and how |
92
+ | `fit` | How a screen holds what is on it: wrapping, folds, sizes |
93
+ | `errors` | What happens when something fails, and what the member is told |
94
+ | `accounts` | Who a member is: signing up, signing in, their account |
95
+ | `data` | What is kept, moved or deleted, and who may see it |
96
+ | `plan` | What was built against what was planned |
97
+ | `build` | How the code is made: its parts, its dependencies, its tools |
98
+ | `limits` | Bounds: sizes, counts, rates and time |
99
+
100
+ The marker is hidden on GitHub and is no part of the decision's words: adding
101
+ or mending a theme never reopens a choice the owner made.
102
+
103
+ ## References: keep them, never invent them
104
+
105
+ A new decision carries no reference. When you rewrite a decision the owner
106
+ already answered, keep its handover's `source.decision.key` as a hidden marker
107
+ right after the bold lead, `<!-- proctolog:decision KEY -->`, so the owner's
108
+ choice follows it. Never give a reference to a new decision, and never reuse
109
+ one for another. Drop a decision the owner settled from the active list when
110
+ the work no longer needs it; its history stays on the desk.
111
+
112
+ ## Rules already made
113
+
114
+ When a rule the owner already made settles a choice (in the project's
115
+ `CLAUDE.md`, a decision record, or a burst's rules), do not list it as a
116
+ decision. Say in Changes which rule settled it: "Dates are the member's own day
117
+ (CLAUDE.md, Dates)." A decision that contradicts a rule is blocking, and says
118
+ which rule.
119
+
120
+ ## Evidence: fewer pictures, each one linked
121
+
122
+ A picture is evidence for a claim, so it belongs beside the claim it shows.
123
+
124
+ - **Take a change as a pair.** Shoot the same screen before the change and
125
+ after it, named `NAME-before` and `NAME-after` (the capture kit's `before
126
+ "NAME"` and `after "NAME"`, with `integrations/sartoria/compare.rb` required
127
+ after `kit.rb`). Upload both halves as they are, never composed side by side:
128
+ the desk joins them into one picture with its own compare views, and a
129
+ decision links the pair as `#screenshot-NAME`.
130
+ - **Compare before uploading.** `Capture.compare(names, out:)` drops a pair
131
+ whose pixels did not change, groups pairs of one size that show the same
132
+ change, and writes `compare.json`. Upload only what its `upload` list names:
133
+ one pair of a group stands for the others, which the description need not
134
+ show.
135
+ - **Link every picture** from a decision or a step of a scenario. The desk
136
+ folds a run of pictures nothing links under "More pictures (N)"; one that
137
+ matters is linked.
138
+ - Uploading is a write to GitHub: it goes in the owner's authorized batch, with
139
+ the rest.
140
+
141
+ ## Check before asking
142
+
143
+ Write the description to a file and check it with the desk's own parser:
144
+
145
+ ```sh
146
+ proctolog check --file body.md
147
+ ```
148
+
149
+ It exits 0 when the desk reads it well, 1 with faults (each on its line), and 2
150
+ when GitHub could not be asked. GitHub renders the file for the check and keeps
151
+ nothing of it. Mend every fault; weigh every warning. A picture link resolves
152
+ only to a picture already uploaded to GitHub, so check again once pictures are
153
+ in place.
154
+
155
+ A published description can be checked as GitHub holds it: `proctolog check 1062`.
156
+
157
+ ## GitHub writes wait for the owner
158
+
159
+ Every write to GitHub waits for the owner's word in that session: an edit of a
160
+ description, an upload of a picture, a push, a comment, a label. Prepare the
161
+ description, check it, show it, and wait. Never edit a description the owner
162
+ marks as another session's (the burst file's `hands_off`).
163
+
164
+ ## Not opened yet
165
+
166
+ When the owner wants the work reviewed before any pull request is opened,
167
+ write each one where the desk reads it instead of on GitHub. One branch, one
168
+ pull request, as `gh pr create` would be told it, in the folder
169
+ `proctolog unopened folder BRANCH` prints:
170
+
171
+ ```markdown
172
+ ---
173
+ title: A reset mail says which account it is for
174
+ base: recovery/fields
175
+ ---
176
+ ## Summary
177
+ …
178
+ ```
179
+
180
+ - `pull_request.md` holds the title and the base as front matter, then the
181
+ description, written as everything above says. The base is the branch it
182
+ goes into: `main`, or the branch of the layer under it. Layers may be built
183
+ with `gh stack init` and `gh stack add`; never `gh stack submit` until the
184
+ owner says to open them.
185
+ - Pictures go beside it, named as their alt text and linked by their file
186
+ names: `![reset-mail-after](reset-mail-after.png)`. Pairs are
187
+ `NAME-before.png` and `NAME-after.png`, compared as above. Nothing is
188
+ uploaded.
189
+ - Check each with `proctolog check BRANCH`: it reads the folder, pictures and
190
+ all. `proctolog unopened` lists those the desk reads, each with its number,
191
+ 01, 02, which the desk gives them.
192
+ - A burst names them by their branch, so it still holds them once they are
193
+ opened. Make it yourself, as part of the work: `proctolog burst new NAME
194
+ --top BRANCH` for a chain, `--pr BRANCH` for one alone. Never hand the
195
+ owner that step. They run `proctolog serve` in the repository or any of its
196
+ worktrees, which share the store, and find the burst on the desk.
197
+
198
+ Writing these files is no write to GitHub: rewrite them as a handover asks
199
+ without waiting for the owner's word. Keep the front matter as it is.
200
+
201
+ ## Opening what was reviewed
202
+
203
+ Only when the owner says to. Opening is a GitHub write, in the owner's
204
+ authorized batch:
205
+
206
+ 1. Push the branches.
207
+ 2. Upload the pictures, and change each link to the uploaded address, its alt
208
+ text as it was.
209
+ 3. Open each from the bottom up, with its own title, base and description:
210
+ `gh pr create --head BRANCH --base BASE --title TITLE --body-file FILE`,
211
+ the file being `pull_request.md` without its front matter. Or
212
+ `gh stack submit`, then `gh pr edit NUMBER --body-file FILE` for each.
213
+ 4. `proctolog check NUMBER` each.
214
+
215
+ The desk notices each opened branch on its next reading, and carries the
216
+ owner's review over to the new number. Decision keys must survive the move
217
+ unchanged, or the owner's choices do not follow them. Leave the folders as
218
+ they are: the desk no longer reads them once their branch is opened.
219
+
220
+ ## Answering a handover
221
+
222
+ The owner's answers come back to you as a handover, which the
223
+ `proctolog-handover` skill takes and answers: `proctolog handover` hands over
224
+ what they decided, even halfway through. The answer is one
225
+ `proctolog-response/v1` JSON file, in the folder the brief names. Its schema is
226
+ `../../public/schemas/proctolog-response-v1.json` from this skill's folder.
227
+ Answer only the request ids the handover gives; the desk refuses invented ones.
228
+ A rewritten description goes through everything above again, references kept.
@@ -0,0 +1,33 @@
1
+ ## Summary
2
+
3
+ A member who signs in with a passkey now lands where they were going, not on the home page. Before, every passkey sign-in dropped the page they had asked for; a member who followed a link to a house had to find it again.
4
+
5
+ ## How to test locally
6
+
7
+ **Back to the page asked for.**
8
+ 1. Sign out, then open a house's page. Expect the sign-in page.
9
+ 2. Sign in with a passkey. Expect the house's page, not the home page.
10
+
11
+ **No page asked for.**
12
+ 1. Sign out, then open the sign-in page itself. Expect the sign-in page.
13
+ 2. Sign in with a passkey. Expect the home page.
14
+
15
+ ## Changes
16
+
17
+ - The passkey sign-in keeps the page asked for, as the password sign-in already did.
18
+ - A page asked for is kept for ten minutes, as the password sign-in keeps it (CLAUDE.md, Sessions), so it is no decision here.
19
+
20
+ ## Decisions for the owner
21
+
22
+ - [ ] **A page asked for on another site is dropped, and the member lands on the home page.** A link from outside cannot send a member anywhere after they sign in; it affects members who follow links from email. Alternative: keep such a page when it is on this site's own domain, at the cost of a check that can be got wrong. <!-- proctolog:theme accounts -->
23
+ - [ ] **A member who signs in on a second device keeps the page asked for on that device alone.** Each device keeps its own; a member who starts on a phone and signs in on a laptop lands on the laptop's home page. Alternative: keep it with the account, which needs a column and a migration. <!-- proctolog:theme data -->
24
+
25
+ ### Non-blocking
26
+
27
+ - [ ] **The sign-in page says "Continue with a passkey".** As the password button's "Continue" does. Alternative: "Sign in with a passkey". <!-- proctolog:theme copy -->
28
+
29
+ ## Verification
30
+
31
+ - `bin/rails test test/controllers/passkey_sessions_controller_test.rb`: 9 runs, 0 failures.
32
+
33
+ Not verified: a passkey on a second device, by hand.