voku / itp-context
Architecture context attributes, inline enum rule definitions, validation and summarization helpers for PHP projects.
Requires
- php: >=8.3.0
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- 0.4.0
- 0.3.0
- 0.2.0
- 0.1.1
- 0.1.0
- dev-fix/phpunit-12-compat
- dev-chore/renovate-phpunit-php83-floor
- dev-claude/phpstan-agent-format-refactor-ejfdqz
- dev-docs/phpstan-agent-format-handoff
- dev-fix/rule-arm-scanner-definition-scope
- dev-feat/change-guidance
- dev-copilot/refactor-enum-definition-separation
- dev-copilot/update-changelog
- dev-copilot/check-architecture-requirements
- dev-copilot/run-blind-spot-analysis
- dev-copilot/add-shared-skills-library
- dev-copilot/combine-with-existing-adr-concept
- dev-copilot/integrate-infection-static-analysis
- dev-copilot/setup-renovate-automated-dependency-updates
This package is auto-updated.
Last update: 2026-10-18 21:21:39 UTC
README
🎯 itp-context
A small PHP library for attaching architecture rules to code via PHP attributes and resolving those rules directly from typed enums.
It gives you:
- typed rule identifiers via enums
- repeatable
#[Rule(...)]attributes for classes, methods and functions - inline enum rule definitions with ownership, rationale, references and proof metadata
- validation helpers for broken or incomplete rule definitions
- summary output for annotated PHP symbols, including multiple symbols per file
- compact markdown context exports with searchable metadata for coding agents and repository assistants
- a small query helper for searching exported context by rule, owner, proof, refs or free text
- a small generator for bootstrapping new rule enums
Index
Install via "composer require"
composer require voku/itp-context
Why?
When architecture guidance only lives in ADRs and wikis, it drifts away from the code that is supposed to follow it.
itp-context keeps the rule identifier, definition, and supporting context references together in one typed enum instead of splitting them across sibling catalog files. That gives you a compact way to:
- attach architecture intent to classes, methods and functions
- validate whether enum cases still expose usable definitions
- summarize relevant architecture context for one PHP file
The goal is to add context without burning tokens:
- prefer a few broad, high-signal rules over many narrow ones
- annotate central symbols, not every class in the tree
- export compact markdown that is easy for humans and LLMs to scan
Usage
1. Create a rule enum in your project
<?php declare(strict_types=1); namespace Acme\Context; use Acme\Tests\I18nTest; use ItpContext\Contract\RuleIdentifier; use ItpContext\Enum\Tier; use ItpContext\Model\RuleDef; enum ArchitectureRules implements RuleIdentifier { case ViewAbstraction; case I18n; public function getDefinition(): RuleDef { return match ($this) { self::ViewAbstraction => new RuleDef( statement: 'Use a dedicated view abstraction for rendering.', tier: Tier::Standard, owner: 'Team-Architecture', rationale: 'A dedicated view layer keeps rendering concerns isolated from domain and controller code.', refs: ['docs/adr/view-abstraction.md', 'docs/ui/rendering.md'], changeGuidance: 'Read docs/adr/view-abstraction.md before changing rendering boundaries.', ), self::I18n => new RuleDef( statement: 'Use locale-aware formatting and translated UI labels.', tier: Tier::Standard, owner: 'Team-Architecture', rationale: 'Locale-aware rendering avoids user-facing regressions once the UI contains translated labels and formatted values.', verifiedBy: [I18nTest::class], refs: ['docs/adr/i18n.md'], ), }; } }
Use refs for the context you want nearby: ADRs, docs, design notes, diagrams, tickets or related code.
Optionally set changeGuidance on a rule to tell a reader what to do before changing the protected symbol (for example which ADR to read). It is advisory text, shown as **When changing:**, and never an enforcement mechanism.
2. Annotate your code
Keep annotations selective: tag the classes or methods where architecture context changes decisions, not every file.
<?php declare(strict_types=1); namespace Acme\Ui; use Acme\Context\ArchitectureRules; use ItpContext\Attribute\Rule; #[Rule(ArchitectureRules::ViewAbstraction)] final class DashboardView { #[Rule(ArchitectureRules::I18n)] public function render(): string { return 'ok'; } }
3. Validate the enum definitions
<?php declare(strict_types=1); use Acme\Context\ArchitectureRules; use ItpContext\Service\Validator; $errors = (new Validator())->validateEnumClass(ArchitectureRules::class);
Validation walks every enum case and calls getDefinition(), so broken match arms or incomplete inline definitions fail in one place.
4. Summarize one file
<?php declare(strict_types=1); use ItpContext\Service\Summarizer; $output = (new Summarizer())->summarize(__DIR__ . '/src/Ui/DashboardView.php');
Example output:
# Context: DashboardView
### [INFO] Use a dedicated view abstraction for rendering.
- **ID:** `Acme\Context\ArchitectureRules::ViewAbstraction`
- **Owner:** Team-Architecture
- **Why:** A dedicated view layer keeps rendering concerns isolated from domain and controller code.
- **Refs:** docs/adr/view-abstraction.md, docs/ui/rendering.md
- **When changing:** Read docs/adr/view-abstraction.md before changing rendering boundaries.
## Method: `render`
### [INFO] Use locale-aware formatting and translated UI labels.
- **ID:** `Acme\Context\ArchitectureRules::I18n`
- **Owner:** Team-Architecture
- **Why:** Locale-aware rendering avoids user-facing regressions once the UI contains translated labels and formatted values.
- **Proof:** Acme\Tests\I18nTest
- **Refs:** docs/adr/i18n.md
Resolve the rules that apply to a set of files
<?php declare(strict_types=1); use ItpContext\Service\ApplicableRuleResolver; $rules = (new ApplicableRuleResolver())->resolveFiles([ __DIR__ . '/src/Database.php', __DIR__ . '/src/UserRepository.php', ]); foreach ($rules as $rule) { echo $rule->ruleId, ': ', $rule->statement, "\n"; echo ' applies to: ', implode(', ', $rule->subjects), "\n"; }
ApplicableRuleResolver is stateless: it reads the #[Rule] attributes of the given files with the TokenParser, resolves each RuleIdentifier case to its RuleDef and returns every rule once (sorted by rule id) as an ApplicableRule with ruleId, statement, changeGuidance, refs and the annotated subjects (Class, Class::method or function names). Files without annotations contribute nothing. A rule id that cannot be resolved to an enum case (for example because the enum is not autoloadable) throws a RuntimeException.
Show the applicable rules in the PHPStan agent report
Install voku/phpstan-agent-format next to this package and enable the integration:
includes: - vendor/voku/phpstan-agent-format/extension.neon parameters: errorFormat: agent agentFormat: itpContext: true
vendor/bin/phpstan analyse then appends one advisory architecture[<rule id>] notice per rule that applies to the files PHPStan analysed in this run: phpstan analyse src/Database.php only reports the rules of that file, phpstan analyse reports every rule used in the project. No Git, no diff and no state file are involved, and notices never change PHPStan's exit code. See the formatter's documentation for output formats and redaction.
5. Export agent-friendly context for a source tree
<?php declare(strict_types=1); use ItpContext\Service\ContextExporter; $report = (new ContextExporter())->export( outputDir: __DIR__ . '/var/itp-context', sourceDirs: [__DIR__ . '/src'], excludePaths: ['vendor', 'tests'], );
This writes:
var/itp-context/index.md- one markdown file per annotated PHP symbol under
var/itp-context/php/
The export is intentionally lean:
- annotate only the few symbols that carry important architecture context
- use broad rules that stay stable as the code evolves
- keep frontmatter small:
id,title,source_path,kindandrule_ids
This repository dogfoods that approach with a few high-signal ItpContext\Context\PackageRules annotations on core services, and a committed self-export snapshot lives under docs/package-export/.
That docs/package-export/ tree is meant to be a ready-made reference for coding agents: it shows the compact export shape, the searchable metadata (owners, refs, verified_by, annotated_methods) and the level of abstraction that keeps context useful without wasting tokens.
Local Development
If you want to test the package before publishing it, use a Composer path repository in a separate project:
{
"require": {
"voku/itp-context": "*@dev"
},
"repositories": [
{
"type": "path",
"url": "../itp-context",
"options": {
"symlink": false
}
}
],
"minimum-stability": "dev",
"prefer-stable": true
}
Then install it via Composer:
composer install
After that the package CLIs are available in the consumer project via:
vendor/bin/itp-context-validate 'Acme\Context\ArchitectureRules' vendor/bin/itp-context-summarize src/Ui/DashboardView.php vendor/bin/itp-context-export var/itp-context src --exclude=vendor --exclude=tests vendor/bin/itp-context-query var/itp-context --rule-id='Acme\Context\ArchitectureRules::I18n'
Project Structure
This package only contains generic framework code under the ItpContext\\ namespace.
Your project-specific files stay in your own codebase, for example:
src/Context/ArchitectureRules.php
A minimal example project is included under examples/basic-domain, and the repository's self-export snapshot lives under docs/package-export.
The example project also includes sample context docs under examples/basic-domain/docs/, including ADR-style notes referenced from the enum definitions.
Portable agent skills
This repository keeps one shared, repo-owned coding-agent skill under docs/skills/itp-context.md.
Agent-specific entrypoints stay thin and point back to that file:
AGENTS.md.github/copilot-instructions.mdCODEX.mdCLAUDE.mdGEMINI.md
That keeps the actual guidance in one place while still exposing it to different agent runtimes.
CLI Tools
The package ships with six small CLI helpers.
itp-context-summarize
vendor/bin/itp-context-summarize path/to/src/DashboardView.php
itp-context-validate
vendor/bin/itp-context-validate 'Acme\Context\ArchitectureRules'
itp-context-generate
vendor/bin/itp-context-generate Architecture SecurityBoundary src/Context Acme\\Context
This creates or extends:
src/Context/ArchitectureRules.php
The generator only updates the enum: it adds the new case plus a matching getDefinition() arm and seeds statement, owner, rationale, verifiedBy, and refs placeholders so new rules start with a fuller definition.
itp-context-export
vendor/bin/itp-context-export var/itp-context src --exclude=vendor --exclude=tests
The export contains:
index.mdwith an overview of all exported symbols- one markdown file per annotated PHP symbol under
php/ - compact frontmatter fields for
id,title,source_path,kind,rule_ids,owners,refs,verified_by,annotated_methodsandrule_count
itp-context-query
vendor/bin/itp-context-query var/itp-context --rule-id='Acme\Context\ArchitectureRules::I18n' vendor/bin/itp-context-query var/itp-context --owner='Team-Architecture' vendor/bin/itp-context-query var/itp-context --text='i18n'
For small libraries, prefer a tiny export with a few high-value symbols over exhaustive annotation. The goal is context density, not full documentation coverage.
Tests
composer phpstan composer test # strict mutation run with uncovered code and example sources included composer infection
There is also a package smoke check in tests/smoke/package_smoke.php.
License
MIT