Repository navigation
feat(typesafe-docs-drift): phase-1 docs-drift detector with fixture replay - #23
Merged
Merged
Conversation
…re replay Extract facts from a NanoClaw checkout with deterministic parsers (ncl verbs, flags and enums; environment variables; container_configs columns and cli_scope values; workspace skills; gateway selection; timestamp rules), find the top doc sections per fact with a BM25 index over the nanoclaw-docs pages, and send one TypeSafe fan-out request per fact x section asking contradicts, covers and staleness. Gate the answers and print ranked DRIFT / MISSING / UNSURE / OK rows with the doc page and heading, the model's numbers and the code evidence; save raw answers under a gitignored directory; --json for phase 2. --facts-only and --plan show the deterministic half without a key; --fixture replays recorded facts, sections and responses offline and --record saves a live run in that format. TYPESAFE_API_KEY is read from the environment only and never printed. Nothing is written to either repository.
…rse LLM Add drift-benchmark.py: every contender judges the same 60 fact x section pairs sampled (stratified, seeded) from the recorded live run. Contenders are the recorded TypeSafe answers, TypeSafe live (sequential), and headless `claude -p` with tools disabled, 10 pairs per call, strict JSON out; a read-only `codex exec` fallback is implemented. Reports wall time, per-pair p50/p95 with the measured CLI startup overhead separated, tokens, cost, parse failures and format violations per run, run-to-run flips with their denominator, pairwise agreement, and precision/recall on `contradicts` against a 30-pair gold file labeled by an LLM-assisted reviewer. Only the recorded contender runs by default; network and CLI use is opt-in through --contenders. The TypeSafe key is checked before any paid contender starts, never printed, and removed from every spawned CLI's environment; CLI diagnostics are redacted; a timeout or interrupt kills the CLI's whole process group. Results of the first run are recorded in docs/typesafe-docs-drift.md.
glifocat
force-pushed
the
feat/typesafe-docs-drift
branch
from
September 23, 2026 16:09
0eae383 to
876a3e9
Compare
… HTML error bodies; note the gold set's enum caveat
glifocat
marked this pull request as ready for review
September 23, 2026 16:12
The server creates its private state file with O_EXCL and writes the body afterwards, so setUp could read an empty file and fail with a JSON decode error (ubuntu CI on this branch and on main's #25 merge run). Poll until the file parses, within the same 8 s deadline.
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 changes?
Adds the
typesafe-docs-driftskill, phase 1 of a docs-drift detector for NanoClaw. It ranks where the nanoclaw-docs portal disagrees with the code and writes to neither repository.nclresources, verbs, access levels, declared or help-text flags and column enums (src/cli/resources/); environment variables with precedence and defaults (src/config.ts, gateway and driver defaults,.env.example);container_configscolumns, defaults andcli_scopevalues; workspace skill names and descriptions; gateway selection rules (docs/gateway-seam.mdwhen present, otherwisesrc/gateway-providers/index.ts); the## Timestampsrules inCLAUDE.md. Each fact is{id, area, statement, evidence: file:line excerpt}.docs.json, split on headings, returns the top 3 sections per fact.contradicts(noul),covers(noul),staleness(score over current / slightly outdated / wrong / dangerous if followed).documented_anywhereis the maxcoversper fact.DRIFT/MISSING/UNSURE/OKrows with the doc page and heading, the model's numbers and the code evidence; per-area counts, tokens, wall time.--json,--fixture/--recordreplay,--facts-onlyand--planfor the key-free half. Raw answers go to a gitignored output directory.TYPESAFE_API_KEYis read from the environment at request time only; it is never read from a file, printed or saved, and error bodies that echo it are redacted.docs/typesafe-docs-drift.md(pipeline, gate, JSON contract for phase 2),SKILL.md, catalog, README and changelog entries; plugin version 0.12.0.Independent of
feat/typesafe-triage-dry-run; both bump the plugin to 0.12.0 and touch the same changelog, catalog and.gitignorelines, so whichever merges second needs a trivial conflict resolution.Benchmark (second commit).
drift-benchmark.pyruns TypeSafe (recorded and live) and a prompt-and-parse LLM through headlessclaude -p(tools disabled, 10 pairs per call, strict JSON) on the same 60 recorded fact x section pairs. Only the recorded contender runs by default; network and CLI use is opt-in through--contenders.Validation
Offline
python3 -m unittest tests.test_typesafe_docs_drift: 47 tests pass (every extractor against a synthetic checkout, section splitting, lexical ranking, request shape, gate and fold logic, mocked HTTP transport with retry and redaction, concurrent and sequential pair execution with partial failures, fixture replay end to end, a mocked live run that records a replayable fixture, key never in stdout, stderr or saved JSON). No test contacts the network.python3 -m unittest discover -s tests: 286 tests, 10 failures and 25 errors, identical tomainbefore this change (239 tests, same 35). They come from the missingpytedependency on bare system Python 3.14 in the e2e wizard, Windows, macOS and Proxmox modules; none are in the new module.bash -non all skill scripts,git diff --check, plugin manifest JSON parse: clean.Live inference (one run)
2026-09-16 22:29 UTC, nanocoai/nanoclaw
6e5008feagainst nanoclaw-docse3e1546,jev-latest, default gates, concurrency 4: 142 facts, 426 requests, 609,315 input and 22,578 output tokens, 71.5 s. DRIFT 7, MISSING 1, UNSURE 11, OK 123. The top five DRIFT rows were checked by hand against the doc text and are real (for example the docs list anawaiting_reasonapproval status and anunknown_sender_decline_notifydrop reason that the CLI enums do not have, and sayWEBHOOK_PORTis process-environment only whilesrc/config.tsalso reads.env). The sanitized summary is on the doc page; the raw payload stays in the gitignored output directory.Review
Codex adversarial review on the diff: 8 findings, all fixed before commit (redact before truncating error bodies, prefer declared
argsover flags mentioned in help text, multiline enums, non-transport exceptions in workers, replayable partial records, failed requests areUNSUREnotMISSING, fixture mode honours--limit-facts/--plan/--facts-only, section line numbers match the source file).Limits
Detection only. Candidate recall is lexical top-3. The shipped fixture's answers are hand-written in the documented response shapes.
docs/gateway-seam.mdand a non-empty.env.exampleare not on NanoClawmainat6e5008fe; the run notes say so.Benchmark run (live inference)
2026-09-17 10:12 to 10:34 UTC, one macOS machine, 60 pairs, two passes per live contender,
claudeCLI 2.1.273,jev-latest.Precision and recall are for
contradictson a 30-pair gold file (12 positive). The gold labels were written by Claude, the LLM-assisted reviewer that built this tool, by reading the code and the doc section without looking at contender answers; no human has verified them and 3 are marked borderline. The sample was stratified by TypeSafe's own verdicts, which favors its recall, and a Claude labeler may favor the Claude contenders. One positive pair moves recall by 8 points. Method and caveats:docs/typesafe-docs-drift.md.tests/test_drift_benchmark.py, 24 tests (mocked subprocess and transport). Last clean full suite: 308 tests with the same 35 pre-existingpytefailures. The final full run also showed 19 Proxmox subprocess timeouts under machine load; both Proxmox modules pass when rerun alone (28 tests).CI flake fix (fourth commit). The ubuntu job failed in
tests/test_shared_terminal.pysetUp with a JSON decode error: the server creates its state file withO_EXCLbefore writing the body, and setUp read it as soon as the path existed. Main's own run for the #25 merge failed the same way. setUp now polls until the file parses, within the same deadline. Full offline suite withpyteinstalled in a venv: 341 tests, OK, three consecutive green runs of the shared-terminal module.