Skip to content

v0.3.0 — fix the plumbing, add the net - #8

Merged
RichardHightower merged 4 commits into
mainfrom
feature/v0.3.0-plumbing-and-tests
Aug 1, 2026
Merged

v0.3.0 — fix the plumbing, add the net#8
RichardHightower merged 4 commits into
mainfrom
feature/v0.3.0-plumbing-and-tests

Conversation

@RichardHightower

@RichardHightower RichardHightower commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

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.json passed "$FILE_PATH", but Claude Code delivers the PostToolUse payload as JSON on stdin — there is no such environment variable. So okf-curate.sh bound an empty string and hit its exit 0 guard on every single edit since the feature shipped. Post-edit curation has been a no-op the entire time.

parse_frontmatter silently dropped standard YAML. A block sequence —

tags:
  - alpha
  - beta

— returned '', which the isinstance(..., list) guard in load_bundle then converted to [] with no warning anywhere. Every generator in this repo emits inline tags: [a, b], which is exactly why sample-okf looked 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 seven index.md files in sample-okf collapsed into a single node, and agents/foo.md merged with docs/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 run validate after edits lives in CLAUDE.md — addressed to an agent, not automation — and validate exits 0 on every non-error issue anyway.

What changed

Area Change
hooks/hooks.json, okf-curate.sh Read .tool_input.file_path from stdin JSON; matcher now covers MultiEdit, which bypassed curation entirely
okf-curate.sh Fallback calls this repo's own okf-graph.py validate instead of hand-rolled grep. Deletes a realpath -m call absent from stock macOS — that path was broken on the primary dev platform. Net negative diff
okf-graph.py Block-sequence YAML parses; mermaid_id/render_mermaid derive IDs from the full relative path; validate --strict
tests/test_okf_graph.py New — 11 cases, plain asserts, no framework
CI + pre-commit Run the suite and validate sample-okf --strict
commands/*.md Skill paths qualified with ${CLAUDE_PLUGIN_ROOT} per CLAUDE.md's own rule

validate's default stays lenient — the skills and okf-curate.sh call it and expect 0 on warnings. --strict exists so CI can gate. sample-okf passes --strict today 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.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.

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.md despite 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-index and wiki-publish, sync-state.json still pointing at RichardHightower/okf-plugin with dangling issue keys #1#6, the wiki-published plan doc showing 7 unchecked boxes for closed items, no CHANGELOG, and scripts/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 graph subcommand

Built 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 the okf-visualize skill real — it previously told the model to "crawl all concepts via the validate load path" because no emitter existed. It reuses render_mermaid/mermaid_id rather than growing a second emitter, which is the reason those were extracted as a pair in the first commit.

mermaid/html print their artifact raw; json prints JSON. pack wraps its markdown in a JSON envelope because included/excluded/edges are its real payload, but graph has one product per invocation and --format json already covers the machine-readable case — an envelope would just force every caller through jq -r before 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 a prefers-color-scheme dark block, and no CDN script — the tables carry the same information wherever the diagram doesn't draw. A test asserts against http://, https://, <script, src=, @import and url(.

This also caught a latent bug: render_mermaid only declares nodes it sees on an edge, so isolated concepts appeared in --format json and silently vanished from the diagram. pack never hit it because its neighborhood is edge-derived by construction, but a whole-bundle view does. cmd_graph now emits explicit node lines for unlinked nodes — verified as 22/22 nodes declared with 0 dangling edge endpoints.

Rounding it out: commands/okf-visualize.md and commands/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 for backlinks, subgraph and orphans — and sample-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

python3 tests/test_okf_graph.py                      # 11/11
python3 scripts/okf-graph.py validate sample-okf --strict

# the hook now fires; before this PR it printed nothing at all
echo '{"tool_name":"Write","tool_input":{"file_path":"'"$PWD"'/sample-okf/index.md"}}' \
  | scripts/okf-curate.sh

🤖 Generated with Claude Code

https://claude.ai/code/session_01LKmdobV4dykomYAENb3USC

RichardHightower and others added 4 commits August 1, 2026 15:16
… 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
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