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, uncomment source_roots in .tears.toml and point it at your importable Python package root.

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
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.
  • 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 Python package discovery.
  • import_rules: optional per-tier import relaxation or restriction.

Current Scope

tears is early. Today, it enforces Python package imports only. The hook can insert or demote headers in many file types, but the scanner currently checks .py files discovered through Python package roots.

Current scanner limitations:

  • tears is a full-repo scan.
  • tears PATH treats PATH as the config/scan root, not as a subpath filter.
  • Target filtering is not implemented yet.
  • Single-file scans such as tears src/foo.py are not implemented.
  • Flat scripts and namespace packages may be missed by the current grimp-backed scanner.

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. Some future scanner expectations are 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.3.0.post1.tar.gz (20.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.3.0.post1-py3-none-any.whl (26.2 kB view details)

Uploaded Python 3

File details

Details for the file tears_cli-0.3.0.post1.tar.gz.

File metadata

  • Download URL: tears_cli-0.3.0.post1.tar.gz
  • Upload date:
  • Size: 20.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.3.0.post1.tar.gz
Algorithm Hash digest
SHA256 bad3d27a5f4b916ead33e6c74f403ccd45952041b99b54115d3d75f08f3dff4e
MD5 672c0b65d62f57fcab7d9bc1bd11d80f
BLAKE2b-256 064453213b006cf8be3020c4e5a6804b42e71a7561b209e3833b3d4348020812

See more details on using hashes here.

Provenance

The following attestation bundles were made for tears_cli-0.3.0.post1.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.3.0.post1-py3-none-any.whl.

File metadata

  • Download URL: tears_cli-0.3.0.post1-py3-none-any.whl
  • Upload date:
  • Size: 26.2 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.3.0.post1-py3-none-any.whl
Algorithm Hash digest
SHA256 8a53cb4e1097fd7cb1e853a741ba0d7115dc352183ed8306a79f30f1aab497be
MD5 1b1e589d9a07417b8aa4719cf92e83d2
BLAKE2b-256 aa7b56e51c91b9bf0ca03a2822c4c78c2896c036a392a1076f461ba29c66734d

See more details on using hashes here.

Provenance

The following attestation bundles were made for tears_cli-0.3.0.post1-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