@aperant/framework 0.8.0 → 0.8.4

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 (271) hide show
  1. package/CHANGELOG.md +311 -1
  2. package/README.md +12 -0
  3. package/agents/apt-planner.md +5 -4
  4. package/dist/cli/commands/adr.mjs +1 -1
  5. package/dist/cli/commands/audit-branch-current.d.mts +25 -0
  6. package/dist/cli/commands/audit-branch-current.d.mts.map +1 -0
  7. package/dist/cli/commands/audit-branch-current.mjs +252 -0
  8. package/dist/cli/commands/audit-branch-current.mjs.map +1 -0
  9. package/dist/cli/commands/audit.d.mts.map +1 -1
  10. package/dist/cli/commands/audit.mjs +13 -2
  11. package/dist/cli/commands/audit.mjs.map +1 -1
  12. package/dist/cli/commands/check-version.d.mts.map +1 -1
  13. package/dist/cli/commands/check-version.mjs +19 -1
  14. package/dist/cli/commands/check-version.mjs.map +1 -1
  15. package/dist/cli/commands/context.mjs +3 -3
  16. package/dist/cli/commands/context.mjs.map +1 -1
  17. package/dist/cli/commands/detect-runtime.d.mts +6 -0
  18. package/dist/cli/commands/detect-runtime.d.mts.map +1 -0
  19. package/dist/cli/commands/detect-runtime.mjs +136 -0
  20. package/dist/cli/commands/detect-runtime.mjs.map +1 -0
  21. package/dist/cli/commands/driver-doctor.d.mts +29 -0
  22. package/dist/cli/commands/driver-doctor.d.mts.map +1 -0
  23. package/dist/cli/commands/driver-doctor.mjs +291 -0
  24. package/dist/cli/commands/driver-doctor.mjs.map +1 -0
  25. package/dist/cli/commands/gate.mjs +14 -0
  26. package/dist/cli/commands/gate.mjs.map +1 -1
  27. package/dist/cli/commands/init.d.mts.map +1 -1
  28. package/dist/cli/commands/init.mjs +76 -33
  29. package/dist/cli/commands/init.mjs.map +1 -1
  30. package/dist/cli/commands/install-from-source.d.mts +2 -0
  31. package/dist/cli/commands/install-from-source.d.mts.map +1 -0
  32. package/dist/cli/commands/install-from-source.mjs +2 -0
  33. package/dist/cli/commands/install-from-source.mjs.map +1 -0
  34. package/dist/cli/commands/modes.mjs +1 -1
  35. package/dist/cli/commands/route.d.mts.map +1 -1
  36. package/dist/cli/commands/route.mjs +38 -1
  37. package/dist/cli/commands/route.mjs.map +1 -1
  38. package/dist/cli/commands/triage.mjs +1 -1
  39. package/dist/cli/config/load.d.mts.map +1 -1
  40. package/dist/cli/config/load.mjs +9 -3
  41. package/dist/cli/config/load.mjs.map +1 -1
  42. package/dist/cli/coordination/auto-emit-artifact-ready.mjs +1 -1
  43. package/dist/cli/coordination/event-schema.d.mts.map +1 -1
  44. package/dist/cli/coordination/event-schema.mjs +18 -0
  45. package/dist/cli/coordination/event-schema.mjs.map +1 -1
  46. package/dist/cli/design/frontmatter-schema.d.mts +4 -4
  47. package/dist/cli/dispatch.d.mts.map +1 -1
  48. package/dist/cli/dispatch.mjs +18 -1
  49. package/dist/cli/dispatch.mjs.map +1 -1
  50. package/dist/cli/gate/context.d.mts +1 -0
  51. package/dist/cli/gate/context.d.mts.map +1 -1
  52. package/dist/cli/gate/context.mjs +14 -1
  53. package/dist/cli/gate/context.mjs.map +1 -1
  54. package/dist/cli/gate/gates/consistency-check.d.mts +1 -0
  55. package/dist/cli/gate/gates/consistency-check.d.mts.map +1 -1
  56. package/dist/cli/gate/gates/consistency-check.mjs +1 -0
  57. package/dist/cli/gate/gates/consistency-check.mjs.map +1 -1
  58. package/dist/cli/gate/gates/review-clean.d.mts +1 -0
  59. package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
  60. package/dist/cli/gate/gates/review-clean.mjs +1 -0
  61. package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
  62. package/dist/cli/gate/gates/verify-approved.d.mts +1 -0
  63. package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
  64. package/dist/cli/gate/gates/verify-approved.mjs +1 -0
  65. package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
  66. package/dist/cli/gate/registry.d.mts.map +1 -1
  67. package/dist/cli/gate/registry.mjs +9 -1
  68. package/dist/cli/gate/registry.mjs.map +1 -1
  69. package/dist/cli/install/cli-to-install-root.d.mts.map +1 -1
  70. package/dist/cli/install/cli-to-install-root.mjs +0 -2
  71. package/dist/cli/install/cli-to-install-root.mjs.map +1 -1
  72. package/dist/cli/install/find-workspace-framework-version.d.mts +16 -0
  73. package/dist/cli/install/find-workspace-framework-version.d.mts.map +1 -0
  74. package/dist/cli/install/find-workspace-framework-version.mjs +85 -0
  75. package/dist/cli/install/find-workspace-framework-version.mjs.map +1 -0
  76. package/dist/cli/install/install-from-source.d.mts +10 -0
  77. package/dist/cli/install/install-from-source.d.mts.map +1 -0
  78. package/dist/cli/install/install-from-source.mjs +271 -0
  79. package/dist/cli/install/install-from-source.mjs.map +1 -0
  80. package/dist/cli/install/install-kernel.d.mts.map +1 -1
  81. package/dist/cli/install/install-kernel.mjs +24 -0
  82. package/dist/cli/install/install-kernel.mjs.map +1 -1
  83. package/dist/cli/install/legacy-paths.d.mts +33 -11
  84. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  85. package/dist/cli/install/legacy-paths.mjs +161 -39
  86. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  87. package/dist/cli/install/runtime-detect.d.mts +2 -6
  88. package/dist/cli/install/runtime-detect.d.mts.map +1 -1
  89. package/dist/cli/install/runtime-detect.mjs +9 -4
  90. package/dist/cli/install/runtime-detect.mjs.map +1 -1
  91. package/dist/cli/install/runtime-migrate.d.mts +34 -14
  92. package/dist/cli/install/runtime-migrate.d.mts.map +1 -1
  93. package/dist/cli/install/runtime-migrate.mjs +63 -30
  94. package/dist/cli/install/runtime-migrate.mjs.map +1 -1
  95. package/dist/cli/install/transforms/codex.d.mts.map +1 -1
  96. package/dist/cli/install/transforms/codex.mjs +28 -17
  97. package/dist/cli/install/transforms/codex.mjs.map +1 -1
  98. package/dist/cli/install/transforms/pi.d.mts +6 -0
  99. package/dist/cli/install/transforms/pi.d.mts.map +1 -0
  100. package/dist/cli/install/transforms/pi.mjs +37 -0
  101. package/dist/cli/install/transforms/pi.mjs.map +1 -0
  102. package/dist/cli/personas/sidecar.d.mts +1 -1
  103. package/dist/cli/roadmap/lifecycle.d.mts +1 -1
  104. package/dist/cli/roadmap/rollup.d.mts +2 -2
  105. package/dist/cli/skill-author/contract.mjs +2 -2
  106. package/dist/cli/skill-author/contract.mjs.map +1 -1
  107. package/dist/cli/verify-proof/audit.d.mts +34 -0
  108. package/dist/cli/verify-proof/audit.d.mts.map +1 -0
  109. package/dist/cli/verify-proof/audit.mjs +53 -0
  110. package/dist/cli/verify-proof/audit.mjs.map +1 -0
  111. package/dist/cli/verify-proof/exec.d.mts +20 -0
  112. package/dist/cli/verify-proof/exec.d.mts.map +1 -0
  113. package/dist/cli/verify-proof/exec.mjs +74 -0
  114. package/dist/cli/verify-proof/exec.mjs.map +1 -0
  115. package/dist/cli/verify-proof/idl/index.d.mts +2 -0
  116. package/dist/cli/verify-proof/idl/index.d.mts.map +1 -0
  117. package/dist/cli/verify-proof/idl/index.mjs +14 -0
  118. package/dist/cli/verify-proof/idl/index.mjs.map +1 -0
  119. package/dist/cli/verify-proof/idl/types.d.ts +9 -0
  120. package/dist/cli/verify-proof/idl/types.d.ts.map +1 -0
  121. package/dist/cli/verify-proof/idl/types.js +9 -0
  122. package/dist/cli/verify-proof/idl/types.js.map +1 -0
  123. package/dist/cli/verify-proof/manifest-validator.d.mts +19 -0
  124. package/dist/cli/verify-proof/manifest-validator.d.mts.map +1 -0
  125. package/dist/cli/verify-proof/manifest-validator.mjs +182 -0
  126. package/dist/cli/verify-proof/manifest-validator.mjs.map +1 -0
  127. package/dist/cli/verify-proof/resolver.d.mts +43 -0
  128. package/dist/cli/verify-proof/resolver.d.mts.map +1 -0
  129. package/dist/cli/verify-proof/resolver.mjs +160 -0
  130. package/dist/cli/verify-proof/resolver.mjs.map +1 -0
  131. package/dist/cli/verify-proof/runtime-detect.d.mts +27 -0
  132. package/dist/cli/verify-proof/runtime-detect.d.mts.map +1 -0
  133. package/dist/cli/verify-proof/runtime-detect.mjs +116 -0
  134. package/dist/cli/verify-proof/runtime-detect.mjs.map +1 -0
  135. package/dist/cli/verify-proof/trust.d.mts +70 -0
  136. package/dist/cli/verify-proof/trust.d.mts.map +1 -0
  137. package/dist/cli/verify-proof/trust.mjs +174 -0
  138. package/dist/cli/verify-proof/trust.mjs.map +1 -0
  139. package/dist/plugin/.claude-plugin/plugin.json +4 -1
  140. package/dist/plugin/agents/apt-executor.md +12 -0
  141. package/dist/plugin/agents/apt-planner.md +5 -4
  142. package/dist/plugin/agents/apt-team-docs-narrator.md +11 -0
  143. package/dist/plugin/skills/apt/SKILL.md +78 -8
  144. package/dist/plugin/skills/apt-caveman/SKILL.md +64 -0
  145. package/dist/plugin/skills/apt-debug/SKILL.md +36 -4
  146. package/dist/plugin/skills/apt-debug/appendices/diagnose-discipline.md +2 -11
  147. package/dist/plugin/skills/apt-discuss/SKILL.md +4 -4
  148. package/dist/plugin/skills/apt-discuss/appendices/grill-discipline.md +2 -11
  149. package/dist/plugin/skills/apt-discuss/appendices/zoom-out-helper.md +0 -8
  150. package/dist/plugin/skills/apt-execute/SKILL.md +40 -4
  151. package/dist/plugin/skills/apt-execute/appendices/tdd-mode.md +2 -10
  152. package/dist/plugin/skills/apt-fan-out/SKILL.md +159 -0
  153. package/dist/plugin/skills/apt-handoff/SKILL.md +228 -0
  154. package/dist/plugin/skills/apt-improve/DEEPENING.md +0 -8
  155. package/dist/plugin/skills/apt-improve/INTERFACE-DESIGN.md +0 -8
  156. package/dist/plugin/skills/apt-improve/LANGUAGE.md +0 -8
  157. package/dist/plugin/skills/apt-improve/SKILL.md +4 -4
  158. package/dist/plugin/skills/apt-plan/SKILL.md +23 -5
  159. package/dist/plugin/skills/apt-plan/adapters/conductor.md +1 -1
  160. package/dist/plugin/skills/apt-pr-review/SKILL.md +8 -1
  161. package/dist/plugin/skills/apt-prototype/LOGIC.md +0 -8
  162. package/dist/plugin/skills/apt-prototype/SKILL.md +4 -4
  163. package/dist/plugin/skills/apt-prototype/UI.md +0 -8
  164. package/dist/plugin/skills/apt-quick/SKILL.md +31 -1
  165. package/dist/plugin/skills/apt-resume/SKILL.md +54 -31
  166. package/dist/plugin/skills/apt-review/SKILL.md +4 -0
  167. package/dist/plugin/skills/apt-run/SKILL.md +134 -21
  168. package/dist/plugin/skills/apt-setup/SKILL.md +97 -8
  169. package/dist/plugin/skills/apt-ship/SKILL.md +165 -9
  170. package/dist/plugin/skills/apt-spar/SKILL.md +77 -25
  171. package/dist/plugin/skills/apt-triage/AGENT-BRIEF.md +0 -8
  172. package/dist/plugin/skills/apt-triage/OUT-OF-SCOPE.md +0 -7
  173. package/dist/plugin/skills/apt-triage/SKILL.md +2 -2
  174. package/dist/plugin/skills/apt-update/SKILL.md +74 -17
  175. package/dist/plugin/skills/apt-verify/SKILL.md +11 -0
  176. package/dist/plugin/skills/apt-verify-proof/SKILL.md +48 -1
  177. package/dist/plugin/skills/apt-watch-ci/SKILL.md +2 -2
  178. package/dist/plugin/skills/apt-zoom-out/SKILL.md +3 -3
  179. package/dist/schemas/quick-task.d.ts +17 -17
  180. package/dist/schemas/quick-task.d.ts.map +1 -1
  181. package/dist/schemas/quick-task.js +2 -2
  182. package/dist/schemas/quick-task.js.map +1 -1
  183. package/dist/types/config.d.ts +33 -0
  184. package/dist/types/config.d.ts.map +1 -1
  185. package/dist/types/state.d.ts +1 -1
  186. package/dist/types/state.d.ts.map +1 -1
  187. package/dist/types/task-record.d.ts +1 -1
  188. package/dist/types/task-record.d.ts.map +1 -1
  189. package/package.json +134 -133
  190. package/prompts/planner.md +1 -1
  191. package/prompts/spec_writer.md +1 -1
  192. package/skills/apt/SKILL.md +78 -8
  193. package/skills/apt-caveman/SKILL.md +64 -0
  194. package/skills/apt-debug/SKILL.md +36 -4
  195. package/skills/apt-debug/appendices/diagnose-discipline.md +2 -11
  196. package/skills/apt-discuss/SKILL.md +4 -4
  197. package/skills/apt-discuss/appendices/grill-discipline.md +2 -11
  198. package/skills/apt-discuss/appendices/zoom-out-helper.md +0 -8
  199. package/skills/apt-execute/SKILL.md +27 -4
  200. package/skills/apt-execute/appendices/tdd-mode.md +2 -10
  201. package/skills/apt-fan-out/SKILL.md +36 -1
  202. package/skills/apt-handoff/SKILL.md +228 -0
  203. package/skills/apt-improve/DEEPENING.md +0 -8
  204. package/skills/apt-improve/INTERFACE-DESIGN.md +0 -8
  205. package/skills/apt-improve/LANGUAGE.md +0 -8
  206. package/skills/apt-improve/SKILL.md +4 -4
  207. package/skills/apt-plan/SKILL.md +23 -5
  208. package/skills/apt-plan/adapters/conductor.md +1 -1
  209. package/skills/apt-planner.md +1 -1
  210. package/skills/apt-prototype/LOGIC.md +0 -8
  211. package/skills/apt-prototype/SKILL.md +4 -4
  212. package/skills/apt-prototype/UI.md +0 -8
  213. package/skills/apt-quick/SKILL.md +31 -1
  214. package/skills/apt-resume/SKILL.md +54 -31
  215. package/skills/apt-run/SKILL.md +114 -16
  216. package/skills/apt-setup/SKILL.md +97 -8
  217. package/skills/apt-ship/SKILL.md +165 -9
  218. package/skills/apt-spar/SKILL.md +65 -16
  219. package/skills/apt-triage/AGENT-BRIEF.md +0 -8
  220. package/skills/apt-triage/OUT-OF-SCOPE.md +0 -7
  221. package/skills/apt-triage/SKILL.md +2 -2
  222. package/skills/apt-update/SKILL.md +74 -17
  223. package/skills/apt-verify-proof/SKILL.md +48 -1
  224. package/skills/apt-zoom-out/SKILL.md +3 -3
  225. package/src/cli/commands/adr.mjs +1 -1
  226. package/src/cli/commands/audit-branch-current.mjs +250 -0
  227. package/src/cli/commands/audit.mjs +13 -2
  228. package/src/cli/commands/check-version.mjs +20 -1
  229. package/src/cli/commands/context.mjs +3 -3
  230. package/src/cli/commands/detect-runtime.mjs +134 -0
  231. package/src/cli/commands/driver-doctor.mjs +294 -0
  232. package/src/cli/commands/gate.mjs +15 -0
  233. package/src/cli/commands/init.mjs +75 -33
  234. package/src/cli/commands/install-from-source.mjs +1 -0
  235. package/src/cli/commands/modes.mjs +1 -1
  236. package/src/cli/commands/route.mjs +42 -1
  237. package/src/cli/commands/triage.mjs +1 -1
  238. package/src/cli/config/load.mjs +9 -3
  239. package/src/cli/coordination/auto-emit-artifact-ready.mjs +1 -1
  240. package/src/cli/coordination/event-schema.mjs +19 -0
  241. package/src/cli/dispatch.mjs +17 -1
  242. package/src/cli/gate/context.mjs +20 -1
  243. package/src/cli/gate/gates/consistency-check.mjs +1 -0
  244. package/src/cli/gate/gates/review-clean.mjs +1 -0
  245. package/src/cli/gate/gates/verify-approved.mjs +1 -0
  246. package/src/cli/gate/registry.mjs +11 -1
  247. package/src/cli/install/cli-to-install-root.mjs +0 -2
  248. package/src/cli/install/find-workspace-framework-version.mjs +88 -0
  249. package/src/cli/install/install-from-source.mjs +276 -0
  250. package/src/cli/install/install-kernel.mjs +24 -0
  251. package/src/cli/install/legacy-paths.mjs +162 -39
  252. package/src/cli/install/runtime-detect.mjs +9 -4
  253. package/src/cli/install/runtime-migrate.mjs +63 -30
  254. package/src/cli/install/transforms/codex.mjs +26 -17
  255. package/src/cli/install/transforms/pi.mjs +37 -0
  256. package/src/cli/skill-author/contract.mjs +2 -2
  257. package/src/cli/verify-proof/.gitkeep +0 -0
  258. package/src/cli/verify-proof/audit.mjs +55 -0
  259. package/src/cli/verify-proof/exec.mjs +74 -0
  260. package/src/cli/verify-proof/idl/index.mjs +24 -0
  261. package/src/cli/verify-proof/idl/types.ts +41 -0
  262. package/src/cli/verify-proof/manifest-schema.json +211 -0
  263. package/src/cli/verify-proof/manifest-validator.mjs +184 -0
  264. package/src/cli/verify-proof/resolver.mjs +163 -0
  265. package/src/cli/verify-proof/runtime-detect.mjs +122 -0
  266. package/src/cli/verify-proof/trust.mjs +187 -0
  267. package/templates/adr-format.md +0 -8
  268. package/templates/aperant-claude-md-appendix.md +1 -1
  269. package/templates/context-format.md +1 -9
  270. package/templates/proof-verification.md +19 -5
  271. package/workflows/verify-proof.md +56 -14
@@ -16,9 +16,12 @@ execution_modes:
16
16
  - step
17
17
  allowed-tools: "Bash, Read, Write, Edit, Grep, Glob"
18
18
  argument-hint: "apt:ship [--draft | --title \"...\"]"
19
- gates:
19
+ gates:
20
+ - constitution-read
21
+ - verify-approved
20
22
  - review-clean
21
- - all-subtasks-complete
23
+ - consistency-check
24
+ - r-persona-alignment
22
25
  - gitignore-in-sync
23
26
  config_keys:
24
27
  - pipeline.auto_branch
@@ -59,7 +62,7 @@ node packages/framework/bin/apt-tools.mjs gate check . --task-id {task-id} --ski
59
62
  ```
60
63
 
61
64
  Parse the JSON envelope:
62
- - If `status == "ok"`: proceed to Section 1.
65
+ - If `status == "ok"`: proceed to Section 0.5 / 1.
63
66
  - If `status == "fail"` AND `forced` is empty (i.e. the user did NOT pass `--force`): STOP. Report the failed gates verbatim from `failed:[{id, severity, reason}]`, then emit:
64
67
  ```
65
68
  Hard gate failed. Fix the issues above, or re-run with:
@@ -70,6 +73,17 @@ Parse the JSON envelope:
70
73
 
71
74
  The ship gate covers constitution-read, verify-approved (qa_signoff.json verdict), review-clean (no unresolved critical/warning findings), gitignore-in-sync (.aperant/.gitignore conformance), and consistency-check (G8 — cross-artifact consistency between spec.md, implementation_plan.json, review.json, qa_signoff.json, and build-progress.json). Because these gates run here, Sections 1–3 below no longer need to re-check the verify verdict or block on review findings — the gate already did it.
72
75
 
76
+ ### 0.1 Parse `profile` from the envelope
77
+
78
+ The envelope carries `profile: "lite" | "full-pipeline"` derived from `taskRecord.track` (QUICK / DEBUG → lite; STANDARD / DEEP → full-pipeline). Capture it now — Sections 1, 2, and 3 branch on it:
79
+
80
+ ```bash
81
+ profile=$(echo "$gate_envelope" | jq -r '.profile // "full-pipeline"')
82
+ ```
83
+
84
+ - **`profile === "lite"`** (QUICK or DEBUG track): the three full-pipeline gates (`verify-approved`, `review-clean`, `consistency-check`) were skipped by design — those tracks do not produce `qa_signoff.json`, `review.json`, or a multi-subtask `implementation_plan.json`. SKIP Sections 1 (review.json + decision notes reads) and 2 (commit-to-subtask map). JUMP to Section 3's lite body branch.
85
+ - **`profile === "full-pipeline"`** (STANDARD or DEEP track): full pipeline ran — Sections 1, 2, and 3's full-body template all apply unchanged.
86
+
73
87
  ## 0.5 Pre-PR Committable Drift Check (C56 B2)
74
88
 
75
89
  Before gathering PR context, audit for uncommitted committable `.aperant/` state and stage anything the self-staging helper missed. This closes the artifact-leak class that the framework shipped with pre-C56: phases flipped in-progress, roadmap JSON touched, decision notes written — none of which followed into the PR's commits.
@@ -97,8 +111,79 @@ Parse the JSON envelope:
97
111
 
98
112
  Classification reference: `packages/framework/docs/artifact-lifecycle.md` (committable vs ephemeral matrix for every `.aperant/` subpath).
99
113
 
114
+ ## 0.7 Pre-PR Branch-Current Audit + Rebase Gate
115
+
116
+ Before any team-sync push (§5) or PR creation (§6), audit whether the task branch has drifted behind `origin/<base_branch>`. A PR opened against a stale base can silently no-op GitHub Actions when path-filter matrices evaluate against the merge-base — the recent Source Control panel pipeline lost ~20 min to this defect class before the operator noticed CI never engaged.
117
+
118
+ This section runs BEFORE Section 5 (Sync Team Bookkeeping). Order is load-bearing: if §0.7 runs after §5, the team-sync push races the post-§0.7 rebase, the captured `remote_tip_sha` is stale by the time §6 leases against it, and the lease fails on a commit `apt:ship` itself just pushed. The TD-02 structural test locks the §0.7 → §5 ordering.
119
+
120
+ Resolve the base branch (same `task get` envelope §6 uses):
121
+
122
+ ```bash
123
+ task_info=$(node packages/framework/bin/apt-tools.mjs task get . --id {task-id})
124
+ base_branch=$(echo "$task_info" | jq -r '.task.base_branch // "main"')
125
+ worktree_path=$(echo "$task_info" | jq -r '.task.worktree_path // empty')
126
+ ship_cwd=${worktree_path:-.}
127
+ ```
128
+
129
+ Run the audit from the worktree (or the project root when no worktree is recorded):
130
+
131
+ ```bash
132
+ audit_env=$(node packages/framework/bin/apt-tools.mjs audit branch-current "$ship_cwd" --base "$base_branch")
133
+ behind=$(echo "$audit_env" | jq -r '.behind // 0')
134
+ ahead=$(echo "$audit_env" | jq -r '.ahead // 0')
135
+ mergeable=$(echo "$audit_env" | jq -r '.mergeable')
136
+ audit_warning=$(echo "$audit_env" | jq -r '.audit_warning // empty')
137
+ remote_tip_sha_pre_rebase=$(echo "$audit_env" | jq -r '.remote_tip_sha // empty')
138
+ ```
139
+
140
+ Capture `remote_tip_sha_pre_rebase` now — §6's lease uses it.
141
+
142
+ **Branch on the envelope:**
143
+
144
+ - **`behind === 0`** → no rebase needed. The branch is current with `origin/<base_branch>` (or strictly ahead). Skip the rebase and proceed to §1. §6's push will use the plain `git push -u origin <branch>` form, not the lease form.
145
+
146
+ - **`behind > 0` AND `mergeable === true`** → run a clean rebase:
147
+ ```bash
148
+ git -C "$ship_cwd" rebase "origin/$base_branch"
149
+ ```
150
+ Proceed to §1. §6's push will use `--force-with-lease=refs/heads/<branch>:$remote_tip_sha_pre_rebase` because history was rewritten.
151
+
152
+ - **`behind > 0` AND `mergeable === false`** → STOP. Emit a structured error with the conflict preview verbatim:
153
+ ```
154
+ [APT:ship] Cannot ship — task branch is behind origin/{base_branch} and the rebase would conflict.
155
+ Conflicts preview:
156
+ - {path1}
157
+ - {path2}
158
+ Resolve manually (rebase or merge in the worktree), then re-run /apt:ship.
159
+ ```
160
+ Exit the skill — do NOT auto-resolve, do NOT proceed to §5 or §6.
161
+
162
+ - **`mergeable === null` AND `audit_warning === "git-version-too-old"`** → STOP with a friendlier message:
163
+ ```
164
+ [APT:ship] Cannot ship — local git is too old to probe mergeability (need git ≥ 2.38 for 'merge-tree --write-tree').
165
+ Upgrade git, rebase manually, or re-run /apt:ship after upgrading.
166
+ ```
167
+ Exit the skill.
168
+
169
+ - **`mergeable === null` AND `audit_warning === "fetch-failed"` or `"no-remote-base-ref"`** → STOP. The audit could not verify the remote tip, so we cannot lease safely. Emit:
170
+ ```
171
+ [APT:ship] Cannot ship — could not fetch origin/{base_branch} ({audit_warning}). Check network + remote, then re-run /apt:ship.
172
+ ```
173
+ Exit the skill.
174
+
175
+ **Autonomy gating.** Read the active task's autonomy level from `.aperant/state.json` (`active_tasks[{task-id}].autonomy`, falling back to `autonomy.default`):
176
+
177
+ - **Autonomy 0–1 (step / guided)** — print the audit envelope to the operator and ask `Rebase now? [Y/n]` before executing the `git rebase` line. On `N`, exit the skill (user can rebase manually and re-invoke).
178
+ - **Autonomy 2 (auto-chain)** — proceed silently on the clean-rebase branch; STOP on conflict regardless.
179
+ - **Autonomy 3 (YOLO)** — same as 2: silent on clean, STOP on conflict. Auto-resolution of conflicts is explicitly out of scope (see spec §Out of Scope).
180
+
181
+ The captured `remote_tip_sha_pre_rebase` and `behind` integer are referenced verbatim in §6's push command. Treat them as read-only state for the remainder of the skill.
182
+
100
183
  ## 1. Gather Context
101
184
 
185
+ **Profile guard:** Sections 1, 2, and 2.6 apply ONLY to `profile === "full-pipeline"`. When the envelope carried `profile === "lite"` (QUICK / DEBUG), SKIP Sections 1, 2, 2.6 and jump straight to Section 3 (`3.lite` branch).
186
+
102
187
  Read all available context for building the PR:
103
188
 
104
189
  ```bash
@@ -197,6 +282,53 @@ The helper lives at `packages/framework/src/cli/release-notes/ship-autodraft.mjs
197
282
 
198
283
  **Title:** Derived from task description. Keep it short (<70 characters).
199
284
 
285
+ ### 3.lite — `profile === "lite"` (QUICK and DEBUG)
286
+
287
+ When the gate envelope carries `profile === "lite"`, emit the lite PR body. QUICK and DEBUG do not produce `qa_signoff.json` or `review.json`, so the lite body sources verification evidence from `task.json.verification` (QUICK: `.aperant/quick/{task-id}/task.json`) or, when no per-track task.json exists (DEBUG fallback), from the active-task record in `state.active_tasks[task_id]`.
288
+
289
+ ```bash
290
+ # QUICK: per-track task.json carries the verification block
291
+ task_json=$(cat .aperant/quick/{task-id}/task.json 2>/dev/null)
292
+ # DEBUG fallback: no per-track task.json — verification is best-effort, omit the block if absent
293
+ ```
294
+
295
+ Lite body shape:
296
+
297
+ ```markdown
298
+ ## Summary
299
+
300
+ {one-liner from task.description in state.active_tasks[task-id]}
301
+
302
+ ## Verification
303
+
304
+ - Typecheck: {task.json.verification.typecheck}
305
+ - Lint: {task.json.verification.lint}
306
+ - Tests: {task.json.verification.test}
307
+ - Visual: {task.json.verification.visual}{when task.json.verification.evidence_path present, append: ` ({evidence_path})`}
308
+
309
+ {IF track === "DEBUG" AND .aperant/debug/{task-id}/DEBUG.md exists, append the RCA section distilled from the file's `## Conclusion` heading. The distillation is best-effort — quote the conclusion paragraph verbatim, keeping it under ~10 lines:}
310
+
311
+ ## RCA
312
+
313
+ {distilled from .aperant/debug/{task-id}/DEBUG.md `## Conclusion` section}
314
+
315
+ ## Commits
316
+
317
+ | Commit | Description |
318
+ |--------|-------------|
319
+ | `{sha}` | {message} |
320
+
321
+ ---
322
+
323
+ Built with [Aperant Framework](https://github.com/Mikalsen-AI/aperant-cloud-desktop/tree/main/packages/framework)
324
+ ```
325
+
326
+ The lite body omits `## QA Scores`, `## Review Findings`, `## Decision Traceability`, and the per-subtask column in `## Commits` — none of those signals exist on lite-profile tasks. Once the body is assembled, skip the rest of Section 3 and jump to Section 4.
327
+
328
+ ### 3.full — `profile === "full-pipeline"` (STANDARD and DEEP)
329
+
330
+ The body structure below is the full-pipeline template — unchanged from the pre-fix behavior. Render it when `profile === "full-pipeline"`.
331
+
200
332
  **Body structure:**
201
333
 
202
334
  ```markdown
@@ -307,27 +439,51 @@ Parse `worktree_path`, `branch`, and `base_branch` from the result.
307
439
 
308
440
  - **If the task record has `branch` and `worktree_path` (task-level worktree isolation active):**
309
441
  ```bash
310
- git -C {worktree_path} push -u origin {branch}
442
+ if [ "$behind" = "0" ]; then
443
+ git -C {worktree_path} push -u origin {branch}
444
+ else
445
+ # §0.7 rebased — lease against the pre-rebase remote tip so a concurrent
446
+ # advance of origin/{branch} between §0.7 and §6 trips the lease instead
447
+ # of silently overwriting the racing party's commit.
448
+ git -C {worktree_path} push --force-with-lease=refs/heads/{branch}:$remote_tip_sha_pre_rebase origin {branch} || {
449
+ echo '[APT:ship] Cannot ship — remote-advanced: origin/{branch} moved between §0.7 audit and §6 push. Re-run /apt:ship.' >&2
450
+ exit 1
451
+ }
452
+ fi
311
453
  gh pr create --title "{title}" --body "{body}" --base {base_branch} --head {branch}
312
454
  ```
313
- The PR targets the recorded `base_branch` (the branch the task was forked from), NOT `main`. This is the correct target when the user is shipping an intermediate milestone onto a longer-lived feature branch.
455
+ The PR targets the recorded `base_branch` (the branch the task was forked from), NOT `main`. This is the correct target when the user is shipping an intermediate milestone onto a longer-lived feature branch. The lease form is used only when §0.7 reported `behind > 0` (history was rewritten); on `behind === 0` a plain push is correct.
314
456
 
315
457
  - **If the task record has `branch` but no `worktree_path`:** Push the branch from the current cwd and create the PR against `base_branch`:
316
458
  ```bash
317
- git push -u origin {branch}
459
+ if [ "$behind" = "0" ]; then
460
+ git push -u origin {branch}
461
+ else
462
+ git push --force-with-lease=refs/heads/{branch}:$remote_tip_sha_pre_rebase origin {branch} || {
463
+ echo '[APT:ship] Cannot ship — remote-advanced: origin/{branch} moved between §0.7 audit and §6 push. Re-run /apt:ship.' >&2
464
+ exit 1
465
+ }
466
+ fi
318
467
  gh pr create --title "{title}" --body "{body}" --base {base_branch} --head {branch}
319
468
  ```
320
469
 
321
- - **If no task branch is recorded and we're on main/master:** Legacy behaviour — create a feature branch first:
470
+ - **If no task branch is recorded and we're on main/master:** Legacy behaviour — create a feature branch first. §0.7 only ran when a task record exists; this path is reached only on ad-hoc shipping and the rebase gate is necessarily skipped, so a plain push is correct (no rewritten history to lease against):
322
471
  ```bash
323
472
  git checkout -b {task-slug}
324
473
  git push -u origin {task-slug}
325
474
  gh pr create --title "{title}" --body "{body}"
326
475
  ```
327
476
 
328
- - **If no task branch is recorded and we're already on a feature branch:** Legacy behaviour — push HEAD and open a PR against `main`:
477
+ - **If no task branch is recorded and we're already on a feature branch:** Legacy behaviour — push HEAD and open a PR against `main`. When §0.7 ran (task record present), the lease form applies when `behind > 0`; on the ad-hoc / no-task-record path the gate is skipped and a plain push is correct:
329
478
  ```bash
330
- git push -u origin HEAD
479
+ if [ -n "${remote_tip_sha_pre_rebase:-}" ] && [ "$behind" != "0" ]; then
480
+ git push --force-with-lease=refs/heads/$(git rev-parse --abbrev-ref HEAD):$remote_tip_sha_pre_rebase origin HEAD || {
481
+ echo '[APT:ship] Cannot ship — remote-advanced: branch tip moved between §0.7 audit and §6 push. Re-run /apt:ship.' >&2
482
+ exit 1
483
+ }
484
+ else
485
+ git push -u origin HEAD
486
+ fi
331
487
  gh pr create --title "{title}" --body "{body}"
332
488
  ```
333
489
 
@@ -29,7 +29,7 @@ Two boundaries with adjacent skills:
29
29
  - **Spar is not a review skill.** `/codex:adversarial-review` and `/apt:review` are one-shot. Spar loops until convergence or the round cap.
30
30
  - **Spar is bidirectional 1:1.** Roundtable is 10-agent. If you want a 3-way debate, use `/apt:roundtable`.
31
31
 
32
- Hard rule, v1: **no artifact directory written.** The transcript stays in-conversation. The host prints a final verdict block, nothing persists to disk under `.aperant/sparring/` or anywhere else.
32
+ Termination posture (v1.1, amending SPAR-06): the termination headline is in-conversation; the long-form converged position is written to a single timestamped file under `.aperant/spar/`. No conversation-transcript dump, no `.aperant/sparring/` directory see `<state_files>` for the exact contract.
33
33
  </objective>
34
34
 
35
35
  <your_environment>
@@ -52,7 +52,7 @@ Hard rule, v1: **no artifact directory written.** The transcript stays in-conver
52
52
  - The live conversation transcript (when `[topic]` is empty)
53
53
 
54
54
  **Writes:**
55
- - **None.** No artifact directory written transcript stays in-conversation per SPAR-06. The final verdict is printed to stdout; nothing is persisted under `.aperant/sparring/` or any other path. This is a hard contractdo not "helpfully" add a transcript-dump file.
55
+ - A single markdown file at `.aperant/spar/<short-slug>-<YYYYMMDD-HHMMSS>.md` containing the long-form converged position, the verdict-per-round table, and any parallel-bug call-outs. No subdirectories; flat layout mirroring `.aperant/debug/` and `.aperant/roundtables/`. Write-once; not loaded back into context on subsequent runs. This narrows the SPAR-06 commitment (amended 2026-05-21 see task `apt-spar-readable-termination-summary_21-05-26`): the conversation transcript still stays in-conversation, full-transcript dumps to `.aperant/sparring/` are still rejected, but the long-form converged position is now persisted so it survives terminal close.
56
56
  </state_files>
57
57
 
58
58
  <rationalization>
@@ -64,7 +64,7 @@ The verify-before-cede rule is the whole skill. Every rationalization below name
64
64
  | "I don't need to verify — I already know I'm right." | Holding without evidence is equally forbidden. Your prior beliefs are not citations. If you can't open a file and point at a line, you don't actually know — you remember. The rule is symmetric: cede with evidence OR hold with evidence; never neither. |
65
65
  | "Only 1 round is needed — the partner's first response settles it." | True only if verification was performed. If round 1 ended with `verdict: cede` or `verdict: hold` and the verdict cited concrete evidence (`file:line`, test output, doc reference), terminating after round 1 is fine and expected. The danger is treating round 1 as a verdict because the partner's response *felt* conclusive. Speed without verification is not victory. |
66
66
  | "I'll skip host-detection and just call codex directly." | This breaks the bidirectional contract. `apt:spar` must work whether the user is on Claude Code, Codex CLI, Gemini CLI, or any of the 15+ runtimes `host-detect` covers. Hardcoding a partner makes the skill Claude-Code-centric and silently breaks the Codex→Claude direction. Always run `host-detect` (or honor `--with`) — never assume the host. |
67
- | "I'll persist the transcript so the user can revisit it." | SPAR-06 is non-negotiable in v1: no artifact directory written. The conversation transcript is the artifact. Adding a write under `.aperant/sparring/` creates an orphan-cleanup concern the user explicitly rejected; if persistence is needed later, that's a follow-up task, not a "helpful" addition here. |
67
+ | "I'll dump the full conversation transcript to disk." | Wrong scope. The SPAR-06 amendment (2026-05-21) writes ONLY the long-form converged position + verdict-per-round table + parallel call-outs to ONE timestamped file under `.aperant/spar/`. The conversation transcript stays in-conversation; full-transcript dumps to `.aperant/sparring/` are still rejected per the original SPAR-06 rationale (orphan-cleanup concern). Stick to the bounded artifact. |
68
68
  </rationalization>
69
69
 
70
70
  <autonomy_interaction>
@@ -74,7 +74,7 @@ The verify-before-cede rule is the whole skill. Every rationalization below name
74
74
  - **Autonomy 2 (Balanced, default):** Run the full loop (up to `--rounds N`, default 3) without mid-loop pauses. Pause **once** before the final converge/escalate decision in `<process>` §9 so the user can accept the termination summary or redirect.
75
75
  - **Autonomy 3 (YOLO):** Run end-to-end with no pauses. Print the termination summary and return.
76
76
 
77
- **v1 has no auto-trigger hooks (SPAR-07).** `apt:spar` is manually invoked onlyneither `/apt:plan` nor `/apt:review` chain into it automatically. Manual usage data will tell us whether auto-triggering helps; until then, deferring keeps the surface small.
77
+ **v2 honors install-time consent (SPAR-07).** When `.aperant/config.local.json` carries `spar.enabled: true` (set via the `/apt:setup` install prompt) AND `/apt:run` was invoked withor the `/apt` router auto-injected — `--spar-gates plan` (STANDARD) or `--spar-gates plan,execute` (DEEP), `/apt:spar` auto-fires between named pipeline stages. Manual `/apt:spar` invocation remains the canonical path (§1–§9 unchanged); auto-fire is opt-in per-device, never default-on per-project, never enabled on QUICK (Fast Path Guarantee). v1's "manually invoked only" rule was always deferred-pending-data per the original SPAR-07 escape hatch; founder manual-usage data is that trigger.
78
78
  </autonomy_interaction>
79
79
 
80
80
  <process>
@@ -298,21 +298,70 @@ The hard cap of 3 rounds is non-negotiable in v1 (see SPAR-05). Long-tail disagr
298
298
 
299
299
  ## 9. Termination Report
300
300
 
301
- Print a final block:
301
+ The termination report has two layers. **Layer 1 — Headline** is printed to stdout and is the only thing the user reads to understand the spar at a glance. **Layer 2 — Detail** is the verbatim long-form converged position; it is persisted to a single timestamped file under `.aperant/spar/` so it survives terminal close (per the SPAR-06 amendment of 2026-05-21 — see `<state_files>`).
302
302
 
303
+ ### Layer 1 — Headline (always printed)
304
+
305
+ Emit exactly this shape — `<-- copy this exact shape -->`:
306
+
307
+ ```text
308
+ === apt:spar — <outcome> ===
309
+ Topic: <one-line topic restatement>
310
+ Host: <cli-id> ↔ Partner: <cli-id> · Rounds: <n>/<cap>
311
+
312
+ Round 1: <one-sentence shift — what changed in the host's position, or what the partner caught>
313
+ Round 2: <one-sentence shift, omit this line entirely if rounds<2>
314
+ Round 3: <one-sentence shift, omit this line entirely if rounds<3>
315
+
316
+ Outcome: <one-sentence plain-English statement of what was agreed/disagreed and what it means for the next plan>
317
+
318
+ Next: <ONE suggested action — a single slash command or a single human-readable instruction. NOT a 4-item menu.>
319
+
320
+ Full detail: <path>
303
321
  ```
304
- === apt:spar — termination summary ===
305
- Topic: <one-line>
306
- Host: <cli-id> Partner: <cli-id> Rounds: <n>/<cap>
307
- Outcome: converged | held-disagreement | round-cap | partner-unreachable
308
- Converged position OR Held disagreement: <one-paragraph>
309
-
310
- Recommended next step:
311
- - continue: <best follow-up usually `/apt:plan` or `/apt:execute`>
312
- - escalate: `/apt:roundtable "<topic>"` for a 10-agent debate
313
- - pause: no further action; surface to the user for human input
322
+
323
+ Constraints baked into the headline (do NOT relax):
324
+
325
+ - **One sentence per round, max ~25 words.** No bullets, no nested lists.
326
+ - **No code fences, no JSON, no ASCII tables inside the headline.** Schemas, trust models, verdict-per-round tables, and parallel-bug call-outs all go in the Layer 2 file — never in the headline.
327
+ - **No trailing disposition question.** Do not append "Want me to file this?" or any similar re-ask. The autonomy policy in `<autonomy_interaction>` already governs whether the host pauses for user input; the headline does not re-prompt.
328
+ - **Outcome vocabulary** is exactly one of: `converged`, `held-disagreement`, `round-cap`, `partner-unreachable`. Do not invent new outcome words. These are the terminal-outcome words for the spar as a whole; they are distinct from the §7 per-round verdict words (`cede` / `hold` / `next-round` / `partner-unreachable`) — `partner-unreachable` is the only word shared between the two vocabularies.
329
+ - **`Full detail:`** names the path to the Layer 2 file written below.
330
+
331
+ ### Layer 2 Persist the long-form (Path B)
332
+
333
+ ALSO write a single markdown file at `.aperant/spar/<slug>-<YYYYMMDD-HHMMSS>.md` containing the verbatim long-form: the converged-position content (architecture, schemas, trust model), the verdict-per-round table, and any parallel-bug call-outs the spar surfaced. The headline's `Full detail:` line MUST reference this exact path.
334
+
335
+ **Slug derivation.** Lowercase the topic; replace each run of non-`[a-z0-9]` characters with a single `-`; trim leading/trailing `-`; take the first 6 hyphen-separated tokens; truncate the resulting string to 40 chars; trim any trailing `-` left by truncation. Final form MUST match `^[a-z0-9-]{1,40}$` — the slug is interpolated directly into the path string, so any character outside `[a-z0-9-]` is a path-injection vector and must NOT survive this step. Example: topic `"Should we use Shape C++ for the IDL layer or stay with Shape B"` → tokens `should we use shape c for` (the `++` collapses with the trailing whitespace into a single `-`) → slug `should-we-use-shape-c-for` (24 chars; under the 40-char cap).
336
+
337
+ **Timestamp format.** `YYYYMMDD-HHMMSS` (UTC, per-second granularity). Two spar invocations within the same second produce identical paths — acceptable risk for v1.1.
338
+
339
+ **Write invocation.** Use the same single-quoted heredoc safety pattern §5 uses for prompt-quoting — the body is opaque text and may legitimately contain backticks, `$(...)`, or other shell-active characters that bash would otherwise expand:
340
+
341
+ ```bash
342
+ mkdir -p .aperant/spar && cat > ".aperant/spar/<slug>-<YYYYMMDD-HHMMSS>.md" <<'APT_SPAR_LONGFORM_EOF'
343
+ # apt:spar long-form — <topic>
344
+
345
+ **Host:** <cli-id> · **Partner:** <cli-id> · **Rounds:** <n>/<cap> · **Outcome:** <outcome>
346
+
347
+ ## Converged position (or held disagreement)
348
+
349
+ <verbatim long-form: architecture, schemas, trust model, etc.>
350
+
351
+ ## Verdict per round
352
+
353
+ | Round | What host held | What partner caught | What host conceded |
354
+ |-------|----------------|---------------------|--------------------|
355
+ | 1 | ... | ... | ... |
356
+
357
+ ## Parallel call-outs
358
+
359
+ <bugs / observations surfaced during sparring that are independent of the topic; omit the whole section if none>
360
+ APT_SPAR_LONGFORM_EOF
314
361
  ```
315
362
 
316
- Nothing is written to disk the termination summary is in-conversation output only (per SPAR-06).
363
+ The `'APT_SPAR_LONGFORM_EOF'` (single-quoted heredoc terminator) is load-bearing — identical to the `APT_SPAR_PROMPT_EOF` convention in §5. It disables variable/command substitution inside the body so an attacker-controlled topic cannot execute commands.
364
+
365
+ This file is write-once and is NOT loaded back into context on subsequent `apt:spar` runs — it is purely a preservation artifact for the user.
317
366
 
318
367
  </process>
@@ -1,11 +1,3 @@
1
- <!--
2
- Adapted from Matt Pocock's MIT-licensed skill suite:
3
- https://github.com/mattpocock/skills/blob/main/skills/engineering/triage/AGENT-BRIEF.md
4
- Licensed under MIT. Modifications: aligned with Aperant's 5-state
5
- machine (needs-triage / needs-info / ready-for-agent / ready-for-human /
6
- wontfix) and the backend-pluggable mirror model (local-only / github-issues / app-inbox).
7
- -->
8
-
9
1
  # Triage Agent Brief — what to do with each state
10
2
 
11
3
  ## needs-triage (inbound)
@@ -1,10 +1,3 @@
1
- <!--
2
- Adapted from Matt Pocock's MIT-licensed skill suite:
3
- https://github.com/mattpocock/skills/blob/main/skills/engineering/triage/OUT-OF-SCOPE.md
4
- Licensed under MIT. Modifications: aligned with Aperant's pipeline
5
- (other skills exist for the out-of-scope concerns — explicit pointers).
6
- -->
7
-
8
1
  # Triage Out-of-Scope
9
2
 
10
3
  What this skill is NOT. Knowing the boundary prevents triage from
@@ -31,8 +31,8 @@ This skill moves a task through a 5-state machine. The state machine
31
31
  runs identically regardless of backend (local-only / github-issues /
32
32
  app-inbox); only the mirror destination changes.
33
33
 
34
- **Pocock adoption attribution.** State machine + AGENT-BRIEF.md +
35
- OUT-OF-SCOPE.md sub-files ported from Matt Pocock's MIT-licensed
34
+ **Aperant adoption attribution.** State machine + AGENT-BRIEF.md +
35
+ OUT-OF-SCOPE.md sub-files ported from
36
36
  `triage` skill. Aperant-specific extensions:
37
37
 
38
38
  - Backend abstraction (configured at `apt:setup` Batch 8 — task_tracking.backend)
@@ -49,13 +49,14 @@ The cache file is at `~/.cache/aperant/apt-update-check.json`. It contains `runt
49
49
  node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs check-version . --json
50
50
  ```
51
51
 
52
- Parse the JSON. From 0.6.7 the envelope merges the SessionStart worker's cache (when fresh — written within the last hour to `~/.cache/aperant/apt-update-check.json`). Capture:
52
+ Parse the JSON. From 0.6.7 the envelope merges the SessionStart worker's cache (when fresh — written within the last hour to `~/.cache/aperant/apt-update-check.json`). From 0.8.3 it also carries the workspace-aware `framework.workspace_version` field (Bundle A). Capture:
53
53
 
54
54
  - `framework.installed_version` — what's currently on disk
55
55
  - `framework.latest_version` — what npm reports as the latest published version (MAY BE `null` when the worker cache is stale / missing / wrong schema)
56
+ - `framework.workspace_version` — set when a workspace `packages/framework/package.json` (with `name === "@aperant/framework"`) is found above the project root; null otherwise. When non-null AND ahead of `installed_version`, the workspace is the source of truth for self-host contributors (auto-detect, no `--from-source` required).
56
57
  - `runtimes[]` — list of installed runtime ids + per-runtime `stale_files` count
57
58
  - `stale_files_total` — drift tally across runtimes
58
- - `update_available` — true when worker cache is fresh AND `installed_version < latest_version`
59
+ - `update_available` — true when worker cache is fresh AND `installed_version < latest_version`, OR when `workspace_version > installed_version` (Bundle A — fires regardless of npm cache state)
59
60
  - `dev_install_ahead` — true when local kernel is ahead of published (typical local dev)
60
61
  - `not_published` — true when npm returned 404 (package not yet on registry)
61
62
  - `lookup_error` — non-null when npm view failed for any non-404 reason (timeout, ENETDOWN, etc.)
@@ -64,11 +65,20 @@ The envelope is **single-shape** — every field above is always present. When t
64
65
 
65
66
  If `runtimes[]` is empty, tell the user they haven't installed Aperant into any runtime yet and suggest `apt-tools init . --claude` (or their preferred runtime). Stop.
66
67
 
67
- ## 1.5. Bump the local kernel from npm when newer is available
68
+ ## 1.5. Workspace-aware bump auto-detect dev-host self-host (Bundle A)
68
69
 
69
- The Aperant kernel lives at `.aperant/deps/node_modules/@aperant/framework/`. `apt-tools init` re-installs runtime files at THE LOCAL KERNEL'S VERSION — so a stale kernel means `/apt:update` re-stamps manifests at the OLD version even when npm has a newer release. Bump the kernel BEFORE invoking init.
70
+ The Aperant kernel lives at `.aperant/deps/node_modules/@aperant/framework/`. `apt-tools init` re-installs runtime files at THE LOCAL KERNEL'S VERSION — so a stale kernel means `/apt:update` re-stamps manifests at the OLD version. Bump the kernel BEFORE invoking init.
70
71
 
71
- First, resolve `latest_version` when the envelope had `null` (stale cache).
72
+ **FIRST**, branch on `framework.workspace_version` from step 1's envelope (Bundle A — automatic on every `/apt:update` run, no flag required):
73
+
74
+ - When `workspace_version` is non-null AND ahead of `installed_version` — the user is an Aperant contributor self-hosting the framework, and a same-PR change bumped `packages/framework/package.json:version`. The workspace is the source of truth (NOT npm). Proceed to §1.7's workspace-pack mechanism. Skip §1.6 (npm view).
75
+ - When `workspace_version` is null OR equal to `installed_version` — no workspace package, OR workspace + kernel already match. Fall through to §1.6 (npm bump path).
76
+
77
+ This auto-detection replaces the previous "user must remember `--from-source`" footgun. The `--from-source` flag is still honored as an explicit override (§1.7) for: (a) testing pre-publish bumps even when auto-detect didn't fire, (b) forcing the workspace path when a stale `latest_version > workspace_version`.
78
+
79
+ ## 1.6. Bump the local kernel from npm when newer is available
80
+
81
+ When §1.5 fell through (no workspace-ahead signal), resolve `latest_version` from npm. First, resolve `latest_version` when the envelope had `null` (stale cache).
72
82
 
73
83
  **Timeout enforcement (ID-04):** The `npm view` call must complete within 10 s — never block the user longer than the SessionStart worker would. Use npm's `--fetch-timeout` flag as the primary mechanism (universally portable across all npm versions); it sets the HTTP fetch timeout in milliseconds:
74
84
 
@@ -90,17 +100,47 @@ npm install --prefix .aperant/deps @aperant/framework@<latest_version> --no-save
90
100
 
91
101
  On `npm 404` (`E404` in stderr) treat it as `not_published` and skip the kernel bump (proceed to step 3). On any other npm failure, surface the npm stderr verbatim and STOP — do NOT proceed to init with a half-installed kernel.
92
102
 
93
- ## 1.6. Self-host case — bump from local workspace (FRAMEWORK-BUG-017)
103
+ ## 1.7. Self-host case — bump from local workspace (FRAMEWORK-BUG-017)
104
+
105
+ Fires when EITHER §1.5's workspace-ahead auto-detect signaled it, OR the user invoked `/apt:update --from-source` as an explicit override, OR the route envelope's `update_check.reason` mentions `workspace source ahead of installed kernel`. The source of truth is the local `packages/framework/` workspace — NOT npm. This is the Aperant-contributor self-host case: a same-PR framework fix bumps the workspace `packages/framework/package.json:version` but the kernel under `.aperant/deps/node_modules/@aperant/framework/` still reflects the previous version. §1.6's `npm view` would fetch the published version (still the OLD one), so we skip it.
106
+
107
+ The reliable bump mechanism is the `apt-tools install-from-source` subcommand (Bundle D), which packs the framework AND every `workspace:*` dep (transitively) into a single `npm install --prefix` invocation. Handles the same-PR unpublished-pair case (e.g. `framework@0.8.3` + `driver-sdk@0.2.0` both at `workspace:*`, both unpublished) without `EUNSUPPORTEDPROTOCOL`.
108
+
109
+ ```bash
110
+ node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs install-from-source . --json
111
+ ```
112
+
113
+ Parse the returned envelope:
114
+
115
+ ```json
116
+ {
117
+ "status": "ok",
118
+ "command": "install-from-source",
119
+ "tarballs": ["/tmp/aperant-driver-sdk-0.2.0.tgz", "/tmp/aperant-framework-0.8.3.tgz"],
120
+ "kernel_path": "/path/to/project/.aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs"
121
+ }
122
+ ```
123
+
124
+ Capture `tarballs[]` count and `kernel_path` for the Scenario A summary line (the kernel-version slot reads from `kernel_path`'s parent `package.json` rather than a static `{latest_version}` format string when this branch fired).
125
+
126
+ On `status: 'error'`, the envelope carries an `error` field with the underlying failure (e.g. `pnpm pack ... exited with N`, `no @aperant/framework workspace found`, etc.). Surface it verbatim and STOP — do NOT proceed to init with a half-installed kernel.
127
+
128
+ If `--from-source` was passed but `install-from-source` returns `no @aperant/framework workspace found`, surface a one-line error (`⚠ --from-source: no workspace package.json found — falling back to npm`) and proceed with the standard §1.6 npm path.
129
+
130
+ Then proceed to step 3 to re-install the runtime files at the new kernel version. Skip step 4 (cache clear) — the cache is keyed on `latest_version` from npm, and a `--from-source` bump never wrote to it, so there's nothing to invalidate.
94
131
 
95
- When the user invokes `/apt:update --from-source` OR the route envelope's `update_check.reason` mentions `workspace source ahead of installed kernel`, the source of truth is the local `packages/framework/` workspace — NOT npm. This is the Aperant-contributor self-host case: a same-PR framework fix bumps the workspace `packages/framework/package.json:version` but the kernel under `.aperant/deps/node_modules/@aperant/framework/` still reflects the previous version. Step 1.5's `npm view` would fetch the published version (still the OLD one), so we must skip it.
132
+ ### What `install-from-source` does under the hood
96
133
 
97
- The reliable bump mechanism mirrors what `runKernelInstall` does today`pnpm pack` produces a versioned tarball, then `npm install --prefix` consumes it. Symlinking is fragile across re-installs and `pnpm install --filter` doesn't relocate the published artifact into `.aperant/deps`.
134
+ The `apt-tools install-from-source` helper materializes the workspace chain end-to-end. This is the equivalent manual recipe useful for debugging or for users who want to understand the mechanism. **Prefer the one-line invocation above** unless you have a reason to do it by hand:
98
135
 
99
- Walk up from the project root looking for a sibling `packages/framework/package.json`. When found AND its `name === "@aperant/framework"`:
136
+ Do NOT substitute `npm pack` it leaves `workspace:*` literal in the tarball's package.json and breaks `npm install --prefix` with `EUNSUPPORTEDPROTOCOL`. Only `pnpm pack` resolves workspace protocols.
100
137
 
101
138
  ```bash
102
- # 1. Pack the workspace source into a versioned tarball.
139
+ # 0. Ensure the dep closure is materialized the `...` suffix installs transitive workspace deps.
103
140
  cd <workspace-root>
141
+ pnpm install --filter @aperant/framework... --frozen-lockfile=false
142
+
143
+ # 1. Pack the workspace source into a versioned tarball.
104
144
  pnpm --filter @aperant/framework pack --pack-destination /tmp
105
145
 
106
146
  # 2. Install the tarball into the project's kernel root.
@@ -108,19 +148,16 @@ cd <project-root>
108
148
  npm install --prefix .aperant/deps /tmp/aperant-framework-<version>.tgz --no-save --no-package-lock --no-audit --no-fund
109
149
  ```
110
150
 
111
- Replace `<version>` with the workspace `package.json:version` you just read. The tarball name follows npm's `pack` convention (`@aperant/framework@X.Y.Z` `aperant-framework-X.Y.Z.tgz`).
112
-
113
- If `--from-source` was passed but no `packages/framework/package.json` is found anywhere above the project root, surface a one-line error (`⚠ --from-source: no workspace package.json found — falling back to npm`) and proceed with the standard step 1.5 npm path.
114
-
115
- Then proceed to step 3 to re-install the runtime files at the new kernel version. Skip step 4 (cache clear) — the cache is keyed on `latest_version` from npm, and a `--from-source` bump never wrote to it, so there's nothing to invalidate.
151
+ The manual recipe DOES NOT handle workspace:* deps transitively — for a same-PR pair (framework + driver-sdk both at `workspace:*`), you would need to pnpm-pack each dep separately and pass every tarball to the single `npm install --prefix` call. The `apt-tools install-from-source` subcommand walks `dependencies` + `peerDependencies` automatically.
116
152
 
117
153
  ## 2. Classify the update scenario
118
154
 
119
155
  Pick ONE of three scenarios based on what you parsed in step 1:
120
156
 
121
- - **A. Version bump available** — `installed_version !== latest_version` (regardless of drift). Users see this most often. Step 1.5 bumped the kernel; step 3 re-stamps manifests at the new version.
122
- - **B. Drift only** — `installed_version === latest_version` but `stale_files_total > 0`. User hand-edited an installed file or something was partially overwritten. Step 1.5 was a no-op; step 3 still needs `--force-runtimes` to re-stamp.
157
+ - **A. Version bump available** — `latest_version != null && installed_version !== latest_version` (regardless of drift). Users see this most often. Step 1.5 (workspace) or 1.6 (npm) bumped the kernel; step 3 re-stamps manifests at the new version. **Important:** the `latest_version != null` guard prevents this scenario from firing when npm-latest is unknown (cache stale, package not published, network down — see Scenario E for that case).
158
+ - **B. Drift only** — `installed_version === latest_version` but `stale_files_total > 0`. User hand-edited an installed file or something was partially overwritten. Steps 1.5 / 1.6 were no-ops; step 3 still needs `--force-runtimes` to re-stamp.
123
159
  - **C. Nothing to do** — `installed_version === latest_version` AND `stale_files_total === 0`. Still re-run init with `--force-runtimes` to refresh manifests + cache, but tell the user plainly that no version change happened.
160
+ - **E. No version info available** — `latest_version === null` (cache stale OR package not published OR network down) AND `stale_files_total === 0`. Step 1.5 was a no-op (no workspace-ahead signal), step 1.6 found no usable npm version. Step 3 still re-stamps runtime files for safety, but the skill does NOT claim a version bump happened.
124
161
 
125
162
  ## 3. Re-install the currently-installed runtimes
126
163
 
@@ -169,6 +206,7 @@ Use the format in `<output_format>` below, tailored to the scenario from step 2.
169
206
  {install_root_basename} {file_count} files refreshed
170
207
  {install_root_basename} {file_count} files refreshed
171
208
  .aperant/deps/ kernel v{latest_version}
209
+ Migrated: legacy .codex/agents/* swept ({removed_count} files removed)
172
210
 
173
211
  Next: restart Claude Code / Codex to activate the refreshed skills.
174
212
  ```
@@ -194,6 +232,7 @@ Example:
194
232
 
195
233
  {install_root_basename} {file_count} files ({stale_count} refreshed)
196
234
  {install_root_basename} {file_count} files
235
+ Migrated: legacy .codex/agents/* swept ({removed_count} files removed)
197
236
 
198
237
  Statusline chip cleared.
199
238
  ```
@@ -205,10 +244,28 @@ Example:
205
244
 
206
245
  {install_root_basename} {file_count} files
207
246
  {install_root_basename} {file_count} files
247
+ Migrated: legacy .codex/agents/* swept ({removed_count} files removed)
208
248
 
209
249
  No changes applied. Statusline cache cleared.
210
250
  ```
211
251
 
252
+ ## Scenario E — no version info available
253
+
254
+ ```
255
+ ⚠ Aperant version info unavailable — refreshed runtime files only
256
+
257
+ Reason: {lookup_error || 'cache stale / not published'}
258
+ {install_root_basename} {file_count} files
259
+ {install_root_basename} {file_count} files
260
+ Migrated: legacy .codex/agents/* swept ({removed_count} files removed)
261
+
262
+ Run /apt:update again later to bump the kernel when npm view becomes reachable.
263
+ ```
264
+
265
+ Scenario E fires when `latest_version === null` AND `stale_files_total === 0`. Step 1.5 (workspace-aware) was a no-op (no workspace-ahead signal); step 1.6 (npm view) returned null (cache stale, not published, or network down). The runtime refresh in step 3 still happens — the skill just does NOT claim a bump occurred.
266
+
267
+ **`Migrated:` slot — conditional render.** The `Migrated:` line in Scenarios A / B / C / E fires ONLY when `init.legacy_cleanup?.ran === true && init.legacy_cleanup.rmdirs.some(p => p.includes('.codex'))`. When no migration happened, OMIT the line entirely (do NOT render an empty placeholder). The label reports the legacy Codex topology sweep (v0.7/v0.8 `.codex/agents/` + `.codex/commands/` misdirect → v0.8.2+ canonical `.agents/skills/<folder>/SKILL.md`) so users understand what changed under `.codex/` on disk.
268
+
212
269
  ## Scenario D — kernel install failed
213
270
 
214
271
  If init's `kernel.status !== "installed"`, append a warning ABOVE the summary:
@@ -40,9 +40,56 @@ an action, evidence, and a verdict. No hallucinated passes.
40
40
  - For code review — use `/apt:review`
41
41
  - If no UI/visual changes were made — `/apt:verify` is faster
42
42
 
43
+ ## Driver Architecture
44
+
45
+ `/apt:verify-proof` is **runtime-agnostic** — the workflow refers to IDL
46
+ verbs (`driver.click`, `driver.screenshot`, `driver.navigate`, ...) and
47
+ NEVER to concrete MCP tool names (per ID-01). The driver layer at
48
+ `packages/framework/drivers/<id>/` maps verbs to transports.
49
+
50
+ ### Auto-detect
51
+
52
+ The resolver auto-selects the highest-scoring driver per ID-02
53
+ (capability_coverage > compatibility > stability > priority > locality).
54
+ Preview the selection before running verify-proof:
55
+
56
+ ```bash
57
+ apt-tools detect-runtime .
58
+ # → { runtime: {is_electron, is_nextjs, ...}, selected_driver_id: "..." }
59
+ ```
60
+
61
+ ### Override
62
+
63
+ Force a specific driver chain in `.aperant/config.json`:
64
+
65
+ ```json
66
+ { "verification": { "runtimes": ["electron", "cli"] } }
67
+ ```
68
+
69
+ ### Bundled drivers (4)
70
+
71
+ - **browser** (priority=100, ga) — Next.js / vanilla web frontends.
72
+ - **electron** (priority=90, ga) — Electron desktop apps.
73
+ - **cli** (priority=60, ga) — headless CI, output-shape assertions.
74
+ - **api** (priority=50, ga) — HTTP / JSON-API regression tests.
75
+
76
+ ### Driver authors
77
+
78
+ Third-party driver authors should run `apt-tools driver doctor .` before
79
+ publishing to validate manifest schema + IDL conformance:
80
+
81
+ ```bash
82
+ apt-tools driver doctor .
83
+ # → { drivers: [{driverId, manifest_valid, idl_conformant, ...}], summary: {...} }
84
+ ```
85
+
86
+ See `@aperant/driver-sdk` for the IDL types, error taxonomy, and
87
+ conformance kit.
88
+
43
89
  ## Execution
44
90
 
45
- Execute the verify-proof workflow end-to-end:
91
+ Execute the verify-proof workflow end-to-end using IDL verbs (the driver
92
+ maps verbs to its transport — MCP server, local command, HTTP):
46
93
 
47
94
  <execution_context>
48
95
  @packages/framework/workflows/verify-proof.md