# ourdigital-okf Skill — Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Build the `/ourdigital-okf` Claude skill that produces, visualizes, and validates Google Open Knowledge Format (OKF) v0.1 bundles, following OurDigital skill conventions. **Architecture:** A single OurDigital-family skill. Claude natively drafts conformant OKF concept docs (produce); two zero-dependency Python utilities handle deterministic work — `okf_validate.py` (conformance + broken-link lint) and `okf_viz.py` (minimal Cytoscape graph HTML). A shared `okf_common.py` holds the frontmatter/link parser. Tests use stdlib `unittest` and are verified against Google's three mirrored sample bundles. **Tech Stack:** Python 3 (stdlib only — no pip deps), `unittest` for tests, Cytoscape.js + marked via CDN for the viewer, OurDigital skill structure (`SKILL.md` + `code/` + `desktop/` + `docs/`). **Repo:** `/Users/ourdigital/Project/our-claude-skills` (git, branch `main`). Skill dir: `custom-skills/97-ourdigital-okf/`. **Reference fixtures (already on disk):** `~/Documents/reference-library/open-knowledge-format/okf/bundles/{crypto_bitcoin,ga4,stackoverflow}` and the spec at `.../okf/SPEC.md`. --- ## File Structure ``` custom-skills/97-ourdigital-okf/ ├── SKILL.md # top-level canonical (frontmatter + mode dispatch) ├── README.md # overview ├── DESIGN.md # (exists) approved spec ├── install.sh # symlink SKILL.md → ~/.claude/skills/ourdigital-okf ├── code/ │ ├── SKILL.md # Claude Code variant (detailed flows) │ ├── CLAUDE.md # code-pattern pointer │ ├── references/ │ │ ├── okf-spec-v0.1.md # distilled actionable spec + conformance checklist │ │ └── frontmatter-fields.md │ ├── assets/ │ │ ├── concept.md # template │ │ ├── index.md # template │ │ └── log.md # template │ └── scripts/ │ ├── okf_common.py # shared parser (stdlib) │ ├── okf_validate.py # validator CLI │ ├── okf_viz.py # visualizer CLI │ ├── requirements.txt # (documents: no runtime deps) │ └── tests/ │ ├── test_okf_common.py │ ├── test_okf_validate.py │ ├── test_okf_viz.py │ └── fixtures/mini_bundle/ # tiny conformant bundle (from SPEC Appendix A) ├── desktop/ │ ├── SKILL.md # Claude Desktop variant (leaner) │ └── skill.yaml └── docs/ ├── CHANGELOG.md └── IMPLEMENTATION-PLAN.md # this file ``` Responsibilities: `okf_common.py` = parsing only; `okf_validate.py` = conformance rules; `okf_viz.py` = graph build + HTML render. Each file is independently testable. --- ## Task 1: Branch + directory skeleton **Files:** - Create branch `feat/ourdigital-okf` - Create: `code/scripts/`, `code/references/`, `code/assets/`, `code/scripts/tests/fixtures/mini_bundle/`, `desktop/`, `docs/` - [ ] **Step 1: Create a feature branch** ```bash cd /Users/ourdigital/Project/our-claude-skills git checkout -b feat/ourdigital-okf ``` - [ ] **Step 2: Create the directory skeleton** ```bash cd custom-skills/97-ourdigital-okf mkdir -p code/references code/assets code/scripts/tests/fixtures/mini_bundle desktop docs ``` - [ ] **Step 3: Create `code/scripts/requirements.txt`** ```text # ourdigital-okf scripts use the Python standard library only. # No third-party runtime dependencies. Tests use stdlib `unittest`. ``` - [ ] **Step 4: Commit** ```bash git add custom-skills/97-ourdigital-okf git commit -m "feat(okf): scaffold ourdigital-okf skill skeleton" ``` --- ## Task 2: Mini fixture bundle (test data, from SPEC Appendix A) **Files:** - Create: `code/scripts/tests/fixtures/mini_bundle/{index.md, datasets/sales.md, tables/orders.md, tables/customers.md}` - [ ] **Step 1: Create `tests/fixtures/mini_bundle/index.md`** ```markdown # Datasets * [Sales](datasets/sales.md) - All sales-related tables. # Tables * [Orders](tables/orders.md) - One row per completed order. * [Customers](tables/customers.md) - One row per customer. ``` - [ ] **Step 2: Create `tests/fixtures/mini_bundle/datasets/sales.md`** ```markdown --- type: BigQuery Dataset title: Sales description: All sales-related tables for the retail business. resource: https://console.cloud.google.com/bigquery?p=acme&d=sales tags: [sales] timestamp: 2026-05-28T00:00:00Z --- The sales dataset contains [orders](/tables/orders.md) and [customers](/tables/customers.md). ``` - [ ] **Step 3: Create `tests/fixtures/mini_bundle/tables/orders.md`** ```markdown --- type: BigQuery Table title: Orders description: One row per completed customer order. resource: https://console.cloud.google.com/bigquery?p=acme&d=sales&t=orders tags: [sales, orders] timestamp: 2026-05-28T00:00:00Z --- # Schema | Column | Type | Description | |---------------|---------|------------------------------------------| | `order_id` | STRING | Unique order identifier. | | `customer_id` | STRING | FK to [customers](/tables/customers.md). | Part of the [sales dataset](/datasets/sales.md). ``` - [ ] **Step 4: Create `tests/fixtures/mini_bundle/tables/customers.md`** ```markdown --- type: BigQuery Table title: Customers description: One row per customer. resource: https://console.cloud.google.com/bigquery?p=acme&d=sales&t=customers tags: [sales, customers] timestamp: 2026-05-28T00:00:00Z --- # Schema | Column | Type | Description | |---------------|--------|-----------------------| | `customer_id` | STRING | Unique customer id. | Referenced by [orders](/tables/orders.md). ``` - [ ] **Step 5: Commit** ```bash git add custom-skills/97-ourdigital-okf/code/scripts/tests/fixtures git commit -m "test(okf): add mini conformant fixture bundle" ``` --- ## Task 3: `okf_common.py` — shared parser (TDD) **Files:** - Create: `code/scripts/okf_common.py` - Test: `code/scripts/tests/test_okf_common.py` - [ ] **Step 1: Write the failing test** — `tests/test_okf_common.py` ```python import sys, unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from okf_common import (parse_frontmatter, parse_yaml_subset, extract_links, concept_id, FrontmatterError) class TestFrontmatter(unittest.TestCase): def test_parse_basic(self): text = "---\ntype: BigQuery Table\ntitle: Orders\ntags: [sales, revenue]\n---\n\n# Body\n" meta, body = parse_frontmatter(text) self.assertEqual(meta["type"], "BigQuery Table") self.assertEqual(meta["title"], "Orders") self.assertEqual(meta["tags"], ["sales", "revenue"]) self.assertIn("# Body", body) def test_url_value_with_colons(self): meta, _ = parse_frontmatter("---\ntype: X\nresource: https://e.com/a?b=c\n---\nbody") self.assertEqual(meta["resource"], "https://e.com/a?b=c") def test_quoted_value(self): self.assertEqual(parse_yaml_subset('okf_version: "0.1"')["okf_version"], "0.1") def test_empty_list(self): self.assertEqual(parse_yaml_subset("tags: []")["tags"], []) def test_no_frontmatter(self): meta, body = parse_frontmatter("# Just markdown\n") self.assertIsNone(meta) self.assertEqual(body, "# Just markdown\n") def test_unclosed_raises(self): with self.assertRaises(FrontmatterError): parse_frontmatter("---\ntype: X\n\nbody without close") class TestLinksAndIds(unittest.TestCase): def test_extract_links(self): body = "See [a](/tables/a.md) and [b](./b.md) and [ext](https://x.com)." self.assertEqual(extract_links(body), ["/tables/a.md", "./b.md", "https://x.com"]) def test_concept_id(self): self.assertEqual(concept_id("/bundle", "/bundle/tables/users.md"), "tables/users") if __name__ == "__main__": unittest.main() ``` - [ ] **Step 2: Run test to verify it fails** Run: `cd custom-skills/97-ourdigital-okf/code/scripts && python3 -m unittest tests.test_okf_common -v` Expected: FAIL with `ModuleNotFoundError: No module named 'okf_common'` - [ ] **Step 3: Write `code/scripts/okf_common.py`** ```python """Shared OKF v0.1 parsing utilities (Python standard library only).""" from __future__ import annotations import re from pathlib import Path RESERVED = {"index.md", "log.md"} _LINK_RE = re.compile(r"\[[^\]]*\]\(([^)]+)\)") class FrontmatterError(ValueError): """Raised when a frontmatter block is present but cannot be parsed.""" def split_frontmatter(text): """Return (raw_frontmatter, body). raw is None if there is no leading '---' block.""" if not text.startswith("---"): return None, text lines = text.splitlines() if lines[0].strip() != "---": return None, text for i in range(1, len(lines)): if lines[i].strip() == "---": return "\n".join(lines[1:i]), "\n".join(lines[i + 1:]) raise FrontmatterError("opening '---' without a closing '---'") def _scalar(value): value = value.strip() if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'": return value[1:-1] return value def parse_yaml_subset(raw): """Parse the small YAML subset OKF uses: `key: value` and `key: [a, b]`.""" meta = {} for line in raw.splitlines(): stripped = line.strip() if not stripped or stripped.startswith("#"): continue if ":" not in line: raise FrontmatterError("unparseable frontmatter line: %r" % line) key, _, value = line.partition(":") key, value = key.strip(), value.strip() if value.startswith("[") and value.endswith("]"): inner = value[1:-1].strip() meta[key] = [s for s in (_scalar(x) for x in inner.split(",")) if s] if inner else [] else: meta[key] = _scalar(value) return meta def parse_frontmatter(text): """Return (meta, body). meta is None when no frontmatter block is present.""" raw, body = split_frontmatter(text) if raw is None: return None, text return parse_yaml_subset(raw), body def iter_concepts(bundle_dir): """Yield Path for every non-reserved .md file under bundle_dir, sorted.""" root = Path(bundle_dir) for path in sorted(root.rglob("*.md")): if path.name not in RESERVED: yield path def concept_id(bundle_dir, path): """Concept ID = bundle-relative path with the .md suffix removed.""" rel = Path(path).relative_to(Path(bundle_dir)).as_posix() return rel[:-3] if rel.endswith(".md") else rel def extract_links(body): """Return the list of link targets from markdown links in body.""" return _LINK_RE.findall(body) ``` - [ ] **Step 4: Run test to verify it passes** Run: `python3 -m unittest tests.test_okf_common -v` Expected: PASS (8 tests OK) - [ ] **Step 5: Commit** ```bash git add custom-skills/97-ourdigital-okf/code/scripts/okf_common.py custom-skills/97-ourdigital-okf/code/scripts/tests/test_okf_common.py git commit -m "feat(okf): add okf_common frontmatter/link parser with tests" ``` --- ## Task 4: `okf_validate.py` — conformance + broken-link linter (TDD) **Files:** - Create: `code/scripts/okf_validate.py` - Test: `code/scripts/tests/test_okf_validate.py` - [ ] **Step 1: Write the failing test** — `tests/test_okf_validate.py` ```python import sys, tempfile, unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from okf_validate import validate_bundle FIX = Path(__file__).resolve().parent / "fixtures" / "mini_bundle" def write_bundle(root, files): for rel, content in files.items(): p = Path(root) / rel p.parent.mkdir(parents=True, exist_ok=True) p.write_text(content, encoding="utf-8") class TestValidate(unittest.TestCase): def test_mini_fixture_conformant(self): report = validate_bundle(FIX) self.assertTrue(report["conformant"], report["errors"]) self.assertGreaterEqual(report["concepts"], 3) def test_missing_type_is_error(self): with tempfile.TemporaryDirectory() as d: write_bundle(d, {"tables/x.md": "---\ntitle: X\n---\nbody"}) report = validate_bundle(d) self.assertFalse(report["conformant"]) self.assertTrue(any(e["rule"] == "type" for e in report["errors"])) def test_missing_frontmatter_is_error(self): with tempfile.TemporaryDirectory() as d: write_bundle(d, {"tables/x.md": "# no frontmatter\n"}) self.assertFalse(validate_bundle(d)["conformant"]) def test_reserved_files_not_required_to_have_type(self): with tempfile.TemporaryDirectory() as d: write_bundle(d, {"index.md": "# Index\n* [x](/tables/x.md)\n", "tables/x.md": "---\ntype: T\n---\nb"}) self.assertTrue(validate_bundle(d)["conformant"]) def test_broken_link_is_warning_not_error(self): with tempfile.TemporaryDirectory() as d: write_bundle(d, {"tables/x.md": "---\ntype: T\n---\nSee [y](/tables/y.md)."}) report = validate_bundle(d) self.assertTrue(report["conformant"]) self.assertTrue(any(w["rule"] == "broken_link" for w in report["warnings"])) if __name__ == "__main__": unittest.main() ``` - [ ] **Step 2: Run test to verify it fails** Run: `python3 -m unittest tests.test_okf_validate -v` Expected: FAIL with `ModuleNotFoundError: No module named 'okf_validate'` - [ ] **Step 3: Write `code/scripts/okf_validate.py`** ```python #!/usr/bin/env python3 """OKF v0.1 conformance + broken-link validator (Python standard library only). Conformance (SPEC.md §9): every non-reserved .md has a parseable frontmatter block with a non-empty `type`. Broken cross-links are reported as warnings, never errors (consumers MUST tolerate them). """ from __future__ import annotations import argparse import json import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) from okf_common import (FrontmatterError, concept_id, extract_links, iter_concepts, parse_frontmatter) def _resolve_link(bundle, path, target): """Return a Path for an internal .md link, or None for external/anchor/non-md.""" t = target.split("#", 1)[0].strip() if not t or "://" in t or t.startswith("mailto:") or not t.endswith(".md"): return None if t.startswith("/"): return bundle / t.lstrip("/") return path.parent / t def validate_bundle(bundle_dir): bundle = Path(bundle_dir) errors, warnings = [], [] concept_files = list(iter_concepts(bundle)) existing = {p.resolve() for p in bundle.rglob("*.md")} for path in concept_files: cid = concept_id(bundle, path) text = path.read_text(encoding="utf-8") try: meta, body = parse_frontmatter(text) except FrontmatterError as exc: errors.append({"concept": cid, "rule": "frontmatter", "message": str(exc)}) continue if meta is None: errors.append({"concept": cid, "rule": "frontmatter", "message": "missing YAML frontmatter block"}) continue if not str(meta.get("type", "")).strip(): errors.append({"concept": cid, "rule": "type", "message": "missing or empty required 'type' field"}) for target in extract_links(body): resolved = _resolve_link(bundle, path, target) if resolved is not None and resolved.resolve() not in existing: warnings.append({"concept": cid, "rule": "broken_link", "message": "link target not found: %s" % target}) return { "bundle": str(bundle), "concepts": len(concept_files), "conformant": len(errors) == 0, "errors": errors, "warnings": warnings, } def format_report(report): status = "CONFORMANT" if report["conformant"] else "NON-CONFORMANT" lines = [ "OKF v0.1 validation: %s" % report["bundle"], " concepts: %d status: %s" % (report["concepts"], status), " errors: %d warnings: %d" % (len(report["errors"]), len(report["warnings"])), ] for e in report["errors"]: lines.append(" ERROR [%s] %s: %s" % (e["concept"], e["rule"], e["message"])) for w in report["warnings"]: lines.append(" WARN [%s] %s: %s" % (w["concept"], w["rule"], w["message"])) return "\n".join(lines) def main(argv=None): ap = argparse.ArgumentParser(description="Validate an OKF v0.1 bundle.") ap.add_argument("bundle", help="Path to the bundle directory") ap.add_argument("--json", action="store_true", help="Emit a JSON report") args = ap.parse_args(argv) report = validate_bundle(args.bundle) print(json.dumps(report, indent=2) if args.json else format_report(report)) return 0 if report["conformant"] else 1 if __name__ == "__main__": sys.exit(main()) ``` - [ ] **Step 4: Run test to verify it passes** Run: `python3 -m unittest tests.test_okf_validate -v` Expected: PASS (5 tests OK) - [ ] **Step 5: Commit** ```bash git add custom-skills/97-ourdigital-okf/code/scripts/okf_validate.py custom-skills/97-ourdigital-okf/code/scripts/tests/test_okf_validate.py git commit -m "feat(okf): add okf_validate conformance linter with tests" ``` --- ## Task 5: Validate against Google's 3 reference bundles (integration + parser hardening) **Files:** - Modify (if needed): `code/scripts/okf_common.py` - Test: append to `code/scripts/tests/test_okf_validate.py` - [ ] **Step 1: Add the integration test** — append to `tests/test_okf_validate.py` (before the `if __name__` block) ```python GOOGLE = Path.home() / "Documents/reference-library/open-knowledge-format/okf/bundles" class TestGoogleBundles(unittest.TestCase): @unittest.skipUnless(GOOGLE.exists(), "Google reference bundles not present") def test_google_sample_bundles_conformant(self): for name in ("crypto_bitcoin", "ga4", "stackoverflow"): bundle = GOOGLE / name if not bundle.exists(): continue report = validate_bundle(bundle) self.assertTrue( report["conformant"], "%s non-conformant; first errors: %s" % (name, report["errors"][:3]), ) ``` - [ ] **Step 2: Run it** Run: `python3 -m unittest tests.test_okf_validate.TestGoogleBundles -v` Expected: PASS. **If it FAILS:** inspect the reported errors. If they are real conformance gaps in Google's bundles, that's a finding — record it in `docs/CHANGELOG.md` and relax that specific check to a warning. If they are *parser limitations* (e.g. a frontmatter value shape `parse_yaml_subset` doesn't handle), extend the parser in `okf_common.py` minimally and re-run Task 3 tests to confirm no regressions. - [ ] **Step 3: Commit** ```bash git add custom-skills/97-ourdigital-okf/code/scripts git commit -m "test(okf): validate against Google reference bundles" ``` --- ## Task 6: `okf_viz.py` — minimal graph visualizer (TDD) **Files:** - Create: `code/scripts/okf_viz.py` - Test: `code/scripts/tests/test_okf_viz.py` - [ ] **Step 1: Write the failing test** — `tests/test_okf_viz.py` ```python import sys, tempfile, unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from okf_viz import build_graph, render_html, main FIX = Path(__file__).resolve().parent / "fixtures" / "mini_bundle" class TestViz(unittest.TestCase): def test_build_graph_nodes_and_edges(self): g = build_graph(FIX) ids = {n["id"] for n in g["nodes"]} self.assertIn("tables/orders", ids) self.assertTrue(any(e["source"] == "tables/orders" and e["target"] == "tables/customers" for e in g["edges"])) def test_render_html_embeds_data_and_cdn(self): html = render_html(build_graph(FIX), "Mini") self.assertIn("cytoscape", html) self.assertIn('"nodes"', html) def test_render_html_escapes_script_close(self): g = {"nodes": [{"id": "x", "type": "T", "title": "x", "description": "", "body": "hi"}], "edges": []} html = render_html(g, "X") self.assertNotIn("hi", html) def test_main_writes_file(self): with tempfile.TemporaryDirectory() as d: out = Path(d) / "v.html" self.assertEqual(main(["--bundle", str(FIX), "--out", str(out)]), 0) self.assertTrue(out.exists() and out.stat().st_size > 500) if __name__ == "__main__": unittest.main() ``` - [ ] **Step 2: Run test to verify it fails** Run: `python3 -m unittest tests.test_okf_viz -v` Expected: FAIL with `ModuleNotFoundError: No module named 'okf_viz'` - [ ] **Step 3: Write `code/scripts/okf_viz.py`** ```python #!/usr/bin/env python3 """Generate a minimal, self-contained OKF bundle visualizer (stdlib only). Emits one HTML file: a Cytoscape.js force-directed graph of the bundle's concepts (nodes colored by `type`), with a side panel that renders the selected concept's markdown body via marked. Both libraries load from a CDN; the bundle is embedded as JSON, so no backend and no data leaves the page. """ from __future__ import annotations import argparse import html import json import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) from okf_common import (concept_id, extract_links, iter_concepts, parse_frontmatter) def _target_id(bundle, path, target, ids): t = target.split("#", 1)[0].strip() if not t or "://" in t or not t.endswith(".md"): return None if t.startswith("/"): cid = t.lstrip("/")[:-3] else: rel = (path.parent / t).resolve().relative_to(Path(bundle).resolve()) cid = rel.as_posix()[:-3] return cid if cid in ids else None def build_graph(bundle_dir): bundle = Path(bundle_dir) concepts = list(iter_concepts(bundle)) ids = {concept_id(bundle, p) for p in concepts} nodes, edges = [], [] for path in concepts: cid = concept_id(bundle, path) try: meta, body = parse_frontmatter(path.read_text(encoding="utf-8")) except Exception: meta, body = None, path.read_text(encoding="utf-8") meta = meta or {} nodes.append({ "id": cid, "type": str(meta.get("type", "Concept")), "title": str(meta.get("title", Path(cid).name)), "description": str(meta.get("description", "")), "body": body, }) for target in extract_links(body): tid = _target_id(bundle, path, target, ids) if tid and tid != cid: edges.append({"source": cid, "target": tid}) return {"nodes": nodes, "edges": edges} _HTML = """ __NAME__ — OKF Viewer
__NAME__

Click a concept node to view it.

""" def render_html(graph, name="OKF Bundle"): data = json.dumps(graph, ensure_ascii=False).replace("0. Open in a browser to confirm a graph renders. (This is a manual visual check.) - [ ] **Step 6: Commit** ```bash git add custom-skills/97-ourdigital-okf/code/scripts/okf_viz.py custom-skills/97-ourdigital-okf/code/scripts/tests/test_okf_viz.py git commit -m "feat(okf): add minimal okf_viz Cytoscape generator with tests" ``` --- ## Task 7: Run the full test suite - [ ] **Step 1: Run every test** Run: `cd custom-skills/97-ourdigital-okf/code/scripts && python3 -m unittest discover -s tests -v` Expected: all tests PASS (Google-bundle test passes or skips if fixtures absent). - [ ] **Step 2: Commit any fixes** (only if changes were needed) ```bash git add -A custom-skills/97-ourdigital-okf git commit -m "test(okf): green full suite" ``` --- ## Task 8: Reference docs (`code/references/`) **Files:** - Create: `code/references/okf-spec-v0.1.md`, `code/references/frontmatter-fields.md` - [ ] **Step 1: Create `code/references/okf-spec-v0.1.md`** Write a distilled, *actionable* version of OKF v0.1 (source: `~/Documents/reference-library/open-knowledge-format/okf/SPEC.md`). It MUST contain these sections with this content: - **Bundle/Concept/Concept-ID definitions** (concept ID = path minus `.md`). - **Reserved filenames table:** `index.md` (directory listing / progressive disclosure, no frontmatter except optional root `okf_version`), `log.md` (date-grouped `YYYY-MM-DD`, newest-first). - **Frontmatter table:** `type` REQUIRED; `title`/`description`/`resource`/`tags`/`timestamp` recommended; arbitrary extra keys allowed. - **Conventional body headings:** `# Schema`, `# Examples`, `# Citations`. - **Cross-linking:** bundle-relative (`/path/concept.md`, leading `/`) recommended; relative (`./x.md`) allowed; broken links tolerated. - **Conformance checklist (§9):** parseable frontmatter + non-empty `type` per concept; never reject for missing optional fields / unknown types / broken links / missing index. - **Authoring rules for the producer** (numbered): one concept per file; pick descriptive `type` values; prefer structural markdown; cross-link related concepts; add `# Citations` when sourcing external claims; generate `index.md` per directory. (The "OKF v0.1 at a glance" cheat-sheet in `~/Documents/reference-library/open-knowledge-format/README.md` is a ready source for the tables — adapt it.) - [ ] **Step 2: Create `code/references/frontmatter-fields.md`** A one-page field reference: for each of `type, title, description, resource, tags, timestamp` give purpose, requiredness, example value, and example `type` values (`BigQuery Table`, `BigQuery Dataset`, `Metric`, `Playbook`, `Reference`, `API Endpoint`). - [ ] **Step 3: Commit** ```bash git add custom-skills/97-ourdigital-okf/code/references git commit -m "docs(okf): add distilled spec + frontmatter reference" ``` --- ## Task 9: Templates (`code/assets/`) **Files:** - Create: `code/assets/concept.md`, `code/assets/index.md`, `code/assets/log.md` - [ ] **Step 1: Create `code/assets/concept.md`** ```markdown --- type: title: description: resource: tags: [, ] timestamp: --- # Schema | Column | Type | Description | |--------|------|-------------| | `col` | TYPE | What it is. FK to [other](/tables/other.md). | # Citations [1] [Source title](https://example.com) ``` - [ ] **Step 2: Create `code/assets/index.md`** ```markdown # Group Heading * [Title](relative-or-bundle-relative-path) - short description from the concept's frontmatter ``` - [ ] **Step 3: Create `code/assets/log.md`** ```markdown # Update Log ## 2026-06-16 * **Initialization**: Created the bundle structure. ``` - [ ] **Step 4: Commit** ```bash git add custom-skills/97-ourdigital-okf/code/assets git commit -m "docs(okf): add concept/index/log templates" ``` --- ## Task 10: `code/SKILL.md` + `code/CLAUDE.md` (Claude Code variant) **Files:** - Create: `code/SKILL.md`, `code/CLAUDE.md` - [ ] **Step 1: Create `code/SKILL.md`** with this frontmatter and a body (target 800–1,200 words) covering the sections below. ```yaml --- name: ourdigital-okf description: | Produce, visualize, and validate Google Open Knowledge Format (OKF) v0.1 knowledge bundles. Activated with the "ourdigital" keyword for OKF work. Triggers (ourdigital or our prefix): - "ourdigital okf", "our okf" - "ourdigital open knowledge format", "our knowledge bundle" - "ourdigital okf 만들기", "our okf 검증" Features: - Produce conformant OKF bundles from a pasted/exported schema, a docs folder, or a research topic - Validate a bundle for OKF v0.1 conformance + broken-link report - Visualize a bundle as a self-contained interactive graph (viz.html) version: "1.0" author: OurDigital environment: Both --- ``` Body sections (write actual instructions, not placeholders): 1. **What is OKF** (3–4 sentences; point to `references/okf-spec-v0.1.md` as the authority — instruct: read it before producing). 2. **Mode dispatch** — decide produce / visualize / validate from the request. 3. **Produce flow** (the 6 steps from DESIGN.md §5). Emphasize: (a) **confirm the target bundle directory with the user before creating anything** (no-directory-without-consent rule); (b) input adapters: *schema* = ingest a pasted/exported schema (BigQuery DDL, GA4 export schema, CSV/JSON Schema, OpenAPI) — do **not** call a live MCP; *docs* = read the provided files/folder; *research* = invoke `/reference-curator`; (c) every concept gets a required `type`; (d) cross-link with bundle-relative paths; (e) generate `index.md` per directory; (f) run `python3 code/scripts/okf_validate.py ` and fix all errors before reporting done. 4. **Validate flow** — `python3 code/scripts/okf_validate.py [--json]`; explain exit code and that broken links are warnings. 5. **Visualize flow** — `python3 code/scripts/okf_viz.py --bundle [--out viz.html] [--name X]`. 6. **Resources** — list `references/`, `assets/`, `scripts/`. - [ ] **Step 2: Create `code/CLAUDE.md`** ```markdown # ourdigital-okf (Claude Code) Use `SKILL.md` in this directory as the instruction set. Scripts live in `scripts/` (stdlib-only Python). Read `references/okf-spec-v0.1.md` before producing a bundle. Always confirm the output directory with the user before creating it. ``` - [ ] **Step 3: Verify word count** Run: `wc -w custom-skills/97-ourdigital-okf/code/SKILL.md` Expected: body in the 800–1,200 word range (OurDigital convention). - [ ] **Step 4: Commit** ```bash git add custom-skills/97-ourdigital-okf/code/SKILL.md custom-skills/97-ourdigital-okf/code/CLAUDE.md git commit -m "feat(okf): add code-variant SKILL.md and CLAUDE.md" ``` --- ## Task 11: Top-level `SKILL.md`, `README.md`, and `desktop/` variant **Files:** - Create: `SKILL.md` (top-level), `README.md`, `desktop/SKILL.md`, `desktop/skill.yaml` - [ ] **Step 1: Create top-level `SKILL.md`** Same frontmatter as `code/SKILL.md` (Task 10 Step 1). Body: a concise canonical version (300–500 words) summarizing the three modes and pointing to `code/SKILL.md` for the detailed Claude Code flows and `code/references/` for the spec. - [ ] **Step 2: Create `README.md`** Overview: what the skill does, the three modes, install instructions (`./install.sh`), the script commands, and a note that bundles are validated against Google's reference bundles. Link to `DESIGN.md`. - [ ] **Step 3: Create `desktop/SKILL.md`** Leaner Claude Desktop variant: same frontmatter with `environment: Desktop`; body explains produce + validate + visualize conceptually and notes the Python scripts run in the Code environment (Desktop users run them via a terminal). - [ ] **Step 4: Create `desktop/skill.yaml`** (mirror the pattern in `04-ourdigital-research/desktop/skill.yaml`) ```yaml # Skill metadata (extracted from SKILL.md frontmatter) name: ourdigital-okf description: | Produce, visualize, and validate Google Open Knowledge Format (OKF) v0.1 bundles. Triggers: "ourdigital okf", "our okf", "open knowledge format", "knowledge bundle". ``` - [ ] **Step 5: Commit** ```bash git add custom-skills/97-ourdigital-okf/SKILL.md custom-skills/97-ourdigital-okf/README.md custom-skills/97-ourdigital-okf/desktop git commit -m "feat(okf): add top-level + desktop skill variants and README" ``` --- ## Task 12: `install.sh` + smoke test **Files:** - Create: `install.sh` - [ ] **Step 1: Create `install.sh`** ```bash #!/bin/bash # ourdigital-okf installer — symlinks the skill into ~/.claude/skills/ourdigital-okf set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SKILLS_DIR="$HOME/.claude/skills" TARGET="$SKILLS_DIR/ourdigital-okf" mkdir -p "$SKILLS_DIR" if [ -e "$TARGET" ] || [ -L "$TARGET" ]; then echo "Removing existing $TARGET" rm -rf "$TARGET" fi ln -s "$SCRIPT_DIR" "$TARGET" echo "Linked $TARGET -> $SCRIPT_DIR" echo "Verifying scripts (stdlib-only)…" python3 "$SCRIPT_DIR/code/scripts/okf_validate.py" \ "$SCRIPT_DIR/code/scripts/tests/fixtures/mini_bundle" >/dev/null && echo "validate OK" echo "Done. Invoke with /ourdigital-okf" ``` - [ ] **Step 2: Make it executable and run it** Run: ```bash chmod +x custom-skills/97-ourdigital-okf/install.sh custom-skills/97-ourdigital-okf/install.sh ``` Expected: prints `Linked …`, `validate OK`, `Done.` and exit 0. - [ ] **Step 3: Verify the symlink and that the skill resolves** Run: `ls -l ~/.claude/skills/ourdigital-okf && test -f ~/.claude/skills/ourdigital-okf/SKILL.md && echo PRESENT` Expected: symlink shown, `PRESENT`. - [ ] **Step 4: Commit** ```bash git add custom-skills/97-ourdigital-okf/install.sh git commit -m "feat(okf): add install.sh symlink installer" ``` --- ## Task 13: End-to-end produce test (manual, real flow) - [ ] **Step 1: Produce a tiny bundle from a docs folder** Create a scratch input and ask the skill (or follow the produce flow manually) to emit a 2–3 concept bundle into a **user-confirmed** temp dir, e.g. `/tmp/okf-e2e/`. Each concept must have a `type`. - [ ] **Step 2: Validate it** Run: `python3 custom-skills/97-ourdigital-okf/code/scripts/okf_validate.py /tmp/okf-e2e` Expected: `status: CONFORMANT`, `errors: 0`. - [ ] **Step 3: Visualize it** Run: `python3 custom-skills/97-ourdigital-okf/code/scripts/okf_viz.py --bundle /tmp/okf-e2e` Expected: writes `/tmp/okf-e2e/viz.html`; opens to a graph in a browser. - [ ] **Step 4: Record the result in `docs/CHANGELOG.md`** ```markdown # Changelog ## 1.0 — 2026-06-16 * Initial release: produce / validate / visualize OKF v0.1 bundles. * Validated against Google reference bundles (crypto_bitcoin, ga4, stackoverflow). ``` - [ ] **Step 5: Commit** ```bash git add custom-skills/97-ourdigital-okf/docs/CHANGELOG.md git commit -m "docs(okf): add changelog; e2e produce→validate→visualize verified" ``` --- ## Task 14: OurDigital consistency check + finalize - [ ] **Step 1: Run the OurDigital skill-creator validation** Invoke `ourdigital-skill-creator` in "validate existing skill" mode against `97-ourdigital-okf`. It checks: frontmatter has `ourdigital` triggers; description states activation; body 800–1,200 words; `shared/` (here `code/references` + `code/assets`) referenced; no overlap with existing skills; numbering/structure conventions. - [ ] **Step 2: Fix any reported violations**, re-running the relevant tests/word-count checks. Commit fixes: ```bash git add -A custom-skills/97-ourdigital-okf git commit -m "fix(okf): address ourdigital-skill-creator consistency findings" ``` - [ ] **Step 3: Update the Notion spec page** (`381581e5-8a1e-8112-8280-f43839902dc8`) Status → Done. - [ ] **Step 4: Final full-suite run** Run: `cd custom-skills/97-ourdigital-okf/code/scripts && python3 -m unittest discover -s tests -v` Expected: all green. - [ ] **Step 5: Offer to merge `feat/ourdigital-okf`** (use superpowers:finishing-a-development-branch). --- ## Self-Review (completed by plan author) **Spec coverage:** produce (Tasks 8–10 author flow; validator self-check in Task 4) · visualize (Task 6) · validate (Tasks 4–5) · pasted/exported schema adapter (Task 10 §3b) · MCP-agnostic (no live MCP calls in scripts/flows) · minimal viz first (Task 6) · OurDigital structure (Tasks 10–11) · DESIGN.md repo copy (exists) · ourdigital-skill-creator check (Task 14) · Google-bundle verification (Tasks 5, 6) · no-directory-without-consent (Task 10 §3a, Task 13). All spec sections map to a task. **Placeholder scan:** No "TBD/TODO". Content tasks (8–11) specify exact sections + content to write, not vague "fill in". Code tasks carry complete implementations. **Type consistency:** `parse_frontmatter`, `parse_yaml_subset`, `split_frontmatter`, `iter_concepts`, `concept_id`, `extract_links`, `FrontmatterError`, `RESERVED` defined in Task 3 and imported consistently in Tasks 4 & 6. `validate_bundle` report keys (`bundle/concepts/conformant/errors/warnings`) used consistently. `build_graph`→`{nodes,edges}` and `render_html(graph,name)` consistent across Task 6 code and tests. **Known risk:** Task 5 may surface frontmatter shapes in Google's real bundles that the minimal parser doesn't handle; Task 5 Step 2 gives the explicit remediation (extend parser or downgrade to warning).