Repository navigation
Conversation
A SKILL.md with no frontmatter now has one finding whose message holds a name/description block made from its H1 and first paragraph, and the summary counts these apart from other errors. In the GitSkills corpus 165K of 1.88M SKILL.md files are such plain Markdown. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…lain Markdown A SKILL.md whose YAML block starts after a blank line was counted as plain Markdown and given a hint that folded its real frontmatter into the description. It now keeps the loader's own error, as before this check. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
There was a problem hiding this comment.
🟡 Changes recommended
Hint generation can abort catalog scans on invalid date-like names and incorrectly extract metadata from fenced code because fence markers and lengths are not matched.
2 open issues
What changed in this PR
2 new must-fix findings.
This PR distinguishes plain-Markdown SKILL.md files from malformed skills in catalog validation and provides suggested frontmatter to help users convert them.
Changes:
- Adds
not-a-skill-no-frontmatterwith a name and description derived from Markdown content. - Counts these files separately in catalog summaries and rejects ignoring the new error code.
- Adds focused tests, documentation, and a changelog entry.
| File | Description |
|---|---|
dokimi/tests/test_catalog_no_frontmatter.py |
Tests classification, hints, summaries, JSON output, and ignore rejection. |
dokimi/dokimi/external/validate.py |
Classifies unloaded Markdown files using the new finding code. |
dokimi/dokimi/external/no_frontmatter.py |
Generates suggested frontmatter from headings and prose. |
dokimi/dokimi/commands/catalog.py |
Updates catalog summaries and rejects ignoring the new code. |
dokimi/dokimi/commands/catalog_scan.py |
Tracks plain-Markdown findings in per-skill scan summaries. |
dokimi/docs/external-content.md |
Documents the new finding and summary count. |
dokimi/changelog.d/20261109_170000_napetrov_no-frontmatter.md |
Announces the new catalog diagnostics and hints. |
Review effort: Balanced
|
OpenCodeReview: Commit c70579a was not reviewed. This PR has reached its review limit (6 reviews). CI at `c70579a`: 8 still running, 9 passing, 2 skipped
To review this commit: re-request review from exai-bot in the Reviewers list. <details><summary>Last review (56c6f9b): no findings</summary> Open threads from the bot: 0 </details> |
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #558 +/- ##
==========================================
+ Coverage 95.71% 95.73% +0.01%
==========================================
Files 415 416 +1
Lines 55346 55461 +115
Branches 9514 9536 +22
==========================================
+ Hits 52977 53093 +116
+ Misses 1466 1465 -1
Partials 903 903
🚀 New features to boost your workflow:
|
The hint scan toggled on any line starting with a backtick or tilde run, so a four-backtick fence holding a three-backtick line ended early and the code after it could become the suggested name or description. Read the body with skill_markdown.outside_fences, which follows CommonMark's marker and length rules. Thread: PRRT_kwDOVBg7g861eNgi Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
A heading or directory named like an invalid date (2026-02-30) made the hint parse fail in yaml.safe_load and abort the catalog scan. The name is now always a JSON string, which is valid YAML, so the parse check is gone. Thread: PRRT_kwDOVBg7g861eNhE Thread: PRRT_kwDOVBg7g861eOGp Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Review at 7a679d0. The new finding and the summary count work, and the load error still fails the scan. The hint needs work before it helps: its suggested Agreed with @exai-bot on the intro paragraph before the H1; I added the README badge case in its thread. What I ran, at 7a679d0:
Checked, no issues
Comments on lines outside the diff:
Every other finding line is indented by two spaces, and on a corpus scan this block repeats for every plain-Markdown file, so the rows can no longer be read line by line. Suggestion: indent the continuation lines of a message in |
Bring the PR branch up to main: the conflicts in catalog.py, catalog_scan.py and validate.py keep both sides (the --ignore refusals; the unreadable row and the plain-Markdown tally). Thread: IC_kwDOVBg7g88AAAABkzfAFw.a Thread: IC_kwDOVBg7g88AAAABkzfAFw.b Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
An intro paragraph above the H1, a badge line, or an H1 inside a paragraph made the hint use the wrong paragraph. Find the first H1, then take the paragraph after it; the first paragraph only when there is no H1. Thread: PRRT_kwDOVBg7g861j-L9 Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The hint took the H1 as the name, and the loader then reported name-mismatch for the directory the skill sits in. Below the scanned root the name is the directory name when it is a valid spec name; the H1 is used at the root and when the directory name is not valid. Thread: PRRT_kwDOVBg7g861puZB Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…Markdown " ---" as the first line counted as plain Markdown, so the hint was built from the YAML keys. Compare the first line stripped, as the blank-line case does: the loader error stands and no hint is offered. Thread: PRRT_kwDOVBg7g861pubH Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…eview too The docstrings of the unloaded-entry review, review_skills, skill_row and review_skill_dir said an unloaded skill gets a review only for broken YAML, or one not-loaded finding. A plain-Markdown SKILL.md gets not-a-skill-no-frontmatter. Thread: PRRT_kwDOVBg7g861pucO Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ragraph
outside_fences has already taken the quote markers off each line, so the
startswith(">") branch never runs on a file read. No behaviour changes: the
"> Master it." case of the test table still pins the quote text.
Thread: PRRT_kwDOVBg7g861pudd
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The hint block in a finding message broke the text output: its lines printed at column 0 between the skill rows. Continuation lines of a row's state and of its findings are indented, as the finding lines already were. Thread: IC_kwDOVBg7g88AAAABkzfAFw.c Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
external-content.md described the hint as the H1 and the first paragraph; the name below the scanned root is the directory's now. Thread: PRRT_kwDOVBg7g861puZB Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…a-skill-no-frontmatter Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
A DEL or a C1 control character in the first paragraph went into the hint raw, and PyYAML refuses it, so the hint block did not load. Remove the characters PyYAML does not accept in a scalar. The blank-line test is now one case of the parametrized opener test. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
external-content.md described the hint as the H1 and the first paragraph; the name below the scanned root is the directory's now, and the description is the first paragraph when there is no H1. Thread: PRRT_kwDOVBg7g861puZB Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…mes only a narrower root A --kind filter cannot leave out plain-Markdown SKILL.md files: --kind skills keeps them all. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
vlad-perevezentsev
left a comment
There was a problem hiding this comment.
LGTM
I checked b7fa886. Everything from my earlier rounds still holds, and the new commits fix the points @vchamarthi and @xaleryb raised: a --- rule before prose, an H1 in a comment, a setext H1, an indented code block, quoted or flow YAML keys, and an unclosed block.
Merge: safe now: it merges cleanly into current main, 10 commits behind, and main has not changed its files since. Once it is in, #454 conflicts with it and needs a rebase.
Disagreed with @vchamarthi on the two unverified notes. The inventory now has not-a-skill-no-frontmatter next to skill-load, which only adds the hint. The --resume gap applies to every new check, not only this one, so it is better as its own issue.
What I ran, at b7fa886:
make ci-local --offline: 18,993 passed, 0 failed, 72 skipped; every step passes.- The hint on a few more layouts: a setext H1 with a paragraph, a tab-indented code block before the paragraph, an HTML paragraph, a list after the H1. Each gives the expected name and description.
Checked, no issues
- Broken skills vs prose: a
---block with a YAML key (plain, quoted, flow or list) keeps the loader's error. A---rule followed by prose gets the hint, and so does an unclosed block whose first line is prose. - Hint: the H1 search and the paragraph both skip HTML comments. A setext H1 is the title. The 64-character limit is tested for a name from the H1 and for one from the directory.
- Resume: a resumed finding with no
codeno longer raises.
Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
|
xaleryb
left a comment
There was a problem hiding this comment.
Re-checked b7fa886 + 4c63e3e + 58a6fdd merged with main (Python 3.13): full suite, ruff, mypy, bandit, growth, comment-block and docs check --strict pass, and the original input of every earlier point (ours, vchamarthi's, antonwolfy's, vlad-perevezentsev's and the bots') passes. Since then only main (#737) was merged in; the PR's own diff is unchanged, and CI on 364d8da is green.
|
@vchamarthi every point of your changes-requested review is fixed at the head, and its original input was re-run and passes (details in the comments above). Could you re-review so the changes-requested state clears? |
antonwolfy
left a comment
There was a problem hiding this comment.
The merge waits for the author on two notes from @vchamarthi's last round: whether the catalog eval inventory should carry the new finding next to skill-load, and --resume over rows written before this change. Neither is answered yet. My own review finds no bug or risk: the classification, the hint and the summary count hold up, and every earlier point is fixed at this head. Four nits inline, all on edge cases of the classifier and the hint. None changes an exit code: each variant is still a failing error row.
What I ran, on a Linux review host at 364d8da:
make ci-local: 31 passed, 1 failed, 14 skipped; 19,813 tests passed, 19 skipped; changed-line coverage gate passes. The one failure is the test step, where 1 test and 2 fixtures timed out at 60 s in tests/test_import_architecture.py and tests/test_audit_test_quality.py, which the PR does not touch; both files pass alone (225 passed)- Reproductions for the classifier findings, with the transport offline.
- A mutation run on the new tests: 106 mutants, 40 of which pass the new test file; the ones that matter are in the test comment.
Checked, no issues
- Commits: all 38 non-merge commits have a
Signed-off-by. --ignore:not-a-skill-no-frontmatteris refused and named in the docs list.- Hint YAML: about 40,000 random inputs plus odd layouts (fences, setext, CRLF, BOM, surrogates, NUL) always give a block that
yaml.safe_loadreads, with a spec-valid name of at most 64 characters and a description of at most 304. - Pasted hint: below the root with a valid directory name, pasting the block gives no
name-mismatch. - Summary count: the same in the whole-catalog,
--per-skilland--jsonl --resumepaths.
…a-skill-no-frontmatter Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Thread: PRRT_kwDOVBg7g863f9ni Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Thread: PRRT_kwDOVBg7g863f9no Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…window Thread: PRRT_kwDOVBg7g863f9nr Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
A closed comment is taken out anywhere outside a code span; an unclosed one only when it opens a line. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
A mid-line comment or a code span cannot run over a blank line; a comment that opens a line can. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
An inline comment or code span is searched for within 1024 characters, so a long paragraph of unclosed markers stays linear in its length. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
A code span takes the whole run of backticks, and closes with a run of the same length. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Waiting on a decision before I change this: should the Waiting on a decision before I change this: should |
|
Round on
|
antonwolfy
left a comment
There was a problem hiding this comment.
The merge waits for the author on two notes from @vchamarthi's last round: whether the catalog eval inventory should carry the new finding next to skill-load, and --resume over rows written before this change. Both were handed back as decisions and are not answered. The comment removal added since my last review holds up, and one nit is inline. My earlier thread on the late-frontmatter window stays open as a nit.
What I ran, on a Linux review host at c4f27c6:
make ci-local: 32 passed, 1 failed, 14 skipped; 20,505 tests passed, 19 skipped; changed-line coverage gate passes. The one failure is the test step, wheretest_a_fixed_finding_leaves_the_baseline_tooin tests/test_audit_test_quality.py timed out at 60 s in setup. The PR does not touch that file, and it passes alone (172 passed)- Reproductions for the comment and code-span handling, with the transport offline: comments before, inside and after text, unclosed and multi-line comments, markers inside code spans, and about 20,000 random inputs.
- A mutation run on the new tests: 48 mutants, 23 of which pass the new test file; the ones that matter are in the test comment.
Still open from my earlier review:
dokimi/dokimi/external/validate.py:367: late frontmatter is recognised only within the first 20 lines and only when---is directly followed byname:ordescription:. A 22-line<!-- ... -->header before a full block, and# Deck,---,title: x,name: deck,description: Use when x.,---, both still getnot-a-skill-no-frontmatterwith a description built from the YAML lines. Your recommendation to keep the window is fine for the code, butexternal-content.mddoes not state it. Say there that frontmatter after another line, or after line 20, or opening with another key, is plain Markdown, and that a block of blank lines counts as frontmatter. That closes the thread.
@vchamarthi's two questions wait for the author, on the inventory and on --resume.
Checked, no issues
- Comment removal:
<!-- a --> Real.givesReal., a mid-line comment keeps the text around it, a multi-line comment over a blank line is removed, an unclosed line-start comment runs to the end, and a stray-->stays. - Code spans:
<!--inside a backtick span is kept, and a comment after an unmatched backtick is still removed. - Hint YAML: about 20,000 random inputs of backticks, comment markers, fences, CRLF, BOM, NUL and quotes always give a block that
yaml.safe_loadreads, with stringnameanddescription. - Blank-line block:
---, a blank line,---keeps the loader's error. - Commits: all 45 non-merge commits have a
Signed-off-by. - Repo rules: the changelog fragment, the SPDX header, the docs nav entry and the growth baseline are in order.
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
- a numeric key (`123: [unclosed`) opens a frontmatter block - a block opening with a flow collection (`[unclosed`) is frontmatter, not a rule - late frontmatter is found by a `name:` or `description:` line anywhere in its block, not only right after `---` - the docs say such blocks keep the loader's error and get no hint Signed-off-by: vchamarthi <123653452+vchamarthi@users.noreply.github.com>
antonwolfy
left a comment
There was a problem hiding this comment.
I pushed a commit per nit, up to d1a8ea0, which fix the two nits from my review:
fcd55c7:dokimi/docs/external-content.md:244: states that a---block after another line, after line 20, or opening with a key other than name or description is plain Markdown, and that a block of blank lines counts as frontmatter.d1a8ea0:dokimi/tests/test_catalog_no_frontmatter.py:317: adds a test for the unclosed line-start comment, the blank line inside a line-start comment, the code span before a later-->, and the 1024-character span bound on both sides.
make ci-local at d1a8ea0: 33 passed, 0 failed, 14 skipped.
Not approving yet. Still open: @vchamarthi on whether the catalog eval inventory should carry not-a-skill-no-frontmatter beside skill-load; @vchamarthi on whether --resume should match on the dokimi version so rows written before this change are re-reviewed.
|
Pushed 1 commit to
Threads: 1 fixed, 0 answered as not a defect. Seen in files outside this PR, not changed here:
The PR description needs these corrections:
Questions for @napetrov1. Should a
|

Summary
A
SKILL.mdthat is plain Markdown with no frontmatter now gets its ownerrorcode,not-a-skill-no-frontmatter. The message carries a suggested frontmatter block: the description is the paragraph after the file's H1 (the first paragraph when there is no H1). The name is the directory's below the scanned root, where the loader checks it against the directory; at the root, or when the directory name is not a valid name, it is the H1 in kebab-case. The catalog summary reports these files apart from broken skills ("N error, of them M plain Markdown with no frontmatter"). This code cannot be passed to--ignore.A file whose
---block starts after a blank line is a broken skill, not prose, and keeps the loader's existing error.Evidence
SKILL.mdfiles have no frontmatter (141K of them start with#), against 39K with broken YAML.tests/test_catalog_no_frontmatter.py(25 test functions, 62 cases); full suite, ruff and mypy pass. Self-review of this head: 1 pass, none blocking.Merge danger
Conflicts with the other PRs in this series on
FINDING_CODESand the docs. The JSON rows of these files change code from the loader error to the new one, so a--resumeover old rows keeps the old code.🤖 Generated with Claude Code