Corrected agent confabulations
This commit is contained in:
19
CLAUDE.md
19
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": "<div>...</div>"}
|
||||
```
|
||||
|
||||
@@ -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 <worker_path>` on first render
|
||||
- Spawns `bun run <worker>` 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}`
|
||||
|
||||
|
||||
@@ -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<u8>`), 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
|
||||
|
||||
16
README.md
16
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<u8>`). 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`.
|
||||
|
||||
@@ -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`).
|
||||
|
||||
---
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user