docs: LaTeX framing revision 3 --- step zero corrected the premise

The .texlabroot caveat is discharged, and discharging it falsified the
reasoning behind it.

.texlabroot is real, and texlab marker set is wider than this document
listed --- the bare texlabroot and latexmkrc spellings count too, and
the walk takes the innermost. The implementation ships texlab own set.

But the caveat framed marker 1 as conditional on texlab honouring the
FILE, which was the wrong question. Every arm of texlab ancestor walk
searches documents already loaded, and that workspace is built from the
folders the CLIENT supplies. Observed: with rootUri at chapters/, no
ancestor marker widened texlab view and its dependency graph never
reached the parent document; with rootUri at the marker directory the
parent resolved either way.

So texlab honours the root it is handed and never corrects a too-narrow
one. config.latex.root IS the project scope, which makes the resolver
the whole value of the lane for a multi-file thesis rather than a
nicety --- the opposite of how section 2 "Slice 1 is one config entry"
reads.

Second correction, and this one was a real trap. Revision 2 said .git
is deliberately excluded from the walk and stopped there. Omitting it
is not sufficient: project_root_for falls through to
pmacs.project.detect when a resolver returns nil, and that walk lists
.git among its markers at src/project.rs:184. A resolver that politely
declined on a markerless file would hand texlab the monorepo by the
back door, with the exclusion looking correct at every line you would
think to read. The resolver never declines for a file with a directory,
and the fixture asserts the shared detector really would have answered
the repo root so the pin cannot pass vacuously.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
This commit is contained in:
Levi Neuwirth 2026-08-09 18:04:21 +02:00
parent 22d72fa7f7
commit 12b7ee58d8
No known key found for this signature in database
2 changed files with 43 additions and 12 deletions

View File

@ -275,7 +275,7 @@ from #171 and #215.
authoritative tip** — the ref, not a SHA. Recover with authoritative tip** — the ref, not a SHA. Recover with
`git fetch githubsucks && git checkout lsp-latex-coverage`. `git fetch githubsucks && git checkout lsp-latex-coverage`.
- **Framing `docs/lsp-language-coverage-framing.md`, revision 2 - **Framing `docs/lsp-language-coverage-framing.md`, revision 3
IMPLEMENTATION AUTHORIZED 2026-08-09**, after a summary of its four IMPLEMENTATION AUTHORIZED 2026-08-09**, after a summary of its four
corrections rather than a findings round on the document itself. corrections rather than a findings round on the document itself.
Recorded that way deliberately: the §3 `.texlabroot` verification Recorded that way deliberately: the §3 `.texlabroot` verification

View File

@ -1,6 +1,6 @@
# LSP language coverage: LaTeX (and the Haskell/OCaml question) # LSP language coverage: LaTeX (and the Haskell/OCaml question)
**Status: revision 2, IMPLEMENTATION AUTHORIZED 2026-08-09.** **Status: revision 3. Implemented at `d79afdc`; step zero discharged by observation, and its result corrected two things this document had wrong.**
*Recorded precisely: the user authorized dispatch after a summary of *Recorded precisely: the user authorized dispatch after a summary of
revision 2's four corrections, rather than returning findings on the revision 2's four corrections, rather than returning findings on the
@ -119,22 +119,53 @@ inside a monorepo would otherwise get the monorepo. This is the one
place where copying the other fourteen entries' instinct would be place where copying the other fourteen entries' instinct would be
actively wrong. actively wrong.
**And omitting it is NOT sufficient — revision 2 stopped one step
short.** `project_root_for` falls through to `pmacs.project.detect`
when a resolver returns `nil`, and **that** walk lists `.git` among its
markers (`src/project.rs:184`). So a resolver that politely declined on
a markerless file would hand texlab the monorepo **by the back door**,
with the exclusion looking correct at every line you would think to
read. The resolver therefore **never declines** for a file with a
directory, and the pin is end-to-end through attach — with the same
fixture asserting the shared detector really would have answered the
repository root, so the test cannot pass vacuously.
**Deliberately NOT proposed: scanning for `\documentclass`.** That is **Deliberately NOT proposed: scanning for `\documentclass`.** That is
the semantically correct notion of a root document, and it is a the semantically correct notion of a root document, and it is a
directory scan on every resolve, with its own caching and invalidation directory scan on every resolve, with its own caching and invalidation
questions. If the marker walk proves insufficient in use, that is the questions. If the marker walk proves insufficient in use, that is the
next increment — with evidence. next increment — with evidence.
**VERIFICATION CAVEAT, stated rather than buried.** `texlab 5.25.1` is **CAVEAT DISCHARGED (revision 3), and the premise behind it was wrong
installed and its version and CLI surface were checked directly. Its in a way that raises the lane's stakes.**
**LSP-level** behaviour — whether it honours `.texlabroot`, and how it
resolves multi-file projects from a root URI — was **not** verified Established by driving a hand-written LSP client against `texlab run`
here; the CLI exposes only `run` / `inverse-search`, so this needs a and reading texlab's source at the exact installed tag `v5.25.1`:
live session. **Marker 1 is provisional and must be confirmed against a
running texlab before implementation**, exactly as the sibling - **`.texlabroot` is real**, and so is a wider marker set than this
`gate-protocol-build` lane requires its precondition to be observed document listed. `crates/distro/src/language.rs` maps
rather than reasoned about. If `.texlabroot` is not a real marker, it `.texlabroot`/**`texlabroot`** → Root, `Tectonic.toml` → Tectonic,
drops and the walk starts at `latexmkrc`. `.latexmkrc`/**`latexmkrc`** → Latexmkrc, and
`ProjectRoot::walk_and_find` walks ancestors testing all three,
**innermost winning**. The implementation ships texlab's own set,
including the bare spellings §3 omitted.
- **But texlab cannot rescue a root we get wrong.** Every arm of that
walk searches `workspace.iter()` — documents *already loaded* — and
the workspace is built from the folders **the client supplies**.
Observed directly: with `rootUri` at `chapters/`, no ancestor marker
(`.texlabroot` included) widened texlab's view, and its dependency
graph never reached the parent document; with `rootUri` at the marker
directory, the parent resolved whether or not a marker was present.
**So `config.latex.root` IS the project scope.** Revision 2 framed the
resolver as choosing between plausible roots that texlab might refine.
It does not refine. The resolver is the whole value of the lane for a
multi-file thesis, not a nicety — which is the opposite of how §2's
"Slice 1 is one config entry" reads, and worth stating plainly.
*(Also observed rather than assumed: bare `texlab` answers `initialize`
over stdio, so `args = {}` is correct and the `run` subcommand is
unnecessary.)*
## 4. Open questions ## 4. Open questions