The gate carried a flat RECOMMENDED list ending in `timestamp`. Upstream retired
that field in v0.2: "`timestamp` is superseded by `generated.at`" (okf/SPEC.md
§13.1:802-803, read at frozen 3fcbb9f), one of the version's two breaking
changes — while :804 still lets a consumer "fall back to a legacy `timestamp`
when `generated` is absent".
A version-unconditional list cannot serve both readings. It either nags a
correct v0.2 bundle about a retired field, or goes silent about a field v0.1
still wants. So the list is now chosen by the bundle root's own okf_version:
< 0.2 (or absent/unshaped) keeps `timestamp`, >= 0.2 asks for `generated`.
Absence gets the legacy floor deliberately — §3 echoes a missing marker rather
than failing it, so it still needs a defined list.
Measured, not assumed:
- Every fixture in both corpora and both live emitters (okr, linkedin-studio)
still write `okf_version: 0.1`, so this changes NO verdict today. It is
written now because the upstream reading is fresh and pinned to a commit.
- The parity signature is conceptCount|untyped|okfVersion|okfVersionAccepted
(check-okf-parity.mjs:73-76) — warnings are not in it, so diverging from
okr's list here cannot red the parity gate. Confirmed: 9/9 fixtures pass.
- The compare is component-wise, NOT parseFloat: okf_version is version-SHAPED,
and parseFloat('0.10') is 0.1, which would sort 0.10 before 0.2 and hand a
newer bundle the retired field. Guarded by its own test.
spec §4 is updated in the same commit — a gate and the convention it enforces
must not disagree about which field they want.
Tests 98 -> 103 (okf-check 17 -> 22). All six suites green; check-versions
11 OK / 0 WARN / 0 ERROR.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0135YZBcCDvH3BgG5yEtuHCA
21 KiB
OKF-compatible second-brain form
A cross-plugin convention for how each plugin stores the user's own context — their personal/organizational "second brain" / LLM-wiki — as a portable, interoperable markdown bundle, compatible with Google's Open Knowledge Format (OKF) v0.1.
Version 0.3 · 2026-07-31 · Cross-cutting catalog artifact, owned by no single plugin. (0.3 corrects this convention's placement rule to match upstream: the bundle-root
okf_versionmarker belongs in the rootindex.md's frontmatter block, which is the one place upstream permits frontmatter in anindex.md(§6). The value is now unquoted before the shape check, because upstream's own canonical example is quoted.okf_layoutstays in body text (§12). 0.2 had tightened the §3 floor:okf_versionenforced on shape. Distinct from — and deliberately no longer numerically confusable with — upstream Google OKF v0.1, which this convention targets and does not version.) Reference design: linkedin-studio'sbrain/. Interop layer: Google OKF v0.1 (thin veneer). Change log + per-plugin rollout status + coordination protocol:log.md(same directory).
1. Purpose & scope
This convention exists for interop, not standard-adoption for its own sake. Three plugins in this marketplace independently grew a user-owned "second brain" — a wiki of the user's personal and organizational context the plugin retrieves from during chat and commands. This document defines the one shared on-disk form so a single reader can traverse all three, and so a future shared retrieval skill (if ever justified — §10) has one contract to build against.
- In scope: the user's own context/data — the per-user second brain. Where that brain sits on
disk is not part of this contract (§8): the reference impl (okr) runs per installation root
over a project installation root (
.claude/okr/, cwd-relative) and a home installation root (~/.claude/okr/org/), and other consumers install none at all. A single~/.claude/<plugin>/…example would misstate both. ("Root" is overloaded across this ecosystem — installation root (here, §8), bundle root (an interior link-resolution origin,method-spec.md:68-69), source root (ingest extraction,ingest-spec.md:106-108), and the §3 root marker. Every bare use in this document means installation root.) - Explicitly out of scope: each plugin's domain reference files (skill
references/*). Those stay native Claude Code skill-references (Anthropic-recommended progressive disclosure). The decisive test, which all three plugins reached independently: not "is it an LLM-wiki" (both are) but "is there already a native, recommended mechanism?" — for skill-refs YES (skills + references + grep), for the second brain NO (it lived in ad-hocorg/*.mdwith no retrieval mechanism). OKF fills a real gap only for the second brain.
2. The three consumers
| Plugin | Second-brain maturity | Role here |
|---|---|---|
| linkedin-studio | Provenance-weighted learning system (episodic/semantic split, evidence-threshold promotion, temporal validity). Most mature. | Reference design. Siblings rise toward it; it is not levelled down to bare OKF. |
| okr | Built: writer + checker (okf-check.mjs, okf-index.mjs) + retrieval skill okr-second-brain-search. |
Reference checker (§7). |
| ms-ai-architect | Designed, not built. Targets the fuller OKF package + a retrieval skill. | Builds against this spec. |
Live rollout status (🔵/🟡/🟢 + commit-refs) lives in log.md, not here.
3. Minimal contract (normative)
A conforming bundle is a directory tree of markdown files, one concept per file. Concept ID =
file path minus .md.
- MUST — every concept file (every
.mdexceptindex.md) carries atype:frontmatter key (free string, e.g.Profile,Operations,JournalEntry). - MUST — every directory level has an
index.md(an authored enumeration of that directory's children — never a filesystem read; carries progressive-disclosure prose). Anindex.mdcarries no frontmatter, with one exception: the bundle-root one MAY carryokf_versionin a frontmatter block (§6, §12). This mirrors upstream verbatim (okf/SPEC.md§8:509-510). - MUST — the bundle-root
index.mdcarries anokf_versionmarker (the upstream OKF version the bundle targets, currently0.1). The value is the upstream version alone; a plugin's own layout revision belongs in the optionalokf_layoutmarker (§12), never here. Enforced on shape — the gate rejects a value that is not version-shaped (/^\d+(\.\d+)*$/), which is what catches a layout snapshot sitting in this field. The value is unquoted before that check (0.3): quotes are YAML syntax, not value, and upstream's own canonical example writesokf_version: "0.2"(okf/SPEC.md:773) — a gate that rejected it would assert something upstream contradicts. Unquoting does not weaken the rule: a quoted layout snapshot still fails. The check deliberately asserts nothing about which upstream versions exist: that value set is Google's (§12), so a bundle targeting a newer upstream version passes. Presence is not enforced — an absent marker is reported, not failed; that is a separate step, not this one. Placement is not enforced either (0.3) — see §6. - MUST (consumers) — preserve unknown frontmatter keys, tolerate unknown
typevalues, tolerate broken cross-links.
This is a superset of upstream OKF, not a restatement of it. The difference is deliberate and
sits in one place: upstream binds the index.md requirement to the bundle root alone — a nested
directory without its own index.md is not an error there (method-spec.md:81-85) — whereas the
MUST above binds every level. §8 permits a plugin to go further than the floor; this is the
catalog exercising that, and a reader must not take it as upstream's requirement restated. okr's
okf-check.mjs (§7) is the reference implementation of these semantics.
4. Recommended fields (warnings, not errors)
title, description, resource (canonical source URI), tags, and a last-content-change
marker whose name depends on the bundle's okf_version (below). Supply where cheap.
- The last-change marker is version-dependent. A bundle declaring
okf_version< 0.2 (or none at all) usestimestamp; >= 0.2 usesgenerated. Upstream retired the first in favour ofgenerated: { by, at }— "timestampis superseded bygenerated.at", one of v0.2's two breaking changes (okf/SPEC.md§13.1:802-803, read at3fcbb9f) — while still permitting a consumer to "fall back to a legacytimestampwhengeneratedis absent" (:804). The gate therefore asks each bundle for the marker its own version names, and never nags a correct bundle of either version. An absent or non-version-shapedokf_versiongets the legacy floor: absence is echoed, not failed (§3), so it still needs a defined list. - Canonical name is
resource(the OKF spec's name) — notsource. - A field that would break a plugin's invariant may be omitted. Example: linkedin-studio omits
timestamp(its serializer is pure/deterministic — a timestamp would break round-trip) andresource(an internal concept has no canonical URI), keepingtype/title/description.
5. Extension keys — rich fields ride along
OKF's permissiveness is the whole point for us: conforming costs type + index.md, nothing more. A
plugin's richer schema survives untouched as extension frontmatter keys that consumers MUST
preserve. linkedin-studio's brain keeps provenance, first_seen, last_seen, evidence_count,
status, and its episodic/semantic split — all as extension keys. Its model-collapse guard
(provenance=published only) is unaffected. Plugins rise toward the richest design; they are not
levelled down to bare OKF.
6. Reserved files & cross-links
-
index.md— an authored enumeration of the directory's children (never a filesystem read — §3), progressive-disclosure prose, and no frontmatter — with one exception: the bundle-rootindex.mdcarriesokf_versionin a frontmatter block.Canonical placement (0.3): the frontmatter block. This follows upstream, which is unambiguous on both halves — «Index files contain no frontmatter, with one exception: a bundle-root
index.mdMAY carry anokf_versionkey» (okf/SPEC.md§8:509-510), declared «in a bundle-rootindex.mdfrontmatter block (the only place frontmatter is permitted in anindex.md)» (§12:773-775). Read at upstream commit3fcbb9f. Through 0.2 this convention said the opposite — marker in body text, index files with no frontmatter at all — so the divergence was ours against upstream, and 0.3 removes it rather than documenting it.The pre-0.3 body-text form is still read and still passes. Placement is declared, not enforced: the gate reports which form it found and does not fail on either. Two marketplace emitters wrote body text when 0.3 was cut (measured 2026-07-31), and the §7 parity gate compares this value against a sibling implementation's live checker — enforcing placement before the emitters migrate would redden that gate without a single bundle having become less conformant. Enforcement is a later step and needs its own ruling, exactly as presence does.
-
log.md— change log (optional per bundle; reserved name). -
Cross-links — plain markdown (bundle-relative
/...or relative); relation type conveyed by prose. Consumers MUST tolerate broken links.
7. Reference checker
okr's scripts/okf-check.mjs is the de-facto reference implementation of the minimal contract (§3):
only type required (missing → fail + names the files), recommended fields → warnings, root
index.md okf_version echoed for human comparison (no network — hooks are offline). ~91 lines, zero
npm dependencies, only couples to a ~55-line frontmatter.mjs. The shared spec generalizes okr's
semantics; it does not reinvent them. Reading okr's code is fine; writing okr is a separate go.
A shared checker now lives here: catalog/scripts/okf-check.mjs (+ vendored
okf-frontmatter.mjs), originally lifted from okr's reference impl at c06e4d7 (2026-06-29),
output in English, zero deps, self-contained. It is the canonical cross-plugin acceptance gate
for this convention's §3 minimal contract. Run it per bundle root:
node catalog/scripts/okf-check.mjs <bundle-root>
The two checkers provably diverge on the same input — and that divergence is now a running, tested
signal rather than a latent risk. The catalog copy was lifted from okr once (c06e4d7); okr began
hardening its checker the next day (3b45be7, 2026-06-30: an innboks/+dot-dir walk ignore
okf-check.mjs:37-38, and a scoped checkBundle(root, { strictIngest, files }) signature :73) and
added BOM/CRLF normalization weeks later (482effb, 2026-07-17; lib/frontmatter.mjs:23), while this
copy stayed frozen at the lift. A running parity gate now measures exactly this: scripts/check-okf-parity.mjs
compares the implementations per file (concept-set membership + type verdict, over default
read-mode) across a committed spec-adversarial corpus (test/okf-parity-corpus/) that carries
red-proof fixtures — inputs the gate MUST go red on, proving it is not green-on-everything. Honest
scope: only two implementations expose a runnable per-file checker today (catalog + okr), so the gate
runs 2-way now and is architected n-way — llm-ingestion-okf's Python checker (fase 2) and its future
Node port slot in behind an availability guard when they legitimately exist. The gate does not claim
the two checkers are identical (they are not); it makes their divergence a monitored red/green fact and
pins a shared latent gap (both still descend into node_modules). Treat the catalog checker as its
own implementation of the §3 floor whose drift against okr is now watched, not a verified twin. A scaffolded linkedin-studio brain/
validates clean under this gate ("OK: valid OKF bundle", exit 0). Each plugin may keep its own dev-loop
check (linkedin-studio's TypeScript impl under scripts/brain/ stays for its own suite); the catalog
.mjs is the shared gate for this convention's §3 floor. The only Stage-3 remainder is reconciling
the two language implementations into one — deferred until measured need, and not required for the
gate to function.
8. Deliberately NOT mandated
- Auto-classify / convert of arbitrary documents into the bundle — OKF provides nothing for it; a manual inbox/drop-zone seam suffices; build only on demonstrated need.
- Retrieval mechanism — native Grep/Glob/Read (skill instruction "search the wiki first, open only what's relevant") vs. a dedicated fileskb MCP server. All three plugins lean native (Claude Code's Grep/Glob/Read already cover OKF's list/search/read). Per-plugin choice; a "build both, measure" candidate.
- Degree of OKF formalism — full v0.1 conformance vs. this "OKF-compatible form." Plugins sit at
different points (ms-ai-architect targets the fuller package; linkedin-studio emits the minimal form
- extension keys; okr has writer + checker). The minimal contract (§3) is the floor all meet; going further is per-plugin and never required by this spec.
- Placement — where a bundle lives on disk. Unmandated, and measured rather than assumed
(
log.md, 2026-07-25): of the four consumers polled, exactly one installs a bundle anywhere (okr's two roots — §1), and three install none — the bundle directory is a caller-supplied argument per run, with no default, no discovery, and no$HOME/~expansion anywhere in their sources. "No installation root" is a conforming consumer shape, not a gap: a consumer may be handed a bundle it never placed. Note also that installing and writing are separate axes — a consumer can write into a bundle it did not place — so no rule here may assume the writer is the installer. This is why discovery keys on the §3 root marker rather than on a path (log.md, distilled note 3): a marker-based reader serves both shapes; a mandated path form serves neither.- OPEN — which root wins when there is more than one. The two shapes above are installed and
not installed; okr is a third, two marked installation roots at once, and the marker
predicate answers "is this a bundle?" without answering "which of these do I read?". Nor is there
one answer: okr resolves it per axis. Retrieval globs both roots — union is the
search space, with project content winning on conflict only (
SKILL.md:35,46,95); the precedence there is a tie-break, not a lookup that stops at the first root. The org profile is the one axis that truly short-circuits (project overrides home,inject-okr-context.mjs:54-56). So union and precedence are not two axes but two layers on one: the roots carry different content rather than two versions of the same, which is why the search cannot stop early even though a conflict still needs a winner. This costs nothing while placement stays descriptive. It becomes the first question a second implementer asks the moment marker-based discovery is proposed as normative (§10, Stage 3), so it is named here rather than left implied by the two-shape framing. Unresolved; do not plan against an assumed answer.
- OPEN — which root wins when there is more than one. The two shapes above are installed and
not installed; okr is a third, two marked installation roots at once, and the marker
predicate answers "is this a bundle?" without answering "which of these do I read?". Nor is there
one answer: okr resolves it per axis. Retrieval globs both roots — union is the
search space, with project content winning on conflict only (
9. Verified premise corrections (dead-ends — do not plan against these)
Ground-truth-checked against the live GoogleCloudPlatform/knowledge-catalog repo (research agent,
2026-06-26, file+URL log retained). These overturn earlier framing in the per-plugin design notes:
mdcode/kcmdis NOT an OKF tool. It is a Google Cloud Dataplex git-sync tool whose on-disk markdown carries a different frontmatter schema (id/resource.name/createTime/links) than OKF (type/title/description/tags/timestamp). Do not plan to emit or sync OKF bundles via mdcode. (Corrects the "metadata as code" pattern listed in ms-ai-architect's ecosystem digest.)- No reusable OKF ingest code exists. The repo's
reference_agentis a BigQuery+web → OKF producer, Gemini/GCP-bound; it reads a BQ dataset + seed URLs, not a document folder. The GCP-free reusable parts are the SPEC, the emit/serialize/validate core, and theindex.mdsynthesis — patterns, not a drop-in library. Classify/convert of arbitrary docs is 100% build-yourself. - "OKF has no ingest" is true of the format, not the repo. And the per-plugin design notes never actually asked for auto-classification — all three frame the work as OKF as the storage form for a user-owned wiki + a retrieval skill + a maintenance mechanism, with ingest being light ("onboarding writes OKF-conformant").
10. Staged plan
- Stage 1 — Shared form (this document). Cheap, delivers interop alone. Each plugin's user-data conforms; one reader traverses all three. This alone meets the interop goal.
- Stage 2 — Measure divergence. Do the per-plugin retrieval paths diverge enough to hurt? Only a measured "yes" justifies Stage 3 (operator anti-pattern: ambitious initiatives where a config tweak suffices).
- Stage 3 — Conditional shared skill. If justified: extract/generalize okr's working retrieval skill into one home (§11), with a discovery convention for where each plugin's brain lives. Do not build before Stage 2 says so.
11. Homes
- This spec — catalog/marketplace level (here), owned by no single plugin.
- A future shared skill (Stage 3 only) — a standalone marketplace plugin (own repo, release-tagged,
catalog-pinned), installable alongside the others, serving the user's own context directly. Rejected
alternatives: duplicate-per-plugin (drift risk); user-level
~/.claude/skills/(unversioned, outside the catalog).
12. Versioning
Markers are kept deliberately separate. A single field must not carry two unrelated concerns — the upstream OKF version and a plugin's own layout revision — so those are two distinct markers:
-
okf_version— a key in each bundle-rootindex.md's frontmatter block (§6); the upstream Google OKF version the bundle targets (currently0.1). Required (§3). Its value set is owned by Google, so the field has exactly one meaning and stays single-valued — the bundle-root marker (§6) relies on that. When Google bumps OKF, each plugin re-checks conformance. -
okf_layout— an optional marker; a plugin's own layout-revision snapshot (e.g.kb-layout-2026-06). Its value set is owned by the emitting plugin — not by OKF, not by this convention. It is not a version marker, never triggers a cross-plugin re-check, and a consumer that doesn't recognize it ignores it. This is where a plugin stamps its own layout revision, keepingokf_versionreserved for the upstream version alone.Placement (0.3): body text, not the frontmatter block. When
okf_versionmoved into frontmatter (§6), this marker deliberately did not follow it. Upstream's exception is enumerated to a single key — «a bundle-rootindex.mdMAY carry anokf_versionkey» (okf/SPEC.md§8:509-510) — so the frontmatter block in anindex.mdexists by upstream's leave and for upstream's key.okf_layoutis this convention's extension, and it stays outside the block upstream governs. The two markers therefore sit in the same file in different places, which is asymmetric on purpose: it is the reading that holds whichever way upstream's parenthesis is later construed (whether it bounds where a frontmatter block may appear or which keys it may carry — an open question this convention does not own). The gate does not read this marker at all. -
This convention's version (top of this file) — bumped when the shared form (the §3 floor) changes.
log.mdrecords bumps of the two versions (okf_versionand this one); each triggers a per-plugin re-check. Hooks are offline (no auto-poll); version drift is caught by human review + theokf_versionecho inokf-check.
13. Success criterion
Measured against user value (does the plugin retrieve the right personal/org context in chat and commands?) + maintenance reliability — not against formal OKF conformance for its own sake. (Operator, inherited identically by all three tracks.)
14. References
- OKF SPEC v0.1:
github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md(12 June 2026, "a starting point, not a finished standard"). - Per-plugin design notes: linkedin-studio
docs/okf-convergence-brief.md; okrdocs/okf-second-brain-note-2026-06.md; ms-ai-architectdocs/okf-second-brain-brief-2026-06.md. - Shared checker (the cross-plugin gate):
catalog/scripts/okf-check.mjs(+okf-frontmatter.mjs,okf-check.test.mjs) — lifted from okr's reference implokr/scripts/okf-check.mjs(+okf-index.mjs,lib/frontmatter.mjs). - Reference design: linkedin-studio
docs/second-brain/architecture.md; enginescripts/brain/. - Coordination + rollout status:
log.md(this directory).