@cluesmith/codev 3.3.0 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/dashboard-dist/assets/{index-JCbkpAcV.js → index-D3XaKpZW.js} +45 -43
  2. package/dashboard-dist/assets/index-D3XaKpZW.js.map +1 -0
  3. package/dashboard-dist/index.html +1 -1
  4. package/dist/agent-farm/cli.d.ts.map +1 -1
  5. package/dist/agent-farm/cli.js +99 -36
  6. package/dist/agent-farm/cli.js.map +1 -1
  7. package/dist/agent-farm/commands/reset/constants.d.ts +119 -1
  8. package/dist/agent-farm/commands/reset/constants.d.ts.map +1 -1
  9. package/dist/agent-farm/commands/reset/constants.js +119 -1
  10. package/dist/agent-farm/commands/reset/constants.js.map +1 -1
  11. package/dist/agent-farm/commands/reset/context.d.ts +20 -2
  12. package/dist/agent-farm/commands/reset/context.d.ts.map +1 -1
  13. package/dist/agent-farm/commands/reset/context.js +44 -3
  14. package/dist/agent-farm/commands/reset/context.js.map +1 -1
  15. package/dist/agent-farm/commands/reset/index.d.ts +7 -7
  16. package/dist/agent-farm/commands/reset/index.js +8 -8
  17. package/dist/agent-farm/commands/reset/index.js.map +1 -1
  18. package/dist/agent-farm/commands/reset/receipt.d.ts +1 -1
  19. package/dist/agent-farm/commands/reset/receipt.d.ts.map +1 -1
  20. package/dist/agent-farm/commands/reset/receipt.js +19 -8
  21. package/dist/agent-farm/commands/reset/receipt.js.map +1 -1
  22. package/dist/agent-farm/commands/reset/reorient.d.ts +1 -1
  23. package/dist/agent-farm/commands/reset/reorient.d.ts.map +1 -1
  24. package/dist/agent-farm/commands/reset/reorient.js +12 -5
  25. package/dist/agent-farm/commands/reset/reorient.js.map +1 -1
  26. package/dist/agent-farm/commands/reset/self.d.ts +335 -0
  27. package/dist/agent-farm/commands/reset/self.d.ts.map +1 -0
  28. package/dist/agent-farm/commands/reset/self.js +636 -0
  29. package/dist/agent-farm/commands/reset/self.js.map +1 -0
  30. package/dist/agent-farm/commands/reset.d.ts +18 -3
  31. package/dist/agent-farm/commands/reset.d.ts.map +1 -1
  32. package/dist/agent-farm/commands/reset.js +55 -33
  33. package/dist/agent-farm/commands/reset.js.map +1 -1
  34. package/dist/agent-farm/commands/self-refresh.d.ts +60 -0
  35. package/dist/agent-farm/commands/self-refresh.d.ts.map +1 -0
  36. package/dist/agent-farm/commands/self-refresh.js +506 -0
  37. package/dist/agent-farm/commands/self-refresh.js.map +1 -0
  38. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  39. package/dist/agent-farm/commands/send.js +27 -2
  40. package/dist/agent-farm/commands/send.js.map +1 -1
  41. package/dist/agent-farm/commands/spawn-worktree.js +1 -1
  42. package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
  43. package/dist/agent-farm/commands/spawn.d.ts +7 -0
  44. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  45. package/dist/agent-farm/commands/spawn.js +13 -1
  46. package/dist/agent-farm/commands/spawn.js.map +1 -1
  47. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  48. package/dist/agent-farm/commands/tower.js +13 -0
  49. package/dist/agent-farm/commands/tower.js.map +1 -1
  50. package/dist/agent-farm/lib/reconnect-backoff.d.ts +10 -0
  51. package/dist/agent-farm/lib/reconnect-backoff.d.ts.map +1 -1
  52. package/dist/agent-farm/lib/reconnect-backoff.js +10 -0
  53. package/dist/agent-farm/lib/reconnect-backoff.js.map +1 -1
  54. package/dist/agent-farm/lib/tower-client.d.ts +12 -0
  55. package/dist/agent-farm/lib/tower-client.d.ts.map +1 -1
  56. package/dist/agent-farm/lib/tower-client.js +12 -0
  57. package/dist/agent-farm/lib/tower-client.js.map +1 -1
  58. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
  59. package/dist/agent-farm/lib/tunnel-client.js +7 -3
  60. package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
  61. package/dist/agent-farm/servers/canvas-relay.d.ts +60 -0
  62. package/dist/agent-farm/servers/canvas-relay.d.ts.map +1 -0
  63. package/dist/agent-farm/servers/canvas-relay.js +376 -0
  64. package/dist/agent-farm/servers/canvas-relay.js.map +1 -0
  65. package/dist/agent-farm/servers/gate-profiles.d.ts +1 -1
  66. package/dist/agent-farm/servers/gate-profiles.js +1 -1
  67. package/dist/agent-farm/servers/mailbox-delivery.d.ts +141 -13
  68. package/dist/agent-farm/servers/mailbox-delivery.d.ts.map +1 -1
  69. package/dist/agent-farm/servers/mailbox-delivery.js +172 -16
  70. package/dist/agent-farm/servers/mailbox-delivery.js.map +1 -1
  71. package/dist/agent-farm/servers/mailbox-wiring.d.ts +24 -2
  72. package/dist/agent-farm/servers/mailbox-wiring.d.ts.map +1 -1
  73. package/dist/agent-farm/servers/mailbox-wiring.js +92 -32
  74. package/dist/agent-farm/servers/mailbox-wiring.js.map +1 -1
  75. package/dist/agent-farm/servers/message-write.d.ts +58 -18
  76. package/dist/agent-farm/servers/message-write.d.ts.map +1 -1
  77. package/dist/agent-farm/servers/message-write.js +73 -28
  78. package/dist/agent-farm/servers/message-write.js.map +1 -1
  79. package/dist/agent-farm/servers/overview.d.ts +19 -0
  80. package/dist/agent-farm/servers/overview.d.ts.map +1 -1
  81. package/dist/agent-farm/servers/overview.js +78 -1
  82. package/dist/agent-farm/servers/overview.js.map +1 -1
  83. package/dist/agent-farm/servers/render-gate.d.ts +13 -0
  84. package/dist/agent-farm/servers/render-gate.d.ts.map +1 -1
  85. package/dist/agent-farm/servers/render-gate.js +21 -0
  86. package/dist/agent-farm/servers/render-gate.js.map +1 -1
  87. package/dist/agent-farm/servers/session-submit.d.ts +221 -28
  88. package/dist/agent-farm/servers/session-submit.d.ts.map +1 -1
  89. package/dist/agent-farm/servers/session-submit.js +334 -43
  90. package/dist/agent-farm/servers/session-submit.js.map +1 -1
  91. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
  92. package/dist/agent-farm/servers/tower-routes.js +269 -64
  93. package/dist/agent-farm/servers/tower-routes.js.map +1 -1
  94. package/dist/agent-farm/servers/tower-server.js +25 -3
  95. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  96. package/dist/agent-farm/servers/tower-terminals.d.ts +6 -0
  97. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
  98. package/dist/agent-farm/servers/tower-terminals.js +30 -5
  99. package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
  100. package/dist/agent-farm/servers/tower-tunnel.d.ts.map +1 -1
  101. package/dist/agent-farm/servers/tower-tunnel.js +14 -0
  102. package/dist/agent-farm/servers/tower-tunnel.js.map +1 -1
  103. package/dist/agent-farm/servers/tower-websocket.d.ts +6 -1
  104. package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -1
  105. package/dist/agent-farm/servers/tower-websocket.js +90 -37
  106. package/dist/agent-farm/servers/tower-websocket.js.map +1 -1
  107. package/dist/agent-farm/types.d.ts +37 -2
  108. package/dist/agent-farm/types.d.ts.map +1 -1
  109. package/dist/agent-farm/utils/harness.d.ts +3 -3
  110. package/dist/agent-farm/utils/harness.js +1 -1
  111. package/dist/agent-farm/utils/message-format.d.ts +67 -3
  112. package/dist/agent-farm/utils/message-format.d.ts.map +1 -1
  113. package/dist/agent-farm/utils/message-format.js +127 -14
  114. package/dist/agent-farm/utils/message-format.js.map +1 -1
  115. package/dist/agent-farm/utils/server-utils.d.ts +86 -4
  116. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  117. package/dist/agent-farm/utils/server-utils.js +303 -5
  118. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  119. package/dist/commands/porch/context-refresh.d.ts +181 -0
  120. package/dist/commands/porch/context-refresh.d.ts.map +1 -0
  121. package/dist/commands/porch/context-refresh.js +270 -0
  122. package/dist/commands/porch/context-refresh.js.map +1 -0
  123. package/dist/commands/porch/index.d.ts +6 -0
  124. package/dist/commands/porch/index.d.ts.map +1 -1
  125. package/dist/commands/porch/index.js +46 -0
  126. package/dist/commands/porch/index.js.map +1 -1
  127. package/dist/commands/porch/next.d.ts +0 -9
  128. package/dist/commands/porch/next.d.ts.map +1 -1
  129. package/dist/commands/porch/next.js +133 -4
  130. package/dist/commands/porch/next.js.map +1 -1
  131. package/dist/commands/porch/protocol.d.ts.map +1 -1
  132. package/dist/commands/porch/protocol.js +102 -0
  133. package/dist/commands/porch/protocol.js.map +1 -1
  134. package/dist/commands/porch/types.d.ts +61 -0
  135. package/dist/commands/porch/types.d.ts.map +1 -1
  136. package/dist/lib/forge-contracts.d.ts +9 -0
  137. package/dist/lib/forge-contracts.d.ts.map +1 -1
  138. package/dist/lib/forge-contracts.js.map +1 -1
  139. package/dist/lib/github.d.ts +15 -1
  140. package/dist/lib/github.d.ts.map +1 -1
  141. package/dist/lib/github.js +17 -0
  142. package/dist/lib/github.js.map +1 -1
  143. package/dist/lib/self-refresh-invocation.d.ts +60 -0
  144. package/dist/lib/self-refresh-invocation.d.ts.map +1 -0
  145. package/dist/lib/self-refresh-invocation.js +64 -0
  146. package/dist/lib/self-refresh-invocation.js.map +1 -0
  147. package/dist/lib/test-env.d.ts +50 -0
  148. package/dist/lib/test-env.d.ts.map +1 -1
  149. package/dist/lib/test-env.js +76 -0
  150. package/dist/lib/test-env.js.map +1 -1
  151. package/dist/terminal/attach-replay.d.ts +39 -0
  152. package/dist/terminal/attach-replay.d.ts.map +1 -0
  153. package/dist/terminal/attach-replay.js +76 -0
  154. package/dist/terminal/attach-replay.js.map +1 -0
  155. package/dist/terminal/pty-manager.d.ts.map +1 -1
  156. package/dist/terminal/pty-manager.js +35 -17
  157. package/dist/terminal/pty-manager.js.map +1 -1
  158. package/dist/terminal/pty-session.d.ts +81 -12
  159. package/dist/terminal/pty-session.d.ts.map +1 -1
  160. package/dist/terminal/pty-session.js +113 -25
  161. package/dist/terminal/pty-session.js.map +1 -1
  162. package/dist/terminal/session-screen.d.ts +25 -0
  163. package/dist/terminal/session-screen.d.ts.map +1 -1
  164. package/dist/terminal/session-screen.js +53 -8
  165. package/dist/terminal/session-screen.js.map +1 -1
  166. package/package.json +10 -7
  167. package/scripts/forge/gitea/pr-view.sh +4 -1
  168. package/scripts/forge/github/pr-view.sh +1 -1
  169. package/scripts/forge/gitlab/pr-view.sh +4 -1
  170. package/skeleton/.claude/skills/afx/SKILL.md +54 -0
  171. package/skeleton/.claude/skills/arch-save/SKILL.md +8 -2
  172. package/skeleton/.claude/skills/builder-refresh/SKILL.md +138 -0
  173. package/skeleton/.codex/skills/afx/SKILL.md +54 -0
  174. package/skeleton/.codex/skills/arch-save/SKILL.md +8 -2
  175. package/skeleton/.codex/skills/builder-refresh/SKILL.md +138 -0
  176. package/skeleton/protocol-schema.json +17 -0
  177. package/skeleton/protocols/air/protocol.json +1 -1
  178. package/skeleton/protocols/aspir/protocol.json +5 -1
  179. package/skeleton/protocols/bugfix/protocol.json +1 -1
  180. package/skeleton/protocols/experiment/protocol.json +1 -1
  181. package/skeleton/protocols/maintain/protocol.json +1 -1
  182. package/skeleton/protocols/pir/builder-prompt.md +3 -2
  183. package/skeleton/protocols/pir/prompts/implement.md +1 -1
  184. package/skeleton/protocols/pir/prompts/plan.md +1 -1
  185. package/skeleton/protocols/pir/prompts/review.md +1 -1
  186. package/skeleton/protocols/pir/protocol.json +1 -1
  187. package/skeleton/protocols/pir/protocol.md +4 -4
  188. package/skeleton/protocols/protocol-schema.json +17 -0
  189. package/skeleton/protocols/research/protocol.json +1 -1
  190. package/skeleton/protocols/spike/protocol.json +1 -1
  191. package/skeleton/protocols/spir/protocol.json +5 -1
  192. package/skeleton/resources/commands/agent-farm.md +14 -11
  193. package/skeleton/roles/builder.md +3 -4
  194. package/templates/3d-viewer.html +32 -4
  195. package/templates/open.html +58 -12
  196. package/templates/tower.html +26 -7
  197. package/templates/vendor/three-3MFLoader.js +1478 -0
  198. package/templates/vendor/three-STLLoader.js +410 -0
  199. package/templates/vendor/three-TrackballControls.js +828 -0
  200. package/templates/vendor/three-fflate.module.js +2474 -0
  201. package/templates/vendor/three.module.js +53044 -0
  202. package/dashboard-dist/assets/index-JCbkpAcV.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"session-screen.d.ts","sourceRoot":"","sources":["../../src/terminal/session-screen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAOH,OAAO,KAAK,EAAE,QAAQ,IAAI,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAcpE,wFAAwF;AACxF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,KAAK,CAAS;IAItB,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,QAAQ,CAAS;gBAEb,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAMtC;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKxB;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAOxC;;;;;;;;;;;;;;;;OAgBG;IACG,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAMjC,0DAA0D;IAC1D,IAAI,IAAI,IAAI,MAAM,CAEjB;IACD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,0FAA0F;IAC1F,OAAO,IAAI,IAAI;CAUhB"}
1
+ {"version":3,"file":"session-screen.d.ts","sourceRoot":"","sources":["../../src/terminal/session-screen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAQH,OAAO,KAAK,EAAE,QAAQ,IAAI,gBAAgB,EAAkB,MAAM,iBAAiB,CAAC;AAsBpF,wFAAwF;AACxF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsC;IACjE,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,KAAK,CAAS;IAItB,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,QAAQ,CAAS;gBAEb,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAWtC;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKxB;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAOxC;;;;;;;;;;;;;;;;OAgBG;IACG,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAMjC;;;;;;;;;;;;;OAaG;IACH,SAAS,IAAI,MAAM;IAKnB;;;;;;;OAOG;IACH,IAAI,UAAU,IAAI,QAAQ,GAAG,WAAW,CAEvC;IAED,0DAA0D;IAC1D,IAAI,IAAI,IAAI,MAAM,CAEjB;IACD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,0FAA0F;IAC1F,OAAO,IAAI,IAAI;CAUhB"}
@@ -40,19 +40,29 @@
40
40
  // and its named exports are not statically analyzable, so a native-node ESM
41
41
  // `import { Terminal }` throws "Named export 'Terminal' not found" under the compiled
42
42
  // dist (see the identical note in render-gate.ts). Default-import the module object.
43
+ // `@xterm/addon-serialize` ships the same way (same repo and release train).
43
44
  import xtermHeadless from '@xterm/headless';
45
+ import xtermSerialize from '@xterm/addon-serialize';
44
46
  const { Terminal } = xtermHeadless;
47
+ const { SerializeAddon } = xtermSerialize;
45
48
  /**
46
- * Scrollback retained by the mirror. The gate reads ONLY the current viewport
47
- * (`viewportY … viewportY + rows`), never scrollback, so this can be modest — it exists
48
- * only so a transient scroll doesn't momentarily drop viewport lines during reflow. Kept
49
- * small to bound per-session memory (one Terminal per live session): at ~200 lines it is a
50
- * few hundred KB even at a wide geometry. The viewport a classify sees is identical for any
51
- * scrollback ≥ rows, so this never changes a verdict (asserted by the production-path tests).
49
+ * Scrollback retained by the mirror, now serving two consumers (PIR #1354):
50
+ *
51
+ * - The render gate reads ONLY the current viewport (`viewportY … viewportY + rows`),
52
+ * never scrollback, and its verdict is identical for any scrollback ≥ rows (asserted
53
+ * by the production-path tests) — so for the gate this value is a free variable.
54
+ * - {@link SessionScreen.serialize} is the viewer-attach replay payload, and its
55
+ * scrollback IS the history a reconnecting viewer gets back. 1000 matches the ring
56
+ * buffer's default line capacity (`ringBufferLines`), so snapshot replay does not
57
+ * regress history depth versus the raw-ring replay it replaces.
58
+ *
59
+ * Memory: measured ~509 KB per fully-scrolled 200x50 terminal at 1000 lines (~215 KB
60
+ * at the previous 200) — the bounded per-session budget the plan commits to.
52
61
  */
53
- const GATE_SCROLLBACK = 200;
62
+ const SCREEN_SCROLLBACK = 1000;
54
63
  export class SessionScreen {
55
64
  term;
65
+ serializer;
56
66
  _cols;
57
67
  _rows;
58
68
  // Promise of the most recently issued write's parse completion. `@xterm/headless`
@@ -63,7 +73,12 @@ export class SessionScreen {
63
73
  constructor(cols, rows) {
64
74
  this._cols = cols;
65
75
  this._rows = rows;
66
- this.term = new Terminal({ cols, rows, allowProposedApi: true, scrollback: GATE_SCROLLBACK });
76
+ this.term = new Terminal({ cols, rows, allowProposedApi: true, scrollback: SCREEN_SCROLLBACK });
77
+ this.serializer = new SerializeAddon();
78
+ // The addon's typings bind to the full `@xterm/xterm` Terminal; its runtime touches
79
+ // only the buffer/parser surface the headless build shares (proven by the round-trip
80
+ // suite), so the declared-type gap is bridged explicitly here.
81
+ this.term.loadAddon(this.serializer);
67
82
  }
68
83
  /**
69
84
  * Fold one chunk of PTY output into the screen. Called at the session's single output
@@ -112,6 +127,36 @@ export class SessionScreen {
112
127
  await this.pending;
113
128
  return { term: this.term, cols: this._cols, rows: this._rows };
114
129
  }
130
+ /**
131
+ * Serialize the current screen state (both buffers, colors/attrs, cursor, alt-screen
132
+ * mode, plus up to {@link SCREEN_SCROLLBACK} lines of history) back into a terminal
133
+ * byte stream a client can replay — the O(screen) viewer-attach payload (PIR #1354).
134
+ *
135
+ * SYNCHRONOUS by design: it reads whatever is parsed into the grid right now.
136
+ * Callers wanting a coherent frame must flush first ({@link read}) and must call this
137
+ * with no intervening `await` afterward — the same no-interleave discipline the render
138
+ * gate's classifier relies on. `PtySession.replaySnapshot` owns that protocol.
139
+ *
140
+ * Throws whatever the addon throws; the caller maps failures to the raw-replay
141
+ * fallback. After {@link dispose} the term is freed — return '' rather than touching it
142
+ * (callers treat an empty snapshot for a non-empty session as a fallback signal).
143
+ */
144
+ serialize() {
145
+ if (this.disposed)
146
+ return '';
147
+ return this.serializer.serialize({ scrollback: SCREEN_SCROLLBACK });
148
+ }
149
+ /**
150
+ * Which buffer the emulated app is currently in. 'alternate' identifies the
151
+ * full-screen-TUI case whose delta resume is unservable from the line-based ring
152
+ * (`RingBuffer.getSince` returns [] for a caught-up client), so the attach path
153
+ * routes it to {@link serialize} instead. Read without flushing: worst case the
154
+ * routing decision lags the last few unparsed bytes, and both routes remain correct
155
+ * (the raw path keeps its client-nudge recovery).
156
+ */
157
+ get bufferType() {
158
+ return this.term.buffer.active.type;
159
+ }
115
160
  /** Current mirror geometry (matches the live session). */
116
161
  get cols() {
117
162
  return this._cols;
@@ -1 +1 @@
1
- {"version":3,"file":"session-screen.js","sourceRoot":"","sources":["../../src/terminal/session-screen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,wFAAwF;AACxF,4EAA4E;AAC5E,sFAAsF;AACtF,qFAAqF;AACrF,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAG5C,MAAM,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC;AAEnC;;;;;;;GAOG;AACH,MAAM,eAAe,GAAG,GAAG,CAAC;AAS5B,MAAM,OAAO,aAAa;IACP,IAAI,CAAmB;IAChC,KAAK,CAAS;IACd,KAAK,CAAS;IACtB,kFAAkF;IAClF,kFAAkF;IAClF,wFAAwF;IAChF,OAAO,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3C,QAAQ,GAAG,KAAK,CAAC;IAEzB,YAAY,IAAY,EAAE,IAAY;QACpC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,IAAI,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,CAAC;IAChG,CAAC;IAED;;;;;;OAMG;IACH,IAAI,CAAC,IAAY;QACf,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACxF,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QAClF,MAAM,IAAI,CAAC,OAAO,CAAC;QACnB,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IACjE,CAAC;IAED,0DAA0D;IAC1D,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IACD,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,0FAA0F;IAC1F,OAAO;QACL,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACpB,8FAA8F;QAC9F,kGAAkG;QAClG,+FAA+F;QAC/F,+FAA+F;QAC/F,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IACnC,CAAC;CACF"}
1
+ {"version":3,"file":"session-screen.js","sourceRoot":"","sources":["../../src/terminal/session-screen.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cluesmith/codev",
3
- "version": "3.3.0",
3
+ "version": "3.3.2",
4
4
  "description": "Codev CLI - AI-assisted software development framework",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,6 +24,7 @@
24
24
  "@anthropic-ai/claude-agent-sdk": "^0.2.41",
25
25
  "@google/genai": "^1.0.0",
26
26
  "@openai/codex-sdk": "^0.146.0",
27
+ "@xterm/addon-serialize": "^0.14.0",
27
28
  "@xterm/headless": "^6.0.0",
28
29
  "better-sqlite3": "^12.10.0",
29
30
  "chalk": "^5.3.0",
@@ -34,24 +35,25 @@
34
35
  "open": "^10.1.0",
35
36
  "tree-kill": "^1.2.2",
36
37
  "ws": "^8.18.0",
37
- "@cluesmith/codev-core": "3.3.0",
38
- "@cluesmith/codev-sdk": "3.3.0"
38
+ "@cluesmith/codev-core": "3.3.2",
39
+ "@cluesmith/codev-sdk": "3.3.2",
40
+ "@cluesmith/codev-types": "3.3.2"
39
41
  },
40
42
  "devDependencies": {
41
43
  "@playwright/test": "^1.58.0",
42
- "@xterm/xterm": "^5.5.0",
43
44
  "@types/better-sqlite3": "^7.6.13",
44
45
  "@types/js-yaml": "^4.0.9",
45
46
  "@types/node": "^22.10.1",
46
47
  "@types/ws": "^8.18.1",
47
48
  "@vitest/coverage-v8": "^4.0.18",
49
+ "@xterm/xterm": "^5.5.0",
48
50
  "jsdom": "^28.1.0",
49
51
  "playwright": "^1.58.0",
52
+ "three": "0.160.0",
50
53
  "tsx": "^4.19.2",
51
54
  "typescript": "^6.0.3",
52
55
  "vitest": "^4.0.15",
53
- "@cluesmith/codev-web": "0.0.0",
54
- "@cluesmith/codev-types": "3.3.0"
56
+ "@cluesmith/codev-web": "0.0.0"
55
57
  },
56
58
  "engines": {
57
59
  "node": ">=20.0.0"
@@ -73,7 +75,8 @@
73
75
  },
74
76
  "scripts": {
75
77
  "clean": "rm -rf dist",
76
- "build": "pnpm --filter \"@cluesmith/codev^...\" build && pnpm clean && tsc && pnpm copy-dashboard && pnpm copy-skeleton",
78
+ "build": "pnpm --filter \"@cluesmith/codev^...\" build && pnpm clean && tsc && pnpm bundle-assets",
79
+ "bundle-assets": "pnpm copy-dashboard && pnpm copy-skeleton && node ./scripts/copy-three.mjs",
77
80
  "copy-dashboard": "rm -rf dashboard-dist && cp -r ../../apps/web/dist dashboard-dist",
78
81
  "dev:dashboard": "cd ../../apps/web && pnpm dev",
79
82
  "copy-skeleton": "rm -rf skeleton && cp -r ../../codev-skeleton skeleton",
@@ -1,3 +1,6 @@
1
1
  #!/bin/sh
2
2
  # Forge concept: pr-view (Gitea via tea CLI)
3
- exec tea pulls view "$CODEV_PR_NUMBER" --output json
3
+ # Sets `url` to the PR's browser page (`html_url`). Gitea's own `url` field is
4
+ # the API endpoint (would render raw JSON in a browser), so we prefer `html_url`
5
+ # and fall back to the existing `url` only if `html_url` is absent.
6
+ tea pulls view "$CODEV_PR_NUMBER" --output json | jq '.url = (.html_url // .url)'
@@ -7,5 +7,5 @@ if [ "$CODEV_INCLUDE_COMMENTS" = "1" ]; then
7
7
  exec gh pr view "$CODEV_PR_NUMBER" --comments
8
8
  else
9
9
  exec gh pr view "$CODEV_PR_NUMBER" \
10
- --json title,body,state,author,baseRefName,headRefName,additions,deletions
10
+ --json title,body,state,url,author,baseRefName,headRefName,additions,deletions
11
11
  fi
@@ -1,3 +1,6 @@
1
1
  #!/bin/sh
2
2
  # Forge concept: pr-view (GitLab via glab CLI)
3
- exec glab mr view "$CODEV_PR_NUMBER" --output json
3
+ # Adds a `url` field mapped from GitLab's `web_url` (the MR's browser page);
4
+ # all other fields glab emits are passed through unchanged. `. + {url: …}` is
5
+ # non-destructive — if `web_url` is absent, `url` is null (optional by contract).
6
+ glab mr view "$CODEV_PR_NUMBER" --output json | jq '. + {url: .web_url}'
@@ -69,6 +69,60 @@ afx send 0042 "PR approved, please merge"
69
69
  afx send 0585 "check the test output" --file /tmp/test-results.txt
70
70
  ```
71
71
 
72
+ ## afx interrupt
73
+
74
+ Sends an ESC keystroke to a builder's PTY — the only thing that reaches it **mid-turn**.
75
+
76
+ ```
77
+ afx interrupt <builder>
78
+ ```
79
+
80
+ | Flag | Description |
81
+ |------|-------------|
82
+ | `--no-enter` | Send ESC alone, without the trailing Enter |
83
+
84
+ A builder chaining foreground waits inside one turn queues every `afx send` unread — including your order
85
+ to stop. ESC ends the turn so the queue processes. Distinct from `afx send --interrupt` (Ctrl+C).
86
+
87
+ ```bash
88
+ afx interrupt 0042
89
+ afx send 0042 "That producer died — stop waiting and report."
90
+ ```
91
+
92
+ ## afx refresh
93
+
94
+ Refreshes a builder's context: save working state → `/clear` → re-orient. Use when a builder's context window
95
+ is exhausted; `afx spawn --resume` reattaches the *same* conversation and does **not** give it a fresh one.
96
+
97
+ ```
98
+ afx refresh <builder>
99
+ ```
100
+
101
+ | Flag | Description |
102
+ |------|-------------|
103
+ | `--dry-run` | Print what would be sent; write nothing to the builder |
104
+ | `--note <text>` | Extra context appended to the re-orientation |
105
+ | `--file <path>` | Append file content (48KB max, read from *your* filesystem) |
106
+ | `--interrupt-first` | ESC before the save request, for a builder already wedged |
107
+ | `--mode <strict\|soft>` | Override mode if it cannot be detected |
108
+ | `--timeout <seconds>` | Wait for the save-state receipt (default 300) |
109
+ | `--min-bytes <n>` | Minimum state-file size to accept (default 1000) |
110
+ | `--quiet-window <ms>` | Terminal silence counting as turn-ended (default 1500) |
111
+
112
+ Every gate fails safe: if the state file never arrives, carries a stale nonce, is a stub, is still being
113
+ written, or the builder will not go quiet, the command **aborts without clearing** and exits non-zero,
114
+ naming the gate. Requires a harness with in-session context clearing (Claude Code); others abort loudly.
115
+
116
+ `afx reset` is a deprecated alias: it still works, prints a one-line notice to stderr, and will be removed
117
+ in a future release.
118
+
119
+ ```bash
120
+ afx refresh 0042 --dry-run # inspect first — touches nothing
121
+ afx refresh 0042
122
+ afx refresh 0042 --note "PR #90 merged while you were mid-phase. Rebase first."
123
+ afx refresh 0042 --interrupt-first # builder is wedged mid-turn
124
+ ```
125
+
72
126
  ## afx cleanup
73
127
 
74
128
  Removes a builder's worktree and branch after work is done.
@@ -107,8 +107,14 @@ guarantee about the clear's completion. If the timing is wrong the re-init arriv
107
107
  wrong moment, which costs one manual message (see below) and nothing else. That is the
108
108
  whole reason this cycle can be built on a delay rather than on machinery.
109
109
 
110
- Delayed sends are **not persisted** — if Tower restarts inside the window, the message is
111
- dropped. That is recoverable; see below.
110
+ Delayed sends **are persisted**. The body is written to the durable mailbox at request time
111
+ with a `not_before` timestamp, so Tower keeps no timer for it and a restart inside the window
112
+ does not lose it — the re-init is delivered once the delay passes. The one thing a restart does
113
+ drop is the Ctrl+C nudge of a delayed `--interrupt`, which this cycle does not use.
114
+
115
+ That removes a failure mode this section used to warn about, but not the need for the recovery
116
+ path below: delivery still waits on an empty prompt, so a re-init can arrive late rather than
117
+ never.
112
118
 
113
119
  **If this send fails, do not end your turn.** Step 4 queued the `/clear`, but it does not
114
120
  take effect until your turn ends — so at this moment you still have your full context and
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: builder-refresh
3
+ description: Refresh a builder's own context at a protocol boundary — save working state, verify it, clear, and re-orient. Use when porch emits a context-refresh task, or the builder is told to "refresh your context" / "run a self-refresh". A builder does not invoke this autonomously mid-task; porch chooses the moment. Counterpart to the architect's /arch-save.
4
+ argument-hint: "(none — porch's refresh task supplies the commands)"
5
+ ---
6
+
7
+ # /builder-refresh — save, clear, and come back knowing where you are
8
+
9
+ You are a builder that has reached a protocol boundary. Your context can be discarded
10
+ here safely, because everything that matters is already on disk: the spec, the plan,
11
+ `status.yaml`, your thread narrative, and git history. A fresh context re-orients from
12
+ those artifacts rather than from memory.
13
+
14
+ **The enforcement is in the command, not in this document.** `afx self-refresh` refuses to
15
+ clear on an unverified save, refuses when the re-entry cannot be scheduled, and refuses
16
+ when your worktree has uncommitted tracked changes. This skill sequences the steps and
17
+ explains the judgement calls; it cannot and does not decide whether the clear is safe.
18
+
19
+ ## When NOT to run this
20
+
21
+ **Porch chooses the moment. You do not.** Run this when porch's refresh task tells you to,
22
+ or when the architect directs it. Do not invoke it mid-task on your own judgement — a
23
+ boundary is a point a fresh context can resume *from*, and a mid-task snapshot resumes into
24
+ confusion.
25
+
26
+ If you are not at a boundary and think you need a refresh anyway, say so to the architect
27
+ rather than doing it: `afx send architect "..."`.
28
+
29
+ ## The procedure
30
+
31
+ ### 1. Use the commands porch gave you
32
+
33
+ Porch's refresh task contains the exact two commands for this boundary, including the
34
+ `--boundary` flag. **Run those, verbatim.**
35
+
36
+ Do not retype them from memory and do not omit the flag. `--boundary` binds the challenge
37
+ to the boundary it was issued at, so a challenge left behind by an aborted refresh cannot
38
+ be used to clear you at a *later* boundary, against a save describing work that has since
39
+ moved on. That guard has been silently disabled twice by an instruction that dropped the
40
+ flag, which is why this document deliberately does not restate the commands.
41
+
42
+ If you arrived here without a porch task — an architect-directed refresh, say — there is no
43
+ boundary to bind, and the flagless form is correct. Run `afx self-refresh --help` for the
44
+ exact spelling rather than guessing it.
45
+
46
+ ### 2. Write your working state
47
+
48
+ The first command prints a save request naming a file and a marker line. Write that file.
49
+
50
+ **The marker must be the FIRST LINE**, reproduced exactly. A save whose nonce appears
51
+ further down is refused — that check exists because echoing the request back into the file
52
+ otherwise passed every gate, and echoing instructions is something agents do by accident.
53
+
54
+ **Write for a cold reader** — a competent agent that wakes up with your worktree, your
55
+ branch, and no memory of this conversation. Most of your state is already on disk, so do
56
+ not restate the spec or the plan. Carry only what the artifacts do not contain:
57
+
58
+ - **receipts** — what is done and *verified*, with paths and commit hashes, distinguishing
59
+ "written" from "verified";
60
+ - **deviations** from the plan, and why;
61
+ - **flaky or skipped tests**, and what you did about them;
62
+ - **deferred work**, and the reason;
63
+ - **standing orders** from the architect you are still bound by, including anything you
64
+ were told NOT to do;
65
+ - **the next concrete action**.
66
+
67
+ Pointers, not prose. The save has a minimum size, and it is not a word count to pad — a
68
+ file below it is indistinguishable from a stub. If you genuinely have less than that to
69
+ say, you are probably omitting receipts or standing orders.
70
+
71
+ ### 3. Run the execute command, then stop
72
+
73
+ The second command verifies your save, writes a re-orientation to disk, schedules your
74
+ re-entry, and clears you — in that order, and only if every step succeeds.
75
+
76
+ **Then end your turn.** Do not start new work. The clear takes effect when the turn ends.
77
+
78
+ ## If it refuses
79
+
80
+ Most refusals happen **before** anything destructive, and those leave your context
81
+ untouched. One does not. Read which you got.
82
+
83
+ ### Pre-clear refusals — your context is intact
84
+
85
+ | Refusal | What it means |
86
+ |---|---|
87
+ | state file missing / too small / wrong nonce | Your save did not satisfy the gate — check the marker is on line 1 |
88
+ | worktree has uncommitted tracked changes | Commit first; a refreshed context re-orients from git, so uncommitted work is invisible to it |
89
+ | Tower is not running, or did not schedule the re-entry | Clearing would strand you, so it refused |
90
+ | challenge missing / already consumed / for a different boundary | Run the begin step again |
91
+ | invalid parameters | A flag is out of range — nothing was read or written |
92
+ | challenge could not be marked consumed | Nothing was cleared, but a re-entry **is** already queued and will arrive; ignore it. Retrying queues a second one |
93
+
94
+ For any of these: **report it and carry on.**
95
+
96
+ ```bash
97
+ afx send architect "Context refresh refused at <boundary>: <the reason it printed>"
98
+ ```
99
+
100
+ Then run `porch next` for your normal tasks. **The refresh never blocks your work** — the
101
+ boundary is recorded as consumed either way, so a failed refresh costs you some context and
102
+ nothing else.
103
+
104
+ ### `clear-failed` — you cannot assume anything
105
+
106
+ If the command reports that the clear was attempted but **may still have landed**, do not
107
+ treat that as a pre-clear refusal. Sending `/clear` can succeed on the wire and still report
108
+ an error, so from inside this turn it is genuinely unknown whether your context survives.
109
+
110
+ - A re-entry message **is** already queued and will arrive shortly. If your context was
111
+ cleared, that message is your re-orientation. If it was not, the message is harmless and
112
+ can be ignored.
113
+ - Do not start new work in this state.
114
+ - Tell the architect explicitly that the outcome is ambiguous:
115
+
116
+ ```bash
117
+ afx send architect "Self-refresh clear-failed at <boundary> — clear may or may not have landed; re-entry is queued"
118
+ ```
119
+
120
+ ## After the clear
121
+
122
+ A re-entry message arrives shortly, identifying itself as an automatic context refresh. It
123
+ carries your identity, protocol, project, worktree and branch, and points at
124
+ `.builder-reorient.md` — the full spawn-quality frame, on disk.
125
+
126
+ It will tell you to run `porch next`. Do that; porch's state is untouched by the clear and
127
+ its next task emission *is* your re-orientation.
128
+
129
+ **If the re-entry never arrives**, nothing is lost. `.builder-reorient.md` is on disk and
130
+ the architect can send you back with one message. Say so if you notice.
131
+
132
+ ## What this does not do
133
+
134
+ - It does not write `status.yaml`. Only porch does, and the boundary is already recorded
135
+ there before you are asked to refresh.
136
+ - It does not decide *whether* to refresh. Porch decided that.
137
+ - It does not retry. A boundary is refreshed at most once, so a refusal means this boundary
138
+ simply does not get a refresh.
@@ -69,6 +69,60 @@ afx send 0042 "PR approved, please merge"
69
69
  afx send 0585 "check the test output" --file /tmp/test-results.txt
70
70
  ```
71
71
 
72
+ ## afx interrupt
73
+
74
+ Sends an ESC keystroke to a builder's PTY — the only thing that reaches it **mid-turn**.
75
+
76
+ ```
77
+ afx interrupt <builder>
78
+ ```
79
+
80
+ | Flag | Description |
81
+ |------|-------------|
82
+ | `--no-enter` | Send ESC alone, without the trailing Enter |
83
+
84
+ A builder chaining foreground waits inside one turn queues every `afx send` unread — including your order
85
+ to stop. ESC ends the turn so the queue processes. Distinct from `afx send --interrupt` (Ctrl+C).
86
+
87
+ ```bash
88
+ afx interrupt 0042
89
+ afx send 0042 "That producer died — stop waiting and report."
90
+ ```
91
+
92
+ ## afx refresh
93
+
94
+ Refreshes a builder's context: save working state → `/clear` → re-orient. Use when a builder's context window
95
+ is exhausted; `afx spawn --resume` reattaches the *same* conversation and does **not** give it a fresh one.
96
+
97
+ ```
98
+ afx refresh <builder>
99
+ ```
100
+
101
+ | Flag | Description |
102
+ |------|-------------|
103
+ | `--dry-run` | Print what would be sent; write nothing to the builder |
104
+ | `--note <text>` | Extra context appended to the re-orientation |
105
+ | `--file <path>` | Append file content (48KB max, read from *your* filesystem) |
106
+ | `--interrupt-first` | ESC before the save request, for a builder already wedged |
107
+ | `--mode <strict\|soft>` | Override mode if it cannot be detected |
108
+ | `--timeout <seconds>` | Wait for the save-state receipt (default 300) |
109
+ | `--min-bytes <n>` | Minimum state-file size to accept (default 1000) |
110
+ | `--quiet-window <ms>` | Terminal silence counting as turn-ended (default 1500) |
111
+
112
+ Every gate fails safe: if the state file never arrives, carries a stale nonce, is a stub, is still being
113
+ written, or the builder will not go quiet, the command **aborts without clearing** and exits non-zero,
114
+ naming the gate. Requires a harness with in-session context clearing (Claude Code); others abort loudly.
115
+
116
+ `afx reset` is a deprecated alias: it still works, prints a one-line notice to stderr, and will be removed
117
+ in a future release.
118
+
119
+ ```bash
120
+ afx refresh 0042 --dry-run # inspect first — touches nothing
121
+ afx refresh 0042
122
+ afx refresh 0042 --note "PR #90 merged while you were mid-phase. Rebase first."
123
+ afx refresh 0042 --interrupt-first # builder is wedged mid-turn
124
+ ```
125
+
72
126
  ## afx cleanup
73
127
 
74
128
  Removes a builder's worktree and branch after work is done.
@@ -107,8 +107,14 @@ guarantee about the clear's completion. If the timing is wrong the re-init arriv
107
107
  wrong moment, which costs one manual message (see below) and nothing else. That is the
108
108
  whole reason this cycle can be built on a delay rather than on machinery.
109
109
 
110
- Delayed sends are **not persisted** — if Tower restarts inside the window, the message is
111
- dropped. That is recoverable; see below.
110
+ Delayed sends **are persisted**. The body is written to the durable mailbox at request time
111
+ with a `not_before` timestamp, so Tower keeps no timer for it and a restart inside the window
112
+ does not lose it — the re-init is delivered once the delay passes. The one thing a restart does
113
+ drop is the Ctrl+C nudge of a delayed `--interrupt`, which this cycle does not use.
114
+
115
+ That removes a failure mode this section used to warn about, but not the need for the recovery
116
+ path below: delivery still waits on an empty prompt, so a re-init can arrive late rather than
117
+ never.
112
118
 
113
119
  **If this send fails, do not end your turn.** Step 4 queued the `/clear`, but it does not
114
120
  take effect until your turn ends — so at this moment you still have your full context and
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: builder-refresh
3
+ description: Refresh a builder's own context at a protocol boundary — save working state, verify it, clear, and re-orient. Use when porch emits a context-refresh task, or the builder is told to "refresh your context" / "run a self-refresh". A builder does not invoke this autonomously mid-task; porch chooses the moment. Counterpart to the architect's /arch-save.
4
+ argument-hint: "(none — porch's refresh task supplies the commands)"
5
+ ---
6
+
7
+ # /builder-refresh — save, clear, and come back knowing where you are
8
+
9
+ You are a builder that has reached a protocol boundary. Your context can be discarded
10
+ here safely, because everything that matters is already on disk: the spec, the plan,
11
+ `status.yaml`, your thread narrative, and git history. A fresh context re-orients from
12
+ those artifacts rather than from memory.
13
+
14
+ **The enforcement is in the command, not in this document.** `afx self-refresh` refuses to
15
+ clear on an unverified save, refuses when the re-entry cannot be scheduled, and refuses
16
+ when your worktree has uncommitted tracked changes. This skill sequences the steps and
17
+ explains the judgement calls; it cannot and does not decide whether the clear is safe.
18
+
19
+ ## When NOT to run this
20
+
21
+ **Porch chooses the moment. You do not.** Run this when porch's refresh task tells you to,
22
+ or when the architect directs it. Do not invoke it mid-task on your own judgement — a
23
+ boundary is a point a fresh context can resume *from*, and a mid-task snapshot resumes into
24
+ confusion.
25
+
26
+ If you are not at a boundary and think you need a refresh anyway, say so to the architect
27
+ rather than doing it: `afx send architect "..."`.
28
+
29
+ ## The procedure
30
+
31
+ ### 1. Use the commands porch gave you
32
+
33
+ Porch's refresh task contains the exact two commands for this boundary, including the
34
+ `--boundary` flag. **Run those, verbatim.**
35
+
36
+ Do not retype them from memory and do not omit the flag. `--boundary` binds the challenge
37
+ to the boundary it was issued at, so a challenge left behind by an aborted refresh cannot
38
+ be used to clear you at a *later* boundary, against a save describing work that has since
39
+ moved on. That guard has been silently disabled twice by an instruction that dropped the
40
+ flag, which is why this document deliberately does not restate the commands.
41
+
42
+ If you arrived here without a porch task — an architect-directed refresh, say — there is no
43
+ boundary to bind, and the flagless form is correct. Run `afx self-refresh --help` for the
44
+ exact spelling rather than guessing it.
45
+
46
+ ### 2. Write your working state
47
+
48
+ The first command prints a save request naming a file and a marker line. Write that file.
49
+
50
+ **The marker must be the FIRST LINE**, reproduced exactly. A save whose nonce appears
51
+ further down is refused — that check exists because echoing the request back into the file
52
+ otherwise passed every gate, and echoing instructions is something agents do by accident.
53
+
54
+ **Write for a cold reader** — a competent agent that wakes up with your worktree, your
55
+ branch, and no memory of this conversation. Most of your state is already on disk, so do
56
+ not restate the spec or the plan. Carry only what the artifacts do not contain:
57
+
58
+ - **receipts** — what is done and *verified*, with paths and commit hashes, distinguishing
59
+ "written" from "verified";
60
+ - **deviations** from the plan, and why;
61
+ - **flaky or skipped tests**, and what you did about them;
62
+ - **deferred work**, and the reason;
63
+ - **standing orders** from the architect you are still bound by, including anything you
64
+ were told NOT to do;
65
+ - **the next concrete action**.
66
+
67
+ Pointers, not prose. The save has a minimum size, and it is not a word count to pad — a
68
+ file below it is indistinguishable from a stub. If you genuinely have less than that to
69
+ say, you are probably omitting receipts or standing orders.
70
+
71
+ ### 3. Run the execute command, then stop
72
+
73
+ The second command verifies your save, writes a re-orientation to disk, schedules your
74
+ re-entry, and clears you — in that order, and only if every step succeeds.
75
+
76
+ **Then end your turn.** Do not start new work. The clear takes effect when the turn ends.
77
+
78
+ ## If it refuses
79
+
80
+ Most refusals happen **before** anything destructive, and those leave your context
81
+ untouched. One does not. Read which you got.
82
+
83
+ ### Pre-clear refusals — your context is intact
84
+
85
+ | Refusal | What it means |
86
+ |---|---|
87
+ | state file missing / too small / wrong nonce | Your save did not satisfy the gate — check the marker is on line 1 |
88
+ | worktree has uncommitted tracked changes | Commit first; a refreshed context re-orients from git, so uncommitted work is invisible to it |
89
+ | Tower is not running, or did not schedule the re-entry | Clearing would strand you, so it refused |
90
+ | challenge missing / already consumed / for a different boundary | Run the begin step again |
91
+ | invalid parameters | A flag is out of range — nothing was read or written |
92
+ | challenge could not be marked consumed | Nothing was cleared, but a re-entry **is** already queued and will arrive; ignore it. Retrying queues a second one |
93
+
94
+ For any of these: **report it and carry on.**
95
+
96
+ ```bash
97
+ afx send architect "Context refresh refused at <boundary>: <the reason it printed>"
98
+ ```
99
+
100
+ Then run `porch next` for your normal tasks. **The refresh never blocks your work** — the
101
+ boundary is recorded as consumed either way, so a failed refresh costs you some context and
102
+ nothing else.
103
+
104
+ ### `clear-failed` — you cannot assume anything
105
+
106
+ If the command reports that the clear was attempted but **may still have landed**, do not
107
+ treat that as a pre-clear refusal. Sending `/clear` can succeed on the wire and still report
108
+ an error, so from inside this turn it is genuinely unknown whether your context survives.
109
+
110
+ - A re-entry message **is** already queued and will arrive shortly. If your context was
111
+ cleared, that message is your re-orientation. If it was not, the message is harmless and
112
+ can be ignored.
113
+ - Do not start new work in this state.
114
+ - Tell the architect explicitly that the outcome is ambiguous:
115
+
116
+ ```bash
117
+ afx send architect "Self-refresh clear-failed at <boundary> — clear may or may not have landed; re-entry is queued"
118
+ ```
119
+
120
+ ## After the clear
121
+
122
+ A re-entry message arrives shortly, identifying itself as an automatic context refresh. It
123
+ carries your identity, protocol, project, worktree and branch, and points at
124
+ `.builder-reorient.md` — the full spawn-quality frame, on disk.
125
+
126
+ It will tell you to run `porch next`. Do that; porch's state is untouched by the clear and
127
+ its next task emission *is* your re-orientation.
128
+
129
+ **If the re-entry never arrives**, nothing is lost. `.builder-reorient.md` is on disk and
130
+ the architect can send you back with one message. Say so if you notice.
131
+
132
+ ## What this does not do
133
+
134
+ - It does not write `status.yaml`. Only porch does, and the boundary is already recorded
135
+ there before you are asked to refresh.
136
+ - It does not decide *whether* to refresh. Porch decided that.
137
+ - It does not retry. A boundary is refreshed at most once, so a refusal means this boundary
138
+ simply does not get a refresh.
@@ -39,6 +39,23 @@
39
39
  "description": "Signals that can be emitted during protocol execution",
40
40
  "additionalProperties": { "$ref": "#/$defs/signal" }
41
41
  },
42
+ "context_refresh": {
43
+ "type": "object",
44
+ "description": "Boundaries at which builders automatically refresh their context (Spec 1470). Absent means no refreshes.",
45
+ "additionalProperties": false,
46
+ "properties": {
47
+ "on_enter": {
48
+ "type": "array",
49
+ "description": "Protocol phase ids to refresh on entry to. Each must name a phase this protocol declares.",
50
+ "uniqueItems": true,
51
+ "items": { "type": "string" }
52
+ },
53
+ "on_plan_phase_advance": {
54
+ "type": "boolean",
55
+ "description": "Refresh when advancing from one plan phase to the next. Requires a 'per_plan_phase' phase. Excludes the first plan phase, which coincides with entering the phase itself."
56
+ }
57
+ }
58
+ },
42
59
  "phase_completion": {
43
60
  "type": "object",
44
61
  "description": "Checks run when a plan phase completes",