Skip to content

(withdrawn — opened against wrong repo) - #650

Closed
tylerwagler wants to merge 420 commits into
antirez:mainfrom
tylerwagler:worktree-claude-harness-router
Closed

(withdrawn — opened against wrong repo)#650
tylerwagler wants to merge 420 commits into
antirez:mainfrom
tylerwagler:worktree-claude-harness-router

Conversation

@tylerwagler

@tylerwagler tylerwagler commented Aug 2, 2026

Copy link
Copy Markdown

Withdrawn — opened against the wrong repository by mistake. Apologies for the noise.

tylerwagler and others added 30 commits July 11, 2026 20:28
a569b5e froze the MoE tuning matrix to the release configuration; this
propagates those constants through the dispatch. write_gate_up==0 drops
the gate/up aux stores (and their kernel params/output pointers);
expert_tile_m==8, gate_row_span==1024 and down_row_span==2048 collapse
the tile/rowspan ladders; use_gate_row2048/use_atomic_down/
use_down_tile16/use_down_row2048 were all the same predicate and fold
into one use_big_batch. Nine unreachable kernels deleted (sorted/p2
gate+down, tile4 gate+down, gate row2048, down tile16 row32, down
rowspan template) plus the two orphaned block4 dot helpers; -970 lines.

Validated: sm_120f build clean, full model test exit 0 (all gates OK),
bench unchanged (prefill 449.6/419.0/413.4/408.2, decode within noise).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
… hot paths

Kills (semantic variants per AGENTS.md one-release-path rule):
- DS4_MTP_CONTINUOUS: opt-in non-bit-exact continuous depth-1 speculation
  (~105 lines), plus cont_anchor_row/valid session state and the
  cont-argmax-gap test that existed only to validate it.
- DS4_MTP_MIN_MARGIN: env override of the configured e->mtp_margin.
- DS4_MTP_BATCH_VERIFY: forced the near-greedy batch verifier under
  --quality/strict, violating the strictness contract.
- DS4_MTP_CAPTURE_PREFIX1: enabled the older no-replay partial path
  under strict; capture_prefix1 is now simply draft_n==2 && !strict.
- DS4_ROUTED_TOKEN_PARALLEL: force-on half of a dual-polarity pair;
  DS4_NO_ROUTED_TOKEN_PARALLEL stays as the single escape.

Hot paths no longer call getenv (surviving diagnostics read once):
- session.c: MTP_SPEC_LOG/PROBE/FULL_LOGITS/STRICT/TIMING/CONF_LOG/
  EXACT_REPLAY/FORCE_SNAPSHOT, DSPARK_STATS/DTREE_STATS/MIN_CONF ->
  gpu_graph_env_flag cached statics (PROBE/FULL_LOGITS were read every
  token, SPEC_LOG at six sites per spec cycle).
- moe.c: DECODE/PREFILL_PROFILE_DETAIL (per layer) cached.
- gpu_prefill.c/gpu_decode.c: LAYER/DECODE/INDEXER/Q stage profile
  gates cached; GPU_BATCH_EMBED_MIN parsed once.
- ds4_cli.c: DS4_MTP_SPEC_DISABLE read once per generation, not per
  token.

Kept as-is: load-time knobs (DIRECT_MODEL, PRELOAD_SPAN_MB,
PREFILL_CHUNK, PREFILL_SLICE already cached), per-request server reads
(generate.c), operational escapes (DSPARK_LEGACY_LOOP, MTP_SPEC_DISABLE,
DSPARK_DISABLE) and dump/replay tooling.

Validated: full model test exit 0 (all gates OK), bench unchanged
(449.6/419.0/413.4/408.2 prefill; decode within noise, two runs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…_MAX test plumbing

Both surfaced by post-commit review: the capture-prefix1 comment still
documented the deleted env escape, and test_local_golden_vectors still
saved/unset DS4_CUDA_MOE_TILE_MAX, gone since a569b5e.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
User decision 2026-07-12: the DSpark drafter is the production spec-decode
path and MTP will never be used; remove the whole subsystem (-1,744 lines).

Gone: --mtp/--mtp-draft/--mtp-margin across cli/agent/server/eval + help,
the mtp download_model.sh target, ds4_engine_has_mtp/_mtp_draft_tokens and
ds4_session_eval_speculative_argmax from the public API, the MTP engine
state (support-model load/map/preload/close, ds4_mtp_weights binder), the
draft functions (gpu_graph_eval_mtp_draft{,_from_hc},
gpu_graph_encode_output_head_mtp), the strict decode2 verifier
(gpu_graph_verify_decode2_exact), the MTP graph tensors + private raw
cache + mtp_n_raw, the prefix1 partial-accept capture machinery
(MTP-only: both DSpark verify calls pass capture=false), the per-session
mtp_logits/draft state, all DS4_MTP_* env gates, the mtp-verify-depth
test + helpers + DS4_TEST_MTP, and two orphaned leaves the compiler pass
surfaced (logits_top2, ds4_gpu_repeat_hc_tensor + kernel).

Kept (shared spec-decode infrastructure, used by DSpark):
spec_frontier_{snapshot,restore,free}, gpu_graph_verify_suffix_tops
(capture_prefix1 param dropped; timing gate renamed
DS4_SPEC_VERIFY_TIMING), gpu_graph_read_spec_logits_row, spec_logits,
spec attn/index frontier scratch (now gated by enable_spec ==
dspark_ready), gpu_graph_encode_output_head_batch, argmax kernel.

Validated: sm_120f build clean (no unused-function leaves), full model
test exit 0 all gates OK, bench identical to pre-strip
(prefill 449.0/418.1/411.9/407.2, decode 15.62/15.52/15.48/15.33),
grep residue zero.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
… detect

gguf-tools/merge_dspark_gguf.py splices the drafter's dspark.* tensors +
the deepseek_v4_dspark.embedding_length / dspark.target_layer_ids.* kv
into the main model (main blob byte-identical, drafter tensors appended
32-byte aligned). Engine auto-enables DSpark when dspark.main_proj.weight
is present: dspark_model aliases the main model by value, teardown and
the external map/cache plumbing are gated on a new dspark_external flag.
--dspark PATH still loads a split file; new --no-dspark opts out and the
tensor-span cache now skips a disabled drafter's dspark.* spans
(skip_prefix param on accelerator_cache_model_tensors).

The CLI greedy path now consumes ds4_session_eval_speculative_block
(same toks[] chunk plumbing the server uses), so ds4 gets the DSpark
speedup at temperature 0. ds4-bench (plain-decode baseline by design),
ds4-eval (probe headroom) and ds4-agent (per-token DSML/forcing loop
cannot consume speculative chunks yet) set dspark_disable.

Artifact: gguf/ds4flash-reap25compact-mxfp8head-dspark-v1.gguf (86.7 GB,
1328+78 tensors, 67+4 kv). Validated: full model test exit 0 on the
merged file; server A/B merged-auto vs split --dspark byte-identical
output at equal t/s (20.6 warm, essay prompt); CLI greedy smoke 31.7 t/s;
plain bench baseline-identical with 70.45 GiB cached (drafter spans
skipped). model.gguf symlink now points at the merged artifact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
A stale deepseek4-quantize binary (built 07-02, before the 4fbeb3e MXFP4
sign fix) kept producing sign-stripped donors while make test rebuilt and
passed test_quants against the fixed sources. Tie the binary into the
test target so a green test run implies a current binary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…allocator

New all-Q2_K expert combo for the mid quant rung (between IQ2_XXS/Q2_K
cheap and lossless MXFP4 rich): binder accepts gate/up/down all-q2_k, and
routed_moe_launch dispatches three Q2_K gate/up kernel clones (plain
qwarp32 for decode, tile8_row32 and rowspan<1024> for sorted prefill
tiles) built on the same production-validated dev_dot_q2_K_q8_K_block/
block8 helpers every down kernel uses. The IQ2 decode-LUT kernel stays
IQ2-only.

Validated: temp/q2kgate_test.cu oracle (synthetic experts, independent
f64 host reference) passes all three dispatch paths at n_tokens=1/32/160
with max |diff| <= 2.6e-5; full model test on production exit 0 (no
regression — production has no q2k-gate layers).

prisma_alloc.py: adds the mid preset and generalizes the exact 0/1 DP to
a grouped per-layer choice (cheap|mid|rich) fed by a second --kl-mid
measurement file; mid benefit is capped at the rich benefit (subset
improvement). Binary solves reproduce exactly when --kl-mid is absent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…ntizer

The deepseek4-quantize --output flag already re-encodes output.weight;
document it in the pipeline recipe so the temp/splice_mxfp8_head.py
post-step is no longer needed for new builds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…A header

quality-score still linked the pre-restructure ds4.o/ds4_cuda.o monolith
objects; point it at the split engine/cuda TUs (built via ds4-bench) and
add -lcublasLt. collect_official.py sends a User-Agent so Cloudflare-
fronted OpenAI-compatible gateways (error 1010) accept it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Three nondeterminism sources removed so speculative verify and plain
decode see stable numerics:

- indexer compaction: the comp_rows atomicAdd slot race made the float
  accumulation order of indexed attention depend on warp scheduling.
  Replaced with an order-preserving Hillis-Steele scan in smem; costs
  nothing vs the racy version (decode 15.66/15.30/15.29/15.15 t/s at
  2k..8k, within noise of the pre-change baseline on the same box).
- MoE big-batch down: per-token atomicAdd accumulation replaced with
  per-pair stores + the fixed-order moe_sum pass (zero_kernel retired).
- cuBLASLt fp8 GEMMs: split-K reduction schemes masked off in both
  cached-handle sites; NONE-scheme algos accumulate in a fixed order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The drafter previously disabled itself whenever temperature > 0, so the
sampling recipes everyone actually uses got zero speedup. Rebuilt the
speculative path around exact sampled acceptance:

- accept draft token d with probability p_filtered(d); on rejection,
  sample from the residual distribution (p excluding d, renormalized).
  Greedy is the point-mass special case of the same walk.
- new ds4_sample_dist API in the tokenizer mirrors the plain sampler's
  filtering exactly (temp scale, top-k, min-p floor, top-p nucleus incl.
  the crossing candidate), so spec and plain draw from identical
  distributions.
- carry-token architecture: sampling is owned inside the speculative
  block; the bonus token from a fully-accepted draft is carried into the
  next call and dropped if sampling params change mid-stream.
- snapshot-restore hygiene: load_payload now resets the spec carry, the
  pending drafts, and the drafter context ring -- stale draft state
  across restores was shaping batch sizes and leaking rounding
  differences into the output (the run-to-run flakes).
- server + cli generation loops rewired onto the new
  ds4_session_generate_speculative at every temperature.

Validated by a statistical oracle (2500 trajectories x 2 paths, chi2
clean at p~.001 per position), 24/24 greedy prefix agreement with plain
decode, and a byte-stable double run of the full test suite. Sampled
spec decode at temp 0.95/top-p 0.38: 16.34 t/s vs 14.75 plain (+11%).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Code review of 27376c8 found the carry surviving session-state advances
it wasn't conditioned on, letting a token drawn from a PREVIOUS request's
distribution be committed unverified as the first token of the next one:

- cross-request: a generation truncated by max_tokens or a stop string
  leaves the carry armed; the next request hits the prefix cache, sync
  extends the checkpoint, and the stale carry is emitted.
- mid-decode: the think-close recovery injects a token via plain eval;
  the pre-injection carry then fires right after the forced token.

Fixes, layered: the carry is stamped with the checkpoint length it was
drawn at and refused if the position moved; sync drops it uncondition-
ally (a same-length full rebuild defeats the stamp); plain eval drops
it; load_payload drops carry + pendings at entry so a mid-restore
failure can't leave them armed.

Regression test temp/carry_stale_test.c reproduces both scenarios at
greedy (PASS with fix); full test suite + spec sampling oracle re-pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
A generation that hits the token cap (or a stop) inside its think block
never emits </think>. The non-stream parse path returned the entire raw
text -- reasoning included -- as visible content in that case, handing
raw chain-of-thought to any client that displays or scores the answer
channel. The streaming path already promised the opposite.

This was found the expensive way: it silently gutted the tool-eval-bench
accuracy legs (41/300 MMLU answers unextractable, IFEval strict rate
~10% vs a class norm of ~80) because long-thinking responses truncated
mid-think and were scored on their reasoning dump. Both parse sites are
fixed -- the thinking-mode branch (which also covers the prompt-pre-
opened <think> form) and the generic splitter -- and the regression test
pins content=="" + full text on reasoning_content for both forms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The v5mx prisma allocation promotes gate/up and down independently per
layer, so the artifact mixes iq2_xxs and mxfp4 across roles (mxfp4 gate/up
with iq2_xxs down and vice versa) alongside the existing uniform combos.
Loading it required two things:

- weights.c combo validation goes per-role: gate/up must be a matching
  pair (the fused gate+up kernels assume one format) in
  {iq2_xxs, q2_k, mxfp4, cutlass_mxfp4}; down independently in
  {iq2_xxs, q2_k, mxfp4}; combos may differ per layer. CUTLASS stays
  all-three (the grouped GEMM runs the whole expert FFN in one dispatch).

- ds4_cuda_moe.cu grows the missing IQ2_XXS down family, cloned from the
  Q2_K down kernels on the production-validated iq2 dot helpers:
  qwarp32 (generic), sum6 decode (smem-LUT dot, direct final-row store),
  tile8_row32, and tile16_row2048 big-batch with per-pair stores + the
  fixed-order moe_sum pass (no atomicAdd -- numerics stay run-to-run
  deterministic). routed_moe_launch now selects gate/up and down kernels
  independently from the per-layer tensor types passed in by the engine
  (no new hot-path env/flag branching); sorted expert tiles are built
  whenever at least one stage consumes them, and mxfp4 stages keep their
  per-pair qwarp32 kernels.

Validated: temp/iq2down_test.cu oracle on REAL iq2_xxs down experts
extracted from the v5mx artifact (synthetic q2_k gate/up => also covers
mixed-combo dispatch), independent f64 host reference with a bit-exact
host replica of the GPU q8_K mid requant; all four down dispatch paths
(n_tokens=1/e6, 1/e4, 32, 160) pass with max |out diff| <= 1.4e-5
(bound 2e-3). ds4_test --server passes; the v5mx artifact (previously
rejected at load) now loads and generates coherently at 17.8 t/s decode
with the DSpark drafter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
- Model Weights: per-layer, per-role expert combos (gate/up matching
  pair and down independently in IQ2_XXS/Q2_K/MXFP4; CUTLASS layout
  all-or-nothing), prisma measured-allocation RC + all-Q2_K fallback,
  byte-lossless MXFP4 re-encode, REAP expert pruning, merged DSpark
  drafter, deepseek4-quantize --format-map pointer.
- New "Speculative decoding (DSpark)" section: MTP removed, merged-GGUF
  auto-enable / --no-dspark / --dspark, exact sampled acceptance at all
  temperatures (output distribution identical to plain sampling), and
  run-to-run deterministic decode/prefill numerics.
- Model residency: current ~87 GB REAP-pruned build figures; note the
  default DS4_ATTN_PACK packed comp cache and DS4_IDX_FP4 MXFP4 indexer
  cache.
- Server: release scope (one live session, contexts up to 1M tokens),
  64-connection cap + 30 s request-read deadline, unterminated <think>
  always returned as reasoning, never visible content.
- More Documentation: link gguf-tools/prisma/README.md and
  docs/MODEL_CARD.md; drop the stale 26GB/22GB 1M-context KV figures
  that predate the packed caches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Near-term (v1.x): temperature-matched p/q draft sampling with fused GPU
verify, fused K-row accept kernel, online drafter confidence calibration,
agent-loop speculative decoding, and the final measured-allocation release
artifact plus scoreboard. Longer-term: multi-session serving, MXFP6
tensor-core paths, MoE verify-microbatch routing audit, CPU-path retirement,
and upstreaming prisma pipeline fixes to PrismaQuant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…rter/

Absorb the last converter/ capability into deepseek4-quantize: routed-expert
tensors can now target cutlass_mxfp4 (GGUF type 40, the tensor-core CUTLASS B
layout) via --experts/--routed-w*/--format-map, packed in one pass straight
from the QAT E2M1+E8M0 safetensors source — byte-lossless (data verbatim,
E8M0 scales placed by the Blackwell 128x4 SF tile swizzle, same formula as
the engine's mx_sfoff and CUTLASS Sm1xxBlkScaledConfig SFB).

- quants.h/quants_common.c: repurpose the unused NVFP4=40 slot as
  DS4Q_TYPE_CUTLASS_MXFP4 (matches the engine's DS4_TENSOR_CUTLASS_MXFP4);
  not a fixed-stride row format, sized via ds4q_cutlass_mxfp4_bytes()
- quants_fp.c: ds4q_pack_cutlass_mxfp4 + SF sizing helpers
- dsq_generate.c: expert-path lossless pack branch, per-expert/tensor sizing,
  regular-tensor rejection; dsq_names.c: allow type 40 as a planned target
  (also lets a type-40 template like gguf/dspark.gguf load for --compare)
- test_quants.c: sizing ground truth (real dspark expert blob), data-verbatim
  and SF-swizzle injectivity/round-trip invariants
- Makefile test-cutlass-mxfp4: byte-compares quantizer output against the
  packer-produced gguf/dspark.gguf on gate+down (both N/K orientations);
  verified byte_compare: OK (identical fnv1a64) for both

Retire converter/ (all three superseded):
- splice_cutlass_mxfp4.py: replaced by the native type-40 path above;
  prisma_alloc.py rich preset now emits CUTLASS_MXFP4 manifests directly
  (same 4.25 bpw, DP budget math unchanged), no post-step splice
- dspark_convert.py + p0conv.py: drafter pipeline is now
  build_dspark_template.py -> deepseek4-quantize --experts cutlass_mxfp4
  -> merge_dspark_gguf.py (type-40 experts byte-identical; FP8/F32 tensors
  value-lossless per test_quants idempotency, same precedent as the MXFP8
  head absorption in b2bf7ec)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Replace the upstream antirez/deepseek-v4-gguf targets (old imatrix
filenames, PRO variants we do not ship) with the ds4 release repo and the
measured-allocation v5mx build. Single self-contained artifact (merged
drafter), Xet-aware hf download with curl fallback, token handling
unchanged. Repo is private pre-release; downloads need a token until
public.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Single ptr-array grouped launch to replace the per-expert CUTLASS
dispatch loop + blocking offset readback in prefill; available in the
SM120 blockscaled builder at the current CUTLASS pin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The measured-allocation build won the quality bake-off (92/100 vs 84/100
all-Q2_K) and shipped as ds4flash-v5mx-reap25-mxfp8head-dspark-v1.gguf
(91 GB). Update the README to match:

- Model Weights: state the measured build as the shipped release, not a
  candidate under evaluation; drop the all-Q2_K fallback framing; add the
  recommended sampling (temp 0.95 / top-p 0.38).
- download_model.sh description: rewrite for the single `v5mx` target and
  our now-public Elytron Defense HF repo (filename + size + sha256); drop
  the stale upstream q2/q4/pro-imatrix commands and antirez source.
- Speed: replace the TODO placeholder with the measured GB10 table
  (prefill ~420→390 t/s, decode 16.5 t/s flat, DSpark accept α=77.2%).
- Correct the ~87 GB residency figures to ~91 GB.
- Soften the intro PRO claim to an opportunistic target; mark the
  measured-allocation release roadmap item as shipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Attribution rides on repo ownership + links, not free-text org claims.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Drop the upstream multi-size-class / PRO-on-big-machines opportunism
(512 GB, other size classes) that doesn't match this fork's deliberate
one-model, one-box scope. Remaining PRO references are factual (model
family, API aliases, tool-format spec location), not scope claims.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The --power throttle will be stripped from the binaries post-release;
drop its documentation now so the README doesn't advertise a going-away
knob.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The native agent is out of scope for this release and untested after the
CUDA-only cleanup edits. Remove it from the default build targets and the
test suite, and strip its README section + status note. Source and the
standalone `make ds4-agent` rule are kept; it stays a longer-term
roadmap item, just not a v1 deliverable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
- Single build target (cuda-spark / sm_120f); drop the generic-CUDA and
  explicit-arch targets and their docs. This is a GB10-only fork.
- Drop ds4-eval from the default build, test suite, and docs (out of
  scope for this release, like ds4-agent). Both keep their source and a
  standalone `make <name>` rule; neither is a v1 deliverable.
- Remove the Motivations section and the intro's 'integrated coding
  agent' framing — the native agent is deferred, so the README no longer
  advertises it as a shipped goal.

Shipped binaries: ds4, ds4-server, ds4-bench.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The HTTP API server is the product. The ds4 CLI joins ds4-bench,
ds4-eval, and ds4-agent as a development tool: source kept, buildable
by name, not in the default build. `make cuda-spark` now builds just
ds4-server.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…ver)

The release surface is now just ds4-server (the OpenAI/Anthropic-compatible
HTTP API). ds4, ds4-bench, ds4-eval, and ds4-agent are development tools:
source kept, buildable by name, not shipped and not built by `make cuda-spark`.

README:
- Intro reframed around ds4-server as the product; offline/dev tools noted.
- Build/run examples center on `./ds4-server -m ds4flash.gguf --ctx ...`;
  "default model path used by both binaries" -> ds4-server; dropped
  `./ds4 --help`.
- Removed the standalone CLI section (`./ds4 -p`, interactive `ds4>`).
- Removed the Benchmarking (`./ds4-bench`) section; kept the prefill-chunk
  guidance as its own section pointing at the server `--prefill-chunk` flag.
  Speed numbers untouched.
- Added a "Development tools" subsection under Building.
- Steering: added the ds4-server `--dir-steering-*` server form.
- Recommended-sampling note and spec-decode text de-CLI'd (API temperature/
  top_p, temperature 0).
- Test Vectors / Debugging Notes: kept `--dump-*` probes but labelled them the
  ds4 dev CLI; led Debugging with `ds4-server --trace`.

download_model.sh:
- Post-download help now points at `./ds4-server -m ds4flash.gguf --ctx 100000`
  instead of `./ds4 -p "Hello"`. v5mx target + ds4flash.gguf symlink unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
tylerwagler and others added 28 commits July 27, 2026 18:12
… (transliteration; numerics verbatim) — engine host phase fully C++

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…x gguf_types array designators

The harness #includes every server TU, so the server converts atomically:
renames, explicit casts at void* boundaries, four goto-crossing declarations
split into decl+assign (semantics identical — the variable is simply
uninitialized at the label, as in C), remaining 'namespace' locals/params
renamed tool_namespace, two test designated-initializer order fixes.

Also: gguf_types converts from C array designators (not valid C++) to
positional rows with a static_assert pinning the id mapping — the designator
form had never actually compiled as C++; a stale C-era object was masking it
(caught by a forced clean host rebuild, which is now the standard check).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…ent TUs, ds4_help, ds4_agent_test harness -> C++

Vendored src/vendor/*.c (linenoise, rax) intentionally stay C. Mechanical
fixes: explicit casts at void* boundaries, {0} -> {} where the first member
is an enum, hardmode-eval .choice[i] designators -> positional lists,
ds4_bench engine-options designators reordered to declaration order,
agent-internal header gains extern-C guards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…l tree one language)

Keyword-safe pass: 31 identifier uses of 'new' renamed to 'newn' (comments/
strings untouched), the rax*rax type/variable shadow goes through 'struct rax',
one goto-crossed decl split, casts at rax_malloc/realloc boundaries, one
string-literal char* constified. linenoiseEditInsert — deliberately absent
from linenoise.h — gets explicit extern "C" so the agent editor keeps
linking. Vendor Makefile rule moves to CXX.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…C++ linkage

Removes the extern "C" blocks from all nine headers (ds4.h, ds4_gpu.h,
engine/server/agent internals, help, kvstore, rax, linenoise) and strips the
explicit extern "C" from all ~150 .cu API definitions (nvcc and g++ share
the Itanium ABI, so host<->device TU linkage needs no C bridge). The twelve
model-gate harnesses under tests/ move to C++ with the same mechanical fixes
(casts, two goto splits, bank_fork's &(uint64_t){7} compound literals become
one shared greedy seed — greedy never consumes RNG state). The
prefill-gate baseline flow ships the .cpp gate source into its baseline
worktree. linenoiseEditInsert loses its C-linkage pin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…es (Dot8 traits)

The remaining family variants (iq2 sum6/tile8/tile16, gate_up_mid iq2 tiles)
are NOT clones — they deliberately use the smem-LUT decoder and, in tile16,
a restructured midq staging order — so they stay separate kernels. This
completes the collapsible set flagged in the kernel-phase review (~180 lines
of hand-cloned duplication removed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…riptor slots

The slot/session/bank overload is gone: struct server owns THE session
(classic mode == 1 bank-less slot over it, pool mode == pool_banks banks);
session_slot loses its sess pointer (a bool provisioned carries the
provisioned/evicted idiom) and the classic-mode multi-independent-session
provisioning + its eviction/free paths are deleted (~250 net lines). A
classic-mode request needing a second slot now queues for slot 0 — the
pre-pool single-session behavior. Pool-mode bank switching, eviction,
spill/restore and warm-fork routing are unchanged.

Refactor executed by a subagent to spec; reviewed. Approved by Tyler
2026-07-23 as the port-window structural cleanup (plan-70 adjacent).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…inaries, strings, docs

7,167 ds4_->pulsar_ + 3,761 DS4_->PULSAR_ identifier renames (string-literal-
aware lexer; comments included), 25 file renames, binaries pulsar{,-server,
-bench,-eval,-agent,_test,_agent_test} with transitional make aliases +
symlinks. Brand strings (log prefixes, banners, usage text, dump-JSON
source, /version engine) rebranded; docs rebranded.

Compatibility surfaces deliberately unchanged: all 117 getenv("DS4_*")
names, /tmp/ds4.lock (old+new binaries must contend on ONE lock or the GB10
OOMs), ds4flash.gguf refs, dspark.* GGUF keys, KVC/DS4KLRF1/DS4PFXG1 disk
magics, kv-cache directory paths, +DWARFSTAR_* protocol markers, and
comments where DS4 names the DeepSeek-V4 architecture.

Rename executed by a subagent to spec; reviewed. Note: cuda-prefill-gate-
baseline can no longer build against pre-rename refs (harness includes
pulsar.h) — rebaseline on next use.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
… (23 methods)

The struct+verb families become real classes in the engine-internal header
(static factories create/open, destroy() for the teardown verbs, const where
the free decls were const). Method bodies are the original bodies behind an
'auto *s = this;' alias — zero transcription of numerics code. The public
boundary (pulsar.h) deliberately keeps the opaque-handle free-function API:
76 one-line facades in engine_api.cpp, each preserving the original
null-object path OUTSIDE the member call (a member call through NULL is UB
that -O3 null-check elision would exploit). 28 engine-internal call sites
converted to method calls. Three verbs stay free functions where a method
would collide with a same-named public field (noted in the header).

Refactor executed by a subagent to spec; reviewed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
… + make seam-check

pulsar_gpu.h now states the seam contract (engine talks only pulsar_gpu_*;
legacy DS4_CUDA_* env NAMES grandfathered as ops compat; backend-branded
logs come only from backend TUs). tools/seam_check.py enforces it: strips
comments/strings, then flags any CUDA include/API identifier/launch syntax
outside src/cuda — wired into 'make test'. 73 host files verified clean,
so Metal/ROCm can return as src/<backend>/ implementing this header with
zero engine changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…multiseq TUs

session.cpp 4722 -> 1510 lines; session_spec.cpp (1536: speculative loop +
yield-quench family), session_payload.cpp (969: payload/snapshot codec),
session_banks.cpp (557: 18 bank_* methods + carries), session_multiseq.cpp
(162). Move-only (bodies verbatim); the only two cross-TU statics
(payload_set_err, spec_quench_reset) are promoted to the internal header.
No file-scope data statics moved, so initialization order cannot matter.

Refactor executed by a subagent to spec; reviewed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
generate.cpp 4026 -> 202 (keeps the cross-TU request-text helpers);
server_sched.cpp (1682: worker/quantum lanes, provisioning, eviction,
spill guard, bank switching), server_jobs.cpp (2055: gen_* state machine,
prefill progress, tool-checkpoint canonicalization, DSML recovery);
cli_main.cpp 6261 -> 1302 with the ~130 embedded unit tests extracted to
server_tests.cpp (PULSAR_SERVER_TEST-guarded; empty object in the server
link, verified 0 symbols). Move-only, proven by sorted-line diff; struct
gen_state promoted to the internal header (the scheduler lanes drive job
phases directly — field-level encapsulation would be a rewrite, not a move).

Refactor executed by a subagent to spec; reviewed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…n, gate fidelity tests on pruned models

test_thinking_checkpoint_remember_gate still asserted the pre-openwebui-fix
rule (has_tools disqualifies); the implementation deliberately made
prompt_preserves_reasoning the sole gate — the test now pins the current
contract. The official-logprob-vector suite and think-tool-recovery's
content assertions are recorded against the FULL reference model; the
production REAP25 model legitimately diverges, so a new
pulsar_engine_is_pruned() accessor gates them: vectors skip loudly, the
recovery test keeps its machinery assertions (decode/end-marker/parse) and
skips only the exact-tool-choice/phrasing checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
… guarded default-ON

Implements the condition the opt-in revert set for re-flipping: fusing is
refused while >=2 active decoders' summed committed depth exceeds
DS4_MIXED_DEEP_GUARD_ROWS (default 16384 — the measured-neutral boundary;
4x8192=32768 was the -29% pp / -48% tg regime). Regimes (a) shallow-neutral
and (b) one-big-prefill-over-shallow-decoders keep the fused win; regime (c)
now takes the classic path automatically with an edge-logged pause notice.
Default flips to ON in pool mode (DS4_MIXED_BATCH=0 opts out); pending teb
--perf-only A/B verification before this lands on pulsar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…embers

The server_*/worker_* free-function family (103 fns: 95 mutating + 5 const
readers + the 3 wrapped-signature trace_* helpers) becomes struct server
member methods, mirroring the pulsar_session/pulsar_engine class-ify (2ba96c3).
Bodies are byte-verbatim behind an 'auto *s = this;' alias; the type-name
prefix is dropped from method names (server_bank_switch -> s->bank_switch),
worker_* names kept. Const readers become const methods.

served_model_id / served_model_name stay FREE functions (each collides with a
same-named struct server data field, the documented precedent exception);
worker_main stays free (pthread void* entry).

Header: 103 member decls added; family forward-decls removed; three types
referenced by pointer-params but defined after struct server are forward-
declared before it (struct job / server_prefill_progress / thinking_state /
trace_cache_diag, the last three tagged on their anonymous typedefs), and the
provision_refusal enum is relocated ahead of the struct. Byte-preserving; no
numerics or behavior change.

Gate: make pulsar-server (clean compile+link, zero warnings) + make seam-check
(80 host files CUDA-clean). GPU numerics gates N/A (no numerics touched).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The agent_bash_* verb family (bash_jobs.cpp: 8 static helpers + the public
agent_bash_observation) becomes struct agent_bash_job member methods, same
pattern as struct server / pulsar_session. Bodies byte-verbatim behind an
'auto *job = this;' alias; agent_bash_ prefix dropped from method names
(agent_bash_poll -> job->poll()); the three const readers (display_lines,
read_head, read_tail_lines) become const methods. No field-name collisions.

Header: 9 member decls added to struct agent_bash_job; the lone free decl
(agent_bash_observation) removed. No forward-decl needed (agent_worker is
defined earlier). Only external call site (tool_dispatch.cpp) converted.

Gate: make pulsar-agent + pulsar_agent_test (clean compile+link, zero
warnings) + make seam-check. No numerics/behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…-> members

The pulsar_vocab verb family in tokenizer.cpp (8 static bpe_/tokenize_/
special_/lookup helpers + vocab_load/vocab_free/tokenize_rendered_chat_vocab/
dump_tokens) becomes struct pulsar_vocab member methods, same pattern as the
prior class-ify commits. Bodies byte-verbatim behind an 'auto *vocab = this;'
alias; the eight read-only helpers become const methods. Names kept verbatim —
none carry the pulsar_vocab type-name prefix, and a bare 'free' would shadow
libc free() used inside the bodies. No field-name collisions.

dump_tokens_fp stays a free function (takes vocab as its SECOND arg, not a
natural receiver). pulsar_engine::dump_tokens now calls e->vocab.dump_tokens()
directly instead of the ::-qualified global (stale comment updated). All
internal cross-calls and the session.cpp external sites (&vocab / &e->vocab)
converted.

Header: 11 member decls added to struct pulsar_vocab; four free decls removed
(dump_tokens_fp kept); owned_str (bpe_rank param, defined after the struct) is
tagged on its anonymous typedef and forward-declared before the struct. Pure
host tokenization — NO kernels/numerics touched; token IDs unchanged (bodies
verbatim).

Gate: make pulsar-server + pulsar + pulsar-agent (clean compile+link across the
engine, zero warnings) + make seam-check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Adds pulsar::FileHandle (src/lib/pulsar_file.hpp), a move-only RAII owner for a
C FILE* with an explicit close() that returns the fclose status and disarms the
destructor. The destructor is a leak safety-net for early-return paths.

Applies it to write_f32_binary_file / read_f32_binary_file (util.cpp), which
both had a real leak: `ok = n_io == n && fclose(fp) == 0` short-circuits past
fclose on a short read/write, leaking fp. Now fp.close() is short-circuited the
same way but the FileHandle destructor closes it — no leak. The success-path
fclose-error check is preserved exactly (close() returns the status).

First RAII increment. The higher-value / higher-risk RAII (GPU-tensor teardown,
mutex guards) changes resource LIFETIMES and needs the GPU runtime/teardown
gates to verify — deferred to a server-down window.

Gate: make pulsar-server + pulsar + pulsar-agent (clean, zero warnings) +
make seam-check (81 host files CUDA-clean). Behaviour-preserving except the
leak fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
… class-ify

The struct-server class-ify (cf207fb) mis-converted the value-fixture call
sites in server_tests.cpp — `method(&s, args)` became `&s->method(args)` (address
of a call rvalue, `->` on a value) instead of `s.method(args)` — and left the
free-function call in pulsar_test.cpp (chat_think_tool_recovery(&srv, ...))
unconverted. These slipped the original gate because server_tests.cpp is wrapped
in #ifdef PULSAR_SERVER_TEST, which is defined ONLY by tests/pulsar_test.cpp;
the pulsar-server build compiles that TU empty, so `make pulsar-server` never
saw the errors. Building pulsar_test (the server-test runner) surfaced them.

Fix: `&VAR->method(` -> `VAR.method(` across server_tests.cpp value fixtures
(s/src/dst/srv), and srv.chat_think_tool_recovery(...) in pulsar_test.cpp.
Test-only files; no shipped-code change.

Gate LESSON: `make pulsar_test` (with PULSAR_SERVER_TEST) must be part of the
class-ify gate, not just `make pulsar-server`. Compile-clean now; runtime run
pending an sm_120f build (plain make targets sm_75, engine won't open on GB10).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…uard elision)

The class-ify (cf207fb) turned several NULL-tolerant free functions into
server:: methods. Those functions had an internal `if (!s) return` guard
because callers legitimately pass a null server (parse-without-server and the
streaming unit tests, which pass s=NULL). As members, `s == this`, and at -O3
the compiler assumes `this` is non-null and ELIDES the `!s` guard entirely —
so `s->method()` on a null server dereferences null and SIGSEGVs. Caught by
running pulsar_test on the GB10 (sm_120f); invisible to compile+seam and to
the pulsar-server build (production always passes a real server, so the
shipped binary never hit it — this was test-reachable UB).

Two correct fixes, depending on where the guard can live:
- CALL-SITE guard when the receiver is a normal pointer (not `this`):
  parse_chat/anthropic/responses_request wrap the tool-memory restore/attach
  pair in `if (s) { ... }` and gate the validate_* calls with `s &&`. A guard
  on a normal param is NOT elided.
- REVERT TO FREE FUNCTION when the method itself is invoked with null `this`:
  anthropic_tool_stream_id / openai_tool_stream_id are called with s=NULL from
  the stream path, so an internal guard would always be dead. Restored to
  `static ... (server *s, ...)` (their original form) where `if (!s)` holds;
  removed their method decls from struct server.

RULE: a NULL-tolerant function must not be a non-static member — the `!s`
guard is unreachable under -O3. Keep it free, or guard outside the call.

Gate: full pulsar_test suite GREEN at runtime on GB10 (sm_120f) — 15/15
including server/tool-call-quality/api-sampling; pulsar-server clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…y, tail_capture, syntax

Completes the method-ification: the remaining small struct+verb families become
member methods (same pattern as the big three).
- thinking_state (server): tail_ends_with() const + feed()
- pulsar_bank_carry (engine): copy() + free_one()
- agent_tail_capture (agent): append() + take()
- agent_syntax (agent): line_comment() const + match_keyword() const

Each has a NULL-tolerant verb (a bare `if (!obj)` guard) — the exact -O3
elision hazard from 260ffbc. VERIFIED SAFE before converting: every receiver is
provably non-null at every call site (agent_syntax_for_lang always returns
&agent_syntaxes[0]; chat_think_tool_recovery derefs thinking->inside with no
null check; bank carry is &s->bank_carry[i]; append is called under
`if (r->capture)`). The dead `!obj` guards are kept verbatim in the bodies but
never fire. Test-file call sites (server_tests.cpp, pulsar_test.cpp thinking_*)
converted too (the 08885a1 lesson).

Gate: all 5 binaries + seam-check clean; FULL RUNTIME suites GREEN on GB10
sm_120f — pulsar_test 15/15 (server: OK) AND pulsar_agent_test ok.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…fe single-bracket sites

Adds pulsar::ScopedLock (src/lib/pulsar_lock.hpp): locks a pthread_mutex_t on
construction, unlocks on scope exit, so an early return cannot leak the lock
(missed-unlock deadlock). Replaces the hand-paired lock/unlock idiom, which is
correct today but one forgotten unlock away from a hang.

Adopted ONLY where a lock brackets a clean lexical scope — verified per function:
- kv_cache.cpp kv_tool_map_write: the most fragile site (one lock, THREE unlock
  paths incl. two early returns) -> one guard, unlocks vanish.
- tool_memory.cpp: 9 single-lock live-tool-state / has-id / snapshot functions.

Deliberately NOT converted (a scope-end guard would be wrong): functions that
unlock EARLY to do expensive work outside the lock (e.g. kv_cache.cpp:588's
xstrdup-then-process), multi-bracket unlock/relock functions, and the
cond_wait worker/drain loops (http_server, cli_main, server_sched) — those need
their own careful pass. Behaviour-preserving: same mutex, same acquire point,
release at the same scope end the manual unlock already sat at.

Gate: pulsar-server + pulsar_test compile clean, seam-check (82 files). Runtime
verification next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…r + drain)

Extends ScopedLock (aee3cf7) to the two cond_wait sites that are clean single
brackets: the worker_main wait-for-work loop (server_sched.cpp) and the
shutdown client-drain (cli_main.cpp). Each is `lock; while(!cond) cond_wait;
[read state]; unlock` with NO early return between — cond_wait is RAII-safe (it
atomically releases+reacquires the held mutex), so a nested-scope guard is
behaviour-identical. The worker case hoists `quit` out so it's read under the
lock then acted on after the guard closes.

These had no missed-unlock leak to fix (already clean) — this is consistency,
not a bugfix. NOT converted: http_server.cpp:513 — it has the leak-prone
early-exit pattern BUT destroys j.mu right after unlock, so a scope-end guard
would destroy a still-held mutex; it needs a restructure, left as-is.

Gate: compile clean + seam-check, and LIVE server verification — a real
/v1/chat/completions request drove the worker cond_wait wakeup (32 tok @ 20 t/s)
and clean SIGTERM shutdown drained with no deadlock (exit in 2s).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…gle teardown

client_main held j.mu across enqueue + the wait for the worker's completion,
then unlocked and pthread_mutex_destroy'd it — with the teardown (destroy cv,
destroy mu, request_free) DUPLICATED on the enqueue-fail and enqueue-ok paths,
and a goto out of the locked section. A plain ScopedLock could not help because
the destroy must follow the unlock; and the duplicated cleanup is exactly the
kind of thing that rots when one copy is edited.

Restructure: hold j.mu in an inner { ScopedLock } scope spanning enqueue + the
cond_wait loop; the guard releases at scope close, THEN the single shared
teardown runs once. `enqueued` carries the branch out so the 503 error is
emitted after unlock. Behaviour-identical on both paths; the ~12 early goto-done
exits (before the mutex is even init'd) are untouched. Removes the duplicate
teardown and the lock leak-surface.

Gate: compile clean + seam-check; LIVE server battery on the new binary —
/health + /v1/models (early-goto paths) 200, a real completion drove the
restructured enqueue/wait/cleanup path (finish=length), a 2nd request 200, and
clean SIGTERM shutdown drained with no deadlock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…ar-*

Completes the DwarfStar/ds4 -> Pulsar rename. Removes the Makefile transitional
alias targets (ds4/ds4-server/ds4-bench/ds4-eval/ds4-agent/ds4_test/
ds4_agent_test, each of which symlinked to its pulsar-* target) and the ds4
names from `clean`, and updates the ops/bench/profiling scripts to invoke
./pulsar-server and ./pulsar-bench (including the pgrep "already running"
guards). The DS4_* runtime env vars are a separate, larger surface and are
left untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Completes the DwarfStar/ds4 -> Pulsar rename by renaming every DS4_* env var to
PULSAR_* across the engine/server/cli/agent code (getenv reads + the diagnostic
log/error strings that name them), the Makefile gate targets, tests, ops/bench/
profiling scripts, and docs. 169 getenv reads, ~120 distinct knobs. NO backward-
compat shim: the old DS4_* names are gone; set PULSAR_* instead.

Excluded (deliberately, not env vars):
- DS4_IMATRIX_PROMPT — a data-format marker in the imatrix dataset .txt files;
  renaming it would break existing datasets.
- gguf-tools/ DS4_* — C macros (gguf metadata keys, tensor-type names), not env
  vars.
- upstream-ds4/ — vendored upstream submodule.
Also refreshed the pulsar_gpu.h backend-seam note (the env names are no longer
"legacy/grandfathered" — PULSAR_CUDA_* is just the current convention).

Gate: clean full rebuild (all 7 binaries) + seam-check; PULSAR_TEST_MODEL read
proven end-to-end (bogus path honored); full pulsar_test suite GREEN on GB10
sm_120f (15/15 incl. tensor-equivalence bit-exact + server).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…lsar

Claude Code sends all traffic (main + haiku-class utility calls) to one
ANTHROPIC_BASE_URL; per-model endpoints were declined upstream (#25146,
#24160). pulsar-model-router.py splits by the body's model field —
haiku-pattern to the small/fast box, everything else to pulsar-server —
as a raw byte relay so replay byte-stability (prefix cache) is preserved.
claude-pulsar launcher requests sonnet[1m] so Claude Code budgets a 1M
context window; pulsar ignores the client model string and serves the
loaded model.

Keeps throwaway small-model calls from churning pulsar's session bank
pool (LRU eviction of the big cached session = full re-prefill).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VE5ruaUJTbG7rnX26vWjL3
@tylerwagler tylerwagler closed this Aug 2, 2026
@tylerwagler tylerwagler changed the title tools: claude-harness — Claude Code on pulsar (model router + 1M-ctx launcher) (withdrawn — opened against wrong repo) Aug 2, 2026
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