Static cross-harness rule coherence auditor for AI coding agents (Claude Code, Codex, ...)
Project description
ssoty
English | 한국어
Static cross-harness rule coherence auditor for AI coding agents. A symlink shares files. It does not guarantee the rule is applied the same way.
ssoty reads the rule surfaces of multiple agent harnesses (Claude Code, Codex, …)
and finds — deterministically, with no LLM and no network — where shared rules
silently fail to apply across a harness boundary, then quantifies the per-turn token
cost ("Context Tax").
The problem
Teams symlink one AGENTS.md / CLAUDE.md / rule set into every tool to get a
"single source of truth." But a symlink is a distribution mechanism, not a
coherence mechanism. The same canonical file can:
- load always-on in one harness (injected every turn) but skill-gated in another (loaded only when a skill triggers) — same file, unequal guarantee;
- reference a sibling rule that exists in one harness but was never distributed to the other — a broken pointer across the boundary;
- be duplicated across files, paying token rent every turn.
These are invisible until an agent in harness B quietly ignores a rule you "share."
What ssoty does
$ uvx ssoty audit examples/messy-setup
ssoty audit — 2 Critical, 3 Warning, 6 FYI
[Critical] broken_symlink (claude-code)
.../.claude/rules/broken-link.md
symlink target does not resolve: ./nope.md
[Critical] dangling_cross_ref (codex)
.../.codex/skills/global-agent-rules/references/shared-style.md
references 'team-rules.md', which exists in another harness but is NOT
loaded by 'codex' — broken pointer across the harness boundary
[Warning] load_asymmetry (claude-code+codex)
shared-style.md
same rule loads differently per harness (claude-code=always-on,
codex=skill-gated) — shared file, unequal guarantee
...
[FYI] dangling_cross_ref (codex)
references 'meta-layout.md' (absent here, intentional per .ssotyignore)
It distinguishes a genuine broken cross-reference (Critical) from
intentional non-sharing you declared in .ssotyignore (FYI) — precision over
noise.
Context Tax (reproducible before/after)
$ uvx ssoty metrics examples/messy-setup $ uvx ssoty metrics examples/clean-setup
claude-code: claude-code:
always-on : 206 tokens always-on : 149 tokens (-27.7%)
codex: codex:
skill-gated: 106 tokens skill-gated: 0 tokens
Numbers are reported per harness and never summed across harnesses: always-on
(actual, every turn) and skill-gated (potential, only when a skill fires) are
different load guarantees. Compare within one harness, before vs after a cleanup.
Token counts are a deterministic char/4 heuristic by default (portable — same
numbers on any machine); set SSOTY_EXACT_TOKENS=1 to opt into tiktoken.
Reproduce: uvx ssoty metrics examples/messy-setup (see benchmarks/REPORT.md).
Checks
| Check | Severity | What it catches |
|---|---|---|
broken_symlink |
Critical | symlinked rule whose target is gone |
dangling_cross_ref |
Critical / FYI | a rule references a sibling absent in this harness (FYI if declared intentional) |
load_asymmetry |
Warning | same rule, different load basis per harness |
duplicate_content |
Warning | identical blocks duplicated across files (token rent) |
non_shared_surface |
FYI | a rule present in one harness only |
skill_integrity |
Warning | skill dir without a SKILL.md |
Install
# zero-install run
uvx ssoty audit # audits $HOME (~/.claude, ~/.codex)
# or install
pipx install ssoty
ssoty audit --redact # mask home paths + emails in output
ssoty audit --ci # exit non-zero on any Critical (for CI)
CI (GitHub Action)
- uses: snowlaxc/ssoty@v0
with: { path: . } # runs `ssoty audit --ci`
Harness adapters (optional)
Thin wrappers so you can run ssoty from inside an agent:
- Claude Code: copy
adapters/claude-code/skills/ssotyinto~/.claude/skills/ - Codex: copy
adapters/codex/skills/ssotyinto~/.codex/skills/
The CLI is the product; adapters just shell out to it.
How it works
ssoty resolves each harness's effective rule surface from disk (which files load,
and whether always-on or skill-gated), then runs deterministic checks. No model
calls, no network — same input, same output. It is harness-agnostic by design:
a cross-harness tool shouldn't live inside one harness.
Privacy
ssoty audits your config; its output can quote your rules verbatim. It runs
entirely locally (no hosted service). This repo ships synthetic fixtures
only. See SECURITY.md. Never commit ssoty output to a public repo.
Roadmap (phase 2)
ssoty fix (auto-dedup), opt-in live "canary" runtime probe, LLM semantic
conflict detection, Gemini support, marketplace packaging.
Background
The design rationale lives in docs/RFC.md.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ssoty-0.1.1.tar.gz.
File metadata
- Download URL: ssoty-0.1.1.tar.gz
- Upload date:
- Size: 87.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39cdd7336e3845a90dbc27ac242fba6cf464e9a8e17231e4ed8d8b2ffe5f9dc4
|
|
| MD5 |
74262948888dc074d15d8f817a6952ca
|
|
| BLAKE2b-256 |
136b99ea762d7d31cc471ccb84308f28e415fc21c7c4f7fc0e6a005af9cfc294
|
Provenance
The following attestation bundles were made for ssoty-0.1.1.tar.gz:
Publisher:
release.yml on snowlaxc/ssoty
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ssoty-0.1.1.tar.gz -
Subject digest:
39cdd7336e3845a90dbc27ac242fba6cf464e9a8e17231e4ed8d8b2ffe5f9dc4 - Sigstore transparency entry: 1668914125
- Sigstore integration time:
-
Permalink:
snowlaxc/ssoty@c4615b1539a0e8dd63f42e882348c508f0195a26 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/snowlaxc
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c4615b1539a0e8dd63f42e882348c508f0195a26 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ssoty-0.1.1-py3-none-any.whl.
File metadata
- Download URL: ssoty-0.1.1-py3-none-any.whl
- Upload date:
- Size: 16.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4540e0ddc369856e9d14de4f8a67ee7265f6ce3b284cdac68e5e6ef4610466ce
|
|
| MD5 |
c1deed761e6fcd5e6133fcd613a971fd
|
|
| BLAKE2b-256 |
926f255e62449de91886fcc5b11c082cba253775f2421d95cd5582686bf38371
|
Provenance
The following attestation bundles were made for ssoty-0.1.1-py3-none-any.whl:
Publisher:
release.yml on snowlaxc/ssoty
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ssoty-0.1.1-py3-none-any.whl -
Subject digest:
4540e0ddc369856e9d14de4f8a67ee7265f6ce3b284cdac68e5e6ef4610466ce - Sigstore transparency entry: 1668914477
- Sigstore integration time:
-
Permalink:
snowlaxc/ssoty@c4615b1539a0e8dd63f42e882348c508f0195a26 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/snowlaxc
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c4615b1539a0e8dd63f42e882348c508f0195a26 -
Trigger Event:
push
-
Statement type: