Go to file T
Code
.cargo ls00: workspace, transcript matcher engine with normalization split, harness skeleton with five-state doctor, loud-SKIP acquisition (serves_lsp=false until the s52 pin bump), 3-OS CI; upstream pinned at ecea37c matching the vendored snapshot
.github nightly: the drift job compares the latest release's prelude with the pinned one, by digest; only: drift runs it alone (#30)
assets assets: the wolf mark; the vscode extension gains its icon (256px render, manifest regenerated and reviewed — the icon is the 33rd entry)
ci ci/tl30-prediction.md: the plant, seen red (run 39609663691)
clients zed: pin the grammar at 936a96f
crates Revert "plant: the checkout-name rule never matches (#18's two rows must go red in CI)" — seen red in run 39687583216
docs docs: tl31 scored, evidence
fixtures transcripts: 26 sessions across lifecycle/diagnostics/requests/cancel/encoding; astral fixture under the gap policy
profiles fackr: the upstream is tenseleyFlow/fackr, not wolffe-lang/fackr (#49)
src lspconf: replay and verify name a captured transcript's teardown, and refuse a cut-short one
tests tests: helix's two teardown shapes replayed live at the pin; the server lane runs them
transcripts transcripts: the six smokes restamped at 9da7bdc, not re-captured — their captured traffic replays record for record at the new pin
vendor Revert "plant: the vendored prelude.json drops os_fork; clean-tree reads git status --porcelain first and its failure as clean; prelude-drift forgets the marks" — seen red in run 39686802417
xtask Revert "plant: the vendored prelude.json drops os_fork; clean-tree reads git status --porcelain first and its failure as clean; prelude-drift forgets the marks" — seen red in run 39686802417
.editorconfig ls00: workspace, transcript matcher engine with normalization split, harness skeleton with five-state doctor, loud-SKIP acquisition (serves_lsp=false until the s52 pin bump), 3-OS CI; upstream pinned at ecea37c matching the vendored snapshot
.gitattributes seed: licenses, LF enforcement, readme — the editor layer; the engine is wolf lsp itself
.gitignore ci: helix-config + emacs-mode lanes (3 OS), zed-extension wasm build, config checks in the server-free job — all loud-skip
.gitmodules org migration: tenseleyFlow -> wolffe-lang (repos transferred, URLs follow)
CHANGELOG.md changelog: tl31
CONTRIBUTING.md contributing: the gauntlet's clean-tree step is cargo xtask clean-tree, never git status --porcelain (#45)
Cargo.lock ci: server lane runs replay+onetruth+fuzz when a binary resolves; nightly latency + 15-min fuzz sweep; divergences.toml ledger with DIV-LSP-001 filed
Cargo.toml license: GPL-3.0-or-later across the server and every client (D41 as amended)
LICENSE license: GPL-3.0-or-later across the server and every client (D41 as amended)
README.md xtask: type-names-check — TYPE_NAMES against the acquired compiler and the vendored spec index; range is the first row it catches (#24)
clippy.toml ls00: workspace, transcript matcher engine with normalization split, harness skeleton with five-state doctor, loud-SKIP acquisition (serves_lsp=false until the s52 pin bump), 3-OS CI; upstream pinned at ecea37c matching the vendored snapshot
deny.toml ls00: workspace, transcript matcher engine with normalization split, harness skeleton with five-state doctor, loud-SKIP acquisition (serves_lsp=false until the s52 pin bump), 3-OS CI; upstream pinned at ecea37c matching the vendored snapshot
divergences.toml harness: exit-code test inverted to its fixed form, raw-utf8 wire test, 28 transcripts re-recorded at the new pin
notes-tl32-editor-revs.md docs: record the editor grammar revision result
rust-toolchain.toml ls00: workspace, transcript matcher engine with normalization split, harness skeleton with five-state doctor, loud-SKIP acquisition (serves_lsp=false until the s52 pin bump), 3-OS CI; upstream pinned at ecea37c matching the vendored snapshot
rustfmt.toml ls00: workspace, transcript matcher engine with normalization split, harness skeleton with five-state doctor, loud-SKIP acquisition (serves_lsp=false until the s52 pin bump), 3-OS CI; upstream pinned at ecea37c matching the vendored snapshot
upstream @ 9da7bdc upstream: pin bumped to 9da7bdc (wolf 0.2.34); re-vendored, 0.2.34's seven prelude names and the mark busy

Contributing to wolf-lsp

The engine is not here

The language server is wolf lsp, the wolf compiler itself, serving LSP from the same code that compiles (D34: one process, one truth). This repo contains no server code, and none is accepted here.

No fallback. No "temporary" diagnostic parser. No formatting shim. No client-side approximation of a capability that has not shipped. A second implementation of anything server-side is the exact failure this track exists to avoid: the moment the editor layer can answer a question itself, it stops proving that the compiler can.

Concretely:

  • A capability an editor wants is a wolf-lang sprint, not a shim here. Semantic tokens, inlay hints, rename, refactoring are all compiler-track post-v1. Their tests get written when the server ships them.
  • No cargo dependency on any wolf_* crate, and no vendored wolf-lang source. This repo talks to a wolf process over stdio or it does not talk.
  • CI never builds the compiler. It downloads the artifact wolf-lang publishes, keyed by vendor/upstream/PIN, or it skips loudly.

cargo xtask independence enforces all three, on every PR.

Commits

Mirroring the compiler and interpreter tracks:

  • Commit in chunks. One logical change per commit. A refactor and the feature it enables are two commits. A toolchain bump or a pin bump is always its own commit. Never git add -A.
  • Terse, imperative subjects, under ~250 characters unless the change genuinely needs elaboration: matcher: default ServerCapabilities to subset, not Added some logic so that capabilities can grow without....
  • Never git checkout files that were just written but not yet committed. Stage or stash first. This has eaten work before.

Tests are first class

  • Every behaviour worth having is worth a test, and every bug fix arrives with the test that would have caught it. Tests land in the same commit as the code.
  • CI is first class too. A red CI is a stop-the-line event, never a thing to merge around.
  • cargo xtask ci is the local gate: fmt-check, clippy -D warnings, tests, sync-pin, independence, and the lspconf doctor report.

The two halves

Everything in this repo is either server-free or server-dependent, and the split is load-bearing:

  • Server-free (transcript parsing, matchers, normalization, profile validation, pin integrity) must be green on a fresh clone with no wolf on PATH. That is why the matcher engine is a pure function of two JSON values.
  • Server-dependent (record, replay, bench) must skip loudly: print SKIP: <reason> and exit 77. Never exit 0 for work that did not happen. A lane that reports success for doing nothing is a lane nobody notices is dark. --require-server turns the skip into a failure, for jobs that are supposed to have a binary.

The snapshot ritual (insta)

Snapshots live in crates/*/tests/snapshots/. When one legitimately changes:

INSTA_UPDATE=always cargo test        # rewrite the .snap files
git diff '**/snapshots/'              # READ the diff — this is the review
cargo test                            # verify-clean run against the new snaps

The middle step is the whole point. INSTA_UPDATE=always makes any diff disappear, including the one that was a real regression. Reviewing the diff is what turns that hazard into a workflow. Never commit a .snap.new or a .pending-snap.

Snapshots hold the normalized view of a transcript and never the raw one, so a diff means a behavior change and not a different run.

Corpus samples are read-only

vendor/upstream/samples/ is the only source of .lu in this repo, and its bytes are canonical wolf fmt output at a STYLE_VERSION. Never reformat a sample, never fork one into a local fixture, and never "fix" one that looks wrong. That is a finding to report upstream. A sample whose bytes change is a pin bump, in its own commit (vendor/README.md).

Platform lessons

These were paid for once already, upstream. Do not re-derive them.

  • Line endings are protocol surface. .gitattributes pins * text=auto eol=lf and it stays that way. LSP positions are derived from byte offsets, and a CRLF checkout silently shifts every column in every transcript. CI proves it held.
  • Capture, then grep. Never pipe a program's stdout into grep -q. grep -q exits at the first match and closes the read end. The producer's exit-time flush then hits EPIPE, and Rust's default SIGPIPE-ignore turns that into a panic, nondeterministically, and it surfaced on macOS first. Write out=$(cmd) then printf '%s' "$out" | grep -q ….
  • Sort read_dir output before it can influence any generated artifact or any message a human reads. Directory order is platform noise.
  • Never seed an RNG from a raw file path. Normalize separators first. A Windows \ explored an unvetted seed space compiler-side. ls01's edit fuzzer takes explicit seeds. More generally, no raw platform path reaches a transcript, a report key, or a hash: use lsp_harness::slash_path.
  • Pin CI toolchains explicitly. rust-toolchain.toml names the toolchain. CI installs it with an explicit rustup step, then adds components. An implicit proxy-install races the first cargo invocation and picks up neither rustfmt nor clippy.
  • Content-Length counts bytes, not characters. The most common framing bug in hand-written clients, which is most of the clients this track integrates with.

Toolchain

Pinned in rust-toolchain.toml (rustup and CI) and rust-version (everyone). Bump deliberately, in a dedicated commit, CI-green on all three platforms.

Model policy

Track agents run Opus. The work is integration against hand-rolled LSP clients in three languages, where the failure modes are subtle (framing, position encoding, unanswered server requests) and a wrong patch lands in someone's daily driver. Agent guidance itself stays untracked: AGENTS.md and CLAUDE.md are gitignored, as on the compiler and interpreter tracks.

Before you push

cargo xtask ci

Green, on a clean tree, and checked: the last step is cargo xtask clean-tree, which names every file that differs from HEAD or is untracked and not ignored, and fails when git cannot answer. It reads the tree with git diff HEAD and git ls-files --others, the submodule path excluded, never git status --porcelain, which exits 128 beside a symlinked upstream (a lane's switch script points the submodule path at a tag clone) and so recorded an empty, "clean" tree whatever it held (wolf-lsp#45). Run it on its own as the last step of any gauntlet. wolf lsp exists upstream as of s52, so the server-dependent lanes light up as soon as a binary at the pinned version resolves. Without one, lspconf doctor reports SERVER UNAVAILABLE, which is a skip and not a failure, and xtask ci treats it as one.

CI is stricter than this, deliberately (wolf-lsp#28). On a runner the server is acquired, from the pinned version's own release tag, and absence is a failure — a skip there would be a green run reporting on suites that did not execute, which is what the old workflow did on every merge made between a wolf-lang release and this repo's next pin bump. The single exception is a PIN at an unreleased (trunk) commit, where there is no archive to fetch; that path is green and announces itself with a warning annotation. Locally the skip stays a skip, because a contributor without a binary should be able to run the server-free half.

Where the plan lives

The sprint plan is the lsp track (lsNN) in the wolf metarepo. Sprint files are the implementation contract, and the decision log is the design authority.