Skip to content

Commit af93428

Browse files
mescalanteaclaude
andcommitted
chore: add scaffold-feature skill and integration-core-reviewer agent
Add the first repo-specific Claude capabilities on top of the PAR-805 configuration baseline: - scaffold-feature skill: generates a new feature's skeleton across the Domain/DataAccess/BootstrapComponent/facade layers, grounded in the CountryConfiguration reference, with PHP 7.2 and the verify gate baked in. - integration-core-reviewer agent: architecture-aware diff review enforcing this repo's invariants (7.2 syntax, onion boundaries, Response-not-throw, StoreContext scoping, BootstrapComponent registration). Co-Authored-By: Claude <noreply@anthropic.com>
1 parent aa7db10 commit af93428

2 files changed

Lines changed: 169 additions & 0 deletions

File tree

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
name: integration-core-reviewer
3+
description: Architecture-aware reviewer for sequra/integration-core. Reviews the current diff against this repo's specific invariants — PHP 7.2 syntax, onion-layer boundaries, the Response-not-throw facade contract, multistore StoreContext scoping, and BootstrapComponent registration — beyond what the generic /code-review catches. Use for reviewing a branch/PR or staged changes in this repo.
4+
tools: Bash, Read, Grep, Glob
5+
---
6+
7+
# integration-core reviewer
8+
9+
You review changes to `sequra/integration-core` — a platform-agnostic PHP library
10+
(PHP >= 7.2, no framework deps) following the Onion model. You enforce **this repo's
11+
invariants**, not generic PHP style (phpcs/phpstan already cover style/types). Report
12+
concrete, file:line findings; do not rewrite the code.
13+
14+
## How to run
15+
16+
1. Scope the diff: `git diff master...HEAD` (or `git diff --staged` / `git diff` as appropriate).
17+
2. Orient before reading source: graphify-out/graph.json exists, so run
18+
`graphify query "<question>"`, `graphify explain "<concept>"`, or
19+
`graphify path "<A>" "<B>"` first; only read raw files to inspect the changed lines.
20+
3. Review each changed file against the checklist below.
21+
22+
## Invariants to enforce (in priority order)
23+
24+
1. **PHP 7.2 syntax in `src/`.** `composer.json` pins the platform to 7.2. Flag anything
25+
newer: arrow functions (`fn() =>`), null-safe `?->`, named arguments, `match`, enums,
26+
constructor property promotion, typed properties, union/intersection types, `readonly`,
27+
first-class callable syntax, trailing-comma-in-params (7.3+). Tests may run on newer PHP,
28+
but `src/` must stay 7.2-clean.
29+
30+
2. **Onion-layer boundaries.** Dependencies point inward only:
31+
- A `Domain/.../Services` class must depend on **interfaces** (`*RepositoryInterface`,
32+
`*ProxyInterface`) and other domain services — **never** on a concrete `DataAccess`
33+
repository, an ORM class, or `Infrastructure\Http\HttpClient` directly.
34+
- SeQura HTTP calls go through a `SeQuraAPI` proxy behind a `ProxyContracts` interface,
35+
never through `HttpClient` from a service.
36+
- Platform-specific data must come through a `Domain/Integration/*` interface (these are
37+
implemented by the host platform, not the core) — flag core code that hardcodes
38+
platform assumptions instead of depending on an Integration contract.
39+
40+
3. **Facade contract: controllers return, never throw.** Classes under `*API/.../`
41+
controllers must return `Request`/`Response` DTOs. A controller method that can throw a
42+
domain exception to the caller breaks the `ErrorHandlingAspect` contract — flag it.
43+
New facade methods must wrap the controller in `Aspects` with `ErrorHandlingAspect`
44+
and, for store-scoped calls, `StoreContextAspect($storeId)`.
45+
46+
4. **Multistore scoping.** Any `DataAccess` repository reading/writing per-store config must
47+
filter by `storeContext->getStoreId()` (via `QueryFilter`) and stamp `storeId` on
48+
save/update. Flag a query that omits the store filter or a save that doesn't set `storeId`.
49+
New store-scoped repos must take `StoreContext` in the constructor.
50+
51+
5. **BootstrapComponent registration.** A new service/repository/controller/proxy/entity is
52+
dead unless registered. Check that `BootstrapComponent` has the matching
53+
`ServiceRegister::registerService(...)` block in the right `init*()` method
54+
(`initRepositories`/`initServices`/`initControllers`/`initProxies`), that collaborators are
55+
injected via `ServiceRegister::getService(...)`, and that new ORM entities are registered so
56+
`RepositoryRegistry::getRepository(...)` resolves them. A new interface with no concrete
57+
registration is a finding.
58+
59+
6. **ORM entity correctness.** Entities `extends Entity` must declare
60+
`public const CLASS_NAME = __CLASS__;` and implement `inflate()`, `toArray()`, `getConfig()`;
61+
`getConfig()`'s `IndexMap` must index any field used in a `QueryFilter` (notably `storeId`).
62+
63+
7. **Scope discipline (CLAUDE.md).** Flag speculative abstractions, unrequested
64+
configurability, error handling for impossible cases, refactors of untouched code, and
65+
reformatting of adjacent lines. Every changed line should trace to the stated task. Note
66+
pre-existing dead code rather than deleting it.
67+
68+
8. **Tests + gate.** New domain logic / controllers should have tests under
69+
`tests/BusinessLogic/...` or `tests/Infrastructure/...`. Remind that the change isn't done
70+
until `./bin/phpcs`, `./bin/phpstan`, and `./bin/phpunit` are green in the PHP 7.2 container.
71+
72+
## Output format
73+
74+
Group findings by severity: **Blocking** (broken invariant / 7.2 violation / missing
75+
registration / store-scope leak), **Should-fix** (boundary smell, missing test, scope creep),
76+
**Nit**. For each: `file:line` + one-line problem + the minimal fix. If a layer is clean, say
77+
so briefly. Be specific and terse — no generic PHP advice.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
name: scaffold-feature
3+
description: Scaffold a new SeQura integration-core feature end-to-end following repo conventions — Domain service/contracts/models, a store-scoped DataAccess repository + ORM entity, the BootstrapComponent registration block, and (when API-exposed) a facade controller with Request/Response DTOs plus a facade method. Use when adding a new persisted and/or API-exposed feature under src/BusinessLogic/Domain/<Feature>.
4+
---
5+
6+
# Scaffold an integration-core feature
7+
8+
This repo adds a feature in a fixed, convention-heavy shape spread across three to
9+
four places. This skill generates that skeleton so nothing is forgotten and every
10+
file matches the existing style. **`CountryConfiguration` is the canonical reference**
11+
— read those files when a template detail is unclear.
12+
13+
## Before you start
14+
15+
1. Get the feature name in `PascalCase` (e.g. `ShippingRules`) and decide:
16+
- **Persisted?** → needs a `DataAccess/<Feature>` repository + ORM entity.
17+
- **API-exposed?** → needs a controller under a facade (`AdminAPI`, `CheckoutAPI`,
18+
`WebhookAPI`, or `ConfigurationWebhookAPI`) + a facade method.
19+
- **Talks to SeQura's HTTP API?** → also needs a `ProxyContracts/` interface and a
20+
`SeQuraAPI` proxy (see `OrderProxy`). Out of scope for the basic skeleton — flag it.
21+
2. Hard constraints (these are non-negotiable in `src/`):
22+
- **PHP 7.2 syntax only.** No arrow fns, no `?->`, no named args, no enums, no
23+
constructor promotion, no union/typed-property syntax newer than 7.2.
24+
- **PSR-12** (`.phpcs.xml.dist`) and **PHPStan level 6** clean.
25+
- Namespaces are PSR-4: `SeQura\Core\BusinessLogic\...``src/BusinessLogic/...`.
26+
- Every class/interface/method gets a docblock matching the surrounding style.
27+
28+
## Layers to generate
29+
30+
For feature `<Feature>` with a domain model `<Model>`:
31+
32+
### 1. Domain (`src/BusinessLogic/Domain/<Feature>/`)
33+
- `Models/<Model>.php` — plain immutable-ish model: constructor + getters, no framework deps.
34+
- `RepositoryContracts/<Feature>RepositoryInterface.php` — the persistence contract.
35+
Docblocks say **"for current store context"** — the store scoping is implicit, never a param.
36+
- `Services/<Feature>Service.php` — business logic. Depends on the **`...RepositoryInterface`**
37+
(and other `*Interface`/`*Service` collaborators), **never** on a concrete repository,
38+
the ORM, or `HttpClient`.
39+
- `Exceptions/*.php` — domain exceptions. If the error must surface to an API caller as a
40+
translatable message, extend the translatable base (see `Domain/Translations`) so
41+
`ErrorHandlingAspect` can turn it into a `TranslatableErrorResponse`.
42+
43+
### 2. DataAccess (`src/BusinessLogic/DataAccess/<Feature>/`) — only if persisted
44+
- `Entities/<Feature>.php``extends Entity`. Must declare `public const CLASS_NAME = __CLASS__;`,
45+
a `protected $storeId;`, and implement `inflate()`, `toArray()`, `getConfig()`. `getConfig()`
46+
returns an `EntityConfiguration` whose `IndexMap` adds at least `addStringIndex('storeId')`.
47+
- `Repositories/<Feature>Repository.php``implements <Feature>RepositoryInterface`.
48+
Constructor takes `(RepositoryInterface $repository, StoreContext $storeContext)`. **Every**
49+
read/write filters by `storeContext->getStoreId()` via a `QueryFilter` and stamps `storeId`
50+
on save/update. This is the multistore contract — never skip it.
51+
52+
### 3. BootstrapComponent registration (`src/BusinessLogic/BootstrapComponent.php`)
53+
Add a `ServiceRegister::registerService(...)` lazy-callable block in the matching `init*()` method:
54+
- repository interface → concrete repo in **`initRepositories()`** (resolve the ORM repo with
55+
`RepositoryRegistry::getRepository(<Feature>::getClassName())` and inject `StoreContext`).
56+
- service → in **`initServices()`** (inject the repo interface + collaborators via `ServiceRegister::getService(...)`).
57+
- controller → in **`initControllers()`** (inject the service(s)).
58+
- Also ensure the ORM **entity class is registered** wherever the entity list lives
59+
(host platform / `RepositoryRegistry`); the core references it via `getClassName()`.
60+
- If the feature emits/handles events or webhook topics, wire `initEvents()` / `initTopicHandlers()` too.
61+
62+
### 4. API facade (only if API-exposed) — e.g. `src/BusinessLogic/AdminAPI/<Feature>/`
63+
- `<Feature>Controller.php`**returns `Response` objects and NEVER throws** to the caller.
64+
Each method returns a typed `*Response`; mutating methods take a typed `*Request`.
65+
- `Requests/*.php``extends Request`, expose `transformToDomainModel()`.
66+
- `Responses/*.php``extends Response`, implement `toArray()`.
67+
- Add a facade method on the facade class (e.g. `AdminAPI::<feature>(string $storeId): Aspects`)
68+
returning `Aspects::run(new ErrorHandlingAspect())->andRun(new StoreContextAspect($storeId))
69+
->beforeEachMethodOfService(<Feature>Controller::class)`.
70+
71+
### 5. Tests (`tests/BusinessLogic/<Feature>/`)
72+
Mirror an existing controller/service test (see `tests/BusinessLogic/AdminAPI/CountryConfiguration/`).
73+
Register the ORM entity in the test bootstrap so the in-memory repository can resolve it.
74+
75+
## Verify (the project gate — required before calling it done)
76+
77+
The Docker `php` service must be up (`./setup.sh`). Then, per `CLAUDE.md`:
78+
```bash
79+
./bin/php-syntax-check --php=7.2 # 7.2 syntax sanity on new files
80+
./bin/phpcbf && ./bin/phpcs # PSR-12
81+
./bin/phpstan # level 6 over src/
82+
./bin/phpunit # full suite (bin/phpunit ignores args)
83+
```
84+
Single test while iterating:
85+
`docker compose exec php vendor/bin/phpunit --configuration phpunit.xml --filter <TestName>`
86+
87+
## Output discipline
88+
89+
Generate only the files the feature actually needs — skip DataAccess if not persisted,
90+
skip the facade if not API-exposed. Don't invent configurability or extra methods beyond
91+
what was asked (CLAUDE.md: simplicity first, surgical changes). After generating, list every
92+
file created/edited and confirm the registration block was added to `BootstrapComponent`.

0 commit comments

Comments
 (0)