PHPackages                             codeheures-fr/scrutineer - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. codeheures-fr/scrutineer

ActiveSymfony-bundle[Testing &amp; Quality](/categories/testing)

codeheures-fr/scrutineer
========================

Scrutineer — in-app manual acceptance testing console. Framework-agnostic, contract-driven (PHP back).

v0.1.3(yesterday)017↓37.5%MITPHP &gt;=8.2

Since Jul 7Compare

[ Source](https://github.com/CODEheures/scrutineer-symfony)[ Packagist](https://packagist.org/packages/codeheures-fr/scrutineer)[ RSS](/packages/codeheures-fr-scrutineer/feed)WikiDiscussions Synced today

READMEChangelogDependencies (8)Versions (4)Used By (0)

Scrutineer — back Symfony
=========================

[](#scrutineer--back-symfony)

**Scrutineer** est une librairie **bicéphale** (back + front, pilotée par **contrat**) qui ajoute à *votre*application une **console de recette manuelle** : préconditions pré-seedées, scénarios joués depuis vos vrais écrans, résultats horodatés et historisés — **sans coupler la lib à votre domaine**.

Ce dépôt est le **back** — **une** implémentation de référence (bundle Symfony, PHP). Le front vit dans un dépôt à part : 👉 **[scrutineer-front](https://github.com/CODEheures/scrutineer-front)** (Web Component, sans build). Les deux se parlent **uniquement** via le **contrat** (OpenAPI + JSON Schema), qui vit **côté front**([scrutineer-front/contract](https://github.com/CODEheures/scrutineer-front/tree/main/contract)) : le front est le consommateur invariant, ce back n'est qu'un back **conforme au contrat** (PHP aujourd'hui, un autre langage demain).

> Conception &amp; décisions techniques : [`docs/technical-decisions.md`](docs/technical-decisions.md).

Ce que fournit la lib / ce que vous câblez
------------------------------------------

[](#ce-que-fournit-la-lib--ce-que-vous-câblez)

Fournit (la lib)Vous câblez (l'hôte)surface HTTP (`catalog` / `availability` / `reset` / `events` / `history` / `publish`)les **adaptateurs** des portsstore d'historique par défaut (Doctrine, surchargeable)où il vit (`.env` / config)console front `` servie par le backun `` + 1 appel d'initorchestration reset + gating modevos **scénarios** + vos **seeds****capture des mails de recette** (optionnel) → boîte in-console scopée par posteactiver + recopier 1 header sur le mail**La lib ne connaît pas votre domaine.** Tout le métier vit derrière les ports.

Installation (back)
-------------------

[](#installation-back)

### 1. Installer le package

[](#1-installer-le-package)

```
composer require codeheures-fr/scrutineer
```

### 2. Enregistrer et configurer le bundle

[](#2-enregistrer-et-configurer-le-bundle)

```
// config/bundles.php
return [
    // …
    CODEHeures\Scrutineer\Bridge\Symfony\ScrutineerBundle::class => ['all' => true],
];
```

```
# config/packages/scrutineer.yaml
scrutineer:
    enabled: '%env(bool:SCRUTINEER_ENABLED)%'   # la SEULE barrière serveur (403 si false)
    asset_path: '%kernel.project_dir%/vendor/… /scrutineer-console.js'  # ou le front vendoré
    language: fr
    table: scrutineer_test_event                # nom du journal (schéma-qualifiable)
    # connection: shared                        # connexion DBAL du journal (défaut = celle par défaut)
```

### 3. Monter les routes sous un préfixe

[](#3-monter-les-routes-sous-un-préfixe)

```
# config/routes.yaml
scrutineer:
    resource: '@ScrutineerBundle/Resources/config/routes.php'
    prefix: /scrutineer
```

### 4. Configurer `security.yaml`

[](#4-configurer-securityyaml)

Vos routes de recette (§3) montées, autorisez-les. Celles **lues avant authentification** (le catalogue sur `/login`, la boîte mail pendant un login 2FA) passent en `PUBLIC_ACCESS` — la barrière reste `SCRUTINEER_ENABLED`(403 si off), pas la session ; les mutations exigent un user connecté. `access_control` est **first-match-wins**(règles publiques **avant** le fourre-tout) :

```
# config/packages/security.yaml
    access_control:
        - { path: ^/scrutineer/catalog$,      roles: PUBLIC_ACCESS }
        - { path: ^/scrutineer/availability$, roles: PUBLIC_ACCESS }
        - { path: ^/scrutineer/console,       roles: PUBLIC_ACCESS }
        - { path: ^/scrutineer/poste$,        roles: PUBLIC_ACCESS }   # capture mail (§7)
        - { path: ^/scrutineer/mails$,        roles: PUBLIC_ACCESS }   # capture mail (§7)
        - { path: ^/scrutineer/,              roles: IS_AUTHENTICATED_FULLY }
```

### 5. Implémenter les ports

[](#5-implémenter-les-ports)

3 requis (`ScenarioSeeder`, `ScrutineerContextProvider`, `CatalogProvider`) + 3 optionnels (`ActorResolver`, `HistoryStore` — un défaut Doctrine existe —, `TicketPublisher`). Chaque port est une interface **documentée dans [`src/Port/`](src/Port/)** (signatures + docblocks) ; liez chaque interface à votre implémentation avec l'attribut **`#[AsAlias(::class)]`** posé sur la classe (ou, si vous préférez, un alias dans `config/services.yaml`).

👉 **Exemple complet et autonome** — un domaine **fictif** « Acme Books » (en mémoire, données factices), les **6 ports** câblés bout à bout + le wiring : **[`example/`](example/)**.

### 6. Appliquer le schéma du journal

[](#6-appliquer-le-schéma-du-journal)

Le **seul** objet de schéma que la lib ajoute (sans FK, sans PII). Générez la migration **dans votre propre pipeline** (elle respecte votre config `doctrine_migrations`) :

```
php bin/console scrutineer:generate-migration
php bin/console doctrine:migrations:migrate
```

*(Capture des mails activée → la même commande émet aussi la table `scrutineer_captured_mail`.)*

### 7. (Optionnel) Capturer les mails d'un domaine

[](#7-optionnel-capturer-les-mails-dun-domaine)

En recette prod-like (pas de serveur mail), les mails aux personas seedés (2FA, invitation, reset) n'ont pas de boîte réelle → on les **capture** dans une boîte in-console (scopée par « poste ») au lieu de les envoyer. Nécessite `symfony/mailer`.

**1. Activer** (`config/packages/scrutineer.yaml`, sous `scrutineer:`) :

```
    mail_capture:
        enabled: true                    # off par défaut — la lib ne touche votre mailer que si activé
        domain: scrutineer.invalid       # destinataires @ → capturés (seedez vos personas ainsi)
        table: scrutineer_captured_mail  # 2e (et seul autre) objet de schéma de la lib
        retention_days: 7                # purge auto au mint du jeton de poste (aucun cron)
```

**2. Poser le jeton de poste sur le mail sortant** — le **seul contrat hôte**. La lib lit puis strippe ce header au transport ; à vous de l'y mettre (sync, Messenger, workers… votre ressort) :

```
// $posteToken = cookie httpOnly `scrutineer_poste` (posé par POST /poste), présent sur la requête
$email->getHeaders()->addTextHeader('X-Scrutineer-Poste', $posteToken);
```

La console gagne alors un onglet « Boîte mail » ; le tag `sha256(jeton)` isole les recetteurs en // sur la même persona. Routes `/poste` + `/mails` à exposer en public (cf. §4).

Le front (dépôt compagnon)
--------------------------

[](#le-front-dépôt-compagnon)

La console est un Web Component servi par ce back (`GET /console.js`). Côté page, chargez le script et posez l'élément — voir **[scrutineer-front](https://github.com/codeheures/scrutineer-front)** pour les attributs (`base`, `lang`, `active`, `auth-token`) et les cas même-origine / cross-origine.

Licence
-------

[](#licence)

[MIT](LICENSE).

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance100

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

3

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3c6c54e44e49e9e78b59cb60df0110a7f783953354a4dc390d9166ff0cebb3e1?d=identicon)[codeheures-fr](/maintainers/codeheures-fr)

### Embed Badge

![Health badge](/badges/codeheures-fr-scrutineer/health.svg)

```
[![Health](https://phpackages.com/badges/codeheures-fr-scrutineer/health.svg)](https://phpackages.com/packages/codeheures-fr-scrutineer)
```

###  Alternatives

[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.9M389](/packages/easycorp-easyadmin-bundle)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

85036.3k](/packages/flow-php-flow)[open-dxp/opendxp

Content &amp; Product Management Framework (CMS/PIM)

9421.6k64](/packages/open-dxp-opendxp)[chameleon-system/chameleon-base

The Chameleon System core.

1028.7k5](/packages/chameleon-system-chameleon-base)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.6M582](/packages/shopware-core)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
