From 9e95222d1248d045d64bd031b1165722f23cb833 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Fri, 26 Jun 2026 20:52:41 +0200 Subject: [PATCH] =?UTF-8?q?feat(linkedin-studio):=20brain=20emits=20OKF-co?= =?UTF-8?q?mpatible=20form=20=E2=80=94=20Stage=201=20(bundle=3Dbrain/,=20i?= =?UTF-8?q?ngest/=20excluded)=20[skip-docs]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-plugin OKF convergence, Stage 1 (docs/okf-convergence-brief.md): the second-brain hub now conforms to OKF-compatible form so a shared retrieval skill (and a sibling agent) can traverse it. - serializeProfile leads with a constant `type: Profile` frontmatter block (round-trip-safe: parseProfile skips it, parse-serialize identity holds). - operations.md seed -> `type: Operations`; index.md seed -> `okf_version: 0.1` marker (markdown text; index files carry no frontmatter per OKF); new brain/journal/index.md (per-level index). Premise correction: the brain is deliberately YAML-free and ingest/published has a byte-exact round-trip invariant (SC2) a frontmatter block would break, so the concept-bundle is scoped to brain/ ONLY; the ingest/ tributary is excluded and pointed to from the hub. We emit frontmatter, add no parser. Verified: new tests/okf-conform.test.ts (5/5); full brain suite 132/132 (0 regressions); cross-tool — okr/scripts/okf-check.mjs validates brain/ (exit 0). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_011vmzxpsFpc8q19LaogAWLD --- scripts/brain/src/profile.ts | 11 ++- scripts/brain/src/scaffold.ts | 17 +++- scripts/brain/tests/okf-conform.test.ts | 103 ++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 scripts/brain/tests/okf-conform.test.ts diff --git a/scripts/brain/src/profile.ts b/scripts/brain/src/profile.ts index 0cbbf4f..0d86004 100644 --- a/scripts/brain/src/profile.ts +++ b/scripts/brain/src/profile.ts @@ -36,9 +36,18 @@ function serializeFact(f: ProfileFact): string { return f.value === "" ? `- [${bracket}]` : `- [${bracket}] ${f.value}`; } -/** Deterministic full-document serialization; both sections always present. */ +/** + * Deterministic full-document serialization; both sections always present. A + * constant OKF frontmatter block (`type: Profile`) leads the document so the brain + * bundle is OKF-compatible form (docs/okf-convergence-brief.md); it carries no + * typed-doc data, so `parseProfile` skips it and `parse ∘ serialize` stays identity. + */ export function serializeProfile(doc: ProfileDoc): string { const lines: string[] = [ + "---", + "type: Profile", + "---", + "", "# Profile", "", `schemaVersion: ${doc.schemaVersion}`, diff --git a/scripts/brain/src/scaffold.ts b/scripts/brain/src/scaffold.ts index d04e435..4a60eb9 100644 --- a/scripts/brain/src/scaffold.ts +++ b/scripts/brain/src/scaffold.ts @@ -35,6 +35,8 @@ function indexSeed(): string { > Map of Content — one screen pointing at every tributary, with a freshness flag. > Generated by \`brain init\`; safe to hand-edit (a re-run never clobbers it). +okf_version: 0.1 + | Tributary | What it holds | Freshness | |-----------|---------------|-----------| | voice-samples | writing style | — | @@ -50,7 +52,10 @@ function indexSeed(): string { } function operationsSeed(): string { - return `# Operations + return `--- +type: Operations +--- +# Operations > The operations centre — where you're headed now, what you're working on, what you might > do next. User-authored: the brain motor never writes here (a re-run of init never @@ -74,6 +79,15 @@ _As of YYYY-MM-DD:_ Append-only session entries (\`YYYY-MM-session.md\`). Raw, never edited — the +> source the consolidation loop distils from. One concept per file. A reserved +> OKF index (no frontmatter); the entries carry the \`type\`. +`; +} + function profileSeed(): string { const templateText = readFileSync(TEMPLATE_PATH, "utf8"); const instancePath = dataRoot(INSTANCE_SUB); @@ -111,6 +125,7 @@ export function initBrain(): InitResult { { sub: "brain/profile.md", seed: profileSeed }, { sub: "brain/index.md", seed: indexSeed }, { sub: "brain/operations.md", seed: operationsSeed }, + { sub: "brain/journal/index.md", seed: journalIndexSeed }, ]; for (const { sub, seed } of files) { diff --git a/scripts/brain/tests/okf-conform.test.ts b/scripts/brain/tests/okf-conform.test.ts new file mode 100644 index 0000000..f63eed7 --- /dev/null +++ b/scripts/brain/tests/okf-conform.test.ts @@ -0,0 +1,103 @@ +import { describe, test, beforeEach, afterEach } from "node:test"; +import assert from "node:assert/strict"; +import { mkdtempSync, rmSync, existsSync, readFileSync, readdirSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; + +import { initBrain } from "../src/scaffold.js"; +import { parseProfile } from "../src/profile.js"; + +/** + * OKF-compatible-form conformance for the `brain/` knowledge bundle. + * + * Stage 1 of the cross-plugin OKF convergence (docs/okf-convergence-brief.md): the + * brain is the REFERENCE design, OKF is a thin interop veneer. Contract — mirrors + * `okr/scripts/okf-check.mjs`, the reference checker: + * - every CONCEPT file (`*.md` except `index.md`) carries a non-empty `type` in a + * leading YAML frontmatter block; + * - the bundle-root `index.md` carries an `okf_version` marker as markdown TEXT + * (index files carry no frontmatter per the OKF spec); + * - each directory level has its own `index.md` (progressive disclosure). + * + * EXCLUDED by design (brief §6): the `ingest/` tributary. `ingest/published/*.md` + * is the byte-exact gold store with a hard round-trip invariant (SC2) that a YAML + * frontmatter block would break — it is a raw tributary, not a navigable concept, + * and the hub `index.md` points to it rather than folding it in. This test walks + * `brain/` ONLY; it never asserts frontmatter on the tributary. + */ + +/** + * Minimal frontmatter `type` reader. We only ever EMIT OKF frontmatter; we never add + * a YAML-parser dependency (the brain is deliberately YAML-free), so a constrained + * regex over the leading `---` block is the right reader here. + */ +function frontmatterType(text: string): string | null { + const block = text.match(/^---\n([\s\S]*?)\n---\n/); + if (!block) return null; + const t = block[1].match(/^type:\s*(.+?)\s*$/m); + return t ? t[1].trim() : null; +} + +/** All concept files (`*.md` except `index.md`) under `root`, recursive. */ +function walkConceptMd(root: string): string[] { + const out: string[] = []; + const walk = (dir: string) => { + for (const e of readdirSync(dir, { withFileTypes: true })) { + const p = join(dir, e.name); + if (e.isDirectory()) walk(p); + else if (e.isFile() && e.name.endsWith(".md") && e.name !== "index.md") out.push(p); + } + }; + walk(root); + return out; +} + +describe("brain/ bundle is OKF-compatible form (Stage 1)", () => { + let root: string; + const prevEnv = process.env.LINKEDIN_STUDIO_DATA; + + beforeEach(() => { + root = mkdtempSync(join(tmpdir(), "brain-okf-")); + process.env.LINKEDIN_STUDIO_DATA = root; + initBrain(); + }); + + afterEach(() => { + if (prevEnv === undefined) delete process.env.LINKEDIN_STUDIO_DATA; + else process.env.LINKEDIN_STUDIO_DATA = prevEnv; + rmSync(root, { recursive: true, force: true }); + }); + + test("bundle-root index.md carries an okf_version marker (markdown text, no frontmatter)", () => { + const index = readFileSync(join(root, "brain/index.md"), "utf8"); + assert.match(index, /^okf_version:\s*\S+/m, "root index.md declares okf_version"); + assert.equal(frontmatterType(index), null, "index.md carries NO frontmatter (OKF reserved file)"); + }); + + test("every concept file under brain/ carries a non-empty frontmatter type", () => { + const concepts = walkConceptMd(join(root, "brain")); + assert.ok(concepts.length > 0, "the bundle has concept files to check"); + for (const f of concepts) { + const type = frontmatterType(readFileSync(f, "utf8")); + assert.ok(type && type.length > 0, `concept ${f} is missing a frontmatter type:`); + } + }); + + test("profile.md is type: Profile AND still parses (rich fields preserved — round-trip intact)", () => { + const text = readFileSync(join(root, "brain/profile.md"), "utf8"); + assert.equal(frontmatterType(text), "Profile"); + const doc = parseProfile(text); + assert.equal(doc.schemaVersion, 1, "line-grammar still parses through the frontmatter"); + assert.ok(doc.static.length > 0, "the seeded static facts survive conformance"); + }); + + test("operations.md is type: Operations", () => { + const text = readFileSync(join(root, "brain/operations.md"), "utf8"); + assert.equal(frontmatterType(text), "Operations"); + }); + + test("each directory level under brain/ has its own index.md (progressive disclosure)", () => { + assert.ok(existsSync(join(root, "brain/index.md")), "brain/index.md"); + assert.ok(existsSync(join(root, "brain/journal/index.md")), "brain/journal/index.md"); + }); +});