Skip to content

feat(project-brain): define OKF schema in SKILL.md + shared conversion script - #205

Merged
jinyeow merged 12 commits into
mainfrom
feat/196-okf-schema-templates
Aug 23, 2026
Merged

feat(project-brain): define OKF schema in SKILL.md + shared conversion script#205
jinyeow merged 12 commits into
mainfrom
feat/196-okf-schema-templates

Conversation

@jinyeow

@jinyeow jinyeow commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Defines the OKF v0.2 schema for project-brain markdown per
docs/adr/adopt-okf-for-project-brain-markdown.md, and builds the shared conversion script
the eight migration tickets (#197-204) all run instead of hand-formatting the same transform.

What changed

  • ai-agents/skills/project-brain/SKILL.md: documents the type: enum, the frontmatter
    shape per type, the [[wikilink]] → markdown-link convention, and links the ADR.
  • ai-agents/skills/project-brain/scripts/convert-to-okf.ps1 (new): rewrites both wikilink
    forms, inserts type:/stale_after: frontmatter, adds an empty verified: [] on
    adr/research files, and derives generated.at from the file's own git add-commit,
    omitting it entirely when git history has none rather than guessing today's date.
    Idempotent.
  • tests/convert-to-okf.Tests.ps1 (new): covers both wikilink forms, the git-log-present
    and git-log-absent provenance cases, and idempotency.

The two registered brain repos' templates/{core.md,STATUS.md,adr.md} and root index.md
(okf_version: "0.2") are updated separately, committed directly in their own repos per
project-brain's update contract (no PR flow there):

  • E:\Personal Projects\brain: fcd96f1
  • E:\HollardInsuranceRetail\brain: 465a8b3

Verify

Invoke-Pester -Path tests/convert-to-okf.Tests.ps1 -Output Detailed
Invoke-ScriptAnalyzer -Recurse -Path . -Settings PSScriptAnalyzerSettings.psd1

Closes #196.

jinyeow added 12 commits August 24, 2026 01:05
Resolves the design-pass questions #195 flagged as open: type: enum per
directory role, ADR status remapped to draft/stable/deprecated, wikilink
migration folded into scope, whole-brain-as-one-bundle, generated:/verified:
with conservative non-fabricated provenance, and the shared-conversion-script
approach for the mechanical parts of the migration.

Also adds two CONTEXT.md glossary entries (OKF bundle, OKF concept).

Refs: AB#196
…n script

Per docs/adr/adopt-okf-for-project-brain-markdown.md:
- SKILL.md documents the type: enum, per-type frontmatter shape, the
  [[wikilink]] -> markdown-link convention, and links the ADR
- new scripts/convert-to-okf.ps1: the shared mechanical conversion
  (#197-204 all run this) — wikilink rewrite, type:/stale_after:
  insertion, verified: [] insertion (adr/research), generated.at
  provenance from git log --diff-filter=A (omitted, never guessed,
  when git history has none); idempotent
- Pester tests covering both wikilink forms, git-log-present/absent
  provenance, and idempotency

Closes #196

Refs: AB#196
…es [#f205-6]

SKILL.md's frontmatter-shape table documented generated: { by, at }
unconditionally; a backfilled file may legitimately carry { at } only
when original authorship can't be attributed. Clarified in SKILL.md
and the ADR so a conformance check against either doc doesn't flag
every migrated file.

Refs: AB#196
…rgets [#f205-2, #f205-3]

Convert-Wikilinks double-appended .md onto targets already ending in
.md and folded #anchor fragments into the filename before appending
.md, both breaking the resulting link. It also ran over the raw
whole-file text, so it could rewrite wikilink-looking text inside
inline code, fenced code, or frontmatter.

Added Format-WikilinkTarget to strip an existing .md suffix and
re-append a #fragment after .md instead of before. Split the body on
code spans/fences before transforming (prose only), and reordered
ConvertTo-OkfFile to run the transform on the frontmatter-split body
so frontmatter is never touched. 7 new tests, 17/17 green.

Refs: AB#196
stale_after was only inserted when absent, so a later STATUS.md edit
to updated: was never reflected on rerun -- the old stale_after value
stayed in place, understating or overstating staleness.

Now always recomputed from the current updated: value and replaced
in place when present. 2 new tests (recompute-on-change, no-op when
updated: is unchanged), 19/19 green.

Refs: AB#196
…ute [#f205-1]

ConvertTo-OkfFile passed the absolute file path into Get-OkfType via
a dead-alias variable, so its role-directory patterns (research/,
adr/, templates/, ...) matched any ancestor segment outside the brain
repo -- a brain checked out under a parent directory sharing a role
name would mistype or suppress conversion of the whole tree.

Now computes the path relative to the git root (Find-GitRoot) once
and passes that through. 1 new test (a fixture nested under an
adr-named ancestor, file actually in reports/), 20/20 green.

Refs: AB#196
… omitting provenance [#f205-5]

Get-GitAddedDate redirected stderr to $null and never checked
$LASTEXITCODE, so a genuinely broken git log call (corrupt repo, bad
pathspec) was indistinguishable from the legitimate 'no history for
this file' case -- both silently omitted generated.at.

Now distinguishes an unborn HEAD (a real empty repo, no commits yet
-- the legitimate no-history case, still returns $null) from any
other git failure (throws with repo + file context) via
git rev-parse --is-inside-work-tree / --verify -q HEAD rather than
parsing error text. 1 new test, 21/21 green.

Refs: AB#196
…d CRLF [#f205-7, #f205-8]

Get-FrontMatter's regex required a newline after the closing --- fence,
so a file whose fence was the last line with no trailing newline fell
through to being treated as body -- a second frontmatter block got
prepended on rerun, the exact duplicate the script's idempotency
docstring claims doesn't happen.

Separately, the frontmatter rebuild always rejoined with a bare LF
regardless of the file's actual line endings, so a CRLF file's
frontmatter flipped to LF on every run while the body stayed CRLF --
mixed endings, and changed:$true forever instead of a true no-op.

Fixed both: the closing-fence newline is now optional in the regex,
and the rebuild detects and preserves the file's dominant line-ending
style. 2 new tests, 23/23 green.

Refs: AB#196
…ates/ exclusion [#f205-9]

Closes 3 real coverage gaps against the script's documented behavior:
a -WhatIf no-op assertion (SupportsShouldProcess was advertised but
untested), a generated.by-absence assertion alongside the existing
at:-presence test for backfilled files, and a templates/ exclusion
test -- confirmed against the actual code that templates/ only
excludes type-derived fields, not wikilink rewriting, and named the
test to match that real behavior rather than an assumed one. 26/26
green.

Refs: AB#196
…inks and delimiter run-length matching [#f205-2, #f205-3]

Cycle-1's fixes for these findings were incomplete, caught on a
cycle-2 re-review:

- Format-WikilinkTarget still corrupted a bare local-heading link
  ([[#Heading]] -> [](/.md#Heading), empty label, malformed path).
  A target with no path segment before # isn't a cross-file link at
  all (OKF's link syntax only covers file references), so it's now
  left as a plain #fragment link instead of getting a .md path.

- The code/frontmatter skip only recognized a literal triple-backtick
  fence and single-backtick span, missing tilde fences, longer
  fences wrapping a shorter example, and double-backtick spans.
  Replaced with run-length-aware matching (backreference to the
  opening delimiter's exact run length) via manual Matches +
  StringBuilder reconstruction, since multiple capture groups broke
  regex.Split's even/odd interleaving.

5 new tests (bare heading x2, tilde fence, 4-backtick-wrapping-3,
double-backtick span), 31/31 green. Known pre-existing gap flagged,
not fixed: an unclosed fence with no closing delimiter before EOF
falls through as prose (same in the original code, unlikely in
well-formed brain files).

Refs: AB#196
…at into an incomplete block [#f205-12, #f205-13]

f205-5's fix distinguished unborn-HEAD from a real git-log failure,
but treated ANY failure of the verify-HEAD check as the legitimate
unborn case -- a narrower version of the original silent-swallow
bug survived inside its own fix. Now requires both symbolic-ref -q
HEAD to succeed and verify -q HEAD to fail before treating it as
unborn; anything else (corrupt HEAD content, a valid HEAD pointing
at a corrupt branch ref) falls through to the existing throw.

Separately, a file with an existing generated: block containing only
by: (no at:) never got at: backfilled -- the insertion logic only
handled the no-block-at-all case. Now detects an existing generated:
mapping (block or inline form) missing at: and adds it in place.

5 new tests (corrupt HEAD content, corrupt branch-target ref,
block-style and inline-style at: backfill, existing legitimate-
unborn-HEAD case reconfirmed unregressed), 35/35 green.

Refs: AB#196
…ets, split fragment at first # [#f205-16, #f205-17, #f205-18]

Three more bugs in Format-WikilinkTarget found on a third independent
review pass:

- A wikilink fragment with spaces ([[a/b#Some Heading]],
  [[#My Heading]]) produced an unencoded space in the link
  destination, breaking CommonMark link parsing. Fragments are now
  slugified (lowercase, non-alphanumeric runs to a single hyphen,
  trimmed) to match how real Markdown renderers generate heading
  anchors -- a punctuation-only or empty fragment slugifies to
  nothing and is dropped rather than left as a dangling #.

- A non-markdown asset target ([[image.png]], [[report.pdf]]) got
  .md wrongly appended after only a literal .md suffix was being
  stripped. Now any target already ending in a file extension is
  left untouched (no .md appended), while .md targets still have
  their existing suffix stripped and cleanly re-added -- not
  double-appended.

- The fragment-split regex split at the LAST # in the target, so a
  nested-heading link ([[file#H1#H2]]) mis-split at the wrong point.
  Now splits at the first #, keeping the rest of the fragment intact.

Necessarily updated two pre-existing tests whose expected output
encoded the same fragment-casing bug (#Background -> #background,
matching real GitHub anchor generation). 7 new tests, 42/42 green.

Known accepted limitation, not fixed: the asset-extension check is a
generic "ends in a dot-suffix" heuristic, so a multi-dot page name
with no real extension would misclassify as an asset -- real brain
filenames don't use multi-dot names, and a hardcoded extension
allowlist would be speculative complexity beyond what this finding
asked for.

Refs: AB#196
@jinyeow
jinyeow marked this pull request as ready for review August 23, 2026 23:46
@jinyeow
jinyeow merged commit 7e69be2 into main Aug 23, 2026
5 checks passed
@jinyeow
jinyeow deleted the feat/196-okf-schema-templates branch August 23, 2026 23:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

project-brain: define OKF schema in templates + SKILL.md

1 participant