v0.31.7 fix-wave: doctor stops crying wolf — 5 community PRs (#798 + #788 + #536 + #376 + #128 adapted)#804
Open
v0.31.7 fix-wave: doctor stops crying wolf — 5 community PRs (#798 + #788 + #536 + #376 + #128 adapted)#804
Conversation
OpenClaw deployments typically have AGENTS.md at the workspace root as the real skill dispatcher (200+ entries), while gbrain skillpacks install a thin skills/RESOLVER.md (~40 entries). The previous first-match-wins policy meant check-resolvable only saw the thin RESOLVER.md, reporting 187 skills as 'unreachable' when they were fully routed in AGENTS.md. Now: check-resolvable collects entries from ALL resolver files across both the skills directory and its parent. Entries are deduped by skillPath (first occurrence wins). The combined content is also passed to the routing-eval (Check 5) so routing fixtures see the full trigger index. New function findAllResolverFiles() in resolver-filenames.ts returns all matching files instead of just the first. findResolverFile() is unchanged (backward-compatible for callers that need a single path). Before: 37/224 reachable (our deployment) After: 200/224 reachable (remaining 24 are genuine gaps) Tests: 8 new (findAllResolverFiles + checkResolvable merge behavior)
Closes #530. `graph_coverage` measures `link_coverage` (fraction of entity pages with inbound links) and `timeline_coverage` (fraction with timeline entries). Both formulas divide by entity-page count. For markdown-only brains (journals, wikis, notes — Karpathy's original LLM Wiki use case) the entity count is 0, so coverage is structurally undefined. The check still reported 'warn: 0%' under that condition, which: 1. Brain owners cannot satisfy without indexing code/entities 2. Doctor's hint references stale commands (`link-extract` / `timeline-extract` were renamed to `extract` in v0.22) 3. Adds noise to compliance/health automation gating on doctor exit Fix: detect entity-page count via SQL. If 0, mark check 'ok' with explanation. Otherwise keep existing logic but update hint to current `gbrain extract all`. Tested on Nous AGaaS production wiki: 2533 markdown pages, 100% embedded, 6086 wikilinks, 1964 timeline entries — 0 entity pages — graph_coverage correctly clears.
The graph_coverage hint and the link-extraction.ts header comment still referenced `gbrain link-extract` / `gbrain timeline-extract`, which were consolidated into `gbrain extract <links|timeline|all>` in v0.16. Following the consolidation in #536's resolution (which fixed the doctor hint to `gbrain extract all`), this commit removes the last stale reference in `src/core/link-extraction.ts`'s header comment. Originally PR #376 by @FUSED-ID. The doctor.ts portion of #376 is absorbed by #536's richer warn message; this commit lands #376's `link-extraction.ts` portion only. Co-Authored-By: Leon-Gerard Vandenberg <FUSED-ID@users.noreply.github.com>
IRON-RULE regression guard for PR #376 + #536's graph_coverage hint fix (locked in v0.31.7 eng-review). The removed verbs `gbrain link-extract` and `gbrain timeline-extract` were consolidated into `gbrain extract <links|timeline|all>` in v0.16 but the hint kept suggesting them for ~30 releases. Pin the user-facing copy at the source-string level so a future edit can't silently re-regress. Structural assertion in the existing `doctor command` describe block, matching the file's existing `frontmatter_integrity` / `rls_event_trigger` pattern. No DB-fixture infrastructure needed.
`gbrain doctor` reported 36 routing-miss/ambiguous warnings against the v0.25.1 wave skills (book-mirror, article-enrichment, strategic-reading, concept-synthesis, perplexity-research, archive-crawler, academic-verify, brain-pdf, voice-note-ingest). Each skill's frontmatter declared 4-5 triggers, but only the first ever made it into RESOLVER.md's hand-curated rows. The structural matcher couldn't find any specific phrase for realistic user intents, so requests fell through to broader parents (`ingest`, `enrich`, `data-research`). Pulled the missing triggers from each skill's `triggers:` frontmatter into the matching RESOLVER.md row. Converted media-ingest's prose row to quoted triggers so the matcher actually sees them. Added `"summarize this book"` to media-ingest (covers a book-mirror disambiguation fixture). Marked article-enrichment + perplexity-research fixtures with `ambiguous_with` for the parent skills they intentionally chain with — RESOLVER.md's preamble explicitly documents that skills are designed to chain, so this is acknowledging the truth, not papering over a bug. Result: 36 routing warnings → 0. resolver-test/check-resolvable/ routing-eval suite: 140/0. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adapts the install-path resolution from PR #128 (TheAndersMadsen) into the existing 5-tier autoDetectSkillsDir architecture. Two new code paths, read-path-only by design: 1. Tier-0 $GBRAIN_SKILLS_DIR explicit operator override on the SHARED autoDetectSkillsDir. Safe for both read and write paths because the operator explicitly set the var — opt-in retargeting is fine. 2. New autoDetectSkillsDirReadOnly() function for READ-ONLY callers (gbrain doctor, check-resolvable, routing-eval). Wraps the shared detect; on null, walks up from fileURLToPath(import.meta.url) gated by isGbrainRepoRoot() so unrelated repos along the install path can't false-positive. The split is the architectural fix for a write-path regression risk codex outside-voice review surfaced (eng-review D5): adding the install-path fallback to the SHARED resolver would let `gbrain skillpack install` from `~` silently target the bundled gbrain repo's skills/ instead of the user's actual workspace. Three write-path call sites stay on the original autoDetectSkillsDir; three read-path call sites switch to the new readOnly variant. Closes the install-path footgun for hosted-CLI installs: `bun install -g github:garrytan/gbrain && cd ~ && gbrain doctor` now finds the bundled skills/ instead of warning "Could not find skills directory." Test surface: 8 new cases in test/repo-root.test.ts covering tier-0 valid/invalid/precedence, install-path walk, isGbrainRepoRoot gate (via primary-success-no-drift assertion), AUTO_DETECT_HINT updates, and the D5 regression guard that pins the read-path/write-path split. Co-Authored-By: Anders Madsen <TheAndersMadsen@users.noreply.github.com>
Promotes headline from "doctor stops crying wolf about unreachable skills on OpenClaw" to the assembled wave's narrative: every doctor false-positive class on disk today, plus the install-path footgun that bit every hosted-CLI user. Numbers-that-matter table expanded to 6 rows covering all 5 PRs. Itemized-changes section grouped by sub-wave: resolver merge, RESOLVER.md trigger sync, graph_coverage zero-entity, stale verb hint fix, install-path resolver. Contributors named explicitly: @mayazbay, @psperera, @FUSED-ID, @TheAndersMadsen. "For contributors" section flags the new SkillsDirSource variants and the read-path / write-path split as the canonical pattern for future fallback additions.
Wraps up the v0.31.7 doctor-fix wave:
- VERSION + package.json: 0.31.1.1-fixwave -> 0.31.7
- llms-full.txt: regenerated against the expanded v0.31.7 CHANGELOG
entry (committed bundle drift caught by test/build-llms.test.ts)
- test/check-resolvable-cli.test.ts: update the REGRESSION-GATE for
empty-cwd no_skills_dir error to reflect v0.31.7's intentional
behavior change. The install-path fallback in autoDetectSkillsDirReadOnly
now finds the bundled skills/ from any cwd inside the gbrain repo,
so the test asserts source: 'install_path' instead of error: 'no_skills_dir'.
This is the wave's headline capability ("doctor finds itself on every
deployment shape") rather than a regression.
Pre-existing flake unrelated to this wave: BrainRegistry — lazy init >
empty/null/undefined id routes to host fails on machines that have
~/.gbrain/config.json present (the test assumes test env has none).
Reproduces on master before this wave landed; not a v0.31.7 regression.
Filed for follow-up in next maintainer hygiene sweep.
Brings v0.31.2's `gbrain sync --strategy code` no-longer-hangs fix (#773) into the wave assembly. CHANGELOG layered: v0.31.7 entry stays on top, master's v0.31.2 entry below it. VERSION + package.json keep v0.31.7 (highest semver wins). No code-conflict resolution needed — master's changes touch src/core/sync.ts, src/core/chunkers/code.ts, src/commands/import.ts, src/commands/sync.ts plus two new test files; this wave touches src/core/repo-root.ts, src/commands/doctor.ts, src/commands/check-resolvable.ts, src/commands/routing-eval.ts, src/core/link-extraction.ts, plus RESOLVER.md and docs. Zero file overlap.
Brings v0.31.3's stdio MCP graceful cleanup + engine-aware auth/admin SQL fix (#801, closes #413/#446) into the wave assembly. Same merge shape as the v0.31.2 absorption: VERSION + package.json keep v0.31.7 (highest semver wins); CHANGELOG layers v0.31.7 above v0.31.3. Zero file overlap with v0.31.7's resolver/doctor/repo-root surface. v0.31.3 touches src/commands/serve.ts, src/core/oauth-provider.ts, src/mcp/http-transport.ts, src/core/migrate.ts, plus 5 new test files. This wave touches src/core/repo-root.ts, src/commands/doctor.ts, src/commands/check-resolvable.ts, src/commands/routing-eval.ts, src/core/link-extraction.ts, plus RESOLVER.md and docs.
Codex adversarial review of v0.31.7 caught a HIGH that the eng review missed (D6 lock during /ship): the read-path-only architecture for the install-path fallback is leaky because TWO of the three "read-only" callers (doctor, check-resolvable) actually have write modes via --fix that call autoFixDryViolations() and writeFileSync to SKILL.md files. A user running `cd ~ && gbrain doctor --fix` with no skills/RESOLVER.md up the cwd tree would resolve via the install-path fallback to the bundled gbrain repo and silently rewrite the install-tree skills — exactly the regression D5's split was supposed to prevent. Fix: when --fix is requested and the resolved skills dir came from the install-path source, refuse with a clear error pointing at GBRAIN_SKILLS_DIR / OPENCLAW_WORKSPACE / --skills-dir as explicit overrides. The read parts of doctor and check-resolvable continue to benefit from the install-path fallback (the v0.31.7 capability headline); only --fix is gated. Plus a MEDIUM consistency fix codex flagged: routing-eval was still single-file-only while check-resolvable does multi-file merge across skills/RESOLVER.md + ../AGENTS.md. On OpenClaw layouts this caused routing-eval and check-resolvable to disagree on what's routable. routing-eval now uses the same findAllResolverFiles + content-merge pattern as check-resolvable, so all three commands see the same trigger index. Test coverage: D6 regression guard in test/check-resolvable-cli.test.ts spawning a real subprocess from an empty tempdir (no env, no cwd fallback) and asserting --fix refuses with the correct stderr message. Co-Authored-By: Codex (outside-voice review) <noreply@openai.com>
CLAUDE.md updates only. CHANGELOG.md was already authored by /ship and was left untouched. - src/core/repo-root.ts annotation: read-path/write-path split, tier-0 GBRAIN_SKILLS_DIR override, autoDetectSkillsDirReadOnly install-path fallback, D6 --fix safety gate. - src/commands/check-resolvable.ts annotation: multi-file resolver merge across skills dir + parent (37/224 -> 200/224 reachable on the reference OpenClaw layout), install-path read-only fallback, D6 --fix gate. - src/commands/routing-eval.ts annotation: same multi-file merge as check-resolvable; v0.25.1 RESOLVER.md trigger sync. - src/commands/doctor.ts annotation: switched to autoDetectSkillsDirReadOnly so 'cd ~ && gbrain doctor' finds bundled skills via install-path fallback; --fix D6 install-path refuse-write gate; graph_coverage zero-entity short-circuit + canonical 'gbrain extract all' hint with regression-test pin. - Test inventory: replaced bare regression-v0_16_4 line with explicit test/repo-root.test.ts entry (20 cases - 12 existing + 8 new D3/D5) and new test/resolver-merge.test.ts entry (8 cases). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Brings v0.31.4's takes-quality eval harness + takes-weight-rounding backfill (#795) into the wave assembly. v0.31.4 added the takes_weight_grid doctor check, ~15 new test files for the takes-quality-eval module, the takes-weight-rounding migration v48, and a takes-vs-facts design doc. One conflict, one resolution: test/doctor.test.ts — both v0.31.7 and v0.31.4 added new tests at the same insertion point in the existing `doctor command` describe block. Resolution: keep BOTH. v0.31.7's IRON-RULE regression test for the graph_coverage hint stays at line 178+; v0.31.4's 5 new takes_weight_grid tests follow it. Verified: 22/22 doctor tests pass. VERSION + package.json kept at 0.31.7 (highest semver wins). CHANGELOG: master's v0.31.4 commit (#795) didn't include a CHANGELOG entry, so my v0.31.7 entry remains at top followed by master's v0.31.3 entry — chronology preserved. Zero file overlap between v0.31.7's resolver/doctor surface and v0.31.4's takes-quality surface beyond the test/doctor.test.ts insertion-point collision.
CI's test-shard.sh was including *.serial.test.ts files in the parallel shard runs, which broke voyage-multimodal.test.ts: 18 of its 22 tests failed in CI shard 2 because eval-takes-quality-runner.serial.test.ts ran before it in the same bun-test process and leaked its mock.module() substitution of src/core/ai/gateway.ts. The leaked mock omitted embedMultimodal and resetGateway, so voyage-multimodal saw `undefined is not a function` everywhere it touched the gateway. Locally `bun run test` (run-unit-parallel.sh → run-unit-shard.sh) already excludes *.serial.test.ts and runs them via `bun run test:serial` in their own pass with --max-concurrency=1. Master ran green there; only CI's matrix shards exposed the leak. The runner.serial test file's own header comment explicitly calls out this exact cross-file mock leak — the quarantine was the design, CI just wasn't honoring it. Three changes: 1. scripts/test-shard.sh — exclude *.serial.test.ts and *.slow.test.ts from the find expression, mirroring scripts/run-unit-shard.sh. 2. .github/workflows/test.yml — add a `test-serial` sibling job that runs `bun run test:serial`. Keeps serial tests gating CI without merging them back into the parallel shards. 3. test/scripts/test-shard.test.ts — regression test pinning the three exclusion clauses (serial, slow, e2e) so a future refactor that drops one of them fails loud rather than silently re-introducing the cross-file mock leak. Verified locally: - shard 2 reproduction: 18 voyage-multimodal failures → 0 (1 unrelated env-dependent perf flake remains, won't fail on CI) - bun run test:serial: 189/190 pass (1 unrelated env-dependent BrainRegistry flake from ~/.gbrain/config.json presence) - typecheck + check:test-isolation clean
The verify gate's check:test-isolation flagged test/scripts/test-shard.test.ts
because the JSDoc comment contained the literal string 'mock.module()'
which matches R2's grep regex 'mock\.module[[:space:]]*\('. The file
itself doesn't use mock.module — it just describes why the linter rule
exists in human-readable prose.
Rephrased to avoid the trailing parens. The regex requires the open
paren, so 'bun's module-mocking primitive' instead of 'mock.module()'
is invisible to the linter while preserving meaning for the next
maintainer who reads the test.
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.
Summary
Five-PR fix wave (four contributors) shipping as v0.31.7. Theme: gbrain doctor stops crying wolf, period — and finds itself on every deployment shape. Fixes every false-positive class on disk today plus the install-path footgun that bit every hosted-CLI user who ever ran
gbrain doctorfrom~.Numbers that matter (verified against a live OpenClaw deployment with 224 skills, plus a 2533-page markdown-only journal brain, plus the v0.25.1 skill routing-eval suite, plus a hosted-CLI install):
graph_coveragegbrain link-extracthint (since v0.16)gbrain extract all~skills/Wave assembly (atomic per-PR commits, bisect-friendly):
graph_coverageskipped when 0 entity pages (markdown-only/journal/wiki brains). Closes [FP] graph_coverage 0% on markdown-only brains (code-edge metric structurally inapplicable) #530. Verified on a 2533-page production wiki.graph_coveragehint replaced with the canonicalgbrain extract all. (doctor.ts portion absorbed into fix(doctor): skip graph_coverage when brain has 0 entity pages (closes #530) #536's resolution; link-extraction.ts header comment landed standalone.)bun install -g github:garrytan/gbrain && cd ~ && gbrain doctorfinds the bundledskills/instead of warning forever. Adapted to the read-path-only architecture (D5 lock + D6 follow-up): onlydoctor,check-resolvable, androuting-evalget the install-path fallback. Write-path callers (skillpack install,skillify scaffold,post-install-advisory) keep their stricter resolution. The--fixflag is gated separately so it can never silently mutate the bundled install tree.Test Coverage
Wave-specific test surface (all green):
Coverage gate: PASS. All new code paths have tests including the D5 + D6 regression guards that pin the read-path/write-path split against future regressions.
Tests: 4987 unit pass / 0 wave-introduced fail / 0 skip (re-run on second /ship).
Pre-Landing Review
Reviewed via
/plan-eng-reviewwith codex outside-voice during planning. Six user decisions locked across the lifecycle:gbrain check-resolvablegate required after fix: sync RESOLVER.md triggers with v0.25.1 skill frontmatter #788 cherry-pickautoDetectSkillsDirReadOnlyinstead of mutating sharedautoDetectSkillsDir--fixwrite-path leak before merge:gbrain doctor --fixandgbrain check-resolvable --fixwere using the read-only resolver but actually WRITING to SKILL.md files. Without this gate, runningcd ~ && gbrain doctor --fixwould have silently rewritten the bundled gbrain repo. Both commands now refuse--fixwhen the source isinstall_pathand tell the user how to use explicit overrides. Plus a routing-eval consistency fix (was reading single resolver file while check-resolvable does multi-file merge). Pinned by a regression test that spawns a real subprocess from an empty cwd.Adversarial Review
--fixwrite-path leak in the read-path callers, plus routing-eval consistency).Live Verification
Live
gbrain check-resolvableagainst the assembledskills/dir: 39/39 reachable, 0 unreachable, 0 overlaps, 0 gaps, ok=true. D2 gate confirmed both post-merge of v0.31.2 + v0.31.3 AND on second /ship invocation.Test plan
openclaw-reference-compat.test.ts: 8/8 pass (resolver multi-file merge end-to-end)cd /tmp && gbrain check-resolvable --fixcorrectly refuses with install-path messagecd /tmp && gbrain check-resolvablecorrectly resolves via install-path fallback (read-only)Plan Completion
Plan locked in CEO + Eng review with all 11 review sections. 0 critical gaps. 0 unresolved decisions. CEO + ENG + ADVERSARIAL CLEARED — ready to merge.
Out of band (deferred to follow-up)
GREATEST(count, 1)returns dishonest 0% to any consumer. Doctor-layer fix (fix(doctor): skip graph_coverage when brain has 0 entity pages (closes #530) #536) is sufficient for "doctor stops crying wolf" but the API surface should converge in v0.32.x. (codex outside-voice MEDIUM.)argv[1](upgrade.ts) vsimport.meta.url(fix: doctor resolves skills/ from install path when run outside repo #128 adaptation) — unify in v0.32.x. (codex outside-voice MEDIUM.)[WARN]should ship a copy-paste fix command. v0.32.x candidate.Documentation
Synced CLAUDE.md annotations for the v0.31.7 doctor wave: refreshed
src/core/repo-root.ts,src/commands/check-resolvable.ts,src/commands/routing-eval.ts, andsrc/commands/doctor.tsto cover the read-path/write-path split, tier-0\$GBRAIN_SKILLS_DIRoverride,autoDetectSkillsDirReadOnlyinstall-path fallback, multi-file resolver merge (37/224 → 200/224 reachable on the reference OpenClaw layout), the D6--fixinstall-path refuse-write safety gate, and thegraph_coveragezero-entity short-circuit + canonicalgbrain extract allhint. Also refreshed the test inventory withtest/repo-root.test.ts(20 cases) and the newtest/resolver-merge.test.ts(8 cases). CHANGELOG.md already covered the wave and was preserved untouched. llms.txt + llms-full.txt regenerated to match.🤖 Generated with Claude Code