Skip to content

Upgrading to 2.0.0

Everything 2.0.0 asks of you, in the order it is likely to bite. Most installs need only step 1, and it happens by itself.

2.0.0 is the remediation of a production-readiness audit that scored 1.8.0 45/100 — not ready across 189 findings. A major version is warranted because 47 public contracts changed: tool names, argument names, result shapes, environment variables, exit codes, and the on-disk layout of .sankshep/. The changelog lists what changed and why; this page lists what you have to do.


1. Every index rebuilds on first start

Applies to: everyone. Action: none, but expect a delay.

Chunks used to be sized by line count (400 lines), while the embedding model reads the first 512 tokens and discards the rest. So a 400-line class was stored under a vector describing roughly its first thirty lines, and a search for something declared halfway down it returned nothing.

Chunks are now sized by what the model can actually read, and anything still larger — including a file in a language Sankshep has no grammar for — is split into overlapping windows. That moved the chunker version, so an existing index.db is discarded and rebuilt.

This is correct rather than merely convenient: every vector in an existing index describes only the beginning of its chunk. The index is derived data, so a rebuild is always safe. It takes minutes on a large repository.

You do not have to delete anything. If you would rather do it explicitly, remove .sankshep/index.db and its -wal/-shm siblings.


2. A scripted search_code caller has to read text, not JSON

Applies to: anything that parses search_code output. Action: change the parser.

Hits used to come back as a single JSON line inside a text block, with every newline, quote and tab escaped — the form a model reads worst, and pure token overhead in a product whose purpose is spending fewer tokens. The result is now the same shape get_context already used:

// ---- Sankshep search: 2 hits, best first ----
// Pass any path below to get_context for the whole minimized file.

// Calculator.cs:6-28 (Type Calculator)
public class Calculator
{
    ...
}

Everything a hit carried is still present: path, line range, kind and symbol name, on the // line above each block. The internal content hash is gone, deliberately — it identified a chunk inside Sankshep's own index and meant nothing to a caller.

Also: an empty index is now an error, not an empty success. If your code treated {count:0,results:[]} as "no such code", it was reading an unbuilt index the same way — which is how a model came to state that code did not exist. Run index_repo first.


3. Helm users behind an Ingress will get 403 until they act

Applies to: chart users with an Ingress. Action: one value.

Chart 1.3.0 renders the Host allow-list by default, derived from the release's own Service DNS plus the loopback names a kubectl port-forward makes a browser send. An Ingress hostname cannot be derived from anything the chart knows, so it has to be declared:

allowedHosts:
  extra: ["sankshep.internal.example.com"]

allowedHosts.enabled: false restores the previous behaviour exactly.

Health probes are exempt either way, so this can never take the pod down — the deployment comes up healthy and refuses traffic, which is the failure mode you want of the two. NOTES.txt prints the effective list after install.


4. The first export_decisions fails once, if you already have a DECISIONS.md

Applies to: anyone who has run export_decisions. Action: move or rename the old file.

The generated file now carries a marker on its third line:

# Decisions

<!-- Generated by sankshep export_decisions. Edits to this file will be overwritten. -->

A DECISIONS.md without that marker is refused rather than overwritten, because the tool could not previously tell its own output from a file you wrote by hand — and it overwrote either. Move or rename yours and re-run; from then on it rewrites its own file as it always did.


5. service install refuses a user-writable binary

Applies to: Windows Service installs. Action: install from a protected directory.

A service registered by an administrator runs as LocalSystem, so anyone who can replace its executable can run code as LocalSystem. dotnet tool install -g puts the binary under your user profile, which you can write — so installing from there handed that privilege to your own account and to anything running as you.

The installer now refuses, and names the principal that could replace the file. Copy the binary somewhere only administrators can write, and install from there:

mkdir "C:\Program Files\Sankshep"
copy "$env:USERPROFILE\.dotnet\tools\sankshep.exe" "C:\Program Files\Sankshep\"
& "C:\Program Files\Sankshep\sankshep.exe" service install --repo C:\path\to\repo

6. A supervised service keeps state outside the repository

Applies to: Windows Service and systemd installs. Action: copy facts.db if you want to keep it.

Under a service manager, facts.db, index.db and stats.db move from <repo>/.sankshep to a machine-wide root — %ProgramData%\Sankshep on Windows — and the embedding model moves out of the service account's profile. Startup names both the old and new paths.

The index rebuilds anyway (step 1). Authored memory is not migrated, and that is deliberate: copying a file out of an attacker-controllable directory while running as LocalSystem is the defect the move exists to close. Copy facts.db across by hand if you want your remembered facts.

Interactive runs are unchanged, and so are SANKSHEP_STATE_DIR and SANKSHEP_MODEL_DIR if you set them.


7. A fail-closed refusal now exits 1

Applies to: anything that parses the exit code. Action: expect 1.

A refused start — a non-loopback bind with no authentication, most often — used to surface as an unhandled exception: a .NET stack trace and exit code 0xE0434352, which reads like a crash to a service manager and to anyone watching a container restart loop. It is now one line on stderr and exit code 1.


Things that changed but ask nothing of you

  • summarize_repo covers every language Sankshep parses, and bounds itself at 20,000 tokens by default, saying so when it truncates. Pass maxTokens to change it; the floor is 500.
  • recall caps its result set at 50 and reports matched alongside count, plus truncated.
  • index_repo's path is optional and defaults to the whole repo. Its message now describes the walk rather than the index's total, and it reports progress per file when the client sends a progress token.
  • get_context's tokenBudget is a ceiling on the whole response, header included, and a file larger than the budget is truncated rather than dropped whole.
  • Raising the log level cannot print your code. See Environment variables.
  • appsettings.json in a served repository is ignored on both transports. If you were configuring Sankshep that way, move those settings to environment variables — but note that a repo-root appsettings.json could previously re-bind the server to 0.0.0.0, which is why it is gone.
  • An absolute path is refused on the HTTP transport, and by index_repo on both. Use repo-relative paths, which work everywhere.
  • Runtime is .NET 10; the MCP SDK is 2.2.0.

If something goes wrong

Troubleshooting covers the common cases, and Feedback is the channel for anything it does not.