docs(witan): put the import-needs-omnigraph decision on record - #286
Merged
Conversation
`import witan.server` raises `RuntimeError: omnigraph binary not found` on a fresh install, because `_ensure_graph` creates the local store at module scope. The clean-install floor check printed that under UNRELATED on every run, alongside genuinely unexplained failures, so a reader had to re-derive from scratch whether it was a defect awaiting a fix. The second or third time you do that, you stop reading the section. It is not a defect. Deferring the bootstrap to first use was weighed and declined: importing this module IS a write, and the CLI depends on that being true. `_srv` diagnoses local-vs-deployed routing BEFORE importing and hands the guard the import unevaluated, so a refused write never reaches the store it is refusing to touch — the #261 fix, for a `task close` that printed success, exited 0, and wrote locally while the deployed graph showed the task open nine days later. "Import is a write" is checkable in one line against sys.modules; "the store is touched on first use" would not be, since several legitimate paths reach the client without passing the guard. A bright line around a failure mode that hid for nine days outweighs the import ergonomics it costs. So `_ensure_graph` carries the reasoning, and the check reports the case as EXPECTED with it. Entries match on module, exception type and a message substring together — keyed on the module alone, some future unrelated RuntimeError from `witan.server` would inherit an explanation that does not apply to it, which is the failure mode an allowlist like this has. No behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RH7kCwSp8TbKQLZ7kbVnY1
Contributor
There was a problem hiding this comment.
Pull request overview
Records the intentional import-time omnigraph dependency and clarifies it in core-floor diagnostics.
Changes:
- Documents why
witan.serverbootstraps storage during import. - Classifies the known missing-binary failure as
EXPECTED. - Adds the decision to the witan changelog.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
mcp/servers/witan/witan/server.py |
Documents import-time bootstrap rationale. |
mcp/servers/witan/CHANGELOG.md |
Records the design decision. |
bin/check_core_floor.py |
Reports the known failure separately without changing exit status. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What are the relevant tickets?
tk-import-witan-server-needs-the-omnigraph-binary-s-e9e822— filed as a question, not a bug report ("THIS MAY WELL BE INTENDED"). This answers it and takes the "IF IT IS NOT DONE" branch.Description (What does it do?)
import witan.serverraisesRuntimeError: omnigraph binary not foundon a machine that has installed witan-council but not runwitan setup.bin/check_core_floor.pyreported it on every run underUNRELATED, next to genuinely unexplained failures — so a reader had to re-derive whether it was a defect awaiting a fix, and the second or third time you do that you stop reading the section.Which call actually raises. Not the
OmnigraphClientconstructor. Module-scope_ensure_graph(cfg.graph_uri)(server.py:155) takes its create path for a store that does not exist yet, and the_find_binary()there (server.py:140) has no degradation, unlike the re-apply path above it. Verified by importing in an env withPATHandHOMEstripped; on the base commit the frames wereserver.py:131 → server.py:116 → omnigraph.py:2173(the first two shift in this PR, since the docstring grew). The constructor two lines below_ensure_graphwould raise for the same reason but never gets there — which matters, because it means a lazy client would not have fixed this.Why it stays. Deferring the bootstrap to first use was weighed and declined. Importing this module IS a write and the CLI depends on it:
witan.cli._common._srvdiagnoses local-vs-deployed routing before importing, and handslocal_dispatch.local_serverthe import as an unevaluated callable, so a refused write never reaches the store it is refusing to touch. That ordering is the #261 fix — awitan task closethat printedClosed <slug>, exited 0, and wrote to a local store while the deployed graph still showed the task open nine days later.The invariant is checkable in one line,
"witan.server" not in sys.modules, andtest_local_dispatch.pyasserts exactly that — written after the weaker loader-injection test passed against a deliberately reintroduced regression. "The store is touched on first use" would not be checkable that way, because several legitimate paths reach the client without passing the guard (witan serve, the admin CLI commands, tests). A bright line around a failure mode that hid for nine days outweighs the import ergonomics it costs.So:
_ensure_graph's docstring carries the reasoning and names the guard it is load-bearing for.check_core_floor.pygets anEXPECTEDbucket betweenFAILandUNRELATED, printing the case with its reason. Exit status is unchanged — onlyfloorstill fails the check.RuntimeErrorfromwitan.serverwould inherit an explanation that does not apply to it, which is the failure mode an allowlist like this has.No behaviour change.
How can this be tested?
Passes:
witan-council … imported 49/49,witan-code … imported 30/30. Note this does not exercise the new branch on a developer machine —_find_binaryfalls back to~/.local/bin/omnigraphwhen PATH misses, so the import succeeds and there is nothing to classify. The new branch only fires where the binary is absent, which is CI, and is what this PR's own core-floor job will show.To exercise the classifier directly, the
IMPORT_ALLsnippet was extracted verbatim from the file and run against an env withPATH=/nonexistentandHOMEpointed at a temp dir:just test-witan-council(984 passed),just check-versions,./bin/gen_docs.py --checkand prek all clean.Additional Context
bin/scripts have no test suite in this repo, so the classifier is verified by executing it rather than by a new test file.A third option was considered and dropped: making
OmnigraphClientresolve its binary on first use instead of in__init__. It is correct on its own merits — constructing a client is not using it — and would fix the store-exists-but-no-binary case. It does not touch the symptom here, since_ensure_graphraises first, so it would be churn bundled into a decision PR. Worth doing separately if anyone wants it.