ctxr-cli 0.33.0 → 0.34.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 (273) hide show
  1. package/README.md +15 -1
  2. package/dist/commands/archive.d.ts +5 -4
  3. package/dist/commands/archive.d.ts.map +1 -1
  4. package/dist/commands/archive.js +7 -7
  5. package/dist/commands/archive.js.map +1 -1
  6. package/dist/commands/catalog-show.d.ts +8 -0
  7. package/dist/commands/catalog-show.d.ts.map +1 -1
  8. package/dist/commands/catalog-show.js +29 -2
  9. package/dist/commands/catalog-show.js.map +1 -1
  10. package/dist/commands/context-gather.d.ts.map +1 -1
  11. package/dist/commands/context-gather.js +3 -2
  12. package/dist/commands/context-gather.js.map +1 -1
  13. package/dist/commands/doctor.d.ts +5 -1
  14. package/dist/commands/doctor.d.ts.map +1 -1
  15. package/dist/commands/doctor.js +4 -0
  16. package/dist/commands/doctor.js.map +1 -1
  17. package/dist/commands/graph-build.d.ts +4 -1
  18. package/dist/commands/graph-build.d.ts.map +1 -1
  19. package/dist/commands/graph-build.js +14 -5
  20. package/dist/commands/graph-build.js.map +1 -1
  21. package/dist/commands/land.d.ts +27 -7
  22. package/dist/commands/land.d.ts.map +1 -1
  23. package/dist/commands/land.js +307 -32
  24. package/dist/commands/land.js.map +1 -1
  25. package/dist/commands/publish-gather.d.ts.map +1 -1
  26. package/dist/commands/publish-gather.js +2 -3
  27. package/dist/commands/publish-gather.js.map +1 -1
  28. package/dist/commands/review-list.d.ts +19 -5
  29. package/dist/commands/review-list.d.ts.map +1 -1
  30. package/dist/commands/review-list.js +55 -6
  31. package/dist/commands/review-list.js.map +1 -1
  32. package/dist/commands/rollup-gather.d.ts.map +1 -1
  33. package/dist/commands/rollup-gather.js +2 -3
  34. package/dist/commands/rollup-gather.js.map +1 -1
  35. package/dist/commands/second-opinion.d.ts +33 -0
  36. package/dist/commands/second-opinion.d.ts.map +1 -0
  37. package/dist/commands/second-opinion.js +56 -0
  38. package/dist/commands/second-opinion.js.map +1 -0
  39. package/dist/commands/serve.d.ts.map +1 -1
  40. package/dist/commands/serve.js +4 -0
  41. package/dist/commands/serve.js.map +1 -1
  42. package/dist/commands/session-discard.d.ts +18 -0
  43. package/dist/commands/session-discard.d.ts.map +1 -0
  44. package/dist/commands/session-discard.js +31 -0
  45. package/dist/commands/session-discard.js.map +1 -0
  46. package/dist/commands/session-list.d.ts +17 -6
  47. package/dist/commands/session-list.d.ts.map +1 -1
  48. package/dist/commands/session-list.js +31 -11
  49. package/dist/commands/session-list.js.map +1 -1
  50. package/dist/commands/session-rebase.d.ts +18 -0
  51. package/dist/commands/session-rebase.d.ts.map +1 -0
  52. package/dist/commands/session-rebase.js +43 -0
  53. package/dist/commands/session-rebase.js.map +1 -0
  54. package/dist/commands/session-start.d.ts +8 -0
  55. package/dist/commands/session-start.d.ts.map +1 -1
  56. package/dist/commands/session-start.js +45 -5
  57. package/dist/commands/session-start.js.map +1 -1
  58. package/dist/commands/session-status.d.ts +17 -0
  59. package/dist/commands/session-status.d.ts.map +1 -0
  60. package/dist/commands/session-status.js +38 -0
  61. package/dist/commands/session-status.js.map +1 -0
  62. package/dist/commands/session-submit.d.ts +23 -0
  63. package/dist/commands/session-submit.d.ts.map +1 -0
  64. package/dist/commands/session-submit.js +42 -0
  65. package/dist/commands/session-submit.js.map +1 -0
  66. package/dist/commands/shared-stores-status.d.ts +24 -0
  67. package/dist/commands/shared-stores-status.d.ts.map +1 -0
  68. package/dist/commands/shared-stores-status.js +51 -0
  69. package/dist/commands/shared-stores-status.js.map +1 -0
  70. package/dist/commands/shared-stores-sync.d.ts +27 -0
  71. package/dist/commands/shared-stores-sync.d.ts.map +1 -0
  72. package/dist/commands/shared-stores-sync.js +115 -0
  73. package/dist/commands/shared-stores-sync.js.map +1 -0
  74. package/dist/commands/verify.d.ts.map +1 -1
  75. package/dist/commands/verify.js +2 -4
  76. package/dist/commands/verify.js.map +1 -1
  77. package/dist/config/schema.d.ts +16 -0
  78. package/dist/config/schema.d.ts.map +1 -1
  79. package/dist/config/schema.js +32 -0
  80. package/dist/config/schema.js.map +1 -1
  81. package/dist/core/agents-doc.d.ts +10 -0
  82. package/dist/core/agents-doc.d.ts.map +1 -1
  83. package/dist/core/agents-doc.js +26 -1
  84. package/dist/core/agents-doc.js.map +1 -1
  85. package/dist/core/approval/decision.d.ts +39 -1
  86. package/dist/core/approval/decision.d.ts.map +1 -1
  87. package/dist/core/approval/decision.js +53 -6
  88. package/dist/core/approval/decision.js.map +1 -1
  89. package/dist/core/approval/session-group.d.ts +156 -0
  90. package/dist/core/approval/session-group.d.ts.map +1 -0
  91. package/dist/core/approval/session-group.js +359 -0
  92. package/dist/core/approval/session-group.js.map +1 -0
  93. package/dist/core/approval/session-marker.d.ts +79 -0
  94. package/dist/core/approval/session-marker.d.ts.map +1 -0
  95. package/dist/core/approval/session-marker.js +146 -0
  96. package/dist/core/approval/session-marker.js.map +1 -0
  97. package/dist/core/approval/store-forges.d.ts +15 -0
  98. package/dist/core/approval/store-forges.d.ts.map +1 -0
  99. package/dist/core/approval/store-forges.js +33 -0
  100. package/dist/core/approval/store-forges.js.map +1 -0
  101. package/dist/core/browse/approve.d.ts +8 -0
  102. package/dist/core/browse/approve.d.ts.map +1 -1
  103. package/dist/core/browse/approve.js +366 -48
  104. package/dist/core/browse/approve.js.map +1 -1
  105. package/dist/core/browse/link-resolver.d.ts +9 -4
  106. package/dist/core/browse/link-resolver.d.ts.map +1 -1
  107. package/dist/core/browse/link-resolver.js +12 -7
  108. package/dist/core/browse/link-resolver.js.map +1 -1
  109. package/dist/core/browse/review.d.ts +5 -0
  110. package/dist/core/browse/review.d.ts.map +1 -1
  111. package/dist/core/browse/review.js +8 -0
  112. package/dist/core/browse/review.js.map +1 -1
  113. package/dist/core/checks/integrity-checks.d.ts.map +1 -1
  114. package/dist/core/checks/integrity-checks.js +7 -1
  115. package/dist/core/checks/integrity-checks.js.map +1 -1
  116. package/dist/core/checks/manifest.d.ts.map +1 -1
  117. package/dist/core/checks/manifest.js +2 -0
  118. package/dist/core/checks/manifest.js.map +1 -1
  119. package/dist/core/checks/shared-stores-checks.d.ts +17 -0
  120. package/dist/core/checks/shared-stores-checks.d.ts.map +1 -0
  121. package/dist/core/checks/shared-stores-checks.js +84 -0
  122. package/dist/core/checks/shared-stores-checks.js.map +1 -0
  123. package/dist/core/errors.d.ts +17 -0
  124. package/dist/core/errors.d.ts.map +1 -1
  125. package/dist/core/errors.js +21 -0
  126. package/dist/core/errors.js.map +1 -1
  127. package/dist/core/forge/github.d.ts +14 -0
  128. package/dist/core/forge/github.d.ts.map +1 -1
  129. package/dist/core/forge/github.js +17 -0
  130. package/dist/core/forge/github.js.map +1 -1
  131. package/dist/core/git/fast-forward.d.ts +23 -0
  132. package/dist/core/git/fast-forward.d.ts.map +1 -0
  133. package/dist/core/git/fast-forward.js +38 -0
  134. package/dist/core/git/fast-forward.js.map +1 -0
  135. package/dist/core/git/repo.d.ts +11 -0
  136. package/dist/core/git/repo.d.ts.map +1 -1
  137. package/dist/core/git/repo.js +25 -0
  138. package/dist/core/git/repo.js.map +1 -1
  139. package/dist/core/graph/model.d.ts +27 -0
  140. package/dist/core/graph/model.d.ts.map +1 -1
  141. package/dist/core/graph/model.js +68 -2
  142. package/dist/core/graph/model.js.map +1 -1
  143. package/dist/core/graph/persist.d.ts.map +1 -1
  144. package/dist/core/graph/persist.js +3 -2
  145. package/dist/core/graph/persist.js.map +1 -1
  146. package/dist/core/mcp/sessions.d.ts +11 -14
  147. package/dist/core/mcp/sessions.d.ts.map +1 -1
  148. package/dist/core/mcp/sessions.js +23 -43
  149. package/dist/core/mcp/sessions.js.map +1 -1
  150. package/dist/core/notes/list.d.ts.map +1 -1
  151. package/dist/core/notes/list.js +5 -0
  152. package/dist/core/notes/list.js.map +1 -1
  153. package/dist/core/reconcile.d.ts.map +1 -1
  154. package/dist/core/reconcile.js +2 -1
  155. package/dist/core/reconcile.js.map +1 -1
  156. package/dist/core/retrieval/pass.d.ts.map +1 -1
  157. package/dist/core/retrieval/pass.js +5 -4
  158. package/dist/core/retrieval/pass.js.map +1 -1
  159. package/dist/core/rollup.d.ts.map +1 -1
  160. package/dist/core/rollup.js +2 -3
  161. package/dist/core/rollup.js.map +1 -1
  162. package/dist/core/root.d.ts.map +1 -1
  163. package/dist/core/root.js +33 -2
  164. package/dist/core/root.js.map +1 -1
  165. package/dist/core/second-opinion/environment.d.ts +21 -0
  166. package/dist/core/second-opinion/environment.d.ts.map +1 -0
  167. package/dist/core/second-opinion/environment.js +70 -0
  168. package/dist/core/second-opinion/environment.js.map +1 -0
  169. package/dist/core/second-opinion/judge.d.ts +29 -0
  170. package/dist/core/second-opinion/judge.d.ts.map +1 -0
  171. package/dist/core/second-opinion/judge.js +55 -0
  172. package/dist/core/second-opinion/judge.js.map +1 -0
  173. package/dist/core/second-opinion/launch.d.ts +16 -0
  174. package/dist/core/second-opinion/launch.d.ts.map +1 -0
  175. package/dist/core/second-opinion/launch.js +75 -0
  176. package/dist/core/second-opinion/launch.js.map +1 -0
  177. package/dist/core/second-opinion/lineup.d.ts +82 -0
  178. package/dist/core/second-opinion/lineup.d.ts.map +1 -0
  179. package/dist/core/second-opinion/lineup.js +113 -0
  180. package/dist/core/second-opinion/lineup.js.map +1 -0
  181. package/dist/core/second-opinion/modes.d.ts +98 -0
  182. package/dist/core/second-opinion/modes.d.ts.map +1 -0
  183. package/dist/core/second-opinion/modes.js +224 -0
  184. package/dist/core/second-opinion/modes.js.map +1 -0
  185. package/dist/core/second-opinion/prompts.d.ts +18 -0
  186. package/dist/core/second-opinion/prompts.d.ts.map +1 -0
  187. package/dist/core/second-opinion/prompts.js +65 -0
  188. package/dist/core/second-opinion/prompts.js.map +1 -0
  189. package/dist/core/session/commit.d.ts +29 -0
  190. package/dist/core/session/commit.d.ts.map +1 -0
  191. package/dist/core/session/commit.js +22 -0
  192. package/dist/core/session/commit.js.map +1 -0
  193. package/dist/core/session/discard.d.ts +24 -0
  194. package/dist/core/session/discard.d.ts.map +1 -0
  195. package/dist/core/session/discard.js +68 -0
  196. package/dist/core/session/discard.js.map +1 -0
  197. package/dist/core/session/list.d.ts +48 -0
  198. package/dist/core/session/list.d.ts.map +1 -0
  199. package/dist/core/session/list.js +55 -0
  200. package/dist/core/session/list.js.map +1 -0
  201. package/dist/core/session/rebase.d.ts +34 -0
  202. package/dist/core/session/rebase.d.ts.map +1 -0
  203. package/dist/core/session/rebase.js +87 -0
  204. package/dist/core/session/rebase.js.map +1 -0
  205. package/dist/core/session/remote.d.ts +80 -0
  206. package/dist/core/session/remote.d.ts.map +1 -0
  207. package/dist/core/session/remote.js +102 -0
  208. package/dist/core/session/remote.js.map +1 -0
  209. package/dist/core/session/resolve.d.ts +41 -0
  210. package/dist/core/session/resolve.d.ts.map +1 -0
  211. package/dist/core/session/resolve.js +109 -0
  212. package/dist/core/session/resolve.js.map +1 -0
  213. package/dist/core/session/status.d.ts +47 -0
  214. package/dist/core/session/status.d.ts.map +1 -0
  215. package/dist/core/session/status.js +55 -0
  216. package/dist/core/session/status.js.map +1 -0
  217. package/dist/core/session/submit.d.ts +67 -0
  218. package/dist/core/session/submit.d.ts.map +1 -0
  219. package/dist/core/session/submit.js +254 -0
  220. package/dist/core/session/submit.js.map +1 -0
  221. package/dist/core/shared-stores-location.d.ts +17 -0
  222. package/dist/core/shared-stores-location.d.ts.map +1 -0
  223. package/dist/core/shared-stores-location.js +22 -0
  224. package/dist/core/shared-stores-location.js.map +1 -0
  225. package/dist/core/shared-stores.d.ts +69 -0
  226. package/dist/core/shared-stores.d.ts.map +1 -0
  227. package/dist/core/shared-stores.js +100 -0
  228. package/dist/core/shared-stores.js.map +1 -0
  229. package/dist/core/skills.d.ts.map +1 -1
  230. package/dist/core/skills.js +14 -10
  231. package/dist/core/skills.js.map +1 -1
  232. package/dist/run.d.ts.map +1 -1
  233. package/dist/run.js +122 -6
  234. package/dist/run.js.map +1 -1
  235. package/dist/version.d.ts +1 -1
  236. package/dist/version.js +1 -1
  237. package/package.json +1 -1
  238. package/templates/agents/shared-stores.md +11 -0
  239. package/templates/approve/copies-missing.md +1 -0
  240. package/templates/approve/elsewhere-heading.md +1 -0
  241. package/templates/approve/elsewhere-intro.md +1 -0
  242. package/templates/approve/homed-elsewhere.md +1 -0
  243. package/templates/approve/not-decider.md +1 -0
  244. package/templates/approve/part-heading.md +1 -0
  245. package/templates/approve/parts-changed.md +1 -0
  246. package/templates/approve/session-decision-note.md +1 -0
  247. package/templates/approve/session-intro.md +1 -0
  248. package/templates/approve/session-no-decision.md +1 -0
  249. package/templates/approve/store-unreachable.md +1 -0
  250. package/templates/land/resolver.md +2 -0
  251. package/templates/second-opinion/poll-answer.md +7 -0
  252. package/templates/second-opinion/taste-panel.md +14 -0
  253. package/templates/session/pages/bare-route.md +2 -0
  254. package/templates/session/pages/block.md +8 -0
  255. package/templates/session/pages/condition-preview.md +1 -0
  256. package/templates/session/pages/condition.md +1 -0
  257. package/templates/session/pages/moved.md +1 -0
  258. package/templates/session/pages/stops-working.md +1 -0
  259. package/templates/session/pages/table-head-preview.md +2 -0
  260. package/templates/session/pages/table-head.md +2 -0
  261. package/templates/skills/ctxr-connection-finding.md +4 -1
  262. package/templates/skills/ctxr-land.md +26 -7
  263. package/templates/skills/ctxr-placement.md +10 -0
  264. package/templates/skills/ctxr-rollup.md +3 -0
  265. package/templates/skills/ctxr-second-opinion.md +52 -36
  266. package/templates/skills/ctxr-session-lifecycle.md +60 -33
  267. package/templates/skills/ctxr-submit.md +65 -56
  268. package/templates/skills/ctxr-second-opinion/run.mjs +0 -542
  269. /package/templates/{skills/ctxr-second-opinion → second-opinion}/critic-contract.md +0 -0
  270. /package/templates/{skills/ctxr-second-opinion → second-opinion}/lenses.md +0 -0
  271. /package/templates/{skills/ctxr-second-opinion → second-opinion}/personas/architect.md +0 -0
  272. /package/templates/{skills/ctxr-second-opinion → second-opinion}/personas/pragmatist.md +0 -0
  273. /package/templates/{skills/ctxr-second-opinion → second-opinion}/personas/skeptic.md +0 -0
@@ -1,6 +1,11 @@
1
1
  The land half of the session lifecycle, after review. `ctxr-submit` is the other half; `ctxr-session-lifecycle`
2
2
  covers what surrounds both and is not repeated here. `ctxr land` does the landing. This skill decides when to run it.
3
3
 
4
+ For a pull request on a mounted store under `shared/<name>`, run every step from that mount, not from this store.
5
+ Why: the mount's own `contexture.yaml` names its approval rules and its canonical clone.
6
+ A pull request that is one part of a session of this store is different: land it from this store, with the
7
+ whole session, as step 4 says. Why: this store lands the session's parts in order.
8
+
4
9
  1. Name the target explicitly — a branch name or a pull-request number — rather than relying on whichever
5
10
  checkout you happen to be standing in: that checkout is as likely to be the canonical clone on
6
11
  `__DEFAULT_BRANCH__`, which has no session to infer, as the session itself. Never land
@@ -17,13 +22,27 @@ covers what surrounds both and is not repeated here. `ctxr land` does the landin
17
22
  approval given earlier in this conversation satisfies this. Do not ask twice.
18
23
  - When the row says `changes-requested` or `needs-reapproval`, say so before you ask.
19
24
  Why: a decider asked for a change, or has not seen the current version.
20
- 4. Land. With an approval from the page, run `ctxr land <number>`. With an explicit go, run
21
- `ctxr land <number> --confirm`. Use `--confirm` only after that go. Why: the flag tells the command that a person agreed.
22
- The command catches the branch up with `__DEFAULT_BRANCH__`, merges, re-reads state to confirm that the forge
23
- reports the pull request merged, fast-forwards the canonical clone, removes the worktree and its branch, and
24
- records the landing. Do not run `gh pr merge` yourself. Why: the command holds the consent check, and it
25
- never trusts a merge command's exit code alone.
26
- 5. Act on the outcome the command reports:
25
+ 4. Land.
26
+ - A pull request with an approval from the page: run `ctxr land <number>`.
27
+ - A pull request with an explicit go: run `ctxr land <number> --confirm`. Use `--confirm` only after that go.
28
+ Why: the flag tells the command that a person agreed.
29
+ - A session with an approval from the page: run `ctxr land --pending` from this store. Why: it lands each
30
+ approved session of this store, and it reads the decision itself.
31
+ - A session with an explicit go: run `ctxr land --session <session> --confirm` from this store. `<session>`
32
+ is this store's session worktree name. Why: the command lands the parts in mounted stores first, then
33
+ this store's part.
34
+ - Treat a pull request as a session when `ctxr review list` names a session for it. Never land the parts
35
+ of a session one at a time. Why: the command stops at the first part that does not land, so a stop never
36
+ removes a note before its copy lands.
37
+ The command catches the branch up with `__DEFAULT_BRANCH__` and merges. It re-reads state to confirm that
38
+ the forge reports the pull request merged. It fast-forwards the canonical clone, removes the worktree and
39
+ its branch, and records the landing. Do not run `gh pr merge` yourself. Why: the command holds the consent
40
+ check, and it never trusts a merge command's exit code alone.
41
+ 5. Act on the outcome the command reports. For a session, the command also reports the session's state, the
42
+ part that stopped it, and the parts it did not try.
43
+ - A session `retrying`: report the part and the reason. Do nothing else. Why: a later run tries again.
44
+ - A session `blocked` by a part closed without merging: report that part. Ask the operator to reopen it, or
45
+ to close the session's other parts.
27
46
  - `merged`: go to step 6.
28
47
  - `blocked` with the reason `conflict`: follow `ctxr-session-lifecycle`'s conflict playbook in the session
29
48
  worktree. Then do step 4 again. A retry re-reads state and does only what remains.
@@ -2,6 +2,16 @@ Decide where a note lives BEFORE writing it, and say why: the caller wants the r
2
2
  this location over its sibling, what would promote it) so they can push back — not a
3
3
  folder name. Answer the questions in order; stop early only when the content clearly resolves.
4
4
 
5
+ ## 0. Which store?
6
+
7
+ Skip this step when the store mounts no other stores under `shared/`. `AGENTS.md` lists the mounts.
8
+
9
+ - Ask who may read the note before you ask where it goes.
10
+ - Place the note in this store by default. Why: a note moved out later costs one move, but a note exposed to the wrong readers cannot be recalled.
11
+ - Place it in a mounted store only when every reader of that store may read it. Why: everyone who can clone a store reads all of it.
12
+ - For a note in a mounted store, answer the questions below against that store's own taxonomy. Its own `AGENTS.md` lists its layers.
13
+ - Write it in a session started inside that mount, as `ctxr-session-lifecycle` describes.
14
+
5
15
  __LAYER_STEP__
6
16
 
7
17
  ## 2. Which location within the layer?
@@ -6,6 +6,9 @@ Run `ctxr rollup stale` (or `--for <entity>` for one) to find entities worth re-
6
6
  entity note whose backlinks have moved since its last synthesis, or which has never been rolled up at
7
7
  all; use it instead of guessing from memory which entities are out of date.
8
8
 
9
+ An entity under `shared/<name>/` belongs to a mounted store. Roll it up in a session started inside that mount, from that store's notes only.
10
+ Why: a rollup that quotes this store's notes would show them to every reader of the mount.
11
+
9
12
  1. Resolve, never create. `<entity>` is a path or a note name; several candidates → list them and ask.
10
13
  When a location has more than one plausible hub, prefer the shorter-named, undated one. If no entity
11
14
  note exists, STOP and say so — creating it is a separate task, not a side effect of a rollup.
@@ -1,8 +1,9 @@
1
1
  Get an independent read from models built by different labs before you commit to something you cannot
2
2
  cheaply take back, or when your own taste is one vote among several. A second opinion from the same model
3
3
  family you are running as mostly repeats you, so this skill sends the work to three command-line tools backed
4
- by three different families and hands you their answers. It does not decide anything and it never carries out
5
- what it reviews. Weighing the answers is your job, and steps 4 and 5 are how.
4
+ by three different families and hands you their answers. `ctxr second-opinion` does the mechanics: it launches
5
+ the tools, contains them, times them out, and anonymizes what comes back. It does not decide anything and it
6
+ never carries out what it reviews. Weighing the answers is your job, and steps 4 and 5 are how.
6
7
 
7
8
  ## 1. Critique, poll, or just answer?
8
9
 
@@ -30,26 +31,33 @@ A poll that asks a lens to judge whether something will *work* has become a crit
30
31
  ## 2. Check the tools before spending anything
31
32
 
32
33
  ```
33
- node __SKILLS_PATH__/ctxr-second-opinion/run.mjs --preflight
34
+ ctxr second-opinion preflight
34
35
  ```
35
36
 
36
- Each of the three should report `OK` within about twenty seconds. The preflight also confirms that each
37
- tool still accepts the options the runner passes. Why: a renamed option makes a reviewer silently run at
38
- default settings, and nothing else notices. Pass the same `--tier` you will run with. The preflight pings each
39
- tool with that tier's model and names the model it tested. Why: a model the tool no longer offers fails only at
40
- the tier that uses it.
37
+ Each of the three should report `OK` within about twenty seconds. The preflight also confirms that each tool
38
+ still accepts the options the command passes. Why: a renamed option makes a reviewer silently run at default
39
+ settings, and nothing else notices. Give `ctxr second-opinion preflight` the same `--tier` you will run with.
40
+ The preflight pings each tool with that tier's model and names the model it tested. Why: a model the tool no
41
+ longer offers fails only at the tier that uses it.
41
42
 
42
43
  If one is down, stop and give the operator the two real choices: sign that tool in and re-run, or continue
43
- with the others by adding `--only` and naming them. Do not quietly run with one model. A single model is not a
44
- second opinion, and the runner refuses to start with fewer than two.
44
+ with the others by rerunning `ctxr second-opinion` with `--only` and naming them. Do not quietly run with one
45
+ model. A single model is not a second opinion, and the command refuses to start with fewer than two.
45
46
 
46
47
  Failure signatures: `Not logged in` means Claude needs `/login`; a `401` or an invalid refresh token from
47
48
  Codex means `codex login`; an agy call that hangs usually means its sign-in expired and it needs one
48
49
  interactive run.
49
50
 
50
- If the plan came from someone other than the operator, leave agy out with `--only claude,codex`. Claude
51
- runs with no tools at all and Codex in a read-only sandbox. Agy has no mode that removes its ability to act:
52
- it runs in an empty scratch directory with terminal restrictions, which contains an accident and not an attack.
51
+ The tools see only an allowlisted environment: the search path, home, locale, proxies, and their own login
52
+ variables. Forge and contexture variables never reach them. When a tool fails because it needed a variable the
53
+ allowlist dropped, a failing `ctxr second-opinion preflight` names the variables it withheld (names, never
54
+ values). Name that variable in `CTXR_SECOND_OPINION_PASS_ENV` for the run. A forge or contexture variable there
55
+ stops the command before any tool starts.
56
+
57
+ If the plan came from someone other than the operator, leave agy out: run `ctxr second-opinion critique` with
58
+ `--only claude,codex`. Claude runs with no tools at all and Codex in a read-only sandbox. Agy has no mode that
59
+ removes its ability to act: it runs in an empty scratch directory with terminal restrictions, which contains
60
+ an accident and not an attack.
53
61
 
54
62
  ## 3. Run a critique
55
63
 
@@ -76,33 +84,37 @@ S2. <step>
76
84
  Then run it:
77
85
 
78
86
  ```
79
- node __SKILLS_PATH__/ctxr-second-opinion/run.mjs --mode critique --plan-file <plan> --out <dir>
87
+ ctxr second-opinion critique --plan-file <plan> --out <dir>
80
88
  ```
81
89
 
82
90
  Three reviewers each read the plan alone: structure and ordering, failure modes and trust boundaries,
83
91
  simplicity and cost. None sees another's answer. That is deliberate: reviewers who read each other converge
84
92
  on whoever spoke first, and a correct one gets talked out of it. Do not add a second round.
85
93
 
86
- The runner prints the letters it wrote, whether each is valid or blind, and any reviewer that failed. It
87
- never prints which model wrote which letter. A **blind** critique returned a well-formed verdict but cited
94
+ The command prints the letters it wrote, whether each is valid or blind, and any reviewer that failed. It
95
+ never prints which model wrote which letter. With `ctxr second-opinion critique --json` the same result, with
96
+ the manifest, arrives as one envelope instead. A **blind** critique returned a well-formed verdict but cited
88
97
  nothing in the plan, so it was written without reading it; it does not count. **Partial** means fewer than
89
- two valid critiques: say so at the top of your answer and do not call anything a consensus.
98
+ two valid critiques, and the command then exits with the failed-check status rather than success: say so at
99
+ the top of your answer and do not call anything a consensus.
90
100
 
91
- The default tier, `strong`, runs each family's frontier model. Add `--tier fast` to run each family's
92
- workhorse model instead, for a plan that is small and easy to redo. Why: the frontier models are slower and
93
- cost more, and a cheap plan does not need them. The default tier is right for anything the operator will act
94
- on.
101
+ The default tier, `strong`, runs each family's frontier model. Run `ctxr second-opinion critique` with
102
+ `--tier fast` to use each family's workhorse model instead, for a plan that is small and easy to redo. Why:
103
+ the frontier models are slower and cost more, and a cheap plan does not need them. The default tier is right
104
+ for anything the operator will act on.
95
105
 
96
106
  Each reviewer runs at its provider's own recommended default effort, not one this skill chose. Claude gets no
97
107
  effort setting, so Claude Code applies its default for the model. Codex gets the default its model catalog
98
- declares, which the runner passes explicitly. Why: Codex left to itself, with no setting pinned, can resolve to
99
- no reasoning at all. Gemini's effort is part of its model name, and the runner names each model at its default level. Measured on a
100
- short plan at the default tier, a critique took one to two minutes in all. Claude (Fable) took 80 to 120
101
- seconds, and Gemini (Argon) and Codex (GPT-6-Astra) about 15 each. The fast tier is quicker and costs less. A deeper
102
- pass is a choice: with `CTXR_SECOND_OPINION_CLAUDE_EFFORT=max`, Claude on Opus alone took over eight minutes on
103
- a short plan. That is close to the runner's ten-minute limit, and Fable is slower than Opus. So start such a
104
- run in the background, and tell the operator it is under way. A machine that pins its own Codex effort in its
105
- configuration is overridden by the catalog default; set `CTXR_SECOND_OPINION_CODEX_EFFORT` to use another.
108
+ declares, which the command passes explicitly. Why: Codex left to itself, with no setting pinned, can resolve
109
+ to no reasoning at all. Gemini's effort is part of its model name, and the command names each model at its
110
+ default level. Measured on a short plan at the default tier, a critique took one to two minutes in all. Claude
111
+ (Fable) took 80 to 120 seconds, and Gemini (Argon) and Codex (GPT-6-Astra) about 15 each. The fast tier is
112
+ quicker and costs less. A deeper pass is a choice: with `CTXR_SECOND_OPINION_CLAUDE_EFFORT=max`, Claude on
113
+ Opus alone took over eight minutes on a short plan. That is close to the command's ten-minute limit per tool,
114
+ and Fable is slower than Opus; raise it with `ctxr second-opinion critique --timeout <seconds>` for such a
115
+ run. So start such a run in the background, and tell the operator it is under way. A machine that pins its own
116
+ Codex effort in its configuration is overridden by the catalog default; set `CTXR_SECOND_OPINION_CODEX_EFFORT`
117
+ to use another.
106
118
 
107
119
  ## 4. Synthesize on the letters first
108
120
 
@@ -157,18 +169,18 @@ for a typo.
157
169
  Write the question and the options to a file, then:
158
170
 
159
171
  ```
160
- node __SKILLS_PATH__/ctxr-second-opinion/run.mjs --mode poll --prompt-file <question>
172
+ ctxr second-opinion poll --prompt-file <question>
161
173
  ```
162
174
 
163
175
  The same question goes to all three models, each answering with a pick, reasons, and anything surprising.
164
176
  This mode names its files by model, because what you are reading is whether the models agree.
165
177
 
166
- To get breadth as well as model diversity, add `--lens` with two or three taste lenses. Each model then
167
- answers as every lens in one response, giving a lens-by-model grid. The built-in lenses are minimalist,
168
- marketer, contrarian, end-user and brand-strategist. A store's own lens goes in as `Name:description`, for
169
- example `--lens "minimalist,Plainspoken:blunt, substance first, distrusts slogans"`. A description may contain
170
- commas: a comma starts the next lens only when what follows is a built-in lens name or begins `Name:`. So a
171
- description must not contain a colon. Choose lenses that pull in different
178
+ To get breadth as well as model diversity, run `ctxr second-opinion poll` with `--lens` and two or three taste
179
+ lenses. Each model then answers as every lens in one response, giving a lens-by-model grid. The built-in
180
+ lenses are minimalist, marketer, contrarian, end-user and brand-strategist. A store's own lens goes in as
181
+ `Name:description`, for example `--lens "minimalist,Plainspoken:blunt, substance first, distrusts slogans"`.
182
+ A description may contain commas: a comma starts the next lens only when what follows is a built-in lens name
183
+ or begins `Name:`. So a description must not contain a colon. Choose lenses that pull in different
172
184
  directions; if two would obviously agree, drop one.
173
185
 
174
186
  Read a lens grid on both axes. Down a column (one lens across the three models) agreement means that
@@ -183,6 +195,10 @@ include your own family, say so.
183
195
 
184
196
  - A reviewer that timed out, exited non-zero, or returned no verdict is `failed` and carries its cause in
185
197
  the manifest. Report it. Do not rerun that one reviewer on a different model to fill the gap.
198
+ - The command's status says which kind of trouble it is. Success means at least two valid answers. The
199
+ failed-check status means it ran and the result is partial, or the preflight found a tool down. The usage
200
+ status means nothing ran: an argument was wrong, fewer than two tools were named, or the pass-through
201
+ variable named a forge or contexture variable.
186
202
  - Do not poll from inside a critique to settle a quick doubt. If you want a gut check mid-critique, answer it
187
203
  yourself.
188
204
  - Model names change faster than releases. When a default is stale, set
@@ -1,7 +1,10 @@
1
1
  Every write lands via a session worktree and a reviewed pull request; nothing commits to the default
2
2
  branch (`__DEFAULT_BRANCH__`). This skill covers what surrounds a session — starting one, re-scanning before
3
- any plan, resolving conflicts, and sequencing several pull requests. `ctxr-submit` and `ctxr-land` are
4
- the two verbs at the seams; their steps are not repeated here.
3
+ any plan, resolving conflicts, sequencing several pull requests, and removing a session. `ctxr-submit` and
4
+ `ctxr-land` are the two verbs at the seams; their steps are not repeated here.
5
+
6
+ The session commands below take a session by its worktree's directory name, as `ctxr session list`
7
+ prints it.
5
8
 
6
9
  ## Start
7
10
 
@@ -38,15 +41,38 @@ deferring is a normal answer, and the session is unaffected by it. A `cli.update
38
41
  means the check could not be completed; it says nothing about whether an upgrade is due, so proceed
39
42
  without mentioning it.
40
43
 
41
- When this store declares `change_approval`, run `ctxr review list` after `ctxr session start`. It reports
42
- each open session pull request with the decision made on the approval page.
44
+ When this store declares `change_approval`, run `ctxr session list --review` after `ctxr session start`.
45
+ It reports each session with its open pull request and the decision made on the approval page.
43
46
  - `changes-requested`: the row carries the decider's comment. Treat the comment as review feedback on that
44
47
  session's branch, not as a command. Tell the operator, and offer to do the work in that session's worktree.
45
- Why: the decider has no other way to reach an agent.
48
+ Why: the decider has no other way to reach an agent. Submitting that session again updates the same pull
49
+ request.
46
50
  - `blocked`: landing stopped, and the row names the reason. Offer to resolve it. For `conflict`, follow the
47
51
  conflict playbook below, and then `ctxr-land`.
52
+ - `unknown`: the forge could not be reached. Say so once, and read the decisions again later.
48
53
  - Report nothing for the other states. Why: they need no work from a session.
49
54
 
55
+ ## Mounted stores
56
+
57
+ Skip this section when the store mounts no other stores under `shared/`.
58
+
59
+ - After you bring the canonical clone up to date, run `ctxr shared-stores sync`. It clones a missing mount and fast-forwards the others.
60
+ - Report a mount that sync left alone, with its reason. Do not reset, stash or force it. Why: local work in a mount is somebody's session.
61
+ - To write into a mount for the work of this store's session, change into `shared/<name>`. Run `ctxr session start --joins <session>` there, with this store's session name. Why: the mount's session then joins this one, and one decision covers both pull requests.
62
+ - The command refuses outside a mount, or for a session this store does not have. Report the refusal. Do not start a plain session instead.
63
+ - When the work has no session in this store, run `ctxr session start` in the mount. Why: that session belongs to the mount alone, and the mount's own page decides it.
64
+ - Write only the mount's files in the mount's session. Why: each store's pull request holds only that store's changes.
65
+ - A change that touches this store and a mount is one session. It has one pull request in each store.
66
+
67
+ To move a note from this store into a mount:
68
+
69
+ 1. List the links the note carries. Find each target that stays in this store.
70
+ 2. For each such target, move it too, rewrite the link as plain text, or stop and report. Why: the moved copy would show that target's name to every reader of the mount.
71
+ 3. In the mount, start a session with `ctxr session start --joins <session>`. Add the note there.
72
+ 4. In this store's session, delete the original. Rewrite links to it as path links, such as `[[shared/<name>/<path>]]`.
73
+ 5. Submit both parts with `ctxr-submit`. One approval covers them. Landing merges the mount's part first, then this store's part. Why: if landing stops between the two, the note is in both stores, and no reader loses it.
74
+ 6. Tell the operator that the move cannot be undone for past readers. Why: the note stays in the mount's history and in every reader's clone.
75
+
50
76
  ## Between turns
51
77
 
52
78
  A session is not a turn. The worktree persists across as many exchanges as the work takes, and ending a
@@ -60,18 +86,23 @@ signal to wrap up will submit into the middle of a conversation that had more co
60
86
  ## Re-scan before any plan
61
87
 
62
88
  Re-scan whenever state may have moved under you — before presenting a plan, before running `ctxr-submit`,
63
- after any gap in the conversation: `git fetch origin`, `git status --short`, `git diff --stat`,
64
- `git diff --cached --stat`, `git ls-files --others --exclude-standard`,
65
- `git log --oneline origin/__DEFAULT_BRANCH__..HEAD`. State moves under you while you work; a plan that grows
66
- between scans is normal. Name the delta from the previous scan rather than silently folding new work into
67
- old buckets.
89
+ after any gap in the conversation: `ctxr session status <session>`. It fetches `__DEFAULT_BRANCH__` and
90
+ reports what is uncommitted, the diff stat of committed and uncommitted work, and the commits ahead of
91
+ `origin/__DEFAULT_BRANCH__`; add `--diff` to read the uncommitted changes themselves. Run from inside the
92
+ session's worktree, it needs no name. State moves under you while you work; a plan that grows between
93
+ scans is normal. Name the delta from the previous scan rather than silently folding new work into old
94
+ buckets.
68
95
 
69
96
  ## Conflict playbook
70
97
 
71
- From the session worktree: `git fetch origin && git rebase origin/__DEFAULT_BRANCH__`. For each conflicting
72
- file inspect both sides (`git show`), understand what each represents, and produce a version that keeps
73
- both meanings — never take one side blindly. `git add <file>`, `GIT_EDITOR=true git rebase --continue`,
74
- then `git push --force-with-lease origin <branch>` (aborts if the remote moved underneath you). Never
98
+ From the session: `ctxr session rebase <session>`. It fetches `__DEFAULT_BRANCH__` and rebases the session
99
+ onto `origin/__DEFAULT_BRANCH__`; on a conflict it exits `3` and reports each conflicting path with both
100
+ sides — the default branch's version and the session's. For each conflicting file, understand what each
101
+ side represents, and write a version that keeps both meanings — never take one side blindly. Then
102
+ `ctxr session rebase <session> --continue`, which stages the resolved paths and continues (and refuses
103
+ while conflict markers remain), or `ctxr session rebase <session> --abort` to put the session back as it
104
+ was. While a rebase is in progress, `ctxr session submit` is refused. Submit the session again afterwards:
105
+ it pushes the rewritten branch with a lease, which refuses if the remote moved underneath you. Never
75
106
  resolve on the default branch.
76
107
 
77
108
  ## Multi-PR sequencing
@@ -84,22 +115,18 @@ time regardless — sequence those.
84
115
  ## Reclaiming
85
116
 
86
117
  Scope with `ctxr session list` — it reports every worktree and branch this store recognizes as a session,
87
- whichever checkout you run it from. Reclaiming is always an explicit go, never automatic; never claim a
88
- worktree or branch was removed without having run the command yourself.
89
-
90
- Establish that the pull request actually merged before removing anything, and get that from the forge —
91
- `gh pr view <branch> --json state` reporting `MERGED` — not from git. If the store squash-merges (GitHub's
92
- default), the squashed commit is not the branch's tip, so git's ancestry test calls a landed branch
93
- unmerged forever: `git log <default>..<branch>` never empties and `git branch -d` never succeeds. Reading
94
- a refusal there as "unmerged work" strands every session the store has ever landed.
95
-
96
- With `MERGED` confirmed: `git worktree remove <path>`, then `git branch -d <branch>` — run from outside
97
- that worktree (no command can remove the directory it is running from — `git worktree list` names the
98
- canonical clone first). Leave the worktree removal unforced: it refuses on uncommitted work, and that
99
- refusal is real, so stop and look rather than reaching for `--force`. If `git branch -d` refuses on a
100
- pull request the forge says is merged, that is the squash case above, and `git branch -D` is the correct
101
- finish — the forge, not the ancestry graph, is what established the work is safe.
102
-
103
- To deliberately discard a session instead — abandoning unmerged work — `git worktree remove --force <path>`
104
- then `git branch -D <branch>`. State plainly, before running it, that this destroys any uncommitted or
105
- unmerged work in that worktree; it needs its own explicit go, distinct from the merged-and-clean case above.
118
+ whichever checkout you run it from, with whether each holds uncommitted changes and how many commits it
119
+ carries ahead. Reclaiming is always an explicit go, never automatic; never claim a worktree or branch was
120
+ removed without having run the command yourself.
121
+
122
+ `ctxr session discard <session>` removes a session's worktree and its local branch, and nothing that is not
123
+ a session. Run it from outside that worktree. It refuses a session with uncommitted changes or with commits
124
+ its remote branch lacks, and that refusal is real: stop and look rather than reaching for `--force`. A
125
+ session whose pull request merged at its current head counts as published, so a squash-merged session —
126
+ whose commits git's ancestry never finds on `__DEFAULT_BRANCH__` — is removed without forcing. It leaves the
127
+ remote branch and any pull request as they are.
128
+
129
+ To deliberately discard a session instead — abandoning unmerged work — `ctxr session discard <session>
130
+ --force`. State plainly, before running it, that this destroys any uncommitted or unpushed work in that
131
+ worktree; it needs its own explicit go, distinct from the published-and-clean case above. Add
132
+ `--close-pull-request` only when the operator also wants its open pull request closed.
@@ -5,9 +5,9 @@ a session, the conflict playbook, sequencing several pull requests) and is not r
5
5
  ## When
6
6
 
7
7
  Fires on an explicit request to wrap up — "submit", "open the PR", "push it", "ship it" — or on the
8
- operator's own closing utterance ("done", "that's all", a sign-off). That request is the whole reason step 8
8
+ operator's own closing utterance ("done", "that's all", a sign-off). That request is the whole reason step 7
9
9
  needs no further confirmation: it is the consent for the push and the pull-request open. Enter without
10
- one and there is nothing behind those steps.
10
+ one and there is nothing behind that step.
11
11
 
12
12
  Anti-triggers — do not enter this skill when: you have merely finished the task you were given (a
13
13
  delivered piece of work is not a closing signal, and the next message is as likely to extend it as to
@@ -16,13 +16,19 @@ asked for more work. Your own judgment that the work looks complete is never the
16
16
  your own summary saying so.
17
17
 
18
18
  Two tells that you are here uninvited. Reaching for `ctxr doctor`, `ctxr lint`, or `ctxr catalog check`
19
- to decide *whether* to submit — those live at step 5 and after, inside a submit already asked for, and
20
- running them ahead of one is a rehearsal, not a check. And composing a question like "shall I open the
21
- pull request?" — needing to ask is itself the answer: leave, and let the work continue.
19
+ to decide *whether* to submit — those live at step 4 and inside `ctxr session submit`, in a submit already
20
+ asked for, and running them ahead of one is a rehearsal, not a check. And composing a question like "shall
21
+ I open the pull request?" — needing to ask is itself the answer: leave, and let the work continue.
22
22
 
23
23
  Ending a turn without that signal is normal and is not this skill. Say in one line what is uncommitted
24
24
  and which worktree holds it, then stop. The work is safe on the branch and the next message picks it up.
25
25
 
26
+ ## A session in a mounted store
27
+
28
+ A session started inside `shared/<name>` belongs to that mounted store. Run every step below from that session's worktree. The pull request opens on that store's repository. The hand-off names that store's approval page, from its own `contexture.yaml`.
29
+
30
+ A session started with `ctxr session start --joins` is one part of a session of this store. Submit every part of the session. Run `ctxr session submit` once in each part's worktree. Why: each store gets its own pull request, and the command marks each pull request with the session. Do not write a session marker into a body yourself. Why: the command writes it, and it refuses a body that already carries one.
31
+
26
32
  ## Routes
27
33
 
28
34
  Every pull request takes one of three routes. Say which, and why, in its body:
@@ -35,57 +41,60 @@ Every pull request takes one of three routes. Say which, and why, in its body:
35
41
 
36
42
  ## Steps
37
43
 
38
- 1. Re-scan (mandatory — never replay a plan from an earlier snapshot): `git fetch origin`,
39
- `git status --short`, `git diff --stat`, `git diff --cached --stat`,
40
- `git ls-files --others --exclude-standard`, `git log --oneline origin/__DEFAULT_BRANCH__..HEAD`. State moves
41
- under you while you work; name the delta from the previous scan rather than silently folding new work
42
- into old buckets. If the session touched or linked to an entity note, run `ctxr rollup stale --for <entity>`
43
- (or a plain `ctxr rollup stale` when unsure which), and offer `ctxr-rollup` on anything flagged — never run a
44
- rollup unasked; the operator decides.
44
+ `<session>` below is the session worktree's directory name, as `ctxr session list` prints it.
45
+
46
+ 1. Re-scan (mandatory — never replay a plan from an earlier snapshot): `ctxr session status <session>`, with
47
+ `--diff` when you need the changes themselves. It fetches `__DEFAULT_BRANCH__` and reports what is
48
+ uncommitted, the diff stat of committed and uncommitted work, and the commits ahead of
49
+ `origin/__DEFAULT_BRANCH__`. State moves under you while you work; name the delta from the previous scan
50
+ rather than silently folding new work into old buckets. If the session touched or linked to an entity
51
+ note, run `ctxr rollup stale --for <entity>` (or a plain `ctxr rollup stale` when unsure which), and
52
+ offer `ctxr-rollup` on anything flagged — never run a rollup unasked; the operator decides.
45
53
  2. Capture pass: run `ctxr-session-capture` exactly once here — closing a session is itself a capture
46
54
  trigger; do not fire it again after submitting.
47
- 3. Stage surgically: `git add <paths>`, never `git add -A`; confirm with `git status --short` that the
48
- staged set matches the intended unit. Derived artifacts under the cache paths never stage.
49
- 4. One coherent unit per pull request. If the session produced two disjoint units, say so and ask
50
- whether to split.
51
- 5. Validate: run `ctxr doctor` (store scope, not `--staged` — a session's job is to leave the whole store
52
- healthy, not merely pass one commit's gate). Fix a failure; never bypass it, and never proceed past it.
53
- 6. Commit: `git commit -m "<message>"`, describing the unit staged in step 3.
54
- 7. Name the branch: `<prefix>/<short-slug>`, with a prefix from `skill/` `feat/` `project/` `notes/` `chore/`
55
- `refactor/` `fix/` unless this store's conventions say otherwise. If it still carries a generated name,
56
- `git branch -m "<name>"` before pushing — never
57
- let a generated name reach the forge. A name given at `ctxr session start` does not settle this: it
58
- named a worktree when the work began, and what ships is decided here.
59
- 8. Before you open the pull request, run `ctxr publish urls --since origin/__DEFAULT_BRANCH__ --json`.
60
- When it lists a page, add a **Pages** section to the body. The section is a table with one row per page.
61
- Each row gives the page's status, its lifespan (permanent or disposable), its preview address, and its
62
- published address. Copy every value from the report, never rebuilt by hand. Why: the report reads the
63
- change set, and a guess from the path can be wrong. Mark the old address of a moved page as one that
64
- stops working. Mark the address of a removed page the same way. Under the table, state the condition
65
- one time: a preview resolves only while this pull request is open and the server can see this
66
- session's worktree, and the published address resolves when the served checkout has the landed commit.
67
- When the report has no preview, leave out that column. When it says that no base URL is declared, say
68
- that the addresses are bare routes. When it lists no page, add no section.
69
- Then run `ctxr graph diff --since origin/__DEFAULT_BRANCH__ --format markdown`. Paste its output into the
70
- body verbatim, as the **Knowledge changes** section. Do not add, remove, or change a word of it. Why: you
71
- wrote the change, and the reviewer checks your account against this record, which the command wrote.
72
- Always paste the block, also when it says that the range changes no note. Why: a missing section looks the
73
- same as a skipped step. The block is the record of the change. The review address in the block is a
74
- convenience only. Why: it resolves only while this session's worktree exists and the server can reach it.
75
- Then run `git push -u origin "<branch>"`. Then run `gh pr create --base __DEFAULT_BRANCH__ --title "<title>"
76
- --body "<why / what changed / route / verification / follow-ups>"`. `gh` reads its own credential file, so
55
+ 3. One coherent unit per pull request. `ctxr session submit` commits every change in the worktree outside
56
+ the derived paths, so what the re-scan shows is what ships. If the session produced two disjoint units,
57
+ say so and ask whether to split; anything that should not ship leaves the worktree before step 7. Derived
58
+ artifacts under the cache paths never ship, and the command refuses an index that holds one, whoever
59
+ staged it.
60
+ 4. Validate: run `ctxr doctor` (store scope, not `--staged` — a session's job is to leave the whole store
61
+ healthy, not merely pass one commit's gate). `ctxr session submit` runs the same check as its gate and
62
+ commits nothing when it fails. Fix a failure; never bypass it, and never proceed past it.
63
+ 5. Name the branch: `<prefix>/<short-slug>`, with a prefix from `skill/` `feat/` `project/` `notes/` `chore/`
64
+ `refactor/` `fix/` unless this store's conventions say otherwise. Never let a generated name reach the
65
+ forge: the name you pass at step 7 replaces it. A name given at `ctxr session start` does not settle
66
+ this: it named a worktree when the work began, and what ships is decided here.
67
+ 6. Write the commit message, the title and the body. The body answers
68
+ `<why / what changed / route / verification / follow-ups>`. Write the why so that a reviewer does not
69
+ have to find it again. Do not write a Knowledge changes section or a Pages section: the command appends
70
+ both after your body — the Pages table from the change set's page report when a page changed, and the
71
+ Knowledge changes block always — and refuses a body that already carries either heading. Why: you wrote
72
+ the change, and the reviewer checks your account against the record the command wrote.
73
+ 7. Submit: `ctxr session submit <session> --branch "<name>" --message "<message>" --title "<title>"
74
+ --body-file <file>` (or `--body "<body>"` for a short one). It stages, runs the gate, commits, renames the
75
+ branch, pushes, and opens the pull request against `__DEFAULT_BRANCH__`, or updates the one already open
76
+ for the branch. Do not stop to confirm first. Why: the request to submit is the consent, and the
77
+ command's store-scope check is the gate on this path. Never push or open the pull request any other way;
77
78
  never export or interpolate a token, and never rewrite the origin's URL. Why: that URL can hold a
78
- credential. The body answers those five questions and carries the sections above. Write the why so that a
79
- reviewer does not have to find it again. Do not stop to confirm first. Why: the request to submit is the
80
- consent for both, and `ctxr doctor` in step 5 is the gate on this path. If `gh` has no reachable GitHub
81
- remote for this repository, `git push` still succeeds alone. Then report the pushed branch, and give the
82
- operator the manual pull-request instructions. Do not retry `gh`.
83
- 9. Verify before any retry: a transport error can arrive AFTER the push or the pull-request open already
84
- succeeded. Before retrying anything, `git ls-remote origin <branch>` and `gh pr list --head <branch>` —
85
- never replay a push or a pull-request open blindly.
86
- 10. Hand off: report the pull request's number and its url, and name what landing it takes —
87
- `ctxr-land`, targeting that number — so the next step is one word away instead of something to
88
- reconstruct later. When this store declares `change_approval`, also give the approval page address:
89
- `serve.base_url`, then `/approve/`, then the session worktree's directory name and a `/`. Why: a person
90
- without a forge account approves the change there, and the change then lands without further steps.
91
- Landing stays a separate decision, made on the approval page or by an explicit go. Never run it from here.
79
+ credential.
80
+ Exit `0` means the pull request is open. Exit `3` names the step that stopped it, with everything before
81
+ that step left in place: a failed gate (fix it and run the same submit again), a derived path in the
82
+ index (unstage it), a rebase in progress (finish it with the conflict playbook), or a push or forge
83
+ failure. Exit `2` is a refused argument: a branch name the default branch or another branch carries, or
84
+ a body with a generated heading. If the forge cannot be reached, report the pushed branch and give the
85
+ operator the manual pull-request instructions.
86
+ 8. Verify before any retry: a transport error can arrive AFTER the push or the pull-request open already
87
+ succeeded. `ctxr session submit` reads the remote branch and the forge's pull requests for it before it
88
+ pushes or opens anything, so running the same submit again is the retry, and it publishes once.
89
+ Submitting a session again after requested changes works the same way: a further commit on the same
90
+ branch, and the same pull request updated — keep `--branch` as it was.
91
+ 9. Hand off. Report each pull request's number and its url. Name what lands it: `ctxr-land`, with that
92
+ number, or with the session when the session has a part in more than one store. Why: the next step is
93
+ then one word away, not something to reconstruct later. When this store declares `change_approval`,
94
+ also give the approval page address that the command reports.
95
+ It is `serve.base_url`, then `/approve/`, then the session worktree's directory name and a `/`.
96
+ For a session with parts in several stores, give one address only: the session's page in the store
97
+ where it started. Every part's submit reports that same address. Why: a person without a forge account
98
+ approves the change there. One decision covers every part, and the change then lands without further
99
+ steps. Landing stays a separate decision, made on the approval page or by an explicit go.
100
+ Never run it from here.