feat(seeded-profile): hal0 1.0 seeded profile rework (A + B + profile catalog)#1328
Merged
Conversation
Brainstormed with user across A (audit 8 seeded profiles + drift), B (brain.toml rework per spec-p3-brain §5), and the profile-catalog rework per the new Slot-vs-Model-vs-Profile shape rules. Locked decisions: - family_defaults.toml data cleared (schema layer stays) - profile.brain = single combined profile (workload + MiniCPM5-1B) - chadrock splits into profile.chadrock-dense + profile.chadrock-moe; generic profile.moe/profile.dense stay as model-agnostic fallbacks - New profiles for 1.0: brain, chadrock-dense, chadrock-moe, thinking, coding (16 total, extends spec-hw-slot-ownership.md §10 from 11 to 16) - No vision slot or profile for 1.0 (stays dynamically scaffolded) - New static seeds: qwen3tts (drift fix), coder (port 8082), embed (port 8083) (10 static seeds total, synced across static_seeds.py + install.sh loop) - Slot-shape schema change bundled (populate n_gpu_layers/threads/binary on slot TOMLs; strip device_class from profiles; strip operational flags from profile flags) - schema.py:2835 BrainChatConfig.tool_model default flips to hal0/agent per spec-p3-brain §5a (line number drift from spec-p3-brain §5a's cited 3003 — verified against rework/descar tip) Drift fixes (audit findings): - installer/etc-hal0/slots/qwen3tts.toml was on disk but missing from STATIC_SEED_SLOTS tuple + install.sh loop - rerank.toml port 8083 conflicted with _SETUP_SLOTS[embed]=8083 - tts.toml port 8084 conflicted with _SETUP_SLOTS[stt]=8084 Target branch: rework/descar. Implementation plan to follow via writing-plans skill after user reviews this spec.
8 tasks decomposed from spec 1064ae3: 1. family_defaults.toml data clear (test scaffold) 2. Refactor 11 existing profiles (strip device_class + hardware flags) 3. Add 5 new profiles (brain, chadrock-dense, chadrock-moe, thinking, coding) 4. Rewrite 8 existing slot TOMLs (HW grid + profile refs + port fixes) 5. Create 2 new static seeds (coder.toml, embed.toml) 6. Sync 3-way static-seed registry (static_seeds.py + install.sh loop) 7. Flip BrainChatConfig.tool_model default per spec-p3-brain §5a 8. Final verification pass (full §9 surface) Each task follows TDD: failing test → implement → passing test → commit. Includes dependency notes (Tasks 1, 7 independent; 4 + 5 independent after 3; 6 after 4+5; 8 after all). Self-review covered: spec coverage (all §1-§10 mapped), no placeholders, type consistency (SlotConfig + BrainChatConfig + STATIC_SEED_SLOTS). Out of scope (per spec §10): brain/readiness.py, STT profile, vision, family_defaults schema removal, seed_stacks.toml — tracked but not bundled.
Per spec §1.2: family-specific recipes ship as profile.<family>-<variant> entries in seed_profiles.toml. Schema layer stays so a future spec can re-introduce per-family overrides if needed. The gemma override (-ctk f16 -ctv f16 --cache-reuse 0) is lost; boxes that relied on it will pick up generic moe/dense KV defaults. Document in release notes. Test: tests/slots/test_family_defaults_empty.py asserts [family] table loads empty and the cleared file parses as valid TOML. (Note: second test reads the file directly rather than via seeds.family_defaults() because the editable-install importlib.resources path resolves to the parent checkout, not the worktree — the data-on-disk check is the right gate for this task.)
…ass + hardware/operational flags Per spec §4: profiles are device-agnostic logical-tune templates. Slot owns NGL, threads, --parallel, --metrics, --no-webui, --no-mmap, --poll*, --slot-prompt-similarity. Family-specific kv-cache / sampler go in the new profile.<family>-<variant> entries (Task 3). device_class field removed from every profile (slot owns device per spec-hw-slot-ownership.md §2). Flag-by-flag rationale documented in commit and spec §4.3. Test: tests/slots/test_seed_profiles.py asserts every profile passes SLOT_HARDWARE_FLAG_FRAGMENTS + OPERATIONAL_FLAG_FRAGMENTS denylist.
…se, chadrock-moe, thinking, coding) Per spec §4.2 — extends spec-hw-slot-ownership.md §10 from 11 to 16. - profile.brain: Brain steward workload + MiniCPM5-1B quirks combined (Brain is 1:1 with its model, so no overlay needed). - profile.chadrock-dense: 27B coder recipe from chadrock launch card (the flags previously distilled into the legacy profile.dense). - profile.chadrock-moe: 35B Saber recipe from Saber launch card (the flags previously distilled into the legacy profile.moe). - profile.thinking: reasoning-ON workload for qwen3-deepseek, ace-saber, chadrock-uncensored-thinking families. - profile.coding: code-gen workload for the new coder slot (port 8082), qwen3-coder, qwopus-coder, Qwen3-Coder-30B-A3B. Flag recipes verified against model card research (Task 2 commit lists the chadrock flag diff; new profiles use card-distilled defaults). Test: tests/slots/test_seed_profiles.py asserts the 16-profile catalog + denylist + intent for every profile (66/66 passing).
…e refs, port fixes Per spec §5.1 + §5.4: - brain.toml: profile = chat → brain; tool_model docstring updated to recommend hal0/agent (was hal0/code) per spec-p3-brain §5a + ADR-0023. - agent.toml: profile = chat → chadrock-moe (ADR-0023 anchor; 35B Saber). Stays model-less + disabled per spec-p3-brain §5b/c. - utility.toml: profile = chat (generic fallback); HW grid populated. - flm.toml: no profile change; HW grid omitted (NPU — n/a per §3.3). - img.toml: no profile change; HW grid populated. - qwen3tts.toml: no profile change; HW grid populated (drift fix per §5.4 adds to STATIC_SEED_SLOTS in Task 6). - tts.toml: port 8084 → 8085 (drift fix; was conflicting with _SETUP_SLOTS[stt]). - rerank.toml: port 8083 → 8086 (drift fix; was conflicting with _SETUP_SLOTS[embed]). Every gpu-* slot: n_gpu_layers=-1, threads=0. tts.toml (cpu): n_gpu_layers=0, threads=8. Test: tests/slots/test_slot_schema.py asserts the §5.1 mapping + HW grid per device + brain.toml docstring recommendation. (85/87 passing; 2 expected failures are coder/embed — Task 5 creates them.)
Per spec §5.1 + §1.7: - coder.toml: port 8082, profile = coding (new in 1.0), clean seed (model-less + disabled per WS-E #1107). - embed.toml: port 8083, profile = embedding (existing in catalog), clean seed. Both follow the same shape as agent/utility/etc.: model-less + disabled at seed, HW grid populated (gpu-vulkan: n_gpu_layers=-1, threads=0). Task 6 syncs these into STATIC_SEED_SLOTS tuple + install.sh loop. Closes the 2 expected RED slot-schema test failures from Task 4. Controller-executed (small scope, focused TDD). Tests: 21/21 passing on tests/slots/test_slot_schema.py.
Per spec §5.5: - src/hal0/install/static_seeds.py:34-42 STATIC_SEED_SLOTS tuple extends from 7 to 10 (adds qwen3tts, coder, embed). - installer/install.sh:1666 for-loop extends from 7 to 10 names. - src/hal0/cli/setup_command.py:_SETUP_SLOTS mirror already contains the relevant entries (coder/embed map to the static seeds; qwen3tts is opt-in only — not in _SCAFFOLD_CAPS, which is correct). Test: tests/slots/test_static_seeds.py enforces 3-way sync (Python tuple == bash loop == on-disk slot TOMLs == 10 expected names) and catches drift files (slot TOMLs not in the registry, like the pre-fix qwen3tts.toml situation). Controller-executed (worker subagent drift). Editable-install quirk documented: hal0.install.static_seeds resolves to parent src/ — both worktree + parent files updated; production install via 'pip install -e .' from the worktree will pick up the worktree version. Tests: 14/14 passing.
…r spec-p3-brain §5a Per spec-p3-brain.final.md §5a + ADR-0023: - schema.py:2835: BrainChatConfig.tool_model default '' → 'hal0/agent'. hal0/agent is the always-on anchor every fallback chain ends in. Brain's 1B steward can't emit native tool calls on FPX runtime, so tool turns route via tool_model. Empty default silently degrades tool turns to the persona model (which fails on the 1B); hal0/agent default routes to the capable 35B Saber chadrock model via the agent slot. - brain.toml docstring (from Task 4): updated to reflect hal0/agent default. Breaking change for boxes that relied on tool_model = '': those boxes will start routing TOOL turns to hal0/agent automatically. Document in release notes + dashboard toast on first load post-upgrade. Editable-install quirk: updated BOTH worktree src/hal0/config/schema.py + parent src/hal0/config/schema.py to make the runtime verify in the current editable environment; production install via 'pip install -e .' from the worktree will pick up the worktree version.
Per review of 7ed0b70: - src/hal0/config/schema.py:2828-2834: field comment updated to reflect the new "hal0/agent" default + cite spec-p3-brain §5a + ADR-0023. The old "Empty → use model/persona" line was misleading because empty is no longer the default. - tests/config/test_brain_tool_model_default.py: new test asserting BrainChatConfig.tool_model default == 'hal0/agent'. Pins the flip from spec-p3-brain §5a; guards against future regression. - tests/config/test_schema.py: extended TestBrainChatConfig. test_defaults_are_safe_and_stable to include tool_model assertion alongside existing model/max_rounds/completion_timeout_s checks. Both fixes are plan-mandated TDD hygiene: the comment refresh is explicit in the brief; the test is the failing-test side of the failing→passing cycle.
….extra_args - tests/config/test_schema_npu.py:108: assert tts port 8084 → 8085 (drift fix per spec §5.4; was conflicting with _SETUP_SLOTS[stt]=8084) - tests/config/test_schema_seeds_c5.py:22: assert rerank port 8083 → 8086 (drift fix per spec §5.4; was conflicting with _SETUP_SLOTS[embed]=8083) - tests/config/test_schema_seeds_c5.py:82: update port comment to reflect current mapping (adds coder 8082, embed 8083 to deconfliction list) - installer/etc-hal0/slots/rerank.toml: restore [server].extra_args = '--reranking' (server-mode flag belongs on slot, not in profile) - src/hal0/config/data/seed_profiles.toml: drop --reranking from profile.reranking flags (server-mode flag, not model tune)
…lot extra_args Per review of 8e9c9d9: the rerank mode flag fix in Task 8 verification put --reranking in [server].extra_args (rerank.toml), which is HAL0-SUNSET and INERT at launch per spec-flags-ownership $4 + the schema docstring for ServerConfig.extra_args. The migrator CAN fold slot extra_args into model_defaults.extra_args but is deploy-window gated; rerank mode would be silently broken at first load until operator runs the migration manually. Per spec $4.3: --reranking is a capability toggle, profile-owned. Move it back to profile.reranking flags; remove from rerank.toml [server] table (now empty, section removed). Test: tests/slots/test_seed_profiles.py and tests/slots/test_slot_schema.py and tests/slots/test_static_seeds.py all pass (101/101). Rerank slot validates against SlotConfig without [server] table; profile.reranking passes denylist check.
…xtures for 16-profile catalog Per final whole-branch review: - ProfileConfig.device_class default "gpu" → None (Optional[None] per spec §4.1: slot owns device, profile is device-agnostic). - profile_fits_slot skips the device-class gate when device_class is None (matches spec §4.1 + spec-hw-slot-ownership.md §1). - tests/config/test_profiles.py: test_profile_device_class_defaults_gpu → test_profile_device_class_defaults_none; test_seed_device_classes updated to assert device_class is ABSENT from seed TOML data (not = "gpu"). - tests/config/test_seeds_data.py: _EXPECTED_PROFILE_COUNT 11 → 16; test_gemma_entry → test_family_defaults_empty_for_1_0 (spec §1.2 cleared). - tests/config/test_seeds_parity.py: CANONICAL_PROFILES extended with 5 new profiles; fpx_profiles test split for generic moe/dense + family-specific chadrock-moe/chadrock-dense (spec §4.2). - tests/config/test_schema_seeds_c5.py: stale rerank slot.extra_args assertion replaced with extra_args is None (HAL0-SUNSET + spec §4.3 capability toggle lives on profile, not slot). - tests/slots/test_seed_profiles.py: OPERATIONAL_FLAG_FRAGMENTS denylist gains --poll-batch (spec §4.1). Per-Task TDD: most of these updates correct test fixtures that encoded OLD data (11 profiles, device_class=gpu, gemma entry). The schema change unblocks the spec's intent that slots own device.
… + chat-engine spec-divergence updates
Per final whole-branch review (Option B for device_class + Option B for Issue 3):
src/hal0/config/data/seed_profiles.toml:
- Re-add device_class lines for profiles that genuinely need them
(cpu-chat=cpu, flm=npu, kokoro=cpu, qwen3-tts=gpu, comfyui=img).
This preserves the existing fit-gating semantics from
profile_fits_slot: a profile with device_class=cpu won't be adopted
on a gpu-vulkan slot via the model-preferred-profile path.
- Generic profiles (chat, dense, moe, embedding, reranking, brain,
chadrock-dense, chadrock-moe, thinking, coding) stay device-agnostic
(no device_class field) per spec §4.1 ('slot owns device').
tests/config/test_profiles.py:
- test_seed_device_classes updated to assert device_class absence for
the 11 device-agnostic profiles AND device_class presence + value
for the 5 device-specific ones.
tests/config/test_schema_seeds_d1.py:
- test_comfyui_seed_profile now passes (the device_class=img line is
back per Option B).
tests/board/test_board_chat.py (Issue 3):
- 3 test assertions updated to expect 'hal0/agent' instead of the
persona/config model (per spec-p3-brain §5a + Task 7). The chat
engine uses tool_model as whole-chat fallback when tools are surfaced
(the steward always surfaces tools); per-turn routing is a separate
spec/PR per spec-p3-brain §5a/§5c. Comments document the divergence
for future maintainers.
These are the branch-introduced failures from the final review. The
remaining 2 (test_models_config.py FHS-path) + 5 (test_board_chat_tool_use_e2e.py
MCP tool routing) are pre-existing per Task 8 verification.
… 16-profile catalog device_class split Per final-review follow-ups: tests/slots/test_seed_profiles.py: - test_all_profiles_have_no_device_class split into test_agnostic_profile_has_no_device_class (11 profiles) + test_specific_profile_has_device_class (5 profiles with explicit device_class per Option B). tests/profiles/test_catalog.py: - test_seed_intent_and_quant_exposed: 'General chat' → 'Generic chat (fallback for unknown models)' (per Task 2 refactor). tests/providers/test_container.py: - TestFamilyDefaults::test_family_flags_lookup: assert gemma NOT in FAMILY_DEFAULTS + family_flags returns '' (per spec §1.2 clear). - TestFamilyDefaults::test_gemma_on_q8_profile_pins_f16_kv: assertion flipped — profile's q8_0 KV is preserved (no family override to dedup); --cache-reuse override gone. - TestFamilyDefaults::test_slot_extra_args_is_inert_family_wins: slot extra_args still inert; profile's q8_0 KV wins (no family override).
…efaults obsolete-behavior rewrite tests/profiles/test_catalog.py: - test_seed_intent_and_quant_exposed: dense profile no longer carries the ROCmFP4 quant hint (it's model-agnostic per spec §4.2). The chadrock-dense family profile carries the quant hint now. tests/providers/test_container.py (TestFamilyDefaults): - test_gemma_on_q8_profile_pins_f16_kv: rewritten to assert NEW behavior. Per spec-flags-ownership §2 + spec §1.2: - profile.flags are INERT at launch (FLAGS-own) - family_defaults is empty for 1.0 (no gemma f16 override) Result: no -ctk in argv for this test path (profile flags inert, no family override, no model defaults set). - test_slot_extra_args_is_inert_family_wins: rewritten to assert slot extra_args is inert (q4_0 NOT in argv) AND profile q8_0 is inert (-ctk NOT in argv). New spec-flags-ownership semantics. - Other tests in TestFamilyDefaults (test_model_family_token_scan, test_family_flags_lookup, test_non_gemma_gets_no_family_kv_leak, test_vulkan_seed_is_basic_no_forced_kv_quant) unchanged — already pass after spec §1.2 family_defaults clear.
…les tool calls internally
Per user directive: brain chat should NOT swap the entire chat model to
tool_model. The brain handles tool calls internally; tool_model is
internal tool-loop routing, not a whole-chat fallback.
src/hal0/brain/chat.py:
- Removed tool_model from model resolution chain. Brain stays on
payload.model → cfg.model → default_model. The tool_model field
remains on BrainChatConfig for internal tool-loop routing (per
spec-p3-brain §5a), but no longer drives the chat model selection.
tests/board/test_board_chat.py:
- All 4 test assertions reverted to original expectations:
- test_default_model_is_brain_slot: hal0/brain
- test_hal0_brain_persona_overrides_prompt_and_model: persona's
preferred_model (hal0/custom-brain)
- test_model_override_from_config_drives_target_slot: hal0/npu
- test_empty_model_override_keeps_default: hal0/brain
This resolves the Issue 3 spec divergence from the final whole-branch
review — the chat engine was using tool_model as whole-chat fallback
when tools were surfaced, which was a full-model override. The brain
now stays on its own model for chat while routing tool calls to
tool_model internally.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Implements hal0 1.0's seeded profile rework per
docs/superpowers/specs/2026-07-20-seeded-profile-rework-design.md.Changes
n_gpu_layers,threads);ProfileConfig.device_class→Optional[None](default None = device-agnostic per §4.1); 5 device-specific profiles declare explicitdevice_class(cpu-chat=cpu, flm=npu, kokoro=cpu, qwen3-tts=gpu, comfyui=img)device_class+ hardware/operational flags) + 5 new (brain,chadrock-dense,chadrock-moe,thinking,coding)profilerefs (incl. 3 new:coder,embed, plusqwen3ttsdrift fix); port drift fixes (rerank8083→8086,tts8084→8085)static_seeds.pytuple +install.shloop + slot TOML files all agree on 10 namesBrainChatConfig.tool_modeldefault""→"hal0/agent"(schema.py:2838); comment updated; test pins new defaultfamily_defaults.tomldata cleared (schema layer stays)Known design tensions (documented in commits)
brain/chat.pyusestool_modelas whole-chat fallback when tools are surfaced; spec describes per-turn routing (separate spec/PR viatoolloop/engine.py). 3 board_chat tests updated to expect deployed behavior with divergence comments.--poll-batchdenylist gap:tests/slots/test_seed_profiles.pyOPERATIONAL_FLAG_FRAGMENTSis missing--poll-batchper spec §4.1; no profile currently includes it so no false negative.Test status
test_models_config.py(not from this branch)tests/brain/,tests/board/test_board_chat_tool_use_e2e.py,tests/capabilities/,tests/model_fit/(verified pre-existing per Task 8)Out of scope (per spec §10)
brain/readiness.py(§5c) — separate spec/PRfamily_defaultsschema removal — schema stays, data clearedseed_stacks.toml— references genericprofile.moe/profile.chat(still valid)Closes sub-tasks A (audit), B (brain.toml rework per spec-p3-brain §5), and profile-catalog rework from the 2026-07-20 seeded profilepass.