Mizan IR: cut over to KDL, delete OpenAPI envelope

Replaces the transitional OpenAPI 3.0 + `x-mizan-*` extensions
substrate with the canonical Mizan IR as KDL, per docs/AFI_ARCHITECTURE.md:
"KDL is the contract; everything else (REST envelopes, OpenAPI
documents, framework idioms) is sediment around it."

End-to-end cutover. No transitional path left on main.

Forward direction:
  cores/mizan-python/src/mizan_core/ir.py
    build_ir() walks mizan_core.registry, introspects Pydantic
    models directly (no JSON-Schema indirection), and emits the
    Mizan IR document. The KDL grammar is locked in this file's
    module docstring.

Backends emit KDL:
  backends/mizan-fastapi/src/mizan_fastapi/ir.py
    `python -m mizan_fastapi.ir <module>` — CLI entry point.
  backends/mizan-django/.../management/commands/export_mizan_ir.py
    `manage.py export_mizan_ir` — Django mgmt command.

Codegen consumes KDL:
  protocol/mizan-codegen/Cargo.toml: + kdl = "6"
  protocol/mizan-codegen/src/ir.rs: NamedType { Struct/List/Enum/Alias }
    + TypeShape { Primitive/Ref/List/Optional/Enum/Union } sum types,
    replacing the JsonSchema sprawl. KDL parser walks the
    `kdl::KdlDocument` tree into typed Rust structs.
  protocol/mizan-codegen/src/fetch.rs: subprocess command switches
    to the new IR-export entry points.
  All emit modules (stage1 / react / python / rust / vue / svelte /
    channels) port their type-walkers from JsonSchema to the new
    sum types — case analysis collapses substantially.

Substrate-honesty wins beyond the moat closure:
  - `int | bool` multi-arm unions land as `TypeShape::Union` (was
    silently coerced to "string" before).
  - `<CamelName>Output = list[T]` returns emit as named alias
    types instead of struct-shaped wrappers, so consumer code
    `.map()` works directly on the type.
  - Pydantic field defaults flow through to `default` properties
    in KDL, then back to non-optional shape in every target.

Deleted:
  - backends/mizan-fastapi/src/mizan_fastapi/{cli,schema}.py
  - backends/mizan-django/.../export_mizan_schema.py
  - openapi-bearing half of mizan/export/__init__.py (edge
    manifest generator preserved — separate concern).
  - tests/afi/schema_normalizer.py
  - tests/fixtures/{afi_schema.json, channels_schema.json}
  - tests/fixtures/js_* baseline directories.

Verification:
  - 20 mizan-codegen unit tests green (IR deserialization,
    byte-equivalence parity across stage1/rust/python/react/vue/svelte
    against fresh KDL-driven baselines, channels structural).
  - tests/rust/run_wire_parity.py: 12/12 probes green driving
    the binary end-to-end through KDL.
  - Blazr studio-ui typechecks against the regenerated React client.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-17 19:14:47 -04:00
parent 7fb0c4a400
commit 9900f8a36f
86 changed files with 2231 additions and 2272 deletions

View File

@@ -4,11 +4,6 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HTTPValidationError {
pub detail: Option<Vec<ValidationError>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderOutput {
pub id: i64,
@@ -16,16 +11,6 @@ pub struct OrderOutput {
pub total: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationError {
pub loc: Vec<serde_json::Value>,
pub msg: String,
#[serde(rename = "type")]
pub r#type: String,
pub input: Option<serde_json::Value>,
pub ctx: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EchoInput {
pub text: String,
@@ -75,9 +60,7 @@ pub struct UserOrdersInput {
pub user_id: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct UserOrdersOutput(pub Vec<OrderOutput>);
pub type UserOrdersOutput = Vec<OrderOutput>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserProfileInput {

View File

@@ -61,7 +61,7 @@ async fn main() -> ExitCode {
match fetch_user_context(&client, &UserContextParams { user_id: 5 }).await {
Ok(out) => println!(
"fetch_user_context(5) -> user_profile={{ user_id:{}, name:{:?} }} user_orders.len={}",
out.user_profile.user_id, out.user_profile.name, out.user_orders.0.len(),
out.user_profile.user_id, out.user_profile.name, out.user_orders.len(),
),
Err(e) => { eprintln!("fetch_user_context ERR {e}"); failures += 1; }
}