A spec-driven development setup for running OpenSpec
against two local models via llama.cpp, built to fight the most common
local-model failure: unchecked assumptions against the current implementation.
The whole design converts silent assumptions into things that must survive three confrontations with reality — being written down, being fact-checked by a second model, and being executed by tests — before code is trusted.
| Model | Role | Why | Server |
|---|---|---|---|
| Gemma 4 26B-A4B (QAT, MoE ~4B active) | explore, propose |
Fits a 16GB card, fast and interactive for the read-and-draft loop | localhost:8080 id gemma4 |
| Qwen3.6 35B-A3B (MoE ~3.8B active) | verify, apply |
Stronger reasoning / agentic coding; used to fact-check the other model's plan and to implement | localhost:8081 id qwen36 |
Running verification on the opposite model from the one that wrote the proposal is the point: the checker has no investment in the plan, so it won't wave through a convenient assumption.
.
├── Makefile # launches both models with correct aliases/ports
├── AGENTS.md # apply-phase discipline (read by your coding agent)
├── README.md # this file
└── openspec/
├── config.yaml # anti-assumption rules injected into every request
└── verify-prompt.md # cross-model assumption audit prompt
- A
llama.cppbuild withllama-server(recent enough to support--n-cpu-moeand--flash-attn on). - ~16GB VRAM (tested against an RTX 4060 Ti 16GB) and ~32GB system RAM for expert offload.
- Node.js (for the OpenSpec CLI).
- A Hugging Face account with a read token, and having accepted the license on each model's HF page while signed in (both are gated).
- A coding agent that supports a custom OpenAI-compatible base URL and model id (Cursor, Cline, OpenCode, Continue, Aider, etc.).
-
Drop these files into your project root. Merge
AGENTS.mdand theopenspec/folder into your repo. If you already have anAGENTS.md, append the "Implementation Discipline" section from this one. -
Install and initialize OpenSpec (if you haven't already):
npm install -g @fission-ai/openspec@latest cd your-project openspec init # detects your coding agent(s) and wires slash commands
openspec initwill create/overwrite its ownopenspec/config.yamlscaffold. After running it, re-apply thecontext:andrules:blocks from theconfig.yamlin this package (the CLI's default config won't contain them). -
Enable the expanded profile so
verifyexists as a command:openspec config profile # choose the profile that includes verify openspec update # regenerates skills/commands for your tool
Then open the generated
verifyskill for your agent (e.g..claude/skills/openspec-verify/…, or the OpenCode/Continue/etc. equivalent) and replace its body with the contents ofopenspec/verify-prompt.md, keeping any YAML frontmatter the generator wrote at the top. If your version doesn't offerverify, skip this — you can run the verify prompt manually (see the workflow below). That path is more robust anyway. -
Set your HF token and launch a model:
export HF_TOKEN=hf_your_token_here make gemma4 # first run downloads + caches the GGUF (several GB)
Edit
LLAMA ?=at the top of theMakefileif yourllama-serverbinary lives somewhere other than./src/build/bin/llama-server. -
Point your coding agent at the servers. Create two model profiles:
http://localhost:8080/v1, model idgemma4— for explore/proposehttp://localhost:8081/v1, model idqwen36— for verify/apply
No API key is needed (llama-server ignores it; put any placeholder if the agent demands one).
VRAM note: on a 16GB card you generally run one model at a time. Use
make gemma4for planning, stop it, thenmake qwen36for implementation — or run them in two machines/terminals if you have the headroom.make bothexists but only fits if you've verified both models load together.
Both --n-cpu-moe values are starting points. Watch nvidia-smi while a model
loads and runs a real long prompt:
- Loads and OOMs → raise
--n-cpu-moe(more experts to system RAM). - Loads with VRAM headroom → lower
--n-cpu-moefor faster generation. - System RAM (
free -h) approaching 32GB / swapping → onqwen36, drop--no-mmapso experts page from disk instead of being fully resident.
Gemma sampling is temp 1.0 / top-k 64 / top-p 0.95 (Google's rec); Qwen is temp 1.0 / top-k 20 / top-p 0.95 (Qwen's rec) — the top-k differs, don't copy one to the other.
Goal: add a role+team filter to profile search in an existing codebase.
1. Explore (Gemma). Agent on gemma4. This reads the codebase first — the
single best habit against building the wrong thing.
/opsx:explore add profile search filters by role and team
2. Propose (Gemma). Still on gemma4.
/opsx:propose add-profile-filters
Because of openspec/config.yaml, the generated proposal.md MUST include:
- a Current State section citing real file paths + line ranges, and
- an Assumptions section listing each dependency as a verifiable claim,
e.g. "assumes
searchProfiles()accepts afiltersarg atsrc/search/profiles.ts:88", with anything unconfirmed markedUNVERIFIED.
Review the proposal yourself. The Assumptions section is your at-a-glance list of everything that could be wrong.
3. Verify (Qwen). Switch the agent to qwen36. Either:
/opsx:verify add-profile-filters
or, if you didn't install the command, paste:
Follow the instructions in openspec/verify-prompt.md for change add-profile-filters.
Qwen opens each cited file and returns a table marking every assumption VERIFIED / FALSE / UNVERIFIABLE, then a verdict. If any assumption is FALSE or UNVERIFIABLE, it says "NOT ready to apply" — go back to step 2 and fix the proposal. Only a fully CLEAR verdict proceeds.
4. Apply (Qwen). Still on qwen36.
/opsx:apply add-profile-filters
AGENTS.md keeps the agent honest here: it re-confirms assumptions against the
files before editing, and if reality contradicts the spec it STOPS and reports
the discrepancy instead of inventing a workaround. Tasks include writing tests
for the assumed integration points and running the suite before completion.
5. Archive. Once tests pass:
/opsx:archive add-profile-filters
The change's deltas merge into openspec/specs/ (your source of truth), so the
next change builds on verified reality, not stale assumptions.
- Assumptions become explicit (config.yaml forces the section) instead of hiding in prose.
- A second, independent model fact-checks them against real code before any edit (verify-prompt.md on Qwen).
- The apply agent must stop on mismatch rather than paper over it (AGENTS.md).
- Tests execute the assumed integration points before archive.
- Specs stay current so future proposals inherit truth, not drift.
Local models will still assume more than a frontier model would — this doesn't eliminate the problem, it forces every assumption into the open and makes it survive verification and execution before you trust it.
- OpenSpec's config/profile schema is still evolving (the OPSX rework was
recent). Confirm the
config.yamlkeys and theverifycommand name against your installed version; the manual verify-prompt path works regardless. - Model names/quant tags are matched as filename substrings by
-hf repo:TAG. If a download fails listing "available GGUF files," copy the exact tag from that list (e.g.UD-Q4_K_XL, notUD-QA_K_XL). - Both models are gated on Hugging Face — accept each license in a browser while signed in, or downloads return 401 even with a valid token.