@zihanw/pi-forge 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/CHANGELOG.md +41 -1
  2. package/README.md +11 -11
  3. package/README.zh-CN.md +3 -5
  4. package/SUBAGENT_ADAPTER_CONTRACT.md +3 -1
  5. package/dist/agent-profile.d.ts +18 -62
  6. package/dist/agent-profile.d.ts.map +1 -1
  7. package/dist/agent-profile.js +27 -249
  8. package/dist/agent-profile.js.map +1 -1
  9. package/dist/codecs/agent-profile.d.ts +53 -0
  10. package/dist/codecs/agent-profile.d.ts.map +1 -0
  11. package/dist/codecs/agent-profile.js +176 -0
  12. package/dist/codecs/agent-profile.js.map +1 -0
  13. package/dist/codecs/index.d.ts +5 -0
  14. package/dist/codecs/index.d.ts.map +1 -0
  15. package/dist/codecs/index.js +3 -0
  16. package/dist/codecs/index.js.map +1 -0
  17. package/dist/codecs/prompt-stack.d.ts +18 -0
  18. package/dist/codecs/prompt-stack.d.ts.map +1 -0
  19. package/dist/codecs/prompt-stack.js +479 -0
  20. package/dist/codecs/prompt-stack.js.map +1 -0
  21. package/dist/compile-cycle.d.ts +18 -0
  22. package/dist/compile-cycle.d.ts.map +1 -0
  23. package/dist/compile-cycle.js +13 -0
  24. package/dist/compile-cycle.js.map +1 -0
  25. package/dist/compiler.d.ts +10 -4
  26. package/dist/compiler.d.ts.map +1 -1
  27. package/dist/compiler.js +53 -19
  28. package/dist/compiler.js.map +1 -1
  29. package/dist/extension-registry.d.ts.map +1 -1
  30. package/dist/extension-registry.js +5 -2
  31. package/dist/extension-registry.js.map +1 -1
  32. package/dist/forge-config.d.ts +0 -105
  33. package/dist/forge-config.d.ts.map +1 -1
  34. package/dist/forge-config.js +1 -313
  35. package/dist/forge-config.js.map +1 -1
  36. package/dist/forge-v1/analyzer.d.ts +3 -0
  37. package/dist/forge-v1/analyzer.d.ts.map +1 -0
  38. package/dist/forge-v1/analyzer.js +50 -0
  39. package/dist/forge-v1/analyzer.js.map +1 -0
  40. package/dist/forge-v1/index.d.ts +6 -0
  41. package/dist/forge-v1/index.d.ts.map +1 -0
  42. package/dist/forge-v1/index.js +13 -0
  43. package/dist/forge-v1/index.js.map +1 -0
  44. package/dist/forge-v1/parser.d.ts +3 -0
  45. package/dist/forge-v1/parser.d.ts.map +1 -0
  46. package/dist/forge-v1/parser.js +186 -0
  47. package/dist/forge-v1/parser.js.map +1 -0
  48. package/dist/forge-v1/renderer.d.ts +9 -0
  49. package/dist/forge-v1/renderer.d.ts.map +1 -0
  50. package/dist/forge-v1/renderer.js +181 -0
  51. package/dist/forge-v1/renderer.js.map +1 -0
  52. package/dist/forge-v1/types.d.ts +87 -0
  53. package/dist/forge-v1/types.d.ts.map +1 -0
  54. package/dist/forge-v1/types.js +4 -0
  55. package/dist/forge-v1/types.js.map +1 -0
  56. package/dist/index.d.ts +12 -12
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +49 -81
  59. package/dist/index.js.map +1 -1
  60. package/dist/lifecycle.d.ts +6 -5
  61. package/dist/lifecycle.d.ts.map +1 -1
  62. package/dist/lifecycle.js +43 -112
  63. package/dist/lifecycle.js.map +1 -1
  64. package/dist/loader.d.ts +5 -8
  65. package/dist/loader.d.ts.map +1 -1
  66. package/dist/loader.js +10 -467
  67. package/dist/loader.js.map +1 -1
  68. package/dist/macro-engine.d.ts +9 -14
  69. package/dist/macro-engine.d.ts.map +1 -1
  70. package/dist/macro-engine.js +5 -238
  71. package/dist/macro-engine.js.map +1 -1
  72. package/dist/payload-command.d.ts +6 -6
  73. package/dist/payload-command.d.ts.map +1 -1
  74. package/dist/payload-command.js.map +1 -1
  75. package/dist/payload-state.d.ts +16 -0
  76. package/dist/payload-state.d.ts.map +1 -0
  77. package/dist/payload-state.js +14 -0
  78. package/dist/payload-state.js.map +1 -0
  79. package/dist/preset-command.d.ts +4 -4
  80. package/dist/preset-command.d.ts.map +1 -1
  81. package/dist/preset-command.js +33 -104
  82. package/dist/preset-command.js.map +1 -1
  83. package/dist/preview.d.ts +4 -4
  84. package/dist/preview.d.ts.map +1 -1
  85. package/dist/preview.js +17 -8
  86. package/dist/preview.js.map +1 -1
  87. package/dist/profile-command.d.ts +2 -2
  88. package/dist/profile-command.d.ts.map +1 -1
  89. package/dist/profile-command.js +15 -14
  90. package/dist/profile-command.js.map +1 -1
  91. package/dist/profile-service.d.ts.map +1 -1
  92. package/dist/profile-service.js +23 -39
  93. package/dist/profile-service.js.map +1 -1
  94. package/dist/prompt-analysis.d.ts +24 -0
  95. package/dist/prompt-analysis.d.ts.map +1 -0
  96. package/dist/prompt-analysis.js +84 -0
  97. package/dist/prompt-analysis.js.map +1 -0
  98. package/dist/prompt-runtime.d.ts +19 -0
  99. package/dist/prompt-runtime.d.ts.map +1 -0
  100. package/dist/prompt-runtime.js +30 -0
  101. package/dist/prompt-runtime.js.map +1 -0
  102. package/dist/regex.js +6 -12
  103. package/dist/regex.js.map +1 -1
  104. package/dist/render-helpers.d.ts +2 -10
  105. package/dist/render-helpers.d.ts.map +1 -1
  106. package/dist/render-helpers.js +3 -53
  107. package/dist/render-helpers.js.map +1 -1
  108. package/dist/repositories/agent-profile.d.ts +36 -0
  109. package/dist/repositories/agent-profile.d.ts.map +1 -0
  110. package/dist/repositories/agent-profile.js +153 -0
  111. package/dist/repositories/agent-profile.js.map +1 -0
  112. package/dist/repositories/index.d.ts +3 -0
  113. package/dist/repositories/index.d.ts.map +1 -0
  114. package/dist/repositories/index.js +3 -0
  115. package/dist/repositories/index.js.map +1 -0
  116. package/dist/repositories/prompt-stack.d.ts +57 -0
  117. package/dist/repositories/prompt-stack.d.ts.map +1 -0
  118. package/dist/repositories/prompt-stack.js +175 -0
  119. package/dist/repositories/prompt-stack.js.map +1 -0
  120. package/dist/runtime/profile-runtime.d.ts +2 -2
  121. package/dist/runtime/profile-runtime.d.ts.map +1 -1
  122. package/dist/runtime/profile-runtime.js +13 -10
  123. package/dist/runtime/profile-runtime.js.map +1 -1
  124. package/dist/runtime/prompt-stack-runtime.d.ts +4 -3
  125. package/dist/runtime/prompt-stack-runtime.d.ts.map +1 -1
  126. package/dist/runtime/prompt-stack-runtime.js +42 -57
  127. package/dist/runtime/prompt-stack-runtime.js.map +1 -1
  128. package/dist/runtime/tool-policy-runtime.d.ts +2 -2
  129. package/dist/runtime/tool-policy-runtime.d.ts.map +1 -1
  130. package/dist/runtime/tool-policy-runtime.js +3 -3
  131. package/dist/runtime/tool-policy-runtime.js.map +1 -1
  132. package/dist/session-adapter.d.ts +17 -0
  133. package/dist/session-adapter.d.ts.map +1 -0
  134. package/dist/session-adapter.js +57 -0
  135. package/dist/session-adapter.js.map +1 -0
  136. package/dist/slot-renderers.d.ts +16 -3
  137. package/dist/slot-renderers.d.ts.map +1 -1
  138. package/dist/slot-renderers.js +15 -63
  139. package/dist/slot-renderers.js.map +1 -1
  140. package/dist/stack-migration.d.ts +6 -0
  141. package/dist/stack-migration.d.ts.map +1 -1
  142. package/dist/stack-migration.js +49 -52
  143. package/dist/stack-migration.js.map +1 -1
  144. package/dist/subagent/fingerprints.d.ts +24 -0
  145. package/dist/subagent/fingerprints.d.ts.map +1 -0
  146. package/dist/subagent/fingerprints.js +81 -0
  147. package/dist/subagent/fingerprints.js.map +1 -0
  148. package/dist/subagent/host-port.d.ts +296 -0
  149. package/dist/subagent/host-port.d.ts.map +1 -0
  150. package/dist/subagent/host-port.js +560 -0
  151. package/dist/subagent/host-port.js.map +1 -0
  152. package/dist/subagent/index.d.ts +10 -10
  153. package/dist/subagent/index.d.ts.map +1 -1
  154. package/dist/subagent/index.js +9 -10
  155. package/dist/subagent/index.js.map +1 -1
  156. package/dist/subagent-host.d.ts +99 -18
  157. package/dist/subagent-host.d.ts.map +1 -1
  158. package/dist/subagent-host.js +143 -146
  159. package/dist/subagent-host.js.map +1 -1
  160. package/dist/template-render.d.ts +18 -0
  161. package/dist/template-render.d.ts.map +1 -0
  162. package/dist/template-render.js +232 -0
  163. package/dist/template-render.js.map +1 -0
  164. package/dist/types.d.ts +40 -27
  165. package/dist/types.d.ts.map +1 -1
  166. package/dist/types.js.map +1 -1
  167. package/dist/web-editor/client-script.generated.d.ts.map +1 -1
  168. package/dist/web-editor/client-script.generated.js +1 -1
  169. package/dist/web-editor/client-script.generated.js.map +1 -1
  170. package/dist/web-editor/client-styles.generated.d.ts.map +1 -1
  171. package/dist/web-editor/client-styles.generated.js +1 -1
  172. package/dist/web-editor/client-styles.generated.js.map +1 -1
  173. package/dist/web-editor/server.d.ts.map +1 -1
  174. package/dist/web-editor/server.js +1 -68
  175. package/dist/web-editor/server.js.map +1 -1
  176. package/dist/web-editor/styles.d.ts.map +1 -1
  177. package/dist/web-editor/styles.js +13 -0
  178. package/dist/web-editor/styles.js.map +1 -1
  179. package/dist/web-editor/types.d.ts +0 -35
  180. package/dist/web-editor/types.d.ts.map +1 -1
  181. package/dist/web-host.d.ts +4 -2
  182. package/dist/web-host.d.ts.map +1 -1
  183. package/dist/web-host.js +32 -103
  184. package/dist/web-host.js.map +1 -1
  185. package/dist/workspace.d.ts +74 -0
  186. package/dist/workspace.d.ts.map +1 -0
  187. package/dist/workspace.js +351 -0
  188. package/dist/workspace.js.map +1 -0
  189. package/docs/README.md +6 -4
  190. package/docs/concepts/agent-profiles.md +1 -1
  191. package/docs/concepts/prompt-stacks.md +5 -5
  192. package/docs/design/README.md +21 -3
  193. package/docs/design/architecture-0.5.md +211 -0
  194. package/docs/design/archive/0.5-full-proposal/0.5-consumer-audit.md +62 -0
  195. package/docs/design/archive/0.5-full-proposal/0.5-inventory.md +208 -0
  196. package/docs/design/archive/0.5-full-proposal/0.5-phase0-decision-drafts.md +237 -0
  197. package/docs/design/archive/0.5-full-proposal/README.md +12 -0
  198. package/docs/design/archive/0.5-full-proposal/architecture-0.5.md +423 -0
  199. package/docs/design/archive/0.5-full-proposal/host-discovery-spike.md +103 -0
  200. package/docs/design/archive/0.5-full-proposal/template-language-spike.md +156 -0
  201. package/docs/design/context-diff-plan.md +72 -0
  202. package/docs/design/decision-template.md +33 -0
  203. package/docs/design/roadmap-0.4-archive.md +1 -1
  204. package/docs/design/subagents/interface-design.md +1 -1
  205. package/docs/development/architecture-rules.md +148 -0
  206. package/docs/development/roadmap.md +37 -5
  207. package/docs/development/scoped-global-profiles-stacks.md +1 -1
  208. package/docs/development/setup.md +6 -4
  209. package/docs/getting-started.md +5 -4
  210. package/docs/guides/custom-macros-and-slots.md +8 -7
  211. package/docs/guides/debugging.md +1 -1
  212. package/docs/guides/delegation.md +18 -22
  213. package/docs/guides/migrating-to-0.5.md +107 -0
  214. package/docs/guides/use-cases.md +7 -11
  215. package/docs/guides/web-editor.md +5 -9
  216. package/docs/reference/commands.md +4 -3
  217. package/docs/reference/configuration.md +17 -18
  218. package/docs/reference/features.md +34 -80
  219. package/docs/reference/macros-and-slots.md +70 -49
  220. package/docs/reference/public-api.md +64 -17
  221. package/docs/reference/stack-schema.md +14 -9
  222. package/docs/reference/subagent-host-port.md +49 -0
  223. package/docs/zh-CN/README.md +3 -3
  224. package/docs/zh-CN/concepts/agent-profiles.md +1 -1
  225. package/docs/zh-CN/concepts/prompt-stacks.md +4 -4
  226. package/docs/zh-CN/getting-started.md +3 -2
  227. package/docs/zh-CN/guides/delegation.md +10 -12
  228. package/docs/zh-CN/guides/migrating-to-0.5.md +92 -0
  229. package/docs/zh-CN/guides/web-editor.md +4 -4
  230. package/docs/zh-CN/reference/commands.md +3 -2
  231. package/examples/custom-system-status-extension/README.md +3 -3
  232. package/examples/custom-system-status-extension/index.ts +2 -1
  233. package/examples/custom-system-status-extension/prompt-stack.json +4 -3
  234. package/examples/default-prompt-stack.json +9 -4
  235. package/examples/image-reader-prompt-stack.json +16 -26
  236. package/examples/reviewer-prompt-stack.json +13 -24
  237. package/package.json +101 -120
  238. package/dist/runtime/subagent-runtime.d.ts +0 -45
  239. package/dist/runtime/subagent-runtime.d.ts.map +0 -1
  240. package/dist/runtime/subagent-runtime.js +0 -343
  241. package/dist/runtime/subagent-runtime.js.map +0 -1
  242. package/dist/runtime-state.d.ts +0 -30
  243. package/dist/runtime-state.d.ts.map +0 -1
  244. package/dist/runtime-state.js +0 -17
  245. package/dist/runtime-state.js.map +0 -1
  246. package/dist/sillytavern-importer/items.d.ts +0 -3
  247. package/dist/sillytavern-importer/items.d.ts.map +0 -1
  248. package/dist/sillytavern-importer/items.js +0 -88
  249. package/dist/sillytavern-importer/items.js.map +0 -1
  250. package/dist/sillytavern-importer/macros.d.ts +0 -15
  251. package/dist/sillytavern-importer/macros.d.ts.map +0 -1
  252. package/dist/sillytavern-importer/macros.js +0 -141
  253. package/dist/sillytavern-importer/macros.js.map +0 -1
  254. package/dist/sillytavern-importer/prompt-order.d.ts +0 -6
  255. package/dist/sillytavern-importer/prompt-order.d.ts.map +0 -1
  256. package/dist/sillytavern-importer/prompt-order.js +0 -38
  257. package/dist/sillytavern-importer/prompt-order.js.map +0 -1
  258. package/dist/sillytavern-importer/regex.d.ts +0 -3
  259. package/dist/sillytavern-importer/regex.d.ts.map +0 -1
  260. package/dist/sillytavern-importer/regex.js +0 -275
  261. package/dist/sillytavern-importer/regex.js.map +0 -1
  262. package/dist/sillytavern-importer/report.d.ts +0 -21
  263. package/dist/sillytavern-importer/report.d.ts.map +0 -1
  264. package/dist/sillytavern-importer/report.js +0 -166
  265. package/dist/sillytavern-importer/report.js.map +0 -1
  266. package/dist/sillytavern-importer/types.d.ts +0 -106
  267. package/dist/sillytavern-importer/types.d.ts.map +0 -1
  268. package/dist/sillytavern-importer/types.js +0 -2
  269. package/dist/sillytavern-importer/types.js.map +0 -1
  270. package/dist/sillytavern-importer.d.ts +0 -5
  271. package/dist/sillytavern-importer.d.ts.map +0 -1
  272. package/dist/sillytavern-importer.js +0 -117
  273. package/dist/sillytavern-importer.js.map +0 -1
  274. package/dist/subagent/canonical.d.ts +0 -22
  275. package/dist/subagent/canonical.d.ts.map +0 -1
  276. package/dist/subagent/canonical.js +0 -24
  277. package/dist/subagent/canonical.js.map +0 -1
  278. package/dist/subagent/context.d.ts +0 -8
  279. package/dist/subagent/context.d.ts.map +0 -1
  280. package/dist/subagent/context.js +0 -125
  281. package/dist/subagent/context.js.map +0 -1
  282. package/dist/subagent/contract.d.ts +0 -10
  283. package/dist/subagent/contract.d.ts.map +0 -1
  284. package/dist/subagent/contract.js +0 -10
  285. package/dist/subagent/contract.js.map +0 -1
  286. package/dist/subagent/plan.d.ts +0 -18
  287. package/dist/subagent/plan.d.ts.map +0 -1
  288. package/dist/subagent/plan.js +0 -157
  289. package/dist/subagent/plan.js.map +0 -1
  290. package/dist/subagent/preflight.d.ts +0 -4
  291. package/dist/subagent/preflight.d.ts.map +0 -1
  292. package/dist/subagent/preflight.js +0 -108
  293. package/dist/subagent/preflight.js.map +0 -1
  294. package/dist/subagent/request.d.ts +0 -4
  295. package/dist/subagent/request.d.ts.map +0 -1
  296. package/dist/subagent/request.js +0 -154
  297. package/dist/subagent/request.js.map +0 -1
  298. package/dist/subagent/response.d.ts +0 -8
  299. package/dist/subagent/response.d.ts.map +0 -1
  300. package/dist/subagent/response.js +0 -155
  301. package/dist/subagent/response.js.map +0 -1
  302. package/dist/subagent/tools.d.ts +0 -4
  303. package/dist/subagent/tools.d.ts.map +0 -1
  304. package/dist/subagent/tools.js +0 -42
  305. package/dist/subagent/tools.js.map +0 -1
  306. package/dist/subagent/types.d.ts +0 -272
  307. package/dist/subagent/types.d.ts.map +0 -1
  308. package/dist/subagent/types.js +0 -3
  309. package/dist/subagent/types.js.map +0 -1
  310. package/dist/subagent/validation.d.ts +0 -35
  311. package/dist/subagent/validation.d.ts.map +0 -1
  312. package/dist/subagent/validation.js +0 -314
  313. package/dist/subagent/validation.js.map +0 -1
  314. package/dist/subagent-command.d.ts +0 -5
  315. package/dist/subagent-command.d.ts.map +0 -1
  316. package/dist/subagent-command.js +0 -249
  317. package/dist/subagent-command.js.map +0 -1
  318. package/dist/subagent-profile-tool.d.ts +0 -49
  319. package/dist/subagent-profile-tool.d.ts.map +0 -1
  320. package/dist/subagent-profile-tool.js +0 -125
  321. package/dist/subagent-profile-tool.js.map +0 -1
  322. package/dist/subagent-tool.d.ts +0 -77
  323. package/dist/subagent-tool.d.ts.map +0 -1
  324. package/dist/subagent-tool.js +0 -559
  325. package/dist/subagent-tool.js.map +0 -1
  326. package/docs/guides/sillytavern-import.md +0 -47
  327. package/docs/reference/subagent-adapter.md +0 -204
  328. package/examples/sillytavern-dm-writer-prompt-stack.json +0 -190
@@ -0,0 +1,103 @@
1
+ # Pi cross-extension host-discovery spike
2
+
3
+ [Design index](../../README.md) · [0.5 architecture plan](../../architecture-0.5.md) · [0.5 migration inventory](0.5-inventory.md)
4
+
5
+ Status: completed Phase 0 spike; architecture decision pending
6
+
7
+ Date: 2026-08-17
8
+
9
+ ## Question
10
+
11
+ Can a separately installed `pi-forge-subagents` Pi extension reliably discover
12
+ and use the active main `pi-forge` extension without importing its internal
13
+ runtime state or relying on a process-global registry?
14
+
15
+ ## Environment and method
16
+
17
+ - Tested against the repository's installed
18
+ `@earendil-works/pi-coding-agent` 0.83.0 fixture.
19
+ - Used Pi's exported `createEventBus`, `createExtensionRuntime`, and actual
20
+ `loadExtensionFromFactory` loader. The factories used only `pi.events`.
21
+ - This was an in-memory Phase-0 harness, not a shipped extension or production
22
+ host-port implementation.
23
+
24
+ The harness covered both extension load orders, compatible and incompatible
25
+ versions, and unregistering a host listener. All cases passed:
26
+
27
+ | Case | Result |
28
+ |---|---|
29
+ | Host factory loads before client factory | Client discovery receives one compatible host reply |
30
+ | Client factory loads before host factory | Client's availability listener receives the later host announcement |
31
+ | Client requires an unsupported version | No host is accepted |
32
+ | Host unregisters its listener | A subsequent discovery request receives no reply |
33
+
34
+ ## Observations
35
+
36
+ 1. `ExtensionAPI.events` is a shared event bus across extension factories
37
+ loaded into the same Pi runtime.
38
+ 2. The bus exposes only `emit(channel, data)` and `on(channel, handler)`, with
39
+ an unsubscribe function. It does not expose a service registry, request
40
+ return values, sender identity, serialization, timeout, version negotiation,
41
+ duplicate-host handling, or lifecycle ownership.
42
+ 3. Pi wraps each event handler in an asynchronous error boundary. `emit()` is
43
+ therefore fire-and-forget; callers must not depend on handler order or a
44
+ synchronous reply, even though a handler with no initial `await` may appear
45
+ to reply synchronously in a simple test.
46
+ 4. The bus is in-process. It can coordinate packages loaded into one Pi
47
+ invocation, but it is not a cross-process, remote, or security boundary.
48
+ 5. A direct import of a module-level workspace singleton remains unsuitable:
49
+ Pi package installation can produce independent package instances, and that
50
+ mechanism provides no duplicate-host or reload semantics.
51
+
52
+ ## Recommended decision candidate
53
+
54
+ Use Pi's event bus as the transport for a **versioned, session-scoped Forge host
55
+ RPC protocol**. Do not use a process registry or a direct reference to
56
+ `ForgeWorkspace`.
57
+
58
+ The port must use plain, validated data messages rather than function callbacks
59
+ or the return value of `emit()`. A minimal protocol has these message families:
60
+
61
+ | Channel family | Direction | Required behavior |
62
+ |---|---|---|
63
+ | `@zihanw/pi-forge/host/v1/discover` | Client → host | Carries request ID, client identity/version, and supported protocol range |
64
+ | `@zihanw/pi-forge/host/v1/available` | Host → clients | Announces host ID, protocol version, capability set, and lifecycle generation after discovery listener registration |
65
+ | `@zihanw/pi-forge/host/v1/reply` | Host → client | Correlates discovery or operation result with request ID; carries typed success/failure data |
66
+ | `@zihanw/pi-forge/host/v1/request` | Client → host | Invokes only documented operations using validated request data |
67
+ | `@zihanw/pi-forge/host/v1/update` | Host → client | Optional bounded progress/resource-change events correlated to subscription or run IDs |
68
+ | `@zihanw/pi-forge/host/v1/unavailable` | Host → clients | Invalidates host ID/generation during disposal or replacement |
69
+
70
+ The final operation catalogue is still open, but must expose results and
71
+ snapshots—not Pi contexts, `ForgeWorkspace`, loaded resource objects, web-editor
72
+ state, callbacks, or internal registries. Cancellation must be a request by
73
+ run ID; it must not transfer `AbortSignal` objects through the port.
74
+
75
+ ## Required protocol rules before implementation
76
+
77
+ - Clients subscribe to reply, availability, update, and unavailable events
78
+ before announcing/discovering; they use a bounded timeout and clean up all
79
+ listeners.
80
+ - A host registers discovery/request listeners before sending `available`.
81
+ - Host and client validate every message and ignore unknown protocol versions,
82
+ malformed payloads, stale generations, and unrelated request IDs.
83
+ - More than one live compatible host is an explicit `host.duplicate` failure;
84
+ the client must not silently choose a host by load order. Incompatible hosts
85
+ are reported distinctly.
86
+ - `ForgeWorkspace` owns host registration, generation, and disposal. On Pi
87
+ session shutdown/reload it sends `unavailable`, unregisters listeners, and
88
+ rejects new work; clients invalidate outstanding handles and report a clear
89
+ unavailable diagnostic.
90
+ - The port is an integration contract, not a trust boundary. Pi extensions
91
+ already execute in the same trusted process; the host must not transmit raw
92
+ credentials, arbitrary live contexts, or undeclared project data.
93
+ - The optional package must remain usable as an ordinary Pi extension only when
94
+ the main host is present and protocol-compatible. Its absence must leave the
95
+ main pi-forge extension fully functional.
96
+
97
+ ## Limits and follow-up
98
+
99
+ This validates the event-bus transport in Pi 0.83.0 only. The Phase-0 Pi
100
+ compatibility gate still needs a documented range and packed-install matrix that
101
+ includes the release-time Pi version. The protocol, public operations, package
102
+ versioning, configuration ownership, and migration remain architecture decisions
103
+ to be accepted before Phase 5 implementation.
@@ -0,0 +1,156 @@
1
+ # Template-language and compiler spike
2
+
3
+ [Design index](README.md) · [0.5 architecture plan](architecture-0.5.md) · [0.5 migration inventory](0.5-inventory.md)
4
+
5
+ Status: completed Phase 0 spike; architecture decision pending
6
+
7
+ Date: 2026-08-17
8
+
9
+ ## Question
10
+
11
+ Which template language can replace 0.4 macros while keeping prompt compilation
12
+ deterministic over immutable inputs, statically analyzable, migrationable, and
13
+ free of arbitrary evaluation or template loading?
14
+
15
+ ## Characterized 0.4 surface
16
+
17
+ The current macro language mixes four separate concerns:
18
+
19
+ | Concern | 0.4 behavior | 0.5 direction |
20
+ |---|---|---|
21
+ | Immutable runtime values | Latest user message, date/time, cwd, selected tools, active model | Keep as explicit values in `PromptEnvironment` |
22
+ | Static reusable values | `stack.variables`, referenced as `{{name}}` | Keep as JSON-compatible immutable `parameters` |
23
+ | Text transforms | `trim`, `upper`, `lower`, `json`, `xml`, including nesting | Retain only as a finite, pure filter set if accepted |
24
+ | Control flow | `ifvar`, `ifeq`, `iftools`, `ifslot`, with lazy branches | Replace only with a parsed, side-effect-free conditional form if accepted |
25
+ | Mutable state | Turn/session variables plus set/get/clear macros and `variables` slot | Remove; emit migration diagnostics |
26
+ | Executable extension code | Trusted custom macro and slot renderers receive live runtime objects | Replace only through a separately accepted, immutable template/slot port |
27
+
28
+ The current examples demonstrate simple parameter and runtime interpolation;
29
+ the image-reader and SillyTavern examples also depend on nested filters,
30
+ conditionals, and mutable variables. SillyTavern is already removed from 0.5
31
+ scope. Any remaining non-mechanical construct must be diagnosed by migration,
32
+ not silently approximated.
33
+
34
+ ## Requirements
35
+
36
+ The v2 engine must provide one `parse → analyze → render` contract and meet all
37
+ of the following:
38
+
39
+ - parsing has no filesystem, network, process, clock, environment, or Pi access;
40
+ - rendering receives only a frozen normalized environment and parsed template;
41
+ - undefined paths, invalid filters, invalid conditions, parse errors, recursion,
42
+ and output-limit breaches are typed errors, never silent mutation or ambient
43
+ fallback;
44
+ - analysis returns source-spanned dependencies on environment paths, filters,
45
+ control predicates, and registered template/slot capabilities;
46
+ - no includes, extends, imports, macros, function calls, loops, arbitrary
47
+ property traversal, or general expression evaluation;
48
+ - whitespace is preserved unless a deliberately documented syntax construct
49
+ changes it; rendering has a bounded output size;
50
+ - preview, parent runtime, and subagent preparation invoke the same engine and
51
+ receive recorded environment/provenance values.
52
+
53
+ ## Candidate comparison
54
+
55
+ | Candidate | Parse/analyze/render fit | Restriction and determinism | Migration fit | Result |
56
+ |---|---|---|---|---|
57
+ | General Jinja-like engine (Nunjucks) | Has parsing/rendering and strict-undefined option | Fails the trust requirement: its own documentation says it does not sandbox user-defined templates; it supports loaders, includes, extensions, async behavior, and a rich expression language | Familiar delimiters, but an unsafe/surplus language would need a fragile denylist | Reject |
58
+ | Restricted LiquidJS wrapper | Provides parsed templates and documented static analysis | Better parser option, but its Liquid tags, includes, filters, and plugin model would still require a security-sensitive allowlist/wrapper; syntax is not Jinja-compatible | Requires a new Liquid migration plus wrapper-specific dependency semantics | Do not adopt for 0.5 |
59
+ | Small Forge AST grammar | Contract is designed exactly for parse/analyze/render and the allowed syntax is closed | No ambient capabilities or loaders by construction; all values and filters are explicit | Breaking but small, predictable, and mechanically migrates common interpolation | Recommended candidate |
60
+
61
+ Nunjucks documents both its lack of sandboxing and its extensible loaders,
62
+ includes, custom tags, and asynchronous behavior. [Nunjucks API](https://mozilla.github.io/nunjucks/api.html)
63
+ LiquidJS documents parsed-template reuse and static analysis, but is a broader
64
+ Liquid language with tags and plugins rather than the deliberately closed v2
65
+ language. [LiquidJS static analysis](https://liquidjs.com/tutorials/static-analysis.html)
66
+
67
+ ## Recommended decision candidate: `forge-v1`
68
+
69
+ Adopt a small parsed Forge template grammar. It resembles Jinja only at the
70
+ delimiter level; it must not claim Jinja compatibility.
71
+
72
+ ### Closed grammar
73
+
74
+ ```text
75
+ text := any text outside delimiters
76
+ output := "{{" path filters? "}}"
77
+ filters := ("|" filterName)*
78
+ if-block := "{% if" predicate "%}" template
79
+ ("{% else %}" template)? "{% endif %}"
80
+ path := identifier ("." identifier)*
81
+ predicate := path | path "==" json-string | path "!=" json-string
82
+ ```
83
+
84
+ The only initially proposed paths are `parameters.*` and documented
85
+ `runtime.*` fields. No bracket access, method invocation, object construction,
86
+ numeric arithmetic, loops, user-defined functions, imports, includes, extends,
87
+ macro definitions, or arbitrary expressions are in the grammar.
88
+
89
+ The initially proposed filters are `trim`, `upper`, `lower`, `json`, and `xml`.
90
+ They are pure, unary, versioned built-ins; unknown filters are errors. Tool and
91
+ slot conditions, if retained, must be represented as documented boolean values
92
+ in the environment rather than callable helpers. The exact v2 environment field
93
+ names remain part of the architecture decision.
94
+
95
+ ### Engine contract
96
+
97
+ ```ts
98
+ interface TemplateEngine {
99
+ readonly id: "forge-v1";
100
+ readonly version: 1;
101
+ parse(source: string): TemplateParseResult;
102
+ analyze(ast: TemplateAst): readonly TemplateDependency[];
103
+ render(ast: TemplateAst, environment: PromptEnvironment): TemplateRenderResult;
104
+ }
105
+ ```
106
+
107
+ `PromptEnvironment` is a frozen, JSON-compatible snapshot. It must carry an
108
+ explicit timestamp and timezone/formatting policy, cwd, model identity, selected
109
+ tool facts, latest-user-message data, static parameters, and the Pi-derived
110
+ inputs required by slots as separate structured values. The compiler—not the
111
+ engine—owns history placement, structured slots, tool/skill policy, base-system
112
+ prompt handling, message assembly, and regex stages.
113
+
114
+ The render receipt must identify engine ID/version, template dependencies,
115
+ environment/provenance fingerprint, diagnostics, and output size. Dependencies
116
+ are authoritative for preview and subagent preparation; no consumer may
117
+ re-parse syntax independently.
118
+
119
+ ### Extension boundary implication
120
+
121
+ The current `registerMacro()` API cannot cross this boundary unchanged because
122
+ it receives live runtime objects and can mutate or observe ambient state. The
123
+ template decision does not yet authorize a replacement API. If custom template
124
+ values, filters, or slots remain in 0.5, a separate accepted port must give them
125
+ only frozen input, declared dependency/capability metadata, bounded output, and
126
+ a pure-rendering contract. Trusted extension code is not a security boundary;
127
+ the core engine nevertheless must remain deterministic when no such extension
128
+ port participates.
129
+
130
+ ## Migration disposition
131
+
132
+ | 0.4 construct | Candidate v2 migration | Result when not mechanical |
133
+ |---|---|---|
134
+ | `{{name}}` for a known static value | `{{ parameters.name }}` | Warn when name could be a custom macro or ambiguous |
135
+ | `{{lastUserMessage}}`, date/time, cwd, model, selected tools | Corresponding documented `runtime.*` path | Mechanical only after field names are accepted |
136
+ | Nested value filters | Equivalent output plus pipeline where semantics match | Diagnose when nested arguments or behavior differ |
137
+ | `ifvar` / `ifeq` | Parsed `if` predicate where semantics match | Diagnose `iftools`/`ifslot` until boolean environment fields are accepted |
138
+ | Custom macros and slots | New port only after its decision | Diagnose; no compatibility execution shim |
139
+ | Set/get/clear variable macros and `variables` slot | None | Error-level migration diagnostic; removed behavior |
140
+ | Unknown macros / unresolved-policy behavior | None | Error-level diagnostic in v2; no keep/warn rendering policy |
141
+
142
+ The migration tool must preserve the original source, produce a field/item-level
143
+ report, and refuse a partly transformed file unless the user explicitly accepts
144
+ diagnostics. It must not rewrite SillyTavern inputs in 0.5.
145
+
146
+ ## Required acceptance and implementation evidence
147
+
148
+ Before Phase 3 implementation, accept the grammar, exact environment schema,
149
+ filter/condition set, undefined/error semantics, output limits, engine ID in
150
+ stack schema v2, extension-port disposition, and migration behavior.
151
+
152
+ Implementation then needs characterization and conformance coverage for parser
153
+ errors, source spans, strict undefined values, filters, nested/else conditionals,
154
+ whitespace, output limits, deterministic repeated render, dependency receipts,
155
+ preview/runtime identity, and migration diagnostics for every removed mutable or
156
+ custom-macro construct.
@@ -0,0 +1,72 @@
1
+ # Context Diff — design plan (post-0.5.0)
2
+
3
+ Status: planned. Target release: 0.5.1 (first feature release after 0.5.0).
4
+ Owner of truth: this document; discussion record lives in the 0.5.x review thread.
5
+
6
+ ## Goal
7
+
8
+ Per-turn observability for prompt changes so users can optimize KV-cache reuse:
9
+
10
+ - Mark which blocks of the prompt changed after each turn.
11
+ - Show how many tokens were added/removed/modified (delta vs the previous turn).
12
+ - Mark where the KV-cache prefix survives ("cache boundary").
13
+
14
+ Secondary goal: merge with live preview — while editing a stack, debounce-compile and diff against the previous compile, so edits show their prompt/token impact immediately.
15
+
16
+ ## Existing building blocks
17
+
18
+ - `src/payload-capture.ts` — captures the real provider request payload (secret-redacted), with `approxTokens` (chars/4) estimation. `SAFE_TOKEN_METADATA_KEYS` already whitelists `cached_tokens` etc., so real cache-hit numbers can be surfaced if response usage ever becomes available.
19
+ - `src/preview.ts` — edit-time compile output split into sections (system + per-message), each with chars/approxTokens.
20
+ - Legacy editor already polls payload state every 2s.
21
+
22
+ ## Two data sources
23
+
24
+ | Scenario | Source | Question answered |
25
+ |---|---|---|
26
+ | Edit time (live preview merge point) | debounced compile vs previous compile | "what does this edit change, how many tokens" |
27
+ | Run time (KV-cache truth) | diff of consecutive real payloads | "where did the cache break, how many extra tokens this turn" |
28
+
29
+ ## Core engine: `src/context-diff.ts` (host-neutral pure functions)
30
+
31
+ ```
32
+ TurnSnapshot { turnId, capturedAt, stackId, blocks: Block[] }
33
+ Block { key, role, text, chars, approxTokens, hash }
34
+ TurnDiff { blocks: DiffBlock[], prefixTokens, prefixRatio, deltaTokens, summary }
35
+ DiffBlock { status: same|added|removed|modified, before?, after?, tokenDelta }
36
+ ```
37
+
38
+ Cache-boundary algorithm: KV-cache hits depend on the longest common prefix of the serialized request. Walk the block arrays in order while hashes match; at the first mismatch, trim a char-level common prefix inside that block and convert to tokens. Render a boundary marker: "cache valid up to ~18,204 tokens (63% of prompt)". No Myers diff needed — prefix + block classification suffices.
39
+
40
+ Honesty note: tokens are chars/4 estimates. Relative deltas are accurate; absolute boundary claims must be labeled "approx" in UI and in promo material.
41
+
42
+ ## UI
43
+
44
+ Promote the Preview modal into a dockable right-side panel with two tabs:
45
+
46
+ - **Compiled** — current preview content, auto-refresh with 500ms debounce while editing (this is the live preview).
47
+ - **Diff** — top summary strip (`Turn N · +412 tokens vs previous · cache boundary ~63% · 2 blocks changed`), block list with green/red/yellow gutters and per-block token chips, scissor-line boundary marker.
48
+
49
+ Run-time mode: payload poll captures a new payload → store in rolling history (last 20 turns) → auto-compute diff.
50
+ Edit mode: diff current edited compile vs the active on-disk version.
51
+
52
+ Implementation constraint: write the panel as a self-contained Vue component bridged in via the vue-host mechanism. Do NOT add more imperative code to `legacy-editor.ts`. Do NOT refactor the legacy editor in the same lane.
53
+
54
+ ## Phases / estimate
55
+
56
+ | Phase | Content | Effort |
57
+ |---|---|---|
58
+ | 0 | Design freeze + golden fixtures (turn payload sets) | 0.5d |
59
+ | 1 | `context-diff.ts` engine + unit tests (prefix/add/remove/modify/token rollups) | 1d |
60
+ | 2 | Server endpoint + rolling snapshot state on the web host | 0.5d |
61
+ | 3 | Web UI: preview dock + Diff view (self-contained Vue, bridged) | 1.5d |
62
+ | 4 | Browser tests + docs + changelog | 0.5–1d |
63
+
64
+ Total ~4–5 working days; ~1 calendar week with review. Descope option (~3d): run-time diff only, live preview reduced to plain auto-refresh without edit-time diffing.
65
+
66
+ ## Release narrative
67
+
68
+ Do not fold into 0.5.0 — 0.5.0 ships as the architecture-split release; context diff headlines 0.5.1 as "observability". The promo video leads with this feature (money shot: edit one system-prompt line, watch the cache-boundary marker jump to the top).
69
+
70
+ ## Promo video pipeline (reference: AIGC/VIDEO_PRODUCTION ep02)
71
+
72
+ Reuse the ep02 pipeline: `PLAN.md` → script draft → `narration.json` (production source of truth) → roughcut → review. Terminology follows the ep02 rule: say it in plain words first, name the formal term once ("KV cache" on first mention, then "prefix cache / cache reuse region").
@@ -0,0 +1,33 @@
1
+ # Architecture decision: title
2
+
3
+ [Design index](README.md)
4
+
5
+ Status: proposed
6
+
7
+ Date: YYYY-MM-DD
8
+
9
+ Owners: maintainer names or roles
10
+
11
+ ## Context
12
+
13
+ Describe the concrete problem, current constraints, and evidence that a decision is needed.
14
+
15
+ ## Decision
16
+
17
+ State the chosen behavior and ownership boundary precisely. Include a small interface or diagram when it makes the boundary easier to verify.
18
+
19
+ ## Consequences
20
+
21
+ Describe what becomes easier, what becomes harder or impossible, and which compatibility or operational costs are accepted.
22
+
23
+ ## Migration
24
+
25
+ List the implementation order, compatibility period if any, user migration, and removal condition for temporary shims.
26
+
27
+ ## Alternatives considered
28
+
29
+ Record the serious alternatives and why they were rejected. Do not use this section to restate obviously inferior options.
30
+
31
+ ## Verification
32
+
33
+ Define the tests, dependency checks, documentation, and release evidence that prove the decision was implemented.
@@ -10,7 +10,7 @@ This file was the forward-looking product roadmap during 0.4 development. Comple
10
10
  - [Feature inventory](../reference/features.md): implemented profile, prompt-stack, web-editor, payload, extension, packaging, and experimental subagent behavior.
11
11
  - [Changelog](../../CHANGELOG.md): release and unreleased change history.
12
12
  - [Subagent interface design](subagents/interface-design.md): accepted request/response architecture, context model, responsibility boundaries, and implementation history.
13
- - [Subagent adapter contract](../reference/subagent-adapter.md): current exported contract, validation, enforcement receipts, and adapter responsibilities.
13
+ - Subagent adapter contract: the 0.4 exported contract documented the execution surface that moved to `@zihanw/pi-forge-subagents` in 0.5; see the [subagent host port contract](../reference/subagent-host-port.md) for the current boundary.
14
14
  - [SDK spike findings](subagents/sdk-spike-findings.md): historical real Pi SDK findings for model/auth resolution, exact prompt preparation, dynamic tools, media, timeout, and cleanup.
15
15
  - [Subagent design review](subagents/design-review.md): issues that drove resolution, preflight, enforcement, task-preservation, fingerprint, and response revisions.
16
16
  - [Public API policy](../reference/public-api.md): stable, experimental, and internal compatibility surfaces.
@@ -1,6 +1,6 @@
1
1
  # Subagent request/response design
2
2
 
3
- Status: historical design and implementation record for the completed 0.4 path. Execution ownership (backend registry, preflight binding, plan sealing, conversation/execution fingerprints, lifecycle, and the fresh-process `pi-subprocess-readonly`/`pi-rpc-readonly` backends) lives in `@zihanw/pi-subagent-runtime`; the former in-package registry and `pi-sdk-isolated` compatibility backend were removed in that migration. The broader Pi SDK spike is recorded in the [SDK spike findings](sdk-spike-findings.md) (harness since removed), and the concrete exported semantics are documented in the [subagent adapter contract](../../reference/subagent-adapter.md).
3
+ Status: historical design and implementation record for the completed 0.4 path. Execution ownership (backend registry, preflight binding, plan sealing, conversation/execution fingerprints, lifecycle, and the fresh-process `pi-subprocess-readonly`/`pi-rpc-readonly` backends) lives in `@zihanw/pi-subagent-runtime`; the former in-package registry and `pi-sdk-isolated` compatibility backend were removed in that migration. The broader Pi SDK spike is recorded in the [SDK spike findings](sdk-spike-findings.md) (harness since removed), and the 0.5 boundary is documented in the [subagent host port contract](../../reference/subagent-host-port.md).
4
4
 
5
5
  ## Goals
6
6
 
@@ -0,0 +1,148 @@
1
+ # Architecture and development rules
2
+
3
+ [Documentation](../README.md) · [0.5 architecture plan](../design/architecture-0.5.md)
4
+
5
+ These rules keep implementation throughput from outrunning architectural understanding. They apply to humans and coding agents. The intent is not to slow local implementation; it is to make boundary changes scarce, explicit, and reviewable.
6
+
7
+ ## Decision authority
8
+
9
+ Implementation may move quickly inside an accepted boundary. A change to a boundary, persistent model, public contract, or product concept requires an explicit design decision before implementation.
10
+
11
+ Agents may draft decisions, diagrams, interfaces, migration plans, and implementation slices. A human maintainer accepts the decision and its tradeoffs.
12
+
13
+ ## Development mode for 0.5
14
+
15
+ Until the 0.5 release gates are complete:
16
+
17
+ - Feature development is frozen unless the feature is explicitly added to the 0.5 plan.
18
+ - Only one boundary-changing initiative should be active at a time.
19
+ - Simplification and removal are preferred over compatibility layers without a demonstrated consumer.
20
+ - A completed slice must leave the repository in a coherent, documented, verified state; partial architecture migrations must not silently become permanent.
21
+
22
+ ## Required dependency direction
23
+
24
+ ```mermaid
25
+ flowchart LR
26
+ Adapters["Adapters<br/>Pi lifecycle · commands · HTTP · browser"]
27
+ Application["Application<br/>ForgeWorkspace · stack/profile services"]
28
+ Domain["Domain/core<br/>schemas · catalogs · compiler · templates"]
29
+ Ports["Ports<br/>repositories · runtime controller · host capabilities"]
30
+ Infrastructure["Infrastructure<br/>filesystem · Pi SDK · process registry"]
31
+
32
+ Adapters --> Application
33
+ Application --> Domain
34
+ Application --> Ports
35
+ Infrastructure --> Ports
36
+ ```
37
+
38
+ Infrastructure implements inward-facing ports. Domain/core code must not import adapters or infrastructure implementations.
39
+
40
+ Until the target directory/package structure exists, apply the rule to logical ownership rather than relying on current file locations.
41
+
42
+ ## Component rules
43
+
44
+ ### Domain and compiler
45
+
46
+ - Domain types contain no command, HTTP, browser, TUI, or filesystem behavior.
47
+ - Prompt compilation consumes a normalized, immutable prompt environment rather than a live Pi context.
48
+ - Compilation returns prompt output, diagnostics, dependencies, and any explicit state transition. Renderers must not silently mutate shared state.
49
+ - Template dependency analysis is part of the template-engine contract; subagents and previews must not reverse-engineer template syntax independently.
50
+ - Tool policy remains stack-owned. Profiles may reference a stack but must not duplicate the policy.
51
+
52
+ ### Application services
53
+
54
+ - `ForgeWorkspace` owns a coherent workspace snapshot and coordinates resource reloads.
55
+ - `PromptStackService` owns stack resolution, mutation, activation, validation, and preview/compile entry points.
56
+ - `AgentProfileService` owns profile resolution, mutation, preflight, transactional application, provenance, and drift.
57
+ - Services expose typed results. Adapters translate those results into command messages, HTTP statuses, and UI view models.
58
+
59
+ ### Persistence
60
+
61
+ - All profile and stack reads/writes/deletes go through repositories.
62
+ - Repositories use codecs as the single source of parsing, normalization, validation, and serialization.
63
+ - Mutations validate scope and containment. No adapter may call `writeFileSync` or `unlinkSync` for a domain resource.
64
+ - **Lean 0.5.0 interim:** repositories and codecs are introduced in minimal form. Expected-fingerprint writes, fingerprinting in codecs, and guaranteed atomic file replacement are 0.5.x work. Current replacement semantics remain characterized by tests until then.
65
+
66
+ ### State
67
+
68
+ - Every mutable value has one named owner.
69
+ - Debug payload capture, browser presentation state, prompt resource state, and session provenance are separate state slices.
70
+ - Reload produces a complete snapshot before publishing it. Consumers must not observe a half-reloaded stack/profile graph.
71
+ - New session-persisted state requires a version, restoration semantics, migration behavior, and an architecture decision.
72
+
73
+ ### Adapters and optional packages
74
+
75
+ - Commands and HTTP handlers parse input, invoke an application service, and render a result; they do not implement domain workflows.
76
+ - Browser view models must not leak into core runtime state.
77
+ - Optional extensions use versioned host ports and fail clearly when the required host version is unavailable.
78
+ - `pi-forge-subagents` must not load an independent conflicting copy of the active Forge workspace or reach into pi-forge internals.
79
+
80
+ ### Public APIs
81
+
82
+ - Public API is an allowlist of package entry points, not a side effect of file placement.
83
+ - The 0.5 release removes the broad `src/*` compatibility surface.
84
+ - A public type should describe a stable domain or port, not an internal runtime object.
85
+ - Breaking changes belong in the changelog and the 0.5 migration guide.
86
+
87
+ ## Change classification
88
+
89
+ ### Local change
90
+
91
+ A local change stays within one accepted component and preserves schemas, state ownership, dependency direction, and public behavior. It needs focused tests and ordinary review.
92
+
93
+ Examples include fixing a renderer, improving a view mapper, or extracting a private helper within one service.
94
+
95
+ ### Boundary-affecting change
96
+
97
+ A boundary-affecting change moves responsibility, adds a dependency across components, changes an application port, or introduces a new state owner. It requires an accepted architecture decision or an amendment to the 0.5 plan.
98
+
99
+ ### Product-affecting change
100
+
101
+ A product-affecting change adds/removes a feature, changes a schema, alters persistence or trust semantics, or changes a public API/package. It requires an accepted decision, migration notes, documentation updates, and explicit human review.
102
+
103
+ ## Architecture-decision triggers
104
+
105
+ An architecture decision is required when changing any of the following:
106
+
107
+ - package or extension boundaries;
108
+ - JSON schemas or persisted session entries;
109
+ - public exports or compatibility promises;
110
+ - `ForgeWorkspace` or another state owner;
111
+ - compiler/template semantics;
112
+ - filesystem repository guarantees;
113
+ - trust, approval, tool-policy, or provider-egress boundaries;
114
+ - cross-extension discovery;
115
+ - removal or addition of a product-level feature.
116
+
117
+ Use the [architecture decision template](../design/decision-template.md). Small decisions can amend the active 0.5 plan instead of creating a separate document if the alternatives and consequences remain clear.
118
+
119
+ ## Pull-request requirements
120
+
121
+ During lean 0.5.0, every pull request states summary, breaking impact, and verification, as in the repository template. Boundary- or product-affecting changes must link to the accepted decision in the [lean 0.5 plan](../design/architecture-0.5.md); a separate decision document is not required for decisions already accepted there.
122
+
123
+ For full-target 0.5.x work, pull requests additionally state change classification, affected components, dependency-direction/state/schema/public-API impact, and the linked decision.
124
+
125
+ ## Definition of done
126
+
127
+ A slice is complete when:
128
+
129
+ 1. Ownership and dependency direction match the accepted architecture.
130
+ 2. Behavior is covered at the narrowest useful level; cross-adapter behavior has integration coverage.
131
+ 3. No domain persistence remains in command, HTTP, or browser-host adapters.
132
+ 4. Public and persisted changes include migration notes.
133
+ 5. Current and target documentation reflect any moved boundary.
134
+ 6. Generated artifacts are synchronized.
135
+ 7. `npm run verify` passes for release-sized or cross-cutting work.
136
+
137
+ ## Review stop conditions
138
+
139
+ Stop and request architectural review when an implementation needs to:
140
+
141
+ - import an adapter from domain/application code;
142
+ - access `PiForgeRuntimeState` from a new component;
143
+ - add mutable template variables or render-time side effects;
144
+ - write a resource outside a repository;
145
+ - add a wildcard export;
146
+ - duplicate profile/stack loading in another extension;
147
+ - retain a compatibility layer with no named consumer or removal date;
148
+ - introduce a second boundary-changing initiative before the current one is complete.
@@ -4,6 +4,37 @@
4
4
 
5
5
  This file contains forward-looking product work only. Completed capability belongs in the [feature inventory](../reference/features.md), release history in the root [changelog](../../CHANGELOG.md), and completed investigation in the [design archive](../design/README.md).
6
6
 
7
+ ## 0.5.0 breaking cleanup (lean)
8
+
9
+ 0.5.0 is a deliberately breaking cleanup release plus the minimum foundation for 0.5.x. Net-new feature work is frozen.
10
+
11
+ The authoritative scope, accepted decisions, implementation lanes, and release gates are in the [lean 0.5 architecture plan](../design/architecture-0.5.md). The original six-phase architecture target remains the long-term goal and is archived in the [full proposal](../design/archive/0.5-full-proposal/README.md).
12
+
13
+ Implementation order:
14
+
15
+ 0. Documentation convergence: archive the full proposal and make the lean plan active.
16
+ 1a. Removals only: remove SillyTavern, mutable variable state/macros/slot, variable session entries, and regex `display`/`both`.
17
+ 1b. Compiler, schema v2, and extension contract: implement `forge-v1`, frozen `PromptEnvironment`, immutable `parameters`, and the redesigned trusted extension port; retain `finalize` under lifecycle-adapter ownership.
18
+ 1c. Migration and documentation: v1-to-v2 utility, example migration, and English/Chinese breaking notes.
19
+ 1d. Compiler/extension conformance: pure slot contract symmetry, slot dependency resolution, shared dependency analysis/compilation context, lazy extension evaluation, nested conditionals, parser fixes, and focused conformance tests.
20
+ 2a. Minimal repositories and codecs: make repositories/codecs the only stack/profile read/write path; defer fingerprint and atomic writes.
21
+ 2b. ForgeWorkspace and host port v1: minimal snapshot owner plus `/subagent` discovery, profile listing/snapshot, and prepare with mandatory lifecycle semantics.
22
+ 3. Subagent extraction: move subagent code into `pi-forge-subagents`; remove the main-package hard dependency and delegation UI; optional package owns dedicated `subagents.json` files.
23
+ 3.5. Host-neutrality hardening: host-neutral prompt compiler, `ForgeWorkspace` as the single resource-state owner, optional-package docs/config alignment, and portable packed smoke.
24
+ 4. Public surface and release: (4a) Forge-native host contract — main drops the runtime dependency and the synthetic preparation model, and the 0.4 execution contract moves to the optional package; (4b) public-surface cut — root default, root named extension API, and `/subagent` only, `src/*` aliases removed, package checks flipped to the allowlist; (4c) optional-package packed smoke over a loopback transport; (4d) migration notes and English/Chinese docs; (4e) packed-install verification and the 0.5.0 release.
25
+
26
+ Only one lane is active at a time. Sandbox, staged writes, new prompt features, richer imports, and the remaining full-plan architecture work remain deferred until after this sequence.
27
+
28
+ ## 0.5.x continuation
29
+
30
+ After 0.5.0, continue toward the archived full target in small increments:
31
+
32
+ - expected-fingerprint writes, codec fingerprinting, and atomic persistence;
33
+ - full `PromptStackService` / `AgentProfileService` / `ForgeWorkspace` ownership;
34
+ - physical `pi-forge-core` boundaries and dependency-direction checking;
35
+ - complete host RPC catalogue, progress events, and optional subagent UI;
36
+ - public-surface classification and rolling Pi compatibility matrix.
37
+
7
38
  ## 0.4 baseline
8
39
 
9
40
  The profile UI, foreground-delegation dogfooding, dependency compatibility work, and documentation reorganization are complete and merged to `main`. Runtime beta.2 is published, and the packed 0.4.0 extension passes the full verification surface against the documented Pi versions.
@@ -12,25 +43,26 @@ The 0.4.1 release adds scoped global profiles and prompt stacks with project-ove
12
43
 
13
44
  Stable 0.4 does not imply that the subagent adapter or runtime has become stable. Prompt stacks and ordinary profile use remain independent of delegation.
14
45
 
15
- ## Near-term candidates
46
+ ## Deferred candidates
16
47
 
17
48
  ### Sandbox and staged writes
18
49
 
19
50
  Evaluate an optional backend that can honestly enforce roots, process behavior, symlink containment, and requested network policy. Keep shared-user as an explicit compatibility boundary. Design writes as a separately approved staged patch/change set; do not add write/edit/shell tools directly to the shared-user child.
20
51
 
21
- ### Import, history, and prompt diagnostics
52
+ ### History and prompt diagnostics
22
53
 
23
- Add SillyTavern fidelity only from real unsupported fixtures. Candidate history controls need concrete use cases and dangling tool-pair tests. Provider-payload rewriting and display-only streaming regex remain deferred until a stable, previewable lifecycle hook exists.
54
+ Candidate history controls need concrete use cases and dangling tool-pair tests. Provider-payload rewriting and display-only streaming regex remain deferred until a stable, previewable lifecycle hook exists. SillyTavern fidelity is no longer a core roadmap goal; 0.5 removes it.
24
55
 
25
56
  ## Product guardrails
26
57
 
27
58
  - Prompt-stack JSON stays declarative; executable customization stays in trusted extensions/packages.
59
+ - Prompt rendering moves toward immutable inputs and explicit outputs; do not add mutable variable behavior during 0.5.
28
60
  - Profiles remain one-shot presets, not continuous runtime owners.
29
61
  - Tool and skill policy stays in prompt stacks, not profiles.
30
62
  - Skill filtering is model-visible prompt filtering, not an invocation or security boundary.
31
- - Delegation remains opt-in, foreground, clean-context, and fail-closed on missing capabilities.
63
+ - Delegation remains opt-in, foreground, clean-context, and fail-closed on missing capabilities, and lives in the optional package.
32
64
  - Do not report shared-user read-only policy as an OS sandbox.
33
- - New editor workflows require real-browser coverage.
65
+ - New editor product workflows are frozen; migration changes retain real-browser coverage.
34
66
  - Run the full verification and package checks before release.
35
67
 
36
68
  The detailed completed 0.4 plan is retained in the [historical roadmap](../design/roadmap-0.4-archive.md).
@@ -2,7 +2,7 @@
2
2
 
3
3
  [Documentation](../README.md) · [Roadmap](roadmap.md)
4
4
 
5
- Status: accepted design; implementation complete for 0.4.1 (identity, catalogs, scoped stacks/profiles, persistence, auto-activation, delegation, zh-CN docs, and web-editor global create/fork/import with explicit scope).
5
+ Status: accepted design; implementation complete for 0.4.1 (identity, catalogs, scoped stacks/profiles, persistence, auto-activation, delegation, zh-CN docs, and web-editor global create/fork/import with explicit scope). Historical record: the delegation-policy references to `.pi/forge/config.json` `subagents.*` below are superseded in 0.5 — delegation configuration moved to the optional package's dedicated `subagents.json` files (see the [0.5 migration guide](../guides/migrating-to-0.5.md)).
6
6
 
7
7
  ## Confirmed design decisions
8
8
 
@@ -14,6 +14,8 @@ pi
14
14
 
15
15
  Trust the project in Pi and use `/reload` when needed.
16
16
 
17
+ Before making structural changes, read the [architecture and development rules](architecture-rules.md) and the active [0.5 architecture plan](../design/architecture-0.5.md). Boundary, schema, persistence, public API, and product changes require an accepted architecture decision before implementation.
18
+
17
19
  The npm package loads compiled `dist/index.js` and intentionally omits physical `src/` files. Clone or fork the repository to inspect or modify source; do not edit generated files in `node_modules`.
18
20
 
19
21
  ## Load the extension
@@ -67,9 +69,9 @@ Document the tested range separately from peer constraints. Pi-coupled experimen
67
69
 
68
70
  ## Package boundaries
69
71
 
70
- - `@zihanw/pi-forge` is the Pi extension and stable macro/slot registration surface.
71
- - `@zihanw/pi-forge/subagent` is the preferred experimental host-contract entry point.
72
- - `@zihanw/pi-subagent-runtime` owns execution lifecycle and fresh-process backends.
73
- - Legacy `@zihanw/pi-forge/src/*` aliases resolve to compiled compatibility modules and are not a promise that internals are public.
72
+ - `@zihanw/pi-forge` is the Pi extension (default export) and the stable macro/slot registration surface; no other root exports or `src/*` aliases exist.
73
+ - `@zihanw/pi-forge/subagent` is the experimental versioned host-port entry point (wire DTOs, validators, transport, client/host lifecycle, canonical fingerprints).
74
+ - `@zihanw/pi-forge-subagents` is the optional package owning subagent execution, configuration, and the 0.4 execution contract.
75
+ - `@zihanw/pi-subagent-runtime` owns execution lifecycle and fresh-process backends (a dependency of the optional package, not of the main package).
74
76
 
75
77
  See the [public API policy](../reference/public-api.md).