AFI parity: close all 35 gaps — every adapter wires every AFI-common capability

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>
This commit is contained in:
2026-06-04 13:44:35 -04:00
parent 58d2cb2848
commit 6c5f6f1fba
81 changed files with 9893 additions and 463 deletions

View File

@@ -0,0 +1,90 @@
//! Cross-language pin: Rust `format_invalidate_header` must be byte-identical
//! to `cores/mizan-python/.../invalidation.py::format_invalidate_header`.
//!
//! The `X-Mizan-Invalidate` header is co-equal with the JSON body channel in
//! the spec; Edge parses it to purge. The Python reference is the oracle.
use mizan_core::{format_invalidate_header, InvalidationTarget};
use serde_json::json;
use std::path::PathBuf;
use std::process::Command;
fn afi_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../tests/afi")
.canonicalize()
.expect("tests/afi exists")
}
fn py_header(json_list: &str) -> String {
let code = format!(
"import json; from mizan_core.invalidation import format_invalidate_header; \
print(format_invalidate_header(json.loads(r'''{json_list}''')))",
);
let out = Command::new("uv")
.args(["run", "python", "-c", &code])
.current_dir(afi_dir())
.output()
.expect("invoke uv run python");
assert!(
out.status.success(),
"python reference failed: {}",
String::from_utf8_lossy(&out.stderr)
);
// Trim the trailing newline only — the header value itself may be empty.
let s = String::from_utf8(out.stdout).unwrap();
s.strip_suffix('\n').unwrap_or(&s).to_string()
}
fn scoped(ctx: &str, params: &[(&str, serde_json::Value)]) -> InvalidationTarget {
InvalidationTarget::ScopedContext {
context: ctx.to_string(),
params: params.iter().map(|(k, v)| (k.to_string(), v.clone())).collect(),
}
}
#[test]
fn matches_python_reference() {
let cases: Vec<(Vec<InvalidationTarget>, &str)> = vec![
(vec![InvalidationTarget::Context("user".into())], r#"["user"]"#),
(
vec![
InvalidationTarget::Context("user".into()),
InvalidationTarget::Context("notifications".into()),
],
r#"["user", "notifications"]"#,
),
(
vec![scoped("user", &[("user_id", json!(5))])],
r#"[{"context": "user", "params": {"user_id": 5}}]"#,
),
(
vec![scoped("search", &[("q", json!("hello world"))])],
r#"[{"context": "search", "params": {"q": "hello world"}}]"#,
),
(
// Multiple params → sorted by key, semicolon-joined.
vec![scoped("u", &[("b", json!("2")), ("a", json!("1"))])],
r#"[{"context": "u", "params": {"b": "2", "a": "1"}}]"#,
),
(
// Special chars that must percent-encode: &, =, /, space, unicode.
vec![scoped("c", &[("k", json!("a&b=c/d e—ñ"))])],
r#"[{"context": "c", "params": {"k": "a&b=c/d e—ñ"}}]"#,
),
(
// Mixed bare + scoped.
vec![
scoped("user", &[("user_id", json!(5))]),
InvalidationTarget::Context("notifications".into()),
],
r#"[{"context": "user", "params": {"user_id": 5}}, "notifications"]"#,
),
];
for (targets, json_list) in cases {
let rust = format_invalidate_header(&targets);
let expected = py_header(json_list);
assert_eq!(rust, expected, "header mismatch for {json_list}");
}
}