padosoft / laravel-patent-box-tracker
Laravel package that classifies R&D activity across one or more git repositories with a deterministic LLM-based classifier and emits an audit-grade PDF + JSON dossier suitable for the Italian Patent Box (110% R&D super-deduction) regime.
Package info
github.com/padosoft/laravel-patent-box-tracker
pkg:composer/padosoft/laravel-patent-box-tracker
Requires
- php: ^8.3
- illuminate/console: ^12.0|^13.0
- illuminate/database: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- laravel/ai: ^0.6
- symfony/yaml: ^7.0|^8.0
Requires (Dev)
- dompdf/dompdf: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0|^12.0
- spatie/browsershot: ^4.0|^5.0
Suggests
- dompdf/dompdf: Pure-PHP PDF dossier renderer fallback for environments without headless Chromium (CI runners, air-gapped servers).
- padosoft/laravel-ai-regolo: Lets the classifier run on Italian sovereign Regolo infrastructure for cost + GDPR reasons. Any laravel/ai SDK provider works.
- spatie/browsershot: Highest-fidelity PDF dossier renderer (Chromium-backed). Pair with DomPDF for environments without headless Chromium.
README
Audit-grade Italian Patent Box dossier generator for Laravel.
Walks one or more git repositories, classifies every commit with a deterministic LLM pipeline, correlates the activity with design-doc evidence and AI-attribution markers, and emits a tamper-evident PDF + JSON dossier suitable for the Italian Patent Box (110% R&D super-deduction) regime β the artefact the taxpayer attaches to "documentazione idonea" filings with Agenzia delle Entrate.
Table of contents
- Why this package
- Design rationale
- Features at a glance
- Comparison vs alternatives
- Italian Patent Box primer
- Installation
- Quick start
- Usage examples
- Configuration reference
- Architecture
- π AI vibe-coding pack included
- Testing
- Roadmap
- Contributing
- Security
- License & credits
Pre-release status
All four W4 sub-tasks have shipped on
main:
- W4.A β package scaffold,
PatentBoxTrackerServiceProvider,config/patent-box-tracker.php, the CI matrix on PHP 8.3 / 8.4 / 8.5 Γ Laravel 12 / 13, the.claude/vibe-coding pack, and theLivetestsuite scaffold.- W4.B.1 β four
EvidenceCollectorimplementations (GitSourceCollector,AiAttributionExtractor,DesignDocCollector,BranchSemanticsCollector) +CollectorRegistryboot-time validation per R23.- W4.B.2 β
CommitClassifier+ClassifierBatcher+CostCapGuard+GoldenSetValidator+ storage models (TrackingSession,TrackedCommit,TrackedEvidence,TrackedDossier).- W4.C β
PdfDossierRenderer(Browsershot + DomPDF fallback) +JsonDossierRenderer+ Italian Blade template +HashChainBuilder+RenderCommand.- W4.D β
TrackCommand(patent-box:track) +CrossRepoCommand(patent-box:cross-repo) +CrossRepoConfigValidator+ the fluent builder API documented in Quick start.The next milestone is the
v0.1.0tag and the corresponding Packagist publish. The Roadmap section below is now accurate; planned items are explicitly tagged for v0.2 and beyond.
Why this package
The Italian Patent Box (regime "Nuovo Patent Box" introduced from FY2021 and consolidated under D.L. 146/2021 art. 6) grants a 110% super-deduction on qualified R&D costs incurred to develop intangible assets β patents, registered software (SIAE), industrial designs. Each β¬1 of qualified R&D effectively saves around β¬0.30 of tax (IRPEF + IRAP + INPS combined for an Italian sole proprietor).
The mandatory counterpart is documentation. To survive an Agenzia delle Entrate audit, the taxpayer must produce on demand:
- Identification of the qualified IP β patent number, SIAE registration ID, design certificate.
- Time-bound trail of R&D activity β what was done, when, by whom, on which IP.
- Phase classification of each activity β research, design, implementation, validation, documentation.
- Cost allocation β hourly rates Γ hours Γ phase, mapped to fiscal-year buckets.
- Tamper-evident integrity β the documentation must demonstrate it could not have been retroactively fabricated.
The current state of practice for software IP is manually compiled spreadsheets reconstructed from memory, calendars, and Git logs. This is expensive (a commercialista bills 8β20 hours per dossier) and error-prone (commits drift in classification, AI-assisted code is rarely separated, cross-repo work is hand-stitched).
laravel-patent-box-tracker replaces the manual reconstruction with a deterministic pipeline that walks the repositories and produces the same artefact in minutes β with hash-chain tamper evidence, deterministic-seeded LLM classification, and a hand-graded golden-set release gate.
Dogfooding angle
The package was built by an Italian Patent Box filer for Italian Patent Box filers. Padosoft (ditta individuale) uses it to file the FY2026 dossier on the AskMyDocs IP and the Padosoft Apache-2.0 sister packages. Every release is validated against a real feature/v4.x history with commercialista review before tagging.
Community angle
No equivalent package exists on Packagist as of 2026. The closest analogues β gitinspector, commitlint + custom downstream tooling, Toggl / Harvest plugins, commercialista Excel templates β solve adjacent problems but none produce the documentation that Agenzia delle Entrate actually accepts for the documentazione idonea regime. The market gap is real and large enough that the package is expected to drive its own adoption arc.
Design rationale
A few decisions are worth surfacing up front, because they shape the package's footprint and the kind of bugs you can or cannot have.
1. Deterministic-seeded classifier, not "AI magic"
Every LLM call goes out with temperature=0, top_p=1, and a fixed seed. Re-running on the same commit produces an identical classification. The dossier metadata records the prompt, the model, and the seed, so a Patent Box auditor can re-execute the run and verify byte-for-byte. This is non-negotiable for an audit-grade artefact: a stochastic classifier is not "documentation" β it is "guesswork that happens to be written down".
2. Hand-graded golden set as a release gate
Before tagging v0.1.0, a 50-commit manually-labelled validation set is held to β₯ 80% F1. The golden set is drawn from real feature/v4.x history with the labels assigned by Lorenzo + a commercialista review. Synthetic accuracy claims ("the model says it works") do not pass the gate; only labels a fiscal reviewer signed off on do.
3. Provider-agnostic via laravel/ai
The classifier depends on the SDK abstraction, not on a specific provider. The default uses Regolo (Italian sovereign cloud, EUR-billed, GDPR-friendly), but anyone can swap to OpenAI, Anthropic, Gemini, or any other laravel/ai provider in one config line. The package itself ships zero provider credentials β they live in the consumer's .env.
4. Hash-chain tamper evidence
Every tracked commit gets H(prev_hash || commit_sha) recorded in the dossier. The full chain is published in the appendix of the PDF and as a manifest in the JSON sidecar. Any retroactive edit to the underlying repo is detectable; any retroactive edit to the dossier itself breaks the chain at the exact tampered row.
5. Standalone agnostic β no AskMyDocs glue
The package has zero dependencies on lopadova/askmydocs (CE), padosoft/askmydocs-pro, or any other Padosoft proprietary code. It works in any Laravel 12/13 application that has laravel/ai installed. The reverse direction is the only one that exists: AskMyDocs uses this package, never the inverse. An architecture test enforces the boundary on every CI run.
Features at a glance
- Audit-grade dossier β Italian fiscal A4 portrait PDF + machine-readable JSON sidecar, suitable for the documentazione idonea regime under D.M. 6 ottobre 2022 + provv. AdE 15 febbraio 2023.
- Deterministic LLM classifier β
temperature=0, fixedseed, recorded prompt and model version in the dossier metadata. Re-runs are byte-identical. - Hash-chain tamper evidence β per-commit
H(prev_hash || commit_sha)plus a SHA-256 of the entire JSON sidecar. Retroactive edits to either the repo or the dossier break the chain at the tampered row. - Four pluggable evidence collectors β
GitSourceCollector,AiAttributionExtractor,DesignDocCollector,BranchSemanticsCollector. Boot-time FQCN validation + non-overlappingsupports()predicates. - Cross-repository orchestration β single YAML config drives the dossier across N repos with per-repo roles (
primary_ip,support,meta_self). - AI attribution detection β parses
Co-Authored-Bytrailers (Claude, Copilot, custom) and committer-email signatures intohuman/ai_assisted/ai_authored/mixedper commit. - Design-doc correlation β auto-links commits to PLAN / ADR / spec / lessons-learned files by filename match, slug match, and date proximity.
- Branch-semantics heuristics β
feature/v4.0-W*-...β development phase,chore/*β typically non-qualified,fix/*β context-dependent (pre-release qualifies, post-release maintenance does not). - Cost-cap hard stop β pre-flight token-cost projection; aborts with exit code 2 before exceeding
cost_cap_eur_per_run(default β¬50). No accidental classification of a 10-year-old monorepo at full price. - Three Artisan commands β
patent-box:track,patent-box:render,patent-box:cross-repo. Plus a fluent PHP builder API for programmatic use. - Hand-graded golden set β₯ 80% F1 β release gate enforced before every
v*.*.0tag. Realfeature/v4.xcommits, real commercialista-validated labels. - Browsershot PDF + DomPDF fallback β Chromium fidelity by default, DomPDF for environments where headless Chromium is unavailable.
- Standalone agnostic β zero dependency on
lopadova/askmydocsorpadosoft/askmydocs-pro. Works on any Laravel project. Composable with sister Padosoft packages. - Strict typing β PHP 8.3+, readonly DTOs, fully-typed signatures, Pint-formatted, PHPStan analysis on every PR.
- CI matrix β every push runs against PHP 8.3 / 8.4 / 8.5 Γ Laravel 12 / 13 (6 cells).
- π AI vibe-coding pack ships in the box β every release includes the Padosoft Claude pack under
.claude/(skills, rules, agents, slash-commands). The moment youcomposer requirethis package and open the project in Claude Code, the agent picks up Padosoft's house conventions automatically. - π§ͺ Opt-in live test suite β point
PATENT_BOX_LIVE_API_KEYat a real provider key and runvendor/bin/phpunit --testsuite Liveto verify classifier accuracy and PDF rendering against real APIs. Default suite remains 100% offline.
Comparison vs alternatives
If you are evaluating how to assemble an Italian Patent Box dossier today, here are the realistic options on the table.
| Capability | gitinspector + scripts |
Toggl / Harvest plugins | Commercialista Excel templates | DIY git-log + spreadsheet | laravel-patent-box-tracker |
|---|---|---|---|---|---|
| Walks git history per repo | β | β | β | β | β |
| Cross-repository orchestration | β οΈ DIY | β | β | β οΈ DIY | β |
| Phase classification (research/design/impl/etc.) | β | β οΈ manual | β οΈ manual | β οΈ manual | β |
AI-attribution detection (Co-Authored-By) |
β | β | β | β οΈ DIY | β |
| Design-doc evidence linking (PLAN / ADR / spec) | β | β | β | β οΈ DIY | β |
| Deterministic / reproducible runs | β | β | β | β | β |
| Hash-chain tamper evidence | β | β | β | β | β |
| Italian fiscal A4 PDF template | β | β | β οΈ partial | β | β |
| Machine-readable JSON sidecar (gestionale-friendly) | β | β | β | β | β |
| Cost-cap pre-flight guard | N/A | N/A | N/A | N/A | β |
| Hand-graded accuracy gate (β₯ 80% F1) | N/A | N/A | N/A | N/A | β |
| Reproducible by Agenzia delle Entrate auditor | β οΈ partial | β | β | β οΈ partial | β |
| Time to assemble FY dossier | days | days | ~10β20 h | days | minutes |
Bottom line: generic git-stat tools and time-trackers solve a different problem; commercialista Excel templates are the manual baseline this package replaces; DIY scripts duplicate the work each fiscal year. laravel-patent-box-tracker is the only option that produces the exact artefact the documentazione idonea regime expects, with the tamper-evidence and reproducibility a fiscal review demands.
Italian Patent Box primer
Skip this section if you already file Italian Patent Box dossiers.
The Patent Box is a tax incentive available to entrepreneurs (artigiani included), companies (SRL, SpA), and professionals (with VAT registration) that develop qualified intangible IP and incur R&D costs to do so.
| Element | Detail |
|---|---|
| Qualified IP | Software protected by copyright (typically registered with SIAE), patents (UIBM or EPO), industrial designs, certain trademarks before 2022. |
| Qualified R&D costs | Personnel, AI tooling subscriptions, cloud compute, contractor invoices, dedicated equipment, IP registration fees. Marketing, sales, generic admin do NOT qualify. |
| Phase taxonomy | Research, experimental development, design, implementation, integration, validation, documentation. Manutenzione (post-release bug fixes) does NOT qualify; pre-release fixes generally do. |
| Documentation regime | "Documentazione idonea" (D.M. 6 ottobre 2022 + provv. AdE 15 febbraio 2023) protects against monetary penalties on classification errors if the dossier is filed. Option communicated in the tax return. |
| Cost calculation | Qualified costs Γ 110% = additional deduction. The base 100% is already deducted normally; the +10% is the incentive. |
| Audit window | Agenzia delle Entrate can challenge the dossier within 5 fiscal years from filing. |
This package targets the documentazione idonea regime: the output dossier is what the taxpayer attaches when communicating the option, and what they hand to Agenzia delle Entrate during audit.
Installation
composer require laravel/ai composer require padosoft/laravel-patent-box-tracker
The package auto-registers via Laravel's package discovery β no manual provider entry in config/app.php needed.
Publish the config:
php artisan vendor:publish --tag=patent-box-tracker-config
Run the migrations to create the storage tables (tracking_sessions, tracked_commits, tracked_evidence, tracked_dossiers):
php artisan migrate
Configure your laravel/ai provider of choice (Regolo is the default; OpenAI, Anthropic, Gemini all work). Minimal .env:
# Pick any laravel/ai provider β Regolo recommended for cost (~β¬0.05 per 1k commits). REGOLO_API_KEY=rg_live_... PATENT_BOX_DRIVER=regolo PATENT_BOX_MODEL=claude-sonnet-4-6
Quick start
Single-repository dossier
php artisan patent-box:track /path/to/your/repo \
--from=2026-01-01 --to=2026-12-31 \
--provider=regolo --model=claude-sonnet-4-6
The command walks the repo, classifies every commit, persists a tracking_session, and prints the session id. Render the dossier:
php artisan patent-box:render <session-id> --format=pdf --out=storage/dossier-2026.pdf php artisan patent-box:render <session-id> --format=json --out=storage/dossier-2026.json
Cross-repository dossier (the canonical Padosoft use case)
Save your fiscal config under version control:
# config/patent-box-2026.yml fiscal_year: 2026 period: from: 2026-01-01 to: 2026-12-31 tax_identity: denomination: Padosoft di Lorenzo Padovani p_iva: IT01234567890 regime: documentazione_idonea cost_model: hourly_rate_eur: 80 daily_hours_max: 8 classifier: provider: regolo model: claude-sonnet-4-6 repositories: - path: /home/lpad/Code/askmydocs role: primary_ip - path: /home/lpad/Code/laravel-ai-regolo role: support - path: /home/lpad/Code/laravel-flow role: support - path: /home/lpad/Code/eval-harness role: support - path: /home/lpad/Code/laravel-pii-redactor role: support - path: /home/lpad/Code/laravel-patent-box-tracker role: meta_self manual_supplement: off_keyboard_research_hours: 60 conferences: - { name: "Laracon EU 2026", days: 3 } ip_outputs: - kind: software_siae title: "AskMyDocs Enterprise Platform v4.0" registration_id: "SIAE-2026-..." - kind: brevetto_uibm title: "Canonical Knowledge Compilation Engine" application_id: "PCT/IT2026/..."
Then run:
php artisan patent-box:cross-repo config/patent-box-2026.yml
You get one consolidated dossier across N repositories, with cross-repo summaries and per-repo subtotals β in minutes.
Usage examples
Fluent builder API
use Padosoft\PatentBoxTracker\PatentBoxTracker; $session = PatentBoxTracker::for([ '/path/to/askmydocs', '/path/to/laravel-ai-regolo', '/path/to/laravel-flow', ]) ->coveringPeriod('2026-01-01', '2026-12-31') ->classifiedBy('regolo') ->withTaxIdentity([ 'denomination' => 'Padosoft di Lorenzo Padovani', 'p_iva' => 'IT01234567890', 'fiscal_year' => '2026', 'regime' => 'documentazione_idonea', ]) ->withCostModel([ 'hourly_rate_eur' => 80, 'daily_hours_max' => 8, ]) ->run(); // $session is a TrackingSession Eloquent model with relations to TrackedCommit / TrackedEvidence.
Render the dossier
$dossier = $session->renderDossier() ->locale('it') ->toPdf(); $dossier->save(storage_path('dossier-2026.pdf')); // JSON sidecar (machine-readable, hash-chain signed). $session->renderDossier()->toJson()->save(storage_path('dossier-2026.json'));
Inspect classification results
use Padosoft\PatentBoxTracker\Models\TrackedCommit; $qualified = TrackedCommit::query() ->where('tracking_session_id', $session->id) ->where('is_rd_qualified', true) ->orderByDesc('rd_qualification_confidence') ->get(); foreach ($qualified as $commit) { echo "{$commit->sha} [{$commit->phase}] {$commit->ai_attribution} β {$commit->rationale}\n"; }
Verify hash chain integrity
use Padosoft\PatentBoxTracker\Hash\HashChainBuilder; $verified = HashChainBuilder::verify($session); if (! $verified->isValid()) { throw new RuntimeException( "Tamper detected at commit {$verified->brokenAt} β chain breaks at this row." ); }
Extending the pipeline with a custom collector
use Padosoft\PatentBoxTracker\Sources\EvidenceCollector; final class CalendarCollector implements EvidenceCollector { public function supports(string $kind): bool { return $kind === 'calendar'; } public function collect(array $context): array { // Walk Google Calendar / Outlook events, return Evidence DTOs. return []; } } // Register in a service provider: $this->app->tag([CalendarCollector::class], 'patent-box.collectors');
The registry validates at boot that every tagged FQCN actually implements EvidenceCollector, and that no two collectors' supports() predicates overlap (R23).
Configuration reference
The full default config lives at config/patent-box-tracker.php. Key entries:
| Key | Type | Default | Notes |
|---|---|---|---|
patent-box-tracker.classifier.driver |
string | regolo |
Any laravel/ai provider key (openai, anthropic, gemini, regolo, ...). |
patent-box-tracker.classifier.model |
string | env('PATENT_BOX_MODEL', 'claude-sonnet-4-6') |
Default classifier model. Heavier models trade cost for F1. |
patent-box-tracker.classifier.temperature |
float | 0 |
Do not change unless you understand the audit-trail implications. |
patent-box-tracker.classifier.seed |
int | 0xC0DEC0DE |
Fixed seed for byte-identical re-runs. |
patent-box-tracker.classifier.batch_size |
int | 20 |
Commits per LLM call. Higher = cheaper, lower = more diff context per commit. |
patent-box-tracker.classifier.cost_cap_eur_per_run |
float | 50.00 |
Hard stop. Run aborts with exit code 2 before exceeding this projected cost. |
patent-box-tracker.regime |
string | documentazione_idonea |
Italian Patent Box regime. documentazione_idonea enables the penalty-protection contract under D.M. 6 ottobre 2022; non_documentazione is the alternative. |
patent-box-tracker.locale |
string | it |
Dossier locale. it ships in v0.1; en and others planned for v0.2. |
patent-box-tracker.excluded_authors |
array | ['dependabot[bot]', 'renovate[bot]', 'github-actions[bot]'] |
Author email substrings skipped during the git walk so bot-authored commits do not count as qualified R&D. |
patent-box-tracker.renderer.driver |
string | browsershot |
browsershot (Chromium) or dompdf fallback. |
patent-box-tracker.renderer.browsershot.chrome_path |
string|null | null |
Override the headless Chromium binary path; defaults to Browsershot's auto-detection. |
patent-box-tracker.renderer.browsershot.timeout |
int | 60 |
Browsershot render timeout (seconds). |
The W4.A scaffold ships exactly the keys listed above. Cost-model inputs (hourly_rate_eur, daily_hours_max), evidence-collector knobs (docs_paths, proximity_days, first_parent_only), and storage routing (disk) are added by W4.B / W4.C alongside the code that reads them β the documentation evolves with the feature surface, not ahead of it. Every key is also overridable per-call via the fluent builder or the YAML cross-repo config (W4.D).
Architecture
flowchart LR
subgraph Inputs[Inputs]
Repos[(Git repos)]
Docs[(docs/ + plans/)]
Yaml[/cross-repo YAML/]
Manual[/manual_supplement/]
end
subgraph Collectors[Evidence collectors β pluggable]
Git[GitSourceCollector]
Ai[AiAttributionExtractor]
Design[DesignDocCollector]
Branch[BranchSemanticsCollector]
end
subgraph Pipeline[Pipeline]
Batch[ClassifierBatcher]
Clf[CommitClassifier β temp=0, fixed seed]
Hash[HashChainBuilder]
Golden[GoldenSetValidator β release gate]
end
subgraph Storage[Storage]
TS[(tracking_sessions)]
TC[(tracked_commits)]
TE[(tracked_evidence)]
TD[(tracked_dossiers)]
end
subgraph Renderers[Renderers]
Pdf[PdfDossierRenderer β Browsershot + DomPDF]
Json[JsonDossierRenderer β hash-chain signed]
end
subgraph LLM[laravel/ai SDK]
Sdk[(any provider β Regolo / OpenAI / Anthropic / Gemini)]
end
Repos --> Git
Repos --> Ai
Docs --> Design
Repos --> Branch
Yaml --> Batch
Manual --> Pdf
Git --> Batch
Ai --> Batch
Design --> Batch
Branch --> Batch
Batch --> Clf
Clf <--> Sdk
Clf --> Hash
Hash --> TC
TC --> Golden
TC --> Pdf
TC --> Json
TS --> Pdf
TE --> Pdf
Pdf --> TD
Json --> TD
Loading
A few notes on the architecture:
- Collectors are independent. Each implements
EvidenceCollectorwith a non-overlappingsupports()predicate. v0.1 ships four; future versions add more without breaking the registry. - The classifier is the only LLM-touching component. Everything upstream is deterministic git + filesystem walk. Everything downstream is template rendering + cryptographic hashing. The non-determinism budget is bounded to one component, and that component runs at
temperature=0with a fixed seed. - Storage is the audit trail.
tracking_sessionsrecords the classifier provider / model / seed;tracked_commitsrecords the hash chain;tracked_dossiersrecords the SHA-256 of every rendered artefact. The dossier you hand to Agenzia delle Entrate is reproducible from the same DB at any point within the 5-year audit window.
π AI vibe-coding pack included
Every Padosoft package ships with the same
.claude/pack, so AI-driven contribution stays consistent across the family.
Every release of this package includes the Padosoft Claude pack under the .claude/ directory: the same skills, rules, agents, and slash-commands the Padosoft team uses internally to keep AI-driven development consistent across all our repos. The moment you composer require padosoft/laravel-patent-box-tracker and open the project in Claude Code, the agent automatically picks up the pack and applies it.
What ships in the pack
.claude/
βββ skills/
β βββ copilot-pr-review-loop/ β R36: 9-step PR flow (--reviewer copilot,
β β wait CI green, wait Copilot review,
β β fix, re-CI, merge only when both green)
β βββ branching-strategy-feature-vx/ β R37: feature/v*.*.x integration branches
β βββ ci-failure-investigation/ β R22: artefact-first CI failure protocol
β βββ pluggable-pipeline-registry/ β R23: FQCN validation + supports() mutex
β βββ docs-match-code/ β R9: README / config / migration drift gate
β βββ (more skills sync'd from sister padosoft/* packages)
βββ rules/ β Padosoft baseline coding rules
βββ agents/
βββ copilot-review-anticipator β pre-push review sub-agent
βββ classifier-prompt-tuner β package-specific golden-set tuner
Why this matters
When a contributor (you, a team-mate, an open-source PR author) opens this repo in Claude Code:
- The agent reads the pack on session start.
- The R36 PR-review skill kicks in automatically the first time the contributor types
gh pr create. The agent uses--reviewer copilot-pull-request-reviewer, waits for CI, waits for Copilot review, addresses comments, re-checks CI, and only merges when both gates are green. - Future skills (style enforcement, security review, release-note generator, ...) plug into the same pack with zero configuration on the consumer side.
The result: you get the Padosoft AI engineering culture in the same composer require that gets you the Patent Box tracker.
Opting out
Do not want the pack? Add .claude/ to your .gitignore (or delete it locally). The package code under src/ works completely independently of the pack β the pack is purely a developer-experience layer for repos that use Claude Code.
Want to contribute a skill?
The same pack is shared across padosoft/laravel-ai-regolo, padosoft/laravel-flow, padosoft/eval-harness, padosoft/laravel-pii-redactor, and padosoft/laravel-patent-box-tracker β open a PR on any of those repos and we will sync the skill across the family.
Testing
Default suite β offline, zero cost, runs everywhere
The package ships a comprehensive offline test suite that runs against Http::fake() for the LLM, recorded git fixtures for the repo walks, and synthetic docs/ trees for the design-doc correlator. The test suite never hits a real API and is safe to run in CI on every PR.
composer install vendor/bin/phpunit
Coverage breakdown:
| Suite | What it covers |
|---|---|
CommitClassifierTest |
Fixture commit + canned LLM response β expected CommitClassification. Http::fake() only. |
GitSourceCollectorTest |
Recorded bare repo tests/fixtures/repos/synthetic-r-and-d.git (30 commits, 5 phases) β exact metadata. |
AiAttributionExtractorTest |
12 commit-message variants β expected attribution flag (human / ai_assisted / ai_authored / mixed). |
DesignDocCollectorTest |
Synthetic docs/ tree β expected evidence-link graph (filename / slug / date-proximity matches). |
BranchSemanticsCollectorTest |
Branch-name patterns (feature/v4.0-W*, chore/, fix/) β expected semantics. |
HashChainBuilderTest |
Same input β same chain. Tampering one commit breaks the chain at that exact position. |
JsonDossierRendererTest |
Round-trip a synthetic session β JSON β re-parse β re-render produces byte-identical output. |
GoldenSetValidatorTest |
Synthetic golden set + synthetic classifier outputs β F1 score matches a hand-computed value. |
TrackCommandTest |
php artisan patent-box:track <fixture-repo> end-to-end with Http::fake(). No live HTTP calls. |
CrossRepoCommandTest |
YAML config + 3 fixture repos β one session with 3 repository-roles, cross-repo summary correct. |
RenderCommandTest |
Pre-populated session β PDF exists, page count > 1, JSON sidecar valid against schema. |
StandaloneAgnosticTest |
Architecture test β package source contains zero KnowledgeDocument / KbSearchService / lopadova/* references. |
CI matrix: PHP 8.3 / 8.4 / 8.5 Γ Laravel 12 / 13 (6 cells), plus a separate static-analysis job that runs PHPStan and Pint.
Running the live test suite (against a real LLM provider)
If you want to verify behaviour against real provider servers β for example you are validating a model upgrade, an open-source contributor confirming wire compatibility before tagging a release, or a downstream adopter doing a pre-deploy smoke-test β the package ships a dedicated Live PHPUnit testsuite that hits a real laravel/ai provider end-to-end.
The live suite is opt-in by design:
- A fresh
git clone+composer install+vendor/bin/phpunitruns only the offline suite. No accidental cost. - The
Livesuite self-skips whenPATENT_BOX_LIVE_API_KEYis missing, so it cannot accidentally fail a CI job that does not have credentials. - The CI matrix on this repo runs only the offline suite. Live tests run only when invoked explicitly with
--testsuite Live.
1. Configure the environment
The bare minimum is a single env var:
export PATENT_BOX_LIVE_API_KEY=rg_live_...
Optional overrides (defaults pick the same models the package ships as defaults):
export PATENT_BOX_LIVE_PROVIDER=regolo export PATENT_BOX_LIVE_MODEL=claude-sonnet-4-6 export PATENT_BOX_LIVE_FIXTURE_REPO=/path/to/a/small/test/repo
On Windows PowerShell:
$env:PATENT_BOX_LIVE_API_KEY = "rg_live_..."
2. Run the live suite
vendor/bin/phpunit --testsuite Live
β¦or, with the testdox formatter so each scenario prints by name:
vendor/bin/phpunit --testsuite Live --testdox
What the live suite verifies
| File | What it asserts on the real provider | Cost |
|---|---|---|
ClassifierLiveTest |
5 hand-picked real feature/v4.x commits β β₯ 80% match against the golden labels. |
~β¬0.05 |
RendererLiveTest |
Generates a real PDF and validates it opens in pdf-parser without errors. |
minimal |
Total cost per run: well under β¬0.10 with the default small-model selection. Pick a heavier model via PATENT_BOX_LIVE_MODEL if you want to validate a specific catalogue entry β the cost scales linearly.
CI policy
The live suite is never run from this package's .github/workflows/ci.yml. The matrix invokes vendor/bin/phpunit (default config = offline suite). To run the live suite in your own pipeline:
- name: Live verification (manual workflow_dispatch only) if: env.PATENT_BOX_LIVE_API_KEY != '' env: PATENT_BOX_LIVE_API_KEY: ${{ secrets.PATENT_BOX_LIVE_API_KEY }} run: vendor/bin/phpunit --testsuite Live
Open an issue or PR if you want a workflow_dispatch job added to this repo to support scheduled live verification.
Roadmap
| Version | Status | Highlights |
|---|---|---|
| v0.1 | code complete; tag pending | 4 evidence collectors + deterministic LLM classifier + Italian PDF + JSON sidecar + hash-chain tamper evidence + cross-repo YAML orchestration + cost-cap guard + hand-graded golden set β₯ 80% F1 + opt-in Live testsuite + AI vibe-coding pack. First public release. |
| v0.2 | planned | Time-tracking integrations (Toggl / Harvest / RescueTime / Clockify). Calendar collector for off-keyboard R&D. Live terminal session capture (Cursor / Claude Code). Token-by-token AI-vs-human line-level attribution. English locale for the dossier template. |
| v0.3 | planned | Direct UIBM / SIAE / EPO API integration β auto-link IP filings to dossier entries. |
| v1.0 | tracking | Web UI dashboard (Filament panel). Tax-jurisdiction support beyond Italy (UK Patent Box, Irish Knowledge Box, German FuE-Zulage). Multi-tenant SaaS deployment. |
Open issues and feature votes: github.com/padosoft/laravel-patent-box-tracker/issues.
Contributing
Contributions are welcome β bug reports, test cases, new evidence collectors, golden-set commits with commercialista-validated labels, documentation polish.
- Fork the repository.
- Create a feature branch (
feature/your-thing) targetingmain. - Run
vendor/bin/phpunitandvendor/bin/pint --testlocally. - Open a PR with a clear description, a test that covers the change, and (if you touch the classifier prompt) a re-run of the golden-set validator showing F1 β₯ 80%.
We follow the project conventions documented in CONTRIBUTING.md. Please respect the existing concern split (src/Sources/, src/Classifier/, src/Renderers/, src/Hash/) when adding capabilities β it keeps the package legible and audit-friendly.
Adding a new evidence collector
- Implement
Padosoft\PatentBoxTracker\Sources\EvidenceCollector. - Make sure your
supports()predicate does not overlap with any existing collector β the boot-time mutex check (R23) will refuse to register otherwise. - Tag the FQCN with
patent-box.collectorsin your service provider. - Add a Unit test that exercises the collector against a synthetic fixture.
Security
Found a security issue? Please do not open a public issue. Email lorenzo.padovani@padosoft.com instead. We follow standard responsible-disclosure timelines documented in SECURITY.md.
Special note on dossier integrity: if you discover a way to mutate a tracked dossier without breaking the hash chain, this is a critical finding β please flag it as such in the disclosure. The chain is the audit-trail anchor; an integrity break is a release blocker.
License & credits
Apache-2.0 β see LICENSE.
Built and maintained by Padosoft, authored by Lorenzo Padovani. Initially developed alongside AskMyDocs β and dogfooded against Padosoft's own FY2026 Italian Patent Box dossier β but the package is fully standalone agnostic: no AskMyDocs dependency, no Padosoft proprietary glue. It works in any Laravel 12/13 application that has laravel/ai installed.
Sister packages in the Padosoft AI stack:
padosoft/laravel-ai-regoloβ first-class Regolo (Italian sovereign AI) provider forlaravel/ai.padosoft/laravel-flowβ saga / workflow orchestration for Laravel.padosoft/eval-harnessβ RAG + agent evaluation harness.padosoft/laravel-pii-redactorβ PII redaction middleware for AI prompts.
Each is independently usable. None requires the others. Pick what you need.
Made with β in Italy by Padosoft.