agent-signage
Road signs for coding agents. One true fact, delivered at the moment your agent acts — and silence the rest of the time.
Contents
- What it does
- Install
- Why this instead of a rule in your prompt file
- The signs
- Guarantees
- What it costs
- When it stays quiet
- Is it working?
- Acknowledging
- Configuration
- Adding your own sign
- Operational cards
- Publication boundary
- Verify it yourself
- Status and limitations
- Research
- License
What it does
When a coding agent edits a file, nothing in its tool loop errors if the checkout is stale, a symlink points outside the repo, or another worktree is mid-edit on the same path — the edit just lands, clean and wrong.
agent-signage is a PreToolUse hook that closes that gap: a Claude Code hook today, and, being
a plain subprocess, usable in any agent harness that can shell out before a file operation.
Before a Read, Edit, Write, Grep, or Glob call runs, it checks measurable facts about the git
state of the file about to be touched and injects one short line into the agent's context if
something matters. The rest of the time it says nothing.
Your agent opens a repo and starts fixing things. The checkout is on a stale branch, 26 commits behind the branch you actually deploy. Every edit is correct, well-tested, and applied to a version nobody can see. Nothing errors. Nothing warns. You find out later, if you find out at all.
agent-signage puts a sign on that road:
STALE CHECKOUT - hermes-labs-v2 is 27 commit(s) behind origin/main (upstream tip 31 minutes
ago). This working copy may not be what is deployed; confirm which source is authoritative.
Inspect: git -C /Users/you/dev/hermes-labs-v2 log --oneline HEAD..@{u}
Every number in that line comes from a ref already on disk, so when the last fetch is old the sign says so rather than going quiet:
STALE CHECKOUT - example-app is 8 commit(s) behind origin/main and 6 ahead from cached
origin/main at a1b2c3d4e5f6 (FETCH_HEAD was 4 days old when checked); the current gap is
unmeasured. This working copy may not be what is deployed; confirm
which source is authoritative.
Inspect: git -C /path/to/example-app fetch && git -C /path/to/example-app log --oneline HEAD..@{u}
That text reaches the model alongside the tool result, at the moment it touches the file. Not at the start of the session, not in a config file it read twenty turns ago.
Install
Requires Python 3.9+ and git on PATH. No package dependencies.
pip install agent-signage
Or from source:
pip install git+https://github.com/hermes-labs-ai/agent-signage.git
Claude Code
agent-signage install # writes the hook entry for you
Or install the self-contained Claude Code plugin from the Hermes Labs
marketplace. The plugin bundles its dependency-free runtime, so this path does
not require a separate pip install:
claude plugin marketplace add hermes-labs-ai/agent-signage
claude plugin install agent-signage@hermes-labs
Or add it yourself to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Edit|Write|NotebookEdit|Grep|Glob",
"hooks": [{"type": "command", "command": "python3 -m agent_signage", "timeout": 8}]
}
]
}
}
Any other harness
It is a subprocess that reads JSON on stdin and writes JSON or nothing on stdout:
echo '{"session_id":"abc","tool_input":{"file_path":"/path/to/file.py"}}' | python3 -m agent_signage
Empty output means "nothing to say". Exit code is always 0. A full runnable example (no Claude
Code needed) is in examples/README.md; harness maintainers wiring this in permanently should
read docs/integrating.md.
Why this instead of a rule in your prompt file
Standing rules in a prompt file are a blunt instrument for context engineering: they are read once at the start of a session, then have to survive dozens of turns of unrelated work before the one moment they were written for actually arrives — and often they don't.
That is the first of two costs, and the second is the reason this project exists.
It doesn't fire when you need it. The failure is not a knowledge gap — your agent already knows that a local checkout can diverge from what's deployed. It just has no reason to form that hypothesis at turn fifteen, mid-task, when nothing in front of it looks wrong. A rule it read at turn zero is competing with everything that has happened since.
It conditions every other task too. A standing instruction is in context for every request, including the ones it has nothing to do with. It is attended to while the model is writing a migration, reviewing a diff, or answering a question about documentation — narrowing how it interprets and what it generates in all of them. A constraint written for one situation becomes a standing bias on every situation. Add enough of them and you have quietly traded general capability for a set of reflexes, most of which are irrelevant most of the time.
A sign is present only while the action that needs it is happening. The rest of the time the context is exactly as it would have been if this tool were not installed — which is the point. Constrain the model where the constraint is load-bearing, and leave it alone everywhere else. Anthropic calls this shape just-in-time context: retrieve the fact at the moment of need rather than pre-loading everything that might matter.
This is a design argument, not a measured result. The token cost is measurable and small; the conditioning cost is not something this project has quantified.
The signs
| Sign | Reports | Fires on |
|---|---|---|
stale_checkout |
the repo is N commits behind (and M ahead of) its upstream | read + write |
symlink_escape |
the path resolves through a symlink to outside the repo | read + write |
conflict_markers |
the file still contains unresolved <<<<<<< markers |
read + write |
concurrent_worktree_edit |
another worktree has uncommitted changes to this same file | write |
binary_edit |
the file contains NUL bytes and a text edit will corrupt it | write |
generated_file |
the file declares itself machine-generated in its header | write |
Each was selected against a documented failure report rather than invented; the evidence is
cited in the docstring of each sign in src/agent_signage/more_signs.py. Signs whose only
consequence is "what you are about to write will go wrong" fire on writes only — firing them
on a read would be true but useless, and a true-but-useless sign is how a tool like this gets
muted.
Guarantees
These are asserted by the test suite. If any regresses, CI fails. The reasoning behind each one is in docs/design.md.
| Property | Guarantee |
|---|---|
| Sound | Every sign reports a measurement, never an inference. When it speaks, the stated fact is true. |
| Non-blocking | It never emits a block decision and never exits non-zero. It cannot stop a tool call. |
| Fails open | Malformed input, missing git, unwritable state, hung subprocess — all end in silence and exit 0. |
| Bounded | A 3 s deadline is checked before each sign starts and inside the only scan that grows with the repository, and every individual git call is capped at 2 s. Worst case is therefore one in-flight git call past the deadline. |
| No network while measuring | No sign's measurement contacts the network. The one network call this tool makes is a detached background git fetch, spawned at most once per repo per 2 minutes and never awaited; AGENT_SIGNAGE_NO_FETCH=1 turns it off entirely. |
| Zero dependencies | Python 3.9+ standard library only. |
| Quiet | Each sign speaks once per file per session per observed state. Nothing at all when nothing is wrong. |
A note on two of these, because both were overstated before 0.1.2. "Bounded" previously
claimed a deadline capped every invocation; it was checked only after every sign had already
run, so it suppressed output rather than stopping work. And the no-network test patched
subprocess.run while the only call that reaches the network goes through subprocess.Popen,
so it asserted over a path that had nothing to find. Both the code and the claims were
corrected rather than one or the other.
What it costs
Measured end-to-end as a subprocess — what your harness actually pays per tool call — on
macOS/arm64 with CPython 3.14, p50 of 30 runs. Numbers live in evals/metrics-0.1.2.json.
Measure it on your own machine and your own repo with agent-signage doctor.
| Case | Cost |
|---|---|
Bare interpreter floor (python -c pass) |
~17 ms |
| Silent — not a repo, or a vendored path | ~35 ms |
| Read inside a repo — any number of worktrees | ~76 ms |
| Edit inside a repo with no other worktree | ~84 ms |
| Edit inside a repo with 22 other worktrees | ~330 ms |
The last row is the one to know about, and the 0.1.0 and 0.1.1 tables did not show it because
they were measured on a small repository. concurrent_worktree_edit runs one git status per
sibling worktree, so a write costs roughly 84 ms + ~11 ms per other worktree. Reads are
unaffected — that sign only fires on write-shaped tools — so the cost scales with how parallel
your setup is, on exactly the events where a lost edit is the risk.
The loop is deliberately not capped at some number of worktrees: that would quietly cut coverage for the people running the most parallel agents, who are the ones the sign exists for. It stops at the deadline instead, and says "at least N" when it did not finish looking.
Silence is otherwise the common case. Argparse and the git layer load lazily, a parent-directory
walk rules out non-repos before git is spawned, and the six signs share memoised git answers
for the duration of one evaluation — which took an in-repo edit from 119 ms to 84 ms.
If that is too much for your harness, call it on a subset of events; first-touch-per-directory still catches the failures it targets.
When it stays quiet
Silence is the default and the common case. It deliberately says nothing when:
- the repo is current, has no upstream, or is not a git repo at all
- you are behind on purpose — mid-bisect, detached HEAD, or an in-progress rebase, merge, or cherry-pick
- the path is vendored or generated (
node_modules,vendor,.venv,dist,build, …) - your agent already fetched during this session, so it has current knowledge
- it already told you this exact fact about this file in this session
- you acknowledged it (see below)
An old fetch is deliberately not on that list any more. Up to 0.1.1 it was, and it was the single biggest source of missed drift: the sign returned before it had even asked how far behind the repo was. It now reports the reading and dates it.
Silence never means "verified current." It means "no drift known." That distinction is what keeps the tool honest: it can miss drift, but it cannot invent it.
Is it working?
Silence is the design, which makes a broken install look exactly like a clean repo. doctor
is the difference:
agent-signage doctor # this repo
agent-signage doctor path/to/file # a specific file
It reports whether a hook entry naming this package exists in any settings file Claude Code reads, what git says about the repository right now, and — for each of the six signs — whether it speaks here or the measured reason it does not:
signs
stale_checkout SPEAKS
STALE CHECKOUT - lintlang is 5 commit(s) behind origin/main …
symlink_escape quiet cli.py is not reached through a symlink
conflict_markers quiet no conflict markers in the first 8KB of cli.py
concurrent_worktree_edit quiet 16 other worktree(s), none holding uncommitted changes to cli.py
binary_edit quiet cli.py has no NUL bytes
generated_file quiet cli.py declares no generator in its first 5 lines
no upstream is configured for main — nothing to compare against is the answer worth knowing:
it means stale_checkout is inert in that repo and no amount of drift will produce a sign.
doctor exits non-zero only when it finds no hook entry, and it has no side effects — it
writes no stamps and never fetches.
Acknowledging
agent-signage ack /path/to/repo stale_checkout "origin/main@a1b2c3d4e5f6:27"
agent-signage doctor path/to/repo prints the current acknowledgement token.
The acknowledgement is bound to the observed state, not to the repository. If upstream
moves, the key no longer matches and the sign speaks again — so "stop telling me" can never
suppress genuinely new information.
Configuration
| Variable | Default | Purpose |
|---|---|---|
AGENT_SIGNAGE_IGNORE |
— | PATH-separated repos to skip entirely |
AGENT_SIGNAGE_STATE_DIR |
system temp | Where stamps live |
AGENT_SIGNAGE_SESSION_START |
— | Unix timestamp of session start; enables "already fetched this session" suppression |
AGENT_SIGNAGE_NO_FETCH |
— | Set to disable the background refresh. Does not make the tool quieter: the reading already on disk is still reported, still dated. For metered connections, CI runners with no credentials for the remote, or anywhere a surprise subprocess is unwelcome. |
Adding your own sign
A sign is a function that returns a Sign or None. Register it and it runs:
from agent_signage import signs
@signs.register
def my_sign(ctx):
if not_worth_saying:
return None
return signs.Sign(id="my_sign", text="...", state_token="...", repo=root)
A sign must be sound (report a measurement, never an inference), silent (produce nothing when there is nothing to say), and actionable (end in the command that resolves it). Anything that cannot meet all three is not a sign.
Operational cards
Some facts only become useful after another tool has classified an action boundary. For example, a release gate may know that one exact command would publish to a public repository. The checkout-local renderer gives that caller a small, consistent way to present a trusted local card at that moment:
python3 scripts/render.py \
--card /absolute/path/to/card.json \
--format text \
--context "owner/repository version"
A card is a JSON object with exactly four nonempty, one-line string fields:
{
"id": "release.authorization",
"headline": "PUBLIC RELEASE",
"fact": "The release gate classified this command as a public release boundary.",
"next": "Check current authorization before continuing."
}
Use --format text when a caller needs the rendered line, or --format hook for the standard
Claude Code PreToolUse additionalContext envelope. Optional --context is bounded and JSON
quoted as target data; it is never interpolated into the card. Card fields and total output are
also bounded, and malformed cards make the command fail with no stdout so callers can use a
known fallback.
The renderer does not inspect shell commands, decide whether a card applies, grant approval, or allow or block an action. Keep cards in a trusted local configuration path. The caller that already identifies the boundary owns its facts, authorization checks, enforcement, and fallback. This keeps operational guidance timely without turning agent-signage into a policy engine.
Reliability Lab result envelopes
The same operational-card renderer can turn another Hermes Reliability Lab
product's own hermes.reliability-lab.result/1 result into a completion
card, or into nothing:
python3 -m agent_signage.evidence \
--source that-product/evidence.json \
--id release.checks --headline "GATE PASSED" --next "Continue." \
--fact-label "checks passed" --fact-value "2 of 2"
The trust rule: a card is licensed only by an envelope whose mode is
executed (a real run, not a preview) and whose status is pass. A
warn/unknown/fail status, a preview, a wrong-schema file, or a status
that disagrees with its own findings all license no card — reported as
such, never guessed past. The caller declares the one measured fact worth
stating (a precise label and a value already computed from that other
product's own data); this module does not interpret what a "test count"
means for a product it did not write, only whether the evidence is
genuinely completed and the card fits the same bounds every card here does.
--source, --id, --headline, and --next are required; --fact-label
and --fact-value are optional and must be given together.
Publication boundary
Everything above is passive. The hook cannot block, cannot exit non-zero, and goes quiet on every error — that contract is what makes it safe in front of every file operation, and it is exactly why it cannot carry a publication requirement. A mechanism that says nothing when it breaks is not a gate.
So the boundary is separate, and it has two halves. One owns execution, so what was checked
and what was sent are the same bytes by construction. The other is a PreToolUse Bash adapter
that stops an agent reaching gh around it.
The publisher
python3 scripts/publish.py pr-create \
--body-file /abs/path/to/pr-body.md \
--target owner/repo --title "feat: ..." \
--kind contribution --oversight active
It runs gh itself, in a fixed order that a caller cannot reassemble wrongly:
- Open the body once, on a bounded, non-blocking file descriptor —
O_NOFOLLOWrefuses a symlink,O_NONBLOCKprevents a FIFO from hanging the preflight,fstatchecks the opened object, and a capped read rejects oversized input. - Check that snapshot. Not the file — the snapshot. There is no second read.
- For
pr-edit, read the live body and require every recognized human/upstream disclosure trailer in that snapshot to remain. Project-specific lines can be bound with--preserve. - Emit the action-time sign, before any mutating child process exists.
- Run
ghwith an argv list, never a shell string, and always--body-file -, handing the snapshot bytes to its stdin.ghis never given the path, so it cannot re-read a file that changed after step 2. - Read the body back with
gh pr view --json bodyand require exact equality.
Success is claimed only after step 6.
| Exit | Meaning |
|---|---|
0 |
Published, and the body read back byte for byte |
1 |
Artifact rejected — no mutating gh child was started; edit preservation may make one read-only view call |
2 |
Input or usage rejected — no gh child was started |
3 |
A gh child failed; its exit status and stderr are reported, not swallowed. On pr-edit a failed read-only pre-read exits here too, and no update was attempted |
4 |
gh succeeded but the published body could not be verified as the checked bytes |
Exits 3 and 4 say plainly what is true: the pull request may exist, nothing was reverted, and this is not a successful publication.
Supported operations are exactly pr-create and pr-edit. pr-comment was in an
earlier draft and is gone — an operation nobody had exercised end to end was scope, not
coverage.
The Bash boundary adapter
The publisher only owns the path that goes through it. scripts/gate.py is a separate
PreToolUse hook for the Bash tool that denies a scoped gh pr create/gh pr new or a
body-mutating gh pr edit call, and names the publisher instead:
$ echo '{"tool_name":"Bash","tool_input":{"command":"gh pr create --repo hermes-labs-ai/example --title t"}}' \
| python3 scripts/gate.py
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny", ...}}
$ echo '{"tool_name":"Bash","tool_input":{"command":"gh pr view 12"}}' | python3 scripts/gate.py
$ # empty: not its business
It never executes the command it judges. The string is lexed as data; physical lines, shell
continuations, heredoc data, wrappers, control-flow prefixes, and command substitutions are
handled explicitly. When --repo is absent, two bounded read-only git calls with fixed
argvs (git rev-parse --show-toplevel, then git remote get-url origin) establish work context. An untokenisable
guarded shape is denied only when that enclosing work context is in scope.
Scope is deliberately narrow. A call is covered when it originates in a hermes-labs-ai/*
checkout, the Hermes infrastructure checkout used for upstream contributions, or explicitly
targets hermes-labs-ai/*. A personal/non-Hermes source targeting a non-Hermes repository is
silent. Metadata-only gh pr edit, read-only/help commands, shell comments and literal heredoc
data, other gh nouns, and unrelated commands are also silent.
For Codex, install it additively with agent-signage install-publication-gate; for Claude Code,
use the equivalent settings entry below. See Wire the adapter.
Attribution
<!-- hermes-labs:attribution v1 -->
[Rolando Bosch](https://github.com/roli-lpci) is the responsible human contributor and provided
active oversight and steering. This contribution was selected through
[Hermes Labs](https://hermes-labs.ai)’ autonomous triage and executed through its engineering
infrastructure.
<!-- /hermes-labs:attribution -->
Generate it rather than typing it, so the wording has one source of truth:
python3 -m agent_signage attribution --kind contribution --oversight active
python3 -m agent_signage attribution --kind review --oversight none
The role noun is adapted to the work: responsible human contributor for a contribution, responsible human reviewer for a review. The oversight clause appears only when it was declared. The possessive follows the linked organization name in "Hermes Labs’ autonomous triage", making the trailing "its" refer to Hermes Labs rather than to the contribution.
The block is delimited, so "exactly one attribution" is checkable. Line wrapping is allowed;
rewording is not. A block inside fenced or indented code, a multiline backtick span, raw
pre/code-like HTML, or an enclosing HTML comment is rejected, because a statement nobody
sees as ordinary prose is not a disclosure.
Disclosure lines named with --preserve must still be present, so an agent rewriting a body
cannot quietly delete someone else's.
--kind and --oversight are caller declarations
Stated plainly, because an earlier revision overstated it. Neither flag is verified by anything here. An earlier draft read them from an unsigned local JSON sidecar and called it an "attestation", which was worse than useless: ceremony that looked like verification, while anything able to write the body could write the sidecar. Renaming it would not have fixed the overclaim, so the sidecar is gone.
What remains is the part that actually works: --oversight has no default. Claiming that a
human provided active oversight and steering is the strongest statement this tool will publish
about a person, so it is always an explicit, recorded choice by whoever ran the command, and
the artifact may never state more than was declared. The sign printed at the moment of action
says "declared oversight", not "verified oversight", for the same reason.
The checker checks the block, not your prose. An earlier revision swept the whole body for phrases like "approved by" and "I reviewed"; an independent review was right that this rejects a maintainer's own true statement, misses any paraphrase of a false one, and buys the feeling of rigour rather than rigour. It is gone.
Recovery
| Reason code | Fix |
|---|---|
attribution-missing, attribution-duplicated |
Regenerate with agent-signage attribution; keep exactly one block |
attribution-hidden |
Move the block out of the code fence or HTML comment wrapping it |
attribution-wording-mismatch, attribution-kind-mismatch |
Replace the block with generated text for the right --kind |
oversight-claimed-beyond-declaration |
Either pass --oversight active, or emit the block with --oversight none |
oversight-declared-but-not-stated |
Emit the block with --oversight active |
contributor-mismatch |
The body names someone the declaration does not |
disclosure-dropped |
Restore the disclosure line the rewrite removed |
attribution-malformed |
Remove the control or bidi character from the block |
A local attribution rejection has no effect of any kind: no child is started. On pr-edit, a
candidate that passes locally is followed by a read-only live-body check; dropping a recognized
disclosure then rejects with only that gh pr view call and no update.
Wire the adapter
Codex has an additive installer for its user-level hook file:
agent-signage install-publication-gate
It preserves every existing hook group, creates a timestamped backup, writes atomically, and is
idempotent. Restart Codex, open /hooks, and review and trust the new hook definition; Codex
does not run a new non-managed hook before that trust step. The installer prints the exact
backup or removal recovery path. The configuration is not retroactive: an already-running task
that loaded hooks before installation remains uncovered until that restart and trust step.
The equivalent Claude Code entry can be added to the settings file that owns the session:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 /abs/path/to/agent-signage/scripts/gate.py",
"timeout": 5
}
]
}
]
}
}
That entry is additive: it does not touch the existing Read|Edit|Write|NotebookEdit|Grep|Glob
entry that runs the passive hook, and the two never share a process. Until the adapter is both
installed and active in the harness, gh pr create remains reachable from Bash and the
publisher is a convention, not a boundary.
What this does not give you
- Only the Bash tool, and only once wired and active. A harness that reaches GitHub through the REST
API, a browser session, an MCP server, or its own built-in PR tool never produces a Bash
command, so this adapter never sees it. Codex and Claude Code are covered only when their
respective
PreToolUseentry is active; Cursor, Aider, and other harnesses remain uncovered. - A
PreToolUsedeny is a harness-level decision, not an OS one. Anything that can spawn a process outside the harness's tool loop — a Makefile target, a CI job, a shell the user opens themselves — is outside it. --oversightis a declaration. See above. It records who claimed what; it does not establish that a person read anything.- Exact readback is exact. If GitHub ever normalises a body — line endings, trailing whitespace — the publisher reports a mismatch and exits 4 rather than accepting the difference. Keep bodies LF-only. This is the conservative direction on purpose, but it means a mismatch is not automatically a security event; read the two digests it prints.
- It checks the artifact, not the work. A body can carry a perfectly true attribution and describe a change nobody should merge.
- Edit preservation is snapshot-bound.
pr-editautomatically binds conventional trailers such asDisclosure:,Co-Authored-By:, andSigned-off-by:from the live body it reads; use--preservefor project-specific wording.gh pr editexposes no conditional revision token, so a concurrent body edit after that pre-read remains a race. Exact post-write readback proves what this publisher wrote, not that no one raced it. - Nothing in this repository enforces the boundary on itself.
Verify it yourself
agent-signage selftest # asserts the runtime guarantees, no repo needed
agent-signage doctor # what is live, what is inert, and what it costs here
pytest # full behavioural suite over real synthetic git repos
pytest tests/test_publication.py # the publisher and the Bash adapter, against a fake gh
The exact-commit deterministic readback for 0.1.2, including a safe isolated
hook demonstration, is in evals/proof-0.1.2.json.
The live Codex installation probe for this boundary, including the intentionally harmless
current-session non-enforcement observation, is in
evals/codex-hook-installation.json.
Status and limitations
0.2.0 — early, and honest about it. Six signs, tested over real synthetic git repositories,
in production use at Hermes Labs. The sign registry is stable and extensible.
Limits worth knowing before you adopt:
- Every count is as of the last fetch.
stale_checkoutnever contacts the network while measuring, so what it reports is what the remote-tracking ref on disk says. When that ref is old the sign says so and calls the present gap unmeasured — the true gap can be larger if upstream advanced, or smaller if upstream was rewound. It can undercount drift. It cannot invent it. - Bash-invoked edits are invisible.
cat,sed -i, or a shell script carry no tool path, so nothing is checked. - Git only. A stale deployed API, database, or service is out of scope.
- Writes get slower as your worktree count grows — about 11 ms per sibling worktree. Reads do not. See What it costs.
- The guarantees are self-attested. They are asserted by this repository's own test suite, which is a real bar but not an independent one. Nobody outside the project has exercised it adversarially yet. Read the tests — they are the specification. Two guarantees published in 0.1.0 and 0.1.1 did not hold as stated; both were found by auditing the shipped artifact against its own README, and both are corrected in 0.1.2. That is the honest track record.
- The publication boundary is only as good as its active wiring. The publisher owns
execution and fails closed, which the passive file hook cannot. The separate Bash adapter
stops direct
gh pr createandgh pr editcalls only after the harness has loaded it (and, in Codex, the user has trusted it). API, browser, and other non-Bash paths are uncovered.--kindand--oversightare caller declarations, not verified facts. - The false-positive evidence is thin. 0/12 and 0/8 on small corpora whose controls were arguably incapable of firing. That is direction, not a result.
Research
agent-signage comes out of Hermes Labs, an AI reliability
engineering studio. The research behind the wider programme — on how AI systems lose meaning,
misreport their own state, and fail in ways standard evaluations miss — is published with DOIs
at hermes-labs.ai/research.
The one most directly adjacent to this tool is Precise Records, Unstable Meanings (10.5281/zenodo.21652317), a measurement-validity audit separating what agent telemetry can actually establish from what gets claimed on top of it. That distinction — report the measurement, not the inference — is the rule every sign here has to satisfy.
License
Apache-2.0 · Hermes Labs
Metadata
Release files for agent-signage 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agent_signage-0.2.0.tar.gz | 195.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agent_signage-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 273.3 kB
Release files / agent_signage-0.2.0.tar.gz
| Download URL | agent_signage-0.2.0.tar.gz |
|---|---|
| Size | 195.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6660c4a1367a4ebcd39a15966b0eb2d4b87de4418f292fc4f7c67927d4513ddb
|
|
BLAKE2b-256 checksum How to use checksums |
31179f6c26eef4fa22aafb23bf0c4fb7f1595f05b6d67fad4e93be48307495dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency logRelease files / agent_signage-0.2.0-py3-none-any.whl
| Download URL | agent_signage-0.2.0-py3-none-any.whl |
|---|---|
| Size | 77.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3efdfb30290d8148545532dca0e7717dffda35706f15ee4d80148a9bfe9d255f
|
|
BLAKE2b-256 checksum How to use checksums |
f6d04aeda14341207f83137ba5e990aae38c4d4e0d57480e551070315bc12f34
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency log