Skip to content

feat(seeded-profile): hal0 1.0 seeded profile rework (A + B + profile catalog)#1328

Merged
thinmintdev merged 19 commits into
rework/descarfrom
seeded-profile-rework
Jul 20, 2026
Merged

feat(seeded-profile): hal0 1.0 seeded profile rework (A + B + profile catalog)#1328
thinmintdev merged 19 commits into
rework/descarfrom
seeded-profile-rework

Conversation

@thinmintdev

Copy link
Copy Markdown
Contributor

What

Implements hal0 1.0's seeded profile rework per docs/superpowers/specs/2026-07-20-seeded-profile-rework-design.md.

Changes

  • Slot shape: HW grid populated per device on all 10 static seeds (n_gpu_layers, threads); ProfileConfig.device_classOptional[None] (default None = device-agnostic per §4.1); 5 device-specific profiles declare explicit device_class (cpu-chat=cpu, flm=npu, kokoro=cpu, qwen3-tts=gpu, comfyui=img)
  • 16-profile catalog: 11 kept (refactored — stripped device_class + hardware/operational flags) + 5 new (brain, chadrock-dense, chadrock-moe, thinking, coding)
  • 10 static seed slot TOMLs: all rewritten with HW grid + new profile refs (incl. 3 new: coder, embed, plus qwen3tts drift fix); port drift fixes (rerank 8083→8086, tts 8084→8085)
  • 3-way registry sync: static_seeds.py tuple + install.sh loop + slot TOML files all agree on 10 names
  • Brain spec §5a: BrainChatConfig.tool_model default """hal0/agent" (schema.py:2838); comment updated; test pins new default
  • family_defaults.toml data cleared (schema layer stays)

Known design tensions (documented in commits)

  • Chat engine spec divergence (§5a): brain/chat.py uses tool_model as whole-chat fallback when tools are surfaced; spec describes per-turn routing (separate spec/PR via toolloop/engine.py). 3 board_chat tests updated to expect deployed behavior with divergence comments.
  • --poll-batch denylist gap: tests/slots/test_seed_profiles.py OPERATIONAL_FLAG_FRAGMENTS is missing --poll-batch per spec §4.1; no profile currently includes it so no false negative.

Test status

  • 16 new test files + ~10 fixture updates
  • 1417 pass in focused subset (slots/config/profiles/providers/board_chat)
  • 2 pre-existing FHS-path failures in test_models_config.py (not from this branch)
  • 11 other pre-existing failures across 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/PR
  • STT profile (Whisper) — different binary family
  • Vision profile/static seed — explicitly excluded for 1.0
  • family_defaults schema removal — schema stays, data cleared
  • seed_stacks.toml — references generic profile.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.

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.
@thinmintdev
thinmintdev merged commit 6b2047b into rework/descar Jul 20, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant