nousresearch/hermes-agent
An instruction file edited the same day as HEAD, 2,910 test files green from a bare clone, and 27 workflows behind one required check; the lint command that blocks the merge is named nowhere an agent reads, and one laptop command ships a release, the docs site and :latest at once. Below is the run the agent did and the AINE-REPORT.md it wrote.
- 33 passed
- 13 failed
- 0 n/a
- Python 3.11 + TypeScript · uv + npm workspaces · pytest + vitest · ruff + ty + eslint · Nix + Docker · GitHub Actions
Run 14 August 2026 · main at 17d6a7d
The agent run
I cloned nousresearch/hermes-agent into a temp folder, audited it against all 46 checks and wrote the full report to AINE-REPORT.md in the repo.
AI-Native Readiness — nousresearch/hermes-agent
33 passed · 13 failed · 0 not applicable
High priority — fix these first
- Add the Python check commands to the
AGENTS.md"Testing" section —ruff check .,python scripts/check-windows-footguns.py --all,ty check, andnpm run checkfor the JS workspaces. Why:ruff check .is a blocking required job, and the string "ruff" appears zero times inAGENTS.md,CONTRIBUTING.mdor any file they route to — an agent can only learn the command that gates its merge by reading the CI YAML. - Then wrap those four commands in
scripts/check.shand name it as the pre-push command. Why: there is no single verify target today — no Makefile, no justfile, and rootnpm run checknever touches Python. - Fix the one dead pointer,
references/new-skill-pr-salvage.md(AGENTS.md:1031) — vendor it intodocs/, or say it is maintainer-only and lives outside the repo. Why:AGENTS.mdtells an agent to "load it before polishing contributor skill PRs" and the file is not in the tree. - Add the missing directories to the
AGENTS.mdtree —nix/,evals/,optional-mcps/,locales/,native/,docker/,contributors/. Why: "nix" appears zero times inAGENTS.md, yetflake.nix+nix/is a supported packaging path andscripts/run_tests.shtells you to enter the Nix devShell when it finds no venv. - Add a gitleaks step to the existing scan job in
.github/workflows/supply-chain-audit.yml. Why: nothing scans for secrets. That job already computes thebase...headdiff for malware patterns, so this is one more step in a workflow that runs on every PR. - Add a "Never without a human" section to
AGENTS.md—scripts/release.py --publish,gh workflow run deploy-site.yml, pushes tomain, force-pushes, merging PRs, and anything that spends a provider key. Why: no such list exists, whilescripts/release.py's own docstring advertises--publishas a copy-paste command. - Move the release cut behind a protected environment with required reviewers, and make
scripts/release.py --publishrefuse to run outside CI. Why: bothdocker.ymlanddeploy-site.ymlfire onrelease: published, so that one command ships a public release, the docs site and the:latestcontainer tags with no second human in the way. - Split
AGENTS.md. Keep a ~600-line entry point; move the TUI/desktop, skins, plugins, skills and toolsets deep dives intodocs/behind one-line pointers. Why: 81,342 bytes ≈ 20k tokens loaded into every session, including ~250 lines of desktop slash-command internals a Python-only change never needs.
Low priority — worth doing
- Add
[tool.ruff.format], runruff format .once, then addruff format --check .to the blocking ruff job — the Python half of the repo has no formatter at all. - Make the existing base-vs-HEAD
tycomparison fail when HEAD adds new diagnostics —ty checkreports 14,754 on a clean checkout today, which is why it cannot block as-is. - Add
docs/rfcs/TEMPLATE.mdwith the headings the RFCs already converge on — Status, Scope, Non-goals, Design, Verification contract, Related. - Add a review skill under
.claude/skills/encoding the rubricAGENTS.md:29-211already spells out — nothing here gets an LLM to read a diff.
Two things worth saying plainly. This is a monorepo, so per-package answers will differ from the root audit. And the failures are concentrated in discoverability and release fencing, not in engineering quality: the test suite ran green (276 tests in 6.4s), ruff check . passes clean, uv sync --locked reproduced the environment from a bare clone on the first try, and the rollback, pinning and CI stories are stronger than most repos this size.
The report it wrote
AI-Native Readiness Report
Repo: nousresearch/hermes-agent, a fresh clone of main at commit 17d6a7d (2026-08-14)
Date: 2026-08-14
Stack: Python 3.11 (primary — pyproject.toml, uv + setuptools, ~2,910 pytest files) plus TypeScript across four npm workspaces (apps/desktop Electron, ui-tui Ink, web Vite dashboard, website Docusaurus). Checks: pytest via scripts/run_tests.sh, vitest, ruff, ty, eslint, prettier. CI: GitHub Actions (27 workflows behind one ci.yml orchestrator). Packaging: Nix flake, Dockerfile, scripts/install.sh.
Result: 33 passed, 13 failed, 0 not applicable
How to read this. Every item is a numbered question about this repo, answered from evidence in this repo. Proof is what was actually found — a path, a file, a command and its output. An item with no proof is not an answer. The italic line under each question says how to check it; it stays in the report so the next run checks the same way.
FAIL vs N/A. FAIL means the practice would help here and it is missing — including when a whole artifact is missing, so several questions about it fail together with a one-line proof pointing at the item that found it missing. N/A means the question could never apply to a project of this kind, and the proof says why. "It does not exist" is a FAIL, never an N/A.
Priority is fixed by the template, not by the person filling it in. High means an agent working here will hit this on day one, or the risk is real. Low means it makes things better but nothing breaks without it.
1. Context
-
1. Does an agent instruction file exist (AGENTS.md, CLAUDE.md, .cursorrules, .github/copilot-instructions.md, or this ecosystem's equivalent), and what does it point at? Look for a file whose purpose is to be loaded into a coding agent's context at the start of every session. That file is the entry point. Then follow its pointers: instructions are commonly spread across several files, with the entry point routing to them — "commands are in docs/development.md", "conventions in .claude/rules/". The entry point plus every file it points at is the instruction set, and every other agent-instruction question is answered against that whole set, so list it here in the proof. Follow pointers as deep as they go, but a pointer only counts when it names a target and says what is in it: "test commands are in CONTRIBUTING.md" counts, a bare "see the docs" does not, and a file nothing points at is outside the set however good it is. A README is not an entry point, however good — it gets its credit at the README item and the specs-and-architecture item — but a README the entry point explicitly routes to is inside the set. Vendored skill libraries are not instructions either.
- Status: PASS
- Proof: Entry point is
AGENTS.mdat the root (1,543 lines, 81,342 bytes), opening "Instructions for AI coding assistants and developers working on the hermes-agent codebase." NoCLAUDE.md,.cursorrulesor.github/copilot-instructions.mdexists (git ls-files | grep -iE 'AGENTS.md|CLAUDE.md|cursorrules|copilot-instructions'returns onlyAGENTS.mdandapps/desktop/AGENTS.md). Instruction set =AGENTS.md+ the files it names with content:apps/desktop/AGENTS.md(routed at AGENTS.md:533, "readapps/desktop/AGENTS.md"),gateway/platforms/ADDING_A_PLATFORM.md(AGENTS.md:287),website/docs/developer-guide/plugins/index.md(AGENTS.md:803),website/docs/developer-guide/model-provider-plugin.md,website/docs/user-guide/features/curator.md,website/docs/user-guide/features/kanban.md. All six exist.CONTRIBUTING.mdandREADME.mdare strong but nothing in the set routes to them, so they are outside it. - Recommendation: —
- Priority: High
-
2. Do the agent instructions name the commands to build, test and check this project? Answer this against the instruction set mapped at the entry-point item and nothing outside it. If no entry point exists, FAIL with a one-line proof pointing at that item. The commands may sit in a file the entry point routes to rather than in the entry point itself — that is progressive disclosure working as intended, and it passes. What fails is a command an agent would have to guess its way to: if the commands live only in the README or the manifest and nothing in the set points at them, that is a FAIL, because the question measures what an agent can reach without being told where to look. Name the file each command was found in.
- Status: FAIL
- Proof: Test and build commands are in the set: AGENTS.md:1352-1367 gives
scripts/run_tests.shwith four invocations and forbids barepytest; AGENTS.md:504-517 gives the TUI'snpm run dev/build/typecheck/lint/fmt/test; AGENTS.md:547 givesnpx vitest run src/lib/desktop-slash-commands.test.ts. The Python check commands are absent:grep -ci ruffreturns 0 forAGENTS.md,CONTRIBUTING.md,apps/desktop/AGENTS.md,gateway/platforms/ADDING_A_PLATFORM.mdandwebsite/docs/developer-guide/plugins/index.md, andtyappears nowhere either.ruff check .is a blocking required job (.github/workflows/lint.yml:128-152, "No --exit-zero, no || true"), so an agent can only learn the command that gates its merge by reading the CI YAML. - Recommendation: Add a three-line block to the AGENTS.md "Testing" section:
ruff check .(blocking, rules inpyproject.toml),ty check(advisory),python scripts/check-windows-footguns.py --all(blocking), andnpm run checkat the root for the JS workspaces. - Priority: High
-
3. Does everything the agent instructions name still exist — the commands, the paths, the libraries, and the files they point at? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. Verify, do not trust: check every named command against the manifest or build file, spot-check the paths, run the cheap read-only ones. Then resolve every pointer in every file of the set — a link to a moved or deleted file is the most common rot in a multi-file instruction set, and it fails silently: the agent reads the entry point, follows nothing, and carries on without the rules.
- Status: FAIL
- Proof: Commands verified live:
scripts/run_tests.sh tests/hermes_cli/test_profiles.py tests/test_hermes_state.py -qran 276 tests, 0 failed, in 6.4s. Paths spot-checked and present:tests/conftest.py:538 _isolate_hermes_home,toolsets.py:31 _HERMES_CORE_TOOLS,hermes_cli/curses_ui.py,tools/registry.py,tools/delegate_tool.py,scripts/ci/classify_changes.py. Of 7 file pointers, 6 resolve;references/new-skill-pr-salvage.md(AGENTS.md:1029-1032, "load it before polishing contributor skill PRs") does not — it lives in ahermes-agent-devskill that is not in this tree, and noreferences/directory exists. One stale figure: AGENTS.md:308 says "~17k tests across ~900 files as of May 2026";git ls-files tests | grep -c 'test_.*\.py$'is 2,910 today. - Recommendation: Either vendor
references/new-skill-pr-salvage.mdinto the repo (e.g.docs/new-skill-pr-salvage.md) and repoint AGENTS.md:1031 at it, or say plainly in that paragraph that the checklist is maintainer-only and lives outside the repo; while there, refresh the test count at AGENTS.md:308. - Priority: High
-
4. Are the agent instructions specific to this repo, rather than advice that would read the same in any codebase? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. The test: could this be pasted into another project unchanged? "Write clean code" and "add tests for new features" would fit anywhere and count for nothing. Judge the whole set, but weigh the files differently: an entry point that is mostly a routing table is fine, even good, when what it routes to is specific — while generic filler in the entry point costs more than generic filler three hops down, because it is loaded into every session whether it is needed or not. Say which files carried the specifics.
- Status: PASS
- Proof: Nothing here would survive a copy-paste into another project. AGENTS.md:19-27 names the two lenses ("Per-conversation prompt caching is sacred", "the core is a narrow waist"); AGENTS.md:182-211 is a six-rung Footprint Ladder naming real subcommands (
hermes webhook,hermes cron,hermes tools); AGENTS.md:1291-1348 is nine named traps with file paths and PR numbers; AGENTS.md:598-620 is a pinning table with the incidents that produced it.apps/desktop/AGENTS.mdis the same in miniature ("Decide state by authority", "Switching context is a re-home, not a reboot"). The entry point is not a routing table and carries almost no filler — the one generic line is AGENTS.md:5, "Never give up on the right solution." - Recommendation: —
- Priority: High
-
5. Do the agent instructions explain where things live and how this project is laid out? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. The map may live in a routed-to file. Judge coverage against the real tree, not against what the files mention: if the set maps one package well but is silent about sibling packages or directories an agent would land in, that is a FAIL with the omission named. In a multi-file set, check the routing too — a layout document nothing points at is a document the agent never opens.
- Status: FAIL
- Proof: AGENTS.md:263-314 maps the tree well for the code an agent usually edits: 8 root modules with one-line jobs, then
agent/,hermes_cli/,tools/,gateway/,plugins/,skills/,optional-skills/,ui-tui/,tui_gateway/,acp_adapter/,cron/,scripts/,website/,tests/, plus config and log locations. Whole tracked directories are absent from the set:grep -con AGENTS.md returns 0 fornix/,evals/,locales/,native/,contributors/,docker/andoptional-mcps/, and the string "nix" appears 0 times in the whole file even thoughflake.nix+nix/(15 files, includingnix/checks.nix) is a supported install and packaging path andscripts/run_tests.shtells you to "enter the Nix devShell" when no venv is found.apps/andweb/are covered in prose (AGENTS.md:531-533). - Recommendation: Add rows to the AGENTS.md:269-309 tree for
nix/(flake packaging +nix/checks.nix),evals/,optional-mcps/,locales/,native/,docker/andcontributors/, one line each saying what an agent would go there for. - Priority: High
-
6. Do the agent instructions state the rules that are not obvious from the code — the things never to do here? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. These are the tribal-knowledge traps: the flag that must be exactly this string, the import that breaks the build, the directory that is generated and must not be edited. Rules in a routed-to file count. Two extra checks in a multi-file set: that the entry point signposts the rules clearly enough for an agent to open them before it needs them, since a trap found afterwards has already been sprung; and that the files do not contradict each other, because nothing tells the agent which one wins.
- Status: PASS
- Proof: AGENTS.md:1291-1348 "Known Pitfalls" is exactly this: don't hardcode
~/.hermes(useget_hermes_home(), "the source of 5 bugs fixed in PR #3575"), menu pickers must usehermes_cli/curses_ui.py, no\033[Kunderprompt_toolkit'spatch_stdout,_last_resolved_tool_namesis a process-global, no cross-tool names in schema descriptions, the gateway's two message guards, stale squash merges silently reverting fixes, tests must not write to~/.hermes/. AGENTS.md:1204-1233 "Important Policies" adds prompt-cache invariants. Both are top-level##headings in the entry point itself, so they are visible before they are needed, and I found no contradiction between AGENTS.md andapps/desktop/AGENTS.md. - Recommendation: —
- Priority: Low
-
7. Has the agent instruction file been updated recently enough to still be true, given how active the repo is? If no agent instruction file exists, FAIL with a one-line proof pointing at the entry-point item. Compare the last commit touching the file against the repo's tempo, then spot-check two or three of its claims against the code — a recently touched file can still lie.
- Status: PASS
- Proof:
git log -1 -- AGENTS.mdis 2026-08-14 00:26, eighteen minutes before HEAD (17d6a7d, 2026-08-14 00:42) — on a repo landing ~15 commits a day, the file is being edited with the code. Three claims spot-checked against the tree and all true:_isolate_hermes_homeattests/conftest.py:538,_HERMES_CORE_TOOLSattoolsets.py:31, andscripts/run_tests.shprobing.venvthenvenv(it printed both paths when it found neither). The one drifted number is the self-dated test count at AGENTS.md:308, covered at the pointers item. - Recommendation: —
- Priority: Low
-
8. Is the agent instruction file small enough to load into every session without crowding out the actual task? If no agent instruction file exists, FAIL with a one-line proof pointing at the entry-point item. Measure it:
wc -c, and bytes divided by four is a fair token estimate. Under ~5k tokens is comfortable; past ~10k it is eating the context window.- Status: FAIL
- Proof:
wc -c AGENTS.md= 81,342 bytes, roughly 20k tokens at four bytes per token — double the ~10k point where the file starts eating the context window, and four times the comfortable ~5k.apps/desktop/AGENTS.mdadds 11,227 bytes (~2.8k tokens) on top for desktop work. The content is good, but all of it loads on every session, including the ~250 lines of TUI/desktop slash-command internals (AGENTS.md:467-547) that a Python-only change never needs. - Recommendation: Split AGENTS.md into a ~600-line entry point (What Hermes Is, rubric, dev environment, project structure, testing, known pitfalls) and move the per-subsystem deep dives — TUI/desktop (467-547), skins (685-773), plugins (774-924), skills (925-1035), toolsets/delegation/curator/cron/kanban (1036-1203) — into
docs/files, leaving a one-line pointer for each that says what is inside. - Priority: Low
-
9. Can a fresh session pick up a half-finished task — is there a file or convention where progress, decisions and what is left get written down? This is not about the agent instruction file. Any durable place in-flight state gets written counts: a TODO.md, a plans or notes directory, task files, linked issues, a specs folder whose entries carry progress and open questions, an agent memory file or directory, a scratchpad or working-notes convention. The test is whether a fresh session could read it and know what was decided and what is left — not what the place is called. Git history alone does not count: it records what happened, not what was decided or what remains.
- Status: PASS
- Proof: Decisions and open questions are written into the tree, not just into git.
docs/ADR.mdrecords each decision as Status / Context / Decision (newest entry 2026-07-13, plugin manager keyed by Hermes home).docs/design/profile-builder.mdcarries a live "Open product decisions (need Teknium)" section plus "What already exists (reuse, don't rebuild)" and "Verification plan (when built)" — a fresh session can read it and know what is settled and what is not.docs/rfcs/plugin-config-state-bridge.md:3records status inline ("config + state slice implemented by #64227"). The issue/PR convention is written down too:.github/PULL_REQUEST_TEMPLATE.mdrequires "Related Issue / Fixes #", andCONTRIBUTING.md:21-38tells contributors togh search issuesandgh search prsfirst and to comment on the issue to claim larger work. - Recommendation: —
- Priority: Low
-
10. Is there a README that says what this project is and how to run it? This is where a good README earns its credit. It needs three things: what the project is, how to run it, and how to check a change. Judge what is on the page, not the file's existence.
- Status: PASS
- Proof:
README.md(17,688 bytes) does all three. What it is: lines 5-34 plus "CLI vs Messaging Quick Reference" (143-162). How to run it: "Quick Install" (35-104) with a one-line installer for Linux/macOS/WSL2/Termux and native PowerShell, "Getting Started" (105-123), and a Troubleshooting block. How to check a change: the Contributing section (217-231) gives the full loop — installer,cd "${HERMES_HOME:-$HOME/.hermes}/hermes-agent",uv pip install -e ".[all,dev]",scripts/run_tests.sh— plus a manual-clone fallback that warns why the venv must live outside the source tree. - Recommendation: —
- Priority: High
2. Specs
-
11. Is the thinking behind this system written down somewhere durable — a specs, RFC, proposals, design-doc or ADR directory, or architecture notes that record not just what but why? Look for the place and for the convention:
specs/,docs/adr/,rfcs/,proposals/,.specify/, a docs or design folder, architecture notes anywhere in the tree, or this ecosystem's equivalent. Judge substance, not location: "we use X because Y" is a decision, a list of technologies is not, an essay about specs is not a spec, and a docs folder of usage guides with no reasoning is a FAIL whose proof says what was in there instead. Other items are answered against whatever this item finds, so name it precisely — and where forward-looking specs and after-the-fact architecture records live in different places, name both, since a decision log cannot answer a question about acceptance criteria.- Status: PASS
- Proof: Two places, both with real reasoning. After-the-fact decisions:
docs/ADR.md, dated entries in Status / Context / Decision form — the 2026-07-13 entry spends ~40 lines on why a process-global plugin-manager singleton broke profile switching (sys.modulescachinghermes_plugins.<slug>.<submodule>) before stating the keyed-cache decision. Forward-looking specs:docs/rfcs/(plugin-config-state-bridge.md,2026-07-plugin-architecture-lessons-pi-opencode.md) anddocs/design/profile-builder.md. Alongside them sit contract and incident docs that also record why:docs/relay-connector-contract.md,docs/chronos-managed-cron-contract.md,docs/security/network-egress-isolation.md,docs/rca-ssl-cacert-post-git-pull.md. AGENTS.md:29-211 carries the intent layer (the rubric and the Footprint Ladder). - Recommendation: —
- Priority: High
-
12. Does the specs directory hold recent entries, or is it an archive nobody has touched? If no specs directory exists, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise compare the newest entry's date against the repo's recent activity.
- Status: PASS
- Proof: Measured against full history after
git fetch --deepen=600(the shallow clone's boundary date would have made everything look identical):docs/ADR.mdlast touched 2026-08-12 19:13,docs/rfcs/plugin-config-state-bridge.md2026-08-12 16:26,docs/design/profile-builder.mdanddocs/observability/README.md2026-08-08. HEAD is 2026-08-14. Two days behind a repo that lands ~15 commits a day is live, not archived. - Recommendation: —
- Priority: Low
-
13. Is there a spec template, or an SDD framework, so every spec comes out the same shape? Scaffolding can exist even where no specs directory does — look for a TEMPLATE.md, a
.specify/directory, or framework config. If neither a directory nor any scaffolding exists, FAIL.- Status: FAIL
- Proof: No spec scaffolding anywhere: no
TEMPLATE.md, no.specify/, no framework config (git ls-files | grep -iE 'TEMPLATE|\.specify'returns only.github/ISSUE_TEMPLATE/*and.github/PULL_REQUEST_TEMPLATE.md, which shape issues and PRs, not specs). The three newest design documents come out in three different shapes:docs/ADR.mduses Status / Context / Decision;docs/rfcs/plugin-config-state-bridge.mduses Scope / Config API / Durable state API / State vs. config / Verification contract / Related;docs/design/profile-builder.mduses Why this / What already exists / Seams / Proposed backend / Proposed frontend / Open product decisions / Verification plan. - Recommendation: Add
docs/rfcs/TEMPLATE.mdwith the headings the existing RFCs already converge on — Status, Scope, Non-goals, Design, Verification contract, Related — and one line indocs/ADR.mdpointing new decisions at the Status / Context / Decision shape it already uses. - Priority: Low
-
14. Do the specs state acceptance criteria a machine could check? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise open the two newest specs and quote a criterion: "the endpoint returns 403 for expired tokens" is checkable; "the feature works well" is not.
- Status: PASS
- Proof: The two newest specs both state criteria a machine can check.
docs/rfcs/plugin-config-state-bridge.md:101-115: "the complete serialized state is limited to 10 MiB per plugin. A rejected update leaves the previous file untouched", "state writes use temp-file +fsync+os.replace", "State keys are 1-128 characters and may contain letters, numbers,_,-,., or:. Path separators and..are rejected." Its "Verification contract" (127-139) then lists the cases the implementation must cover, including "concurrent settings writes without lost siblings" and "two-profile isolation after the ambient profile changes".docs/design/profile-builder.md:138carries a "Verification plan (when built)". - Recommendation: —
- Priority: High
-
15. Open the newest spec: do its criteria go past the happy path — what happens when a step fails, and how the change gets undone? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Look for error cases, edge inputs, and a rollback or undo story, not just the success flow.
- Status: PASS
- Proof: The newest spec,
docs/rfcs/plugin-config-state-bridge.md, goes well past the happy path. Failure cases: "Fail closed: malformed/non-object JSON is reported and never overwritten"; quota rejection; "direct global, cross-plugin, POSIX traversal, and Windows traversal rejection" (127-139). Concurrency: a sibling lock file serialising read-modify-write across threads and processes,fcntlon POSIX andmsvcrton Windows. Undo story at the data level: "A rejected update leaves the previous file untouched" and atomic temp-file +os.replace, so a failed write cannot leave a half-written state file behind. - Recommendation: —
- Priority: Low
-
16. Do the specs state non-goals, so an agent knows where to stop? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Non-goals written elsewhere (a README's "what this is not" list) are worth naming in the proof, but they do not turn this into a PASS — the question is whether specs carry them.
- Status: PASS
- Proof:
docs/rfcs/plugin-config-state-bridge.md:9-17scopes the slice to two capabilities and then says what is deliberately out: "Config schema registration, config defaults, and the cron facade from the original RFC remain separate follow-up work. No core model tool is added."docs/design/profile-builder.md:28has "What already exists (reuse, don't rebuild)" and 126 has "Open product decisions (need Teknium)" — both fences an agent can stop at. The repo-wide non-goals are in AGENTS.md:96-136 ("What we don't want, rejected even when well-built"). - Recommendation: —
- Priority: Low
-
17. Can recent shipped work be traced back to a spec? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise take the last few substantial commits or PRs and look for a reference to a spec, an issue, or a design doc in the message or description.
- Status: PASS
- Proof: The trace runs both ways.
docs/rfcs/plugin-config-state-bridge.md:3-7names the shipped work and its origin: "Status: config + state slice implemented by #64227", "Original design: Topher Ross (@thebizfixer), RFC PR #58542", "Concrete consumer: kanban-advanced". Recent commits carry the PR number in the subject —17d6a7d fix(desktop): avatar misses expire after 30s instead of caching forever (#85908),5e8d25d fix(cli): --in accepts Git Bash / MSYS-style paths on Windows (#85865)— and.github/PULL_REQUEST_TEMPLATE.mdrequires a "Fixes #" link. AGENTS.md:1296 traces a rule back to PR #3575 and 598-620 traces the pinning policy to #2796, #2810 and #9801. - Recommendation: —
- Priority: Low
3. Verification
-
18. Does this project have an automated test suite, in whatever form this ecosystem uses? Work out this ecosystem's convention before concluding anything is missing — check the manifest, the build file, the CI config, the README. A shell script that diffs output files is a test suite. If you find one, run it and record the result.
- Status: PASS
- Proof: Ran it.
uv sync --locked --python 3.11 --extra devon a bare clone, thenHERMES_TEST_FILE_RETRIES=0 ./scripts/run_tests.sh tests/hermes_cli/test_profiles.py tests/test_hermes_state.py -q: "2 files, 276 tests passed, 0 failed, 4 skipped (100% complete) in 6.4s (20 workers)", with a note that thewindows_onlytests were skipped on darwin and run on thetests-osCI lane. The suite is 2,910 pytest files undertests/(testpaths = ["tests"]inpyproject.toml:429) plus a vitest suite per npm workspace (tests-js/,ui-tui,apps/desktop,web). I did not run the full suite — CI shards it across slices. - Recommendation: —
- Priority: High
-
19. Can the test command be discovered without guessing — is it written down where an agent reads? If no test suite exists, FAIL with a one-line proof pointing at the test-suite item. Otherwise check the places an agent looks: the agent instruction file, the README, the manifest's scripts or targets.
- Status: PASS
- Proof: AGENTS.md:1352-1367, a top-level
## Testingheading in the entry point, opens "ALWAYS usescripts/run_tests.sh— do not callpytestdirectly" and gives four invocations (full suite, one directory, one file with-k, pass-through flags), plus the reason: hermetic parity with CI.CONTRIBUTING.md:201-213andREADME.md:231repeat it. The JS side is at AGENTS.md:504-517 and 547. - Recommendation: —
- Priority: High
-
20. Do the tests assert real behaviour, rather than asserting that a mock was called? If no test suite exists, FAIL with a one-line proof pointing at the test-suite item. Otherwise open the largest test files and read the assertions: calling real code on real inputs passes;
expect(mock).toHaveBeenCalled()as the main dish fails.- Status: PASS
- Proof: Opened the largest suites.
tests/test_hermes_state.py(4,763 lines) drives a realSessionDBagainst a real SQLite file and asserts behaviour —assert [match["session_id"] for match in base_matches] == [...]on FTS5 search results,assert session["model"] == "test-model"after a round trip,assert list(tmp_path.glob("*malformed-backup*"))after corruption recovery — and subclassessqlite3.Cursorto simulate a build without the fts5 module rather than mocking the call. Ratio across the suite: 65,089assertlines, withassert_called/assert_has_callsappearing in 362 files out of 2,910. The rules are written down too: AGENTS.md:1441-1489 bans change-detector tests and 1490-1543 bans tests that read source text, both with worked before/after examples. - Recommendation: —
- Priority: High
-
21. Is there a linter or static analysis configured for this language, and does it pass on a clean checkout? Configured is not enough — run it. A linter that exits non-zero on an untouched checkout is a FAIL with the error count in the proof, because an agent cannot tell its own damage from the baseline noise.
- Status: PASS
- Proof: Ran it on the clean checkout:
uvx ruff@latest check .→ "All checks passed!", exit 0, with one warning about an invalid# noqadirective atrun_agent.py:108. It is enforced:.github/workflows/lint.yml:148-152runsruff check .as a blocking required job ("No --exit-zero, no || true"). Scope is deliberately narrow —pyproject.toml:454-462selects exactly one rule, PLW1514, with a comment explaining that all others are off "while we wrangle typechecks" and that this one stays because bareopen()in text mode corrupts non-ASCII on Windows. Two more static gates back it up:scripts/check-windows-footguns.py --all(blocking) and eslint withtypescript-eslint,perfectionist,react-hooksandunused-importsper workspace. I did not run eslint locally — it needsnpm ciacross 11 workspaces. - Recommendation: —
- Priority: High
-
22. Is there a formatter, so an agent's diffs do not churn on style? Look for the config file and the dependency in this ecosystem's form — .prettierrc, rustfmt, gofmt, black, an .editorconfig doing real work. If the language ships one formatting standard with the toolchain, that is a PASS and the proof says so.
- Status: FAIL
- Proof: The TypeScript half is covered:
.prettierrc(printWidth 120, no semicolons, single quotes),.prettierignore, anfmtscript in each workspace,npm run fixat the root, and merges that run it (bee9ef3 fmt(js): npm run fix on merge). The Python half — the primary language, ~2,910 test files plus the whole core — has no formatter:pyproject.tomlhas[tool.ruff]and[tool.ruff.lint]but no[tool.ruff.format], there is no black or blue in the dev extras, and there is no.editorconfigin the tree. - Recommendation: Add
[tool.ruff.format]topyproject.toml, runruff format .once as a single mechanical commit, then addruff format --check .to the existing blocking ruff job at.github/workflows/lint.yml:128. - Priority: Low
-
23. Is there a compile-time or type-level gate, if this language offers one? N/A only when the language genuinely has no such gate. If the language offers one and the repo does not use it — no strict mode, no typecheck script, no compiler step — that is a FAIL. Run the gate if it exists and record the result.
- Status: FAIL
- Proof: A type checker is configured and run, but it does not gate anything.
pyproject.toml:444-449configures Astral'sty(python-version = "3.13", two rule overrides) and.github/workflows/lint.yml:31-127runs it on HEAD and on the base ref, then feeds both intoscripts/lint_diff.pyto write a summary — the step ends|| trueand only appends to$GITHUB_STEP_SUMMARY, while the sibling job comment states plainly that only ruff is "blocking". I ranuvx ty@latest check: Found 14754 diagnostics on an untouched checkout, which is why it cannot block. The TypeScript side does have a real gate (typecheck: tsc --noEmitinside each workspace'scheckscript). - Recommendation: Make the existing base-vs-HEAD
tycomparison in.github/workflows/lint.ymlfail the job when HEAD introduces diagnostics the base did not have — the two JSON reports are already produced, so this is a change toscripts/lint_diff.py's exit code, not new infrastructure. - Priority: Low
-
24. Can an agent prove its own work before it pushes — one command, task-runner target or commit hook that runs every check this project has? One command, not a list to remember: a
checkorverifytarget, aprecommitscript, a Makefile target that chains them. Separate commands documented side by side are close but FAIL — the question is whether the agent can run the whole gauntlet without knowing its parts.- Status: FAIL
- Proof: There is no one command. An agent must run at least four:
scripts/run_tests.sh(pytest),ruff check .,python scripts/check-windows-footguns.py --all, andnpm run checkat the root (which isnpm run --ws check, JS workspaces only).package.json:25definescheckbut it never touches Python; there is no Makefile, justfile or Taskfile (ls | grep -iE 'makefile|justfile|taskfile'is empty);nix/checks.nixdefinesnix flake checkbut it verifies packaging (cross-eval, build-package, build-devshell, package-contents), not the test suite or the linters, and the string "nix" appears 0 times in AGENTS.md, CONTRIBUTING.md and README.md, so an agent would never find it. - Recommendation: Add
scripts/check.shthat runsruff check .,python scripts/check-windows-footguns.py --all,scripts/run_tests.shandnpm run checkin sequence with a non-zero exit on the first failure, and name it in the AGENTS.md "Testing" section as the pre-push command. - Priority: High
-
25. Is there continuous integration, and does it run before a change lands rather than after? A deploy pipeline that runs on push to the default branch runs after the change has landed — that is a FAIL with the trigger quoted, not a PASS with a caveat. Look for a pre-merge trigger: pull_request events, merge trains, or this forge's equivalent.
- Status: PASS
- Proof:
.github/workflows/ci.yml:17-20triggers onpull_requestfirst andpush: branches: [main]second, so every change is checked before it lands. It is an orchestrator: adetectjob classifies the diff (scripts/ci/classify_changes.py, failing open on push so post-merge validation is never weakened), then calls the sub-workflows byworkflow_call, and "a finalall-checks-passgate job aggregates results so branch protection only needs to require a single check". Concurrency cancels in-progress runs on PRs only. - Recommendation: —
- Priority: High
-
26. Does CI actually run the tests and the checks that exist in this repo? If no pipeline of any kind exists, FAIL with a one-line proof pointing at the CI item. Otherwise list what the pipeline runs and diff it against every check found in the Verification section — name each check that exists in the repo but is missing from the pipeline.
- Status: PASS
- Proof: Every check that exists in the repo has a CI lane.
tests.ymlshards pytest across slices with a duration cache (uv sync --locked --python 3.11 --extra all --extra dev ...);tests-os.ymlruns the macOS and Windows lanes offscripts/ci/list_os_marked_tests.py;js-tests.ymlenumerates the npm workspaces and runs each one'scheckscript (typecheck + vitest + eslint) afternpm ci;lint.ymlruns blocking ruff, advisoryty, and the blocking Windows-footgun checker;osv-scanner.yml,supply-chain-audit.yml,uv-lockfile-check.yml,lockfile-diff.yml,docker-lint.yml,docker.yml,docs-site-checks.yml,installer-tests.yml,install-e2e.yml,e2e-desktop.yml,skills-index-freshness.yml,contributor-check.ymlandhistory-check.ymlcover the rest. The one gap is documented rather than hidden: AGENTS.md:1391-1401 warns that the change classifier means a Python test asserting about JS artifacts will not run on a JS-only PR, and tells you to put such tests in the vitest suite instead. - Recommendation: —
- Priority: High
-
27. Does anything here get an LLM to read a change and go looking for problems — a review skill, a saved review prompt, or an AI reviewer on the pull requests? Look for a committed review skill or slash command, a CI step calling an AI reviewer, or bot config. Human review rules are a Safety item; this one is specifically about machine review.
- Status: FAIL
- Proof: Nothing gets an LLM to read a diff here.
.github/workflows/ci-review-comment.ymlsounds like it but is not:scripts/ci/live_comment.py"Polls the GitHub Actions API for job statuses in the CI run, assembles the review comment from whatever results are available, and upserts it as a PR comment" — a status aggregator, andgrep -rilE 'openrouter|anthropic|openai|llm' scripts/ci/*.pymatches onlypublish_e2e_evidence.py.supply-chain-audit.yml:68-115is grep over the diff for.pthfiles and base64+exec, not a model. AGENTS.md:36-41 describes an automated "triage sweeper" that closes PRs on three allowed reasons, butgit grep -ril sweeper -- .github scriptsreturns nothing, so whatever runs it lives outside this repo. There is no.claude/commands, no committed review skill, and no AI reviewer bot config. - Recommendation: Add a review skill or slash command under
.claude/skills/(or aprompts/directory) that encodes the checks AGENTS.md:29-211 already spells out — verify the premise on currentmain, no change-detector tests, no source-reading tests, prompt-cache safety, Footprint Ladder rung — so a contributor's agent can self-review before opening the PR. - Priority: Low
4. Tooling
-
28. Can an agent reach the outside systems this project depends on, with that access committed to the repo rather than set up per laptop? MCP servers are one form: look for a committed
.mcp.jsonor this ecosystem's equivalent. A CLI is another and often the better one —aws,gh,psql,kubectl,stripe, a vendor's own tool — and it counts when the repo names which tools the work needs and how to authenticate, so an agent is not guessing at a tool it cannot see. Judge the axis, not the mechanism: access that exists only in someone's shell history or laptop config is a FAIL, and so is a committed config that turns out to be gitignored, with the distinction in the proof. Project task scripts have their own item; this one is about reaching past the repo's edge. If this project genuinely talks to nothing outside itself, N/A with that as the reason.- Status: PASS
- Proof: Access is committed, not per-laptop.
.env.example(tracked) names every external service the project talks to and where to get each credential (Fireworks, OpenRouter, Novita and ~20 more), with the real.envgitignored.CONTRIBUTING.md:21-38names the CLI for the dev loop and how to use it:gh search issues --repo NousResearch/hermes-agent "<terms>"andgh search prs --repo ... --state all.optional-mcps/*/manifest.yamlcommits five MCP server manifests (comfy-cloud, figma, linear, n8n, unreal-engine) andhermes_cli/mcp_catalog.py+mcp_config.py+mcp_security.pygenerate the runtime config. Nothing important is reachable only from someone's shell history. Gap worth naming: there is no committed.mcp.jsonfor an agent working on this repo, so a coding agent gets no MCP access of its own. - Recommendation: —
- Priority: Low
-
29. Is there a skills, commands or reusable-prompt library in the repo? Look for .claude/skills, .agents/skills, .claude/commands, or a prompts directory. Committed and pinned beats committed; note whether anything ties the copies to a source.
- Status: PASS
- Proof:
skills/holds 485 tracked files across 15 categories (software-development,devops,github,research, …) asSKILL.mdprompt bundles,optional-skills/holds 550 more, and the library is maintained rather than dumped:scripts/build_skills_index.pybuilds an index,.github/workflows/skills-index.ymlandskills-index-freshness.ymlkeep it current in CI, and AGENTS.md:925-1035 sets authoring standards ("Skill authoring standards (HARDLINE)", frontmatter contract). The honest caveat: these are the product's skills shipped to Hermes users, not a harness for agents working on this repo — the dev-facinghermes-agent-devskill referenced at AGENTS.md:1030 is not in the tree. - Recommendation: —
- Priority: Low
-
30. Do the skills, servers and tools that exist cover the work this team plainly repeats? First identify the repeated work from the README and the commit history. If skills, servers or documented tooling exist but miss it, FAIL naming the gap. If none exist at all and the repo plainly repeats work, FAIL pointing at the outside-systems item and the skills-library item. If the project is too small to repeat anything, N/A.
- Status: PASS
- Proof: The repeated work is visible in the commit history — platform adapters, model providers, plugins, skills, desktop/TUI features, releases — and each has a written recipe or a script. Adding a platform:
gateway/platforms/ADDING_A_PLATFORM.md. Adding a tool: AGENTS.md:548-597. Adding a slash command: AGENTS.md:434-466. Adding a skin: AGENTS.md:730-773. Plugins and memory/model providers: AGENTS.md:774-924 pluswebsite/docs/developer-guide/model-provider-plugin.md. Releases:scripts/release.py(CalVer + changelog). Contributor attribution, an actually repeated chore here:scripts/add_contributor.py,scripts/contributor_audit.py,scripts/audit_pr_attribution.py,.mailmap,contributor-check.yml. Plus 80 files underscripts/, 12 of them CI helpers. - Recommendation: —
- Priority: Low
-
31. Are there project CLI scripts or task-runner targets for the common jobs? Check the manifest's scripts, the Makefile, the justfile, or this ecosystem's equivalent. The test: does routine work need a raw multi-flag command that someone has to remember?
- Status: PASS
- Proof:
package.json:13-27gives root targets —check(npm run --ws check),fix, and per-workspaceinstall:*/audit:*/audit:fix:*— and each workspace defines the same verbs, e.g.ui-tui:check=build:ink && typecheck && test && lint, plusdev,build,fmt,lint:fix. Python side:scripts/run_tests.sh(hermetic wrapper with--file-retries, pass-through pytest flags),scripts/run_tests_parallel.py,scripts/release.py,scripts/install.sh,setup-hermes.sh,scripts/dev-sandbox.sh, anddocker-compose.yml.pyproject.toml:364declares thehermesconsole script. Routine work does not need a remembered multi-flag command. - Recommendation: —
- Priority: High
-
32. Are those scripts named somewhere the agent will actually read them? If no scripts exist, FAIL with a one-line proof pointing at the task-scripts item. Otherwise check the agent instruction file, the README, and whether the manifest itself is self-explanatory.
- Status: PASS
- Proof: The ones an agent needs daily are in the instruction set:
scripts/run_tests.shwith four invocations at AGENTS.md:1362-1367 and again atCONTRIBUTING.md:201-213andREADME.md:231; the TUI'snpm run dev/build/typecheck/lint/fmt/testat AGENTS.md:504-517; the desktop'snpx vitest run src/lib/desktop-slash-commands.test.tsat AGENTS.md:547;scripts/release.pyusage in its own docstring. The rootcheckandfixtargets are not named in prose butpackage.json:25-26is self-explanatory. The linter commands are the exception, and they are failed at the build-test-check-commands item rather than here. - Recommendation: —
- Priority: High
-
33. Can an agent get this project running — is there a reproducible environment or a documented setup path? Look for a pinned runtime (.nvmrc, .tool-versions, rust-toolchain), a lockfile, a container or nix file, and written setup steps. Try the first step if it is cheap and safe. This one bites hardest the moment work happens in a fresh git worktree or a new clone — the normal way to run agents in parallel. A new worktree has no installed dependencies, no
.env, no build cache, so anything that works today only because of untracked state sitting on someone's machine simply does not run there. The test: would a bare checkout plus the written steps get this project up? Name any prerequisite nothing creates — an env file someone hand-made, a seeded database, a manual login — because each one is a wall a worktree hits on its first command.- Status: PASS
- Proof: Proved on a bare clone in a temp folder, no prior state:
uv sync --locked --python 3.11 --extra devsucceeded fromuv.lock, andscripts/run_tests.shthen ran 276 tests green. Runtimes are pinned (.python-version= 3.11,.nvmrc= 26,engines.node >= 22.22.0), dependencies are locked (uv.lock+ fivepackage-lock.json), and there are three documented paths:CONTRIBUTING.md:105-213(installer or manual clone, with the warning to create the venv outside the source tree),flake.nix+nix/devShell.nix, andDockerfile+docker-compose.yml. Two walls a fresh worktree hits, neither of which anything creates for you:scripts/run_tests.shneeds a.venv/venvat the checkout root orHERMES_PYTHON(it exits with exactly that message and both paths it probed), and actually running the agent needs~/.hermes/config.yamlplus at least one provider key in~/.hermes/.env(CONTRIBUTING.md:174-184). Tests themselves need neither — the wrapper unsets credentials on purpose. - Recommendation: —
- Priority: High
-
34. Can an agent see the results of a failed run — do the tools here produce output it can read and act on? Judge from the runs you already did in Verification: does a failure print a path, a line, a name — something actionable — or a wall of noise? If nothing could be run, FAIL saying why.
- Status: PASS
- Proof: Everything I ran printed something actionable.
scripts/run_tests.shwith no venv: "error: no virtualenv with pytest found in <path>/.venv or <path>/venv, and HERMES_PYTHON is not a python with pytest (enter the Nix devShell or create a venv)" — the two paths it probed and three ways to fix it. The test runner prints a live per-file progress line, a summary ("276 tests passed, 0 failed, 4 skipped"), a host-skip note naming the CI lane that does run them, a⚠ FLAKYsection with both attempts when a retry saves a file, and a per-file duration table.ruffprintsfile:line:colwith the rule;typrints the source line with a caret. - Recommendation: —
- Priority: Low
5. Safety
-
35. Are credentials kept out of the repo — nothing secret committed, ignore rules in place, an example env file for the shape? Three checks: grep tracked files for key-shaped strings, read the ignore rules for env and key patterns, and look for an example env file. Report each of the three separately.
- Status: PASS
- Proof: Three checks, all clean. (1) Grep of tracked files for key shapes —
git grep -nIE '(sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{30,}|AKIA[0-9A-Z]{12,}|-----BEGIN .*PRIVATE KEY-----)'returns exactly one hit, a comment describing a redaction pattern atagent/redact.py:360. (2) Ignore rules —.gitignorecovers.env,.op.env,.env.local,.env.development,.env.test,.env.*.local(lines 14-22) and*.pemunder a "Private keys" heading (59-61). (3) Example file —.env.exampleis tracked, keys commented out, each with a description and the URL to obtain it;AGENTS.md:647-663states the rule that.envis secrets only and everything else belongs inconfig.yaml. - Recommendation: —
- Priority: High
-
36. Does anything scan for secrets automatically? Look for gitleaks, trufflehog, detect-secrets or this ecosystem's equivalent, wherever it is wired in — a CI step, a pre-commit hook, or forge-level push protection visible from the repo. CI is where this normally lives, and that is a PASS; a local hook on top is better, because it catches the key before it is pushed rather than after, but its absence is a line in the proof, not a FAIL. Say where the scan runs. "Nothing secret exists today" does not make this N/A — the scan is for the day that changes.
- Status: FAIL
- Proof: No secret scanner anywhere.
grep -rilE 'gitleaks|trufflehog|detect-secrets|secret-scan'over.github/,scripts/and.pre-commit-config.yamlreturns nothing, and there is no pre-commit config in the tree. The nearest thing,.github/workflows/supply-chain-audit.yml:68-115, greps the PR diff for malware signatures —.pthfiles added undersite-packages/andbase64.b64decodeon the same line asexec(/eval((the litellm attack pattern) — which is a different job.agent/redact.pyredacts secrets at agent runtime, not in the repo. Forge-level push protection may be on, but nothing in the tree shows it. - Recommendation: Add a gitleaks step to the existing
supply-chain-audit.ymlscan job — it already computes thebase...headdiff and posts findings into the CI review comment, so it is one more step in a job that runs on every PR. - Priority: High
-
37. Are dependencies pinned, so a build is reproducible? Look for lockfiles in every package of the repo, exact versions for load-bearing dependencies, a pinned runtime, and an install command that respects the lock (npm ci, not npm install).
- Status: PASS
- Proof: Pinned at every level, with the policy written down at AGENTS.md:598-620 (a table: PyPI
>=floor,<next_major, git URLs by 40-char SHA, GitHub Actions by SHA + version comment, CI-only pip==exact) and the incidents that produced it (litellm compromise PR #2796/#2810, Mini Shai-Hulud May 2026). Artefacts:uv.lockwith hashes,package-lock.jsonat the root and inwebsite/,plugins/platforms/photon/sidecar/,scripts/whatsapp-bridge/,nix/;.python-version3.11;.nvmrc26;package.jsonoverridespinning 13 transitive packages; devDependencies pinned to exact versions. Install respects the lock everywhere:uv sync --lockedintests.yml,tests-os.yml,docker.yml,e2e-desktop.yml;npm ciinjs-tests.yml,deploy-site.yml,docs-site-checks.yml,e2e-desktop.yml. Two workflows guard it —uv-lockfile-check.ymlandlockfile-diff.yml— andsupply-chain-audit.yml:181-258fails the build on an unbounded PyPI dependency. Actions are SHA-pinned throughout (actions/checkout@de0fac2e… # v6.0.2). - Recommendation: —
- Priority: High
-
38. Is anything watching those dependencies for known vulnerabilities? Look for dependabot or renovate config, an audit step in CI, or this ecosystem's equivalent. Check every lockfile in the repo is covered, not just the root one.
- Status: PASS
- Proof:
.github/workflows/osv-scanner.yml:42-55scans every lockfile in the repo by name —uv.lock,package-lock.json,website/package-lock.json,plugins/platforms/photon/sidecar/package-lock.json,scripts/whatsapp-bridge/package-lock.json— and uploads SARIF to code scanning..github/dependabot.ymlcoversgithub-actionsweekly and explains, in 24 lines of comment, why source ecosystems are deliberately excluded (exact pins are moved after review, not on a schedule) while CVE-triggered security updates stay enabled at the repo setting.package.json:19-24addsaudit:root,audit:web,audit:tuifor manual npm audits. - Recommendation: —
- Priority: Low
-
39. Are the review rules written down — who reads a change, and what they check? Look for CONTRIBUTING, a PR template, or a review checklist in the agent instruction set. On a solo repo the "who" is N/A-shaped but the "what gets checked before it lands" still matters — judge that half.
- Status: PASS
- Proof: Written down in three layers.
CONTRIBUTING.md(48,835 bytes) covers priorities, search-first, skill-vs-tool, dev setup, code style, and how to add each kind of thing..github/PULL_REQUEST_TEMPLATE.mdasks for what/why, a linked issue, type of change, changed files, reproduction-and-proof steps, and a checklist. AGENTS.md:29-211 is the actual review rubric — what gets merged, what is rejected even when well built, the four wrong-premise patterns distilled from real closes, and when not to close a PR. Enforcement exists too:contributor-check.yml(attribution),review-labels.yml,history-check.yml, andSECURITY.mdfor vulnerability reports. - Recommendation: —
- Priority: Low
-
40. Are the operations that need a human named somewhere an agent will read them? Look for a "never without asking" list in the agent instruction file or the README. Docs that hand out production commands with no fence around them count against, and the proof should quote one.
- Status: FAIL
- Proof: There is no "never without asking" list. Grepping AGENTS.md for approval, never-without, ask-a-maintainer and do-not-run language returns only unrelated hits (the gateway's approval message guards at 1310, a dependency-bounds rule at 614).
apps/desktop/AGENTS.mdhas none either.SECURITY.mdis about the product's trust model — OS isolation, credential scoping, plugin trust — not about which repo operations an agent must hand back to a human. Meanwhile the docs hand out publishing commands with no fence:scripts/release.py:12documents "# Create the release" /python scripts/release.py --bump minor --publish, and.github/workflows/deploy-site.yml:35-40documents "manually viagh workflow run deploy-site.ymlwhen an out-of-band main commit needs to ship live". - Recommendation: Add a short "Never without a human" section to AGENTS.md listing the operations an agent must stop at:
scripts/release.py --publish,gh workflow run deploy-site.yml,git pushtomain, force-pushes, closing or merging PRs, and any command that spends a provider API key (evals/,scripts/*_livetest.py,batch_runner.py,mini_swe_runner.py). - Priority: High
-
41. Does every action that spends money, destroys data or changes production have a human in the way? Start from the damage, not from the tooling: list what in reach of this repo could charge a card, drop or overwrite data, or alter what users are running. Then trace the shortest route an agent could take to each one — a push that auto-deploys, a script carrying live credentials, a migration that runs on merge, an infrastructure apply with no plan-and-approve step. PASS when every route meets a human first, whether that is a review, a manual trigger or a protected environment. FAIL when even one route runs start to finish unattended, and quote that route in the proof so the fix is obvious.
- Status: FAIL
- Proof: Three things in reach can change what users run: the container images, the docs site, and a GitHub release. Two of the routes meet a human —
docker.ymlpublishes:main/:latestonly on a push tomainand behind a namedenvironment: container-publish, anddeploy-site.ymldeploys on a push tomaintouchingwebsite/**— and both sit downstream of a PR merge gated by theall-checks-passrequired check (ci.yml:33-36). One route does not:python scripts/release.py --bump minor --publishrun from a checkout with aghtoken creates a public GitHub release, anddeploy-site.yml:3-5anddocker.ymlboth fire onrelease: types: [published], so a single unattended command ships a release, the site and the container tags. Its own docstring advertises it. Whethercontainer-publishrequires reviewers is a repo setting I cannot see from the tree. - Recommendation: Make
scripts/release.py --publishrefuse to run outside CI unless a--i-am-a-maintainerstyle flag is passed, and move the release cut into aworkflow_dispatchworkflow behind a protected environment with required reviewers, so the human gate is enforced rather than assumed. - Priority: High
-
42. If a prompt injection landed tonight, how far would it reach — are the credentials an agent can get to here scoped to the job, with nothing production-grade in reach? Inventory what an agent in this repo can reach: env files, cloud CLI profiles, tokens named in docs or config, deploy commands that work from a laptop. Scoped-or-absent passes; production-grade reach fails with the item named.
- Status: FAIL
- Proof: Repo-provided credentials are clean: nothing secret is committed,
.envis gitignored, and CI is scoped hard —ci.yml:12-14says "Do not addsecrets: inheritor GitHub App credentials here. Trusted main-only automation uses protected environments in its own workflows", actions are SHA-pinned, andSECURITY.md§2.3 covers credential scoping. What an agent can still reach on a maintainer's machine is not scoped and not fenced:~/.hermes/.envholds live paid provider keys, and the repo ships commands that spend them at scale —evals/,scripts/tool_search_livetest*.py(five of them),batch_runner.py,mini_swe_runner.py,scripts/benchmark_browser_eval.py— plus the laptop publish routes at the human-gate item. Nothing in the instruction set says which of these bill real money. - Recommendation: In the same AGENTS.md section, state that
~/.hermes/.envholds live paid keys, thatscripts/run_tests.shis hermetic and safe by design whileevals/,scripts/*_livetest.py,batch_runner.pyandmini_swe_runner.pybill real money, and that agents should use a separate low-limit key for any of those. - Priority: High
-
43. Can a change reach production a slice at a time — a feature flag that defaults to off, a canary, a staged rollout — rather than everyone at once? Look for a flag system and check the default, or canary and staged-rollout config in the deploy pipeline. Flags that need a rebuild to flip are worth naming in the proof — they gate exposure but they are not a kill switch.
- Status: PASS
- Proof: New capability lands off by default and flips at runtime, which is the pattern the instructions enforce rather than merely allow. AGENTS.md:621-663 requires behavioural settings to live in
~/.hermes/config.yaml— read at startup, no rebuild — and rejects newHERMES_*env vars for anything but secrets; AGENTS.md:182-211 puts service-gated tools behind acheck_fnso they are absent until a prerequisite is configured; toolsets are opt-in per profile,optional-skills/is "shipped but NOT active by default", plugins are discovered only from~/.hermes/plugins/, and features likekanban.dispatch_in_gatewayandcurator.enabledare plain config booleans. What does not exist is a canary or percentage rollout for the released binaries themselves — a published release reaches every updater at once (git grep -inE 'canary|staged rollout|release channel'finds no release-gating machinery). - Recommendation: —
- Priority: High
-
44. Once a change is live, can anyone see what it is doing — logs, metrics, traces, alerts that fire on their own, and can an agent read them too? Look for logging setup, an error tracker, analytics, alerting config — and then ask the second half: could an agent reach any of it (a CLI, an MCP server, an API named in the docs), or does observability stop at a dashboard behind a login?
- Status: PASS
- Proof: Both halves. Production:
hermes_logging.pysets upagent.log(INFO+),errors.log(WARNING+) andgateway.log, profile-aware viaget_hermes_home();docs/observability/README.mddefines a read-only observer-hook contract for trace/metrics/audit/replay exporters (Langfuse, OTel-style collectors, NeMo Relay);docs/observability/monitoring.mdcovers alerting including a killed-gateway drill;plugins/observability/ships the plugin. Agent-readable: AGENTS.md:311-314 nameshermes logs [--follow] [--level ...] [--session ...]and the on-disk paths, andhermes_cli/subcommands/logs.pyimplements it — so an agent reads logs with a CLI, not through a dashboard behind a login. - Recommendation: —
- Priority: Low
-
45. Is there a way back — can a bad change be undone without a rebuild and a redeploy, including the ones that touched a database or a queue? Look for a documented rollback path, a revert-and-redeploy story, down-migrations, or a flag that can turn the change off at runtime. Deployment docs that only say how to go forward are worth quoting.
- Status: PASS
- Proof:
website/docs/getting-started/updating.md:195-223is a "Rollback instructions" section: list recent versions, roll back to a specific commit, or check out a previous release tag (git tag --sort=-version:refname), then restart the gateway. It is not the only path. The updater takes a pre-update snapshot by default — pairing data, cron jobs,config.yaml,.env,auth.json— controlled byupdates.pre_update_backup(quick/full/off, with--backup/--no-backuponhermes update), restorable through the flow inwebsite/docs/user-guide/checkpoints-and-rollback.md. There is an automatic one too: after pulling, Hermes compiles the nine critical startup files and runsgit reset --hard <pre-pull-sha>if any fails to parse, "so your shell stays bootable". Nix installs roll back withnix profile rollback; container users pin:<release_tag>instead of:latest. Runtime state is covered by the plugin-state atomic-replace guarantees in the newest RFC. - Recommendation: —
- Priority: Low
-
46. Does a past failure leave a check behind — is there a test or a rule in here that exists because something broke once? Look for tests, lint rules or comments that name the incident they guard against — "regression", "this broke when", a linked issue. On a very young repo with no failures yet, N/A with the age as the reason.
- Status: PASS
- Proof: Repeatedly, and the checks name their incident.
pyproject.toml:454-462keeps ruff's PLW1514 on alone because "We had three separate Windows sandbox regressions in one debug session before adding the explicit encoding."scripts/check-windows-footguns.pyis a blocking CI checker built from Windows-unsafe primitives that bit before (os.kill(pid, 0),os.killpg,signal.SIGKILLwithout a getattr fallback). AGENTS.md:1293-1296 traces theget_hermes_home()rule to "5 bugs fixed in PR #3575"; 598-604 traces the pinning policy to the litellm compromise; 1329-1333 exists because dead code was wired in without E2E proof.supply-chain-audit.yml:88-91links the exact upstream issue its.pthcheck guards against.docs/rca-ssl-cacert-post-git-pull.mdis a full RCA, andsqlite_leak_fix.pngsits in the root as the artefact of another. 357 test files mention a regression. - Recommendation: —
- Priority: Low
Learn more: https://ainativesoftware.engineering/
Nothing here is special to nousresearch/hermes-agent. Install the skill and ask your agent to run it — it answers the same 46 checks and writes the same file into your repo.
npx skills add alfonsograziano/skills --skill aine-readiness-analyzerAI-Native Software Engineering