Core debt skill
The doctrine. It separates real debt from local mess, taste complaints, and deliberate strategic shortcuts.
Technical debt seer tooling
A product family for finding future cost: hidden contracts, growing suppressions, brittle tests, upgrade friction, deploy friction, and the tiny assumptions that become expensive folklore.
The suite
Vølven is split into focused tools so each one can stay useful instead of becoming another lint-shaped attic.
The doctrine. It separates real debt from local mess, taste complaints, and deliberate strategic shortcuts.
Weak arrays, stringly workflows, hidden Eloquent side effects, unsafe jobs, migration risk, and stale framework assumptions.
Implicit schemas, mutable globals, swallowed exceptions, async failure paths, and scripts pretending to be architecture.
Runtime contracts, casts that silence uncertainty, duplicated state, unsafe API boundaries, and mock-heavy tests.
Deterministic debt budgets for PHP projects. Old debt can be captured. New debt fails CI.
Debt signals
Not every rough edge matters. Vølven looks for signals that change how expensive the next month, deploy, upgrade, or incident becomes.
Call order, magic names, lifecycle assumptions, and test fixtures that know more than production code.
Inline ignores and tolerated uncertainty that quietly become project policy because CI stopped asking questions.
Mocks that assert choreography while the real behavior, boundary, queue, or integration path can still break.
Migrations, environment assumptions, stale adapters, and framework quirks that turn ordinary releases into archaeology.
Boundaries
Pint owns style. PHPStan and Psalm own type correctness. Rector owns mechanical fixes. Pest arch() owns architecture rules. ESLint, TypeScript, Ruff, mypy, and pyright already have jobs.
Vølven owns debt pressure: whether known debt is growing, whether hidden contracts are getting riskier, and whether maintenance cost is moving in the wrong direction.
Pest plugin
The plugin tracks deterministic debt signals, compares them to a committed baseline, and reports findings in the same shape as the Vølven doctrine.
debt()->budget()
->phpstanIgnores(max: 12)
->todoComments(max: 20)
->mixedTypes(max: 12)
->largeClasses(max: 5)
->mustNotGrow()
->requireReasons()
->assert();
Counts suppression annotations only. Type-shaping annotations stay clean, because contracts are not debt.
Tracks unowned work before it becomes “everybody knows why this is here” folklore.
Finds important unknowns crossing boundaries without a visible contract or validation point.
Flags class-like files large enough to trap unrelated reasons to change.
Workflow
Vølven starts where the codebase is. Capture the existing debt, then stop the next commit from making the pile taller.
Find deterministic debt signals and format failures like useful review findings, not lint confetti.
Commit .volven/debt-baseline.json so existing debt is visible without blocking all progress.
Use mustNotGrow() to fail CI when new debt appears outside the agreed budget.
Fix the smallest useful slice first. Large rewrites are usually procrastination with a cape.
Language variants
Model events hiding side effects, queued jobs without idempotency, production-lock migrations, config reads outside config files.
Read Laravel docsDynamic dictionaries through core paths, swallowed exceptions, async code without cancellation discipline, implicit data-pipeline schemas.
Read Python docsany, unsafe casts, runtime API trust, React components owning business rules, state split across URL, cache, store, and component.
Hidden object shapes, callback/promise mixing, mutable shared state, framework magic without local conventions.
Read JavaScript docsDoctrine