feat: Jetson AGX Orin becomes a first-class card — senses on unsloth/gemma-4-12B-it-qat-w4a16, VALIDATED live (0.55.0) - #176
Conversation
Scope-explored (13 surfaces), converged, and exported. User decisions: orin-only pin, new orin-lobe shape, drop MTP if unsupported, model card guides context (re-measured live). Capability goals image/video/audio/ reasoning are probe-gated; audio rides on vLLM #101, video unknown. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
Rigorous pass (hardware + hard-to-reverse + distributed-state signals). Findings: config-verified 262144 ctx + declared video/audio + int4 quant (c3 precedent narrowed); incumbent-first benchmark ordering; Tegra iowait threshold persistence; snapshot rollback; evidence-key redaction; the reasoning-field probe trap (h11 rejected, h20 replaces). q3 resolved: accept the served-id break + role-name migration follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
…e /spec-to-plan) Frame extended mid-plan with c29/h26 (worker->Thor proxy new, cortex->Spark preserved) and re-exported. Plan covers all 48 targets; risks: KV-at-256K, mesh-visible downtime, video/audio verdicts, operator-provided Thor key. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
t4: ~/.lobes snapshotted and verified byte-identical (rollback point). t3: incumbent coolthor baseline on the current engine — decode 9.99 tok/s (medium) / 6.39 (long) from usage.completion_tokens, MTP acceptance 69-83%, KV 11.29 GiB / 480,431 tok / 3.67x. d1 (approved): docs/orin-profiles.md overstates that KV pool 1.67x — boot ORDER, not knobs, sets it on unified memory. Filed as #171; frame c20 amended to measured reality per h16. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
…a16 (t1) Adds a candidate SupportedModel entry for the Gemma 4 12B QAT int4 W4A16 checkpoint (unsloth-qat-senses-first-class-orin-variation plan, t1), mirroring the coolthor gemma-4-12B-it-NVFP4A16 gear in architecture (Gemma4UnifiedForConditionalGeneration, gemma4 tool parser, compressed-tensors) but citing its own config.json for the knobs that differ: 262144 native context (double the incumbent 131072), INT4 pack-quantized weight-only quantization (not FP4), and a natively declared video_token_id the repo's prior "text+image+audio" Gemma capability line predates. role_hint is deliberately "candidate", not the literal "multimodal" the covering plan names, to preserve test_exactly_one_gemma_multimodal_gear's singular-tier-owner invariant -- the same reason the sakamakismile coder entry is "candidate" rather than "multimodal". This keeps the fleet-wide multimodal/senses/normal tier default, and every thor/spark profile that pins the raw coolthor id, untouched. docs/gemma-4-12b-qat-w4a16.md documents the checkpoint with a capability table whose rows are all marked pending-live-probe -- nothing is claimed validated, and issue #101's audio-drop gap is flagged as an expected risk carried over from the vLLM path, not resolved by this checkpoint's own audio_config declaration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add lobes/machines/orin.py — the Jetson AGX Orin 64GB Developer Kit (Ampere sm_87), following the thor.py CardStrategy convention exactly. One new module + one register() import line in machines/__init__.py (slotted after thor; its sole "orin" marker never overlaps spark/thor/blackwell's markers, verified by test). Ground truth (docs/orin-profiles.md, live 2026-07-16/17): compute capability 8.7 (sm_87), hostname "orin", device-tree model "NVIDIA Jetson AGX Orin Developer Kit". Legacy single-model defaults use the measured 0.45 gpu_mem_util and TRITON_ATTN. The embedder/reranker pooling divergences (TRITON_ATTN, reranker enforce_eager) are declared as orin's OWN role_overrides rather than composing the shared SM_110 trait — the doc is explicit these are a conservative carry-over from Thor's sm_110 finding, not independently proven on sm_87, so reusing a trait named "sm_110" would misattribute the cause. Detection-only scope: no lobes/profiles/builtin/orin.toml (that's a later task) and no shapes/goldens/catalog/compose changes. TDD: failing tests added first to test_detect.py (fact-set resolution, device-tree fallback, hostname-alone resolution, still-UNKNOWN for unrelated hardware) and test_machines.py (signature, non-shadowing precedence, role_overrides provenance, no SM_110 composition), then the implementation made them pass. Verified on the physical Orin dev box: `pytest tests/ -k "detect or machine or profile" -n auto` is green (268 passed). The unfiltered full suite has 20 pre-existing failures, all in test_init.py/ test_cli_logs.py's `main(["init", ...])` helpers that don't mock hardware detection (unlike the neighboring _inject_spark_detection fixture) — now that this literal box resolves to the real "orin" card instead of UNKNOWN, `lobes init`'s fleet path 404s on the not-yet-created orin.toml profile. Confirmed via git-stash that these same tests pass pre-orin on this exact box; they exercise the `init`/ `logs` verbs, not detection, and creating orin.toml is out of this task's scope (it would break the builtin_names() goldens this task is committed to leaving alone). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gpu_mem_util is a fraction of the whole device, so co-resident engines are deducted one-for-one from KV — the only elastic term. Arithmetic reconciles to within ~0.5 GiB. Also records the 31% CUDA-graph under-estimate (~0.37 GiB of unused headroom) vLLM's own log flags. Detail added to #171. Records d2 (t1 role_hint precedent) and d3 (host-dependent tests, #172). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
…tion The 20 failures the coordinator reproduced were a pre-existing test-isolation gap: main(["init", ...]) helpers in both files called real lobes.runtime._detect.detect_card() with no injection, unlike the neighboring _fake_card/_patch_detect pattern already established in test_init_profile.py, test_init_shape.py, test_cli_fleet.py and test_upgrade_compat.py. That worked by luck as long as the box running pytest resolved UNKNOWN -> the conservative 'base' profile; landing the new orin CardStrategy exposed it, because this dev box IS a real Jetson AGX Orin (hostname "orin") that now correctly resolves an "orin" card with no built-in profile yet (still t6's job). Fix: a file-local autouse pytest fixture (_pin_spark_detection) in each file, monkeypatching _detect.detect_card to a fixed "spark" card for every test. Chose autouse over the repo's more common per-test _patch_detect() call because every test in these two files wants the exact same neutral, resolvable card — the card's identity is incidental to what's being asserted (scaffold mechanics / lobes logs plumbing), unlike test_init_profile.py/test_init_shape.py where the injected card IS the thing under test. Mirrors tests/conftest.py's existing offline_runtime autouse fixture, which neutralises docker/ health/capabilities probes for the identical reason. No assertions weakened or tests skipped — same expectations, now driven by an injected fact instead of whatever hardware happens to run pytest. Removed the now-fully-redundant explicit _inject_spark_detection(monkeypatch) call sites in test_init.py (5) now that the autouse fixture covers them. Host-independence is proven, not just asserted: a new parametrized test_detection_pin_drives_the_resolved_profile_not_the_host overrides the fixture per-test with two different injected cards (spark, thor) and checks the dry-run output's "Profile: <name> (...)" line tracks the injected card — direct evidence the outcome is driven by the fixture, never by socket.gethostname()/nvidia-smi/device-tree on the box actually running pytest. Does not create lobes/profiles/builtin/orin.toml (still out of scope for t2/this fix) — the fix works by never letting detection resolve a real card at all in these two files. Verified on the physical Orin dev box: `uv run pytest -n auto` is now fully green — 2730 passed, 15 skipped, 0 failed (up from 20 failed pre-fix; +22 = the 20 previously-failing tests plus 2 new parametrize cases). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds lobes/machines/orin.py (Ampere sm_87, 64GB) with orin's own role_overrides (TRITON_ATTN pooling, reranker enforce_eager) carrying provenance that flags them as unproven-on-sm_87 carry-overs rather than composing the sm_110 trait. Also makes test_init.py / test_cli_logs.py hermetic (autouse detection pin) — they previously inherited the runner's real GPU card and passed only because CI is not an Orin. See #172.
…off-switch) The senses swap targets unsloth/gemma-4-12B-it-qat-w4a16, which ships no MTP draft head. The recorded operator decision is to attempt the incumbent google/gemma-4-12B-it-assistant draft at boot and, if vLLM refuses it, drop speculative decoding rather than block the swap. That was impossible: the flag was a hardcoded literal in the vllm-multimodal command. MECHANISM — a shell-lexed STRING command:, scoped to this lane only. `docker compose` cannot conditionally omit a `command:` LIST item. An item that substitutes to "" renders as an empty argv element, and `vllm serve` reads that as a second positional and exits 2 with `error: unrecognized arguments:` — VERIFIED by parsing that exact argv through the served build's own serve parser (vLLM 0.23.1rc1.dev672+g93d8f834d, image lobes/vllm-gemma4:local). So folding the flag into one list item and blanking it is dead, as suspected. A string `command:` is shell-lexed AFTER substitution, so a variable expanding to nothing leaves no token at all — the flag is omitted ENTIRELY, the only "off" rendering vLLM accepts. Verified end to end with real `docker compose config` (Compose v5.3.1): unset renders argv byte-identical to before; MULTIMODAL_ SPECULATIVE_CONFIG= renders 13 tokens with no --speculative-config and no empty element. The dash-only default operator is deliberate — the colon-dash form every other knob uses treats an empty value as unset and would put the draft straight back. Every other lane keeps its list; the two other Gemma lanes are byte-identical. - ZERO golden changes (template-defaults.env and friends untouched): the knob uses the dash-only operator, which regen's extractor documents as outside the tracked defaults surface. - test_catalog.py's byte-guard is STRENGTHENED, not deleted: it now pins the catalog item as the knob's full substitution rather than as a bare substring — the item string also appears in the lane's new prose comments, which a substring check would match after someone deleted the actual flag. The same hole is closed in test_cli_fleet.py. - test_tool_parser_plugin.py's vllm-multimodal service hash recomputed (the rendered argv is unchanged; only the template's representation of it moved). - env.example documents unset / empty / retarget, including the two-layer quoting a custom value needs (bare single quotes silently mangle the JSON). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
…-switch)
String command: form so an empty MULTIMODAL_SPECULATIVE_CONFIG omits the
flag entirely; dash-only default operator (${VAR-default}) is load-bearing
so the off-switch stays reachable. Zero golden changes. Byte-guards
strengthened rather than deleted.
Checkpoint on disk (9.56 GiB, +1.91 vs incumbent), config.json read locally: int4 W4A16, 262144 native, video_token_id declared. Projection from measured incumbent facts: 262144 reachable at util 0.45 either way, but senses-first gives 2.67x vs gears-first 1.52x. Also records Thor peer state (keyless, worker live) and this box's 0.45.0 gateway gap (no worker role) — risk r5 / deviation d5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
… lobes vetoed Adds `lobes/profiles/builtin/orin.toml`, the fleet-shaped half of the Jetson AGX Orin 64GB card (Ampere sm_87) whose detection landed in lobes/machines/orin.py (t2). It supersedes the hand-authored operator profile docs/orin-profiles.md records. - `senses` = unsloth/gemma-4-12B-it-qat-w4a16 (compressed-tensors, TRITON_ATTN). Every checkpoint fact in the comment block is read off the checkpoint's own config.json, not the model card: Gemma4UnifiedForConditionalGeneration, 262144 native, num_bits=4/type=int/strategy=group (INT4 weight-only, NOT FP4), vision_config + audio_config both present. - `gpu_mem_util = 0.45` / `max_model_len = 262144` are marked MEASURED-PENDING in a prominent block, with the reason they cannot simply be inherited: the 2026-08-04 live re-measurement on this box (approved deviation d1, issue #171) contradicts docs/orin-profiles.md — the incumbent at 0.45/131072 holds 11.29 GiB KV = 480,431 tokens = 3.67x, not 18.86 GiB / 802,644 / 6.12x, because gpu_memory_utilization is a fraction of the WHOLE device and the doc's figure came from a senses-FIRST boot into an empty one. At 480,431 tokens the 256K window is ~1.83x and may be refused outright. The live boot (t9) backfills both; a refusal is the more useful datum (thor-muse's 0.40 was refused, thor-worker's 0.45 booted first try). - `cortex` / `muse` / `worker` feasible=false: all three are NVFP4 exports that quantize ACTIVATIONS to FP4, which needs Blackwell tensor cores. A hard architecture line, not a memory tradeoff — contrast senses's W4A16, whose weights dequantize through Marlin (cc >= 8.0) with 16-bit activations. - The pooling gears' carried-over Jetson divergences (embedder/reranker TRITON_ATTN, reranker enforce_eager) are deliberately ABSENT from the TOML: `orin` joins `thor` in `_MACHINE_DERIVED_BUILTINS`, so the loader overlays them from lobes/machines/orin.py's role_overrides and the literals are never re-typed. A test pins both halves (values match the registry; the knob names do not appear in the file). Goldens regenerated with tests/goldens/regen.py: six NEW files only (tests/goldens/orin.env + five shapes/*__orin.env). Zero byte changes to the existing base/spark/thor goldens or to spark.toml/thor.toml, and the spark/thor asserts in tests/test_profile_schema.py are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
senses = unsloth/gemma-4-12B-it-qat-w4a16 at util 0.45 / 262144 marked MEASURED-PENDING (d1/#171 cited). cortex/muse/worker vetoed on the FP4-activations-need-Blackwell line. loader now derives orin's role overrides from lobes/machines/orin.py rather than re-typing them, exactly as thor does.
image PASS (control held), video INGESTED but no directional discrimination, audio encoded yet unusable, reasoning opt-in via enable_thinking, tools PASS. Refines #101: audio tokens DO scale with duration (~25 tok/s), so the documented '~19 placeholder tokens' signature is wrong on this build — the honest test is discrimination, which the model fails (it reports hearing nothing while carrying 112 audio tokens). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
All four stream through the gateway with usage in the final chunk. Records the consumer trap: with thinking on, the trace streams on delta.reasoning while delta.content stays empty for the whole trace. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
…n card Two live-found regressions on the Jetson AGX Orin 64GB, both previously survivable only by hand-patching the deployed box. 1. The budget clobber. The box ran `--profile orin --shape thor-lobe`, and a shape's [overrides.senses] WINS over the card profile at render time, so Thor's measured sm_110 values (0.30 / 131072) replaced the Orin card's own (0.45 / 262144) — visible in the wave-1 goldens: thor-lobe__orin.env vs orin.env. The new builtin `orin-lobe` shape hosts senses + embedder + reranker, drops cortex, and carries the Orin budget as its own declaration: orin-lobe__orin.env renders 0.45 / 262144. Unlike every other builtin shape it hosts NO stt/tts — the Parakeet base image (scitrera/dgx-spark-vllm) ships a torch with no sm_87 kernels (measured live: 8 restarts, "no kernel image is available"), so audio forwards to a peer via AUDIO_URL. DECLARED/UNVALIDATED per #108: no box has booted it, and its senses values are the card profile's MEASURED-PENDING hypothesis, pending the t9 boot. A lockstep test fails CI if the card is backfilled and the shape is not. 2. The Tegra iowait quirk, now persistent. /proc/stat here reports ~59% iowait with zero disk I/O (the sugov:* cpufreq-governor kthreads flicker in D state and inflate nr_iowait), so at the shipped LOBES_IOWAIT_DEGRADED_THRESHOLD=50 the gateway 429-sheds every full-tier request indefinitely — on this card, all of senses. The live box survived on an ephemeral shell-env override any `docker compose up` reverts. Mechanism: neither render.py nor shape_render.py could emit a non-role env key (only shape_render's hardcoded opt-in activation tables), so Profile grows one optional top-level `[host_env]` table — string-valued, validated, rendered BEFORE the role keys so a role knob always wins a collision — and builtin/orin.toml declares LOBES_IOWAIT_DEGRADED_THRESHOLD = "100" with the accounting quirk written out so nobody "fixes" it back to 50. It sits on the CARD, not the shape: it is a fact about the board, so every shape over `orin` inherits it, machine-as-brain (what a bare `lobes init` renders) included — a shape-scoped fix would leave the default path shedding. No other card declares a host_env, so base/spark/thor renderings are byte-unchanged; the six orin goldens each gain exactly one line. `lobes doctor` now names the key on a deployment still carrying 50. Also: --shape metavar derived from builtin_shape_names() instead of a hand-listed string that had already gone stale (thor-worker was missing), and orin-lobe added to scripts/accept-shape.sh so t9 can produce its transcript. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t_env orin-lobe hosts senses+embedder+reranker (no local stt/tts on sm_87) and carries the orin senses budget so thor-lobe's Thor-measured 0.30/131072 no longer clobbers it — pinned by a lockstep test against builtin/orin.toml. New optional [host_env] table on Profile renders the Tegra iowait threshold (100) from the CARD, so a bare machine-as-brain init is fixed too, not just this shape. DECLARED/UNVALIDATED per #108.
…pu_access` (0.55.0)
This Jetson AGX Orin's NVIDIA container toolkit (1.19.1, `mode = "auto"`)
resolves to legacy **csv** mode, where the compose templates'
`deploy.resources.reservations.devices` GPU request fails at container CREATE:
"invoking the NVIDIA Container Runtime Hook directly … is not supported."
The live fix was a HAND EDIT of every GPU service's `deploy:` stanza into
`runtime: nvidia` — and a re-init reverts it (docs/orin-profiles.md
divergence 1), which is precisely what the next live boot is about to do.
Mechanism: a card profile declares `gpu_access = "devices"` (the default,
today's behaviour) or `"runtime"`, and `lobes init --apply` GENERATES two
compose overrides that `!reset` each GPU service's `deploy:` stanza and set
`runtime: nvidia` — on EVERY render, and removed when the resolved card no
longer declares it.
Why a generated override rather than a template `${VAR}`: docker-compose has no
conditional-block syntax, so a service needs one form or the other and no
substitution can pick between them — only a second compose file can. That is
the mechanism `docker-compose.shape.yml` already proved.
Why TWO files: verified with real `docker compose config` on this box, an
override naming a service no file in the same `-f` chain declares is a hard
error ("service chatterbox has neither an image nor a build context
specified"). The audio sidecars live in the opt-in audio overlay that
`lobes up <non-audio-role>` deliberately leaves out, so their half is paired
with that overlay and the base half with docker-compose.yml.
Why on the CARD and not a shape: which syntax the runtime accepts is a fact
about the board, true of every shape rendered over it — a shape-scoped fix
would leave a bare `lobes init` on the same board broken. Same reasoning as
the `[host_env]` iowait declaration.
`gpu_access` renders NO .env key, so every profile and shape golden — orin's
included — is byte-unchanged (regen.py reproduces them bit for bit).
UNVALIDATED (#108): `docker compose config` on the real csv-mode board renders
and merges the overrides correctly (all 12 GPU services convert, zero legacy
stanzas remain). That proves the compose merge, not a container create; only
the live boot can do that.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
…ed overrides
Profile.gpu_access ('devices' default | 'runtime') drives a generated
docker-compose.gpu.yml pair using 'deploy: !reset null' to drop the base
devices stanza. Card-scoped, so a bare machine-as-brain init on a csv-mode
board works too. Renders no .env key — every golden byte-unchanged.
Compose MERGE verified live; container create is UNVALIDATED until t9.
d6: operator profile shadowed the new builtin (#175, archived on-box). d7: live re-render blocked by the environment's permission classifier — t9/t11 and t12's measured backfill become operator follow-ups. r6: help-surface follow-up (#173). r7: MEASURED-PENDING budgets unvalidated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
Accountability artifact for the assign-to-workforce run: every plan task accounted for, 7 drift entries classified, delivery claims carrying evidence or an explicit unverified marker. The four capability claims and the budget hypothesis are marked UNVERIFIED — no boot occurred (d7). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
Adds the orin card/profile/shape entries, the speculative-config knob, the host-dependent-test fix, and a 'Known issues found while building this release' section pointing at #171/#173/#174/#175 and the #101 correction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
PR Summary by QodoJetson AGX Orin first-class card + orin-lobe + QAT senses profile
AI Description
Diagram
High-Level Assessment
Files changed (58)
|
Escapes the pipes in <|channel> / <|tool_call> inside table cells (they were splitting the rows), and stops a line starting with '#108' being read as an ATX heading. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
Code Review by Qodo
1.
|
…t11) 0.45/262144 booted FIRST TRY at the full native window: weights 9.07 GiB, KV 11.81 GiB = 609,266 tokens = 2.32x. csv-mode runtime:nvidia proven at container CREATE (t8's mechanism, live). orin-lobe is now VALIDATED. Capability matrix vs the incumbent, same box/engine/day: VIDEO now PASSES the reversed-motion control the incumbent FAILS; image/reasoning/tools hold; audio still unusable (#101, vLLM-side). Decode +8% medium, +79% long; the incumbent's long-generation decay does not reproduce. Three blockers hit and recorded: operator profile shadowing (#175), --force destroying operator .env lines (#174), and the checkpoint shipping without vision_config.num_soft_tokens (#177, patched locally to boot). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
✅ Live-validated on the physical Jetson AGX Orin — the UNVALIDATED caveat in the description is now supersededThe re-render, boot, and probe matrix all ran on the real board (2026-08-04). The hypothesis booted first try
That last line is the live proof Does it fit the machine better? Mostly yes — with one honest exception
Lighter, cheaper per KV token, faster — and the incumbent's decode decay on long generations (9.99 → 6.39) simply does not reproduce (10.81 → 11.45). The exception is concurrency headroom: ~6.7 GiB of the budget goes to peak activation that vLLM profiles "with 1 video items of the maximum feature size". That is the price of the ViT actually working — see below. Capability matrix, same box and engine as the baseline
Video is the headline. The forward clip and its reversed control now get different, correct answers — a single-frame read cannot pass that. The incumbent answered Audio remains unusable and is honestly reported as such. Tokens scale with duration (~25 tok/s, so the audio is encoded), but the discrimination control fails 0/3 — the model answers Three blockers hit on the way — all filed
Live fleet state nowStill outstanding: the gateway image is cached at 0.45.0, so |
…lease cortex->Spark verified proxied=True after the --force .env rewrite. worker->Thor wiring is in .env (keyless, peer verified live) but the gateway image installs the PUBLISHED wheel, so 0.55.0 is undeployable until #176 merges (d8). The failed build left the running fleet healthy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
1. Orin marker false positives (lobes/machines/orin.py) — the 'orin' marker matches every Jetson Orin variant, and DetectionSignature.matches ignored the declared compute_capability/total_memory_gb, so an Orin Nano/NX would resolve to the 64GB card and inherit a senses budget (util 0.45 @ 256K) it cannot hold. Declared traits are now CONSTRAINTS when the caller supplies the corresponding probed fact; unprobed facts constrain nothing, so the legacy name-only behaviour and every existing call site are unchanged. Memory is a band, not equality — a '64GB' board reports 61.3. This also caught three tests asserting a physically impossible board: they described an Orin while passing the helper's 128 GB default. Corrected to the real 61.3, plus a new Orin-Nano regression test. 2. host_env newline corrupts .env (lobes/profiles/schema.py) — values are written verbatim into a line-oriented file, so an embedded newline SPLITS the entry rather than escaping. Rejected at profile load (where the operator can see the cause) with a last-resort guardrail in _env.set_env so no code path can write a malformed .env. Live re-verified on the physical box: detect_card() still resolves 'orin' (sm_87, 61.3 GB). Suite 2866 passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
The live boot the classifier initially blocked was operator-approved and ran: t9/t11/t12 move blocked -> delivered, and the four unverified capability claims are re-stated with evidence (video PASS is now a high-confidence claim; audio is marked DISPROVEN rather than merely unverified). t10 stays partial and honest: cortex verified, worker wired but gated on releasing 0.55.0 (d8). Adds d8 and the #177 checkpoint-config finding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
S5799 (init.py) — implicit string concatenation that read as a missing comma; merged into one literal. S3776 (init.py) — _emit_dry_run cognitive complexity 17 > 15; split its two independent jobs into _dry_run_payload / _dry_run_lines / _dry_run_scope. Behaviour unchanged (106 init tests green). S5958 (test_init_gpu_access.py) — pytest.raises(Exception) would have passed on an incidental TypeError; narrowed to ModelGearError + EXIT_USER_ERROR + message. S6353 (schema.py) — PUSHBACK, not fixed. Python's \w is Unicode-aware, so the suggested [A-Za-z_]\w* also accepts CAFÉ_VAR and Aπ (verified). These names get written verbatim into .env, which must stay ASCII. Documented at the regex and pinned by a new test so the 'concise' refactor cannot land silently. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD
|



Makes the Jetson AGX Orin a first-class card alongside
spark/thor: a detected card, a built-in profile, a neworin-lobeshape, andsenseson the QAT checkpointunsloth/gemma-4-12B-it-qat-w4a16. The portability goal is the point — moving this setup to another architecture stays a profile pick, not a rework.What landed (8 of 13 plan tasks)
orincard detectionlobes/machines/orin.py— sm_87, verified live:detect_card()resolvesorinon the real board with no--profile. Pooling-gear divergences are orin's own overrides, deliberately not theSM_110trait — those values are an untested Thor carry-over, and borrowing an sm_110 trait would misstate why they hold.orinprofilesenses= the QAT checkpoint (compressed-tensors int4 W4A16 — weight-only, so Ampere qualifies without Blackwell FP4 tensor cores).cortex/muse/workervetoed on the hard architecture line.orin-lobeshapesenses+embedder+reranker, no localstt/tts(the Parakeet image ships no sm_87 kernels). Fixes a real clobber —thor-lobeover the orin card rendered Thor's0.30/131072, which the deployed box was hand-patching in.env.Profile.host_envlobes initis fixed too — not just this shape. Without it the gateway 429-sheds allsensestraffic indefinitely on a board whose/proc/statreports ~59% iowait with zero disk I/O.gpu_accessruntime: nvidiasurvives a re-render instead of being hand-edited back each time.MULTIMODAL_SPECULATIVE_CONFIGconfig.json. All capability rows marked pending-live-probe.Boundary proof
spark.toml,thor.toml,base.tomland every pre-existing golden are byte-identical —git diff mainover those paths is empty, and the golden diff is additions-only (neworin*files). This release adds a card; it does not move the Blackwell fleet.Verification
Every merge was TDD-gated: the suite ran green before and after each one (2725 → 2736 → 2745 → 2760 → 2816 → 2860). One branch was deliberately held back from merge until its failures were fixed rather than merged red.
Pre-existing defects this work surfaced — filed, not fixed here
These are the reviewable substance as much as the feature is. Each was found by measurement, not inspection:
docs/orin-profiles.mdoverstates the Orin KV pool 1.67× (18.86 GiB documented vs 11.29 GiB measured at identical knobs). Cause:gpu_mem_utilis a fraction of the whole device, so co-resident engines are deducted one-for-one from KV. The documented figure was measured senses-first into an empty device. Boot order changes the served budget, not just OOM risk.lobes init --forcesilently destroys operator-typed .env lines (peer wiring, API keys) — 12 keys lost on a real deployment #174 —lobes init --forcesilently destroys operator-typed.envkeys: 12 lost on a real deployment, including the cortex peer-proxy origin, flag, and credential. After it,model=cortexstops proxying and 404s with no error and no diff — and the credential cannot be re-derived. Found by rehearsing the render against a copy rather than running it live.lobes help <topic>+ aGET /helpgateway endpoint — surface the field traps that currently only live in evidence transcripts #173 — proposeslobes help <role>/GET /help, because the field traps below are discoverable only in evidence transcripts.Measured on the incumbent, before the swap (unrecoverable afterwards)
docs/evidence/2026-08-04-accept-senses-unsloth-orin.txtrecords the baseline the playbook demands, plus a capability baseline that validated the probe harness:usage.completion_tokens; the chunk-count trap reproduced (~1.8× under-report), both figures kept so the difference is auditableRIGHT-TO-LEFT— no directional discrimination, which only the control revealedenable_thinking), and streaming sends the trace ondelta.reasoningwhiledelta.contentstays empty for its entire duration — a client reading onlycontentsees a hung laneTo finish this on hardware
Boot senses first (#171 — order changes the KV pool), restore the 12 operator
.envlines immediately after (#174), then run the probe harness and backfill the measured budget into bothorin.tomlandorin-lobe.toml— a lockstep test fails CI if only one moves. Record the value that booted and any value refused; the refusal is the more useful datum.🤖 Generated with Claude Code
https://claude.ai/code/session_01HzuzZKukvUXJooAZSBn2XD