@cluesmith/codev 3.3.4 → 3.3.6

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 (290) hide show
  1. package/dashboard-dist/assets/index-BjcqV9Vv.css +32 -0
  2. package/dashboard-dist/assets/index-b7GnZV2i.js +220 -0
  3. package/dashboard-dist/assets/index-b7GnZV2i.js.map +1 -0
  4. package/dashboard-dist/index.html +2 -2
  5. package/dist/agent-farm/cli.d.ts.map +1 -1
  6. package/dist/agent-farm/cli.js +50 -17
  7. package/dist/agent-farm/cli.js.map +1 -1
  8. package/dist/agent-farm/commands/architect.d.ts.map +1 -1
  9. package/dist/agent-farm/commands/architect.js +5 -1
  10. package/dist/agent-farm/commands/architect.js.map +1 -1
  11. package/dist/agent-farm/commands/attach.d.ts +1 -1
  12. package/dist/agent-farm/commands/attach.d.ts.map +1 -1
  13. package/dist/agent-farm/commands/cleanup.d.ts +1 -1
  14. package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
  15. package/dist/agent-farm/commands/cleanup.js +2 -1
  16. package/dist/agent-farm/commands/cleanup.js.map +1 -1
  17. package/dist/agent-farm/commands/decisions.d.ts +25 -0
  18. package/dist/agent-farm/commands/decisions.d.ts.map +1 -0
  19. package/dist/agent-farm/commands/decisions.js +54 -0
  20. package/dist/agent-farm/commands/decisions.js.map +1 -0
  21. package/dist/agent-farm/commands/ide.d.ts +32 -3
  22. package/dist/agent-farm/commands/ide.d.ts.map +1 -1
  23. package/dist/agent-farm/commands/ide.js +180 -19
  24. package/dist/agent-farm/commands/ide.js.map +1 -1
  25. package/dist/agent-farm/commands/reset/context.d.ts +4 -2
  26. package/dist/agent-farm/commands/reset/context.d.ts.map +1 -1
  27. package/dist/agent-farm/commands/reset/context.js +12 -4
  28. package/dist/agent-farm/commands/reset/context.js.map +1 -1
  29. package/dist/agent-farm/commands/send.d.ts +3 -2
  30. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  31. package/dist/agent-farm/commands/send.js +35 -21
  32. package/dist/agent-farm/commands/send.js.map +1 -1
  33. package/dist/agent-farm/commands/spawn-worktree.d.ts +7 -0
  34. package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
  35. package/dist/agent-farm/commands/spawn-worktree.js +63 -20
  36. package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
  37. package/dist/agent-farm/commands/spawn.d.ts +17 -0
  38. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  39. package/dist/agent-farm/commands/spawn.js +41 -19
  40. package/dist/agent-farm/commands/spawn.js.map +1 -1
  41. package/dist/agent-farm/lib/builder-lookup.d.ts +3 -2
  42. package/dist/agent-farm/lib/builder-lookup.d.ts.map +1 -1
  43. package/dist/agent-farm/lib/builder-lookup.js +24 -9
  44. package/dist/agent-farm/lib/builder-lookup.js.map +1 -1
  45. package/dist/agent-farm/lib/ide-extensions.d.ts +82 -0
  46. package/dist/agent-farm/lib/ide-extensions.d.ts.map +1 -0
  47. package/dist/agent-farm/lib/ide-extensions.js +179 -0
  48. package/dist/agent-farm/lib/ide-extensions.js.map +1 -0
  49. package/dist/agent-farm/lib/ide-record.d.ts +6 -1
  50. package/dist/agent-farm/lib/ide-record.d.ts.map +1 -1
  51. package/dist/agent-farm/lib/ide-record.js +8 -1
  52. package/dist/agent-farm/lib/ide-record.js.map +1 -1
  53. package/dist/agent-farm/lib/ide-version.d.ts +97 -0
  54. package/dist/agent-farm/lib/ide-version.d.ts.map +1 -0
  55. package/dist/agent-farm/lib/ide-version.js +224 -0
  56. package/dist/agent-farm/lib/ide-version.js.map +1 -0
  57. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
  58. package/dist/agent-farm/lib/tunnel-client.js +7 -3
  59. package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
  60. package/dist/agent-farm/servers/agent-state/broadcast.d.ts +19 -0
  61. package/dist/agent-farm/servers/agent-state/broadcast.d.ts.map +1 -0
  62. package/dist/agent-farm/servers/agent-state/broadcast.js +30 -0
  63. package/dist/agent-farm/servers/agent-state/broadcast.js.map +1 -0
  64. package/dist/agent-farm/servers/agent-state/identity.d.ts +50 -0
  65. package/dist/agent-farm/servers/agent-state/identity.d.ts.map +1 -0
  66. package/dist/agent-farm/servers/agent-state/identity.js +120 -0
  67. package/dist/agent-farm/servers/agent-state/identity.js.map +1 -0
  68. package/dist/agent-farm/servers/agent-state/liveness.d.ts +107 -0
  69. package/dist/agent-farm/servers/agent-state/liveness.d.ts.map +1 -0
  70. package/dist/agent-farm/servers/agent-state/liveness.js +224 -0
  71. package/dist/agent-farm/servers/agent-state/liveness.js.map +1 -0
  72. package/dist/agent-farm/servers/agent-state/route.d.ts +38 -0
  73. package/dist/agent-farm/servers/agent-state/route.d.ts.map +1 -0
  74. package/dist/agent-farm/servers/agent-state/route.js +170 -0
  75. package/dist/agent-farm/servers/agent-state/route.js.map +1 -0
  76. package/dist/agent-farm/servers/agent-state/serve.d.ts +31 -0
  77. package/dist/agent-farm/servers/agent-state/serve.d.ts.map +1 -0
  78. package/dist/agent-farm/servers/agent-state/serve.js +49 -0
  79. package/dist/agent-farm/servers/agent-state/serve.js.map +1 -0
  80. package/dist/agent-farm/servers/agent-state/store.d.ts +76 -0
  81. package/dist/agent-farm/servers/agent-state/store.d.ts.map +1 -0
  82. package/dist/agent-farm/servers/agent-state/store.js +157 -0
  83. package/dist/agent-farm/servers/agent-state/store.js.map +1 -0
  84. package/dist/agent-farm/servers/decisions/file.d.ts +50 -0
  85. package/dist/agent-farm/servers/decisions/file.d.ts.map +1 -0
  86. package/dist/agent-farm/servers/decisions/file.js +146 -0
  87. package/dist/agent-farm/servers/decisions/file.js.map +1 -0
  88. package/dist/agent-farm/servers/decisions/grammar.d.ts +36 -0
  89. package/dist/agent-farm/servers/decisions/grammar.d.ts.map +1 -0
  90. package/dist/agent-farm/servers/decisions/grammar.js +109 -0
  91. package/dist/agent-farm/servers/decisions/grammar.js.map +1 -0
  92. package/dist/agent-farm/servers/decisions/notice.d.ts +11 -0
  93. package/dist/agent-farm/servers/decisions/notice.d.ts.map +1 -0
  94. package/dist/agent-farm/servers/decisions/notice.js +18 -0
  95. package/dist/agent-farm/servers/decisions/notice.js.map +1 -0
  96. package/dist/agent-farm/servers/decisions/parse.d.ts +36 -0
  97. package/dist/agent-farm/servers/decisions/parse.d.ts.map +1 -0
  98. package/dist/agent-farm/servers/decisions/parse.js +338 -0
  99. package/dist/agent-farm/servers/decisions/parse.js.map +1 -0
  100. package/dist/agent-farm/servers/decisions/routes.d.ts +17 -0
  101. package/dist/agent-farm/servers/decisions/routes.d.ts.map +1 -0
  102. package/dist/agent-farm/servers/decisions/routes.js +160 -0
  103. package/dist/agent-farm/servers/decisions/routes.js.map +1 -0
  104. package/dist/agent-farm/servers/decisions/splice.d.ts +26 -0
  105. package/dist/agent-farm/servers/decisions/splice.d.ts.map +1 -0
  106. package/dist/agent-farm/servers/decisions/splice.js +120 -0
  107. package/dist/agent-farm/servers/decisions/splice.js.map +1 -0
  108. package/dist/agent-farm/servers/decisions/stamp.d.ts +8 -0
  109. package/dist/agent-farm/servers/decisions/stamp.d.ts.map +1 -0
  110. package/dist/agent-farm/servers/decisions/stamp.js +195 -0
  111. package/dist/agent-farm/servers/decisions/stamp.js.map +1 -0
  112. package/dist/agent-farm/servers/decisions/template.d.ts +2 -0
  113. package/dist/agent-farm/servers/decisions/template.d.ts.map +1 -0
  114. package/dist/agent-farm/servers/decisions/template.js +15 -0
  115. package/dist/agent-farm/servers/decisions/template.js.map +1 -0
  116. package/dist/agent-farm/servers/ide-server.d.ts +28 -8
  117. package/dist/agent-farm/servers/ide-server.d.ts.map +1 -1
  118. package/dist/agent-farm/servers/ide-server.js +147 -50
  119. package/dist/agent-farm/servers/ide-server.js.map +1 -1
  120. package/dist/agent-farm/servers/overview.d.ts +31 -4
  121. package/dist/agent-farm/servers/overview.d.ts.map +1 -1
  122. package/dist/agent-farm/servers/overview.js +235 -55
  123. package/dist/agent-farm/servers/overview.js.map +1 -1
  124. package/dist/agent-farm/servers/tower-instances.d.ts +14 -1
  125. package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
  126. package/dist/agent-farm/servers/tower-instances.js +51 -9
  127. package/dist/agent-farm/servers/tower-instances.js.map +1 -1
  128. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
  129. package/dist/agent-farm/servers/tower-routes.js +113 -14
  130. package/dist/agent-farm/servers/tower-routes.js.map +1 -1
  131. package/dist/agent-farm/servers/tower-server.js +35 -1
  132. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  133. package/dist/agent-farm/servers/tower-terminals.d.ts +25 -0
  134. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
  135. package/dist/agent-farm/servers/tower-terminals.js +128 -4
  136. package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
  137. package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -1
  138. package/dist/agent-farm/servers/tower-utils.js +18 -5
  139. package/dist/agent-farm/servers/tower-utils.js.map +1 -1
  140. package/dist/agent-farm/utils/builder-worktree-identity.d.ts +39 -0
  141. package/dist/agent-farm/utils/builder-worktree-identity.d.ts.map +1 -0
  142. package/dist/agent-farm/utils/builder-worktree-identity.js +42 -0
  143. package/dist/agent-farm/utils/builder-worktree-identity.js.map +1 -0
  144. package/dist/agent-farm/utils/harness.d.ts +71 -0
  145. package/dist/agent-farm/utils/harness.d.ts.map +1 -1
  146. package/dist/agent-farm/utils/harness.js +153 -0
  147. package/dist/agent-farm/utils/harness.js.map +1 -1
  148. package/dist/agent-farm/utils/lifecycle-reporter.d.ts +79 -0
  149. package/dist/agent-farm/utils/lifecycle-reporter.d.ts.map +1 -0
  150. package/dist/agent-farm/utils/lifecycle-reporter.js +347 -0
  151. package/dist/agent-farm/utils/lifecycle-reporter.js.map +1 -0
  152. package/dist/agent-farm/utils/message-format.d.ts +8 -0
  153. package/dist/agent-farm/utils/message-format.d.ts.map +1 -1
  154. package/dist/agent-farm/utils/message-format.js +16 -0
  155. package/dist/agent-farm/utils/message-format.js.map +1 -1
  156. package/dist/cli.d.ts.map +1 -1
  157. package/dist/cli.js +17 -0
  158. package/dist/cli.js.map +1 -1
  159. package/dist/commands/adopt.d.ts +16 -2
  160. package/dist/commands/adopt.d.ts.map +1 -1
  161. package/dist/commands/adopt.js +67 -23
  162. package/dist/commands/adopt.js.map +1 -1
  163. package/dist/commands/bootstrap-docs.d.ts +132 -0
  164. package/dist/commands/bootstrap-docs.d.ts.map +1 -0
  165. package/dist/commands/bootstrap-docs.js +725 -0
  166. package/dist/commands/bootstrap-docs.js.map +1 -0
  167. package/dist/commands/consult/index.d.ts +20 -1
  168. package/dist/commands/consult/index.d.ts.map +1 -1
  169. package/dist/commands/consult/index.js +70 -3
  170. package/dist/commands/consult/index.js.map +1 -1
  171. package/dist/commands/consult/tree-tripwire.d.ts +2 -75
  172. package/dist/commands/consult/tree-tripwire.d.ts.map +1 -1
  173. package/dist/commands/consult/tree-tripwire.js +7 -184
  174. package/dist/commands/consult/tree-tripwire.js.map +1 -1
  175. package/dist/commands/doctor.d.ts.map +1 -1
  176. package/dist/commands/doctor.js +36 -4
  177. package/dist/commands/doctor.js.map +1 -1
  178. package/dist/commands/porch/checks.d.ts +7 -1
  179. package/dist/commands/porch/checks.d.ts.map +1 -1
  180. package/dist/commands/porch/checks.js +77 -14
  181. package/dist/commands/porch/checks.js.map +1 -1
  182. package/dist/commands/porch/index.d.ts +38 -1
  183. package/dist/commands/porch/index.d.ts.map +1 -1
  184. package/dist/commands/porch/index.js +79 -43
  185. package/dist/commands/porch/index.js.map +1 -1
  186. package/dist/commands/porch/protocol.d.ts +4 -3
  187. package/dist/commands/porch/protocol.d.ts.map +1 -1
  188. package/dist/commands/porch/protocol.js +32 -5
  189. package/dist/commands/porch/protocol.js.map +1 -1
  190. package/dist/commands/porch/state.d.ts +0 -4
  191. package/dist/commands/porch/state.d.ts.map +1 -1
  192. package/dist/commands/porch/state.js +23 -10
  193. package/dist/commands/porch/state.js.map +1 -1
  194. package/dist/commands/porch/types.d.ts +2 -0
  195. package/dist/commands/porch/types.d.ts.map +1 -1
  196. package/dist/commands/update.d.ts +5 -1
  197. package/dist/commands/update.d.ts.map +1 -1
  198. package/dist/commands/update.js +73 -18
  199. package/dist/commands/update.js.map +1 -1
  200. package/dist/lib/agent-env.d.ts +31 -1
  201. package/dist/lib/agent-env.d.ts.map +1 -1
  202. package/dist/lib/agent-env.js +38 -1
  203. package/dist/lib/agent-env.js.map +1 -1
  204. package/dist/lib/agent-process.d.ts +23 -0
  205. package/dist/lib/agent-process.d.ts.map +1 -0
  206. package/dist/lib/agent-process.js +24 -0
  207. package/dist/lib/agent-process.js.map +1 -0
  208. package/dist/lib/config.d.ts +8 -0
  209. package/dist/lib/config.d.ts.map +1 -1
  210. package/dist/lib/config.js.map +1 -1
  211. package/dist/lib/forge-contracts.d.ts +9 -3
  212. package/dist/lib/forge-contracts.d.ts.map +1 -1
  213. package/dist/lib/forge-contracts.js.map +1 -1
  214. package/dist/lib/forge.d.ts +8 -5
  215. package/dist/lib/forge.d.ts.map +1 -1
  216. package/dist/lib/forge.js +57 -1
  217. package/dist/lib/forge.js.map +1 -1
  218. package/dist/lib/github.d.ts.map +1 -1
  219. package/dist/lib/github.js +38 -0
  220. package/dist/lib/github.js.map +1 -1
  221. package/dist/lib/governance-bootstrap.d.ts +155 -0
  222. package/dist/lib/governance-bootstrap.d.ts.map +1 -0
  223. package/dist/lib/governance-bootstrap.js +469 -0
  224. package/dist/lib/governance-bootstrap.js.map +1 -0
  225. package/dist/lib/issue-id.d.ts +34 -0
  226. package/dist/lib/issue-id.d.ts.map +1 -0
  227. package/dist/lib/issue-id.js +61 -0
  228. package/dist/lib/issue-id.js.map +1 -0
  229. package/dist/lib/scaffold.d.ts +4 -0
  230. package/dist/lib/scaffold.d.ts.map +1 -1
  231. package/dist/lib/scaffold.js +29 -5
  232. package/dist/lib/scaffold.js.map +1 -1
  233. package/dist/lib/shipped-skill-hashes.d.ts +10 -0
  234. package/dist/lib/shipped-skill-hashes.d.ts.map +1 -0
  235. package/dist/lib/shipped-skill-hashes.js +197 -0
  236. package/dist/lib/shipped-skill-hashes.js.map +1 -0
  237. package/dist/lib/skill-refresh.d.ts +90 -0
  238. package/dist/lib/skill-refresh.d.ts.map +1 -0
  239. package/dist/lib/skill-refresh.js +267 -0
  240. package/dist/lib/skill-refresh.js.map +1 -0
  241. package/dist/lib/tree-snapshot.d.ts +172 -0
  242. package/dist/lib/tree-snapshot.d.ts.map +1 -0
  243. package/dist/lib/tree-snapshot.js +393 -0
  244. package/dist/lib/tree-snapshot.js.map +1 -0
  245. package/dist/terminal/pty-session.d.ts +5 -0
  246. package/dist/terminal/pty-session.d.ts.map +1 -1
  247. package/dist/terminal/pty-session.js +7 -0
  248. package/dist/terminal/pty-session.js.map +1 -1
  249. package/ide-extension/codev-vscode.vsix +0 -0
  250. package/ide-extension/manifest.json +7 -0
  251. package/package.json +8 -6
  252. package/scripts/forge/jira/_lib.sh +113 -0
  253. package/scripts/forge/jira/auth-status.sh +29 -0
  254. package/scripts/forge/jira/issue-comment.sh +23 -0
  255. package/scripts/forge/jira/issue-list.sh +32 -0
  256. package/scripts/forge/jira/issue-search.sh +40 -0
  257. package/scripts/forge/jira/issue-view.sh +41 -0
  258. package/scripts/forge/jira/recently-closed.sh +81 -0
  259. package/scripts/forge/jira/user-identity.sh +18 -0
  260. package/skeleton/.claude/skills/afx/SKILL.md +51 -0
  261. package/skeleton/.claude/skills/arch-init/SKILL.md +56 -9
  262. package/skeleton/.claude/skills/arch-save/SKILL.md +100 -9
  263. package/skeleton/.claude/skills/codev/SKILL.md +18 -2
  264. package/skeleton/.claude/skills/consult/SKILL.md +9 -0
  265. package/skeleton/.claude/skills/porch/SKILL.md +18 -0
  266. package/skeleton/.claude/skills/update-arch-docs/SKILL.md +28 -2
  267. package/skeleton/.codex/skills/afx/SKILL.md +51 -0
  268. package/skeleton/.codex/skills/arch-init/SKILL.md +56 -9
  269. package/skeleton/.codex/skills/arch-save/SKILL.md +100 -9
  270. package/skeleton/.codex/skills/codev/SKILL.md +18 -2
  271. package/skeleton/.codex/skills/consult/SKILL.md +9 -0
  272. package/skeleton/.codex/skills/porch/SKILL.md +18 -0
  273. package/skeleton/.codex/skills/update-arch-docs/SKILL.md +28 -2
  274. package/skeleton/protocol-schema.json +6 -0
  275. package/skeleton/protocols/maintain/protocol.md +5 -0
  276. package/skeleton/protocols/pir/prompts/review.md +0 -1
  277. package/skeleton/protocols/protocol-schema.json +6 -0
  278. package/skeleton/resources/commands/agent-farm.md +156 -0
  279. package/skeleton/resources/commands/codev.md +47 -0
  280. package/skeleton/resources/commands/consult.md +9 -2
  281. package/skeleton/resources/commands/overview.md +1 -0
  282. package/skeleton/resources/decision-protocol.md +215 -0
  283. package/skeleton/resources/workflow-reference.md +12 -0
  284. package/skeleton/roles/architect.md +6 -0
  285. package/skeleton/templates/bootstrap-docs-prompt.md +16 -0
  286. package/skeleton/templates/bootstrap-docs-review-prompt.md +19 -0
  287. package/skeleton/templates/cheatsheet.md +2 -1
  288. package/dashboard-dist/assets/index-DZf6pNPE.js +0 -199
  289. package/dashboard-dist/assets/index-DZf6pNPE.js.map +0 -1
  290. package/dashboard-dist/assets/index-DnYLy5C_.css +0 -32
@@ -0,0 +1,113 @@
1
+ # Shared helpers for the Jira forge preset scripts (Jira Cloud via Atlassian's `acli`).
2
+ #
3
+ # This file is SOURCED, not executed (`. "$(dirname "$0")/_lib.sh"`), so it has
4
+ # no shebang and defines only functions/vars. POSIX sh only — no bashisms — the
5
+ # scripts are #!/bin/sh and forge runs them via `sh -c`. It is not a forge
6
+ # concept: forge.ts builds presets from an explicit KNOWN_CONCEPTS allowlist, so
7
+ # a leading-underscore file in this directory is never registered as a concept.
8
+ #
9
+ # Sibling-file dependency: the concept scripts that source this file need it to
10
+ # sit next to them. A hand-copied override in `.codev/scripts/forge/jira/` must
11
+ # copy `_lib.sh` alongside the script, or the `.` line fails.
12
+ #
13
+ # acli facts these helpers encode (verified against acli 1.3.39-stable):
14
+ # - `acli jira auth status` round-trips to the site and prints text (no
15
+ # --json): `Site: <host>`, `Email: <addr>`, `Authentication Type: …`.
16
+ # - A work item's REST `self` link names an Atlassian infrastructure host, not
17
+ # the site, so browser URLs are built from the site, never from `self`.
18
+ # - Jira rejects unbounded JQL, so every query carries a status-category
19
+ # clause; project scoping is optional on top of it.
20
+
21
+ # Print the site's browser base URL (`https://<site>.atlassian.net`), no
22
+ # trailing slash. Honors CODEV_JIRA_SITE (`forge.jira-site`), with or without a
23
+ # scheme; otherwise asks acli which site it is logged into. Callers must use:
24
+ # SITE="$(jira_site)" || exit 1
25
+ jira_site() {
26
+ if [ -n "$CODEV_JIRA_SITE" ]; then
27
+ _site="$CODEV_JIRA_SITE"
28
+ else
29
+ _site="$(acli jira auth status 2>/dev/null | sed -n 's/^[[:space:]]*Site:[[:space:]]*//p' | head -n 1)"
30
+ fi
31
+ _site="$(printf '%s' "$_site" | sed -e 's#^[a-zA-Z]*://##' -e 's#/*$##')"
32
+ if [ -z "$_site" ]; then
33
+ echo "jira: cannot determine the Jira site; set forge.jira-site in .codev/config.json or run: acli jira auth login" >&2
34
+ return 1
35
+ fi
36
+ printf 'https://%s' "$_site"
37
+ }
38
+
39
+ # Print the JQL project-scope prefix (`project = "KEY" AND `), or nothing when
40
+ # CODEV_JIRA_PROJECT (`forge.jira-project`) is unset. The key is validated
41
+ # rather than escaped: Jira project keys are `[A-Z][A-Z0-9_]+`, and anything
42
+ # else is a misconfiguration that must not reach the query. Callers must use:
43
+ # SCOPE="$(jira_scope)" || exit 1
44
+ jira_scope() {
45
+ [ -z "$CODEV_JIRA_PROJECT" ] && return 0
46
+ _key="$(printf '%s' "$CODEV_JIRA_PROJECT" | tr '[:lower:]' '[:upper:]')"
47
+ case "$_key" in
48
+ [A-Z]*) ;;
49
+ *) _key="" ;;
50
+ esac
51
+ if [ -z "$_key" ] || printf '%s' "$_key" | grep -q '[^A-Z0-9_]'; then
52
+ echo "jira: invalid forge.jira-project '$CODEV_JIRA_PROJECT' (expected a project key like ENG)" >&2
53
+ return 1
54
+ fi
55
+ printf 'project = "%s" AND ' "$_key"
56
+ }
57
+
58
+ # Fail unless CODEV_ISSUE_ID is a work item key (`KEY-123`). It is passed to
59
+ # acli as an argument, so the check also keeps a leading `-` from being read as
60
+ # a flag. Callers must use: jira_require_issue_id || exit 1
61
+ jira_require_issue_id() {
62
+ if [ -z "$CODEV_ISSUE_ID" ]; then
63
+ echo "CODEV_ISSUE_ID is not set" >&2
64
+ return 1
65
+ fi
66
+ if ! printf '%s' "$CODEV_ISSUE_ID" | grep -Eq '^[A-Za-z][A-Za-z0-9_]*-[0-9]+$'; then
67
+ echo "jira: '$CODEV_ISSUE_ID' is not a Jira work item key (expected KEY-123)" >&2
68
+ return 1
69
+ fi
70
+ }
71
+
72
+ # jq definitions shared by the JSON-emitting scripts. Prepend to a filter:
73
+ # jq "$JIRA_JQ_DEFS"' …filter… '
74
+ #
75
+ # adf_text flattens Atlassian Document Format (descriptions and comment bodies)
76
+ # to plain text: inline text joined per block, blocks separated by blank lines,
77
+ # list items prefixed with "- " or "N. ". Media and unknown leaf nodes drop out.
78
+ # No markdown fidelity is attempted.
79
+ #
80
+ # person maps a Jira user to the contract's `{login}`. The email is preferred
81
+ # because `user-identity` can only report the current user's email (acli
82
+ # exposes nothing else), and the "assigned to me" filter compares the two.
83
+ # Users who hide their email degrade to their display name.
84
+ JIRA_JQ_DEFS='
85
+ def adf_inline:
86
+ if .type == "text" then (.text // "")
87
+ elif .type == "hardBreak" then "\n"
88
+ elif .type == "mention" then (.attrs.text // "")
89
+ elif .type == "emoji" then (.attrs.text // .attrs.shortName // "")
90
+ elif .type == "inlineCard" or .type == "blockCard" then (.attrs.url // "")
91
+ elif (.content | type) == "array" then ([.content[] | adf_inline] | join(""))
92
+ else "" end;
93
+ def adf_block:
94
+ if .type == "paragraph" or .type == "heading" or .type == "codeBlock" then
95
+ ([(.content // [])[] | adf_inline] | join(""))
96
+ elif .type == "bulletList" then
97
+ ([(.content // [])[] | "- " + ([(.content // [])[] | adf_block] | join("\n"))] | join("\n"))
98
+ elif .type == "orderedList" then
99
+ ([(.content // []) | to_entries[] | "\(.key + 1). " + ([(.value.content // [])[] | adf_block] | join("\n"))] | join("\n"))
100
+ elif .type == "mediaSingle" or .type == "mediaGroup" or .type == "media" or .type == "rule" then ""
101
+ elif (.content | type) == "array" then
102
+ ([.content[] | adf_block | select(. != "")] | join("\n\n"))
103
+ else adf_inline end;
104
+ def adf_text:
105
+ if . == null then ""
106
+ elif type == "string" then .
107
+ elif type == "object" then ([(.content // [])[] | adf_block | select(. != "")] | join("\n\n"))
108
+ else "" end;
109
+ def person:
110
+ if . == null then null else { login: (.emailAddress // .displayName // "") } end;
111
+ def labels_of: [(.fields.labels // [])[] | { name: . }];
112
+ def assignees_of: (if .fields.assignee then [.fields.assignee | person] else [] end);
113
+ '
@@ -0,0 +1,29 @@
1
+ #!/bin/sh
2
+ # Forge concept: auth-status (Jira Cloud via acli)
3
+ # forge-executable: acli
4
+ # Input (optional): CODEV_JIRA_SITE — the site this workspace expects
5
+ # Output: exit code (0 = authenticated); acli's status text on stdout
6
+ #
7
+ # `acli jira auth status` round-trips to the site (it fails when the site is
8
+ # unreachable), so it is an authenticated call, not a local config read. acli
9
+ # owns the credential (`acli jira auth login`, browser OAuth or API token);
10
+ # codev never reads or passes a Jira token. When CODEV_JIRA_SITE is set, a login
11
+ # to a different site is reported as unauthenticated, so `codev doctor` never
12
+ # reports success against the wrong site.
13
+ set -e
14
+
15
+ STATUS="$(acli jira auth status 2>&1)" || {
16
+ printf '%s\n' "$STATUS" >&2
17
+ exit 1
18
+ }
19
+
20
+ if [ -n "$CODEV_JIRA_SITE" ]; then
21
+ WANT="$(printf '%s' "$CODEV_JIRA_SITE" | sed -e 's#^[a-zA-Z]*://##' -e 's#/*$##' | tr '[:upper:]' '[:lower:]')"
22
+ HAVE="$(printf '%s\n' "$STATUS" | sed -n 's/^[[:space:]]*Site:[[:space:]]*//p' | head -n 1 | tr '[:upper:]' '[:lower:]')"
23
+ if [ "$WANT" != "$HAVE" ]; then
24
+ echo "jira: acli is logged into '${HAVE:-no site}', but forge.jira-site is '$WANT'; run: acli jira auth switch" >&2
25
+ exit 1
26
+ fi
27
+ fi
28
+
29
+ printf '%s\n' "$STATUS"
@@ -0,0 +1,23 @@
1
+ #!/bin/sh
2
+ # Forge concept: issue-comment (Jira Cloud via acli)
3
+ # forge-executable: acli
4
+ # Input: CODEV_ISSUE_ID, CODEV_COMMENT_BODY
5
+ # Output: exit code only
6
+ #
7
+ # The body goes to acli as plain text through a temp file (no quoting or argv
8
+ # length limits). acli accepts plain text or Atlassian Document Format there;
9
+ # codev's markdown is not converted, so it renders as plain text.
10
+ set -e
11
+ . "$(dirname "$0")/_lib.sh"
12
+
13
+ jira_require_issue_id || exit 1
14
+ if [ -z "${CODEV_COMMENT_BODY+set}" ]; then
15
+ echo "CODEV_COMMENT_BODY is not set" >&2
16
+ exit 1
17
+ fi
18
+
19
+ BODY_FILE="$(mktemp)"
20
+ trap 'rm -f "${BODY_FILE:?}"' EXIT
21
+ printf '%s\n' "$CODEV_COMMENT_BODY" > "$BODY_FILE"
22
+
23
+ acli jira workitem comment create --key "$CODEV_ISSUE_ID" --body-file "$BODY_FILE" >/dev/null
@@ -0,0 +1,32 @@
1
+ #!/bin/sh
2
+ # Forge concept: issue-list (Jira Cloud via acli)
3
+ # forge-executable: acli
4
+ # Input (optional): CODEV_JIRA_PROJECT (project key), CODEV_JIRA_SITE
5
+ # Output: JSON [{number, title, url, labels, author, assignees}]
6
+ #
7
+ # Open = status category not Done (never status names, which are per-site).
8
+ # `acli jira workitem search` only returns an allow-listed set of fields, none
9
+ # of them dates, so `createdAt` is omitted: consumers treat it as unknown, and
10
+ # the JQL orders items newest first. The limit stays at 100 because a search
11
+ # takes ~5s per 100 items and forge commands time out at 30s.
12
+ set -e
13
+ . "$(dirname "$0")/_lib.sh"
14
+
15
+ SCOPE="$(jira_scope)" || exit 1
16
+ SITE="$(jira_site)" || exit 1
17
+
18
+ RAW="$(acli jira workitem search \
19
+ --jql "${SCOPE}statusCategory != Done ORDER BY created DESC" \
20
+ --fields summary,status,labels,creator,assignee \
21
+ --limit 100 \
22
+ --json)"
23
+
24
+ printf '%s' "$RAW" | jq --arg site "$SITE" "$JIRA_JQ_DEFS"'
25
+ [.[] | select(. != null and (.key | type) == "string") | {
26
+ number: .key,
27
+ title: (.fields.summary // ""),
28
+ url: "\($site)/browse/\(.key)",
29
+ labels: labels_of,
30
+ author: (.fields.creator | person),
31
+ assignees: assignees_of
32
+ }]'
@@ -0,0 +1,40 @@
1
+ #!/bin/sh
2
+ # Forge concept: issue-search (Jira Cloud via acli)
3
+ # forge-executable: acli
4
+ #
5
+ # Like issue-list, but for the backlog-search webview: includes `body` (the
6
+ # description, flattened from ADF) and honors a requested state, mapped to the
7
+ # status category: open = not Done, closed = Done, all = any category (still a
8
+ # bounded query, which Jira requires).
9
+ #
10
+ # Input (optional): CODEV_ISSUE_STATE — open|closed|all (default: open)
11
+ # CODEV_JIRA_PROJECT, CODEV_JIRA_SITE
12
+ # Output: JSON [{number, title, url, labels, author, assignees, body}]
13
+ set -e
14
+ . "$(dirname "$0")/_lib.sh"
15
+
16
+ case "${CODEV_ISSUE_STATE:-open}" in
17
+ closed) STATE_CLAUSE='statusCategory = Done' ;;
18
+ all) STATE_CLAUSE='statusCategory is not EMPTY' ;;
19
+ *) STATE_CLAUSE='statusCategory != Done' ;;
20
+ esac
21
+
22
+ SCOPE="$(jira_scope)" || exit 1
23
+ SITE="$(jira_site)" || exit 1
24
+
25
+ RAW="$(acli jira workitem search \
26
+ --jql "${SCOPE}${STATE_CLAUSE} ORDER BY created DESC" \
27
+ --fields summary,status,labels,creator,assignee,description \
28
+ --limit 100 \
29
+ --json)"
30
+
31
+ printf '%s' "$RAW" | jq --arg site "$SITE" "$JIRA_JQ_DEFS"'
32
+ [.[] | select(. != null and (.key | type) == "string") | {
33
+ number: .key,
34
+ title: (.fields.summary // ""),
35
+ url: "\($site)/browse/\(.key)",
36
+ labels: labels_of,
37
+ author: (.fields.creator | person),
38
+ assignees: assignees_of,
39
+ body: (.fields.description | adf_text)
40
+ }]'
@@ -0,0 +1,41 @@
1
+ #!/bin/sh
2
+ # Forge concept: issue-view (Jira Cloud via acli)
3
+ # forge-executable: acli
4
+ # Input: CODEV_ISSUE_ID (e.g. "ENG-123"); CODEV_JIRA_SITE (optional)
5
+ # Output: JSON {title, body, state, url, author, createdAt, assignees, labels, milestone, comments[]} (IssueViewResult)
6
+ #
7
+ # One `workitem view` call carries everything, comments included. Description
8
+ # and comment bodies are Atlassian Document Format, flattened to plain text.
9
+ # `state` is the status *name* (display only); open/closed decisions elsewhere
10
+ # use the status category. Jira has no milestones; the first fix version stands
11
+ # in (optional by contract). `url` is the browser page on the site, never the
12
+ # REST `self` link (which names an infrastructure host).
13
+ set -e
14
+ . "$(dirname "$0")/_lib.sh"
15
+
16
+ jira_require_issue_id || exit 1
17
+ SITE="$(jira_site)" || exit 1
18
+
19
+ RAW="$(acli jira workitem view "$CODEV_ISSUE_ID" \
20
+ --fields summary,description,status,creator,reporter,assignee,labels,created,comment,fixVersions \
21
+ --json)"
22
+
23
+ printf '%s' "$RAW" | jq --arg site "$SITE" "$JIRA_JQ_DEFS"'
24
+ if (.key | type) != "string" then
25
+ error("issue not found: \(env.CODEV_ISSUE_ID)")
26
+ else {
27
+ title: (.fields.summary // ""),
28
+ body: (.fields.description | adf_text),
29
+ state: (.fields.status.name // ""),
30
+ url: "\($site)/browse/\(.key)",
31
+ author: ((.fields.creator // .fields.reporter) | person),
32
+ createdAt: .fields.created,
33
+ assignees: assignees_of,
34
+ labels: labels_of,
35
+ milestone: (if ((.fields.fixVersions // []) | length) > 0 then { title: .fields.fixVersions[0].name } else null end),
36
+ comments: [(.fields.comment.comments // [])[] | {
37
+ body: (.body | adf_text),
38
+ createdAt: .created,
39
+ author: ((.author | person) // { login: "" })
40
+ }]
41
+ } end'
@@ -0,0 +1,81 @@
1
+ #!/bin/sh
2
+ # Forge concept: recently-closed (Jira Cloud via acli)
3
+ # forge-executable: acli
4
+ # Input (optional): CODEV_SINCE_DATE (ISO 8601), CODEV_JIRA_PROJECT, CODEV_JIRA_SITE
5
+ # Output: JSON [{number, title, url, labels, createdAt, closedAt}]
6
+ #
7
+ # Closed = status category Done, never status names (names are per-site).
8
+ #
9
+ # `workitem search` cannot return dates, but consumers drop items without
10
+ # `closedAt`, so each hit is fetched with `workitem view` (one key per call)
11
+ # for `statuscategorychangedate`, with bounded parallelism. The window is small
12
+ # (the overview asks for the last 24h) and capped at 25 hits.
13
+ #
14
+ # JQL date literals are day-or-minute precision in the user's Jira time zone,
15
+ # so the query bound is the since date minus one day: a superset. The consumer
16
+ # applies the exact cutoff against `closedAt`.
17
+ set -e
18
+ . "$(dirname "$0")/_lib.sh"
19
+
20
+ SCOPE="$(jira_scope)" || exit 1
21
+ SITE="$(jira_site)" || exit 1
22
+
23
+ DATE_CLAUSE=""
24
+ if [ -n "$CODEV_SINCE_DATE" ]; then
25
+ SINCE_DAY="$(jq -rn --arg s "$CODEV_SINCE_DATE" '
26
+ try (
27
+ $s | (if test("T") then . else . + "T00:00:00Z" end)
28
+ | sub("\\.[0-9]+"; "") | fromdateiso8601 - 86400 | strftime("%Y-%m-%d")
29
+ ) catch ($s[0:10])')"
30
+ # The fallback takes the input's first ten characters, so validate before it
31
+ # reaches the JQL, as _lib.sh validates keys.
32
+ if ! printf '%s' "$SINCE_DAY" | grep -Eq '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'; then
33
+ echo "jira: invalid CODEV_SINCE_DATE '$CODEV_SINCE_DATE' (expected an ISO 8601 date)" >&2
34
+ exit 1
35
+ fi
36
+ DATE_CLAUSE=" AND statusCategoryChangedDate >= \"$SINCE_DAY\""
37
+ fi
38
+
39
+ HITS="$(acli jira workitem search \
40
+ --jql "${SCOPE}statusCategory = Done${DATE_CLAUSE} ORDER BY statusCategoryChangedDate DESC" \
41
+ --fields summary,status,labels \
42
+ --limit 25 \
43
+ --json)"
44
+
45
+ KEYS="$(printf '%s' "$HITS" | jq -r '.[] | select(. != null) | .key | select(type == "string" and test("^[A-Za-z][A-Za-z0-9_]*-[0-9]+$"))')"
46
+ if [ -z "$KEYS" ]; then
47
+ echo '[]'
48
+ exit 0
49
+ fi
50
+
51
+ WORK="$(mktemp -d)"
52
+ trap 'rm -rf "${WORK:?}"' EXIT
53
+
54
+ # One file per key, so concurrent views never interleave on a shared stdout.
55
+ # Each view writes a .part file that is renamed only on success: a failed view
56
+ # leaves no .json, its item is emitted without dates (the consumer drops it),
57
+ # and the warning makes that visible.
58
+ printf '%s\n' "$KEYS" | xargs -P 6 -I {} sh -c '
59
+ if acli jira workitem view "$1" --fields created,statuscategorychangedate,resolutiondate --json > "$2/$1.part" 2>/dev/null; then
60
+ mv "$2/$1.part" "$2/$1.json"
61
+ else
62
+ echo "jira: could not fetch dates for $1" >&2
63
+ fi
64
+ ' _ {} "$WORK"
65
+
66
+ DATES="$WORK/dates.map"
67
+ if ls "$WORK"/*.json >/dev/null 2>&1; then
68
+ cat "$WORK"/*.json | jq -s 'map(select(.key != null) | { key: .key, value: .fields }) | from_entries' > "$DATES"
69
+ else
70
+ echo '{}' > "$DATES"
71
+ fi
72
+
73
+ printf '%s' "$HITS" | jq --arg site "$SITE" --slurpfile dates "$DATES" "$JIRA_JQ_DEFS"'
74
+ [.[] | select(. != null and (.key | type) == "string") | ($dates[0][.key] // {}) as $d | {
75
+ number: .key,
76
+ title: (.fields.summary // ""),
77
+ url: "\($site)/browse/\(.key)",
78
+ labels: labels_of,
79
+ createdAt: $d.created,
80
+ closedAt: ($d.statuscategorychangedate // $d.resolutiondate)
81
+ }]'
@@ -0,0 +1,18 @@
1
+ #!/bin/sh
2
+ # Forge concept: user-identity (Jira Cloud via acli)
3
+ # forge-executable: acli
4
+ # Output: plain text — the logged-in account's email
5
+ #
6
+ # acli reports the current account only as `Email:` in `auth status` (no
7
+ # display name or account id), so the email is the identity. The issue scripts
8
+ # map assignees to their email for the same reason, which keeps the "assigned
9
+ # to me" filter matching.
10
+ set -e
11
+
12
+ STATUS="$(acli jira auth status)"
13
+ EMAIL="$(printf '%s\n' "$STATUS" | sed -n 's/^[[:space:]]*Email:[[:space:]]*//p' | head -n 1)"
14
+ if [ -z "$EMAIL" ]; then
15
+ echo "jira: acli auth status reported no account email" >&2
16
+ exit 1
17
+ fi
18
+ printf '%s\n' "$EMAIL"
@@ -179,13 +179,63 @@ afx tower disconnect # Disconnect from Codev Cloud
179
179
 
180
180
  There is NO `afx tower restart` — use `afx tower stop && afx tower start`.
181
181
 
182
+ ## afx ide
183
+
184
+ Manages the Codev IDE server (VS Code server-web) that Tower forwards at `/ide/`. Tower spawns
185
+ and owns the process; these commands drive it. Open a workspace at
186
+ `http://localhost:4100/ide/?folder=<abs path>`.
187
+
188
+ ```bash
189
+ afx ide install # npm i -g @cluesmith/codev-ide, then report the installed version
190
+ afx ide start # Find codev-ide-server, ask Tower to spawn it (port 8200)
191
+ afx ide start --server-path <bin> # Use this binary instead of discovery
192
+ afx ide start -p <port> # Run the server on another port
193
+ afx ide stop # Stop the server
194
+ afx ide status # Port, PID, server path/version, extensions dir, sidebar version
195
+ ```
196
+
197
+ **Discovery** (when `--server-path` is absent): `codev-ide-server` on `PATH`, then
198
+ `@cluesmith/codev-ide` next to the running codev install, then under `npm root -g`. Nothing
199
+ found exits 1 before Tower is contacted, naming `afx ide install` and `--server-path`.
200
+
201
+ **On every start** Tower checks `codev-ide-server --version` against a minimum (an older server
202
+ is refused with the installed version, the floor, and the upgrade command), installs the
203
+ sidebar vsix that ships inside `@cluesmith/codev` into `~/.agent-farm/ide-extensions` (skipped
204
+ when that version is already installed; other extensions in the directory are left alone), and
205
+ spawns the server with `--extensions-dir` pointing there. The first start can take a minute or more.
206
+
207
+ The server loads extensions only from `~/.agent-farm/ide-extensions`, so anything installed into
208
+ its default extensions directory is no longer loaded. Install additional extensions there:
209
+ `codev-ide-server --install-extension <id-or-vsix> --extensions-dir ~/.agent-farm/ide-extensions`.
210
+
211
+ A server that is already running is never killed by `start`. If it predates the current server
212
+ or sidebar, `start` warns: restart it with `afx ide stop && afx ide start`.
213
+
214
+ There is NO `afx ide restart` and NO `afx ide upgrade`. To upgrade, run `afx ide install`, then
215
+ `afx ide stop && afx ide start`.
216
+
182
217
  ## afx workspace
183
218
 
184
219
  ```bash
185
220
  afx workspace start # Start workspace for current project
186
221
  afx workspace stop # Stop workspace processes
222
+ afx workspace add-architect --name <name> # Add a named sibling architect terminal
223
+ afx workspace remove-architect <name> # Remove a sibling architect (cannot remove main)
224
+ afx workspace recover # Revive builders whose shellper died (e.g. after reboot)
187
225
  ```
188
226
 
227
+ **Recreating a dead architect** (its process died while Tower kept the registration):
228
+ re-add it under the SAME name, then re-init its identity:
229
+
230
+ ```bash
231
+ afx workspace add-architect --name <name>
232
+ afx send architect:<name> "/arch-init <name>"
233
+ ```
234
+
235
+ Builder-to-architect affinity survives the death (builders keep their Owner), so a
236
+ same-name re-add restores routing with no worktree impact and no builder disruption.
237
+ `afx workspace recover` revives builders only, not architects.
238
+
189
239
  `afx dash` is a deprecated alias — use `afx workspace` instead.
190
240
 
191
241
  ## afx cron
@@ -220,5 +270,6 @@ afx open <file> # Open file in annotation viewer (NOT system ope
220
270
  afx shell # Spawn utility shell
221
271
  afx attach # Attach to running builder terminal
222
272
  afx rename <name> # Rename current shell session
273
+ afx decisions init # Create the decisions file; turns on the Decisions tab
223
274
  afx architect # Start architect session in current terminal
224
275
  ```
@@ -46,12 +46,56 @@ name in a multi-architect workspace).
46
46
  (`codev/state/*.md`); never commit them. Builder `*_thread.md` files
47
47
  are the opposite: versioned, shipping with each builder PR.
48
48
 
49
- 3. **Confirm identity + orient, then follow the state file.** In one tight
50
- block, report: who you now are (name + one-line role from the banner, if
51
- present), the file you read, and the current-state / open-loops summary
52
- from the most recent dated section (or the file's leading content if it
53
- has no dated sections). Then carry out whatever the state file says to do
54
- on resume. Do not invent a new agenda — resume the one the state file
49
+ 3. **Confirm identity + orient.** In one tight block, report: who you now are
50
+ (name + one-line role from the banner, if present), the file you read, and
51
+ the current-state / open-loops summary from the most recent dated section
52
+ (or the file's leading content if it has no dated sections). If the banner
53
+ carries a `NEXT TASK` line, report both halves of it, as
54
+ `Next task from the owner at save time: <verbatim> → resolved: <gloss>`
55
+ (omit `→ resolved: …` when the line carries no `[resolved at save: …]` gloss).
56
+
57
+ 4. **Start on the next task, if the banner carries one.** It is the **first
58
+ action of the resumed session**, ahead of the general resume agenda — the
59
+ owner wrote it at save time precisely so it would not have to be typed
60
+ again once you came back. Begin it without waiting for a further prompt.
61
+ - It carries the owner's authority the way any owner message does, **with
62
+ the standard limits unchanged**. A next task never by itself approves a
63
+ porch gate, merges a PR, cuts a release, restarts Tower, or performs any
64
+ other act that needs a per-occasion word. If the next task *is* such an
65
+ act, prepare it and ask for the word live: a saved instruction is an
66
+ instruction, not a pre-spent approval.
67
+ - **The gloss carries the word — for the items it names, and only them.**
68
+ Items named in a `[resolved at save: …]` gloss are approved for the act
69
+ the owner's words name: "merge them all [resolved at save: PRs #1710,
70
+ #1712]" means merge #1710 and #1712 without asking again. The owner gave
71
+ the word at save time, and the saving session pinned it to those items.
72
+ This narrows the rule above; it does not remove it — and a line with no
73
+ gloss pre-approves nothing:
74
+ - The act must be the one the owner's words say. **A gloss never widens
75
+ the verb** — "merge" does not also approve a release.
76
+ - **Anything not in the gloss still needs the word live.** A PR that
77
+ appeared after the save, an item the gloss did not name, a gate the
78
+ words did not mention → prepare and ask, exactly as above.
79
+ - Porch gates are still relayed to the builder, never run by you: the
80
+ gloss lets you *relay* the approval without re-asking; it does not
81
+ change who runs `porch approve`.
82
+ - Ordinary verification still applies: an approved merge still waits for
83
+ green CI, and a gloss item you find already merged, closed, or changed
84
+ is reported, not forced.
85
+ - **A line marked `[carried over — …]` is orientation only.** Its gloss
86
+ was written by an earlier cycle and carries no word: ask for it before
87
+ any gated act.
88
+ - **This does not narrow the gate rule.** The human decision was explicit
89
+ at save time, and the gloss is its record. What the rule forbids —
90
+ inferring approval from a gate notification, from silence, or from an
91
+ agent's own judgment — stays forbidden; a gloss is none of those.
92
+ - **Once you have started, delete the `NEXT TASK` line from the banner**
93
+ and record the pickup as a log entry (`picked up next task: <verbatim>
94
+ → resolved: <gloss>`, omitting `→ resolved: …` when there was no gloss).
95
+ A second re-init, or the next `/arch-save`, must not re-run it.
96
+
97
+ 5. **Then follow the state file.** Carry out whatever it says to do on
98
+ resume. Do not invent a new agenda — resume the one the state file
55
99
  describes.
56
100
 
57
101
  ## Saving your state (and knowing when to `/clear`)
@@ -118,9 +162,12 @@ Do not repeat it, and do not prompt for it at any other time.
118
162
  **`/arch-save` packages this whole loop**, and is the preferred path when the
119
163
  owner directs a refresh: it stops your monitors, writes the pruned state file,
120
164
  clears, and schedules `/arch-init` to bring you back — in that order, which is
121
- the part that matters. The save discipline above is what it performs at its
122
- step 3, so this section remains the source of truth for *how to write the
123
- file*; `/arch-save` is the source of truth for *the sequence*. The manual path
165
+ the part that matters. It also accepts a **next task** as free text
166
+ (`/arch-save file and spawn that issue`), which it writes into the banner as a
167
+ `NEXT TASK` line for step 4 above to pick up. The save discipline above is
168
+ what it performs at its step 3, so this section remains the source of truth
169
+ for *how to write the file*; `/arch-save` is the source of truth for *the
170
+ sequence*. The manual path
124
171
  (save → human clears → `/arch-init`) stays valid and is the fallback when
125
172
  Tower is unavailable.
126
173