Agents address intent rather than bytes. The PositionSpec union supports cursor, range, symbolic, AST‑path, and content‑anchor forms with an optional docVersion.
// Structured PositionSpec (subset)
{ kind:"cursor", uri, line, col, indexing:"utf-16|utf-8|codepoint" }
{ kind:"range", uri, start:[l,c], end:[l,c] }
{ kind:"symbol", qualname:"pkg.mod:Class.method", role:"def|sig|body|doc", overload:0 }
{ kind:"ast", path:[["module","pkg.mod"],["class","C"],["def","m"]] }
{ kind:"anchor", uri, snippet:"def load_data(", ctx:24, hash:"sha1:…"}
Canonical strings for the CLI:
# Cursor / range
src/app.py@L42:C7
src/app.py@R(42,7->44,1)
# Symbolic
py://pkg.mod#Class.method:body
py://pkg.mod#function_name:sig
# AST path (subset)
ast://[module=pkg.mod]/[class=Class]/[def=method]/name[1]
# Content anchor (snippet + context N chars)
anchor://src/app.py#"def load_data("?ctx=24
Indexing Semantics
Lanser‑CLI negotiates positionEncoding with the server (default LSP utf‑16) and allows CLI I/O in
utf‑8 or codepoint. When they differ, both are surfaced in verbose traces; bundles retain server coordinates.
Relocate Algorithm (deterministic)
Algorithm: Relocate(selector s, workspace W, optional snapshot v)
1. If v present and W has exact map(s, v): return [(map(s, v), 1.0)].
2. If s.kind ∈ {symbol, ast}: resolve via module graph + parser → candidates A.
3. If s.kind = anchor: fuzzy match k-grams within context → candidates H.
4. Score candidates by: 0.5·ast + 0.2·module + 0.2·tokenJaccard + 0.1·proximity.
5. Sort desc by (score, uri, range). If empty → E/NOT_FOUND.
6. If ties or low top-score: attach disambiguation evidence. Return top-k.