Skip to content

Add documentation set with drift-detecting coverage checks - #2

Merged
johncarpenter merged 5 commits into
mainfrom
001-project-documentation
Aug 28, 2026
Merged

Add documentation set with drift-detecting coverage checks#2
johncarpenter merged 5 commits into
mainfrom
001-project-documentation

Conversation

@johncarpenter

Copy link
Copy Markdown
Owner

Summary

  • README.md documented 12 of 16 commands, 17 of 19 configuration keys, and named 6 of 7 MCP tools incorrectly. It advertised get_entity, list_entities, get_neighbors, find_path, list_relations, get_schema; the server registers those with a _tool suffix. Anyone scripting against the documented MCP surface got unknown-tool errors.
  • The four commands you need when a build half-fails (extract, resolve, induce) or when you want to start over (reset) were undiscoverable.
  • Adds a docs/ set (16 pages) covering install, use, and maintain, plus three end-to-end walkthroughs.
  • The load-bearing part is tests/test_docs.py: 22 offline checks that make documentation drift fail the build. Prose alone would have rotted the same way.

Approach: the tool's surface is introspectable at runtime (Click command tree, DEFAULT_CONFIG, the FastMCP registry, the --format choice list), so coverage is a computable property rather than a review checklist. The checks assert bidirectional set equality — documenting something that does not exist fails too, which is the direction that would have caught the wrong tool names.

Changes

Documentation (16 pages, ~3.4k lines)

  • docs/README.md index; install.md (incl. the loadable-SQLite-extension prerequisite with symptom and pyenv remedy); quickstart.md (two corpus paths, incl. a checkout-free heredoc for pip install readers); concepts.md (pipeline + the six vocabulary terms).
  • docs/reference/: cli.md (all 16 commands, 49 parameters, global --debug, structured output for the 8 --json commands), configuration.md (all 19 keys, precedence with a worked merge, per-platform paths), export.md (3 formats).
  • docs/guides/: mcp.md (7 real tool names, client config, the load-bearing cwd), maintenance.md (incrementality, spend, reset, locking, backup), troubleshooting.md (12 symptom/cause/fix entries).
  • docs/examples/: three walkthroughs against tests/fixtures/.
  • docs/contributing/: development, architecture, release (incl. the manual pre-release checklist).
  • README.md becomes the orientation surface and PyPI landing page.

Verification (tests/test_docs.py, 22 checks, 0.11s)

  • Bidirectional coverage for commands, parameters, config keys, MCP tools, export formats.
  • Structured-output parity; inert-key disclosure; concept-term definitions.
  • Per-page version stamps checked against kgmd.__version__; internal link resolution; two-link reachability from the index; quoted troubleshooting errors still present in kgmd/**/*.py; walkthrough structure; no credential-shaped strings.

Governance

  • Project constitution (v1.1.0) derived from practices the codebase already follows, plus specs/001-project-documentation/ (spec, plan, research, data model, contracts, validation guide, tasks) and the Spec Kit scaffold.

Testing

  • Unit tests added/updated — 22 new checks in tests/test_docs.py
  • Integration tests added/updated — n/a, no runtime behaviour changed
  • Manual testing completed — see below
  • All tests passing — ruff check . clean, pytest -v 72 passed (50 pre-existing + 22 new)

Negative validation (10/10). A coverage test that never fails is decoration, so each check was proven to fail: adding an undocumented command, deleting a documented section, adding a phantom MCP heading, adding a config key, adding an option, bumping the version with stale stamps, rewording an error message, renaming a linked page, pasting a credential-shaped string, and burying a page three levels deep. Every scenario produced exactly the predicted failure and reverted clean.

Hermetic. The suite passes with OPENROUTER_API_KEY/OPENAI_API_KEY/ANTHROPIC_API_KEY unset — no provider calls, no model downloads, no new dependencies.

Not automated (by design). Examples that build a real graph need a credential and make nondeterministic paid calls, so they are a checklist in docs/contributing/release.md, not a CI step. Onboarding/contributor timings need a human; no baseline exists yet and the release page says so.

Additional Notes

No breaking changes. No runtime code touched — nothing under kgmd/ is modified. No migration steps. No dependencies added.

Product defects found while documenting — documented, not fixed (each verified against source; fixing them is out of scope for a documentation change):

  1. kgmd reset and kgmd reset --hard are no-ops in 0.1.0. VACUUM runs inside the transaction opened by the preceding DELETEs: Error: cannot VACUUM from within a transaction, exit 1, nothing reset. Reproduced directly. Docs give deleting .kgmd/graph.db as the working reset. Recommend a separate fix PR — this is user-facing.
  2. kgmd build --config binds a path that is never read (caught by Greptile pre-review; row now reads "Accepted but ignored").
  3. Deleted or renamed notes are never removed from the graph.
  4. reset leaves vec_chunks/vec_entity_mentions rows behind while integer ids get reused, so stale vectors can bind to fresh rows.
  5. The three inert configuration keys (extraction.max_entities_per_chunk, extraction.max_relations_per_chunk, induction.include_attribute_summary) are marked as having no effect rather than implied to work.

Follow-up recommended: rename the MCP tools to drop the _tool suffix. This PR documents reality; renaming is a breaking change to a public surface and needs its own spec and a compatibility decision.

Unverified claim, flagged in-page: the Windows global-config path %LOCALAPPDATA%\kgmd\kgmd\config.yaml is derived from the platformdirs source, not executed on Windows. configuration.md marks it unconfirmed and the release checklist carries a confirmation item.

Reviewer note: the coverage tests assert that every surface item is documented, not that its description is true. Prose accuracy is reviewer-owned — Greptile's --config catch is exactly that gap in action.

The repository had no recorded engineering principles and no planning
artifacts. This adds the Spec Kit scaffold plus a constitution derived from
practices the codebase already follows (single SQLite artifact behind
PRAGMA user_version, one deterministic LLM boundary, CLI/MCP parity over a
single query layer, content-hash incrementality, an offline-deterministic
test gate).

Constitution is v1.1.0: the initial ratification plus one amendment
extending "documentation as contract" from README.md alone to both README.md
(orientation) and docs/ (authoritative depth), and requiring surface coverage
to be mechanically verifiable in both directions. The amendment is a
prerequisite for the docs/ work in the following commit, which would
otherwise contradict the governance rule it is measured against.

Also includes the spec, plan, research, data model, contracts, validation
guide, and task list for the documentation feature under
specs/001-project-documentation/.
The tool exposed 16 commands, 19 configuration keys, 7 MCP tools and 3 export
formats; README.md documented 12 commands, 17 keys, and named 6 of the 7 MCP
tools incorrectly (it advertised get_entity, list_entities, get_neighbors,
find_path, list_relations, get_schema; the server registers those names with a
_tool suffix). Anyone scripting against the documented MCP surface got
unknown-tool errors, and the four commands needed to recover from a partial
build (extract, resolve, induce, reset) were undiscoverable.

Adds 16 pages under docs/ covering install, quickstart, concepts, full
reference, MCP integration, maintenance, troubleshooting, three end-to-end
walkthroughs, and contributor/release material. README.md becomes the
orientation surface and PyPI landing page, linking into docs/.

The load-bearing part is tests/test_docs.py: 22 offline checks that derive the
real surface from the Click command tree, DEFAULT_CONFIG, an AST scan of
mcp_server.py, and the --format choice list, then assert bidirectional set
equality against the reference pages. Documenting something that does not
exist fails the suite, which is the direction that would have caught the wrong
tool names. Also verifies per-page version stamps against kgmd.__version__,
internal link resolution, two-link reachability from the index, quoted error
strings still present in kgmd/**/*.py, walkthrough structure, and the absence
of credential-shaped strings. No new dependencies; no network or credential
required, so the gate stays hermetic.

Documented, not fixed (each verified against source, out of scope for a
documentation change):
- kgmd reset and reset --hard are no-ops in 0.1.0: VACUUM runs inside the
  transaction opened by the preceding DELETEs and raises. Deleting
  .kgmd/graph.db is the working reset.
- kgmd build --config binds a value that is never read.
- Deleted or renamed notes are never removed from the graph.
- reset leaves vec_ rows behind while integer ids get reused.
- The three inert configuration keys are marked as having no effect.
Two P1 findings, both confirmed against the tree:

.specify/integrations/omp.manifest.json pins ten .omp/commands/speckit.*.md
files by sha256, but those files were left untracked, so the committed Spec Kit
scaffold was unusable on a fresh checkout. Commits them so the manifest and the
tree agree.

docs/reference/cli.md described kgmd build --config as "Config file path",
implying it works. It does not: the value is bound and discarded, and build
always loads the corpus .kgmd/config.yaml. The row now reads "Accepted but
ignored" with a warning, matching what the configuration reference already
said. The coverage test asserts that every option is documented, not that its
description is true, so prose accuracy stays reviewer-owned by design.
@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown

Greptile Summary

The PR replaces the project’s orientation documentation with a comprehensive installation, CLI, configuration, MCP, maintenance, and walkthrough set, backed by offline drift-detection tests.

  • Documents all current commands, configuration keys, MCP tools, and export formats.
  • Adds 22 documentation coverage, consistency, link, version, and credential checks.
  • Adds the Spec Kit governance scaffold, command definitions, templates, scripts, and bundled workflow.

Confidence Score: 3/5

This PR should not merge until repository-provided Spec Kit hooks can no longer execute automatically without an explicit trust boundary.

The new default OMP workflow reaches command definitions that load hook command identifiers from the checkout and require their automatic invocation, allowing untrusted repository configuration to direct execution with the agent session's authority.

Files Needing Attention: .omp/commands/speckit.specify.md and the sibling .omp/commands/speckit.*.md command definitions

Security Review

The newly added Spec Kit command definitions automatically execute mandatory hooks loaded from repository-controlled configuration without a trust or approval boundary. An untrusted checkout can therefore direct the agent session to invoke an arbitrary command or skill. How this was verified: The added command definitions trace .specify/extensions.yml command values directly to mandatory invocation while specifying no allowlist, sandbox, or confirmation step.

Fix all with Greploop Fix All in Claude Code

Reviews (1): Last reviewed commit: "Address greptile pre-review: complete th..." | Re-trigger Greptile

Comment thread .omp/commands/speckit.specify.md Outdated
Greptile flagged a P1 security issue on the vendored Spec Kit command
definitions this PR added: their hook sections instructed the agent to invoke
any mandatory hook automatically, taking the command id from
.specify/extensions.yml. That file is repository-controlled, so a checkout the
user did not author could name any command there and have it run with the agent
session's authority. No allowlist, sandbox, or confirmation stood in the way.

Patches all 20 hook sites across the 10 command definitions to fail closed: the
hook block is still emitted, but the command is never invoked without explicit
user approval that quotes the extension name and the exact command id. Only a
user-level allowlist stored outside the checkout may pre-approve a hook; an
allowlist inside the repository is itself untrusted input and is explicitly
non-authoritative.

The .omp/commands/*.md files are vendored and pinned by checksum, so
.specify/integrations/omp.manifest.json is regenerated against the patched
content and records the divergence. docs/contributing/development.md documents
that re-running `specify init` restores the upstream text and drops the patch,
which must then be re-applied.

This repository defines no hooks and expects none: there is no
.specify/extensions.yml.
The 3.10 CI leg failed with `ImportError: cannot import name 'NotRequired'
from 'typing'` while importing tests/test_extract.py, test_resolve.py and
test_induce.py. Those modules import kgmd.extract/resolve/induce, which reach
litellm.

Cause is dependency drift, not a code change. `litellm>=1.50` had no upper
bound, so CI resolved 1.83+, and litellm/types/compression.py does
`from typing import NotRequired` — added in Python 3.11 — while the package
still declares requires-python >=3.10. Verified by scanning published wheels:
1.78.0 through 1.82.0 are clean, 1.83.14 through the current 1.98.0 all carry
the 3.11-only import. main was green in May under an older resolution.

Capped at <1.83, which resolves to 1.82.6. Verified on a Python 3.10.21
virtualenv: litellm imports and the full suite passes 72/72. Also re-verified
on 3.12 after the downgrade, so nothing in kgmd depends on a litellm API newer
than 1.82.

Lift the cap once litellm either fixes the import or drops its 3.10 claim.
@johncarpenter

Copy link
Copy Markdown
Owner Author

CI: all four legs green (8b9364d)

The test (3.10) leg was failing on pre-existing dependency drift, not on anything in this PR:

ImportError: cannot import name 'NotRequired' from 'typing'
  while importing tests/test_extract.py, test_resolve.py, test_induce.py

litellm>=1.50 had no upper bound, so CI resolved 1.83+, and litellm/types/compression.py does from typing import NotRequired — added in Python 3.11 — while the package still declares requires-python >=3.10. Scanned the published wheels to find the boundary: 1.78.0–1.82.0 clean, 1.83.14 through the current 1.98.0 all broken. main was green in May under an older resolution.

Attribution: the three failing modules are untouched by this PR (git diff origin/main...HEAD -- tests/ shows only test_docs.py added), and tests/test_docs.py never reaches litellm — kgmd.cli does not import it at module level.

Capped at <1.83 (resolves 1.82.6) and verified on a real Python 3.10.21 virtualenv: litellm imports and the suite passes 72/72. Re-verified on 3.12 after the downgrade, so nothing in kgmd needs a litellm API newer than 1.82.

Lift the cap once litellm fixes the import or drops its 3.10 claim.

Leg Result
test (3.10) success
test (3.11) success
test (3.12) success
test (3.13) success

@johncarpenter
johncarpenter merged commit 82c6f74 into main Aug 28, 2026
4 checks passed
@johncarpenter
johncarpenter deleted the 001-project-documentation branch August 28, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant