Skip to content

Refuse Skill Frontmatter That Parses to a List or a Scalar - #3847

Merged
ptr727 merged 2 commits into
developfrom
feature/reject-non-mapping-skill-frontmatter
Nov 3, 2026
Merged

ptr727 merged 2 commits into
developfrom
feature/reject-non-mapping-skill-frontmatter

Conversation

@ptr727

@ptr727 ptr727 commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes a finding Copilot raised on promotion PR #3843, against the description length gate #3839 added.

skill_description in scripts/build_dist.py returned None ("states no description") for any frontmatter that was not a mapping. A SKILL.md whose frontmatter parsed to a list or a scalar was therefore never measured, and it got past the 1024-character gate. skill-lifecycle and scripts/README.md both say the gate refuses "frontmatter too malformed to measure one". The code now does the same: frontmatter that is not a mapping raises frontmatter is not a mapping, as unclosed and invalid frontmatter already did. Empty frontmatter still reads as stating no description.

  • scripts/build_dist.py: refuse non-mapping frontmatter, and keep returning None for empty frontmatter.
  • tests/test_build_dist.py: a refusal case for list frontmatter, and a not-measured case for empty frontmatter.

Verification

  • python -m unittest discover -s tests -p test_build_dist.py: 99 tests OK.
  • ruff check and ruff format --check on both files: clean.
  • scripts/build_dist.py --check: generated distributions are current.
  • Every SKILL.md under .agents/skills/ opens with mapping frontmatter, so no existing skill newly fails the build.
  • local-strict-review: two passes recorded at the frontier tier. The first raised one finding (no test covered the empty-frontmatter branch), fixed in ce00bf3. The second raised none. The receipt covers the pushed head.

🤖 Generated with Claude Code

ptr727 and others added 2 commits November 2, 2026 20:51
skill_description mapped every non-mapping frontmatter to "no
description", so a SKILL.md whose frontmatter parsed to a list or a
scalar skipped the description length gate. That frontmatter is now
refused as unmeasurable, as an unclosed or invalid one already is.
Empty frontmatter still reads as no description.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings November 3, 2026 04:55
@ptr727

ptr727 commented Nov 3, 2026

Copy link
Copy Markdown
Owner Author

A recorded local strict-review pass covers head ce00bf35178bd28ea312500d66e2a35900d9744d, the content this pull request carries at that commit against develop, and it recorded 0 findings.

@coderabbitai

coderabbitai Bot commented Nov 3, 2026

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are recorded for the change, supporting its progression.

No open issues 🎉

What changed in this PR

This PR strengthens skill frontmatter validation so non-mapping YAML cannot bypass the description-length gate, while preserving empty frontmatter as having no description.

Changes:

  • Reject list and scalar frontmatter with a mapping error.
  • Preserve the existing empty-frontmatter behavior.
  • Add regression coverage for list and empty frontmatter.

This pull request requires changes.

File Summary
scripts/​build_dist.py Validates non-mapping frontmatter, but explicit YAML null or ~ can still bypass refusal. [P1, moderate]
tests/​test_build_dist.py Covers list and empty frontmatter, but lacks an explicit scalar refusal case. [P2, nit]

Review effort: Lite

@codecov

codecov Bot commented Nov 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.30%. Comparing base (a5f318e) to head (ce00bf3).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #3847   +/-   ##
========================================
  Coverage    83.29%   83.30%           
========================================
  Files           29       29           
  Lines         9933     9937    +4     
========================================
+ Hits          8274     8278    +4     
  Misses        1659     1659           
Flag Coverage Δ
python-3.13 83.30% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ptr727
ptr727 merged commit 536bddb into develop Nov 3, 2026
14 checks passed
@ptr727
ptr727 deleted the feature/reject-non-mapping-skill-frontmatter branch November 3, 2026 05:07
ptr727 added a commit that referenced this pull request Nov 3, 2026
…rge on a Running Hosted Review, and Gate Skill Description Length (#3843)

Promotes the changes merged into `develop` since promotion #3797 to
`main`, the end of an away-mode backlog burndown.

- [#3803](#3803) exempts
the Roslyn embedded-language marker (`// lang=json`, `language=`) from
the prose gate's comment rules as a tool directive.
- [#3807](#3807) has
`local_review.py record` take `--report`, the file the reviewer wrote,
bind the pass to its sha256, and derive the finding count from it.
`status` gains `reportPath`.
- [#3809](#3809) holds
`pr_review.py merge` while a hosted review is still running on the head,
and routes a declined Qodo finding to a dismiss request.
- [#3811](#3811) dates an
anchored `intentRef` by the section its anchor names, and lets a
`driftNote` acknowledge a verified intent advisory.
- [#3816](#3816) moves the
null GraphQL node check into the shared `gh_graphql` transport as
`NullNodeError`.
- [#3818](#3818) explains
the workspace-edit default in `GOVERNANCE.md` and names the aggregator's
expression form.
- [#3822](#3822) moves the
abandon discard to the `backlog-burndown` orchestrator, aligns the
abandoned-branch delete, and holds a grown agent-safety set.
- [#3823](#3823) scopes
the Husky prose gate to staged paths and adds `--skip-generated` so a
named file inside a generated tree is skipped.
- [#3828](#3828) reads
Copilot's `Open findings:` label, drops the duplicate `null_node`, and
matches `scripts/README.md` to the Merge Gate.
- [#3829](#3829) bumps the
two stale hub caller snippet pins and documents the fallback's
mixed-pair refusal.
- [#3831](#3831) keeps an
inherited `GIT_DIR` from rerooting the prose gate's subdirectory git
reads.
- [#3832](#3832) gives
`backlog-burndown` a disposition for a worker's standalone clone and
keeps the agent-safety hold past a park.
- [#3835](#3835) makes
`carry.py check-sections` refuse what `apply-sections` would, and name
an open fence.
- [#3836](#3836) pins the
reusable-workflow stubs by placeholder and ticks the PlexCleaner gates
item.
- [#3838](#3838) has the
`runtime-secrets` audit dimension detect a bare `secrets/` directory
from the tracked files that name it.
- [#3839](#3839) gates
skill descriptions at the 1024-character limit and trims every one over
it.
- [#3846](#3846) fixes an
adjacent doc, an in-file pre-existing defect, and an edge case on new
code in the open pull request instead of filing an issue for each, and
keeps filing for code that runs elsewhere.
- [#3847](#3847) refuses
skill frontmatter that parses to a list or a scalar, so the description
length gate cannot be bypassed.

Fixes #3770
Fixes #3699
Fixes #3705
Fixes #3789
Fixes #1819
Fixes #727
Fixes #3710
Fixes #3706
Fixes #3623
Fixes #3687
Fixes #3617
Fixes #3757
Fixes #3790
Fixes #3786
Fixes #3785
Fixes #3772
Fixes #1657
Fixes #3443
Fixes #3824
Fixes #3817
Fixes #3805
Fixes #1666
Fixes #3700
Fixes #3821
Fixes #3771
Fixes #3794
Fixes #3825
Fixes #3690
Fixes #1472
Fixes #3813
Fixes #3719
Fixes #1592
Fixes #3845

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

2 participants