Skip to content

feat(scripts): lint-skill-frontmatter — enforce required keys on SKILL.md frontmatter (soc-e8pj #skill-md-frontmatter-linter)#376

Open
boshu2 wants to merge 1 commit into
mainfrom
feat/skill-md-frontmatter-linter-soc-e8pj
Open

feat(scripts): lint-skill-frontmatter — enforce required keys on SKILL.md frontmatter (soc-e8pj #skill-md-frontmatter-linter)#376
boshu2 wants to merge 1 commit into
mainfrom
feat/skill-md-frontmatter-linter-soc-e8pj

Conversation

@boshu2
Copy link
Copy Markdown
Owner

@boshu2 boshu2 commented May 20, 2026

Why

The hexagonal-architecture model generates docs/contracts/context-map.md from every skills/*/SKILL.md frontmatter (hexagonal_role, consumes, produces, context_rel). When a SKILL.md misses one of those keys, the generated map drifts silently and the next consumer (e.g., soc-vuu6.4's skills/catalog.json work) hits surprises. Today 1-of-79 skills (expert-council) was missing the three list keys.

What

scripts/lint-skill-frontmatter.sh — walks every skills/*/SKILL.md, parses the YAML frontmatter, and validates:

  • name, description present + non-empty
  • hexagonal_role ∈ {domain, driving-adapter, driven-adapter, supporting, generic}
  • consumes, produces, context_rel keys present (value may be empty list)

Modes: --check (default; exits 1 on any miss), --list (per-skill report), --json (machine), --skill <name> (scope to one).

Also backfills the 3 missing keys on skills/expert-council/SKILL.md so the linter can ship strict from day one (mirrors the council parent skill it aliases).

Test

15 bats tests, all passing. Cover: clean skill / each missing-key case / invalid enum value / empty value / all 5 valid enum values / no-frontmatter / --skill scoping / unknown-skill error / --list format / --json shape / no-skills-dir graceful / unknown-flag error.

Self-dogfooded: linter run on the full skills/ tree → 79 clean (after the expert-council backfill).

Closes-scenario: soc-e8pj#skill-md-frontmatter-linter
Bounded-context: BC1-Corpus
Evidence: scripts/lint-skill-frontmatter.sh
Evidence: tests/scripts/lint-skill-frontmatter.bats

@boshu2 boshu2 enabled auto-merge (squash) May 20, 2026 22:02
@boshu2 boshu2 force-pushed the feat/skill-md-frontmatter-linter-soc-e8pj branch from 1c381f3 to f9d20fe Compare May 20, 2026 22:11
@boshu2 boshu2 force-pushed the feat/skill-md-frontmatter-linter-soc-e8pj branch 3 times, most recently from 38ab179 to c32983d Compare May 20, 2026 23:18
boshu2 added a commit that referenced this pull request May 20, 2026
… 1 #catalog-generator) (#379)

## Why

`registry.json` (47KB) is too coarse — agents grepping for "which skill
produces a ratchet?" have to scan markdown. Hand-curated /skills tables
drift. Future `ao skill new` scaffolders need a queryable inventory.
This is slice 1 of soc-vuu6.4 (the generator + schema + CI gate). Go CLI
subcommands and showcase consumer are filed as follow-up beads.

## What

Three artifacts:

1. `schemas/skill-catalog.schema.json` — JSON Schema for the catalog.
Required fields: name, description, hexagonal_role, consumes, produces,
context_rel, user_invocable, references_count, codex_override_present.
hexagonal_role enum mirrors soc-e8pj/PR #376.

2. `scripts/generate-skill-catalog.sh` — walks every
`skills/*/SKILL.md`, parses YAML frontmatter via awk, emits
`skills/catalog.json` with all 79 skills. Modes: default (write),
`--check` (CI drift gate, exits 1 on drift), `--stdout`, `--out PATH`.

3. `scripts/check-skill-catalog-drift.sh` — thin wrapper over `--check`
so the future workflow has a stable, named entry point.

4. `skills/catalog.json` — generated artifact, committed. 79 skills,
40KB.

The parser handles the SKILL.md frontmatter subset we actually use:
scalar key:value, lists (`- item`), and `context_rel` nested objects (`-
kind:` / `with:`). A regression test covers the "last context_rel entry
dropped on transition to next scalar" parser bug caught during
development.

## Test

14 bats tests, all passing. Cover: envelope fields, name/description
extraction, list round-trip, context_rel parsing (incl. the regression
for the dropped-last-entry bug), user_invocable boolean,
references_count, codex_override_present, default file-write, `--check`
pass + drift detection, drift wrapper exit codes, empty-skills case,
unknown-flag error.

Self-dogfood: real skills/ tree → 79 skills generated, all 4 keys
validated by lint-skill-frontmatter (#376), drift check green.

## Follow-up beads filed

- Slice 2 — `ao skills list/consumers/producers/graph` (Go CLI
subcommands, blocks on this PR)
- Slice 3 — Showcase consumes catalog.json instead of hand tables

Closes-scenario: soc-vuu6.4#catalog-generator
Bounded-context: BC1-Corpus
Evidence: scripts/generate-skill-catalog.sh
Evidence: scripts/check-skill-catalog-drift.sh
Evidence: tests/scripts/generate-skill-catalog.bats

Co-authored-by: Codex <codex@example.invalid>
@boshu2 boshu2 force-pushed the feat/skill-md-frontmatter-linter-soc-e8pj branch from c32983d to 0a934d3 Compare May 20, 2026 23:30
…L.md frontmatter (soc-e8pj #skill-md-frontmatter-linter)

Walks every skills/*/SKILL.md, validates name/description/hexagonal_role/consumes/produces/context_rel. hexagonal_role enum: {domain, driving-adapter, driven-adapter, supporting, generic}. Modes: --check --list --json --skill.

Backfills the 3 missing keys on expert-council/SKILL.md so the linter ships strict from day one. Self-dogfood: 79 clean.

Closes-scenario: soc-e8pj#skill-md-frontmatter-linter
Bounded-context: BC1-Corpus
Evidence: scripts/lint-skill-frontmatter.sh
Evidence: tests/scripts/lint-skill-frontmatter.bats
@boshu2 boshu2 force-pushed the feat/skill-md-frontmatter-linter-soc-e8pj branch from 0a934d3 to 8b4ba5b Compare May 20, 2026 23:48
boshu2 added a commit that referenced this pull request May 21, 2026
…e-2-of-3) (#393)

## Why

soc-dspz asks for 3 advisory I0 jobs to wire in. Two of the three source
scripts are already on `main` (after PRs #373, #379 merged); the third
(`scripts/lint-skill-frontmatter.sh` from #376) is still pending merge.
Per the new agile invariant (soc-5qit, PR #392), the right move is to
ship the unblocked part and leave the parent bead open for the third
when #376 lands — instead of waiting for everything.

## What changed

| Surface | Change |
|---|---|
| `.github/workflows/validate.yml` | Added 2 stanzas:
`lint-evidence-lines-advisory` (PR-event triggered) and
`check-skill-catalog-drift-advisory` (skills/CI path-filtered). Both
`continue-on-error: true` so they emit `::warning::` annotations without
blocking merges. |
| `.github/workflows/validate.yml` summary job | `needs:` extended with
both new advisory jobs. |
| `docs/contracts/ci-jobs.yaml` | 2 manifest entries, alphabetically
slotted. |
| `AGENTS-CI.md` | Regenerated via `scripts/generate-ci-jobs-table.sh
--write` (69 rows). |

## Promotion path

Per soc-dspz acceptance: I0 advisory → T2 required after one clean week
of runs. File a follow-up bead to flip `continue-on-error: true` off and
promote both jobs to required.

## What's NOT in this PR

- `lint-skill-frontmatter-advisory` — depends on PR #376 (open) adding
`scripts/lint-skill-frontmatter.sh`. soc-dspz stays open after this
merges; a follow-up small PR will add that 3rd stanza once #376 lands.

## Validation

- `python3 -c 'import yaml; yaml.safe_load(...)'` parses both YAML files
clean
- `scripts/validate-ci-policy-parity.sh`: `CI_JOBS_TABLE: PASS (69
rows)`

Closes-scenario: soc-dspz#wire-2-of-3
Bounded-context: BC4-CI
Evidence: scripts/validate-ci-policy-parity.sh
@boshu2 boshu2 added the parked Parked PR — actively deferred, will return after upstream work lands label May 21, 2026
@boshu2
Copy link
Copy Markdown
Owner Author

boshu2 commented May 21, 2026

Parking pending soc-g2qd (/evolve --mode=loop epic). Both this PR's surface and the epic touch overlapping infrastructure; resolving the upstream contract first avoids rebase churn. Will rebase + resume after epic ships.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

parked Parked PR — actively deferred, will return after upstream work lands skills tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant