diff options
Diffstat (limited to '.dispatch/rules')
| -rw-r--r-- | .dispatch/rules/frontend-inject-transport.md | 7 | ||||
| -rw-r--r-- | .dispatch/rules/frontend-interpreter-generic.md | 8 | ||||
| -rw-r--r-- | .dispatch/rules/frontend-no-ambient-state.md | 7 | ||||
| -rw-r--r-- | .dispatch/rules/frontend-pure-core.md | 7 |
4 files changed, 29 insertions, 0 deletions
diff --git a/.dispatch/rules/frontend-inject-transport.md b/.dispatch/rules/frontend-inject-transport.md new file mode 100644 index 0000000..fbe83d7 --- /dev/null +++ b/.dispatch/rules/frontend-inject-transport.md @@ -0,0 +1,7 @@ +# Rule: inject the transport; parsers are pure + +The WS/NDJSON framing + parsing is a PURE function (bytes/messages → typed events); +the socket/fetch is INJECTED. Test the parser with crafted chunk inputs (and +trace-replay-style fixtures), never a live connection. The op-protocol core is a +pure state machine: `reduce(intent, incoming) → { viewModel, outgoingCommands }`; +the carrier (WebSocket) is the injected shell. diff --git a/.dispatch/rules/frontend-interpreter-generic.md b/.dispatch/rules/frontend-interpreter-generic.md new file mode 100644 index 0000000..557f2d4 --- /dev/null +++ b/.dispatch/rules/frontend-interpreter-generic.md @@ -0,0 +1,8 @@ +# Rule: the surface interpreter is generic + +The surface interpreter switches on field KINDS (toggle/progress/selector/stat/ +button/custom), NEVER on a surface id. An `if (surface.id === "...")` imports a +feature's identity into the platform and breaks isolation (guardrail 1). An +unknown field `kind` or a `custom` `rendererId` with no registered renderer → +GRACEFUL SKIP, never a crash. Render from the spec; the backend owns what a +surface contains. diff --git a/.dispatch/rules/frontend-no-ambient-state.md b/.dispatch/rules/frontend-no-ambient-state.md new file mode 100644 index 0000000..663bf1a --- /dev/null +++ b/.dispatch/rules/frontend-no-ambient-state.md @@ -0,0 +1,7 @@ +# Rule: no ambient state (frontend) + +State is owned per-unit and passed explicitly. NO module-global mutable store +reached from everywhere — that is the old FE's "tools leak across tabs" / +"model resets on tab switch" bug class. Svelte runes (`$state`) are a THIN +reactive wrapper over a pure reducer, never the home of logic. Subscriptions are +owned and disposed on unmount (no orphaned or duplicate subscriptions). diff --git a/.dispatch/rules/frontend-pure-core.md b/.dispatch/rules/frontend-pure-core.md new file mode 100644 index 0000000..fa7bc2e --- /dev/null +++ b/.dispatch/rules/frontend-pure-core.md @@ -0,0 +1,7 @@ +# Rule: pure core / injected shell (frontend) + +Decision logic — reducers, view-models, formatters, parsers — is pure +(input → output): NO DOM, NO `fetch`/WebSocket, NO Svelte import. Put it in a +`.ts` module that tests with zero mounting and zero mocks. Effects (socket, fetch, +IndexedDB, clock) are INJECTED at the edges (props or an adapter). This is for +testability, not purity dogma — stop where it would only add ceremony. |
