okf: generalize into a reusable OKF knowledge-bundle CLI - #27
Merged
Conversation
All four commands now consume a single strict loader (loadContext(): root + normalized config + bundle path) instead of viz privately reading okf-viz.toml while validate/index/scaffold hardcoded knowledge/. Fixes the latent validate.ts repo-link misresolution and index-gen's literal '# knowledge' root heading under a non-default bundle.dir. Malformed config now fails every command, matching viz's strict philosophy. Step 1a of the okf generalization arc (okf.toml rename is 1b).
The file is no longer viz settings — it is the okf workspace config. Legacy okf-viz.toml still loads with a deprecation warning (dropped when the generalization arc completes). Pages CI trigger path updated in the same commit so config edits keep redeploying the published graph. Decision record: knowledge/decisions/okf-toml-unified-config.md (also documents the loadContext() singleton chosen over the planned run(ctx) dispatch and why). Step 1b of the okf generalization arc. Verified: validate 156/0/0, scaffold 0-written, viz delta exactly the new decision doc (146 nodes/493 edges/288 files, commit links 33/42 unchanged), 244 bun tests, store-built okf identical.
required-fields ('type' always enforced), recommended-fields,
reserved-files, rooted-links (error|allow), repo-links
(check|ignore|forbid) — previously the hardcoded RESERVED /
PROFILE_FIELDS constants in lib.ts and inline rules in validate.ts.
Defaults reproduce prior behavior exactly; this repo's okf.toml sets
nothing. splitCliSections() in config-cli.ts consumes CLI-only sections
before the viewer normalizer sees the raw TOML (which strict-rejects
unknown keys), keeping them out of the #data embed.
New pure tests: flakes/okf/test/config-cli.test.ts (11 cases).
Battery: validate 156/0/0, viz counts unchanged, 255 bun tests,
store-copy identical; scratch-bundle spot check confirms overrides
react (rooted-links=allow, recommended-fields=[], unknown-key failure).
Step 2 of the okf generalization arc.
All version-control access moves behind vcs/{types,git,index}.ts —
lib.ts is now pure text helpers (frontmatter/links/walk/slug/ANSI).
Git provider = the batched one-spawn implementations moved verbatim.
Forge-agnostic outbound links: [vcs] commit-url-template (default
'{url}/commit/{hash}'; [repo] stays as deprecated alias of [vcs]),
remote detection normalizes any https/scp/ssh origin, repoNameFromUrl
derives header names from any forge (subgroup chains kept whole). The
viewer fills {hash} from an embedded pre-substituted template and no
longer knows what GitHub is.
Decision record: knowledge/decisions/okf-vcs-provider.md.
Battery: validate 157/0/0, commit links 33/42 unchanged (the step's
oracle), 264 bun tests + svelte-check clean, store-built okf identical.
Step 3 of the okf generalization arc; the 'none' provider and [vcs]
provider selection land next.
okf now runs with no version control at all. [vcs] provider = auto|git|none (auto = git only when the root is a git toplevel — the batched git paths are toplevel-relative, and silent mtime fallback would let a later scaffold --force rewrite every timestamp; explicit git on a non-toplevel root fails loudly instead). The none provider walks the filesystem (junk names + [vcs] ignore globs + the generated viz output skipped), stamps mtime dates with git-matching newest-under-prefix dir semantics, and has no citations/remote. Workspace root is discovered config-first: nearest okf.toml at or above cwd (resolves the config<->root circularity, enables monorepo sub-bundles), else the git toplevel as before. Verified: no-git scratch bundle validates + builds viz with git removed from PATH; 275 bun tests incl. tempdir fixtures (git-dependent cases skip in the git-less nix sandbox — checks.test green); full battery on this repo unchanged (git auto-selected, commit links 33/42). Step 4 of the okf generalization arc.
[facet.<n>.nix-packages] generalizes to a [facet.<n>.classify] tagged union: provider 'nix-optional-attrs' (existing optionalAttrs parser, still built-in — a Nix battery costs non-Nix users nothing) or 'command' (any argv run at the workspace root printing a JSON name->value map; non-zero exit / bad JSON / out-of-values FAIL the viz build). Shared keys: types + key = basename|id. Legacy nix-packages spelling normalizes to the union (idempotent, tested); this repo's okf.toml moved to the canonical spelling same commit. Decision record: knowledge/decisions/okf-facet-classify.md. Verified: platform facet map byte-identical to baseline (5 entries); command provider smoke-tested (success/failure/out-of-range) in a scratch bundle; viz boots to interactive in headless Chrome (--perf); 278 bun tests + svelte-check clean; store-built okf identical. Step 5 of the okf generalization arc.
okf scaffold is now a generic driver. The 566-line dotfiles metadata pass moved out of the flake to scripts/okf-scaffold.ts (mechanical port), dynamically imported per okf.toml [scaffold] script with an injected ScaffoldContext (flakes/okf/scaffold-api.ts: emit owning idempotence/--force/counters, VCS timestamps, configurable-marker comment extraction, text helpers). Scripts use type-only okf imports + builtins, so vendored/cloned/store okf all work and the script survives a future okf repo split. [scaffold] command is the non-JS escape hatch (OKF_* env); [[scaffold.collect]] adds a declarative glob+template tier (placeholders validated at config load) for simple repos. Gold check: pre/post worktree scaffold --force diffed BYTE-IDENTICAL modulo the pre-existing now()-fallback on 4 merge-added packages (git log --name-only omits merges; tracked as a follow-up task). Battery: 0-written/100-skipped unchanged; store-built okf runs the repo script; collect tier smoke-tested in a scratch workspace (comment extraction, templates, idempotence, validate-clean output); 285 bun tests + svelte-check clean. Step 6 of the okf generalization arc.
Help text derives the bundle dir, viz output path, and the profile-doc footer pointer from the workspace's okf.toml via quietBundleInfo() — a never-throwing loader, so a broken config can't break 'okf help'. The repo-specific docs footer (.claude skill path) is gone; OKF_PROG mechanism unchanged (dev.nix untouched). New 'okf init [--dir=<d>]': commented starter okf.toml + bundle skeleton (<d>/index.md with okf_version, <d>/log.md) at cwd, which becomes the workspace root. Never overwrites; re-run is a no-op. Flag dispatcher now accepts declared '--flag=<value>' spellings. Smoke: init -> validate 0/0 -> index -> viz all green in a fresh dir; --dir=kb variant green; re-init no-op; battery on this repo unchanged (159/0/0, 0-written, commit links 33/42); store-built okf help OK. Step 7 of the okf generalization arc.
- flakes/okf source carries no dotfiles assumptions: header comments genericized, okf-viz.toml legacy fallback removed, package.nix homepage TODO'd for the eventual repo split (maintainer stays). - Test fixtures neutralized (Darwin/NixOS Module -> Alpha/Beta Module, nvim->wiki, nebula->europa, kriswill/dotfiles -> acme/widgets, incl. URL-encoded hash forms); 'knowledge' remains only as okf's generic default. 285 tests green, git-less nix sandbox check green. - README: generic tool doc with a no-Nix adoption section (copy/vendor + bun install + init). - Acceptance: three-way second-repo smoke on a fresh non-Nix Python repo — (A) git provider with real commit timestamps, (B) .git deleted -> none provider, (C) okf copied standalone + bun install, no Nix and no git anywhere. init/scaffold/index/validate/viz green in all three. Closing decision record: knowledge/decisions/okf-generalization.md. This repo's behavior unchanged end to end (validate 160/0/0, scaffold 0-written, commit links 33/42, platform map 5 entries). Step 8 of 8 — okf is now reusable by any project; the repo split remains a one-line input-URL swap.
Files created during merge conflict resolution (in neither parent) never appeared in the batched git log --name-only pass, so lastModified returned null and scaffold --force stamped them with the current time. Combined diff (-c) lists exactly what a merge changed vs all parents; clean merges list nothing, so no other dates move. first-parent was rejected: it would restamp entire PRs with their merge date.
# Conflicts: # knowledge/log.md
The --diff-merges=c fix landed (okf-vcs-provider.md); the closing arc record no longer lists it as a remaining gap.
…fields, port stripping
Three confirmed bugs from the xhigh code review:
- scaffold collect tier: output templates expanded before {repo}/
{description}/{description-sentence} existed, silently emitting
filenames like 'widget-{description}.md' or 'widget-.md'. Fixed at
the right altitude: OUTPUT_PLACEHOLDERS restricts output to the
path-safe set ({path} {name} {Title} {dir} {timestamp}) at config
load, with a message naming the allowed set ({repo} is derived FROM
the output path — circular by construction).
- validate: '!v || v === ""' never flags an empty array ([] is
truthy), so a user-configured array-typed required field passed
empty. Now: empty covers "" and [].
- repoNameFromUrl: explicit ports leaked into the display name
('8080/team/repo') for manual [vcs] url overrides, which never pass
through normalizeRemoteUrl's port stripping. Regex now drops ports.
Also from the review: git-missing vs not-a-repo error messages
distinguished again (Bun.which hint in loadContext + createProvider);
[vcs] non-table self-validated like [profile]/[scaffold]; isObj +
fieldIn deduped (exported from viz-app/config.ts, one copy);
PLACEHOLDER_RE shared between validation and expansion (can't drift);
none-provider directory dates memoized; dead exports trimmed
(findConfigUp un-exported + names param dropped, ConceptDoc and
FacetClassify un-exported); ctxCache singleton invariant documented.
3 new regression tests (289 pass); svelte-check clean; all three bug
repros re-run and confirmed fixed; battery unchanged (validate
160/0/0, scaffold 0-written, viz 33/42 commit links); store-built okf
identical.
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
Turns
okf(flakes/okf/) from a dotfiles-specific tool into a generic OKF knowledge-bundle CLI any project can adopt — any language or domain, no Nix required, git optional (or no version control at all). Finished extraction-ready in-tree: splitting okf to its own repository remains a one-line input-URL swap.Eight steps, one commit each (plus the merge-date fix), every step leaving the repo fully green:
okf.toml(renamed fromokf-viz.toml) — one strict loader (config-cli.ts) for all commands;bundle.dirhonored everywhere (validate/index/scaffold previously hardcodedknowledge/). Pages CI trigger updated in the same commit.[profile]— validation policy (required/recommended fields, reserved files, rooted/repo link rules) moves from code to config; defaults reproduce prior behavior exactly.VcsProvideradapters (vcs/) — git's batched one-spawn implementations moved verbatim behind an interface;lib.tsis now pure text helpers. Forge-agnostic revision links via[vcs] commit-url-template({url}/commit/{hash}; GitLab/-/commit/); remote detection accepts any https/scp/ssh origin.noneprovider + config-first discovery — nearestokf.tomlat/above cwd defines the workspace (monorepo sub-bundles work), else the git toplevel; the filesystem provider (fs walk + ignore globs + mtime) runs with git absent from PATH.autopicks git only at a git toplevel; explicitgiton a nested root fails loudly instead of silently mtime-dating.classifyproviders — the NixoptionalAttrsparser stays built-in; newprovider = "command"runs any argv printing a JSON name→value map (failures fail the build). Legacynix-packagesspelling still normalizes.scripts/okf-scaffold.ts, driven by an injectedScaffoldContextAPI (type-only okf imports — vendored/cloned/store okf all work); plus declarative[[scaffold.collect]]glob+template entries and a non-JScommandescape hatch.okf init— help derives paths from the workspace config via a never-throwing loader;initbootstraps a starterokf.toml+ bundle skeleton, never overwrites.Also includes the
--diff-merges=cfix: files introduced by merge conflict resolution now get their real commit date, makingscaffold --forcefully deterministic (evil-merge fixture test added).Decision records:
okf-toml-unified-config,okf-vcs-provider,okf-facet-classify,okf-scaffold-hook, closing umbrellaokf-generalization(all underknowledge/decisions/).Verification
okf validate0 errors/0 warnings throughout;okf scaffold= 0 written/100 skipped; viz commit links 33/42 and the platform facet map (5 entries) byte-stable; 286 bun tests + svelte-check clean;nix build/nix runstore copy verified per step (incl. the git-less sandbox test check).scaffold --forceoutput diffed byte-identical..gitdeleted → identical on the none provider; (C) okf copied standalone +bun install→ green with no Nix and no git anywhere.scaffold --forceruns now byte-identical (merge-date fix).Note for merge: the Pages workflow deploys the public graph (kris.net/dotfiles) on merge to main — the trigger path moved from
okf-viz.tomltookf.tomlin this PR; worth watching the first run.