ckb/.agents/skills/ckb-lint/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

13 KiB

name description
ckb-lint Health-check the wiki/ — conformance, freshness, confidence decay, retention sweep, supersession detection, orphan detection, graph consistency, index/log consistency, and error-book entries — auto-fixing what it safely can and reporting the rest, then reminding the user to review and sync to origin. Also runs the same checks against each connector-backed libs/<name>/'s own generated index (see ckb-index-external), plus a source.yaml validity check, without ever touching a git-copy lib or a connector's source.yaml itself. Use when the user says "Lint", "health-check the wiki", "check the wiki", or asks for a periodic/scheduled wiki health check. This is the maintenance workflow routed by CLAUDE.md/AGENTS.md, distinct from `ckb-ingest` (which adds new content) and `ckb-sync-changes` (a pure git-level operation with no wiki synthesis at all).

Lint skill

Purpose

Health-check the local, mutable wiki/ for structural rot: missing or malformed frontmatter, stale pages, decayed confidence, orphaned or archivable content, broken graph edges, and index/log drift. This is maintenance, not growth — it never adds new knowledge the way ckb-ingest does; it only checks and repairs the shape of what's already there.

Implemented as a skill (rather than living inline in CLAUDE.md/AGENTS.md) so the full checklist only loads into context when actually invoked — on demand, or on a schedule if the user has set one up.

The detection half of this checklist is mechanical — a fixed set of frontmatter, date, link, graph, and index/log rules over a tree that keeps growing — so it runs as a read-only Python script rather than as per-page model reasoning (Step 0 below). What the script cannot decide is what this skill is actually for: which findings to auto-fix, which to leave for the user, whether two pages genuinely supersede one another, and whether a cluster of findings is a systemic pattern worth an error-book entry. Read the script's report, then do that work.

This skill only ever writes within wiki/ (including moves into wiki/archived/) and, for any connector-backed libs/<name>/ (one with a source.yaml — see CLAUDE.md/AGENTS.md directory contract) where this user has local access: write (see ckb-index-external), within that connector's own agent-owned generated index (index.md/entities//graph//log.md). For a connector-backed libs/<name>/ where this user is read-only (the default), findings against that index are reported only, never auto-fixed — the same access gate ckb-index-external applies to rebuilding applies here too. It never touches linked/, a git-copy libs/<name>/, or a connector's source.yaml/source.local.yaml — those stay immutable or user-owned — and it never touches this repo's own git remote; pushing the resulting changes is a separate, explicit step (see the final step below).

Trigger phrases

Use this skill when the user says:

  • "Lint"
  • "health-check the wiki" / "check the wiki"
  • "run the periodic/scheduled wiki check"

Do not use this skill for "Ingest" / "Sync the wiki" (that's ckb-ingest — turning raw material into new wiki pages) or for "sync changes" / "sync with git" (that's ckb-sync-changes — reconciling this repo's own commit history with origin, no wiki content involved).

How to run this skill

Work through each check in order. A malformed page breaks every later check's ability to reason about it, so conformance comes first.

Checks 1-9 below are written against wiki/, but the same checks also run a second time against each connector-backed libs/<name>/'s own generated index — scoped entirely to that one connector's index.md/entities// graph//log.md. Auto-fixes there only happen where this user has local access: write for that source (see ckb-index-external); everywhere read-only, findings are reported but not applied. Findings and fixes there are logged in that connector's own log.md, never in wiki/log.md. A git-copy libs/<name>/ (no source.yaml) and any source.yaml/ source.local.yaml are never touched by any of these checks.

0 — Run the checker script

From the repository root:

python3 "<skill-dir>/scripts/lint_report.py"

Resolve <skill-dir> to this skill's own directory. Flags: --scope wiki|libs|all (default all) to limit which trees are checked; --today YYYY-MM-DD to pin the date for reproducible runs; --json for a machine-readable report. Exit code is 0 when nothing was found and 1 when there are findings — read the report either way.

The script is strictly read-only: it writes nothing, moves nothing, and runs no git commands. It covers the mechanical detection in checks 1, 2, 3, 4, 6, 7, 8, and 10 below — plus the structural half of check 5 (decision records) — across wiki/ and every connector-backed libs/<name>/ (reporting each one's access: level so you know where a fix is even permitted). Check 9, the semantic half of check 5, and every auto-fix-vs-report decision, are yours.

The numbered checks below stay as the reference for what each finding means and what to do about it — you don't need to re-derive the detection by hand. Do read the pages the script flags: a finding is a pointer to a page that needs a decision, not the decision itself.

1 — Conformance check

The script flags every non-reserved .md file under wiki/ (i.e. excluding index.md and log.md) whose frontmatter is unparseable, absent, or missing a non-empty type. Treat flagged pages as unreliable input for the checks below rather than guessing at their intended type/content, and fix the frontmatter before acting on any later finding about the same page.

2 — Freshness check

The script reports every page whose last_updated exceeds its freshness_window_days, and by how much. Suggest the user confirm or update the content — don't silently rewrite stale content yourself.

3 — Confidence decay

The script reports pages already below 0.3 confidence (and any unparseable value). Decaying confidence on pages not reinforced by a new source since the last check is a write and a judgment call, so it stays yours — the script only surfaces where the floor has been crossed.

4 — Retention sweep

The script reports retention: low pages older than 2x their freshness_window_days as archive candidates. Move them into wiki/archived/ — never delete, always move — and log each move (see Rule B in CLAUDE.md/AGENTS.md) with a note explaining why. Check each candidate against recent use before moving it (see Edge cases).

5 — Supersession and decision records

The script checks the structure of every type: decision page: a status from the vocabulary, a decided_on date where the status implies one, a decided_by (unknown counts — an omitted field doesn't), affects targets and supersession links that resolve, supersession set on both sides, status: superseded/reversed matched by a superseded_by, unique four-digit decision numbers, and any review_on date that has passed. Fix those directly — they're mechanical. A review_on that has come due is not a defect, though: report it so the user can decide whether to revisit, and never change a decision's substance yourself (ckb-decide → decisions are append-only).

The semantic half stays yours: when two pages appear to cover the same entity or the same choice, check for contradictions. If one is clearly newer/better corroborated, add supersedes / superseded_by links between them. Preserve the older page rather than deleting it, but mark it stale. For two decisions, that judgment is exactly what the script can't make — it can tell you a link is one-sided, not that a link should exist.

6 — Orphan detection

The script reports pages with no inbound links ([[wikilinks]] or [markdown](path.md) references from anywhere else in the tree). For each one, either add backlinks from relevant pages where an obvious connection exists, or move the orphan to wiki/archived/ with a log note if no natural backlink does — that choice is the judgment the script leaves you.

7 — Graph consistency

The script reports every edge in wiki/graph/edges.json whose from/to does not resolve to an existing page (plus malformed edges and invalid JSON). Remove or fix them; note what was removed rather than silently dropping entries.

8 — Index/log consistency

The script reports subdirectories holding pages but no index.md, index files that don't list a page sitting next to them, and any change recorded in both a subdirectory log.md and the root wiki/log.md (per the Recursive Index & Log Convention). Fix missing index entries and duplicate log entries directly — these are the safest auto-fixes on the list.

9 — Error Book entry

Record any systemic issue found above (a repeated broken pattern, a recurring format mismatch, the same kind of orphan appearing again) in wiki/error-book.md with its root cause, the fix applied, and the derived constraint that should prevent recurrence. A one-off, isolated issue doesn't need an Error Book entry — this is for patterns, not incidents.

10 — External source config check

The script validates each libs/<name>/source.yaml (non-empty connector and location; non-empty store and location inside any index: block; a positive integer refresh_interval_days) and flags a libs/<name>/ that ambiguously holds both real content files and a source.yaml. All of this is report onlysource.yaml is never edited by any skill, and the ambiguous-content case is a configuration conflict for the user to resolve, not something to guess at.

It also reports any connector-backed source whose generated index is overdue — newest last_updated in libs/<name>/ older than its refresh_interval_days (default 30) — with how overdue it is, since a source two days past a 7-day interval is a different situation from one six months past a 30-day one. Relay that the same way whether or not this user has write access: a read-only user can't fix it, but knowing which source to chase the admin about is the actionable part. Never re-index here; that's ckb-index-external's job, and suggesting it is as far as this check goes.

Don't flag a missing libs/<name>/source.local.yaml as an issue — its absence is the correct, read-only default (see ckb-index-external), not a gap to report or fix. The script follows the same rule: it reads that file only to label each connector access: read-only / access: write in its report.

Auto-fix vs. report

Auto-fix what can be done safely and mechanically: broken links, missing backlinks, stale flags, missing index entries, duplicate log entries, dangling graph edges. Report anything that needs a judgment call (supersession decisions, low-confidence content, ambiguous orphans) rather than guessing on the user's behalf.

Final step — Remind to review and sync

This is always the last step, every time this skill makes any change at all to wiki/. Close with a short reminder — do not sync or push anything yourself here:

"Lint complete — see wiki/log.md for what changed (auto-fixed: ...; flagged for your review: ...). Please review the changes, and once you're happy with them, say 'sync changes' to commit and push them to origin."

If any connector-backed libs/<name>/ was also touched, mention it separately (its own log.md, not wiki/log.md) rather than folding it into the same sentence.

If the lint pass made no changes at all (clean bill of health, nothing to fix or flag), skip this reminder — there's nothing to review or sync.

Edge cases

  • Wiki is empty or near-empty — report that there's little to lint yet; don't fabricate findings to look thorough. A total findings: 0 report is a valid outcome; say so plainly rather than hunting for something to say.
  • A page's frontmatter is unparseable (not just missing type, but invalid YAML) — the script reports it under conformance and skips it in the later checks rather than reasoning from a half-parsed page. Surface it prominently and fix the frontmatter first; a page can't be judged stale, orphaned, or archivable until it parses.
  • Retention sweep would archive a page that's clearly still in active use (e.g. linked from a very recent workload/ entry) — flag it for the user to confirm rather than auto-archiving; recency of use can override a stale last_updated/retention combination.
  • Supersession is ambiguous (two pages disagree and neither is clearly newer/better corroborated) — report the conflict rather than guessing which one wins.
  • Repeated run with nothing changed since the last lint — produces the same report each time (pass --today to pin the date if you need a byte-identical one); don't invent variation just to seem active.
  • The script errors out or isn't runnable (no Python 3, unreadable tree) — say so and fall back to working the numbered checks by hand rather than reporting a clean bill of health you didn't actually verify.

Licensed under the Apache License, Version 2.0 — see LICENSE at the repository root, or http://www.apache.org/licenses/LICENSE-2.0.