The codegen used to live in mizan-react before mizan-django before
protocol/mizan-generate. Each move left sediment in the previous
home; the bin entry in particular shadowed mizan-generate's own bin
in node_modules/.bin/, breaking `npx mizan-generate`. Caught at
integration time when the harness install picked up the stale link.
frontends/mizan-react/package.json:
- Removed bin entry pointing at the long-gone ./dist/generator/cli.mjs.
- Simplified the build script — dropped `cpSync('src/generator',
'dist/generator', ...)`. src/generator hasn't existed in this package
since the first move; the cpSync would silently fail at every build.
- Removed optionalDependencies (chokidar, minimatch, openapi-typescript) —
these were codegen-watcher deps, no longer relevant to the React adapter.
examples/{django,fastapi}-react-site/harness/package.json:
- Added `mizan-generate` as a file: devDep so `npx mizan-generate
--config <config.mjs>` resolves to the right binary in the monorepo.
Mirrors the install pattern the README documents for downstream users.
Verified: mizan-react vitest 33/33 (78 skipped — integration tests).
Codegen runs from harness via npx for both example apps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the React-codegen rework, ran the full e2e harness against the
docker-stack backend. Surfaced and fixed real friction:
mizan-base/src/index.ts (kernel):
- MizanError now parses both error envelopes — the FastAPI shape
({"error": {"code", "message", "details"}}) and the Django shape
({"error": true, "code", "message", "details"}). Exposes .code and
.details on the thrown error so consumer code can branch on them.
This was needed for the harness's `instanceof MizanError && error.code
=== 'NOT_FOUND'` pattern to work; the previous MizanError only carried
status + raw body, leaving callers to parse the body themselves.
examples/django-react-site/Dockerfile.test:
- Backend image now copies and installs cores/mizan-python before
installing mizan-django (which imports from mizan_core after the
Layer 1 extraction).
harness/src/fixtures.tsx:
- useRun helper updated for the new mutation-hook shape: pulls
{ mutate } off the hook result instead of treating the hook return
as a callable. Same for ValidationError fixture.
mizan.spec.ts:
- DjangoError → MizanError (kernel error class is backend-agnostic).
- Form tests removed (forms codegen deferred per Blazr scope).
- Channel test marked test.skip (channels deferred per Blazr scope).
.gitignore: ignore Playwright test-results/.
Final verification across all surfaces:
- mizan-core unit: 15/15
- mizan-django unit: 348 pass, 21 skip
- mizan-fastapi unit: 11/11
- mizan-ts edge-compat: 34/34 (cross-language HMAC pin)
- harness e2e (Playwright): 14/15 (1 skip = channels deferred)
Co-Authored-By: Claude Opus 4.7 (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>
The fe39fcb commit captured the file moves (git mv stages those automatically)
but didn't catch the content edits I made afterward — npm package rename
(@mizan/runtime → @mizan/base), path updates in Makefile/Dockerfile/examples,
and doc updates were all left unstaged at commit time.
This commit lands those:
- npm rename: 3 frontend package.jsons (base/vue/svelte) + mizan-base/src/index.ts + 4 codegen templates
- path updates: Makefile, Dockerfile.test, two Gitea workflows, four example/harness configs
- doc updates: CLAUDE.md, ROADMAP.md, ISSUES.md, docs/AFI_ARCHITECTURE.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The kernel is no longer a blind refetch pipe. Each context entry has:
{ data, status: idle|loading|success|error, error }
registerContext() returns { getState, subscribe, refetch, unregister }.
Adapters subscribe to state changes via callbacks. The kernel does
the fetch and notifies subscribers with the new state.
React adapter uses useSyncExternalStore for tear-free reads.
Vue adapter uses ref + subscribe callback.
Svelte adapter uses readable store backed by kernel subscription.
All three adapters also get:
- Mutation hooks with { mutate, isPending, error } (fixes H5)
- Vue: onServerPrefetch for Nuxt SSR (fixes M9)
- Svelte: readable store auto-cleans up on unsubscribe (fixes H9)
Co-Authored-By: Claude Opus 4.6 (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>
packages/
mizan-runtime/ Framework-agnostic state engine (~150 lines)
Context registry, batched invalidation, fetch primitives
mizan-django/ Django server adapter (was packages/mizan-rpc/adapters/django/)
Codegen moved to mizan-django/generate/
mizan-react/ React adapter (was packages/mizan-csr/adapters/react/)
Removed premature abstractions: mizan-ast, mizan-schema, mizan-rpc,
mizan-csr, mizan-ssr stub packages. The actual architecture is three
concrete packages, not five abstract layers.
mizan-runtime implements the v1 spec: registerContext with params,
scoped invalidation via microtask batching, server-driven invalidation
from mutation responses, mizanFetch for context bundles, mizanCall for
mutations.
264 Django + 33 React tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mizan is an Application Framework Interface (AFI) with five
independent packages:
packages/
mizan-ast/ Language layer (source → KDL schema)
mizan-schema/ IR layer (KDL schema definition)
mizan-rpc/ Protocol layer (client gen + server adapters)
adapters/django/ ← was django/
generator/ ← was react/src/generator/
mizan-csr/ State layer (client state engine)
adapters/react/ ← was react/
mizan-ssr/ Rendering layer (server-side rendering)
Each package is independent. The adapter directories contain the
framework-specific implementations. Stub packages (ast, schema, ssr)
establish the structure for future work.
264 Django tests + 33 React tests pass from new locations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>