From 222be7adc20ac15c2e9e6aec9c244d5c05c406ff Mon Sep 17 00:00:00 2001 From: Kirill Kolesnikov Date: Thu, 3 Sep 2026 21:56:15 +0700 Subject: [PATCH 1/2] Record why Playground stays VueForge-only, and clean up a stale acknowledgement Every other piece that was retained turned out to be portable once read closely enough: icons looked like a rendering engine that would have to be duplicated, and turned out to be static data once precomputed. Playground is not that shape, and this writes down why rather than leaving the migration map's "retain" read as a placeholder for a future branch. packages/playground-core/src/typescriptWorker.js transpiles with a real Worker. packages/playground-core/src/runtimes/browserRuntime.ts runs the result in an HTMLIFrameElement it sandboxes. Neither has a server equivalent, not because of a framework choice but because the whole premise -- a live, reactive preview of code someone is still typing -- has no request/response analog. An icon's output is a pure function of three known values; Playground's output is a pure function of the next keystroke, which is not a harder version of the same problem. Nothing in the CodeMonster line depends on any of the three playground packages. They stay retain in the migration map, now for a documented reason with the technical evidence behind it, cited from the maturity backlog entries. CodeBlock is left out on purpose -- syntax highlighting has no comparable browser-only primitive forcing the same conclusion, and deserves its own reading rather than riding on this one. Also removed a stale entry from check:frozen-showcase's acknowledgedChanges. Its one entry described an import-path fix from the icons branch that has since merged to main, so the change it acknowledged no longer differs -- exactly the staleness the check exists to catch, catching itself. --- .../architecture/playground-line-ownership.md | 77 +++++++++++++++++++ .../codemonster-ui-maturity-backlog.json | 6 +- scripts/ci/frozen-showcase.mjs | 5 +- 3 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 docs/architecture/playground-line-ownership.md diff --git a/docs/architecture/playground-line-ownership.md b/docs/architecture/playground-line-ownership.md new file mode 100644 index 00000000..e1c99ff4 --- /dev/null +++ b/docs/architecture/playground-line-ownership.md @@ -0,0 +1,77 @@ +# Playground line ownership + +Status: Accepted +Date: 2026-09-03 + +## Decision + +`vueforge-playground`, `vueforge-playground-core`, and `vueforge-playground-vite-plugin` stay +VueForge-only. This is not a deferral pending a future branch — the reason a component, a layout, a +theme, and icon geometry all crossed into CodeMonster and Playground does not is different in kind, +not in degree. + +Every other retained-then-carried-across piece of this migration turned out to be portable once read +closely enough: icons looked like a rendering engine that would have to be reimplemented twice and +turned out to be static data once precomputed. Playground is not that shape. Its entire purpose is +running code a person just typed, live, in a browser tab. There is no equivalent to precompute, +because the input is not a fixed set of combinations — it is arbitrary text a person is still typing. + +## What it actually does + +`packages/playground-core/src/typescriptWorker.js` transpiles TypeScript with a real `Worker`: + +```js +self.addEventListener('message', (event) => { + const request = event.data; + // ... transpiles request.sources with the TypeScript compiler ... + self.postMessage({ type: 'result', id: request.id, outputs: [] }); +}); +``` + +`packages/playground-core/src/runtimes/browserRuntime.ts` runs the result in a sandboxed frame: + +```ts +export function runInIframe(iframe: HTMLIFrameElement, html: string): void { + iframe.setAttribute('sandbox', 'allow-scripts'); + iframe.srcdoc = html; +} +``` + +`Worker` and `HTMLIFrameElement` are not framework choices Vue happened to make; they are what a +live, reactive, sandboxed preview of arbitrary just-edited code requires. Nothing server-rendered can +supply either. A PHP request answers one request with one response; it cannot host a running, +interactive session watching for the next keystroke. + +Nothing in the CodeMonster line depends on any of the three packages — not `ui-vue`, not +`ui-layouts`, not `ui-runtime`, not `ui-icons`. They are consumed by `examples/vue`'s frozen showcase +alone, as documentation tooling, and by nothing this kit ships. + +## Why this is not the icon question again + +Icons looked hard to port for the same surface reason — a 2,135-line renderer nobody wanted to +duplicate — and turned out to be portable because an icon's output is a pure function of three known +values (name, family, variant), computable once and shipped as data to both platforms. + +Playground's output is a pure function of whatever a person is typing at this instant, plus every +prior keystroke's effect on module state, timers, and DOM. There are not 928 combinations to +enumerate; there are infinitely many programs, and the interesting cases are wrong ones nobody has +written yet. Precomputing an infinite space is not a harder version of precomputing a finite one — it +is not the same operation. + +## What would change this decision + +A requirement for a _server-executed_ code sandbox — running untrusted code in an isolated process +and streaming structured results back, the way a hosted notebook or CI runner does — would be a +different product built on different primitives (a subprocess sandbox, not a `Worker`; a result +stream, not a `postMessage` bridge to a live DOM). That is not what today's Playground is, porting it +would not produce it, and no such requirement exists today. + +## Consequences + +- `@codemonster-ru/vueforge-playground`, `-playground-core`, and `-playground-vite-plugin` remain + `retain` in the migration map, now for a documented and provable reason rather than a placeholder + one. +- The migration map's outstanding `retain` entries narrow to `vueforge-codeblock`, which is a + separate decision — syntax highlighting has no comparable browser-only primitive forcing the same + conclusion, and deserves its own reading rather than riding on this one. +- No further branch is expected to revisit this unless the requirement in the section above appears. diff --git a/migration/codemonster-ui-maturity-backlog.json b/migration/codemonster-ui-maturity-backlog.json index 1c7154a4..bc94090a 100644 --- a/migration/codemonster-ui-maturity-backlog.json +++ b/migration/codemonster-ui-maturity-backlog.json @@ -162,15 +162,15 @@ }, { "package": "@codemonster-ru/vueforge-playground", - "reason": "Executable preview infrastructure is a retained Vue product rather than design-system UI." + "reason": "Runs a live, sandboxed browser preview of arbitrary just-typed code -- Worker-based TypeScript transpilation and an HTMLIFrameElement sandbox, neither of which a server request/response can supply. Nothing in the CodeMonster line depends on it. See docs/architecture/playground-line-ownership.md." }, { "package": "@codemonster-ru/vueforge-playground-core", - "reason": "Browser compilation sessions remain framework-independent Playground infrastructure." + "reason": "The Worker transpiler and iframe runtime behind the Playground UI; browser-only by the primitives it uses, not by framework choice. See docs/architecture/playground-line-ownership.md." }, { "package": "@codemonster-ru/vueforge-playground-vite-plugin", - "reason": "Virtual demo module generation remains build-tool infrastructure for the retained Playground product." + "reason": "A Vite/Rollup build plugin that turns demo files into virtual modules for the retained Playground product; build-tool infrastructure with no PHP analog. See docs/architecture/playground-line-ownership.md." } ] } diff --git a/scripts/ci/frozen-showcase.mjs b/scripts/ci/frozen-showcase.mjs index ae1ba539..110191f7 100644 --- a/scripts/ci/frozen-showcase.mjs +++ b/scripts/ci/frozen-showcase.mjs @@ -37,10 +37,7 @@ export function findFrozenShowcaseChanges(changedPaths) { * describes and rot into a list nobody reads. The visual gate remains the authority on whether * pixels actually moved; this only records that someone looked. */ -export const acknowledgedChanges = { - 'examples/vue/src/sections/icons/IconsShowcase.vue': - 'Import path only: the VueForge icons package moved to packages/vueforge-icons so the CodeMonster line could take the packages/icons name. The imported file is unchanged, so nothing renders differently.', -}; +export const acknowledgedChanges = {}; /** Reports acknowledged paths that no longer differ, so the list cannot outlive its reasons. */ export function findStaleAcknowledgements(changedPaths, acknowledged = acknowledgedChanges) { From 87819d7cd419b28e67e3fb9f1054124679f67b86 Mon Sep 17 00:00:00 2001 From: Kirill Kolesnikov Date: Thu, 3 Sep 2026 22:26:59 +0700 Subject: [PATCH 2/2] Fold the evidence into the existing playground-ownership.md, not a new file docs/architecture/playground-ownership.md already decided this on 2026-08-13, with a thorough package-boundary and threat-model argument. Adding a second file reaching the same conclusion under a near-identical name would have fragmented one decision across two documents that don't reference each other -- exactly the kind of stale-pointer problem this repo has been correcting all week. The prior document didn't yet have the technical proof (Worker, HTMLIFrameElement have no server equivalent) or the explicit contrast with icons -- a decision worth re-checking after icons and layouts both turned out portable once read closely enough. That's now a section in the existing file under a Revisited date, not a rewrite of reasoning that was already correct. --- .../architecture/playground-line-ownership.md | 77 ------------------- docs/architecture/playground-ownership.md | 51 ++++++++++-- .../codemonster-ui-maturity-backlog.json | 6 +- 3 files changed, 48 insertions(+), 86 deletions(-) delete mode 100644 docs/architecture/playground-line-ownership.md diff --git a/docs/architecture/playground-line-ownership.md b/docs/architecture/playground-line-ownership.md deleted file mode 100644 index e1c99ff4..00000000 --- a/docs/architecture/playground-line-ownership.md +++ /dev/null @@ -1,77 +0,0 @@ -# Playground line ownership - -Status: Accepted -Date: 2026-09-03 - -## Decision - -`vueforge-playground`, `vueforge-playground-core`, and `vueforge-playground-vite-plugin` stay -VueForge-only. This is not a deferral pending a future branch — the reason a component, a layout, a -theme, and icon geometry all crossed into CodeMonster and Playground does not is different in kind, -not in degree. - -Every other retained-then-carried-across piece of this migration turned out to be portable once read -closely enough: icons looked like a rendering engine that would have to be reimplemented twice and -turned out to be static data once precomputed. Playground is not that shape. Its entire purpose is -running code a person just typed, live, in a browser tab. There is no equivalent to precompute, -because the input is not a fixed set of combinations — it is arbitrary text a person is still typing. - -## What it actually does - -`packages/playground-core/src/typescriptWorker.js` transpiles TypeScript with a real `Worker`: - -```js -self.addEventListener('message', (event) => { - const request = event.data; - // ... transpiles request.sources with the TypeScript compiler ... - self.postMessage({ type: 'result', id: request.id, outputs: [] }); -}); -``` - -`packages/playground-core/src/runtimes/browserRuntime.ts` runs the result in a sandboxed frame: - -```ts -export function runInIframe(iframe: HTMLIFrameElement, html: string): void { - iframe.setAttribute('sandbox', 'allow-scripts'); - iframe.srcdoc = html; -} -``` - -`Worker` and `HTMLIFrameElement` are not framework choices Vue happened to make; they are what a -live, reactive, sandboxed preview of arbitrary just-edited code requires. Nothing server-rendered can -supply either. A PHP request answers one request with one response; it cannot host a running, -interactive session watching for the next keystroke. - -Nothing in the CodeMonster line depends on any of the three packages — not `ui-vue`, not -`ui-layouts`, not `ui-runtime`, not `ui-icons`. They are consumed by `examples/vue`'s frozen showcase -alone, as documentation tooling, and by nothing this kit ships. - -## Why this is not the icon question again - -Icons looked hard to port for the same surface reason — a 2,135-line renderer nobody wanted to -duplicate — and turned out to be portable because an icon's output is a pure function of three known -values (name, family, variant), computable once and shipped as data to both platforms. - -Playground's output is a pure function of whatever a person is typing at this instant, plus every -prior keystroke's effect on module state, timers, and DOM. There are not 928 combinations to -enumerate; there are infinitely many programs, and the interesting cases are wrong ones nobody has -written yet. Precomputing an infinite space is not a harder version of precomputing a finite one — it -is not the same operation. - -## What would change this decision - -A requirement for a _server-executed_ code sandbox — running untrusted code in an isolated process -and streaming structured results back, the way a hosted notebook or CI runner does — would be a -different product built on different primitives (a subprocess sandbox, not a `Worker`; a result -stream, not a `postMessage` bridge to a live DOM). That is not what today's Playground is, porting it -would not produce it, and no such requirement exists today. - -## Consequences - -- `@codemonster-ru/vueforge-playground`, `-playground-core`, and `-playground-vite-plugin` remain - `retain` in the migration map, now for a documented and provable reason rather than a placeholder - one. -- The migration map's outstanding `retain` entries narrow to `vueforge-codeblock`, which is a - separate decision — syntax highlighting has no comparable browser-only primitive forcing the same - conclusion, and deserves its own reading rather than riding on this one. -- No further branch is expected to revisit this unless the requirement in the section above appears. diff --git a/docs/architecture/playground-ownership.md b/docs/architecture/playground-ownership.md index a054844b..cf53cc5b 100644 --- a/docs/architecture/playground-ownership.md +++ b/docs/architecture/playground-ownership.md @@ -2,6 +2,7 @@ Status: Accepted Date: 2026-08-13 +Revisited: 2026-09-03 Roadmap item: `CMUI-148` ## Decision @@ -15,14 +16,52 @@ No `ui-playground` package, `CmPlayground` component, Annabel Razor adapter, or is added to the CodeMonster UI 1.0 topology. The existing packages remain supported under the VueForge migration and maintenance policy and may run alongside CodeMonster UI. +## Why this held up under the same test that moved icons and layouts + +By 2026-09-03 the icon and layout lines had each looked hard to port and turned out portable once +read closely enough — icons looked like a 2,135-line rendering engine that would have to be +duplicated in PHP and turned out to be static data once precomputed. That made "retained" worth +re-checking rather than trusting as a standing label: was Playground actually the same kind of +question, just not yet worked through? + +It is not, and the code says why rather than a category label asserting it. +`packages/playground-core/src/typescriptWorker.js` transpiles with a real `Worker`: + +```js +self.addEventListener('message', (event) => { + const request = event.data; + // ... transpiles request.sources with the TypeScript compiler ... + self.postMessage({ type: 'result', id: request.id, outputs: [] }); +}); +``` + +`packages/playground-core/src/runtimes/browserRuntime.ts` runs the result in a sandboxed frame: + +```ts +export function runInIframe(iframe: HTMLIFrameElement, html: string): void { + iframe.setAttribute('sandbox', 'allow-scripts'); + iframe.srcdoc = html; +} +``` + +`Worker` and `HTMLIFrameElement` are not a framework choice Vue happened to make; they are what a +live, reactive, sandboxed preview of arbitrary just-typed code requires, and nothing server-rendered +can supply either. A PHP request answers one request with one response; it cannot host a running +session watching for the next keystroke. An icon's output is a pure function of three known values, +computable once and shipped as data. Playground's output is a pure function of whatever a person is +typing at this instant — not a harder version of the same precomputation problem, a different one. + +Confirmed rather than assumed: nothing in `ui-vue`, `ui-layouts`, `ui-runtime`, or `ui-icons` +depends on any of the three Playground packages. + ## Reviewed package boundaries -| Package | Responsibility | Ownership outcome | -| --- | --- | --- | -| `@codemonster-ru/vueforge-playground-core` | Framework-independent session state, TypeScript worker, module compilation, iframe document generation, and preview messaging | Retained Playground runtime product | -| `@codemonster-ru/vueforge-playground` | Vue editor/preview UI, component-preview mode, theme bridging, console/files/actions regions, and Playground session orchestration | Retained Vue Playground adapter | -| `@codemonster-ru/vueforge-playground-vite-plugin` | Build-time virtual modules backed by explicitly configured local source files | Retained Playground build integration | -| `examples/vue` | Repository showcase and manual integration consumer | Application; migrated separately by `CMUI-153` | +| Package | Responsibility | Ownership outcome | +| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | +| `@codemonster-ru/vueforge-playground-core` | Framework-independent session state, TypeScript worker, module compilation, iframe document generation, and preview messaging | Retained Playground runtime product | +| `@codemonster-ru/vueforge-playground` | Vue editor/preview UI, component-preview mode, theme bridging, console/files/actions regions, and Playground session orchestration | Retained Vue Playground adapter | +| `@codemonster-ru/vueforge-playground-vite-plugin` | Build-time virtual modules backed by explicitly configured local source files | Retained Playground build integration | +| `examples/vue` | Repository showcase and manual integration consumer | Application; migrated separately by `CMUI-153` | The framework-independent core remains outside `ui-runtime`. The latter enhances canonical CodeMonster UI DOM with small controllers; it must not become a compiler, worker host, module diff --git a/migration/codemonster-ui-maturity-backlog.json b/migration/codemonster-ui-maturity-backlog.json index bc94090a..9a88d3c0 100644 --- a/migration/codemonster-ui-maturity-backlog.json +++ b/migration/codemonster-ui-maturity-backlog.json @@ -162,15 +162,15 @@ }, { "package": "@codemonster-ru/vueforge-playground", - "reason": "Runs a live, sandboxed browser preview of arbitrary just-typed code -- Worker-based TypeScript transpilation and an HTMLIFrameElement sandbox, neither of which a server request/response can supply. Nothing in the CodeMonster line depends on it. See docs/architecture/playground-line-ownership.md." + "reason": "Runs a live, sandboxed browser preview of arbitrary just-typed code -- Worker-based TypeScript transpilation and an HTMLIFrameElement sandbox, neither of which a server request/response can supply. Nothing in the CodeMonster line depends on it. See docs/architecture/playground-ownership.md." }, { "package": "@codemonster-ru/vueforge-playground-core", - "reason": "The Worker transpiler and iframe runtime behind the Playground UI; browser-only by the primitives it uses, not by framework choice. See docs/architecture/playground-line-ownership.md." + "reason": "The Worker transpiler and iframe runtime behind the Playground UI; browser-only by the primitives it uses, not by framework choice. See docs/architecture/playground-ownership.md." }, { "package": "@codemonster-ru/vueforge-playground-vite-plugin", - "reason": "A Vite/Rollup build plugin that turns demo files into virtual modules for the retained Playground product; build-tool infrastructure with no PHP analog. See docs/architecture/playground-line-ownership.md." + "reason": "A Vite/Rollup build plugin that turns demo files into virtual modules for the retained Playground product; build-tool infrastructure with no PHP analog. See docs/architecture/playground-ownership.md." } ] }