From 7a6e09ecc40387dc154750300c9c8bc3451aa945 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 20:27:51 +0200 Subject: [PATCH 01/41] docs: reconcile Qwen affine release baseline --- AGENTS.md | 6 +-- CONTRIBUTING.md | 19 +++++++ QA_BEFORE_RELEASES.md | 31 +++++++++--- docs/ENGINE_REFERENCE.md | 26 +++++----- .../0004-qwen-metal-hardware-memory-policy.md | 11 +++-- docs/contracts/RUNTIME_SUPPORT.md | 10 +++- docs/expert-major-v2-roadmap.md | 17 ++++--- docs/qwen-expert-major-store.md | 6 +++ tests/qwen/README.md | 17 ++++--- tools/context_audit.py | 49 ++++++++++++++++++- tools/context_budget.json | 24 ++++++++- 11 files changed, 171 insertions(+), 45 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 011dfdf3a..9a5e2b575 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,9 +31,9 @@ and resolve the contradiction in the same change. The production path is Apple Metal inference with an embedded ExpertMajor v2 store. DeepSeek V4 and Qwen3.6 have qualified AUTO/resident/SSD modes. GLM 5.2 is SSD-only: normal AUTO resolves to SSD streaming and a resident request is -rejected. The minimum actively qualified host class is Apple Silicon with 64 GB -unified memory. See the runtime support contract and accepted ADRs for exact -boundaries. +rejected. DeepSeek and GLM require at least 64 GiB of unified memory; Qwen has a +separate hardware-aware 16 GiB minimum and necessarily uses SSD at that tier. +See the runtime support contract and accepted ADRs for exact boundaries. CUDA and ROCm are frozen and their backend source is intentionally absent from the active tree. Do not restore or extend it without a new accepted decision; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83d509d99..5e8290f00 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -98,6 +98,25 @@ DS4_TEST_VECTOR_FILE=/path/to/official.vec ./ds4_test --logprob-vectors DS4_TEST_LONG_PROMPT=/path/to/prompt.txt ./ds4_test --long-context ``` +### Artifact publication boundary + +Runtime qualification does not make a local artifact publicly downloadable. +A runtime download target may be enabled only after an immutable repository +revision records the exact filename, byte count, complete output SHA-256, +manifest contract, and compatible runtime commit. Until then, contributor QA +may use an explicitly labelled local candidate, but release and download checks +must report public distribution as unavailable. + +The current Qwen release is +`Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`, 20,808,566,880 +bytes, SHA-256 +`dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. +It is published at immutable repository revision +`7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`, and +`download_model.sh qwen-v2` must pin that exact identity. The older Q4_K_S +object is an incompatible negative fixture, not a runnable fallback. See +[`docs/qwen-expert-major-store.md`](docs/qwen-expert-major-store.md). + CUDA and ROCm are frozen and their backend source and build targets are absent from the active tree. Ordinary changes must not restore them accidentally. A change that intentionally reactivates either backend must restore and pass the diff --git a/QA_BEFORE_RELEASES.md b/QA_BEFORE_RELEASES.md index 7261f8b98..1211e963e 100644 --- a/QA_BEFORE_RELEASES.md +++ b/QA_BEFORE_RELEASES.md @@ -30,12 +30,17 @@ target, or an artifact whose complete output hash is missing. | `DEEPSEEK_V2` | `DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix-DS4-ExpertMajor-v2.gguf`; 86,720,114,272 bytes; SHA-256 `8378080263eb9224f7228d72e2afa4ac3cf74a116023fdec2c596ff228a33e3f` | | `DEEPSEEK_MIXED_V2` | Non-applicable until a mixed-quant DeepSeek ExpertMajor v2 artifact has a publication record with exact filename, bytes, and complete output SHA-256; do not resolve or use this variable before qualification | | `GLM_V2` | `GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf`; 262,147,193,504 bytes; SHA-256 `7f5017e3076e706c78f2a5322b035a9e2f6519c65ff5b6be8b2d91aeff61505d` | -| `QWEN_V2` | `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`; 20,808,566,880 bytes; SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d` | +| `QWEN_V2` | `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`; immutable repository revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`; 20,808,566,880 bytes; SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`; MLX affine4/group-64 routed storage | +| `QWEN_RETIRED_Q4_NEGATIVE` | Rejection-only input: `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf`; 20,808,566,880 bytes; SHA-256 `d7c43a6388ec20e6fe5530850350f96fdb0ac37c5ce36d3e5f92b172c447f56b`; it must fail before inference | Record the test machine by hardware model, unified memory, OS build, and power state in the release evidence. Do not encode local hostnames, addresses, or network routes in this checklist. +The Qwen runtime and downloader paths must both resolve the exact `QWEN_V2` +identity above. The immutable revision, filename, byte count, complete output +SHA-256, manifest contract, and compatible runtime commit are one release gate. + ## 1. Repository And Build Sanity - Start from a clean tree except intentional release notes: @@ -188,13 +193,14 @@ context. Do not add an explicit residency, cache, preload, or ExpertMajor flag. The Qwen path on `main` follows the same repository, build, core-test, and regression rules as the other model paths. Use the verified normalized -`QWEN_V2` artifact above and run the relevant model-backed smoke; +`QWEN_V2` above and run the relevant model-backed smoke; canonical, v1, sidecar, and community GGUFs are not equivalent inputs. - Run `make model-free-test` and `./ds4_test --metal-kernels`. The latter must retain resident/SSD top-8 output equivalence, zero resident cache/`pread` accounting, malformed-route fail-closed behavior, and slab-growth checks. -- Run `./ds4 -m "$QWEN_V2" --ctx 8192` for the normal flag-free AUTO smoke. +- Run `./ds4 -m "$QWEN_V2" --ctx 8192` for the normal flag-free + AUTO smoke. - Run AUTO with the normal flag-free startup command; record both admission plans, their point-in-time inputs, resolved mode, cache tier, configured 321-expert slab target, cache `buffer_allocs`, task physical footprint, and system swap @@ -209,6 +215,10 @@ canonical, v1, sidecar, and community GGUFs are not equivalent inputs. - Resident mode proves complete model mapping and full-tensor Metal execution, not that every mapped GGUF page remained physically resident. Measure the stronger claim separately if it is used in release language. +- Run the model-free ExpertMajor admission fixture for a Qwen storage value of + GGML/Q4, and, when the exact retired file is available, run + `./ds4 -m "$QWEN_RETIRED_Q4_NEGATIVE" --ctx 8192`. Both must reject before + inference; a command that produces tokens is a release blocker. - Physical 16 GiB measurements and normalized-vs-source research comparisons improve hardware and artifact characterization, but are not additional release gates beyond the standard model/backend checks above. Do not claim @@ -352,8 +362,10 @@ The agent is the most stateful component. Test it manually, not only by build. - Test `download_model.sh` in a temporary directory so local weights are not overwritten. -- Verify the `deepseek-v2`, `glm-v2`, and `qwen-v2` targets resolve to the exact - qualified repository and ExpertMajor v2 filename. +- Verify `deepseek-v2`, `glm-v2`, and `qwen-v2` resolve to their exact qualified + repository, immutable revision, and ExpertMajor v2 filename. Qwen must pin + revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02` and must not resolve to the + retired Q4_K_S object. - Treat every `offline-*` target as a converter input, not a runnable artifact. Verify resume and file naming without launching inference from the source. - Verify the script never creates or changes `./ds4flash.gguf`, exposes no @@ -379,8 +391,13 @@ The agent is the most stateful component. Test it manually, not only by build. Do not sign off until: - macOS Metal Flash passed. -- The qualified DeepSeek, GLM, and Qwen artifacts passed their model-backed - lanes with the residency modes defined by `docs/contracts/RUNTIME_SUPPORT.md`. +- The qualified DeepSeek, GLM, and Qwen release artifacts passed their + model-backed lanes with the residency modes defined by + `docs/contracts/RUNTIME_SUPPORT.md`. +- The Qwen immutable publication record and downloader gate matched the same + exact affine artifact used by the runtime lane. +- The retired Qwen Q4_K_S store remained fail-closed and was not presented as a + runnable or downloadable fallback. - CUDA source/tests/build targets were confirmed absent and recorded as frozen; if any were restored, the complete section 6 reactivation gate passed. - ROCm source/tests/build targets were confirmed absent and recorded as frozen; diff --git a/docs/ENGINE_REFERENCE.md b/docs/ENGINE_REFERENCE.md index d196753b2..2f8b85a8f 100644 --- a/docs/ENGINE_REFERENCE.md +++ b/docs/ENGINE_REFERENCE.md @@ -81,10 +81,11 @@ next sections. ## Model Weights -This implementation only works with the ExpertMajor v2 GGUFs published for -this project. It is not a general GGUF loader, and arbitrary Qwen, DeepSeek, -GLM, or community GGUF files will not have the validated embedded store, -tensor layout, quantization mix, or metadata expected by the engine. The 2 bit +This implementation only works with the explicitly qualified ExpertMajor v2 +GGUFs published for this project. It is not a general GGUF loader, and +arbitrary Qwen, DeepSeek, GLM, or community GGUF files will not have the +validated embedded store, tensor layout, quantization mix, or metadata expected +by the engine. The 2 bit DeepSeek quantizations provided here are not a joke: they behave well, work under coding agents, call tools in a reliable way. The 2 bit quants use a very asymmetrical quantization: only the routed MoE @@ -100,15 +101,18 @@ complete output SHA-256 in its publication record: [`deepseek-expert-major-v2.md`](deepseek-expert-major-v2.md). The current canonical mirror is an offline converter source, not the runtime artifact. - [GLM 5.2 DS4 GGUF](https://huggingface.co/andreaborio/GLM-5.2-DS4-GGUF) -- [Qwen3.6-35B-A3B DS4 GGUF](https://huggingface.co/andreaborio/Qwen3.6-35B-A3B-DS4-GGUF) +- [Qwen3.6-35B-A3B DS4 GGUF](https://huggingface.co/andreaborio/Qwen3.6-35B-A3B-DS4-GGUF): + use only the MLX affine4/group-64 artifact identified in + [`qwen-expert-major-store.md`](qwen-expert-major-store.md). The older Q4_K_S + object is incompatible with this runtime. Use the exact artifact, size, and SHA-256 recorded in the family documentation. -`download_model.sh` exposes the three qualified runtime artifacts as explicit -`*-v2` targets. Its clearly named `offline-*` targets download only complete -canonical converter inputs; distributed slices and legacy runtime targets are -not exposed. The script never creates `./ds4flash.gguf`. Convert and verify an -offline source with `gguf-tools/ds4-expert-major.py` before publication or -inference. +`download_model.sh` exposes all three published runtime artifacts as explicit +`*-v2` targets. The clearly named `offline-*` targets download +only complete canonical converter inputs; distributed slices and legacy +runtime targets are not exposed. The script never creates `./ds4flash.gguf`. +Convert and verify an offline source with `gguf-tools/ds4-expert-major.py` +before publication or inference. If you want to regenerate GGUF files or collect a new imatrix, see [gguf-tools/README.md](../gguf-tools/README.md). Those tools are meant for offline diff --git a/docs/adr/0004-qwen-metal-hardware-memory-policy.md b/docs/adr/0004-qwen-metal-hardware-memory-policy.md index bf9e25dc6..5b1a310e3 100644 --- a/docs/adr/0004-qwen-metal-hardware-memory-policy.md +++ b/docs/adr/0004-qwen-metal-hardware-memory-policy.md @@ -44,10 +44,11 @@ AUTO selects resident only when both independent gates pass: An explicit resident request also requires both gates. Failure selects bounded SSD streaming for AUTO and rejects an explicit resident request. The current -Q4_K_S tensor payload alone exceeds 16 GiB, so that profile necessarily uses -SSD. A 24 GiB device is decided from its actual Metal working-set report rather -than an assumed ratio. The 32 GiB profile may use resident for shorter contexts -when current pressure allows it and falls back to SSD otherwise. +19.37 GiB routed tensor payload alone exceeds 16 GiB, so that profile +necessarily uses SSD. A 24 GiB device is decided from its actual Metal +working-set report rather than an assumed ratio. The 32 GiB profile may use +resident for shorter contexts when current pressure allows it and falls back +to SSD otherwise. For Qwen SSD streaming, pageable static weights share the larger ordinary headroom envelope but remain fully charged. While macOS reports normal pressure, @@ -62,7 +63,7 @@ plus the in-flight slot: 3,521 experts for this 40-layer, top-8 model. This keep the previously retained zero-swap tier and rejects the next warm-cache tier, which produced swap in validation. Because the cap is expressed in routing cycles while byte admission uses the artifact's exact per-expert size, it does -not treat Q4 storage as F32. +not treat compact MLX affine4/group-64 storage as F32. ## Consequences diff --git a/docs/contracts/RUNTIME_SUPPORT.md b/docs/contracts/RUNTIME_SUPPORT.md index c2139c9b9..699828d39 100644 --- a/docs/contracts/RUNTIME_SUPPORT.md +++ b/docs/contracts/RUNTIME_SUPPORT.md @@ -15,7 +15,7 @@ and Qwen's lower-memory extension in | Model family | Minimum unified memory | Qualified Metal modes | Release startup | | --- | ---: | --- | --- | | DeepSeek V4 Flash | 64 GiB | AUTO resolving to resident or SSD; explicit resident and SSD according to the artifact gate | `./ds4 -m DEEPSEEK-DS4-ExpertMajor-v2.gguf` | -| Qwen3.6-35B-A3B | 16 GiB | AUTO resolving to resident or SSD; explicit resident and SSD according to the Qwen admission gates | `./ds4 -m QWEN-DS4-ExpertMajor-v2.gguf` | +| Qwen3.6-35B-A3B | 16 GiB | AUTO resolving to resident or SSD; explicit resident and SSD according to the Qwen admission gates | `./ds4 -m Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf` | | GLM 5.2 | 64 GiB | AUTO resolving to SSD streaming only; resident is rejected | `./ds4 -m GLM-DS4-ExpertMajor-v2.gguf --ctx 8192` | All rows require Apple Metal and a validated embedded `ds4.expert_major.v2` @@ -37,6 +37,14 @@ The lower-memory extension is Qwen-specific. Hosts below 64 GiB remain outside the DeepSeek and GLM production contract. Do not infer support for another family from Qwen's successful admission. +Runtime support and public artifact distribution are separate gates. The only +runnable Qwen store is the published 20,808,566,880-byte MLX +affine4/group-64 artifact with SHA-256 +`dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. +`download_model.sh qwen-v2` pins immutable repository revision +`7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`. The older Q4_K_S store is a +rejection fixture, not a downloadable runtime fallback. + ## Model Artifact Admission Production inference accepts the qualified embedded ExpertMajor v2 artifact for diff --git a/docs/expert-major-v2-roadmap.md b/docs/expert-major-v2-roadmap.md index 6d578597c..13e320c2f 100644 --- a/docs/expert-major-v2-roadmap.md +++ b/docs/expert-major-v2-roadmap.md @@ -49,14 +49,15 @@ layout. - **Implemented:** distinct `qwen35moe` family ID, 40-layer fail-closed geometry, generic v2 conversion, logical reconstruction, resident mapping, and SSD translation. -- **Qualified on M5 Pro 64 GiB:** resident output matches the retired v1 - control. The 2K v2/v1/v2 lane measured 318.96/29.54, 320.59/29.59, and - 318.83/29.54 prefill/decode t/s. -- **Correctness:** all three evidence files are byte-identical with SHA-256 - `399504c6ce3d4531ee0f2207702e96e2324c9b5c8dbf98adf47dfb9e64cae54d`; - no new swapout was observed. -- **Release artifact:** 20,808,566,880 bytes, SHA-256 - `d7c43a6388ec20e6fe5530850350f96fdb0ac37c5ce36d3e5f92b172c447f56b`. +- **Qualified candidate:** MLX affine4/group-64 resident and SSD execution on + the hardware lanes recorded in the current benchmark index. +- **Correctness:** the final resident and SSD lane retains deterministic token + and logit comparisons with no new swapout in the recorded qualification. +- **Local candidate, not yet published:** + `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`, + 20,808,566,880 bytes, SHA-256 + `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. + The former Q4_K_S artifact is retained only as a fail-closed negative case. ### DeepSeek V4 diff --git a/docs/qwen-expert-major-store.md b/docs/qwen-expert-major-store.md index e741f4ea4..0198af039 100644 --- a/docs/qwen-expert-major-store.md +++ b/docs/qwen-expert-major-store.md @@ -97,6 +97,12 @@ used for publication. The v2 output is 3,456 bytes larger than its canonical converter input. It does not contain a second copy of the routed weights. +These exact bytes and their matching manifest are published at immutable +repository revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`. +`download_model.sh qwen-v2` pins that revision. The older Q4_K_S object is +incompatible with the current runtime contract and is retained only for +fail-closed testing. + ## Qualification On an M5 Pro with 64 GiB, final resident prefill measured 1,661.18 t/s at 2K, diff --git a/tests/qwen/README.md b/tests/qwen/README.md index 622335133..94e1debfd 100644 --- a/tests/qwen/README.md +++ b/tests/qwen/README.md @@ -109,9 +109,11 @@ Use this exact release identity for every Qwen model-backed gate: | Field | Value | | --- | --- | -| File | `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf` | +| Publication state | Published at immutable revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02` | +| File | `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf` | | Bytes | `20,808,566,880` | -| SHA-256 | `d7c43a6388ec20e6fe5530850350f96fdb0ac37c5ce36d3e5f92b172c447f56b` | +| SHA-256 | `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d` | +| Routed storage | MLX affine4, group size 64 | | Hugging Face | [`andreaborio/Qwen3.6-35B-A3B-DS4-GGUF`](https://huggingface.co/andreaborio/Qwen3.6-35B-A3B-DS4-GGUF) | The v2 file contains one routed-weight copy inside its checksummed embedded @@ -127,7 +129,7 @@ The converter's full byte-identity verifier is the artifact gate: python3 gguf-tools/ds4-expert-major.py inspect CANONICAL-QWEN.gguf python3 gguf-tools/ds4-expert-major.py verify \ CANONICAL-QWEN.gguf \ - Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf + Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf ``` Never use `--skip-verify` for a release artifact. @@ -138,7 +140,7 @@ Resolve `QWEN_V2` to the absolute path whose size and complete SHA-256 match the table above. Normal startup is flag-free AUTO: ```sh -QWEN_V2=/absolute/path/to/Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf +QWEN_V2=/absolute/path/to/Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf ./ds4 -m "$QWEN_V2" --ctx 8192 \ -n 32 --temp 0 \ -p 'Scrivi solo una breve funzione Python che somma due interi.' @@ -179,7 +181,8 @@ SSD mode must allocate the first 321-expert slab within its admitted budget and must not introduce swap. Keep warm page-cache evidence separate from cold device-I/O evidence, and never bypass a failed admission to obtain a number. -The full publication baseline and interpretation are recorded in -[`2026-07-20-qwen-expert-major-v2.md`](../../docs/benchmarks/2026-07-20-qwen-expert-major-v2.md). -That dated record is evidence; `QA_BEFORE_RELEASES.md` remains authoritative for +The current affine qualification and interpretation are recorded in +[`2026-07-21-qwen-unified-affine-auto-ssd.md`](../../docs/benchmarks/2026-07-21-qwen-unified-affine-auto-ssd.md). +That dated record is qualification evidence; the immutable repository revision +is the publication manifest. `QA_BEFORE_RELEASES.md` remains authoritative for the complete release gate. diff --git a/tools/context_audit.py b/tools/context_audit.py index eed713241..f51b0a40a 100644 --- a/tools/context_audit.py +++ b/tools/context_audit.py @@ -119,6 +119,7 @@ def source_metrics(files: list[Path]) -> tuple[dict[str, int], dict[str, list[st "largest_source_lines": largest_lines, } details = { + "direct_ds4_env_names": sorted(env_names), "implementation_includes": implementation_includes, "maybe_unused_markers": maybe_unused, "personal_absolute_path_hits": personal_paths, @@ -292,11 +293,55 @@ def main() -> int: issues.append(f"missing context budget: {args.budget}") else: budget = json.loads(args.budget.read_text(encoding="utf-8")) - for name, maximum in budget.items(): + for name, policy in budget.items(): actual = metrics.get(name) if actual is None: issues.append(f"unknown context budget metric: {name}") - elif actual > maximum: + continue + + maximum = policy + if isinstance(policy, dict): + maximum = policy.get("maximum") + baseline = policy.get("baseline") + reason = policy.get("reason") + classified = policy.get("accepted_additions") + if not isinstance(maximum, int): + issues.append(f"{name} context budget needs an integer maximum") + continue + if not isinstance(baseline, int): + issues.append(f"{name} classified context budget needs an integer baseline") + if not isinstance(reason, str) or not reason.strip(): + issues.append(f"{name} classified context budget needs a reason") + if not isinstance(classified, dict) or not classified: + issues.append(f"{name} classified context budget needs accepted additions") + else: + additions: list[str] = [] + for category, names in classified.items(): + if not isinstance(category, str) or not isinstance(names, list) or not all( + isinstance(item, str) for item in names + ): + issues.append(f"{name} has an invalid accepted-addition category") + continue + additions.extend(names) + if len(additions) != len(set(additions)): + issues.append(f"{name} accepted additions contain duplicates") + if isinstance(baseline, int) and baseline + len(set(additions)) != maximum: + issues.append( + f"{name} classified budget does not match baseline plus accepted additions" + ) + if name == "direct_ds4_env_names": + observed = set(details["direct_ds4_env_names"]) + missing = sorted(set(additions) - observed) + if missing: + issues.append( + f"{name} accepted additions are not direct source reads: " + + ", ".join(missing) + ) + elif not isinstance(policy, int): + issues.append(f"{name} context budget must be an integer or policy object") + continue + + if actual > maximum: issues.append(f"{name} increased: {actual} > budget {maximum}") print(json.dumps(metrics, indent=2, sort_keys=True)) diff --git a/tools/context_budget.json b/tools/context_budget.json index 98d912cef..8a7ddf132 100644 --- a/tools/context_budget.json +++ b/tools/context_budget.json @@ -1,5 +1,27 @@ { - "direct_ds4_env_names": 334, + "direct_ds4_env_names": { + "maximum": 334, + "baseline": 324, + "reason": "Accepted Qwen Metal benchmark, diagnostic-bisect, and profiling controls added with the affine runtime; normal release startup remains flag-free and every later direct name still exceeds this ceiling.", + "accepted_additions": { + "diagnostic_bisect": [ + "DS4_METAL_DISABLE_F32_NAX_PREFILL", + "DS4_METAL_DISABLE_MLX_AFFINE_NAX", + "DS4_METAL_DISABLE_MLX_AFFINE_STREAM_MM_ID", + "DS4_METAL_DISABLE_QWEN_FLASH_PREFILL", + "DS4_METAL_DISABLE_QWEN_GDN_PRENORMALIZE" + ], + "benchmark_canary": [ + "DS4_METAL_DISABLE_QWEN_GDN_PREEXP_DECAY", + "DS4_METAL_ENABLE_F32_NAX_PREFILL", + "DS4_METAL_ENABLE_QWEN_GDN_PREEXP_DECAY" + ], + "profiling": [ + "DS4_QWEN_METAL_PREFILL_DETAIL_PROFILE", + "DS4_QWEN_METAL_PREFILL_STAGE_PROFILE" + ] + } + }, "implementation_includes": 3, "maybe_unused_markers": 50, "personal_absolute_path_hits": 0, From f0c8502648adb63e2d18062360cfabba393cd508 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 20:24:09 +0200 Subject: [PATCH 02/41] runtime: expose machine-readable capabilities --- Makefile | 27 ++++-- QA_BEFORE_RELEASES.md | 4 + README.md | 1 + docs/ENGINE_REFERENCE.md | 30 +++++++ docs/architecture/CODEMAP.md | 3 +- ds4.h | 9 ++ ds4_agent.c | 4 + ds4_bench.c | 4 + ds4_build.c | 100 ++++++++++++++++++++++ ds4_cli.c | 4 + ds4_eval.c | 4 + ds4_help.c | 1 + ds4_server.c | 4 + tests/test_build_isolation.sh | 4 + tests/test_capabilities.py | 155 ++++++++++++++++++++++++++++++++++ 15 files changed, 344 insertions(+), 10 deletions(-) create mode 100644 tests/test_capabilities.py diff --git a/Makefile b/Makefile index aed00f571..9eb1f790b 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ PROGRAMS := ds4 ds4-server ds4-bench ds4-eval ds4-agent metal build-isolation-test q4k-dot-test qwen-metadata-test \ qwen-reference-test qwen-unicode-test qwen-tokenizer-test \ qwen-expert-group-test expert-store-test metal-ssd-profile-test \ - download-model-test $(PROGRAMS) ds4_test ds4_agent_test + download-model-test capabilities-test $(PROGRAMS) ds4_test ds4_agent_test download-model-test: tests/test_download_model.sh download_model.sh sh tests/test_download_model.sh @@ -176,11 +176,11 @@ $(CPU_OBJDIR)/%.o: %.c # Build provenance is intentionally refreshed on every invocation. Keeping it # in this tiny object prevents a clean/dirty transition from forcing the giant # engine translation unit to rebuild while ensuring --build-info is truthful. -$(METAL_OBJDIR)/ds4_build.o: ds4_build.c ds4.h FORCE +$(METAL_OBJDIR)/ds4_build.o: ds4_build.c ds4.h ds4_expert_store.h FORCE @mkdir -p "$(@D)" $(CC) $(CFLAGS) $(DEPFLAGS) -c -o $@ $< -$(CPU_OBJDIR)/ds4_build.o: ds4_build.c ds4.h FORCE +$(CPU_OBJDIR)/ds4_build.o: ds4_build.c ds4.h ds4_expert_store.h FORCE @mkdir -p "$(@D)" $(CC) $(CFLAGS) -DDS4_NO_GPU $(DEPFLAGS) -c -o $@ $< @@ -439,6 +439,9 @@ qwen-unicode-test: $(METAL_BINDIR)/test_qwen_unicode qwen-tokenizer-test: $(METAL_BINDIR)/test_qwen_tokenizer $(METAL_BINDIR)/test_qwen_tokenizer +capabilities-test: metal tests/test_capabilities.py + python3 tests/test_capabilities.py --bin-dir $(METAL_BINDIR) --backend metal + model-free-test: metal ds4_test ds4_agent_test $(METAL_BINDIR)/test_q4k_dot \ $(METAL_BINDIR)/test_q4k_top8 \ $(METAL_BINDIR)/test_qwen_session \ @@ -450,7 +453,9 @@ model-free-test: metal ds4_test ds4_agent_test $(METAL_BINDIR)/test_q4k_dot \ $(METAL_BINDIR)/test_qwen_expert_group \ $(METAL_BINDIR)/test_expert_store \ $(METAL_BINDIR)/test_metal_ssd_profile \ - $(METAL_BINDIR)/test_ssd_residency download-model-test + $(METAL_BINDIR)/test_ssd_residency download-model-test \ + tests/test_capabilities.py + python3 tests/test_capabilities.py --bin-dir $(METAL_BINDIR) --backend metal DS4_BIN_DIR=$(METAL_BINDIR) sh tests/test_retired_distributed_flags.sh sh tests/test_benchmark_env_guard.sh $(METAL_BINDIR)/ds4-eval --self-test-extractors @@ -499,7 +504,7 @@ premerge: context-audit doc-links imatrix-dataset-check prompt-fixture-check bui $(MAKE) model-free-test git diff --check -build-isolation-test: tests/test_build_isolation.sh +build-isolation-test: tests/test_build_isolation.sh tests/test_capabilities.py MAKE="$(MAKE)" sh tests/test_build_isolation.sh -include $(wildcard $(METAL_OBJDIR)/*.d $(CPU_OBJDIR)/*.d) @@ -536,7 +541,7 @@ ds4-eval: ds4_eval_cpu.o ds4_help.o $(CPU_CORE_OBJS) ds4-agent: ds4_agent_cpu.o ds4_help.o ds4_web.o ds4_kvstore.o linenoise.o $(CPU_CORE_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -ds4_build_cpu.o: ds4_build.c ds4.h FORCE +ds4_build_cpu.o: ds4_build.c ds4.h ds4_expert_store.h FORCE $(CC) $(CFLAGS) -DDS4_NO_GPU -c -o $@ ds4_build.c ds4_ssd.o: ds4_ssd.c ds4_ssd.h @@ -638,7 +643,8 @@ model-free-test: ds4 ds4_test ds4_agent_test ds4-eval q4k-dot-test \ tests/test_qwen_expert_group \ tests/test_expert_store \ tests/test_metal_ssd_profile \ - tests/test_ssd_residency download-model-test + tests/test_ssd_residency download-model-test tests/test_capabilities.py + python3 tests/test_capabilities.py --bin-dir . --backend cpu sh tests/test_retired_distributed_flags.sh sh tests/test_benchmark_env_guard.sh ./ds4-eval --self-test-extractors @@ -662,6 +668,9 @@ model-free-test: ds4 ds4_test ds4_agent_test ds4-eval q4k-dot-test \ ./tests/test_ssd_residency python3 tests/test_qwen_metadata.py ./ds4 +capabilities-test: $(PROGRAMS) tests/test_capabilities.py + python3 tests/test_capabilities.py --bin-dir . --backend cpu + test: model-free-test ./ds4_test @@ -696,7 +705,7 @@ tests/test_q4k_top8: test_q4k_top8.o ds4_test_core.o ds4_build_cpu.o \ tests/test_qwen_session: tests/test_qwen_session.c ds4.c ds4.h ds4_ssd.h ds4_profile.h \ ds4_gpu.h ds4_qwen.h ds4_qwen_unicode.h \ - ds4_build.c ds4_ssd.c ds4_profile.c ds4_qwen.c \ + ds4_build.c ds4_expert_store.h ds4_ssd.c ds4_profile.c ds4_qwen.c \ ds4_qwen_unicode.c ds4_qwen_unicode_data.inc \ ds4_streaming_hotlist.inc runtime/ds4_glm_graph.inc \ runtime/ds4_deepseek_cache_phase.inc @@ -707,7 +716,7 @@ tests/test_qwen_session: tests/test_qwen_session.c ds4.c ds4.h ds4_ssd.h ds4_pro tests/test_qwen_tokenizer: tests/test_qwen_tokenizer.c ds4.c ds4.h \ ds4_kvstore.c ds4_kvstore.h ds4_ssd.h ds4_profile.c ds4_profile.h ds4_gpu.h ds4_qwen.h \ - ds4_qwen_unicode.h ds4_build.c ds4_ssd.c \ + ds4_qwen_unicode.h ds4_build.c ds4_expert_store.h ds4_ssd.c \ ds4_qwen.c ds4_qwen_unicode.c ds4_qwen_unicode_data.inc \ ds4_streaming_hotlist.inc tests/qwen/qwen36_tokenizer_fixture.inc $(CC) $(CFLAGS) $(QWEN_CFLAGS) -DDS4_NO_GPU \ diff --git a/QA_BEFORE_RELEASES.md b/QA_BEFORE_RELEASES.md index 1211e963e..41069604c 100644 --- a/QA_BEFORE_RELEASES.md +++ b/QA_BEFORE_RELEASES.md @@ -53,6 +53,10 @@ SHA-256, manifest contract, and compatible runtime commit are one release gate. `build/cpu-$(uname -m)/bin` and must not replace the root Metal commands: `make cpu`. - Record `./ds4 --build-info` and the CPU binary's `--build-info` output. +- Run `--capabilities=json` on all five Metal executables and the CPU profile; + validate schema version 1, executable roles, backend identity, model-family + claims, and ExpertMajor wire values with `make capabilities-test` and + `python3 tests/test_capabilities.py --bin-dir build/cpu-$(uname -m)/bin --backend cpu`. - Run whitespace checks before committing: `git diff --check`. - Confirm `./ds4 --help`, `./ds4-server --help`, and `./ds4-agent --help` render diff --git a/README.md b/README.md index 23b8dc1f8..80e9ac9cc 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ cd ds4 make -j8 ./ds4 --build-info +./ds4-server --capabilities=json ./download_model.sh qwen-v2 ./ds4 \ -m gguf/Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf \ diff --git a/docs/ENGINE_REFERENCE.md b/docs/ENGINE_REFERENCE.md index 2f8b85a8f..d9babd5a1 100644 --- a/docs/ENGINE_REFERENCE.md +++ b/docs/ENGINE_REFERENCE.md @@ -137,6 +137,36 @@ On macOS, Metal and CPU objects/binaries live in separate build profiles. `build/cpu-$(uname -m)/bin/ds4` for the CPU-only binary and `./ds4 --build-info` to verify build provenance. +Every executable also accepts `--capabilities=json` without loading a model. +The deterministic schema reports the build revision, compiled backend, +executable role, supported model families, and the ExpertMajor v2 tensor and +storage wire contract. Consumers should reject unknown `schema_version` values +instead of inferring support from source files, binary strings, or visible +product copy. The bare `--capabilities` spelling is intentionally invalid. + +```json +{ + "schema_version": 1, + "engine_id": "ds4", + "build_git_sha": "", + "backend": "metal", + "executable_role": "server", + "model_families": ["deepseek4", "glm-dsa", "qwen35moe"], + "expert_major": { + "version": 2, + "tensor": "ds4.expert_major.v2", + "storage_formats": [ + {"id": "ggml", "wire_value": 0, "group_sizes": []}, + {"id": "mlx-affine4", "wire_value": 1, "group_sizes": [64]} + ] + } +} +``` + +`backend` is `metal` or `cpu`; `executable_role` is `cli`, `server`, `agent`, +`bench`, or `eval`. The normal build identity is the 12-character Git revision, +optionally followed by `-dirty`; non-Git builds may report `unknown`. + Do not rely on the historical `./ds4flash.gguf` symlink for runtime identity. Pass `-m` with an absolute qualified ExpertMajor v2 path and verify its complete published output SHA-256 before inference. Run `./ds4 --help` and diff --git a/docs/architecture/CODEMAP.md b/docs/architecture/CODEMAP.md index 02f94f29d..72d3d6e8b 100644 --- a/docs/architecture/CODEMAP.md +++ b/docs/architecture/CODEMAP.md @@ -21,7 +21,7 @@ RAM/working-set adaptation is isolated in | `ds4_agent.c` | Stateful coding-agent TUI, tools, session commands, and interruption state | | `ds4_bench.c` | Context-frontier prefill/decode benchmark driver | | `ds4_eval.c` | Evaluation driver and extractor checks | -| `ds4_build.c` | Build identity reported by executables | +| `ds4_build.c` | Build identity and the versioned machine-readable capability contract reported by executables | These programs should use the public engine/session API in `ds4.h`. They should not acquire tensor, ExpertMajor, or Metal-kernel ownership. @@ -98,6 +98,7 @@ not validated. | Path | Primary responsibility | | --- | --- | | `tests/` | Model-free, model-backed, kernel, tokenizer, server, and build-isolation regressions | +| `tests/test_capabilities.py` | Exact schema and cross-executable checks for the model-free build/capability contract | | `tests/qwen/` | Qwen fixtures, provenance, reference collectors, and model-specific gates | | `tests/test-vectors/` | Official and local continuation vectors plus provenance | | `gguf-tools/` | Quantization, ExpertMajor conversion, imatrix, and quality-scoring tools | diff --git a/ds4.h b/ds4.h index 30172962b..56b2906db 100644 --- a/ds4.h +++ b/ds4.h @@ -234,11 +234,20 @@ uint64_t ds4_engine_hidden_f32_values(ds4_engine *e); int ds4_engine_model_id(ds4_engine *e); bool ds4_engine_is_glm_dsa(ds4_engine *e); const char *ds4_backend_name(ds4_backend backend); +typedef enum { + DS4_EXECUTABLE_ROLE_CLI, + DS4_EXECUTABLE_ROLE_SERVER, + DS4_EXECUTABLE_ROLE_AGENT, + DS4_EXECUTABLE_ROLE_BENCH, + DS4_EXECUTABLE_ROLE_EVAL, +} ds4_executable_role; const char *ds4_build_backend(void); const char *ds4_build_arch(void); const char *ds4_build_git_sha(void); void ds4_build_info_print(FILE *fp); bool ds4_build_info_requested(int argc, char **argv); +bool ds4_capabilities_requested(int argc, char **argv); +void ds4_capabilities_print(FILE *fp, ds4_executable_role role); bool ds4_think_mode_enabled(ds4_think_mode mode); const char *ds4_think_mode_name(ds4_think_mode mode); const char *ds4_think_max_prefix(void); diff --git a/ds4_agent.c b/ds4_agent.c index fbfe08748..6b9dea9ee 100644 --- a/ds4_agent.c +++ b/ds4_agent.c @@ -10826,6 +10826,10 @@ int main(int argc, char **argv) { ds4_build_info_print(stdout); return 0; } + if (ds4_capabilities_requested(argc, argv)) { + ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_AGENT); + return 0; + } agent_config cfg = parse_options(argc, argv); if (cfg.chdir_path && chdir(cfg.chdir_path) != 0) { fprintf(stderr, "ds4-agent: failed to chdir to %s: %s\n", diff --git a/ds4_bench.c b/ds4_bench.c index 1355e5e62..6325ea910 100644 --- a/ds4_bench.c +++ b/ds4_bench.c @@ -565,6 +565,10 @@ int main(int argc, char **argv) { ds4_build_info_print(stdout); return 0; } + if (ds4_capabilities_requested(argc, argv)) { + ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_BENCH); + return 0; + } bench_config cfg = parse_options(argc, argv); ds4_engine_options opt = { diff --git a/ds4_build.c b/ds4_build.c index 85f331564..a99fe5b36 100644 --- a/ds4_build.c +++ b/ds4_build.c @@ -1,4 +1,5 @@ #include "ds4.h" +#include "ds4_expert_store.h" #include @@ -6,6 +7,16 @@ #define DS4_BUILD_GIT_SHA "unknown" #endif +#define DS4_BUILD_STATIC_ASSERT(name, condition) \ + typedef char ds4_build_static_assert_##name[(condition) ? 1 : -1] + +DS4_BUILD_STATIC_ASSERT(expert_store_v2_version, + DS4_EXPERT_STORE_V2_VERSION == 2); +DS4_BUILD_STATIC_ASSERT(expert_store_ggml_wire_value, + DS4_EXPERT_STORE_STORAGE_GGML == 0); +DS4_BUILD_STATIC_ASSERT(expert_store_mlx_affine4_wire_value, + DS4_EXPERT_STORE_STORAGE_MLX_AFFINE4 == 1); + const char *ds4_build_backend(void) { #ifdef DS4_NO_GPU return "cpu"; @@ -46,3 +57,92 @@ bool ds4_build_info_requested(int argc, char **argv) { } return false; } + +bool ds4_capabilities_requested(int argc, char **argv) { + for (int i = 1; i < argc; i++) { + if (argv[i] && strcmp(argv[i], "--capabilities=json") == 0) return true; + } + return false; +} + +static const char *ds4_executable_role_name(ds4_executable_role role) { + switch (role) { + case DS4_EXECUTABLE_ROLE_CLI: + return "cli"; + case DS4_EXECUTABLE_ROLE_SERVER: + return "server"; + case DS4_EXECUTABLE_ROLE_AGENT: + return "agent"; + case DS4_EXECUTABLE_ROLE_BENCH: + return "bench"; + case DS4_EXECUTABLE_ROLE_EVAL: + return "eval"; + } + return "unknown"; +} + +static void ds4_json_string_print(FILE *fp, const char *value) { + fputc('"', fp); + for (const unsigned char *p = (const unsigned char *)value; *p; p++) { + switch (*p) { + case '"': + fputs("\\\"", fp); + break; + case '\\': + fputs("\\\\", fp); + break; + case '\b': + fputs("\\b", fp); + break; + case '\f': + fputs("\\f", fp); + break; + case '\n': + fputs("\\n", fp); + break; + case '\r': + fputs("\\r", fp); + break; + case '\t': + fputs("\\t", fp); + break; + default: + if (*p < 0x20) { + fprintf(fp, "\\u%04x", (unsigned)*p); + } else { + fputc(*p, fp); + } + break; + } + } + fputc('"', fp); +} + +void ds4_capabilities_print(FILE *fp, ds4_executable_role role) { + if (!fp) fp = stdout; + fputs("{\n" + " \"schema_version\": 1,\n" + " \"engine_id\": \"ds4\",\n" + " \"build_git_sha\": ", fp); + ds4_json_string_print(fp, ds4_build_git_sha()); + fputs(",\n \"backend\": ", fp); + ds4_json_string_print(fp, ds4_build_backend()); + fputs(",\n \"executable_role\": ", fp); + ds4_json_string_print(fp, ds4_executable_role_name(role)); + fprintf(fp, + ",\n" + " \"model_families\": [\"deepseek4\", \"glm-dsa\", \"qwen35moe\"],\n" + " \"expert_major\": {\n" + " \"version\": %u,\n" + " \"tensor\": \"%s\",\n" + " \"storage_formats\": [\n" + " {\"id\": \"ggml\", \"wire_value\": %u, \"group_sizes\": []},\n" + " {\"id\": \"mlx-affine4\", \"wire_value\": %u, \"group_sizes\": [64]}\n" + " ]\n" + " }\n" + "}\n", + (unsigned)DS4_EXPERT_STORE_V2_VERSION, + DS4_EXPERT_STORE_V2_TENSOR, + (unsigned)DS4_EXPERT_STORE_STORAGE_GGML, + (unsigned)DS4_EXPERT_STORE_STORAGE_MLX_AFFINE4); +} diff --git a/ds4_cli.c b/ds4_cli.c index 49c2a42e7..5f28a7a42 100644 --- a/ds4_cli.c +++ b/ds4_cli.c @@ -1895,6 +1895,10 @@ int main(int argc, char **argv) { ds4_build_info_print(stdout); return 0; } + if (ds4_capabilities_requested(argc, argv)) { + ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_CLI); + return 0; + } cli_config cfg = parse_options(argc, argv); if (cfg.gen.dump_tokens) { if (cfg.gen.prompt == NULL) { diff --git a/ds4_eval.c b/ds4_eval.c index 19fedc61c..b1b597409 100644 --- a/ds4_eval.c +++ b/ds4_eval.c @@ -4081,6 +4081,10 @@ int main(int argc, char **argv) { ds4_build_info_print(stdout); return 0; } + if (ds4_capabilities_requested(argc, argv)) { + ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_EVAL); + return 0; + } eval_config cfg = parse_options(argc, argv); if (cfg.self_test_extractors) return run_extractor_self_tests(); if (cfg.regrade_trace_path) return regrade_trace_file(cfg.regrade_trace_path); diff --git a/ds4_help.c b/ds4_help.c index 1bc3c2887..7713486c2 100644 --- a/ds4_help.c +++ b/ds4_help.c @@ -191,6 +191,7 @@ static void print_model_runtime(FILE *fp, const help_colors *c, opt(fp, c, "-t, --threads N", "CPU helper threads for host-side/reference work."); opt(fp, c, "--power N", "GPU duty-cycle target, 1..100. Default: 100"); opt(fp, c, "--build-info", "Print compiled backend, architecture, and git revision, then exit."); + opt(fp, c, "--capabilities=json", "Print the versioned machine-readable runtime contract, then exit."); opt(fp, c, "--ssd-streaming", "Force SSD-backed model streaming. Metal defaults to AUTO residency."); opt(fp, c, "--resident, --no-ssd-streaming", "Force full model residency instead of Metal AUTO selection."); opt(fp, c, "--ssd-streaming-cold", "SSD streaming: skip default popularity-based expert-cache preload."); diff --git a/ds4_server.c b/ds4_server.c index 1d532c060..6d18f1bbe 100644 --- a/ds4_server.c +++ b/ds4_server.c @@ -13355,6 +13355,10 @@ int main(int argc, char **argv) { ds4_build_info_print(stdout); return 0; } + if (ds4_capabilities_requested(argc, argv)) { + ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_SERVER); + return 0; + } signal(SIGPIPE, SIG_IGN); struct sigaction sa; memset(&sa, 0, sizeof(sa)); diff --git a/tests/test_build_isolation.sh b/tests/test_build_isolation.sh index cdce7ac49..95b5769c2 100755 --- a/tests/test_build_isolation.sh +++ b/tests/test_build_isolation.sh @@ -53,6 +53,8 @@ for name in $PROGRAMS; do root_state="$root_state $name:$(stat -f '%i:%m' "$name")" done +python3 tests/test_capabilities.py --bin-dir "$METAL_REL" --backend metal + [ -f "build/metal-${ARCH}/obj/ds4.o" ] || fail "missing Metal core object" [ -f "build/metal-${ARCH}/obj/ds4_metal.o" ] || fail "missing Metal backend object" @@ -70,6 +72,8 @@ for name in $PROGRAMS; do after_cpu_state="$after_cpu_state $name:$(stat -f '%i:%m' "$name")" done +python3 tests/test_capabilities.py --bin-dir "$CPU_REL" --backend cpu + [ "$root_state" = "$after_cpu_state" ] || \ fail "make cpu modified one or more published Metal root links" diff --git a/tests/test_capabilities.py b/tests/test_capabilities.py new file mode 100644 index 000000000..7977708a0 --- /dev/null +++ b/tests/test_capabilities.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +"""Validate the public, model-free engine capability contract.""" + +from __future__ import annotations + +import argparse +import json +import pathlib +import subprocess +import sys + + +ROLES = { + "ds4": "cli", + "ds4-server": "server", + "ds4-agent": "agent", + "ds4-bench": "bench", + "ds4-eval": "eval", +} + +ROOT_KEYS = { + "schema_version", + "engine_id", + "build_git_sha", + "backend", + "executable_role", + "model_families", + "expert_major", +} + + +def fail(message: str) -> None: + raise AssertionError(message) + + +def run(binary: pathlib.Path, *args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [str(binary), *args], + check=False, + capture_output=True, + text=True, + ) + + +def require_exact_int(value: object, expected: int, label: str) -> None: + if type(value) is not int or value != expected: + fail(f"{label}: expected integer {expected}, got {value!r}") + + +def validate(binary: pathlib.Path, backend: str, role: str) -> dict[str, object]: + result = run(binary, "--capabilities=json") + if result.returncode != 0: + fail(f"{binary}: --capabilities=json exited {result.returncode}: {result.stderr}") + if result.stderr: + fail(f"{binary}: --capabilities=json wrote to stderr: {result.stderr!r}") + try: + document = json.loads(result.stdout) + except json.JSONDecodeError as exc: + fail(f"{binary}: invalid capability JSON: {exc}") + + if type(document) is not dict: + fail(f"{binary}: capability document is not an object") + if set(document) != ROOT_KEYS: + fail(f"{binary}: unexpected root keys: {set(document) ^ ROOT_KEYS}") + require_exact_int(document["schema_version"], 1, f"{binary}: schema_version") + if document["engine_id"] != "ds4" or type(document["engine_id"]) is not str: + fail(f"{binary}: invalid engine_id") + if type(document["build_git_sha"]) is not str or not document["build_git_sha"]: + fail(f"{binary}: invalid build_git_sha") + if document["backend"] != backend or type(document["backend"]) is not str: + fail(f"{binary}: expected backend {backend!r}, got {document['backend']!r}") + if document["executable_role"] != role or type(document["executable_role"]) is not str: + fail(f"{binary}: expected role {role!r}, got {document['executable_role']!r}") + expected_families = ["deepseek4", "glm-dsa", "qwen35moe"] + if document["model_families"] != expected_families: + fail(f"{binary}: invalid model_families") + if any(type(item) is not str for item in document["model_families"]): + fail(f"{binary}: model_families entries must be strings") + + expert = document["expert_major"] + if type(expert) is not dict or set(expert) != {"version", "tensor", "storage_formats"}: + fail(f"{binary}: invalid expert_major object") + require_exact_int(expert["version"], 2, f"{binary}: expert_major.version") + if expert["tensor"] != "ds4.expert_major.v2" or type(expert["tensor"]) is not str: + fail(f"{binary}: invalid ExpertMajor tensor") + expected_storage = [ + {"id": "ggml", "wire_value": 0, "group_sizes": []}, + {"id": "mlx-affine4", "wire_value": 1, "group_sizes": [64]}, + ] + if expert["storage_formats"] != expected_storage: + fail(f"{binary}: invalid ExpertMajor storage formats") + for index, storage in enumerate(expert["storage_formats"]): + if type(storage) is not dict: + fail(f"{binary}: storage format {index} is not an object") + if type(storage["id"]) is not str: + fail(f"{binary}: storage format {index} id is not a string") + require_exact_int(storage["wire_value"], index, f"{binary}: storage wire_value") + if type(storage["group_sizes"]) is not list: + fail(f"{binary}: storage group_sizes is not an array") + if any(type(size) is not int for size in storage["group_sizes"]): + fail(f"{binary}: storage group_sizes must contain integers") + + build_info = run(binary, "--build-info") + if build_info.returncode != 0: + fail(f"{binary}: --build-info exited {build_info.returncode}") + git_lines = [line[9:] for line in build_info.stdout.splitlines() if line.startswith("git: ")] + if git_lines != [document["build_git_sha"]]: + fail(f"{binary}: capability SHA differs from --build-info") + + help_result = run(binary, "--help") + if help_result.returncode != 0 or "--capabilities=json" not in help_result.stdout: + fail(f"{binary}: help does not advertise the capability contract") + + invalid = run(binary, "--capabilities") + if invalid.returncode != 2: + fail(f"{binary}: bare --capabilities exited {invalid.returncode} instead of 2") + if invalid.stdout: + fail(f"{binary}: bare --capabilities unexpectedly wrote to stdout") + if "unknown option: --capabilities" not in invalid.stderr: + fail(f"{binary}: bare --capabilities did not take the invalid-option path") + + repeated = run(binary, "--capabilities=json") + if repeated.stdout != result.stdout or repeated.stderr != result.stderr: + fail(f"{binary}: capability output is not deterministic") + return document + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--bin-dir", required=True, type=pathlib.Path) + parser.add_argument("--backend", required=True, choices=("metal", "cpu")) + args = parser.parse_args() + + shared: dict[str, object] | None = None + for program, role in ROLES.items(): + binary = (args.bin_dir / program).resolve() + if not binary.is_file(): + fail(f"missing executable: {binary}") + document = validate(binary, args.backend, role) + common = {key: value for key, value in document.items() if key != "executable_role"} + if shared is None: + shared = common + elif common != shared: + fail(f"{binary}: shared capability fields differ across executables") + + print(f"capabilities: PASS ({args.backend}, {len(ROLES)} executables)") + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except AssertionError as exc: + print(f"capabilities: FAIL: {exc}", file=sys.stderr) + raise SystemExit(1) From 2b506811a03a99164ef44f839a335750cf57e502 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 20:29:49 +0200 Subject: [PATCH 03/41] docs: define Hebrus compatibility boundary --- ...ebrus-naming-and-compatibility-boundary.md | 77 +++++++++++++++++++ docs/architecture/CODEMAP.md | 4 +- 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 docs/adr/0005-hebrus-naming-and-compatibility-boundary.md diff --git a/docs/adr/0005-hebrus-naming-and-compatibility-boundary.md b/docs/adr/0005-hebrus-naming-and-compatibility-boundary.md new file mode 100644 index 000000000..68d8865c1 --- /dev/null +++ b/docs/adr/0005-hebrus-naming-and-compatibility-boundary.md @@ -0,0 +1,77 @@ +# ADR 0005: Hebrus Naming And Compatibility Boundary + +- Status: Proposed +- Date: 2026-07-21 + +## Context + +This repository began as a fork of [`antirez/ds4`](https://github.com/antirez/ds4) +and still preserves that ancestry, attribution, and a path for generally useful +changes to return upstream. The fork now has a materially different product +focus: Apple Metal inference, mmap-backed execution, embedded ExpertMajor +stores, and SSD streaming across several model families. Continuing to present +the fork itself as DS4 obscures that boundary and makes application/runtime +ownership harder to explain. + +A textual rename is unsafe. Existing installations and automation depend on +`ds4` commands, `DS4_*` environment variables, repository URLs, C names, model +filenames, and application paths. Published GGUFs and disk-KV files also contain +identifiers whose bytes are part of a durable format. The current DSBox release +additionally probes exact source paths, C symbols, and diagnostic strings when +admitting an ExpertMajor runtime. + +## Decision + +After namespace and legal screening is complete, the public engine name will be +**Hebrus**. The existing GitHub fork will be renamed rather than copied, and the +README will continue to identify `antirez/ds4` prominently as the origin of the +fork and of substantial implementation work. Acknowledgments will also name +ggml, llama.cpp, MLX, and other material sources with precise links and scope. + +The migration separates public brand from compatibility identity: + +- canonical executable names become `hebrus`, `hebrus-server`, + `hebrus-agent`, `hebrus-bench`, and `hebrus-eval`; +- all current `ds4` executable names remain aliases to the same object graph + through at least the complete 1.x release line; +- new `HEBRUS_*` environment aliases may be introduced only through one + resolver; existing `DS4_*` variables remain accepted, and conflicting values + fail closed; +- the companion application must consume a versioned, model-free capability + document before internal source names move. Legacy source/binary probe markers + remain a temporary compatibility shim, not the new admission API; +- source, header, symbol, and compile-time macro renames occur later in + mechanical subsystem-sized changes with no inference or performance change. + +The following identifiers do not change as part of the brand migration: + +- the `ds4.expert_major.v2` tensor name and the retired + `ds4.expert_major.v1` rejection marker; +- ExpertMajor v2 magic, version, manifest layout, offsets, digest semantics, + storage wire values (`GGML = 0`, `MLX_AFFINE4 = 1`), and qualified Qwen group + size (`64`); +- published model filenames, byte counts, revisions, and SHA-256 values; +- disk-KV magic, version, and payload ABI; +- Git history, tags, authorship, copyright, fork ancestry, and historical issue, + PR, benchmark, and release links. + +Old model artifacts are not revived by this compatibility policy. In +particular, the retired Qwen GGML/Q4 ExpertMajor store remains rejected under +both command brands. Model-format lifecycle remains governed by ADR 0003. + +## Consequences + +- A bridge release must precede internal source renaming and the companion + application's visible rebrand. +- Every remaining DS4 identifier must be classified as serialized, historical, + compatibility-owned, or erroneous; unclassified additions fail a repository + check. +- Canonical and legacy commands must have parity for options, exit status, + protocols, deterministic output, resolved runtime plan, and performance. +- Existing installations, checkouts, models, caches, and application data do + not need to be copied or rewritten for branding. +- The GitHub repository rename happens only after the compatibility application + recognizes both old and new remote identities. +- This ADR becomes Accepted only after the Hebrus identity is reserved, the + machine-readable capability contract is merged, the DSBox bridge is proven, + and the pre-rebrand release baseline is green. diff --git a/docs/architecture/CODEMAP.md b/docs/architecture/CODEMAP.md index 72d3d6e8b..b14ee0a51 100644 --- a/docs/architecture/CODEMAP.md +++ b/docs/architecture/CODEMAP.md @@ -10,7 +10,9 @@ below that is relevant to the task. Historical benchmark notes are evidence, not architecture specifications. The production-backend boundary is recorded in [`ADR 0002`](../adr/0002-apple-metal-production-runtime.md). Qwen's RAM/working-set adaptation is isolated in -[`ADR 0004`](../adr/0004-qwen-metal-hardware-memory-policy.md). +[`ADR 0004`](../adr/0004-qwen-metal-hardware-memory-policy.md). The proposed +Hebrus public-name migration and its immutable compatibility boundary are +recorded in [`ADR 0005`](../adr/0005-hebrus-naming-and-compatibility-boundary.md). ## Runtime Entry Points From 4c84db33b27ba977e6a4095ff44c034922090e8d Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 20:40:39 +0200 Subject: [PATCH 04/41] build: add Hebrus commands with DS4 aliases --- .gitignore | 5 + Makefile | 108 ++++++++++++++----- QA_BEFORE_RELEASES.md | 17 +-- README.md | 16 ++- docs/ENGINE_REFERENCE.md | 22 ++-- docs/architecture/CODEMAP.md | 5 + docs/contracts/RUNTIME_SUPPORT.md | 6 +- tests/test_build_isolation.sh | 66 ++++++++---- tests/test_capabilities.py | 7 +- tests/test_command_aliases.py | 133 ++++++++++++++++++++++++ tests/test_retired_distributed_flags.sh | 15 ++- 11 files changed, 329 insertions(+), 71 deletions(-) create mode 100644 tests/test_command_aliases.py diff --git a/.gitignore b/.gitignore index e1e8b8577..115613c23 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,11 @@ /ds4-bench /ds4-eval /ds4-agent +/hebrus +/hebrus-server +/hebrus-bench +/hebrus-eval +/hebrus-agent /ds4-qwen-pack /ds4_native /ds4_server_test diff --git a/Makefile b/Makefile index 9eb1f790b..e87ee3530 100644 --- a/Makefile +++ b/Makefile @@ -25,14 +25,17 @@ LDLIBS ?= -lm -pthread METAL_SRCS := $(wildcard metal/*.metal) BUILD_ROOT ?= build -PROGRAMS := ds4 ds4-server ds4-bench ds4-eval ds4-agent +HEBRUS_PROGRAMS := hebrus hebrus-server hebrus-bench hebrus-eval hebrus-agent +DS4_PROGRAMS := ds4 ds4-server ds4-bench ds4-eval ds4-agent +PROGRAMS := $(HEBRUS_PROGRAMS) $(DS4_PROGRAMS) .PHONY: all help clean test model-free-test premerge context-audit doc-links \ imatrix-dataset-check prompt-fixture-check cpu FORCE \ metal build-isolation-test q4k-dot-test qwen-metadata-test \ qwen-reference-test qwen-unicode-test qwen-tokenizer-test \ qwen-expert-group-test expert-store-test metal-ssd-profile-test \ - download-model-test capabilities-test $(PROGRAMS) ds4_test ds4_agent_test + download-model-test capabilities-test command-alias-test $(PROGRAMS) \ + ds4_test ds4_agent_test download-model-test: tests/test_download_model.sh download_model.sh sh tests/test_download_model.sh @@ -75,9 +78,9 @@ METAL_TEST_BINS := \ all: metal help: - @echo "DS4 build targets:" - @echo " make / make metal Build Metal and publish ./ds4* -> $(METAL_BINDIR)" - @echo " make cpu Build CPU-only binaries in $(CPU_BINDIR) (never changes ./ds4*)" + @echo "Hebrus build targets:" + @echo " make / make metal Build Metal and publish ./hebrus* plus ./ds4* aliases" + @echo " make cpu Build CPU-only commands in $(CPU_BINDIR); keep root Metal links" @echo " make test Build and run the Metal test suite" @echo " make model-free-test" @echo " Run all Metal gates that do not require a GGUF" @@ -89,73 +92,99 @@ help: # Root binaries are a Metal-only compatibility surface on macOS. These targets # are phony so an old regular CPU binary is replaced even when its timestamp is # newer than the namespaced Metal binary. -metal: $(PROGRAMS) +metal: $(PROGRAMS) $(METAL_BINS) -$(PROGRAMS): %: $(METAL_BINDIR)/% +hebrus ds4: $(METAL_BINDIR)/hebrus +hebrus-server ds4-server: $(METAL_BINDIR)/hebrus-server +hebrus-bench ds4-bench: $(METAL_BINDIR)/hebrus-bench +hebrus-eval ds4-eval: $(METAL_BINDIR)/hebrus-eval +hebrus-agent ds4-agent: $(METAL_BINDIR)/hebrus-agent + +$(PROGRAMS): @rm -f "$@" @ln -s "$<" "$@" cpu: $(CPU_BINS) @echo "CPU-only binaries: $(CPU_BINDIR)" -$(METAL_BINDIR)/ds4: \ +$(METAL_BINDIR)/hebrus: \ $(METAL_OBJDIR)/ds4_cli.o $(METAL_OBJDIR)/ds4_help.o \ $(METAL_OBJDIR)/linenoise.o $(METAL_CORE_OBJS) @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(METAL_LDLIBS) -$(METAL_BINDIR)/ds4-server: \ +$(METAL_BINDIR)/hebrus-server: \ $(METAL_OBJDIR)/ds4_server.o $(METAL_OBJDIR)/ds4_help.o \ $(METAL_OBJDIR)/ds4_kvstore.o $(METAL_OBJDIR)/rax.o $(METAL_CORE_OBJS) @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(METAL_LDLIBS) -$(METAL_BINDIR)/ds4-bench: \ +$(METAL_BINDIR)/hebrus-bench: \ $(METAL_OBJDIR)/ds4_bench.o $(METAL_OBJDIR)/ds4_help.o $(METAL_CORE_OBJS) @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(METAL_LDLIBS) -$(METAL_BINDIR)/ds4-eval: \ +$(METAL_BINDIR)/hebrus-eval: \ $(METAL_OBJDIR)/ds4_eval.o $(METAL_OBJDIR)/ds4_help.o $(METAL_CORE_OBJS) @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(METAL_LDLIBS) -$(METAL_BINDIR)/ds4-agent: \ +$(METAL_BINDIR)/hebrus-agent: \ $(METAL_OBJDIR)/ds4_agent.o $(METAL_OBJDIR)/ds4_help.o \ $(METAL_OBJDIR)/ds4_web.o $(METAL_OBJDIR)/ds4_kvstore.o \ $(METAL_OBJDIR)/linenoise.o $(METAL_CORE_OBJS) @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(METAL_LDLIBS) -$(CPU_BINDIR)/ds4: \ +$(METAL_BINDIR)/ds4: $(METAL_BINDIR)/hebrus +$(METAL_BINDIR)/ds4-server: $(METAL_BINDIR)/hebrus-server +$(METAL_BINDIR)/ds4-bench: $(METAL_BINDIR)/hebrus-bench +$(METAL_BINDIR)/ds4-eval: $(METAL_BINDIR)/hebrus-eval +$(METAL_BINDIR)/ds4-agent: $(METAL_BINDIR)/hebrus-agent + +$(addprefix $(METAL_BINDIR)/,$(DS4_PROGRAMS)): + @rm -f "$@" + @ln -s "$(notdir $<)" "$@" + +$(CPU_BINDIR)/hebrus: \ $(CPU_OBJDIR)/ds4_cli.o $(CPU_OBJDIR)/ds4_help.o \ $(CPU_OBJDIR)/linenoise.o $(CPU_CORE_OBJS) @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -$(CPU_BINDIR)/ds4-server: \ +$(CPU_BINDIR)/hebrus-server: \ $(CPU_OBJDIR)/ds4_server.o $(CPU_OBJDIR)/ds4_help.o \ $(CPU_OBJDIR)/ds4_kvstore.o $(CPU_OBJDIR)/rax.o $(CPU_CORE_OBJS) @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -$(CPU_BINDIR)/ds4-bench: \ +$(CPU_BINDIR)/hebrus-bench: \ $(CPU_OBJDIR)/ds4_bench.o $(CPU_OBJDIR)/ds4_help.o $(CPU_CORE_OBJS) @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -$(CPU_BINDIR)/ds4-eval: \ +$(CPU_BINDIR)/hebrus-eval: \ $(CPU_OBJDIR)/ds4_eval.o $(CPU_OBJDIR)/ds4_help.o $(CPU_CORE_OBJS) @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -$(CPU_BINDIR)/ds4-agent: \ +$(CPU_BINDIR)/hebrus-agent: \ $(CPU_OBJDIR)/ds4_agent.o $(CPU_OBJDIR)/ds4_help.o \ $(CPU_OBJDIR)/ds4_web.o $(CPU_OBJDIR)/ds4_kvstore.o \ $(CPU_OBJDIR)/linenoise.o $(CPU_CORE_OBJS) @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) +$(CPU_BINDIR)/ds4: $(CPU_BINDIR)/hebrus +$(CPU_BINDIR)/ds4-server: $(CPU_BINDIR)/hebrus-server +$(CPU_BINDIR)/ds4-bench: $(CPU_BINDIR)/hebrus-bench +$(CPU_BINDIR)/ds4-eval: $(CPU_BINDIR)/hebrus-eval +$(CPU_BINDIR)/ds4-agent: $(CPU_BINDIR)/hebrus-agent + +$(addprefix $(CPU_BINDIR)/,$(DS4_PROGRAMS)): + @rm -f "$@" + @ln -s "$(notdir $<)" "$@" + $(METAL_OBJDIR)/%.o: %.c @mkdir -p "$(@D)" $(CC) $(CFLAGS) $(DEPFLAGS) -c -o $@ $< @@ -442,6 +471,10 @@ qwen-tokenizer-test: $(METAL_BINDIR)/test_qwen_tokenizer capabilities-test: metal tests/test_capabilities.py python3 tests/test_capabilities.py --bin-dir $(METAL_BINDIR) --backend metal +command-alias-test: metal tests/test_command_aliases.py + python3 tests/test_command_aliases.py --bin-dir $(METAL_BINDIR) \ + --backend metal --layout profile + model-free-test: metal ds4_test ds4_agent_test $(METAL_BINDIR)/test_q4k_dot \ $(METAL_BINDIR)/test_q4k_top8 \ $(METAL_BINDIR)/test_qwen_session \ @@ -454,8 +487,10 @@ model-free-test: metal ds4_test ds4_agent_test $(METAL_BINDIR)/test_q4k_dot \ $(METAL_BINDIR)/test_expert_store \ $(METAL_BINDIR)/test_metal_ssd_profile \ $(METAL_BINDIR)/test_ssd_residency download-model-test \ - tests/test_capabilities.py + tests/test_capabilities.py tests/test_command_aliases.py python3 tests/test_capabilities.py --bin-dir $(METAL_BINDIR) --backend metal + python3 tests/test_command_aliases.py --bin-dir $(METAL_BINDIR) \ + --backend metal --layout profile DS4_BIN_DIR=$(METAL_BINDIR) sh tests/test_retired_distributed_flags.sh sh tests/test_benchmark_env_guard.sh $(METAL_BINDIR)/ds4-eval --self-test-extractors @@ -504,7 +539,8 @@ premerge: context-audit doc-links imatrix-dataset-check prompt-fixture-check bui $(MAKE) model-free-test git diff --check -build-isolation-test: tests/test_build_isolation.sh tests/test_capabilities.py +build-isolation-test: tests/test_build_isolation.sh tests/test_capabilities.py \ + tests/test_command_aliases.py MAKE="$(MAKE)" sh tests/test_build_isolation.sh -include $(wildcard $(METAL_OBJDIR)/*.d $(CPU_OBJDIR)/*.d) @@ -518,29 +554,39 @@ CPU_CORE_OBJS := ds4_cpu.o ds4_build_cpu.o ds4_ssd.o \ all: cpu help: - @echo "DS4 build targets:" - @echo " make / make cpu Build CPU-only ./ds4* binaries" + @echo "Hebrus build targets:" + @echo " make / make cpu Build ./hebrus* plus ./ds4* aliases" @echo " make test Build and run tests" @echo " make model-free-test Run all tests that do not require a GGUF" @echo " make clean Remove build outputs" cpu: $(PROGRAMS) -ds4: ds4_cli_cpu.o ds4_help.o linenoise.o $(CPU_CORE_OBJS) +hebrus: ds4_cli_cpu.o ds4_help.o linenoise.o $(CPU_CORE_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -ds4-server: ds4_server_cpu.o ds4_help.o ds4_kvstore.o rax.o $(CPU_CORE_OBJS) +hebrus-server: ds4_server_cpu.o ds4_help.o ds4_kvstore.o rax.o $(CPU_CORE_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -ds4-bench: ds4_bench_cpu.o ds4_help.o $(CPU_CORE_OBJS) +hebrus-bench: ds4_bench_cpu.o ds4_help.o $(CPU_CORE_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -ds4-eval: ds4_eval_cpu.o ds4_help.o $(CPU_CORE_OBJS) +hebrus-eval: ds4_eval_cpu.o ds4_help.o $(CPU_CORE_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -ds4-agent: ds4_agent_cpu.o ds4_help.o ds4_web.o ds4_kvstore.o linenoise.o $(CPU_CORE_OBJS) +hebrus-agent: ds4_agent_cpu.o ds4_help.o ds4_web.o ds4_kvstore.o linenoise.o $(CPU_CORE_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) +ds4: hebrus +ds4-server: hebrus-server +ds4-bench: hebrus-bench +ds4-eval: hebrus-eval +ds4-agent: hebrus-agent + +$(DS4_PROGRAMS): + @rm -f "$@" + @ln -s "$(notdir $<)" "$@" + ds4_build_cpu.o: ds4_build.c ds4.h ds4_expert_store.h FORCE $(CC) $(CFLAGS) -DDS4_NO_GPU -c -o $@ ds4_build.c @@ -643,8 +689,10 @@ model-free-test: ds4 ds4_test ds4_agent_test ds4-eval q4k-dot-test \ tests/test_qwen_expert_group \ tests/test_expert_store \ tests/test_metal_ssd_profile \ - tests/test_ssd_residency download-model-test tests/test_capabilities.py + tests/test_ssd_residency download-model-test tests/test_capabilities.py \ + tests/test_command_aliases.py python3 tests/test_capabilities.py --bin-dir . --backend cpu + python3 tests/test_command_aliases.py --bin-dir . --backend cpu --layout profile sh tests/test_retired_distributed_flags.sh sh tests/test_benchmark_env_guard.sh ./ds4-eval --self-test-extractors @@ -671,6 +719,9 @@ model-free-test: ds4 ds4_test ds4_agent_test ds4-eval q4k-dot-test \ capabilities-test: $(PROGRAMS) tests/test_capabilities.py python3 tests/test_capabilities.py --bin-dir . --backend cpu +command-alias-test: $(PROGRAMS) tests/test_command_aliases.py + python3 tests/test_command_aliases.py --bin-dir . --backend cpu --layout profile + test: model-free-test ./ds4_test @@ -793,7 +844,8 @@ endif clean: rm -rf "$(BUILD_ROOT)" - rm -f ds4 ds4-server ds4-bench ds4-eval ds4-agent ds4_cpu ds4_native \ + rm -f hebrus hebrus-server hebrus-bench hebrus-eval hebrus-agent \ + ds4 ds4-server ds4-bench ds4-eval ds4-agent ds4_cpu ds4_native \ ds4_server_test ds4_test ds4_agent_test tests/test_q4k_dot \ tests/test_q4k_top8 \ tests/test_qwen_session \ diff --git a/QA_BEFORE_RELEASES.md b/QA_BEFORE_RELEASES.md index 41069604c..1147bef3c 100644 --- a/QA_BEFORE_RELEASES.md +++ b/QA_BEFORE_RELEASES.md @@ -52,15 +52,20 @@ SHA-256, manifest contract, and compatible runtime commit are one release gate. - Build CPU-only binaries as a compile check. On macOS they remain under `build/cpu-$(uname -m)/bin` and must not replace the root Metal commands: `make cpu`. -- Record `./ds4 --build-info` and the CPU binary's `--build-info` output. -- Run `--capabilities=json` on all five Metal executables and the CPU profile; +- Record `./hebrus --build-info`, `./ds4 --build-info`, and the CPU profile's + canonical and compatibility `--build-info` output. +- Run `--capabilities=json` on all five canonical and all five compatibility + Metal commands and the complete CPU profile; validate schema version 1, executable roles, backend identity, model-family claims, and ExpertMajor wire values with `make capabilities-test` and `python3 tests/test_capabilities.py --bin-dir build/cpu-$(uname -m)/bin --backend cpu`. +- Run `make command-alias-test` and confirm each `ds4*` profile command is a + symlink to its single real `hebrus*` binary, with exact build-info, help, + capability, invalid-option, and retired-option parity. - Run whitespace checks before committing: `git diff --check`. -- Confirm `./ds4 --help`, `./ds4-server --help`, and `./ds4-agent --help` render - cleanly, with readable section colors and no broken wrapping. +- Confirm both names of the CLI, server, and agent render help cleanly, with + readable section colors and no broken wrapping. ## 2. Core Regression Tests @@ -286,8 +291,8 @@ before model loading: `sh tests/test_retired_distributed_flags.sh` -The gate covers `ds4`, `ds4-server`, `ds4-agent`, `ds4-bench`, and `ds4-eval`, -and all nine retired flags: `--role`, `--layers`, `--listen`, `--coordinator`, +The gate covers all five `hebrus*` commands and their five `ds4*` aliases, and +all nine retired flags: `--role`, `--layers`, `--listen`, `--coordinator`, `--dist-prefill-chunk`, `--dist-prefill-window`, `--dist-activation-bits`, `--dist-replay-check`, and `--debug`. Also confirm: diff --git a/README.md b/README.md index 80e9ac9cc..e05e0e7c7 100644 --- a/README.md +++ b/README.md @@ -100,10 +100,10 @@ git clone https://github.com/andreaborio/ds4.git cd ds4 make -j8 -./ds4 --build-info -./ds4-server --capabilities=json +./hebrus --build-info +./hebrus-server --capabilities=json ./download_model.sh qwen-v2 -./ds4 \ +./hebrus \ -m gguf/Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf \ --ctx 8192 ``` @@ -112,20 +112,26 @@ make -j8 `download_model.sh qwen-v2` install revision-pinned release artifacts and verify their complete byte size and SHA-256 before returning success. +`hebrus`, `hebrus-server`, `hebrus-agent`, `hebrus-bench`, and `hebrus-eval` +are the canonical command names. The corresponding `ds4*` names remain direct +aliases to the same binaries for existing installations and automation. During +this bridge tranche their help, diagnostics, build identity, and capability +`engine_id` intentionally remain compatibility-identical. + On macOS, AUTO residency keeps the model resident when it safely fits. Otherwise it selects SSD streaming and derives an expert-cache budget from the model geometry and live host memory. Force the SSD path only when you need a controlled run: ```sh -./ds4 -m /absolute/path/to/MODEL-DS4-ExpertMajor-v2.gguf \ +./hebrus -m /absolute/path/to/MODEL-DS4-ExpertMajor-v2.gguf \ --ssd-streaming --ctx 8192 ``` Start the local API with: ```sh -./ds4-server -m /absolute/path/to/MODEL-DS4-ExpertMajor-v2.gguf --ctx 8192 +./hebrus-server -m /absolute/path/to/MODEL-DS4-ExpertMajor-v2.gguf --ctx 8192 ``` ## How SSD streaming uses memory diff --git a/docs/ENGINE_REFERENCE.md b/docs/ENGINE_REFERENCE.md index d9babd5a1..678ba27d7 100644 --- a/docs/ENGINE_REFERENCE.md +++ b/docs/ENGINE_REFERENCE.md @@ -133,11 +133,19 @@ make cpu # CPU-only diagnostics in build/cpu-/bin on macOS ``` On macOS, Metal and CPU objects/binaries live in separate build profiles. -`make cpu` never replaces the root Metal commands. Use -`build/cpu-$(uname -m)/bin/ds4` for the CPU-only binary and `./ds4 --build-info` -to verify build provenance. - -Every executable also accepts `--capabilities=json` without loading a model. +`make cpu` never replaces the root Metal commands. Use +`build/cpu-$(uname -m)/bin/hebrus` for the canonical CPU-only binary and +`./hebrus --build-info` to verify build provenance. + +Each build profile links one real executable per role under the canonical +`hebrus*` name. The matching `ds4*` command is a relative symlink to that file; +it is not a separately compiled or linked wrapper. On macOS both root command +sets resolve to the same namespaced Metal binaries. The bridge deliberately +keeps help, diagnostics, `--build-info`, and capability `engine_id` output +identical under both names while downstream consumers migrate. + +Every canonical and compatibility executable accepts `--capabilities=json` +without loading a model. The deterministic schema reports the build revision, compiled backend, executable role, supported model families, and the ExpertMajor v2 tensor and storage wire contract. Consumers should reject unknown `schema_version` values @@ -169,8 +177,8 @@ optionally followed by `-dirty`; non-Git builds may report `unknown`. Do not rely on the historical `./ds4flash.gguf` symlink for runtime identity. Pass `-m` with an absolute qualified ExpertMajor v2 path and verify its complete -published output SHA-256 before inference. Run `./ds4 --help` and -`./ds4-server --help` for the full flag list. +published output SHA-256 before inference. Run `./hebrus --help` and +`./hebrus-server --help` for the full flag list. ## Speed diff --git a/docs/architecture/CODEMAP.md b/docs/architecture/CODEMAP.md index b14ee0a51..e668655bc 100644 --- a/docs/architecture/CODEMAP.md +++ b/docs/architecture/CODEMAP.md @@ -16,6 +16,10 @@ recorded in [`ADR 0005`](../adr/0005-hebrus-naming-and-compatibility-boundary.md ## Runtime Entry Points +The Makefile links one canonical `hebrus*` executable per role from these +entrypoints and publishes the corresponding `ds4*` name as a symlink to the +same file. There are no alias-specific wrappers or object graphs. + | Path | Primary responsibility | | --- | --- | | `ds4_cli.c` | One-shot CLI, interactive transcript, and command-line orchestration | @@ -101,6 +105,7 @@ not validated. | --- | --- | | `tests/` | Model-free, model-backed, kernel, tokenizer, server, and build-isolation regressions | | `tests/test_capabilities.py` | Exact schema and cross-executable checks for the model-free build/capability contract | +| `tests/test_command_aliases.py` | Canonical/legacy symlink layout, binary identity, and CLI-output parity checks | | `tests/qwen/` | Qwen fixtures, provenance, reference collectors, and model-specific gates | | `tests/test-vectors/` | Official and local continuation vectors plus provenance | | `gguf-tools/` | Quantization, ExpertMajor conversion, imatrix, and quality-scoring tools | diff --git a/docs/contracts/RUNTIME_SUPPORT.md b/docs/contracts/RUNTIME_SUPPORT.md index 699828d39..6a1beead2 100644 --- a/docs/contracts/RUNTIME_SUPPORT.md +++ b/docs/contracts/RUNTIME_SUPPORT.md @@ -14,9 +14,9 @@ and Qwen's lower-memory extension in | Model family | Minimum unified memory | Qualified Metal modes | Release startup | | --- | ---: | --- | --- | -| DeepSeek V4 Flash | 64 GiB | AUTO resolving to resident or SSD; explicit resident and SSD according to the artifact gate | `./ds4 -m DEEPSEEK-DS4-ExpertMajor-v2.gguf` | -| Qwen3.6-35B-A3B | 16 GiB | AUTO resolving to resident or SSD; explicit resident and SSD according to the Qwen admission gates | `./ds4 -m Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf` | -| GLM 5.2 | 64 GiB | AUTO resolving to SSD streaming only; resident is rejected | `./ds4 -m GLM-DS4-ExpertMajor-v2.gguf --ctx 8192` | +| DeepSeek V4 Flash | 64 GiB | AUTO resolving to resident or SSD; explicit resident and SSD according to the artifact gate | `./hebrus -m DEEPSEEK-DS4-ExpertMajor-v2.gguf` | +| Qwen3.6-35B-A3B | 16 GiB | AUTO resolving to resident or SSD; explicit resident and SSD according to the Qwen admission gates | `./hebrus -m Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf` | +| GLM 5.2 | 64 GiB | AUTO resolving to SSD streaming only; resident is rejected | `./hebrus -m GLM-DS4-ExpertMajor-v2.gguf --ctx 8192` | All rows require Apple Metal and a validated embedded `ds4.expert_major.v2` store. Explicit residency options are controlled qualification tools; normal diff --git a/tests/test_build_isolation.sh b/tests/test_build_isolation.sh index 95b5769c2..b23876bf7 100755 --- a/tests/test_build_isolation.sh +++ b/tests/test_build_isolation.sh @@ -5,7 +5,7 @@ ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) ARCH=$(uname -m) METAL_REL="build/metal-${ARCH}/bin" CPU_REL="build/cpu-${ARCH}/bin" -PROGRAMS="ds4 ds4-server ds4-bench ds4-eval ds4-agent" +PAIRS="hebrus:ds4 hebrus-server:ds4-server hebrus-bench:ds4-bench hebrus-eval:ds4-eval hebrus-agent:ds4-agent" MAKE_BIN=${MAKE:-make} fail() { @@ -43,17 +43,30 @@ cd "$ROOT" "$MAKE_BIN" metal root_state="" -for name in $PROGRAMS; do - [ -L "$name" ] || fail "$name is not a published Metal symlink" - [ "$(readlink "$name")" = "$METAL_REL/$name" ] || \ - fail "$name does not point at $METAL_REL/$name" - [ -x "$METAL_REL/$name" ] || fail "missing $METAL_REL/$name" - assert_metal_binary "$name" - assert_build_info "$name" metal - root_state="$root_state $name:$(stat -f '%i:%m' "$name")" +for pair in $PAIRS; do + canonical=${pair%%:*} + legacy=${pair#*:} + for name in "$canonical" "$legacy"; do + [ -L "$name" ] || fail "$name is not a published Metal symlink" + [ "$(readlink "$name")" = "$METAL_REL/$canonical" ] || \ + fail "$name does not point at $METAL_REL/$canonical" + assert_metal_binary "$name" + assert_build_info "$name" metal + root_state="$root_state $name:$(stat -f '%i:%m' "$name")" + done + [ -x "$METAL_REL/$canonical" ] || fail "missing $METAL_REL/$canonical" + [ ! -L "$METAL_REL/$canonical" ] || \ + fail "$METAL_REL/$canonical is not the canonical binary" + [ -L "$METAL_REL/$legacy" ] || fail "$METAL_REL/$legacy is not an alias" + [ "$(readlink "$METAL_REL/$legacy")" = "$canonical" ] || \ + fail "$METAL_REL/$legacy does not point at $canonical" done python3 tests/test_capabilities.py --bin-dir "$METAL_REL" --backend metal +python3 tests/test_command_aliases.py --bin-dir "$METAL_REL" \ + --backend metal --layout profile +python3 tests/test_command_aliases.py --bin-dir . \ + --backend metal --layout published [ -f "build/metal-${ARCH}/obj/ds4.o" ] || fail "missing Metal core object" [ -f "build/metal-${ARCH}/obj/ds4_metal.o" ] || fail "missing Metal backend object" @@ -64,25 +77,40 @@ python3 tests/test_capabilities.py --bin-dir "$METAL_REL" --backend metal [ ! -e "build/cpu-${ARCH}/obj/ds4_metal.o" ] || fail "CPU profile contains a Metal object" after_cpu_state="" -for name in $PROGRAMS; do - [ -x "$CPU_REL/$name" ] || fail "missing $CPU_REL/$name" - assert_cpu_binary "$CPU_REL/$name" - assert_build_info "$CPU_REL/$name" cpu - assert_metal_binary "$name" - after_cpu_state="$after_cpu_state $name:$(stat -f '%i:%m' "$name")" +for pair in $PAIRS; do + canonical=${pair%%:*} + legacy=${pair#*:} + [ -x "$CPU_REL/$canonical" ] || fail "missing $CPU_REL/$canonical" + [ ! -L "$CPU_REL/$canonical" ] || \ + fail "$CPU_REL/$canonical is not the canonical binary" + [ -L "$CPU_REL/$legacy" ] || fail "$CPU_REL/$legacy is not an alias" + [ "$(readlink "$CPU_REL/$legacy")" = "$canonical" ] || \ + fail "$CPU_REL/$legacy does not point at $canonical" + for name in "$canonical" "$legacy"; do + assert_cpu_binary "$CPU_REL/$name" + assert_build_info "$CPU_REL/$name" cpu + assert_metal_binary "$name" + after_cpu_state="$after_cpu_state $name:$(stat -f '%i:%m' "$name")" + done done python3 tests/test_capabilities.py --bin-dir "$CPU_REL" --backend cpu +python3 tests/test_command_aliases.py --bin-dir "$CPU_REL" \ + --backend cpu --layout profile [ "$root_state" = "$after_cpu_state" ] || \ fail "make cpu modified one or more published Metal root links" "$MAKE_BIN" metal -for name in $PROGRAMS; do - [ "$(readlink "$name")" = "$METAL_REL/$name" ] || \ - fail "final $name does not point at the Metal profile" - assert_metal_binary "$name" +for pair in $PAIRS; do + canonical=${pair%%:*} + legacy=${pair#*:} + for name in "$canonical" "$legacy"; do + [ "$(readlink "$name")" = "$METAL_REL/$canonical" ] || \ + fail "final $name does not point at $METAL_REL/$canonical" + assert_metal_binary "$name" + done done echo "build-isolation: PASS (metal-${ARCH} -> cpu-${ARCH} -> metal-${ARCH})" diff --git a/tests/test_capabilities.py b/tests/test_capabilities.py index 7977708a0..b321a66f6 100644 --- a/tests/test_capabilities.py +++ b/tests/test_capabilities.py @@ -11,6 +11,11 @@ ROLES = { + "hebrus": "cli", + "hebrus-server": "server", + "hebrus-agent": "agent", + "hebrus-bench": "bench", + "hebrus-eval": "eval", "ds4": "cli", "ds4-server": "server", "ds4-agent": "agent", @@ -133,7 +138,7 @@ def main() -> int: shared: dict[str, object] | None = None for program, role in ROLES.items(): - binary = (args.bin_dir / program).resolve() + binary = (args.bin_dir / program).absolute() if not binary.is_file(): fail(f"missing executable: {binary}") document = validate(binary, args.backend, role) diff --git a/tests/test_command_aliases.py b/tests/test_command_aliases.py new file mode 100644 index 000000000..00aef7051 --- /dev/null +++ b/tests/test_command_aliases.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +"""Prove canonical Hebrus commands and DS4 aliases are one executable surface.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +import pathlib +import subprocess +import sys + + +PAIRS = ( + ("hebrus", "ds4", "cli"), + ("hebrus-server", "ds4-server", "server"), + ("hebrus-agent", "ds4-agent", "agent"), + ("hebrus-bench", "ds4-bench", "bench"), + ("hebrus-eval", "ds4-eval", "eval"), +) + +PARITY_CASES = ( + ("--capabilities=json",), + ("--build-info",), + ("--help",), + ("--capabilities",), + ("--role",), + ("--command-alias-invalid-test",), +) + + +def fail(message: str) -> None: + raise AssertionError(message) + + +def run(binary: pathlib.Path, args: tuple[str, ...]) -> subprocess.CompletedProcess[bytes]: + return subprocess.run( + [os.fsencode(binary), *(os.fsencode(arg) for arg in args)], + check=False, + capture_output=True, + ) + + +def sha256(path: pathlib.Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as fp: + for chunk in iter(lambda: fp.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def validate_layout(canonical: pathlib.Path, legacy: pathlib.Path, layout: str) -> None: + if not canonical.is_file() or not legacy.is_file(): + fail(f"missing command pair: {canonical}, {legacy}") + if not legacy.is_symlink(): + fail(f"legacy command is not a symlink: {legacy}") + if layout == "profile": + if canonical.is_symlink(): + fail(f"canonical profile command is unexpectedly a symlink: {canonical}") + if os.readlink(legacy) != canonical.name: + fail(f"{legacy} does not point directly to {canonical.name}") + elif not canonical.is_symlink(): + fail(f"published canonical command is not a symlink: {canonical}") + if not os.path.samefile(canonical, legacy): + fail(f"command pair does not resolve to one file: {canonical}, {legacy}") + if sha256(canonical) != sha256(legacy): + fail(f"command pair has different binary content: {canonical}, {legacy}") + + +def validate_pair( + canonical: pathlib.Path, + legacy: pathlib.Path, + role: str, + backend: str, +) -> None: + capabilities = run(canonical, ("--capabilities=json",)) + if capabilities.returncode != 0 or capabilities.stderr: + fail(f"{canonical}: capability invocation failed") + document = json.loads(capabilities.stdout) + if document.get("engine_id") != "ds4": + fail(f"{canonical}: compatibility engine_id changed before the brand contract") + if document.get("backend") != backend: + fail(f"{canonical}: expected backend {backend}") + if document.get("executable_role") != role: + fail(f"{canonical}: expected executable role {role}") + + for args in PARITY_CASES: + canonical_result = run(canonical, args) + legacy_result = run(legacy, args) + canonical_record = ( + canonical_result.returncode, + canonical_result.stdout, + canonical_result.stderr, + ) + legacy_record = ( + legacy_result.returncode, + legacy_result.stdout, + legacy_result.stderr, + ) + if canonical_record != legacy_record: + fail(f"{canonical.name}/{legacy.name} differ for {' '.join(args)}") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--bin-dir", required=True, type=pathlib.Path) + parser.add_argument("--backend", required=True, choices=("metal", "cpu")) + parser.add_argument("--layout", required=True, choices=("profile", "published")) + args = parser.parse_args() + + bin_dir = args.bin_dir.absolute() + resolved_targets: set[pathlib.Path] = set() + for canonical_name, legacy_name, role in PAIRS: + canonical = bin_dir / canonical_name + legacy = bin_dir / legacy_name + validate_layout(canonical, legacy, args.layout) + validate_pair(canonical, legacy, role, args.backend) + target = canonical.resolve() + if target in resolved_targets: + fail(f"roles unexpectedly share one executable: {target}") + resolved_targets.add(target) + + print(f"command-aliases: PASS ({args.backend}, {len(PAIRS)} pairs, {args.layout})") + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (AssertionError, json.JSONDecodeError) as exc: + print(f"command-aliases: FAIL: {exc}", file=sys.stderr) + raise SystemExit(1) diff --git a/tests/test_retired_distributed_flags.sh b/tests/test_retired_distributed_flags.sh index 2835f841d..d4a7a7a7c 100755 --- a/tests/test_retired_distributed_flags.sh +++ b/tests/test_retired_distributed_flags.sh @@ -3,7 +3,7 @@ set -eu ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) BIN_DIR=${DS4_BIN_DIR:-$ROOT} -PROGRAMS="ds4 ds4-server ds4-agent ds4-bench ds4-eval" +PROGRAMS="hebrus hebrus-server hebrus-agent hebrus-bench hebrus-eval ds4 ds4-server ds4-agent ds4-bench ds4-eval" OPTIONS="--role --layers --listen --coordinator --dist-prefill-chunk --dist-prefill-window --dist-activation-bits --dist-replay-check --debug" fail() { @@ -27,6 +27,17 @@ output_contains() { esac } +diagnostic_name() { + case "$1" in + hebrus) echo ds4 ;; + hebrus-server) echo ds4-server ;; + hebrus-agent) echo ds4-agent ;; + hebrus-bench) echo ds4-bench ;; + hebrus-eval) echo ds4-eval ;; + *) echo "$1" ;; + esac +} + for program in $PROGRAMS; do binary="$BIN_DIR/$program" [ -x "$binary" ] || fail "missing executable $binary" @@ -60,7 +71,7 @@ for program in $PROGRAMS; do done for option in $OPTIONS; do - expected="$program: distributed option $option was retired; distributed inference is not supported" + expected="$(diagnostic_name "$program"): distributed option $option was retired; distributed inference is not supported" run_and_capture "$binary" "$option" [ "$STATUS" -eq 2 ] || From 51b05b0417c8b4af13b153e7257430b67f0b2eff Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 20:50:59 +0200 Subject: [PATCH 05/41] docs: present Hebrus open-source project --- ACKNOWLEDGMENTS.md | 98 ++++++ README.md | 710 ++++++++++---------------------------- THIRD_PARTY_NOTICES.md | 125 +++++++ tools/context_budget.json | 12 +- 4 files changed, 413 insertions(+), 532 deletions(-) create mode 100644 ACKNOWLEDGMENTS.md create mode 100644 THIRD_PARTY_NOTICES.md diff --git a/ACKNOWLEDGMENTS.md b/ACKNOWLEDGMENTS.md new file mode 100644 index 000000000..b03e9d6c6 --- /dev/null +++ b/ACKNOWLEDGMENTS.md @@ -0,0 +1,98 @@ +# Acknowledgments + +Hebrus is the result of an open-source lineage, not a clean-room rewrite. This +document records the principal projects and implementations that materially +shaped the current repository. It describes technical provenance; it does not +claim endorsement by any upstream author, project, company, or model provider. + +## Project origin: `antirez/ds4` + +Hebrus began as a fork of +[`antirez/ds4`](https://github.com/antirez/ds4) and retains substantial +core-engine implementation, architecture, utilities, and Git history from that +project. Salvatore Sanfilippo (antirez) and the upstream contributors created +the foundation on which this fork continues to build. + +The fork has since concentrated on Apple Metal execution, embedded ExpertMajor +stores, adaptive residency, SSD streaming, additional qualified model families, +and the evidence and tooling required to maintain those paths. The repositories +have diverged, but the contribution policy still asks whether general, +reproducible improvements should also be proposed upstream. See +[`FORK_NOTES.md`](FORK_NOTES.md) for the current boundary. + +## GGML and llama.cpp + +[`llama.cpp`](https://github.com/ggml-org/llama.cpp) and +[`GGML`](https://github.com/ggml-org/ggml) are essential engineering references +for this repository. Their work informs GGUF handling, quantization formats and +tables, CPU quantized dot products, Metal kernels, converters, test methods, and +many implementation decisions. + +Hebrus does not link against GGML or llama.cpp as a runtime dependency. Some +source-level pieces are retained or adapted under the MIT license, including +quant layouts and tables, CPU quant/dot logic, and selected kernel techniques. +The GGML authors' copyright notice remains in [`LICENSE`](LICENSE), and +source-specific attribution remains in the relevant files. + +Pinned llama.cpp converter implementations are also used as independent Qwen +validation references. A reference implementation or fixture oracle is not the +same thing as a linked dependency. + +## MLX and MLX-LM + +[`MLX`](https://github.com/ml-explore/mlx) and +[`MLX-LM`](https://github.com/ml-explore/mlx-lm) informed the current Qwen MLX +affine4/group-64 storage layout, scheduling comparisons, and validation work. +The local Qwen candidate repacks routed weights from an MLX-format source into +the existing checksummed ExpertMajor v2 GGUF container. + +Hebrus does not link the MLX libraries at runtime. This credit does not imply +Apple authorship, sponsorship, or endorsement of Hebrus. + +## YaRN + +The Metal RoPE implementation in `metal/dsv4_rope.metal` contains an algorithm +based on [`YaRN`](https://github.com/jquesnelle/yarn), with the source header's +MIT attribution to Jeffrey Quesnelle and Bowen Peng preserved. + +## Bundled utilities + +The repository includes: + +- [`linenoise`](https://github.com/antirez/linenoise), by Salvatore Sanfilippo + and Pieter Noordhuis, for terminal line editing; +- Rax, by Salvatore Sanfilippo, for radix-tree storage used by the server path. + +These files carry BSD-style terms, including the applicable no-endorsement +language. Their exact bundled notices are collected in +[`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md) and remain in the source +headers. + +## Validation references and model ecosystems + +Qwen numerical fixtures use a pinned +[Hugging Face Transformers implementation](https://github.com/huggingface/transformers/blob/4626421dc6b741a329300682a6408246ee465490/src/transformers/models/qwen3_5_moe/modeling_qwen3_5_moe.py) +and a pinned +[llama.cpp Qwen converter](https://github.com/ggml-org/llama.cpp/blob/657e01125aa49577a62a5531fde24cbcc007006d/conversion/qwen.py) +as independent validation references. The collectors use local deterministic +fixtures and do not make those projects runtime dependencies. + +The project also depends intellectually on the researchers and open-weight +model teams behind the DeepSeek, Qwen, and GLM families, and on the wider GGUF, +Metal, and local-inference communities. Exact model, artifact, revision, and +benchmark provenance belongs in the corresponding model documentation and +dated evidence records rather than in a general credit list. + +## Development process + +The long-form engine reference discloses substantial AI assistance during +development, with humans directing design, testing, benchmarking, and release +decisions. That assistance does not replace the authorship, copyright, or +license notices attached to upstream and third-party work. + +## Licenses and model terms + +The project-level software license is [`LICENSE`](LICENSE). Bundled third-party +files retain their own notices and conditions. Model weights, datasets, model +cards, and external repositories have their own terms; this repository's +software license does not grant rights to those assets. diff --git a/README.md b/README.md index e05e0e7c7..76f325d97 100644 --- a/README.md +++ b/README.md @@ -1,580 +1,228 @@ -

DwarfStar

- -

ExpertMajor v2 inference for large MoE models on Apple Metal.

- -

- A transparent research and co-development fork of - antirez/ds4, focused on Metal, - adaptive SSD streaming, Apple Silicon systems, and measured experimentation. -

- -

- MIT license - Apple Metal primary target - ExpertMajor v2 model format - Project status beta -

- -

- Quick start - · Benchmarks - · Models - · DSBox - · Upstream diff - · Documentation -

+# Hebrus + +**Local inference built first for Apple Metal and SSD streaming.** + +Hebrus is a focused inference engine for running a small, explicitly qualified +set of large mixture-of-experts models on Apple Silicon. It combines mmap-backed +GGUF loading, an embedded ExpertMajor v2 routed-weight store, Metal execution, +and adaptive SSD streaming so unified memory is a performance tier rather than +the only capacity boundary. + +[Quick start](#quick-start) · [Supported models](#supported-models) · +[Measured results](#measured-results) · [Documentation](#documentation) · +[Contributing](CONTRIBUTING.md) + +> [!NOTE] +> Hebrus is the working public engine name and the canonical executable +> namespace in the current bridge. The repository is still +> [`andreaborio/ds4`](https://github.com/andreaborio/ds4), and +> [ADR 0005](docs/adr/0005-hebrus-naming-and-compatibility-boundary.md) remains +> **Proposed** while identity screening and the companion-application bridge are +> incomplete. This repository has not been administratively renamed. > [!IMPORTANT] -> This is [`andreaborio/ds4`](https://github.com/andreaborio/ds4), a fork of -> [`antirez/ds4`](https://github.com/antirez/ds4). It does **not** aim to replace -> upstream. The goal is to co-develop DwarfStar: explore complementary hardware -> and model paths here, then propose every general, reproducible improvement back -> to upstream when it clears the correctness and performance bar. - -DwarfStar is a small, self-contained inference engine optimized around a narrow -set of very large models. It includes native model loading, prompt rendering, -tool calling, RAM/on-disk KV state, an HTTP server, a coding agent, GGUF tooling, -and correctness and speed tests. It is intentionally **not** a generic GGUF -runner; arbitrary GGUF files are not expected to work. - -## Why this fork exists - -Upstream DwarfStar provides the core engine and leads the high-memory and -distributed paths. This fork asks a complementary question: - -**How far can the same specialized design be pushed across Apple Silicon memory -tiers, when the SSD becomes an active model-memory tier?** - -The current work concentrates on: - -- adaptive Metal residency and routed-expert cache policies across memory tiers; -- SSD streaming that accounts for page cache, wired memory, swap, I/O, and - throughput together; -- safer model-backed experiments near macOS memory limits; -- one validated ExpertMajor v2 storage contract for DeepSeek, GLM, and Qwen; -- GGUF calibration, incremental quantization, and expert-analysis tooling; -- keeping useful changes small enough to validate and send upstream. - -This is primarily a learning and systems-research project. The fork lets work -continue while upstream changes are under review; it is not a parallel rewrite -or a competing inference ecosystem. - -## Co-development with upstream - -The boundary is explicit and reviewable: - -| Change type | Where it belongs | -| --- | --- | -| General, reproducible, backend-safe improvement | Open a PR against [`antirez/ds4`](https://github.com/antirez/ds4) | -| Model- or hardware-specific experiment | Keep it isolated while evidence is incomplete; open an upstream PR too whenever it applies to an upstream-supported path | -| Change that regresses an existing path | Do not promote it; isolate or revise it first | -| Change already solved upstream | Take the upstream implementation and remove the fork delta | - -This is mandatory, not aspirational: every fork change applicable to an -upstream-supported path will be opened upstream once its scope, correctness, -and performance evidence are ready. Fork development can continue while that -review is in progress. - -Current upstream work includes [#434](https://github.com/antirez/ds4/pull/434) -(quality-score build fix), [#520](https://github.com/antirez/ds4/pull/520) -(GLM streamed-prefill correctness), and -[#528](https://github.com/antirez/ds4/pull/528) (GLM indexed-prefill prepare). -The DeepSeek regression found on the GLM line is tracked in -[#532](https://github.com/antirez/ds4/issues/532). See -[`FORK_NOTES.md`](FORK_NOTES.md) for the status of each fork change and -[`MERGE_LOG.md`](MERGE_LOG.md) for sync history. The same policy is part of -[`CONTRIBUTING.md`](CONTRIBUTING.md). The current `main` delta is always -inspectable in GitHub's -[upstream/fork comparison](https://github.com/antirez/ds4/compare/main...andreaborio:ds4:main). +> Hebrus began as a fork of +> [`antirez/ds4`](https://github.com/antirez/ds4) and retains substantial +> core-engine implementation, Git history, and design work from that project. +> The fork has since diverged toward Apple Metal, embedded ExpertMajor storage, +> and SSD-first execution. This attribution does not imply endorsement by or an +> official partnership with the upstream maintainer. See +> [Acknowledgments](ACKNOWLEDGMENTS.md) and +> [Third-party notices](THIRD_PARTY_NOTICES.md) for the precise source scopes. + +## What Hebrus is for + +Hebrus is designed around one practical question: how effectively can large +MoE models run locally when Metal and the SSD are treated as one coordinated +memory system? + +- **Metal-first inference.** Production model execution targets Apple Metal. +- **SSD streaming as a first-class path.** Routed experts can be cached and + streamed without keeping the complete model in unified memory. +- **Hardware-aware AUTO policy.** Residency and cache plans use model geometry, + context memory, Metal's working-set recommendation, and live memory pressure. +- **One validated routed-weight container.** Supported artifacts embed the + checksummed `ds4.expert_major.v2` store directly in GGUF. +- **Narrow support, explicit failure.** Unsupported model families, old stores, + sidecars, and unqualified backend combinations fail closed instead of taking + an untested fallback. +- **Evidence before claims.** Correctness and performance decisions are tied to + exact commits, model hashes, hardware, prompts, memory state, and context + frontiers. + +Hebrus is not a general GGUF runner. Arbitrary community GGUFs are not expected +to load, even when their model family name looks compatible. + +## Supported models + +The current production contract is Apple Silicon + Metal + embedded +ExpertMajor v2. AUTO is the normal startup mode. + +| Model family | Minimum unified memory | Qualified execution | Artifact availability | +| --- | ---: | --- | --- | +| DeepSeek V4 Flash | 64 GiB | AUTO resolving to resident or SSD; explicit modes for qualification | Published ExpertMajor v2 artifact; `download_model.sh deepseek-v2` | +| GLM 5.2 | 64 GiB | AUTO resolving to SSD streaming only; resident requests are rejected | Published ExpertMajor v2 artifact; `download_model.sh glm-v2` | +| Qwen3.6-35B-A3B | 16 GiB | AUTO resolving to resident or SSD; 16 GiB necessarily uses SSD | Published MLX affine4/group-64 artifact; `download_model.sh qwen-v2` | + +The Qwen release is exactly +`Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`, +20,808,566,880 bytes, SHA-256 +`dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. +It is pinned to immutable repository revision +`7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`. The older Q4_K_S store is +incompatible with the current runtime and remains only a fail-closed negative +case. + +DeepSeek V4 PRO has no qualified runtime artifact in the current contract. +Canonical model files are converter inputs, not inference fallbacks. CPU is a +reference and build-isolation path; CUDA and ROCm are frozen with source absent; +distributed inference is retired. + +The authoritative details are in the +[runtime support contract](docs/contracts/RUNTIME_SUPPORT.md). ## Quick start -Requirements: Apple Silicon, Xcode Command Line Tools, an ExpertMajor v2 GGUF, -and enough SSD space for the selected model. DeepSeek and GLM currently require -at least 64 GiB. Qwen's hardware-aware AUTO path starts at 16 GiB and chooses -resident or SSD from the active Metal and live-memory budgets. +Requirements: + +- Apple Silicon Mac; +- Xcode Command Line Tools; +- enough unified memory for the selected supported family; +- enough SSD space for the exact ExpertMajor v2 artifact. + +Build and verify the engine without loading model weights: ```sh xcode-select --install git clone https://github.com/andreaborio/ds4.git cd ds4 -make -j8 +make -j ./hebrus --build-info -./hebrus-server --capabilities=json -./download_model.sh qwen-v2 -./hebrus \ - -m gguf/Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf \ - --ctx 8192 -``` - -`download_model.sh deepseek-v2`, `download_model.sh glm-v2`, and -`download_model.sh qwen-v2` install revision-pinned release artifacts and -verify their complete byte size and SHA-256 before returning success. - -`hebrus`, `hebrus-server`, `hebrus-agent`, `hebrus-bench`, and `hebrus-eval` -are the canonical command names. The corresponding `ds4*` names remain direct -aliases to the same binaries for existing installations and automation. During -this bridge tranche their help, diagnostics, build identity, and capability -`engine_id` intentionally remain compatibility-identical. - -On macOS, AUTO residency keeps the model resident when it safely fits. -Otherwise it selects SSD streaming and derives an expert-cache budget from the -model geometry and live host memory. Force the SSD path only when you need a -controlled run: - -```sh -./hebrus -m /absolute/path/to/MODEL-DS4-ExpertMajor-v2.gguf \ - --ssd-streaming --ctx 8192 +./hebrus --capabilities=json +make model-free-test ``` -Start the local API with: +For a published DeepSeek V4 Flash artifact on a qualified 64 GiB-or-larger +host: ```sh -./hebrus-server -m /absolute/path/to/MODEL-DS4-ExpertMajor-v2.gguf --ctx 8192 -``` +./download_model.sh deepseek-v2 -## How SSD streaming uses memory - -```mermaid -flowchart LR - GGUF["Model GGUF on SSD"] --> AUTO["AUTO memory planner"] - AUTO -->|"safe fit"| RES["Resident model"] - AUTO -->|"model exceeds budget"| STREAM["SSD-streamed model"] - STREAM --> FIXED["Mapped fixed / non-routed state"] - STREAM --> CACHE["Adaptive routed-expert cache"] - GGUF -->|"cache miss"| CACHE - FIXED --> METAL["Metal graph"] - CACHE --> METAL - METAL --> TOKEN["Next token"] +./hebrus \ + -m gguf/DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix-DS4-ExpertMajor-v2.gguf \ + --ctx 8192 \ + -p "Explain why SSD streaming changes the memory limit for MoE inference." ``` -The fixed model state, KV cache, graph scratch, and macOS file-backed cache all -need headroom. The routed-expert cache is the variable tier; making it larger -can help only until it starts displacing the pages and allocations the rest of -the runtime needs. - -## Model status - -| Model | Location | Status | Current focus | -| --- | --- | --- | --- | -| DeepSeek V4 Flash ExpertMajor v2 | `main` | Supported Apple Metal resident/SSD path | Adaptive SSD streaming and grouped prefill | -| DeepSeek V4 PRO ExpertMajor v2 | offline tooling only | Not runtime-qualified; no release artifact identity | Converter and future per-artifact qualification | -| GLM 5.2 ExpertMajor v2 | `main` | Qualified Apple Metal SSD path; 64 GB minimum | Embedded expert store, grouped prefill, compact DSA KV, 601-record DS4 cache plus adaptive macOS file caching | -| Qwen3.6-35B-A3B ExpertMajor v2 affine4/group-64 | `main` | Supported Apple Metal resident/SSD path; 16 GiB minimum | One AUTO configuration, MLX-affine resident/SSD kernels, and read-once macro prefill | - -### DeepSeek expert-major v2 format - -DS4 uses the self-describing `ds4.expert_major.v2` layout for DeepSeek V4. It -stores each layer as adjacent gate/up/down expert records without requantizing -and without keeping a second routed-weight copy. The runtime accepts only the -native v2 artifact on Apple Metal. Canonical GGUFs are converter inputs, not an -inference fallback; CPU, CUDA, ROCm, distributed, v1, and sidecar paths fail -closed. - -Conversion, full byte-level verification, admission limits, and the -model-backed promotion gate are in -[`docs/deepseek-expert-major-v2.md`](docs/deepseek-expert-major-v2.md). The first -M5 Pro SSD tranche is recorded in -[`docs/benchmarks/2026-07-17-deepseek-native-expert-major.md`](docs/benchmarks/2026-07-17-deepseek-native-expert-major.md). -The distinctly named v2 artifact is -[`DeepSeek-V4-Flash-DS4-GGUF`](https://huggingface.co/andreaborio/DeepSeek-V4-Flash-DS4-GGUF), -with full conversion provenance and runtime limits in its model card. - -### GLM 5.2 ExpertMajor v2 - -The 262,147,193,504-byte GLM release stores its 76 × 256 routed experts once, -inside the GGUF, in the physical order used by Metal prefill and decode. It -does not need a sidecar or ExpertMajor environment variables. The qualified -tier is an Apple Silicon Mac with at least 64 GiB of unified memory. GLM in -this repo intentionally supports only the ExpertMajor v2 Metal SSD path: +Start the local HTTP server with the same artifact: ```sh -make -j8 -./ds4 \ - -m /absolute/path/to/GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf \ +./hebrus-server \ + -m gguf/DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix-DS4-ExpertMajor-v2.gguf \ --ctx 8192 ``` -The artifact is -[`andreaborio/GLM-5.2-DS4-GGUF`](https://huggingface.co/andreaborio/GLM-5.2-DS4-GGUF), -SHA-256 -`7f5017e3076e706c78f2a5322b035a9e2f6519c65ff5b6be8b2d91aeff61505d`. -The simple command automatically selects Metal, SSD residency, the GLM Gold -profile, the measured 601-expert cache on the 64 GiB tier, indexed preparation, -grouped native prefill, contiguous-record decode reads, and compact DSA KV. -Canonical GLM GGUFs and non-Metal/distributed execution fail closed. Do not add -cache, preload, full-layer, or ExpertMajor flags. Format, memory flow, tier -boundaries, and current limits are in -[`docs/glm52-expert-major-v2.md`](docs/glm52-expert-major-v2.md). -Older GLM files, sidecars, layout revisions, and retired tuning modes have no -backward-compatibility contract in this fork. - -### Qwen3.6 ExpertMajor v2 AUTO path - -The supported release is the single-layout -`Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`. It stores the 40 -layers of routed weights once, activates automatically, and is 20,808,566,880 -bytes with SHA-256 -`dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. -It is still a GGUF with the shared `ds4.expert_major.v2` container; only the -routed payload uses MLX affine 4-bit groups (`32 packed bytes + BF16 scale + -BF16 bias` per group of 64). The previous v2 GGML/Q4 payload is deliberately -rejected rather than retained as a slower compatibility mode. -No environment guard, sidecar variable, backend flag, resident flag, or power -flag is part of normal startup: +The canonical commands are `hebrus`, `hebrus-server`, `hebrus-agent`, +`hebrus-bench`, and `hebrus-eval`. Their `ds4*` counterparts remain symlinks to +the same binaries for compatibility; formats, model filenames, environment +variables, and serialized `DS4` identifiers are not renamed by this bridge. -```sh -./ds4 \ - -m /absolute/path/to/Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf \ - --ctx 8192 -``` +## How the runtime fits together + +1. The engine mmaps the GGUF and validates the embedded + `ds4.expert_major.v2` manifest, tensor inventory, geometry, ranges, and + digest before inference. +2. AUTO calculates fixed model and context memory, Metal headroom, live host + pressure, and the model-specific residency policy. +3. When the complete admitted working set fits, Metal uses full mapped tensors. + Otherwise, the runtime keeps non-routed state mapped and streams routed + expert records through a bounded cache. +4. CLI, HTTP server, agent, benchmark, and evaluation frontends share the same + engine and capability contract. -This is a narrow Qwen3.6 artifact contract, not arbitrary Qwen or community -GGUF support. Current inference rejects canonical Qwen, ExpertMajor v1, -sidecars, CPU, CUDA, ROCm, and distributed execution. The canonical GGUF may be -used offline with `gguf-tools/ds4-expert-major.py` to build and verify v2. -Format details and the current resident/SSD promotion gate are in -[`docs/qwen-expert-major-store.md`](docs/qwen-expert-major-store.md) and -[`docs/benchmarks/2026-07-21-qwen-unified-affine-auto-ssd.md`](docs/benchmarks/2026-07-21-qwen-unified-affine-auto-ssd.md). - -Qwen AUTO reports a named 16/24/32/36/48/64/96/128 GiB profile, then computes -from the exact physical RAM and `recommendedMaxWorkingSetSize`; the labels do -not hardcode a cache count. Its resident reserve is -`max(2 GiB, RAM/16) + max(0.25 GiB, RAM/64)`. Full-model mapped Metal mode is -selected only when both that fixed Metal plan and a point-in-time host-memory -pressure check pass. The 19.37 GiB tensor payload cannot be resident on 16 GiB, -so AUTO uses SSD there. A 32 GiB Mac can select resident for shorter contexts -when both gates pass and falls back to SSD when current pressure or context -runtime no longer fits. - -In SSD mode the planner charges static pages, context/runtime memory, and system -headroom, then selects the largest complete expert-cache cycle admitted by the -remaining live and platform budgets. Under normal macOS pressure, equivalent -free and bounded file-backed GGUF pages receive equal credit on every Qwen -profile; warming the model file therefore cannot by itself reduce AUTO cache. -Unknown or elevated pressure remains conservative, and the 16 GiB class -requires an affirmative normal-pressure signal. Its measured AUTO ceiling is -eleven complete routes plus the safety slot, or 3,521 experts for this 40x8 -model; the next tier produced swap and is not retained. `--resident` fails -unless both admission checks pass. `--ssd-streaming` remains a controlled-test -override. Cache storage grows in 321-expert slabs (about 0.529 GiB). - -Here `resident` means that DS4 maps the complete tensor payload, disables its -explicit SSD expert cache, and executes full-tensor Metal kernels. Metal's -residency request is a budgeting hint: it neither pre-faults every GGUF page nor -proves that every page remains physically resident as later pressure changes. -That stronger physical-residency claim requires separate runtime measurement. -All neural math in the supported Qwen path is on Metal. The CPU still performs -tokenization, sampling, route readback, cache bookkeeping, and streamed GGUF -I/O; a CPU+GPU split of layers or experts is not implemented in this path. - -The hard SSD cache floor is 321 complete routed experts (about 0.53 GiB); 640 -(about 1.06 GiB) is a useful controlled small-cache tier. Startup and the -per-layer path fail closed if the effective locked cache falls below the floor. -The affine runtime has completed model-backed resident and SSD generation on an -M5 Pro with 64 GiB, including allocation at the model-declared 262,144-token -maximum. A final 128+16 lane produced identical resident/SSD greedy tokens. -Performance numbers remain tied to each exact host and workload; policy tests -for an unmeasured RAM cut are not presented as a throughput claim. +ExpertMajor v2 is a disk ABI, not a brand label. The tensor identifier +`ds4.expert_major.v2`, storage wire values, published filenames and hashes, and +disk-KV format remain unchanged. ## Measured results -The current table uses the mandatory 32K long-context lane rather than mixing -short-context bests. These rows are not cross-model rankings: every model uses -a different artifact and runtime path. - -| Model | M5 Pro 64 GB setup | 32K prefill | 32K generation / decode | Status | -| --- | --- | ---: | ---: | --- | -| Qwen3.6-35B-A3B ExpertMajor v2 MLX affine4/group-64, 20.81 GB | Metal resident AUTO | **877.34 t/s** | **57.43 t/s** at the qualified 128+16 decode lane, 17.352 ms p50, 18.753 ms p95 | Same GGUF resident/SSD; forced SSD 32K is 83.69 t/s with 1.000x read amplification | -| DeepSeek V4 Flash IQ2XXS, 86.72 GB | Metal SSD, phase-adaptive 259→4,129 through 32K, prose prompt | **164.43 t/s** | **7.27 t/s**, 105.410 ms p50, 149.943 ms p95 | Exact final-stack evidence and zero swap; old AUTO controls can swap, so no 32K speedup percentage | -| GLM 5.2 ExpertMajor v2 Q2_K, 244.14 GiB | Metal AUTO→SSD, 601 records, fixed compact-indexer transition, prose prompt | **44.73 t/s** | **1.87 t/s** overall; about **2.12 t/s** at p50 | Same-prompt output matches the earlier corrected arm; zero swap; original baseline crashes before logits | - -Full identities, invalidations, control drift and remaining release gates are -in the -[`current Qwen split-K and hardware-policy record`](docs/benchmarks/2026-07-21-qwen-split-k-hardware-policy.md) -and the earlier -[`long-context Metal stack record`](docs/benchmarks/2026-07-20-long-context-metal-stack.md). -DeepSeek also completes isolated 65K+128 and 100K+128 AUTO→SSD lanes with the -2,065-expert extended-context tier at 137.29/6.59 and 145.11/6.44 prefill/decode -t/s respectively, with zero swapout. GLM's separate 32K security/coding prompt -lane completes at 45.53/1.33 t/s; its lower decode rate comes from a 13.15% -expert-cache hit rate versus 36.90% for the prose lane and is not a -same-condition regression. -Earlier short-context bests remain historical evidence in the dated records; -they are not substituted for the long-context promotion gate. - -Historical upstream DeepSeek hardware reference bests from the standard -`speed-bench` sweep follow. They are context for comparison, not a statement -that this v2-only fork runtime supports those non-Metal hosts: - -| Host | Model | Prefill | Generation | -| --- | --- | ---: | ---: | -| MacBook Pro M5 Max, 128 GB | Flash q2, 11,707-token context | 463.44 t/s | 25.90 t/s | -| Mac Studio M3 Ultra, 512 GB | Flash q2, 11,709-token context | 468.03 t/s | 27.39 t/s | -| Mac Studio M3 Ultra, 512 GB | PRO q2, 32,768-token context | 138.82 t/s | 9.56 t/s | -| DGX Spark GB10, 128 GB | Flash q2, 7,047-token context | 343.81 t/s | 13.75 t/s | - -The [`benchmark evidence index`](docs/benchmarks/README.md) classifies current, -historical, and rejected records. Full commands and caveats live in those dated -records; the latest pre-policy cross-model correctness/artifact baseline is the -[`2026-07-20 validation record`](docs/benchmarks/2026-07-20-agent-friendly-refactor-validation.md). -See also [`SSD_STREAMING_VERIFICATION.md`](SSD_STREAMING_VERIFICATION.md) and -[`docs/ENGINE_REFERENCE.md`](docs/ENGINE_REFERENCE.md). - -## Memory safety is part of performance - -More expert-cache RAM is not automatically faster. On memory-constrained Macs, -an oversized cache can evict the file-backed pages SSD streaming needs and make -decode slower even when Activity Monitor appears to show free memory. AUTO -therefore treats the routed-expert cache as variable and preserves headroom for -fixed weights, KV, scratch, Metal allocations, and the macOS page cache. - -During development, a model-backed test bypassed SSD streaming and attempted to -make an 80.76 GiB GGUF resident with a 100,000-token context on a 64 GiB Mac. -Global wired memory reached roughly 61.36 GiB before a watchdog kernel panic. -Crashing the host is not an acceptable test outcome. - -Current `main` includes hardware-aware AUTO residency, fail-closed cache -admission, bounded benchmark guards, and GPU cleanup before model mappings are -released (`1523b26`). A stricter guard that rejects resident mappings larger -than 90% of physical RAM is tested and published on -`fix/refuse-oversized-resident-maps` at `06fd005`, but is **not yet on `main`**. -Until it is merged, it must not be described as a mainline guarantee. - -## Prefer a desktop interface? - -[DSBox](https://github.com/andreaborio/dsbox) is the companion desktop -interface, inspired by Unsloth Studio: discover compatible models, manage ds4, -chat locally, connect coding agents, and observe memory, swap, disk, and token -throughput without hand-assembling every command. DSBox is a separate project -and still a work in progress. - -

- - DSBox local chat interface controlling a ds4 server - -
- DSBox is an optional companion UI, maintained in a separate repository. -

+These are current durable measurements from one Apple M5 Pro with 64 GiB of +unified memory. They are not cross-model rankings: artifacts, prompts, +residency modes, and decode lanes differ. Follow the evidence links for exact +commands, hashes, invalidations, and memory telemetry. -## Documentation +| Path | Evidence lane | Measured result | +| --- | --- | --- | +| Qwen3.6 MLX affine4/group-64 | 32K pure prefill; separate 128+16 decode, resident and forced SSD | Resident: 877.34 prefill t/s and 57.43 decode t/s. Forced SSD: 83.69 prefill t/s and 23.94 decode t/s. | +| DeepSeek V4 Flash ExpertMajor v2 | 32K prose + 128 decode, Metal SSD | 164.43 prefill t/s and 7.27 decode t/s, with zero swapout. | +| GLM 5.2 ExpertMajor v2 | 32K prose + 128 decode, AUTO resolving to SSD | 44.73 prefill t/s and 1.87 overall decode t/s, with zero swapout. | -- [`docs/releases/v0.2.0.md`](docs/releases/v0.2.0.md): current ExpertMajor - v2-only release boundary, startup, measured results, and upgrade notes. -- [`docs/ENGINE_REFERENCE.md`](docs/ENGINE_REFERENCE.md): complete model, - runtime, server, agent, KV-cache, supported-backend, retired-feature, and - debugging reference. -- [`docs/qwen-expert-major-store.md`](docs/qwen-expert-major-store.md): - Qwen v2 layout, converter, runtime contract, and parity evidence. -- [`docs/glm52-expert-major-v2.md`](docs/glm52-expert-major-v2.md): embedded GLM - artifact, one-command startup, prefill/decode memory flow, qualification, and - supported memory tiers. -- [`docs/expert-major-v2-roadmap.md`](docs/expert-major-v2-roadmap.md): shared - ExpertMajor v2 contract and family-specific qualification status. -- [`CONTRIBUTING.md`](CONTRIBUTING.md): upstream-first contribution policy and - correctness/performance gates. -- [`FORK_NOTES.md`](FORK_NOTES.md): fork delta and upstreamability ledger. -- [`MERGE_LOG.md`](MERGE_LOG.md): upstream synchronization history. -- [`GOLD_METAL_SSD.md`](GOLD_METAL_SSD.md): Metal build identity, AUTO residency, - and benchmark promotion gates. -- [`SSD_STREAMING_VERIFICATION.md`](SSD_STREAMING_VERIFICATION.md): superseded - July 2026 SSD-streaming campaign evidence; not a current runtime guide. -- [`ONEDGE_IMATRIX.md`](ONEDGE_IMATRIX.md): live, privacy-preserving imatrix - collection. -- [`EXPERT_PRUNE.md`](EXPERT_PRUNE.md): expert profiling and prune-mask research. -- [`gguf-tools/README.md`](gguf-tools/README.md): GGUF, imatrix, quantization, and - quality tooling. - ---- - -
-Detailed fork additions and research notes - -## Fork feature details - -The sections below preserve the longer design notes for the fork's research -features. They are not an exhaustive commit count: adaptive residency, cache -hardening, benchmark guardrails, telemetry, and safe Metal teardown have also -evolved since the original five-feature summary was written. The authoritative -per-change ledger is [`FORK_NOTES.md`](FORK_NOTES.md); upstream syncs are recorded -in [`MERGE_LOG.md`](MERGE_LOG.md). - -### The GLM 5.2 SSD-streaming path - -GLM 5.2 is promoted as a narrow Apple Metal path for the published embedded -ExpertMajor v2 artifact. It includes the streamed-prefill correctness and -indexed-prepare work proposed as [#520](https://github.com/antirez/ds4/pull/520) -and [#528](https://github.com/antirez/ds4/pull/528), plus model-specific compact -DSA KV, grouped Q2_K prefill, physical expert-record addressing, NextN metadata -binding, tokenizer/prompt routing, and a pressure-sized cache policy for the -64 GiB reference tier. - -All admitted families use ExpertMajor v2, but GLM does not become a compute or -cache fallback for DeepSeek or Qwen. Family checks select their independent -Metal schedules, while CPU, CUDA, ROCm, and distributed execution fail closed. -The DeepSeek regression discovered on the historical upstream GLM line remains -tracked in [#532](https://github.com/antirez/ds4/issues/532). - -Speculative streamed decode is still a measured no-go: the existing NextN -probe reached about 55% acceptance, below the level needed to repay its extra -expert I/O. Current release evidence and the remaining decode targets are in -[`docs/benchmarks/2026-07-20-glm52-expert-major-v2.md`](docs/benchmarks/2026-07-20-glm52-expert-major-v2.md). - -### 1. On-edge / real-time imatrix collection: `ds4-server --imatrix-out` - -Upstream collects the routed-MoE importance matrix (imatrix) **offline** from a fixed corpus -(`ds4 --imatrix-dataset … --imatrix-out …`). This fork lets **`ds4-server` collect it from the -live prompt stream on the device**, so a quantized model can be **re-calibrated to its actual -workload**, without ever storing a single user prompt. The only artifact is the imatrix: -aggregate per-(layer, expert) activation statistics (squared activations + hit counts), a -structure that cannot hold prompt text. +Sources: -```sh -ds4-server -m model.gguf --imatrix-out edge.dat # collect from live traffic -ds4-server -m model.gguf --imatrix-out edge.dat --imatrix-every 128 --imatrix-min-requests 32 -``` +- [Qwen unified affine AUTO and SSD promotion](docs/benchmarks/2026-07-21-qwen-unified-affine-auto-ssd.md) +- [DeepSeek and GLM long-context Metal stack](docs/benchmarks/2026-07-20-long-context-metal-stack.md) +- [Benchmark evidence index](docs/benchmarks/README.md) -Default **off** (zero behavioral change); opt-in via `--imatrix-out`, with periodic snapshots -(`--imatrix-every`) and a minimum-requests guard (`--imatrix-min-requests`). Full design, -wiring, limits and privacy verification in [`ONEDGE_IMATRIX.md`](ONEDGE_IMATRIX.md). +Performance acceptance requires isolated short, medium, large, and 32K +long-context lanes; memory-sensitive changes may require 65K and 100K as well. +A short-context best is not substituted for the long-context gate. -### 2. Incremental re-quantization: `deepseek4-quantize --reuse PRIOR.gguf` +## Documentation -Re-forging a *variant* (say, adding a per-layer Q4 "boost" on top of an IQ2 build that used the -same imatrix) normally regenerates **every** routed-expert tensor from the FP weights, even the -ones that don't change. But quantization is deterministic in (FP weights, target type, imatrix -slice), so an unchanged tensor is **byte-identical** to the one already sitting in a prior -build. Recomputing it is pure waste. +### Start and operate -`--reuse PRIOR.gguf` copies a planned output tensor straight from PRIOR when its **name, target -type and shape** match, and quantizes only the tensors that actually changed (the boosted -layers, at their new type). +- [Runtime support contract](docs/contracts/RUNTIME_SUPPORT.md) — supported + families, hardware floors, backends, and fail-closed boundaries. +- [Engine reference](docs/ENGINE_REFERENCE.md) — CLI, server, agent, disk KV, + tracing, evaluation, and operational details. +- [Metal and SSD policy](GOLD_METAL_SSD.md) — build identity, AUTO residency, + cache planning, and benchmark gates. -```sh -# 1. build the 2-bit base once -gguf-tools/deepseek4-quantize --hf FP --template base.gguf --imatrix coder.dat \ - --out coder-iq2.gguf - -# 2. every boost variant reuses the base's unchanged layers, re-quantizing only the boosted ones -gguf-tools/deepseek4-quantize --hf FP --template base.gguf --imatrix coder.dat \ - --reuse coder-iq2.gguf \ - --tensor-type blk.30.ffn_gate_exps.weight=q4_k … --out coder-q4boost.gguf -``` +### Models and formats -**Measured** (DeepSeek-V4-Flash, a 6-of-43-layer Q4 boost over an IQ2 base): a full build is -~80 minutes; the same variant via `--reuse` took **5.5 minutes** (1,310 of 1,328 tensors copied, -18 regenerated), about a **14× speedup**. The output was verified **byte-for-byte identical** to -a from-scratch build across all 1,328 tensors. The fast build is not an approximation, it is the -same file. - -**Correctness.** Every build stamps a `quantize.reuse_key` GGUF KV: an fnv1a64 over the -safetensors index, each weight shard's size and mtime, the imatrix content, and a template -structural salt. `--reuse` copies a tensor only when PRIOR's key matches this build **and** the -per-tensor type and shape match, so a boosted tensor (different target type) is regenerated, and -a stale / foreign / keyless prior (changed weights, imatrix, or recipe) safely falls back to a -full quantize. Copied bytes are size-checked against the plan (a hard error on any mismatch), -and `--reuse` refuses to alias `--out`. This is **not** present in llama.cpp, which always -requantizes from the source weights; the closest prior art is splicing GGUF tensors by hand. - -### 3. Re-calibration reuse: `quantize.reuse_key_weights` - -Changing the *imatrix* only changes the tensors the -imatrix actually steers (the routed expert families: the importance vectors re-allocate bits -inside those tensors). Everything else — attention, shared experts, norms, embeddings, output — -is byte-identical across builds that share the same FP weights and template. So every build now -also stamps `quantize.reuse_key_weights`: the same fnv1a64 **without** the imatrix folded in. -When PRIOR matches the full key, behavior is unchanged; when it matches only the weights key -(same weights, different imatrix — the re-calibration case), `--reuse` copies the -imatrix-independent tensors and regenerates only the steered ones: +- [ExpertMajor v2 roadmap](docs/expert-major-v2-roadmap.md) +- [DeepSeek ExpertMajor v2](docs/deepseek-expert-major-v2.md) +- [GLM 5.2 ExpertMajor v2](docs/glm52-expert-major-v2.md) +- [Qwen ExpertMajor v2 MLX-affine store](docs/qwen-expert-major-store.md) +- [GGUF and quantization tools](gguf-tools/README.md) -``` -reuse: PRIOR.gguf shares the weights key (…) but not the imatrix — copying - imatrix-independent tensors, regenerating the steered ones -``` +### Architecture and evidence -The dependence test is conservative and mirrors the generators' own imatrix lookups (routed -`*_exps.*` families always count as steered; regular tensors are probed with the exact same -name resolution `generate_regular()` uses), so over-approximation can only cost an unneeded -regeneration, never a stale byte. Priors built before this change carry only the old key and -keep the old all-or-nothing behavior. - -Measured (DeepSeek-V4-Flash, 1,328 tensors, M5 Pro): a full re-calibration — same recipe, -`coder.dat` → `general.dat` — copied **1,199 of 1,328 tensors** and regenerated the 129 -routed-expert tensors with the new imatrix, in **~45 minutes vs ~80 for the full quantize**. -Byte-level verification: 40/40 sampled imatrix-independent tensors identical to the prior, -16/16 sampled expert tensors changed, tensor tables identical. The change went through an -adversarial 3-lens review that rejected the first cut (two stale-byte paths, one strict-mode -abort — all reachable, all fixed before this exercise: the no-imatrix gate, the coverage -fingerprint, the I32 probe exclusion). - -### 4. Historical mixed-precision routed-expert work - -Upstream `--ssd-streaming` assumes routed-expert tensors are quantized uniformly across -layers. A GGUF with a few layers boosted to Q4_K over an IQ2 base (the forgequant boost -recipe) failed **every** request under streaming (`model range … is not covered by mapped -model views`) while serving fine with full residency. Two compounding uniformity -assumptions are fixed: the streaming prefill span set now also maps the exps tensors of -off-class ("boosted") layers, so they are read through mmap'd no-copy views; and the -single-size-class expert cache pre-seeds its slab size at startup and **rejects** off-size -layers (which use the mapped path) instead of silently adopting their size and corrupting -the slot accounting. - -Uniform models were verified **byte-identical** under the change (3/3 builds), and mixed -models were validated with the canary benchmark plus the evaluation suites. This is -historical research evidence, not an extension of the current ExpertMajor v2 admission -contract. The original diagnosis and workaround were reported in -[antirez/ds4#388](https://github.com/antirez/ds4/issues/388). - -**Update (upstream converged):** antirez has since implemented equivalent mixed-precision -streaming upstream. After the latest sync this fork **takes upstream's implementation** of -`weights_streaming_layer_experts_uniform` (the only merge conflict; the two designs converged) — -see [`MERGE_LOG.md`](MERGE_LOG.md). This addition is effectively now upstream. - -### 5. Coding-eval expert tooling: prune mask + full expert profile - -Two small, opt-in hooks for studying *which* experts a domain actually needs, used by the -forgequant layer/expert A/B work: - -- **`DS4_EXPERT_PROFILE_FULL`** — the expert profiler (`ds4_expert_profile_write_layer`) emits - the *full* per-expert ranking instead of the top-16, so a static prune/keep set can be chosen - per layer from real routing statistics. -- **`DS4_EXPERT_PRUNE_MASK`** — point it at a `43 × N_EXPERT` grid of `'0'/'1'` (`'1'` = prune). - The mask is applied to the CPU router's `probs` **before top-k** (masked experts get a - large-negative sentinel so they never win), letting each token route to its next-best - surviving expert. This measures "how much of the domain lives in a few experts" without - re-quantizing anything. +- [Code map](docs/architecture/CODEMAP.md) +- [Hebrus naming and compatibility decision](docs/adr/0005-hebrus-naming-and-compatibility-boundary.md) +- [Benchmark methodology and records](docs/benchmarks/README.md) +- [Release checklist](QA_BEFORE_RELEASES.md) +- [Fork/upstream ledger](FORK_NOTES.md) -```sh -# the mask lives in the CPU router, so enable it (streaming-IQ2 path), then prune: -DS4_METAL_ENABLE_STREAMING_IQ2_CPU_ROUTER=1 DS4_EXPERT_PRUNE_MASK=mask.txt \ - ds4 -m coder-iq2.gguf -p "…" --ssd-streaming -# -> "ds4: expert prune mask ACTIVE (N experts pruned) from mask.txt" -``` +## Project status -Both default **off** (zero behavioral change). The mask affects only routed (non-hash) layers, -and only when the CPU router is active (streaming-IQ2 or PRO-Q4 paths). Details in -[`EXPERT_PRUNE.md`](EXPERT_PRUNE.md). +The engine is beta software; `hebrus-agent` remains alpha. Large-model inference +can create substantial memory pressure and I/O. Follow AUTO, use only exact +qualified artifacts, monitor swap and memory pressure, and do not expose a +local server to untrusted networks without an appropriate security boundary. -
+The separate companion desktop application is currently still +[DSBox](https://github.com/andreaborio/dsbox). Its name, persisted application +identity, and migration are outside this engine bridge. -## Full engine reference +## Contributing and security -The long-form guide now lives in -[`docs/ENGINE_REFERENCE.md`](docs/ENGINE_REFERENCE.md). It covers model -downloads, full-resident and SSD-streamed operation, the native agent, -benchmarking, capability evaluation, CLI, server/tool calling, disk KV cache, -historical upstream backend controls, steering, test vectors, and debugging. +Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a change. Contributions +must identify realistic failure modes, run the applicable correctness and +performance gates, and classify whether the work should also be proposed to +`antirez/ds4`. -Keeping the manual separate makes this README a reviewable landing page while -preserving the full operational reference. +Do not publish vulnerability details in an ordinary issue. Until a dedicated +security policy is added, use the repository's +[private GitHub Security Advisory flow](https://github.com/andreaborio/ds4/security/advisories/new) +when available. -## Status, credit, and license +## License and credit -DwarfStar is beta software and `ds4-agent` remains alpha. The core engine and -upstream direction come from [`antirez/ds4`](https://github.com/antirez/ds4). -The project also exists thanks to the kernels, formats, and engineering work -of [`llama.cpp`](https://github.com/ggml-org/llama.cpp) and GGML. +The project-level license is [MIT](LICENSE). Bundled or adapted third-party +code retains its own notices and conditions; model weights and datasets are not +granted rights by this repository's software license. -Released under the [MIT license](LICENSE). Contributions follow the -[upstream-first policy](CONTRIBUTING.md). +Read [ACKNOWLEDGMENTS.md](ACKNOWLEDGMENTS.md) for engineering provenance and +[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for bundled-code notices. diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 000000000..639312416 --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,125 @@ +# Third-party notices + +This file collects notices for third-party code bundled in or materially +adapted by this repository. Source headers remain authoritative and must not be +removed. The project-level [`LICENSE`](LICENSE) does not replace the terms +below. + +## linenoise + +Files: `linenoise.c`, `linenoise.h` + +Upstream: + +```text +Copyright (c) 2010-2023, Salvatore Sanfilippo +Copyright (c) 2010-2013, Pieter Noordhuis + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` + +## Rax + +Bundled copyright lines: + +`rax.c`: + +```text +Copyright (c) 2017-2026, Salvatore Sanfilippo +``` + +`rax.h`: + +```text +Copyright (c) 2017-2018, Salvatore Sanfilippo +``` + +`rax_malloc.h`: + +```text +Copyright (c) 2017, Salvatore Sanfilippo +``` + +All three files carry the following terms: + +```text +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Redis nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +``` + +## GGML and llama.cpp-derived implementation + +The root MIT license retains: + +```text +Copyright (c) 2023-2026 The ggml authors +``` + +The repository retains or adapts narrow pieces of MIT-licensed GGML/llama.cpp +work, including quant layouts and tables, CPU quantization and dot-product +logic, selected kernel techniques, and GGUF/converter behavior. See +[`ACKNOWLEDGMENTS.md`](ACKNOWLEDGMENTS.md), [`LICENSE`](LICENSE), and the +source-file comments for scope. + +## YaRN-derived RoPE algorithm + +File: `metal/dsv4_rope.metal` + +The source contains the following notice: + +```text +YaRN algorithm based on LlamaYaRNScaledRotaryEmbedding.py from https://github.com/jquesnelle/yarn +MIT licensed. Copyright (c) 2023 Jeffrey Quesnelle and Bowen Peng. +``` + +## Preservation + +When redistributing source or binaries, preserve the notices and conditions +required by the relevant source headers. When copying or moving one of these +files, carry its attribution and license text with it. No project or contributor +listed here endorses Hebrus merely because their work is credited. diff --git a/tools/context_budget.json b/tools/context_budget.json index 8a7ddf132..40d71bbe8 100644 --- a/tools/context_budget.json +++ b/tools/context_budget.json @@ -25,5 +25,15 @@ "implementation_includes": 3, "maybe_unused_markers": 50, "personal_absolute_path_hits": 0, - "root_markdown_files": 10 + "root_markdown_files": { + "maximum": 12, + "baseline": 10, + "reason": "The open-source launch adds one provenance document and one exact bundled-code notice file; later root documentation still requires explicit classification.", + "accepted_additions": { + "oss_provenance": [ + "ACKNOWLEDGMENTS.md", + "THIRD_PARTY_NOTICES.md" + ] + } + } } From 4149616807585dcdf964356865f2284691079841 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 20:53:25 +0200 Subject: [PATCH 06/41] docs: add community and release metadata --- CHANGELOG.md | 37 +++++++++++ CITATION.cff | 20 ++++++ CODE_OF_CONDUCT.md | 132 ++++++++++++++++++++++++++++++++++++++ GOVERNANCE.md | 81 +++++++++++++++++++++++ README.md | 17 ++++- SECURITY.md | 54 ++++++++++++++++ tools/context_budget.json | 10 ++- 7 files changed, 346 insertions(+), 5 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 CITATION.cff create mode 100644 CODE_OF_CONDUCT.md create mode 100644 GOVERNANCE.md create mode 100644 SECURITY.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..483cfb173 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog + +This file records user-visible changes. Historical benchmark documents and +release notes remain evidence for their exact commits; they are not rewritten +into current support claims here. + +## Unreleased + +The entries below describe the local Hebrus compatibility bridge after the +current published history. They have no release version or release date and do +not represent a published release. + +### Added + +- Added deterministic, model-free `--capabilities=json` output to the CLI, + server, agent, benchmark, and evaluation executables. The versioned document + reports build identity, backend, executable role, supported model-family IDs, + and the immutable ExpertMajor v2 storage contract. +- Added canonical `hebrus`, `hebrus-server`, `hebrus-agent`, `hebrus-bench`, and + `hebrus-eval` commands. Existing `ds4*` command names remain symlink aliases + to the same binaries for command-surface parity. +- Added model-free tests for capability-schema determinism, cross-executable + consistency, command aliases, build-profile isolation, and canonical/legacy + output parity. +- Added Proposed ADR 0005 to document the intended Hebrus naming and + compatibility boundary without changing durable model, cache, or historical + identifiers. + +### Compatibility + +- The repository remains at until an + administrative rename is actually performed. +- Existing `DS4_*` environment variables and `ds4`-owned serialized identifiers + remain unchanged. This bridge does not introduce `HEBRUS_*` environment + aliases or rename source-level C symbols. +- ExpertMajor tensor names, wire values, model artifact identities, disk-KV + formats, Git history, and historical links are unchanged. diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 000000000..e305a849b --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,20 @@ +cff-version: 1.2.0 +message: >- + If you use this software, please cite it using the metadata in this file and + preserve the upstream acknowledgements in the repository documentation. +title: Hebrus +type: software +authors: + - name: The ds4.c authors +abstract: >- + An open-source inference engine focused on Apple Metal, embedded ExpertMajor + model stores, and SSD streaming for large mixture-of-experts models. +license: MIT +repository-code: "https://github.com/andreaborio/ds4" +url: "https://github.com/andreaborio/ds4" +keywords: + - Apple Metal + - GGUF + - local inference + - mixture of experts + - SSD streaming diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..753e029a6 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic +status, nationality, personal appearance, race, caste, color, religion, or +sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances + of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Project maintainers are responsible for clarifying and enforcing our standards +of acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned with this Code of Conduct, and will communicate reasons +for moderation decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email +address, posting via an official social media account, or acting as an +appointed representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported through GitHub's official abuse-reporting mechanism: + +[Reporting abuse or spam on GitHub][github-abuse] + +The project does not yet publish a dedicated private maintainer contact for +conduct reports. That contact must be established before public launch. Until +then, do not put sensitive conduct reports or personal information in a public +issue. Project maintainers will review conduct matters they receive or observe +fairly and apply the guidelines below. + +All project maintainers are obligated to respect the privacy and security of +the reporter of any incident. + +## Enforcement Guidelines + +Project maintainers will follow these Community Impact Guidelines in +determining the consequences for any action they deem in violation of this +Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from project maintainers, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external +channels like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited +interaction with those enforcing the Code of Conduct, is allowed during this +period. Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available from the [official version 2.1 source][version]. The +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][mozilla]. + +[homepage]: https://www.contributor-covenant.org +[version]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla]: https://github.com/mozilla/diversity +[github-abuse]: https://docs.github.com/en/communities/maintaining-your-safety-on-github/reporting-abuse-or-spam diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 000000000..4894c7374 --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,81 @@ +# Governance + +## Project Model + +This repository uses a lightweight maintainer model. There is no foundation, +steering committee, technical committee, or voting body represented by this +document. Maintainer status is determined by the repository's actual write and +release permissions; this document does not create honorary roles. + +Maintainers have final responsibility for: + +- reviewing, merging, and rejecting changes; +- protecting correctness, compatibility, security, and release quality; +- accepting or rejecting architectural decisions; +- changing the runtime support contract; +- coordinating private security reports; and +- creating tags and publishing releases or artifacts. + +That authority does not override project licenses, preserve a right to remove +authorship or attribution, or weaken the repository's documented validation +gates. + +## Contributions And Review + +Anyone may propose a change through a pull request. The contribution and test +requirements in [`CONTRIBUTING.md`](CONTRIBUTING.md) apply to every change. +Maintainers may request narrower scope, more evidence, changes to ownership +boundaries, or an independent review before merging. + +Reviewers and contributors influence decisions through technical evidence and +discussion, but review activity alone does not grant merge or release +authority. Repository permissions remain the factual source of maintainer +authority. + +## Decision Records And Runtime Authority + +Routine, reversible implementation choices may be decided in pull-request +review. Changes to architecture, compatibility, supported models or backends, +serialized formats, or release policy require a written decision record and +the validation required by the repository guides. + +Accepted records under [`docs/adr/`](docs/adr/) define architectural decisions. +[`docs/contracts/RUNTIME_SUPPORT.md`](docs/contracts/RUNTIME_SUPPORT.md) defines +the current production support boundary. Proposed ADRs describe proposals, not +active policy. In particular, +[`ADR 0005`](docs/adr/0005-hebrus-naming-and-compatibility-boundary.md) remains +Proposed until its acceptance conditions are met. + +When normative documents disagree, the contradiction must be resolved in the +same change. A PR description, benchmark note, or temporary handoff cannot +silently override an accepted ADR, the runtime contract, `CONTRIBUTING.md`, or +`QA_BEFORE_RELEASES.md`. + +## Upstream Relationship + +The repository remains a fork of +[`antirez/ds4`](https://github.com/antirez/ds4). Changes that are general, +reproducible, and applicable to an upstream-supported path follow the +upstream-first classification and submission policy in +[`CONTRIBUTING.md`](CONTRIBUTING.md). Upstream review does not transfer +maintainer authority over this repository, and this governance document does +not imply endorsement by or formal affiliation with upstream. + +## Releases And Security + +A maintainer may publish a release only after the applicable gates in +[`QA_BEFORE_RELEASES.md`](QA_BEFORE_RELEASES.md) are satisfied and the release +scope is documented. Local qualification, an unmerged branch, or an entry under +`Unreleased` is not a published release. + +Undisclosed vulnerabilities follow [`SECURITY.md`](SECURITY.md) and must not be +debated in public issues. GitHub private vulnerability reporting must be +enabled before launch; once it is available, maintainers coordinate +investigation and disclosure through that advisory flow. No response-time or +release-frequency guarantee is created by this document. + +## Changing Governance + +Governance changes use the same public pull-request and review process as other +repository changes. Only a maintainer with the corresponding repository +permissions can merge a governance change or alter access and release rights. diff --git a/README.md b/README.md index 76f325d97..900adc383 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,17 @@ A short-context best is not substituted for the long-context gate. - [Release checklist](QA_BEFORE_RELEASES.md) - [Fork/upstream ledger](FORK_NOTES.md) +### Project and community + +- [Contribution guide](CONTRIBUTING.md) +- [Security policy](SECURITY.md) +- [Code of conduct](CODE_OF_CONDUCT.md) +- [Governance](GOVERNANCE.md) +- [Changelog](CHANGELOG.md) +- [Citation metadata](CITATION.cff) +- [Acknowledgments](ACKNOWLEDGMENTS.md) and + [third-party notices](THIRD_PARTY_NOTICES.md) + ## Project status The engine is beta software; `hebrus-agent` remains alpha. Large-model inference @@ -214,9 +225,9 @@ performance gates, and classify whether the work should also be proposed to `antirez/ds4`. Do not publish vulnerability details in an ordinary issue. Until a dedicated -security policy is added, use the repository's -[private GitHub Security Advisory flow](https://github.com/andreaborio/ds4/security/advisories/new) -when available. +private reporting channel is enabled, follow the zero-disclosure contact +procedure in [SECURITY.md](SECURITY.md). Enabling GitHub private vulnerability +reporting remains a pre-launch administrative gate. ## License and credit diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..3de3d555a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,54 @@ +# Security Policy + +## Reporting A Vulnerability + +The project intends to receive vulnerability reports through GitHub's private +vulnerability reporting flow: + + + +Private vulnerability reporting is not enabled for this repository yet. +Enabling it is a required administrative step before public launch; until then, +the advisory URL above will not accept a report. A reporter may open a minimal +public issue requesting private security contact, but that issue must contain +no vulnerability details, reproduction steps, affected versions, logs, or +attachments. + +Never disclose an undisclosed vulnerability in a public issue, discussion, or +pull request. Public details can expose users before a fix or mitigation is +available. + +A useful private report includes: + +- the affected commit, tag, executable, and runtime configuration; +- a concise description of the impact and the conditions required to trigger + it; +- reproduction steps or a minimal proof of concept, with secrets and model + data removed; +- any known workaround or mitigation; and +- whether the issue has already been disclosed elsewhere. + +Once private reporting is enabled, the maintainers will use the advisory to +investigate, coordinate a fix, and agree on disclosure details with the +reporter. This project does not promise a fixed acknowledgement or remediation +time. Complexity, hardware access, model availability, and impact can all +affect the investigation. + +## Supported Versions + +The project does not currently publish a version-by-version security support +matrix. Include the exact Git commit or tag in every report. Historical tags, +benchmark records, and model artifacts are not by themselves a promise of +ongoing security support. + +## Scope And Disclosure + +Security reports may cover the engine, command-line tools, HTTP server, agent, +model conversion and download tooling, file-format parsing, or build and +release infrastructure in this repository. Model quality problems without a +security impact belong in the normal issue tracker. + +Please keep vulnerability details in the private advisory until the +maintainers and reporter have coordinated a public disclosure. Never attach +private model weights, API keys, access tokens, personal data, or unrelated +system contents to a report. diff --git a/tools/context_budget.json b/tools/context_budget.json index 40d71bbe8..4552604ef 100644 --- a/tools/context_budget.json +++ b/tools/context_budget.json @@ -26,13 +26,19 @@ "maybe_unused_markers": 50, "personal_absolute_path_hits": 0, "root_markdown_files": { - "maximum": 12, + "maximum": 16, "baseline": 10, - "reason": "The open-source launch adds one provenance document and one exact bundled-code notice file; later root documentation still requires explicit classification.", + "reason": "The open-source launch adds explicit provenance, third-party notices, community policy, security policy, governance, and release metadata; later root documentation still requires explicit classification.", "accepted_additions": { "oss_provenance": [ "ACKNOWLEDGMENTS.md", "THIRD_PARTY_NOTICES.md" + ], + "community_and_release_metadata": [ + "CHANGELOG.md", + "CODE_OF_CONDUCT.md", + "GOVERNANCE.md", + "SECURITY.md" ] } } From 7cceb09b201a90d016be9077ecbfaee5ed2b3b0a Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:03:28 +0200 Subject: [PATCH 07/41] cli: present invoked Hebrus command identity --- CHANGELOG.md | 8 ++ ds4_agent.c | 1 + ds4_bench.c | 1 + ds4_cli.c | 1 + ds4_eval.c | 1 + ds4_help.c | 115 ++++++++++++++++-------- ds4_help.h | 5 ++ ds4_server.c | 1 + tests/test_command_aliases.py | 16 +++- tests/test_retired_distributed_flags.sh | 9 +- 10 files changed, 112 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 483cfb173..89c31c0a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,14 @@ not represent a published release. compatibility boundary without changing durable model, cache, or historical identifiers. +### Changed + +- Help headings, usage lines, examples, and retired-option diagnostics now use + the executable name that was invoked. Canonical `hebrus*` commands present + the Hebrus name, while the `ds4*` compatibility aliases preserve their + legacy command identity and the same options, defaults, streams, and exit + codes. + ### Compatibility - The repository remains at until an diff --git a/ds4_agent.c b/ds4_agent.c index 6b9dea9ee..c82655e5d 100644 --- a/ds4_agent.c +++ b/ds4_agent.c @@ -10822,6 +10822,7 @@ static int run_agent(ds4_engine *engine, agent_config *cfg) { #ifndef DS4_AGENT_TEST_NO_MAIN int main(int argc, char **argv) { + hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout); return 0; diff --git a/ds4_bench.c b/ds4_bench.c index 6325ea910..d8fc00e6c 100644 --- a/ds4_bench.c +++ b/ds4_bench.c @@ -561,6 +561,7 @@ static void log_context_memory(ds4_backend backend, } int main(int argc, char **argv) { + hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout); return 0; diff --git a/ds4_cli.c b/ds4_cli.c index 5f28a7a42..0c9bb7dc2 100644 --- a/ds4_cli.c +++ b/ds4_cli.c @@ -1891,6 +1891,7 @@ static cli_config parse_options(int argc, char **argv) { } int main(int argc, char **argv) { + hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout); return 0; diff --git a/ds4_eval.c b/ds4_eval.c index b1b597409..92e763fb4 100644 --- a/ds4_eval.c +++ b/ds4_eval.c @@ -4077,6 +4077,7 @@ static uint32_t eval_residency_context_hint(const eval_config *cfg) { } int main(int argc, char **argv) { + hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout); return 0; diff --git a/ds4_help.c b/ds4_help.c index 7713486c2..ceb4cde46 100644 --- a/ds4_help.c +++ b/ds4_help.c @@ -100,13 +100,24 @@ static bool topic_is(const char *topic, const char *name) { return topic && strcmp(topic, name) == 0; } +static bool help_hebrus_invocation; + +void hebrus_help_set_invocation(const char *argv0) { + const char *name = argv0 ? strrchr(argv0, '/') : NULL; + name = name ? name + 1 : argv0; + help_hebrus_invocation = name && + (streq(name, "hebrus") || streq(name, "hebrus-server") || + streq(name, "hebrus-agent") || streq(name, "hebrus-bench") || + streq(name, "hebrus-eval")); +} + static const char *tool_name(ds4_help_tool tool) { switch (tool) { - case DS4_HELP_DS4: return "ds4"; - case DS4_HELP_SERVER: return "ds4-server"; - case DS4_HELP_AGENT: return "ds4-agent"; - case DS4_HELP_BENCH: return "ds4-bench"; - case DS4_HELP_EVAL: return "ds4-eval"; + case DS4_HELP_DS4: return help_hebrus_invocation ? "hebrus" : "ds4"; + case DS4_HELP_SERVER: return help_hebrus_invocation ? "hebrus-server" : "ds4-server"; + case DS4_HELP_AGENT: return help_hebrus_invocation ? "hebrus-agent" : "ds4-agent"; + case DS4_HELP_BENCH: return help_hebrus_invocation ? "hebrus-bench" : "ds4-bench"; + case DS4_HELP_EVAL: return help_hebrus_invocation ? "hebrus-eval" : "ds4-eval"; } return "ds4"; } @@ -144,28 +155,33 @@ bool ds4_help_reject_retired_distributed_option( return true; } -static const char *tool_usage(ds4_help_tool tool) { +static void print_tool_usage(FILE *fp, ds4_help_tool tool) { switch (tool) { case DS4_HELP_DS4: - return "Usage: ds4 [(-p PROMPT | --prompt-file FILE)] [options]"; + fprintf(fp, "Usage: %s [(-p PROMPT | --prompt-file FILE)] [options]", tool_name(tool)); + return; case DS4_HELP_SERVER: - return "Usage: ds4-server [options]"; + fprintf(fp, "Usage: %s [options]", tool_name(tool)); + return; case DS4_HELP_AGENT: - return "Usage: ds4-agent [options]"; + fprintf(fp, "Usage: %s [options]", tool_name(tool)); + return; case DS4_HELP_BENCH: - return "Usage: ds4-bench (--prompt-file FILE | --chat-prompt-file FILE) [options]"; + fprintf(fp, "Usage: %s (--prompt-file FILE | --chat-prompt-file FILE) [options]", tool_name(tool)); + return; case DS4_HELP_EVAL: - return "Usage: ds4-eval [options]"; + fprintf(fp, "Usage: %s [options]", tool_name(tool)); + return; } - return "Usage: ds4 [options]"; + fprintf(fp, "Usage: %s [options]", tool_name(tool)); } static const char *tool_summary(ds4_help_tool tool) { switch (tool) { case DS4_HELP_DS4: - return "Chat with a local DwarfStar model, run one-shot prompts, or inspect models."; + return "Chat with a local model, run one-shot prompts, or inspect models."; case DS4_HELP_SERVER: - return "Serve one loaded DwarfStar model through OpenAI, Responses, Anthropic, and completion-compatible HTTP APIs."; + return "Serve one loaded model through OpenAI, Responses, Anthropic, and completion-compatible HTTP APIs."; case DS4_HELP_AGENT: return "Run the native terminal coding agent with live tools, session save/restore, and a responsive prompt while the model works."; case DS4_HELP_BENCH: @@ -463,45 +479,71 @@ static void print_more_info(FILE *fp, const help_colors *c, ds4_help_tool tool) fputc('\n', fp); } +static const char *example_command(char *buf, size_t size, ds4_help_tool tool, + const char *arguments) { + snprintf(buf, size, "./%s%s", tool_name(tool), arguments); + return buf; +} + static void print_examples(FILE *fp, const help_colors *c, ds4_help_tool tool, const char *topic) { + char command[256]; title(fp, c, "Examples"); if (topic_is(topic, "distributed")) { para(fp, c, "There is no supported distributed startup command."); } else if (topic_is(topic, "runtime")) { if (tool == DS4_HELP_SERVER) { - opt(fp, c, "Metal API", "./ds4-server -m ds4flash.gguf --metal --ctx 100000"); - opt(fp, c, "quiet API", "./ds4-server --power 60 --host 127.0.0.1 --port 8000"); + opt(fp, c, "Metal API", example_command(command, sizeof(command), tool, + " -m ds4flash.gguf --metal --ctx 100000")); + opt(fp, c, "quiet API", example_command(command, sizeof(command), tool, + " --power 60 --host 127.0.0.1 --port 8000")); } else if (tool == DS4_HELP_AGENT) { - opt(fp, c, "agent", "./ds4-agent -m ds4flash.gguf --ctx 100000"); - opt(fp, c, "quiet agent", "./ds4-agent --power 50"); + opt(fp, c, "agent", example_command(command, sizeof(command), tool, + " -m ds4flash.gguf --ctx 100000")); + opt(fp, c, "quiet agent", example_command(command, sizeof(command), tool, + " --power 50")); } else if (tool == DS4_HELP_BENCH) { - opt(fp, c, "bench", "./ds4-bench --prompt-file long.txt --ctx-max 32768"); - opt(fp, c, "quiet bench", "./ds4-bench --prompt-file long.txt --power 70"); + opt(fp, c, "bench", example_command(command, sizeof(command), tool, + " --prompt-file long.txt --ctx-max 32768")); + opt(fp, c, "quiet bench", example_command(command, sizeof(command), tool, + " --prompt-file long.txt --power 70")); } else if (tool == DS4_HELP_EVAL) { - opt(fp, c, "eval", "./ds4-eval --questions 10 --ctx 100000"); - opt(fp, c, "CPU debug", "./ds4-eval --cpu --questions 1 --tokens 32"); + opt(fp, c, "eval", example_command(command, sizeof(command), tool, + " --questions 10 --ctx 100000")); + opt(fp, c, "CPU debug", example_command(command, sizeof(command), tool, + " --cpu --questions 1 --tokens 32")); } else { - opt(fp, c, "Metal", "./ds4 -m ds4flash.gguf --metal -c 100000"); - opt(fp, c, "quiet thermals", "./ds4 -p \"Summarize README\" --power 50"); + opt(fp, c, "Metal", example_command(command, sizeof(command), tool, + " -m ds4flash.gguf --metal -c 100000")); + opt(fp, c, "quiet thermals", example_command(command, sizeof(command), tool, + " -p \"Summarize README\" --power 50")); } } else if (topic_is(topic, "steering")) { - opt(fp, c, "steer FFN", "./ds4 -p \"Write tersely\" --dir-steering-file dir.bin --dir-steering-ffn 0.8"); + opt(fp, c, "steer FFN", example_command(command, sizeof(command), DS4_HELP_DS4, + " -p \"Write tersely\" --dir-steering-file dir.bin --dir-steering-ffn 0.8")); } else if (tool == DS4_HELP_SERVER || topic_is(topic, "api") || topic_is(topic, "kv-cache")) { - opt(fp, c, "local API", "./ds4-server --ctx 100000 --kv-disk-dir ~/.ds4/server-kv --kv-disk-space-mb 8192"); + opt(fp, c, "local API", example_command(command, sizeof(command), DS4_HELP_SERVER, + " --ctx 100000 --kv-disk-dir ~/.ds4/server-kv --kv-disk-space-mb 8192")); opt(fp, c, "curl", "curl http://127.0.0.1:8000/v1/models"); } else if (tool == DS4_HELP_AGENT || topic_is(topic, "sessions") || topic_is(topic, "tools")) { - opt(fp, c, "interactive", "./ds4-agent"); - opt(fp, c, "one shot", "./ds4-agent --non-interactive -p \"Create /tmp/hello.c\""); + opt(fp, c, "interactive", example_command(command, sizeof(command), DS4_HELP_AGENT, "")); + opt(fp, c, "one shot", example_command(command, sizeof(command), DS4_HELP_AGENT, + " --non-interactive -p \"Create /tmp/hello.c\"")); } else if (tool == DS4_HELP_BENCH || topic_is(topic, "benchmark")) { - opt(fp, c, "csv", "./ds4-bench --prompt-file long.txt --ctx-max 32768 --csv speed.csv"); - opt(fp, c, "prefill only", "./ds4-bench --prompt-file long.txt --gen-tokens 0"); + opt(fp, c, "csv", example_command(command, sizeof(command), DS4_HELP_BENCH, + " --prompt-file long.txt --ctx-max 32768 --csv speed.csv")); + opt(fp, c, "prefill only", example_command(command, sizeof(command), DS4_HELP_BENCH, + " --prompt-file long.txt --gen-tokens 0")); } else if (tool == DS4_HELP_EVAL || topic_is(topic, "evaluation")) { - opt(fp, c, "first 10", "./ds4-eval --questions 10 --trace eval.trace"); - opt(fp, c, "plain", "./ds4-eval --plain --nothink --tokens 512"); + opt(fp, c, "first 10", example_command(command, sizeof(command), DS4_HELP_EVAL, + " --questions 10 --trace eval.trace")); + opt(fp, c, "plain", example_command(command, sizeof(command), DS4_HELP_EVAL, + " --plain --nothink --tokens 512")); } else { - opt(fp, c, "chat", "./ds4"); - opt(fp, c, "one shot", "./ds4 -p \"Explain mmap in C\""); - opt(fp, c, "long prompt", "./ds4 --think-max --prompt-file prompt.txt --ctx 393216"); + opt(fp, c, "chat", example_command(command, sizeof(command), DS4_HELP_DS4, "")); + opt(fp, c, "one shot", example_command(command, sizeof(command), DS4_HELP_DS4, + " -p \"Explain mmap in C\"")); + opt(fp, c, "long prompt", example_command(command, sizeof(command), DS4_HELP_DS4, + " --think-max --prompt-file prompt.txt --ctx 393216")); } fputc('\n', fp); } @@ -578,7 +620,8 @@ void ds4_help_print(FILE *fp, ds4_help_tool tool, const char *topic) { fprintf(fp, "%s%s%s\n", c.bright ? c.bright : "", tool_name(tool), c.off ? c.off : ""); fprintf(fp, "%s\n\n", tool_summary(tool)); - fprintf(fp, "%s\n\n", tool_usage(tool)); + print_tool_usage(fp, tool); + fputs("\n\n", fp); if (topic) print_topic(fp, &c, tool, topic); else { diff --git a/ds4_help.h b/ds4_help.h index a1a32628b..768e66078 100644 --- a/ds4_help.h +++ b/ds4_help.h @@ -12,6 +12,11 @@ typedef enum { DS4_HELP_EVAL, } ds4_help_tool; +/* Select the public command family from argv[0]. Canonical Hebrus binaries and + * legacy DS4 symlinks share one object graph, so help text must use the name + * through which that graph was invoked. */ +void hebrus_help_set_invocation(const char *argv0); + /* Return true after printing the canonical error for a retired distributed * option. Both the exact spelling and --option=value forms are recognized. */ bool ds4_help_reject_retired_distributed_option( diff --git a/ds4_server.c b/ds4_server.c index 6d18f1bbe..aef9da1b7 100644 --- a/ds4_server.c +++ b/ds4_server.c @@ -13351,6 +13351,7 @@ static server_config parse_options(int argc, char **argv) { #ifndef DS4_SERVER_TEST int main(int argc, char **argv) { + hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout); return 0; diff --git a/tests/test_command_aliases.py b/tests/test_command_aliases.py index 00aef7051..a540cda9a 100644 --- a/tests/test_command_aliases.py +++ b/tests/test_command_aliases.py @@ -88,10 +88,16 @@ def validate_pair( for args in PARITY_CASES: canonical_result = run(canonical, args) legacy_result = run(legacy, args) + canonical_name = os.fsencode(canonical.name) + legacy_name = os.fsencode(legacy.name) + + def normalize(data: bytes) -> bytes: + return data.replace(canonical_name, legacy_name) + canonical_record = ( canonical_result.returncode, - canonical_result.stdout, - canonical_result.stderr, + normalize(canonical_result.stdout), + normalize(canonical_result.stderr), ) legacy_record = ( legacy_result.returncode, @@ -101,6 +107,12 @@ def validate_pair( if canonical_record != legacy_record: fail(f"{canonical.name}/{legacy.name} differ for {' '.join(args)}") + if args == ("--help",): + if not canonical_result.stdout.startswith(canonical_name + b"\n"): + fail(f"{canonical.name}: help does not use canonical invocation name") + if not legacy_result.stdout.startswith(legacy_name + b"\n"): + fail(f"{legacy.name}: help does not preserve legacy invocation name") + def main() -> int: parser = argparse.ArgumentParser() diff --git a/tests/test_retired_distributed_flags.sh b/tests/test_retired_distributed_flags.sh index d4a7a7a7c..cb5d34583 100755 --- a/tests/test_retired_distributed_flags.sh +++ b/tests/test_retired_distributed_flags.sh @@ -28,14 +28,7 @@ output_contains() { } diagnostic_name() { - case "$1" in - hebrus) echo ds4 ;; - hebrus-server) echo ds4-server ;; - hebrus-agent) echo ds4-agent ;; - hebrus-bench) echo ds4-bench ;; - hebrus-eval) echo ds4-eval ;; - *) echo "$1" ;; - esac + echo "$1" } for program in $PROGRAMS; do From 95ff84163353c7b4d0ff2b765c4c3a40161eea4b Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:03:29 +0200 Subject: [PATCH 08/41] docs: add Hebrus GitHub community templates --- .github/ISSUE_TEMPLATE/bug_report.yml | 140 ++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/feature_request.yml | 109 ++++++++++++++ .github/ISSUE_TEMPLATE/performance_report.yml | 131 ++++++++++++++++ .github/pull_request_template.md | 83 +++++++++++ 5 files changed, 471 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/ISSUE_TEMPLATE/performance_report.yml create mode 100644 .github/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..45c86b6c8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,140 @@ +name: Bug report +description: Report a reproducible Hebrus runtime, build, API, model-loading, or compatibility problem. +title: "[Bug]: " +body: + - type: markdown + attributes: + value: | + Thanks for helping improve Hebrus. Search existing issues before filing a report. + + For a possible vulnerability, stop here and follow the [security policy](https://github.com/andreaborio/ds4/blob/main/SECURITY.md). Do not post vulnerability details, secrets, private data, model weights, or model blobs in an issue. + + - type: checkboxes + id: prerequisites + attributes: + label: Prerequisites + options: + - label: I searched the open and closed issues for an existing report. + required: true + - label: This is not an undisclosed security vulnerability. + required: true + - label: I will not attach model weights, model blobs, secrets, or private data. + required: true + + - type: textarea + id: summary + attributes: + label: Problem summary + description: Describe the failure and its impact in a few precise sentences. + placeholder: What failed, where, and how does it affect you? + validations: + required: true + + - type: dropdown + id: component + attributes: + label: Affected area + options: + - CLI or inference engine + - Server or API + - Agent mode + - Metal backend + - SSD streaming or ExpertMajor + - GGUF, conversion, or model download + - Build, tests, or documentation + - Compatibility alias or legacy interface + - Other + validations: + required: true + + - type: textarea + id: build + attributes: + label: Build identity + description: Include the exact commit, whether the tree is dirty, and `./hebrus --build-info` when available. + render: shell + placeholder: | + git rev-parse HEAD + git status --short + ./hebrus --build-info + validations: + required: true + + - type: textarea + id: environment + attributes: + label: Environment and resolved execution plan + description: Include Mac model, Apple chip, RAM, macOS build, power mode, selected backend, storage, and the resolved resident/SSD plan. For non-production reference paths, identify the backend explicitly. + placeholder: | + Hardware: + Memory: + OS build: + Power mode: + Storage/model volume: + Backend and resolved plan: + validations: + required: true + + - type: textarea + id: model + attributes: + label: Model artifact identity + description: If model-backed, include family, exact filename, byte size, SHA-256, provenance, and quantization. Do not upload or link redistributed weights unless you are authorized to do so. Write "Not model-backed" when inapplicable. + placeholder: | + Family: + Filename: + Size in bytes: + SHA-256: + Provenance/conversion: + Quantization: + validations: + required: true + + - type: textarea + id: reproduce + attributes: + label: Minimal reproduction + description: Provide the smallest sanitized command or request that reproduces the problem, including relevant flags and environment variables. + render: shell + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What should have happened? + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual behavior and diagnostics + description: Include the exact error and the smallest useful log excerpt. Redact tokens, paths containing personal information, prompts, and private data. + validations: + required: true + + - type: dropdown + id: executable + attributes: + label: Entry point used + options: + - hebrus + - ds4 compatibility alias + - Library or embedded API + - Other + validations: + required: true + + - type: textarea + id: compatibility + attributes: + label: Compatibility observations + description: Note whether the same problem occurs through the canonical Hebrus entry point and any applicable `ds4*` compatibility alias. Include API, exit-code, environment-variable, or on-disk compatibility details when relevant. + + - type: textarea + id: additional + attributes: + label: Additional context + description: Add links to minimal source patches or public logs if useful. Do not attach model files, binaries, large generated outputs, or sensitive data. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..8ab4777ee --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Security policy + url: https://github.com/andreaborio/ds4/blob/main/SECURITY.md + about: Read the private-reporting status before reporting a vulnerability. Do not disclose vulnerability details in a public issue. + - name: Contribution guide + url: https://github.com/andreaborio/ds4/blob/main/CONTRIBUTING.md + about: Review the contribution, validation, evidence, and compatibility requirements before opening a pull request. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..20076297b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,109 @@ +name: Feature request +description: Propose a focused Hebrus capability or support-contract change. +title: "[Feature]: " +body: + - type: markdown + attributes: + value: | + Explain the user problem before the implementation. Hebrus prioritizes Apple Metal inference and SSD streaming while preserving deliberate compatibility and upstream attribution. + + Performance claims belong in the dedicated performance report. Security reports must follow [SECURITY.md](https://github.com/andreaborio/ds4/blob/main/SECURITY.md) and must not be disclosed here. + + - type: checkboxes + id: prerequisites + attributes: + label: Prerequisites + options: + - label: I searched existing issues and discussions for this proposal. + required: true + - label: This request contains no undisclosed vulnerability details, secrets, or model blobs. + required: true + + - type: textarea + id: problem + attributes: + label: Problem and use case + description: Who needs this, what are they trying to do, and what limitation exists today? + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposed behavior + description: Describe observable behavior and acceptance criteria. Keep implementation details separate when possible. + validations: + required: true + + - type: dropdown + id: component + attributes: + label: Primary area + options: + - CLI or inference engine + - Server or API + - Agent mode + - Metal backend + - SSD streaming or ExpertMajor + - GGUF, conversion, or model distribution + - Build, tests, or documentation + - Compatibility or migration + - Other + validations: + required: true + + - type: dropdown + id: contract + attributes: + label: Expected support-contract impact + options: + - No contract change + - Additive behavior within the current production path + - Experimental or reference-only path + - Compatibility or serialized-format change + - Backend or model-family support change + - Unsure + validations: + required: true + + - type: dropdown + id: upstreamability + attributes: + label: Upstreamability classification + description: Choose the classification defined by the contribution policy. + options: + - General, reproducible, and safe for the affected backends + - Potentially general, but not yet proven across the affected paths + - Model-, quant-, or hardware-specific research + - Equivalent change already exists upstream + - Measured regression without a necessary correctness fix + validations: + required: true + + - type: textarea + id: compatibility + attributes: + label: Compatibility and migration impact + description: Cover canonical `hebrus*` interfaces, `ds4*` aliases, `DS4_*` variables, serialized identifiers, GGUF metadata, disk-KV data, API behavior, and exit codes as applicable. State "None" when unaffected. + validations: + required: true + + - type: textarea + id: evidence + attributes: + label: Validation and evidence plan + description: List model-free tests, required model artifacts and hashes, correctness checks, performance frontiers, documentation, and migration coverage needed to accept this feature. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Describe workarounds or smaller designs and why they do not solve the use case. + + - type: textarea + id: references + attributes: + label: References + description: Link specifications, prior art, source code, or public measurements. Do not attach model weights, local benchmark residue, or large binaries. diff --git a/.github/ISSUE_TEMPLATE/performance_report.yml b/.github/ISSUE_TEMPLATE/performance_report.yml new file mode 100644 index 000000000..8d2cdadb0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/performance_report.yml @@ -0,0 +1,131 @@ +name: Performance report +description: Report a reproducible performance regression, result, or optimization opportunity with controlled evidence. +title: "[Performance]: " +body: + - type: markdown + attributes: + value: | + Performance reports must be reproducible and correctness-preserving. Separate cold and warm runs, identify exact artifacts, and disclose memory pressure or competing workloads. + + Do not attach model weights, model blobs, large binaries, secrets, or private prompts. Possible vulnerabilities belong under [SECURITY.md](https://github.com/andreaborio/ds4/blob/main/SECURITY.md), not in a public issue. + + - type: checkboxes + id: prerequisites + attributes: + label: Evidence prerequisites + options: + - label: The result uses an exact build and model artifact identity. + required: true + - label: Correctness was checked before comparing speed or memory. + required: true + - label: Cold and warm measurements are separated, and competing workloads are disclosed. + required: true + - label: I will not attach model weights, model blobs, secrets, or private data. + required: true + + - type: dropdown + id: report_type + attributes: + label: Report type + options: + - Performance regression + - Optimization proposal + - Reproducible benchmark result + - Memory or storage-pressure problem + - Other + validations: + required: true + + - type: textarea + id: summary + attributes: + label: Summary and hypothesis + description: State the observed behavior, the baseline, the candidate, and your current explanation. + validations: + required: true + + - type: textarea + id: build + attributes: + label: Build identities + description: Provide exact baseline and candidate commits, dirty-tree state, compiler/build options, and `./hebrus --build-info` output. + render: shell + validations: + required: true + + - type: textarea + id: artifact + attributes: + label: Model artifact identity + description: Include family, exact filename, size in bytes, SHA-256, provenance/conversion, quantization, and manifest identity if applicable. Do not upload the artifact. + validations: + required: true + + - type: textarea + id: environment + attributes: + label: Hardware and operating environment + description: Include Mac model, Apple chip, RAM, macOS build, power mode, storage device/filesystem, free space, thermal state if known, and all competing processes. + validations: + required: true + + - type: textarea + id: workload + attributes: + label: Workload and resolved plan + description: Include exact sanitized commands, backend, resident/SSD mode, context, prompt/decode lengths, batch settings, thread settings, cache policy, and relevant environment variables. + render: shell + validations: + required: true + + - type: checkboxes + id: frontier + attributes: + label: Performance frontiers covered + description: Select completed lanes. Explain omissions below; do not claim a general promotion from a single lane. + options: + - label: Short, context 128 with at least 128 decoded tokens + - label: Medium, context 2048 with at least 128 decoded tokens + - label: Large, context 8192 with at least 128 decoded tokens + - label: Long, context 32768 with at least 128 decoded tokens + - label: Extended 65K/100K lane when the change affects long-context memory or scheduling + + - type: textarea + id: procedure + attributes: + label: Measurement procedure + description: Describe A/B/B/A ordering or why it is inapplicable, repetition count, warm-up, cold-cache method, sampling tools, and how inherited `DS4_*` variables were excluded or recorded. + validations: + required: true + + - type: textarea + id: correctness + attributes: + label: Correctness and decode evidence + description: Provide parity/cosine/logit checks as appropriate, successful decoded-token evidence, and any quality gates used. Performance without valid output is not a pass. + validations: + required: true + + - type: textarea + id: results + attributes: + label: Results + description: Separate cold and warm values. Include prefill/decode throughput, time to first token, wall time, peak RSS, swap, memory pressure, bytes read, and variance where available. + placeholder: | + Lane | Order | Cold/warm | Baseline | Candidate | Delta | Correctness + validations: + required: true + + - type: textarea + id: conclusion + attributes: + label: Interpretation and requested decision + description: State what the evidence supports, important limitations, and the change or follow-up you propose. + validations: + required: true + + - type: textarea + id: evidence + attributes: + label: Public evidence links + description: Link compact public logs, scripts, or patches. Remove absolute personal paths and sensitive prompts; do not attach model files or large benchmark residue. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..9c61cd88c --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,83 @@ +## Summary + +Explain the user-visible outcome and why this change belongs in Hebrus. + +## Scope + +- [ ] Behavioral or runtime change +- [ ] Mechanical rename or compatibility change +- [ ] Documentation, tests, tooling, or build-only change +- [ ] Performance-sensitive change + +Primary components changed: + +## Upstreamability + +Select one and explain the boundary: + +- [ ] General, reproducible, and safe for the affected backends +- [ ] Potentially general, but not yet proven across the affected paths +- [ ] Model-, quant-, or hardware-specific research +- [ ] Equivalent change already exists upstream +- [ ] Measured regression without a necessary correctness fix + +Rationale and any upstream issue/patch reference: + +## Compatibility contract + +- [ ] `hebrus*` remains the canonical public interface, or the intentional change is documented. +- [ ] Applicable `ds4*` aliases and `DS4_*` environment variables remain compatible, or migration coverage is included. +- [ ] ExpertMajor/GGUF serialized identifiers and metadata remain compatible, or the format change has an ADR, versioning plan, and migration tests. +- [ ] Applicable disk-KV data, server/API behavior, and exit codes remain compatible, or the break is explicitly documented and tested. +- [ ] Not applicable; this change cannot affect public or serialized compatibility. + +Compatibility notes: + +## Validation + +List exact commands and results. Do not write only “tests pass.” + +```text +# command +# result +``` + +Model-free coverage: + +- [ ] Targeted unit/integration tests +- [ ] Compatibility or CLI checks where applicable +- [ ] Documentation links/context audit where applicable +- [ ] `git diff --check` + +## Model-backed evidence + +- [ ] Required and provided below +- [ ] Not applicable; explain why + +When required, include: + +- Exact model family, filename, byte size, SHA-256, provenance/conversion, quantization, and manifest identity. +- Exact hardware, OS build, power state, build identity, backend, and resolved resident/SSD plan. +- Successful decoded-token evidence plus parity/cosine/logit or other appropriate correctness checks. +- Short (128), medium (2048), large (8192), and long (32768) lanes with at least 128 decoded tokens at each frontier when making a general performance claim. +- 65K/100K evidence when changing long-context memory behavior or scheduling. +- A/B/B/A ordering, repeated samples, separate cold/warm results, and memory-pressure/swap observations for performance promotion. + +Evidence or reason it is not applicable: + +## Documentation and release impact + +- [ ] User-facing behavior and current support status are documented. +- [ ] Release QA or support-matrix updates are included when this changes a release contract. +- [ ] No documentation or release change is needed; explain why. + +## Repository hygiene and security + +- [ ] No model weights/blobs, local logs, benchmark scratch, binaries, secrets, private prompts, or personal absolute paths are committed. +- [ ] Generated files have reproducible provenance and are intentionally tracked. +- [ ] This pull request does not publicly disclose an uncoordinated vulnerability; security reports follow [`SECURITY.md`](../SECURITY.md). +- [ ] I reviewed the complete diff, including generated and compatibility changes. + +## Reviewer notes + +Call out risky assumptions, deferred work, and the smallest useful review order. From ac1eb0405389da4650219fa88c7f4172f55e4e63 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:05:51 +0200 Subject: [PATCH 09/41] runtime: distinguish Hebrus and DS4 command identities --- CHANGELOG.md | 3 +++ docs/ENGINE_REFERENCE.md | 16 +++++++++++----- ds4.h | 5 +++-- ds4_agent.c | 4 ++-- ds4_bench.c | 4 ++-- ds4_build.c | 15 ++++++++++----- ds4_cli.c | 4 ++-- ds4_eval.c | 4 ++-- ds4_help.c | 8 ++------ ds4_server.c | 4 ++-- hebrus_identity.h | 20 ++++++++++++++++++++ tests/test_capabilities.py | 11 ++++++++--- tests/test_command_aliases.py | 13 ++++++++++--- 13 files changed, 77 insertions(+), 34 deletions(-) create mode 100644 hebrus_identity.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 89c31c0a4..033a1d35a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,9 @@ not represent a published release. the Hebrus name, while the `ds4*` compatibility aliases preserve their legacy command identity and the same options, defaults, streams, and exit codes. +- Canonical commands report `engine_id: "hebrus"` and `hebrus build`; legacy + aliases retain `engine_id: "ds4"` and `ds4 build`. Both identities use the + same schema-1 capability fields and immutable ExpertMajor wire contract. ### Compatibility diff --git a/docs/ENGINE_REFERENCE.md b/docs/ENGINE_REFERENCE.md index 678ba27d7..d1fd5372e 100644 --- a/docs/ENGINE_REFERENCE.md +++ b/docs/ENGINE_REFERENCE.md @@ -141,8 +141,11 @@ Each build profile links one real executable per role under the canonical `hebrus*` name. The matching `ds4*` command is a relative symlink to that file; it is not a separately compiled or linked wrapper. On macOS both root command sets resolve to the same namespaced Metal binaries. The bridge deliberately -keeps help, diagnostics, `--build-info`, and capability `engine_id` output -identical under both names while downstream consumers migrate. +keeps options, defaults, output streams, exit codes, and runtime behavior +identical while presenting the invoked command identity. Canonical `hebrus*` +commands use Hebrus in help and build metadata; compatibility `ds4*` aliases +retain their legacy command identity. Existing runtime diagnostics remain in +the compatibility namespace during this bridge. Every canonical and compatibility executable accepts `--capabilities=json` without loading a model. @@ -155,7 +158,7 @@ product copy. The bare `--capabilities` spelling is intentionally invalid. ```json { "schema_version": 1, - "engine_id": "ds4", + "engine_id": "hebrus", "build_git_sha": "", "backend": "metal", "executable_role": "server", @@ -172,8 +175,11 @@ product copy. The bare `--capabilities` spelling is intentionally invalid. ``` `backend` is `metal` or `cpu`; `executable_role` is `cli`, `server`, `agent`, -`bench`, or `eval`. The normal build identity is the 12-character Git revision, -optionally followed by `-dirty`; non-Git builds may report `unknown`. +`bench`, or `eval`. Canonical commands report `engine_id: "hebrus"`; legacy +aliases report `engine_id: "ds4"`. Consumers in the bridge window must accept +both values and use the structured fields rather than visible product copy. +The normal build identity is the 12-character Git revision, optionally followed +by `-dirty`; non-Git builds may report `unknown`. Do not rely on the historical `./ds4flash.gguf` symlink for runtime identity. Pass `-m` with an absolute qualified ExpertMajor v2 path and verify its complete diff --git a/ds4.h b/ds4.h index 56b2906db..0d280e107 100644 --- a/ds4.h +++ b/ds4.h @@ -244,10 +244,11 @@ typedef enum { const char *ds4_build_backend(void); const char *ds4_build_arch(void); const char *ds4_build_git_sha(void); -void ds4_build_info_print(FILE *fp); +void ds4_build_info_print(FILE *fp, const char *argv0); bool ds4_build_info_requested(int argc, char **argv); bool ds4_capabilities_requested(int argc, char **argv); -void ds4_capabilities_print(FILE *fp, ds4_executable_role role); +void ds4_capabilities_print(FILE *fp, ds4_executable_role role, + const char *argv0); bool ds4_think_mode_enabled(ds4_think_mode mode); const char *ds4_think_mode_name(ds4_think_mode mode); const char *ds4_think_max_prefix(void); diff --git a/ds4_agent.c b/ds4_agent.c index c82655e5d..ac0945b2b 100644 --- a/ds4_agent.c +++ b/ds4_agent.c @@ -10824,11 +10824,11 @@ static int run_agent(ds4_engine *engine, agent_config *cfg) { int main(int argc, char **argv) { hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { - ds4_build_info_print(stdout); + ds4_build_info_print(stdout, argv[0]); return 0; } if (ds4_capabilities_requested(argc, argv)) { - ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_AGENT); + ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_AGENT, argv[0]); return 0; } agent_config cfg = parse_options(argc, argv); diff --git a/ds4_bench.c b/ds4_bench.c index d8fc00e6c..5c8ecd517 100644 --- a/ds4_bench.c +++ b/ds4_bench.c @@ -563,11 +563,11 @@ static void log_context_memory(ds4_backend backend, int main(int argc, char **argv) { hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { - ds4_build_info_print(stdout); + ds4_build_info_print(stdout, argv[0]); return 0; } if (ds4_capabilities_requested(argc, argv)) { - ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_BENCH); + ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_BENCH, argv[0]); return 0; } bench_config cfg = parse_options(argc, argv); diff --git a/ds4_build.c b/ds4_build.c index a99fe5b36..61468099e 100644 --- a/ds4_build.c +++ b/ds4_build.c @@ -1,5 +1,6 @@ #include "ds4.h" #include "ds4_expert_store.h" +#include "hebrus_identity.h" #include @@ -39,13 +40,14 @@ const char *ds4_build_git_sha(void) { return DS4_BUILD_GIT_SHA; } -void ds4_build_info_print(FILE *fp) { +void ds4_build_info_print(FILE *fp, const char *argv0) { if (!fp) fp = stdout; fprintf(fp, - "ds4 build\n" + "%s build\n" "git: %s\n" "backend: %s\n" "arch: %s\n", + hebrus_is_canonical_invocation(argv0) ? "hebrus" : "ds4", ds4_build_git_sha(), ds4_build_backend(), ds4_build_arch()); @@ -118,12 +120,15 @@ static void ds4_json_string_print(FILE *fp, const char *value) { fputc('"', fp); } -void ds4_capabilities_print(FILE *fp, ds4_executable_role role) { +void ds4_capabilities_print(FILE *fp, ds4_executable_role role, + const char *argv0) { if (!fp) fp = stdout; fputs("{\n" " \"schema_version\": 1,\n" - " \"engine_id\": \"ds4\",\n" - " \"build_git_sha\": ", fp); + " \"engine_id\": ", fp); + ds4_json_string_print(fp, + hebrus_is_canonical_invocation(argv0) ? "hebrus" : "ds4"); + fputs(",\n \"build_git_sha\": ", fp); ds4_json_string_print(fp, ds4_build_git_sha()); fputs(",\n \"backend\": ", fp); ds4_json_string_print(fp, ds4_build_backend()); diff --git a/ds4_cli.c b/ds4_cli.c index 0c9bb7dc2..9619544c2 100644 --- a/ds4_cli.c +++ b/ds4_cli.c @@ -1893,11 +1893,11 @@ static cli_config parse_options(int argc, char **argv) { int main(int argc, char **argv) { hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { - ds4_build_info_print(stdout); + ds4_build_info_print(stdout, argv[0]); return 0; } if (ds4_capabilities_requested(argc, argv)) { - ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_CLI); + ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_CLI, argv[0]); return 0; } cli_config cfg = parse_options(argc, argv); diff --git a/ds4_eval.c b/ds4_eval.c index 92e763fb4..f5b5a74ff 100644 --- a/ds4_eval.c +++ b/ds4_eval.c @@ -4079,11 +4079,11 @@ static uint32_t eval_residency_context_hint(const eval_config *cfg) { int main(int argc, char **argv) { hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { - ds4_build_info_print(stdout); + ds4_build_info_print(stdout, argv[0]); return 0; } if (ds4_capabilities_requested(argc, argv)) { - ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_EVAL); + ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_EVAL, argv[0]); return 0; } eval_config cfg = parse_options(argc, argv); diff --git a/ds4_help.c b/ds4_help.c index ceb4cde46..44ce06ae6 100644 --- a/ds4_help.c +++ b/ds4_help.c @@ -1,4 +1,5 @@ #include "ds4_help.h" +#include "hebrus_identity.h" #include #include @@ -103,12 +104,7 @@ static bool topic_is(const char *topic, const char *name) { static bool help_hebrus_invocation; void hebrus_help_set_invocation(const char *argv0) { - const char *name = argv0 ? strrchr(argv0, '/') : NULL; - name = name ? name + 1 : argv0; - help_hebrus_invocation = name && - (streq(name, "hebrus") || streq(name, "hebrus-server") || - streq(name, "hebrus-agent") || streq(name, "hebrus-bench") || - streq(name, "hebrus-eval")); + help_hebrus_invocation = hebrus_is_canonical_invocation(argv0); } static const char *tool_name(ds4_help_tool tool) { diff --git a/ds4_server.c b/ds4_server.c index aef9da1b7..c1421db5a 100644 --- a/ds4_server.c +++ b/ds4_server.c @@ -13353,11 +13353,11 @@ static server_config parse_options(int argc, char **argv) { int main(int argc, char **argv) { hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { - ds4_build_info_print(stdout); + ds4_build_info_print(stdout, argv[0]); return 0; } if (ds4_capabilities_requested(argc, argv)) { - ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_SERVER); + ds4_capabilities_print(stdout, DS4_EXECUTABLE_ROLE_SERVER, argv[0]); return 0; } signal(SIGPIPE, SIG_IGN); diff --git a/hebrus_identity.h b/hebrus_identity.h new file mode 100644 index 000000000..fd5b9ef5d --- /dev/null +++ b/hebrus_identity.h @@ -0,0 +1,20 @@ +#ifndef HEBRUS_IDENTITY_H +#define HEBRUS_IDENTITY_H + +#include +#include + +static inline const char *hebrus_invocation_name(const char *argv0) { + const char *name = argv0 ? strrchr(argv0, '/') : NULL; + return name ? name + 1 : argv0; +} + +static inline bool hebrus_is_canonical_invocation(const char *argv0) { + const char *name = hebrus_invocation_name(argv0); + return name && + (strcmp(name, "hebrus") == 0 || strcmp(name, "hebrus-server") == 0 || + strcmp(name, "hebrus-agent") == 0 || strcmp(name, "hebrus-bench") == 0 || + strcmp(name, "hebrus-eval") == 0); +} + +#endif diff --git a/tests/test_capabilities.py b/tests/test_capabilities.py index b321a66f6..ae2f7277e 100644 --- a/tests/test_capabilities.py +++ b/tests/test_capabilities.py @@ -68,8 +68,9 @@ def validate(binary: pathlib.Path, backend: str, role: str) -> dict[str, object] if set(document) != ROOT_KEYS: fail(f"{binary}: unexpected root keys: {set(document) ^ ROOT_KEYS}") require_exact_int(document["schema_version"], 1, f"{binary}: schema_version") - if document["engine_id"] != "ds4" or type(document["engine_id"]) is not str: - fail(f"{binary}: invalid engine_id") + expected_engine_id = "hebrus" if binary.name.startswith("hebrus") else "ds4" + if document["engine_id"] != expected_engine_id or type(document["engine_id"]) is not str: + fail(f"{binary}: expected engine_id {expected_engine_id!r}") if type(document["build_git_sha"]) is not str or not document["build_git_sha"]: fail(f"{binary}: invalid build_git_sha") if document["backend"] != backend or type(document["backend"]) is not str: @@ -142,7 +143,11 @@ def main() -> int: if not binary.is_file(): fail(f"missing executable: {binary}") document = validate(binary, args.backend, role) - common = {key: value for key, value in document.items() if key != "executable_role"} + common = { + key: value + for key, value in document.items() + if key not in {"engine_id", "executable_role"} + } if shared is None: shared = common elif common != shared: diff --git a/tests/test_command_aliases.py b/tests/test_command_aliases.py index a540cda9a..beadd16f2 100644 --- a/tests/test_command_aliases.py +++ b/tests/test_command_aliases.py @@ -78,8 +78,11 @@ def validate_pair( if capabilities.returncode != 0 or capabilities.stderr: fail(f"{canonical}: capability invocation failed") document = json.loads(capabilities.stdout) - if document.get("engine_id") != "ds4": - fail(f"{canonical}: compatibility engine_id changed before the brand contract") + if document.get("engine_id") != "hebrus": + fail(f"{canonical}: canonical engine_id is not hebrus") + legacy_document = json.loads(run(legacy, ("--capabilities=json",)).stdout) + if legacy_document.get("engine_id") != "ds4": + fail(f"{legacy}: compatibility engine_id is not ds4") if document.get("backend") != backend: fail(f"{canonical}: expected backend {backend}") if document.get("executable_role") != role: @@ -92,7 +95,11 @@ def validate_pair( legacy_name = os.fsencode(legacy.name) def normalize(data: bytes) -> bytes: - return data.replace(canonical_name, legacy_name) + return ( + data.replace(canonical_name, legacy_name) + .replace(b'"engine_id": "hebrus"', b'"engine_id": "ds4"') + .replace(b"hebrus build", b"ds4 build") + ) canonical_record = ( canonical_result.returncode, From 87a79c6f11916b68c8f512fc2ed6528a9364c3f0 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:09:58 +0200 Subject: [PATCH 10/41] tools: enforce Hebrus brand boundary --- Makefile | 17 +- docs/architecture/CODEMAP.md | 2 + tests/test_brand_boundary_audit.py | 227 +++ tools/brand_boundary.json | 2466 ++++++++++++++++++++++++++++ tools/brand_boundary_audit.py | 523 ++++++ 5 files changed, 3233 insertions(+), 2 deletions(-) create mode 100644 tests/test_brand_boundary_audit.py create mode 100644 tools/brand_boundary.json create mode 100644 tools/brand_boundary_audit.py diff --git a/Makefile b/Makefile index e87ee3530..34d75b47e 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ DS4_PROGRAMS := ds4 ds4-server ds4-bench ds4-eval ds4-agent PROGRAMS := $(HEBRUS_PROGRAMS) $(DS4_PROGRAMS) .PHONY: all help clean test model-free-test premerge context-audit doc-links \ + brand-boundary-audit brand-boundary-test \ imatrix-dataset-check prompt-fixture-check cpu FORCE \ metal build-isolation-test q4k-dot-test qwen-metadata-test \ qwen-reference-test qwen-unicode-test qwen-tokenizer-test \ @@ -40,6 +41,12 @@ PROGRAMS := $(HEBRUS_PROGRAMS) $(DS4_PROGRAMS) download-model-test: tests/test_download_model.sh download_model.sh sh tests/test_download_model.sh +brand-boundary-audit: tools/brand_boundary_audit.py tools/brand_boundary.json + python3 tools/brand_boundary_audit.py --check + +brand-boundary-test: tools/brand_boundary_audit.py tests/test_brand_boundary_audit.py + python3 tests/test_brand_boundary_audit.py + ifeq ($(UNAME_S),Darwin) # A build profile owns every object and binary it produces. In particular, a @@ -86,6 +93,8 @@ help: @echo " Run all Metal gates that do not require a GGUF" @echo " make build-isolation-test" @echo " Prove Metal -> CPU -> Metal cannot mix artifacts" + @echo " make brand-boundary-audit" + @echo " Reject unclassified or increased legacy brand tokens" @echo " make premerge Run context/docs, isolation, and model-free gates" @echo " make clean Remove build outputs and published root binaries" @@ -535,7 +544,8 @@ prompt-fixture-check: # Build isolation removes and rebuilds BUILD_ROOT, so model-free-test must start # only after it completes even when an agent invokes `make -j premerge`. -premerge: context-audit doc-links imatrix-dataset-check prompt-fixture-check build-isolation-test +premerge: context-audit doc-links brand-boundary-audit brand-boundary-test \ + imatrix-dataset-check prompt-fixture-check build-isolation-test $(MAKE) model-free-test git diff --check @@ -558,6 +568,8 @@ help: @echo " make / make cpu Build ./hebrus* plus ./ds4* aliases" @echo " make test Build and run tests" @echo " make model-free-test Run all tests that do not require a GGUF" + @echo " make brand-boundary-audit" + @echo " Reject unclassified or increased legacy brand tokens" @echo " make clean Remove build outputs" cpu: $(PROGRAMS) @@ -737,7 +749,8 @@ imatrix-dataset-check: prompt-fixture-check: python3 speed-bench/build_long_context_prompt.py --check -premerge: context-audit doc-links imatrix-dataset-check prompt-fixture-check model-free-test +premerge: context-audit doc-links brand-boundary-audit brand-boundary-test \ + imatrix-dataset-check prompt-fixture-check model-free-test git diff --check q4k-dot-test: tests/test_q4k_dot.c diff --git a/docs/architecture/CODEMAP.md b/docs/architecture/CODEMAP.md index e668655bc..73e969d7e 100644 --- a/docs/architecture/CODEMAP.md +++ b/docs/architecture/CODEMAP.md @@ -106,6 +106,8 @@ not validated. | `tests/` | Model-free, model-backed, kernel, tokenizer, server, and build-isolation regressions | | `tests/test_capabilities.py` | Exact schema and cross-executable checks for the model-free build/capability contract | | `tests/test_command_aliases.py` | Canonical/legacy symlink layout, binary identity, and CLI-output parity checks | +| `tools/brand_boundary.json` + `tools/brand_boundary_audit.py` | Explicit per-file legacy `ds4`/`DS4`/`DwarfStar` classification and monotonic count ceilings; `--check` rejects new groups and increases, while `--refresh` requires exact authorizations before widening a ceiling | +| `tests/test_brand_boundary_audit.py` | Fail-closed fixtures for new files and tokens, increases, reductions, deterministic refresh, and invalid manifests | | `tests/qwen/` | Qwen fixtures, provenance, reference collectors, and model-specific gates | | `tests/test-vectors/` | Official and local continuation vectors plus provenance | | `gguf-tools/` | Quantization, ExpertMajor conversion, imatrix, and quality-scoring tools | diff --git a/tests/test_brand_boundary_audit.py b/tests/test_brand_boundary_audit.py new file mode 100644 index 000000000..ef7d774de --- /dev/null +++ b/tests/test_brand_boundary_audit.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +"""Regression tests for the monotonic legacy-brand inventory.""" + +from __future__ import annotations + +import json +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +AUDIT = REPOSITORY_ROOT / "tools" / "brand_boundary_audit.py" +CATEGORIES = ( + "serialized/permanent", + "compatibility", + "historical-attribution", + "migration-pending", +) +LOCATION_ORDER = {"path": 0, "content": 1} +TOKEN_ORDER = {"ds4": 0, "DS4": 1, "DwarfStar": 2} + + +def manifest_document(entries: list[dict[str, object]]) -> dict[str, object]: + ordered = sorted( + entries, + key=lambda entry: ( + entry["path"], + LOCATION_ORDER[entry["location"]], + TOKEN_ORDER[entry["token"]], + ), + ) + return { + "schema_version": 1, + "scope": { + "tokens": ["ds4", "DS4", "DwarfStar"], + "locations": ["path", "content"], + "excluded_paths": ["tools/brand_boundary.json"], + }, + "category_definitions": { + category: f"Test definition for {category}." for category in CATEGORIES + }, + "refresh_policy": { + "reductions": "python3 tools/brand_boundary_audit.py --refresh", + "existing_increase": ( + "python3 tools/brand_boundary_audit.py --refresh " + "--accept-increase PATH:LOCATION:TOKEN" + ), + "new_group": ( + "python3 tools/brand_boundary_audit.py --refresh " + "--classify PATH:LOCATION:TOKEN=CLASSIFICATION" + ), + }, + "entries": ordered, + } + + +def entry( + path: str, + location: str, + token: str, + maximum: int, + classification: str = "migration-pending", +) -> dict[str, object]: + return { + "path": path, + "location": location, + "token": token, + "classification": classification, + "maximum": maximum, + } + + +class Fixture: + def __init__(self) -> None: + self.temporary = tempfile.TemporaryDirectory(prefix="brand-boundary-") + self.root = Path(self.temporary.name) + self.manifest = self.root / "tools" / "brand_boundary.json" + self.manifest.parent.mkdir() + subprocess.run(["git", "init", "-q"], cwd=self.root, check=True) + + def close(self) -> None: + self.temporary.cleanup() + + def write(self, relative: str, content: str) -> None: + path = self.root / relative + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding="utf-8") + + def track(self, *relative: str) -> None: + subprocess.run(["git", "add", "--", *relative], cwd=self.root, check=True) + + def write_manifest(self, entries: list[dict[str, object]]) -> None: + self.manifest.write_text( + json.dumps(manifest_document(entries), indent=2) + "\n", + encoding="utf-8", + ) + + def run(self, *arguments: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [ + sys.executable, + str(AUDIT), + "--root", + str(self.root), + "--manifest", + "tools/brand_boundary.json", + *arguments, + ], + text=True, + capture_output=True, + check=False, + ) + + +class BrandBoundaryAuditTests(unittest.TestCase): + def setUp(self) -> None: + self.fixture = Fixture() + + def tearDown(self) -> None: + self.fixture.close() + + def seed_one_token(self) -> None: + self.fixture.write("engine.c", "const char *engine = \"ds4\";\n") + self.fixture.track("engine.c") + self.fixture.write_manifest([entry("engine.c", "content", "ds4", 1)]) + + def test_new_tracked_file_requires_exact_classification(self) -> None: + self.seed_one_token() + self.fixture.write("ds4_notes.txt", "brand bridge\n") + self.fixture.track("ds4_notes.txt") + + checked = self.fixture.run("--check") + self.assertEqual(checked.returncode, 1, checked.stderr) + self.assertIn( + "unclassified brand token group: ds4_notes.txt:path:ds4", + checked.stderr, + ) + + refused = self.fixture.run("--refresh") + self.assertEqual(refused.returncode, 1, refused.stderr) + + refreshed = self.fixture.run( + "--refresh", + "--classify", + "ds4_notes.txt:path:ds4=migration-pending", + ) + self.assertEqual(refreshed.returncode, 0, refreshed.stderr) + self.assertEqual(self.fixture.run("--check").returncode, 0) + + def test_new_token_in_existing_file_is_unclassified(self) -> None: + self.seed_one_token() + self.fixture.write("engine.c", "const char *engine = \"ds4 DS4\";\n") + + checked = self.fixture.run("--check") + self.assertEqual(checked.returncode, 1, checked.stderr) + self.assertIn( + "unclassified brand token group: engine.c:content:DS4", + checked.stderr, + ) + + def test_increase_fails_until_exactly_authorized(self) -> None: + self.seed_one_token() + self.fixture.write("engine.c", "const char *engine = \"ds4 ds4\";\n") + + checked = self.fixture.run("--check") + self.assertEqual(checked.returncode, 1, checked.stderr) + self.assertIn("engine.c:content:ds4 2 > 1", checked.stderr) + + original = self.fixture.manifest.read_bytes() + refused = self.fixture.run("--refresh") + self.assertEqual(refused.returncode, 1, refused.stderr) + self.assertEqual(self.fixture.manifest.read_bytes(), original) + + refreshed = self.fixture.run( + "--refresh", "--accept-increase", "engine.c:content:ds4" + ) + self.assertEqual(refreshed.returncode, 0, refreshed.stderr) + self.assertEqual(self.fixture.run("--check").returncode, 0) + document = json.loads(self.fixture.manifest.read_text(encoding="utf-8")) + self.assertEqual(document["entries"][0]["maximum"], 2) + + def test_reduction_passes_and_refresh_tightens_deterministically(self) -> None: + self.fixture.write("engine.c", "const char *engine = \"ds4\";\n") + self.fixture.track("engine.c") + self.fixture.write_manifest([entry("engine.c", "content", "ds4", 2)]) + + checked = self.fixture.run("--check") + self.assertEqual(checked.returncode, 0, checked.stderr) + self.assertIn("1 reductions", checked.stdout) + + refreshed = self.fixture.run("--refresh") + self.assertEqual(refreshed.returncode, 0, refreshed.stderr) + first = self.fixture.manifest.read_bytes() + self.assertEqual(self.fixture.run("--refresh").returncode, 0) + self.assertEqual(self.fixture.manifest.read_bytes(), first) + + self.fixture.write("engine.c", "const char *engine = \"ds4 ds4\";\n") + increased = self.fixture.run("--check") + self.assertEqual(increased.returncode, 1, increased.stderr) + self.assertIn("2 > 1", increased.stderr) + + def test_invalid_manifest_fails_closed(self) -> None: + self.seed_one_token() + document = json.loads(self.fixture.manifest.read_text(encoding="utf-8")) + document["entries"][0]["path"] = "**/*.c" + self.fixture.manifest.write_text( + json.dumps(document, indent=2) + "\n", encoding="utf-8" + ) + + checked = self.fixture.run("--check") + self.assertEqual(checked.returncode, 2, checked.stderr) + self.assertIn("must be an explicit normalized path", checked.stderr) + + def test_unused_refresh_authorization_fails_closed(self) -> None: + self.seed_one_token() + refreshed = self.fixture.run( + "--refresh", "--accept-increase", "engine.c:content:ds4" + ) + self.assertEqual(refreshed.returncode, 2, refreshed.stderr) + self.assertIn("does not match a current increase", refreshed.stderr) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json new file mode 100644 index 000000000..475800438 --- /dev/null +++ b/tools/brand_boundary.json @@ -0,0 +1,2466 @@ +{ + "schema_version": 1, + "scope": { + "tokens": [ + "ds4", + "DS4", + "DwarfStar" + ], + "locations": [ + "path", + "content" + ], + "excluded_paths": [ + "tools/brand_boundary.json" + ] + }, + "category_definitions": { + "serialized/permanent": "Identifiers embedded in versioned fixtures, wire schemas, on-disk data, or published artifact identities that must not be renamed by the brand migration.", + "compatibility": "Legacy commands, environment names, repository identities, probes, and bridge contracts retained for existing users and integrations.", + "historical-attribution": "Fork ancestry, authorship, third-party credit, release records, and benchmark evidence that must preserve their historical spelling.", + "migration-pending": "Active source, tests, tooling, or current documentation whose internal legacy spelling remains to be handled by a later mechanical migration." + }, + "refresh_policy": { + "reductions": "python3 tools/brand_boundary_audit.py --refresh", + "existing_increase": "python3 tools/brand_boundary_audit.py --refresh --accept-increase 'PATH:LOCATION:TOKEN'", + "new_group": "python3 tools/brand_boundary_audit.py --refresh --classify 'PATH:LOCATION:TOKEN=CLASSIFICATION'" + }, + "entries": [ + { + "path": ".gitignore", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 11 + }, + { + "path": "ACKNOWLEDGMENTS.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 3 + }, + { + "path": "CHANGELOG.md", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 3 + }, + { + "path": "CHANGELOG.md", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": "CITATION.cff", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 3 + }, + { + "path": "CONTRIBUTING.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 22 + }, + { + "path": "CONTRIBUTING.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 8 + }, + { + "path": "CONTRIBUTING.md", + "location": "content", + "token": "DwarfStar", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "EXPERT_PRUNE.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "EXPERT_PRUNE.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 12 + }, + { + "path": "FORK_NOTES.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 20 + }, + { + "path": "FORK_NOTES.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 2 + }, + { + "path": "FORK_NOTES.md", + "location": "content", + "token": "DwarfStar", + "classification": "historical-attribution", + "maximum": 1 + }, + { + "path": "GOLD_METAL_SSD.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 8 + }, + { + "path": "GOLD_METAL_SSD.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 6 + }, + { + "path": "GOVERNANCE.md", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 2 + }, + { + "path": "LICENSE", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 1 + }, + { + "path": "MERGE_LOG.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 29 + }, + { + "path": "MERGE_LOG.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 5 + }, + { + "path": "Makefile", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 504 + }, + { + "path": "Makefile", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 41 + }, + { + "path": "ONEDGE_IMATRIX.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "ONEDGE_IMATRIX.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "QA_BEFORE_RELEASES.md", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 26 + }, + { + "path": "QA_BEFORE_RELEASES.md", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 10 + }, + { + "path": "QA_BEFORE_RELEASES.md", + "location": "content", + "token": "DwarfStar", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": "README.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 11 + }, + { + "path": "README.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "SECURITY.md", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": "SSD_STREAMING_VERIFICATION.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "SSD_STREAMING_VERIFICATION.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 11 + }, + { + "path": "dir-steering/README.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 12 + }, + { + "path": "dir-steering/README.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "dir-steering/out/verbosity.json", + "location": "content", + "token": "ds4", + "classification": "serialized/permanent", + "maximum": 1 + }, + { + "path": "dir-steering/tools/build_direction.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 18 + }, + { + "path": "dir-steering/tools/build_direction.py", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 5 + }, + { + "path": "dir-steering/tools/run_sweep.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 6 + }, + { + "path": "docs/ENGINE_REFERENCE.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 81 + }, + { + "path": "docs/ENGINE_REFERENCE.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 41 + }, + { + "path": "docs/ENGINE_REFERENCE.md", + "location": "content", + "token": "DwarfStar", + "classification": "migration-pending", + "maximum": 7 + }, + { + "path": "docs/adr/0001-expert-major-v2-only.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "docs/adr/0005-hebrus-naming-and-compatibility-boundary.md", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 7 + }, + { + "path": "docs/adr/0005-hebrus-naming-and-compatibility-boundary.md", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 4 + }, + { + "path": "docs/architecture/CODEMAP.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 47 + }, + { + "path": "docs/architecture/CODEMAP.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "docs/architecture/CODEMAP.md", + "location": "content", + "token": "DwarfStar", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "docs/benchmarks/2026-07-14-m5-pro.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 14 + }, + { + "path": "docs/benchmarks/2026-07-14-m5-pro.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 5 + }, + { + "path": "docs/benchmarks/2026-07-15-qwen-ds4-vs-llamacpp.md", + "location": "path", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 1 + }, + { + "path": "docs/benchmarks/2026-07-15-qwen-ds4-vs-llamacpp.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 7 + }, + { + "path": "docs/benchmarks/2026-07-15-qwen-ds4-vs-llamacpp.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 19 + }, + { + "path": "docs/benchmarks/2026-07-17-deepseek-native-expert-major.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 9 + }, + { + "path": "docs/benchmarks/2026-07-17-deepseek-native-expert-major.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 15 + }, + { + "path": "docs/benchmarks/2026-07-17-deepseek-qwen-transfer-audit.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 2 + }, + { + "path": "docs/benchmarks/2026-07-17-deepseek-qwen-transfer-audit.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 4 + }, + { + "path": "docs/benchmarks/2026-07-17-qwen-native-expert-major.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 1 + }, + { + "path": "docs/benchmarks/2026-07-20-agent-friendly-refactor-validation.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 6 + }, + { + "path": "docs/benchmarks/2026-07-20-agent-friendly-refactor-validation.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 8 + }, + { + "path": "docs/benchmarks/2026-07-20-glm52-expert-major-v2.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 2 + }, + { + "path": "docs/benchmarks/2026-07-20-glm52-expert-major-v2.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 7 + }, + { + "path": "docs/benchmarks/2026-07-20-long-context-metal-stack.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 8 + }, + { + "path": "docs/benchmarks/2026-07-20-qwen-expert-major-v2.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 2 + }, + { + "path": "docs/benchmarks/2026-07-20-qwen-expert-major-v2.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 4 + }, + { + "path": "docs/benchmarks/2026-07-21-qwen-f32-router-nax-prefill.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 6 + }, + { + "path": "docs/benchmarks/2026-07-21-qwen-mlx-affine-nax-prefill.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 4 + }, + { + "path": "docs/benchmarks/2026-07-21-qwen-q4-paired-mmid-prefill.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 1 + }, + { + "path": "docs/benchmarks/2026-07-21-qwen-q4-paired-mmid-prefill.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 1 + }, + { + "path": "docs/benchmarks/2026-07-21-qwen-split-k-hardware-policy.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 6 + }, + { + "path": "docs/benchmarks/2026-07-21-qwen-unified-affine-auto-ssd.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 2 + }, + { + "path": "docs/benchmarks/2026-07-21-qwen-unified-affine-auto-ssd.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 4 + }, + { + "path": "docs/benchmarks/2026-07-21-upstream-metal-transfer-audit.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 11 + }, + { + "path": "docs/benchmarks/README.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 2 + }, + { + "path": "docs/contracts/RUNTIME_SUPPORT.md", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": "docs/contracts/RUNTIME_SUPPORT.md", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 3 + }, + { + "path": "docs/deepseek-expert-major-v2.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 7 + }, + { + "path": "docs/deepseek-expert-major-v2.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 7 + }, + { + "path": "docs/expert-major-v2-roadmap.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "docs/expert-major-v2-roadmap.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "docs/glm52-expert-major-v2.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "docs/glm52-expert-major-v2.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 13 + }, + { + "path": "docs/qwen-expert-major-store.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 8 + }, + { + "path": "docs/qwen-expert-major-store.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 8 + }, + { + "path": "docs/releases/v0.1.0.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 7 + }, + { + "path": "docs/releases/v0.1.0.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 3 + }, + { + "path": "docs/releases/v0.1.0.md", + "location": "content", + "token": "DwarfStar", + "classification": "historical-attribution", + "maximum": 1 + }, + { + "path": "docs/releases/v0.2.0.md", + "location": "content", + "token": "ds4", + "classification": "historical-attribution", + "maximum": 2 + }, + { + "path": "docs/releases/v0.2.0.md", + "location": "content", + "token": "DS4", + "classification": "historical-attribution", + "maximum": 4 + }, + { + "path": "docs/releases/v0.2.0.md", + "location": "content", + "token": "DwarfStar", + "classification": "historical-attribution", + "maximum": 1 + }, + { + "path": "download_model.sh", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 6 + }, + { + "path": "download_model.sh", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "ds4.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 5228 + }, + { + "path": "ds4.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 4465 + }, + { + "path": "ds4.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 320 + }, + { + "path": "ds4.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 40 + }, + { + "path": "ds4_agent.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_agent.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 354 + }, + { + "path": "ds4_agent.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 45 + }, + { + "path": "ds4_agent.c", + "location": "content", + "token": "DwarfStar", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_bench.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_bench.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 147 + }, + { + "path": "ds4_bench.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 21 + }, + { + "path": "ds4_build.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_build.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 25 + }, + { + "path": "ds4_build.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 20 + }, + { + "path": "ds4_cli.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_cli.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 403 + }, + { + "path": "ds4_cli.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 45 + }, + { + "path": "ds4_eval.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_eval.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 158 + }, + { + "path": "ds4_eval.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 22 + }, + { + "path": "ds4_expert_store.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_expert_store.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 31 + }, + { + "path": "ds4_expert_store.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 13 + }, + { + "path": "ds4_expert_store.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_expert_store.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 29 + }, + { + "path": "ds4_expert_store.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 21 + }, + { + "path": "ds4_gpu.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_gpu.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 719 + }, + { + "path": "ds4_gpu.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "ds4_help.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_help.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 57 + }, + { + "path": "ds4_help.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 81 + }, + { + "path": "ds4_help.c", + "location": "content", + "token": "DwarfStar", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "ds4_help.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_help.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 5 + }, + { + "path": "ds4_help.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 8 + }, + { + "path": "ds4_kvstore.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_kvstore.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 233 + }, + { + "path": "ds4_kvstore.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 52 + }, + { + "path": "ds4_kvstore.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_kvstore.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 101 + }, + { + "path": "ds4_kvstore.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 20 + }, + { + "path": "ds4_metal.m", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_metal.m", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 6573 + }, + { + "path": "ds4_metal.m", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 1041 + }, + { + "path": "ds4_profile.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_profile.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 13 + }, + { + "path": "ds4_profile.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 10 + }, + { + "path": "ds4_profile.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_profile.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 8 + }, + { + "path": "ds4_profile.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "ds4_qwen.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_qwen.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 51 + }, + { + "path": "ds4_qwen.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_qwen.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 44 + }, + { + "path": "ds4_qwen.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "ds4_qwen_expert_group.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_qwen_expert_group.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 31 + }, + { + "path": "ds4_qwen_expert_group.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 21 + }, + { + "path": "ds4_qwen_expert_group.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_qwen_expert_group.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 26 + }, + { + "path": "ds4_qwen_expert_group.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 8 + }, + { + "path": "ds4_qwen_ref.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_qwen_ref.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 12 + }, + { + "path": "ds4_qwen_ref.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_qwen_ref.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 11 + }, + { + "path": "ds4_qwen_ref.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "ds4_qwen_unicode.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_qwen_unicode.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "ds4_qwen_unicode.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "ds4_qwen_unicode.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_qwen_unicode.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 6 + }, + { + "path": "ds4_qwen_unicode.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 6 + }, + { + "path": "ds4_qwen_unicode_data.inc", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_server.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_server.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 476 + }, + { + "path": "ds4_server.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 565 + }, + { + "path": "ds4_ssd.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_ssd.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 71 + }, + { + "path": "ds4_ssd.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 83 + }, + { + "path": "ds4_ssd.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_ssd.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 74 + }, + { + "path": "ds4_ssd.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 17 + }, + { + "path": "ds4_streaming_hotlist.inc", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_streaming_hotlist.inc", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "ds4_streaming_hotlist_glm52.inc", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_streaming_hotlist_glm52.inc", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "ds4_web.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_web.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 30 + }, + { + "path": "ds4_web.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 17 + }, + { + "path": "ds4_web.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "ds4_web.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 18 + }, + { + "path": "ds4_web.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "gguf-tools/Makefile", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 32 + }, + { + "path": "gguf-tools/Makefile", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "gguf-tools/README.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 14 + }, + { + "path": "gguf-tools/README.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 11 + }, + { + "path": "gguf-tools/deepseek4-quantize.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 47 + }, + { + "path": "gguf-tools/deepseek4-quantize.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 54 + }, + { + "path": "gguf-tools/ds4-expert-major.py", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "gguf-tools/ds4-expert-major.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "gguf-tools/ds4-expert-major.py", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 5 + }, + { + "path": "gguf-tools/imatrix/README.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "gguf-tools/imatrix/README.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 12 + }, + { + "path": "gguf-tools/imatrix/dataset/README.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "gguf-tools/imatrix/dataset/README.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "gguf-tools/imatrix/dataset/build_ds4_imatrix_dataset.py", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "gguf-tools/imatrix/dataset/build_ds4_imatrix_dataset.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 131 + }, + { + "path": "gguf-tools/imatrix/dataset/build_ds4_imatrix_dataset.py", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 8 + }, + { + "path": "gguf-tools/imatrix/dataset/build_ds4_imatrix_dataset.py", + "location": "content", + "token": "DwarfStar", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "gguf-tools/quality-testing/README.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "gguf-tools/quality-testing/score_official.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 28 + }, + { + "path": "gguf-tools/quality-testing/score_official.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "gguf-tools/quants.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 146 + }, + { + "path": "gguf-tools/quants.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 89 + }, + { + "path": "gguf-tools/quants.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 20 + }, + { + "path": "gguf-tools/quants.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 40 + }, + { + "path": "linenoise.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "metal/argsort.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 8 + }, + { + "path": "metal/argsort.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 10 + }, + { + "path": "metal/bin.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 5 + }, + { + "path": "metal/bin.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "metal/concat.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/concat.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/cpy.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/cpy.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/dense.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 28 + }, + { + "path": "metal/dense.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 13 + }, + { + "path": "metal/dsv4_hc.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 27 + }, + { + "path": "metal/dsv4_hc.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 5 + }, + { + "path": "metal/dsv4_kv.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 10 + }, + { + "path": "metal/dsv4_kv.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 5 + }, + { + "path": "metal/dsv4_misc.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 79 + }, + { + "path": "metal/dsv4_misc.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 15 + }, + { + "path": "metal/dsv4_rope.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/dsv4_rope.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/flash_attn.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 11 + }, + { + "path": "metal/flash_attn.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 6 + }, + { + "path": "metal/get_rows.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "metal/get_rows.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/glu.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "metal/glu.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "metal/moe.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 231 + }, + { + "path": "metal/moe.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 11 + }, + { + "path": "metal/norm.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 5 + }, + { + "path": "metal/norm.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "metal/qwen35.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 43 + }, + { + "path": "metal/qwen35.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/repeat.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/repeat.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/set_rows.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/set_rows.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/softmax.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "metal/softmax.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "metal/sum_rows.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/sum_rows.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "metal/unary.metal", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "metal/unary.metal", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "runtime/ds4_deepseek_cache_phase.inc", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "runtime/ds4_deepseek_cache_phase.inc", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 29 + }, + { + "path": "runtime/ds4_deepseek_cache_phase.inc", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 5 + }, + { + "path": "runtime/ds4_glm_graph.inc", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "runtime/ds4_glm_graph.inc", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 779 + }, + { + "path": "runtime/ds4_glm_graph.inc", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 774 + }, + { + "path": "runtime/ds4_metal_glm.inc", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "runtime/ds4_metal_glm.inc", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1104 + }, + { + "path": "runtime/ds4_metal_glm.inc", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 58 + }, + { + "path": "runtime/ds4_qwen_memory_policy.inc", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "runtime/ds4_qwen_memory_policy.inc", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 14 + }, + { + "path": "runtime/ds4_qwen_memory_policy.inc", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "speed-bench/README.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "speed-bench/README.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "speed-bench/build_long_context_prompt.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "speed-bench/plot_speed.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "speed-bench/run_m5_dsflash_arm.sh", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 11 + }, + { + "path": "speed-bench/run_m5_dsflash_arm.sh", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 59 + }, + { + "path": "tests/ds4_agent_test.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/ds4_agent_test.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "tests/ds4_test.c", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/ds4_test.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 715 + }, + { + "path": "tests/ds4_test.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 143 + }, + { + "path": "tests/gen_qwen_unicode.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "tests/generate_long_context_story_prompt.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/glm_long_context_smoke.sh", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "tests/glm_long_context_smoke.sh", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 10 + }, + { + "path": "tests/internal/ds4_agent_unit.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/internal/ds4_agent_unit.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/internal/ds4_agent_unit.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "tests/internal/ds4_agent_unit.inc", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/internal/ds4_agent_unit.inc", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/internal/ds4_qwen_cpu_test_hooks.h", + "location": "path", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/internal/ds4_qwen_cpu_test_hooks.h", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 7 + }, + { + "path": "tests/internal/ds4_qwen_cpu_test_hooks.h", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "tests/qwen/README.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 6 + }, + { + "path": "tests/qwen/README.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "tests/qwen/UNICODE_DATA_PROVENANCE.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/qwen/compare_logits.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 81 + }, + { + "path": "tests/qwen/compare_logits.py", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 25 + }, + { + "path": "tests/qwen/qwen_unicode_ucd_cache.txt", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/qwen/test_compare_logits.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 39 + }, + { + "path": "tests/qwen/test_qwen35_metal.m", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 5 + }, + { + "path": "tests/qwen/test_qwen35_metal.m", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "tests/test-vectors/README.md", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 5 + }, + { + "path": "tests/test-vectors/README.md", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "tests/test-vectors/fetch_official_vectors.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "tests/test-vectors/local-golden.vec", + "location": "content", + "token": "ds4", + "classification": "serialized/permanent", + "maximum": 1 + }, + { + "path": "tests/test-vectors/manifest.json", + "location": "content", + "token": "ds4", + "classification": "serialized/permanent", + "maximum": 1 + }, + { + "path": "tests/test-vectors/official.vec", + "location": "content", + "token": "ds4", + "classification": "serialized/permanent", + "maximum": 1 + }, + { + "path": "tests/test-vectors/official/long_code_audit.official.json", + "location": "content", + "token": "ds4", + "classification": "serialized/permanent", + "maximum": 1 + }, + { + "path": "tests/test-vectors/official/long_memory_archive.official.json", + "location": "content", + "token": "ds4", + "classification": "serialized/permanent", + "maximum": 1 + }, + { + "path": "tests/test-vectors/official/short_code_completion.official.json", + "location": "content", + "token": "ds4", + "classification": "serialized/permanent", + "maximum": 1 + }, + { + "path": "tests/test-vectors/official/short_italian_fact.official.json", + "location": "content", + "token": "ds4", + "classification": "serialized/permanent", + "maximum": 1 + }, + { + "path": "tests/test-vectors/official/short_reasoning_plain.official.json", + "location": "content", + "token": "ds4", + "classification": "serialized/permanent", + "maximum": 1 + }, + { + "path": "tests/test_benchmark_env_guard.sh", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "tests/test_brand_boundary_audit.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 20 + }, + { + "path": "tests/test_brand_boundary_audit.py", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "tests/test_brand_boundary_audit.py", + "location": "content", + "token": "DwarfStar", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "tests/test_build_isolation.sh", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "tests/test_capabilities.py", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 7 + }, + { + "path": "tests/test_command_aliases.py", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 6 + }, + { + "path": "tests/test_command_aliases.py", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": "tests/test_download_model.sh", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 4 + }, + { + "path": "tests/test_download_model.sh", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 14 + }, + { + "path": "tests/test_expert_major.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 3 + }, + { + "path": "tests/test_expert_major.py", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/test_expert_store.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 13 + }, + { + "path": "tests/test_expert_store.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "tests/test_metal_ssd_profile.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 16 + }, + { + "path": "tests/test_metal_ssd_profile.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 19 + }, + { + "path": "tests/test_q4k_dot.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 1 + }, + { + "path": "tests/test_q4k_dot.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "tests/test_q4k_top8.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 8 + }, + { + "path": "tests/test_qwen_attention_ref.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 27 + }, + { + "path": "tests/test_qwen_expert_group.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 63 + }, + { + "path": "tests/test_qwen_expert_group.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 44 + }, + { + "path": "tests/test_qwen_gdn_ref.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 31 + }, + { + "path": "tests/test_qwen_metadata.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 10 + }, + { + "path": "tests/test_qwen_metadata.py", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 7 + }, + { + "path": "tests/test_qwen_session.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 160 + }, + { + "path": "tests/test_qwen_session.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 56 + }, + { + "path": "tests/test_qwen_state.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 56 + }, + { + "path": "tests/test_qwen_tokenizer.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 252 + }, + { + "path": "tests/test_qwen_tokenizer.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 103 + }, + { + "path": "tests/test_qwen_unicode.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 29 + }, + { + "path": "tests/test_qwen_unicode.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 9 + }, + { + "path": "tests/test_retired_distributed_flags.sh", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 10 + }, + { + "path": "tests/test_retired_distributed_flags.sh", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": "tests/test_ssd_residency.c", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 226 + }, + { + "path": "tests/test_ssd_residency.c", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 53 + }, + { + "path": "tools/brand_boundary_audit.py", + "location": "content", + "token": "ds4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "tools/brand_boundary_audit.py", + "location": "content", + "token": "DS4", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "tools/brand_boundary_audit.py", + "location": "content", + "token": "DwarfStar", + "classification": "migration-pending", + "maximum": 2 + }, + { + "path": "tools/context_audit.py", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 17 + }, + { + "path": "tools/context_audit.py", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 6 + }, + { + "path": "tools/context_budget.json", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": "tools/context_budget.json", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 10 + } + ] +} diff --git a/tools/brand_boundary_audit.py b/tools/brand_boundary_audit.py new file mode 100644 index 000000000..3dd9b1571 --- /dev/null +++ b/tools/brand_boundary_audit.py @@ -0,0 +1,523 @@ +#!/usr/bin/env python3 +"""Enforce the explicit legacy-brand inventory during the Hebrus migration.""" + +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +from dataclasses import dataclass +from pathlib import Path, PurePosixPath +from typing import Any + + +DEFAULT_ROOT = Path(__file__).resolve().parents[1] +DEFAULT_MANIFEST = "tools/brand_boundary.json" +SCHEMA_VERSION = 1 +TOKENS = ("ds4", "DS4", "DwarfStar") +LOCATIONS = ("path", "content") +CATEGORIES = ( + "serialized/permanent", + "compatibility", + "historical-attribution", + "migration-pending", +) +TOP_LEVEL_KEYS = { + "schema_version", + "scope", + "category_definitions", + "refresh_policy", + "entries", +} +ENTRY_KEYS = {"path", "location", "token", "classification", "maximum"} +GLOB_CHARS = frozenset("*?[]{}") + + +class AuditError(Exception): + """A malformed manifest or unusable checkout.""" + + +@dataclass(frozen=True) +class Identity: + path: str + location: str + token: str + + +def identity_key(identity: Identity) -> tuple[str, int, int]: + return ( + identity.path, + LOCATIONS.index(identity.location), + TOKENS.index(identity.token), + ) + + +def identity_text(identity: Identity) -> str: + return f"{identity.path}:{identity.location}:{identity.token}" + + +def path_is_explicit(path: str) -> bool: + if not path or any(char in path for char in GLOB_CHARS): + return False + pure = PurePosixPath(path) + return not pure.is_absolute() and str(pure) == path and ".." not in pure.parts + + +def expected_exclusions(root: Path, manifest_path: Path) -> list[str]: + try: + relative = manifest_path.resolve().relative_to(root.resolve()) + except ValueError: + return [] + return [relative.as_posix()] + + +def require_exact_keys(value: dict[str, Any], expected: set[str], context: str) -> None: + actual = set(value) + if actual != expected: + missing = sorted(expected - actual) + extra = sorted(actual - expected) + details = [] + if missing: + details.append("missing " + ", ".join(missing)) + if extra: + details.append("unknown " + ", ".join(extra)) + raise AuditError(f"{context} has invalid keys ({'; '.join(details)})") + + +def load_manifest( + root: Path, manifest_path: Path +) -> tuple[dict[str, Any], dict[Identity, dict[str, Any]]]: + try: + document = json.loads(manifest_path.read_text(encoding="utf-8")) + except FileNotFoundError as exc: + raise AuditError(f"manifest does not exist: {manifest_path}") from exc + except (OSError, json.JSONDecodeError) as exc: + raise AuditError(f"cannot read manifest {manifest_path}: {exc}") from exc + + if not isinstance(document, dict): + raise AuditError("manifest root must be a JSON object") + require_exact_keys(document, TOP_LEVEL_KEYS, "manifest") + if ( + isinstance(document["schema_version"], bool) + or not isinstance(document["schema_version"], int) + or document["schema_version"] != SCHEMA_VERSION + ): + raise AuditError( + f"manifest schema_version must be {SCHEMA_VERSION}, " + f"got {document['schema_version']!r}" + ) + + scope = document["scope"] + if not isinstance(scope, dict): + raise AuditError("manifest scope must be an object") + require_exact_keys(scope, {"tokens", "locations", "excluded_paths"}, "scope") + if scope["tokens"] != list(TOKENS): + raise AuditError(f"scope.tokens must be exactly {list(TOKENS)!r}") + if scope["locations"] != list(LOCATIONS): + raise AuditError(f"scope.locations must be exactly {list(LOCATIONS)!r}") + exclusions = expected_exclusions(root, manifest_path) + if scope["excluded_paths"] != exclusions: + raise AuditError( + "scope.excluded_paths may contain only the manifest itself: " + f"expected {exclusions!r}" + ) + + definitions = document["category_definitions"] + if not isinstance(definitions, dict) or set(definitions) != set(CATEGORIES): + raise AuditError( + "category_definitions must define exactly: " + ", ".join(CATEGORIES) + ) + if any(not isinstance(definitions[name], str) or not definitions[name].strip() + for name in CATEGORIES): + raise AuditError("every category definition must be a non-empty string") + + policy = document["refresh_policy"] + if not isinstance(policy, dict): + raise AuditError("refresh_policy must be an object") + require_exact_keys( + policy, + {"reductions", "existing_increase", "new_group"}, + "refresh_policy", + ) + if any(not isinstance(value, str) or not value.strip() for value in policy.values()): + raise AuditError("every refresh_policy command must be a non-empty string") + + raw_entries = document["entries"] + if not isinstance(raw_entries, list): + raise AuditError("manifest entries must be an array") + + entries: dict[Identity, dict[str, Any]] = {} + order: list[Identity] = [] + for index, entry in enumerate(raw_entries): + context = f"entries[{index}]" + if not isinstance(entry, dict): + raise AuditError(f"{context} must be an object") + require_exact_keys(entry, ENTRY_KEYS, context) + path = entry["path"] + location = entry["location"] + token = entry["token"] + classification = entry["classification"] + maximum = entry["maximum"] + if not isinstance(path, str) or not path_is_explicit(path): + raise AuditError(f"{context}.path must be an explicit normalized path") + if path in exclusions: + raise AuditError(f"{context}.path cannot be the excluded manifest") + if location not in LOCATIONS: + raise AuditError(f"{context}.location must be one of {LOCATIONS!r}") + if token not in TOKENS: + raise AuditError(f"{context}.token must be one of {TOKENS!r}") + if classification not in CATEGORIES: + raise AuditError( + f"{context}.classification must be one of {CATEGORIES!r}" + ) + if isinstance(maximum, bool) or not isinstance(maximum, int) or maximum < 1: + raise AuditError(f"{context}.maximum must be a positive integer") + identity = Identity(path, location, token) + if identity in entries: + raise AuditError(f"duplicate entry: {identity_text(identity)}") + entries[identity] = entry + order.append(identity) + + if order != sorted(order, key=identity_key): + raise AuditError("manifest entries must be in deterministic path/location/token order") + return document, entries + + +def tracked_paths(root: Path) -> list[str]: + try: + result = subprocess.run( + ["git", "ls-files", "-z"], + cwd=root, + check=True, + capture_output=True, + ) + except (OSError, subprocess.CalledProcessError) as exc: + raise AuditError(f"cannot enumerate tracked files under {root}: {exc}") from exc + return sorted( + item.decode("utf-8", "surrogateescape") + for item in result.stdout.split(b"\0") + if item + ) + + +def worktree_bytes(path: Path) -> bytes | None: + try: + if path.is_symlink(): + return os.readlink(path).encode("utf-8", "surrogateescape") + if not path.is_file(): + return None + return path.read_bytes() + except OSError as exc: + raise AuditError(f"cannot read tracked path {path}: {exc}") from exc + + +def count_tokens(blob: bytes) -> dict[str, int]: + counts = {} + for token in TOKENS: + count = blob.count(token.encode("ascii")) + if count: + counts[token] = count + return counts + + +def scan_tree(root: Path, excluded_paths: set[str]) -> dict[Identity, int]: + observations: dict[Identity, int] = {} + for relative in tracked_paths(root): + if relative in excluded_paths: + continue + path_counts = count_tokens(relative.encode("utf-8", "surrogateescape")) + for token, count in path_counts.items(): + observations[Identity(relative, "path", token)] = count + + content = worktree_bytes(root / relative) + if content is None: + continue + for token, count in count_tokens(content).items(): + observations[Identity(relative, "content", token)] = count + return observations + + +def parse_identity(value: str) -> Identity: + try: + path, location, token = value.rsplit(":", 2) + except ValueError as exc: + raise AuditError( + f"invalid identity {value!r}; expected PATH:LOCATION:TOKEN" + ) from exc + identity = Identity(path, location, token) + if not path_is_explicit(path): + raise AuditError(f"identity path must be explicit, not a glob: {path!r}") + if location not in LOCATIONS: + raise AuditError(f"identity location must be one of {LOCATIONS!r}") + if token not in TOKENS: + raise AuditError(f"identity token must be one of {TOKENS!r}") + return identity + + +def parse_classification(value: str) -> tuple[Identity, str]: + try: + identity_value, category = value.rsplit("=", 1) + except ValueError as exc: + raise AuditError( + f"invalid classification {value!r}; expected " + "PATH:LOCATION:TOKEN=CATEGORY" + ) from exc + if category not in CATEGORIES: + raise AuditError(f"classification must be one of {CATEGORIES!r}") + return parse_identity(identity_value), category + + +def render_violations( + unknown: list[Identity], + increases: list[tuple[Identity, int, int]], +) -> None: + print("brand boundary audit failed:", file=sys.stderr) + for identity in unknown: + print( + f"- unclassified brand token group: {identity_text(identity)}", + file=sys.stderr, + ) + for identity, actual, maximum in increases: + print( + f"- brand token count increased: {identity_text(identity)} " + f"{actual} > {maximum}", + file=sys.stderr, + ) + + +def inventory_delta( + observations: dict[Identity, int], + entries: dict[Identity, dict[str, Any]], +) -> tuple[list[Identity], list[tuple[Identity, int, int]], list[Identity]]: + unknown = sorted(set(observations) - set(entries), key=identity_key) + increases = sorted( + ( + (identity, actual, entries[identity]["maximum"]) + for identity, actual in observations.items() + if identity in entries and actual > entries[identity]["maximum"] + ), + key=lambda item: identity_key(item[0]), + ) + reductions = sorted( + ( + identity + for identity, entry in entries.items() + if observations.get(identity, 0) < entry["maximum"] + ), + key=identity_key, + ) + return unknown, increases, reductions + + +def summary( + observations: dict[Identity, int], + entries: dict[Identity, dict[str, Any]], + reductions: list[Identity], +) -> str: + totals = {category: 0 for category in CATEGORIES} + for identity, actual in observations.items(): + entry = entries.get(identity) + if entry: + totals[entry["classification"]] += actual + categories = ", ".join(f"{name}={totals[name]}" for name in CATEGORIES) + return ( + f"{sum(observations.values())} occurrences in {len(observations)} groups; " + f"{len(reductions)} reductions; {categories}" + ) + + +def check_inventory( + observations: dict[Identity, int], + entries: dict[Identity, dict[str, Any]], +) -> int: + unknown, increases, reductions = inventory_delta(observations, entries) + if unknown or increases: + render_violations(unknown, increases) + return 1 + print("brand boundary audit passed: " + summary(observations, entries, reductions)) + return 0 + + +def parse_refresh_authorizations( + raw_increases: list[str], raw_classifications: list[str] +) -> tuple[set[Identity], dict[Identity, str]]: + accepted: set[Identity] = set() + for value in raw_increases: + identity = parse_identity(value) + if identity in accepted: + raise AuditError(f"duplicate --accept-increase: {identity_text(identity)}") + accepted.add(identity) + + classified: dict[Identity, str] = {} + for value in raw_classifications: + identity, category = parse_classification(value) + if identity in classified: + raise AuditError(f"duplicate --classify: {identity_text(identity)}") + classified[identity] = category + return accepted, classified + + +def refresh_inventory( + manifest_path: Path, + document: dict[str, Any], + observations: dict[Identity, int], + entries: dict[Identity, dict[str, Any]], + raw_increases: list[str], + raw_classifications: list[str], +) -> int: + unknown, increases, reductions = inventory_delta(observations, entries) + accepted, classified = parse_refresh_authorizations( + raw_increases, raw_classifications + ) + increase_ids = {identity for identity, _, _ in increases} + unknown_ids = set(unknown) + + unused_increases = accepted - increase_ids + unused_classifications = set(classified) - unknown_ids + if unused_increases: + raise AuditError( + "--accept-increase does not match a current increase: " + + ", ".join(identity_text(item) for item in sorted(unused_increases, key=identity_key)) + ) + if unused_classifications: + raise AuditError( + "--classify may name only a current unclassified group: " + + ", ".join( + identity_text(item) + for item in sorted(unused_classifications, key=identity_key) + ) + ) + + missing_increases = increase_ids - accepted + missing_classifications = unknown_ids - set(classified) + if missing_increases or missing_classifications: + render_violations( + sorted(missing_classifications, key=identity_key), + [item for item in increases if item[0] in missing_increases], + ) + print( + "refresh refused: authorize every increase with --accept-increase " + "and every new group with --classify", + file=sys.stderr, + ) + return 1 + + refreshed_entries = [] + for identity in sorted(observations, key=identity_key): + category = ( + entries[identity]["classification"] + if identity in entries + else classified[identity] + ) + refreshed_entries.append( + { + "path": identity.path, + "location": identity.location, + "token": identity.token, + "classification": category, + "maximum": observations[identity], + } + ) + + refreshed = dict(document) + refreshed["entries"] = refreshed_entries + payload = json.dumps(refreshed, indent=2, ensure_ascii=True) + "\n" + temporary = manifest_path.with_name(manifest_path.name + ".tmp") + try: + temporary.write_text(payload, encoding="utf-8") + os.replace(temporary, manifest_path) + except OSError as exc: + try: + temporary.unlink(missing_ok=True) + except OSError: + pass + raise AuditError(f"cannot update manifest {manifest_path}: {exc}") from exc + + refreshed_map = { + Identity(entry["path"], entry["location"], entry["token"]): entry + for entry in refreshed_entries + } + print( + "brand boundary manifest refreshed: " + + summary(observations, refreshed_map, reductions) + ) + return 0 + + +def parser() -> argparse.ArgumentParser: + argument_parser = argparse.ArgumentParser( + description=( + "Check the exact tracked ds4/DS4/DwarfStar inventory. Plain refresh " + "only tightens the baseline; widening requires exact authorizations." + ) + ) + action = argument_parser.add_mutually_exclusive_group(required=True) + action.add_argument("--check", action="store_true", help="verify the inventory") + action.add_argument( + "--refresh", + action="store_true", + help="rewrite the manifest deterministically after explicit review", + ) + argument_parser.add_argument( + "--root", + help="repository root, defaulting to the checkout containing this tool", + ) + argument_parser.add_argument( + "--manifest", + help=f"manifest path relative to --root (default: {DEFAULT_MANIFEST})", + ) + argument_parser.add_argument( + "--accept-increase", + action="append", + default=[], + metavar="PATH:LOCATION:TOKEN", + help="authorize one existing group whose count increased; repeat as needed", + ) + argument_parser.add_argument( + "--classify", + action="append", + default=[], + metavar="PATH:LOCATION:TOKEN=CATEGORY", + help="classify one new exact group; repeat as needed", + ) + return argument_parser + + +def main(argv: list[str] | None = None) -> int: + arguments = parser().parse_args(argv) + if arguments.check and (arguments.accept_increase or arguments.classify): + print( + "brand boundary audit error: refresh authorizations require --refresh", + file=sys.stderr, + ) + return 2 + + root = Path(arguments.root).resolve() if arguments.root else DEFAULT_ROOT + manifest_path = Path(arguments.manifest) if arguments.manifest else Path(DEFAULT_MANIFEST) + if not manifest_path.is_absolute(): + manifest_path = root / manifest_path + manifest_path = manifest_path.resolve() + + try: + document, entries = load_manifest(root, manifest_path) + exclusions = set(document["scope"]["excluded_paths"]) + observations = scan_tree(root, exclusions) + if arguments.check: + return check_inventory(observations, entries) + return refresh_inventory( + manifest_path, + document, + observations, + entries, + arguments.accept_increase, + arguments.classify, + ) + except AuditError as exc: + print(f"brand boundary audit error: {exc}", file=sys.stderr) + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) From e5ba9198fe4f390257bc9b2a6a35bef938c8b2dd Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:12:36 +0200 Subject: [PATCH 11/41] tools: review integrated Hebrus brand deltas --- ds4_help.c | 31 +++++++++------- ds4_help.h | 2 +- tools/brand_boundary.json | 75 ++++++++++++++++++++++++++++++++------- 3 files changed, 82 insertions(+), 26 deletions(-) diff --git a/ds4_help.c b/ds4_help.c index 44ce06ae6..9ad33af4f 100644 --- a/ds4_help.c +++ b/ds4_help.c @@ -481,6 +481,13 @@ static const char *example_command(char *buf, size_t size, ds4_help_tool tool, return buf; } +static const char *example_root_command(char *buf, size_t size, + const char *arguments) { + snprintf(buf, size, "./%s%s", + help_hebrus_invocation ? "hebrus" : "ds4", arguments); + return buf; +} + static void print_examples(FILE *fp, const help_colors *c, ds4_help_tool tool, const char *topic) { char command[256]; title(fp, c, "Examples"); @@ -514,31 +521,31 @@ static void print_examples(FILE *fp, const help_colors *c, ds4_help_tool tool, c " -p \"Summarize README\" --power 50")); } } else if (topic_is(topic, "steering")) { - opt(fp, c, "steer FFN", example_command(command, sizeof(command), DS4_HELP_DS4, - " -p \"Write tersely\" --dir-steering-file dir.bin --dir-steering-ffn 0.8")); + opt(fp, c, "steer FFN", example_root_command(command, sizeof(command), + " -p \"Write tersely\" --dir-steering-file dir.bin --dir-steering-ffn 0.8")); } else if (tool == DS4_HELP_SERVER || topic_is(topic, "api") || topic_is(topic, "kv-cache")) { - opt(fp, c, "local API", example_command(command, sizeof(command), DS4_HELP_SERVER, + opt(fp, c, "local API", example_command(command, sizeof(command), tool, " --ctx 100000 --kv-disk-dir ~/.ds4/server-kv --kv-disk-space-mb 8192")); opt(fp, c, "curl", "curl http://127.0.0.1:8000/v1/models"); } else if (tool == DS4_HELP_AGENT || topic_is(topic, "sessions") || topic_is(topic, "tools")) { - opt(fp, c, "interactive", example_command(command, sizeof(command), DS4_HELP_AGENT, "")); - opt(fp, c, "one shot", example_command(command, sizeof(command), DS4_HELP_AGENT, + opt(fp, c, "interactive", example_command(command, sizeof(command), tool, "")); + opt(fp, c, "one shot", example_command(command, sizeof(command), tool, " --non-interactive -p \"Create /tmp/hello.c\"")); } else if (tool == DS4_HELP_BENCH || topic_is(topic, "benchmark")) { - opt(fp, c, "csv", example_command(command, sizeof(command), DS4_HELP_BENCH, + opt(fp, c, "csv", example_command(command, sizeof(command), tool, " --prompt-file long.txt --ctx-max 32768 --csv speed.csv")); - opt(fp, c, "prefill only", example_command(command, sizeof(command), DS4_HELP_BENCH, + opt(fp, c, "prefill only", example_command(command, sizeof(command), tool, " --prompt-file long.txt --gen-tokens 0")); } else if (tool == DS4_HELP_EVAL || topic_is(topic, "evaluation")) { - opt(fp, c, "first 10", example_command(command, sizeof(command), DS4_HELP_EVAL, + opt(fp, c, "first 10", example_command(command, sizeof(command), tool, " --questions 10 --trace eval.trace")); - opt(fp, c, "plain", example_command(command, sizeof(command), DS4_HELP_EVAL, + opt(fp, c, "plain", example_command(command, sizeof(command), tool, " --plain --nothink --tokens 512")); } else { - opt(fp, c, "chat", example_command(command, sizeof(command), DS4_HELP_DS4, "")); - opt(fp, c, "one shot", example_command(command, sizeof(command), DS4_HELP_DS4, + opt(fp, c, "chat", example_command(command, sizeof(command), tool, "")); + opt(fp, c, "one shot", example_command(command, sizeof(command), tool, " -p \"Explain mmap in C\"")); - opt(fp, c, "long prompt", example_command(command, sizeof(command), DS4_HELP_DS4, + opt(fp, c, "long prompt", example_command(command, sizeof(command), tool, " --think-max --prompt-file prompt.txt --ctx 393216")); } fputc('\n', fp); diff --git a/ds4_help.h b/ds4_help.h index 768e66078..863ad9f69 100644 --- a/ds4_help.h +++ b/ds4_help.h @@ -13,7 +13,7 @@ typedef enum { } ds4_help_tool; /* Select the public command family from argv[0]. Canonical Hebrus binaries and - * legacy DS4 symlinks share one object graph, so help text must use the name + * legacy command symlinks share one object graph, so help text must use the name * through which that graph was invoked. */ void hebrus_help_set_invocation(const char *argv0); diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index 475800438..0e2e58bd4 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -26,6 +26,62 @@ "new_group": "python3 tools/brand_boundary_audit.py --refresh --classify 'PATH:LOCATION:TOKEN=CLASSIFICATION'" }, "entries": [ + { + "path": ".github/ISSUE_TEMPLATE/bug_report.yml", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 3 + }, + { + "path": ".github/ISSUE_TEMPLATE/config.yml", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 2 + }, + { + "path": ".github/ISSUE_TEMPLATE/feature_request.yml", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 2 + }, + { + "path": ".github/ISSUE_TEMPLATE/feature_request.yml", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": ".github/ISSUE_TEMPLATE/performance_report.yml", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": ".github/ISSUE_TEMPLATE/performance_report.yml", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": ".github/pull_request_template.md", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 1 + }, + { + "path": ".github/pull_request_template.md", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 1 + }, { "path": ".gitignore", "location": "content", @@ -45,7 +101,7 @@ "location": "content", "token": "ds4", "classification": "compatibility", - "maximum": 3 + "maximum": 6 }, { "path": "CHANGELOG.md", @@ -290,7 +346,7 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 81 + "maximum": 82 }, { "path": "docs/ENGINE_REFERENCE.md", @@ -759,7 +815,7 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 25 + "maximum": 26 }, { "path": "ds4_build.c", @@ -885,7 +941,7 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 57 + "maximum": 32 }, { "path": "ds4_help.c", @@ -894,13 +950,6 @@ "classification": "migration-pending", "maximum": 81 }, - { - "path": "ds4_help.c", - "location": "content", - "token": "DwarfStar", - "classification": "migration-pending", - "maximum": 2 - }, { "path": "ds4_help.h", "location": "path", @@ -2208,7 +2257,7 @@ "location": "content", "token": "ds4", "classification": "compatibility", - "maximum": 6 + "maximum": 9 }, { "path": "tests/test_command_aliases.py", @@ -2390,7 +2439,7 @@ "location": "content", "token": "ds4", "classification": "compatibility", - "maximum": 10 + "maximum": 5 }, { "path": "tests/test_retired_distributed_flags.sh", From 13fbb2f5ba61eb4a7f4715977412968c27a4bbfd Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:13:43 +0200 Subject: [PATCH 12/41] tools: record canonical brand identities --- docs/architecture/CODEMAP.md | 2 +- tests/test_brand_boundary_audit.py | 40 ++++++++++++++++++++++++++ tools/brand_boundary.json | 45 +++++++++++++++++++++++++++--- tools/brand_boundary_audit.py | 33 ++++++++++++++++++++++ 4 files changed, 115 insertions(+), 5 deletions(-) diff --git a/docs/architecture/CODEMAP.md b/docs/architecture/CODEMAP.md index 73e969d7e..257c265f0 100644 --- a/docs/architecture/CODEMAP.md +++ b/docs/architecture/CODEMAP.md @@ -106,7 +106,7 @@ not validated. | `tests/` | Model-free, model-backed, kernel, tokenizer, server, and build-isolation regressions | | `tests/test_capabilities.py` | Exact schema and cross-executable checks for the model-free build/capability contract | | `tests/test_command_aliases.py` | Canonical/legacy symlink layout, binary identity, and CLI-output parity checks | -| `tools/brand_boundary.json` + `tools/brand_boundary_audit.py` | Explicit per-file legacy `ds4`/`DS4`/`DwarfStar` classification and monotonic count ceilings; `--check` rejects new groups and increases, while `--refresh` requires exact authorizations before widening a ceiling | +| `tools/brand_boundary.json` + `tools/brand_boundary_audit.py` | Exact canonical, bridged, and permanently preserved identity contract plus explicit per-file legacy `ds4`/`DS4`/`DwarfStar` classification and monotonic count ceilings; `--check` rejects contract drift, new groups, and increases, while `--refresh` requires exact authorizations before widening a ceiling | | `tests/test_brand_boundary_audit.py` | Fail-closed fixtures for new files and tokens, increases, reductions, deterministic refresh, and invalid manifests | | `tests/qwen/` | Qwen fixtures, provenance, reference collectors, and model-specific gates | | `tests/test-vectors/` | Official and local continuation vectors plus provenance | diff --git a/tests/test_brand_boundary_audit.py b/tests/test_brand_boundary_audit.py index ef7d774de..c92ca7318 100644 --- a/tests/test_brand_boundary_audit.py +++ b/tests/test_brand_boundary_audit.py @@ -21,6 +21,33 @@ ) LOCATION_ORDER = {"path": 0, "content": 1} TOKEN_ORDER = {"ds4": 0, "DS4": 1, "DwarfStar": 2} +IDENTITY_CONTRACT = { + "brand": "Hebrus", + "canonical_commands": [ + "hebrus", + "hebrus-server", + "hebrus-agent", + "hebrus-bench", + "hebrus-eval", + ], + "command_aliases": { + "ds4": "hebrus", + "ds4-server": "hebrus-server", + "ds4-agent": "hebrus-agent", + "ds4-bench": "hebrus-bench", + "ds4-eval": "hebrus-eval", + }, + "accepted_engine_ids": ["hebrus", "ds4"], + "environment_prefixes": { + "preserved": ["DS4_"], + "deferred": ["HEBRUS_"], + }, + "permanent_literals": ["ds4.expert_major.v1", "ds4.expert_major.v2"], + "repositories": { + "historical_origin": "antirez/ds4", + "pre_rename": "andreaborio/ds4", + }, +} def manifest_document(entries: list[dict[str, object]]) -> dict[str, object]: @@ -34,6 +61,7 @@ def manifest_document(entries: list[dict[str, object]]) -> dict[str, object]: ) return { "schema_version": 1, + "identity_contract": IDENTITY_CONTRACT, "scope": { "tokens": ["ds4", "DS4", "DwarfStar"], "locations": ["path", "content"], @@ -214,6 +242,18 @@ def test_invalid_manifest_fails_closed(self) -> None: self.assertEqual(checked.returncode, 2, checked.stderr) self.assertIn("must be an explicit normalized path", checked.stderr) + def test_identity_contract_is_exact(self) -> None: + self.seed_one_token() + document = json.loads(self.fixture.manifest.read_text(encoding="utf-8")) + document["identity_contract"]["command_aliases"]["ds4-server"] = "other" + self.fixture.manifest.write_text( + json.dumps(document, indent=2) + "\n", encoding="utf-8" + ) + + checked = self.fixture.run("--check") + self.assertEqual(checked.returncode, 2, checked.stderr) + self.assertIn("identity_contract must exactly match", checked.stderr) + def test_unused_refresh_authorization_fails_closed(self) -> None: self.seed_one_token() refreshed = self.fixture.run( diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index 0e2e58bd4..5ba24742d 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -1,5 +1,42 @@ { "schema_version": 1, + "identity_contract": { + "brand": "Hebrus", + "canonical_commands": [ + "hebrus", + "hebrus-server", + "hebrus-agent", + "hebrus-bench", + "hebrus-eval" + ], + "command_aliases": { + "ds4": "hebrus", + "ds4-server": "hebrus-server", + "ds4-agent": "hebrus-agent", + "ds4-bench": "hebrus-bench", + "ds4-eval": "hebrus-eval" + }, + "accepted_engine_ids": [ + "hebrus", + "ds4" + ], + "environment_prefixes": { + "preserved": [ + "DS4_" + ], + "deferred": [ + "HEBRUS_" + ] + }, + "permanent_literals": [ + "ds4.expert_major.v1", + "ds4.expert_major.v2" + ], + "repositories": { + "historical_origin": "antirez/ds4", + "pre_rename": "andreaborio/ds4" + } + }, "scope": { "tokens": [ "ds4", @@ -2222,14 +2259,14 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 20 + "maximum": 31 }, { "path": "tests/test_brand_boundary_audit.py", "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 4 + "maximum": 5 }, { "path": "tests/test_brand_boundary_audit.py", @@ -2467,14 +2504,14 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 2 + "maximum": 12 }, { "path": "tools/brand_boundary_audit.py", "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 2 + "maximum": 3 }, { "path": "tools/brand_boundary_audit.py", diff --git a/tools/brand_boundary_audit.py b/tools/brand_boundary_audit.py index 3dd9b1571..06e5724cd 100644 --- a/tools/brand_boundary_audit.py +++ b/tools/brand_boundary_audit.py @@ -24,8 +24,36 @@ "historical-attribution", "migration-pending", ) +IDENTITY_CONTRACT = { + "brand": "Hebrus", + "canonical_commands": [ + "hebrus", + "hebrus-server", + "hebrus-agent", + "hebrus-bench", + "hebrus-eval", + ], + "command_aliases": { + "ds4": "hebrus", + "ds4-server": "hebrus-server", + "ds4-agent": "hebrus-agent", + "ds4-bench": "hebrus-bench", + "ds4-eval": "hebrus-eval", + }, + "accepted_engine_ids": ["hebrus", "ds4"], + "environment_prefixes": { + "preserved": ["DS4_"], + "deferred": ["HEBRUS_"], + }, + "permanent_literals": ["ds4.expert_major.v1", "ds4.expert_major.v2"], + "repositories": { + "historical_origin": "antirez/ds4", + "pre_rename": "andreaborio/ds4", + }, +} TOP_LEVEL_KEYS = { "schema_version", + "identity_contract", "scope", "category_definitions", "refresh_policy", @@ -108,6 +136,11 @@ def load_manifest( f"manifest schema_version must be {SCHEMA_VERSION}, " f"got {document['schema_version']!r}" ) + if document["identity_contract"] != IDENTITY_CONTRACT: + raise AuditError( + "identity_contract must exactly match the canonical, bridged, and " + "permanently preserved identity contract" + ) scope = document["scope"] if not isinstance(scope, dict): From 49254e184b22370ad96f1dee1dc6bf45f38bb04b Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:24:32 +0200 Subject: [PATCH 13/41] docs: complete the Hebrus bridge identity surface --- CHANGELOG.md | 4 + CONTRIBUTING.md | 15 ++-- QA_BEFORE_RELEASES.md | 2 +- README.md | 7 +- docs/ENGINE_REFERENCE.md | 78 +++++++++--------- docs/architecture/CODEMAP.md | 1 + docs/contracts/BRAND_COMPATIBILITY.md | 110 ++++++++++++++++++++++++++ ds4_agent.c | 23 ++++-- ds4_bench.c | 6 +- ds4_cli.c | 5 +- ds4_eval.c | 7 +- tools/brand_boundary.json | 37 ++++----- 12 files changed, 217 insertions(+), 78 deletions(-) create mode 100644 docs/contracts/BRAND_COMPATIBILITY.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 033a1d35a..84e9710f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,10 @@ not represent a published release. - Canonical commands report `engine_id: "hebrus"` and `hebrus build`; legacy aliases retain `engine_id: "ds4"` and `ds4 build`. Both identities use the same schema-1 capability fields and immutable ExpertMajor wire contract. +- Current contributor, release, and engine-reference documentation now presents + Hebrus commands first and links an explicit brand compatibility contract. + Canonical CLI/agent/evaluation prompts and new benchmark evidence use the + invoked Hebrus identity; legacy aliases preserve their existing labels. ### Compatibility diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e8290f00..26aad6c0e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,17 +1,20 @@ # Contributing -DwarfStar4 changes should be tested against the failure mode they can realistically +Hebrus changes should be tested against the failure mode they can realistically affect. The project has two regression tracks: correctness and speed. Please include the commands you ran, the machine/backend, the model quant, and any notable failures in the PR or commit notes. ## Co-development with `antirez/ds4` -This repository is a transparent research fork of -[`antirez/ds4`](https://github.com/antirez/ds4), not a replacement for it. The -goal is to co-develop DwarfStar: use the fork to investigate complementary -hardware and model paths without blocking on review latency, then contribute -general improvements back upstream. +Hebrus is an increasingly independent inference engine that began as a +transparent research fork of +[`antirez/ds4`](https://github.com/antirez/ds4). It retains substantial upstream +implementation and history while focusing on Apple Metal, embedded ExpertMajor +storage, and SSD streaming across several model families. It is not a +replacement for the upstream project. General improvements that remain +applicable to upstream-supported paths continue to be contributed back after +validation. Every change applicable to an upstream-supported path **must be opened as an upstream PR** once it is scoped and validated. This includes model- or diff --git a/QA_BEFORE_RELEASES.md b/QA_BEFORE_RELEASES.md index 1147bef3c..920464f1d 100644 --- a/QA_BEFORE_RELEASES.md +++ b/QA_BEFORE_RELEASES.md @@ -1,6 +1,6 @@ # QA Before Releases -This is the release gate for DwarfStar. Run it before tagging or pushing a +This is the release gate for Hebrus. Run it before tagging or pushing a release build. The goal is not to prove every code path exhaustively; it is to exercise the paths that have historically regressed: Metal graph inference, model-family residency, SSD streaming, distributed rejection, disk KV cache, diff --git a/README.md b/README.md index 900adc383..9652202fb 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,9 @@ the only capacity boundary. > namespace in the current bridge. The repository is still > [`andreaborio/ds4`](https://github.com/andreaborio/ds4), and > [ADR 0005](docs/adr/0005-hebrus-naming-and-compatibility-boundary.md) remains -> **Proposed** while identity screening and the companion-application bridge are -> incomplete. This repository has not been administratively renamed. +> **Proposed** while namespace and legal screening remain incomplete. The +> companion-application bridge is implemented locally but not yet published, +> and this repository has not been administratively renamed. > [!IMPORTANT] > Hebrus began as a fork of @@ -174,6 +175,8 @@ A short-context best is not substituted for the long-context gate. - [Runtime support contract](docs/contracts/RUNTIME_SUPPORT.md) — supported families, hardware floors, backends, and fail-closed boundaries. +- [Brand compatibility contract](docs/contracts/BRAND_COMPATIBILITY.md) — + canonical commands, legacy aliases, stable formats, and migration rules. - [Engine reference](docs/ENGINE_REFERENCE.md) — CLI, server, agent, disk KV, tracing, evaluation, and operational details. - [Metal and SSD policy](GOLD_METAL_SSD.md) — build identity, AUTO residency, diff --git a/docs/ENGINE_REFERENCE.md b/docs/ENGINE_REFERENCE.md index d1fd5372e..7fe1b160e 100644 --- a/docs/ENGINE_REFERENCE.md +++ b/docs/ENGINE_REFERENCE.md @@ -1,4 +1,4 @@ -# DwarfStar engine reference +# Hebrus engine reference > [!NOTE] > This is the long-form engine guide inherited and evolved from @@ -26,8 +26,8 @@ That said, a few important things about this project: * The local inference landscape contains many excellent projects, but new models are released continuously, and the attention immediately gets captured by the next model to implement. This project takes a deliberately narrow bet: one model at a time, official-vector validation (logits obtained with the official implementation), long-context tests, and enough agent integration to know if it really works. The exact model may change as the landscape evolves, but the constraint remains: credible local Apple Metal inference, with family-specific memory limits. DeepSeek and GLM currently start at 64 GiB; Qwen's SSD-backed policy starts at 16 GiB. * This software is developed with **strong assistance from GPT 5.5** and with humans leading the ideas, testing, and debugging. We say this openly because it shaped how the project was built. If you are not happy with AI-developed code, this software is not for you. The acknowledgement below is equally important: this would not exist without `llama.cpp` and GGML, largely written by hand. * This implementation is based on the idea that compressed KV caches like the one of DeepSeek v4 and the fast SSD disks of modern MacBooks should change our idea that KV cache belongs to RAM. **The KV cache is actually a first-class disk citizen**. Fast SSD disks also changed the inference game from the point of view of "model needs to fit RAM": while having more RAM than the model size is still preferred, SSD streaming turns the available amount of RAM from a hard cutoff (can I run this model or not?) into a continuous spectrum of speed levels. -* Our vision is that local inference should be a set of three things working well together, out of the box: A) inference engine with HTTP API + B) GGUF specially crafted to run well under a given engine and given assumptions + C) testing and validation with coding agents implementations. D) Purpose built agents for specific models and execution environments. DwarfStar only runs with the GGUF files provided. It gets tested against officially obtained logits at different context sizes. This project exists because we wanted to make one local model feel finished end to end, not just runnable. However this is beta quality code, so probably we are not still there, especially since SSD streaming and the additional model families are recent additions. -* The production graph path targets **Metal on macOS**. The CPU path is only for correctness checks and model/tokenizer diagnostics. For CPU-only Linux builds, use `make cpu`; it builds the normal `./ds4` and `./ds4-server` binaries without a GPU backend. On macOS, **warning: current macOS versions have a bug in the virtual memory implementation that can crash the kernel** if you try to run very large CPU model inference. Do not use CPU as a production fallback. +* Our vision is that local inference should be a set of three things working well together, out of the box: A) inference engine with HTTP API + B) GGUF specially crafted to run well under a given engine and given assumptions + C) testing and validation with coding agents implementations. D) Purpose built agents for specific models and execution environments. Hebrus only runs with the GGUF files provided. It gets tested against officially obtained logits at different context sizes. This project exists because we wanted to make one local model feel finished end to end, not just runnable. However this is beta quality code, so probably we are not still there, especially since SSD streaming and the additional model families are recent additions. +* The production graph path targets **Metal on macOS**. The CPU path is only for correctness checks and model/tokenizer diagnostics. For CPU-only Linux builds, use `make cpu`; it builds canonical `hebrus` commands plus their `ds4*` compatibility aliases without a GPU backend. On macOS, **warning: current macOS versions have a bug in the virtual memory implementation that can crash the kernel** if you try to run very large CPU model inference. Do not use CPU as a production fallback. ## Acknowledgements to llama.cpp and GGML @@ -50,7 +50,7 @@ backend receives the same validation at the same time. We try to keep tested paths usable. If you hit an issue, use `--trace` to log the session and include the full trace in the report. -The `ds4-agent` was added later and remains alpha quality. +The `hebrus-agent` was added later and remains alpha quality. ## More Documentation @@ -233,7 +233,7 @@ routes through experts again. Start with AUTO residency and the automatic cache budget: ```sh -./ds4 -m /absolute/path/to/QUALIFIED-DEEPSEEK-OR-QWEN-DS4-ExpertMajor-v2.gguf +./hebrus -m /absolute/path/to/QUALIFIED-DEEPSEEK-OR-QWEN-DS4-ExpertMajor-v2.gguf ``` For DeepSeek and Qwen qualification, use `--ssd-streaming` to force streaming @@ -270,13 +270,13 @@ If startup reports that the expert cache is too large, or if you want to reserve more memory for context, set the routed expert cache explicitly: ```sh -./ds4 \ +./hebrus \ -m /absolute/path/to/QUALIFIED-DEEPSEEK-OR-QWEN-DS4-ExpertMajor-v2.gguf \ --ssd-streaming --ssd-streaming-cache-experts 32GB ``` The `32GB` value is a memory budget for complete routed experts, not a generic -byte cache. DwarfStar converts it to the number of full experts that fit for the +byte cache. Hebrus converts it to the number of full experts that fit for the current GGUF. Non-routed weights, KV cache, graph scratch, and activations need additional memory. Only the automatic cache budget does the full subtraction for you: it reserves context/KV/scratch, external pressure, and 20% backend @@ -291,7 +291,7 @@ On a qualified 64 GB host, run the published DeepSeek Flash ExpertMajor v2 artifact with its recorded complete output SHA-256 and a moderate expert cache: ```sh -./ds4 \ +./hebrus \ -m /absolute/path/to/QUALIFIED-DEEPSEEK-FLASH-DS4-ExpertMajor-v2.gguf \ --ssd-streaming \ --ssd-streaming-cache-experts 32GB \ @@ -374,9 +374,9 @@ Long local inference runs can keep the GPU busy for extended periods. If you care more about heat, fan noise, battery life on MacBooks, or reducing thermal stress on the hardware than about maximum throughput, use `--power N`. -`--power 100` is the default and means full speed. Lower values ask DwarfStar to target +`--power 100` is the default and means full speed. Lower values ask Hebrus to target that percentage of GPU usage: `--power 70` targets about 70%, `--power 50` -targets about half usage, and so forth. DwarfStar does this by measuring GPU work time +targets about half usage, and so forth. Hebrus does this by measuring GPU work time and inserting small sleeps between work units: during prefill it sleeps between layers, and during generation it sleeps between decoded tokens. This reduces sustained load without changing model output. @@ -385,14 +385,14 @@ The option is available on the CLI, server, agent, eval, and benchmark tools, for example: ```sh -./ds4 -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --power 50 -./ds4-agent -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --power 70 -./ds4-server -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --power 40 --ctx 100000 +./hebrus -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --power 50 +./hebrus-agent -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --power 70 +./hebrus-server -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --power 40 --ctx 100000 ``` ## Native agent -DwarfStar features a native coding agent that works in a different way +Hebrus features a native coding agent that works in a different way than most other systems: the inference is controlled from within the agent itself, without socket/API boundaries, so the session is represented by the on-disk KV cache itself. Moreover the tools and the system prompt @@ -424,7 +424,7 @@ Private compaction instructions never enter the visible transcript or execute tools. `/compact` requests the same operation manually, and the terminal shows `COMPACTING` while the summary and rebuilt prefill are in progress. -Use `--chdir /path/to/ds4` when launching `ds4-agent` from another directory, +Use `--chdir /path/to/engine-checkout` when launching `hebrus-agent` from another directory, so relative runtime files such as `metal/*.metal` resolve from the project tree. However while the system already works, there is a lot of work to do @@ -434,7 +434,7 @@ session-based protocol that can recreate all that in a client-server way. ## Benchmarking -`ds4-bench` measures instantaneous prefill and generation throughput at context +`hebrus-bench` measures instantaneous prefill and generation throughput at context frontiers instead of reporting one whole-run average. It loads the model once, walks a fixed token sequence to frontiers such as 2048, 4096, 6144, and uses incremental prefill so each row measures only the newly-added token interval. @@ -442,7 +442,7 @@ After each frontier it saves the live KV state to memory, generates a fixed greedy non-EOS probe, restores the memory snapshot, and continues prefill. ```sh -./ds4-bench \ +./hebrus-bench \ -m /absolute/path/to/QUALIFIED-DEEPSEEK-FLASH-DS4-ExpertMajor-v2.gguf \ --prompt-file speed-bench/promessi_sposi.txt \ --ctx-start 2048 \ @@ -472,7 +472,7 @@ per-layer chunk dispatch path. ## Capability Evaluation -`ds4-eval` is a small real-model integration benchmark. It is not a leaderboard +`hebrus-eval` is a small real-model integration benchmark. It is not a leaderboard runner and should not be reported as an official GPQA, SuperGPQA, AIME, or security benchmark score: the questions are an embedded 92-item subset chosen to make local regression testing useful and visually inspectable. The program @@ -481,14 +481,14 @@ the final answer, and prints a per-question report with prompt tokens, generated tokens, pass/fail state, the model answer, and the correct answer. ```sh -./ds4-eval \ +./hebrus-eval \ -m /absolute/path/to/QUALIFIED-DEEPSEEK-FLASH-DS4-ExpertMajor-v2.gguf \ --trace /tmp/ds4-eval.txt ``` The default run uses `--tokens 16000`, thinking mode enabled, and a soft/hard `` budget cutoff so the model has room to produce a visible answer. -`ds4-eval` sizes the context internally from the largest selected prompt plus +`hebrus-eval` sizes the context internally from the largest selected prompt plus the generation budget, and refuses runs that would need more than 1M context tokens. Press `p` to pause, `q` to exit and print the report, Up/Down to inspect or select another question, and Enter to run the selected question next. @@ -504,7 +504,7 @@ For inference changes that can affect generation drift, keep this deterministic q1..q4 token-count gate in the test plan: ```sh -./ds4-eval \ +./hebrus-eval \ -m /absolute/path/to/QUALIFIED-DEEPSEEK-FLASH-DS4-ExpertMajor-v2.gguf \ --plain \ --questions 4 \ @@ -556,7 +556,7 @@ a pass/fail unit test. to identify the best source line where the defensive code flaw is introduced, or return `0` for a safe function. -In practice this means `ds4-eval` should not be expected to produce a perfect +In practice this means `hebrus-eval` should not be expected to produce a perfect 92/92 run. It is meant to answer a more useful engineering question: after a kernel, quantization, prompt-rendering, KV-cache, or tool-streaming change, does DeepSeek V4 Flash still solve a representative mix of hard science, broad @@ -568,7 +568,7 @@ path users run? One-shot prompt: ```sh -./ds4 \ +./hebrus \ -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf \ -p "Explain Redis streams in one paragraph." ``` @@ -576,8 +576,8 @@ One-shot prompt: No `-p` starts the interactive prompt: ```sh -./ds4 -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf -ds4> +./hebrus -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf +hebrus> ``` The interactive CLI is a real multi-turn chat. It keeps the rendered chat @@ -597,12 +597,12 @@ experimental slight-speedup path. Start a local OpenAI/Anthropic-compatible server: ```sh -./ds4-server \ +./hebrus-server \ -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf \ --ctx 100000 --kv-disk-dir /tmp/ds4-kv --kv-disk-space-mb 8192 ``` -Use `--chdir /path/to/ds4` when launching `ds4-server` from another directory, +Use `--chdir /path/to/engine-checkout` when launching `hebrus-server` from another directory, so relative runtime files such as `metal/*.metal` resolve from the project tree. The server keeps one mutable backend/KV checkpoint in memory, @@ -653,7 +653,7 @@ assistant `tool_use` blocks or it is rejected. Default sampled API generation uses `temperature=1`, `top_p=1`, and `min_p=0.05`, so the default filter is relative probability rather than -nucleus mass. In thinking mode DwarfStar uses those fixed sampling defaults and +nucleus mass. In thinking mode Hebrus uses those fixed sampling defaults and ignores client sampling knobs, matching DeepSeek's fixed-thinking API behavior. The chat, Responses, and Anthropic endpoints support SSE streaming. In thinking @@ -727,12 +727,12 @@ curl http://127.0.0.1:8000/v1/chat/completions \ ### Agent Client Usage -`ds4-server` can be used by local coding agents that speak OpenAI-compatible +`hebrus-server` can be used by local coding agents that speak OpenAI-compatible chat completions. Start the server first, and set the client context limit no higher than the `--ctx` value you started the server with: ```sh -./ds4-server \ +./hebrus-server \ -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf \ --ctx 100000 --kv-disk-dir /tmp/ds4-kv --kv-disk-space-mb 8192 ``` @@ -905,7 +905,7 @@ non-thinking model alias such as `deepseek-chat`. ## Disk KV Cache Chat/completion APIs are stateless: agent clients usually resend the whole -conversation every request. `ds4-server` first tries the cheap exact token-prefix +conversation every request. `hebrus-server` first tries the cheap exact token-prefix check, then falls back to comparing rendered prompt bytes with decoded checkpoint bytes. The live in-memory checkpoint covers the current session; the disk KV cache makes useful prefixes survive session switches and server @@ -920,7 +920,7 @@ different sessions. Enable it with: ```sh -./ds4-server \ +./hebrus-server \ -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf \ --kv-disk-dir /tmp/ds4-kv --kv-disk-space-mb 8192 ``` @@ -1088,7 +1088,7 @@ Production inference for Qwen, DeepSeek, and GLM ExpertMajor v2 artifacts is local Apple Metal: ```sh -./ds4 -m /absolute/path/to/MODEL-DS4-ExpertMajor-v2.gguf -p "Hello" +./hebrus -m /absolute/path/to/MODEL-DS4-ExpertMajor-v2.gguf -p "Hello" ``` CPU remains useful for build and model-free diagnostics: @@ -1124,12 +1124,12 @@ willingness to provide dual-use or offensive security guidance. captured from the official DeepSeek V4 Flash API. The requests use `deepseek-v4-flash`, greedy decoding, thinking disabled, and the maximum `top_logprobs` slice exposed by the API. Local vectors are generated with -`./ds4 --dump-logprobs` and compared by token bytes, so tokenizer/template or +`./hebrus --dump-logprobs` and compared by token bytes, so tokenizer/template or attention regressions show up before they become long generation failures. The C runner pins `DS4_METAL_PREFILL_CHUNK=2048` for this strict API-vector comparison. -All project tests are driven by the C runner, with a small `ds4-eval` +All project tests are driven by the C runner, with a small `hebrus-eval` extractor self-test run first: ```sh @@ -1144,10 +1144,10 @@ When a generation looks wrong, three small tools are usually enough to get a first answer: ```sh -./ds4 -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --dump-tokens -p "..." -./ds4 -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --dump-logprobs /tmp/out.json --logprobs-top-k 20 --temp 0 -p "..." -./ds4 -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --dump-logits /tmp/logits.json --nothink --prompt-file prompt.txt -./ds4-server -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --trace /tmp/ds4-trace.txt ... +./hebrus -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --dump-tokens -p "..." +./hebrus -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --dump-logprobs /tmp/out.json --logprobs-top-k 20 --temp 0 -p "..." +./hebrus -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --dump-logits /tmp/logits.json --nothink --prompt-file prompt.txt +./hebrus-server -m /absolute/path/to/QUALIFIED-DS4-ExpertMajor-v2.gguf --trace /tmp/ds4-trace.txt ... ``` - `--dump-tokens` tokenizes the `-p` or `--prompt-file` string exactly as diff --git a/docs/architecture/CODEMAP.md b/docs/architecture/CODEMAP.md index 257c265f0..733f22e78 100644 --- a/docs/architecture/CODEMAP.md +++ b/docs/architecture/CODEMAP.md @@ -125,6 +125,7 @@ not be copied into new tests. | `CONTRIBUTING.md` | Contribution, regression, and upstream-coordination gates | | `QA_BEFORE_RELEASES.md` | Complete release checklist | | `docs/contracts/RUNTIME_SUPPORT.md` | Current supported runtime/model matrix | +| `docs/contracts/BRAND_COMPATIBILITY.md` | Canonical Hebrus names, compatibility aliases, and permanently stable identifiers | | `docs/adr/` | Accepted architectural decisions and their consequences | | `GOLD_METAL_SSD.md` | Metal/SSD planner details and performance gates; support authority remains `RUNTIME_SUPPORT.md` | | `FORK_NOTES.md` | Time-stamped fork/upstream boundary ledger | diff --git a/docs/contracts/BRAND_COMPATIBILITY.md b/docs/contracts/BRAND_COMPATIBILITY.md new file mode 100644 index 000000000..91980c920 --- /dev/null +++ b/docs/contracts/BRAND_COMPATIBILITY.md @@ -0,0 +1,110 @@ +# Hebrus Brand Compatibility Contract + +Status: bridge contract for the unreleased Hebrus naming transition. + +This document defines which public names are canonical, which historical names +remain accepted, and which identifiers must never be rewritten merely for +branding. Runtime and model support remain governed by +[`RUNTIME_SUPPORT.md`](RUNTIME_SUPPORT.md). + +## Command names + +Every command pair resolves to one real executable and one object graph. The +name used to invoke the executable controls its help/build identity, public +prompt identity, and `engine_id`; it does not select a different runtime. +Runtime diagnostic prefixes remain in the legacy namespace during this bridge +phase. + +| Canonical command | Compatibility command | Capability `engine_id` | +| --- | --- | --- | +| `hebrus` | `ds4` | `hebrus` / `ds4` | +| `hebrus-server` | `ds4-server` | `hebrus` / `ds4` | +| `hebrus-agent` | `ds4-agent` | `hebrus` / `ds4` | +| `hebrus-bench` | `ds4-bench` | `hebrus` / `ds4` | +| `hebrus-eval` | `ds4-eval` | `hebrus` / `ds4` | + +The compatibility commands remain supported through at least the complete 1.x +release line. Removing them would require usage evidence, a new accepted +decision record, release notes, and a separately tested migration. + +Options, defaults, streams, exit codes, model admission, HTTP protocols, +generated tokens, and runtime plans must remain equivalent across each pair. +Only the invoked command identity may differ. `make command-alias-test` checks +the model-free command surface in both Metal and CPU build profiles. + +## Structured runtime identity + +All ten command names expose `--capabilities=json` without loading a model. +Consumers must accept `engine_id` values `hebrus` and `ds4` during the bridge, +reject unknown schema versions, and validate the remaining fields exactly. +They must not infer support by scanning source filenames, C symbols, diagnostic +sentences, or binary strings. + +Changing `engine_id` does not change the schema-1 model-family list or the +ExpertMajor contract. In particular, both identities report: + +- `expert_major.version: 2`; +- `expert_major.tensor: "ds4.expert_major.v2"`; +- GGML storage wire value `0`; +- MLX affine4 storage wire value `1`, with Qwen group size `64`. + +## Permanently preserved identifiers + +The following are data or provenance, not product copy. They remain unchanged: + +- `ds4.expert_major.v2` and the retired `ds4.expert_major.v1` rejection marker; +- ExpertMajor magic, version, offsets, digest semantics, and numeric wire + values; +- disk-KV magic, version, and payload ABI; +- existing model filenames, immutable revisions, byte counts, and SHA-256 + values containing `DS4`; +- historical benchmark, release, issue, and pull-request links; +- Git history, authorship, copyright, and the fork relationship to + [`antirez/ds4`](https://github.com/antirez/ds4). + +A branded display label may say “Hebrus ExpertMajor v2”; serialized bytes and +identifiers do not change. + +## Environment variables and paths + +`DS4_*` environment variables remain the accepted runtime namespace in the +bridge release. `HEBRUS_*` aliases are deferred until a central resolver can +enforce one policy consistently. When aliases are introduced, conflicting old +and new values must fail closed; a textual mass replacement is not permitted. + +Existing checkouts, model paths, caches, benchmark records, and application +data are not copied or renamed. A repository redirect is transport +compatibility, not permission to reset an absolute local path. + +## Repository and application bridge + +Until the administrative rename occurs, the engine repository remains +`andreaborio/ds4`. Bridge consumers accept both `andreaborio/ds4` and the +planned `andreaborio/hebrus` identity over HTTPS or SSH. The existing fork is +renamed in place; no history-less replacement is created and the old repository +name must not be reused. + +DSBox remains the companion application's visible and persisted identity until +its separate name is selected. The bridge application resolves +`hebrus-server` first, falls back to `ds4-server`, accepts both structured +engine identities, and permits source/string fallback only for a legacy +capability-less `ds4-server`. A capability-less `hebrus-server`, malformed JSON, +unknown schema, wrong backend/role, dirty or wrong revision, and contradictory +ExpertMajor fields fail closed. + +## Migration rules for maintainers + +1. Add compatibility before changing a public name. +2. Keep each rename tranche separate from inference, storage, kernel, and + performance changes. +3. Run `make brand-boundary-audit`; reductions pass, while every new or + increased legacy token group requires an exact reviewed classification. +4. Run the complete model-free gates for identity-only work and the canonical + model-backed release gates for any change that can affect code generation or + inference behavior. +5. Do not begin internal file/symbol migration until the DSBox bridge is + published and the Qwen MLX-affine baseline is qualified. + +The architectural rationale and the acceptance conditions for the rename are +recorded in +[`ADR 0005`](../adr/0005-hebrus-naming-and-compatibility-boundary.md). diff --git a/ds4_agent.c b/ds4_agent.c index ac0945b2b..0a2992f0f 100644 --- a/ds4_agent.c +++ b/ds4_agent.c @@ -2,6 +2,7 @@ #include "ds4_help.h" #include "ds4_kvstore.h" #include "ds4_web.h" +#include "hebrus_identity.h" #include "linenoise.h" #include @@ -33,6 +34,9 @@ * after Enter is pressed while the model is still busy. */ int linenoiseEditInsert(struct linenoiseState *l, const char *c, size_t clen); +static bool agent_hebrus_identity; +static const char *agent_public_command = "ds4-agent"; + static int set_nonblock(int fd, bool on, int *old_flags); static bool agent_parse_bool_default(const char *s, bool def); @@ -8903,7 +8907,7 @@ static void agent_progress_append(char *buf, size_t len, size_t *pos, static void build_prompt_text(const agent_status *st, char *buf, size_t len) { (void)st; - snprintf(buf, len, "ds4-agent> "); + snprintf(buf, len, "%s> ", agent_public_command); } static void agent_progress_bar(int done, int total, double tps, @@ -9981,11 +9985,18 @@ static void agent_format_welcome_banner(const agent_config *cfg, char ctx[32]; agent_format_ctx_size(cfg->gen.ctx_size, ctx, sizeof(ctx)); if (stdout_is_tty()) { - snprintf(buf, len, - "\x1b[1;97mDwarf\x1b[1;94mStar\x1b[0m 🐋 Agent, context %s tokens\n\n", - ctx); + if (agent_hebrus_identity) { + snprintf(buf, len, + "\x1b[1;97mHebrus\x1b[0m 🐋 Agent, context %s tokens\n\n", + ctx); + } else { + snprintf(buf, len, + "\x1b[1;97mDwarf\x1b[1;94mStar\x1b[0m 🐋 Agent, context %s tokens\n\n", + ctx); + } } else { - snprintf(buf, len, "DwarfStar Agent, context %s tokens\n\n", ctx); + snprintf(buf, len, "%s Agent, context %s tokens\n\n", + agent_hebrus_identity ? "Hebrus" : "DwarfStar", ctx); } } @@ -10822,6 +10833,8 @@ static int run_agent(ds4_engine *engine, agent_config *cfg) { #ifndef DS4_AGENT_TEST_NO_MAIN int main(int argc, char **argv) { + agent_hebrus_identity = hebrus_is_canonical_invocation(argv[0]); + if (agent_hebrus_identity) agent_public_command = "hebrus-agent"; hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout, argv[0]); diff --git a/ds4_bench.c b/ds4_bench.c index 5c8ecd517..aa587ac5b 100644 --- a/ds4_bench.c +++ b/ds4_bench.c @@ -1,5 +1,6 @@ #include "ds4.h" #include "ds4_help.h" +#include "hebrus_identity.h" #ifndef DS4_NO_GPU #include "ds4_gpu.h" #endif @@ -53,6 +54,8 @@ typedef struct { bool ssd_streaming_cold; } bench_config; +static const char *bench_public_command = "ds4-bench"; + static double bench_now_sec(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -384,7 +387,7 @@ static int write_frontier_logits_json( } const int argmax = ds4_session_argmax(session); - fprintf(fp, "{\n \"source\":\"ds4-bench\",\n \"model\":"); + fprintf(fp, "{\n \"source\":\"%s\",\n \"model\":", bench_public_command); json_write_string(fp, cfg->model_path); fprintf(fp, ",\n \"backend\":\"%s\",\n \"quality\":%s,\n" @@ -561,6 +564,7 @@ static void log_context_memory(ds4_backend backend, } int main(int argc, char **argv) { + if (hebrus_is_canonical_invocation(argv[0])) bench_public_command = "hebrus-bench"; hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout, argv[0]); diff --git a/ds4_cli.c b/ds4_cli.c index 9619544c2..333e6234e 100644 --- a/ds4_cli.c +++ b/ds4_cli.c @@ -1,5 +1,6 @@ #include "ds4.h" #include "ds4_help.h" +#include "hebrus_identity.h" #include "linenoise.h" /* ds4 CLI. @@ -64,6 +65,7 @@ typedef struct { } cli_config; static volatile sig_atomic_t cli_interrupted; +static const char *cli_repl_prompt = "ds4> "; static void cli_sigint_handler(int sig) { (void)sig; @@ -1495,7 +1497,7 @@ static int run_repl(ds4_engine *engine, cli_config *cfg) { int rc = 0; for (;;) { errno = 0; - char *line = linenoise("ds4> "); + char *line = linenoise(cli_repl_prompt); if (!line) { if (errno == EAGAIN || cli_interrupt_requested()) { cli_interrupt_clear(); @@ -1891,6 +1893,7 @@ static cli_config parse_options(int argc, char **argv) { } int main(int argc, char **argv) { + if (hebrus_is_canonical_invocation(argv[0])) cli_repl_prompt = "hebrus> "; hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout, argv[0]); diff --git a/ds4_eval.c b/ds4_eval.c index f5b5a74ff..f6412fcb0 100644 --- a/ds4_eval.c +++ b/ds4_eval.c @@ -1,5 +1,6 @@ #include "ds4.h" #include "ds4_help.h" +#include "hebrus_identity.h" /* ds4-eval: small built-in benchmark integration test. * @@ -1933,12 +1934,15 @@ static void tui_signal_restore(int sig) { raise(sig); } +static const char *eval_public_command = "ds4-eval"; + static void tui_draw_title(eval_ui *ui) { term_move(1, 1); tui_clear_left_line(ui, 1); char elapsed[32]; format_run_elapsed(elapsed, sizeof(elapsed), tui_run_clock_visible_sec(ui)); - fputs("ds4-eval (" ANSI_BOLD "p" ANSI_RESET ")ause (" ANSI_BOLD "q" ANSI_RESET ")uit", stdout); + printf("%s (" ANSI_BOLD "p" ANSI_RESET ")ause (" ANSI_BOLD "q" ANSI_RESET ")uit", + eval_public_command); printf(" %s", elapsed); if (ui->paused) { fputs(" " ANSI_RED ANSI_BOLD "PAUSED" ANSI_RESET, stdout); @@ -4077,6 +4081,7 @@ static uint32_t eval_residency_context_hint(const eval_config *cfg) { } int main(int argc, char **argv) { + if (hebrus_is_canonical_invocation(argv[0])) eval_public_command = "hebrus-eval"; hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout, argv[0]); diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index 5ba24742d..51da6a0a3 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -168,13 +168,6 @@ "classification": "migration-pending", "maximum": 8 }, - { - "path": "CONTRIBUTING.md", - "location": "content", - "token": "DwarfStar", - "classification": "migration-pending", - "maximum": 2 - }, { "path": "EXPERT_PRUNE.md", "location": "content", @@ -294,13 +287,6 @@ "classification": "compatibility", "maximum": 10 }, - { - "path": "QA_BEFORE_RELEASES.md", - "location": "content", - "token": "DwarfStar", - "classification": "compatibility", - "maximum": 1 - }, { "path": "README.md", "location": "content", @@ -383,7 +369,7 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 82 + "maximum": 48 }, { "path": "docs/ENGINE_REFERENCE.md", @@ -392,13 +378,6 @@ "classification": "migration-pending", "maximum": 41 }, - { - "path": "docs/ENGINE_REFERENCE.md", - "location": "content", - "token": "DwarfStar", - "classification": "migration-pending", - "maximum": 7 - }, { "path": "docs/adr/0001-expert-major-v2-only.md", "location": "content", @@ -623,6 +602,20 @@ "classification": "historical-attribution", "maximum": 2 }, + { + "path": "docs/contracts/BRAND_COMPATIBILITY.md", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 20 + }, + { + "path": "docs/contracts/BRAND_COMPATIBILITY.md", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 2 + }, { "path": "docs/contracts/RUNTIME_SUPPORT.md", "location": "content", From 10904763d6ff96ad7baa77fdde24aafb7df8c2d8 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:26:26 +0200 Subject: [PATCH 14/41] ci: add Linux CPU premerge gate --- .github/workflows/ci.yml | 48 +++++++++++++++++++++++++++++++++++++ Makefile | 15 ++++++------ tests/test_qwen_metadata.py | 39 +++++++++++++++++++++++++----- tools/brand_boundary.json | 2 +- 4 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..70aefae2d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: Linux CPU CI + +on: + pull_request: + push: + branches: + - main + tags: + - "v*" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: linux-cpu-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + premerge: + name: Model-free premerge + runs-on: ubuntu-24.04 + timeout-minutes: 20 + + steps: + - name: Check out the repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + + - name: Record toolchain + run: | + python3 --version + cc --version + make --version + + # This is the complete Linux CPU/model-free gate from the Makefile. It + # does not replace the Apple Metal, qualified-model, or manual release + # evidence required by QA_BEFORE_RELEASES.md. + - name: Run repository and model-free premerge gates + env: + PYTHONDONTWRITEBYTECODE: "1" + run: make premerge diff --git a/Makefile b/Makefile index 34d75b47e..1fdf014ef 100644 --- a/Makefile +++ b/Makefile @@ -570,6 +570,7 @@ help: @echo " make model-free-test Run all tests that do not require a GGUF" @echo " make brand-boundary-audit" @echo " Reject unclassified or increased legacy brand tokens" + @echo " make premerge Run repository audits and Linux CPU/model-free gates" @echo " make clean Remove build outputs" cpu: $(PROGRAMS) @@ -692,7 +693,7 @@ ds4_agent_test: ds4_agent_test.o ds4_agent_test_impl.o ds4_help.o \ ds4_web.o ds4_kvstore.o linenoise.o $(CPU_CORE_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -model-free-test: ds4 ds4_test ds4_agent_test ds4-eval q4k-dot-test \ +model-free-test: $(PROGRAMS) ds4_test ds4_agent_test q4k-dot-test \ tests/test_q4k_top8 \ tests/test_qwen_session \ tests/test_qwen_tokenizer \ @@ -769,24 +770,23 @@ tests/test_q4k_top8: test_q4k_top8.o ds4_test_core.o ds4_build_cpu.o \ tests/test_qwen_session: tests/test_qwen_session.c ds4.c ds4.h ds4_ssd.h ds4_profile.h \ ds4_gpu.h ds4_qwen.h ds4_qwen_unicode.h \ - ds4_build.c ds4_expert_store.h ds4_ssd.c ds4_profile.c ds4_qwen.c \ + ds4_build.c ds4_expert_store.o ds4_ssd.c \ + ds4_profile.c ds4_qwen.c \ ds4_qwen_unicode.c ds4_qwen_unicode_data.inc \ ds4_streaming_hotlist.inc runtime/ds4_glm_graph.inc \ runtime/ds4_deepseek_cache_phase.inc $(CC) $(CFLAGS) $(QWEN_CFLAGS) -DDS4_NO_GPU \ -Wno-unused-function -Wno-unused-parameter -I. -o $@ \ - tests/test_qwen_session.c ds4_build.c ds4_ssd.c \ - ds4_profile.c ds4_qwen.c ds4_qwen_unicode.c $(LDLIBS) + $(filter-out ds4.c,$(filter %.c %.o,$^)) $(LDLIBS) tests/test_qwen_tokenizer: tests/test_qwen_tokenizer.c ds4.c ds4.h \ ds4_kvstore.c ds4_kvstore.h ds4_ssd.h ds4_profile.c ds4_profile.h ds4_gpu.h ds4_qwen.h \ - ds4_qwen_unicode.h ds4_build.c ds4_expert_store.h ds4_ssd.c \ + ds4_qwen_unicode.h ds4_build.c ds4_expert_store.o ds4_ssd.c \ ds4_qwen.c ds4_qwen_unicode.c ds4_qwen_unicode_data.inc \ ds4_streaming_hotlist.inc tests/qwen/qwen36_tokenizer_fixture.inc $(CC) $(CFLAGS) $(QWEN_CFLAGS) -DDS4_NO_GPU \ -Wno-unused-function -Wno-unused-parameter -I. -o $@ \ - tests/test_qwen_tokenizer.c ds4_kvstore.c ds4_build.c ds4_ssd.c \ - ds4_profile.c ds4_qwen.c ds4_qwen_unicode.c $(LDLIBS) + $(filter-out ds4.c,$(filter %.c %.o,$^)) $(LDLIBS) qwen-metadata-test: ds4 tests/test_qwen_metadata.py python3 tests/test_qwen_metadata.py ./ds4 @@ -867,4 +867,5 @@ clean: tests/test_qwen_state tests/test_qwen_unicode \ tests/test_qwen_expert_group \ tests/test_expert_store \ + tests/test_metal_ssd_profile \ tests/test_ssd_residency *.o diff --git a/tests/test_qwen_metadata.py b/tests/test_qwen_metadata.py index e53188cd4..51ae3ac28 100644 --- a/tests/test_qwen_metadata.py +++ b/tests/test_qwen_metadata.py @@ -400,6 +400,24 @@ def require(condition: bool, message: str, result: subprocess.CompletedProcess[s ) +def compiled_backend(binary: Path) -> str: + result = subprocess.run( + [str(binary), "--capabilities=json"], + text=True, + capture_output=True, + check=False, + ) + require(result.returncode == 0, "capability query failed", result) + try: + document = json.loads(result.stdout) + except json.JSONDecodeError as exc: + raise AssertionError(f"capability query returned invalid JSON: {exc}") from exc + backend = document.get("backend") + if backend not in {"cpu", "metal"}: + raise AssertionError(f"capability query returned invalid backend: {backend!r}") + return backend + + def check_frozen_reference() -> None: template = QWEN_CHAT_TEMPLATE_PATH.read_bytes() assert len(template) == 7764 @@ -536,6 +554,7 @@ def main() -> int: print(f"usage: {sys.argv[0]} /path/to/ds4", file=sys.stderr) return 2 binary = Path(sys.argv[1]).resolve() + backend = compiled_backend(binary) check_frozen_reference() with tempfile.TemporaryDirectory(prefix="ds4-qwen-metadata-") as tmp_name: @@ -755,12 +774,20 @@ def replace_tensor( v2_metal_output = v2_metal.stdout + v2_metal.stderr require(v2_metal.returncode != 0, "Qwen native v2 quality guard was not enforced", v2_metal) - require("does not support --quality yet" in v2_metal_output, - "Qwen native v2 did not reach Metal option validation", - v2_metal) - require("DS4_QWEN_EXPERIMENTAL_METAL" not in v2_metal_output, - "Qwen native v2 still requires the experimental Metal flag", - v2_metal) + if backend == "metal": + require("does not support --quality yet" in v2_metal_output, + "Qwen native v2 did not reach Metal option validation", + v2_metal) + require("DS4_QWEN_EXPERIMENTAL_METAL" not in v2_metal_output, + "Qwen native v2 still requires the experimental Metal flag", + v2_metal) + else: + require( + "metal backend requested but it is unavailable in this build" in + v2_metal_output, + "CPU build did not reject the unavailable Metal backend", + v2_metal, + ) v2_model.unlink() print("qwen metadata tests: OK") diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index 51da6a0a3..eb89101e9 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -250,7 +250,7 @@ "location": "content", "token": "ds4", "classification": "compatibility", - "maximum": 504 + "maximum": 493 }, { "path": "Makefile", From 62c6d737f4a402bba4ef925b31963c98611a64a9 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:27:32 +0200 Subject: [PATCH 15/41] build: add portable Hebrus command installation --- Makefile | 81 ++++++++++++++++++++++++++++++++++- QA_BEFORE_RELEASES.md | 4 ++ README.md | 24 +++++++++++ docs/ENGINE_REFERENCE.md | 19 +++++++++ docs/architecture/CODEMAP.md | 6 ++- tests/test_install.sh | 83 ++++++++++++++++++++++++++++++++++++ tools/brand_boundary.json | 11 ++++- 7 files changed, 224 insertions(+), 4 deletions(-) create mode 100755 tests/test_install.sh diff --git a/Makefile b/Makefile index 1fdf014ef..53be2a833 100644 --- a/Makefile +++ b/Makefile @@ -29,13 +29,21 @@ HEBRUS_PROGRAMS := hebrus hebrus-server hebrus-bench hebrus-eval hebrus-agent DS4_PROGRAMS := ds4 ds4-server ds4-bench ds4-eval ds4-agent PROGRAMS := $(HEBRUS_PROGRAMS) $(DS4_PROGRAMS) +PREFIX ?= /usr/local +BINDIR ?= $(PREFIX)/bin +INSTALL ?= install +INSTALL_MODE ?= 0755 +INSTALL_DEST_BINDIR = $(DESTDIR)$(BINDIR) +INSTALL_SOURCE_PROGRAMS = $(addprefix $(INSTALL_SOURCE_BINDIR)/,$(HEBRUS_PROGRAMS)) + .PHONY: all help clean test model-free-test premerge context-audit doc-links \ brand-boundary-audit brand-boundary-test \ imatrix-dataset-check prompt-fixture-check cpu FORCE \ metal build-isolation-test q4k-dot-test qwen-metadata-test \ qwen-reference-test qwen-unicode-test qwen-tokenizer-test \ qwen-expert-group-test expert-store-test metal-ssd-profile-test \ - download-model-test capabilities-test command-alias-test $(PROGRAMS) \ + download-model-test capabilities-test command-alias-test \ + install uninstall install-test $(PROGRAMS) \ ds4_test ds4_agent_test download-model-test: tests/test_download_model.sh download_model.sh @@ -57,6 +65,8 @@ METAL_OBJDIR := $(BUILD_ROOT)/$(METAL_PROFILE)/obj METAL_BINDIR := $(BUILD_ROOT)/$(METAL_PROFILE)/bin CPU_OBJDIR := $(BUILD_ROOT)/$(CPU_PROFILE)/obj CPU_BINDIR := $(BUILD_ROOT)/$(CPU_PROFILE)/bin +INSTALL_SOURCE_BINDIR := $(METAL_BINDIR) +INSTALL_BACKEND := metal METAL_LDLIBS := $(LDLIBS) -framework Foundation -framework Metal @@ -93,6 +103,9 @@ help: @echo " Run all Metal gates that do not require a GGUF" @echo " make build-isolation-test" @echo " Prove Metal -> CPU -> Metal cannot mix artifacts" + @echo " make install Install commands under DESTDIR+$(BINDIR)" + @echo " make uninstall Remove only the ten installed command paths" + @echo " make install-test Verify staged install layout and capabilities" @echo " make brand-boundary-audit" @echo " Reject unclassified or increased legacy brand tokens" @echo " make premerge Run context/docs, isolation, and model-free gates" @@ -547,6 +560,7 @@ prompt-fixture-check: premerge: context-audit doc-links brand-boundary-audit brand-boundary-test \ imatrix-dataset-check prompt-fixture-check build-isolation-test $(MAKE) model-free-test + $(MAKE) install-test git diff --check build-isolation-test: tests/test_build_isolation.sh tests/test_capabilities.py \ @@ -560,6 +574,8 @@ else CFLAGS += -D_GNU_SOURCE -fno-finite-math-only CPU_CORE_OBJS := ds4_cpu.o ds4_build_cpu.o ds4_ssd.o \ ds4_profile.o ds4_expert_store.o ds4_qwen.o ds4_qwen_unicode.o +INSTALL_SOURCE_BINDIR := . +INSTALL_BACKEND := cpu all: cpu @@ -568,6 +584,9 @@ help: @echo " make / make cpu Build ./hebrus* plus ./ds4* aliases" @echo " make test Build and run tests" @echo " make model-free-test Run all tests that do not require a GGUF" + @echo " make install Install commands under DESTDIR+$(BINDIR)" + @echo " make uninstall Remove only the ten installed command paths" + @echo " make install-test Verify staged install layout and capabilities" @echo " make brand-boundary-audit" @echo " Reject unclassified or increased legacy brand tokens" @echo " make premerge Run repository audits and Linux CPU/model-free gates" @@ -752,6 +771,7 @@ prompt-fixture-check: premerge: context-audit doc-links brand-boundary-audit brand-boundary-test \ imatrix-dataset-check prompt-fixture-check model-free-test + $(MAKE) install-test git diff --check q4k-dot-test: tests/test_q4k_dot.c @@ -855,6 +875,65 @@ metal-ssd-profile-test: tests/test_metal_ssd_profile endif +install: $(INSTALL_SOURCE_PROGRAMS) + @set -eu; \ + case "$(BINDIR)" in \ + /*) ;; \ + *) echo "install: BINDIR must be absolute: $(BINDIR)" >&2; exit 2 ;; \ + esac; \ + dest="$(INSTALL_DEST_BINDIR)"; \ + mkdir -p "$$dest"; \ + for name in $(PROGRAMS); do \ + path="$$dest/$$name"; \ + if [ -d "$$path" ] && [ ! -L "$$path" ]; then \ + echo "install: refusing to replace directory $$path" >&2; \ + exit 2; \ + fi; \ + done; \ + tmp=; \ + trap 'test -z "$$tmp" || rm -f "$$tmp"' 0 1 2 3 15; \ + for name in $(HEBRUS_PROGRAMS); do \ + tmp="$$dest/.$$name.install.$$$$"; \ + rm -f "$$tmp"; \ + $(INSTALL) -m "$(INSTALL_MODE)" \ + "$(INSTALL_SOURCE_BINDIR)/$$name" "$$tmp"; \ + rm -f "$$dest/$$name"; \ + mv "$$tmp" "$$dest/$$name"; \ + tmp=; \ + done; \ + for canonical in $(HEBRUS_PROGRAMS); do \ + legacy=ds4$${canonical#hebrus}; \ + tmp="$$dest/.$$legacy.install.$$$$"; \ + rm -f "$$tmp"; \ + ln -s "$$canonical" "$$tmp"; \ + rm -f "$$dest/$$legacy"; \ + mv "$$tmp" "$$dest/$$legacy"; \ + tmp=; \ + done + +uninstall: + @set -eu; \ + case "$(BINDIR)" in \ + /*) ;; \ + *) echo "uninstall: BINDIR must be absolute: $(BINDIR)" >&2; exit 2 ;; \ + esac; \ + dest="$(INSTALL_DEST_BINDIR)"; \ + for name in $(PROGRAMS); do \ + path="$$dest/$$name"; \ + if [ -d "$$path" ] && [ ! -L "$$path" ]; then \ + echo "uninstall: refusing to remove directory $$path" >&2; \ + exit 2; \ + fi; \ + done; \ + for name in $(PROGRAMS); do \ + rm -f "$$dest/$$name"; \ + done + +install-test: $(INSTALL_SOURCE_PROGRAMS) tests/test_install.sh \ + tests/test_capabilities.py tests/test_command_aliases.py + HEBRUS_INSTALL_BACKEND="$(INSTALL_BACKEND)" MAKE="$(MAKE)" \ + sh tests/test_install.sh + clean: rm -rf "$(BUILD_ROOT)" rm -f hebrus hebrus-server hebrus-bench hebrus-eval hebrus-agent \ diff --git a/QA_BEFORE_RELEASES.md b/QA_BEFORE_RELEASES.md index 920464f1d..f28ff8941 100644 --- a/QA_BEFORE_RELEASES.md +++ b/QA_BEFORE_RELEASES.md @@ -62,6 +62,10 @@ SHA-256, manifest contract, and compatible runtime commit are one release gate. - Run `make command-alias-test` and confirm each `ds4*` profile command is a symlink to its single real `hebrus*` binary, with exact build-info, help, capability, invalid-option, and retired-option parity. +- Run `make install-test` and confirm a temporary `DESTDIR` receives five real + canonical executables, five relative compatibility aliases, valid capability + documents, no embedded checkout/staging paths, and an explicit-only, + idempotent uninstall. - Run whitespace checks before committing: `git diff --check`. - Confirm both names of the CLI, server, and agent render help cleanly, with diff --git a/README.md b/README.md index 9652202fb..5a3622bbb 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,30 @@ make -j make model-free-test ``` +For a user-local installation that does not require `sudo`: + +```sh +make install PREFIX="$HOME/.local" +export PATH="$HOME/.local/bin:$PATH" + +hebrus --build-info +``` + +`make install` copies the five canonical `hebrus*` executables and creates the +five `ds4*` compatibility names as relative symlinks to those files. Package +builders can stage the same layout without touching the host filesystem: + +```sh +make install DESTDIR="$PWD/package-root" PREFIX=/usr/local +make uninstall DESTDIR="$PWD/package-root" PREFIX=/usr/local +``` + +`PREFIX` defaults to `/usr/local`; `BINDIR` may override its `bin` directory. +Uninstall removes only the ten explicit command paths and leaves the directory +and every unrelated file intact. `make install-test` verifies the staged +layout, aliases, capabilities, and uninstall boundary without a model or +privileged access. + For a published DeepSeek V4 Flash artifact on a qualified 64 GiB-or-larger host: diff --git a/docs/ENGINE_REFERENCE.md b/docs/ENGINE_REFERENCE.md index 7fe1b160e..e2315b1c3 100644 --- a/docs/ENGINE_REFERENCE.md +++ b/docs/ENGINE_REFERENCE.md @@ -147,6 +147,25 @@ commands use Hebrus in help and build metadata; compatibility `ds4*` aliases retain their legacy command identity. Existing runtime diagnostics remain in the compatibility namespace during this bridge. +The Makefile can install that same single-binary-per-role layout locally or +into a package staging root. `PREFIX` defaults to `/usr/local`, `BINDIR` +defaults to `$(PREFIX)/bin`, and `DESTDIR` prepends a packaging root without +being embedded in the executables. For example: + +```sh +make install PREFIX="$HOME/.local" +make install DESTDIR="$PWD/package-root" PREFIX=/usr/local +make uninstall DESTDIR="$PWD/package-root" PREFIX=/usr/local +make install-test +``` + +Installation copies only the five canonical executables and creates each +legacy command as a relative symlink to its canonical peer. Uninstall removes +only those ten named paths; it does not remove the containing directory or any +other file. The test uses a temporary `DESTDIR`, validates all capability +documents and aliases without loading a model, exercises `BINDIR` overriding, +and proves the explicit uninstall boundary. + Every canonical and compatibility executable accepts `--capabilities=json` without loading a model. The deterministic schema reports the build revision, compiled backend, diff --git a/docs/architecture/CODEMAP.md b/docs/architecture/CODEMAP.md index 733f22e78..5119f41c1 100644 --- a/docs/architecture/CODEMAP.md +++ b/docs/architecture/CODEMAP.md @@ -18,7 +18,10 @@ recorded in [`ADR 0005`](../adr/0005-hebrus-naming-and-compatibility-boundary.md The Makefile links one canonical `hebrus*` executable per role from these entrypoints and publishes the corresponding `ds4*` name as a symlink to the -same file. There are no alias-specific wrappers or object graphs. +same file. It also owns the `DESTDIR`/`PREFIX`/`BINDIR` install boundary: +canonical executables are copied, compatibility aliases stay relative, and +uninstall names every removable path explicitly. There are no alias-specific +wrappers or object graphs. | Path | Primary responsibility | | --- | --- | @@ -106,6 +109,7 @@ not validated. | `tests/` | Model-free, model-backed, kernel, tokenizer, server, and build-isolation regressions | | `tests/test_capabilities.py` | Exact schema and cross-executable checks for the model-free build/capability contract | | `tests/test_command_aliases.py` | Canonical/legacy symlink layout, binary identity, and CLI-output parity checks | +| `tests/test_install.sh` | Temporary-root install/uninstall layout, path portability, capability, and explicit-removal checks | | `tools/brand_boundary.json` + `tools/brand_boundary_audit.py` | Exact canonical, bridged, and permanently preserved identity contract plus explicit per-file legacy `ds4`/`DS4`/`DwarfStar` classification and monotonic count ceilings; `--check` rejects contract drift, new groups, and increases, while `--refresh` requires exact authorizations before widening a ceiling | | `tests/test_brand_boundary_audit.py` | Fail-closed fixtures for new files and tokens, increases, reductions, deterministic refresh, and invalid manifests | | `tests/qwen/` | Qwen fixtures, provenance, reference collectors, and model-specific gates | diff --git a/tests/test_install.sh b/tests/test_install.sh new file mode 100755 index 000000000..ca38ac6fa --- /dev/null +++ b/tests/test_install.sh @@ -0,0 +1,83 @@ +#!/bin/sh +set -eu + +ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +MAKE_BIN=${MAKE:-make} +BACKEND=${HEBRUS_INSTALL_BACKEND:?missing HEBRUS_INSTALL_BACKEND} +TMP_BASE=$(mktemp -d "${TMPDIR:-/tmp}/hebrus-install.XXXXXX") +STAGE="$TMP_BASE/stage root" +PREFIX_ONE=/portable-prefix +PREFIX_TWO=/unused-prefix +BINDIR_TWO=/custom/hebrus-bin +PROGRAMS="hebrus hebrus-server hebrus-bench hebrus-eval hebrus-agent ds4 ds4-server ds4-bench ds4-eval ds4-agent" +PAIRS="hebrus:ds4 hebrus-server:ds4-server hebrus-bench:ds4-bench hebrus-eval:ds4-eval hebrus-agent:ds4-agent" + +cleanup() { + rm -rf "$TMP_BASE" +} +trap cleanup 0 1 2 3 15 + +fail() { + echo "install-layout: FAIL: $*" >&2 + exit 1 +} + +assert_removed() { + bin_dir=$1 + for name in $PROGRAMS; do + path="$bin_dir/$name" + if [ -e "$path" ] || [ -L "$path" ]; then + fail "uninstall left $path" + fi + done +} + +assert_layout() { + bin_dir=$1 + count=$(find "$bin_dir" -mindepth 1 -maxdepth 1 -print | wc -l | tr -d ' ') + [ "$count" = 10 ] || fail "$bin_dir contains $count entries instead of 10" + for pair in $PAIRS; do + canonical=${pair%%:*} + legacy=${pair#*:} + [ -x "$bin_dir/$canonical" ] || fail "missing executable $canonical" + [ ! -L "$bin_dir/$canonical" ] || fail "$canonical is a symlink" + [ -L "$bin_dir/$legacy" ] || fail "$legacy is not a symlink" + [ "$(readlink "$bin_dir/$legacy")" = "$canonical" ] || \ + fail "$legacy is not a relative alias to $canonical" + done +} + +mkdir -p "$STAGE" +cd "$ROOT" + +"$MAKE_BIN" install DESTDIR="$STAGE" PREFIX="$PREFIX_ONE" +BIN_ONE="$STAGE$PREFIX_ONE/bin" +assert_layout "$BIN_ONE" +[ ! -e "$STAGE/usr/local/bin/hebrus" ] || fail "install ignored PREFIX" +python3 tests/test_capabilities.py --bin-dir "$BIN_ONE" --backend "$BACKEND" +python3 tests/test_command_aliases.py --bin-dir "$BIN_ONE" \ + --backend "$BACKEND" --layout profile + +for name in hebrus hebrus-server hebrus-bench hebrus-eval hebrus-agent; do + capabilities=$("$BIN_ONE/$name" --capabilities=json) + case "$capabilities" in + *"$ROOT"*|*"$STAGE"*) fail "$name exposes a checkout or install path" ;; + esac +done + +printf '%s\n' keep > "$BIN_ONE/keep-me" +"$MAKE_BIN" uninstall DESTDIR="$STAGE" PREFIX="$PREFIX_ONE" +assert_removed "$BIN_ONE" +[ "$(cat "$BIN_ONE/keep-me")" = keep ] || fail "uninstall removed an unrelated file" +"$MAKE_BIN" uninstall DESTDIR="$STAGE" PREFIX="$PREFIX_ONE" + +"$MAKE_BIN" install DESTDIR="$STAGE" PREFIX="$PREFIX_TWO" BINDIR="$BINDIR_TWO" +BIN_TWO="$STAGE$BINDIR_TWO" +assert_layout "$BIN_TWO" +[ ! -e "$STAGE$PREFIX_TWO/bin/hebrus" ] || fail "install ignored BINDIR" +python3 tests/test_command_aliases.py --bin-dir "$BIN_TWO" \ + --backend "$BACKEND" --layout profile +"$MAKE_BIN" uninstall DESTDIR="$STAGE" PREFIX="$PREFIX_TWO" BINDIR="$BINDIR_TWO" +assert_removed "$BIN_TWO" + +echo "install-layout: PASS ($BACKEND, DESTDIR/PREFIX/BINDIR, explicit uninstall)" diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index eb89101e9..3e9ed7b8c 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -250,7 +250,7 @@ "location": "content", "token": "ds4", "classification": "compatibility", - "maximum": 493 + "maximum": 494 }, { "path": "Makefile", @@ -292,7 +292,7 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 11 + "maximum": 12 }, { "path": "README.md", @@ -2338,6 +2338,13 @@ "classification": "migration-pending", "maximum": 9 }, + { + "path": "tests/test_install.sh", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 10 + }, { "path": "tests/test_metal_ssd_profile.c", "location": "content", From 178d80530ad1d37750e74d867d85c02c30305a8a Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:30:04 +0200 Subject: [PATCH 16/41] build: track bridge identity dependencies --- Makefile | 20 ++++++++++---------- docs/contracts/BRAND_COMPATIBILITY.md | 3 ++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 53be2a833..b35c30009 100644 --- a/Makefile +++ b/Makefile @@ -635,22 +635,22 @@ ds4_qwen_unicode.o: ds4_qwen_unicode.c ds4_qwen_unicode.h \ ds4_qwen_unicode_data.inc $(CC) $(CFLAGS) -c -o $@ ds4_qwen_unicode.c -ds4_cli.o: ds4_cli.c ds4.h ds4_ssd.h ds4_help.h linenoise.h +ds4_cli.o: ds4_cli.c ds4.h ds4_ssd.h ds4_help.h hebrus_identity.h linenoise.h $(CC) $(CFLAGS) -c -o $@ ds4_cli.c -ds4_help.o: ds4_help.c ds4_help.h +ds4_help.o: ds4_help.c ds4_help.h hebrus_identity.h $(CC) $(CFLAGS) -c -o $@ ds4_help.c ds4_server.o: ds4_server.c ds4.h ds4_ssd.h ds4_help.h ds4_kvstore.h rax.h $(CC) $(CFLAGS) -c -o $@ ds4_server.c -ds4_bench.o: ds4_bench.c ds4.h ds4_ssd.h ds4_help.h +ds4_bench.o: ds4_bench.c ds4.h ds4_ssd.h ds4_help.h hebrus_identity.h $(CC) $(CFLAGS) -c -o $@ ds4_bench.c -ds4_eval.o: ds4_eval.c ds4.h ds4_ssd.h ds4_help.h +ds4_eval.o: ds4_eval.c ds4.h ds4_ssd.h ds4_help.h hebrus_identity.h $(CC) $(CFLAGS) -c -o $@ ds4_eval.c -ds4_agent.o: ds4_agent.c ds4.h ds4_ssd.h ds4_help.h ds4_kvstore.h ds4_web.h linenoise.h +ds4_agent.o: ds4_agent.c ds4.h ds4_ssd.h ds4_help.h hebrus_identity.h ds4_kvstore.h ds4_web.h linenoise.h $(CC) $(CFLAGS) -c -o $@ ds4_agent.c ds4_web.o: ds4_web.c ds4_web.h @@ -666,7 +666,7 @@ ds4_agent_test.o: tests/ds4_agent_test.c tests/internal/ds4_agent_unit.h $(CC) $(CFLAGS) -DDS4_NO_GPU -c -o $@ tests/ds4_agent_test.c ds4_agent_test_impl.o: ds4_agent.c ds4.h ds4_ssd.h \ - ds4_help.h ds4_kvstore.h ds4_web.h linenoise.h \ + ds4_help.h hebrus_identity.h ds4_kvstore.h ds4_web.h linenoise.h \ tests/internal/ds4_agent_unit.h tests/internal/ds4_agent_unit.inc $(CC) $(CFLAGS) -DDS4_NO_GPU -DDS4_AGENT_TEST \ -DDS4_AGENT_TEST_NO_MAIN -Wno-unused-function -c -o $@ ds4_agent.c @@ -690,19 +690,19 @@ ds4_test_core.o: ds4.c ds4.h ds4_ssd.h ds4_profile.h \ $(CC) $(CFLAGS) $(QWEN_CFLAGS) -DDS4_NO_GPU -DDS4_TEST_HOOKS \ -Wno-unused-function -Wno-unused-parameter -c -o $@ ds4.c -ds4_cli_cpu.o: ds4_cli.c ds4.h ds4_ssd.h ds4_help.h linenoise.h +ds4_cli_cpu.o: ds4_cli.c ds4.h ds4_ssd.h ds4_help.h hebrus_identity.h linenoise.h $(CC) $(CFLAGS) -DDS4_NO_GPU -c -o $@ ds4_cli.c ds4_server_cpu.o: ds4_server.c ds4.h ds4_ssd.h ds4_help.h ds4_kvstore.h rax.h $(CC) $(CFLAGS) -DDS4_NO_GPU -c -o $@ ds4_server.c -ds4_bench_cpu.o: ds4_bench.c ds4.h ds4_ssd.h ds4_help.h +ds4_bench_cpu.o: ds4_bench.c ds4.h ds4_ssd.h ds4_help.h hebrus_identity.h $(CC) $(CFLAGS) -DDS4_NO_GPU -c -o $@ ds4_bench.c -ds4_eval_cpu.o: ds4_eval.c ds4.h ds4_ssd.h ds4_help.h +ds4_eval_cpu.o: ds4_eval.c ds4.h ds4_ssd.h ds4_help.h hebrus_identity.h $(CC) $(CFLAGS) -DDS4_NO_GPU -c -o $@ ds4_eval.c -ds4_agent_cpu.o: ds4_agent.c ds4.h ds4_ssd.h ds4_help.h ds4_kvstore.h ds4_web.h linenoise.h +ds4_agent_cpu.o: ds4_agent.c ds4.h ds4_ssd.h ds4_help.h hebrus_identity.h ds4_kvstore.h ds4_web.h linenoise.h $(CC) $(CFLAGS) -DDS4_NO_GPU -c -o $@ ds4_agent.c ds4_test: ds4_test.o ds4_help.o ds4_kvstore.o rax.o $(CPU_CORE_OBJS) diff --git a/docs/contracts/BRAND_COMPATIBILITY.md b/docs/contracts/BRAND_COMPATIBILITY.md index 91980c920..21362a6c8 100644 --- a/docs/contracts/BRAND_COMPATIBILITY.md +++ b/docs/contracts/BRAND_COMPATIBILITY.md @@ -30,7 +30,8 @@ decision record, release notes, and a separately tested migration. Options, defaults, streams, exit codes, model admission, HTTP protocols, generated tokens, and runtime plans must remain equivalent across each pair. Only the invoked command identity may differ. `make command-alias-test` checks -the model-free command surface in both Metal and CPU build profiles. +the active build profile; macOS `make premerge` also exercises the CPU aliases +through the build-isolation gate. ## Structured runtime identity From 277c9b183b3faf35be3d055060787f92dacebb3a Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:44:31 +0200 Subject: [PATCH 17/41] docs: reconcile the published Qwen release --- CONTRIBUTING.md | 12 ++++++++++-- FORK_NOTES.md | 5 ++--- QA_BEFORE_RELEASES.md | 5 ++++- docs/expert-major-v2-roadmap.md | 9 ++++++--- docs/qwen-expert-major-store.md | 9 ++++++--- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26aad6c0e..5fb0f0877 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -116,8 +116,10 @@ bytes, SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. It is published at immutable repository revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`, and -`download_model.sh qwen-v2` must pin that exact identity. The older Q4_K_S -object is an incompatible negative fixture, not a runnable fallback. See +its manifest requires runtime commit +`73a332fef82a0bcdd567d17e0de17aa004cad85d` or a compatible descendant. +`download_model.sh qwen-v2` must pin and validate that exact identity. The older +Q4_K_S object is an incompatible negative fixture, not a runnable fallback. See [`docs/qwen-expert-major-store.md`](docs/qwen-expert-major-store.md). CUDA and ROCm are frozen and their backend source and build targets are absent @@ -141,6 +143,12 @@ On macOS, also run `make build-isolation-test`. The CPU-only binaries live in `build/cpu-$(uname -m)/bin`; `make cpu` intentionally leaves the root Metal commands unchanged. +The GitHub Actions workflow runs the full Linux CPU/model-free premerge gate +and a macOS arm64 lane that repeats the CPU suite, builds the Metal command +surface, and checks Metal/CPU artifact isolation. Hosted compilation is not +Metal kernel or qualified-model evidence; the hardware release gates below +remain mandatory. + The CPU backend is a reference/debug path, not the production performance target. Remember that executing the CPU path on Metal can crash the system because of a kernel bug in macOS. diff --git a/FORK_NOTES.md b/FORK_NOTES.md index b15478baa..05bfd836f 100644 --- a/FORK_NOTES.md +++ b/FORK_NOTES.md @@ -17,7 +17,7 @@ equivalent implementation, this fork converges on it and removes the duplicate. Ledger snapshot: fork-wide ExpertMajor v2-only migration for DeepSeek, GLM, and Qwen, including the earlier GLM promotion and Metal lifecycle milestones; -upstream `main` `6d5572d`, audited 2026-07-20. +upstream `main` `f9602e5`, audited 2026-07-21. Commit links and live branch differences remain authoritative if this dated snapshot drifts. @@ -41,8 +41,7 @@ snapshot drifts. | Mixed-precision routed-expert streaming (`fefa426`, later sync) | serve per-layer boosted expert quants under SSD streaming | **CONVERGED UPSTREAM** — the fork takes upstream's implementation after `5800f15`; no competing delta should be preserved. | | Fork-wide ExpertMajor v2-only admission (2026-07-20 tranche) | use the family-tagged, checksummed `ds4.expert_major.v2` store as the only physical routed-expert layout for DeepSeek, GLM, and Qwen; reconstruct logical tensor identity while translating all Metal mappings and reads through the manifest | **FORK MAIN CONTRACT** — local Apple Metal only. Canonical GGUFs are offline converter inputs, not inference artifacts. ExpertMajor v1, sidecars, CPU, CUDA, ROCm, and distributed execution have no compatibility path. Normal startup is `./ds4 -m MODEL.gguf --ctx N`; invalid family, inventory, geometry, or manifest state fails before inference. | | GLM 5.2 ExpertMajor v2 (`55d2bab` release gate) | #520 correctness + #528 indexed prepare, compact DSA KV, physical grouped prefill, one-record selected decode reads, translated advisory I/O, tokenizer/prompt routing, measured 601-expert 64 GiB policy, embedded single-payload GGUF, and removal of the retired predicted-install path | **FORK MAIN, EXPERTMAJOR-ONLY METAL CONTRACT** — the broken port moved from 1.27 to 1.77-1.81 decode t/s; the final simple gate was 1.79 t/s and the old/new same-condition A/B was 1.75/1.74, restoring runtime parity with the qualified path. The earlier rested-storage median remains 11.08/1.90 t/s. Model-backed output is coherent and deterministic; synthetic canonical/native direct, scalar-batch, and grouped-batch math passes. #520/#528 remain open upstream. GLM canonical/non-Metal/distributed execution and old GLM layouts have no compatibility path. | -| Qwen3.6-35B-A3B ExpertMajor v2 (`qwen35moe`, 2026-07-20 release gate) | distinct v2 family ID, generic converter, embedded single-layout Q4_K_S store, AUTO resident/SSD admission, model-backed output, and existing resident prefill/parallel decode schedules | **FORK MAIN; UPSTREAM ISSUE [#462](https://github.com/antirez/ds4/issues/462)** — the experimental guard, v1 loader, sidecar, and canonical inference paths are retired. The 20,808,566,880-byte v2 artifact has SHA-256 `d7c43a6388ec20e6fe5530850350f96fdb0ac37c5ce36d3e5f92b172c447f56b`. Resident v2/v1/v2 at 2K measured 318.96/29.54, 320.59/29.59, and 318.83/29.54 prefill/decode t/s; evidence was byte-identical and no new swapout occurred. The v1 arm is historical control evidence only. | -| Qwen unified MLX-affine v2 replacement (2026-07-21) | replace the equal-size routed Q4_K_S payload with MLX affine 4-bit/group-64 records while retaining the embedded ExpertMajor v2 GGUF container and one AUTO resident/SSD configuration | **FORK MAIN CONTRACT; SUPERSEDES THE 2026-07-20 Q4_K_S ARTIFACT** — normal inference accepts only `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`, 20,808,566,880 bytes, SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. The old Q4_K_S artifact remains historical evidence and is deliberately rejected rather than decoded through compatibility code. | +| Qwen3.6-35B-A3B ExpertMajor v2 (`qwen35moe`, current affine contract) | distinct v2 family ID, generic converter, one embedded MLX affine4/group-64 routed store, hardware-aware AUTO resident/SSD admission, affine Metal kernels, and model-backed resident/SSD qualification | **FORK MAIN RUNTIME; PUBLISHED ARTIFACT; UPSTREAM ISSUE [#462](https://github.com/antirez/ds4/issues/462)** — the only runnable release is `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`, 20,808,566,880 bytes, SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`, pinned at repository revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`. The previously published Q4_K_S v2 artifact and its SHA-256 `d7c43a6388ec20e6fe5530850350f96fdb0ac37c5ce36d3e5f92b172c447f56b` remain dated 2026-07-20 measurement/control evidence only; the current runtime rejects that store before inference. The experimental guard, v1 loader, sidecar, and canonical inference paths remain retired. | | Qwen hardware-aware Metal policy (2026-07-21) | named 16/24/32/36/48/64/96/128 GiB profiles, continuous resident reserve, normal-pressure cold/warm file-cache equivalence, and live resident/SSD fallback | **FORK MAIN CONTRACT** — `make premerge` passes; AUTO resolves to SSD on M1 Pro 16 GiB and resident on AC-powered M1 Pro 32 GiB with zero swap. Split-K transfer is independently confirmed at 2K on 16 GiB (15.00 versus 9.70 decode t/s) and 32K on 32 GiB (19.02 versus 1.63 decode t/s), with identical greedy token IDs in each A/B. | | Qwen exact K/V pair-blit (2026-07-22) | validate both full-attention K/V cache copies transactionally, then record them in one ordered Metal blit encoder in resident and SSD modes | **FORK MAIN, OWNER-AUTHORIZED STRUCTURAL MERGE; RELEASE MATRIX INCOMPLETE** — removes exactly 50% of K/V blit-encoder creation without changing copy commands, bytes, commits, SSD I/O, logits, or decode evidence. M5 resident 2K decode improved 56.52 to 58.06 t/s (+2.72%); M5 SSD 2K decode improved +0.75%; M1 Pro 32 GiB measured +1.37% at 2K and -0.20% at contaminated 8K. Every valid mean end-to-end inference regression stayed below 2%; the M1 Pro 16 GiB timing gate is explicitly invalid due alternating 88--189 s external host stalls, while its correctness and zero-swap gates passed. The 128/32K/65K/100K release arms remain unqualified, so this merge is not recorded as completion of the normal performance-promotion gate. | | Exact paired Q8 Metal decode (2026-07-20 long-context experiment) | compute two independent Q8 matvec outputs in one exact Metal dispatch while preserving bit-identical accumulation order | **REJECTED AND REMOVED** — the final zero-swap Qwen 32K A/B/B/A preserved exact evidence but did not show a speed win. Scalar means were 67.03 prefill t/s, 40,217.705 ms decode wall, 314.818 ms p50, and 317.563 ms p95; paired means were 40,273.322 ms, 315.395 ms, and 317.665 ms. The differences were neutral-to-worse and smaller than cohort spread, so the M5 kernel, dispatch, and dedicated test were removed. The clean post-removal source then measured 65.76 prefill t/s, about 3.18 decode t/s, and 315.193/317.518 ms p50/p95 with exact evidence and zero swap. | diff --git a/QA_BEFORE_RELEASES.md b/QA_BEFORE_RELEASES.md index f28ff8941..db4f5c05a 100644 --- a/QA_BEFORE_RELEASES.md +++ b/QA_BEFORE_RELEASES.md @@ -30,7 +30,7 @@ target, or an artifact whose complete output hash is missing. | `DEEPSEEK_V2` | `DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix-DS4-ExpertMajor-v2.gguf`; 86,720,114,272 bytes; SHA-256 `8378080263eb9224f7228d72e2afa4ac3cf74a116023fdec2c596ff228a33e3f` | | `DEEPSEEK_MIXED_V2` | Non-applicable until a mixed-quant DeepSeek ExpertMajor v2 artifact has a publication record with exact filename, bytes, and complete output SHA-256; do not resolve or use this variable before qualification | | `GLM_V2` | `GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf`; 262,147,193,504 bytes; SHA-256 `7f5017e3076e706c78f2a5322b035a9e2f6519c65ff5b6be8b2d91aeff61505d` | -| `QWEN_V2` | `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`; immutable repository revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`; 20,808,566,880 bytes; SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`; MLX affine4/group-64 routed storage | +| `QWEN_V2` | `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`; immutable repository revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`; 20,808,566,880 bytes; SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`; MLX affine4/group-64 routed storage; minimum compatible runtime commit `73a332fef82a0bcdd567d17e0de17aa004cad85d` | | `QWEN_RETIRED_Q4_NEGATIVE` | Rejection-only input: `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf`; 20,808,566,880 bytes; SHA-256 `d7c43a6388ec20e6fe5530850350f96fdb0ac37c5ce36d3e5f92b172c447f56b`; it must fail before inference | Record the test machine by hardware model, unified memory, OS build, and power @@ -49,6 +49,9 @@ SHA-256, manifest contract, and compatible runtime commit are one release gate. `make clean && make`. - Prove macOS Metal/CPU artifact isolation: `make build-isolation-test`. +- Confirm the GitHub Linux and macOS jobs passed on the exact commit. Treat the + hosted macOS job as compile/model-free evidence only, never as a substitute + for the Metal kernel and qualified-model lanes on release hardware. - Build CPU-only binaries as a compile check. On macOS they remain under `build/cpu-$(uname -m)/bin` and must not replace the root Metal commands: `make cpu`. diff --git a/docs/expert-major-v2-roadmap.md b/docs/expert-major-v2-roadmap.md index 13e320c2f..3be9db877 100644 --- a/docs/expert-major-v2-roadmap.md +++ b/docs/expert-major-v2-roadmap.md @@ -18,7 +18,7 @@ records: - record, component, layer, and payload offsets with explicit alignment; - source GGUF, payload, and manifest SHA-256 identities. -DS4 reconstructs canonical logical descriptors for graph binding and cache +Hebrus reconstructs canonical logical descriptors for graph binding and cache identity only. Resident mapping, prefill, decode, and SSD cache fills resolve physical bytes through the manifest. Unknown families or versions, canonical routed tensors beside a v2 store, incomplete inventories, unsupported types, @@ -49,14 +49,17 @@ layout. - **Implemented:** distinct `qwen35moe` family ID, 40-layer fail-closed geometry, generic v2 conversion, logical reconstruction, resident mapping, and SSD translation. -- **Qualified candidate:** MLX affine4/group-64 resident and SSD execution on +- **Qualified and published:** MLX affine4/group-64 resident and SSD execution on the hardware lanes recorded in the current benchmark index. - **Correctness:** the final resident and SSD lane retains deterministic token and logit comparisons with no new swapout in the recorded qualification. -- **Local candidate, not yet published:** +- **Release artifact:** `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`, 20,808,566,880 bytes, SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. + The immutable repository revision is + `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`; its manifest requires runtime + commit `73a332fef82a0bcdd567d17e0de17aa004cad85d` or a compatible descendant. The former Q4_K_S artifact is retained only as a fail-closed negative case. ### DeepSeek V4 diff --git a/docs/qwen-expert-major-store.md b/docs/qwen-expert-major-store.md index 0198af039..23e3be711 100644 --- a/docs/qwen-expert-major-store.md +++ b/docs/qwen-expert-major-store.md @@ -90,6 +90,7 @@ used for publication. | Artifact | `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf` | | Artifact bytes | 20,808,566,880 | | Artifact SHA-256 | `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d` | +| Minimum compatible runtime commit | `73a332fef82a0bcdd567d17e0de17aa004cad85d` | | Canonical source bytes | 20,808,563,424 | | Canonical source SHA-256 | `c33efb67bde86c9ba1f9e79c2dc42627170963bef0e915ab9b91a55cfb6d0fcd` | | Storage marker | `MLX_AFFINE4`, group size 64 | @@ -99,9 +100,11 @@ not contain a second copy of the routed weights. These exact bytes and their matching manifest are published at immutable repository revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`. -`download_model.sh qwen-v2` pins that revision. The older Q4_K_S object is -incompatible with the current runtime contract and is retained only for -fail-closed testing. +The manifest requires runtime commit +`73a332fef82a0bcdd567d17e0de17aa004cad85d` or a compatible descendant; +`download_model.sh qwen-v2` pins that revision and validates this relationship. +The older Q4_K_S object is incompatible with the current runtime contract and +is retained only for fail-closed testing. ## Qualification From c0ccef5bde0e5c1acd9d39c54522991d98176782 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:45:07 +0200 Subject: [PATCH 18/41] ci: add the macOS arm64 build gate --- .github/workflows/ci.yml | 48 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70aefae2d..b1ed6e38e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Linux CPU CI +name: Engine CI on: pull_request: @@ -13,12 +13,12 @@ permissions: contents: read concurrency: - group: linux-cpu-${{ github.workflow }}-${{ github.ref }} + group: engine-ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - premerge: - name: Model-free premerge + linux-premerge: + name: Linux CPU model-free premerge runs-on: ubuntu-24.04 timeout-minutes: 20 @@ -46,3 +46,43 @@ jobs: env: PYTHONDONTWRITEBYTECODE: "1" run: make premerge + + macos-build: + name: macOS arm64 build and CPU model-free gates + runs-on: macos-15 + timeout-minutes: 30 + + steps: + - name: Check out the repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + + - name: Record toolchain and runner + run: | + uname -a + python3 --version + cc --version + make --version + + - name: Run CPU model-free gates on macOS + env: + PYTHONDONTWRITEBYTECODE: "1" + run: make UNAME_S=Linux NATIVE_CPU_FLAG= premerge + + - name: Prove clean Metal and CPU build isolation + run: | + make clean + make build-isolation-test + + - name: Verify the Metal command and install surfaces + run: make capabilities-test command-alias-test install-test + + # A standard hosted macOS job proves compilation and model-free command + # contracts only. Metal kernel execution and qualified-GGUF evidence stay + # in the explicit hardware release gate in QA_BEFORE_RELEASES.md. From 7775df30b0aa78bae62a85bace1666c8aceee726 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:42:03 +0200 Subject: [PATCH 19/41] test: cover visible Hebrus alias identity --- Makefile | 32 ++++++++++-- ds4_agent.c | 29 ++++------- ds4_bench.c | 7 +-- ds4_cli.c | 6 +-- ds4_eval.c | 6 +-- hebrus_identity.h | 54 ++++++++++++++++++++ tests/test_visible_identity.c | 92 +++++++++++++++++++++++++++++++++++ tools/brand_boundary.json | 39 +++++++++++---- 8 files changed, 224 insertions(+), 41 deletions(-) create mode 100644 tests/test_visible_identity.c diff --git a/Makefile b/Makefile index b35c30009..9b3b9bf33 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ INSTALL_SOURCE_PROGRAMS = $(addprefix $(INSTALL_SOURCE_BINDIR)/,$(HEBRUS_PROGRAM qwen-reference-test qwen-unicode-test qwen-tokenizer-test \ qwen-expert-group-test expert-store-test metal-ssd-profile-test \ download-model-test capabilities-test command-alias-test \ + visible-identity-test \ install uninstall install-test $(PROGRAMS) \ ds4_test ds4_agent_test @@ -90,7 +91,8 @@ METAL_TEST_BINS := \ $(METAL_BINDIR)/test_qwen_expert_group \ $(METAL_BINDIR)/test_expert_store \ $(METAL_BINDIR)/test_metal_ssd_profile \ - $(METAL_BINDIR)/test_ssd_residency + $(METAL_BINDIR)/test_ssd_residency \ + $(METAL_BINDIR)/test_visible_identity all: metal @@ -406,6 +408,21 @@ $(METAL_BINDIR)/test_ssd_residency: \ @mkdir -p "$(@D)" $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) +$(METAL_BINDIR)/test_visible_identity: \ + tests/test_visible_identity.c hebrus_identity.h + @mkdir -p "$(@D)" + $(CC) $(CFLAGS) -I. -o $@ $< + +$(CPU_BINDIR)/test_visible_identity: \ + tests/test_visible_identity.c hebrus_identity.h + @mkdir -p "$(@D)" + $(CC) $(CFLAGS) -DDS4_NO_GPU -I. -o $@ $< + +visible-identity-test: $(METAL_BINDIR)/test_visible_identity \ + $(CPU_BINDIR)/test_visible_identity + $(METAL_BINDIR)/test_visible_identity + $(CPU_BINDIR)/test_visible_identity + $(METAL_BINDIR)/test_qwen_gdn_ref: \ $(METAL_OBJDIR)/test_qwen_gdn_ref.o $(METAL_OBJDIR)/ds4_qwen_ref.o \ $(METAL_OBJDIR)/ds4_qwen.o @@ -509,6 +526,7 @@ model-free-test: metal ds4_test ds4_agent_test $(METAL_BINDIR)/test_q4k_dot \ $(METAL_BINDIR)/test_expert_store \ $(METAL_BINDIR)/test_metal_ssd_profile \ $(METAL_BINDIR)/test_ssd_residency download-model-test \ + visible-identity-test \ tests/test_capabilities.py tests/test_command_aliases.py python3 tests/test_capabilities.py --bin-dir $(METAL_BINDIR) --backend metal python3 tests/test_command_aliases.py --bin-dir $(METAL_BINDIR) \ @@ -721,7 +739,8 @@ model-free-test: $(PROGRAMS) ds4_test ds4_agent_test q4k-dot-test \ tests/test_qwen_expert_group \ tests/test_expert_store \ tests/test_metal_ssd_profile \ - tests/test_ssd_residency download-model-test tests/test_capabilities.py \ + tests/test_ssd_residency download-model-test visible-identity-test \ + tests/test_capabilities.py \ tests/test_command_aliases.py python3 tests/test_capabilities.py --bin-dir . --backend cpu python3 tests/test_command_aliases.py --bin-dir . --backend cpu --layout profile @@ -754,6 +773,12 @@ capabilities-test: $(PROGRAMS) tests/test_capabilities.py command-alias-test: $(PROGRAMS) tests/test_command_aliases.py python3 tests/test_command_aliases.py --bin-dir . --backend cpu --layout profile +tests/test_visible_identity: tests/test_visible_identity.c hebrus_identity.h + $(CC) $(CFLAGS) -DDS4_NO_GPU -I. -o $@ $< + +visible-identity-test: tests/test_visible_identity + ./tests/test_visible_identity + test: model-free-test ./ds4_test @@ -947,4 +972,5 @@ clean: tests/test_qwen_expert_group \ tests/test_expert_store \ tests/test_metal_ssd_profile \ - tests/test_ssd_residency *.o + tests/test_ssd_residency \ + tests/test_visible_identity *.o diff --git a/ds4_agent.c b/ds4_agent.c index 0a2992f0f..d7c6b976e 100644 --- a/ds4_agent.c +++ b/ds4_agent.c @@ -34,8 +34,7 @@ * after Enter is pressed while the model is still busy. */ int linenoiseEditInsert(struct linenoiseState *l, const char *c, size_t clen); -static bool agent_hebrus_identity; -static const char *agent_public_command = "ds4-agent"; +static const char *agent_invocation = "ds4-agent"; static int set_nonblock(int fd, bool on, int *old_flags); static bool agent_parse_bool_default(const char *s, bool def); @@ -555,7 +554,7 @@ static agent_config parse_options(int argc, char **argv) { .mtp_margin = 3.0f, }, .gen = { - .system = "You are a helpful coding assistant running inside ds4-agent.", + .system = hebrus_agent_system_prompt_for(argv[0]), .n_predict = 50000, .ctx_size = 100000, .temperature = DS4_DEFAULT_TEMPERATURE, @@ -8907,7 +8906,7 @@ static void agent_progress_append(char *buf, size_t len, size_t *pos, static void build_prompt_text(const agent_status *st, char *buf, size_t len) { (void)st; - snprintf(buf, len, "%s> ", agent_public_command); + hebrus_agent_format_prompt_for(agent_invocation, buf, len); } static void agent_progress_bar(int done, int total, double tps, @@ -9984,20 +9983,11 @@ static void agent_format_welcome_banner(const agent_config *cfg, char *buf, size_t len) { char ctx[32]; agent_format_ctx_size(cfg->gen.ctx_size, ctx, sizeof(ctx)); - if (stdout_is_tty()) { - if (agent_hebrus_identity) { - snprintf(buf, len, - "\x1b[1;97mHebrus\x1b[0m 🐋 Agent, context %s tokens\n\n", - ctx); - } else { - snprintf(buf, len, - "\x1b[1;97mDwarf\x1b[1;94mStar\x1b[0m 🐋 Agent, context %s tokens\n\n", - ctx); - } - } else { - snprintf(buf, len, "%s Agent, context %s tokens\n\n", - agent_hebrus_identity ? "Hebrus" : "DwarfStar", ctx); - } + hebrus_agent_format_welcome_banner_for(agent_invocation, + stdout_is_tty(), + ctx, + buf, + len); } static void editor_write_welcome_banner(agent_editor *editor, @@ -10833,8 +10823,7 @@ static int run_agent(ds4_engine *engine, agent_config *cfg) { #ifndef DS4_AGENT_TEST_NO_MAIN int main(int argc, char **argv) { - agent_hebrus_identity = hebrus_is_canonical_invocation(argv[0]); - if (agent_hebrus_identity) agent_public_command = "hebrus-agent"; + agent_invocation = argv[0]; hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout, argv[0]); diff --git a/ds4_bench.c b/ds4_bench.c index aa587ac5b..93f3f5330 100644 --- a/ds4_bench.c +++ b/ds4_bench.c @@ -54,7 +54,7 @@ typedef struct { bool ssd_streaming_cold; } bench_config; -static const char *bench_public_command = "ds4-bench"; +static const char *bench_invocation = "ds4-bench"; static double bench_now_sec(void) { struct timespec ts; @@ -387,7 +387,8 @@ static int write_frontier_logits_json( } const int argmax = ds4_session_argmax(session); - fprintf(fp, "{\n \"source\":\"%s\",\n \"model\":", bench_public_command); + fprintf(fp, "{\n \"source\":\"%s\",\n \"model\":", + hebrus_bench_command_for(bench_invocation)); json_write_string(fp, cfg->model_path); fprintf(fp, ",\n \"backend\":\"%s\",\n \"quality\":%s,\n" @@ -564,7 +565,7 @@ static void log_context_memory(ds4_backend backend, } int main(int argc, char **argv) { - if (hebrus_is_canonical_invocation(argv[0])) bench_public_command = "hebrus-bench"; + bench_invocation = argv[0]; hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout, argv[0]); diff --git a/ds4_cli.c b/ds4_cli.c index 333e6234e..f8d72d0f9 100644 --- a/ds4_cli.c +++ b/ds4_cli.c @@ -65,7 +65,7 @@ typedef struct { } cli_config; static volatile sig_atomic_t cli_interrupted; -static const char *cli_repl_prompt = "ds4> "; +static const char *cli_invocation = "ds4"; static void cli_sigint_handler(int sig) { (void)sig; @@ -1497,7 +1497,7 @@ static int run_repl(ds4_engine *engine, cli_config *cfg) { int rc = 0; for (;;) { errno = 0; - char *line = linenoise(cli_repl_prompt); + char *line = linenoise(hebrus_cli_prompt_for(cli_invocation)); if (!line) { if (errno == EAGAIN || cli_interrupt_requested()) { cli_interrupt_clear(); @@ -1893,7 +1893,7 @@ static cli_config parse_options(int argc, char **argv) { } int main(int argc, char **argv) { - if (hebrus_is_canonical_invocation(argv[0])) cli_repl_prompt = "hebrus> "; + cli_invocation = argv[0]; hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout, argv[0]); diff --git a/ds4_eval.c b/ds4_eval.c index f6412fcb0..06ff104bc 100644 --- a/ds4_eval.c +++ b/ds4_eval.c @@ -1934,7 +1934,7 @@ static void tui_signal_restore(int sig) { raise(sig); } -static const char *eval_public_command = "ds4-eval"; +static const char *eval_invocation = "ds4-eval"; static void tui_draw_title(eval_ui *ui) { term_move(1, 1); @@ -1942,7 +1942,7 @@ static void tui_draw_title(eval_ui *ui) { char elapsed[32]; format_run_elapsed(elapsed, sizeof(elapsed), tui_run_clock_visible_sec(ui)); printf("%s (" ANSI_BOLD "p" ANSI_RESET ")ause (" ANSI_BOLD "q" ANSI_RESET ")uit", - eval_public_command); + hebrus_eval_command_for(eval_invocation)); printf(" %s", elapsed); if (ui->paused) { fputs(" " ANSI_RED ANSI_BOLD "PAUSED" ANSI_RESET, stdout); @@ -4081,7 +4081,7 @@ static uint32_t eval_residency_context_hint(const eval_config *cfg) { } int main(int argc, char **argv) { - if (hebrus_is_canonical_invocation(argv[0])) eval_public_command = "hebrus-eval"; + eval_invocation = argv[0]; hebrus_help_set_invocation(argv[0]); if (ds4_build_info_requested(argc, argv)) { ds4_build_info_print(stdout, argv[0]); diff --git a/hebrus_identity.h b/hebrus_identity.h index fd5b9ef5d..f3e6293be 100644 --- a/hebrus_identity.h +++ b/hebrus_identity.h @@ -2,6 +2,8 @@ #define HEBRUS_IDENTITY_H #include +#include +#include #include static inline const char *hebrus_invocation_name(const char *argv0) { @@ -17,4 +19,56 @@ static inline bool hebrus_is_canonical_invocation(const char *argv0) { strcmp(name, "hebrus-eval") == 0); } +static inline const char *hebrus_cli_prompt_for(const char *argv0) { + return hebrus_is_canonical_invocation(argv0) ? "hebrus> " : "ds4> "; +} + +static inline const char *hebrus_agent_command_for(const char *argv0) { + return hebrus_is_canonical_invocation(argv0) ? "hebrus-agent" : "ds4-agent"; +} + +static inline const char *hebrus_agent_brand_for(const char *argv0) { + return hebrus_is_canonical_invocation(argv0) ? "Hebrus" : "DwarfStar"; +} + +static inline const char *hebrus_agent_system_prompt_for(const char *argv0) { + (void)argv0; + return "You are a helpful coding assistant running inside ds4-agent."; +} + +static inline void hebrus_agent_format_prompt_for(const char *argv0, + char *buf, + size_t len) { + if (len == 0) return; + snprintf(buf, len, "%s> ", hebrus_agent_command_for(argv0)); +} + +static inline void hebrus_agent_format_welcome_banner_for(const char *argv0, + bool tty, + const char *ctx, + char *buf, + size_t len) { + if (len == 0) return; + if (tty && hebrus_is_canonical_invocation(argv0)) { + snprintf(buf, len, + "\x1b[1;97mHebrus\x1b[0m 🐋 Agent, context %s tokens\n\n", + ctx); + } else if (tty) { + snprintf(buf, len, + "\x1b[1;97mDwarf\x1b[1;94mStar\x1b[0m 🐋 Agent, context %s tokens\n\n", + ctx); + } else { + snprintf(buf, len, "%s Agent, context %s tokens\n\n", + hebrus_agent_brand_for(argv0), ctx); + } +} + +static inline const char *hebrus_eval_command_for(const char *argv0) { + return hebrus_is_canonical_invocation(argv0) ? "hebrus-eval" : "ds4-eval"; +} + +static inline const char *hebrus_bench_command_for(const char *argv0) { + return hebrus_is_canonical_invocation(argv0) ? "hebrus-bench" : "ds4-bench"; +} + #endif diff --git a/tests/test_visible_identity.c b/tests/test_visible_identity.c new file mode 100644 index 000000000..549208622 --- /dev/null +++ b/tests/test_visible_identity.c @@ -0,0 +1,92 @@ +#include "hebrus_identity.h" + +#include +#include +#include +#include + +static int failures; + +static void expect_string(const char *label, + const char *actual, + const char *expected) { + if (strcmp(actual, expected) == 0) return; + fprintf(stderr, + "visible-identity: %s mismatch\n expected: %s\n actual: %s\n", + label, + expected, + actual); + failures++; +} + +static void expect_agent_prompt(const char *argv0, const char *expected) { + char buf[64]; + hebrus_agent_format_prompt_for(argv0, buf, sizeof(buf)); + expect_string(argv0, buf, expected); +} + +static void expect_agent_banner(const char *argv0, + bool tty, + const char *expected) { + char buf[192]; + hebrus_agent_format_welcome_banner_for(argv0, tty, "100K", buf, sizeof(buf)); + expect_string(argv0, buf, expected); +} + +int main(void) { + const char *canonical_system = + hebrus_agent_system_prompt_for("/tmp/hebrus-agent"); + const char *legacy_system = + hebrus_agent_system_prompt_for("/tmp/ds4-agent"); + const char *expected_system = + "You are a helpful coding assistant running inside ds4-agent."; + + expect_string("canonical CLI prompt", + hebrus_cli_prompt_for("/tmp/hebrus"), + "hebrus> "); + expect_string("legacy CLI prompt", + hebrus_cli_prompt_for("/tmp/ds4"), + "ds4> "); + + expect_agent_prompt("/tmp/hebrus-agent", "hebrus-agent> "); + expect_agent_prompt("/tmp/ds4-agent", "ds4-agent> "); + expect_agent_banner( + "/tmp/hebrus-agent", + true, + "\x1b[1;97mHebrus\x1b[0m 🐋 Agent, context 100K tokens\n\n"); + expect_agent_banner( + "/tmp/ds4-agent", + true, + "\x1b[1;97mDwarf\x1b[1;94mStar\x1b[0m 🐋 Agent, context 100K tokens\n\n"); + expect_agent_banner("/tmp/hebrus-agent", + false, + "Hebrus Agent, context 100K tokens\n\n"); + expect_agent_banner("/tmp/ds4-agent", + false, + "DwarfStar Agent, context 100K tokens\n\n"); + + expect_string("canonical eval title", + hebrus_eval_command_for("/tmp/hebrus-eval"), + "hebrus-eval"); + expect_string("legacy eval title", + hebrus_eval_command_for("/tmp/ds4-eval"), + "ds4-eval"); + expect_string("canonical benchmark source", + hebrus_bench_command_for("/tmp/hebrus-bench"), + "hebrus-bench"); + expect_string("legacy benchmark source", + hebrus_bench_command_for("/tmp/ds4-bench"), + "ds4-bench"); + + expect_string("canonical agent system prompt", canonical_system, expected_system); + expect_string("legacy agent system prompt", legacy_system, expected_system); + if (strlen(canonical_system) != strlen(legacy_system) || + memcmp(canonical_system, legacy_system, strlen(canonical_system)) != 0) { + fputs("visible-identity: canonical and legacy system prompts differ\n", stderr); + failures++; + } + + if (failures) return EXIT_FAILURE; + puts("visible-identity: PASS"); + return EXIT_SUCCESS; +} diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index 3e9ed7b8c..5c8e04c58 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -257,7 +257,7 @@ "location": "content", "token": "DS4", "classification": "compatibility", - "maximum": 41 + "maximum": 43 }, { "path": "ONEDGE_IMATRIX.md", @@ -796,7 +796,7 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 354 + "maximum": 353 }, { "path": "ds4_agent.c", @@ -805,13 +805,6 @@ "classification": "migration-pending", "maximum": 45 }, - { - "path": "ds4_agent.c", - "location": "content", - "token": "DwarfStar", - "classification": "migration-pending", - "maximum": 1 - }, { "path": "ds4_bench.c", "location": "path", @@ -1568,6 +1561,20 @@ "classification": "migration-pending", "maximum": 40 }, + { + "path": "hebrus_identity.h", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 5 + }, + { + "path": "hebrus_identity.h", + "location": "content", + "token": "DwarfStar", + "classification": "compatibility", + "maximum": 1 + }, { "path": "linenoise.c", "location": "content", @@ -2499,6 +2506,20 @@ "classification": "migration-pending", "maximum": 53 }, + { + "path": "tests/test_visible_identity.c", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 12 + }, + { + "path": "tests/test_visible_identity.c", + "location": "content", + "token": "DwarfStar", + "classification": "compatibility", + "maximum": 1 + }, { "path": "tools/brand_boundary_audit.py", "location": "content", From 8f20de74aca7dac007121c62fe461db7a257ba1f Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:45:37 +0200 Subject: [PATCH 20/41] tools: reconcile the post-release brand inventory --- tools/brand_boundary.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index 5c8e04c58..b4ced0215 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -376,7 +376,7 @@ "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 41 + "maximum": 43 }, { "path": "docs/adr/0001-expert-major-v2-only.md", @@ -656,7 +656,7 @@ "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 4 + "maximum": 3 }, { "path": "docs/glm52-expert-major-v2.md", @@ -740,7 +740,7 @@ "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 9 + "maximum": 10 }, { "path": "ds4.c", @@ -1048,7 +1048,7 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 6573 + "maximum": 6572 }, { "path": "ds4_metal.m", @@ -2119,7 +2119,7 @@ "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 4 + "maximum": 6 }, { "path": "tests/qwen/UNICODE_DATA_PROVENANCE.md", @@ -2308,7 +2308,7 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 4 + "maximum": 5 }, { "path": "tests/test_download_model.sh", From 26a34496a0fa8b9cee3e35827a238a2b71c6a487 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:51:28 +0200 Subject: [PATCH 21/41] docs: describe the published Qwen artifact --- ACKNOWLEDGMENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ACKNOWLEDGMENTS.md b/ACKNOWLEDGMENTS.md index b03e9d6c6..b34e0136a 100644 --- a/ACKNOWLEDGMENTS.md +++ b/ACKNOWLEDGMENTS.md @@ -43,8 +43,8 @@ same thing as a linked dependency. [`MLX`](https://github.com/ml-explore/mlx) and [`MLX-LM`](https://github.com/ml-explore/mlx-lm) informed the current Qwen MLX affine4/group-64 storage layout, scheduling comparisons, and validation work. -The local Qwen candidate repacks routed weights from an MLX-format source into -the existing checksummed ExpertMajor v2 GGUF container. +The published Qwen release repacks routed weights from an MLX-format source +into the existing checksummed ExpertMajor v2 GGUF container. Hebrus does not link the MLX libraries at runtime. This credit does not imply Apple authorship, sponsorship, or endorsement of Hebrus. From 1eb3e371f6cb2fafdaf203a599389308c74bd47b Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 21:53:28 +0200 Subject: [PATCH 22/41] test: cover short resident affine batches --- ds4_gpu.h | 2 + ds4_metal.m | 335 ++++++++++++++++++++++++++++++++++++++ tests/ds4_test.c | 1 + tools/brand_boundary.json | 8 +- 4 files changed, 342 insertions(+), 4 deletions(-) diff --git a/ds4_gpu.h b/ds4_gpu.h index c4d9cef28..e0dbb29a1 100644 --- a/ds4_gpu.h +++ b/ds4_gpu.h @@ -1663,6 +1663,8 @@ int ds4_gpu_internal_qwen35_expert_group_test(void); /* Model-free source-translation/fail-closed regression used by * --metal-expert-pack. */ int ds4_gpu_internal_qwen35_expert_pack_test(void); +/* Model-free resident MLX-affine regression across the routed-MM threshold. */ +int ds4_gpu_internal_qwen35_affine_resident_short_test(void); /* Canonical-vs-embedded GLM Q2 regression for direct and grouped execution. */ int ds4_gpu_internal_expert_store_v2_kernel_test(void); diff --git a/ds4_metal.m b/ds4_metal.m index 4bb329c8d..0304e8686 100644 --- a/ds4_metal.m +++ b/ds4_metal.m @@ -35522,6 +35522,341 @@ int ds4_gpu_internal_qwen35_expert_pack_test(void) { return ok; } +typedef struct { + uint8_t qs[32]; + uint16_t scale_bf16; + uint16_t bias_bf16; +} ds4_gpu_internal_mlx_affine4_block; + +static void ds4_gpu_internal_mlx_affine4_fill_row( + uint8_t *row, + uint8_t value) { + const uint8_t packed = (uint8_t)(value | (uint8_t)(value << 4u)); + for (uint32_t group = 0; group < 4u; group++) { + ds4_gpu_internal_mlx_affine4_block *block = + (ds4_gpu_internal_mlx_affine4_block *)( + row + (uint64_t)group * + sizeof(ds4_gpu_internal_mlx_affine4_block)); + memset(block->qs, packed, sizeof(block->qs)); + block->scale_bf16 = 0x3f80u; + block->bias_bf16 = 0u; + } +} + +static int ds4_gpu_internal_qwen35_affine_resident_case( + const void *model_map, + uint64_t model_size, + uint64_t gate_offset, + uint64_t up_offset, + uint64_t down_offset, + uint64_t gate_expert_bytes, + uint64_t down_expert_bytes, + uint32_t n_tokens, + bool expected_mid_f16) { + enum { + N_EXPERT = 256, + N_ROUTE = 8, + IN_DIM = 256, + MID_DIM = 256, + OUT_DIM = 4, + GGML_TYPE_Q4_K = 12, + }; + const uint64_t row_bytes = + 4u * sizeof(ds4_gpu_internal_mlx_affine4_block); + const uint64_t pair_rows = (uint64_t)n_tokens * N_ROUTE; + const uint64_t mid_values = pair_rows * MID_DIM; + const uint64_t out_values = (uint64_t)n_tokens * OUT_DIM; + const uint64_t input_values_count = (uint64_t)n_tokens * IN_DIM; + const uint64_t route_values = (uint64_t)n_tokens * N_ROUTE; + ds4_gpu_tensor *out = NULL; + ds4_gpu_tensor *gate = NULL; + ds4_gpu_tensor *up = NULL; + ds4_gpu_tensor *mid = NULL; + ds4_gpu_tensor *experts = NULL; + ds4_gpu_tensor *selected = NULL; + ds4_gpu_tensor *weights = NULL; + ds4_gpu_tensor *x = NULL; + float *out_host = NULL; + float *gate_host = NULL; + float *up_host = NULL; + float *input_host = NULL; + int32_t *selected_host = NULL; + float *weights_host = NULL; + int ok = 0; + const char *failure = "allocation"; + + if (n_tokens == 0) return 0; + out = ds4_gpu_tensor_alloc(out_values * sizeof(float)); + gate = ds4_gpu_tensor_alloc(mid_values * sizeof(float)); + up = ds4_gpu_tensor_alloc(mid_values * sizeof(float)); + mid = ds4_gpu_tensor_alloc(mid_values * sizeof(float)); + experts = ds4_gpu_tensor_alloc( + pair_rows * OUT_DIM * sizeof(float)); + selected = ds4_gpu_tensor_alloc(route_values * sizeof(int32_t)); + weights = ds4_gpu_tensor_alloc(route_values * sizeof(float)); + x = ds4_gpu_tensor_alloc(input_values_count * sizeof(float)); + out_host = malloc((size_t)out_values * sizeof(float)); + gate_host = malloc((size_t)mid_values * sizeof(float)); + up_host = malloc((size_t)mid_values * sizeof(float)); + input_host = malloc((size_t)input_values_count * sizeof(float)); + selected_host = malloc((size_t)route_values * sizeof(int32_t)); + weights_host = malloc((size_t)route_values * sizeof(float)); + if (!out || !gate || !up || !mid || !experts || !selected || !weights || + !x || !out_host || !gate_host || !up_host || !input_host || + !selected_host || !weights_host) { + goto cleanup; + } + + for (uint64_t index = 0; index < input_values_count; index++) { + input_host[index] = 1.0f / (float)IN_DIM; + } + for (uint32_t token = 0; token < n_tokens; token++) { + for (uint32_t route = 0; route < N_ROUTE; route++) { + const uint64_t index = (uint64_t)token * N_ROUTE + route; + selected_host[index] = (int32_t)((token + route) % 15u); + weights_host[index] = 1.0f / (float)N_ROUTE; + } + } + failure = "upload"; + if (!ds4_gpu_tensor_write(x, 0, input_host, + input_values_count * sizeof(float)) || + !ds4_gpu_tensor_write(selected, 0, selected_host, + route_values * sizeof(int32_t)) || + !ds4_gpu_tensor_write(weights, 0, weights_host, + route_values * sizeof(float))) { + goto cleanup; + } + + bool mid_is_f16 = !expected_mid_f16; + int expert_group_used = -1; + failure = "execution"; + if (!ds4_gpu_qwen35_routed_moe_batch_select_tensor( + out, gate, up, mid, experts, + model_map, model_size, + gate_offset, up_offset, down_offset, + GGML_TYPE_Q4_K, GGML_TYPE_Q4_K, + gate_expert_bytes, row_bytes, + down_expert_bytes, row_bytes, + IN_DIM, MID_DIM, OUT_DIM, + selected, weights, N_EXPERT, N_ROUTE, 0.0f, x, 0, + n_tokens, &mid_is_f16, 0, &expert_group_used)) { + goto cleanup; + } + if (mid_is_f16 != expected_mid_f16) { + failure = "mid precision"; + goto cleanup; + } + if (expert_group_used != 0) { + failure = "unexpected expert schedule"; + goto cleanup; + } + /* The batch-32 fused pair+SwiGLU kernel does not materialize gate/up. + * Below that threshold, checking both projections proves the MV path + * decoded the affine payload instead of treating its bytes as Q4_K. */ + const bool inspect_projections = n_tokens < 32u; + failure = "readback"; + if (!ds4_gpu_tensor_read(out, 0, out_host, + out_values * sizeof(float)) || + (inspect_projections && + (!ds4_gpu_tensor_read(gate, 0, gate_host, + mid_values * sizeof(float)) || + !ds4_gpu_tensor_read(up, 0, up_host, + mid_values * sizeof(float))))) { + goto cleanup; + } + + const float silu_one = 1.0f / (1.0f + expf(-1.0f)); + const float projection_tolerance = 1.0e-4f; + for (uint32_t token = 0; token < n_tokens; token++) { + float expected_out = 0.0f; + for (uint32_t route = 0; route < N_ROUTE; route++) { + const uint64_t pair = (uint64_t)token * N_ROUTE + route; + const float value = (float)(selected_host[pair] + 1); + expected_out += weights_host[pair] * value * value; + for (uint32_t column = 0; + inspect_projections && column < MID_DIM; + column++) { + const uint64_t index = pair * MID_DIM + column; + if (!isfinite(gate_host[index]) || + !isfinite(up_host[index]) || + fabsf(gate_host[index] - 1.0f) > projection_tolerance || + fabsf(up_host[index] - value) > projection_tolerance) { + fprintf(stderr, + "ds4: resident MLX-affine batch %u projection " + "mismatch token=%u route=%u column=%u " + "gate=%.9g up=%.9g expected_up=%.9g\n", + n_tokens, token, route, column, + gate_host[index], up_host[index], value); + failure = "affine projection"; + goto cleanup; + } + } + } + expected_out *= (float)MID_DIM * silu_one; + const float output_tolerance = fmaxf(0.5f, fabsf(expected_out) * 0.0025f); + for (uint32_t row = 0; row < OUT_DIM; row++) { + const float actual = out_host[(uint64_t)token * OUT_DIM + row]; + if (!isfinite(actual) || + fabsf(actual - expected_out) > output_tolerance) { + fprintf(stderr, + "ds4: resident MLX-affine batch %u output mismatch " + "token=%u row=%u actual=%.9g expected=%.9g " + "tolerance=%.9g\n", + n_tokens, token, row, actual, expected_out, + output_tolerance); + failure = "output"; + goto cleanup; + } + } + } + ok = 1; + +cleanup: + ds4_gpu_tensor_free(out); + ds4_gpu_tensor_free(gate); + ds4_gpu_tensor_free(up); + ds4_gpu_tensor_free(mid); + ds4_gpu_tensor_free(experts); + ds4_gpu_tensor_free(selected); + ds4_gpu_tensor_free(weights); + ds4_gpu_tensor_free(x); + free(out_host); + free(gate_host); + free(up_host); + free(input_host); + free(selected_host); + free(weights_host); + if (!ok) { + fprintf(stderr, + "ds4: resident MLX-affine synthetic batch %u failed at %s\n", + n_tokens, failure); + } + return ok; +} + +int ds4_gpu_internal_qwen35_affine_resident_short_test(void) { + enum { + N_EXPERT = 256, + MID_DIM = 256, + OUT_DIM = 4, + }; + const uint64_t row_bytes = + 4u * sizeof(ds4_gpu_internal_mlx_affine4_block); + const uint64_t gate_expert_bytes = (uint64_t)MID_DIM * row_bytes; + const uint64_t down_expert_bytes = (uint64_t)OUT_DIM * row_bytes; + const uint64_t record_bytes = + gate_expert_bytes * 2u + down_expert_bytes; + const uint64_t component_gate_bytes = + (uint64_t)N_EXPERT * gate_expert_bytes; + const uint64_t component_down_bytes = + (uint64_t)N_EXPERT * down_expert_bytes; + const uint64_t payload_bytes = (uint64_t)N_EXPERT * record_bytes; + const uint64_t gate_offset = 0; + const uint64_t up_offset = component_gate_bytes; + const uint64_t down_offset = component_gate_bytes * 2u; + const uint64_t model_size = down_offset + component_down_bytes; + char path[] = "/tmp/ds4-affine-resident-short.XXXXXX"; + int fd = -1; + void *model_map = MAP_FAILED; + int installed = 0; + int ok = 0; + + if (sizeof(ds4_gpu_internal_mlx_affine4_block) != 36u || + row_bytes != 144u || payload_bytes != model_size || + (!g_initialized && !ds4_gpu_init()) || + g_qwen35_expert_pack.active) { + return 0; + } + ds4_gpu_set_glm_model(false); + ds4_gpu_set_quality(false); + ds4_gpu_set_ssd_streaming(false); + ds4_gpu_set_model_fd(-1); + + fd = mkstemp(path); + if (fd < 0) goto cleanup; + (void)unlink(path); + if (ftruncate(fd, (off_t)model_size) != 0) goto cleanup; + model_map = mmap(NULL, (size_t)model_size, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (model_map == MAP_FAILED) goto cleanup; + + uint8_t *payload = model_map; + for (uint32_t expert = 0; expert < N_EXPERT; expert++) { + const uint8_t value = (uint8_t)(expert % 15u + 1u); + uint8_t *record = payload + (uint64_t)expert * record_bytes; + uint8_t *gate = record; + uint8_t *up = gate + gate_expert_bytes; + uint8_t *down = up + gate_expert_bytes; + for (uint32_t row = 0; row < MID_DIM; row++) { + ds4_gpu_internal_mlx_affine4_fill_row( + gate + (uint64_t)row * row_bytes, 1u); + ds4_gpu_internal_mlx_affine4_fill_row( + up + (uint64_t)row * row_bytes, value); + } + for (uint32_t row = 0; row < OUT_DIM; row++) { + ds4_gpu_internal_mlx_affine4_fill_row( + down + (uint64_t)row * row_bytes, value); + } + } + if (msync(model_map, (size_t)model_size, MS_SYNC) != 0) goto cleanup; + + const ds4_gpu_expert_store_layer_v2 layer = { + .layer = 0, + .data_offset = 0, + .data_size = payload_bytes, + .record_bytes = record_bytes, + .component_offset = { + 0, + gate_expert_bytes, + gate_expert_bytes * 2u, + }, + .component_bytes = { + gate_expert_bytes, + gate_expert_bytes, + down_expert_bytes, + }, + }; + if (!ds4_gpu_expert_store_v2_install( + fd, model_size, 1, N_EXPERT, + DS4_EXPERT_STORE_STORAGE_MLX_AFFINE4, 64, &layer)) { + goto cleanup; + } + installed = 1; + if (!ds4_gpu_expert_store_v2_bind_layer( + 0, model_size, gate_offset, up_offset, down_offset) || + !ds4_gpu_expert_store_v2_enable_resident() || + !ds4_gpu_internal_qwen35_affine_resident_case( + model_map, model_size, + gate_offset, up_offset, down_offset, + gate_expert_bytes, down_expert_bytes, + 1, false) || + !ds4_gpu_internal_qwen35_affine_resident_case( + model_map, model_size, + gate_offset, up_offset, down_offset, + gate_expert_bytes, down_expert_bytes, + 31, false) || + !ds4_gpu_internal_qwen35_affine_resident_case( + model_map, model_size, + gate_offset, up_offset, down_offset, + gate_expert_bytes, down_expert_bytes, + 32, true)) { + goto cleanup; + } + ok = 1; + +cleanup: + if (installed) ds4_gpu_expert_store_v2_clear(); + if (model_map != MAP_FAILED) { + (void)munmap(model_map, (size_t)model_size); + } + if (fd >= 0) close(fd); + if (!ok) { + fprintf(stderr, + "ds4: resident MLX-affine short-prompt regression failed\n"); + } + return ok; +} + typedef struct { uint8_t scales[16]; uint8_t qs[64]; diff --git a/tests/ds4_test.c b/tests/ds4_test.c index 5a3c84dab..b70f73f14 100644 --- a/tests/ds4_test.c +++ b/tests/ds4_test.c @@ -3534,6 +3534,7 @@ static void test_metal_kernel_group(void) { static void test_metal_qwen35_expert_pack(void) { TEST_ASSERT(ds4_gpu_internal_qwen35_expert_pack_test() != 0); + TEST_ASSERT(ds4_gpu_internal_qwen35_affine_resident_short_test() != 0); TEST_ASSERT(ds4_gpu_internal_expert_store_v2_kernel_test() != 0); } diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index b4ced0215..64e9e9c37 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -943,7 +943,7 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 719 + "maximum": 720 }, { "path": "ds4_gpu.h", @@ -1048,14 +1048,14 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 6572 + "maximum": 6634 }, { "path": "ds4_metal.m", "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 1041 + "maximum": 1042 }, { "path": "ds4_profile.c", @@ -2014,7 +2014,7 @@ "location": "content", "token": "ds4", "classification": "migration-pending", - "maximum": 715 + "maximum": 716 }, { "path": "tests/ds4_test.c", From 20a586c0052215316cd17c23f883c91711d42353 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 22:03:47 +0200 Subject: [PATCH 23/41] docs: add Hebrus bridge migration guide --- README.md | 3 + docs/guides/MIGRATING_TO_HEBRUS.md | 224 +++++++++++++++++++++++++++++ tools/brand_boundary.json | 14 ++ 3 files changed, 241 insertions(+) create mode 100644 docs/guides/MIGRATING_TO_HEBRUS.md diff --git a/README.md b/README.md index 5a3622bbb..8382c8b20 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,9 @@ A short-context best is not substituted for the long-context gate. ### Start and operate +- [Migration guide](docs/guides/MIGRATING_TO_HEBRUS.md) — install the bridge, + validate both command identities, move integrations safely, and roll back + without rewriting models or user data. - [Runtime support contract](docs/contracts/RUNTIME_SUPPORT.md) — supported families, hardware floors, backends, and fail-closed boundaries. - [Brand compatibility contract](docs/contracts/BRAND_COMPATIBILITY.md) — diff --git a/docs/guides/MIGRATING_TO_HEBRUS.md b/docs/guides/MIGRATING_TO_HEBRUS.md new file mode 100644 index 000000000..ddb0de638 --- /dev/null +++ b/docs/guides/MIGRATING_TO_HEBRUS.md @@ -0,0 +1,224 @@ +# Migrating From DS4 To Hebrus + +Status: bridge-release guide. The bridge is implemented in source but is not a +published release yet. Use the version, tag, repository URL, and checksums from +the eventual release announcement rather than guessing them from this guide. + +> [!IMPORTANT] +> Hebrus began as a fork of +> [`antirez/ds4`](https://github.com/antirez/ds4) and retains substantial +> implementation, architecture, utilities, and Git history from that project. +> [llama.cpp](https://github.com/ggml-org/llama.cpp), +> [GGML](https://github.com/ggml-org/ggml), and +> [MLX](https://github.com/ml-explore/mlx) are material engineering and +> validation references. The complete provenance boundary is in +> [`ACKNOWLEDGMENTS.md`](../../ACKNOWLEDGMENTS.md) and +> [`THIRD_PARTY_NOTICES.md`](../../THIRD_PARTY_NOTICES.md). + +This migration changes the public engine name without changing model bytes, +cache formats, runtime behavior, or existing automation all at once. The safe +path is to install the bridge first, prove both command identities, and only +then move human-facing scripts to the canonical Hebrus names. + +## What changes in the bridge + +| Surface | Canonical after installing the bridge | Compatibility behavior | +| --- | --- | --- | +| CLI | `hebrus` | `ds4` remains a symlink to the same CLI binary | +| HTTP server | `hebrus-server` | `ds4-server` remains a symlink to the same server binary | +| Agent | `hebrus-agent` | `ds4-agent` remains a symlink to the same agent binary | +| Benchmark | `hebrus-bench` | `ds4-bench` remains a symlink to the same benchmark binary | +| Evaluation | `hebrus-eval` | `ds4-eval` remains a symlink to the same evaluation binary | +| Structured identity | `engine_id: "hebrus"` | Legacy invocation reports `engine_id: "ds4"` | +| Companion app | Hebrus Studio is the selected public name | The bridge preserves its existing bundle ID, data root, and `DSBOX_*` configuration namespace | + +The invoked command name may change help headings, prompts, build labels, and +`engine_id`. Options, defaults, exit codes, protocols, generated tokens, model +admission, and resolved runtime plans must remain equivalent for every command +pair. Compatibility commands remain supported through at least the complete +1.x release line. + +## What does not change + +Do not rename, rewrite, repack, or copy any of the following as part of this +migration: + +- the embedded `ds4.expert_major.v2` tensor or its version, manifest, offsets, + digests, and storage wire values; +- the retired `ds4.expert_major.v1` rejection marker; +- published GGUF filenames, immutable revisions, byte counts, or SHA-256 + values that contain `DS4`; +- disk-KV magic, version, or payload ABI; +- current `DS4_*` engine environment variables; +- current Hebrus Studio `DSBOX_*` variables, `$HOME/.dsbox` data root, or + `com.dsbox.desktop` macOS bundle identifier; +- existing checkouts, model files, caches, conversations, downloads, Git + history, tags, authorship, or historical links. + +ExpertMajor is a disk ABI, not a product label. A display may say “Hebrus +ExpertMajor v2”; serialized identifiers must keep their historical bytes. + +## 1. Record the working installation + +Before changing a checkout or package, stop new inference work and record the +state needed to diagnose or roll back the upgrade: + +```sh +git rev-parse HEAD +git status --short +git remote -v + +ds4 --build-info +ds4 --capabilities=json +``` + +Also record the exact model path, byte count, checksum, and any `DS4_*` +variables used by the service. If Hebrus Studio manages the runtime, quit it +cleanly before replacing an application bundle; do not delete `$HOME/.dsbox`. + +## 2. Install the announced bridge revision + +Use the immutable release tag or commit from the release announcement. Until a +release is announced, the public repository remains +[`andreaborio/ds4`](https://github.com/andreaborio/ds4). + +For a user-local source installation: + +```sh +git fetch --tags origin +git switch --detach + +make -j +make model-free-test +make install PREFIX="$HOME/.local" +export PATH="$HOME/.local/bin:$PATH" +``` + +Replace the angle-bracket placeholder with the exact announced identity. Do +not paste the placeholder literally. Package maintainers should stage the same +layout without writing to the host: + +```sh +make install DESTDIR="$PWD/package-root" PREFIX=/usr/local +``` + +The staged `hebrus*` files are real executables. Each `ds4*` name is a relative +symlink to the corresponding canonical file, so a relocated package does not +capture a build-machine path. + +## 3. Verify both identities before changing automation + +Run the model-free identity checks first: + +```sh +hebrus --build-info +hebrus --capabilities=json +ds4 --build-info +ds4 --capabilities=json +``` + +Both capability documents must use schema version 1, the same non-empty build +SHA, backend, executable role, model-family list, and ExpertMajor contract. +Only `engine_id` changes with invocation. If building from source, the complete +repository checks are: + +```sh +make capabilities-test +make command-alias-test +make install-test +``` + +Stop if the build SHA, backend, role, model list, or ExpertMajor fields +contradict each other. Do not fall back from malformed Hebrus capability JSON +to source-string or binary-string probes. + +## 4. Move command consumers gradually + +New examples, packages, launch agents, and shell scripts should call the +canonical commands. Existing automation can continue using `ds4*` during the +bridge: + +```diff +--- before ++++ after +-exec ds4-server -m "$MODEL" --ctx 8192 ++exec hebrus-server -m "$MODEL" --ctx 8192 +``` + +Do not rename `DS4_*` variables to speculative `HEBRUS_*` variables. Those +aliases are deferred until one central resolver can detect conflicting old and +new values and fail closed. + +Integrations should discover the runtime by executing +`--capabilities=json`. During the bridge they must accept `engine_id` values +`hebrus` and `ds4`, require the expected role and backend, reject unknown schema +versions, and compare the complete ExpertMajor object. Do not infer support +from executable filenames alone. + +## 5. Migrate the companion application without moving its data + +Hebrus Studio is the selected public name for the companion application. Its +engine bridge should: + +1. resolve `hebrus-server` before `ds4-server`; +2. validate schema-1 capability JSON and accept both bridge `engine_id` values; +3. require the server role, expected backend, exact build identity, and exact + ExpertMajor contract; +4. use source/string fallback only for a legacy capability-less `ds4-server`; +5. reject a capability-less `hebrus-server`, malformed JSON, an unknown schema, + a contradictory contract, or the wrong build. + +The visible name does not authorize a storage migration. The bridge release +continues to use `com.dsbox.desktop`, `$HOME/.dsbox`, and the existing `DSBOX_*` +configuration names. Reusing those identities keeps installed models, +configuration, downloads, and local conversations in place. + +## 6. Handle the repository rename only after an announcement + +No repository rename is implied by this guide. Existing clones continue to use +their current remote. After maintainers announce and verify an administrative +rename, inspect the redirect and then update `origin` to the exact URL in that +announcement: + +```sh +git remote get-url origin +git fetch origin +``` + +Do not create a history-less replacement repository, reuse the old repository +name for another project, rewrite historical links, or change remotes to an +unreserved guessed namespace. + +## Rollback + +The compatibility bridge is designed to make rollback mechanical: + +1. stop the running engine or Hebrus Studio process; +2. restore the previously recorded executable package or checkout; +3. continue invoking `ds4*` with the unchanged model and data paths; +4. verify the restored `--build-info` and capability document; +5. preserve logs from the failed upgrade before retrying. + +Do not roll back by rewriting GGUF metadata, moving `$HOME/.dsbox`, or converting a +published model. If the new package installed into a private staging root, use +`make uninstall` with the same `DESTDIR` and `PREFIX`; it removes only the ten +explicit command paths. + +## Completion checklist + +- [ ] The installed build matches the announced tag or commit. +- [ ] `hebrus*` and `ds4*` command pairs resolve to the same executable files. +- [ ] Canonical and compatibility capability documents differ only where the + invoked identity or role requires it. +- [ ] The exact qualified model artifact still passes size and SHA-256 checks. +- [ ] Existing `DS4_*`, `DSBOX_*`, `$HOME/.dsbox`, and serialized identifiers are + unchanged. +- [ ] Hebrus Studio can admit the canonical server and fall back to the legacy + server without weakening validation. +- [ ] Rollback has been tested without copying or deleting user data. +- [ ] Remote URLs were changed only after an authoritative rename announcement. + +The normative compatibility rules are in +[`BRAND_COMPATIBILITY.md`](../contracts/BRAND_COMPATIBILITY.md); the supported +models and hardware boundary are in +[`RUNTIME_SUPPORT.md`](../contracts/RUNTIME_SUPPORT.md). diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index 64e9e9c37..cf9859201 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -672,6 +672,20 @@ "classification": "migration-pending", "maximum": 13 }, + { + "path": "docs/guides/MIGRATING_TO_HEBRUS.md", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 24 + }, + { + "path": "docs/guides/MIGRATING_TO_HEBRUS.md", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 6 + }, { "path": "docs/qwen-expert-major-store.md", "location": "content", From 7e27efd5fb35f3a909a4c1d19cf1ba20d4238d45 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 22:04:02 +0200 Subject: [PATCH 24/41] docs: add Hebrus bridge launch candidate notes --- README.md | 3 + .../hebrus-bridge-launch-candidate.md | 162 ++++++++++++++++++ tools/brand_boundary.json | 14 ++ 3 files changed, 179 insertions(+) create mode 100644 docs/releases/hebrus-bridge-launch-candidate.md diff --git a/README.md b/README.md index 8382c8b20..f26947a77 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,9 @@ A short-context best is not substituted for the long-context gate. ### Project and community +- [Hebrus bridge launch-candidate notes](docs/releases/hebrus-bridge-launch-candidate.md) — + implemented scope, preserved compatibility, release evidence, and external + gates that remain pending. - [Contribution guide](CONTRIBUTING.md) - [Security policy](SECURITY.md) - [Code of conduct](CODE_OF_CONDUCT.md) diff --git a/docs/releases/hebrus-bridge-launch-candidate.md b/docs/releases/hebrus-bridge-launch-candidate.md new file mode 100644 index 000000000..e0abe6fe1 --- /dev/null +++ b/docs/releases/hebrus-bridge-launch-candidate.md @@ -0,0 +1,162 @@ +# Hebrus Bridge Release: Launch Candidate Notes + +- Status: local launch candidate; not published +- Version: to be assigned at release cut +- Release date: to be assigned at release cut +- Candidate commit: to be recorded after all release gates pass + +These notes separate behavior already implemented in source from external +release operations that still require an explicit decision, credentials, or a +published artifact. They are not a release announcement and do not promise a +repository URL, signed package, or availability date. + +> [!IMPORTANT] +> Hebrus began as a fork of +> [`antirez/ds4`](https://github.com/antirez/ds4) and retains substantial core +> implementation, architecture, utilities, and Git history from that project. +> [llama.cpp](https://github.com/ggml-org/llama.cpp), +> [GGML](https://github.com/ggml-org/ggml), and +> [MLX](https://github.com/ml-explore/mlx) remain material implementation and +> validation references. This lineage does not imply endorsement. See +> [`ACKNOWLEDGMENTS.md`](../../ACKNOWLEDGMENTS.md) and +> [`THIRD_PARTY_NOTICES.md`](../../THIRD_PARTY_NOTICES.md). + +## Candidate scope implemented in source + +### Canonical Hebrus command surface + +The bridge adds `hebrus`, `hebrus-server`, `hebrus-agent`, `hebrus-bench`, and +`hebrus-eval` as canonical commands. The corresponding `ds4*` names remain +relative symlinks to the same five binaries and preserve their invocation +identity. Options, protocols, model admission, generated tokens, and runtime +planning are shared; help text, prompts, build labels, and structured +`engine_id` reflect the command actually invoked. + +`make install` supports `PREFIX`, `BINDIR`, and `DESTDIR`. Its uninstall target +names only the ten public command paths and leaves unrelated files intact. + +### Versioned integration contract + +All ten commands expose deterministic `--capabilities=json` output without +loading a model. Schema 1 reports: + +- the invocation-aware `engine_id`; +- exact build Git SHA, backend, and executable role; +- the supported model-family identifiers; +- ExpertMajor version 2, the stable `ds4.expert_major.v2` tensor, and exact + GGML/MLX-affine storage wire values. + +Consumers can therefore validate a server without scanning source filenames, +C symbols, diagnostics, or binary strings. The bridge contract accepts +`engine_id` values `hebrus` and `ds4`; malformed documents, unknown schemas, +wrong roles or backends, and contradictory ExpertMajor fields fail closed. + +### Metal and SSD runtime focus + +The runtime remains deliberately narrow: qualified Apple Metal execution, +mmap-backed GGUF containers, checksummed embedded ExpertMajor v2 routed +weights, hardware-aware AUTO admission, and either resident Metal execution or +bounded SSD expert streaming. It is not a general GGUF runner. + +The current support authority is +[`RUNTIME_SUPPORT.md`](../contracts/RUNTIME_SUPPORT.md). Exact artifact names, +revisions, byte counts, hashes, and measured lanes remain in the model and +benchmark records; these launch-candidate notes do not replace them. + +### Companion bridge identity + +**Hebrus Studio** is the selected public name for the companion application. +The bridge resolves `hebrus-server` before `ds4-server` and validates the +structured capability contract while retaining a narrow legacy fallback for a +capability-less `ds4-server`. + +The visible name is separate from persisted compatibility identity. The bridge +release keeps the existing `com.dsbox.desktop` bundle identifier, `$HOME/.dsbox` +data root, and `DSBOX_*` configuration namespace so installed models, +configuration, downloads, and local conversations are not moved. The desktop +application is delivered separately from the engine and is not embedded in +this repository's command package. + +### Open-source project surface + +The candidate includes contribution, security, governance, conduct, +acknowledgment, third-party notice, changelog, citation, issue-template, and +release-checklist material. Linux and macOS hosted workflows cover the +model-free and compile boundaries appropriate to those runners. Hosted macOS +compilation is not evidence for qualified Metal kernels, model artifacts, +throughput, memory pressure, or SSD behavior. + +## Preserved compatibility + +This bridge intentionally does not rename: + +- `ds4.expert_major.v2`, the retired v1 rejection marker, or any ExpertMajor + on-disk bytes; +- published GGUF filenames, immutable revisions, sizes, or checksums; +- disk-KV magic, version, or payload ABI; +- existing `DS4_*` engine variables; +- Hebrus Studio's `DSBOX_*` variables, data root, or bundle identifier; +- source-level C identifiers, Git history, authorship, tags, or historical + benchmark and release links. + +Compatibility aliases remain supported through at least the complete 1.x +release line. Any future removal requires usage evidence, a new accepted +decision record, release notes, and its own migration tests. + +## Evidence required on the final candidate commit + +Before replacing the placeholders at the top of this document, the release +owner must attach evidence for the exact commit being published: + +- clean context, documentation-link, brand-boundary, model-free, build + isolation, command-alias, staged-install, and download-manifest gates; +- green SHA-pinned Linux and macOS hosted jobs for that commit; +- qualified Apple Silicon model-backed correctness and performance lanes for + every supported artifact, including exact output parity, resolved plan, + memory pressure, swap deltas, and SSD telemetry; +- exact source archive, engine package, model manifest, and companion-package + checksums; +- a clean-install and no-data-loss upgrade/rollback exercise for Hebrus Studio; +- an updated support matrix and benchmark evidence index that cite the same + immutable commit and artifacts. + +Follow [`QA_BEFORE_RELEASES.md`](../../QA_BEFORE_RELEASES.md); a passing narrow +smoke test cannot stand in for this matrix. + +## External and administrative gates still pending + +The following are not implied by source readiness and must remain described as +pending until independently verified: + +- namespace and legal screening, reservation, and acceptance of the naming ADR; +- administrative repository rename and publication of the compatibility app; +- final version/tag selection and remote push; +- GitHub private vulnerability reporting or another verified private intake; +- Apple Developer ID signing, notarization, stapling, and clean-machine + Gatekeeper verification for Hebrus Studio; +- public download URLs, release artifacts, checksums, screenshots, and launch + announcement; +- model-backed release evidence on the exact final commit and qualified + hardware. + +An ad-hoc-signed local application bundle proves package structure and seal +integrity only. It must not be described as Developer ID signed or notarized. + +## Upgrade and rollback + +Users and package maintainers should follow +[`Migrating From DS4 To Hebrus`](../guides/MIGRATING_TO_HEBRUS.md). The guide +keeps existing commands and persisted identities operational, validates both +capability identities before consumers switch, and provides a rollback that +does not rewrite models or user data. + +## Release-owner cut procedure + +1. Choose and record the immutable candidate commit. +2. Run the complete checklist and archive exact raw evidence. +3. Verify the release version appears consistently in packages and notes. +4. Replace only the version, date, and commit placeholders above. +5. Confirm every pending item is either completed with evidence or explicitly + disclosed as unavailable; do not silently omit a gate. +6. Publish only after the engine, Hebrus Studio bridge, migration guide, + checksums, provenance, and rollback instructions agree. diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index cf9859201..a0f1ad4f5 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -700,6 +700,20 @@ "classification": "migration-pending", "maximum": 8 }, + { + "path": "docs/releases/hebrus-bridge-launch-candidate.md", + "location": "content", + "token": "ds4", + "classification": "compatibility", + "maximum": 8 + }, + { + "path": "docs/releases/hebrus-bridge-launch-candidate.md", + "location": "content", + "token": "DS4", + "classification": "compatibility", + "maximum": 2 + }, { "path": "docs/releases/v0.1.0.md", "location": "content", From fd5eace4330cb22742f31e11d7b1b313758c8ee2 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 22:05:57 +0200 Subject: [PATCH 25/41] docs: diagram the Hebrus runtime flow --- README.md | 5 + docs/architecture/CODEMAP.md | 2 + docs/architecture/hebrus-runtime-flow.svg | 121 ++++++++++++++++++++++ tools/brand_boundary.json | 7 ++ 4 files changed, 135 insertions(+) create mode 100644 docs/architecture/hebrus-runtime-flow.svg diff --git a/README.md b/README.md index f26947a77..670985bfd 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,8 @@ variables, and serialized `DS4` identifiers are not renamed by this bridge. ## How the runtime fits together +![Hebrus runtime flow from mmap GGUF through ExpertMajor validation and AUTO to resident Metal or SSD streaming](docs/architecture/hebrus-runtime-flow.svg) + 1. The engine mmaps the GGUF and validates the embedded `ds4.expert_major.v2` manifest, tensor inventory, geometry, ranges, and digest before inference. @@ -219,6 +221,9 @@ A short-context best is not substituted for the long-context gate. ### Architecture and evidence +- [Runtime flow diagram](docs/architecture/hebrus-runtime-flow.svg) — accessible, + versioned view of mmap GGUF, ExpertMajor validation, AUTO admission, and the + resident Metal or SSD-streaming paths. - [Code map](docs/architecture/CODEMAP.md) - [Hebrus naming and compatibility decision](docs/adr/0005-hebrus-naming-and-compatibility-boundary.md) - [Benchmark methodology and records](docs/benchmarks/README.md) diff --git a/docs/architecture/CODEMAP.md b/docs/architecture/CODEMAP.md index 5119f41c1..bd759c768 100644 --- a/docs/architecture/CODEMAP.md +++ b/docs/architecture/CODEMAP.md @@ -13,6 +13,8 @@ RAM/working-set adaptation is isolated in [`ADR 0004`](../adr/0004-qwen-metal-hardware-memory-policy.md). The proposed Hebrus public-name migration and its immutable compatibility boundary are recorded in [`ADR 0005`](../adr/0005-hebrus-naming-and-compatibility-boundary.md). +For the runtime data and admission path at a glance, see the accessible +[`mmap → ExpertMajor → AUTO → Metal/SSD flow`](hebrus-runtime-flow.svg). ## Runtime Entry Points diff --git a/docs/architecture/hebrus-runtime-flow.svg b/docs/architecture/hebrus-runtime-flow.svg new file mode 100644 index 000000000..79ca25d0e --- /dev/null +++ b/docs/architecture/hebrus-runtime-flow.svg @@ -0,0 +1,121 @@ + + + Hebrus mmap, ExpertMajor, AUTO, Metal, and SSD runtime flow + + A mapped GGUF container passes its embedded routed-weight store through + ExpertMajor version 2 validation and indexing. The AUTO planner combines + model geometry, context memory, the Metal working-set recommendation, and + live host pressure. It admits either a fully resident Metal path or a Metal + path with a bounded routed-expert cache filled from SSD. Non-routed tensors + remain mapped in the SSD-streaming path. + + + + + + + + + + + + + + One mapped model, two admitted execution paths + GGUF stays the container; ExpertMajor validates routed weights; AUTO chooses residency from live constraints. + + + + + + + + + 1 + CONTAINER + mmap GGUF v3 + + • map the model once + • keep fixed and non-routed + tensors mapped + • expose one embedded + routed-weight store + + GGUF + mapped ranges + + + + 2 + DISK ABI + ExpertMajor v2 + + • validate manifest and + model geometry + • verify ranges and digest + • index routed-expert + records before inference + + ds4.expert_major.v2 + + + + 3 + ADMISSION + AUTO planner + + • model and context memory + • Metal recommended + working set + • live pressure and + required headroom + + one fail-closed plan + + + + 4A + EXECUTION + Metal resident + + • full admitted working set + • mapped tensors + Metal buffers + • no routed-expert cache fills + + + + + 4B + EXECUTION + Metal + SSD streaming + + • non-routed tensors stay mapped + • bounded routed-expert cache + • SSD fills overlap Metal work + + + + AUTO admits exactly one qualified plan or fails closed. The stable spelling above is a serialized disk identifier, not the product brand. + diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index a0f1ad4f5..7991bfafe 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -420,6 +420,13 @@ "classification": "migration-pending", "maximum": 1 }, + { + "path": "docs/architecture/hebrus-runtime-flow.svg", + "location": "content", + "token": "ds4", + "classification": "serialized/permanent", + "maximum": 1 + }, { "path": "docs/benchmarks/2026-07-14-m5-pro.md", "location": "content", From 7925baecc49ea6f8054361a1a07c1a0fe833cce3 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 22:08:48 +0200 Subject: [PATCH 26/41] docs: define the Hebrus Studio app upgrade --- docs/guides/MIGRATING_TO_HEBRUS.md | 69 +++++++++++++++++-- .../hebrus-bridge-launch-candidate.md | 14 +++- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/docs/guides/MIGRATING_TO_HEBRUS.md b/docs/guides/MIGRATING_TO_HEBRUS.md index ddb0de638..48e1a064d 100644 --- a/docs/guides/MIGRATING_TO_HEBRUS.md +++ b/docs/guides/MIGRATING_TO_HEBRUS.md @@ -50,7 +50,8 @@ migration: values that contain `DS4`; - disk-KV magic, version, or payload ABI; - current `DS4_*` engine environment variables; -- current Hebrus Studio `DSBOX_*` variables, `$HOME/.dsbox` data root, or +- current Hebrus Studio `DSBOX_*` variables, `$HOME/.dsbox` data root, + `$HOME/Library/Application Support/DSBox` Electron `userData` directory, or `com.dsbox.desktop` macOS bundle identifier; - existing checkouts, model files, caches, conversations, downloads, Git history, tags, authorship, or historical links. @@ -173,7 +174,45 @@ continues to use `com.dsbox.desktop`, `$HOME/.dsbox`, and the existing `DSBOX_*` configuration names. Reusing those identities keeps installed models, configuration, downloads, and local conversations in place. -## 6. Handle the repository rename only after an announcement +### Replace `DSBox.app` with `Hebrus Studio.app` + +The macOS upgrade is not an in-place Finder replacement. Because +`DSBox.app` and `Hebrus Studio.app` have different bundle filenames, dragging +the new application into `/Applications` does not replace `DSBox.app`; both +applications remain present until the old bundle is removed. +They nevertheless share the `com.dsbox.desktop` app identifier, legacy state, +and configured local control port (`4242` by default). Never run them at the +same time, including for a side-by-side comparison. + +Use this order after obtaining a verified Hebrus Studio package: + +1. Quit DSBox from its application menu and wait for its managed engine to + stop. Confirm neither DSBox nor its server remains active and that the + configured control port is no longer listening. +2. Confirm the existing state is present at both + `$HOME/Library/Application Support/DSBox` and `$HOME/.dsbox`. Back up any + irreplaceable configuration or conversation data according to the normal + host backup policy; do not rename either directory. +3. Copy `Hebrus Studio.app` into `/Applications`. Finder leaves + `/Applications/DSBox.app` beside it because the names differ; this is + expected and is not permission to launch both. +4. Launch only Hebrus Studio. The bridge must keep Electron `userData` at the + legacy `$HOME/Library/Application Support/DSBox` path and engine/application + state at `$HOME/.dsbox`; it must not create a parallel fresh profile merely + because the visible name changed. +5. Verify that existing settings, installed models, downloads, and local + conversations are visible, then start and stop one model to confirm the + expected control port and engine capability contract. +6. After verification, remove `/Applications/DSBox.app` or retain one old copy + only in an offline rollback archive outside `/Applications`. Do not leave a + routinely launchable old copy alongside Hebrus Studio. + +If either state directory changes, an empty profile appears, or the old process +still owns the control port, quit Hebrus Studio and stop the upgrade. Do not +merge two profile directories or launch DSBox to “repair” state while Hebrus +Studio is running. + +## 7. Handle the repository rename only after an announcement No repository rename is implied by this guide. Existing clones continue to use their current remote. After maintainers announce and verify an administrative @@ -191,9 +230,10 @@ unreserved guessed namespace. ## Rollback -The compatibility bridge is designed to make rollback mechanical: +The engine compatibility bridge is designed to make rollback mechanical: -1. stop the running engine or Hebrus Studio process; +1. stop the running engine or Hebrus Studio process and confirm its port has + been released; 2. restore the previously recorded executable package or checkout; 3. continue invoking `ds4*` with the unchanged model and data paths; 4. verify the restored `--build-info` and capability document; @@ -204,6 +244,22 @@ published model. If the new package installed into a private staging root, use `make uninstall` with the same `DESTDIR` and `PREFIX`; it removes only the ten explicit command paths. +For an application rollback, keep the old app offline until it is needed, then: + +1. quit Hebrus Studio and its managed engine completely; +2. remove or archive `/Applications/Hebrus Studio.app` so only one application + can be launched; +3. restore the archived `DSBox.app` to `/Applications` without moving or + rewriting either legacy state directory; +4. launch only DSBox and verify its settings, model catalog, conversations, + control port, and engine build identity; +5. keep Hebrus Studio offline until the rollback issue is resolved. + +The bridge release does not require a state-schema migration, so both app +identities use the same legacy Electron `userData` and `$HOME/.dsbox` state. +If a later release explicitly introduces a schema migration, its own release +notes and backup procedure take precedence over this bridge rollback. + ## Completion checklist - [ ] The installed build matches the announced tag or commit. @@ -213,6 +269,11 @@ explicit command paths. - [ ] The exact qualified model artifact still passes size and SHA-256 checks. - [ ] Existing `DS4_*`, `DSBOX_*`, `$HOME/.dsbox`, and serialized identifiers are unchanged. +- [ ] Electron `userData` remains at + `$HOME/Library/Application Support/DSBox`; no second empty profile was + created for the visible name. +- [ ] DSBox and Hebrus Studio were never active simultaneously, and only one + application owns the configured control port. - [ ] Hebrus Studio can admit the canonical server and fall back to the legacy server without weakening validation. - [ ] Rollback has been tested without copying or deleting user data. diff --git a/docs/releases/hebrus-bridge-launch-candidate.md b/docs/releases/hebrus-bridge-launch-candidate.md index e0abe6fe1..0aafba58f 100644 --- a/docs/releases/hebrus-bridge-launch-candidate.md +++ b/docs/releases/hebrus-bridge-launch-candidate.md @@ -72,11 +72,18 @@ capability-less `ds4-server`. The visible name is separate from persisted compatibility identity. The bridge release keeps the existing `com.dsbox.desktop` bundle identifier, `$HOME/.dsbox` -data root, and `DSBOX_*` configuration namespace so installed models, +data root, legacy `$HOME/Library/Application Support/DSBox` Electron `userData` +directory, and `DSBOX_*` configuration namespace so installed models, configuration, downloads, and local conversations are not moved. The desktop application is delivered separately from the engine and is not embedded in this repository's command package. +`DSBox.app` and `Hebrus Studio.app` have different Finder names, so installing +the latter does not replace the former. They still share the app +identifier, state, and control port and must never run simultaneously. The +verified upgrade quits DSBox first, installs and validates Hebrus Studio, and +then removes the old bundle or retains it only in an offline rollback archive. + ### Open-source project surface The candidate includes contribution, security, governance, conduct, @@ -116,7 +123,10 @@ owner must attach evidence for the exact commit being published: memory pressure, swap deltas, and SSD telemetry; - exact source archive, engine package, model manifest, and companion-package checksums; -- a clean-install and no-data-loss upgrade/rollback exercise for Hebrus Studio; +- a clean-install and no-data-loss upgrade/rollback exercise for Hebrus Studio + that proves the legacy Electron `userData` and `$HOME/.dsbox` state are + reused, only one app owns the control port, and Finder's two bundle names do + not permit concurrent launch; - an updated support matrix and benchmark evidence index that cite the same immutable commit and artifacts. From 2300e1e01e4fa0c22975e99315e9cf206b2d855e Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 22:10:32 +0200 Subject: [PATCH 27/41] test: enforce the Qwen release contract --- CONTRIBUTING.md | 11 +- Makefile | 20 +- QA_BEFORE_RELEASES.md | 9 +- README.md | 12 +- docs/architecture/CODEMAP.md | 2 + docs/contracts/RUNTIME_SUPPORT.md | 12 +- docs/contracts/qwen-release.json | 22 ++ docs/qwen-expert-major-store.md | 14 +- download_model.sh | 2 + tests/test_download_model.sh | 48 ++- tests/test_qwen_release_contract.py | 206 ++++++++++ tools/brand_boundary.json | 15 +- tools/qwen_release_contract.py | 563 ++++++++++++++++++++++++++++ 13 files changed, 912 insertions(+), 24 deletions(-) create mode 100644 docs/contracts/qwen-release.json create mode 100644 tests/test_qwen_release_contract.py create mode 100644 tools/qwen_release_contract.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5fb0f0877..8a2fc5a50 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -110,7 +110,10 @@ manifest contract, and compatible runtime commit. Until then, contributor QA may use an explicitly labelled local candidate, but release and download checks must report public distribution as unavailable. -The current Qwen release is +The machine-readable +[Qwen release contract](docs/contracts/qwen-release.json) is the canonical +source for every repeated identity below. The current Qwen release is +`published` as `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`, 20,808,566,880 bytes, SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. @@ -119,8 +122,12 @@ It is published at immutable repository revision its manifest requires runtime commit `73a332fef82a0bcdd567d17e0de17aa004cad85d` or a compatible descendant. `download_model.sh qwen-v2` must pin and validate that exact identity. The older -Q4_K_S object is an incompatible negative fixture, not a runnable fallback. See +`Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf` object is `negative-only`, not +a runnable fallback. See [`docs/qwen-expert-major-store.md`](docs/qwen-expert-major-store.md). +Change the JSON contract first, update every intentional human-readable mirror, +then run `make release-contract release-contract-test`; do not update a mirror +as an independent release authority. CUDA and ROCm are frozen and their backend source and build targets are absent from the active tree. Ordinary changes must not restore them accidentally. A diff --git a/Makefile b/Makefile index 9b3b9bf33..9b1097a68 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,7 @@ INSTALL_SOURCE_PROGRAMS = $(addprefix $(INSTALL_SOURCE_BINDIR)/,$(HEBRUS_PROGRAM .PHONY: all help clean test model-free-test premerge context-audit doc-links \ brand-boundary-audit brand-boundary-test \ + release-contract release-contract-test \ imatrix-dataset-check prompt-fixture-check cpu FORCE \ metal build-isolation-test q4k-dot-test qwen-metadata-test \ qwen-reference-test qwen-unicode-test qwen-tokenizer-test \ @@ -47,9 +48,21 @@ INSTALL_SOURCE_PROGRAMS = $(addprefix $(INSTALL_SOURCE_BINDIR)/,$(HEBRUS_PROGRAM install uninstall install-test $(PROGRAMS) \ ds4_test ds4_agent_test -download-model-test: tests/test_download_model.sh download_model.sh +download-model-test: tests/test_download_model.sh download_model.sh \ + docs/contracts/qwen-release.json sh tests/test_download_model.sh +release-contract: tools/qwen_release_contract.py \ + docs/contracts/qwen-release.json README.md CONTRIBUTING.md \ + QA_BEFORE_RELEASES.md docs/contracts/RUNTIME_SUPPORT.md \ + docs/qwen-expert-major-store.md download_model.sh \ + tests/test_download_model.sh + python3 tools/qwen_release_contract.py + +release-contract-test: release-contract tools/qwen_release_contract.py \ + tests/test_qwen_release_contract.py + python3 tests/test_qwen_release_contract.py + brand-boundary-audit: tools/brand_boundary_audit.py tools/brand_boundary.json python3 tools/brand_boundary_audit.py --check @@ -110,6 +123,8 @@ help: @echo " make install-test Verify staged install layout and capabilities" @echo " make brand-boundary-audit" @echo " Reject unclassified or increased legacy brand tokens" + @echo " make release-contract" + @echo " Reject Qwen release identity drift" @echo " make premerge Run context/docs, isolation, and model-free gates" @echo " make clean Remove build outputs and published root binaries" @@ -576,6 +591,7 @@ prompt-fixture-check: # Build isolation removes and rebuilds BUILD_ROOT, so model-free-test must start # only after it completes even when an agent invokes `make -j premerge`. premerge: context-audit doc-links brand-boundary-audit brand-boundary-test \ + release-contract release-contract-test \ imatrix-dataset-check prompt-fixture-check build-isolation-test $(MAKE) model-free-test $(MAKE) install-test @@ -607,6 +623,7 @@ help: @echo " make install-test Verify staged install layout and capabilities" @echo " make brand-boundary-audit" @echo " Reject unclassified or increased legacy brand tokens" + @echo " make release-contract Reject Qwen release identity drift" @echo " make premerge Run repository audits and Linux CPU/model-free gates" @echo " make clean Remove build outputs" @@ -795,6 +812,7 @@ prompt-fixture-check: python3 speed-bench/build_long_context_prompt.py --check premerge: context-audit doc-links brand-boundary-audit brand-boundary-test \ + release-contract release-contract-test \ imatrix-dataset-check prompt-fixture-check model-free-test $(MAKE) install-test git diff --check diff --git a/QA_BEFORE_RELEASES.md b/QA_BEFORE_RELEASES.md index db4f5c05a..b1fdba61e 100644 --- a/QA_BEFORE_RELEASES.md +++ b/QA_BEFORE_RELEASES.md @@ -30,8 +30,13 @@ target, or an artifact whose complete output hash is missing. | `DEEPSEEK_V2` | `DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix-DS4-ExpertMajor-v2.gguf`; 86,720,114,272 bytes; SHA-256 `8378080263eb9224f7228d72e2afa4ac3cf74a116023fdec2c596ff228a33e3f` | | `DEEPSEEK_MIXED_V2` | Non-applicable until a mixed-quant DeepSeek ExpertMajor v2 artifact has a publication record with exact filename, bytes, and complete output SHA-256; do not resolve or use this variable before qualification | | `GLM_V2` | `GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf`; 262,147,193,504 bytes; SHA-256 `7f5017e3076e706c78f2a5322b035a9e2f6519c65ff5b6be8b2d91aeff61505d` | -| `QWEN_V2` | `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`; immutable repository revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`; 20,808,566,880 bytes; SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`; MLX affine4/group-64 routed storage; minimum compatible runtime commit `73a332fef82a0bcdd567d17e0de17aa004cad85d` | -| `QWEN_RETIRED_Q4_NEGATIVE` | Rejection-only input: `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf`; 20,808,566,880 bytes; SHA-256 `d7c43a6388ec20e6fe5530850350f96fdb0ac37c5ce36d3e5f92b172c447f56b`; it must fail before inference | +| `QWEN_V2` | Status `published`; `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`; immutable repository revision `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`; 20,808,566,880 bytes; SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`; MLX affine4/group-64 routed storage; minimum compatible runtime commit `73a332fef82a0bcdd567d17e0de17aa004cad85d` | +| `QWEN_RETIRED_Q4_NEGATIVE` | Status `negative-only`; rejection-only input `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf`; 20,808,566,880 bytes; SHA-256 `d7c43a6388ec20e6fe5530850350f96fdb0ac37c5ce36d3e5f92b172c447f56b`; it must fail before inference | + +The machine-readable +[Qwen release contract](docs/contracts/qwen-release.json) is the canonical +source for both Qwen rows. `make release-contract` rejects drift in this table, +the public documentation, the downloader, and its model-free tests. Record the test machine by hardware model, unified memory, OS build, and power state in the release evidence. Do not encode local hostnames, addresses, or diff --git a/README.md b/README.md index 670985bfd..aebcc3ac9 100644 --- a/README.md +++ b/README.md @@ -65,14 +65,18 @@ ExpertMajor v2. AUTO is the normal startup mode. | GLM 5.2 | 64 GiB | AUTO resolving to SSD streaming only; resident requests are rejected | Published ExpertMajor v2 artifact; `download_model.sh glm-v2` | | Qwen3.6-35B-A3B | 16 GiB | AUTO resolving to resident or SSD; 16 GiB necessarily uses SSD | Published MLX affine4/group-64 artifact; `download_model.sh qwen-v2` | -The Qwen release is exactly +The canonical machine-readable +[Qwen release contract](docs/contracts/qwen-release.json) records the current +artifact as `published`. The release is exactly `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`, 20,808,566,880 bytes, SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. It is pinned to immutable repository revision -`7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`. The older Q4_K_S store is -incompatible with the current runtime and remains only a fail-closed negative -case. +`7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02` and requires runtime commit +`73a332fef82a0bcdd567d17e0de17aa004cad85d` or a compatible descendant. The +older `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf` store is incompatible +with the current runtime and remains `negative-only`, never a download or +inference fallback. DeepSeek V4 PRO has no qualified runtime artifact in the current contract. Canonical model files are converter inputs, not inference fallbacks. CPU is a diff --git a/docs/architecture/CODEMAP.md b/docs/architecture/CODEMAP.md index bd759c768..0a41eb9fa 100644 --- a/docs/architecture/CODEMAP.md +++ b/docs/architecture/CODEMAP.md @@ -114,6 +114,8 @@ not validated. | `tests/test_install.sh` | Temporary-root install/uninstall layout, path portability, capability, and explicit-removal checks | | `tools/brand_boundary.json` + `tools/brand_boundary_audit.py` | Exact canonical, bridged, and permanently preserved identity contract plus explicit per-file legacy `ds4`/`DS4`/`DwarfStar` classification and monotonic count ceilings; `--check` rejects contract drift, new groups, and increases, while `--refresh` requires exact authorizations before widening a ceiling | | `tests/test_brand_boundary_audit.py` | Fail-closed fixtures for new files and tokens, increases, reductions, deterministic refresh, and invalid manifests | +| `docs/contracts/qwen-release.json` + `tools/qwen_release_contract.py` | Canonical published/negative-only Qwen artifact identity and the model-free gate that parses its documentation, downloader, and test surfaces for drift | +| `tests/test_qwen_release_contract.py` | Fail-closed fixtures for prose, table, downloader, schema, status, and negative-only Qwen release-contract drift | | `tests/qwen/` | Qwen fixtures, provenance, reference collectors, and model-specific gates | | `tests/test-vectors/` | Official and local continuation vectors plus provenance | | `gguf-tools/` | Quantization, ExpertMajor conversion, imatrix, and quality-scoring tools | diff --git a/docs/contracts/RUNTIME_SUPPORT.md b/docs/contracts/RUNTIME_SUPPORT.md index 6a1beead2..75e17eb16 100644 --- a/docs/contracts/RUNTIME_SUPPORT.md +++ b/docs/contracts/RUNTIME_SUPPORT.md @@ -38,12 +38,16 @@ the DeepSeek and GLM production contract. Do not infer support for another family from Qwen's successful admission. Runtime support and public artifact distribution are separate gates. The only -runnable Qwen store is the published 20,808,566,880-byte MLX -affine4/group-64 artifact with SHA-256 +runnable Qwen store is the `published` +`Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf`, a +20,808,566,880-byte MLX affine4/group-64 artifact with SHA-256 `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d`. `download_model.sh qwen-v2` pins immutable repository revision -`7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`. The older Q4_K_S store is a -rejection fixture, not a downloadable runtime fallback. +`7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02`; its manifest requires runtime +commit `73a332fef82a0bcdd567d17e0de17aa004cad85d` or a compatible descendant. The +older `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf` store is +`negative-only`, not a downloadable runtime fallback. The machine-readable +[Qwen release contract](qwen-release.json) is the canonical identity record. ## Model Artifact Admission diff --git a/docs/contracts/qwen-release.json b/docs/contracts/qwen-release.json new file mode 100644 index 000000000..cd4ae3ad7 --- /dev/null +++ b/docs/contracts/qwen-release.json @@ -0,0 +1,22 @@ +{ + "schemaVersion": 1, + "modelFamily": "Qwen3.6-35B-A3B", + "downloadTarget": "qwen-v2", + "repository": "andreaborio/Qwen3.6-35B-A3B-DS4-GGUF", + "publishedArtifact": { + "status": "published", + "filename": "Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf", + "revision": "7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02", + "bytes": 20808566880, + "sha256": "dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d", + "runtimeCommit": "73a332fef82a0bcdd567d17e0de17aa004cad85d", + "storage": "mlx-affine4", + "groupSize": 64 + }, + "negativeArtifact": { + "status": "negative-only", + "filename": "Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf", + "bytes": 20808566880, + "sha256": "d7c43a6388ec20e6fe5530850350f96fdb0ac37c5ce36d3e5f92b172c447f56b" + } +} diff --git a/docs/qwen-expert-major-store.md b/docs/qwen-expert-major-store.md index 23e3be711..61a72112f 100644 --- a/docs/qwen-expert-major-store.md +++ b/docs/qwen-expert-major-store.md @@ -86,15 +86,27 @@ used for publication. | Item | Value | |---|---| -| Publication state | Published on Hugging Face; `dsbox.json` selects this exact byte identity | +| Publication state | `published` | +| Repository | `andreaborio/Qwen3.6-35B-A3B-DS4-GGUF` | | Artifact | `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf` | | Artifact bytes | 20,808,566,880 | | Artifact SHA-256 | `dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d` | +| Immutable revision | `7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02` | | Minimum compatible runtime commit | `73a332fef82a0bcdd567d17e0de17aa004cad85d` | +| Storage | `mlx-affine4/group-64` | +| Negative fixture state | `negative-only` | +| Negative fixture | `Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-Q4_K_S.gguf` | +| Negative fixture bytes | 20,808,566,880 | +| Negative fixture SHA-256 | `d7c43a6388ec20e6fe5530850350f96fdb0ac37c5ce36d3e5f92b172c447f56b` | | Canonical source bytes | 20,808,563,424 | | Canonical source SHA-256 | `c33efb67bde86c9ba1f9e79c2dc42627170963bef0e915ab9b91a55cfb6d0fcd` | | Storage marker | `MLX_AFFINE4`, group size 64 | +The machine-readable +[Qwen release contract](contracts/qwen-release.json) is canonical for the +repository, publication states, artifact identities, revision, and runtime +compatibility floor in this table. + The v2 output is 3,456 bytes larger than its canonical converter input. It does not contain a second copy of the routed weights. diff --git a/download_model.sh b/download_model.sh index ce927b490..5e1247eb7 100755 --- a/download_model.sh +++ b/download_model.sh @@ -9,11 +9,13 @@ RUNTIME_GLM_REPO="andreaborio/GLM-5.2-DS4-GGUF" RUNTIME_GLM_FILE="GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf" RUNTIME_GLM_BYTES=262147193504 RUNTIME_GLM_SHA256="7f5017e3076e706c78f2a5322b035a9e2f6519c65ff5b6be8b2d91aeff61505d" +RUNTIME_QWEN_STATUS="published" RUNTIME_QWEN_REPO="andreaborio/Qwen3.6-35B-A3B-DS4-GGUF" RUNTIME_QWEN_FILE="Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf" RUNTIME_QWEN_BYTES=20808566880 RUNTIME_QWEN_SHA256="dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d" RUNTIME_QWEN_REVISION="7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02" +RUNTIME_QWEN_MIN_RUNTIME_COMMIT="73a332fef82a0bcdd567d17e0de17aa004cad85d" RUNTIME_REVISION="ds4-v0.2.0" OFFLINE_DEEPSEEK_REPO="antirez/deepseek-v4-gguf" diff --git a/tests/test_download_model.sh b/tests/test_download_model.sh index 68e504294..fcf2349bb 100755 --- a/tests/test_download_model.sh +++ b/tests/test_download_model.sh @@ -3,6 +3,7 @@ set -eu ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) SCRIPT="$ROOT/download_model.sh" +QWEN_CONTRACT="$ROOT/docs/contracts/qwen-release.json" TMP_ROOT=$(mktemp -d "${TMPDIR:-/tmp}/ds4-download-model.XXXXXX") trap 'rm -rf "$TMP_ROOT"' EXIT HUP INT TERM HOME="$TMP_ROOT/home" @@ -10,6 +11,32 @@ export HOME unset HF_TOKEN mkdir -p "$HOME" +QWEN_EXPECTED="$TMP_ROOT/qwen-release-contract.sh" +python3 - "$QWEN_CONTRACT" >"$QWEN_EXPECTED" <<'PY' +import json +import shlex +import sys + +with open(sys.argv[1], encoding="utf-8") as handle: + contract = json.load(handle) +published = contract["publishedArtifact"] +negative = contract["negativeArtifact"] +values = { + "QWEN_STATUS": published["status"], + "QWEN_REPOSITORY": contract["repository"], + "QWEN_FILE": published["filename"], + "QWEN_BYTES": str(published["bytes"]), + "QWEN_SHA256": published["sha256"], + "QWEN_REVISION": published["revision"], + "QWEN_RUNTIME_COMMIT": published["runtimeCommit"], + "QWEN_NEGATIVE_FILE": negative["filename"], + "QWEN_NEGATIVE_SHA256": negative["sha256"], +} +for name, value in values.items(): + print(f"{name}={shlex.quote(value)}") +PY +. "$QWEN_EXPECTED" + fail() { echo "download-model: FAIL: $*" >&2 exit 1 @@ -62,9 +89,18 @@ assert_exact_line 'RUNTIME_DEEPSEEK_BYTES=86720114272' assert_exact_line 'RUNTIME_DEEPSEEK_SHA256="8378080263eb9224f7228d72e2afa4ac3cf74a116023fdec2c596ff228a33e3f"' assert_exact_line 'RUNTIME_GLM_BYTES=262147193504' assert_exact_line 'RUNTIME_GLM_SHA256="7f5017e3076e706c78f2a5322b035a9e2f6519c65ff5b6be8b2d91aeff61505d"' -assert_exact_line 'RUNTIME_QWEN_BYTES=20808566880' -assert_exact_line 'RUNTIME_QWEN_SHA256="dd17266185833a9f05531ce366fd7284ddca1ed64aa3dcf06e321e8c72c9ea3d"' -assert_exact_line 'RUNTIME_QWEN_REVISION="7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02"' +assert_exact_line "RUNTIME_QWEN_STATUS=\"$QWEN_STATUS\"" +assert_exact_line "RUNTIME_QWEN_REPO=\"$QWEN_REPOSITORY\"" +assert_exact_line "RUNTIME_QWEN_FILE=\"$QWEN_FILE\"" +assert_exact_line "RUNTIME_QWEN_BYTES=$QWEN_BYTES" +assert_exact_line "RUNTIME_QWEN_SHA256=\"$QWEN_SHA256\"" +assert_exact_line "RUNTIME_QWEN_REVISION=\"$QWEN_REVISION\"" +assert_exact_line "RUNTIME_QWEN_MIN_RUNTIME_COMMIT=\"$QWEN_RUNTIME_COMMIT\"" +[ "$QWEN_STATUS" = published ] || fail "canonical Qwen artifact is not published" +if grep -Fq -- "$QWEN_NEGATIVE_FILE" "$SCRIPT" || + grep -Fq -- "$QWEN_NEGATIVE_SHA256" "$SCRIPT"; then + fail "negative-only Qwen artifact is exposed by the downloader" +fi assert_case_assignment deepseek-v2 'MODEL_REPO=$RUNTIME_DEEPSEEK_REPO' assert_case_assignment deepseek-v2 'MODEL_FILE=$RUNTIME_DEEPSEEK_FILE' assert_case_assignment deepseek-v2 'MODEL_BYTES=$RUNTIME_DEEPSEEK_BYTES' @@ -188,9 +224,9 @@ run_runtime_success \ ds4-v0.2.0 run_runtime_success \ qwen-v2 \ - andreaborio/Qwen3.6-35B-A3B-DS4-GGUF \ - Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf \ - 7bf9c3f7f6136aeb2599d75ee61c0cc2f18e2b02 + "$QWEN_REPOSITORY" \ + "$QWEN_FILE" \ + "$QWEN_REVISION" CORRUPT_DIR="$TMP_ROOT/corrupt-existing" CORRUPT_FILE="$CORRUPT_DIR/GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf" diff --git a/tests/test_qwen_release_contract.py b/tests/test_qwen_release_contract.py new file mode 100644 index 000000000..1c158fbb6 --- /dev/null +++ b/tests/test_qwen_release_contract.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +"""Fail-closed fixtures for the machine-readable Qwen release contract.""" + +from __future__ import annotations + +import json +import shutil +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +CHECKER = REPOSITORY_ROOT / "tools" / "qwen_release_contract.py" +MANIFEST = "docs/contracts/qwen-release.json" +SURFACES = ( + MANIFEST, + "README.md", + "CONTRIBUTING.md", + "QA_BEFORE_RELEASES.md", + "docs/contracts/RUNTIME_SUPPORT.md", + "docs/qwen-expert-major-store.md", + "download_model.sh", + "tests/test_download_model.sh", +) + + +class Fixture: + def __init__(self) -> None: + self.temporary = tempfile.TemporaryDirectory(prefix="qwen-release-contract-") + self.root = Path(self.temporary.name) + for relative in SURFACES: + destination = self.root / relative + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(REPOSITORY_ROOT / relative, destination) + + def close(self) -> None: + self.temporary.cleanup() + + def path(self, relative: str) -> Path: + return self.root / relative + + def contract(self) -> dict[str, object]: + return json.loads(self.path(MANIFEST).read_text(encoding="utf-8")) + + def replace_once(self, relative: str, old: str, new: str) -> None: + path = self.path(relative) + text = path.read_text(encoding="utf-8") + if text.count(old) != 1: + raise AssertionError( + f"fixture expected exactly one {old!r} in {relative}, " + f"found {text.count(old)}" + ) + path.write_text(text.replace(old, new, 1), encoding="utf-8") + + def run(self) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [ + sys.executable, + str(CHECKER), + "--root", + str(self.root), + "--manifest", + MANIFEST, + ], + text=True, + capture_output=True, + check=False, + ) + + +class QwenReleaseContractTests(unittest.TestCase): + def setUp(self) -> None: + self.fixture = Fixture() + + def tearDown(self) -> None: + self.fixture.close() + + def assert_fails_with(self, expected: str) -> None: + result = self.fixture.run() + self.assertEqual(result.returncode, 1, result.stdout) + self.assertIn(expected, result.stderr) + + def test_current_contract_and_surfaces_pass(self) -> None: + result = self.fixture.run() + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn("Qwen release contract: PASS", result.stdout) + + def test_every_human_surface_is_bound_to_the_manifest(self) -> None: + contract = self.fixture.contract() + published = contract["publishedArtifact"] + negative = contract["negativeArtifact"] + assert isinstance(published, dict) + assert isinstance(negative, dict) + cases = ( + ("README.md", published["filename"], "Qwen3.6-DRIFT.gguf"), + ("CONTRIBUTING.md", published["revision"], "a" * 40), + ( + "docs/contracts/RUNTIME_SUPPORT.md", + published["runtimeCommit"], + "b" * 40, + ), + ("QA_BEFORE_RELEASES.md", negative["sha256"], "c" * 64), + ( + "docs/qwen-expert-major-store.md", + contract["repository"], + "example.invalid/Qwen-release", + ), + ) + for relative, old, new in cases: + with self.subTest(relative=relative): + fixture = Fixture() + try: + fixture.replace_once(relative, str(old), new) + result = fixture.run() + self.assertEqual(result.returncode, 1, result.stdout) + self.assertIn(relative, result.stderr) + finally: + fixture.close() + + def test_downloader_assignment_drift_fails(self) -> None: + contract = self.fixture.contract() + published = contract["publishedArtifact"] + assert isinstance(published, dict) + old = f"RUNTIME_QWEN_BYTES={published['bytes']}" + self.fixture.replace_once("download_model.sh", old, "RUNTIME_QWEN_BYTES=1") + self.assert_fails_with("download_model.sh: RUNTIME_QWEN_BYTES") + + def test_negative_artifact_cannot_become_downloadable(self) -> None: + contract = self.fixture.contract() + negative = contract["negativeArtifact"] + assert isinstance(negative, dict) + path = self.fixture.path("download_model.sh") + with path.open("a", encoding="utf-8") as handle: + handle.write(f"\n# accidental target: {negative['filename']}\n") + self.assert_fails_with("negative-only Qwen artifact must not be downloadable") + + def test_download_test_must_consume_instead_of_duplicate_contract(self) -> None: + contract = self.fixture.contract() + published = contract["publishedArtifact"] + assert isinstance(published, dict) + path = self.fixture.path("tests/test_download_model.sh") + with path.open("a", encoding="utf-8") as handle: + handle.write(f"\n# stale test fixture {published['sha256']}\n") + self.assert_fails_with("must consume the canonical contract") + + def test_manifest_statuses_fail_closed(self) -> None: + for mutation, expected in ( + (("publishedArtifact", "status", "candidate"), "must be published"), + (("negativeArtifact", "status", "retired"), "must be negative-only"), + ): + with self.subTest(mutation=mutation): + fixture = Fixture() + try: + document = fixture.contract() + group, field, value = mutation + artifact = document[group] + assert isinstance(artifact, dict) + artifact[field] = value + fixture.path(MANIFEST).write_text( + json.dumps(document, indent=2) + "\n", encoding="utf-8" + ) + result = fixture.run() + self.assertEqual(result.returncode, 1, result.stdout) + self.assertIn(expected, result.stderr) + finally: + fixture.close() + + def test_manifest_schema_and_digest_fail_closed(self) -> None: + cases = ( + ("unknown-key", "contract has invalid keys"), + ("invalid-digest", "must be 64 lowercase hexadecimal characters"), + ) + for mutation, expected in cases: + with self.subTest(mutation=mutation): + fixture = Fixture() + try: + document = fixture.contract() + if mutation == "unknown-key": + document["notes"] = "not part of schema 1" + else: + published = document["publishedArtifact"] + assert isinstance(published, dict) + published["sha256"] = "A" * 64 + fixture.path(MANIFEST).write_text( + json.dumps(document, indent=2) + "\n", encoding="utf-8" + ) + result = fixture.run() + self.assertEqual(result.returncode, 1, result.stdout) + self.assertIn(expected, result.stderr) + finally: + fixture.close() + + def test_release_table_is_structural_not_an_unscoped_token_search(self) -> None: + self.fixture.replace_once( + "docs/qwen-expert-major-store.md", + "| Immutable revision |", + "| Unrelated note |", + ) + self.assert_fails_with("'Immutable revision' must be") + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/brand_boundary.json b/tools/brand_boundary.json index 7991bfafe..5d94fe303 100644 --- a/tools/brand_boundary.json +++ b/tools/brand_boundary.json @@ -166,7 +166,7 @@ "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 8 + "maximum": 9 }, { "path": "EXPERT_PRUNE.md", @@ -299,7 +299,7 @@ "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 4 + "maximum": 5 }, { "path": "SECURITY.md", @@ -635,6 +635,13 @@ "location": "content", "token": "DS4", "classification": "compatibility", + "maximum": 5 + }, + { + "path": "docs/contracts/qwen-release.json", + "location": "content", + "token": "DS4", + "classification": "serialized/permanent", "maximum": 3 }, { @@ -705,7 +712,7 @@ "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 8 + "maximum": 10 }, { "path": "docs/releases/hebrus-bridge-launch-candidate.md", @@ -2350,7 +2357,7 @@ "location": "content", "token": "DS4", "classification": "migration-pending", - "maximum": 14 + "maximum": 12 }, { "path": "tests/test_expert_major.py", diff --git a/tools/qwen_release_contract.py b/tools/qwen_release_contract.py new file mode 100644 index 000000000..84bcbbe3d --- /dev/null +++ b/tools/qwen_release_contract.py @@ -0,0 +1,563 @@ +#!/usr/bin/env python3 +"""Validate the Qwen release identity across its authoritative local surfaces.""" + +from __future__ import annotations + +import argparse +import json +import re +import shlex +import sys +from dataclasses import dataclass +from pathlib import Path, PurePosixPath +from typing import Any + + +DEFAULT_ROOT = Path(__file__).resolve().parents[1] +DEFAULT_MANIFEST = "docs/contracts/qwen-release.json" +SCHEMA_VERSION = 1 +HEX40_RE = re.compile(r"^[0-9a-f]{40}$") +HEX64_RE = re.compile(r"^[0-9a-f]{64}$") +HEADING_RE = re.compile(r"^(#{1,6})[ \t]+(.+?)[ \t]*$", re.MULTILINE) +LINK_RE = re.compile(r"(? None: + actual = set(value) + if actual == expected: + return + details: list[str] = [] + missing = sorted(expected - actual) + extra = sorted(actual - expected) + if missing: + details.append("missing " + ", ".join(missing)) + if extra: + details.append("unknown " + ", ".join(extra)) + raise ContractError(f"{label} has invalid keys ({'; '.join(details)})") + + +def require_string(value: Any, label: str) -> str: + if not isinstance(value, str) or not value: + raise ContractError(f"{label} must be a non-empty string") + return value + + +def require_positive_int(value: Any, label: str) -> int: + if isinstance(value, bool) or not isinstance(value, int) or value <= 0: + raise ContractError(f"{label} must be a positive integer") + return value + + +def require_filename(value: Any, label: str) -> str: + filename = require_string(value, label) + pure = PurePosixPath(filename) + if pure.name != filename or filename in {".", ".."}: + raise ContractError(f"{label} must be a bare filename") + return filename + + +def require_hex(value: Any, pattern: re.Pattern[str], label: str) -> str: + digest = require_string(value, label) + if pattern.fullmatch(digest) is None: + width = 40 if pattern is HEX40_RE else 64 + raise ContractError(f"{label} must be {width} lowercase hexadecimal characters") + return digest + + +def load_contract(path: Path) -> Contract: + try: + document = json.loads(path.read_text(encoding="utf-8")) + except FileNotFoundError as exc: + raise ContractError(f"contract does not exist: {path}") from exc + except (OSError, json.JSONDecodeError) as exc: + raise ContractError(f"cannot read contract {path}: {exc}") from exc + if not isinstance(document, dict): + raise ContractError("contract root must be a JSON object") + require_exact_keys( + document, + { + "schemaVersion", + "modelFamily", + "downloadTarget", + "repository", + "publishedArtifact", + "negativeArtifact", + }, + "contract", + ) + if document["schemaVersion"] != SCHEMA_VERSION or isinstance( + document["schemaVersion"], bool + ): + raise ContractError( + f"contract schemaVersion must be {SCHEMA_VERSION}, " + f"got {document['schemaVersion']!r}" + ) + + model_family = require_string(document["modelFamily"], "modelFamily") + if model_family != "Qwen3.6-35B-A3B": + raise ContractError("modelFamily must be Qwen3.6-35B-A3B") + download_target = require_string(document["downloadTarget"], "downloadTarget") + if download_target != "qwen-v2": + raise ContractError("downloadTarget must be qwen-v2") + repository = require_string(document["repository"], "repository") + if re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", repository) is None: + raise ContractError("repository must be an owner/name identifier") + + published_raw = document["publishedArtifact"] + negative_raw = document["negativeArtifact"] + if not isinstance(published_raw, dict) or not isinstance(negative_raw, dict): + raise ContractError("publishedArtifact and negativeArtifact must be objects") + require_exact_keys( + published_raw, + { + "status", + "filename", + "revision", + "bytes", + "sha256", + "runtimeCommit", + "storage", + "groupSize", + }, + "publishedArtifact", + ) + require_exact_keys( + negative_raw, + {"status", "filename", "bytes", "sha256"}, + "negativeArtifact", + ) + if published_raw["status"] != "published": + raise ContractError("publishedArtifact.status must be published") + if negative_raw["status"] != "negative-only": + raise ContractError("negativeArtifact.status must be negative-only") + if published_raw["storage"] != "mlx-affine4": + raise ContractError("publishedArtifact.storage must be mlx-affine4") + if published_raw["groupSize"] != 64 or isinstance( + published_raw["groupSize"], bool + ): + raise ContractError("publishedArtifact.groupSize must be 64") + + published = Artifact( + status="published", + filename=require_filename(published_raw["filename"], "publishedArtifact.filename"), + revision=require_hex( + published_raw["revision"], HEX40_RE, "publishedArtifact.revision" + ), + bytes=require_positive_int(published_raw["bytes"], "publishedArtifact.bytes"), + sha256=require_hex( + published_raw["sha256"], HEX64_RE, "publishedArtifact.sha256" + ), + runtime_commit=require_hex( + published_raw["runtimeCommit"], + HEX40_RE, + "publishedArtifact.runtimeCommit", + ), + storage="mlx-affine4", + group_size=64, + ) + negative = Artifact( + status="negative-only", + filename=require_filename(negative_raw["filename"], "negativeArtifact.filename"), + bytes=require_positive_int(negative_raw["bytes"], "negativeArtifact.bytes"), + sha256=require_hex( + negative_raw["sha256"], HEX64_RE, "negativeArtifact.sha256" + ), + ) + if published.filename == negative.filename: + raise ContractError("published and negative-only filenames must differ") + if published.sha256 == negative.sha256: + raise ContractError("published and negative-only SHA-256 values must differ") + return Contract(model_family, download_target, repository, published, negative) + + +def read_text(root: Path, relative: str) -> str: + path = root / relative + try: + return path.read_text(encoding="utf-8") + except FileNotFoundError as exc: + raise ContractError(f"required release surface does not exist: {relative}") from exc + except OSError as exc: + raise ContractError(f"cannot read release surface {relative}: {exc}") from exc + + +def markdown_section(root: Path, relative: str, heading: str) -> str: + text = read_text(root, relative) + matches = list(HEADING_RE.finditer(text)) + selected = [match for match in matches if match.group(2).strip() == heading] + if len(selected) != 1: + raise ContractError( + f"{relative}: expected one Markdown section {heading!r}, found {len(selected)}" + ) + match = selected[0] + level = len(match.group(1)) + end = len(text) + for candidate in matches: + if candidate.start() > match.start() and len(candidate.group(1)) <= level: + end = candidate.start() + break + return text[match.end() : end] + + +def strip_code(value: str) -> str: + value = value.strip() + if len(value) >= 2 and value.startswith("`") and value.endswith("`"): + return value[1:-1] + return value + + +def parse_table(section: str, relative: str) -> list[dict[str, str]]: + lines = [line.strip() for line in section.splitlines()] + for index in range(len(lines) - 1): + header_line = lines[index] + separator_line = lines[index + 1] + if not header_line.startswith("|") or not separator_line.startswith("|"): + continue + separator = [cell.strip() for cell in separator_line.strip("|").split("|")] + if not separator or not all(re.fullmatch(r":?-{3,}:?", cell) for cell in separator): + continue + headers = [cell.strip() for cell in header_line.strip("|").split("|")] + if len(headers) != len(separator): + raise ContractError(f"{relative}: malformed Markdown table header") + rows: list[dict[str, str]] = [] + for line in lines[index + 2 :]: + if not line.startswith("|"): + break + cells = [cell.strip() for cell in line.strip("|").split("|")] + if len(cells) != len(headers): + raise ContractError(f"{relative}: malformed Markdown table row: {line}") + rows.append(dict(zip(headers, cells, strict=True))) + return rows + raise ContractError(f"{relative}: expected a Markdown table in the release section") + + +def require_tokens(label: str, text: str, tokens: list[str]) -> None: + missing = [token for token in tokens if token not in text] + if missing: + raise ContractError(f"{label}: missing contract value(s): {', '.join(missing)}") + + +def require_contract_link( + root: Path, relative: str, section: str, manifest_path: Path +) -> None: + source_parent = (root / relative).parent + destinations: list[Path] = [] + for match in LINK_RE.finditer(section): + raw = match.group(1).strip().split(maxsplit=1)[0] + if raw.startswith(("#", "http://", "https://", "mailto:")): + continue + destinations.append((source_parent / raw.split("#", 1)[0]).resolve()) + if manifest_path.resolve() not in destinations: + raise ContractError( + f"{relative}: release section must link to the canonical contract" + ) + + +def require_qwen_filenames( + label: str, text: str, expected: set[str] +) -> None: + actual = set(QWEN_FILENAME_RE.findall(text)) + if actual != expected: + raise ContractError( + f"{label}: Qwen artifact identities differ: " + f"expected {sorted(expected)!r}, got {sorted(actual)!r}" + ) + + +def check_prose_surface( + root: Path, + manifest_path: Path, + contract: Contract, + relative: str, + heading: str, +) -> None: + section = markdown_section(root, relative, heading) + published = contract.published + negative = contract.negative + require_contract_link(root, relative, section, manifest_path) + require_tokens( + f"{relative}#{heading}", + section, + [ + contract.model_family, + contract.download_target, + published.status, + published.filename, + f"{published.bytes:,}", + published.sha256, + published.revision or "", + published.runtime_commit or "", + negative.status, + negative.filename, + ], + ) + require_qwen_filenames( + f"{relative}#{heading}", section, {published.filename, negative.filename} + ) + + +def require_table_row( + rows: list[dict[str, str]], key_header: str, key: str, relative: str +) -> dict[str, str]: + matches = [row for row in rows if strip_code(row.get(key_header, "")) == key] + if len(matches) != 1: + raise ContractError(f"{relative}: expected one table row for {key!r}") + return matches[0] + + +def check_qa(root: Path, manifest_path: Path, contract: Contract) -> None: + relative = "QA_BEFORE_RELEASES.md" + section = markdown_section(root, relative, "Release Artifact Identity") + require_contract_link(root, relative, section, manifest_path) + rows = parse_table(section, relative) + published = require_table_row(rows, "Variable", "QWEN_V2", relative) + negative = require_table_row(rows, "Variable", "QWEN_RETIRED_Q4_NEGATIVE", relative) + published_text = published.get("Required identity", "") + negative_text = negative.get("Required identity", "") + p = contract.published + n = contract.negative + require_tokens( + f"{relative}:QWEN_V2", + published_text, + [ + p.status, + p.filename, + p.revision or "", + f"{p.bytes:,}", + p.sha256, + p.runtime_commit or "", + "MLX affine4/group-64", + ], + ) + require_tokens( + f"{relative}:QWEN_RETIRED_Q4_NEGATIVE", + negative_text, + [n.status, n.filename, f"{n.bytes:,}", n.sha256], + ) + + +def rows_by_item(rows: list[dict[str, str]], relative: str) -> dict[str, str]: + result: dict[str, str] = {} + for row in rows: + item = strip_code(row.get("Item", "")) + if not item or item in result: + raise ContractError(f"{relative}: duplicate or empty release identity item") + result[item] = strip_code(row.get("Value", "")) + return result + + +def require_exact_item(items: dict[str, str], key: str, value: str, relative: str) -> None: + actual = items.get(key) + if actual != value: + raise ContractError( + f"{relative}: {key!r} must be {value!r}, got {actual!r}" + ) + + +def check_qwen_store(root: Path, manifest_path: Path, contract: Contract) -> None: + relative = "docs/qwen-expert-major-store.md" + section = markdown_section(root, relative, "Release identity") + require_contract_link(root, relative, section, manifest_path) + items = rows_by_item(parse_table(section, relative), relative) + p = contract.published + n = contract.negative + require_exact_item(items, "Publication state", p.status, relative) + require_exact_item(items, "Repository", contract.repository, relative) + require_exact_item(items, "Artifact", p.filename, relative) + require_exact_item(items, "Artifact bytes", f"{p.bytes:,}", relative) + require_exact_item(items, "Artifact SHA-256", p.sha256, relative) + require_exact_item(items, "Immutable revision", p.revision or "", relative) + require_exact_item( + items, "Minimum compatible runtime commit", p.runtime_commit or "", relative + ) + require_exact_item(items, "Storage", f"{p.storage}/group-{p.group_size}", relative) + require_exact_item(items, "Negative fixture state", n.status, relative) + require_exact_item(items, "Negative fixture", n.filename, relative) + require_exact_item(items, "Negative fixture bytes", f"{n.bytes:,}", relative) + require_exact_item(items, "Negative fixture SHA-256", n.sha256, relative) + + +def parse_shell_assignments(text: str, relative: str) -> dict[str, str]: + assignments: dict[str, str] = {} + for line_number, line in enumerate(text.splitlines(), 1): + match = re.fullmatch(r"([A-Z][A-Z0-9_]*)=(.*)", line) + if match is None: + continue + name, raw_value = match.groups() + try: + values = shlex.split(raw_value, posix=True) + except ValueError as exc: + raise ContractError( + f"{relative}:{line_number}: invalid shell assignment: {exc}" + ) from exc + if len(values) != 1: + continue + if name in assignments: + raise ContractError(f"{relative}: duplicate top-level assignment {name}") + assignments[name] = values[0] + return assignments + + +def case_block(text: str, target: str, relative: str) -> str: + lines = text.splitlines() + starts = [index for index, line in enumerate(lines) if line == f" {target})"] + if len(starts) != 1: + raise ContractError(f"{relative}: expected one case arm for {target}") + start = starts[0] + for index in range(start + 1, len(lines)): + if lines[index] == " ;;": + return "\n".join(lines[start + 1 : index]) + raise ContractError(f"{relative}: unterminated case arm for {target}") + + +def check_downloader(root: Path, contract: Contract) -> None: + relative = "download_model.sh" + text = read_text(root, relative) + assignments = parse_shell_assignments(text, relative) + p = contract.published + expected = { + "RUNTIME_QWEN_STATUS": p.status, + "RUNTIME_QWEN_REPO": contract.repository, + "RUNTIME_QWEN_FILE": p.filename, + "RUNTIME_QWEN_BYTES": str(p.bytes), + "RUNTIME_QWEN_SHA256": p.sha256, + "RUNTIME_QWEN_REVISION": p.revision or "", + "RUNTIME_QWEN_MIN_RUNTIME_COMMIT": p.runtime_commit or "", + } + for name, value in expected.items(): + if assignments.get(name) != value: + raise ContractError( + f"{relative}: {name} must be {value!r}, got {assignments.get(name)!r}" + ) + block = case_block(text, contract.download_target, relative) + required_wiring = { + "MODEL_REPO=$RUNTIME_QWEN_REPO", + "MODEL_FILE=$RUNTIME_QWEN_FILE", + "MODEL_REVISION=$RUNTIME_QWEN_REVISION", + "MODEL_BYTES=$RUNTIME_QWEN_BYTES", + "MODEL_SHA256=$RUNTIME_QWEN_SHA256", + } + wiring = {line.strip() for line in block.splitlines() if line.strip()} + if not required_wiring.issubset(wiring): + missing = sorted(required_wiring - wiring) + raise ContractError( + f"{relative}: {contract.download_target} is missing wiring: {missing!r}" + ) + if contract.negative.filename in text or contract.negative.sha256 in text: + raise ContractError( + f"{relative}: negative-only Qwen artifact must not be downloadable" + ) + + +def check_download_test(root: Path, manifest_path: Path, contract: Contract) -> None: + relative = "tests/test_download_model.sh" + text = read_text(root, relative) + expected_manifest = manifest_path.resolve().relative_to(root.resolve()).as_posix() + require_tokens( + relative, + text, + [ + expected_manifest, + "RUNTIME_QWEN_STATUS", + "RUNTIME_QWEN_REPO", + "RUNTIME_QWEN_FILE", + "RUNTIME_QWEN_BYTES", + "RUNTIME_QWEN_SHA256", + "RUNTIME_QWEN_REVISION", + "RUNTIME_QWEN_MIN_RUNTIME_COMMIT", + "QWEN_NEGATIVE_FILE", + "QWEN_NEGATIVE_SHA256", + ], + ) + duplicated = [ + value + for value in ( + contract.repository, + contract.published.filename, + contract.published.sha256, + contract.published.revision or "", + contract.published.runtime_commit or "", + contract.negative.filename, + contract.negative.sha256, + ) + if value and value in text + ] + if duplicated: + raise ContractError( + f"{relative}: must consume the canonical contract, not duplicate values: " + + ", ".join(duplicated) + ) + + +def validate(root: Path, manifest_path: Path) -> Contract: + try: + manifest_path.resolve().relative_to(root.resolve()) + except ValueError as exc: + raise ContractError("manifest must be inside the repository root") from exc + contract = load_contract(manifest_path) + for relative, heading in ( + ("README.md", "Supported models"), + ("CONTRIBUTING.md", "Artifact publication boundary"), + ("docs/contracts/RUNTIME_SUPPORT.md", "Supported Matrix"), + ): + check_prose_surface(root, manifest_path, contract, relative, heading) + check_qa(root, manifest_path, contract) + check_qwen_store(root, manifest_path, contract) + check_downloader(root, contract) + check_download_test(root, manifest_path, contract) + return contract + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, default=DEFAULT_ROOT) + parser.add_argument("--manifest", default=DEFAULT_MANIFEST) + return parser.parse_args() + + +def main() -> int: + args = parse_args() + root = args.root.resolve() + manifest_path = Path(args.manifest) + if not manifest_path.is_absolute(): + manifest_path = root / manifest_path + try: + contract = validate(root, manifest_path) + except ContractError as exc: + print(f"Qwen release contract: FAIL: {exc}", file=sys.stderr) + return 1 + print( + "Qwen release contract: PASS " + f"({contract.published.status} {contract.published.filename}; " + f"{contract.negative.status} {contract.negative.filename})" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 03830b5b917541809874efdcf80dd73e9ff22af7 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 22:24:15 +0200 Subject: [PATCH 28/41] docs: align launch surfaces with Hebrus Studio --- .github/ISSUE_TEMPLATE/documentation.yml | 26 +++++++++++ .github/ISSUE_TEMPLATE/model-support.yml | 44 +++++++++++++++++++ GOLD_METAL_SSD.md | 15 ++++--- README.md | 14 +++--- ...ebrus-naming-and-compatibility-boundary.md | 9 ++-- docs/contracts/BRAND_COMPATIBILITY.md | 8 ++-- docs/deepseek-expert-major-v2.md | 7 +-- docs/expert-major-v2-roadmap.md | 5 ++- docs/glm52-expert-major-v2.md | 10 ++--- docs/qwen-expert-major-store.md | 7 +-- 10 files changed, 115 insertions(+), 30 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/documentation.yml create mode 100644 .github/ISSUE_TEMPLATE/model-support.yml diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 000000000..9aba42c6a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,26 @@ +name: Documentation +description: Report missing, incorrect, or unclear Hebrus documentation +title: "[Docs]: " +labels: ["documentation"] +body: + - type: input + id: location + attributes: + label: Page or section + validations: + required: true + - type: textarea + id: problem + attributes: + label: What is wrong or unclear? + validations: + required: true + - type: textarea + id: proposed + attributes: + label: Suggested correction + description: Paste-ready wording is welcome, but not required. + - type: input + id: version + attributes: + label: Hebrus version or commit diff --git a/.github/ISSUE_TEMPLATE/model-support.yml b/.github/ISSUE_TEMPLATE/model-support.yml new file mode 100644 index 000000000..1e19382c8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/model-support.yml @@ -0,0 +1,44 @@ +name: Model support +description: Request or report compatibility for a model artifact +title: "[Model]: " +labels: ["model-support"] +body: + - type: markdown + attributes: + value: | + Hebrus supports only explicitly qualified artifacts. Do not attach weights, private prompts, secrets, traces, or KV snapshots. + - type: input + id: artifact + attributes: + label: Artifact identity + description: Repository, immutable revision, exact filename, byte size, and SHA-256. + validations: + required: true + - type: input + id: build + attributes: + label: Hebrus build + description: Commit plus `hebrus --build-info` and `hebrus --capabilities=json`. + validations: + required: true + - type: input + id: hardware + attributes: + label: Hardware and OS + placeholder: Mac model, chip, unified memory, macOS version + validations: + required: true + - type: textarea + id: result + attributes: + label: Observed behavior + description: Include the exact admission or runtime error and the command with personal paths removed. + validations: + required: true + - type: checkboxes + id: hygiene + attributes: + label: Privacy check + options: + - label: I removed prompts, tokens, personal paths, model weights, traces, and cache payloads. + required: true diff --git a/GOLD_METAL_SSD.md b/GOLD_METAL_SSD.md index 7a0115669..ffc289bd1 100644 --- a/GOLD_METAL_SSD.md +++ b/GOLD_METAL_SSD.md @@ -8,14 +8,17 @@ its qualified SSD-streaming path. ## User-facing contract ```sh -make # namespaced Metal build; publishes ./ds4* -./ds4 -m DEEPSEEK-OR-QWEN-DS4-ExpertMajor-v2.gguf # AUTO -./ds4 -m DEEPSEEK-OR-QWEN-DS4-ExpertMajor-v2.gguf --resident -./ds4 -m DEEPSEEK-OR-QWEN-DS4-ExpertMajor-v2.gguf --ssd-streaming -./ds4 -m GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf --ctx 8192 # AUTO -> SSD -./ds4 --build-info +make # publishes ./hebrus* plus legacy ./ds4* aliases +./hebrus -m DEEPSEEK-OR-QWEN-DS4-ExpertMajor-v2.gguf # AUTO +./hebrus -m DEEPSEEK-OR-QWEN-DS4-ExpertMajor-v2.gguf --resident +./hebrus -m DEEPSEEK-OR-QWEN-DS4-ExpertMajor-v2.gguf --ssd-streaming +./hebrus -m GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf --ctx 8192 # AUTO -> SSD +./hebrus --build-info ``` +The `./ds4` and `./ds4-server` names remain byte-identical compatibility +aliases throughout the bridge and 1.x compatibility window. + DeepSeek V4, GLM 5.2, and Qwen3.6 inference accepts only a validated embedded `ds4.expert_major.v2` store on Apple Metal. Canonical GGUFs remain offline converter inputs; ExpertMajor v1, external sidecars, CPU, CUDA, ROCm, and diff --git a/README.md b/README.md index aebcc3ac9..b970fe6a5 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,9 @@ the only capacity boundary. > [`andreaborio/ds4`](https://github.com/andreaborio/ds4), and > [ADR 0005](docs/adr/0005-hebrus-naming-and-compatibility-boundary.md) remains > **Proposed** while namespace and legal screening remain incomplete. The -> companion-application bridge is implemented locally but not yet published, -> and this repository has not been administratively renamed. +> companion application is named **Hebrus Studio** and its bridge release is +> implemented locally but not yet published. This repository has not been +> administratively renamed. > [!IMPORTANT] > Hebrus began as a fork of @@ -255,9 +256,12 @@ can create substantial memory pressure and I/O. Follow AUTO, use only exact qualified artifacts, monitor swap and memory pressure, and do not expose a local server to untrusted networks without an appropriate security boundary. -The separate companion desktop application is currently still -[DSBox](https://github.com/andreaborio/dsbox). Its name, persisted application -identity, and migration are outside this engine bridge. +The separate companion desktop application is **Hebrus Studio**, currently +developed in the existing +[`andreaborio/dsbox`](https://github.com/andreaborio/dsbox) fork until its +administrative rename. Its bridge release preserves the DSBox bundle ID, data +locations, environment namespace, storage keys, and rollback path so the public +rename does not split existing installations. ## Contributing and security diff --git a/docs/adr/0005-hebrus-naming-and-compatibility-boundary.md b/docs/adr/0005-hebrus-naming-and-compatibility-boundary.md index 68d8865c1..d6817b1ab 100644 --- a/docs/adr/0005-hebrus-naming-and-compatibility-boundary.md +++ b/docs/adr/0005-hebrus-naming-and-compatibility-boundary.md @@ -16,7 +16,7 @@ ownership harder to explain. A textual rename is unsafe. Existing installations and automation depend on `ds4` commands, `DS4_*` environment variables, repository URLs, C names, model filenames, and application paths. Published GGUFs and disk-KV files also contain -identifiers whose bytes are part of a durable format. The current DSBox release +identifiers whose bytes are part of a durable format. The pre-rename DSBox release additionally probes exact source paths, C symbols, and diagnostic strings when admitting an ExpertMajor runtime. @@ -61,8 +61,9 @@ both command brands. Model-format lifecycle remains governed by ADR 0003. ## Consequences -- A bridge release must precede internal source renaming and the companion - application's visible rebrand. +- A Hebrus Studio bridge release must precede internal source renaming; its + visible rebrand may ship in that same bridge release once persisted identity + and rollback tests are green. - Every remaining DS4 identifier must be classified as serialized, historical, compatibility-owned, or erroneous; unclassified additions fail a repository check. @@ -73,5 +74,5 @@ both command brands. Model-format lifecycle remains governed by ADR 0003. - The GitHub repository rename happens only after the compatibility application recognizes both old and new remote identities. - This ADR becomes Accepted only after the Hebrus identity is reserved, the - machine-readable capability contract is merged, the DSBox bridge is proven, + machine-readable capability contract is merged, the Hebrus Studio bridge is proven, and the pre-rebrand release baseline is green. diff --git a/docs/contracts/BRAND_COMPATIBILITY.md b/docs/contracts/BRAND_COMPATIBILITY.md index 21362a6c8..7f5dcd30c 100644 --- a/docs/contracts/BRAND_COMPATIBILITY.md +++ b/docs/contracts/BRAND_COMPATIBILITY.md @@ -85,8 +85,10 @@ planned `andreaborio/hebrus` identity over HTTPS or SSH. The existing fork is renamed in place; no history-less replacement is created and the old repository name must not be reused. -DSBox remains the companion application's visible and persisted identity until -its separate name is selected. The bridge application resolves +The companion application's public name is **Hebrus Studio**. Its persisted +DSBox identity remains a compatibility boundary for the bridge release: +`com.dsbox.desktop`, `$HOME/.dsbox`, `DSBOX_*`, browser storage keys, and the +legacy Electron user-data directory do not move. The bridge application resolves `hebrus-server` first, falls back to `ds4-server`, accepts both structured engine identities, and permits source/string fallback only for a legacy capability-less `ds4-server`. A capability-less `hebrus-server`, malformed JSON, @@ -103,7 +105,7 @@ ExpertMajor fields fail closed. 4. Run the complete model-free gates for identity-only work and the canonical model-backed release gates for any change that can affect code generation or inference behavior. -5. Do not begin internal file/symbol migration until the DSBox bridge is +5. Do not begin internal file/symbol migration until the Hebrus Studio bridge is published and the Qwen MLX-affine baseline is qualified. The architectural rationale and the acceptance conditions for the rename are diff --git a/docs/deepseek-expert-major-v2.md b/docs/deepseek-expert-major-v2.md index 3b4c7009f..23bbb8c04 100644 --- a/docs/deepseek-expert-major-v2.md +++ b/docs/deepseek-expert-major-v2.md @@ -1,4 +1,4 @@ -# DeepSeek DS4-native ExpertMajor v2 GGUF +# DeepSeek ExpertMajor v2 GGUF on Hebrus `ds4.expert_major.v2` is the self-describing routed-expert container used by DeepSeek V4, GLM 5.2, and Qwen3.6 in this fork. For DeepSeek it changes storage @@ -73,12 +73,13 @@ Native v2 activates automatically: ```sh make -j8 -./ds4 \ +./hebrus \ -m /absolute/path/to/DEEPSEEK-DS4-EXPERT-MAJOR-V2.gguf \ --ctx 32768 ``` -For the API, replace `./ds4` with `./ds4-server`. AUTO chooses resident mapping +For the API, replace `./hebrus` with `./hebrus-server`. The legacy command +names remain aliases to the same binaries. AUTO chooses resident mapping when the complete model safely fits or SSD streaming otherwise. The release command does not need an ExpertMajor variable, sidecar path, explicit backend, cache budget, preload policy, or residency flag. diff --git a/docs/expert-major-v2-roadmap.md b/docs/expert-major-v2-roadmap.md index 3be9db877..bf33c3cba 100644 --- a/docs/expert-major-v2-roadmap.md +++ b/docs/expert-major-v2-roadmap.md @@ -30,9 +30,12 @@ The release runtime accepts ExpertMajor v2 artifacts only on local Apple Metal. The normal CLI is the same for all three families: ```sh -./ds4 -m /absolute/path/to/MODEL-DS4-ExpertMajor-v2.gguf --ctx 8192 +./hebrus -m /absolute/path/to/MODEL-DS4-ExpertMajor-v2.gguf --ctx 8192 ``` +`./ds4` remains a byte-identical compatibility alias; it is no longer the +primary command in new documentation. + AUTO chooses the family-qualified resident or SSD consumer. No ExpertMajor, sidecar, backend, cache, preload, or power flag is required. Canonical GGUFs remain inputs to `inspect`, `build`, and `verify`, but are not executable by diff --git a/docs/glm52-expert-major-v2.md b/docs/glm52-expert-major-v2.md index 454ff9195..ce9fbaafe 100644 --- a/docs/glm52-expert-major-v2.md +++ b/docs/glm52-expert-major-v2.md @@ -1,6 +1,6 @@ -# GLM 5.2 DS4 ExpertMajor v2 +# GLM 5.2 ExpertMajor v2 on Hebrus -DS4 runs the 244.14 GiB GLM-5.2 Q2_K artifact on Apple Metal while streaming +Hebrus runs the 244.14 GiB GLM-5.2 Q2_K artifact on Apple Metal while streaming the routed experts from local storage. The qualified machine is an M5 Pro with 64 GiB unified memory. No claim is made for Macs below 64 GiB. @@ -17,7 +17,7 @@ revision may require a newly published GGUF. ```sh make -j8 -./ds4 \ +./hebrus \ -m /absolute/path/to/GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf \ --ctx 8192 ``` @@ -25,7 +25,7 @@ make -j8 This starts the interactive CLI. A one-shot request only adds a prompt: ```sh -./ds4 \ +./hebrus \ -m /absolute/path/to/GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf \ --ctx 8192 \ -p "Explain why locality matters in mixture-of-experts inference." @@ -34,7 +34,7 @@ This starts the interactive CLI. A one-shot request only adds a prompt: The local API uses the same contract: ```sh -./ds4-server \ +./hebrus-server \ -m /absolute/path/to/GLM-5.2-DS4-ExpertMajor-v2-Q2_K.gguf \ --ctx 8192 ``` diff --git a/docs/qwen-expert-major-store.md b/docs/qwen-expert-major-store.md index 61a72112f..3c9b161e9 100644 --- a/docs/qwen-expert-major-store.md +++ b/docs/qwen-expert-major-store.md @@ -1,4 +1,4 @@ -# Qwen DS4-native ExpertMajor v2 GGUF +# Qwen ExpertMajor v2 GGUF on Hebrus Qwen3.6-35B-A3B uses the same self-describing `ds4.expert_major.v2` container as DeepSeek and GLM. The optimized Mac artifact stores every routed weight @@ -30,12 +30,13 @@ A valid Qwen v2 file activates automatically: ```sh make -j8 -./ds4 \ +./hebrus \ -m /absolute/path/to/Qwen3.6-35B-A3B-DS4-ExpertMajor-v2-MLX-Affine4-G64.gguf \ --ctx 8192 ``` -Use the same model with `./ds4-server` for the local API. Normal startup does +Use the same model with `./hebrus-server` for the local API. The legacy +`./ds4` and `./ds4-server` names remain aliases to the same build. Normal startup does not need an experimental guard, explicit Metal selection, a sidecar path, payload hashes, cache geometry, `--resident`, or `--ssd-streaming`. AUTO chooses resident mapping when the full working set fits its Metal and host-memory From fb87aec666164a5f24a88d549bf349529a55a527 Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 22:31:01 +0200 Subject: [PATCH 29/41] test: prove full help parity for command aliases --- tests/test_command_aliases.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_command_aliases.py b/tests/test_command_aliases.py index beadd16f2..41ea61b79 100644 --- a/tests/test_command_aliases.py +++ b/tests/test_command_aliases.py @@ -24,6 +24,7 @@ ("--capabilities=json",), ("--build-info",), ("--help",), + ("--help", "all"), ("--capabilities",), ("--role",), ("--command-alias-invalid-test",), @@ -88,6 +89,8 @@ def validate_pair( if document.get("executable_role") != role: fail(f"{canonical}: expected executable role {role}") + canonical_help_size = 0 + legacy_help_size = 0 for args in PARITY_CASES: canonical_result = run(canonical, args) legacy_result = run(legacy, args) @@ -114,11 +117,23 @@ def normalize(data: bytes) -> bytes: if canonical_record != legacy_record: fail(f"{canonical.name}/{legacy.name} differ for {' '.join(args)}") - if args == ("--help",): + if args[0] == "--help": + if canonical_result.returncode != 0 or canonical_result.stderr: + fail(f"{canonical.name}: help invocation failed") + if legacy_result.returncode != 0 or legacy_result.stderr: + fail(f"{legacy.name}: help invocation failed") if not canonical_result.stdout.startswith(canonical_name + b"\n"): fail(f"{canonical.name}: help does not use canonical invocation name") if not legacy_result.stdout.startswith(legacy_name + b"\n"): fail(f"{legacy.name}: help does not preserve legacy invocation name") + if args == ("--help",): + canonical_help_size = len(canonical_result.stdout) + legacy_help_size = len(legacy_result.stdout) + elif ( + len(canonical_result.stdout) <= canonical_help_size + or len(legacy_result.stdout) <= legacy_help_size + ): + fail(f"{canonical.name}/{legacy.name}: --help all is not full help") def main() -> int: From b009dcc8ed94a478e7c1e3fbc0298be0e28efb4d Mon Sep 17 00:00:00 2001 From: Andrea Borio Date: Tue, 21 Jul 2026 22:35:48 +0200 Subject: [PATCH 30/41] brand: publish the Hebrus logo master --- Makefile | 9 ++++-- README.md | 4 +++ docs/contracts/BRAND_COMPATIBILITY.md | 9 ++++++ docs/media/hebrus-logo.png | Bin 0 -> 1040392 bytes tests/test_brand_asset.py | 41 ++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 docs/media/hebrus-logo.png create mode 100644 tests/test_brand_asset.py diff --git a/Makefile b/Makefile index 9b1097a68..c6bec808d 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ INSTALL_DEST_BINDIR = $(DESTDIR)$(BINDIR) INSTALL_SOURCE_PROGRAMS = $(addprefix $(INSTALL_SOURCE_BINDIR)/,$(HEBRUS_PROGRAMS)) .PHONY: all help clean test model-free-test premerge context-audit doc-links \ - brand-boundary-audit brand-boundary-test \ + brand-boundary-audit brand-boundary-test brand-asset-test \ release-contract release-contract-test \ imatrix-dataset-check prompt-fixture-check cpu FORCE \ metal build-isolation-test q4k-dot-test qwen-metadata-test \ @@ -69,6 +69,9 @@ brand-boundary-audit: tools/brand_boundary_audit.py tools/brand_boundary.json brand-boundary-test: tools/brand_boundary_audit.py tests/test_brand_boundary_audit.py python3 tests/test_brand_boundary_audit.py +brand-asset-test: tests/test_brand_asset.py docs/media/hebrus-logo.png README.md + python3 tests/test_brand_asset.py + ifeq ($(UNAME_S),Darwin) # A build profile owns every object and binary it produces. In particular, a @@ -590,7 +593,7 @@ prompt-fixture-check: # Build isolation removes and rebuilds BUILD_ROOT, so model-free-test must start # only after it completes even when an agent invokes `make -j premerge`. -premerge: context-audit doc-links brand-boundary-audit brand-boundary-test \ +premerge: context-audit doc-links brand-boundary-audit brand-boundary-test brand-asset-test \ release-contract release-contract-test \ imatrix-dataset-check prompt-fixture-check build-isolation-test $(MAKE) model-free-test @@ -811,7 +814,7 @@ imatrix-dataset-check: prompt-fixture-check: python3 speed-bench/build_long_context_prompt.py --check -premerge: context-audit doc-links brand-boundary-audit brand-boundary-test \ +premerge: context-audit doc-links brand-boundary-audit brand-boundary-test brand-asset-test \ release-contract release-contract-test \ imatrix-dataset-check prompt-fixture-check model-free-test $(MAKE) install-test diff --git a/README.md b/README.md index b970fe6a5..6bc9588e3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +

+ Hebrus logo +

+ # Hebrus **Local inference built first for Apple Metal and SSD streaming.** diff --git a/docs/contracts/BRAND_COMPATIBILITY.md b/docs/contracts/BRAND_COMPATIBILITY.md index 7f5dcd30c..a5f449e3c 100644 --- a/docs/contracts/BRAND_COMPATIBILITY.md +++ b/docs/contracts/BRAND_COMPATIBILITY.md @@ -7,6 +7,15 @@ remain accepted, and which identifiers must never be rewritten merely for branding. Runtime and model support remain governed by [`RUNTIME_SUPPORT.md`](RUNTIME_SUPPORT.md). +## Canonical logo asset + +The public Hebrus mark is the maintainer-supplied +[`docs/media/hebrus-logo.png`](../media/hebrus-logo.png). Its source pixels stay +unchanged: the repository freezes the 1254 x 1254 RGBA PNG at SHA-256 +`4be8949c73bd52e7abef58396dcd57f636165a8bb6cd6d536a600bcbf880594c`. +Presentation surfaces may add a CSS `drop-shadow()` at render time; they must +not bake shadows, text, color changes, crops, or other edits into this master. + ## Command names Every command pair resolves to one real executable and one object graph. The diff --git a/docs/media/hebrus-logo.png b/docs/media/hebrus-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9d889e6b37cdfa288031dbe3e7be32df6af56bdc GIT binary patch literal 1040392 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�7H05a8!wGiXWV5!y+R&hHh%G0e?bkWVg9ypVO#w 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 Eukw61q!A9#rcH* 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

V4sTko6SQM+B{l1ag;PK4H7faWcP_8_=r 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

!B0ar5YsiUCxhJy2XIbT^{&SI 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