The conformance board (tests/afi/test_capability_parity.py) is now fully green: 90 capability cells + 4 meta-locks + 3 codegen byte-parity = 97 passed. The gaps the prose table used to launder as "Django-only" / "out of scope" are wired, against the pinned-spec model (single-authored spec, byte-identical conformance across languages) — never per-language reimplementation. FastAPI — edge_manifest + PSR (logic single-sourced in mizan_core.manifest), WebSocket RPC (/ws/ through the shared dispatch), SSR (the framework-agnostic SSRBridge relocated to mizan_core.ssr; Django rides it from there), Shapes (SQLAlchemy projection, same declaration surface as django-readers), Forms (Pydantic schema/validate/submit). Rust (Axum + Tauri + cores/mizan-rust) — X-Mizan-Invalidate header, auth= enforcement, origin HMAC cache, edge manifest + PSR, WebSocket handler / IPC subscription channel, multipart upload, SSR bridge, Shapes, Forms; JWT/MWT mint+verify and cache-key derivation byte-pinned to the Python reference (cache_keys_pin, token_pin, invalidate_header_pin). TypeScript — a KDL IR emitter byte-identical to the Python build_ir (so a TS backend can feed the codegen — the largest gap), multipart upload, session-init, WebSocket transport, SSR bridge, JWT/MWT mint (pinned to Python), Shapes, Forms. Verified in the merged tree: core 25, fastapi 74, django 353/21-skip, mizan-rust (incl. cross-language pins) green, axum 10, tauri 8, mizan-ts 103/2-skip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
160 lines
5.1 KiB
TypeScript
160 lines
5.1 KiB
TypeScript
/**
|
|
* KDL IR byte-parity — the mizan-ts `buildIr()` against the canonical Python
|
|
* `build_ir()` (`cores/mizan-python/src/mizan_core/ir.py`).
|
|
*
|
|
* The IR is the codegen contract. A TypeScript backend can only feed
|
|
* `protocol/mizan-codegen` if it emits the same KDL the Python/Rust backends
|
|
* emit for the same registry. This test reconstructs the AFI fixture in both
|
|
* languages, subprocesses the live Python emitter, and asserts byte-equality —
|
|
* the same discipline `protocol/mizan-codegen/tests/python_parity.rs` applies.
|
|
*/
|
|
|
|
import { describe, test, expect, beforeEach } from 'bun:test'
|
|
import { execFileSync } from 'child_process'
|
|
import { existsSync } from 'fs'
|
|
import { resolve } from 'path'
|
|
import { buildIr, clearRegistry } from '../src'
|
|
import { registerFixture } from './ir-fixture'
|
|
|
|
const REPO_ROOT = resolve(import.meta.dir, '../../..')
|
|
const MIZAN_PYTHON = resolve(REPO_ROOT, 'cores/mizan-python')
|
|
|
|
/**
|
|
* Reconstruct the AFI fixture in Python via `mizan_core` only (no backend
|
|
* adapter dependency) and emit `build_ir()`. This is the cross-language oracle:
|
|
* the same registrations the TS fixture makes, run through the reference
|
|
* emitter.
|
|
*/
|
|
const PY_FIXTURE = String.raw`
|
|
import sys
|
|
from typing import Optional
|
|
from pydantic import BaseModel
|
|
from mizan_core.client.function import client
|
|
from mizan_core import registry as reg
|
|
from mizan_core.ir import build_ir
|
|
|
|
reg.clear_registry()
|
|
|
|
class EchoOutput(BaseModel):
|
|
message: str
|
|
|
|
class WhoamiOutput(BaseModel):
|
|
email: str
|
|
authenticated: bool
|
|
|
|
class ProfileOutput(BaseModel):
|
|
user_id: int
|
|
name: str
|
|
|
|
class OrderOutput(BaseModel):
|
|
id: int
|
|
user_id: int
|
|
total: int
|
|
|
|
class StatusOutput(BaseModel):
|
|
ok: bool
|
|
|
|
@client
|
|
def echo(request, text: str) -> EchoOutput: ...
|
|
|
|
@client
|
|
def whoami(request) -> WhoamiOutput: ...
|
|
|
|
@client(context="user")
|
|
def user_profile(request, user_id: int) -> ProfileOutput: ...
|
|
|
|
@client(context="user")
|
|
def user_orders(request, user_id: int) -> list[OrderOutput]: ...
|
|
|
|
@client(affects="user")
|
|
def update_profile(request, user_id: int, name: str) -> StatusOutput: ...
|
|
|
|
@client
|
|
def find_user(request, user_id: int) -> Optional[ProfileOutput]: ...
|
|
|
|
@client(merge="user")
|
|
def rename_user(request, user_id: int, name: str) -> ProfileOutput: ...
|
|
|
|
for f in [echo, whoami, user_profile, user_orders, update_profile, find_user, rename_user]:
|
|
reg.register(f, f.__name__)
|
|
|
|
sys.stdout.write(build_ir())
|
|
`
|
|
|
|
function pythonBuildIr(): string {
|
|
return execFileSync(
|
|
'uv',
|
|
['run', '--project', MIZAN_PYTHON, 'python', '-c', PY_FIXTURE],
|
|
{ encoding: 'utf-8' },
|
|
)
|
|
}
|
|
|
|
const UV_AVAILABLE = (() => {
|
|
try {
|
|
execFileSync('uv', ['--version'], { stdio: 'ignore' })
|
|
return existsSync(resolve(MIZAN_PYTHON, 'pyproject.toml'))
|
|
} catch {
|
|
return false
|
|
}
|
|
})()
|
|
|
|
describe('KDL IR — buildIr()', () => {
|
|
beforeEach(() => clearRegistry())
|
|
|
|
test('emits the canonical type / function / context sections', () => {
|
|
registerFixture()
|
|
const kdl = buildIr()
|
|
|
|
// Types are alphabetical; output structs renamed to <camel>Output.
|
|
expect(kdl).toContain('type "OrderOutput" {')
|
|
expect(kdl).toContain('type "echoInput" {')
|
|
expect(kdl).toContain('type "findUserOutput" {')
|
|
expect(kdl).toContain('type "userOrdersOutput" {')
|
|
|
|
// Functions alphabetical, with transport + context/affects/merge leaves.
|
|
expect(kdl).toContain('function "echo" {')
|
|
expect(kdl).toContain(' camel "echo"')
|
|
expect(kdl).toContain(' has-input #true')
|
|
expect(kdl).toContain(' output-nullable #true') // find_user
|
|
expect(kdl).toContain(' affects "user"') // update_profile
|
|
expect(kdl).toContain(' merge "user"') // rename_user
|
|
|
|
// Context section with shared param elevation.
|
|
expect(kdl).toContain('context "user" {')
|
|
expect(kdl).toContain(' shared-by "user_orders"')
|
|
expect(kdl).toContain(' shared-by "user_profile"')
|
|
})
|
|
|
|
test('has-input #false for a no-arg function', () => {
|
|
registerFixture()
|
|
const kdl = buildIr()
|
|
const whoami = kdl.slice(kdl.indexOf('function "whoami" {'))
|
|
expect(whoami).toContain('has-input #false')
|
|
expect(whoami).not.toContain('input "whoamiInput"')
|
|
})
|
|
|
|
test.skipIf(!UV_AVAILABLE)(
|
|
'byte-identical to the Python build_ir() (cores/mizan-python)',
|
|
() => {
|
|
registerFixture()
|
|
const tsKdl = buildIr()
|
|
const pyKdl = pythonBuildIr()
|
|
|
|
// Line-by-line first so a divergence names the offending line.
|
|
const tsLines = tsKdl.split('\n')
|
|
const pyLines = pyKdl.split('\n')
|
|
const n = Math.max(tsLines.length, pyLines.length)
|
|
for (let i = 0; i < n; i++) {
|
|
if (tsLines[i] !== pyLines[i]) {
|
|
throw new Error(
|
|
`KDL diverges at line ${i + 1}:\n` +
|
|
` python: ${JSON.stringify(pyLines[i])}\n` +
|
|
` ts: ${JSON.stringify(tsLines[i])}`,
|
|
)
|
|
}
|
|
}
|
|
expect(tsKdl).toBe(pyKdl)
|
|
},
|
|
)
|
|
})
|