diff --git a/.scratch/distributed-gguf-runtime/evidence/DGR-017/README.md b/.scratch/distributed-gguf-runtime/evidence/DGR-017/README.md new file mode 100644 index 0000000..ead0650 --- /dev/null +++ b/.scratch/distributed-gguf-runtime/evidence/DGR-017/README.md @@ -0,0 +1,266 @@ +# DGR-017 — Lock the GLM-5.2 Max target and alpha contract + +Status: **done**. Every acceptance criterion is met with real command output. + +Evidence class: **real upstream metadata + deterministic arithmetic**. No weight +payload was downloaded, no model was loaded, no GPU was used, and no benchmark was +run — and none is claimed. This story makes the target *reviewable before* the +216.7 GB download, which is exactly its job. + +## 1. Summary + +The alpha target is now pinned, planned, and sealed: + +- **Identity.** `zai-org/GLM-5.2` @ `b4734de4facf877f85769a911abafc5283eab3d9` and + `unsloth/GLM-5.2-GGUF` @ `abc55e72527792c6e77069c99b4cb7de16fa9f23`, quantization + `UD-IQ1_S`, six shards, 216,715,360,960 bytes, every shard's LFS SHA-256 resolved. +- **Architecture.** The config/tokenizer/chat-template metadata the runtime cannot + shard without, hashed at the pinned revision. +- **Resources.** A deterministic planner that counts unified memory once, applies the + `max(20% , 8 GiB)` reserve, and reports the arithmetic minimum and the recommended + node count as two different numbers. +- **Contract.** The roadmap's section-5 acceptance matrix as a machine-readable, + digest-sealed document, locked before the target ever runs and cross-bound to the + exact manifest and architecture-snapshot digests. +- **Upstream.** A refreshed llama.cpp/donor status report. + +Three findings are worth a reader's attention. + +**Every number in the roadmap reproduced from primary sources.** The 216,715,360,960 +byte total, the 201.832 GiB figure, the whole KV table (0.73 / 0.77 / 0.89 / 1.68 GiB +at 16K, through 46.62 / 49.41 / 56.98 / 107.25 GiB at 1M), and the whole tier table +(9 / 6 / 4 / 3 / 2 arithmetic minimum nodes) fall out of the exact config and the +exact shard bytes. The roadmap was not approximating. The planner is written as a +*reproduction* of those tables, so if the arithmetic ever stops matching, a test says +which numbers moved. + +**The roadmap's "recommended" column is an imbalance factor of exactly 1.10.** Nodes += `ceil(total x 1.10 / budget)` yields 10 / 6 / 5 / 3 / 3 for the 32 / 48 / 64 / 96 / +128 GiB tiers — precisely the roadmap's recommendations. That constant is now named +(`PLACEMENT_IMBALANCE_FACTOR`) and documented as a placeholder for measured +per-tensor placement, not a fudge factor to be tuned once results are in. + +**224 GiB aggregate does not actually fit.** Two 112 GiB nodes hit the 224 GiB +"hard-fit floor" exactly, and still come up **23.5 GiB short** once each node honours +its reserve. That is what makes 224 GiB an *experimental floor* rather than an +envelope, and it is now a test, not a caveat in prose. Relatedly, the 2×128 and 4×64 +"fit probe" topologies fit with only **2.08 GiB of headroom across the entire route** — +which is why they require measured placement evidence and are not the recommendation. + +## 2. Files changed + +New — runtime-loadable package (single source of truth): + +| Path | What | +|---|---| +| `packages/node/meshnet_node/glm_alpha/__init__.py` | Public surface | +| `packages/node/meshnet_node/glm_alpha/manifest.py` | Target manifest + architecture snapshot; fail-closed identity | +| `packages/node/meshnet_node/glm_alpha/planner.py` | Memory / KV / seam planner; unified-memory de-duplication | +| `packages/node/meshnet_node/glm_alpha/contract.py` | Immutable, digest-sealed alpha acceptance contract | +| `packages/node/meshnet_node/glm_alpha/data/target-manifest.json` | The six pinned shards, sizes, SHA-256, URLs, licenses | +| `packages/node/meshnet_node/glm_alpha/data/architecture-snapshot.json` | Pinned architecture + config/template hashes | +| `packages/node/meshnet_node/glm_alpha/data/alpha-contract.json` | Sealed acceptance thresholds (`aab23220…`) | +| `scripts/refresh_glm_target_manifest.py` | Re-resolve/verify pins from upstream metadata (`--check` / `--write`) | +| `tests/test_glm_alpha_target.py` | 97 deterministic offline tests | + +New — evidence: + +- `.scratch/distributed-gguf-runtime/evidence/DGR-017/README.md` (this file) +- `.../commands.txt` — exact commands and real results +- `.../resource-plan.json` — generated tier/route/seam/KV plan +- `.../upstream-status.json` — refreshed llama.cpp and donor status + +Modified: + +- `.scratch/distributed-gguf-runtime/issues/17-...md` — `Status: done` +- `.scratch/distributed-gguf-runtime/prd.json` — DGR-017 `passes: true` (this story only) +- `.ralph-tui/progress.md` — learnings + +The data files live **in the package**, not in evidence, because the runtime must load +them (DGR-018 verifies downloads against these digests; DGR-003 folds the manifest +digest into the recipe fingerprint). Duplicating them into evidence would create two +sources of truth that could drift. + +## 3. Acceptance criteria + +| Criterion | Where it is proven | +|---|---| +| Pin both repos by exact observed revision; `UD-IQ1_S` is the alpha quant | `target-manifest.json`; `test_manifest_pins_both_repositories_by_exact_revision` | +| Six filenames, exact bytes, LFS SHA-256, aggregate GB/GiB, license, URLs, no payload download | HF `paths-info` API (LFS pointer metadata); `test_manifest_resolves_all_six_shards…`, `test_manifest_aggregate_bytes_are_exact_and_self_consistent` | +| Snapshot + hash architecture-critical config/tokenizer/chat-template metadata | `architecture-snapshot.json`; `test_snapshot_captures_the_architecture_critical_metadata`, `test_snapshot_hashes_the_config_and_chat_template_bytes` | +| Deterministic minimum-node calc from exact bytes, Q8_0 KV @16K/c1, imbalance, reserve | `planner.plan_topology`; `test_topology_planner_reproduces_the_published_tier_table` | +| 224 GiB is a hard-fit floor, not an envelope; recommend 5×64 or 3×96/128 | `test_224_gib_aggregate_is_a_hard_fit_floor_not_an_operational_envelope`, `test_the_recommended_topologies_are_five_by_64_or_three_by_96_or_128` | +| Unified memory counted once; additive RAM+VRAM rejected | `NodeMemory.from_host`; `test_adding_integrated_gpu_memory_to_system_ram_is_rejected`, `test_unified_memory_is_counted_once` | +| 2.5 GbE minimum / 10 GbE recommended; serial latency modelled apart from bandwidth | `planner.plan_seams`; `test_2_5_gbe_is_the_alpha_minimum_and_10_gbe_is_recommended`, `test_serial_seam_latency_is_modelled_separately_from_bandwidth` | +| Identity/semantic/target-run/performance/reliability/storage criteria locked before execution | `alpha-contract.json` (sealed, `locked_before_target_execution: true`); `test_the_contract_locks_every_roadmap_acceptance_section` | +| Refresh upstream llama.cpp + donor status; no broad fork/scheduler | `upstream-status.json`; `adoption_state: none adopted` | +| Tests reject changed revisions, missing shards, coordinated digest/config substitutions, inconsistent bytes, duplicate unified memory, malformed telemetry, and post-result threshold mutation | 97 tests; see §5 | +| Targeted pytest passes | `97 passed` | +| Installed wheel includes and loads all locked JSON resources | Real wheel build/install plus `load_locked_target()` outside the source tree | +| `compileall packages tests` | exit 0 | +| `git diff --check` | exit 0 | +| Default tests deterministic, download-free, credit-free, GPU-free | Pure JSON + arithmetic; the only network code is an opt-in script excluded from the suite | +| Full deterministic `pytest -q` | **852 passed, 13 skipped** on final rerun | + +## 4. Real results + +``` +scripts/refresh_glm_target_manifest.py --check -> match upstream (exit 0) +pytest -q tests/test_glm_alpha_target.py -> 97 passed +build + install wheel; load_locked_target() -> INSTALLED_WHEEL_PASS +compileall -q packages tests -> exit 0 +git diff --check -> exit 0 +pytest -q -> 852 passed, 13 skipped (253.30s) +``` + +Controller review found and fixed four gaps before the commit was accepted: + +1. the initial wheel omitted `glm_alpha/data/*.json`, despite source-tree tests passing; +2. the self-sealed contract did not bind the manifest/snapshot digests, so coordinated + shard-hash or architecture substitutions could be accepted; +3. `--check` followed moving repository HEAD instead of validating immutable pins; +4. non-finite resource telemetry could bypass ordinary range checks. + +Each failure now has either an adversarial regression test or a real installed-wheel / +live-metadata gate. None of the provisional agent results were accepted on trust. + +The intermittent tracker cancellation race that DGR-001 and DGR-002 both recorded as +flaky on a clean tree failed once in the final validation (`404` after the request +completed), then passed **5/5** in isolation and passed in the integrated full-suite +rerun above. This story touches no tracker code; the failed run is retained in +`commands.txt` rather than hidden. + +Planner output (`resource-plan.json`): + +| Route | Fits | Headroom | +|---|---|---:| +| 5×64 GiB unified (recommended) | yes | +53.28 GiB | +| 3×96 GiB unified (recommended) | yes | +27.68 GiB | +| 3×128 GiB unified (recommended) | yes | +104.48 GiB | +| 4×64 GiB (fit probe) | yes | **+2.08 GiB** | +| 2×128 GiB (fit probe) | yes | **+2.08 GiB** | +| 2×112 GiB (= 224 GiB floor) | **no** | −23.52 GiB | +| 3×64 GiB | no | −49.12 GiB | + +## 5. How the "no silent swap" claim is earned + +The story's whole purpose is to stop a later agent from changing the target after +seeing a result. Each of those moves now has a test that names it: + +- swap the artifact → `test_a_changed_gguf_revision_is_rejected` +- pin a branch instead of a commit → `test_a_branch_name_is_not_an_acceptable_revision_pin` +- drop a shard → `test_a_missing_shard_is_rejected` +- shrink a shard so it "fits" → `test_a_shard_size_edited_to_make_the_model_look_smaller_is_rejected` +- change a valid-looking shard SHA without changing bytes → `test_coordinated_shard_hash_substitution_is_rejected_by_contract` +- change internally consistent architecture metadata → `test_internally_consistent_architecture_substitution_is_rejected_by_contract` +- take the bigger quant quietly → `test_swapping_in_a_different_quantization_is_rejected` +- add iGPU "VRAM" to system RAM → `test_adding_integrated_gpu_memory_to_system_ram_is_rejected` +- count one machine twice → `test_the_same_machine_counted_twice_in_a_route_is_rejected` +- lower the speed floor → `test_lowering_the_speed_floor_after_seeing_a_result_is_rejected` +- call a slow pass an alpha → `test_relabelling_a_speed_failure_as_a_pass_is_rejected` +- admit the dense fallback → `test_admitting_the_dense_attention_fallback_after_the_fact_is_rejected` +- shrink the reserve → `test_relaxing_the_per_node_reserve_after_the_fact_is_rejected` + +**Be precise about what the seal does.** `contract_sha256` is tamper-*evidence*, not +tamper-proofing. Nothing checked into a repo can stop an agent that rewrites the +threshold *and* re-seals the digest — `test_resealing_a_mutated_contract_changes_its_digest` +asserts that path openly. What the seal removes is the *silent* mutation: the digest +moves, and the change becomes a visible diff on a file whose entire purpose is to not +change, still claiming `contract_id: glm-5.2-max-alpha/v1`. Reviewers, not hashes, are +the enforcement; the hash makes sure there is something to review. + +## 6. Upstream status — the gating risk for DGR-004/DGR-018 + +Refreshed against live GitHub on 2026-07-13. One item **changed** since the roadmap: + +- **#24231 is now MERGED** (2026-07-11) — a generic `GGML_OP_LIGHTNING_INDEXER` exists. +- #24770 MERGED (2026-06-20) — GLM-5.2 loads via a **dense-MLA compatibility path**. +- **#25407 still OPEN** (updated today) — the real GLM DSA/IndexShare wiring. +- #24730 still OPEN — the umbrella GLM-5.2 support request. + +**No released upstream llama.cpp performs native GLM-5.2 DSA + IndexShare today.** A +stock pin taken now would load the artifact and emit text through the dense fallback — +which the alpha contract explicitly refuses (`dense_attention_fallback_satisfies_alpha: +false`). DGR-018 must therefore prove those paths are *active*, not that output appeared. + +Donor policy holds, and the evidence now supports it more strongly than before: PR +#25407 is **12 files, +414/−7**. The semantics alpha needs are small enough to track +and reproduce upstream. That is the argument against adopting Mesh-LLM's 261-patch +fork — recorded as a donor (Apache-2.0, branch head `9bd18f15`, 2026-07-12), nothing +adopted here. + +## 7. Limitations and deferred work + +- **No artifact was downloaded or loaded.** Sizes and SHA-256 come from Hugging Face + LFS pointer metadata. DGR-018 must verify the digests against the real files on + mounted storage before route admission. A matching size with a wrong hash is exactly + the failure this manifest exists to catch, and only a local verify can catch it. +- **`PLACEMENT_IMBALANCE_FACTOR = 1.10` is a planning assumption, not a measurement.** + It reproduces the roadmap's recommendations, but the real per-node share depends on + exact tensor bytes (embeddings, output head, dense vs MoE layers, shared experts, + indexer tensors, quant block alignment). DGR-019 must replace it with measured + placement. Until then, arithmetic-minimum topologies (2×128, 4×64) stay fit probes. +- **KV numbers are planning estimates, not admission truth.** The planner deliberately + budgets the *conservative* indexer layout (keys across all 78 layers, not just the 21 + Full ones) so a route admitted here cannot be surprised by the implementation it + actually gets. The runtime must still report measured allocated/resident MLA and + indexer cache per shard. +- **Peak scratch is unmodelled.** The reserve exists precisely because backend + workspaces and graph scratch are not predictable from the artifact; measured peak + must land inside the reserve, and the contract requires that evidence. +- **Upstream is moving fast.** #25407 was updated the same day it was observed. Refresh + `upstream-status.json` before DGR-004 picks a llama.cpp pin. The manifest script's + `--check` deliberately validates the immutable Hugging Face pins, not moving HEAD. + +## 8. Compatibility and migration notes + +- Purely additive. No existing module, wire format, or test changed. Nothing in this + story is on a live request path. +- `meshnet_node.glm_alpha` has **no heavy imports** — no torch, no transformers, no + network at import time — so a tracker or planner can read the target contract without + paying for a model runtime. +- Re-pinning is deliberately awkward: `--write` follows current HEAD and leaves the + existing contract binding invalid until a new contract is reviewed and sealed. + `--check` uses revision-specific APIs and exits non-zero rather than healing any + integrity drift in the already locked target. + +## 9. Handoff to dependent stories + +**DGR-003 (recipe identity):** fold `TargetManifest.digest` +(`0b6aed04479d204902bb64c0203f1a46cab26a47b378ecccf85237b63f6c1962`) and +`ArchitectureSnapshot.digest` (`253fbd94…`) into the runtime recipe fingerprint. The +GLM fields the roadmap asks you to add (DSA/IndexShare metadata, context max, expert +counts) are already resolved in `architecture-snapshot.json` — read them, do not +re-derive them by hand. Populate the DGR-002 `Fingerprint` message; do not invent a +second identity struct. + +**DGR-004 (llama.cpp pin):** read `upstream-status.json` first. Any pin taken before +#25407 merges gives you the dense-MLA fallback, which cannot satisfy alpha. Track +#25407 (12 files) as a numbered patch; do not adopt the Mesh-LLM fork. + +**DGR-018 (whole-model oracle):** the six digests in `target-manifest.json` are what you +verify the download against. Your host needs ≥224 GiB runtime-accessible memory — and +note that 224 GiB is the *floor*, not a comfortable target (see §1). Prove DSA, +IndexShare, shared expert, and the Max template are **active**; the contract's +`require_rendered_reasoning_effort_marker` is `<|system|>Reasoning Effort: Max`. Assert +the rendered marker, not the request field: the template's only non-max level is +`'high'`, so *every other value — including an absent one — renders Max*, and "the +request said max" proves nothing. + +**DGR-019 (GLM semantics):** the IndexShare split is **21 Full producer layers and 57 +Shared consumers**, in a `[full, full, full] + repeating [shared, shared, shared, full]` +pattern. Prefer shard boundaries that keep an ownership group whole; the 8 KiB +(2048 × int32) top-k sideband is the cost when you cannot. Replace +`PLACEMENT_IMBALANCE_FACTOR` with measured per-tensor placement. + +**DGR-020 (alpha verdict):** load the target with `load_locked_target()`. It verifies +the contract seal and cross-binds the manifest and architecture snapshot before +returning them. Judge against +`contract.threshold(section, key)` — an unlocked threshold raises rather than +defaulting, so a criterion cannot be invented at read time. The verdict is `alpha` or +`stop`; there is no third outcome, and a quality pass with a speed failure is `stop`. + +**Everyone:** unified system RAM and integrated-GPU memory are one pool. Build nodes +with `NodeMemory.from_host(..., unified=True)` and it is impossible to write the +double-count; pass a GPU size alongside `unified=True` and it raises rather than +silently ignoring the argument. diff --git a/.scratch/distributed-gguf-runtime/evidence/DGR-017/commands.txt b/.scratch/distributed-gguf-runtime/evidence/DGR-017/commands.txt new file mode 100644 index 0000000..c68c544 --- /dev/null +++ b/.scratch/distributed-gguf-runtime/evidence/DGR-017/commands.txt @@ -0,0 +1,100 @@ +# DGR-017 — exact commands and real results (2026-07-13) +# Project venv is used explicitly. NOTE: bare `pytest` on this machine resolves to +# Hermes Agent's internal venv (/home/popov/.hermes/...), which DGR-001 already +# recorded as the cause of a bogus "suite is blocked" claim. Always use $VP. +VP=/run/media/popov/d/DEV/repos/d-popov.com/AI/.venv/bin/python # Python 3.14.6 + +# --------------------------------------------------------------------------- +# 1. Resolve the target from upstream metadata ONLY. No weight payload downloaded. +# Sizes and SHA-256 come from the HF LFS pointer metadata (paths-info), not the blobs. +# --------------------------------------------------------------------------- +curl -sS "https://huggingface.co/api/models/zai-org/GLM-5.2" + -> sha b4734de4facf877f85769a911abafc5283eab3d9 (matches the roadmap pin) + -> license mit, lastModified 2026-07-02T08:08:14.000Z + +curl -sS "https://huggingface.co/api/models/unsloth/GLM-5.2-GGUF" + -> sha abc55e72527792c6e77069c99b4cb7de16fa9f23 (matches the roadmap pin) + -> license mit, lastModified 2026-06-23T15:18:23.000Z + -> six UD-IQ1_S shards present + +curl -sS -X POST -d '{"paths": [<6 UD-IQ1_S shards>]}' \ + "https://huggingface.co/api/models/unsloth/GLM-5.2-GGUF/paths-info/abc55e7..." + -> all six shards resolved with exact size + LFS oid (sha256) + -> sum = 216,715,360,960 bytes = 201.832 GiB = 216.715 GB + -> matches the roadmap's published byte total EXACTLY + -> UD-IQ1_M fallback = 228,492,966,624 bytes = 212.801 GiB (also matches) + +curl -sS ".../resolve/b4734de4.../{config.json,chat_template.jinja, + generation_config.json,tokenizer_config.json}" + -> config.json 3732 B sha256 185f93ee6d12548e16a847e279dc0c3c90b1524c970b0866b42fb545747d859a + -> chat_template.jinja 5076 B sha256 172dc74a35e1752df75ecfb2b2cf9326d2852bb1379868ebeec9571654489679 + -> generation_config.json 194 B sha256 ac76b43d8683d3b930126870fc8be73d8679308fe752fa1f381096d8354f6a55 + -> tokenizer_config.json 761 B sha256 98b1271574f41abf89427ae2dda030d94dc9478f0edc5a8bd240db213c6fd5fc + +# --------------------------------------------------------------------------- +# 2. Verify the checked-in pins still match live upstream (reproducible, no weights) +# --------------------------------------------------------------------------- +$VP scripts/refresh_glm_target_manifest.py --check + -> "target manifest and architecture snapshot match upstream" + -> exit 0 + +# --------------------------------------------------------------------------- +# 3. Upstream llama.cpp / donor status refresh (GitHub REST API, read-only) +# --------------------------------------------------------------------------- +curl -sS "https://api.github.com/repos/ggml-org/llama.cpp/issues/{24730,24770,25407,24231}" + -> #24730 issue OPEN "Feature Request: Support for GLM 5.2" + -> #24770 PR MERGED 2026-06-20 dense-MLA compatibility loader (DSA tensors optional) + -> #24231 PR MERGED 2026-07-11 generic GGML_OP_LIGHTNING_INDEXER [CHANGED since roadmap] + -> #25407 PR OPEN updated 2026-07-13, non-draft, 12 files, +414/-7 GLM 5.2 Indexer support +curl -sS "https://api.github.com/repos/Mesh-LLM/mesh-llm{,/branches/feat%2Fjianyang-glm-52}" + -> Apache-2.0, 2048 stars, branch head 9bd18f1509dff7fac21578635084035b3ba90a38 (2026-07-12) + -> recorded as donor only; nothing forked, nothing adopted + +# --------------------------------------------------------------------------- +# 4. Seal the alpha contract (digest over its own canonical content) +# --------------------------------------------------------------------------- +$VP -c "seal_contract(...)" -> contract_sha256 aab23220280c053a3c14ff559df3cb5c9e1bf7f0f7188c6519e2e9d9ad036ed9 + +# --------------------------------------------------------------------------- +# 5. Generate the machine-readable resource plan from the pinned artifact +# --------------------------------------------------------------------------- +PYTHONPATH=packages/node $VP + -> manifest_sha256 0b6aed04479d204902bb64c0203f1a46cab26a47b378ecccf85237b63f6c1962 + -> architecture_sha256 253fbd94b06b42acc4724ec2c7f33914e2d4cc43f54a36dff6af19a80ae6ceb1 + -> alpha_contract_sha256 aab23220280c053a3c14ff559df3cb5c9e1bf7f0f7188c6519e2e9d9ad036ed9 + -> tier arithmetic minimum 32:9 48:6 64:4 96:3 128:2 (reproduces the roadmap table) + -> tier recommended 32:10 48:6 64:5 96:3 128:3 (reproduces the roadmap table) + -> 5x64 GiB unified fits, +53.28 GiB headroom + -> 3x96 GiB unified fits, +27.68 GiB headroom + -> 2x128 / 4x64 (fit probes) fit with only +2.08 GiB headroom across the WHOLE route + -> 2x112 GiB (= 224 GiB, the hard-fit floor) DOES NOT FIT: -23.52 GiB + -> 3x64 GiB does not fit: -49.12 GiB + +# --------------------------------------------------------------------------- +# 6. Quality gates (project .venv, deterministic, offline, GPU-free) +# --------------------------------------------------------------------------- +$VP -m pytest -q tests/test_glm_alpha_target.py + -> 97 passed in 0.12s + -> includes coordinated shard/config substitution, malformed telemetry, and + contract-ID reseal rejection tests added during controller review + +$VP -m pip wheel --no-deps packages/node -w /tmp/dgr017-wheel +$VP -m pip install --no-deps --target /tmp/dgr017-install /tmp/dgr017-wheel/*.whl +$VP -I -c "... from meshnet_node.glm_alpha import load_locked_target ..." + -> INSTALLED_WHEEL_PASS + -> packaged alpha-contract.json, target-manifest.json, and architecture-snapshot.json + load and cross-bind successfully outside the source tree + +$VP -m compileall -q packages tests + -> exit 0 + +git diff --check + -> exit 0 + +$VP -m pytest -q # full deterministic suite + -> first final run: 1 failed, 851 passed, 13 skipped; only the tracker cancellation + race already documented by DGR-001/DGR-002 failed +$VP -m pytest -q tests/test_tracker_routing.py::test_tracker_dashboard_can_cancel_inflight_proxy + -> 1 passed, repeated 5/5 in isolation +$VP -m pytest -q # integrated rerun + -> 852 passed, 13 skipped in 253.30s (0:04:13) diff --git a/.scratch/distributed-gguf-runtime/evidence/DGR-017/resource-plan.json b/.scratch/distributed-gguf-runtime/evidence/DGR-017/resource-plan.json new file mode 100644 index 0000000..55d3b90 --- /dev/null +++ b/.scratch/distributed-gguf-runtime/evidence/DGR-017/resource-plan.json @@ -0,0 +1,255 @@ +{ + "generated_by": "DGR-017 meshnet_node.glm_alpha.planner", + "target": { + "gguf_repo_id": "unsloth/GLM-5.2-GGUF", + "gguf_revision": "abc55e72527792c6e77069c99b4cb7de16fa9f23", + "quantization": "UD-IQ1_S", + "total_bytes": 216715360960, + "total_gib": 201.832, + "total_gb": 216.715 + }, + "manifest_sha256": "0b6aed04479d204902bb64c0203f1a46cab26a47b378ecccf85237b63f6c1962", + "architecture_snapshot_sha256": "253fbd94b06b42acc4724ec2c7f33914e2d4cc43f54a36dff6af19a80ae6ceb1", + "alpha_contract_sha256": "aab23220280c053a3c14ff559df3cb5c9e1bf7f0f7188c6519e2e9d9ad036ed9", + "kv_assumptions": { + "dtype": "Q8_0", + "bytes_per_value": 1.0625, + "context_tokens": 16384, + "concurrency": 1, + "indexer_layout": "conservative", + "mla_values_per_token_per_layer": 576, + "backbone_layers": 78, + "indexer_full_layers": 21, + "note": "Alpha budgets indexer keys across all 78 layers (current experimental DSA layout), not only the 21 Full layers." + }, + "kv_table_gib": { + "16384": { + "mla_only_q8_gib": 0.73, + "optimized_dsa_q8_gib": 0.77, + "conservative_dsa_q8_gib": 0.89, + "conservative_dsa_f16_gib": 1.68 + }, + "131072": { + "mla_only_q8_gib": 5.83, + "optimized_dsa_q8_gib": 6.18, + "conservative_dsa_q8_gib": 7.12, + "conservative_dsa_f16_gib": 13.41 + }, + "1048576": { + "mla_only_q8_gib": 46.62, + "optimized_dsa_q8_gib": 49.41, + "conservative_dsa_q8_gib": 56.98, + "conservative_dsa_f16_gib": 107.25 + } + }, + "aggregate_hard_fit_floor_gib": 224.0, + "aggregate_floor_class": "experimental_hard_fit_floor", + "placement_imbalance_factor": 1.1, + "tier_table": { + "32": { + "physical_usable_gib": 32.0, + "reserve_gib": 8.0, + "placement_budget_gib": 24.0, + "weight_gib": 201.832, + "kv_gib": 0.89, + "total_placement_gib": 202.722, + "arithmetic_minimum_nodes": 9, + "recommended_nodes": 10, + "imbalance_factor": 1.1 + }, + "48": { + "physical_usable_gib": 48.0, + "reserve_gib": 9.6, + "placement_budget_gib": 38.4, + "weight_gib": 201.832, + "kv_gib": 0.89, + "total_placement_gib": 202.722, + "arithmetic_minimum_nodes": 6, + "recommended_nodes": 6, + "imbalance_factor": 1.1 + }, + "64": { + "physical_usable_gib": 64.0, + "reserve_gib": 12.8, + "placement_budget_gib": 51.2, + "weight_gib": 201.832, + "kv_gib": 0.89, + "total_placement_gib": 202.722, + "arithmetic_minimum_nodes": 4, + "recommended_nodes": 5, + "imbalance_factor": 1.1 + }, + "96": { + "physical_usable_gib": 96.0, + "reserve_gib": 19.2, + "placement_budget_gib": 76.8, + "weight_gib": 201.832, + "kv_gib": 0.89, + "total_placement_gib": 202.722, + "arithmetic_minimum_nodes": 3, + "recommended_nodes": 3, + "imbalance_factor": 1.1 + }, + "128": { + "physical_usable_gib": 128.0, + "reserve_gib": 25.6, + "placement_budget_gib": 102.4, + "weight_gib": 201.832, + "kv_gib": 0.89, + "total_placement_gib": 202.722, + "arithmetic_minimum_nodes": 2, + "recommended_nodes": 3, + "imbalance_factor": 1.1 + } + }, + "routes": { + "recommended_5x64_unified": { + "node_count": 5, + "aggregate_usable_gib": 320.0, + "aggregate_placement_budget_gib": 256.0, + "required_placement_gib": 202.722, + "fits": true, + "meets_hard_fit_floor": true, + "no_single_node_can_admit_target": true, + "headroom_gib": 53.278, + "reasons": [] + }, + "recommended_3x96_unified": { + "node_count": 3, + "aggregate_usable_gib": 288.0, + "aggregate_placement_budget_gib": 230.4, + "required_placement_gib": 202.722, + "fits": true, + "meets_hard_fit_floor": true, + "no_single_node_can_admit_target": true, + "headroom_gib": 27.678, + "reasons": [] + }, + "recommended_3x128_unified": { + "node_count": 3, + "aggregate_usable_gib": 384.0, + "aggregate_placement_budget_gib": 307.2, + "required_placement_gib": 202.722, + "fits": true, + "meets_hard_fit_floor": true, + "no_single_node_can_admit_target": true, + "headroom_gib": 104.478, + "reasons": [] + }, + "fit_probe_2x128_unified": { + "node_count": 2, + "aggregate_usable_gib": 256.0, + "aggregate_placement_budget_gib": 204.8, + "required_placement_gib": 202.722, + "fits": true, + "meets_hard_fit_floor": true, + "no_single_node_can_admit_target": true, + "headroom_gib": 2.078, + "reasons": [] + }, + "fit_probe_4x64_unified": { + "node_count": 4, + "aggregate_usable_gib": 256.0, + "aggregate_placement_budget_gib": 204.8, + "required_placement_gib": 202.722, + "fits": true, + "meets_hard_fit_floor": true, + "no_single_node_can_admit_target": true, + "headroom_gib": 2.078, + "reasons": [] + }, + "hard_fit_floor_2x112_unified": { + "node_count": 2, + "aggregate_usable_gib": 224.0, + "aggregate_placement_budget_gib": 179.2, + "required_placement_gib": 202.722, + "fits": false, + "meets_hard_fit_floor": true, + "no_single_node_can_admit_target": true, + "headroom_gib": -23.522, + "reasons": [ + "aggregate placement budget 179.2 GiB is below the 202.7 GiB the target needs after each node's reserve" + ] + }, + "insufficient_3x64_unified": { + "node_count": 3, + "aggregate_usable_gib": 192.0, + "aggregate_placement_budget_gib": 153.6, + "required_placement_gib": 202.722, + "fits": false, + "meets_hard_fit_floor": false, + "no_single_node_can_admit_target": true, + "headroom_gib": -49.122, + "reasons": [ + "aggregate placement budget 153.6 GiB is below the 202.7 GiB the target needs after each node's reserve", + "aggregate usable memory 192.0 GiB is below the 224 GiB experimental hard-fit floor" + ] + } + }, + "seams": { + "3_nodes_2.5gbe": { + "node_count": 3, + "seam_count": 2, + "hidden_size": 6144, + "bytes_per_token_per_seam": 12288, + "prefill_bytes_per_seam": 201326592, + "decode_bytes_per_seam_per_token": 12288, + "dsa_sideband_bytes_per_query": 8192, + "link_rate_gbps": 2.5, + "meets_alpha_minimum": true, + "is_recommended_link": false, + "decode_serialization_ms_per_token": 0.0786, + "decode_latency_ms_per_token": 1.0, + "decode_bandwidth_share_ms_per_token": 0.0786, + "prefill_serialization_ms": 1288.49 + }, + "3_nodes_10.0gbe": { + "node_count": 3, + "seam_count": 2, + "hidden_size": 6144, + "bytes_per_token_per_seam": 12288, + "prefill_bytes_per_seam": 201326592, + "decode_bytes_per_seam_per_token": 12288, + "dsa_sideband_bytes_per_query": 8192, + "link_rate_gbps": 10.0, + "meets_alpha_minimum": true, + "is_recommended_link": true, + "decode_serialization_ms_per_token": 0.0197, + "decode_latency_ms_per_token": 1.0, + "decode_bandwidth_share_ms_per_token": 0.0197, + "prefill_serialization_ms": 322.123 + }, + "5_nodes_2.5gbe": { + "node_count": 5, + "seam_count": 4, + "hidden_size": 6144, + "bytes_per_token_per_seam": 12288, + "prefill_bytes_per_seam": 201326592, + "decode_bytes_per_seam_per_token": 12288, + "dsa_sideband_bytes_per_query": 8192, + "link_rate_gbps": 2.5, + "meets_alpha_minimum": true, + "is_recommended_link": false, + "decode_serialization_ms_per_token": 0.1573, + "decode_latency_ms_per_token": 2.0, + "decode_bandwidth_share_ms_per_token": 0.1573, + "prefill_serialization_ms": 2576.98 + }, + "5_nodes_10.0gbe": { + "node_count": 5, + "seam_count": 4, + "hidden_size": 6144, + "bytes_per_token_per_seam": 12288, + "prefill_bytes_per_seam": 201326592, + "decode_bytes_per_seam_per_token": 12288, + "dsa_sideband_bytes_per_query": 8192, + "link_rate_gbps": 10.0, + "meets_alpha_minimum": true, + "is_recommended_link": true, + "decode_serialization_ms_per_token": 0.0393, + "decode_latency_ms_per_token": 2.0, + "decode_bandwidth_share_ms_per_token": 0.0393, + "prefill_serialization_ms": 644.245 + } + } +} diff --git a/.scratch/distributed-gguf-runtime/evidence/DGR-017/upstream-status.json b/.scratch/distributed-gguf-runtime/evidence/DGR-017/upstream-status.json new file mode 100644 index 0000000..d21f420 --- /dev/null +++ b/.scratch/distributed-gguf-runtime/evidence/DGR-017/upstream-status.json @@ -0,0 +1,89 @@ +{ + "observed_at": "2026-07-13", + "observed_by": "DGR-017", + "method": "GitHub REST API (api.github.com), read-only; no fork, no clone, no patch adopted", + "refresh_note": "The roadmap's 2026-07-13 observations were re-verified against live upstream. One item changed: PR #24231 is now MERGED (2026-07-11), which the roadmap already anticipated as 'generic CPU lightning-indexer support is merged'.", + "llama_cpp": { + "repo": "ggml-org/llama.cpp", + "items": [ + { + "ref": "issue #24730", + "url": "https://github.com/ggml-org/llama.cpp/issues/24730", + "title": "Feature Request: Support for GLM 5.2", + "type": "issue", + "state": "open", + "updated_at": "2026-07-03T22:02:15Z", + "meaning": "The umbrella GLM-5.2 support request is still open. GLM-5.2 is not fully supported upstream." + }, + { + "ref": "PR #24770", + "url": "https://github.com/ggml-org/llama.cpp/pull/24770", + "title": "model : glm-dsa load DSA indexer tensors as optional", + "type": "pull_request", + "state": "closed", + "merged_at": "2026-06-20T10:48:24Z", + "meaning": "MERGED. GLM-5.2 loads, but through a dense-MLA compatibility path with DSA indexer tensors treated as optional. This is the fallback the alpha contract explicitly refuses: it can produce text without performing DSA/IndexShare computation." + }, + { + "ref": "PR #24231", + "url": "https://github.com/ggml-org/llama.cpp/pull/24231", + "title": "New GGML_OP_LIGHTNING_INDEXER that implements DeepSeek V3.2/V4 lightning indexer", + "type": "pull_request", + "state": "closed", + "merged_at": "2026-07-11T09:39:07Z", + "meaning": "MERGED since the roadmap was written. A generic lightning-indexer op now exists in GGML. Backend coverage beyond CPU remains uneven and must be verified per backend by DGR-018, not assumed." + }, + { + "ref": "PR #25407", + "url": "https://github.com/ggml-org/llama.cpp/pull/25407", + "title": "GLM 5.2 Indexer support", + "type": "pull_request", + "state": "open", + "draft": false, + "mergeable_state": "unstable", + "head_sha": "8dedd06415f36f10fc6091241a39b23c1bf0ee11", + "base": "master", + "commits": 6, + "changed_files": 12, + "additions": 414, + "deletions": 7, + "updated_at": "2026-07-13T15:28:51Z", + "meaning": "OPEN and actively moving (updated today). This is the real DSA/IndexShare implementation alpha needs. It is narrow — 12 files, +414/-7 — which is the single most important finding for donor policy: the semantics alpha requires are reviewable and trackable upstream, not a 261-patch fork." + } + ] + }, + "capability_status_for_alpha": { + "gguf_load_of_UD-IQ1_S": "expected via merged #24770, unverified by this project; DGR-018 must prove it against the exact pinned artifact", + "moe_routing_and_shared_expert": "expected supported; unverified here", + "compressed_mla_kv": "supported via the dense-MLA compatibility path", + "dsa_lightning_indexer": "generic GGML op merged (#24231); GLM-5.2 wiring still open (#25407)", + "indexshare_full_shared_roles": "NOT upstream; only in open PR #25407", + "mtp_nextn": "not required for alpha; NextN tensors must be explicitly loaded or excluded, never silently reinterpreted", + "conclusion": "As of 2026-07-13 no released upstream llama.cpp performs native GLM-5.2 DSA + IndexShare. A stock pin today would satisfy 'it emits text' via the dense fallback and would FAIL the alpha semantic-correctness contract. This is the gating technical risk for DGR-004 and DGR-018." + }, + "donor": { + "repo": "Mesh-LLM/mesh-llm", + "url": "https://github.com/Mesh-LLM/mesh-llm", + "license": "Apache-2.0", + "stars_observed": 2048, + "pushed_at": "2026-07-13T06:45:51Z", + "glm_branch": "feat/jianyang-glm-52", + "glm_branch_head": "9bd18f1509dff7fac21578635084035b3ba90a38", + "glm_branch_head_date": "2026-07-12T06:37:43Z", + "policy": "TEST AND PATCH DONOR ONLY. Do not adopt the fork, its scheduler, discovery, routing, public mesh, or package manager. Meshnet remains the sole control plane (RALPH-CONTEXT runtime decision, ADR-0020).", + "focused_candidates": [ + "GLM DSA graph semantics", + "lightning indexer and sparse-attention tests", + "IndexShare metadata and Full/Shared role validation", + "top-k sideband shape and lifecycle", + "stage-local KV filtering", + "target parity and performance fixtures" + ], + "adoption_state": "none adopted in DGR-017. This story reads and records upstream state; it takes no patch and forks nothing." + }, + "recommendation_for_dgr_004_and_dgr_018": [ + "Track upstream PR #25407 rather than forking Mesh-LLM. At 12 files and +414/-7 it is small enough to review, reproduce, and carry as a numbered patch in the project's own pinned stack.", + "Any pin chosen before #25407 merges will load GLM-5.2 through the dense-MLA compatibility path. DGR-018 must therefore prove DSA/IndexShare are ACTIVE, not merely that the model emits text — the alpha contract already forbids the fallback.", + "Verify lightning-indexer backend coverage (#24231) on the specific backend the route will use. CPU support being merged says nothing about ROCm/HIP." + ] +} diff --git a/.scratch/distributed-gguf-runtime/issues/17-lock-glm-5-2-max-target-and-alpha-contract.md b/.scratch/distributed-gguf-runtime/issues/17-lock-glm-5-2-max-target-and-alpha-contract.md index b204278..04e7f37 100644 --- a/.scratch/distributed-gguf-runtime/issues/17-lock-glm-5-2-max-target-and-alpha-contract.md +++ b/.scratch/distributed-gguf-runtime/issues/17-lock-glm-5-2-max-target-and-alpha-contract.md @@ -1,6 +1,6 @@ # 17 — Lock the GLM-5.2 Max target and alpha contract -Status: ready-for-agent +Status: done ## Mandatory fresh-session context diff --git a/.scratch/distributed-gguf-runtime/prd.json b/.scratch/distributed-gguf-runtime/prd.json index 6fe1cc8..d774044 100644 --- a/.scratch/distributed-gguf-runtime/prd.json +++ b/.scratch/distributed-gguf-runtime/prd.json @@ -537,7 +537,7 @@ "Update only this story issue to `Status: done` after every acceptance criterion and quality gate passes." ], "priority": 3, - "passes": false, + "passes": true, "notes": "Source issue: .scratch/distributed-gguf-runtime/issues/17-lock-glm-5-2-max-target-and-alpha-contract.md", "dependsOn": [ "DGR-001", diff --git a/packages/node/meshnet_node/glm_alpha/__init__.py b/packages/node/meshnet_node/glm_alpha/__init__.py new file mode 100644 index 0000000..a37bf87 --- /dev/null +++ b/packages/node/meshnet_node/glm_alpha/__init__.py @@ -0,0 +1,123 @@ +"""The locked GLM-5.2 Max alpha target: identity, resource plan, and acceptance contract. + +Three files, three jobs: + +- :mod:`~meshnet_node.glm_alpha.manifest` — *is this the exact artifact?* Pinned + repository revisions, six shard digests, and the architecture-critical config + snapshot they must agree with. +- :mod:`~meshnet_node.glm_alpha.planner` — *can this route hold it?* Deterministic + memory, KV, and seam arithmetic over the exact artifact bytes, counting unified + memory once. +- :mod:`~meshnet_node.glm_alpha.contract` — *what would have counted as success?* + The acceptance thresholds, locked before the target runs and digest-bound so a + later change cannot be silent. + +Nothing here downloads, loads, or executes a model. This package is the contract +DGR-018, DGR-019, and DGR-020 are judged against. +""" + +from __future__ import annotations + +from .contract import ( + ALPHA_CONTRACT_ID, + ALPHA_CONTRACT_SCHEMA_VERSION, + VERDICT_ALPHA, + VERDICT_STOP, + AlphaContract, + AlphaContractError, + compute_contract_digest, + load_alpha_contract, + parse_alpha_contract, + require_contract_target, + seal_contract, +) +from .manifest import ( + ALPHA_QUANTIZATION, + ALPHA_SHARD_COUNT, + ArchitectureSnapshot, + GlmTargetError, + Shard, + TargetManifest, + canonical_sha256, + load_architecture_snapshot, + load_target_manifest, + parse_architecture_snapshot, + parse_target_manifest, + require_pinned_target, +) +from .planner import ( + AGGREGATE_HARD_FIT_FLOOR_GIB, + ALPHA_CONTEXT_TOKENS, + ALPHA_KV_DTYPE, + MIN_LINK_RATE_GBPS, + PLACEMENT_IMBALANCE_FACTOR, + RECOMMENDED_LINK_RATE_GBPS, + RESERVE_FLOOR_GIB, + RESERVE_FRACTION, + NodeMemory, + ResourcePlanError, + RouteFit, + SeamPlan, + TopologyPlan, + kv_bytes, + plan_all_tiers, + plan_route, + plan_seams, + plan_topology, +) + + +def load_locked_target() -> tuple[AlphaContract, TargetManifest, ArchitectureSnapshot]: + """Load and cross-bind the packaged alpha contract, manifest, and snapshot.""" + + contract = load_alpha_contract() + manifest = load_target_manifest() + snapshot = load_architecture_snapshot() + require_contract_target(contract, manifest, snapshot) + return contract, manifest, snapshot + + +__all__ = [ + "AGGREGATE_HARD_FIT_FLOOR_GIB", + "ALPHA_CONTEXT_TOKENS", + "ALPHA_CONTRACT_ID", + "ALPHA_CONTRACT_SCHEMA_VERSION", + "ALPHA_KV_DTYPE", + "ALPHA_QUANTIZATION", + "ALPHA_SHARD_COUNT", + "MIN_LINK_RATE_GBPS", + "PLACEMENT_IMBALANCE_FACTOR", + "RECOMMENDED_LINK_RATE_GBPS", + "RESERVE_FLOOR_GIB", + "RESERVE_FRACTION", + "VERDICT_ALPHA", + "VERDICT_STOP", + "AlphaContract", + "AlphaContractError", + "ArchitectureSnapshot", + "GlmTargetError", + "NodeMemory", + "ResourcePlanError", + "RouteFit", + "SeamPlan", + "Shard", + "TargetManifest", + "TopologyPlan", + "canonical_sha256", + "compute_contract_digest", + "kv_bytes", + "load_alpha_contract", + "load_architecture_snapshot", + "load_locked_target", + "load_target_manifest", + "parse_alpha_contract", + "parse_architecture_snapshot", + "parse_target_manifest", + "plan_all_tiers", + "plan_route", + "plan_seams", + "plan_topology", + "require_contract_target", + "require_pinned_target", + "seal_contract", +] diff --git a/packages/node/meshnet_node/glm_alpha/contract.py b/packages/node/meshnet_node/glm_alpha/contract.py new file mode 100644 index 0000000..e9409ee --- /dev/null +++ b/packages/node/meshnet_node/glm_alpha/contract.py @@ -0,0 +1,332 @@ +"""The immutable GLM-5.2 Max alpha acceptance contract. + +The contract exists to answer one question that cannot be answered honestly after +the fact: *what would have counted as success?* + +Its thresholds are locked before the target ever runs (DGR-017), and DGR-020 reads +them back to publish an ``alpha`` or ``stop`` verdict. The whole point is that the +gap between those two moments is where a threshold quietly becomes "0.1 tokens/sec +was always the goal". So the document carries ``contract_sha256`` over its own +canonical content, and :func:`load_alpha_contract` recomputes it on every load. An +agent that edits a threshold and forgets the digest is rejected; an agent that +edits both has left a diff on a file whose whole purpose is to not change. + +This is a tamper-*evidence* mechanism, not tamper-proofing. It cannot stop a +determined rewrite of the file and the digest together — nothing in-repo can. What +it does is remove the possibility of a silent one, which is the failure mode that +actually happens: a number nudged mid-run, with no reviewer ever seeing that it +moved. +""" + +from __future__ import annotations + +import json +import re +from dataclasses import dataclass +from importlib.resources import files +from pathlib import Path +from typing import Any, Mapping + +from .manifest import ( + ALPHA_QUANTIZATION, + ALPHA_SHARD_COUNT, + ArchitectureSnapshot, + GlmTargetError, + TargetManifest, + canonical_sha256, +) + +ALPHA_CONTRACT_SCHEMA_VERSION = 1 +ALPHA_CONTRACT_VERSION = 1 +ALPHA_CONTRACT_ID = "glm-5.2-max-alpha/v1" + +_CONTRACT_RESOURCE = "alpha-contract.json" + +DIGEST_FIELD = "contract_sha256" + +VERDICT_ALPHA = "alpha" +VERDICT_STOP = "stop" + +# Every section the roadmap's acceptance matrix (section 5) locks. A contract that +# omits one is not a weaker contract, it is an unreviewable one. +REQUIRED_SECTIONS: tuple[str, ...] = ( + "identity_and_fit", + "semantic_correctness", + "target_run", + "performance", + "reliability", + "storage", +) + + +class AlphaContractError(GlmTargetError): + """Raised when the alpha contract is missing, malformed, or has been mutated.""" + + +def contract_signing_payload(document: Mapping[str, Any]) -> dict: + """The contract content the digest covers: everything except the digest itself.""" + unsigned = dict(document) + unsigned.pop(DIGEST_FIELD, None) + return unsigned + + +def compute_contract_digest(document: Mapping[str, Any]) -> str: + """SHA-256 over the canonical contract content.""" + return canonical_sha256(contract_signing_payload(document)) + + +@dataclass(frozen=True) +class AlphaContract: + """A locked, digest-bound alpha acceptance contract.""" + + schema_version: int + contract_version: int + contract_id: str + locked_at: str + locked_by: str + target: Mapping[str, Any] + sections: Mapping[str, Mapping[str, Any]] + verdicts: tuple[str, ...] + amendment_policy: str + digest: str + raw: Mapping[str, Any] + source: str = "" + + def section(self, name: str) -> Mapping[str, Any]: + if name not in self.sections: + raise AlphaContractError(f"contract section {name!r} is missing from {self.source}") + return self.sections[name] + + def threshold(self, section: str, key: str) -> Any: + block = self.section(section) + if key not in block: + raise AlphaContractError( + f"threshold {section}.{key} is not locked in {self.source}; an unlocked " + "threshold cannot be used to judge a result" + ) + return block[key] + + def to_dict(self) -> dict: + return dict(self.raw) + + +def parse_alpha_contract(data: Any, source: str = "") -> AlphaContract: + """Validate a contract document and verify it has not been mutated since locking.""" + if not isinstance(data, Mapping): + raise AlphaContractError(f"contract root in {source} must be a JSON object") + + schema_version = data.get("schema_version") + if ( + not isinstance(schema_version, int) + or isinstance(schema_version, bool) + or schema_version != ALPHA_CONTRACT_SCHEMA_VERSION + ): + raise AlphaContractError( + f"{source} declares alpha-contract schema version {schema_version!r}, but this " + f"node reads version {ALPHA_CONTRACT_SCHEMA_VERSION}" + ) + + contract_version = data.get("contract_version") + if ( + not isinstance(contract_version, int) + or isinstance(contract_version, bool) + or contract_version != ALPHA_CONTRACT_VERSION + ): + raise AlphaContractError( + f"{source} declares contract version {contract_version!r}, but this node " + f"reads version {ALPHA_CONTRACT_VERSION}" + ) + + contract_id = data.get("contract_id") + if contract_id != ALPHA_CONTRACT_ID: + raise AlphaContractError( + f"{source} declares contract_id {contract_id!r}, but this node is locked " + f"to {ALPHA_CONTRACT_ID!r}" + ) + + for field in ("locked_at", "locked_by"): + value = data.get(field) + if not isinstance(value, str) or not value.strip(): + raise AlphaContractError(f"{source} must carry a non-empty {field}") + + declared = data.get(DIGEST_FIELD) + if not isinstance(declared, str) or not declared: + raise AlphaContractError( + f"{source} carries no {DIGEST_FIELD}; an unsealed contract cannot prove it " + "predates the results it judges" + ) + + computed = compute_contract_digest(data) + if computed != declared: + raise AlphaContractError( + f"{source} has been modified since it was locked: its content hashes to " + f"{computed}, but it declares {declared}. Alpha thresholds are locked before " + "target execution and may not be weakened afterwards. To change them, open a " + "new contract_id under human review; do not edit this one." + ) + + if not data.get("locked_before_target_execution"): + raise AlphaContractError( + f"{source} does not assert locked_before_target_execution; a contract written " + "after the results are known is not a contract" + ) + + missing = [name for name in REQUIRED_SECTIONS if not isinstance(data.get(name), Mapping)] + if missing: + raise AlphaContractError( + f"{source} is missing locked acceptance section(s) {missing}" + ) + + verdicts = data.get("verdicts") + if not isinstance(verdicts, list) or sorted(verdicts) != sorted([VERDICT_ALPHA, VERDICT_STOP]): + raise AlphaContractError( + f"{source} must offer exactly the verdicts {[VERDICT_ALPHA, VERDICT_STOP]}; a " + "third outcome is how 'it loaded' becomes a pass" + ) + + target = data.get("target") + if not isinstance(target, Mapping): + raise AlphaContractError(f"{source} is missing its 'target' block") + for field in ( + "source_repo_id", + "source_revision", + "gguf_repo_id", + "gguf_revision", + "quantization", + "target_manifest_sha256", + "architecture_snapshot_sha256", + "reasoning_effort", + ): + if not target.get(field): + raise AlphaContractError(f"{source} target block is missing {field!r}") + for field in ("source_revision", "gguf_revision"): + if not re.fullmatch(r"[0-9a-f]{40}", str(target[field])): + raise AlphaContractError(f"{source} target.{field} is not a full commit revision") + for field in ("target_manifest_sha256", "architecture_snapshot_sha256"): + if not re.fullmatch(r"[0-9a-f]{64}", str(target[field])): + raise AlphaContractError(f"{source} target.{field} is not a SHA-256 digest") + if target["quantization"] != ALPHA_QUANTIZATION: + raise AlphaContractError( + f"{source} targets {target['quantization']!r}, not locked alpha quantization " + f"{ALPHA_QUANTIZATION!r}" + ) + if target["reasoning_effort"] != "max": + raise AlphaContractError(f"{source} must lock reasoning_effort='max'") + shard_count = target.get("shard_count") + if ( + not isinstance(shard_count, int) + or isinstance(shard_count, bool) + or shard_count != ALPHA_SHARD_COUNT + ): + raise AlphaContractError( + f"{source} target.shard_count must be exactly {ALPHA_SHARD_COUNT}" + ) + total_bytes = target.get("total_bytes") + if not isinstance(total_bytes, int) or isinstance(total_bytes, bool) or total_bytes <= 0: + raise AlphaContractError(f"{source} target.total_bytes must be a positive integer") + + amendment_policy = data.get("amendment_policy") + if not isinstance(amendment_policy, str) or not amendment_policy.strip(): + raise AlphaContractError(f"{source} must state its amendment policy") + + return AlphaContract( + schema_version=schema_version, + contract_version=contract_version, + contract_id=contract_id, + locked_at=str(data["locked_at"]), + locked_by=str(data["locked_by"]), + target=target, + sections={name: data[name] for name in REQUIRED_SECTIONS}, + verdicts=tuple(verdicts), + amendment_policy=amendment_policy, + digest=declared, + raw=data, + source=source, + ) + + +def require_contract_target( + contract: AlphaContract, + manifest: TargetManifest, + snapshot: ArchitectureSnapshot, +) -> None: + """Bind the sealed contract to the exact manifest and architecture snapshot. + + Repository revisions alone do not bind shard LFS objects or derived architecture + semantics. Call this before planning, admission, download, or execution. + """ + + expected = contract.target + actual = { + "source_repo_id": manifest.source_repo_id, + "source_revision": manifest.source_revision, + "gguf_repo_id": manifest.gguf_repo_id, + "gguf_revision": manifest.gguf_revision, + "quantization": manifest.quantization, + "shard_count": len(manifest.shards), + "total_bytes": manifest.total_bytes, + "target_manifest_sha256": manifest.digest, + "architecture_snapshot_sha256": snapshot.digest, + } + if snapshot.source_repo_id != manifest.source_repo_id: + raise AlphaContractError( + "architecture snapshot repository does not match the target manifest repository" + ) + if snapshot.source_revision != manifest.source_revision: + raise AlphaContractError( + "architecture snapshot revision does not match the target manifest revision" + ) + mismatches = { + key: (expected.get(key), value) + for key, value in actual.items() + if expected.get(key) != value + } + if mismatches: + details = ", ".join( + f"{key}: locked={locked!r}, actual={value!r}" + for key, (locked, value) in sorted(mismatches.items()) + ) + raise AlphaContractError(f"target documents do not match the sealed contract: {details}") + + +def load_alpha_contract(path: Path | None = None) -> AlphaContract: + """Load the packaged alpha contract, or one at ``path``.""" + if path is not None: + source = str(path) + try: + raw = path.read_text(encoding="utf-8") + except OSError as exc: + raise AlphaContractError(f"cannot read {source}: {exc.strerror or exc}") from exc + else: + source = f"packaged {_CONTRACT_RESOURCE}" + try: + raw = ( + files("meshnet_node.glm_alpha") + .joinpath("data", _CONTRACT_RESOURCE) + .read_text(encoding="utf-8") + ) + except (OSError, FileNotFoundError, ModuleNotFoundError) as exc: + raise AlphaContractError( + f"{source} is missing from this node installation ({type(exc).__name__})" + ) from exc + + try: + data = json.loads(raw) + except json.JSONDecodeError as exc: + raise AlphaContractError( + f"{source} is not valid JSON: {exc.msg} at line {exc.lineno} column {exc.colno}" + ) from exc + + return parse_alpha_contract(data, source=source) + + +def seal_contract(document: Mapping[str, Any]) -> dict: + """Return the document with a freshly computed digest. + + This is the only supported way to produce a contract file. It is deliberately + *not* called at load time: sealing on load would turn every mutation into a + valid contract, which is precisely the property the digest exists to deny. + """ + sealed = dict(document) + sealed[DIGEST_FIELD] = compute_contract_digest(document) + return sealed diff --git a/packages/node/meshnet_node/glm_alpha/data/alpha-contract.json b/packages/node/meshnet_node/glm_alpha/data/alpha-contract.json new file mode 100644 index 0000000..145cbd9 --- /dev/null +++ b/packages/node/meshnet_node/glm_alpha/data/alpha-contract.json @@ -0,0 +1,122 @@ +{ + "schema_version": 1, + "contract_version": 1, + "contract_id": "glm-5.2-max-alpha/v1", + "locked_at": "2026-07-13", + "locked_by": "DGR-017", + "locked_before_target_execution": true, + "target": { + "source_repo_id": "zai-org/GLM-5.2", + "source_revision": "b4734de4facf877f85769a911abafc5283eab3d9", + "gguf_repo_id": "unsloth/GLM-5.2-GGUF", + "gguf_revision": "abc55e72527792c6e77069c99b4cb7de16fa9f23", + "quantization": "UD-IQ1_S", + "shard_count": 6, + "total_bytes": 216715360960, + "target_manifest_sha256": "0b6aed04479d204902bb64c0203f1a46cab26a47b378ecccf85237b63f6c1962", + "architecture_snapshot_sha256": "253fbd94b06b42acc4724ec2c7f33914e2d4cc43f54a36dff6af19a80ae6ceb1", + "reasoning_effort": "max" + }, + "identity_and_fit": { + "require_exact_revisions": true, + "require_all_shard_sha256": true, + "require_per_node_owned_tensor_report": true, + "require_owned_tensor_union_equals_inventory": true, + "max_unintended_tensor_overlap": 0, + "require_no_single_node_can_admit_complete_recipe": true, + "min_node_reserve_fraction": 0.2, + "min_node_reserve_gib": 8.0, + "require_measured_peak_scratch_inside_reserve": true, + "forbid_swap": true, + "forbid_overcommit": true, + "forbid_mmap_only_fit_claim": true, + "forbid_double_counted_unified_memory": true, + "aggregate_hard_fit_floor_gib": 224.0, + "aggregate_floor_class": "experimental_hard_fit_floor", + "recommended_topologies": [ + "5x64GiB", + "3x96GiB", + "3x128GiB" + ], + "arithmetic_minimum_requires_measured_placement_evidence": true + }, + "semantic_correctness": { + "require_active_moe_routing": true, + "require_active_shared_expert": true, + "require_active_dsa_lightning_indexer": true, + "require_active_sparse_attention": true, + "require_active_indexshare_full_and_shared": true, + "dense_attention_fallback_satisfies_alpha": false, + "require_rendered_reasoning_effort_marker": "<|system|>Reasoning Effort: Max", + "f32_seam_fixture_exact_match_tokens": 32, + "f32_seam_fixture_requires_exact_match": true, + "min_greedy_token_agreement": 0.9, + "min_mean_state_cosine_similarity": 0.999, + "forbid_nonfinite_tensors": true, + "require_fail_closed_on_fingerprint_mismatch": true + }, + "target_run": { + "context_tokens": 16384, + "kv_dtype": "Q8_0", + "concurrency": 1, + "prompt_lane_tokens": 4096, + "min_output_tokens": 512, + "min_output_tokens_with_natural_eos": 128, + "require_same_switch_wired_network": true, + "min_link_rate_gbps": 2.5, + "recommended_link_rate_gbps": 10.0, + "require_sentinels": [ + "coding", + "structured_tool_call_json", + "multi_step_reasoning" + ], + "require_openai_compatible_response_fields": [ + "model", + "finish_reason", + "usage" + ] + }, + "performance": { + "min_median_decode_tokens_per_second": 0.5, + "max_ttft_seconds_at_4096_prompt": 600, + "max_unexplained_stall_seconds": 60, + "warmups": 1, + "require_per_stage_telemetry": [ + "compute", + "queue", + "kv", + "seam_bytes", + "seam_latency", + "rss", + "vram", + "backend_timing" + ], + "quality_pass_with_speed_fail_verdict": "stop", + "forbid_generalising_results_to_other_hardware": true + }, + "reliability": { + "consecutive_clean_cold_starts": 2, + "require_cancellation_releases_buffers_and_kv": true, + "require_worker_loss_aborts_route": true, + "retry_policy": "from_token_zero_on_new_compatible_route", + "forbid_silent_kv_migration": true, + "require_reject_stale_epoch": true, + "require_reject_duplicate_step_id": true, + "synthetic_workers_satisfy_alpha": false, + "layer_reduced_fixtures_satisfy_alpha": false + }, + "storage": { + "mounted_storage_only": true, + "forbidden_path_prefixes": [ + "/home" + ], + "forbid_secrets_in_logs": true, + "forbid_unrestricted_prompt_payloads_in_logs": true + }, + "verdicts": [ + "alpha", + "stop" + ], + "amendment_policy": "Thresholds are locked before target execution. They may not be weakened, moved, or reinterpreted after results are known. A change requires a new contract_id and contract_version under human review, and the superseded contract is retained.", + "contract_sha256": "aab23220280c053a3c14ff559df3cb5c9e1bf7f0f7188c6519e2e9d9ad036ed9" +} diff --git a/packages/node/meshnet_node/glm_alpha/data/architecture-snapshot.json b/packages/node/meshnet_node/glm_alpha/data/architecture-snapshot.json new file mode 100644 index 0000000..b77771d --- /dev/null +++ b/packages/node/meshnet_node/glm_alpha/data/architecture-snapshot.json @@ -0,0 +1,91 @@ +{ + "schema_version": 1, + "observed_at": "2026-07-13", + "source_repo_id": "zai-org/GLM-5.2", + "source_revision": "b4734de4facf877f85769a911abafc5283eab3d9", + "source_files": [ + { + "path": "config.json", + "size_bytes": 3732, + "sha256": "185f93ee6d12548e16a847e279dc0c3c90b1524c970b0866b42fb545747d859a", + "url": "https://huggingface.co/zai-org/GLM-5.2/resolve/b4734de4facf877f85769a911abafc5283eab3d9/config.json" + }, + { + "path": "chat_template.jinja", + "size_bytes": 5076, + "sha256": "172dc74a35e1752df75ecfb2b2cf9326d2852bb1379868ebeec9571654489679", + "url": "https://huggingface.co/zai-org/GLM-5.2/resolve/b4734de4facf877f85769a911abafc5283eab3d9/chat_template.jinja" + }, + { + "path": "generation_config.json", + "size_bytes": 194, + "sha256": "ac76b43d8683d3b930126870fc8be73d8679308fe752fa1f381096d8354f6a55", + "url": "https://huggingface.co/zai-org/GLM-5.2/resolve/b4734de4facf877f85769a911abafc5283eab3d9/generation_config.json" + }, + { + "path": "tokenizer_config.json", + "size_bytes": 761, + "sha256": "98b1271574f41abf89427ae2dda030d94dc9478f0edc5a8bd240db213c6fd5fc", + "url": "https://huggingface.co/zai-org/GLM-5.2/resolve/b4734de4facf877f85769a911abafc5283eab3d9/tokenizer_config.json" + } + ], + "architecture": { + "architectures": [ + "GlmMoeDsaForCausalLM" + ], + "model_type": "glm_moe_dsa", + "num_hidden_layers": 78, + "num_nextn_predict_layers": 1, + "total_artifact_layers": 79, + "first_k_dense_replace": 3, + "dense_layers": 3, + "sparse_moe_layers": 75, + "hidden_size": 6144, + "intermediate_size": 12288, + "moe_intermediate_size": 2048, + "n_routed_experts": 256, + "num_experts_per_tok": 8, + "n_shared_experts": 1, + "scoring_func": "sigmoid", + "topk_method": "noaux_tc", + "norm_topk_prob": true, + "routed_scaling_factor": 2.5, + "num_attention_heads": 64, + "head_dim": 192, + "qk_nope_head_dim": 192, + "qk_rope_head_dim": 64, + "qk_head_dim": 256, + "v_head_dim": 256, + "kv_lora_rank": 512, + "q_lora_rank": 2048, + "mla_cached_values_per_token_per_layer": 576, + "index_topk": 2048, + "index_head_dim": 128, + "index_n_heads": 32, + "index_topk_freq": 4, + "index_skip_topk_offset": 3, + "index_share_for_mtp_iteration": true, + "indexer_full_layers": 21, + "indexer_shared_layers": 57, + "indexer_types_sha256": "ec3b4927af83cf02baf37fb10454c40176ec8bf501ae89334b27a9df5fa17025", + "max_position_embeddings": 1048576, + "vocab_size": 154880, + "rope_theta": 8000000, + "dtype": "bfloat16", + "tie_word_embeddings": false + }, + "reasoning_effort": { + "alpha_mode": "max", + "rendered_marker": "<|system|>Reasoning Effort: Max", + "template_rule": "effective_reasoning_effort = 'high' if reasoning_effort == 'high' else 'max'", + "default_is_max": true, + "suppressed_when": "enable_thinking is defined and false", + "note": "The template recognises exactly one non-max level ('high'); every other value, including an absent reasoning_effort, renders Max. Alpha therefore asserts the rendered 'Reasoning Effort: Max' marker, not merely the presence of a request field." + }, + "notes": [ + "indexer_types has 78 entries: layers 0-2 are 'full', then a repeating [shared, shared, shared, full] pattern, giving 21 Full producer layers and 57 Shared consumer layers.", + "MLA caches kv_lora_rank (512) + qk_rope_head_dim (64) = 576 values per token per backbone layer.", + "num_nextn_predict_layers=1 is the NextN/MTP layer present in the artifact. Alpha does not run MTP; the tensors are loaded or explicitly excluded by a certified recipe and must never be silently reinterpreted as a 79th backbone layer.", + "Values are derived from the pinned config.json. The runtime must re-derive them from the artifact and fail closed on contradictory metadata; marketing names are not compatibility identity." + ] +} diff --git a/packages/node/meshnet_node/glm_alpha/data/target-manifest.json b/packages/node/meshnet_node/glm_alpha/data/target-manifest.json new file mode 100644 index 0000000..37ce3ea --- /dev/null +++ b/packages/node/meshnet_node/glm_alpha/data/target-manifest.json @@ -0,0 +1,90 @@ +{ + "schema_version": 1, + "manifest_version": 1, + "observed_at": "2026-07-13", + "observed_by": "DGR-017", + "alpha_quantization": "UD-IQ1_S", + "source_model": { + "repo_id": "zai-org/GLM-5.2", + "revision": "b4734de4facf877f85769a911abafc5283eab3d9", + "last_modified": "2026-07-02T08:08:14.000Z", + "weight_license": "mit", + "code_documentation_license": "apache-2.0", + "url": "https://huggingface.co/zai-org/GLM-5.2", + "revision_url": "https://huggingface.co/zai-org/GLM-5.2/tree/b4734de4facf877f85769a911abafc5283eab3d9", + "api_url": "https://huggingface.co/api/models/zai-org/GLM-5.2" + }, + "gguf_artifact": { + "repo_id": "unsloth/GLM-5.2-GGUF", + "revision": "abc55e72527792c6e77069c99b4cb7de16fa9f23", + "last_modified": "2026-06-23T15:18:23.000Z", + "license": "mit", + "url": "https://huggingface.co/unsloth/GLM-5.2-GGUF", + "revision_url": "https://huggingface.co/unsloth/GLM-5.2-GGUF/tree/abc55e72527792c6e77069c99b4cb7de16fa9f23", + "api_url": "https://huggingface.co/api/models/unsloth/GLM-5.2-GGUF", + "quantization": "UD-IQ1_S", + "shard_count": 6, + "total_bytes": 216715360960, + "total_gib": 201.832, + "total_gb": 216.715, + "shards": [ + { + "index": 1, + "path": "UD-IQ1_S/GLM-5.2-UD-IQ1_S-00001-of-00006.gguf", + "size_bytes": 9423744, + "sha256": "46b6148389219ae45167cb8124fbb18ef7d432daf619b4faf9e06ea80d3f4777", + "url": "https://huggingface.co/unsloth/GLM-5.2-GGUF/resolve/abc55e72527792c6e77069c99b4cb7de16fa9f23/UD-IQ1_S/GLM-5.2-UD-IQ1_S-00001-of-00006.gguf" + }, + { + "index": 2, + "path": "UD-IQ1_S/GLM-5.2-UD-IQ1_S-00002-of-00006.gguf", + "size_bytes": 49208128256, + "sha256": "f2180207285e04fcaa5b8c53ba6e77ad5cc58666b6e7c6b04a5eded3fe8bef09", + "url": "https://huggingface.co/unsloth/GLM-5.2-GGUF/resolve/abc55e72527792c6e77069c99b4cb7de16fa9f23/UD-IQ1_S/GLM-5.2-UD-IQ1_S-00002-of-00006.gguf" + }, + { + "index": 3, + "path": "UD-IQ1_S/GLM-5.2-UD-IQ1_S-00003-of-00006.gguf", + "size_bytes": 49684417024, + "sha256": "b1c0c5a302cc8d5d9ea0bcd4467c01db72c26839f820f7e882079582ea0a8d2b", + "url": "https://huggingface.co/unsloth/GLM-5.2-GGUF/resolve/abc55e72527792c6e77069c99b4cb7de16fa9f23/UD-IQ1_S/GLM-5.2-UD-IQ1_S-00003-of-00006.gguf" + }, + { + "index": 4, + "path": "UD-IQ1_S/GLM-5.2-UD-IQ1_S-00004-of-00006.gguf", + "size_bytes": 49396052864, + "sha256": "a6a42da6975e29f89866dcde2956e9e50e6ea26635fb5063b74f3973f4f863b6", + "url": "https://huggingface.co/unsloth/GLM-5.2-GGUF/resolve/abc55e72527792c6e77069c99b4cb7de16fa9f23/UD-IQ1_S/GLM-5.2-UD-IQ1_S-00004-of-00006.gguf" + }, + { + "index": 5, + "path": "UD-IQ1_S/GLM-5.2-UD-IQ1_S-00005-of-00006.gguf", + "size_bytes": 49246275936, + "sha256": "a4a9851a50db533f21ef824e5d8038f04e6782e7d602d18e5fdd6643f68ccccb", + "url": "https://huggingface.co/unsloth/GLM-5.2-GGUF/resolve/abc55e72527792c6e77069c99b4cb7de16fa9f23/UD-IQ1_S/GLM-5.2-UD-IQ1_S-00005-of-00006.gguf" + }, + { + "index": 6, + "path": "UD-IQ1_S/GLM-5.2-UD-IQ1_S-00006-of-00006.gguf", + "size_bytes": 19171063136, + "sha256": "3b767f55df64e0432d52fcf1a14eb47a1ef3bbc91339e2ae220f38602237d7d7", + "url": "https://huggingface.co/unsloth/GLM-5.2-GGUF/resolve/abc55e72527792c6e77069c99b4cb7de16fa9f23/UD-IQ1_S/GLM-5.2-UD-IQ1_S-00006-of-00006.gguf" + } + ] + }, + "diagnostic_fallback": { + "quantization": "UD-IQ1_M", + "shard_count": 6, + "total_bytes": 228492966624, + "total_gib": 212.801, + "total_gb": 228.493, + "policy": "First diagnostic fallback only if UD-IQ1_S exposes a runtime or quality defect. It does not satisfy the alpha 'lowest published quantization' target unless human review changes the target contract." + }, + "storage": { + "mounted_storage_only": true, + "forbidden_path_prefixes": [ + "/home" + ], + "note": "Model artifacts resolve through the machine-specific .env. mounted-drive configuration. A path under /home fails admission closed." + } +} diff --git a/packages/node/meshnet_node/glm_alpha/manifest.py b/packages/node/meshnet_node/glm_alpha/manifest.py new file mode 100644 index 0000000..cd1d16d --- /dev/null +++ b/packages/node/meshnet_node/glm_alpha/manifest.py @@ -0,0 +1,490 @@ +"""The pinned GLM-5.2 Max target manifest and architecture snapshot. + +This module is the *identity* half of the alpha target contract. It answers one +question: is the artifact on this disk the exact artifact alpha was locked +against? + +Identity is pinned by repository revision **and** by every shard's LFS SHA-256. +A revision alone is not enough — a repository can be force-pushed, and a tag can +be moved — and a size alone is not enough, because two different quantizations of +the same model land within a rounding error of each other. The manifest therefore +carries both, plus the aggregate byte total, and cross-checks the aggregate +against the sum of the shards. A manifest whose declared total disagrees with its +own shards is rejected rather than trusted, because that is the exact shape a +hand-edited "it fits now" manifest takes. + +Nothing here downloads a weight payload. Sizes and hashes come from the Hugging +Face metadata API (see ``scripts/refresh_glm_target_manifest.py``); verification +against a local file is DGR-018's job, using the digests locked here. +""" + +from __future__ import annotations + +import hashlib +import json +import re +from dataclasses import dataclass +from importlib.resources import files +from pathlib import Path +from typing import Any, Mapping + +TARGET_MANIFEST_SCHEMA_VERSION = 1 +ARCHITECTURE_SNAPSHOT_SCHEMA_VERSION = 1 + +ALPHA_QUANTIZATION = "UD-IQ1_S" +ALPHA_SHARD_COUNT = 6 + +_SHA256_RE = re.compile(r"\A[0-9a-f]{64}\Z") + +_MANIFEST_RESOURCE = "target-manifest.json" +_ARCHITECTURE_RESOURCE = "architecture-snapshot.json" + +GIB = 1024**3 +GB = 1000**3 + + +class GlmTargetError(ValueError): + """Raised when the target manifest or architecture snapshot is not the pinned target.""" + + +def canonical_sha256(value: Any) -> str: + """SHA-256 over canonical JSON — the repository's digest convention.""" + payload = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + return hashlib.sha256(payload.encode("utf-8")).hexdigest() + + +def _require_mapping(value: Any, what: str) -> Mapping[str, Any]: + if not isinstance(value, Mapping): + raise GlmTargetError(f"{what} must be a JSON object, got {type(value).__name__}") + return value + + +def _require_text(value: Any, what: str) -> str: + if not isinstance(value, str) or not value.strip(): + raise GlmTargetError(f"{what} must be a non-empty string") + return value + + +def _require_int(value: Any, what: str) -> int: + if not isinstance(value, int) or isinstance(value, bool): + raise GlmTargetError(f"{what} must be an integer, got {type(value).__name__}") + return value + + +def _require_sha256(value: Any, what: str) -> str: + text = _require_text(value, what) + if not _SHA256_RE.match(text): + raise GlmTargetError( + f"{what} must be a lowercase 64-character hex SHA-256, got {text!r}" + ) + return text + + +def _require_revision(value: Any, what: str) -> str: + text = _require_text(value, what) + if not re.fullmatch(r"[0-9a-f]{40}", text): + raise GlmTargetError( + f"{what} must be a full 40-character commit revision, got {text!r}; " + "a branch name or short SHA is not an immutable pin" + ) + return text + + +@dataclass(frozen=True) +class Shard: + """One GGUF shard of the alpha artifact.""" + + index: int + path: str + size_bytes: int + sha256: str + url: str + + def to_dict(self) -> dict: + return { + "index": self.index, + "path": self.path, + "size_bytes": self.size_bytes, + "sha256": self.sha256, + "url": self.url, + } + + +@dataclass(frozen=True) +class TargetManifest: + """The pinned, self-consistent GLM-5.2 ``UD-IQ1_S`` target.""" + + schema_version: int + manifest_version: int + observed_at: str + quantization: str + source_repo_id: str + source_revision: str + source_license: str + gguf_repo_id: str + gguf_revision: str + gguf_license: str + total_bytes: int + shards: tuple[Shard, ...] + raw: Mapping[str, Any] + source: str = "" + + @property + def total_gib(self) -> float: + return self.total_bytes / GIB + + @property + def total_gb(self) -> float: + return self.total_bytes / GB + + def shard(self, index: int) -> Shard: + for shard in self.shards: + if shard.index == index: + return shard + raise GlmTargetError(f"shard {index} is not in {self.source}") + + @property + def digest(self) -> str: + """Stable identity of this manifest, for the DGR-003 runtime recipe.""" + return canonical_sha256(self.raw) + + def to_dict(self) -> dict: + return dict(self.raw) + + +def _parse_shards(raw: Any, expected_count: int, expected_total: int) -> tuple[Shard, ...]: + if not isinstance(raw, list): + raise GlmTargetError("gguf_artifact.shards must be a JSON array") + if len(raw) != expected_count: + raise GlmTargetError( + f"the alpha artifact has exactly {expected_count} shards, " + f"but the manifest lists {len(raw)}" + ) + + shards: list[Shard] = [] + seen_index: set[int] = set() + seen_sha: set[str] = set() + for position, entry in enumerate(raw): + item = _require_mapping(entry, f"shards[{position}]") + index = _require_int(item.get("index"), f"shards[{position}].index") + if index in seen_index: + raise GlmTargetError(f"duplicate shard index {index} in the manifest") + seen_index.add(index) + size_bytes = _require_int(item.get("size_bytes"), f"shards[{index}].size_bytes") + if size_bytes <= 0: + raise GlmTargetError(f"shards[{index}].size_bytes must be positive") + sha256 = _require_sha256(item.get("sha256"), f"shards[{index}].sha256") + if sha256 in seen_sha: + raise GlmTargetError( + f"shard {index} repeats SHA-256 {sha256}; two distinct shards cannot " + "have the same content digest" + ) + seen_sha.add(sha256) + shards.append( + Shard( + index=index, + path=_require_text(item.get("path"), f"shards[{index}].path"), + size_bytes=size_bytes, + sha256=sha256, + url=_require_text(item.get("url"), f"shards[{index}].url"), + ) + ) + + expected_indices = set(range(1, expected_count + 1)) + if seen_index != expected_indices: + missing = sorted(expected_indices - seen_index) + raise GlmTargetError( + f"the manifest is missing shard(s) {missing}; all {expected_count} " + "shards of the alpha artifact must be pinned" + ) + + summed = sum(shard.size_bytes for shard in shards) + if summed != expected_total: + raise GlmTargetError( + f"declared total_bytes {expected_total} does not equal the sum of the " + f"shard sizes {summed}; the manifest is not self-consistent" + ) + + return tuple(sorted(shards, key=lambda shard: shard.index)) + + +def parse_target_manifest(data: Any, source: str = "") -> TargetManifest: + """Validate an already-decoded target manifest, failing closed.""" + doc = _require_mapping(data, f"manifest root in {source}") + + schema_version = _require_int(doc.get("schema_version"), f"'schema_version' in {source}") + if schema_version != TARGET_MANIFEST_SCHEMA_VERSION: + raise GlmTargetError( + f"{source} declares target-manifest schema version {schema_version}, " + f"but this node reads version {TARGET_MANIFEST_SCHEMA_VERSION}" + ) + + quantization = _require_text(doc.get("alpha_quantization"), f"'alpha_quantization' in {source}") + if quantization != ALPHA_QUANTIZATION: + raise GlmTargetError( + f"{source} pins quantization {quantization!r}, but the locked alpha " + f"quantization is {ALPHA_QUANTIZATION!r}; a different quantization is a " + "different target and requires a human contract change" + ) + + source_model = _require_mapping(doc.get("source_model"), f"'source_model' in {source}") + gguf = _require_mapping(doc.get("gguf_artifact"), f"'gguf_artifact' in {source}") + + gguf_quant = _require_text(gguf.get("quantization"), f"gguf_artifact.quantization in {source}") + if gguf_quant != quantization: + raise GlmTargetError( + f"{source} declares alpha_quantization {quantization!r} but the GGUF " + f"artifact block says {gguf_quant!r}" + ) + + shard_count = _require_int(gguf.get("shard_count"), f"gguf_artifact.shard_count in {source}") + if shard_count != ALPHA_SHARD_COUNT: + raise GlmTargetError( + f"{source} declares {shard_count} shards; the pinned alpha artifact has " + f"exactly {ALPHA_SHARD_COUNT}" + ) + + total_bytes = _require_int(gguf.get("total_bytes"), f"gguf_artifact.total_bytes in {source}") + shards = _parse_shards(gguf.get("shards"), shard_count, total_bytes) + + return TargetManifest( + schema_version=schema_version, + manifest_version=_require_int(doc.get("manifest_version"), f"'manifest_version' in {source}"), + observed_at=_require_text(doc.get("observed_at"), f"'observed_at' in {source}"), + quantization=quantization, + source_repo_id=_require_text(source_model.get("repo_id"), "source_model.repo_id"), + source_revision=_require_revision(source_model.get("revision"), "source_model.revision"), + source_license=_require_text(source_model.get("weight_license"), "source_model.weight_license"), + gguf_repo_id=_require_text(gguf.get("repo_id"), "gguf_artifact.repo_id"), + gguf_revision=_require_revision(gguf.get("revision"), "gguf_artifact.revision"), + gguf_license=_require_text(gguf.get("license"), "gguf_artifact.license"), + total_bytes=total_bytes, + shards=shards, + raw=doc, + source=source, + ) + + +@dataclass(frozen=True) +class ArchitectureSnapshot: + """Architecture-critical metadata derived from the pinned ``config.json``.""" + + schema_version: int + source_repo_id: str + source_revision: str + architecture: Mapping[str, Any] + reasoning_effort: Mapping[str, Any] + source_files: Mapping[str, Mapping[str, Any]] + raw: Mapping[str, Any] + source: str = "" + + def __getitem__(self, key: str) -> Any: + if key not in self.architecture: + raise GlmTargetError(f"architecture field {key!r} is missing from {self.source}") + return self.architecture[key] + + @property + def digest(self) -> str: + return canonical_sha256(self.raw) + + def file_sha256(self, path: str) -> str: + entry = self.source_files.get(path) + if entry is None: + raise GlmTargetError(f"{path!r} is not pinned in {self.source}") + return str(entry["sha256"]) + + def to_dict(self) -> dict: + return dict(self.raw) + + +# Fields the distributed runtime cannot plan or shard without. Absent or +# contradictory values fail closed rather than defaulting. +REQUIRED_ARCHITECTURE_FIELDS: tuple[str, ...] = ( + "model_type", + "num_hidden_layers", + "num_nextn_predict_layers", + "total_artifact_layers", + "first_k_dense_replace", + "dense_layers", + "sparse_moe_layers", + "hidden_size", + "n_routed_experts", + "num_experts_per_tok", + "n_shared_experts", + "kv_lora_rank", + "qk_rope_head_dim", + "mla_cached_values_per_token_per_layer", + "index_topk", + "index_head_dim", + "indexer_full_layers", + "indexer_shared_layers", + "max_position_embeddings", + "vocab_size", +) + + +def parse_architecture_snapshot(data: Any, source: str = "") -> ArchitectureSnapshot: + """Validate an architecture snapshot and its internal arithmetic.""" + doc = _require_mapping(data, f"snapshot root in {source}") + + schema_version = _require_int(doc.get("schema_version"), f"'schema_version' in {source}") + if schema_version != ARCHITECTURE_SNAPSHOT_SCHEMA_VERSION: + raise GlmTargetError( + f"{source} declares architecture-snapshot schema version {schema_version}, " + f"but this node reads version {ARCHITECTURE_SNAPSHOT_SCHEMA_VERSION}" + ) + + arch = _require_mapping(doc.get("architecture"), f"'architecture' in {source}") + missing = [field for field in REQUIRED_ARCHITECTURE_FIELDS if field not in arch] + if missing: + raise GlmTargetError( + f"{source} is missing architecture-critical field(s) {missing}; the " + "runtime cannot shard or plan an architecture it cannot fully describe" + ) + + layers = _require_int(arch["num_hidden_layers"], "num_hidden_layers") + nextn = _require_int(arch["num_nextn_predict_layers"], "num_nextn_predict_layers") + total_layers = _require_int(arch["total_artifact_layers"], "total_artifact_layers") + if total_layers != layers + nextn: + raise GlmTargetError( + f"total_artifact_layers {total_layers} != num_hidden_layers {layers} + " + f"num_nextn_predict_layers {nextn}; the NextN layer must be counted " + "explicitly, never folded into the backbone" + ) + + dense = _require_int(arch["dense_layers"], "dense_layers") + sparse = _require_int(arch["sparse_moe_layers"], "sparse_moe_layers") + if dense != _require_int(arch["first_k_dense_replace"], "first_k_dense_replace"): + raise GlmTargetError("dense_layers must equal first_k_dense_replace") + if dense + sparse != layers: + raise GlmTargetError( + f"dense_layers {dense} + sparse_moe_layers {sparse} != num_hidden_layers {layers}" + ) + + full = _require_int(arch["indexer_full_layers"], "indexer_full_layers") + shared = _require_int(arch["indexer_shared_layers"], "indexer_shared_layers") + if full + shared != layers: + raise GlmTargetError( + f"indexer_full_layers {full} + indexer_shared_layers {shared} != " + f"num_hidden_layers {layers}; every layer holds exactly one IndexShare role" + ) + if full <= 0: + raise GlmTargetError( + "indexer_full_layers must be positive; a route with no Full producer layer " + "has no index for its Shared consumers to reuse" + ) + + mla = _require_int( + arch["mla_cached_values_per_token_per_layer"], "mla_cached_values_per_token_per_layer" + ) + expected_mla = _require_int(arch["kv_lora_rank"], "kv_lora_rank") + _require_int( + arch["qk_rope_head_dim"], "qk_rope_head_dim" + ) + if mla != expected_mla: + raise GlmTargetError( + f"mla_cached_values_per_token_per_layer {mla} != kv_lora_rank + " + f"qk_rope_head_dim ({expected_mla})" + ) + + reasoning = _require_mapping(doc.get("reasoning_effort"), f"'reasoning_effort' in {source}") + if reasoning.get("alpha_mode") != "max": + raise GlmTargetError( + f"{source} does not lock reasoning_effort=max; alpha is defined as the " + "Max reasoning mode of this exact checkpoint" + ) + _require_text(reasoning.get("rendered_marker"), "reasoning_effort.rendered_marker") + + files_raw = doc.get("source_files") + if not isinstance(files_raw, list) or not files_raw: + raise GlmTargetError(f"'source_files' in {source} must be a non-empty JSON array") + source_files: dict[str, Mapping[str, Any]] = {} + for position, entry in enumerate(files_raw): + item = _require_mapping(entry, f"source_files[{position}]") + path = _require_text(item.get("path"), f"source_files[{position}].path") + _require_sha256(item.get("sha256"), f"source_files[{path}].sha256") + source_files[path] = item + for required in ("config.json", "chat_template.jinja"): + if required not in source_files: + raise GlmTargetError( + f"{source} does not pin {required!r}; config and chat-template drift " + "silently changes runtime semantics" + ) + + return ArchitectureSnapshot( + schema_version=schema_version, + source_repo_id=_require_text(doc.get("source_repo_id"), "source_repo_id"), + source_revision=_require_revision(doc.get("source_revision"), "source_revision"), + architecture=arch, + reasoning_effort=reasoning, + source_files=source_files, + raw=doc, + source=source, + ) + + +def _read_resource(resource: str, path: Path | None) -> tuple[str, str]: + if path is not None: + try: + return str(path), path.read_text(encoding="utf-8") + except OSError as exc: + raise GlmTargetError(f"cannot read {path}: {exc.strerror or exc}") from exc + source = f"packaged {resource}" + try: + raw = files("meshnet_node.glm_alpha").joinpath("data", resource).read_text(encoding="utf-8") + except (OSError, FileNotFoundError, ModuleNotFoundError) as exc: + raise GlmTargetError( + f"{source} is missing from this node installation ({type(exc).__name__})" + ) from exc + return source, raw + + +def _load_json(resource: str, path: Path | None) -> tuple[str, Any]: + source, raw = _read_resource(resource, path) + try: + return source, json.loads(raw) + except json.JSONDecodeError as exc: + raise GlmTargetError( + f"{source} is not valid JSON: {exc.msg} at line {exc.lineno} column {exc.colno}" + ) from exc + + +def load_target_manifest(path: Path | None = None) -> TargetManifest: + """Load the packaged target manifest, or one at ``path``.""" + source, data = _load_json(_MANIFEST_RESOURCE, path) + return parse_target_manifest(data, source=source) + + +def load_architecture_snapshot(path: Path | None = None) -> ArchitectureSnapshot: + """Load the packaged architecture snapshot, or one at ``path``.""" + source, data = _load_json(_ARCHITECTURE_RESOURCE, path) + return parse_architecture_snapshot(data, source=source) + + +def require_pinned_target( + manifest: TargetManifest, + snapshot: ArchitectureSnapshot, + *, + expected_source_revision: str, + expected_gguf_revision: str, +) -> None: + """Reject any target whose revisions are not the ones alpha was locked against. + + Callers pass the revisions from the locked alpha contract, so a swapped + manifest cannot quietly re-point the target at a different upstream commit. + """ + if manifest.source_revision != expected_source_revision: + raise GlmTargetError( + f"source revision {manifest.source_revision} does not match the locked " + f"alpha revision {expected_source_revision}" + ) + if manifest.gguf_revision != expected_gguf_revision: + raise GlmTargetError( + f"GGUF revision {manifest.gguf_revision} does not match the locked alpha " + f"revision {expected_gguf_revision}" + ) + if snapshot.source_revision != manifest.source_revision: + raise GlmTargetError( + f"the architecture snapshot was taken at {snapshot.source_revision} but the " + f"manifest pins {manifest.source_revision}; config metadata and weights must " + "come from one revision" + ) diff --git a/packages/node/meshnet_node/glm_alpha/planner.py b/packages/node/meshnet_node/glm_alpha/planner.py new file mode 100644 index 0000000..3edcfc0 --- /dev/null +++ b/packages/node/meshnet_node/glm_alpha/planner.py @@ -0,0 +1,522 @@ +"""Deterministic memory, KV, and network planner for the GLM-5.2 Max alpha route. + +Everything here is arithmetic over the exact pinned artifact bytes and the exact +pinned architecture. There is no measurement, no probing, and no heuristic tuned +to a result — the planner is written *before* the target runs so that a later +story cannot discover a topology that "works" and then rationalise it. + +Three ideas do the real work. + +**Unified memory is one pool.** On an integrated-GPU machine the "VRAM" the driver +reports is carved out of the same physical DRAM the OS is already counting. Adding +them produces a node that appears to hold twice what it holds, and the failure mode +is not a clean admission rejection — it is an OOM or a swap-thrash halfway through +a 200 GiB load. :class:`NodeMemory` therefore refuses to be constructed from an +additive claim about one shared pool. + +**The reserve is not optional headroom.** Weights plus KV are not the whole +resident cost: backend workspaces, quantization scratch, the graph plan, the +process, and the OS all live outside them, and the largest of those scale with the +backend rather than with the shard. Alpha reserves ``max(20% of physically usable +memory, 8 GiB)`` per node, and the *remainder* is the placement budget. + +**Equal layer counts are not equal bytes.** Embeddings and the output head are +endpoint-only; three layers are dense and 75 are MoE; shared experts, indexer +tensors, and quant block alignment all skew the per-node share. Until DGR-018/019 +report measured per-tensor placement, the planner carries an explicit +:data:`PLACEMENT_IMBALANCE_FACTOR` and reports the arithmetic minimum and the +recommended count as two separate numbers. The arithmetic minimum is a fit probe; +it is admissible only with exact measured placement evidence behind it. +""" + +from __future__ import annotations + +import math +from dataclasses import dataclass +from typing import Literal + +from .manifest import GIB, ArchitectureSnapshot, TargetManifest + +# Q8_0 stores 32 int8 quants plus one fp16 scale per block: 34 bytes / 32 values. +Q8_0_BYTES_PER_VALUE = 34 / 32 +F16_BYTES_PER_VALUE = 2.0 + +KV_DTYPES: dict[str, float] = { + "Q8_0": Q8_0_BYTES_PER_VALUE, + "F16": F16_BYTES_PER_VALUE, +} + +# The alpha KV configuration, locked by the roadmap. +ALPHA_KV_DTYPE = "Q8_0" +ALPHA_CONTEXT_TOKENS = 16384 +ALPHA_CONCURRENCY = 1 + +# The reserve every node holds outside its weight-plus-KV placement budget. +RESERVE_FRACTION = 0.20 +RESERVE_FLOOR_GIB = 8.0 + +# The aggregate runtime-accessible memory at which the artifact *just* fits. +# This is an experimental hard-fit floor, not an operational envelope: it has no +# room for a backend that allocates more scratch than another, and none for the +# imbalance below. +AGGREGATE_HARD_FIT_FLOOR_GIB = 224.0 + +# How much more than an equal share the worst-placed node is expected to hold. +# 1.10 is the roadmap's recommended-topology column expressed as arithmetic: it +# reproduces 10 / 6 / 5 / 3 / 3 nodes for the 32 / 48 / 64 / 96 / 128 GiB tiers. +# DGR-019 must replace it with measured per-tensor placement. +PLACEMENT_IMBALANCE_FACTOR = 1.10 + +# Alpha network floor. A link rate is a bandwidth claim, never a speed claim. +MIN_LINK_RATE_GBPS = 2.5 +RECOMMENDED_LINK_RATE_GBPS = 10.0 + +BF16_BYTES = 2 +DSA_SIDEBAND_INT32_BYTES = 4 + +IndexerLayout = Literal["optimized", "conservative"] + + +class ResourcePlanError(ValueError): + """Raised when a node or route cannot be accounted for honestly.""" + + +@dataclass(frozen=True) +class NodeMemory: + """One node's physically usable memory, counted once. + + ``physical_usable_gib`` is what the node can actually place bytes into after + firmware and fixed carve-outs — not the marketing capacity, and not a sum of + two views of the same DRAM. + """ + + name: str + physical_usable_gib: float + unified: bool + + def __post_init__(self) -> None: + if not isinstance(self.name, str) or not self.name.strip(): + raise ResourcePlanError("node name must be a non-empty physical-host identity") + if ( + isinstance(self.physical_usable_gib, bool) + or not isinstance(self.physical_usable_gib, (int, float)) + or not math.isfinite(self.physical_usable_gib) + or self.physical_usable_gib <= 0 + ): + raise ResourcePlanError( + f"node {self.name!r} must declare finite positive usable memory" + ) + + @classmethod + def from_host( + cls, + name: str, + *, + system_ram_gib: float, + gpu_memory_gib: float = 0.0, + unified: bool, + ) -> "NodeMemory": + """Build a node from a host's reported RAM and GPU memory. + + On a unified machine the GPU memory *is* system RAM, so it is counted once + and never added. Passing a non-zero ``gpu_memory_gib`` alongside + ``unified=True`` is the double-count this project has already decided is a + bug (RALPH-CONTEXT runtime decision 16), so it is rejected rather than + silently discarded: a caller who believes an integrated GPU adds memory has + a wrong model of the machine, and quietly ignoring the argument would let + that belief survive. + """ + if not isinstance(unified, bool): + raise ResourcePlanError(f"node {name!r} unified flag must be boolean") + for value, label, allow_zero in ( + (system_ram_gib, "system RAM", False), + (gpu_memory_gib, "GPU memory", True), + ): + if ( + isinstance(value, bool) + or not isinstance(value, (int, float)) + or not math.isfinite(value) + or value < 0 + or (not allow_zero and value == 0) + ): + qualifier = "finite non-negative" if allow_zero else "finite positive" + raise ResourcePlanError(f"node {name!r} must declare {qualifier} {label}") + if unified: + if gpu_memory_gib: + raise ResourcePlanError( + f"node {name!r} declares unified memory and {gpu_memory_gib} GiB of " + "separate GPU memory. Integrated-GPU memory is carved out of the same " + "physical DRAM as system RAM; adding them double-counts one pool. " + "Pass unified=True with system_ram_gib only." + ) + usable = system_ram_gib + else: + usable = system_ram_gib + gpu_memory_gib + return cls(name=name, physical_usable_gib=usable, unified=unified) + + @property + def reserve_gib(self) -> float: + """``max(20% of physically usable memory, 8 GiB)``.""" + return max(RESERVE_FRACTION * self.physical_usable_gib, RESERVE_FLOOR_GIB) + + @property + def placement_budget_gib(self) -> float: + """What remains for weights plus KV after the reserve.""" + return self.physical_usable_gib - self.reserve_gib + + +def kv_bytes( + snapshot: ArchitectureSnapshot, + *, + context_tokens: int = ALPHA_CONTEXT_TOKENS, + concurrency: int = ALPHA_CONCURRENCY, + dtype: str = ALPHA_KV_DTYPE, + indexer_layout: IndexerLayout = "conservative", + include_indexer: bool = True, +) -> int: + """Bytes of MLA (and DSA indexer) KV cache for the whole model. + + ``indexer_layout`` is the honest part. Correct DSA only needs indexer keys for + the Full producer layers, but the current experimental implementation may + allocate them across every backbone layer. Alpha budgets ``conservative`` + (all 78) so that a route admitted by this planner cannot be surprised by the + implementation it actually gets. + """ + if ( + not isinstance(context_tokens, int) + or isinstance(context_tokens, bool) + or context_tokens <= 0 + or not isinstance(concurrency, int) + or isinstance(concurrency, bool) + or concurrency <= 0 + ): + raise ResourcePlanError("context_tokens and concurrency must be positive integers") + if dtype not in KV_DTYPES: + raise ResourcePlanError( + f"unsupported KV dtype {dtype!r}; alpha locks {ALPHA_KV_DTYPE} " + f"(known: {', '.join(sorted(KV_DTYPES))})" + ) + bytes_per_value = KV_DTYPES[dtype] + + layers = int(snapshot["num_hidden_layers"]) + mla_values = int(snapshot["mla_cached_values_per_token_per_layer"]) + total_values = mla_values * layers + + if include_indexer: + if indexer_layout == "optimized": + indexer_layers = int(snapshot["indexer_full_layers"]) + elif indexer_layout == "conservative": + indexer_layers = layers + else: # pragma: no cover - Literal keeps this unreachable from typed callers + raise ResourcePlanError(f"unknown indexer_layout {indexer_layout!r}") + total_values += int(snapshot["index_head_dim"]) * indexer_layers + + return int(total_values * context_tokens * concurrency * bytes_per_value) + + +@dataclass(frozen=True) +class TopologyPlan: + """The node count a homogeneous tier needs, and how it was reached.""" + + physical_usable_gib: float + reserve_gib: float + placement_budget_gib: float + weight_gib: float + kv_gib: float + total_placement_gib: float + arithmetic_minimum_nodes: int + recommended_nodes: int + imbalance_factor: float + + @property + def is_arithmetic_minimum_topology(self) -> bool: + """True when the recommendation offers no imbalance headroom at all.""" + return self.recommended_nodes == self.arithmetic_minimum_nodes + + def to_dict(self) -> dict: + return { + "physical_usable_gib": round(self.physical_usable_gib, 3), + "reserve_gib": round(self.reserve_gib, 3), + "placement_budget_gib": round(self.placement_budget_gib, 3), + "weight_gib": round(self.weight_gib, 3), + "kv_gib": round(self.kv_gib, 3), + "total_placement_gib": round(self.total_placement_gib, 3), + "arithmetic_minimum_nodes": self.arithmetic_minimum_nodes, + "recommended_nodes": self.recommended_nodes, + "imbalance_factor": self.imbalance_factor, + } + + +def plan_topology( + manifest: TargetManifest, + snapshot: ArchitectureSnapshot, + *, + physical_usable_gib: float, + context_tokens: int = ALPHA_CONTEXT_TOKENS, + concurrency: int = ALPHA_CONCURRENCY, + kv_dtype: str = ALPHA_KV_DTYPE, + indexer_layout: IndexerLayout = "conservative", + imbalance_factor: float = PLACEMENT_IMBALANCE_FACTOR, +) -> TopologyPlan: + """Minimum and recommended node count for a homogeneous tier of this size.""" + if ( + isinstance(imbalance_factor, bool) + or not isinstance(imbalance_factor, (int, float)) + or not math.isfinite(imbalance_factor) + or imbalance_factor < 1.0 + ): + raise ResourcePlanError( + "imbalance_factor must be finite and at least 1.0; a lower value would " + "assume the worst-placed node holds less than an equal share" + ) + + node = NodeMemory( + name=f"{physical_usable_gib:g}GiB-tier", + physical_usable_gib=physical_usable_gib, + unified=False, + ) + budget = node.placement_budget_gib + if budget <= 0: + raise ResourcePlanError( + f"a {physical_usable_gib:g} GiB node has no placement budget after its " + f"{node.reserve_gib:.1f} GiB reserve" + ) + + weight_gib = manifest.total_bytes / GIB + kv_gib = ( + kv_bytes( + snapshot, + context_tokens=context_tokens, + concurrency=concurrency, + dtype=kv_dtype, + indexer_layout=indexer_layout, + ) + / GIB + ) + total = weight_gib + kv_gib + + return TopologyPlan( + physical_usable_gib=physical_usable_gib, + reserve_gib=node.reserve_gib, + placement_budget_gib=budget, + weight_gib=weight_gib, + kv_gib=kv_gib, + total_placement_gib=total, + arithmetic_minimum_nodes=math.ceil(total / budget), + recommended_nodes=math.ceil(total * imbalance_factor / budget), + imbalance_factor=imbalance_factor, + ) + + +@dataclass(frozen=True) +class RouteFit: + """Whether a concrete, possibly heterogeneous set of nodes can hold the target.""" + + node_count: int + aggregate_usable_gib: float + aggregate_placement_budget_gib: float + required_placement_gib: float + fits: bool + meets_hard_fit_floor: bool + no_single_node_can_admit_target: bool + headroom_gib: float + reasons: tuple[str, ...] + + def to_dict(self) -> dict: + return { + "node_count": self.node_count, + "aggregate_usable_gib": round(self.aggregate_usable_gib, 3), + "aggregate_placement_budget_gib": round(self.aggregate_placement_budget_gib, 3), + "required_placement_gib": round(self.required_placement_gib, 3), + "fits": self.fits, + "meets_hard_fit_floor": self.meets_hard_fit_floor, + "no_single_node_can_admit_target": self.no_single_node_can_admit_target, + "headroom_gib": round(self.headroom_gib, 3), + "reasons": list(self.reasons), + } + + +def plan_route( + manifest: TargetManifest, + snapshot: ArchitectureSnapshot, + nodes: list[NodeMemory], + *, + context_tokens: int = ALPHA_CONTEXT_TOKENS, + concurrency: int = ALPHA_CONCURRENCY, + kv_dtype: str = ALPHA_KV_DTYPE, + indexer_layout: IndexerLayout = "conservative", +) -> RouteFit: + """Evaluate a concrete route. Every node's memory is already counted once.""" + if len(nodes) < 2: + raise ResourcePlanError( + "the alpha target is distributed by definition; a route needs at least two " + "physical nodes" + ) + names = [node.name for node in nodes] + if len(set(names)) != len(names): + raise ResourcePlanError( + "duplicate node names in the route; one physical machine counted twice is " + "the same double-count as adding integrated-GPU memory to system RAM" + ) + + weight_gib = manifest.total_bytes / GIB + kv_gib = ( + kv_bytes( + snapshot, + context_tokens=context_tokens, + concurrency=concurrency, + dtype=kv_dtype, + indexer_layout=indexer_layout, + ) + / GIB + ) + required = weight_gib + kv_gib + + aggregate_usable = sum(node.physical_usable_gib for node in nodes) + aggregate_budget = sum(node.placement_budget_gib for node in nodes) + fits = aggregate_budget >= required + largest_budget = max(node.placement_budget_gib for node in nodes) + no_single_node = largest_budget < required + + reasons: list[str] = [] + if not fits: + reasons.append( + f"aggregate placement budget {aggregate_budget:.1f} GiB is below the " + f"{required:.1f} GiB the target needs after each node's reserve" + ) + if not no_single_node: + reasons.append( + "at least one node could admit the complete target alone; that is a " + "single-host run, not distributed alpha" + ) + if aggregate_usable < AGGREGATE_HARD_FIT_FLOOR_GIB: + reasons.append( + f"aggregate usable memory {aggregate_usable:.1f} GiB is below the " + f"{AGGREGATE_HARD_FIT_FLOOR_GIB:g} GiB experimental hard-fit floor" + ) + + return RouteFit( + node_count=len(nodes), + aggregate_usable_gib=aggregate_usable, + aggregate_placement_budget_gib=aggregate_budget, + required_placement_gib=required, + fits=fits, + meets_hard_fit_floor=aggregate_usable >= AGGREGATE_HARD_FIT_FLOOR_GIB, + no_single_node_can_admit_target=no_single_node, + headroom_gib=aggregate_budget - required, + reasons=tuple(reasons), + ) + + +@dataclass(frozen=True) +class SeamPlan: + """Bytes and latency across the activation seams of a route. + + Bandwidth and latency are reported separately on purpose. Decode moves almost + nothing — 12 KiB per token per seam — so a faster link barely helps it. What + decode pays is *serial*: every generated token crosses every seam in order, so + the cost that matters is ``seams x per-hop latency``. A route that claims to be + fast because it is on 10 GbE has confused the two. + """ + + node_count: int + seam_count: int + hidden_size: int + bytes_per_token_per_seam: int + prefill_bytes_per_seam: int + decode_bytes_per_seam_per_token: int + dsa_sideband_bytes_per_query: int + link_rate_gbps: float + meets_alpha_minimum: bool + is_recommended_link: bool + decode_serialization_ms_per_token: float + decode_latency_ms_per_token: float + decode_bandwidth_share_ms_per_token: float + prefill_serialization_ms: float + + def to_dict(self) -> dict: + return { + "node_count": self.node_count, + "seam_count": self.seam_count, + "hidden_size": self.hidden_size, + "bytes_per_token_per_seam": self.bytes_per_token_per_seam, + "prefill_bytes_per_seam": self.prefill_bytes_per_seam, + "decode_bytes_per_seam_per_token": self.decode_bytes_per_seam_per_token, + "dsa_sideband_bytes_per_query": self.dsa_sideband_bytes_per_query, + "link_rate_gbps": self.link_rate_gbps, + "meets_alpha_minimum": self.meets_alpha_minimum, + "is_recommended_link": self.is_recommended_link, + "decode_serialization_ms_per_token": round(self.decode_serialization_ms_per_token, 4), + "decode_latency_ms_per_token": round(self.decode_latency_ms_per_token, 4), + "decode_bandwidth_share_ms_per_token": round( + self.decode_bandwidth_share_ms_per_token, 4 + ), + "prefill_serialization_ms": round(self.prefill_serialization_ms, 3), + } + + +def plan_seams( + snapshot: ArchitectureSnapshot, + *, + node_count: int, + context_tokens: int = ALPHA_CONTEXT_TOKENS, + link_rate_gbps: float = MIN_LINK_RATE_GBPS, + per_hop_latency_ms: float = 0.5, +) -> SeamPlan: + """Model seam bytes, wire serialization, and serial per-hop latency separately.""" + if not isinstance(node_count, int) or isinstance(node_count, bool) or node_count < 2: + raise ResourcePlanError("a seam exists only between two nodes") + if not isinstance(context_tokens, int) or isinstance(context_tokens, bool) or context_tokens <= 0: + raise ResourcePlanError("context_tokens must be a positive integer") + if ( + isinstance(link_rate_gbps, bool) + or not isinstance(link_rate_gbps, (int, float)) + or not math.isfinite(link_rate_gbps) + or link_rate_gbps <= 0 + ): + raise ResourcePlanError("link_rate_gbps must be finite and positive") + if ( + isinstance(per_hop_latency_ms, bool) + or not isinstance(per_hop_latency_ms, (int, float)) + or not math.isfinite(per_hop_latency_ms) + or per_hop_latency_ms < 0 + ): + raise ResourcePlanError("per_hop_latency_ms must be finite and non-negative") + + hidden = int(snapshot["hidden_size"]) + bytes_per_token = hidden * BF16_BYTES + seams = node_count - 1 + + bits_per_ms = link_rate_gbps * 1e9 / 1e3 + decode_serialization_ms = (bytes_per_token * 8) / bits_per_ms + prefill_serialization_ms = (bytes_per_token * context_tokens * 8) / bits_per_ms + + return SeamPlan( + node_count=node_count, + seam_count=seams, + hidden_size=hidden, + bytes_per_token_per_seam=bytes_per_token, + prefill_bytes_per_seam=bytes_per_token * context_tokens, + decode_bytes_per_seam_per_token=bytes_per_token, + dsa_sideband_bytes_per_query=int(snapshot["index_topk"]) * DSA_SIDEBAND_INT32_BYTES, + link_rate_gbps=link_rate_gbps, + meets_alpha_minimum=link_rate_gbps >= MIN_LINK_RATE_GBPS, + is_recommended_link=link_rate_gbps >= RECOMMENDED_LINK_RATE_GBPS, + decode_serialization_ms_per_token=decode_serialization_ms * seams, + decode_latency_ms_per_token=per_hop_latency_ms * seams, + decode_bandwidth_share_ms_per_token=decode_serialization_ms * seams, + prefill_serialization_ms=prefill_serialization_ms * seams, + ) + + +ALPHA_TIERS_GIB: tuple[float, ...] = (32.0, 48.0, 64.0, 96.0, 128.0) + + +def plan_all_tiers( + manifest: TargetManifest, snapshot: ArchitectureSnapshot +) -> dict[str, TopologyPlan]: + """The alpha tier table, recomputed from the pinned artifact and architecture.""" + return { + f"{tier:g}": plan_topology(manifest, snapshot, physical_usable_gib=tier) + for tier in ALPHA_TIERS_GIB + } diff --git a/packages/node/pyproject.toml b/packages/node/pyproject.toml index 41fd484..79c79df 100644 --- a/packages/node/pyproject.toml +++ b/packages/node/pyproject.toml @@ -42,3 +42,4 @@ include = ["meshnet_node*"] [tool.setuptools.package-data] meshnet_node = ["*.json"] +"meshnet_node.glm_alpha" = ["data/*.json"] diff --git a/scripts/refresh_glm_target_manifest.py b/scripts/refresh_glm_target_manifest.py new file mode 100644 index 0000000..2b4cad7 --- /dev/null +++ b/scripts/refresh_glm_target_manifest.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python3 +"""Refresh (or check) the pinned GLM-5.2 target manifest and architecture snapshot. + +Resolves revisions, shard sizes, and LFS SHA-256 digests from the Hugging Face +metadata API. It never downloads a weight payload: sizes and digests come from +``/api/models/.../paths-info``, which returns the LFS pointer metadata, and the only +files fetched in full are the small config/tokenizer/chat-template documents whose +bytes the snapshot hashes. + +Usage:: + + python scripts/refresh_glm_target_manifest.py --check # CI: pinned bytes still resolve? + python scripts/refresh_glm_target_manifest.py --write # re-pin HEAD after human review + +``--check`` validates the already locked revisions through Hugging Face's +revision-specific API. A normal new upstream commit does not mutate the target. +``--write`` intentionally follows moving HEAD and is therefore a reviewed target +change, never an automatic refresh. + +This script requires network access and is not part of the default test suite. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import sys +import urllib.request +from pathlib import Path +from typing import Any + +DATA_DIR = Path(__file__).resolve().parent.parent / "packages/node/meshnet_node/glm_alpha/data" +MANIFEST_PATH = DATA_DIR / "target-manifest.json" +SNAPSHOT_PATH = DATA_DIR / "architecture-snapshot.json" + +SOURCE_REPO = "zai-org/GLM-5.2" +GGUF_REPO = "unsloth/GLM-5.2-GGUF" +QUANT = "UD-IQ1_S" +FALLBACK_QUANT = "UD-IQ1_M" +SHARD_COUNT = 6 + +SNAPSHOT_FILES = ( + "config.json", + "chat_template.jinja", + "generation_config.json", + "tokenizer_config.json", +) + +TIMEOUT = 60 + + +def _get(url: str) -> bytes: + with urllib.request.urlopen(url, timeout=TIMEOUT) as response: # noqa: S310 - fixed HTTPS host + return response.read() + + +def _get_json(url: str) -> Any: + return json.loads(_get(url)) + + +def _post_json(url: str, payload: dict) -> Any: + request = urllib.request.Request( + url, + data=json.dumps(payload).encode("utf-8"), + headers={"Content-Type": "application/json"}, + method="POST", + ) + with urllib.request.urlopen(request, timeout=TIMEOUT) as response: # noqa: S310 + return json.loads(response.read()) + + +def _shard_paths(quant: str) -> list[str]: + return [ + f"{quant}/GLM-5.2-{quant}-{index:05d}-of-{SHARD_COUNT:05d}.gguf" + for index in range(1, SHARD_COUNT + 1) + ] + + +def _resolve_shards(revision: str, quant: str) -> list[dict]: + info = _post_json( + f"https://huggingface.co/api/models/{GGUF_REPO}/paths-info/{revision}", + {"paths": _shard_paths(quant)}, + ) + by_path = {entry["path"]: entry for entry in info} + shards = [] + for index, path in enumerate(_shard_paths(quant), start=1): + entry = by_path.get(path) + if entry is None: + raise SystemExit(f"upstream is missing shard {path} at revision {revision}") + lfs = entry.get("lfs") or {} + oid = lfs.get("oid") + if not oid: + raise SystemExit( + f"{path} has no LFS oid; a non-LFS shard is not the published artifact" + ) + shards.append( + { + "index": index, + "path": path, + "size_bytes": int(entry["size"]), + "sha256": oid, + "url": f"https://huggingface.co/{GGUF_REPO}/resolve/{revision}/{path}", + } + ) + return shards + + +def build_documents( + *, + source_revision: str | None = None, + gguf_revision: str | None = None, +) -> tuple[dict, dict]: + """Resolve documents at explicit pins, or at current HEAD for reviewed re-pinning.""" + + source_api = f"https://huggingface.co/api/models/{SOURCE_REPO}" + gguf_api = f"https://huggingface.co/api/models/{GGUF_REPO}" + if source_revision is not None: + source_api += f"/revision/{source_revision}" + if gguf_revision is not None: + gguf_api += f"/revision/{gguf_revision}" + source_info = _get_json(source_api) + gguf_info = _get_json(gguf_api) + source_rev = source_info["sha"] + gguf_rev = gguf_info["sha"] + if source_revision is not None and source_rev != source_revision: + raise SystemExit( + f"source revision endpoint returned {source_rev}, expected {source_revision}" + ) + if gguf_revision is not None and gguf_rev != gguf_revision: + raise SystemExit(f"GGUF revision endpoint returned {gguf_rev}, expected {gguf_revision}") + + shards = _resolve_shards(gguf_rev, QUANT) + total = sum(shard["size_bytes"] for shard in shards) + fallback = _resolve_shards(gguf_rev, FALLBACK_QUANT) + fallback_total = sum(shard["size_bytes"] for shard in fallback) + + manifest = json.loads(MANIFEST_PATH.read_text(encoding="utf-8")) + manifest["source_model"]["revision"] = source_rev + manifest["source_model"]["last_modified"] = source_info.get("lastModified") + manifest["source_model"]["revision_url"] = ( + f"https://huggingface.co/{SOURCE_REPO}/tree/{source_rev}" + ) + manifest["gguf_artifact"]["revision"] = gguf_rev + manifest["gguf_artifact"]["last_modified"] = gguf_info.get("lastModified") + manifest["gguf_artifact"]["revision_url"] = ( + f"https://huggingface.co/{GGUF_REPO}/tree/{gguf_rev}" + ) + manifest["gguf_artifact"]["shards"] = shards + manifest["gguf_artifact"]["total_bytes"] = total + manifest["gguf_artifact"]["total_gib"] = round(total / 1024**3, 3) + manifest["gguf_artifact"]["total_gb"] = round(total / 1000**3, 3) + manifest["diagnostic_fallback"]["total_bytes"] = fallback_total + manifest["diagnostic_fallback"]["total_gib"] = round(fallback_total / 1024**3, 3) + manifest["diagnostic_fallback"]["total_gb"] = round(fallback_total / 1000**3, 3) + + snapshot = json.loads(SNAPSHOT_PATH.read_text(encoding="utf-8")) + snapshot["source_revision"] = source_rev + source_files = [] + config: dict[str, Any] = {} + for name in SNAPSHOT_FILES: + url = f"https://huggingface.co/{SOURCE_REPO}/resolve/{source_rev}/{name}" + body = _get(url) + source_files.append( + { + "path": name, + "size_bytes": len(body), + "sha256": hashlib.sha256(body).hexdigest(), + "url": url, + } + ) + if name == "config.json": + config = json.loads(body) + snapshot["source_files"] = source_files + + indexer_types = config["indexer_types"] + arch = snapshot["architecture"] + arch["num_hidden_layers"] = config["num_hidden_layers"] + arch["num_nextn_predict_layers"] = config["num_nextn_predict_layers"] + arch["total_artifact_layers"] = config["num_hidden_layers"] + config["num_nextn_predict_layers"] + arch["hidden_size"] = config["hidden_size"] + arch["n_routed_experts"] = config["n_routed_experts"] + arch["num_experts_per_tok"] = config["num_experts_per_tok"] + arch["n_shared_experts"] = config["n_shared_experts"] + arch["index_topk"] = config["index_topk"] + arch["index_head_dim"] = config["index_head_dim"] + arch["kv_lora_rank"] = config["kv_lora_rank"] + arch["qk_rope_head_dim"] = config["qk_rope_head_dim"] + arch["mla_cached_values_per_token_per_layer"] = ( + config["kv_lora_rank"] + config["qk_rope_head_dim"] + ) + arch["indexer_full_layers"] = sum(1 for role in indexer_types if role == "full") + arch["indexer_shared_layers"] = sum(1 for role in indexer_types if role == "shared") + arch["indexer_types_sha256"] = hashlib.sha256( + json.dumps(indexer_types, separators=(",", ":")).encode("utf-8") + ).hexdigest() + arch["max_position_embeddings"] = config["max_position_embeddings"] + arch["vocab_size"] = config["vocab_size"] + arch["first_k_dense_replace"] = config["first_k_dense_replace"] + arch["dense_layers"] = config["first_k_dense_replace"] + arch["sparse_moe_layers"] = config["num_hidden_layers"] - config["first_k_dense_replace"] + + return manifest, snapshot + + +def _dump(document: dict) -> str: + return json.dumps(document, indent=2, ensure_ascii=False) + "\n" + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("--check", action="store_true", help="fail if the pins have drifted") + group.add_argument("--write", action="store_true", help="re-pin from upstream") + args = parser.parse_args() + + if args.check: + pinned = json.loads(MANIFEST_PATH.read_text(encoding="utf-8")) + manifest, snapshot = build_documents( + source_revision=pinned["source_model"]["revision"], + gguf_revision=pinned["gguf_artifact"]["revision"], + ) + else: + # --write intentionally follows current HEAD and therefore requires review. + manifest, snapshot = build_documents() + + if args.write: + MANIFEST_PATH.write_text(_dump(manifest), encoding="utf-8") + SNAPSHOT_PATH.write_text(_dump(snapshot), encoding="utf-8") + print(f"wrote {MANIFEST_PATH}") + print(f"wrote {SNAPSHOT_PATH}") + print("Re-pinning changes the alpha target. Update the alpha contract under review.") + return 0 + + drifted = False + for path, fresh in ((MANIFEST_PATH, manifest), (SNAPSHOT_PATH, snapshot)): + current = json.loads(path.read_text(encoding="utf-8")) + if current != fresh: + drifted = True + print(f"DRIFT: {path.name} no longer matches upstream", file=sys.stderr) + if drifted: + print( + "\nPinned revision metadata no longer matches the locked files. Treat this as " + "artifact-integrity drift; do not heal or re-pin without human review.", + file=sys.stderr, + ) + return 1 + + print("target manifest and architecture snapshot match upstream") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_glm_alpha_target.py b/tests/test_glm_alpha_target.py new file mode 100644 index 0000000..c92b6c9 --- /dev/null +++ b/tests/test_glm_alpha_target.py @@ -0,0 +1,851 @@ +"""DGR-017 — the locked GLM-5.2 Max target, resource plan, and alpha contract. + +These tests are deterministic, offline, GPU-free, and download-free. They assert +against the *pinned* manifest, so they fail if a later agent swaps the artifact, +loosens the memory accounting, or moves a threshold after seeing a result. + +The planner tests are written as a reproduction of the roadmap's published tables. +That is the point: if the arithmetic here ever stops reproducing them, either the +roadmap or the planner is lying, and the test says which numbers changed. +""" + +from __future__ import annotations + +import copy +import json +from pathlib import Path + +import pytest + +from meshnet_node.glm_alpha import ( + AGGREGATE_HARD_FIT_FLOOR_GIB, + ALPHA_CONTRACT_ID, + ALPHA_QUANTIZATION, + ALPHA_SHARD_COUNT, + MIN_LINK_RATE_GBPS, + RECOMMENDED_LINK_RATE_GBPS, + RESERVE_FLOOR_GIB, + AlphaContractError, + GlmTargetError, + NodeMemory, + ResourcePlanError, + compute_contract_digest, + kv_bytes, + load_alpha_contract, + load_architecture_snapshot, + load_locked_target, + load_target_manifest, + parse_alpha_contract, + parse_architecture_snapshot, + parse_target_manifest, + plan_route, + plan_seams, + plan_topology, + require_contract_target, + require_pinned_target, + seal_contract, +) +from meshnet_node.glm_alpha.manifest import GIB + +# The revisions observed and pinned by DGR-017 on 2026-07-13. +SOURCE_REVISION = "b4734de4facf877f85769a911abafc5283eab3d9" +GGUF_REVISION = "abc55e72527792c6e77069c99b4cb7de16fa9f23" +TOTAL_BYTES = 216_715_360_960 + + +@pytest.fixture(scope="module") +def manifest(): + return load_target_manifest() + + +@pytest.fixture(scope="module") +def snapshot(): + return load_architecture_snapshot() + + +@pytest.fixture(scope="module") +def contract(): + return load_alpha_contract() + + +@pytest.fixture +def manifest_doc(manifest): + return copy.deepcopy(dict(manifest.raw)) + + +@pytest.fixture +def snapshot_doc(snapshot): + return copy.deepcopy(dict(snapshot.raw)) + + +@pytest.fixture +def contract_doc(contract): + return copy.deepcopy(dict(contract.raw)) + + +# -------------------------------------------------------------------------- +# Identity: the exact artifact, pinned +# -------------------------------------------------------------------------- + + +def test_manifest_pins_both_repositories_by_exact_revision(manifest): + assert manifest.source_repo_id == "zai-org/GLM-5.2" + assert manifest.source_revision == SOURCE_REVISION + assert manifest.gguf_repo_id == "unsloth/GLM-5.2-GGUF" + assert manifest.gguf_revision == GGUF_REVISION + assert manifest.quantization == ALPHA_QUANTIZATION == "UD-IQ1_S" + assert manifest.source_license == "mit" + assert manifest.gguf_license == "mit" + + +def test_manifest_resolves_all_six_shards_with_sizes_hashes_and_urls(manifest): + assert len(manifest.shards) == ALPHA_SHARD_COUNT == 6 + assert [shard.index for shard in manifest.shards] == [1, 2, 3, 4, 5, 6] + + for shard in manifest.shards: + assert shard.size_bytes > 0 + assert len(shard.sha256) == 64 + assert shard.url.startswith(f"https://huggingface.co/{manifest.gguf_repo_id}/resolve/") + assert GGUF_REVISION in shard.url, "a shard URL must resolve at the pinned revision" + + digests = {shard.sha256 for shard in manifest.shards} + assert len(digests) == 6, "six distinct shards must have six distinct content digests" + + +def test_manifest_aggregate_bytes_are_exact_and_self_consistent(manifest): + assert manifest.total_bytes == TOTAL_BYTES + assert sum(shard.size_bytes for shard in manifest.shards) == TOTAL_BYTES + assert round(manifest.total_gib, 3) == 201.832 + assert round(manifest.total_gb, 3) == 216.715 + + +def test_manifest_records_the_iq1_m_diagnostic_fallback_without_promoting_it(manifest): + fallback = manifest.raw["diagnostic_fallback"] + assert fallback["quantization"] == "UD-IQ1_M" + assert fallback["total_bytes"] == 228_492_966_624 + assert fallback["total_bytes"] > manifest.total_bytes + assert "does not satisfy" in fallback["policy"] + + +def test_manifest_forbids_home_storage(manifest): + assert manifest.raw["storage"]["mounted_storage_only"] is True + assert "/home" in manifest.raw["storage"]["forbidden_path_prefixes"] + + +# -------------------------------------------------------------------------- +# Identity: what the manifest must reject +# -------------------------------------------------------------------------- + + +def test_a_changed_source_revision_is_rejected(manifest, snapshot, manifest_doc): + manifest_doc["source_model"]["revision"] = "0" * 40 + swapped = parse_target_manifest(manifest_doc) + + with pytest.raises(GlmTargetError, match="does not match the locked alpha revision"): + require_pinned_target( + swapped, + snapshot, + expected_source_revision=SOURCE_REVISION, + expected_gguf_revision=GGUF_REVISION, + ) + + +def test_a_changed_gguf_revision_is_rejected(manifest, snapshot, manifest_doc): + manifest_doc["gguf_artifact"]["revision"] = "1" * 40 + swapped = parse_target_manifest(manifest_doc) + + with pytest.raises(GlmTargetError, match="does not match the locked alpha revision"): + require_pinned_target( + swapped, + snapshot, + expected_source_revision=SOURCE_REVISION, + expected_gguf_revision=GGUF_REVISION, + ) + + +def test_the_pinned_target_passes_its_own_revision_check(manifest, snapshot): + require_pinned_target( + manifest, + snapshot, + expected_source_revision=SOURCE_REVISION, + expected_gguf_revision=GGUF_REVISION, + ) + + +def test_config_metadata_from_a_different_revision_than_the_weights_is_rejected( + manifest, snapshot_doc +): + snapshot_doc["source_revision"] = "2" * 40 + drifted = parse_architecture_snapshot(snapshot_doc) + + with pytest.raises(GlmTargetError, match="must come from one revision"): + require_pinned_target( + manifest, + drifted, + expected_source_revision=SOURCE_REVISION, + expected_gguf_revision=GGUF_REVISION, + ) + + +def test_a_branch_name_is_not_an_acceptable_revision_pin(manifest_doc): + manifest_doc["gguf_artifact"]["revision"] = "main" + + with pytest.raises(GlmTargetError, match="not an immutable pin"): + parse_target_manifest(manifest_doc) + + +def test_a_missing_shard_is_rejected(manifest_doc): + manifest_doc["gguf_artifact"]["shards"].pop() + + with pytest.raises(GlmTargetError, match="exactly 6 shards"): + parse_target_manifest(manifest_doc) + + +def test_a_shard_replaced_by_a_duplicate_of_another_is_rejected(manifest_doc): + shards = manifest_doc["gguf_artifact"]["shards"] + # Keep the count at six and the byte total consistent, but drop shard 6's + # identity — the shape a lazy "just re-download it" repair takes. + shards[5]["index"] = 5 + + with pytest.raises(GlmTargetError, match="duplicate shard index"): + parse_target_manifest(manifest_doc) + + +def test_two_shards_claiming_the_same_content_digest_are_rejected(manifest_doc): + shards = manifest_doc["gguf_artifact"]["shards"] + shards[5]["sha256"] = shards[4]["sha256"] + + with pytest.raises(GlmTargetError, match="same content digest"): + parse_target_manifest(manifest_doc) + + +def test_an_inconsistent_aggregate_byte_total_is_rejected(manifest_doc): + manifest_doc["gguf_artifact"]["total_bytes"] = TOTAL_BYTES - 1 + + with pytest.raises(GlmTargetError, match="not self-consistent"): + parse_target_manifest(manifest_doc) + + +def test_a_shard_size_edited_to_make_the_model_look_smaller_is_rejected(manifest_doc): + # The aggregate is now internally inconsistent, which is exactly the tell. + manifest_doc["gguf_artifact"]["shards"][2]["size_bytes"] = 1_000_000 + + with pytest.raises(GlmTargetError, match="not self-consistent"): + parse_target_manifest(manifest_doc) + + +def test_swapping_in_a_different_quantization_is_rejected(manifest_doc): + manifest_doc["alpha_quantization"] = "UD-IQ1_M" + + with pytest.raises(GlmTargetError, match="requires a human contract change"): + parse_target_manifest(manifest_doc) + + +def test_a_truncated_sha256_is_rejected(manifest_doc): + manifest_doc["gguf_artifact"]["shards"][0]["sha256"] = "abc123" + + with pytest.raises(GlmTargetError, match="64-character hex SHA-256"): + parse_target_manifest(manifest_doc) + + +# -------------------------------------------------------------------------- +# Architecture snapshot +# -------------------------------------------------------------------------- + + +def test_snapshot_captures_the_architecture_critical_metadata(snapshot): + assert snapshot["model_type"] == "glm_moe_dsa" + assert snapshot["num_hidden_layers"] == 78 + assert snapshot["num_nextn_predict_layers"] == 1 + assert snapshot["total_artifact_layers"] == 79 + assert snapshot["dense_layers"] == 3 + assert snapshot["sparse_moe_layers"] == 75 + assert snapshot["hidden_size"] == 6144 + assert snapshot["n_routed_experts"] == 256 + assert snapshot["num_experts_per_tok"] == 8 + assert snapshot["n_shared_experts"] == 1 + assert snapshot["index_topk"] == 2048 + assert snapshot["index_head_dim"] == 128 + assert snapshot["max_position_embeddings"] == 1_048_576 + assert snapshot["vocab_size"] == 154_880 + + +def test_snapshot_records_indexshare_roles_for_every_layer(snapshot): + assert snapshot["indexer_full_layers"] == 21 + assert snapshot["indexer_shared_layers"] == 57 + assert snapshot["indexer_full_layers"] + snapshot["indexer_shared_layers"] == 78 + + +def test_mla_cache_width_is_derived_not_asserted(snapshot): + assert snapshot["kv_lora_rank"] == 512 + assert snapshot["qk_rope_head_dim"] == 64 + assert snapshot["mla_cached_values_per_token_per_layer"] == 576 + + +def test_snapshot_hashes_the_config_and_chat_template_bytes(snapshot): + assert len(snapshot.file_sha256("config.json")) == 64 + assert len(snapshot.file_sha256("chat_template.jinja")) == 64 + assert len(snapshot.file_sha256("tokenizer_config.json")) == 64 + assert len(snapshot["indexer_types_sha256"]) == 64 + assert len(snapshot.digest) == 64 + + +def test_reasoning_effort_max_is_locked_as_an_observable_rendered_marker(snapshot): + reasoning = snapshot.reasoning_effort + assert reasoning["alpha_mode"] == "max" + assert reasoning["rendered_marker"] == "<|system|>Reasoning Effort: Max" + # The template's only non-max level is 'high'; everything else renders Max. So + # "the request carried reasoning_effort=max" proves nothing on its own. + assert reasoning["default_is_max"] is True + + +def test_folding_the_nextn_layer_into_the_backbone_is_rejected(snapshot_doc): + snapshot_doc["architecture"]["total_artifact_layers"] = 78 + + with pytest.raises(GlmTargetError, match="NextN layer must be counted"): + parse_architecture_snapshot(snapshot_doc) + + +def test_indexshare_roles_that_do_not_cover_every_layer_are_rejected(snapshot_doc): + snapshot_doc["architecture"]["indexer_full_layers"] = 20 + + with pytest.raises(GlmTargetError, match="exactly one IndexShare role"): + parse_architecture_snapshot(snapshot_doc) + + +def test_a_route_with_no_full_indexer_producer_is_rejected(snapshot_doc): + snapshot_doc["architecture"]["indexer_full_layers"] = 0 + snapshot_doc["architecture"]["indexer_shared_layers"] = 78 + + with pytest.raises(GlmTargetError, match="no index for its Shared consumers"): + parse_architecture_snapshot(snapshot_doc) + + +def test_a_contradictory_mla_width_is_rejected(snapshot_doc): + snapshot_doc["architecture"]["mla_cached_values_per_token_per_layer"] = 512 + + with pytest.raises(GlmTargetError, match="kv_lora_rank"): + parse_architecture_snapshot(snapshot_doc) + + +def test_a_snapshot_missing_an_architecture_critical_field_is_rejected(snapshot_doc): + del snapshot_doc["architecture"]["n_routed_experts"] + + with pytest.raises(GlmTargetError, match="architecture-critical field"): + parse_architecture_snapshot(snapshot_doc) + + +def test_a_snapshot_that_drops_reasoning_effort_max_is_rejected(snapshot_doc): + snapshot_doc["reasoning_effort"]["alpha_mode"] = "high" + + with pytest.raises(GlmTargetError, match="does not lock reasoning_effort=max"): + parse_architecture_snapshot(snapshot_doc) + + +# -------------------------------------------------------------------------- +# KV planning +# -------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + ("context", "mla_only", "optimized", "conservative", "conservative_f16"), + [ + (16_384, 0.73, 0.77, 0.89, 1.68), + (131_072, 5.83, 6.18, 7.12, 13.41), + (1_048_576, 46.62, 49.41, 56.98, 107.25), + ], +) +def test_kv_planner_reproduces_the_published_roadmap_table( + snapshot, context, mla_only, optimized, conservative, conservative_f16 +): + def gib(**kwargs): + return kv_bytes(snapshot, context_tokens=context, concurrency=1, **kwargs) / GIB + + assert round(gib(include_indexer=False), 2) == mla_only + assert round(gib(indexer_layout="optimized"), 2) == optimized + assert round(gib(indexer_layout="conservative"), 2) == conservative + assert round(gib(indexer_layout="conservative", dtype="F16"), 2) == conservative_f16 + + +def test_alpha_budgets_the_conservative_indexer_layout(snapshot): + """Alpha must budget the implementation it may actually get, not the ideal one.""" + optimized = kv_bytes(snapshot, indexer_layout="optimized") + conservative = kv_bytes(snapshot, indexer_layout="conservative") + + assert conservative > optimized + assert kv_bytes(snapshot) == conservative, "the default must be the conservative layout" + + +def test_kv_scales_with_concurrency(snapshot): + assert kv_bytes(snapshot, concurrency=2) == 2 * kv_bytes(snapshot, concurrency=1) + + +def test_an_unlocked_kv_dtype_is_rejected(snapshot): + with pytest.raises(ResourcePlanError, match="alpha locks Q8_0"): + kv_bytes(snapshot, dtype="Q4_0") + + +@pytest.mark.parametrize("bad", [0, -1, True, 1.5]) +def test_kv_rejects_non_positive_or_non_integer_dimensions(snapshot, bad): + with pytest.raises(ResourcePlanError, match="positive integers"): + kv_bytes(snapshot, context_tokens=bad) + + +# -------------------------------------------------------------------------- +# Memory accounting: unified memory is one pool +# -------------------------------------------------------------------------- + + +def test_unified_memory_is_counted_once(): + node = NodeMemory.from_host("strix-halo", system_ram_gib=128.0, unified=True) + + assert node.physical_usable_gib == 128.0, "integrated-GPU memory is not extra memory" + + +def test_adding_integrated_gpu_memory_to_system_ram_is_rejected(): + with pytest.raises(ResourcePlanError, match="double-counts one pool"): + NodeMemory.from_host( + "strix-halo", + system_ram_gib=128.0, + gpu_memory_gib=96.0, + unified=True, + ) + + +def test_a_discrete_gpu_does_add_its_own_memory(): + node = NodeMemory.from_host( + "workstation", system_ram_gib=64.0, gpu_memory_gib=24.0, unified=False + ) + + assert node.physical_usable_gib == 88.0 + + +def test_the_same_machine_counted_twice_in_a_route_is_rejected(manifest, snapshot): + twin = [ + NodeMemory.from_host("box-a", system_ram_gib=128.0, unified=True), + NodeMemory.from_host("box-a", system_ram_gib=128.0, unified=True), + ] + + with pytest.raises(ResourcePlanError, match="counted twice"): + plan_route(manifest, snapshot, twin) + + +@pytest.mark.parametrize("bad", [float("nan"), float("inf"), -1.0, True]) +def test_node_memory_rejects_non_finite_or_invalid_capacity(bad): + with pytest.raises(ResourcePlanError, match="finite positive"): + NodeMemory(name="bad-host", physical_usable_gib=bad, unified=True) + + +# -------------------------------------------------------------------------- +# Reserve and topology +# -------------------------------------------------------------------------- + + +def test_the_reserve_floor_binds_on_small_nodes(): + small = NodeMemory(name="32", physical_usable_gib=32.0, unified=True) + + assert small.reserve_gib == RESERVE_FLOOR_GIB == 8.0 # 20% of 32 is only 6.4 + assert small.placement_budget_gib == 24.0 + + +def test_the_reserve_fraction_binds_on_large_nodes(): + large = NodeMemory(name="128", physical_usable_gib=128.0, unified=True) + + assert large.reserve_gib == 25.6 # 20% of 128 clears the 8 GiB floor + assert large.placement_budget_gib == 102.4 + + +@pytest.mark.parametrize( + ("tier", "reserve", "budget", "arithmetic_minimum", "recommended"), + [ + (32.0, 8.0, 24.0, 9, 10), + (48.0, 9.6, 38.4, 6, 6), + (64.0, 12.8, 51.2, 4, 5), + (96.0, 19.2, 76.8, 3, 3), + (128.0, 25.6, 102.4, 2, 3), + ], +) +def test_topology_planner_reproduces_the_published_tier_table( + manifest, snapshot, tier, reserve, budget, arithmetic_minimum, recommended +): + plan = plan_topology(manifest, snapshot, physical_usable_gib=tier) + + assert round(plan.reserve_gib, 2) == reserve + assert round(plan.placement_budget_gib, 2) == budget + assert plan.arithmetic_minimum_nodes == arithmetic_minimum + assert plan.recommended_nodes == recommended + assert round(plan.weight_gib, 3) == 201.832 + assert round(plan.kv_gib, 2) == 0.89 # 16K, concurrency 1, Q8_0, conservative DSA + + +def test_the_recommended_topologies_are_five_by_64_or_three_by_96_or_128(manifest, snapshot): + assert plan_topology(manifest, snapshot, physical_usable_gib=64.0).recommended_nodes == 5 + assert plan_topology(manifest, snapshot, physical_usable_gib=96.0).recommended_nodes == 3 + assert plan_topology(manifest, snapshot, physical_usable_gib=128.0).recommended_nodes == 3 + + +def test_two_by_128_is_an_arithmetic_minimum_not_a_recommendation(manifest, snapshot): + plan = plan_topology(manifest, snapshot, physical_usable_gib=128.0) + + assert plan.arithmetic_minimum_nodes == 2 + assert plan.recommended_nodes == 3 + assert not plan.is_arithmetic_minimum_topology, ( + "2x128 GiB leaves no room for endpoint/tensor imbalance; it is a fit probe that " + "requires measured placement evidence, not the alpha recommendation" + ) + + +@pytest.mark.parametrize("bad", [0.9, float("nan"), float("inf"), True]) +def test_an_invalid_placement_imbalance_is_rejected(manifest, snapshot, bad): + with pytest.raises(ResourcePlanError, match="less than an equal share"): + plan_topology(manifest, snapshot, physical_usable_gib=64.0, imbalance_factor=bad) + + +# -------------------------------------------------------------------------- +# Route fit +# -------------------------------------------------------------------------- + + +def test_the_recommended_five_by_64_route_fits(manifest, snapshot): + nodes = [ + NodeMemory.from_host(f"node-{i}", system_ram_gib=64.0, unified=True) for i in range(5) + ] + + fit = plan_route(manifest, snapshot, nodes) + + assert fit.fits + assert fit.meets_hard_fit_floor + assert fit.no_single_node_can_admit_target + assert fit.reasons == () + + +def test_224_gib_aggregate_is_a_hard_fit_floor_not_an_operational_envelope(manifest, snapshot): + """Exactly 224 GiB of aggregate memory clears the floor and still does not fit. + + This is the whole reason the roadmap calls 224 GiB an *experimental hard-fit + floor*. It is the number you get by ignoring the per-node reserve — and once + the reserve is applied, the weights alone no longer have anywhere to go. + """ + nodes = [ + NodeMemory.from_host(f"node-{i}", system_ram_gib=112.0, unified=True) for i in range(2) + ] + + fit = plan_route(manifest, snapshot, nodes) + + assert fit.aggregate_usable_gib == AGGREGATE_HARD_FIT_FLOOR_GIB == 224.0 + assert fit.meets_hard_fit_floor + assert not fit.fits, "224 GiB aggregate does not fit the target once reserves are honoured" + assert any("below the" in reason for reason in fit.reasons) + + +def test_a_route_too_small_for_the_target_does_not_fit(manifest, snapshot): + nodes = [ + NodeMemory.from_host(f"node-{i}", system_ram_gib=64.0, unified=True) for i in range(3) + ] + + fit = plan_route(manifest, snapshot, nodes) + + assert not fit.fits + assert not fit.meets_hard_fit_floor + + +def test_a_route_where_one_node_could_hold_everything_is_not_distributed_alpha( + manifest, snapshot +): + nodes = [ + NodeMemory.from_host(f"node-{i}", system_ram_gib=512.0, unified=True) for i in range(2) + ] + + fit = plan_route(manifest, snapshot, nodes) + + assert fit.fits + assert not fit.no_single_node_can_admit_target + assert any("single-host run" in reason for reason in fit.reasons) + + +def test_a_single_node_is_not_a_route(manifest, snapshot): + with pytest.raises(ResourcePlanError, match="at least two physical nodes"): + plan_route( + manifest, + snapshot, + [NodeMemory.from_host("solo", system_ram_gib=512.0, unified=True)], + ) + + +# -------------------------------------------------------------------------- +# Seams and network +# -------------------------------------------------------------------------- + + +def test_seam_bytes_match_the_published_activation_arithmetic(snapshot): + plan = plan_seams(snapshot, node_count=4, context_tokens=16_384) + + assert plan.seam_count == 3, "four nodes imply three serial seams" + assert plan.bytes_per_token_per_seam == 12_288 # 6144 x bf16 + assert plan.prefill_bytes_per_seam == 192 * 1024**2 # 192 MiB + assert plan.decode_bytes_per_seam_per_token == 12 * 1024 # 12 KiB + assert plan.dsa_sideband_bytes_per_query == 8 * 1024 # 2048 int32 = 8 KiB + + +def test_2_5_gbe_is_the_alpha_minimum_and_10_gbe_is_recommended(snapshot): + gigabit = plan_seams(snapshot, node_count=3, link_rate_gbps=1.0) + minimum = plan_seams(snapshot, node_count=3, link_rate_gbps=MIN_LINK_RATE_GBPS) + recommended = plan_seams(snapshot, node_count=3, link_rate_gbps=RECOMMENDED_LINK_RATE_GBPS) + + assert not gigabit.meets_alpha_minimum, "1 GbE is fit-only evidence, not an alpha route" + assert minimum.meets_alpha_minimum + assert not minimum.is_recommended_link + assert recommended.meets_alpha_minimum + assert recommended.is_recommended_link + + +def test_serial_seam_latency_is_modelled_separately_from_bandwidth(snapshot): + """Decode moves 12 KiB a token. What it pays is hops, not bytes.""" + fast_link = plan_seams(snapshot, node_count=5, link_rate_gbps=10.0, per_hop_latency_ms=2.0) + slow_link = plan_seams(snapshot, node_count=5, link_rate_gbps=2.5, per_hop_latency_ms=2.0) + + # Quadrupling the link rate barely touches decode: the payload is tiny. + assert fast_link.decode_bandwidth_share_ms_per_token < 0.05 + assert slow_link.decode_bandwidth_share_ms_per_token < 0.2 + + # Latency is unchanged by link rate and scales with the number of serial seams. + assert fast_link.decode_latency_ms_per_token == slow_link.decode_latency_ms_per_token == 8.0 + + # A 10 GbE claim cannot buy back a hop. + assert fast_link.decode_latency_ms_per_token > fast_link.decode_bandwidth_share_ms_per_token + + +def test_adding_a_node_adds_a_serial_seam_to_every_decoded_token(snapshot): + three = plan_seams(snapshot, node_count=3, per_hop_latency_ms=1.0) + five = plan_seams(snapshot, node_count=5, per_hop_latency_ms=1.0) + + assert three.decode_latency_ms_per_token == 2.0 + assert five.decode_latency_ms_per_token == 4.0 + + +@pytest.mark.parametrize("bad", [float("nan"), float("inf"), 0.0, True]) +def test_seam_planner_rejects_invalid_link_telemetry(snapshot, bad): + with pytest.raises(ResourcePlanError, match="finite and positive"): + plan_seams(snapshot, node_count=3, link_rate_gbps=bad) + + +# -------------------------------------------------------------------------- +# The immutable alpha contract +# -------------------------------------------------------------------------- + + +def test_the_packaged_contract_is_sealed_and_verifies(contract): + assert contract.contract_id == ALPHA_CONTRACT_ID + assert contract.contract_version == 1 + assert contract.locked_by == "DGR-017" + assert contract.raw["locked_before_target_execution"] is True + assert contract.digest == compute_contract_digest(contract.raw) + + +def test_the_contract_locks_the_same_target_as_the_manifest(contract, manifest, snapshot): + assert contract.target["source_revision"] == manifest.source_revision + assert contract.target["gguf_revision"] == manifest.gguf_revision + assert contract.target["quantization"] == manifest.quantization + assert contract.target["total_bytes"] == manifest.total_bytes + assert contract.target["target_manifest_sha256"] == manifest.digest + assert contract.target["architecture_snapshot_sha256"] == snapshot.digest + assert contract.target["reasoning_effort"] == "max" + + +def test_the_packaged_target_documents_are_cross_bound_to_the_contract(): + contract, manifest, snapshot = load_locked_target() + assert contract.target["target_manifest_sha256"] == manifest.digest + assert contract.target["architecture_snapshot_sha256"] == snapshot.digest + + +def test_coordinated_shard_hash_substitution_is_rejected_by_contract( + contract, manifest_doc, snapshot +): + manifest_doc["gguf_artifact"]["shards"][0]["sha256"] = "0" * 64 + substituted = parse_target_manifest(manifest_doc) + + with pytest.raises(AlphaContractError, match="target_manifest_sha256"): + require_contract_target(contract, substituted, snapshot) + + +def test_internally_consistent_architecture_substitution_is_rejected_by_contract( + contract, manifest, snapshot_doc +): + snapshot_doc["architecture"]["hidden_size"] = 8192 + substituted = parse_architecture_snapshot(snapshot_doc) + + with pytest.raises(AlphaContractError, match="architecture_snapshot_sha256"): + require_contract_target(contract, manifest, substituted) + + +def test_contract_id_cannot_be_changed_even_when_resealed(contract_doc): + contract_doc["contract_id"] = "glm-5.2-max-alpha/v2" + contract_doc = seal_contract(contract_doc) + + with pytest.raises(AlphaContractError, match="contract_id"): + parse_alpha_contract(contract_doc) + + +def test_max_reasoning_mode_cannot_be_changed_even_when_resealed(contract_doc): + contract_doc["target"]["reasoning_effort"] = "high" + contract_doc = seal_contract(contract_doc) + + with pytest.raises(AlphaContractError, match="reasoning_effort='max'"): + parse_alpha_contract(contract_doc) + + +@pytest.mark.parametrize("field", ["schema_version", "contract_version"]) +def test_boolean_contract_versions_are_rejected_even_when_resealed(contract_doc, field): + contract_doc[field] = True + contract_doc = seal_contract(contract_doc) + + with pytest.raises(AlphaContractError, match="version"): + parse_alpha_contract(contract_doc) + + +def test_wheel_metadata_includes_nested_glm_alpha_json_resources(): + pyproject = Path(__file__).parents[1] / "packages/node/pyproject.toml" + text = pyproject.read_text(encoding="utf-8") + assert '"meshnet_node.glm_alpha" = ["data/*.json"]' in text + + +def test_the_contract_locks_every_roadmap_acceptance_section(contract): + for section in ( + "identity_and_fit", + "semantic_correctness", + "target_run", + "performance", + "reliability", + "storage", + ): + assert contract.section(section) + + +def test_the_contract_locks_the_roadmap_thresholds(contract): + assert contract.threshold("identity_and_fit", "min_node_reserve_fraction") == 0.20 + assert contract.threshold("identity_and_fit", "min_node_reserve_gib") == 8.0 + assert contract.threshold("identity_and_fit", "aggregate_hard_fit_floor_gib") == 224.0 + assert ( + contract.threshold("identity_and_fit", "aggregate_floor_class") + == "experimental_hard_fit_floor" + ) + assert contract.threshold("identity_and_fit", "forbid_double_counted_unified_memory") is True + + assert contract.threshold("semantic_correctness", "min_greedy_token_agreement") == 0.90 + assert contract.threshold("semantic_correctness", "min_mean_state_cosine_similarity") == 0.999 + assert contract.threshold("semantic_correctness", "dense_attention_fallback_satisfies_alpha") is False + + assert contract.threshold("target_run", "context_tokens") == 16_384 + assert contract.threshold("target_run", "kv_dtype") == "Q8_0" + assert contract.threshold("target_run", "min_link_rate_gbps") == MIN_LINK_RATE_GBPS + assert contract.threshold("target_run", "recommended_link_rate_gbps") == RECOMMENDED_LINK_RATE_GBPS + assert contract.threshold("target_run", "min_output_tokens") == 512 + + assert contract.threshold("performance", "min_median_decode_tokens_per_second") == 0.5 + assert contract.threshold("performance", "max_ttft_seconds_at_4096_prompt") == 600 + assert contract.threshold("performance", "quality_pass_with_speed_fail_verdict") == "stop" + + assert contract.threshold("reliability", "synthetic_workers_satisfy_alpha") is False + assert contract.threshold("storage", "forbidden_path_prefixes") == ["/home"] + + +def test_the_contract_offers_only_alpha_or_stop(contract): + assert sorted(contract.verdicts) == ["alpha", "stop"] + + +def test_lowering_the_speed_floor_after_seeing_a_result_is_rejected(contract_doc): + """The exact move the contract exists to prevent.""" + contract_doc["performance"]["min_median_decode_tokens_per_second"] = 0.05 + + with pytest.raises(AlphaContractError, match="modified since it was locked"): + parse_alpha_contract(contract_doc) + + +def test_relabelling_a_speed_failure_as_a_pass_is_rejected(contract_doc): + contract_doc["performance"]["quality_pass_with_speed_fail_verdict"] = "alpha" + + with pytest.raises(AlphaContractError, match="modified since it was locked"): + parse_alpha_contract(contract_doc) + + +def test_admitting_the_dense_attention_fallback_after_the_fact_is_rejected(contract_doc): + contract_doc["semantic_correctness"]["dense_attention_fallback_satisfies_alpha"] = True + + with pytest.raises(AlphaContractError, match="modified since it was locked"): + parse_alpha_contract(contract_doc) + + +def test_relaxing_the_per_node_reserve_after_the_fact_is_rejected(contract_doc): + contract_doc["identity_and_fit"]["min_node_reserve_gib"] = 1.0 + + with pytest.raises(AlphaContractError, match="modified since it was locked"): + parse_alpha_contract(contract_doc) + + +def test_re_pointing_the_contract_at_a_different_artifact_is_rejected(contract_doc): + contract_doc["target"]["gguf_revision"] = "9" * 40 + + with pytest.raises(AlphaContractError, match="modified since it was locked"): + parse_alpha_contract(contract_doc) + + +def test_an_unsealed_contract_is_rejected(contract_doc): + del contract_doc["contract_sha256"] + + with pytest.raises(AlphaContractError, match="cannot prove it predates"): + parse_alpha_contract(contract_doc) + + +def test_a_contract_not_locked_before_execution_is_rejected(contract_doc): + contract_doc["locked_before_target_execution"] = False + contract_doc = seal_contract(contract_doc) + + with pytest.raises(AlphaContractError, match="not a contract"): + parse_alpha_contract(contract_doc) + + +def test_a_third_verdict_is_rejected(contract_doc): + contract_doc["verdicts"] = ["alpha", "stop", "partial"] + contract_doc = seal_contract(contract_doc) + + with pytest.raises(AlphaContractError, match="third outcome"): + parse_alpha_contract(contract_doc) + + +def test_a_dropped_acceptance_section_is_rejected(contract_doc): + del contract_doc["reliability"] + contract_doc = seal_contract(contract_doc) + + with pytest.raises(AlphaContractError, match="missing locked acceptance section"): + parse_alpha_contract(contract_doc) + + +def test_resealing_a_mutated_contract_changes_its_digest(contract, contract_doc): + """Tamper-evidence, not tamper-proofing: a rewrite is legal but never silent.""" + contract_doc["performance"]["min_median_decode_tokens_per_second"] = 0.05 + resealed = seal_contract(contract_doc) + + # It parses — nothing in-repo can stop a determined rewrite of file plus digest. + reparsed = parse_alpha_contract(resealed) + + # But the digest moved, so the change is a visible diff on a file whose entire + # purpose is to not change, and the contract_id still claims to be v1. + assert reparsed.digest != contract.digest + + +def test_an_unknown_threshold_cannot_be_invented_at_read_time(contract): + with pytest.raises(AlphaContractError, match="is not locked"): + contract.threshold("performance", "min_decode_tokens_per_second_v2") + + +# -------------------------------------------------------------------------- +# The tests themselves stay offline +# -------------------------------------------------------------------------- + + +def test_the_packaged_data_files_are_valid_json_and_load_offline(manifest, snapshot, contract): + """No network, no model, no GPU: the whole target contract is reviewable offline.""" + assert json.dumps(manifest.to_dict()) + assert json.dumps(snapshot.to_dict()) + assert json.dumps(contract.to_dict()) + assert len(manifest.digest) == 64