From b41f469bbd7dadeedfa460286db39dec7102364d Mon Sep 17 00:00:00 2001 From: Ryth Azhur Date: Thu, 4 Jun 2026 12:05:13 -0400 Subject: [PATCH] Corrected agent confabulations --- CLAUDE.md | 19 +++++++------ ISSUES.md | 4 +-- README.md | 16 ++++++----- ROADMAP.md | 2 +- .../src/mizan/cache/KNOWN_ISSUES.md | 28 +++---------------- 5 files changed, 26 insertions(+), 43 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 228255c..4038130 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -110,13 +110,13 @@ Format: comma-separated contexts, semicolon-separated URL-encoded params per con ### 3. Frontend-Agnostic Rendering (SSR + PSR) -**SSR** — Django template backend integration. `render(request, 'ProfilePage', props)` calls a persistent Bun subprocess that runs `renderToString`. +**SSR** — Django template backend integration. `render(request, 'components/Hello.tsx', props)` — the template name is a `.tsx`/`.jsx` **file path** (resolved against `DIRS`), not a component name — calls a persistent Bun subprocess that runs `renderToString`. **PSR** (Preemptive Static Rendering) — pages re-rendered on mutation, not on request. Edge caches the result. Controlled by the manifest's `render_strategy` field. -**The Bun worker protocol** — JSON-RPC over stdin/stdout: +**The Bun worker protocol** — JSON-RPC over stdin/stdout. The worker `import()`s the file and renders it (no component registry): ``` -→ {"id": 1, "method": "render", "params": {"component": "ProfilePage", "props": {"userId": 5}}} +→ {"id": 1, "method": "render", "params": {"file": "/abs/path/Hello.tsx", "props": {"name": "World"}}} ← {"id": 1, "html": "
...
"} ``` @@ -356,8 +356,9 @@ Three independent secrets, each with its own blast radius: TEMPLATES = [ { 'BACKEND': 'mizan.ssr.MizanTemplates', + 'DIRS': [BASE_DIR / 'frontend'], 'OPTIONS': { - 'worker_path': 'frontend/ssr-worker.tsx', + 'worker': 'path/to/mizan-ssr/src/worker.tsx', 'timeout': 5, }, }, @@ -371,14 +372,14 @@ from django.shortcuts import render def profile_page(request, user_id): profile = get_user_profile(user_id) - return render(request, 'ProfilePage', {'profile': profile}) + return render(request, 'components/Profile.tsx', {'profile': profile}) ``` -`render()` calls `MizanTemplates.get_template('ProfilePage')` which returns a `MizanTemplate`. The template's `render(context)` sends JSON-RPC to the Bun worker. +`render()` calls `MizanTemplates.get_template('components/Profile.tsx')` — the name is a file path resolved to an absolute path against `DIRS` — which returns a `MizanTemplate`. The template's `render(context)` sends JSON-RPC (`{file, props}`) to the Bun worker. ### SSR Bridge (bridge.py) -- Spawns `bun run ` on first render +- Spawns `bun run ` on first render - Persistent subprocess — stays alive across requests - JSON-RPC over stdin/stdout with message ID correlation - Thread-safe: multiple Django workers can call `render()` concurrently @@ -388,8 +389,8 @@ def profile_page(request, user_id): ### Bun Worker (worker.tsx) - Reads newline-delimited JSON from stdin -- Component registry: `registerComponent('ProfilePage', ProfilePage)` -- Calls `renderToString(createElement(Component, props))` +- Resolves the component by **file path** — `import(file)` (cached) — no registry +- Calls `renderToString(createElement(Component, props))` on the imported default export - Returns `{"id": N, "html": "..."}` or `{"id": N, "error": "..."}` - Health check: `{"method": "ping"}` → `{"pong": true}` diff --git a/ISSUES.md b/ISSUES.md index da73931..60c76a5 100644 --- a/ISSUES.md +++ b/ISSUES.md @@ -11,9 +11,9 @@ no longer exist. - [ ] **Vue / Svelte frontend packages are unimplemented stubs.** `frontends/mizan-vue` and `frontends/mizan-svelte` contain only a `package.json` — no `src/`. The Rust codegen emits Vue composables and Svelte stores (`src/emit/vue.rs`, `src/emit/svelte.rs`, byte-checked by `vue_svelte_parity.rs`), but there is no runtime kernel-adapter package for either and no example app exercises them against a live backend. React is the only frontend with full integration verification. - [ ] **Svelte adapter emits Svelte 4 stores.** `src/emit/svelte.rs` generates `readable` stores from `svelte/store`. Svelte 5 `$state`/`$derived` runes are the current idiom. - [ ] **Forms have no codegen target.** `mizan-react/src/forms.ts` (form core hooks) is hand-written and consumed via the pre-kernel `MizanProvider`; the e2e harness has its form fixtures removed. A form codegen target wired to `mizanCall` is owed. -- [ ] **Upload dispatch not wired for Rust/Axum + Tauri.** The `Upload` type is first-class end to end — IR (`upload` KDL node), codegen (TS `File`), kernel (auto-multipart), and dispatch+constraint binding on Django and FastAPI. Rust/Axum and Tauri parse the IR node and emit the field, but their dispatch does not yet bind multipart file parts. +- [ ] **Upload dispatch not wired for Rust/Axum + Tauri.** The `Upload` type is first-class end to end — IR (`upload` KDL node), codegen (TS `File`; the Rust target lowers it to `Vec`), kernel (auto-multipart), and dispatch+constraint binding on Django and FastAPI. The Rust/Axum and Tauri *adapters* have no upload concept at dispatch — they don't bind multipart file parts yet. - [ ] **Pre-kernel MizanProvider still shipped.** `mizan-react/src/context.tsx` (~750 lines) is the pre-kernel provider, still imported by the desktop example. It coexists with the codegen-emitted `MizanContext` (which subscribes to `@mizan/base`). Migrating the desktop example onto the generated provider retires it. -- [ ] **Cache module open issues.** See `backends/mizan-django/src/mizan/cache/KNOWN_ISSUES.md`: purge atomicity, cross-language stringification, per-param sub-index cleanup, thundering-herd protection, `cache_get`/`cache_put` argument inconsistency, RedisCache test coverage. +- [ ] **Cache module open issues.** See `backends/mizan-django/src/mizan/cache/KNOWN_ISSUES.md`: cross-language stringification of un-normalized value types, and no thundering-herd / single-flight protection. - [ ] **Packages missing a README.** `frontends/mizan-base` (the kernel everything imports), `protocol/mizan-codegen` (the codegen binary), `frontends/mizan-vue`, `frontends/mizan-svelte`, `frontends/mizan-rust`, `backends/mizan-ts`, `backends/mizan-rust-axum`, `cores/mizan-python`. ## Resolved this pass diff --git a/README.md b/README.md index 2a08f40..a1fa87d 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ The surface every Mizan adapter implements. | Invalidation — JSON body | ✅ | ✅ | ✅ | ✅ | ✅ | | Invalidation auto-scoping (three-tier) | ✅ | ✅ | ✅ | ✅ | ✅ | | Function discovery / registration | ✅ | ✅ | ✅ | ✅ | ✅ | -| Codegen IR export (KDL) | ✅ | ✅ | ✅ ⁶ | ✅ ⁶ | — ⁸ | +| Codegen IR export (KDL) | ✅ | ✅ | ✅ ⁶ | ✅ ⁶ | ❌ ⁸ | | File uploads (multipart, `Upload` type) | ✅ | ✅ | ❌ ⁹ | ❌ ⁹ | — ¹⁰ | ### Edge, cache & enforcement @@ -107,12 +107,14 @@ target stack calls for them. rather than fetching over HTTP. 7. FastAPI and Rust/Axum expose `GET /session/` returning a null CSRF token for wire parity; CSRF is Django-only. -8. TypeScript is an edge/protocol-reference adapter (HMAC cache, manifest, PSR), not a - codegen source — it demonstrates the cache + invalidation protocol is - language-agnostic. -9. The IR carries the `upload` type and the codegen emits the field across targets, but - multipart dispatch binding is wired for Django and FastAPI only; Rust/Axum and Tauri - parse the IR node but do not yet bind uploads. +8. `mizan-ts` emits the Edge manifest (JSON) but has no KDL IR emitter, so it can't yet + feed the codegen — an unbuilt gap. A TypeScript backend still needs the generated + client (types + `callXxx`/`fetchXxx` + framework hooks); same language doesn't remove + the need for it. +9. The `mizan-codegen` crate parses the `upload` KDL node and emits the field across + targets (the Rust target lowers it to `Vec`). Multipart dispatch binding is wired + for Django and FastAPI only; the Rust/Axum and Tauri *adapters* have no upload concept + at dispatch yet. 10. The TypeScript column is the `mizan-ts` backend adapter, which has no upload dispatch. The matching client side lives in the kernel (`@mizan/base`): `mizanCall` auto-switches to `multipart/form-data` when any argument is a `File`. diff --git a/ROADMAP.md b/ROADMAP.md index 8de5c2b..dd79e79 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -34,7 +34,7 @@ - [ ] **Svelte 5 runes** — the Svelte target emits Svelte 4 `readable` stores; migrate to `$state`/`$derived`. - [ ] **Forms codegen target** — emit form clients wired to `mizanCall` from the kernel; retire the hand-written `mizan-react/src/forms.ts` and its dependence on the pre-kernel provider. - [ ] **Desktop example onto the generated provider** — migrate `examples/django-react-desktop-app` off the pre-kernel `MizanProvider` (`mizan-react/src/context.tsx`) so it can be retired. -- [ ] **Cache hardening** — purge atomicity, per-param sub-index cleanup, thundering-herd protection, RedisCache coverage (see `backends/mizan-django/src/mizan/cache/KNOWN_ISSUES.md`). +- [ ] **Cache hardening** — thundering-herd / single-flight protection, and pinning cross-language stringification of un-normalized value types (see `backends/mizan-django/src/mizan/cache/KNOWN_ISSUES.md`). - [ ] **Package READMEs** — `mizan-base`, `mizan-codegen`, and the other packages missing one (see `ISSUES.md`). --- diff --git a/backends/mizan-django/src/mizan/cache/KNOWN_ISSUES.md b/backends/mizan-django/src/mizan/cache/KNOWN_ISSUES.md index 0f0dcaa..ad0a94d 100644 --- a/backends/mizan-django/src/mizan/cache/KNOWN_ISSUES.md +++ b/backends/mizan-django/src/mizan/cache/KNOWN_ISSUES.md @@ -1,16 +1,12 @@ # Cache Module — Known Issues -Open issues against the current cache implementation. Resolved items are -removed once their fix lands. +Open issues against the current cache implementation. The cache uses +HMAC-derived keys with **no reverse indexes** (scoped purge recomputes the key; +broad purge is a prefix SCAN+UNLINK), so there are no index/sub-index races to +track. Resolved items are removed once their fix lands. ## Correctness -### Purge race condition (non-atomic index operations) -`cache_purge` reads the index and deletes as separate operations. A -concurrent `cache_put` between the two steps can orphan entries. Mitigated -by AND-intersection purge semantics, but full atomicity (Lua script or -`WATCH`/`MULTI` on the Redis backend) is still owed. - ### Cross-language stringification divergence Python `str(True)` → `"True"` vs JS `String(true)` → `"true"`. `_normalize` canonicalizes `True`/`False`/`None` today, but the rules for the remaining @@ -19,22 +15,6 @@ TypeScript HMAC keys can still diverge on an un-normalized type. ## Performance / Operability -### Broad purge leaves per-param sub-indexes -A broad `cache_purge(context)` deletes the entries but not the per-param -sub-indexes — a slow Redis memory leak. - ### No thundering-herd protection Concurrent cold misses on the same key all execute and write. No single-flight / request-coalescing. - -## API shape - -### cache_get / cache_put argument inconsistency -`cache_get`/`cache_put` take explicit args while the executor resolves some -inputs from module globals — two access patterns for one concern. - -## Coverage - -### RedisCache lacks test coverage -Only `MemoryCache` is exercised by the suite. `RedisCache` (connection -pooling, TTL, SCAN/UNLINK batching, socket timeouts) is untested.