Skip to main content

Tiered Enforcement, Authorship Review System — Vibe Code responsibly.

Project description

Tears

Tiered Enforcement, Authorship Review System.

Vibe-Code Responsibly

Not everything in your repo needs the same level of scrutiny. You should be able to vibe-code a dashboard, prototype a feature, iterate on a script — without giving each one the same ceremony as your auth layer. But they live in the same repo, and right now nothing stops a carelessly imported module from pulling unreviewed code into your most sensitive systems.

tears lets you vibe-code where it's safe and stay careful where it matters. Files declare a trust tier via a @tear header. Agents automatically demote files they touch. Humans restore the tier after review — or don't. CI enforces that trusted code can't depend on untrusted code.

The useful mechanic is the diff:

- # @tear: 1
+ # @tear: 3

If you saw this in your diff and changed it back, you reviewed the code. If you didn't notice, you didn't — and the tier stays where it belongs.

Why

You want to prototype fast. You want to vibe-code that internal tool, iterate on that analytics page, let AI write the first draft of a migration script. You want to ship things that are changing quickly — maybe a POC, maybe an eval, maybe a little dashboard — without treating every file like it's launch-day production code.

But you also want to know that your auth logic, your payment flow, your core business rules haven't quietly started depending on code that nobody actually read.

tears makes both possible at once:

  • Iterate freely on the periphery. Scripts, tools, dashboards, prototypes — leave them at @tear: 3 or @tear: 2. Vibe-code them, change them daily, they don't need a ceremony.
  • Stay rigorous at the core. Auth, payments, security — these stay at @tear: 0. The import rule guarantees they can only depend on equally reviewed code.
  • Know what's what. The tier lives in the file, in source control, in the diff. No separate tracking system, no stale spreadsheet, no guessing.

The tiers aren't a judgment about code quality. Tier 3 code might be perfectly fine. It just hasn't been through the process yet — and until it has, it stays in its lane.

Quick Start

pip install tears-cli
tears init  # create default config
tears       # scan your repo

No more tears!

Adoption Modes

Soft trial mode uses default_tear = 1, so existing headerless files are treated as reviewed while you try the tool. The starter config also includes commented examples for source roots and directory requirements; uncomment and edit them when you are ready to enforce project-specific boundaries.

If the scan checks 0 files, check languages and source_roots in .tears.toml.

Full adoption tags only files that do not already have a deliberate tier:

tears set . --tear 1 --missing-only

Then change default_tear to 3, or remove it and set:

missing_header = "error"

Then set up your agent hook, pre-commit, and GitHub Actions.

Hooks

Hooks demote files after agents edit. They mutate headers; they do not run the scanner. They require uv run python -m tears.hook or the tool-specific wrapper to work from the repo where the edit happens.

Claude Code

Add this to .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "uv run python -m tears.hook"
          }
        ]
      }
    ]
  }
}

The hook reads the edited file path from Claude Code's stdin JSON payload. It runs after Edit, Write, and MultiEdit tool calls. Manual editor changes are not demoted.

Codex

This repo includes a Codex hook config at .codex/config.toml. Place that file in the same path in another repo to enable the hook there.

On startup, Codex will ask whether to enable the hook. Enable it if you want Codex edits made through apply_patch to demote touched files automatically.

The Codex config runs:

uv run python -m tears.codex_hook

The wrapper reads Codex's PostToolUse stdin payload, extracts file paths from the patch, and delegates header mutation to the shared hook logic.

Current Codex hook limitations:

  • it is repo-local rather than a packaged installer;
  • it currently handles apply_patch edits only;
  • Codex prompts to enable the hook on startup, so a user must opt in before it runs.

OpenCode

This repo includes an OpenCode plugin at .opencode/plugins/tears-hook.js. Place that file in the same path in another repo to enable the hook there.

The plugin listens for edit, write, and apply_patch tool calls and passes edited file paths to:

uv run python -m tears.hook FILE

Current OpenCode plugin limitation:

  • it is repo-local rather than a packaged installer.

Pre-commit and CI

Pre-commit

Add this to .pre-commit-config.yaml:

repos:
  - repo: https://github.com/Thillel/tears
    rev: v0.1.0
    hooks:
      - id: tears

The published hook intentionally ignores filenames and runs a full repo scan. This matches the current scanner model.

GitHub Actions

Use the bundled action in a workflow:

name: tears

on:
  pull_request:
  push:
    branches: [main]

jobs:
  tears:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: Thillel/tears/.github/actions@v0.1.0
        with:
          path: .

The GitHub Action accepts a path input. With the current scanner, that path is the scan root, not a subpath filter.

Tear Levels

Tear Meaning
0 Deeply reviewed. Security-critical or domain-owner reviewed.
1 Reviewed by a human, line by line.
2 Eyeballed for exfiltration, network calls, and obvious security issues.
3 Vibe Coded.

Lower numbers are more trusted.

Rules

By default, a file may import from files at its own tier or a more trusted tier:

Importer May import
0 0
1 0, 1
2 0, 1, 2
3 0, 1, 2, 3

Directory requirements can require sensitive paths to stay at a higher trust tier.

Header Format

Python files use:

# @tear: 1

Other supported hook insertion styles include:

// @tear: 1
<!-- @tear: 1 -->

Only Python files are mechanically scanned in the current release.

Commands

tears                            # scan the repo
tears down FILE_OR_DIR --tear 1  # promote: more trusted
tears up FILE_OR_DIR --tear 3    # demote: less trusted
tears set FILE_OR_DIR --tear 2   # exact level
tears set . --tear 1 --missing-only

--missing-only is available for up, down, and set; it tags only files that lack an existing @tear header.

Configuration

tears reads .tears.toml from the scan root.

# @tear: 3
max_tear = 3
missing_header = "warn"
respect_gitignore = true
languages = ["python"]
exclude = ["tests/scan/fixtures/**", "**/*.generated.py"]
default_tear = 3

[default_tears]
"tests" = 3

[directory_requirements]
"src/auth" = 0
"src/api" = 1

[artificial_tears]
"tests/unit" = 3

[imports]
source_roots = ["src"]

[scan]
exclude = ["fixtures/**"]

[mutate]
exclude = ["vendor/**"]

[import_rules]
"1" = 2

Config fields:

  • max_tear: highest tier number. Defaults to 3.
  • missing_header: warn or error. Defaults to warn.
  • languages: languages to scan. Supports c, cpp, csharp, dart, go, java, javascript, kotlin, php, python, ruby, rust, and typescript. Defaults to ["python"].
  • exclude: glob patterns ignored by scanner and hook.
  • scan.exclude: additional glob patterns ignored only by scanner.
  • mutate.exclude: additional glob patterns ignored only by hooks and mutation commands (set, up, and down).
  • respect_gitignore: whether gitignored paths are skipped. Defaults to true.
  • scan.respect_gitignore: scanner-specific override for respect_gitignore.
  • mutate.respect_gitignore: hook and mutation-command override for respect_gitignore.
  • default_tear: tier to assume for headerless files without warning.
  • default_tears: path-specific defaults for headerless files.
  • directory_requirements: path-specific maximum allowed tier.
  • artificial_tears: path-specific import budgets. Matching files may import targets up to the configured tier regardless of their own reviewedness tier.
  • imports.source_roots: roots used for source discovery.
  • import_rules: optional per-tier import relaxation or restriction.

Current Scope

Scanner:

  • Python is enabled by default.
  • C, C++, C#, Dart, Go, Java, JavaScript, Kotlin, PHP, Ruby, Rust, and TypeScript can be enabled with languages.
  • Import resolution is local and conservative; package aliases and build metadata are not modeled yet.

Agent hooks:

  • Claude Code, Codex, and OpenCode hooks can auto-demote files after AI edits.

Path behavior:

  • tears PATH treats PATH as the config/scan root, not as a subpath filter.
  • Target filtering and single-file scans are not implemented yet.

See DESIGN.md for the design rationale and roadmap.md for planned fixes.

Development

git clone https://github.com/Thillel/tears
cd tears
uv sync
make check
make test

make check runs formatting, linting, strict type checking, a tears . self-scan, and tests.

Test Dogfooding

Real test code dogfoods tears; this repo uses [artificial_tears] where tests need to import lower-trust implementation files. Scan fixtures under tests/scan/fixtures/<suite>/<fixture>/ are linter input data, so they are excluded and may contain deliberate missing headers, unusual tear values, and future expectations. Future scanner expectations may be recorded as strict-xfailed fixtures.

License

MIT. See LICENSE.

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

tears_cli-0.4.0.tar.gz (26.3 kB view details)

Uploaded Source

Built Distribution

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

tears_cli-0.4.0-py3-none-any.whl (35.5 kB view details)

Uploaded Python 3

File details

Details for the file tears_cli-0.4.0.tar.gz.

File metadata

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

File hashes

Hashes for tears_cli-0.4.0.tar.gz
Algorithm Hash digest
SHA256 f8b8c4d0e13e88e1710800103470ddf30c23b7d064f33213338b033aa5690715
MD5 ef09eece5200dbaa875dc8438ec5e13f
BLAKE2b-256 62a8143235fa3d07e4bff5c1ebceefe1e600689e009c09abf718eae237a5b3c6

See more details on using hashes here.

Provenance

The following attestation bundles were made for tears_cli-0.4.0.tar.gz:

Publisher: publish.yml on Thillel/tears

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

File details

Details for the file tears_cli-0.4.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for tears_cli-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e15557cdfe7b30f57568e9ab3584e36aaae4ba8bd526f1d79c91721715d831d7
MD5 644254e5104aaf6d9f8b8957dac617dd
BLAKE2b-256 8b0f3ab425ea232af56513b39eb0febe7f96bcdf5c7a8a31bb2523e0fc88cf61

See more details on using hashes here.

Provenance

The following attestation bundles were made for tears_cli-0.4.0-py3-none-any.whl:

Publisher: publish.yml on Thillel/tears

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