@aperant/framework 0.6.7 → 0.7.3

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 (205) hide show
  1. package/CHANGELOG.md +240 -0
  2. package/agents/apt-planner.md +12 -0
  3. package/agents/apt-pr-review-fixer.md +13 -9
  4. package/bin/apt-tools.mjs +7 -0
  5. package/dist/cli/ci-watch/stop-matrix.d.mts.map +1 -1
  6. package/dist/cli/ci-watch/stop-matrix.mjs +16 -0
  7. package/dist/cli/ci-watch/stop-matrix.mjs.map +1 -1
  8. package/dist/cli/commands/ci-watch.d.mts +11 -0
  9. package/dist/cli/commands/ci-watch.d.mts.map +1 -1
  10. package/dist/cli/commands/ci-watch.mjs +147 -3
  11. package/dist/cli/commands/ci-watch.mjs.map +1 -1
  12. package/dist/cli/commands/features-audit.d.mts +24 -0
  13. package/dist/cli/commands/features-audit.d.mts.map +1 -1
  14. package/dist/cli/commands/features-audit.mjs +159 -5
  15. package/dist/cli/commands/features-audit.mjs.map +1 -1
  16. package/dist/cli/commands/health-check.d.mts +16 -0
  17. package/dist/cli/commands/health-check.d.mts.map +1 -1
  18. package/dist/cli/commands/health-check.mjs +119 -3
  19. package/dist/cli/commands/health-check.mjs.map +1 -1
  20. package/dist/cli/commands/init.d.mts +19 -1
  21. package/dist/cli/commands/init.d.mts.map +1 -1
  22. package/dist/cli/commands/init.mjs +143 -8
  23. package/dist/cli/commands/init.mjs.map +1 -1
  24. package/dist/cli/commands/modes.d.mts.map +1 -1
  25. package/dist/cli/commands/modes.mjs +11 -0
  26. package/dist/cli/commands/modes.mjs.map +1 -1
  27. package/dist/cli/commands/pr-review-audit-fixer.d.mts +13 -0
  28. package/dist/cli/commands/pr-review-audit-fixer.d.mts.map +1 -1
  29. package/dist/cli/commands/pr-review-audit-fixer.mjs +18 -5
  30. package/dist/cli/commands/pr-review-audit-fixer.mjs.map +1 -1
  31. package/dist/cli/commands/route.d.mts.map +1 -1
  32. package/dist/cli/commands/route.mjs +37 -2
  33. package/dist/cli/commands/route.mjs.map +1 -1
  34. package/dist/cli/commands/task.d.mts.map +1 -1
  35. package/dist/cli/commands/task.mjs +132 -5
  36. package/dist/cli/commands/task.mjs.map +1 -1
  37. package/dist/cli/commands/validate-evidence.d.mts +24 -2
  38. package/dist/cli/commands/validate-evidence.d.mts.map +1 -1
  39. package/dist/cli/commands/validate-evidence.mjs +154 -17
  40. package/dist/cli/commands/validate-evidence.mjs.map +1 -1
  41. package/dist/cli/commands/vitest-doctor.d.mts +2 -0
  42. package/dist/cli/commands/vitest-doctor.d.mts.map +1 -0
  43. package/dist/cli/commands/vitest-doctor.mjs +168 -0
  44. package/dist/cli/commands/vitest-doctor.mjs.map +1 -0
  45. package/dist/cli/config/gitignore-drift.d.mts +23 -1
  46. package/dist/cli/config/gitignore-drift.d.mts.map +1 -1
  47. package/dist/cli/config/gitignore-drift.mjs +81 -3
  48. package/dist/cli/config/gitignore-drift.mjs.map +1 -1
  49. package/dist/cli/config/load.d.mts +56 -2
  50. package/dist/cli/config/load.d.mts.map +1 -1
  51. package/dist/cli/config/load.mjs +192 -2
  52. package/dist/cli/config/load.mjs.map +1 -1
  53. package/dist/cli/consistency/parse-review.mjs +7 -0
  54. package/dist/cli/consistency/parse-review.mjs.map +1 -1
  55. package/dist/cli/dispatch.d.mts.map +1 -1
  56. package/dist/cli/dispatch.mjs +24 -2
  57. package/dist/cli/dispatch.mjs.map +1 -1
  58. package/dist/cli/gate/gates/gitignore-in-sync.d.mts +1 -1
  59. package/dist/cli/gate/gates/gitignore-in-sync.d.mts.map +1 -1
  60. package/dist/cli/gate/gates/gitignore-in-sync.mjs +5 -2
  61. package/dist/cli/gate/gates/gitignore-in-sync.mjs.map +1 -1
  62. package/dist/cli/gate/gates/review-clean.d.mts +5 -1
  63. package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
  64. package/dist/cli/gate/gates/review-clean.mjs +23 -18
  65. package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
  66. package/dist/cli/gate/gates/verify-approved.d.mts +49 -1
  67. package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
  68. package/dist/cli/gate/gates/verify-approved.mjs +93 -14
  69. package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
  70. package/dist/cli/help.d.mts.map +1 -1
  71. package/dist/cli/help.mjs +8 -2
  72. package/dist/cli/help.mjs.map +1 -1
  73. package/dist/cli/host/detect.d.mts +122 -9
  74. package/dist/cli/host/detect.d.mts.map +1 -1
  75. package/dist/cli/host/detect.mjs +132 -0
  76. package/dist/cli/host/detect.mjs.map +1 -1
  77. package/dist/cli/install/legacy-paths.d.mts +38 -0
  78. package/dist/cli/install/legacy-paths.d.mts.map +1 -0
  79. package/dist/cli/install/legacy-paths.mjs +69 -0
  80. package/dist/cli/install/legacy-paths.mjs.map +1 -0
  81. package/dist/cli/install/runtime-detect.d.mts +13 -0
  82. package/dist/cli/install/runtime-detect.d.mts.map +1 -1
  83. package/dist/cli/install/runtime-detect.mjs +9 -0
  84. package/dist/cli/install/runtime-detect.mjs.map +1 -1
  85. package/dist/cli/install/runtime-migrate.d.mts +84 -0
  86. package/dist/cli/install/runtime-migrate.d.mts.map +1 -0
  87. package/dist/cli/install/runtime-migrate.mjs +244 -0
  88. package/dist/cli/install/runtime-migrate.mjs.map +1 -0
  89. package/dist/cli/route/drift-detect.d.mts +20 -0
  90. package/dist/cli/route/drift-detect.d.mts.map +1 -0
  91. package/dist/cli/route/drift-detect.mjs +107 -0
  92. package/dist/cli/route/drift-detect.mjs.map +1 -0
  93. package/dist/cli/task/index-md.d.mts.map +1 -1
  94. package/dist/cli/task/index-md.mjs +14 -2
  95. package/dist/cli/task/index-md.mjs.map +1 -1
  96. package/dist/cli/util/aperant-section.d.mts +34 -0
  97. package/dist/cli/util/aperant-section.d.mts.map +1 -0
  98. package/dist/cli/util/aperant-section.mjs +127 -0
  99. package/dist/cli/util/aperant-section.mjs.map +1 -0
  100. package/dist/cli/util/copy.d.mts +28 -1
  101. package/dist/cli/util/copy.d.mts.map +1 -1
  102. package/dist/cli/util/copy.mjs +43 -55
  103. package/dist/cli/util/copy.mjs.map +1 -1
  104. package/dist/cli/util/semver.d.mts +17 -0
  105. package/dist/cli/util/semver.d.mts.map +1 -0
  106. package/dist/cli/util/semver.mjs +29 -0
  107. package/dist/cli/util/semver.mjs.map +1 -0
  108. package/dist/cli/util/skill-installs.d.mts +65 -9
  109. package/dist/cli/util/skill-installs.d.mts.map +1 -1
  110. package/dist/cli/util/skill-installs.mjs +130 -21
  111. package/dist/cli/util/skill-installs.mjs.map +1 -1
  112. package/dist/cli/util/version-preflight.d.mts +44 -0
  113. package/dist/cli/util/version-preflight.d.mts.map +1 -0
  114. package/dist/cli/util/version-preflight.mjs +66 -0
  115. package/dist/cli/util/version-preflight.mjs.map +1 -0
  116. package/dist/plugin/.claude-plugin/plugin.json +11 -2
  117. package/dist/plugin/agents/apt-improver.md +99 -0
  118. package/dist/plugin/agents/apt-planner.md +127 -10
  119. package/dist/plugin/agents/apt-pr-review-fixer.md +13 -9
  120. package/dist/plugin/skills/apt/SKILL.md +1 -0
  121. package/dist/plugin/skills/apt-close-task/SKILL.md +63 -1
  122. package/dist/plugin/skills/apt-debug/SKILL.md +39 -6
  123. package/dist/plugin/skills/apt-debug/appendices/diagnose-discipline.md +119 -0
  124. package/dist/plugin/skills/apt-diagram/SKILL.md +378 -0
  125. package/dist/plugin/skills/apt-diagram/appendices/design-discipline.md +97 -0
  126. package/dist/plugin/skills/apt-discuss/SKILL.md +72 -5
  127. package/dist/plugin/skills/apt-discuss/appendices/grill-discipline.md +104 -0
  128. package/dist/plugin/skills/apt-discuss/appendices/zoom-out-helper.md +79 -0
  129. package/dist/plugin/skills/apt-execute/SKILL.md +57 -5
  130. package/dist/plugin/skills/apt-execute/appendices/tdd-mode.md +107 -0
  131. package/dist/plugin/skills/apt-improve/DEEPENING.md +84 -0
  132. package/dist/plugin/skills/apt-improve/INTERFACE-DESIGN.md +97 -0
  133. package/dist/plugin/skills/apt-improve/LANGUAGE.md +104 -0
  134. package/dist/plugin/skills/apt-improve/SKILL.md +141 -0
  135. package/dist/plugin/skills/apt-plan/SKILL.md +171 -4
  136. package/dist/plugin/skills/apt-plan/adapters/conductor.md +98 -0
  137. package/dist/plugin/skills/apt-pr-review/SKILL.md +57 -18
  138. package/dist/plugin/skills/apt-prototype/LOGIC.md +109 -0
  139. package/dist/plugin/skills/apt-prototype/SKILL.md +143 -0
  140. package/dist/plugin/skills/apt-prototype/UI.md +90 -0
  141. package/dist/plugin/skills/apt-quick/SKILL.md +49 -8
  142. package/dist/plugin/skills/apt-release-notes/SKILL.md +193 -0
  143. package/dist/plugin/skills/apt-release-notes/appendices/persona-voice.md +59 -0
  144. package/dist/plugin/skills/apt-review/SKILL.md +2 -0
  145. package/dist/plugin/skills/apt-run/SKILL.md +32 -4
  146. package/dist/plugin/skills/apt-setup/SKILL.md +308 -6
  147. package/dist/plugin/skills/apt-ship/SKILL.md +122 -1
  148. package/dist/plugin/skills/apt-spar/SKILL.md +315 -0
  149. package/dist/plugin/skills/apt-triage/AGENT-BRIEF.md +84 -0
  150. package/dist/plugin/skills/apt-triage/OUT-OF-SCOPE.md +75 -0
  151. package/dist/plugin/skills/apt-triage/SKILL.md +169 -0
  152. package/dist/plugin/skills/apt-update/SKILL.md +77 -10
  153. package/dist/plugin/skills/apt-verify/SKILL.md +3 -0
  154. package/dist/plugin/skills/apt-verify-proof/SKILL.md +10 -5
  155. package/dist/plugin/skills/apt-watch-ci/SKILL.md +166 -0
  156. package/dist/plugin/skills/apt-zoom-out/SKILL.md +130 -0
  157. package/package.json +133 -133
  158. package/prompts/conductor-framework-context.md +63 -0
  159. package/prompts/conductor-system.md +11 -0
  160. package/skills/apt-close-task/SKILL.md +6 -0
  161. package/skills/apt-discuss/SKILL.md +47 -5
  162. package/skills/apt-execute/SKILL.md +9 -0
  163. package/skills/apt-plan/SKILL.md +12 -0
  164. package/skills/apt-pr-review/SKILL.md +11 -2
  165. package/skills/apt-quick/SKILL.md +19 -8
  166. package/skills/apt-researcher.md +1 -0
  167. package/skills/apt-setup/SKILL.md +33 -2
  168. package/skills/apt-ship/SKILL.md +16 -4
  169. package/skills/apt-spar/SKILL.md +36 -11
  170. package/skills/apt-update/SKILL.md +26 -1
  171. package/skills/apt-verify-proof/SKILL.md +7 -5
  172. package/skills/apt-watch-ci/SKILL.md +4 -1
  173. package/src/cli/ci-watch/stop-matrix.mjs +17 -0
  174. package/src/cli/commands/ci-watch.mjs +152 -3
  175. package/src/cli/commands/features-audit.mjs +164 -5
  176. package/src/cli/commands/health-check.mjs +116 -3
  177. package/src/cli/commands/init.mjs +154 -6
  178. package/src/cli/commands/modes.mjs +11 -0
  179. package/src/cli/commands/pr-review-audit-fixer.mjs +18 -5
  180. package/src/cli/commands/route.mjs +38 -2
  181. package/src/cli/commands/task.mjs +132 -5
  182. package/src/cli/commands/validate-evidence.mjs +158 -17
  183. package/src/cli/commands/vitest-doctor.mjs +173 -0
  184. package/src/cli/config/gitignore-drift.mjs +74 -3
  185. package/src/cli/config/load.mjs +188 -2
  186. package/src/cli/consistency/parse-review.mjs +6 -0
  187. package/src/cli/dispatch.mjs +23 -2
  188. package/src/cli/gate/gates/gitignore-in-sync.mjs +5 -2
  189. package/src/cli/gate/gates/review-clean.mjs +24 -19
  190. package/src/cli/gate/gates/verify-approved.mjs +97 -14
  191. package/src/cli/help.mjs +8 -2
  192. package/src/cli/host/detect.mjs +135 -0
  193. package/src/cli/install/legacy-paths.mjs +69 -0
  194. package/src/cli/install/runtime-detect.mjs +9 -0
  195. package/src/cli/install/runtime-migrate.mjs +252 -0
  196. package/src/cli/route/drift-detect.mjs +107 -0
  197. package/src/cli/task/index-md.mjs +15 -2
  198. package/src/cli/util/aperant-section.mjs +136 -0
  199. package/src/cli/util/copy.mjs +43 -56
  200. package/src/cli/util/semver.mjs +28 -0
  201. package/src/cli/util/skill-installs.mjs +134 -21
  202. package/src/cli/util/version-preflight.mjs +65 -0
  203. package/templates/aperant-claude-md-appendix.md +37 -0
  204. package/templates/config.json +2 -7
  205. package/workflows/verify-proof.md +8 -3
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Preflight check: is the installed kernel new enough to support the
3
+ * named feature?
4
+ *
5
+ * - Returns `null` when the install is at-or-above `introducedIn` (pass).
6
+ * - Returns `null` when `installedVersion` is malformed (fail-open — a
7
+ * corrupted `package.json` shouldn't lock users out of every cmd; the
8
+ * route-level drift check still catches the same install via the
9
+ * version mismatch path).
10
+ * - Returns a `version_drift` error envelope when the install predates
11
+ * `introducedIn`. Callers `exitWith(envelope, 1)`.
12
+ *
13
+ * @param {Object} args
14
+ * @param {string} args.feature Short name of the feature being gated.
15
+ * @param {string} args.introducedIn Minimum framework version that ships the feature.
16
+ * @param {string} args.installedVersion The installed kernel's version.
17
+ * @param {string} args.missingFlag The CLI flag that requires this version (e.g. '--payload-file').
18
+ * @returns {null | {
19
+ * status: 'error',
20
+ * command: 'version-preflight',
21
+ * error_code: 'version_drift',
22
+ * feature: string,
23
+ * introduced_in: string,
24
+ * installed_version: string,
25
+ * missing_flag: string,
26
+ * reason: string,
27
+ * }}
28
+ */
29
+ export function requireMinVersion({ feature, introducedIn, installedVersion, missingFlag }: {
30
+ feature: string;
31
+ introducedIn: string;
32
+ installedVersion: string;
33
+ missingFlag: string;
34
+ }): null | {
35
+ status: "error";
36
+ command: "version-preflight";
37
+ error_code: "version_drift";
38
+ feature: string;
39
+ introduced_in: string;
40
+ installed_version: string;
41
+ missing_flag: string;
42
+ reason: string;
43
+ };
44
+ //# sourceMappingURL=version-preflight.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-preflight.d.mts","sourceRoot":"","sources":["../../../src/cli/util/version-preflight.mjs"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,4FAfG;IAAqB,OAAO,EAApB,MAAM;IACO,YAAY,EAAzB,MAAM;IACO,gBAAgB,EAA7B,MAAM;IACO,WAAW,EAAxB,MAAM;CACd,GAAU,IAAI,GAAG;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAmBH"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * util/version-preflight.mjs — FRAMEWORK-BUG-017 (rung 2 of 3).
3
+ *
4
+ * Pure helper for cmds that depend on a flag / behavior introduced in a
5
+ * specific framework version. When the installed kernel predates that
6
+ * version, returning a structured `version_drift` envelope is strictly
7
+ * better than the silent infra-backoff loop that bit us in
8
+ * FRAMEWORK-BUG-010.
9
+ *
10
+ * Scope (Codex review edit 1): this is a **future-guard** for the next
11
+ * framework fix that introduces a new flag. A stale 0.6.6 install
12
+ * obviously cannot execute 0.6.8 preflight code (the helper doesn't
13
+ * exist there). The actual catch-point for already-stale installs is
14
+ * the route-side `detectSourceWorkspaceDrift`. This helper makes the
15
+ * NEXT bug bundle cheaper to ship safely.
16
+ */
17
+ import { compareVersions, SEMVER_RE } from './semver.mjs';
18
+ /**
19
+ * Preflight check: is the installed kernel new enough to support the
20
+ * named feature?
21
+ *
22
+ * - Returns `null` when the install is at-or-above `introducedIn` (pass).
23
+ * - Returns `null` when `installedVersion` is malformed (fail-open — a
24
+ * corrupted `package.json` shouldn't lock users out of every cmd; the
25
+ * route-level drift check still catches the same install via the
26
+ * version mismatch path).
27
+ * - Returns a `version_drift` error envelope when the install predates
28
+ * `introducedIn`. Callers `exitWith(envelope, 1)`.
29
+ *
30
+ * @param {Object} args
31
+ * @param {string} args.feature Short name of the feature being gated.
32
+ * @param {string} args.introducedIn Minimum framework version that ships the feature.
33
+ * @param {string} args.installedVersion The installed kernel's version.
34
+ * @param {string} args.missingFlag The CLI flag that requires this version (e.g. '--payload-file').
35
+ * @returns {null | {
36
+ * status: 'error',
37
+ * command: 'version-preflight',
38
+ * error_code: 'version_drift',
39
+ * feature: string,
40
+ * introduced_in: string,
41
+ * installed_version: string,
42
+ * missing_flag: string,
43
+ * reason: string,
44
+ * }}
45
+ */
46
+ export function requireMinVersion({ feature, introducedIn, installedVersion, missingFlag }) {
47
+ if (!installedVersion || typeof installedVersion !== 'string')
48
+ return null;
49
+ if (!SEMVER_RE.test(installedVersion))
50
+ return null;
51
+ if (!SEMVER_RE.test(introducedIn))
52
+ return null;
53
+ if (compareVersions(installedVersion, introducedIn) >= 0)
54
+ return null;
55
+ return {
56
+ status: 'error',
57
+ command: 'version-preflight',
58
+ error_code: 'version_drift',
59
+ feature,
60
+ introduced_in: introducedIn,
61
+ installed_version: installedVersion,
62
+ missing_flag: missingFlag,
63
+ reason: `cmd needs feature ${feature} introduced in version ${introducedIn}; installed version is ${installedVersion} — run /apt:update`,
64
+ };
65
+ }
66
+ //# sourceMappingURL=version-preflight.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-preflight.mjs","sourceRoot":"","sources":["../../../src/cli/util/version-preflight.mjs"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE;IACzF,IAAI,CAAC,gBAAgB,IAAI,OAAO,gBAAgB,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC1E,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAA;IAClD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAA;IAE9C,IAAI,eAAe,CAAC,gBAAgB,EAAE,YAAY,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAErE,OAAO;QACN,MAAM,EAAE,OAAO;QACf,OAAO,EAAE,mBAAmB;QAC5B,UAAU,EAAE,eAAe;QAC3B,OAAO;QACP,aAAa,EAAE,YAAY;QAC3B,iBAAiB,EAAE,gBAAgB;QACnC,YAAY,EAAE,WAAW;QACzB,MAAM,EAAE,qBAAqB,OAAO,0BAA0B,YAAY,0BAA0B,gBAAgB,oBAAoB;KACxI,CAAA;AACF,CAAC"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aperant",
3
- "version": "0.6.4",
3
+ "version": "0.7.3",
4
4
  "description": "AI coding framework — composable skills for planning, executing, verifying, and reviewing code with any LLM provider. Works as Claude Code commands, Codex tasks, or programmatically.",
5
5
  "author": {
6
6
  "name": "Mikalsen AI",
@@ -30,15 +30,19 @@
30
30
  "skills/apt-create-docs",
31
31
  "skills/apt-debug",
32
32
  "skills/apt-design",
33
+ "skills/apt-diagram",
33
34
  "skills/apt-discuss",
34
35
  "skills/apt-docs",
35
36
  "skills/apt-execute",
37
+ "skills/apt-improve",
36
38
  "skills/apt-mockup",
37
39
  "skills/apt-pause",
38
40
  "skills/apt-personas",
39
41
  "skills/apt-plan",
40
42
  "skills/apt-pr-review",
43
+ "skills/apt-prototype",
41
44
  "skills/apt-quick",
45
+ "skills/apt-release-notes",
42
46
  "skills/apt-resume",
43
47
  "skills/apt-review",
44
48
  "skills/apt-roadmap",
@@ -47,15 +51,20 @@
47
51
  "skills/apt-scan",
48
52
  "skills/apt-setup",
49
53
  "skills/apt-ship",
54
+ "skills/apt-spar",
50
55
  "skills/apt-stress-test",
51
56
  "skills/apt-terminal",
57
+ "skills/apt-triage",
52
58
  "skills/apt-update",
53
59
  "skills/apt-verify",
54
- "skills/apt-verify-proof"
60
+ "skills/apt-verify-proof",
61
+ "skills/apt-watch-ci",
62
+ "skills/apt-zoom-out"
55
63
  ],
56
64
  "agents": [
57
65
  "agents/apt-docs-author.md",
58
66
  "agents/apt-executor.md",
67
+ "agents/apt-improver.md",
59
68
  "agents/apt-planner.md",
60
69
  "agents/apt-pr-review-codebase-fit.md",
61
70
  "agents/apt-pr-review-fixer.md",
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: apt-improver
3
+ description: Proactive refactor discovery agent — runs apt:improve Phase 1 (explore a named area through DEEPENING / INTERFACE-DESIGN / LANGUAGE lenses) and outputs a candidate refactor inventory. Read-only, no code changes.
4
+ apt-skill-version: {{APT_VERSION}}
5
+ tools: Read, Bash, Grep, Glob
6
+ color: orange
7
+ ---
8
+
9
+ <role>
10
+ You are an Aperant Framework proactive-refactor agent. You scan a named
11
+ area of the codebase through three lenses (DEEPENING, INTERFACE-DESIGN,
12
+ LANGUAGE) and produce a candidate refactor inventory.
13
+
14
+ Spawned by `/apt:improve` for Phase 1 (Explore). Phases 2 (Present
15
+ candidates) and 3 (Deepen via grill loop) run in the parent agent
16
+ context because they require user interaction.
17
+
18
+ Your job: read the area, apply the three lenses, output a candidate
19
+ inventory. You are READ-ONLY — no code changes, no decisions locked.
20
+
21
+ **CRITICAL: Mandatory Initial Read**
22
+ If the prompt contains a `<files_to_read>` block, you MUST use the `Read`
23
+ tool to load every file listed there before performing any other actions.
24
+ </role>
25
+
26
+ <project_context>
27
+ **Project instructions:** Read `./CLAUDE.md` if it exists for project structure and conventions.
28
+ **Constitution:** Read `AGENTS.md` if it exists for architecture overview.
29
+ **Glossary:** Read `CONTEXT.md` if it exists — its term-blocks (especially `Aliases to avoid` fields) are direct inputs to the LANGUAGE lens.
30
+
31
+ **Rationalizations (R4):** Read `.aperant/rationalizations/claude-opus-4-6.md` once at session start. Pay particular attention to R-2 (skipping pre-read investigation) and R-5 ("I already verified this implicitly"). As an improver, you only have value if your candidate inventory is grounded in actually-read code — drift here ships hallucinated refactor candidates.
32
+ </project_context>
33
+
34
+ <environment>
35
+ - **Working directory:** The project root
36
+ - **Note:** You have NO write/edit tools. You investigate and report findings only. Phase 2 (filter + present) and Phase 3 (deepen + lock) run in the parent agent's context.
37
+ </environment>
38
+
39
+ <process>
40
+
41
+ ## 1. Parse the area name
42
+
43
+ Extract the area from the spawn prompt. Areas are noun phrases ("sparring engine", "feature registry", "auth flow") — not file paths.
44
+
45
+ If the area is ambiguous, ask the parent ONE clarifying question via your report — do NOT enter a multi-turn interview.
46
+
47
+ ## 2. Load CONTEXT.md (if present)
48
+
49
+ If `CONTEXT.md` exists at the project root, read it and extract:
50
+
51
+ - Term-blocks whose canonical term, definition, or aliases-to-avoid match keywords in the area
52
+ - The full alias list (LANGUAGE lens grep source)
53
+
54
+ If CONTEXT.md does not exist, skip silently — the LANGUAGE lens runs without the alias grep but still catches in-file conflations.
55
+
56
+ ## 3. Apply the three lenses
57
+
58
+ Read the lens descriptions (the parent has them in context; if you don't, read them from disk).
59
+ Paths below are relative to the project root (monorepo: `<repo>/packages/framework/` is the
60
+ framework root; standalone install: the project root IS the framework root, so use
61
+ `skills/apt-improve/` directly):
62
+
63
+ - `packages/framework/skills/apt-improve/DEEPENING.md` (monorepo) or `skills/apt-improve/DEEPENING.md` (standalone)
64
+ - `packages/framework/skills/apt-improve/INTERFACE-DESIGN.md` (monorepo) or `skills/apt-improve/INTERFACE-DESIGN.md` (standalone)
65
+ - `packages/framework/skills/apt-improve/LANGUAGE.md` (monorepo) or `skills/apt-improve/LANGUAGE.md` (standalone)
66
+
67
+ For each lens, walk the area's files and flag candidates per the lens's red-flag list.
68
+
69
+ ## 4. Output the candidate inventory
70
+
71
+ Produce a structured report:
72
+
73
+ ```markdown
74
+ # apt:improve Phase-1 inventory — {area}
75
+
76
+ ## DEEPENING candidates
77
+ - `{File}:{Symbol}` — shallow ({reason}); deletion-test target.
78
+ - ...
79
+
80
+ ## INTERFACE-DESIGN candidates
81
+ - `{Module A}` ↔ `{Module B}` — {red-flag name} ({one-line evidence}).
82
+ - ...
83
+
84
+ ## LANGUAGE candidates
85
+ - `{File}:{Line}` — uses `{wrong-term}` for `{canonical}` ({CONTEXT.md reference if applicable}).
86
+ - ...
87
+
88
+ ## Files read
89
+ - {file1}
90
+ - ...
91
+ ```
92
+
93
+ Keep the inventory grounded — every candidate must cite a file + symbol the user can grep for. No speculation; no candidates inferred from filenames alone.
94
+
95
+ ## 5. Hand back to parent for Phase 2/3
96
+
97
+ Your output is the input to Phase 2 (the parent agent's filter step). Do NOT enter Phase 2 yourself — your role ends here.
98
+
99
+ </process>
@@ -59,6 +59,18 @@ For STANDARD and COMPLEX tasks:
59
59
  3. Read key files to understand current architecture
60
60
  4. Identify: files to modify, interfaces to respect, tests to update, config to change
61
61
 
62
+ ### 3.35. React state guidance — sync-store vs render-committed ref (BUG-025-planner)
63
+
64
+ When a planned event handler fires a flush/persist immediately after mutating a Zustand/Jotai/refs-stored store, the spec MUST recommend reading from `store.getState()` (or the equivalent direct-read primitive) on the synchronous path, NOT from a `useRef`-populated-by-`useEffect`.
65
+
66
+ Why: refs populated via `useEffect` lag the store by one render cycle. A handler that mutates the store and immediately reads the ref sees the pre-mutation value, silently corrupting persists and order-of-operations contracts. Reference pattern: `apps/desktop/src/renderer/src/App.tsx::runUpsertForIds` (post-fix commit `4c263856`).
67
+
68
+ Checklist when the task touches a Zustand/Jotai store or any handler doing flush-after-mutate:
69
+
70
+ - Grep for `useRef(` references that cache store state (e.g. `const fooRef = useRef(foo); useEffect(() => { fooRef.current = foo }, [foo])`).
71
+ - If the spec or planned subtasks read from such a ref on a synchronous path that ALSO mutates the store, recommend `store.getState()` as the read primitive.
72
+ - Note the caveat explicitly in the spec's Implementation Decisions section so the executor doesn't silently revert it.
73
+
62
74
  ### 3.4. Consume design.md (C24)
63
75
 
64
76
  If `{task_dir}/design.md` exists (emitted by a prior `/apt:design` run), load it before decomposing subtasks:
@@ -199,7 +211,13 @@ If unable to enumerate (network unavailable, action docs unclear), flag it expli
199
211
 
200
212
  ## 4. Write Specification
201
213
 
202
- Create `{task_dir}/spec.md`:
214
+ The spec.md content format **branches on track** (Pocock adoption ID-05,
215
+ Fast Path Guarantee). QUICK-routed tasks keep the existing 4-section
216
+ shape; STANDARD/DEEP/COMPLEX tasks get a PRD-shaped artifact that
217
+ survives the task as a second-consumer document.
218
+
219
+ ### 4.A. QUICK track / SIMPLE complexity — legacy 4-section spec
220
+
203
221
  ```markdown
204
222
  # Task: {task description}
205
223
 
@@ -219,6 +237,57 @@ Create `{task_dir}/spec.md`:
219
237
  {What this task explicitly does NOT cover}
220
238
  ```
221
239
 
240
+ ### 4.B. STANDARD / DEEP / COMPLEX — PRD-shaped spec.md
241
+
242
+ ```markdown
243
+ # Spec: {task title}
244
+
245
+ **Task ID:** {task-id}
246
+ **Track:** {STANDARD | DEEP}
247
+ **Scope:** {framework | desktop | web | core | ui | backend | docs}
248
+
249
+ ## Problem Statement
250
+ {What pain are we solving? Cite empirical evidence where possible.}
251
+
252
+ ## Solution
253
+ {Chosen approach + bulleted net delta of files / flags / config / skills.}
254
+
255
+ ## User Stories
256
+ {One **US-NN (persona).** story per user-value slice. Stories map 1:1
257
+ onto subtasks in implementation_plan.json.}
258
+
259
+ ## Implementation Decisions
260
+ {Load-bearing principles carried into every subtask. Format:
261
+ "### ID-NN: {title}" + rationale + practical consequence.}
262
+
263
+ ## Testing Decisions
264
+ {One block per test family: "### TD-NN: {family}" + what's covered +
265
+ which subtask owns the test file. Fast Path regression tests MUST be
266
+ enumerated here.}
267
+
268
+ ## Out of Scope
269
+ {Non-goals. Each bullet names what is deferred + where it belongs.}
270
+
271
+ ## Further Notes
272
+ {Sequencing rationale, risk surface, dependencies, files-affected
273
+ preview, and any other executor-load-bearing context.}
274
+
275
+ ### Acceptance Criteria
276
+ {Cross-cutting **AC1..ACN** list — each AC gets a stable ID. Subtasks
277
+ reference these IDs via the per-subtask `acceptance_criteria` field.}
278
+ ```
279
+
280
+ **PRD section ordering is load-bearing.** `apt-tools coverage-check
281
+ validate` greps `## Acceptance Criteria`; `apt-tools consistency check`
282
+ greps `## Solution` and `## User Stories`. Renaming or reordering these
283
+ sections breaks downstream tooling and the C15 consistency rules.
284
+
285
+ **Do NOT interview the user.** The PRD is synthesized from the task
286
+ description + codebase investigation + AGENTS.md, not from a clarifying
287
+ back-and-forth. If a section truly cannot be filled, write
288
+ `{TBD — needs user input}`; do NOT pause for clarification (that is
289
+ `apt:discuss`'s surface, not the planner's).
290
+
222
291
  ### 4.05. Design Reference section (C24 — when design.md exists)
223
292
 
224
293
  When `{task_dir}/design.md` exists, `spec.md` MUST include a `## Design Reference` section directly below `## Requirements`. Shape:
@@ -275,12 +344,41 @@ fix it now, not later.
275
344
 
276
345
  ## 5. Create Implementation Plan
277
346
 
278
- Create `{task_dir}/implementation_plan.json`:
347
+ Subtask schema branches on track (Pocock adoption ID-05 — Fast Path Guarantee).
348
+
349
+ ### 5.A. QUICK track / SIMPLE complexity — legacy flat subtask schema
350
+
279
351
  ```json
280
352
  {
281
353
  "task": "{task description}",
282
- "complexity": "simple|standard|complex",
354
+ "complexity": "simple",
283
355
  "spec": "spec.md",
356
+ "subtasks": [
357
+ {
358
+ "id": "1",
359
+ "title": "{subtask title}",
360
+ "description": "{what to do — concrete, not vague}",
361
+ "files": ["path/to/file1.ts", "path/to/file2.ts"],
362
+ "dependencies": [],
363
+ "status": "pending",
364
+ "verification": "{command or check to verify completion}"
365
+ }
366
+ ]
367
+ }
368
+ ```
369
+
370
+ QUICK subtasks omit `hitl`, `afk`, `acceptance_criteria`, and `user_value`
371
+ fields. The one-shot path doesn't need user-value framing or per-subtask
372
+ HITL gates.
373
+
374
+ ### 5.B. STANDARD / DEEP / COMPLEX — vertical-slice subtask schema
375
+
376
+ ```json
377
+ {
378
+ "task": "{task description}",
379
+ "complexity": "standard|complex",
380
+ "spec": "spec.md",
381
+ "vertical_slice_schema_version": "1",
284
382
  "declared_persona_tier": "primary",
285
383
  "declared_personas": ["mobile-dev"],
286
384
  "tier_confidence": "high",
@@ -308,29 +406,48 @@ Create `{task_dir}/implementation_plan.json`:
308
406
  "subtasks": [
309
407
  {
310
408
  "id": "1",
311
- "title": "{subtask title}",
409
+ "title": "{subtask title — user-value framing, not 'add UI'}",
312
410
  "description": "{what to do — concrete, not vague}",
313
411
  "files": ["path/to/file1.ts", "path/to/file2.ts"],
314
412
  "acceptance_criteria": ["AC1", "AC3"],
315
413
  "dependencies": [],
316
414
  "status": "pending",
415
+ "hitl": "required | review | none",
416
+ "afk": "safe | unsafe",
417
+ "user_value": "{one-line user-value framing — who benefits, how}",
317
418
  "verification": "{command or check to verify completion}"
318
419
  }
319
420
  ]
320
421
  }
321
422
  ```
322
423
 
424
+ Mandatory STANDARD/DEEP fields per subtask:
425
+
426
+ - `hitl` — `"required" | "review" | "none"`. `required` blocks the
427
+ executor pending human approval (autonomy 1 always pauses; autonomy 2
428
+ batches; autonomy 3 escalates only on conflict). `review` means the
429
+ human reviews the diff post-hoc, not blocking. `none` is agent-only.
430
+ - `afk` — `"safe" | "unsafe"`. `safe` means proceed unattended. `unsafe`
431
+ means pause at the start of this subtask if no human is present
432
+ (autonomy < 3).
433
+ - `acceptance_criteria` — `string[]` of id-refs (`"AC1"`, …) into spec.md's
434
+ `## Acceptance Criteria` section.
435
+ - `user_value` — one-line framing of WHO benefits and HOW. Prevents
436
+ horizontal slicing — every subtask is a vertical user-shippable slice.
437
+
438
+ Plan-level: `vertical_slice_schema_version: "1"` declares the schema
439
+ version so future migrations can detect this plan as v1.
440
+
323
441
  Rules:
324
442
  - Each subtask completable in one focused session
325
443
  - Dependencies form a DAG (no circular deps)
326
444
  - Verification must be automated (test command, grep check, type check)
327
445
  - Files list must be exhaustive — every file the subtask touches
328
- - `acceptance_criteria` is mandatory: list the canonical AC id-refs
329
- (`"AC1"`, `"AC3"`, ...) from the `## Acceptance Criteria` section of
330
- spec.md that this subtask satisfies. Verbatim AC text is accepted but
331
- discouraged — the id-ref form is stable across spec edits. Every AC in
332
- spec.md must be referenced by at least one subtask; gate G8
333
- (`consistency-check`) enforces this on ship.
446
+ - `acceptance_criteria` is mandatory on STANDARD/DEEP: every AC in spec.md
447
+ must be referenced by at least one subtask; gate G8
448
+ (`consistency-check`) enforces this on ship. Verbatim AC text is
449
+ accepted but discouraged — id-refs are stable across spec edits.
450
+ - Subtask title MUST frame user value, not file scope.
334
451
 
335
452
  ### 5.1 Framework-scope doc-sync rule
336
453
 
@@ -64,7 +64,7 @@ The only valid reasons to skip a finding:
64
64
  ```
65
65
  [TOOLCHAIN_COMMANDS]
66
66
  ```
67
- 6. **If verification passes**: Stage with `git add [specific files]`
67
+ 6. **If verification passes**: Stage AND commit atomically per finding with `git add [specific files] && git commit -m "<finding-id>: <one-line>"` (BUG-016 — the audit gate only observes COMMITTED edits in the iteration window; stage-only leaves the index dirty and trips a false-positive hallucination verdict).
68
68
  7. **If verification fails**: Revert with `git checkout -- [file]`, mark as "manual fix required" in your report
69
69
 
70
70
  ---
@@ -89,7 +89,7 @@ Findings with severity `critical`:
89
89
 
90
90
  ## Report Format
91
91
 
92
- Write to `[REVIEW_DIR]/iterations/[N]/fixes-applied.md`:
92
+ Write to `[REVIEW_DIR]/iterations/[N]/fixes-applied-[FIXER_NAME].md` (BUG-030 canonical naming — the audit gate resolves `fixes-applied-${FIXER_NAME}.md` ahead of the legacy `fixes-applied.md`, so parallel fixers no longer collide on a shared report path):
93
93
 
94
94
  ```markdown
95
95
  # Fix Report — Iteration [N]
@@ -142,11 +142,13 @@ Write to `[REVIEW_DIR]/iterations/[N]/fixes-applied.md`:
142
142
 
143
143
  ## Pre-Return Self-Audit (mandatory)
144
144
 
145
- Before emitting `VERIFICATION: PASS` or any `FIXED: N > 0` status, run BOTH of these inside the worktree:
145
+ Before emitting `VERIFICATION: PASS` or any `FIXED: N > 0` status, run BOTH of these inside the worktree.
146
+
147
+ The orchestrator passes you `[ITERATION_START_SHA]` — the SHA captured BEFORE fixers were spawned. The audit gate uses the same SHA as its `--since-sha` window, so your self-audit must mirror it (BUG-016):
146
148
 
147
149
  ```bash
150
+ cd [WORKTREE_PATH] && git log [ITERATION_START_SHA]..HEAD --oneline
148
151
  cd [WORKTREE_PATH] && git diff --cached --name-only
149
- cd [WORKTREE_PATH] && git diff --name-only
150
152
  ```
151
153
 
152
154
  Paste the **literal command output** into your report under a new section:
@@ -154,11 +156,13 @@ Paste the **literal command output** into your report under a new section:
154
156
  ```markdown
155
157
  ## Pre-Return Self-Audit
156
158
 
157
- git diff --cached --name-only:
158
- <paste literal output, or empty if nothing staged>
159
+ git log [ITERATION_START_SHA]..HEAD --oneline:
160
+ <paste literal output MUST list 1 commit per claimed fix>
159
161
 
160
- git diff --name-only:
161
- <paste literal output, or empty if nothing unstaged>
162
+ git diff --cached --name-only:
163
+ <paste literal output MUST be empty (index clean after every commit)>
162
164
  ```
163
165
 
164
- **Rule:** if BOTH outputs are empty AND you claimed to have fixed ≥ 1 finding, your status line MUST be `FIXED: 0 | FAILED: N | SKIPPED: 0 | VERIFICATION: FAIL` and every claimed fix MUST be marked FAILED in the report with `- **Verification**: FAILno diff produced`. Do not emit `VERIFICATION: PASS` with an empty diff — the orchestrator runs an **independent** deterministic audit (see `/apt:pr-review` SKILL.md Phase 6 Step 4b, `apt-tools pr-review audit-fixer`) that cross-checks your status line against the worktree git state. Emitting `PASS` when the diff is empty will be caught deterministically and counted as a hallucination; your findings will be treated as unresolved and you may be re-spawned with an explicit hallucination callout.
166
+ **Pass condition (BUG-016 commit discipline):** the `git log` output MUST contain ≥ 1 commit attributable to this fixer (one commit per fixed finding, message prefixed with the finding id) AND `git diff --cached --name-only` MUST be empty. The previous "I see staged files" rule is OBSOLETE staging without committing leaves the index dirty and the audit gate's `--since-sha` window (which scopes to committed diff for the current iteration) sees zero matching paths, false-positiving `hallucinated`.
167
+
168
+ **Fail condition:** if `git log [ITERATION_START_SHA]..HEAD --oneline` is EMPTY AND you claimed to have fixed ≥ 1 finding, your status line MUST be `FIXED: 0 | FAILED: N | SKIPPED: 0 | VERIFICATION: FAIL` and every claimed fix MUST be marked FAILED in the report with `- **Verification**: FAIL — no commit produced`. Likewise if `git diff --cached --name-only` is NON-EMPTY (you forgot to commit some staged edits), status MUST be `VERIFICATION: FAIL` until the index is clean. Do not emit `VERIFICATION: PASS` with an empty commit log or dirty index — the orchestrator runs an **independent** deterministic audit (see `/apt:pr-review` SKILL.md Phase 6 Step 4b, `apt-tools pr-review audit-fixer`) that cross-checks your status line against the worktree git state. Emitting `PASS` when no commits exist will be caught deterministically and counted as a hallucination; your findings will be treated as unresolved and you may be re-spawned with an explicit hallucination callout.
@@ -130,6 +130,7 @@ Show available commands table:
130
130
  | `apt:resume` | Restore context after session break |
131
131
  | `apt:pause` | Human-initiated handoff — save state when YOU need to stop |
132
132
  | `apt:ship` | Create PR with traceability |
133
+ | `apt:close-task` | Post-merge closer — confirm PR merged, flip phase, drain narration ledger |
133
134
  | `apt:setup` | Change framework preferences |
134
135
 
135
136
  Show current state summary and autonomy flags (`--supervised`, `--guided`, `--autonomous`, `--yolo`).
@@ -15,7 +15,7 @@ execution_modes:
15
15
  - auto
16
16
  - step
17
17
  allowed-tools: "Bash, Read, Grep, Glob, Write"
18
- argument-hint: "apt:close-task [--all | --task <id>]"
18
+ argument-hint: "apt:close-task [--all | --task <id>] [--narrate-only]"
19
19
  gates: []
20
20
  ---
21
21
  <objective>
@@ -40,6 +40,7 @@ Confirm a shipped PR has merged on GitHub, then run the closer side of the task
40
40
  - `.aperant/events/{today}.jsonl` — emits `task.closed.merged`.
41
41
  - `.aperant/config.json` — strips `authorship_overrides['pr-{N}']` entries for merged PRs.
42
42
  - `.aperant/roadmap/{scope}/**` — linked-phase flip (when `task_isolation.auto_close_phase: true`).
43
+ - `.aperant/ci-watches/{pr}.json` — removed for each merged PR (BUG-027 (b) side-effect: prevents watcher state files leaking past PR-terminal-state). Idempotent — already-removed state is a no-op. For retroactive cleanup of orphan state files left behind by pre-fix runs, use `apt-tools ci-watch sweep . --orphaned`.
43
44
  </state_files>
44
45
 
45
46
  <process>
@@ -70,6 +71,7 @@ Internally, close-merged does:
70
71
  a. `apt-tools task close . --id {task-id} --verdict approved` (respects `task_isolation.auto_close_phase`).
71
72
  b. Strips `config.pr_review.authorship_overrides['pr-{N}']` if present.
72
73
  c. Emits `task.closed.merged` event.
74
+ d. Appends a `pending_narration[]` ledger row to `.aperant/state.json` so `--narrate-only` (see §4) can pick it up. Additive only; never blocks the close.
73
75
  3. When `state !== 'MERGED'`, the task stays in `shipped-pending-merge` with a skip reason the user can inspect.
74
76
 
75
77
  ## 2.5 Narrate completed phases (moved from /apt:ship in C56 B5)
@@ -85,6 +87,33 @@ For each entry in the close-merged envelope's `closed[]` whose record has a non-
85
87
 
86
88
  Do NOT wait on the narrator. Its status lands in `.aperant/digests/.last-run.json`. Skip narrator spawn entirely when `closed[]` is empty or no closed item has a phase_id.
87
89
 
90
+ After step 2.5 spawns narrators for the just-closed candidates, the skill ALSO drains `state.pending_narration[]` (entries the passive sweep parked there) — see §4.
91
+
92
+ ## 2.6 Drop user-facing release-note fragment (persona-aware release-notes opt-in)
93
+
94
+ This step is additive bookkeeping — it must NEVER block the close path or surface a destructive failure. Runs ONLY when `.aperant/config.json` has `changelog.release_notes.enabled === true`. When the toggle is `false` (the default for projects that don't publish user-facing notes), skip the section entirely.
95
+
96
+ For each entry in the close-merged envelope's `closed[]`:
97
+
98
+ 1. Read the merged PR body via `gh pr view <pr_number> --json body`. Look for a single `Release note:` line (case-insensitive). The line is what `/apt:ship` Section 2.6 auto-drafted (or a manual edit by the author).
99
+ 2. Look for a persona tag in the PR body or in commits. Reasonable sources: the `Release note:` line itself if the author included one (e.g. `Release note: [Maya] …`), a `release-note-persona:` trailer in the most recent commit, or the persona name embedded in the auto-drafted line by §2.6.2 of /apt:ship.
100
+ 3. Invoke the deterministic drafter:
101
+
102
+ ```bash
103
+ node packages/framework/bin/apt-tools.mjs release-notes draft . \
104
+ --pr <pr_number> \
105
+ --note "<release-note-line-text>" \
106
+ [--persona <persona-name>]
107
+ ```
108
+
109
+ This call is synchronous, sub-100ms, and idempotent per subtask 2 — re-running close-task does not duplicate the fragment.
110
+
111
+ 4. On non-zero exit from `release-notes draft`, append a `pending_release_note[]` row to `.aperant/state.json` so a manual re-run can recover. Do NOT block close-merged on this — it is additive bookkeeping, not core lifecycle. Use `apt-tools state-update . --append-pending-release-note '{"task_id": "<id>", "pr_number": <n>, "reason": "<error>"}'` if such a helper exists, otherwise call `apt-tools state-artifacts . --note '<msg>'` or write a one-line warning to stderr.
112
+
113
+ Skip the section quietly when:
114
+ - The PR body has no `Release note:` line AND no auto-draft signal (intentional infra-only PR, or the `no-notes` label was applied at ship time).
115
+ - The CLI returns `{status: 'ok', written: false, reason: 'no-change'}` — already idempotent-handled by subtask 2's contract; treat as success.
116
+
88
117
  ## 3. Summary
89
118
 
90
119
  close-merged emits an envelope with:
@@ -95,6 +124,39 @@ close-merged emits an envelope with:
95
124
 
96
125
  Report the summary verbatim. If anything landed in `skipped[]` with a recoverable reason (`offline`, `rate_limited`), suggest the user re-run once the network clears.
97
126
 
127
+ **Local cleanup recap (best-effort).** On confirmed merge, `task close-merged` runs `computeWorktreeCleanup` against the primary repo (`packages/framework/src/cli/task/worktree-cleanup.mjs:140-230`): `git fetch origin <base>` (failure → recorded as `fetch_failed` warning, cleanup proceeds), `git checkout <base>` (failure → returns `action: 'checkout_failed'` and preserves BOTH the worktree and the task branch — nothing is removed), `git merge --ff-only origin/<base>` (failure → `ff_only_failed` warning, GC still runs), `git worktree remove <wtPath>`, and `git branch -d <taskBranch>` (failure on squash/rebase merges → `branch_delete_failed` warning, `branch_deleted: false` in the envelope; user can `git branch -D` manually). This is why `/apt:watch-ci` auto-merge MUST NOT pass `gh pr merge --delete-branch` — that flag would try to do the same work from inside the worktree where `git switch <base>` cannot succeed (see FRAMEWORK-BUG-019).
128
+
129
+ ## 4. Narrate-only mode
130
+
131
+ When invoked with `--narrate-only`, skip §1 (no `gh pr view` polling) and §2 (no destructive close path). The deterministic close already ran via the passive post-merge sweep — your job is to drain the `state.pending_narration[]` ledger the sweep parked.
132
+
133
+ Flags:
134
+ - `--narrate-only --all` (default when `--narrate-only` is bare) — drain every ledger entry.
135
+ - `--narrate-only --task <id>` — drain only the entry for `<id>`.
136
+
137
+ Per ledger entry `{ task_id, scope, phase_id, closed_at, pr_number }`:
138
+
139
+ 0. **Enumerate the ledger first.** Run `apt-tools task close-merged . --narrate-only [--task <id>]`. The envelope's `closed[]` is the read-only list of pending narration rows (each carries `narration_pending: true`); state.json is not mutated. When `closed[]` is empty, exit early with `{status: 'ok', narrated: [], skipped: [], remaining: 0}` — there is nothing to drain.
140
+ 1. Run `apt-tools features-audit . --apply-stubs --task <task_id>` so the feature registry catches whatever the closed task introduced. If the subprocess exits non-zero, skip the entry with `reason: 'features-audit-failed'` — never block narration on registry update.
141
+ 2. When `phase_id` is non-null, spawn `apt-team-docs-narrator` (background, fire-and-forget) using the same shape as §2.5: `subagent_type: apt-team-docs-narrator`, `run_in_background: true`, `description: Narrate shipped phase {phase_id}`, `prompt: Run in phase-ship mode with --phase {phase_id} --scope {scope}.`. When `phase_id` is null, skip the spawn with `reason: 'no-phase-id'` (quick tasks have no phase to narrate).
142
+ 3. On successful spawn (or successful no-phase skip), drain the row via `apt-tools task narration-drain . --task <task_id>` which acquires `withFileLock(state.json)` and removes the matching `pending_narration[]` entry. On spawn failure, leave the row in place with `reason: 'narrator-spawn-failed'` so the user can re-invoke.
143
+
144
+ **At-least-once retry semantics (FRAMEWORK-BUG-020).** Step 0 is read-only — it does NOT remove rows. Steps 1–3 are the existing per-row drain contract: a row stays in `pending_narration[]` until step 3 calls `narration-drain` successfully. If the orchestrator crashes between step 2 (narrator spawn) and step 3 (drain), the row remains parked and a retry will spawn narration again. Re-spawning is safe because the narrator is **anchor-replace idempotent** by design (see `agents/apt-team-docs-narrator.md` §6 / §2 and the "Idempotent operations only" rule in §notes) — it uses HTML-comment anchors to bound the replaced region in ROADMAP.md, so a second run replaces the same span rather than appending.
145
+
146
+ Emit a summary envelope:
147
+
148
+ ```json
149
+ {
150
+ "status": "ok",
151
+ "command": "close-task-narrate-only",
152
+ "narrated": [{ "task_id": "<id>", "phase_id": "<p|null>", "scope": "<s>" }],
153
+ "skipped": [{ "task_id": "<id>", "reason": "no-phase-id|features-audit-failed|narrator-spawn-failed" }],
154
+ "remaining": <ledger.length>
155
+ }
156
+ ```
157
+
158
+ The regular `apt:close-task` flow ALSO falls through to this drain step at the tail of §2.5 — so users who run the skill manually after the sweep get narration without remembering the flag. The `--narrate-only` flag exists for the explicit "drain only, don't poll gh" case.
159
+
98
160
  </process>
99
161
 
100
162
  <notes>