ckb/.agents/skills/ckb-export-okf/SKILL.md
Michał Kopeć 65b1e422b3 Add decision log, scriptify OKF export and lint detection
Decision log (VERSION 1.6.0, kb_schema_version 1.4):
- wiki/decisions/ scaffold — numbered NNNN-slug.md records, own index
  (with status vocabulary) and log
- type: decision adds status/decided_on/decided_by/affects/review_on;
  supersedes/superseded_by carry history and must be set on both sides
- New ckb-decide skill: records decisions and answers what/why/who/when,
  what superseded what, and what is still open. Decision pages are
  append-only — a changed mind is a new superseding decision
- Graph gains decided_by and affects edge types
- ckb-ingest routes decisions found in raw material to this format;
  ckb-retrieve gains the decisions index as a cascade step

Scriptified the mechanical skills:
- ckb-export-okf/scripts/export_okf.py does the whole OKF transform
  (frontmatter remap, link rewriting, index/log regeneration, conformance
  validation); --check validates without writing
- ckb-lint/scripts/lint_report.py does the read-only detection half
  (conformance, freshness, confidence, retention, decisions, orphans,
  graph, index/log, source.yaml); judgment calls stay with the model

Also: removed the duplicate personal quiz skill, fixed stale cbk-quiz
doc paths, gitignored __pycache__.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 20:50:36 +02:00

159 lines
8.3 KiB
Markdown

---
name: ckb-export-okf
description: Export the local wiki/ knowledge base as an Open Knowledge Format (OKF) v0.1-conformant bundle at outputs/okf/, so any generic OKF tool can consume it. Use when the user asks to "export the wiki as OKF", "generate the OKF bundle", "export to OKF", "publish the OKF export", or "sync outputs/okf".
---
# Export to OKF skill
## Purpose
Regenerate `outputs/okf/` from the current `wiki/` tree as a bundle that
conforms to Google's Open Knowledge Format (OKF) v0.1 spec
(https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md),
so any generic OKF consumer (e.g. Google's reference graph visualizer) can
read this knowledge base without needing to understand this repo's richer,
more opinionated schema (`confidence`, `quality`, `retention`, `supersedes`,
dual-linking, the cascade layers). This is a one-way, on-demand export —
`wiki/` stays the authoritative source; `outputs/okf/` is always a derived
artifact of it, never edited by hand and never fed back in.
The whole transform — frontmatter remapping, link rewriting, index and log
regeneration, conformance validation — is done by a deterministic Python
script, not by reading and rewriting every page by hand. The mapping is a
fixed ruleset over a wiki that will keep growing, and a mechanical transform
like this belongs in code, not in per-page model reasoning. Nothing in this
skill needs an LLM to run correctly; the model's job is to invoke the script
and relay its report.
This skill only runs when explicitly invoked — it is deliberately not part
of the always-loaded `CLAUDE.md`/`AGENTS.md` Ingest/Lint workflows.
## Trigger phrases
Use this skill when the user says things like:
- "export the wiki as OKF" / "export to OKF"
- "generate the OKF bundle" / "regenerate outputs/okf"
- "publish the OKF export"
- "sync outputs/okf with the wiki"
## How to run this skill
### Step 1 — Run the script
From the repository root:
```bash
python3 "<skill-dir>/scripts/export_okf.py"
```
Resolve `<skill-dir>` to this skill's own directory. Flags:
- `--check` — build into a temporary directory, run the conformance checks,
print the report, and write nothing to `outputs/`. Use this when the user
wants to know whether the wiki *would* export cleanly without touching the
working tree (e.g. as part of a review, or before a lint pass).
- `--out DIR` — write somewhere other than `outputs/okf` (rarely needed).
The script deletes and rebuilds the output directory on every run, so the
bundle can never accumulate stale files from pages that were since renamed
or removed. It touches nothing outside the output directory and runs no
`git` commands.
The exit code is `0` when the generated bundle conforms and `1` when it does
not — the report always prints either way, so read it rather than relying on
the exit code alone.
### Step 2 — Relay the script's report
The script prints, and you should summarize back to the user:
- Concept documents exported, and `index.md`/`log.md` files regenerated.
- Wikilinks stripped and `/wiki/` links rewritten to bundle-root paths.
- How many `linked/`/`libs/` cross-cascade references were left unconverted
— those won't resolve if the bundle is ever moved or shipped standalone,
which is spec-compliant (OKF tolerates broken links) but worth stating.
- Any `SOURCE ISSUE:` lines. These are problems in `wiki/` itself, not in
the export — most commonly a page with no `type`, which the bundle exports
as `type: unknown`. Surface them; the fix belongs in the source wiki (run
`ckb-lint`), not in the export.
- Any `NONCONFORMANT:` lines. These are bugs in the export — an
intra-bundle link that doesn't resolve, an index with the wrong
frontmatter shape, a malformed log header. Do not report the run as
successful when these appear; investigate the script rather than
hand-patching `outputs/okf/`.
- A reminder that `outputs/okf/` has been regenerated on disk but not staged
or committed — that's a separate step for the user when they're ready to
publish the update. (`outputs/okf` is gitignored by default in this
template, so "publish" usually means copying the bundle elsewhere rather
than committing it in place.)
## What the transform does (for context, not something you need to re-derive)
**Concept documents** (every `.md` that isn't `index.md`/`log.md`, including
`overview.md`, `error-book.md`, and `query-gaps.md` — only `index.md` and
`log.md` are OKF reserved names):
| wiki/ field | OKF output field | Rule |
|---|---|---|
| `type` | `type` | passthrough; a page with no `type` exports as `unknown` and is reported as a source issue |
| *(derive)* | `title` | the first `# H1` in the body, else the slugified filename (`foo-bar.md` → "Foo Bar") |
| `tldr` | `description` | rename |
| `resource` | `resource` | passthrough |
| `last_updated` | `timestamp` | passthrough as-is (bare `YYYY-MM-DD` is valid ISO 8601 — no time-of-day is fabricated) |
| `confidence`, `quality`, `retention`, `supersedes`, `superseded_by`, `freshness_window_days` | same keys | passthrough as OKF extension fields, which consumers must tolerate |
| `kb_schema_version` | *(dropped)* | the root `index.md`'s frontmatter is spec-limited to `okf_version`; this key has no valid home in the bundle |
| *(none)* | `tags` | omitted — no source field to derive it from |
**Links:** the `[[Wikilink]]` half of every dual-link is dropped, keeping the
markdown half (a bare wikilink with no markdown twin degrades to its plain
label). Root-absolute `/wiki/entities/foo.md` becomes `/entities/foo.md`,
since the bundle root is `outputs/okf/`, not the repo root. Relative links
need no change — the export mirrors `wiki/`'s tree exactly.
`linked/<name>/...` and `libs/<name>/...` references are left untouched and
counted.
**Indexes:** regenerated from the tree rather than transcribed from the
source, so they can't drift. The root `index.md` carries only
`okf_version: "0.1"` (the one documented exception to "index.md has no
frontmatter"); nested indexes carry none. Each body is an H1 plus a flat
`* [Title](path) - description` bullet list of that directory's direct
children, sorted by path, with each page's own `description` as the
description text. The source's "Use when" column and prose sections are
dropped — they are Claude-agent lazy-loading optimizations with no meaning
to a generic OKF consumer. An empty list is spec-valid.
**Logs:** entries are regrouped under `## YYYY-MM-DD` headers (date only,
newest date first) as `* **Verb**: [affected files] — description (source: ...)`.
`CREATE` → Creation, `UPDATE` → Update, `DELETE` → Deprecation, anything else
→ Update. The `- **Source:**` line rides along as the trailing parenthetical
rather than being dropped — it's provenance worth keeping. The `---`
separators between entries are dropped; they aren't part of OKF's log format.
**Validation** runs against the generated bundle before the report prints:
every non-reserved page has a non-empty `type`; the root index has only
`okf_version` and nested indexes have no frontmatter; every `log.md` header
matches `## YYYY-MM-DD`; and every intra-bundle link resolves to a file that
exists (cross-cascade `linked/`/`libs/` references are exempt by design).
## Edge cases
- **Empty `wiki/entities/` or `wiki/graph/`:** their `index.md` is still
regenerated, as an H1 with an empty bullet list. An empty index is
spec-valid; the directory is never skipped.
- **A future `wiki/<newtopic>/` subdirectory:** handled automatically — the
script discovers directories dynamically, mirrors them, and generates an
index for each. No script changes needed.
- **A future `wiki/archived/`:** exported like any other subdirectory. OKF
has no notion of archival status; `retention`/`freshness_window_days`
already ride along as extension fields for any consumer that cares.
- **Non-markdown files in `wiki/`** (e.g. `graph/edges.json`): copied
verbatim into the same relative position and listed in their directory's
index. Dotfiles (`.gitadd`) are skipped.
- **Re-running with no wiki changes:** produces byte-identical output — every
directory walk and generated list is sorted. If a run is ever
nondeterministic, that's a bug in the script, not expected behavior.
---
*Licensed under the Apache License, Version 2.0 — see [LICENSE](../../../LICENSE)
at the repository root, or <http://www.apache.org/licenses/LICENSE-2.0>.*