The IR called them react-message and django-message, so a FastAPI channel had
to declare a DjangoMessage. They are client-message and server-message now,
and the direction words hold wherever a channel is declared: Params /
ClientMessage / ServerMessage, with mizan-core deriving <Pascal>Params and
friends so no backend names a type itself. Django's ReactChannel and
FastAPI's ReactChannel are both Channel.
mizan-fastapi never registered a channels extension, so build_ir() emitted no
channel at all and every payload type was invisible to codegen. It registers
one now. RegistryExtension is an ABC requiring all(), which is what the IR
reads — an extension that cannot enumerate its registrations no longer exists.
The gate that should have caught the rename could not: tests/afi registered no
channel because mizan-rust had no channel registry to register one in, so a
five-package rename of the wire contract passed byte-parity without a channel
byte crossing it. mizan-rust grows ChannelSlotKind, a CHANNELS slice, a
#[mizan::channel] macro, and KDL emission whose wire_to_pascal matches Python's
split; the AFI fixture now carries a channel with every slot and one with a
single slot, so all three backends prove the contract byte for byte.
MizanChannel held three Option<String> beside three has_*() predicates and
unwrapped them with defaults; it holds an ordered slot vector, so an absent
slot is absent rather than defaulted. The channels target emitted a React
hooks file that a stage1-only consumer could not compile — react emits that
now. The codegen's parity tests byte-compared emitted source against baselines
without ever compiling it: they compile the generated crate and run its tests,
import the generated Python package and call every method, and typecheck each
TypeScript target against a consumer.
Also fixed at source: app_visitor printed its import diagnostic to stdout, the
stream export_mizan_ir writes KDL to, so a failed import silently corrupted the
IR; the apps root was hardcoded to "apps"; _default_literal crashed build_ir on
any non-JSON-serializable field default; Django and mizan-core derived Pascal
names two different ways, disagreeing on every dotted channel name.
ir.py builds a document and renders templates/ir/document.kdl.j2 rather than
appending KDL strings with hand-tracked indentation, and named types resolve to
a fixed point — a model reachable only through a union branch was referenced by
a ref that no type block ever defined.
The rest is the write-gate's own classifiers run over the standing tree:
relative imports, silent swallows, Protocol contracts that should be ABCs,
emitters hand-rendering target source, catch-all arms over closed enums, and
comments narrating the project rather than the code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The harness was written against the MIZAN.md oracle (<MizanContext>,
provider-per-context, useMizan, etc.) but the codegen had been narrowed
to just hooks-direct-on-kernel after the kernel split. Restoring the
React-idiomatic layer on top of the kernel.
backends/mizan-django/generate/generator/lib/adapters/react.mjs:
- Emits <MizanContext baseUrl="…"> root provider that calls configure()
once and (if a global context is registered) wraps children in
<GlobalContextProvider>.
- Emits <GlobalContextProvider> + <{Name}Context> per named context —
kernel registration happens once per provider mount, not per hook
call. Consumers read from React Context.
- Base hooks: useGlobalContext() / use{Name}Context() return full
ContextState<T> (data + status + error).
- Convenience hooks per context-function (use{Fn}() returns data | null)
and per regular function/mutation (use{Fn}() returns
{ mutate, isPending, error }).
- useMizan() returns { call, fetch } as an imperative escape hatch
for test harnesses or rare cases where typed hooks don't fit.
- Re-exports MizanError, configure, initSession, ContextState from
@mizan/base.
backends/mizan-django/generate/generator/cli.mjs:
- After Stage 2, appends `export * from './<adapter>'` to index.ts so
`import { useEcho, MizanContext } from './api'` works as a barrel.
Bug fixes surfaced during integration:
- react.mjs was generating `from '../index'` (wrong path); flat layout
needs `./index`.
- harness django.config.mjs had `output: 'src/api/generated.ts'` which
the codegen treated as a directory; corrected to `output: 'src/api'`.
- example testapp/clients.py imported from the deleted
mizan.setup.registry path; routed through mizan.setup aggregator.
harness/package.json: adds @mizan/base dep so the generated react.tsx
can resolve its kernel imports.
harness/src/fixtures.tsx:
- DjangoError → MizanError (kernel error class, backend-agnostic).
- useChatChannel sourced from ./api/channels.hooks directly (not
re-exported from the unified index for now).
- Form fixtures removed — forms codegen deferred per Blazr scope.
Verified: harness `vite build` succeeds, 53 modules transformed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Discovery convention per MIZAN.md is `clients.py`. The example backend's
asgi.py was still importing the older `mizan_clients` name, causing the
example Django container to fail to start.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The worker receives a file path in the JSON message, dynamically
imports it, renders it. No registerComponent API, no app entry file,
no export maps. Django's template backend resolves the template name
to an absolute path against DIRS, same as every other template engine.
render(request, 'components/Hello.tsx', {'name': 'World'})
Verified working: curl http://localhost:8000/hello/ returns
<div id="mizan-root"><div>Hello, World!</div></div>
Changes:
- worker.tsx: receives file path, dynamic import with cache
- bridge.py: sends file path instead of component name
- backend.py: resolves template name against DIRS to absolute path
- Fix bridge.py:147 bug (referenced deleted 'component' variable)
- Example app: Hello.tsx component, /hello/ view, template config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix testapp/apps.py: import djarea_clients (file was never renamed)
- Fix fetch.mjs: command is export_djarea_schema not export_mizan_schema
- Fix harness package.json: dependency path to mizan-react after restructure
- Add package.json for generator (openapi-typescript dependency)
- Regenerate all example code with new protocol format:
- generated.provider.tsx uses raw context responses + SSR hydration
- generated.server.ts uses GET /ctx/global/ with response.ok check
- generated.forms.ts, channels.ts, channels.hooks.tsx refreshed
- Remove stale generated.django.tsx and generated.django.server.ts
- Update imports: fixtures.tsx and main.tsx import from ./api (index)
- Use MizanContext instead of deprecated DjangoContext in examples
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>