[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M724BWSE5R2ZE5P98BHEK65M":3,"skill-same:01M724BWSE5R2ZE5P98BHEK65M":62},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":18,"linkOnly":19,"markdownContent":20,"markdownBody":21,"frontMatter":22,"refName":36,"commitSha":37,"skillDirectory":38,"forksCount":39,"repositoryHtmlUrl":40,"repositoryHomepageUrl":6,"ownerHtmlUrl":41,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":42,"install":43,"resources":55},"01M724BWSE5R2ZE5P98BHEK65M","frontmatter-guard",null,"Validate and auto-repair YAML frontmatter on brain pages. Catches malformed\npages before they enter the brain (missing closing ---, nested quotes, slug\nmismatches, null bytes, empty frontmatter, YAML parse failures), explains\nfiles sync holds instead of importing, and drives the previewed\n`gbrain repair frontmatter` fix. Wraps the `gbrain frontmatter` CLI for\nagent-driven workflows.","weiping\u002Fgbrain-cn","weiping","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F577376?v=4","01M723RHKK6K83FDAAGE48DDS1","01M723RHM767WYBV95VAHQ03A7",1,"MIT","https:\u002F\u002Fgithub.com\u002Fweiping\u002Fgbrain-cn\u002Fblob\u002Fgbrain-cn\u002Fskills\u002Ffrontmatter-guard\u002FSKILL.md",[],[],"2026-11-10 18:39:20",false,"---\nname: frontmatter-guard\nversion: 1.1.0\ndescription: |\n  Validate and auto-repair YAML frontmatter on brain pages. Catches malformed\n  pages before they enter the brain (missing closing ---, nested quotes, slug\n  mismatches, null bytes, empty frontmatter, YAML parse failures), explains\n  files sync holds instead of importing, and drives the previewed\n  `gbrain repair frontmatter` fix. Wraps the `gbrain frontmatter` CLI for\n  agent-driven workflows.\ntriggers:\n  - \"validate frontmatter\"\n  - \"check frontmatter\"\n  - \"fix frontmatter\"\n  - \"frontmatter audit\"\n  - \"brain lint\"\n  - \"held files\"\n  - \"sync held a file\"\ntools:\n  - exec\nmutating: true\n---\n\n# Frontmatter Guard Skill\n\n> **Convention:** see `skills\u002Fconventions\u002Fquality.md` for citation rules; this skill is structural validation, not citation auditing.\n\n## Contract\n\nThis skill guarantees:\n- Every brain page is scanned against the eight canonical frontmatter validation classes\n- Mechanical errors (nested quotes, missing closing `---`, null bytes, slug mismatch) are auto-repairable on demand with `.bak` backups\n- Validation logic is shared with `gbrain doctor`'s `frontmatter_integrity` subcheck — single source of truth\n- Reports per source (gbrain is multi-source since v0.18.0); never silently audits the wrong root\n- Files sync holds are explained from their hold record (code, reason, key, line, fix) and fixed only through a previewed, hash-bound repair the user approved\n\n## Why This Exists\n\nBrain pages pile up over months. Agents write them with malformed frontmatter:\n- Missing closing `---` (entity detector bugs)\n- Unstructured YAML in meeting pages (ingestion bugs)\n- Slug mismatches (path renames not propagated)\n- Null bytes (binary corruption from copy-paste accidents)\n- Nested double quotes in titles (`title: \"Alice \"Ace\" Example\"`)\n\nWithout a guard, these accumulate silently until `gbrain sync` chokes or search returns garbage. The guard makes the failure visible at audit time and trivially fixable.\n\n## Write brain files safely (do this first)\n\n- Write pages through `put_page` \u002F `capture` (MCP or `gbrain put`). gbrain serializes frontmatter itself, so the file on disk is always valid YAML.\n- A script or agent that writes Markdown files directly must build frontmatter with a YAML serializer (`yaml.dump`, js-yaml `dump`), never by string interpolation. `title: ${title}` breaks as soon as a title contains `: `, `#`, quotes or a newline.\n- Check generated content before writing it, without touching disk:\n\n  ```bash\n  printf '%s' \"$content\" | gbrain frontmatter validate --stdin --path notes\u002F2026-10-04-digest.md\n  ```\n\n  Exit 1 means fix it first. The default is the strict producer rule: YAML gbrain could still import by quoting a value fails too, because other tools reject it. `--importable` answers the narrower question \"would sync hold it?\".\n- Never write a key gbrain reads for access or identity (`visibility`, `derived_from`, `slug`, `type`, `id`, `source_id`) from untrusted text.\n- Write no `visibility`, `access`, `confidentiality` or `readable_by` unless the page needs it ([page privacy](..\u002Fconventions\u002Fpage-privacy.md)).\n\n## Held files: read them, then fix them\n\nA file sync cannot import without guessing is **held**: the rest of the source still syncs, a new file has no page yet, and an existing page keeps its last good revision and refuses `put_page` until the file is repaired (do not retry the write). Where holds show up: sync output (`Held \u003Cpath>: \u003Ccode> … Next: \u003Ccommand>`), `gbrain sources status \u003Cid>` (`--json`: `git_holds.items[]`), doctor `git_held_files`, `get_page` `file_held`, and search hits marked `stale`.\n\nEach hold record carries `code`, `reason`, `key`, `line`, `message` (location only, never the value), `fix` (exact argv) and `docs`. Act on `reason`:\n\n| Hold | What to do |\n|------|-----------|\n| `invalid_frontmatter` \u002F `yaml_parse` | Preview `gbrain repair frontmatter --source \u003Cid>`; usually `needs_review` with the exact line to fix by hand. |\n| `invalid_frontmatter` \u002F `needs_interpretation` | Preview with `--include-ambiguous`, show the user each per-file diff, apply only what they approve. |\n| `invalid_frontmatter` \u002F `ambiguous_identity_key` or `ambiguous_protected_key`; `visibility_noncanonical` | Never guess. Show the user the line; they decide the one value (`visibility` decides who can read the page; page privacy reads only `visibility: private`, lowercase on its own line). |\n| `frontmatter_slug_conflict` | Remove the `slug:` line or move the file; the repair proposes removing it under `--include-ambiguous`. |\n| `file_too_large` | The file is over the fixed page limit (24 MiB for Markdown, 5 MB for code); every page under it is searchable, so never split one that fits. Ask the user to add it to `sync.exclude`, or to split it into files under the limit. A smaller page an MCP transport refused goes by file instead (`docs\u002Fguides\u002Fmemory-boundaries.md#large-page-limits`). |\n| `content_rejected` | The user chose `content_sanity.junk_disposition=reject`; ask before changing it. |\n\nThe fix flow (walkthrough with real output: `docs\u002Fguides\u002Frepair.md#held-files`):\n\n```bash\ngbrain sources status \u003Cid>                                  # what is held and why\ngbrain repair frontmatter --source \u003Cid>                     # pass 1: safe quoting only; writes nothing\ngbrain repair frontmatter --source \u003Cid> --include-ambiguous --diff   # pass 2: every interpretation\ngbrain repair frontmatter --source \u003Cid> --include-ambiguous --only \u003Cpath> --apply --expect \u003Chash> --yes\n```\n\n**Ask the user before** every `--apply` (it rewrites their files) and before any `--include-ambiguous` apply: each interpretation (folding lines into a title, keeping the later duplicate) is a guess only the user can confirm. Show the diff, then apply exactly the previewed hash. Safe-class changes only quote a value exactly as gbrain already reads it, but they still rewrite files, so they need the same agreement.\n\nA source that a broken file blocked before the upgrade recovers on its next sync; to do it now run `gbrain sync --source \u003Cid> --no-pull`.\n\n## Validation classes\n\n| Code | Meaning | Auto-fixable? |\n|------|---------|---------------|\n| `MISSING_OPEN` | File doesn't start with `---` | No (needs human) |\n| `MISSING_CLOSE` | No closing `---` before first heading | Yes |\n| `YAML_PARSE` | YAML failed to parse | Sometimes (depends on cause) |\n| `SLUG_MISMATCH` | Frontmatter `slug:` differs from path-derived slug | Yes (removes the field) |\n| `NULL_BYTES` | Binary corruption (`\\x00`) | Yes |\n| `NESTED_QUOTES` | `title: \"outer \"inner\" outer\"` shape | Yes |\n| `NON_STRING_FIELD` | `title`\u002F`type`\u002F`slug` is an unquoted non-string scalar (e.g. `title: 123`, `slug: 2024-06-01`) | No (quote the value) |\n| `EMPTY_FRONTMATTER` | Open + close present but nothing between | No (needs human) |\n\n## Phases\n\n### Phase 1: Audit\n\nRun a read-only scan across all registered sources (or one with `--source \u003Cid>`).\n\n```bash\ngbrain frontmatter audit --json\n```\n\nReports:\n- Per-source counts grouped by error code\n- Sample of up to 20 affected pages per source\n- Total count\n- Scan timestamp\n\nOutput is JSON; agents parse `errors_by_code` and `per_source` to decide next steps.\n\n### Phase 2: Validate one path\n\nValidate a single file or directory (does not require source registration):\n\n```bash\ngbrain frontmatter validate \u003Cpath> --json\n```\n\nExit code 0 = clean; 1 = errors found. Use this in CI pipelines or pre-commit hooks.\n\n### Phase 3: Fix\n\nWhen issues are found:\n\n```bash\ngbrain frontmatter validate \u003Cpath> --fix\n```\n\n`--fix` backs up every modified file under `~\u002F.gbrain\u002Fbackups\u002Ffrontmatter\u002F` before mutating, quotes YAML gbrain reads by quoting (only those lines change), re-validates, and exits 1 when errors remain. Restage fixed files in git (`git add`). `--include-ambiguous` adds interpretations; preview them with `--dry-run` first.\n\n`--dry-run` previews without writing. Use this before applying fixes in batch.\n\nOn a managed brain `--fix` in place is refused: use `gbrain repair frontmatter --source \u003Cid>` (above), which publishes each repaired file through the coordinated writer.\n\n### Phase 4: Pre-commit hook (optional)\n\nFor brain repos that ARE git repos, install the pre-commit hook to block malformed pages from being committed in the first place:\n\n```bash\ngbrain frontmatter install-hook [--source \u003Cid>]\n```\n\nThe hook runs one `gbrain frontmatter validate --staged` process over the staged content of `.md`\u002F`.mdx` files (what will be committed, not the working copy). Bypass with `git commit --no-verify`. Installing writes into the user's repository, so ask first. When doctor `frontmatter_hook` reports an older hook, refresh it with `gbrain frontmatter install-hook --force`.\n\n## Trigger words\n\nWhen the user says any of these, route here:\n- \"validate frontmatter\"\n- \"check frontmatter\"\n- \"fix frontmatter\"\n- \"frontmatter audit\"\n- \"brain lint\"\n\n## Output rules\n\n- Always run `gbrain frontmatter audit --json` first; never assume a brain is clean.\n- Surface counts to the user in plain language; do not dump raw JSON.\n- For `--fix` operations: state how many files will be modified BEFORE running, then confirm.\n- `SLUG_MISMATCH` fixes remove the frontmatter `slug:` field — gbrain derives slug from path. Mention this when the user's title is intentionally renamed.\n- Never auto-fix `MISSING_OPEN` or `EMPTY_FRONTMATTER` without explicit user input — these usually mean a human author started a page and didn't finish.\n\n## Chains with\n\n- `gbrain doctor` — the `frontmatter_integrity` subcheck reports the same counts as `audit`; `frontmatter_repairable` counts files `gbrain repair frontmatter` can fix and `git_held_files` lists held files.\n- `skills\u002Fmaintain\u002FSKILL.md` — broader brain health audit; chain after this skill if other classes of issue are suspected.\n- `gbrain lint` — overlapping rules for skill-file lint (a CLI command, not a skill); the `frontmatter-*` rule names in lint output come from this skill's validation surface.\n\n## Output Format\n\nAudit summary (terse, agent-friendly):\n\n```\nFrontmatter audit — 17 issue(s) across 1 source(s)\n\n[default] \u002FUsers\u002Fme\u002Fbrain\n  17 issue(s)\n    MISSING_CLOSE: 8\n    NESTED_QUOTES: 5\n    NULL_BYTES: 4\n  sample:\n    people\u002Fjane.md — MISSING_CLOSE\n    companies\u002Facme.md — NESTED_QUOTES\n    (+ 12 more)\n\nFix with: gbrain frontmatter validate \u002FUsers\u002Fme\u002Fbrain --fix\n```\n\nJSON envelope (when `--json` is passed):\n\n```json\n{\n  \"ok\": false,\n  \"total\": 17,\n  \"errors_by_code\": { \"MISSING_CLOSE\": 8, \"NESTED_QUOTES\": 5, \"NULL_BYTES\": 4 },\n  \"per_source\": [\n    {\n      \"source_id\": \"default\",\n      \"source_path\": \"\u002FUsers\u002Fme\u002Fbrain\",\n      \"total\": 17,\n      \"errors_by_code\": { \"MISSING_CLOSE\": 8, \"NESTED_QUOTES\": 5, \"NULL_BYTES\": 4 },\n      \"sample\": [{ \"path\": \"people\u002Fjane.md\", \"codes\": [\"MISSING_CLOSE\"] }]\n    }\n  ],\n  \"scanned_at\": \"2026-04-25T22:30:00.000Z\"\n}\n```\n\n`gbrain frontmatter validate \u003Cpath> --json` returns a similar envelope keyed on per-file results instead of per-source.\n\n## Prevention — Writing Valid Frontmatter\n\n**This is the most important section.** Fixing broken frontmatter is good. Not writing broken frontmatter in the first place is better.\n\n### YAML arrays (the historical #1 error source)\n\n```yaml\n# Correct: single-quoted YAML flow (canonical form gbrain emits)\ntags: ['yc', 'w2025', 'ai']\n\n# Correct: unquoted scalars (fine when values have no special chars)\ntags: [yc, w2025, ai]\n\n# Correct: block style\ntags:\n  - yc\n  - w2025\n\n# Tolerated post-v0.37.5.0 but non-canonical: JSON-style double quotes\ntags: [\"yc\", \"w2025\"]\n\n# Broken: mixed JSON objects and strings (invalid YAML)\ntags: [{\"name\": \"sports\"}, \"posterous\"]\n```\n\n**Why this used to break:** before v0.37.5.0, the validator counted unescaped `\"` characters and flagged any line with 3+. A flow sequence like `tags: [\"yc\", \"w2025\"]` has 4 unescaped `\"` by design — it's valid YAML, but the dumb counter flagged it anyway. One brain saw 6,981 of these on a single doctor run. v0.37.5.0 parses suspicious values with `js-yaml.safeLoad` before flagging, so JSON-style arrays no longer trigger NESTED_QUOTES.\n\n**Why you should still write the canonical form:** the auto-fix engine (`gbrain frontmatter validate --fix`) and the inferred-frontmatter serializer both emit single-quoted YAML for `tags:` \u002F `aliases:`. Writing the canonical form in new content keeps the source files stylistically consistent and makes diffs against `--fix` runs empty.\n\n**The classic LLM trap:** code like `tags: [${items.map(t => JSON.stringify(t)).join(', ')}]` produces `tags: [\"yc\", \"w2025\"]`. Use single quotes with an apostrophe fallback: `tags: [${items.map(t => t.includes(\"'\") ? JSON.stringify(t) : \"'\" + t + \"'\").join(', ')}]`. Or use a YAML library that knows how to emit canonical YAML.\n\n### Quoted scalars\n\n```yaml\n# Correct: single quotes for values with special chars\ntitle: 'My \"Quoted\" Title'\n\n# Correct: double quotes when value has apostrophes\ntitle: \"Men's Fashion Guide\"\n\n# Broken: double quotes wrapping inner double quotes\ntitle: \"My \"Quoted\" Title\"\n```\n\n### When to quote at all\n\n- **Unquoted** is fine for simple values: `type: person`, `batch: w2025`\n- **Quote** when the value contains `: \" ' # [ ] { } | > & * ! ? ,` or starts with `@`\n- **Single quotes** are the default safe choice\n- **Double quotes** only when the value itself contains apostrophes\n\n## When it fails\n\nFollow the [agent operator protocol](..\u002Fconventions\u002Fagent-operator-protocol.md) for any gbrain error `code`, exit code, `[AGENT]` block or notice block. Specific to this skill:\n\n- `gbrain frontmatter validate` exits 1: errors were found (that is the result, not a crash). Parse `errors_by_code` and report counts per source.\n- Before a fix pass, state how many files will change and get the user's agreement; the fix writes `.bak` backups, and YAML_PARSE errors are not always auto-repairable.\n- `gbrain sync` holds a file (`invalid_frontmatter`, `frontmatter_slug_conflict`, `file_too_large`, `content_rejected`, `visibility_noncanonical`): the sync still succeeded. Read the hold (`gbrain sources status \u003Cid>`) and follow \"Held files\" above.\n- `put_page` refuses a page whose newer file is held: repair the file; retrying the write refuses again.\n\n## Anti-Patterns\n\n**Don't auto-fix `MISSING_OPEN` or `EMPTY_FRONTMATTER` without user input.** These usually mean a human author started a page and didn't finish — silently inserting `---` markers around an unfinished draft is wrong.\n\n**Don't use `--fix` to \"make doctor green\" without reading the audit first.** SLUG_MISMATCH cases are surfaced for manual review specifically because gbrain derives the slug from path. A mismatch usually means the user renamed a file intentionally; auto-removing the slug field is the right outcome only when you've confirmed the rename was deliberate.\n\n**Don't skip the `.bak` backups.** The `.bak` is the safety contract for non-git brain repos. If `.bak` files accumulate after a fix run, that's a feature, not a bug — the user can review the diffs and delete the backups when satisfied.\n\n**Don't run `audit` on a brain where sources aren't registered.** The CLI returns \"no registered sources to audit\" gracefully, but the migration emits a `skipped: no_sources` phase result. Don't paper over this with a manual path-walk; the right fix is to register the source via `gbrain sources add`.\n\n**Don't install the pre-commit hook on brain dirs outside any git repo.** The install-hook command skips them automatically with a one-line note (a brain that is a subdirectory of a host repo is fine — the hook installs at the host root, scoped to that subdirectory). If you see \"skipped, not a git repo\" and want validation at write time anyway, use the `audit` command on a cron schedule.\n","# Frontmatter Guard Skill\n\n> **Convention:** see `skills\u002Fconventions\u002Fquality.md` for citation rules; this skill is structural validation, not citation auditing.\n\n## Contract\n\nThis skill guarantees:\n- Every brain page is scanned against the eight canonical frontmatter validation classes\n- Mechanical errors (nested quotes, missing closing `---`, null bytes, slug mismatch) are auto-repairable on demand with `.bak` backups\n- Validation logic is shared with `gbrain doctor`'s `frontmatter_integrity` subcheck — single source of truth\n- Reports per source (gbrain is multi-source since v0.18.0); never silently audits the wrong root\n- Files sync holds are explained from their hold record (code, reason, key, line, fix) and fixed only through a previewed, hash-bound repair the user approved\n\n## Why This Exists\n\nBrain pages pile up over months. Agents write them with malformed frontmatter:\n- Missing closing `---` (entity detector bugs)\n- Unstructured YAML in meeting pages (ingestion bugs)\n- Slug mismatches (path renames not propagated)\n- Null bytes (binary corruption from copy-paste accidents)\n- Nested double quotes in titles (`title: \"Alice \"Ace\" Example\"`)\n\nWithout a guard, these accumulate silently until `gbrain sync` chokes or search returns garbage. The guard makes the failure visible at audit time and trivially fixable.\n\n## Write brain files safely (do this first)\n\n- Write pages through `put_page` \u002F `capture` (MCP or `gbrain put`). gbrain serializes frontmatter itself, so the file on disk is always valid YAML.\n- A script or agent that writes Markdown files directly must build frontmatter with a YAML serializer (`yaml.dump`, js-yaml `dump`), never by string interpolation. `title: ${title}` breaks as soon as a title contains `: `, `#`, quotes or a newline.\n- Check generated content before writing it, without touching disk:\n\n  ```bash\n  printf '%s' \"$content\" | gbrain frontmatter validate --stdin --path notes\u002F2026-10-04-digest.md\n  ```\n\n  Exit 1 means fix it first. The default is the strict producer rule: YAML gbrain could still import by quoting a value fails too, because other tools reject it. `--importable` answers the narrower question \"would sync hold it?\".\n- Never write a key gbrain reads for access or identity (`visibility`, `derived_from`, `slug`, `type`, `id`, `source_id`) from untrusted text.\n- Write no `visibility`, `access`, `confidentiality` or `readable_by` unless the page needs it ([page privacy](..\u002Fconventions\u002Fpage-privacy.md)).\n\n## Held files: read them, then fix them\n\nA file sync cannot import without guessing is **held**: the rest of the source still syncs, a new file has no page yet, and an existing page keeps its last good revision and refuses `put_page` until the file is repaired (do not retry the write). Where holds show up: sync output (`Held \u003Cpath>: \u003Ccode> … Next: \u003Ccommand>`), `gbrain sources status \u003Cid>` (`--json`: `git_holds.items[]`), doctor `git_held_files`, `get_page` `file_held`, and search hits marked `stale`.\n\nEach hold record carries `code`, `reason`, `key`, `line`, `message` (location only, never the value), `fix` (exact argv) and `docs`. Act on `reason`:\n\n| Hold | What to do |\n|------|-----------|\n| `invalid_frontmatter` \u002F `yaml_parse` | Preview `gbrain repair frontmatter --source \u003Cid>`; usually `needs_review` with the exact line to fix by hand. |\n| `invalid_frontmatter` \u002F `needs_interpretation` | Preview with `--include-ambiguous`, show the user each per-file diff, apply only what they approve. |\n| `invalid_frontmatter` \u002F `ambiguous_identity_key` or `ambiguous_protected_key`; `visibility_noncanonical` | Never guess. Show the user the line; they decide the one value (`visibility` decides who can read the page; page privacy reads only `visibility: private`, lowercase on its own line). |\n| `frontmatter_slug_conflict` | Remove the `slug:` line or move the file; the repair proposes removing it under `--include-ambiguous`. |\n| `file_too_large` | The file is over the fixed page limit (24 MiB for Markdown, 5 MB for code); every page under it is searchable, so never split one that fits. Ask the user to add it to `sync.exclude`, or to split it into files under the limit. A smaller page an MCP transport refused goes by file instead (`docs\u002Fguides\u002Fmemory-boundaries.md#large-page-limits`). |\n| `content_rejected` | The user chose `content_sanity.junk_disposition=reject`; ask before changing it. |\n\nThe fix flow (walkthrough with real output: `docs\u002Fguides\u002Frepair.md#held-files`):\n\n```bash\ngbrain sources status \u003Cid>                                  # what is held and why\ngbrain repair frontmatter --source \u003Cid>                     # pass 1: safe quoting only; writes nothing\ngbrain repair frontmatter --source \u003Cid> --include-ambiguous --diff   # pass 2: every interpretation\ngbrain repair frontmatter --source \u003Cid> --include-ambiguous --only \u003Cpath> --apply --expect \u003Chash> --yes\n```\n\n**Ask the user before** every `--apply` (it rewrites their files) and before any `--include-ambiguous` apply: each interpretation (folding lines into a title, keeping the later duplicate) is a guess only the user can confirm. Show the diff, then apply exactly the previewed hash. Safe-class changes only quote a value exactly as gbrain already reads it, but they still rewrite files, so they need the same agreement.\n\nA source that a broken file blocked before the upgrade recovers on its next sync; to do it now run `gbrain sync --source \u003Cid> --no-pull`.\n\n## Validation classes\n\n| Code | Meaning | Auto-fixable? |\n|------|---------|---------------|\n| `MISSING_OPEN` | File doesn't start with `---` | No (needs human) |\n| `MISSING_CLOSE` | No closing `---` before first heading | Yes |\n| `YAML_PARSE` | YAML failed to parse | Sometimes (depends on cause) |\n| `SLUG_MISMATCH` | Frontmatter `slug:` differs from path-derived slug | Yes (removes the field) |\n| `NULL_BYTES` | Binary corruption (`\\x00`) | Yes |\n| `NESTED_QUOTES` | `title: \"outer \"inner\" outer\"` shape | Yes |\n| `NON_STRING_FIELD` | `title`\u002F`type`\u002F`slug` is an unquoted non-string scalar (e.g. `title: 123`, `slug: 2024-06-01`) | No (quote the value) |\n| `EMPTY_FRONTMATTER` | Open + close present but nothing between | No (needs human) |\n\n## Phases\n\n### Phase 1: Audit\n\nRun a read-only scan across all registered sources (or one with `--source \u003Cid>`).\n\n```bash\ngbrain frontmatter audit --json\n```\n\nReports:\n- Per-source counts grouped by error code\n- Sample of up to 20 affected pages per source\n- Total count\n- Scan timestamp\n\nOutput is JSON; agents parse `errors_by_code` and `per_source` to decide next steps.\n\n### Phase 2: Validate one path\n\nValidate a single file or directory (does not require source registration):\n\n```bash\ngbrain frontmatter validate \u003Cpath> --json\n```\n\nExit code 0 = clean; 1 = errors found. Use this in CI pipelines or pre-commit hooks.\n\n### Phase 3: Fix\n\nWhen issues are found:\n\n```bash\ngbrain frontmatter validate \u003Cpath> --fix\n```\n\n`--fix` backs up every modified file under `~\u002F.gbrain\u002Fbackups\u002Ffrontmatter\u002F` before mutating, quotes YAML gbrain reads by quoting (only those lines change), re-validates, and exits 1 when errors remain. Restage fixed files in git (`git add`). `--include-ambiguous` adds interpretations; preview them with `--dry-run` first.\n\n`--dry-run` previews without writing. Use this before applying fixes in batch.\n\nOn a managed brain `--fix` in place is refused: use `gbrain repair frontmatter --source \u003Cid>` (above), which publishes each repaired file through the coordinated writer.\n\n### Phase 4: Pre-commit hook (optional)\n\nFor brain repos that ARE git repos, install the pre-commit hook to block malformed pages from being committed in the first place:\n\n```bash\ngbrain frontmatter install-hook [--source \u003Cid>]\n```\n\nThe hook runs one `gbrain frontmatter validate --staged` process over the staged content of `.md`\u002F`.mdx` files (what will be committed, not the working copy). Bypass with `git commit --no-verify`. Installing writes into the user's repository, so ask first. When doctor `frontmatter_hook` reports an older hook, refresh it with `gbrain frontmatter install-hook --force`.\n\n## Trigger words\n\nWhen the user says any of these, route here:\n- \"validate frontmatter\"\n- \"check frontmatter\"\n- \"fix frontmatter\"\n- \"frontmatter audit\"\n- \"brain lint\"\n\n## Output rules\n\n- Always run `gbrain frontmatter audit --json` first; never assume a brain is clean.\n- Surface counts to the user in plain language; do not dump raw JSON.\n- For `--fix` operations: state how many files will be modified BEFORE running, then confirm.\n- `SLUG_MISMATCH` fixes remove the frontmatter `slug:` field — gbrain derives slug from path. Mention this when the user's title is intentionally renamed.\n- Never auto-fix `MISSING_OPEN` or `EMPTY_FRONTMATTER` without explicit user input — these usually mean a human author started a page and didn't finish.\n\n## Chains with\n\n- `gbrain doctor` — the `frontmatter_integrity` subcheck reports the same counts as `audit`; `frontmatter_repairable` counts files `gbrain repair frontmatter` can fix and `git_held_files` lists held files.\n- `skills\u002Fmaintain\u002FSKILL.md` — broader brain health audit; chain after this skill if other classes of issue are suspected.\n- `gbrain lint` — overlapping rules for skill-file lint (a CLI command, not a skill); the `frontmatter-*` rule names in lint output come from this skill's validation surface.\n\n## Output Format\n\nAudit summary (terse, agent-friendly):\n\n```\nFrontmatter audit — 17 issue(s) across 1 source(s)\n\n[default] \u002FUsers\u002Fme\u002Fbrain\n  17 issue(s)\n    MISSING_CLOSE: 8\n    NESTED_QUOTES: 5\n    NULL_BYTES: 4\n  sample:\n    people\u002Fjane.md — MISSING_CLOSE\n    companies\u002Facme.md — NESTED_QUOTES\n    (+ 12 more)\n\nFix with: gbrain frontmatter validate \u002FUsers\u002Fme\u002Fbrain --fix\n```\n\nJSON envelope (when `--json` is passed):\n\n```json\n{\n  \"ok\": false,\n  \"total\": 17,\n  \"errors_by_code\": { \"MISSING_CLOSE\": 8, \"NESTED_QUOTES\": 5, \"NULL_BYTES\": 4 },\n  \"per_source\": [\n    {\n      \"source_id\": \"default\",\n      \"source_path\": \"\u002FUsers\u002Fme\u002Fbrain\",\n      \"total\": 17,\n      \"errors_by_code\": { \"MISSING_CLOSE\": 8, \"NESTED_QUOTES\": 5, \"NULL_BYTES\": 4 },\n      \"sample\": [{ \"path\": \"people\u002Fjane.md\", \"codes\": [\"MISSING_CLOSE\"] }]\n    }\n  ],\n  \"scanned_at\": \"2026-04-25T22:30:00.000Z\"\n}\n```\n\n`gbrain frontmatter validate \u003Cpath> --json` returns a similar envelope keyed on per-file results instead of per-source.\n\n## Prevention — Writing Valid Frontmatter\n\n**This is the most important section.** Fixing broken frontmatter is good. Not writing broken frontmatter in the first place is better.\n\n### YAML arrays (the historical #1 error source)\n\n```yaml\n# Correct: single-quoted YAML flow (canonical form gbrain emits)\ntags: ['yc', 'w2025', 'ai']\n\n# Correct: unquoted scalars (fine when values have no special chars)\ntags: [yc, w2025, ai]\n\n# Correct: block style\ntags:\n  - yc\n  - w2025\n\n# Tolerated post-v0.37.5.0 but non-canonical: JSON-style double quotes\ntags: [\"yc\", \"w2025\"]\n\n# Broken: mixed JSON objects and strings (invalid YAML)\ntags: [{\"name\": \"sports\"}, \"posterous\"]\n```\n\n**Why this used to break:** before v0.37.5.0, the validator counted unescaped `\"` characters and flagged any line with 3+. A flow sequence like `tags: [\"yc\", \"w2025\"]` has 4 unescaped `\"` by design — it's valid YAML, but the dumb counter flagged it anyway. One brain saw 6,981 of these on a single doctor run. v0.37.5.0 parses suspicious values with `js-yaml.safeLoad` before flagging, so JSON-style arrays no longer trigger NESTED_QUOTES.\n\n**Why you should still write the canonical form:** the auto-fix engine (`gbrain frontmatter validate --fix`) and the inferred-frontmatter serializer both emit single-quoted YAML for `tags:` \u002F `aliases:`. Writing the canonical form in new content keeps the source files stylistically consistent and makes diffs against `--fix` runs empty.\n\n**The classic LLM trap:** code like `tags: [${items.map(t => JSON.stringify(t)).join(', ')}]` produces `tags: [\"yc\", \"w2025\"]`. Use single quotes with an apostrophe fallback: `tags: [${items.map(t => t.includes(\"'\") ? JSON.stringify(t) : \"'\" + t + \"'\").join(', ')}]`. Or use a YAML library that knows how to emit canonical YAML.\n\n### Quoted scalars\n\n```yaml\n# Correct: single quotes for values with special chars\ntitle: 'My \"Quoted\" Title'\n\n# Correct: double quotes when value has apostrophes\ntitle: \"Men's Fashion Guide\"\n\n# Broken: double quotes wrapping inner double quotes\ntitle: \"My \"Quoted\" Title\"\n```\n\n### When to quote at all\n\n- **Unquoted** is fine for simple values: `type: person`, `batch: w2025`\n- **Quote** when the value contains `: \" ' # [ ] { } | > & * ! ? ,` or starts with `@`\n- **Single quotes** are the default safe choice\n- **Double quotes** only when the value itself contains apostrophes\n\n## When it fails\n\nFollow the [agent operator protocol](..\u002Fconventions\u002Fagent-operator-protocol.md) for any gbrain error `code`, exit code, `[AGENT]` block or notice block. Specific to this skill:\n\n- `gbrain frontmatter validate` exits 1: errors were found (that is the result, not a crash). Parse `errors_by_code` and report counts per source.\n- Before a fix pass, state how many files will change and get the user's agreement; the fix writes `.bak` backups, and YAML_PARSE errors are not always auto-repairable.\n- `gbrain sync` holds a file (`invalid_frontmatter`, `frontmatter_slug_conflict`, `file_too_large`, `content_rejected`, `visibility_noncanonical`): the sync still succeeded. Read the hold (`gbrain sources status \u003Cid>`) and follow \"Held files\" above.\n- `put_page` refuses a page whose newer file is held: repair the file; retrying the write refuses again.\n\n## Anti-Patterns\n\n**Don't auto-fix `MISSING_OPEN` or `EMPTY_FRONTMATTER` without user input.** These usually mean a human author started a page and didn't finish — silently inserting `---` markers around an unfinished draft is wrong.\n\n**Don't use `--fix` to \"make doctor green\" without reading the audit first.** SLUG_MISMATCH cases are surfaced for manual review specifically because gbrain derives the slug from path. A mismatch usually means the user renamed a file intentionally; auto-removing the slug field is the right outcome only when you've confirmed the rename was deliberate.\n\n**Don't skip the `.bak` backups.** The `.bak` is the safety contract for non-git brain repos. If `.bak` files accumulate after a fix run, that's a feature, not a bug — the user can review the diffs and delete the backups when satisfied.\n\n**Don't run `audit` on a brain where sources aren't registered.** The CLI returns \"no registered sources to audit\" gracefully, but the migration emits a `skipped: no_sources` phase result. Don't paper over this with a manual path-walk; the right fix is to register the source via `gbrain sources add`.\n\n**Don't install the pre-commit hook on brain dirs outside any git repo.** The install-hook command skips them automatically with a one-line note (a brain that is a subdirectory of a host repo is fine — the hook installs at the host root, scoped to that subdirectory). If you see \"skipped, not a git repo\" and want validation at write time anyway, use the `audit` command on a cron schedule.\n",{"name":5,"tools":23,"version":25,"mutating":26,"triggers":27,"description":35},[24],"exec","1.1.0",true,[28,29,30,31,32,33,34],"validate frontmatter","check frontmatter","fix frontmatter","frontmatter audit","brain lint","held files","sync held a file","Validate and auto-repair YAML frontmatter on brain pages. Catches malformed\npages before they enter the brain (missing closing ---, nested quotes, slug\nmismatches, null bytes, empty frontmatter, YAML parse failures), explains\nfiles sync holds instead of importing, and drives the previewed\n`gbrain repair frontmatter` fix. Wraps the `gbrain frontmatter` CLI for\nagent-driven workflows.\n","gbrain-cn","b593c7f55100def8c860cb1d65c74631c9879c3b","skills\u002Ffrontmatter-guard",0,"https:\u002F\u002Fgithub.com\u002Fweiping\u002Fgbrain-cn","https:\u002F\u002Fgithub.com\u002Fweiping","2026-11-11 06:54:34",{"source":8,"skillName":5,"commands":44,"prompt":54,"zipAvailable":26},[45,48,51],{"tool":46,"command":47},"npx","npx skills add weiping\u002Fgbrain-cn --skill frontmatter-guard",{"tool":49,"command":50},"bunx","bunx skills add weiping\u002Fgbrain-cn --skill frontmatter-guard",{"tool":52,"command":53},"pnpm","pnpm dlx skills add weiping\u002Fgbrain-cn --skill frontmatter-guard","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fweiping\u002Fgbrain-cn\u002Fblob\u002Fgbrain-cn\u002Fskills\u002Ffrontmatter-guard\u002FSKILL.md",[56],{"path":57,"resourceType":58,"resourceTypeDesc":59,"sizeBytes":60,"sourceHtmlUrl":61},"routing-eval.jsonl","OTHER","其他",673,"https:\u002F\u002Fgithub.com\u002Fweiping\u002Fgbrain-cn\u002Fblob\u002Fgbrain-cn\u002Fskills\u002Ffrontmatter-guard\u002Frouting-eval.jsonl",[63,70,77,84,91,98],{"skillNo":64,"name":65,"nameZh":6,"description":66,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":67,"categories":68,"keywords":69,"publishedAt":18},"01M724KW82DWENSNNY6W7PWDHD","two-tier-extraction","Tiered LLM extraction pattern for large corpus processing (email archives,\ndocument dumps, transcript libraries). A utility-tier model triages and\nclassifies at speed; the reasoning tier does the default deep read; the\ndeep tier is the escalation for the highest-value content. Prevents\nspending deep-tier money on noise while ensuring the important content\ngets the best eyes. A deterministic privacy wall runs before any LLM call.","https:\u002F\u002Fgithub.com\u002Fweiping\u002Fgbrain-cn\u002Fblob\u002Fgbrain-cn\u002Fskills\u002Ftwo-tier-extraction\u002FSKILL.md",[],[],{"skillNo":71,"name":72,"nameZh":6,"description":73,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":74,"categories":75,"keywords":76,"publishedAt":18},"01M724F7V2HQHRWJFT40DPZ0SK","research-compendium","Deep-research a topic end to end and produce a permanent, reusable knowledge asset: archive every primary source verbatim (gated by the user's privacy\u002Fretention posture), write one 1:1 summary per source, then synthesize a single self-contained compendium page. Depth is a dial (base synthesis → grounded primaries → books + counter-canon → saturation), each level an idempotent superset of the one below. Distinct from data-research (structured trackers) and perplexity-research (web deltas): this produces prose knowledge synthesis backed by an archived source corpus.","https:\u002F\u002Fgithub.com\u002Fweiping\u002Fgbrain-cn\u002Fblob\u002Fgbrain-cn\u002Fskills\u002Fresearch-compendium\u002FSKILL.md",[],[],{"skillNo":78,"name":79,"nameZh":6,"description":80,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":83,"publishedAt":18},"01M724ANZBT3M2V17RZYRA9GJ3","data-loss-gate","Confirmation gate before any bulk delete, cleanup, or destructive operation that could result in data loss — shell-level (rm -rf, git rm, bulk sed) or brain-level (bulk forget, delete sweeps, purge-deleted, source removal, raw-SQL truncation). Presents a recoverability card and requires an explicit \"yes\" from the user before proceeding. Routing convention, not an operation-boundary enforcement.","https:\u002F\u002Fgithub.com\u002Fweiping\u002Fgbrain-cn\u002Fblob\u002Fgbrain-cn\u002Fskills\u002Fdata-loss-gate\u002FSKILL.md",[],[],{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":90,"publishedAt":18},"01M7248WZN33SV7Z4E3Z44WA9G","bulk-ingestion","End-to-end discipline for turning any large data source (audio libraries,\nemail takeouts, document corpora, chat exports, API dumps) into brain pages\nat scale. The lifecycle spine: SCHEMA → ACCESS → TRIAL → EVALUATE → IMPROVE\n→ CODIFY → TEST → SKILLIFY → BULK → MONITOR. State is tracked in a durable\nJSON manifest (see MANIFEST-PATTERN.md) so any crash, session boundary, or\nsubagent fan-out resumes from ground truth instead of memory.","https:\u002F\u002Fgithub.com\u002Fweiping\u002Fgbrain-cn\u002Fblob\u002Fgbrain-cn\u002Fskills\u002Fbulk-ingestion\u002FSKILL.md",[],[],{"skillNo":92,"name":93,"nameZh":6,"description":94,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":97,"publishedAt":18},"01M7248JBG4P1JFKF7F4475WG1","brain-ops","Brain knowledge base operations. The core read\u002Fwrite cycle: brain-first lookup,\nread-enrich-write loop, source attribution, ambient enrichment, back-linking.\nRead this before any brain interaction.","https:\u002F\u002Fgithub.com\u002Fweiping\u002Fgbrain-cn\u002Fblob\u002Fgbrain-cn\u002Fskills\u002Fbrain-ops\u002FSKILL.md",[],[],{"skillNo":99,"name":100,"nameZh":6,"description":101,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":104,"publishedAt":18},"01M72480R6DR7D1YDQ5YEDP55M","blog-ingest","Feed and whole-publication ingestion: turn an entire blog, newsletter, or\nRSS\u002FAtom archive into brain source pages. Covers feed discovery, pagination\nwalking, normalization to a common article shape, canonical-URL dedup,\nidempotent re-runs, 429 pacing, and empty-husk repair. This is the\nPUBLICATION-scope skill — a single article URL routes to idea-ingest\ninstead. Per-article enrichment hands off to the brain-ingest-gate skill;\npublic posts only (gated content is skipped, never worked around).","https:\u002F\u002Fgithub.com\u002Fweiping\u002Fgbrain-cn\u002Fblob\u002Fgbrain-cn\u002Fskills\u002Fblog-ingest\u002FSKILL.md",[],[]]