All notable changes to this project are documented here. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Fallback model + single front OpenAI gateway ("fleet"). A new
scaffold-based deployment runs two always-warm vLLM backends behind one
stdlib gateway that model-gear manages as three containers
(
model-gear-gateway,model-gear-vllm-primary,model-gear-vllm-fallback). The gateway routes each request by itsmodelfield, defaults an unknown/missing name to the primary, and fails over to the other backend when the chosen one refuses the connection or returns a 5xx before the response body (4xx is returned verbatim; no mid-stream retry). SSE streams are relayed chunk-by-chunk. Default fallback: the MoEmmangkad/Qwen3.6-35B-A3B-NVFP4. - New gateway package
model_gear/gateway/— a pure-stdlib (http.server+http.client, no runtime deps) reverse proxy:_routing.py(pure name/alias/default routing + failover ordering),_config.py(env → routing table + server config),server.py(thehandle_postfailover seam, upstream client, andThreadingHTTPServerhandler), run aspython -m model_gear.gateway. model init --fleetscaffolds the fleet templates (docker-compose.yml+.env+Dockerfile.gateway) and pinsMODEL_GEAR_VERSIONto the running release;model fleet up | down | statusdrives the deployment (up/downdry-run by default,--applyto commit;statusis read-only and reports all three containers + the gateway/health+/v1/models).- Docs:
docs/gateway-fleet.md(topology, routing/failover, memory, verbs),docs/qwen3.6-35b-a3b-nvfp4.md(the MoE fallback), a README "fleet" section, andmodel explain fleet/model explain gatewayentries.
model_gear/runtime/_compose.pygained a template registry (SINGLE_TEMPLATES/FLEET_TEMPLATES), atemplates=argument onscaffold_plan/write_scaffold(single-model stays the default — existing callers unchanged), acompose_up_buildhelper, andFLEET_CONTAINERS.- The fleet
.envmirrorsVLLM_MODEL/VLLM_SERVED_NAME/VLLM_TOOL_CALL_PARSER(= the primary) so the read-only single-model verbs (status/whoami/doctor) stay coherent on a fleet deployment.model switchremains single-model only.
- SonarCloud cleanup (no behavior change). Split
cmd_switchinto_select_parser/_emit_dry_run/_apply_switchhelpers to bring its cognitive complexity under the gate, and hoisted the repeated"(unset)"literal inmodel statusinto a_UNSETconstant.
- Per-model tool-call parser auto-selection. New
model_gear/runtime/_parser.pyinfer_parser()maps a model name to its parser (qwen3_coderfor Qwen3-Coder / Qwen3.6,hermesfor Qwen3 dense, unknown → leave untouched).model switchnow picks the right parser automatically so tool calling keeps working across a switch without the caller remembering it;--tool-call-parserstill overrides (issue #13). - Post-switch / post-start tool-calling probe.
model switch --applyandmodel serve --applynow probetool_choice:"auto"once the container is healthy and report PASS/FAIL (with the called tool names) — reusing the existingassessprobe.--no-probeskips it; the probe never aborts the command (unreachable / HTTP 400 degrade to a FAIL result). model statusreports the activetool_call_parser(VLLM_TOOL_CALL_PARSER), so "which gear am I in" is complete withoutdocker inspect.
lepenseuris retired; the deployed agent is nowmodel-gear. The tool and the deployed agent share one identity. Updatedculture.yaml(suffix: model-gear), theAGENTS.mdsystem prompt,model whoami/learn/explainoutput, the posting nick (.claude/skills.local.yaml.example), the compose/.envtemplates,README.md, andCLAUDE.md(the former "two identities" section now describes one).
- OpenAI tool/function calling on the served vLLM model. The packaged compose
template (
model_gear/templates/docker-compose.yml) now serves with--enable-auto-tool-choiceand--tool-call-parser=${VLLM_TOOL_CALL_PARSER:-hermes}, sotool_choice:"auto"requests return atool_callsarray instead of HTTP 400. Additive — plain chat/reasoning is unaffected, no extra GPU/memory cost. Unblocks coder-agent harnesses that drive the model entirely through tool calls (issue #9). VLLM_TOOL_CALL_PARSERenv var (defaulthermes) +model switch --tool-call-parser— the parser is per-model:hermesfits Qwen3 dense (e.g.Qwen3-32B), while Qwen3-Coder / Qwen3.6 checkpoints emit the XML function format and needqwen3_coder.switchwrites the var only when the flag is given, so retuning a model never clobbers its parser.model assess --tools— an opt-in tool-calling probe that verifies atool_choice:"auto"request returns atool_callsarray naming afinishfunction. Degrades gracefully (a FAIL row, no abort) against a server that lacks the flags.
- devague workflow trio vendored under
.claude/skills/(cite-don't-import):think(idea→spec),spec-to-plan(spec→plan), andassign-to-workforce(plan→parallel implementation) — the operator chain for the deterministicdevagueCLI. Authored inagentculture/devague, vendored via guildmaster; each carriestype: command(load-bearing on the culture/agex backend, where aSKILL.mdwithouttype:is silently skipped). They drive thedevagueCLI at runtime (uv tool install devague), resolved portably by the wrappers. docs/skill-sources.md— provenance ledger recording the citation path and authoring origin of every vendored skill (the trio plus the six steward-sourced skills).
Redesigned the repo around running, assessing, and switching the local vLLM
model. The model-ops logic that lived in the model-runner skill is now a
first-class CLI. lepenseur is still the deployed agent that consumes the served
model; model-gear is the tool that runs it.
- Model-ops verbs on the
modelCLI:switch <model>,serve(aliasstart) /stop,status,assess(correctness probes),benchmark(decode throughput + prefill), andinit(scaffold a deployment dir). Write verbs (switch/serve/stop/init) are dry-run by default and require--apply(mutation-safety rule). - Scaffold-based deployment.
docker-compose.yml+env.exampleship as packaged templates undermodel_gear/templates/;model initmaterialises them into~/.model-gear(default), aTARGET, or the local folder. Every model-ops verb resolves the deployment dir via--compose-dir→$MODEL_GEAR_DIR→~/.model-gear. - Ported runtime modules (
model_gear/runtime/+model_gear/assess.py), stdlib-only (urllib, fixed-argvsubprocess), with full unit tests. model overviewnow folds in the currently-served model and the candidate-model list, filterable with--current/--list.
- PyPI distribution renamed
lepenseur→model-gear; binarylepenseur→model; Python packagelepenseur→model_gear. Error classLepenseurError→ModelGearError. Thelepenseurconsole script is removed. - Agent-first verbs reframed for the tool:
whoamireports tool/machine/served model/container health/agent;learnteaches the model-ops surface;explaincatalog rewritten (switch/assess/backend/models/…). doctoris now real — checks docker availability, deployment scaffold,.env↔culture.yamlcoherence, and/healthreachability (a down model is a warning, not a failure).- The
model-runnerskill is now a thin shim thatexecsmodel; its_assess.pywas removed (the logic lives inmodel_gear/assess.py). AGENTS.md/culture.yamlclarified: they describe the deployedlepenseuragent, not the repo. README + CLAUDE.md reoriented around model-gear.
- BREAKING: the vLLM container is renamed
lepenseur-vllm→model-gear-vllm. A box running the old container mustdocker compose downunder the old name, thenmodel init --apply+model serve --apply.
model-runnerskill (local, not vendored):switchthe local vLLM runtime model andassess/benchmark it (stdlib_assess.pyfor correctness + throughput, host-side facts via the wrapper). Drives this repo's compose +.env; documented in CLAUDE.md and README. Mutating verbs (switch,down) are dry-run by default and require--apply(CLAUDE.md mutation-safety rule);--portdefaults to.env'sVLLM_PORT(then 8000).
docs/qwen3.6-27b-nvfp4.md: filled with the live load-test (DGX Spark/GB10, 2026-05-27).mmangkad/Qwen3.6-27B-NVFP4loads and serves under our vLLM image (no--trust-remote-code); ~7.9–8.0 tok/s decode, ~70 GB reserved, 29 GB weights. It is a hybrid Mamba/linear-attention vision-language model and is slower on decode than the 32B here — recommendation: keep the 32B. All pre-flight caveats (SGLang-only, multimodal, ModelOpt rc) validated/resolved.
docs/qwen3-32b-nvfp4.md: per-model doc for the current runtime model, with a live test on DGX Spark (GB10) —nvcr.io/nvidia/vllm:26.04-py3(engine0.19.0+...nv26.04), ~9.7 tok/s decode (batch=1), ~2,800 tok/s prefill, ~72 GB reserved atgpu-memory-utilization=0.6, correctness verified.docs/qwen3.6-27b-nvfp4.md: per-model doc for candidatemmangkad/Qwen3.6-27B-NVFP4. ItsQwen3_5ForConditionalGenerationarch is registered in the current vLLM image (so the same compose can serve it); live load-test/benchmark tracked by issue #6.- README "Per-model notes" linking both docs.
docker-compose.yml: corrected the--reasoning-parser=qwen3comment — on the nv26.04 build the<think>trace is returned in thereasoningfield, notreasoning_content.
docker-compose.yml+.env.example: a local vLLM server (NGCnvcr.io/nvidia/vllmimage) that serves the runtime model as an OpenAI-compatible API on:8000for theacpbackend, tuned for DGX Spark (GB10 Blackwell, 128 GB unified memory).- README "Running the model locally (vLLM)" section.
- Switched lepenseur's runtime model from
nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4tonvidia/Qwen3-32B-NVFP4acrossculture.yaml,AGENTS.md,lepenseur/explain/catalog.py,README.md, andCLAUDE.md(32B dense NVFP4 reasoning model with a thinking mode).
- Initial CLI/PyPI sibling scaffold (copied and adapted from the
lecodeurtwin): top-levellepenseurpackage with thelepenseurconsole script. - Read-only verbs:
whoami,learn,explain,overview, and aclinoun withcli overview. doctorverb shipped as a rubric-shaped stub; real self-diagnosis semantics for a thinking ("non-doer") agent are deferred to a follow-up.- Runtime identity files:
AGENTS.mdandculture.yaml(acp backend,vllm-local/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4). - CI:
tests.yml(test + lint +afi cli doctor . --strictgate + version-check) andpublish.yml(PyPI/TestPyPI via Trusted Publishing). - Six vendored skills under
.claude/skills/(cicd, communicate, version-bump, run-tests, sonarclaude, doc-test-alignment), provenance: steward.