# How book-to-skill Ensures the Quality of Generated Skills: A 3-Layer Validation Pipeline

> Learn how book-to-skill guarantees skill quality with its 3-layer validation pipeline. Discover static validation, content safety scanning, and automated CI enforcement.

- Repository: [Virgilio Junior/book-to-skill](https://github.com/virgiliojr94/book-to-skill)
- Tags: internals
- Published: 2026-08-27

---

**book-to-skill ensures quality through a three-layer pipeline: static validation of the skill manifest, content-level safety scanning of generated markdown, and automated CI enforcement that blocks deployment of invalid or unsafe skills.**

Converting books into AI agent skills requires rigorous quality control. The `book-to-skill` repository by `virgiliojr94` implements a deterministic extraction pipeline that ships with built-in safeguards at every stage. According to the source code, quality assurance is not an afterthought—it is woven into the architecture through dedicated validation tools, safety scanners, and continuous integration gates.

## Static Validation of the Skill Manifest

The first quality gate operates on the [`SKILL.md`](https://github.com/virgiliojr94/book-to-skill/blob/main/SKILL.md) file that defines every generated skill.

[`tools/validate_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tools/validate_skill.py) parses the YAML front-matter and enforces structural correctness. It verifies that required fields—`name` and `description`—are present, checks length limits to prevent oversized manifests, and validates the `allowed-tools` list against **host-specific lenses** (Claude, Copilot, or Amp). The validator also warns about unknown front-matter keys and rejects manifests with overly long bodies that could degrade host performance.

The validation logic uses two core functions:

- `parse_frontmatter()` — extracts and parses YAML metadata from [`SKILL.md`](https://github.com/virgiliojr94/book-to-skill/blob/main/SKILL.md)
- `LENSES` dictionary — maps host names to their permitted tool sets

If validation fails, the tool exits with status `1`, preventing broken skills from advancing.

```bash

# Validate a skill manifest for Claude

python -m tools.validate_skill --lens claude path/to/SKILL.md

```

## Content-Level Safety Scanning of Generated Markdown

The second layer inspects the actual content of generated files for security risks.

[`tools/scan_generated_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tools/scan_generated_skill.py) walks the entire skill directory—including [`SKILL.md`](https://github.com/virgiliojr94/book-to-skill/blob/main/SKILL.md), supporting markdown files, and chapter files—and applies multiple detection rules:

- **Invisible Unicode code points** — flagged via `is_invisible_codepoint()` (shared logic in [`book_to_skill/sanitize.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/sanitize.py))
- **Prompt-injection phrases** — patterns like "ignore previous instructions"
- **Tool-call control tokens** — unauthorized model directives
- **Exfiltration language combined with sensitive-data terms**
- **Front-matter modifications** — attempts to widen tool authority or enable model invocation

The scanner uses `_CONTENT_RULES`, a set of regular expressions, to match dangerous patterns. It reads every file with UTF-8-sig handling and enforces size limits to prevent resource exhaustion. Symbolic links are explicitly rejected to block directory-traversal attacks.

Exit codes distinguish recoverable warnings from fatal errors:

| Code | Meaning |
|------|---------|
| `0` | Clean scan |
| `1` | Structural validation errors (reserved) |
| `2` | Unrecoverable scan failure (symlink misuse, size violations) |

```bash

# Safety-scan a generated skill directory

python -m tools.scan_generated_skill path/to/generated-skill/

```

## Automated Testing and CI Enforcement

The third layer ensures that validation and scanning run automatically on every change.

The repository ships with a comprehensive pytest suite under `tests/` that exercises the extractor, sanitizers, parsers, and both quality-assurance tools. The CI workflow at [`.github/workflows/ci.yml`](https://github.com/virgiliojr94/book-to-skill/blob/main/.github/workflows/ci.yml) runs `pytest -q` and `ruff check .` on every push, failing the build if any validation error or scan failure occurs.

The CI gate implements a two-tiered response:

- **ERROR** from [`validate_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/validate_skill.py) — aborts the pipeline immediately
- **WARN** from [`scan_generated_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/scan_generated_skill.py) — surfaced for human review without blocking

This design catches structural violations automatically while flagging content-level concerns for security review.

## How the Quality Layers Interact

The complete pipeline follows four deterministic stages:

1. **Extraction** — [`scripts/extract.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/scripts/extract.py) (powered by `book_to_skill/` modules) generates [`SKILL.md`](https://github.com/virgiliojr94/book-to-skill/blob/main/SKILL.md) and supporting files
2. **Validation** — [`validate_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/validate_skill.py) checks manifest compliance against host rules
3. **Safety scan** — [`scan_generated_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/scan_generated_skill.py) inspects all content for security threats
4. **CI gate** — GitHub Actions enforces both tools, blocking deployment of invalid skills

The [`docs/architecture.md`](https://github.com/virgiliojr94/book-to-skill/blob/main/docs/architecture.md) file visualizes this workflow, showing how extractor output feeds sequentially into validator and scanner before final packaging.

## Summary

- book-to-skill validates skill quality through **manifest validation**, **content scanning**, and **CI enforcement**
- [`tools/validate_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tools/validate_skill.py) enforces host-specific rules for Claude, Copilot, and Amp
- [`tools/scan_generated_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tools/scan_generated_skill.py) detects invisible characters, prompt injections, and exfiltration patterns
- Both tools return non-zero exit codes that fail CI builds when quality standards are violated
- The shared [`sanitize.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/sanitize.py) module provides Unicode validation used across the pipeline
- Automated testing in `tests/` and [`.github/workflows/ci.yml`](https://github.com/virgiliojr94/book-to-skill/blob/main/.github/workflows/ci.yml) guarantees quality gates run on every commit

## Frequently Asked Questions

### What happens if a generated skill fails validation?

The CI pipeline aborts. [`validate_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/validate_skill.py) exits with status `1` when required fields are missing, tool lists are invalid, or length limits are exceeded. This prevents the broken skill from being published or deployed.

### Does the safety scanner block builds or just warn?

It depends on severity. The scanner returns exit code `2` only for unrecoverable failures like symlink attacks or oversized files. Regular security findings print as `WARN` messages for human review without stopping CI—though teams can configure stricter policies.

### Which hosts does book-to-skill support for validation?

The validator includes built-in lenses for **Claude**, **Copilot**, and **Amp**. Each lens defines the permitted `allowed-tools` set via the `LENSES` dictionary in [`tools/validate_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tools/validate_skill.py). You specify the target host with `--lens <name>`.

### How does the scanner detect invisible characters?

[`scan_generated_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/scan_generated_skill.py) imports `is_invisible_codepoint()` from [`book_to_skill/sanitize.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/sanitize.py) to identify non-printable Unicode code points that could hide malicious instructions or corrupt skill rendering.