-
Notifications
You must be signed in to change notification settings - Fork 0
feat(ci,cli): gate the SKILL.md description cap in CI and at pack time #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,12 +22,25 @@ use crate::commands::sync; | |
| use crate::context::Context; | ||
| use crate::install::plan::NON_SKILL_DIRS; | ||
| use crate::output::error::{AppError, ErrorCode}; | ||
| use crate::sources::skillmd; | ||
| use crate::output::{CommandOutput, HumanRender}; | ||
|
|
||
| /// Default catalogue clone to ship from. | ||
| const DEFAULT_REPO: &str = "/spacecraft-software/construct"; | ||
| /// The remote a ship is allowed to push to (substring check). | ||
| /// The remote a ship is allowed to push to (substring check). Standard §6.4: | ||
| /// publication targets are limited to namespaces Spacecraft Software controls. | ||
| const EXPECTED_REMOTE: &str = "Spacecraft-Software/Construct"; | ||
| /// Maximum rendered length of a skill's frontmatter `description` (Standard | ||
| /// §5.6). | ||
| /// | ||
| /// The consuming skill loader rejects anything over **1024** characters at | ||
| /// install time — after the bundles are built and pushed — so the cap sits at | ||
| /// 1000 for a 24-character margin covering encoding and trailing-newline edge | ||
| /// cases. Raising it past the loader's limit would ship bundles that cannot be | ||
| /// installed. `.githooks/check-description-length.py` enforces the same number | ||
| /// in CI and in the pre-commit hook; changing one without the other lets a | ||
| /// bundle pass one gate and fail the next. | ||
| const DESCRIPTION_CAP: usize = 1000; | ||
| /// Assistant co-authorship trailer (CONTRIBUTING §4). | ||
| const COAUTHOR: &str = "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"; | ||
|
|
||
|
|
@@ -94,6 +107,42 @@ pub(crate) fn run(ctx: &Context, args: &ShipArgs) -> Result<CommandOutput, AppEr | |
| .with_extension("drifted_skills", json!(drifted))); | ||
| } | ||
|
|
||
| // Enforce the Standard §5.6 description cap before anything is staged: the | ||
| // loader rejects an over-long description at install time, by which point | ||
| // the bundles are built, committed, and pushed. Cheaper to refuse here. | ||
| let oversized: Vec<(String, usize)> = shipped | ||
| .iter() | ||
| .filter_map(|skill| { | ||
| let len = skillmd::description_len(&repo.join(skill).join("SKILL.md"))?; | ||
| (len > DESCRIPTION_CAP).then(|| (skill.clone(), len)) | ||
|
Comment on lines
+113
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the worktree is clean but AGENTS.md reference: AGENTS.md:L17-L21 Useful? React with 👍 / 👎. |
||
| }) | ||
| .collect(); | ||
| if let Some((first, _)) = oversized.first() { | ||
| let detail = oversized | ||
| .iter() | ||
| .map(|(skill, len)| format!("{skill} ({len} chars, {} over)", len - DESCRIPTION_CAP)) | ||
| .collect::<Vec<_>>() | ||
| .join(", "); | ||
| return Err(AppError::new( | ||
| ctx, | ||
| ErrorCode::Conflict, | ||
| 5, | ||
| format!("SKILL.md description exceeds the {DESCRIPTION_CAP}-character cap: {detail}"), | ||
| format!("$EDITOR {first}/SKILL.md # trim the `description` frontmatter field"), | ||
| ) | ||
| .with_extension( | ||
| "oversized_skills", | ||
| json!(oversized | ||
| .iter() | ||
| .map(|(skill, len)| json!({ | ||
| "skill": skill, | ||
| "chars": len, | ||
| "over_by": len - DESCRIPTION_CAP, | ||
| })) | ||
| .collect::<Vec<_>>()), | ||
| )); | ||
| } | ||
|
|
||
| // Build the explicit stage list: shipped skills' files + their bundles + | ||
| // catalogue-level root files (README.md, flake.lock). Never `git add -A`. | ||
| let mut stage: Vec<String> = Vec::new(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For folded descriptions containing trailing spaces, this new CI gate can pass an over-cap value because
.githooks/check-description-length.pyappliesstrip()to every block line (line 51), although YAML preserves those spaces. For example, 999 visible characters followed by 10 spaces renders to 1010 characters including the newline, while the invoked checker reports exactly 1000 and succeeds; use the already-installed YAML parser here/checker so everySKILL.mdis measured from the decoded scalar.AGENTS.md reference: AGENTS.md:L11-L18
Useful? React with 👍 / 👎.