Contributing to BLIS¶
This guide covers the engineering standards that keep BLIS (Blackbox Inference Simulator) correct and maintainable.
Quick Start¶
# Build
go build -o simulation_worker main.go
# Test
go test ./...
# Install linter (one-time setup)
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.9.0
# Lint
golangci-lint run ./...
All three must pass before submitting a PR. CI uses golangci-lint v2.9.0 (see .github/workflows/ci.yml).
# Local docs preview (requires Python + mkdocs-material)
pip install mkdocs-material==9.7.3
sed 's|](|](|g' CONTRIBUTING.md > docs/contributing.md
mkdocs serve
Your First Contribution¶
This walkthrough adds a trivial admission policy — the lightest extension type (~3 files). Follow it step-by-step to learn the patterns, then apply them to your own contribution.
What we'll build: A CountingAdmit admission policy that admits the first N requests and rejects the rest. We'll use test-driven development, starting with a test for the feature we want to implement.
Step 1: Create a branch¶
Step 2: Write the failing test¶
Add a test to sim/admission_test.go:
func TestCountingAdmit_RejectsAfterLimit(t *testing.T) {
// GIVEN a CountingAdmit policy with limit=2
policy := &CountingAdmit{Limit: 2}
req := &Request{ID: "test", InputTokens: make([]int, 3)}
state := &RouterState{Clock: 0}
// WHEN 3 requests arrive
r1, _ := policy.Admit(req, state)
r2, _ := policy.Admit(req, state)
r3, reason := policy.Admit(req, state)
// THEN the first 2 are admitted and the 3rd is rejected
if !r1 {
t.Error("first request should be admitted")
}
if !r2 {
t.Error("second request should be admitted")
}
if r3 {
t.Errorf("third request should be rejected, got reason: %s", reason)
}
}
Run: go test ./sim/... -run TestCountingAdmit -v
Expected: FAIL (type CountingAdmit does not exist yet)
Step 3: Implement the policy¶
In sim/admission.go, add after the existing policies:
// CountingAdmit admits the first Limit requests, then rejects all subsequent ones.
type CountingAdmit struct {
Limit int
count int
}
func (c *CountingAdmit) Admit(_ *Request, _ *RouterState) (bool, string) {
c.count++
if c.count <= c.Limit {
return true, ""
}
return false, "counting-admit limit exceeded"
}
Step 4: Register in the factory¶
Two files need changes:
In sim/bundle.go, add "counting-admit" to the validAdmissionPolicies map:
validAdmissionPolicies = map[string]bool{"": true, "always-admit": true, "token-bucket": true, "reject-all": true, "counting-admit": true}
In sim/admission.go, add a case to the NewAdmissionPolicy factory switch:
Note: In a real policy, you would wire the limit through the factory parameters (e.g.,
Limit: int(capacity)) or viaPolicyBundleYAML config. Hardcoded defaults would fail code review — see howtoken-bucketusescapacityandrefillRate.
Step 5: Verify tests pass¶
go test ./sim/... -run TestCountingAdmit -v # Your new test
go test ./... # All tests still pass
golangci-lint run ./... # No lint issues
Step 6: Commit and open a PR¶
git add sim/admission.go sim/admission_test.go sim/bundle.go
git commit -m "feat(sim): add counting-admit admission policy
- Admits first N requests, rejects the rest
- Registered in factory with default limit=100"
git push -u origin feature/counting-admit
gh pr create --title "feat: add counting-admit admission policy" --body "My first BLIS contribution!"
That's it! You've added a complete, tested, registered policy. Real contributions follow the same pattern — just with more contracts and a formal implementation plan.
Important: This example is for learning only. Do not submit this as a real PR —
CountingAdmitis a toy policy with no practical use. For your actual first contribution, check open issues for tasks labeledgood first issue.
Contributing with Claude Code¶
Canonical source:
docs/process/pr-workflow.md. If this section diverges, pr-workflow.md is authoritative.
BLIS development workflows are orchestrated through Claude Code skills — structured sequences that handle worktree creation, plan generation, multi-perspective review with convergence enforcement, and PR creation. Contributors with Claude Code get the full automated pipeline. Contributors without it follow the manual path below and still go through the same quality gates (maintainers run the automated reviews on submitted PRs).
Prerequisites: Claude Code installed with project skills available (convergence-review, hypothesis-experiment) and general Claude Code skills (writing-plans, executing-plans, commit-push-pr). See docs/process/pr-workflow.md for the full skill table. Before your first contribution, read docs/templates/design-guidelines.md — it covers module architecture, extension types, and DES foundations.
Choosing Your Journey¶
| You want to... | Journey | Starts with |
|---|---|---|
| Fix a bug or make a small change | Bug Fix / Small Change | A GitHub issue or observed bug |
| Add a new policy, scorer, or extension | New Policy or Extension | An existing interface to implement |
| Build a new feature or subsystem | New Feature (Idea to PR) | An idea or requirement |
| Validate simulator behavior | Hypothesis Experiment | A behavioral prediction |
For hypothesis experiments, see Running or Contributing Hypothesis Experiments below. With Claude Code, the hypothesis-experiment skill orchestrates the full Steps 0–10 workflow.
Bug Fix / Small Change¶
The lightest path. For bug fixes, docs updates, and single-PR changes that don't introduce new module boundaries.
- Create worktree —
/superpowers:using-git-worktrees fix-<name> - Write micro plan —
/superpowers:writing-plansusing@docs/templates/micro-plan.md - Review plan —
/pr-review-toolkit:review-prthen/convergence-review pr-plan <plan-path> - Human approval — review contracts and tasks, approve to proceed
- Implement —
/superpowers:executing-plans @<plan-path> - Review code —
/pr-review-toolkit:review-prthen/convergence-review pr-code - Self-audit + commit — deliberate critical thinking, then
/commit-commands:commit-push-pr
Full process: docs/process/pr-workflow.md
New Policy or Extension¶
For adding a routing policy, admission policy, scorer, scheduler, priority policy, or tier composition — anything behind an existing interface.
- Identify extension type — see Adding New Components below
- Create worktree —
/superpowers:using-git-worktrees <extension-name> - Write micro plan —
/superpowers:writing-plansusing@docs/templates/micro-plan.mdand@docs/extension-recipes.md - Follow steps 3–7 from Bug Fix (review → approve → implement → review → commit)
No design doc needed for policy templates. For tier compositions, a design doc is recommended — see the extension type table in Adding New Components. Full process: docs/process/pr-workflow.md
New Feature (Idea to PR)¶
The full pipeline for features that introduce new module boundaries, new interfaces, or span multiple PRs.
Phase 1 — Idea to Design:
1. Explore approaches — discuss design options with Claude, settle on an approach
2. Write design doc — following docs/templates/design-guidelines.md
3. Review design — /convergence-review design <path> (8 perspectives)
4. Human approval — review design doc before planning begins
Full process: docs/process/design.md
Phase 2 — Design to Macro Plan (skip if single-PR):
- Write macro plan — decompose into PRs following
docs/templates/macro-plan.md - Review macro plan —
/convergence-review macro-plan <path>(8 perspectives) - Human approval — review PR decomposition and module contracts
Full process: docs/process/macro-plan.md
Phase 3 — Plan to PR (repeat for each PR):
- Follow the Bug Fix journey (steps 1–7) using the macro plan section or design doc as the source document
Each phase produces an artifact that feeds the next. Human approval gates between phases prevent wasted work.
Without Claude Code¶
If you are not using Claude Code, here is the simplified workflow:
- Branch —
git checkout -b feature/my-change - Plan — write an implementation plan following
docs/templates/micro-plan.md. Include behavioral contracts (GIVEN/WHEN/THEN) and a task breakdown. Post the plan as a PR draft or issue comment for review. - Implement — follow TDD: write a failing test, implement the minimal code to pass it, run
go test ./..., rungolangci-lint run ./..., commit. Repeat for each contract. - Self-review — check the Antipattern Checklist below. Run
go build ./... && go test ./... && golangci-lint run ./...one final time. - PR — push your branch and open a PR. Maintainers will run the automated review protocols (convergence-review with 10 perspectives).
The automated review tools (convergence-review, pr-review-toolkit) are run by maintainers — you do not need Claude Code installed. Your PR will go through the same quality gates regardless of tooling.
For design docs and macro plans: follow the same templates (docs/templates/design-guidelines.md, docs/templates/macro-plan.md) and submit for review. Maintainers will run convergence review.
Full process: docs/process/pr-workflow.md (the same workflow applies regardless of tooling)
Engineering Principles¶
See docs/standards/principles.md for the full principles guide covering: separation of concerns, interface design, configuration design, canonical constructors, output channel separation, error handling boundaries, and BDD/TDD development.
Key points for new contributors:
- sim/ is a library — never call os.Exit or logrus.Fatalf. Return errors. Only cmd/ may terminate.
- Write behavioral contracts (GIVEN/WHEN/THEN) before tests. Test observable behavior, not internal structure.
- If your PR touches request lifecycle, KV cache, or metrics, add or extend invariant tests (see docs/standards/invariants.md).
Antipattern Checklist¶
20 rules, each tracing to a real bug. See docs/standards/rules.md for full details.
Before submitting a PR, verify:
- R1: No silent
continue/returndropping data - R2: Map keys sorted before float accumulation or ordered output
- R3: Every new CLI flag validated (zero, negative, NaN, Inf)
- R4: All struct construction sites audited for new fields
- R5: Resource allocation loops handle mid-loop failure with rollback
- R6: No
logrus.Fatalforos.Exitinsim/packages - R7: Invariant tests alongside any golden tests
- R8: No exported mutable maps
- R9:
*float64for YAML fields where zero is valid - R10: YAML strict parsing (
KnownFields(true)) - R11: Division by runtime-derived denominators guarded
- R12: Golden dataset regenerated if output changed
- R13: New interfaces work for 2+ implementations
- R14: No method spans multiple module responsibilities
- R15: Stale PR references resolved
- R16: Config params grouped by module
- R17: Routing scorer signals documented for freshness tier
- R18: CLI flag values not silently overwritten by defaults.yaml
- R19: Unbounded retry/requeue loops have circuit breakers
- R20: Detectors and analyzers handle degenerate inputs (empty, skewed, zero)
Adding New Components¶
BLIS has four extension types. Identify which type your change is, then follow the corresponding recipe. See docs/templates/design-guidelines.md Section 5 for full details.
| Extension Type | What It Is | Design Doc Required? | Example |
|---|---|---|---|
| Policy Template | New algorithm behind an existing interface | No | New routing algorithm |
| Subsystem Module | New module with its own interface and events | Yes | AutoScaler, P/D disaggregation |
| Backend Swap | Alternative implementation of internal module | Yes (covers both phases) | SGLang latency model |
| Tier Composition | Wrapper layering behavior on existing module | Recommended | NVMe KV tier |
Adding a New Model to defaults.yaml¶
When adding a new model configuration:
- Add an entry to the
defaults:section withGPU,tensor_parallelism, andvllm_version - Add an
hf_repofield mapping the BLIS model name (lowercase) to the case-sensitive HuggingFace repository path (e.g.,hf_repo: meta-llama/Llama-3.1-8B-Instruct). This enables--rooflineauto-fetch. Models without real HuggingFace repos (e.g., synthetic benchmarks) may omithf_repo— document why with a YAML comment. - If trained coefficients exist, add a corresponding entry to the
models:list
Policy Template (lightest — ~3 files)¶
- Implement the interface in the corresponding file (
sim/admission.go,sim/routing.go,sim/priority.go,sim/scheduler.go) - Register in
sim/bundle.go(valid names map +IsValid*function) - Add
caseto factory function - Add behavioral tests (
TestMyPolicy_Scenario_Behavior) - Update CLAUDE.md and README
Subsystem Module (heaviest — new interface + integration)¶
Requires a design doc defining the module contract (observes / controls / owns / invariants / events / extension friction). See design guidelines Section 5.3.
- Write design doc with module contract, event integration, state ownership, failure modes, default behavior
- Create implementation plan via
docs/templates/micro-plan.md - Implement interface + default implementation + factory
- Integrate into cluster event pipeline
- Add CLI flags with full validation
- Add behavioral tests + invariant tests
- Update CLAUDE.md, README, and design guidelines module map if needed
Backend Swap (two phases — extract interface, then add alternative)¶
Phase A (refactoring): Extract interface from hardcoded logic, verify existing tests pass unchanged. Phase B (extension): Implement new backend behind extracted interface, add configuration to select between backends.
See design guidelines Section 5.4 for the full two-phase recipe.
Tier Composition (delegation pattern — ~4 files)¶
- Implement the same interface as the inner module (Liskov substitution)
- Compose existing tiers using delegation pattern
- Update factory with validation
- Add CLI flags with validation (zero, negative, NaN/Inf guards)
- Aggregate metrics from all tiers
- Add conservation invariant tests
New Trace Record Type¶
- Define record struct in
sim/trace/record.go(pure data, nosim/dependency) - Add slice field to
SimulationTrace - Add recording method
- Hook into cluster event pipeline (
if cs.trace != nil) - Update
Summarize()aggregation - Add behavioral tests
Running or Contributing Hypothesis Experiments¶
Canonical source:
docs/process/hypothesis.md. If this section diverges, hypothesis.md is authoritative.
BLIS uses hypothesis-driven experimentation to validate system behavior, surface bugs, and document design tradeoffs. Experiments are organized into 6 families (workload/arrival, scheduler invariants, performance-regime, structural model, robustness, cross-policy comparative).
To run existing experiments:
Seehypotheses/README.md for the full list and coverage gaps.
To propose a new hypothesis: File a GitHub issue using the "Hypothesis Proposal" template. Include: the hypothesis sentence, family, diagnostic value, and rough experiment design.
To implement and run a new experiment:
Follow docs/process/hypothesis.md for the full process (Steps 0-10). Key phases:
1. Create worktree, classify hypothesis, design experiment
2. Design Review (5 perspectives) → convergence → human approval
3. Implement run.sh and analyze.py using shared harness (hypotheses/lib/)
4. Code Review (5 perspectives) → convergence
5. Run experiments, document FINDINGS.md
6. FINDINGS Review (10 perspectives) → convergence
7. Self-audit (6 dimensions), verification gate, commit and PR
Review protocol: Three review gates at different lifecycle stages, each using the universal convergence protocol (zero CRITICAL + zero IMPORTANT from all reviewers). External contributors without AI review infrastructure should submit their artifacts via PR — maintainers will run the review protocols. Only standard-library Python packages are needed (json, math, re, sys, pathlib).
| Document | Purpose |
|---|---|
hypotheses/README.md |
Existing experiments, coverage gaps |
docs/process/hypothesis.md |
Full process (Steps 0-10, three review gates) |
docs/process/convergence.md |
Universal Convergence Protocol (used by all review gates) |
docs/standards/experiments.md |
Rigor requirements (families, types, VV&UQ, RCV rules) |
docs/templates/hypothesis.md |
FINDINGS.md template |
Code Style¶
- Composition over inheritance
- Timestamp-based event ordering via min-heap
- Partitioned RNG per subsystem for deterministic isolation
- BDD-style test naming:
TestType_Scenario_Behavior - Conventional commits:
feat(scope),fix(scope),refactor(scope),test(scope),docs(scope)
Key References¶
| Document | What It Covers | When to Read |
|---|---|---|
CLAUDE.md |
Code architecture, file organization, CLI flags, compact rule/invariant tables | Always — authoritative for current codebase state |
docs/standards/rules.md |
20 antipattern rules with evidence, checks, enforcement | When reviewing or writing code |
docs/standards/invariants.md |
8 system invariants (INV-1 through INV-8) with verification strategies | When touching request lifecycle, KV cache, or metrics |
docs/standards/experiments.md |
Experiment taxonomy, rigor requirements, findings classification | When running hypothesis experiments |
docs/process/pr-workflow.md |
End-to-end PR lifecycle (worktree → plan → review → implement → audit → PR) | Before starting any PR |
docs/design/ |
System architecture, core engine, concepts glossary, configuration reference | When learning how BLIS works before contributing |
docs/templates/design-guidelines.md |
DES foundations, module architecture, extension framework | Before designing a new feature or extending BLIS |
docs/templates/micro-plan.md |
Template for single-PR implementation plans | When creating any PR implementation plan |
docs/templates/macro-plan.md |
Template for multi-PR feature expansions | When planning a large feature with multiple PRs |