Skip to content

Instantly share code, notes, and snippets.

@danielhe4rt
Created June 14, 2026 20:33
Show Gist options
  • Select an option

  • Save danielhe4rt/332778942ec4a905f47deb073815539e to your computer and use it in GitHub Desktop.

Select an option

Save danielhe4rt/332778942ec4a905f47deb073815539e to your computer and use it in GitHub Desktop.
#:schema https://mago.carthage.software/1.30.0/schema.json
# mago.toml — tuned to approximate the current He4rtDevs toolchain as closely as Mago allows:
# Pint (format) + PHPStan level 6 / larastan (analyze) + Rector (lint detection)
# Covers the whole modular monorepo (app/ + the 18 modules under app-modules/).
#
# CAVEAT: Rector's TRANSFORMATIVE work (PHP version upgrades, Laravel 13 / Laravel-idiom /
# Pest API migrations, dead-code REMOVAL, type-hint AUTHORING) has NO Mago equivalent.
# Mago lint/analyze only DETECT — keep Rector for those. See MAGO-COMPARISON.md.
version = "1"
php-version = "8.4.0"
# ─────────────────────────────────────────────────────────────────────────────
# SOURCE — what every Mago tool scans. Mirrors Rector's path set (the broadest of
# the three tools). Replaces phpstan.modules.php glob + 18 included *.neon files.
[source]
workspace = "."
paths = [
"app",
"app-modules", # all 18 modules: src/ + config/ + database/ + routes/ + tests/
"bootstrap",
"config",
"database",
"routes",
"tests",
]
includes = ["vendor"] # resolve Laravel/vendor symbols for the analyzer; never reported on
excludes = [
"bootstrap/cache",
"**/*.blade.php", # Blade templates are not plain PHP — keep them out
]
[source.glob]
literal-separator = true
# ─────────────────────────────────────────────────────────────────────────────
# FORMATTER — replaces Pint. The dedicated `pint` preset reproduces Pint's laravel
# preset and already sets: concat spacing = none, and no parens on parameterless
# `new` (matching your disabled new_with_parentheses). The preserve-breaking-*
# flags keep your existing manual line breaks → a much smaller one-time diff.
[formatter]
preset = "pint"
preserve-breaking-member-access-chain = true
preserve-breaking-member-access-chain-first-method-on-same-line = true
preserve-breaking-argument-list = true
preserve-breaking-array-like = true
preserve-breaking-parameter-list = true
preserve-breaking-attribute-list = true
preserve-breaking-conditional-expression = true
preserve-breaking-condition-expression = true
preserve-redundant-logical-binary-expression-parentheses = true
# Pint kept not_operator_with_successor_space DISABLED (no space after `!`), but the
# pint preset turns it ON — override back to match the current codebase.
space-after-logical-not-unary-prefix-operator = false
# NOTE: Pint's ordered_class_elements (custom 19-element member order) is NOT
# reproducible — Mago's `sort-class-methods` only reorders methods by its own scheme,
# so it is left OFF (the pint preset's default) to avoid churn that diverges from
# the current layout. This member-ordering policy is a known gap vs Pint.
# ─────────────────────────────────────────────────────────────────────────────
# LINTER — Mago's own opinionated layer (no 1:1 in the current stack). The laravel
# + pest integrations enable the framework rule packs; Pint's semantic rules
# (strict-types, unused-use, identity-comparison, lowercase, …) are on by default.
# `symfony` dropped from the init defaults — this is a Laravel app, not Symfony.
[linter]
integrations = ["laravel", "pest", "symfony"]
[linter.rules]
ambiguous-function-call = { enabled = false }
literal-named-argument = { enabled = false }
halstead = { effort-threshold = 7000 }
prefer-static-closure= {enabled = false}
# ─────────────────────────────────────────────────────────────────────────────
# ANALYZER — replaces PHPStan level 6 + larastan.
# IMPORTANT: Mago 1.30.0 has NO Laravel type plugin, so Eloquent magic, relations,
# query-builder chains, facade return types and container resolution are invisible
# → expect a wave of false positives. Adopt a baseline (bottom) to absorb them,
# exactly like phpstan.ignore.neon did.
[analyzer]
plugins = []
# Restrict analysis to PHPStan's original scope (app/ + each module's src/). Pint and
# Rector also touch database/config/routes/tests, but PHPStan did not analyze them.
excludes = [
"bootstrap/**",
"config/**",
"routes/**",
"database/**",
"tests/**",
"app-modules/*/config/**",
"app-modules/*/routes/**",
"app-modules/*/database/**",
"app-modules/*/tests/**",
]
# Level-6 parity: report missing type hints (OFF by default in Mago).
check-missing-type-hints = true
check-closure-missing-type-hints = true
# Arrows kept exempt — the team rejects AddArrowFunctionReturnTypeRector in Rector.
check-arrow-function-missing-type-hints = false
# Dead-code DETECTION (mirrors Rector's deadCode set; Mago detects, never removes).
find-unused-definitions = true
find-unused-expressions = false
analyze-dead-code = false
memoize-properties = true
# Keep at level-6 strictness — these are level 7+ checks; leave loose to match.
allow-possibly-undefined-array-keys = true
strict-array-index-existence = false
strict-list-index-checks = false
# Honor the team's current opt-outs:
check-missing-override = false # AddOverrideAttributeToOverriddenMethodsRector is skipped
enforce-class-finality = false # Pint's final_class is disabled
find-unused-parameters = false
check-throws = false
unchecked-exceptions = ["Error", "LogicException"]
register-super-globals = true
minimum-fail-level = "Error"
# Baseline — generate once to absorb the larastan-awareness gap, then commit it.
# (PHPStan messages do NOT port across engines, so regenerate rather than translate.)
# mago analyze --generate-baseline --baseline mago-analyze-baseline.toml
# baseline = "mago-analyze-baseline.toml"
# baseline-variant = "loose"
# ─────────────────────────────────────────────────────────────────────────────
# GUARD — NOT part of the current stack (pure bonus). None of Pint/PHPStan/Rector
# enforce architectural boundaries. Once piloted, encode CLAUDE.md's dependency
# direction (Domain → Integration → Presentation) over the He4rt\* namespaces here.
# Left inert for now to stay faithful to "current tools only".
# [guard.perimeter]
# layering = ["He4rt\\<Domain>", "He4rt\\<Integration>", "He4rt\\<Presentation>"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment