{"id":49725811,"url":"https://github.com/b7s/catraca","last_synced_at":"2026-05-18T04:16:37.885Z","repository":{"id":356624522,"uuid":"1233349066","full_name":"b7s/catraca","owner":"b7s","description":"PHP Quality Guardian that enforces the Catraca (ratchet) principle: quality metrics can only improve or stay the same, never regress.","archived":false,"fork":false,"pushed_at":"2026-05-08T22:40:38.000Z","size":378,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-09T00:26:08.416Z","etag":null,"topics":["analysis","analytics","babysit","code","laravel","php","quality","ratchet"],"latest_commit_sha":null,"homepage":"https://brunots.dev/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/b7s.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-08T21:35:00.000Z","updated_at":"2026-05-08T22:40:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/b7s/catraca","commit_stats":null,"previous_names":["b7s/catraca"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/b7s/catraca","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b7s%2Fcatraca","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b7s%2Fcatraca/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b7s%2Fcatraca/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b7s%2Fcatraca/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/b7s","download_url":"https://codeload.github.com/b7s/catraca/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b7s%2Fcatraca/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32806692,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"online","status_checked_at":"2026-05-09T02:00:06.633Z","response_time":123,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["analysis","analytics","babysit","code","laravel","php","quality","ratchet"],"created_at":"2026-05-09T04:02:50.487Z","updated_at":"2026-05-18T04:16:37.877Z","avatar_url":"https://github.com/b7s.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003cimg src=\"logo.webp\" width=\"256\" alt=\"Catraca Logo\"\u003e\n\u003c/div\u003e\n\n# Catraca\n\nPHP Quality Guardian that enforces the **Catraca (ratchet) principle**: quality metrics can only improve or stay the same, never regress.\n\n\u003e **Catraca** (Portuguese for \"turnstile\" / \"ratchet\") — like a turnstile at a subway station, quality can only move forward.\n\n## Install\n\n```bash\ncomposer require --dev b7s/catraca\n```\n\n## Dependencies\n\nCatraca wraps your existing PHP quality tools. Install the ones you need:\n\n```bash\n# Code style\ncomposer require --dev laravel/pint\n# or\ncomposer require --dev friendsofphp/php-cs-fixer\n\n# Static analysis\ncomposer require --dev phpstan/phpstan\n# or\ncomposer require --dev vimeo/psalm\n\n# Test coverage\ncomposer require --dev phpunit/phpunit\n# or\ncomposer require --dev pestphp/pest\n\n# Duplication detection\n# PHP 8.3\ncomposer require --dev systemsdk/phpcpd:^8.0\n# PHP 8.4+\ncomposer require --dev systemsdk/phpcpd:^9.0\n\n# Cyclomatic complexity\ncomposer require --dev phpmetrics/phpmetrics\n```\n\nAny tool not installed is **skipped** (not failed). Security audit uses `composer audit` (built-in).\n\n## Usage\n\n### `catraca init` — Initialize baseline\n\nCreates `catraca_baseline.json` in your project root with default thresholds:\n\n```bash\nvendor/bin/catraca init\n```\n\nDefault baseline:\n\n| Setting | Default |\n|---------|---------|\n| Source Dirs | `[\"src\", \"app\", \"lib\"]` |\n| Code Style | 0 violations |\n| Static Analysis | 0 errors (level 5 if no `phpstan.neon`) |\n| Test Coverage | 85% minimum |\n| Duplication | 2% maximum, min 3 lines, min 30 tokens |\n| File Size | 1000 lines maximum per file |\n| Complexity | Block at CCN 50, warn at CCN 20 |\n| Performance | 0 violations |\n\nYou can edit `catraca_baseline.json` directly to adjust thresholds.\n\n### Configuration — `catraca_baseline.json`\n\n```json\n{\n    \"source_dirs\": {\n        \"paths\": [\"src\", \"app\", \"lib\"]\n    },\n    \"security\": {\n        \"advisories\": 0\n    },\n    \"style\": {\n        \"violations\": 0\n    },\n    \"static_analysis\": {\n        \"errors\": 0\n    },\n    \"coverage\": {\n        \"percentage\": 85.0\n    },\n    \"duplication\": {\n        \"percentage\": 2.0,\n        \"min_lines\": 3,\n        \"min_tokens\": 30\n    },\n    \"file_size\": {\n        \"max_lines\": 1000\n    },\n    \"complexity\": {\n        \"max_ccn\": 0\n    },\n    \"performance\": {\n        \"violations\": 0,\n        \"rules\": {\n          \"global_namespace_import\": true,\n          \"no_unused_imports\": true,\n          \"fully_qualified_strict_types\": true,\n          \"lambda_not_used_import\": true,\n          \"native_function_invocation\": true,\n          \"no_redundant_readonly_property\": true,\n          \"static_lambda\": true,\n          \"array_push\": true,\n          \"ereg_to_preg\": true,\n          \"modernize_strpos\": true,\n          \"pow_to_exponentiation\": true,\n          \"random_api_migration\": true,\n          \"set_type_to_cast\": true,\n          \"autoload_optimization\": true,\n          \"condition_order\": true\n        },\n        \"fixers\": {\n            \"condition_order\": false\n        }\n    }\n}\n```\n\n**`source_dirs.paths`** — which directories Catraca scans for PHP files. Only directories that exist on disk are used. If none of the configured directories exist, the project root is used as fallback. Defaults to `[\"src\", \"app\", \"lib\"]`.\n\n### `catraca check` — Run quality gates\n\nRuns all 8 gates and compares against baseline. If `catraca_baseline.json` doesn't exist, it is created automatically.\n\n```bash\n# Human-readable (default)\nvendor/bin/catraca check\n\n# Plain text (no colors)\nvendor/bin/catraca check --plain\n\n# JSON output for AI agents / CI\nvendor/bin/catraca check --format=json\n# or vendor/bin/catraca check --format=json-pretty\n\n# GitHub Actions annotations\nvendor/bin/catraca check --format=github\n\n# Specify project path\nvendor/bin/catraca check --path=/path/to/project\n\n# Auto-fix issues if any gate fails, then verify\nvendor/bin/catraca check --fix\n```\n\n\u003e **AI Agent Detection:** When Catraca detects it is running inside an AI agent (Cursor, Claude Code, OpenCode, etc.), it automatically switches to `--format=json` for structured output. You can still override this by explicitly passing `--format`.\n\n### `catraca fix` — Auto-fix issues\n\nRuns auto-fixers for code style, performance, and autoload optimization.\n\n```bash\nvendor/bin/catraca fix\n\n# Specify project path\nvendor/bin/catraca fix --path=/path/to/project\n\n# Skip the automatic check after fixing\nvendor/bin/catraca fix --no-check\n```\n\nWhat it fixes:\n\n| Fixer | Tool | What it does |\n|-------|------|--------------|\n| Condition Order | Built-in | Swaps expensive conditions to come after cheaper ones in `\u0026\u0026` / `||` expressions |\n| Code Style | `pint` or `php-cs-fixer` | Fixes all code style violations |\n| Performance | `php-cs-fixer` | Adds missing imports, removes unused imports, cleans FQCNs, optimizes native calls and more |\n| Autoload | `composer` | Runs `composer dump-autoload -o` if not optimized |\n\n### Exit Codes\n\n| Code | Meaning |\n|------|---------|\n| 0 | All gates passed |\n| 1 | One or more gates failed |\n\n## Output Formats\n\n### Human (default)\n\nTerminal-friendly output with ANSI colors:\n\n```\n  ┌──────────────────────────────────────────────────┐\n  │ CATRACA — PHP Quality Gate Report                │\n  └──────────────────────────────────────────────────┘\n  ────────────────────────────────────────────────────────────\n  ✔ Security Audit          PASS     0 total advisories, 0 critical/high\n  ✔ Code Style              PASS     0 violations (baseline: 0)\n  ✘ Static Analysis         FAIL     3 errors (baseline: 0)\n  ✔ Test Coverage           PASS     85.00% (baseline: 85.00%)\n  ✘ Duplication             FAIL     5.22% (baseline: 2.00%, 2 clones)\n  ✔ File Size               PASS     0 files exceed 1000 lines\n  ✔ Cyclomatic Complexity   PASS     max CCN 8, 0 violations (\u003e50), 1 warnings (\u003e20)\n  ✔ Performance             PASS     No performance improvements needed\n  ────────────────────────────────────────────────────────────\n  RESULT: FAIL — 6/8 gates passed\n\n  ┌──────────────────────────────────┐\n  │ Required Actions                 │\n  └──────────────────────────────────┘\n  [1] FIX SA — Fix 3 PHPStan errors\n      → app/Service.php:42\n      → app/Repository.php:15\n      → app/Controller.php:88\n  [2] REFACTOR DUP — Duplication increased from 2.00% to 5.20%\n      → src/A.php:10-50 \u003c-\u003e src/B.php:100-140 (40L)\n```\n\n### JSON (for AI agents)\n\nUse `catraca check --format=json` to get structured JSON output for AI agents.\nIf you want it formatted, use `catraca check --format=json-pretty`. Note: Consumes more AI agent tokens.\n\nCatraca auto-detects AI agents (Cursor, Claude Code, OpenCode, Gemini CLI, Codex, Augment, and others) and automatically uses JSON output — no flag needed.\n\n```json\n{\n  \"schema\": \"catraca/v1\",\n  \"result\": \"fail\",\n  \"timestamp\": \"2025-05-08T10:30:00+00:00\",\n  \"summary\": {\n    \"total\": 7,\n    \"passed\": 5,\n    \"failed\": 2,\n    \"skipped\": 0\n  },\n  \"gates\": [\n    {\n      \"name\": \"security\",\n      \"label\": \"Security Audit\",\n      \"status\": \"pass\",\n      \"severity\": \"block\",\n      \"message\": \"0 total advisories, 0 critical/high\",\n      \"baseline\": { \"advisories\": 0 },\n      \"current\": { \"advisories\": 0, \"critical\": 0 }\n    }\n  ],\n  \"actions\": [\n    {\n      \"type\": \"FIX SA\",\n      \"priority\": 0,\n      \"message\": \"Fix 3 PHPStan errors\",\n      \"files\": [\"app/Service.php:42\", \"app/Repository.php:15\"]\n    }\n  ]\n}\n```\n\n### GitHub Actions\n\nUses `::error::`, `::warning::`, and `::group::` annotations for native GitHub integration.\n\n## Quality Gates\n\nGates run in order. A failure blocks the PR.\n\n| # | Gate | Tool | Default Threshold |\n|---|------|------|-------------------|\n| 1 | Security Audit | `composer audit` | 0 critical/high advisories |\n| 2 | Code Style | `pint` or `php-cs-fixer` | 0 violations |\n| 3 | Static Analysis | `phpstan` or `psalm` | 0 errors (level 5 if no config) |\n| 4 | Test Coverage | `phpunit` or `pest` | 85% minimum |\n| 5 | Duplication | `phpcpd` | 2% maximum |\n| 6 | File Size | Built-in | 1000 lines per file |\n| 7 | Cyclomatic Complexity | `phpmetrics` | Block at 50, warn at 20 |\n| 8 | Performance | `php-cs-fixer` | 0 violations |\n\nThe Performance gate runs `php-cs-fixer` with configurable rules (all enabled by default):\n\n| Rule                             | What it detects                                                         |\n|----------------------------------|-------------------------------------------------------------------------|\n| `global_namespace_import`        | Missing `use class/const` statements                                    |\n| `no_unused_imports`              | Dead imports that slow parsing                                          |\n| `fully_qualified_strict_types`   | `\\Foo\\Bar` when `use Foo\\Bar` already exists                            |\n| `lambda_not_used_import`         | Closures importing variables they don't use                             |\n| `native_function_invocation`     | Native function calls without `\\` prefix optimization                   |\n| `no_redundant_readonly_property` | Redundant readonly property declarations                                |\n| `static_lambda`                  | Lambdas not using `$this` that should be `static`                       |\n| `array_push`                     | `array_push()` calls — use `$arr[] =` instead                           |\n| `ereg_to_preg`                   | Deprecated `ereg` function calls                                        |\n| `modernize_strpos`               | `strpos()` calls — use `str_contains`/`str_starts_with`/`str_ends_with` |\n| `pow_to_exponentiation`          | `pow()` calls — use `**` operator instead                               |\n| `random_api_migration`           | `rand()`/`mt_rand()` calls — use `random_int()` instead                 |\n| `set_type_to_cast`               | `settype()` calls — use type casting instead                            |\n| `autoload_optimization`          | Missing `composer dump-autoload -o`                                     |\n| `condition_order`                | Expensive conditions placed before cheaper ones in `\u0026\u0026` / `||` (fixer is **experimental**) |\n\nAll rules are configurable in `catraca_baseline.json` under `performance.rules`. Set any rule to `false` to disable it, or `true` to enable it.\n\nThe `condition_order` check is **enabled by default** — it detects expensive conditions placed before cheaper ones. However, the auto-fix is **disabled by default** and marked as **experimental** because it modifies source code using AST transformations. Review all changes before committing. To enable automatic fixing, set `performance.fixers.condition_order` to `true`:\n\n```json\n{\n    \"performance\": {\n        \"rules\": {\n            \"condition_order\": true\n        },\n        \"fixers\": {\n            \"condition_order\": true\n        }\n    }\n}\n```\n\n### PHPStan Configuration\n\nIf your project has a `phpstan.neon`, `phpstan.neon.dist`, or `phpstan.dist.neon`, Catraca uses it as-is. If no config file exists, it defaults to **level 5**.\n\n## CI/CD Integration\n\n### GitHub Actions\n\n```yaml\n# .github/workflows/catraca.yml\nname: Catraca Quality Gate\n\non:\n  pull_request:\n    branches: [main]\n\njobs:\n  quality-gate:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: shivammathur/setup-php@v2\n        with:\n          php-version: '8.3'\n          coverage: pcov\n          tools: composer, phpstan, pint, phpmetrics\n\n      - run: composer install --no-interaction --prefer-dist\n      - run: vendor/bin/catraca init --plain\n        continue-on-error: true\n      - run: vendor/bin/catraca check --format=github --plain\n```\n\n### GitLab CI\n\n```yaml\n# .gitlab-ci.yml\nstages:\n  - test\n\ncatraca:\n  stage: test\n  image: php:8.3-cli\n  cache:\n    key: ${CI_COMMIT_REF_SLUG}\n    paths:\n      - vendor/\n  before_script:\n    - apt-get update -qq \u0026\u0026 apt-get install -yqq unzip\n    - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer\n    - composer install --no-interaction --prefer-dist\n  script:\n    - vendor/bin/catraca init --plain || true\n    - vendor/bin/catraca check --plain\n```\n\n### Forgejo Actions\n\nUses the same `--format=github` output — Forgejo Runner supports GitHub Actions workflow commands (`::error::`, `::warning::`, `::group::`).\n\n```yaml\n# .forgejo/workflows/catraca.yml\nname: Catraca Quality Gate\n\non:\n  pull_request:\n    branches: [main]\n\njobs:\n  quality-gate:\n    runs-on: docker\n    container:\n      image: php:8.3-cli\n    steps:\n      - uses: https://code.forgejo.org/actions/checkout@v4\n\n      - name: Install Composer\n        run: |\n          apt-get update -qq \u0026\u0026 apt-get install -yqq unzip\n          curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer\n\n      - name: Install Dependencies\n        run: composer install --no-interaction --prefer-dist\n\n      - name: Init Baseline\n        run: vendor/bin/catraca init --plain\n        continue-on-error: true\n\n      - name: Run Quality Gate\n        run: vendor/bin/catraca check --format=github --plain\n```\n\n\u003e **Note:** Adjust `runs-on` to match your runner's labels (e.g., `docker`, `ubuntu-latest`, `self-hosted`).\n\n## GrumPHP Integration\n\nCreate a custom task in your project:\n\n```php\n// app/GrumPHP/CatracaTask.php\nuse GrumPHP\\Runner\\TaskResult;\nuse GrumPHP\\Task\\AbstractExternalTask;\nuse GrumPHP\\Task\\Config\\EmptyTaskConfig;\nuse GrumPHP\\Task\\Config\\TaskConfigInterface;\n\nclass CatracaTask extends AbstractExternalTask\n{\n    public function getConfig(): TaskConfigInterface\n    {\n        return new EmptyTaskConfig;\n    }\n\n    public function run(): TaskResult\n    {\n        $process = $this-\u003eprocessBuilder-\u003ebuild(['vendor/bin/catraca', 'check', '--plain']);\n        $process-\u003erun();\n\n        if (!$process-\u003eisSuccessful()) {\n            return TaskResult::createFailed($this, $this-\u003egetContext(), [\n                $process-\u003egetOutput(),\n            ]);\n        }\n\n        return TaskResult::createPassed($this, $this-\u003egetContext());\n    }\n}\n```\n\nRegister it in `grumphp.yml`:\n\n```yaml\n# grumphp.yml\ngrumphp:\n  tasks:\n    catraca: ~\n\nservices:\n  CatracaTask:\n    class: App\\GrumPHP\\CatracaTask\n    arguments:\n      - '@process_builder'\n      - '@formatter.raw_process'\n    tags:\n      - { name: grumphp.task, task: catraca }\n```\n\n## Programmatic Usage\n\n```php\nuse B7S\\Catraca\\Catraca;\nuse B7S\\Catraca\\Output\\JsonFormatter;\nuse B7S\\Catraca\\Output\\HumanFormatter;\n\n$catraca = new Catraca('/path/to/project');\n$result = $catraca-\u003echeck();\n\nif ($result-\u003eisPass()) {\n    echo \"All quality gates passed!\\n\";\n} else {\n    foreach ($result-\u003egetActions() as $action) {\n        echo sprintf(\"[%s] %s\\n\", $action-\u003etype-\u003evalue, $action-\u003emessage);\n    }\n}\n\n// Get structured JSON for AI agents\necho json_encode($result-\u003etoArray(), JSON_PRETTY_PRINT);\n```\n\n## Tool Resolution\n\nEach tool is resolved in this order:\n\n1. **Local** — `vendor/bin/\u003ctool\u003e` (project-level)\n2. **Global** — `\u003ctool\u003e` in `$PATH`\n3. **Composer global** — `~/.composer/vendor/bin/\u003ctool\u003e`\n4. **Skip** — gate is skipped if tool not found\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fb7s%2Fcatraca","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fb7s%2Fcatraca","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fb7s%2Fcatraca/lists"}