Skip to main content

Static cross-harness rule coherence auditor for AI coding agents (Claude Code, Codex, ...)

Project description

ssoty

English | 한국어

CI License: MIT

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 use tiktoken when installed, otherwise a clearly-labelled char/4 heuristic.

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

Status: pre-release. Not on PyPI yet — until v0.1.0 is published, install from GitHub: uvx --from git+https://github.com/snowlaxc/ssoty ssoty audit. The commands below work once published (see RELEASING.md).

# 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/ssoty into ~/.claude/skills/
  • Codex: copy adapters/codex/skills/ssoty into ~/.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

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ssoty-0.1.0.tar.gz (85.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ssoty-0.1.0-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

Details for the file ssoty-0.1.0.tar.gz.

File metadata

  • Download URL: ssoty-0.1.0.tar.gz
  • Upload date:
  • Size: 85.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ssoty-0.1.0.tar.gz
Algorithm Hash digest
SHA256 54082436875b570c5c9ea8d85325847e8a2ace1c7a936cad72bf990c210ce04a
MD5 5d1bff848c242cb42bc8e190dd613eff
BLAKE2b-256 70833d27d743a9c779334c8fcb498331567c095d4489e28971797fc382cbc187

See more details on using hashes here.

Provenance

The following attestation bundles were made for ssoty-0.1.0.tar.gz:

Publisher: release.yml on snowlaxc/ssoty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ssoty-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ssoty-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ssoty-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a8c1b75fdbbc6e674d4476a4335208479f0b9b53890b7ace2ee557cd8ac0909b
MD5 efb964eee037d93930b93949aa6e3a36
BLAKE2b-256 a4426cb6bb624480c377c00330bb18182e9182fd87e49f31f408d14acd505b69

See more details on using hashes here.

Provenance

The following attestation bundles were made for ssoty-0.1.0-py3-none-any.whl:

Publisher: release.yml on snowlaxc/ssoty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page