Skip to content

Release line: Agent Skills lockfile - #11

Merged
bnymnDev merged 18 commits into
mainfrom
develop
Oct 3, 2026
Merged

bnymnDev merged 18 commits into
mainfrom
develop

Conversation

@bnymnDev

@bnymnDev bnymnDev commented Oct 3, 2026

Copy link
Copy Markdown
Owner

What

Brings develop to main. The only content is the skills lockfile from #10: agentgate skills with lock, verify, diff, approve, label and scan, plus the new Action inputs.

How

develop was even with main before #10, so this PR carries exactly that change. It does not tag a release, because the release workflow only runs on v* tags. The CHANGELOG keeps the entry under Unreleased.

Effect on enforcement and the audit log

None. See #10.

Checklist

  • make fmt vet lint test passes
  • make e2e passes, if the proxy, CLI or audit store changed
  • make golden was run and the diff reviewed, if policy evaluation changed (not affected)
  • make docs was run, if a command, flag, matcher or redaction pattern changed
  • docs/ and the README reflect any user-visible change

Generated by Claude Code

A skill is a directory with a SKILL.md: instructions a coding agent loads
and follows, often with scripts next to them. internal/skills pins every
file of a skill in an RFC 6962-shaped Merkle tree, derives a label of what
the skill can do (shell, network, secrets, file writes outside the project,
package installs, remote content, hooks, pre-approved tools, ...), runs 40
deterministic rules for the tricks poisoned skills use - hidden Unicode,
look-alike hosts, encoded payloads, curl | sh, conditional triggers,
exfiltration, persistence - and compares all of it with a lockfile.

Every rule has a fixture and a golden file; ordinary skills must trip none.
skills.lock sits next to the project's skills. lock pins what is not pinned
yet, verify fails on any change nobody approved, diff shows a changed
SKILL.md sentence by sentence with new imperatives and the rules each added
sentence trips, approve accepts a skill as it is, label shows what a skill
can do and where it says so, and scan vets a skill before it is installed.
verify and diff write Markdown for a pull request or a job summary; label
writes a badge.
A new skills input names a project directory; the step writes the skills
report to the job summary, hands it out as the skills-report output for a
pull request comment, and fails on any change nobody approved unless
skills-fail is false. Without the input nothing changes. make golden
re-approves the fixture project CI checks the Action against.
Also catches curl -F field=@file as exfiltration: the form field name sat
between the flag and the @.
tabwriter counts the bytes of colour codes, so a coloured status pushed
every column after it out of line. The table is plain, as lock's is; the
details below it carry the colour.
… bytes

From review:
- A skill whose SKILL.md is a symlink was not a skill at all; it is now
  found, pinned and scanned. A symlink to a file is pinned by its target
  and its content, and the content is scanned.
- Only a real repository (HEAD, objects, refs) at the root of a skill is
  left out, and its presence is pinned with a git-repository finding. A
  .git directory anywhere else, .DS_Store and Thumbs.db are hashed.
- A text file with one NUL or invalid UTF-8 byte was treated as binary and
  skipped every rule. It is now scanned with the bad bytes replaced, and
  flagged by malformed-text.
- A repository that is itself one skill (--path .) gets the key '.', and
  the lockfile inside it is not hashed as part of it.
- An HTML comment that starts a line runs to its -->, so a fenced block
  inside one is read as the hidden comment it is.
- Variation selectors, the grapheme joiner and soft hyphens are folded out
  before matching, curly quotes are straightened and runs of spaces are one;
  a selector after an ASCII character is itself a finding.
- Keyword windows start a line before the keyword, for wrapped prose.
- curl | /bin/bash, | /usr/bin/env bash and | tee f | sh are pipe-to-shell.
- An added sentence is read as prose and its inline code as code, and every
  sentence of the diff carries the line it starts on.
- Line numbers are counted once per pass, and one rule is reported at most
  50 times per stretch of a file.
- A skill is matched by its directory, never by the name it gives itself.
verify needs a lockfile (or --missing-ok), --dir has to be a directory and
an explicit --path has to name something. A name that matches more than one
skill is refused. Everything printed from a skill stays on its line, lock
--json prints [] for nothing pinned, and the lockfile is never hashed as
part of a skill it sits in.
A skills check that cannot run - an agentgate without skills, a missing or
broken lockfile - fails the step instead of passing as drift or as nothing
at all, and skill text in the log cannot issue workflow commands.

gendocs put generated text into a regexp replacement template, where a $
is a submatch reference: ${IFS} vanished from the rule table.
The status section listed what comes next for the proxy only. It now also
names the next steps for skills - signed approvals, a SessionStart hook
and SARIF output - and links the full roadmap in the design notes.
…ty label, prose diff and approval gate

agentgate skills pins Agent Skills in skills.lock, labels what each skill
can do, shows changed instructions sentence by sentence and fails CI on any
change nobody approved. 42 deterministic rules, no network, no model.
@bnymnDev
bnymnDev merged commit 611681a into main Oct 3, 2026
16 checks passed
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.

1 participant