feat: add agents-md, the AGENTS.md validation tool - #3
Draft
tonyandrewmeyer wants to merge 6 commits into
Draft
Conversation
Every Charm Tech repo carries an AGENTS.md, several agents read it, and a stale line in one is worse than a missing line: agents trust the file over the repo, so a wrong line produces confident errors where absence would have produced exploration. This package is the deterministic half of the scheme for keeping them honest -- three checks and one fix, plus the ten per-repo question batteries the battery check reads. The checks were written as part of the charm-tech-baseline audit tool and would otherwise ship with it. They are separated here because the two have different consumers and very different cadences: the baseline audit runs against a repo when someone asks it to, while this runs monthly across the estate and on every PR that touches an AGENTS.md. Splitting them means the monthly routine can pin a package that is only these checks, and a change to either does not force a re-review of the other. The batteries move with the checks rather than staying with the skill, so that the code that reads them and the data it reads ship together. common.py and tier.py are the same as their charm-tech-baseline counterparts. That duplication is deliberate for now: the alternative is a third package for 150 lines of exit codes and a JSON emitter, and a forced release order between the two. Worth revisiting if a third tool wants them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XdsdR8QdcZY6GUJgd1Wt7c
Matches the root README and ai-failure-notifier's, which are both soft wrapped. GitHub wraps to the reader's window, so the hard wraps only made the paragraphs ragged there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XdsdR8QdcZY6GUJgd1Wt7c
Four conventions settled on canonical#1 that apply here too, so that the second package in the repo does not arrive with a different set. * Apache licence header on every Python file, which none of these had. * Modules are private (`_cli`, `_common`, `_tier`, `_checks`, `_fixes`), so it stays hard to depend on tool internals from a CI hack later. * `__init__.py` is a docstring and nothing else -- the tool is a console script, not something to import -- with the entry point moved to `._cli`. * `[tool.ruff] extend = "../pyproject.toml"` rather than a comment asking people not to add a `[tool.ruff]` block, since extending means a setting added here overrides one key instead of the whole shared config. The modules inside `_checks` and `_fixes` keep their public-looking names: the runner derives check and fix identifiers from them, so `add_agents_md` is the name of the fix on the command line rather than an importable API. Also drops two comments explaining what a check used to do when it was a standalone script, which is the same leftover-from-one-file class that was picked up on canonical#1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XdsdR8QdcZY6GUJgd1Wt7c
All three checks declared `product,canonical,personal`, so the tier gate never excluded anything -- it computed a value and then applied it to nothing. That is not an accident of this extraction: a well-maintained AGENTS.md is worth the same in a personal fork as in a product repo, which is not true of the requirements the tiers were built for (SBOM submission, TICS targets, security documentation). The baseline audit needs tiers; this does not have to inherit them. Removing it takes a network round trip out of every run: on a fork, tier detection shelled out to `gh repo view` to find the upstream, for a check that is meant to run in CI on every PR touching the file. Goes with it: `--tier=`, the `detect-tier` command, `parse_tier` and `tier_applies`, and the `tier`/`tier_source` fields in the report. Anything reading the JSON should not miss them, since they never varied by anything except the flag it passed in. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XdsdR8QdcZY6GUJgd1Wt7c
Follows the team Python style guide in canonical/charm-tech (style/python.md): a name prefixed with its module says where it came from. So `import pathlib` and `pathlib.Path`, `import types` and `types.ModuleType`, and `from .. import _common` with every use prefixed, rather than pulling the names in one at a time. `typing.Any` and `collections.abc.Iterator` stay as they are, under the guide's exception for typing names -- `Iterator` is only ever an annotation here, and the same verbosity argument applies to it. Worth noting for the review: `run` is a common enough word that prefixing its uses also caught it inside two regexes and a summary string, where `go run` and `make run` are side-effect keywords. Those are back as they were. The checks report the same findings against concierge and operator clones as they did before this change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XdsdR8QdcZY6GUJgd1Wt7c
tonyandrewmeyer
force-pushed
the
feat/agents-md
branch
from
September 8, 2026 08:07
b69c41c to
dba5617
Compare
Nothing in a public repo should point at a path only some of us can read. * `seeded_from:` in all ten batteries named a file in that tree and nothing else, so the field goes rather than gets reworded. `seeded_on` and the AGENTS.md ref/SHA it was seeded against stay, since those are the provenance a reader can actually check. * The "Layer 1/2/3" numbering came from that document too, and without it the numbers say nothing. Each is now named for what it does, which mostly means saying `agents-md-content` where the text said Layer 1. * Two docstrings pointed at `lib/common.py` and `check.py`, which is the layout this code had two moves ago. Ten batteries still parse, entry counts unchanged, and the checks report the same findings against concierge and operator clones. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XdsdR8QdcZY6GUJgd1Wt7c
tonyandrewmeyer
force-pushed
the
feat/agents-md
branch
from
September 8, 2026 08:08
dba5617 to
9534eeb
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every Charm Tech repo carries an AGENTS.md, several agents read it, and a stale line in one is worse than a missing line: agents trust the file over the repo, so a wrong line produces confident errors where absence would have produced exploration. This package is the deterministic half of the scheme for keeping them honest : three checks and one fix, plus the ten per-repo questions the check reads.
The intention is that this runs monthly across the repos and on every PR that touches an AGENTS.md.
(Some of the top-level files here are duplicates of #1, whichever lands first will trigger the removal in the other via a rebase.)