v0.3.0 — fix the plumbing, add the net - #8
Merged
Conversation
… nodes Three defects verified against shipped v0.2.0 code. The plugin's only hook has never run in any install. hooks.json passed "$FILE_PATH", but Claude Code delivers the PostToolUse payload as JSON on stdin -- there is no such variable -- so okf-curate.sh bound an empty string and exited at its first guard on every edit. It now reads .tool_input.file_path from stdin (python3, not jq: python3 is already a hard dependency, jq is not) and the matcher covers MultiEdit, which bypassed curation entirely. okf-curate.sh's fallback also stopped hand-rolling grep link checks and now calls this repo's own okf-graph.py validate, which sits next to it and understands typed edges. That deletes the realpath -m call, absent from stock macOS without coreutils, so the fallback was broken on the primary dev platform anyway. Net negative diff. parse_frontmatter silently dropped standard YAML block sequences: `tags:` followed by `- item` lines returned '', which the isinstance guard in load_bundle then turned into [] with no warning. Every generator in this repo emits inline `tags: [a, b]`, which is why sample-okf looked clean -- the bug only ever hit user-authored bundles. Mermaid node ids came from Path(...).stem, so all seven index.md files in sample-okf collapsed into one node and agents/foo.md merged with docs/foo.md. Ids now derive from the full relative path via a shared mermaid_id/render_mermaid pair, which the graph subcommand will reuse rather than growing a second emitter with the same bug. validate gains --strict, exiting non-zero on warnings so CI can gate. The default stays lenient because the skills and okf-curate.sh call validate and expect 0 on warnings. 01KYZFDBAY0AX03XQ6SAT6SXJW 01KYZFDBAY2VPBHBNXW7E4ZKHF Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKmdobV4dykomYAENb3USC
The plugin had no tests at all, and CI validated only worklog log invariants -- it never invoked scripts/okf-graph.py or sample-okf. The one instruction to run validate after edits (CLAUDE.md) was addressed to an agent, not automation, and validate exits 0 on every non-error issue anyway. So the 704-line file the plugin exists to run had nothing watching it. tests/test_okf_graph.py is plain asserts with a __main__ runner -- no pytest, no fixtures -- and takes -q, matching the convention pre-commit already uses for tests/test_fold.py. Written before the fixes in 1cabdab and confirmed failing on the block-sequence and mermaid-id cases first, so the net is known to catch what it claims. Two cases are tripwires rather than unit tests: sample-okf's concept and edge counts are quoted in the skills, and the version string lives in four manifests that have drifted before. Four lines of assert beats building a sync tool for the latter. One sharp edge worth recording: loading okf-graph.py by path requires registering it in sys.modules before exec_module, or the @DataClass decorators raise AttributeError on Python 3.13 -- dataclasses looks the class's module up in sys.modules and gets None. CI runs the suite plus validate sample-okf --strict; pre-commit gets the same guarded line as the fold tests. 01KYZFDBAZ0T4W5TZYVXSBXADV Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKmdobV4dykomYAENb3USC
CLAUDE.md requires ${CLAUDE_PLUGIN_ROOT} for intra-plugin paths, but all
five command files pointed at their skills by bare relative path, which
does not resolve from a consuming project's working directory.
bin/worklog is the honest exception: it is repo-local and genuinely not
under the plugin root, so the three references to it are annotated as
worklog-only rather than given a prefix that would resolve to nothing.
Version bumped 0.2.0 -> 0.3.0 across the four manifests and the README
row. The new version-sync assert gates this from here on.
Skipping 0.2.1: that name is already rendered as "shipped" in
docs/.index/rendered/releases/v0.2.1.md despite never having been tagged,
so reusing it would collide with what the generated docs already claim.
01KYZFDBAZ62YTTMN7SRGD2QKC 01KYZFDBAZW0F3RCN8P9J1R29A
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKmdobV4dykomYAENb3USC
The okf-visualize skill promised Mermaid, JSON, and HTML views but no emitter existed, so it told the model to "crawl all concepts via the validate load path" and hand-assemble a diagram. That is exactly the non-deterministic path CLAUDE.md's "deterministic tools first" rule is meant to avoid, and it produced a different graph every run. `graph <bundle> [--format mermaid|json|html] [--focus C] [--hops N]` reuses render_mermaid/mermaid_id rather than growing a second emitter, so pack and graph cannot drift apart and the index.md collision fixed in 89f584f stays fixed in both. Output convention: `--format json` prints JSON like every other subcommand; `mermaid` and `html` print the raw artifact. This departs from `pack`, which wraps its markdown in a JSON envelope — but pack's envelope exists because included/excluded/edges are the real payload and the markdown is a byproduct. A rendered graph is the only product here, so an envelope would force every caller through `jq -r` before the output could be pasted into a doc or redirected to a file, and `--format json` already covers the machine-readable case. The HTML view is one self-contained file: Mermaid source in a `pre.mermaid` block plus concept and edge tables, inline CSS, light and dark. No CDN script, because a diagram that only renders with network access is not a shareable artifact — and the tables carry the same information wherever the diagram does not draw. A test asserts no http(s), no `<script`, no `src=`, no `url(` so nobody re-adds one. Also ships the two missing slash commands. The plugin had seven skills and five commands; okf-visualize and okf-maintain were reachable only by auto-invoke. Isolated concepts are declared explicitly after the edge-derived node lines — render_mermaid only names nodes it sees on an edge, so orphans would otherwise appear in `--format json` and silently vanish from the diagram. 01KYZFDBAZ6G090EZ3FSS0FXS9 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKmdobV4dykomYAENb3USC
This was referenced Aug 1, 2026
17 tasks
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.
Why
All v0.2.0 work was closed and the roadmap was empty, so this started as "decide what to do next." An audit of the shipped plugin surface turned up three real defects plus zero automated coverage of
scripts/okf-graph.py— the 704-line file the plugin exists to run.If you review one thing, review the first commit. Two of the three defects were invisible from inside this repo.
What was broken
The plugin's only hook had never run, in any install.
hooks/hooks.jsonpassed"$FILE_PATH", but Claude Code delivers thePostToolUsepayload as JSON on stdin — there is no such environment variable. Sookf-curate.shbound an empty string and hit itsexit 0guard on every single edit since the feature shipped. Post-edit curation has been a no-op the entire time.parse_frontmattersilently dropped standard YAML. A block sequence —— returned
'', which theisinstance(..., list)guard inload_bundlethen converted to[]with no warning anywhere. Every generator in this repo emits inlinetags: [a, b], which is exactly whysample-okflooked clean and nobody noticed: the bug only ever hit user-authored bundles, which is the entire target audience.Mermaid node IDs collided. They came from
Path(...).stem, so all sevenindex.mdfiles insample-okfcollapsed into a single node, andagents/foo.mdmerged withdocs/foo.md. Every pack spanning more than one directory rendered a wrong graph.Nothing was watching. No tests existed anywhere in the repo. CI validated worklog log invariants only and never invoked the graph engine or
sample-okf. The one instruction to runvalidateafter edits lives inCLAUDE.md— addressed to an agent, not automation — andvalidateexits 0 on every non-errorissue anyway.What changed
hooks/hooks.json,okf-curate.sh.tool_input.file_pathfrom stdin JSON; matcher now coversMultiEdit, which bypassed curation entirelyokf-curate.shokf-graph.py validateinstead of hand-rolled grep. Deletes arealpath -mcall absent from stock macOS — that path was broken on the primary dev platform. Net negative diffokf-graph.pymermaid_id/render_mermaidderive IDs from the full relative path;validate --stricttests/test_okf_graph.pypre-commitvalidate sample-okf --strictcommands/*.md${CLAUDE_PLUGIN_ROOT}perCLAUDE.md's own rulevalidate's default stays lenient — the skills andokf-curate.shcall it and expect 0 on warnings.--strictexists so CI can gate.sample-okfpasses--stricttoday with 0 warnings.On the tests
Written before the fixes and confirmed failing on the block-sequence and mermaid-ID cases first, so the net is known to catch what it claims rather than just being green. I also verified the guard fires: reintroducing the stem-based ID bug fails the suite and blocks the commit.
Two cases are tripwires rather than unit tests —
sample-okf's concept/edge counts (the skills quote these numbers) and version consistency across the four manifests, which have drifted before. Four lines of assert beat building a version-sync tool.One sharp edge worth knowing: loading
okf-graph.pyby path requires registering it insys.modulesbeforeexec_module, or the@dataclassdecorators raiseAttributeErroron Python 3.13 —dataclasseslooks the class's module up insys.modulesand getsNone.Scope notes
Version goes to 0.3.0, skipping 0.2.1: that name is already rendered as "shipped" in
docs/.index/rendered/releases/v0.2.1.mddespite never having been tagged, so reusing it would collide with what the generated docs already claim.Deliberately not in this PR — the docs/IA drift: stale
ia-indexandwiki-publish,sync-state.jsonstill pointing atRichardHightower/okf-pluginwith dangling issue keys #1–#6, the wiki-published plan doc showing 7 unchecked boxes for closed items, no CHANGELOG, andscripts/substack_okf.py(917 lines, shipped in v0.2.0) documented nowhere. All real, all mechanical, all separable — filed for v0.3.1. That's also why the IA warnings appear in the commit output here.Also landed: the
graphsubcommandBuilt in parallel and folded in once verified, so it never gated the fixes above.
okf-graph.py graph <bundle> [--format mermaid|json|html] [--focus <concept>] [--hops N]makes theokf-visualizeskill real — it previously told the model to "crawl all concepts via thevalidateload path" because no emitter existed. It reusesrender_mermaid/mermaid_idrather than growing a second emitter, which is the reason those were extracted as a pair in the first commit.mermaid/htmlprint their artifact raw;jsonprints JSON.packwraps its markdown in a JSON envelope becauseincluded/excluded/edgesare its real payload, butgraphhas one product per invocation and--format jsonalready covers the machine-readable case — an envelope would just force every caller throughjq -rbefore the output could be redirected to a file. Error paths still return{"error": ...}and exit 1 like every other subcommand.The HTML is one self-contained file: Mermaid source in a
<pre class="mermaid">block plus concept and edge tables, inline CSS with aprefers-color-schemedark block, and no CDN script — the tables carry the same information wherever the diagram doesn't draw. A test asserts againsthttp://,https://,<script,src=,@importandurl(.This also caught a latent bug:
render_mermaidonly declares nodes it sees on an edge, so isolated concepts appeared in--format jsonand silently vanished from the diagram.packnever hit it because its neighborhood is edge-derived by construction, but a whole-bundle view does.cmd_graphnow emits explicit node lines for unlinked nodes — verified as 22/22 nodes declared with 0 dangling edge endpoints.Rounding it out:
commands/okf-visualize.mdandcommands/okf-maintain.md(all seven skills now have a command, up from five), and the subcommand tables in the README,docs/user_guide/cli-reference.md— which was missing rows forbacklinks,subgraphandorphans— andsample-okf/knowledge/tool-okf-graph-py.md, which enumerates the subcommands as part of the plugin's self-describing sample.Suite is now 16/16.
Verification
🤖 Generated with Claude Code
https://claude.ai/code/session_01LKmdobV4dykomYAENb3USC