From f54b193fae899a9eb530ccacdd05fd632f31ef8d Mon Sep 17 00:00:00 2001 From: chrisbaner <48162172+ChrisAbner@users.noreply.github.com> Date: Mon, 28 Sep 2026 23:10:11 -0500 Subject: [PATCH 1/2] Match public namespaces at any depth (ADR 0010) Contracts, Events, Data, Enums and Exceptions now count as public API wherever they appear inside a module, e.g. Billing\Invoices\Enums\Status. Field tests showed these nested classes were ~20% of the reports in some apps. #[Internal] still keeps a class internal; the per-module public list stays anchored at the module root. --- CHANGELOG.md | 3 +- README.md | 16 ++-- config/cordon.php | 3 +- docs-site/guide/public-api.md | 7 +- docs/adr/0010-nested-public-namespaces.md | 22 ++++++ resources/boost/guidelines/core.blade.php | 2 +- .../skills/cordon-fix-violations/SKILL.md | 2 +- src/Analysis/PublicApiPolicy.php | 33 +++++++- .../app/Modules/Billing/Services/Reporter.php | 14 ++++ .../Modules/Ledger/Invoices/Enums/Hidden.php | 11 +++ .../Modules/Ledger/Invoices/Enums/Status.php | 8 ++ .../Invoices/Events/Sub/InvoicePaid.php | 5 ++ .../Ledger/Invoices/Models/Invoice.php | 5 ++ .../Modules/Ledger/Invoices/MyEnums/Kind.php | 5 ++ tests/Unit/PublicApiPolicyTest.php | 77 +++++++++++++++++++ 15 files changed, 195 insertions(+), 18 deletions(-) create mode 100644 docs/adr/0010-nested-public-namespaces.md create mode 100644 tests/Fixtures/nested-public-namespaces/app/Modules/Billing/Services/Reporter.php create mode 100644 tests/Fixtures/nested-public-namespaces/app/Modules/Ledger/Invoices/Enums/Hidden.php create mode 100644 tests/Fixtures/nested-public-namespaces/app/Modules/Ledger/Invoices/Enums/Status.php create mode 100644 tests/Fixtures/nested-public-namespaces/app/Modules/Ledger/Invoices/Events/Sub/InvoicePaid.php create mode 100644 tests/Fixtures/nested-public-namespaces/app/Modules/Ledger/Invoices/Models/Invoice.php create mode 100644 tests/Fixtures/nested-public-namespaces/app/Modules/Ledger/Invoices/MyEnums/Kind.php create mode 100644 tests/Unit/PublicApiPolicyTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 8088e09..cd04344 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to Cordon Modulith are documented here. The project follows [Semantic Versioning](https://semver.org) from 1.0.0; before that, minor versions may contain breaking changes. -## [Unreleased] - 0.1.0 +## [0.1.0] - 2026-09-29 ### Added - `cordon:verify` command with `text`, `json` and `github` output formats. @@ -11,6 +11,7 @@ All notable changes to Cordon Modulith are documented here. The project follows - Static dependency extraction with nikic/php-parser (the analysed code is never loaded). - Rules: `internal_access`, `undeclared_dependency`, `cycles`. - Public API model: public namespaces, `#[PublicApi]`, `#[Internal]`, per-module `public` list and `open` modules. +- Global `public_namespaces` entries match at any depth of the class namespace (e.g. `Invoices\Enums\Status` is public), see ADR 0010; the per-module `public` list stays anchored at the module root. - Baseline file for gradual adoption (`--generate-baseline`, `--no-baseline`). - Laravel Boost guideline for AI coding agents. - `--module` option for `cordon:verify` to report the violations of one or more modules. diff --git a/README.md b/README.md index 8411d27..a6e5171 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,14 @@ # Cordon Modulith +[![Tests](https://github.com/ChrisAbner/Cordon-Modulith/actions/workflows/tests.yml/badge.svg)](https://github.com/ChrisAbner/Cordon-Modulith/actions/workflows/tests.yml) +[![Latest version](https://img.shields.io/packagist/v/chrisabner/cordon-modulith.svg)](https://packagist.org/packages/chrisabner/cordon-modulith) +[![Downloads](https://img.shields.io/packagist/dt/chrisabner/cordon-modulith.svg)](https://packagist.org/packages/chrisabner/cordon-modulith) +[![PHP](https://img.shields.io/packagist/dependency-v/chrisabner/cordon-modulith/php.svg)](composer.json) +[![License](https://img.shields.io/github/license/ChrisAbner/Cordon-Modulith.svg)](LICENSE.md) + **Cordon off your modules.** Verifiable boundaries between the modules of a Laravel application, checked in CI. -> **Status: 0.1 (alpha).** The public API may change before 1.0. +> **Status: 0.1 (alpha).** The public API may change before 1.0. [Documentation](https://chrisabner.github.io/Cordon-Modulith/) Modular monoliths in Laravel usually rely on nwidart/laravel-modules, InterNACHI/modular or a hand-made `app/Modules` folder. All of them organise code, but none of them stops the `Billing` module from reaching into `Catalog`'s models. Six months later the "modules" are just folders. @@ -12,6 +18,8 @@ Cordon Modulith reads your code statically, builds the dependency graph between - depends on a module it **did not declare**, or - takes part in a **dependency cycle**. +It also keeps AI coding agents honest: Cordon Modulith ships Laravel Boost resources that teach agents to go through a module's public API and to fix the violations `cordon:verify` reports. + It is inspired by [Spring Modulith](https://spring.io/projects/spring-modulith) and Shopify's Packwerk, adapted to Laravel conventions. ```text @@ -79,11 +87,7 @@ Other modules may only use classes that belong to a module's public API. A class 4. it lives under one of the public namespaces, relative to the module: `Contracts`, `Events`, `Data`, `Enums`, `Exceptions` by default, plus the module's own `public` list → public; 5. otherwise → **internal**. -`public_namespaces` match the **first namespace segment relative to the module root** only. `Modules\Billing\Contracts\Gateway` is public, but `Modules\Billing\Invoices\Enums\Status` is **not**: its relative name starts with `Invoices`, not `Enums`. Field tests showed that nested enums and events cause a large share of violations in some apps. To expose them, do one of these: - -- list the nested namespace in the module's `public` list, e.g. `'Billing' => ['public' => ['Invoices\\Enums', 'Invoices\\Events']]`; -- mark the class with `#[PublicApi]`; -- move it to a top-level public namespace. +`public_namespaces` match at **any depth** relative to the module: an entry counts when it appears as a whole segment (or a contiguous run of segments, such as `Http\Resources`) in the class's namespace. `Modules\Billing\Contracts\Gateway`, `Modules\Billing\Invoices\Enums\Status` and `Modules\Billing\Invoices\Events\Sub\InvoicePaid` are public; `Modules\Billing\Invoices\MyEnums\Status` and `Modules\Billing\EnumsHelper\Foo` are not (whole segments only). The module's own `public` list is different: its entries stay anchored at the module root, e.g. `Services\BillingService`. If a nested `Enums` or `Events` class must stay internal, mark it with `#[Internal]`. `DTOs` is not in the defaults (only `Data`). If you use it, add it to `public_namespaces`, e.g. `['Contracts', 'Events', 'Data', 'DTOs', 'Enums', 'Exceptions']` (setting the option replaces the default list). diff --git a/config/cordon.php b/config/cordon.php index 9791a18..6eead3d 100644 --- a/config/cordon.php +++ b/config/cordon.php @@ -45,7 +45,8 @@ |-------------------------------------------------------------------------- | | Other modules may only use classes that belong to a module's public API: - | classes under one of these namespaces (relative to the module namespace) + | classes under one of these namespaces, matched as whole segments at any + | depth relative to the module namespace (Enums also covers Invoices\Enums) | or classes marked with #[Cordon\Attributes\PublicApi]. | #[Cordon\Attributes\Internal] always wins. | diff --git a/docs-site/guide/public-api.md b/docs-site/guide/public-api.md index 7accbd3..3bc7818 100644 --- a/docs-site/guide/public-api.md +++ b/docs-site/guide/public-api.md @@ -8,11 +8,7 @@ Other modules may only use classes that belong to a module's public API. A class 4. it lives under one of the public namespaces, relative to the module: `Contracts`, `Events`, `Data`, `Enums`, `Exceptions` by default, plus the module's own `public` list → public; 5. otherwise → **internal**. -`public_namespaces` match the **first namespace segment relative to the module root** only. `Modules\Billing\Contracts\Gateway` is public, but `Modules\Billing\Invoices\Enums\Status` is **not**: its relative name starts with `Invoices`, not `Enums`. Field tests showed that nested enums and events cause a large share of violations in some apps. To expose them, do one of these: - -- list the nested namespace in the module's `public` list, e.g. `'Billing' => ['public' => ['Invoices\\Enums', 'Invoices\\Events']]`; -- mark the class with `#[PublicApi]`; -- move it to a top-level public namespace. +`public_namespaces` match at **any depth** relative to the module: an entry counts when it appears as a whole segment (or a contiguous run of segments, such as `Http\Resources`) in the class's namespace. `Modules\Billing\Contracts\Gateway`, `Modules\Billing\Invoices\Enums\Status` and `Modules\Billing\Invoices\Events\Sub\InvoicePaid` are public; `Modules\Billing\Invoices\MyEnums\Status` and `Modules\Billing\EnumsHelper\Foo` are not (whole segments only). The module's own `public` list is different: its entries stay anchored at the module root, e.g. `Services\BillingService`. If a nested `Enums` or `Events` class must stay internal, mark it with `#[Internal]`. `DTOs` is not in the defaults (only `Data`). If you use it, add it to `public_namespaces`, e.g. `['Contracts', 'Events', 'Data', 'DTOs', 'Enums', 'Exceptions']` (setting the option replaces the default list). @@ -23,6 +19,7 @@ For a module `Modules\Catalog`: | `Modules\Catalog\Contracts\ProductCatalog` | yes | `Contracts` namespace | | `Modules\Catalog\Data\ProductData` | yes | `Data` namespace | | `Modules\Catalog\Events\ProductDiscontinued` | yes | `Events` namespace | +| `Modules\Catalog\Products\Enums\Status` | yes | `Enums` at any depth | | `Modules\Catalog\Models\Product` | no | internal by default | | `Modules\Catalog\Support\PriceFormatter` with `#[PublicApi]` | yes | attribute | | `Modules\Catalog\Contracts\LegacyCatalog` with `#[Internal]` | no | `#[Internal]` always wins | diff --git a/docs/adr/0010-nested-public-namespaces.md b/docs/adr/0010-nested-public-namespaces.md new file mode 100644 index 0000000..99717e8 --- /dev/null +++ b/docs/adr/0010-nested-public-namespaces.md @@ -0,0 +1,22 @@ +# ADR 0010: Nested public namespaces + +**Status:** accepted +**Date:** 2026-09-29 + +## Context +ADR 0003 defines the public API by namespaces relative to the module (`Contracts`, `Events`, `Data`, `Enums`, `Exceptions` by default). The first implementation matched only the first segment relative to the module root. + +Field tests on real applications showed that about 20% of the `internal_access` reports in some apps were classes such as `He4rt\Recruitment\Requisitions\Enums\RequisitionStatusEnum` or `...\Requisitions\Events\JobRequisitionGenerationEvent`. Their relative name starts with a feature folder (`Requisitions`), so they were reported, but users see enums and events as public API wherever they live. + +## Decision +- An entry of the global `public_namespaces` list matches at any depth: a class is public when the entry appears as a contiguous run of whole segments in its namespace relative to the module (the short class name is excluded). Multi-segment entries such as `Http\Resources` match the same way. +- With module root `Modules\Billing`: `Contracts\Charges`, `Invoices\Enums\Status` and `Invoices\Events\Sub\InvoicePaid` are public; `EnumsHelper\Foo`, `Invoices\MyEnums\Status` and `Invoices\Status` are internal. +- A class whose relative name is exactly an entry at the module root (`Modules\Billing\Enums`) stays public, as before. +- The per-module `public` list (`modules.X.public`) keeps its root-anchored behaviour: it names specific classes or namespaces such as `Services\BillingService`. +- Precedence is unchanged: `#[Internal]` > `#[PublicApi]` > open module > public namespaces > internal. `#[Internal]` remains the way to keep a nested `Enums` or `Events` class internal. +- No change to the config shape, the contracts or the baseline format. + +## Consequences +- Fewer reports, in line with the zero-false-positives rule: nested enums, events, data objects, contracts and exceptions are no longer flagged. +- Possible under-reporting when a team meant a nested `Events` (or similar) namespace to be internal. Mark those classes with `#[Internal]`. +- Existing baselines may contain entries for classes that are now public. They are harmless: `Baseline::filter()` only suppresses violations that are actually found, and unmatched entries are ignored. They can be dropped by regenerating the baseline with `--generate-baseline`. diff --git a/resources/boost/guidelines/core.blade.php b/resources/boost/guidelines/core.blade.php index 139f66f..207b3e3 100644 --- a/resources/boost/guidelines/core.blade.php +++ b/resources/boost/guidelines/core.blade.php @@ -2,7 +2,7 @@ This application uses Cordon Modulith to enforce boundaries between its modules. Follow these rules when writing or changing code: -- A module may only use another module's public API: classes under its public namespaces (by default `Contracts`, `Events`, `Data`, `Enums` and `Exceptions`) or classes marked with `#[\Cordon\Attributes\PublicApi]`. +- A module may only use another module's public API: classes under its public namespaces at any depth (by default `Contracts`, `Events`, `Data`, `Enums` and `Exceptions`) or classes marked with `#[\Cordon\Attributes\PublicApi]`. - Never use models, services, jobs, actions or other internal classes from a different module. Add a contract, event or data object to the target module's public API instead, and depend on that. - To react to something that happens in another module without depending on it, listen to one of its public events. - If a module declares `depends_on` in `config/cordon.php`, only add a new module dependency there when it is intended, and never add it just to silence a violation. diff --git a/resources/boost/skills/cordon-fix-violations/SKILL.md b/resources/boost/skills/cordon-fix-violations/SKILL.md index 1159265..820302f 100644 --- a/resources/boost/skills/cordon-fix-violations/SKILL.md +++ b/resources/boost/skills/cordon-fix-violations/SKILL.md @@ -30,7 +30,7 @@ Run `php artisan cordon:modules` to see each module's namespace, path and declar - **Never add a violation to the baseline** (`--generate-baseline`) to make the build pass. The baseline is only for adopting Cordon Modulith in an existing project, and the maintainers decide when to regenerate it. - **Never add a module to `depends_on`** in `config/cordon.php` only to silence `undeclared_dependency`. Ask the user if the dependency is intended. - **Never mark a class `#[PublicApi]` just to silence `internal_access`.** Only do it when the class was designed to be used by other modules (stable, no persistence details). Prefer a contract or a data object. -- Do not move classes into a public namespace (`Contracts`, `Events`, `Data`, `Enums`, `Exceptions`) unless they really are contracts, events, data objects, enums or exceptions. +- Do not move classes into a public namespace (`Contracts`, `Events`, `Data`, `Enums`, `Exceptions`, at any depth) unless they really are contracts, events, data objects, enums or exceptions. - Re-run `php artisan cordon:verify` after each fix. ## 3. internal_access diff --git a/src/Analysis/PublicApiPolicy.php b/src/Analysis/PublicApiPolicy.php index 1d194cb..dda5a2b 100644 --- a/src/Analysis/PublicApiPolicy.php +++ b/src/Analysis/PublicApiPolicy.php @@ -10,12 +10,13 @@ * Decides whether a class belongs to its module's public API. * * Precedence: #[Internal] > #[PublicApi] > open module > public namespaces - * (global config + module "public" list) > internal by default. + * (global config, matched at any depth; module "public" list, anchored at the + * module root) > internal by default. */ final readonly class PublicApiPolicy { /** - * @param list $publicNamespaces Namespaces relative to each module, e.g. "Contracts". + * @param list $publicNamespaces Namespaces matched at any depth relative to each module, e.g. "Contracts". */ public function __construct( private array $publicNamespaces = ['Contracts', 'Events', 'Data', 'Enums', 'Exceptions'], @@ -33,7 +34,13 @@ public function isPublic(string $fqcn, Module $module, ?ClassDeclaration $declar $relative = $module->relativeName($fqcn); - foreach ([...$this->publicNamespaces, ...$module->publicApi] as $prefix) { + foreach ($this->publicNamespaces as $entry) { + if ($this->matchesAnywhere($relative, trim($entry, '\\'))) { + return true; + } + } + + foreach ($module->publicApi as $prefix) { $prefix = trim($prefix, '\\'); if ($prefix !== '' && ($relative === $prefix || str_starts_with($relative, $prefix.'\\'))) { @@ -43,4 +50,24 @@ public function isPublic(string $fqcn, Module $module, ?ClassDeclaration $declar return false; } + + /** + * Whether the entry appears as a contiguous run of whole segments in the + * namespace of the relative class name (the short class name is excluded), + * or is the relative name itself. + */ + private function matchesAnywhere(string $relative, string $entry): bool + { + if ($entry === '') { + return false; + } + + if ($relative === $entry) { + return true; + } + + $namespace = substr($relative, 0, (int) strrpos($relative, '\\')); + + return str_contains('\\'.$namespace.'\\', '\\'.$entry.'\\'); + } } diff --git a/tests/Fixtures/nested-public-namespaces/app/Modules/Billing/Services/Reporter.php b/tests/Fixtures/nested-public-namespaces/app/Modules/Billing/Services/Reporter.php new file mode 100644 index 0000000..a31e7e6 --- /dev/null +++ b/tests/Fixtures/nested-public-namespaces/app/Modules/Billing/Services/Reporter.php @@ -0,0 +1,14 @@ +isPublic($fqcn, billing_module()))->toBe($public); +})->with([ + 'top-level Contracts' => ['Modules\Billing\Contracts\Charges', true], + 'root class named like the entry' => ['Modules\Billing\Enums', true], + 'nested Enums' => ['Modules\Billing\Invoices\Enums\Status', true], + 'nested Events deeper still' => ['Modules\Billing\Invoices\Events\Sub\InvoicePaid', true], + 'entry only as a name prefix' => ['Modules\Billing\EnumsHelper\Foo', false], + 'entry only as a name suffix' => ['Modules\Billing\Invoices\MyEnums\Status', false], + 'short class name is not a namespace' => ['Modules\Billing\Invoices\Enums', false], + 'plain nested class' => ['Modules\Billing\Invoices\Status', false], + 'plain top-level class' => ['Modules\Billing\Services\Charger', false], +]); + +it('matches multi-segment entries as a contiguous run anywhere', function (string $fqcn, bool $public) { + expect((new PublicApiPolicy(['Http\Resources']))->isPublic($fqcn, billing_module()))->toBe($public); +})->with([ + 'at root' => ['Modules\Billing\Http\Resources\InvoiceResource', true], + 'nested' => ['Modules\Billing\Invoices\Http\Resources\InvoiceResource', true], + 'not contiguous' => ['Modules\Billing\Http\Foo\Resources\InvoiceResource', false], + 'wrong order' => ['Modules\Billing\Resources\Http\InvoiceResource', false], +]); + +it('keeps the per-module public list anchored at the module root', function () { + $policy = new PublicApiPolicy([]); + $module = billing_module(['Services\BillingService', 'Api']); + + expect($policy->isPublic('Modules\Billing\Services\BillingService', $module))->toBeTrue() + ->and($policy->isPublic('Modules\Billing\Api\V1\Thing', $module))->toBeTrue() + ->and($policy->isPublic('Modules\Billing\Invoices\Api\Thing', $module))->toBeFalse() + ->and($policy->isPublic('Modules\Billing\Invoices\Services\BillingService', $module))->toBeFalse(); +}); + +it('lets #[Internal] keep a nested public-namespace class internal', function () { + $declaration = new ClassDeclaration('Modules\\Billing\\Invoices\\Enums\\Status', 'Status.php', 1, DeclaredVisibility::Internal); + + expect((new PublicApiPolicy)->isPublic('Modules\Billing\Invoices\Enums\Status', billing_module(), $declaration))->toBeFalse(); +}); + +it('does not report nested public-namespace classes of another module', function () { + $modules = (new NamespaceResolver(fixture_path('nested-public-namespaces/app/Modules'), 'App\Modules'))->resolve(); + $analyzer = new Analyzer( + new PhpParserExtractor, + new FileCollector(['vendor', 'node_modules', 'tests', 'Tests']), + new PublicApiPolicy, + RuleSet::fromConfig([]), + ); + + $targets = array_map( + fn (Violation $v) => $v->target, + violations_for($analyzer->analyze($modules, fixture_path('nested-public-namespaces')), 'internal_access'), + ); + + expect($targets)->toEqualCanonicalizing([ + 'App\Modules\Ledger\Invoices\Models\Invoice', + 'App\Modules\Ledger\Invoices\MyEnums\Kind', + 'App\Modules\Ledger\Invoices\Enums\Hidden', + ]); +}); From b7700a380bf7de57c2ba74cd49804f010c5364cb Mon Sep 17 00:00:00 2001 From: chrisbaner <48162172+ChrisAbner@users.noreply.github.com> Date: Mon, 28 Sep 2026 23:10:12 -0500 Subject: [PATCH 2/2] Add security policy, code of conduct and issue templates Private vulnerability reporting is enabled on the repository. The social preview image lives in .github/ and is uploaded in the repo settings. --- .github/ISSUE_TEMPLATE/bug_report.yml | 31 ++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 11 ++++++ .github/ISSUE_TEMPLATE/false_positive.yml | 41 +++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 23 ++++++++++++ .github/pull_request_template.md | 11 ++++++ .github/social-preview.png | Bin 0 -> 56117 bytes CODE_OF_CONDUCT.md | 7 ++++ CONTRIBUTING.md | 2 +- SECURITY.md | 19 ++++++++++ 9 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/false_positive.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/social-preview.png create mode 100644 CODE_OF_CONDUCT.md create mode 100644 SECURITY.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..c4f2b4d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,31 @@ +name: Bug report +description: A crash, a missed violation or wrong output. +labels: ["bug"] +body: + - type: textarea + id: what + attributes: + label: What happened + description: What you ran, what you expected and what you got instead. + validations: + required: true + - type: textarea + id: output + attributes: + label: Output + description: Full command output or stack trace. + render: text + - type: textarea + id: reproduce + attributes: + label: How to reproduce + description: The smallest code or project layout that shows the problem. A failing test or fixture is even better. + validations: + required: true + - type: input + id: versions + attributes: + label: Versions + placeholder: Cordon Modulith 0.1.0, PHP 8.4, Laravel 13, nwidart/laravel-modules 13 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..e368393 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Questions and ideas + url: https://github.com/ChrisAbner/Cordon-Modulith/discussions + about: Ask how to model your modules or propose a new rule. + - name: Documentation + url: https://chrisabner.github.io/Cordon-Modulith/ + about: Installation, configuration, rules and recipes. + - name: Security vulnerability + url: https://github.com/ChrisAbner/Cordon-Modulith/security/advisories/new + about: Report security issues privately, not as a public issue. diff --git a/.github/ISSUE_TEMPLATE/false_positive.yml b/.github/ISSUE_TEMPLATE/false_positive.yml new file mode 100644 index 0000000..b0bd41d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/false_positive.yml @@ -0,0 +1,41 @@ +name: False positive +description: Cordon Modulith reports something that is not a real boundary violation. +labels: ["bug", "false positive"] +body: + - type: markdown + attributes: + value: A rule that reports correct code is a bug. The fastest way to get it fixed is the smallest code that reproduces it. + - type: textarea + id: report + attributes: + label: Reported violation + description: Paste the output of `php artisan cordon:verify` for this case. + render: text + validations: + required: true + - type: textarea + id: code + attributes: + label: Code that triggers it + description: The relevant lines of the file, including its `namespace` and `use` statements. + render: php + validations: + required: true + - type: textarea + id: why + attributes: + label: Why it is not a violation + validations: + required: true + - type: input + id: layout + attributes: + label: Module layout + description: "Output of `php artisan cordon:modules` (first lines are enough), e.g. nwidart, InterNACHI or app/Modules." + - type: input + id: versions + attributes: + label: Versions + placeholder: Cordon Modulith 0.1.0, PHP 8.4, Laravel 13 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..4b2d441 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,23 @@ +name: Feature request +description: Suggest a rule, an option or an integration. +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What boundary problem in your codebase does this solve? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposal + description: How it could work, with an example of the output or config if possible. + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives + description: Workarounds you use today, or other tools that do something similar. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..593a5b0 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ +## What and why + + + +## Checklist + +- [ ] Tests added or updated; a bug fix starts with a failing test +- [ ] New detection behaviour has a fixture in `tests/Fixtures` +- [ ] `composer check` passes (Pint, PHPStan, Pest) +- [ ] No change to `src/Contracts`, the config shape, the baseline format or command options without an ADR in `docs/adr` +- [ ] `CHANGELOG.md` updated if users will notice the change diff --git a/.github/social-preview.png b/.github/social-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..4b8c35ba68ecbb4565c7b3818e86f9f1641c0741 GIT binary patch literal 56117 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%0AGWX25cojQk? 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 pM;QLS^#8xg`hVZh^qm`WStS{LRGx3(+&v14_