Claude 44b5163713 WP-05 : moteur de règles de convention, effectif-daté
Le planning cesse d'être un tableur : il est confronté aux durées légales et
conventionnelles à chaque écriture.

Domaine — dix-huit règles pures
- `src/domain/compliance/` : chaque règle est une fonction pure, testée à sa
  borne exacte. La valeur limite passe, un cran au-delà déclenche — c'est la
  seule forme de test qui protège d'une inégalité écrite à l'envers, et une
  inégalité à l'envers sur un repos quotidien est une infraction que personne
  ne verra.
- 58 tests de règles, 14 sur les tranches d'heures : 43 h donnent 8 h à +25 %,
  45 h donnent 8 h à +25 % et 2 h à +50 %.

Aucune valeur dans le code
- Les seuils vivent en base (`CollectiveAgreement.parameters`), validés par un
  schéma Zod qui refuse un jeu amputé : un seuil manquant lu comme `undefined`
  désactiverait silencieusement une règle de sécurité.
- Un test charge deux jeux différents et vérifie que les résultats diffèrent.
- `MAX_DAILY_AMPLITUDE` reste muette : l'IDCC 1517 ne fixe pas d'amplitude
  quotidienne. Inventer une borne ferait désactiver l'ensemble des alertes par
  le premier manager excédé.

Effectif-datage — exigence n° 1 de la matrice
- Les versions de convention coexistent ; un trigger PostgreSQL refuse de
  réécrire le contenu d'une version publiée, tout en laissant enregistrer une
  approbation postérieure.
- Chaque constat mémorise la version appliquée. Un test d'intégration pose deux
  versions et vérifie qu'une semaine de mars n'est pas jugée sur la règle
  publiée en juillet.

Dimanche — la double contrepartie
Le taux de 100 % ne vient pas de la convention : l'IDCC 1517 n'en fixe aucun, et
l'entreprise n'a pas d'accord. Il vient de l'article L3132-27, qui impose la
rémunération doublée **et** un repos compensateur d'égale durée. Le moteur
produit les deux ; un test échoue si l'un manque. Le quota des douze dimanches
du maire est opposable, avec la liste arrêtée par établissement.

Restitution
- Les bloquants — chevauchement, créneau pendant une absence — annulent la
  transaction : mieux vaut refuser une saisie que garder un planning dont les
  heures se comptent deux fois.
- Les avertissements se franchissent, avec un motif enregistré sur chaque
  constat et une entrée d'audit. Le constat acquitté reste affiché avec sa
  justification : le faire disparaître donnerait l'illusion qu'il a été résolu
  alors qu'il a été assumé.
- Une modification revalide la semaine **et ses voisines** : le repos entre
  dimanche soir et lundi matin appartient à deux semaines.

Règles de mineurs — non implémentées, volontairement
La matrice ne couvre que les majeurs et le dossier n'a aucune source primaire
sur les moins de 18 ans. Les codes sont réservés, les seuils absents. Les
inventer donnerait une fausse assurance sur la population que le droit protège
le plus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
2026-08-08 07:38:39 +00:00
2026-08-07 17:54:11 +00:00
2026-08-07 17:54:11 +00:00
2026-08-07 17:54:11 +00:00
2026-08-07 17:54:11 +00:00
2026-08-07 17:54:11 +00:00
2026-08-07 17:54:11 +00:00
2026-08-07 17:54:11 +00:00
2026-08-07 17:54:11 +00:00
2026-08-07 17:54:11 +00:00
2026-08-07 17:54:11 +00:00

PlanFlow

Gestion du personnel, des plannings et des temps, multi-établissements, auto-hébergée. La paie est exportée vers Silae ; PlanFlow ne produit ni bulletin ni DSN.

La spécification de construction est PLAN.md. Elle est normative : en cas d'écart entre le code et le plan, c'est le plan qui a raison, ou le plan qui doit être corrigé — jamais l'écart qui s'installe.

Document Rôle
PLAN.md Spécification : périmètre, modèle de données, règles, lots de travail
matrice-conformite-rh-france-2026.md Exigences réglementaires françaises, cotées P0/P1/P2
Audit Combo/ Audit fonctionnel du produit de référence

État

WP-00 — socle et implémentation du design : six écrans sur données de démonstration.

WP-01 — tenancy, identité, autorisation : en cours. Modèle de données, RLS, journal d'audit append-only, catalogue de 70 capacités, cinq rôles, sessions en base. Les écrans lisent encore src/lib/demo.

Démarrer

Avec Docker

cp .env.example .env
# Renseigner POSTGRES_PASSWORD et ENCRYPTION_KEY (voir ci-dessous)
docker compose up --build

L'application écoute sur http://localhost:3000. Les migrations s'appliquent au démarrage du conteneur.

En local

Nécessite Node 22, pnpm 10 et un PostgreSQL 16 accessible.

pnpm install
cp .env.example .env          # renseigner DATABASE_URL et ENCRYPTION_KEY
pnpm db:generate
pnpm db:deploy
pnpm dev

Clé de chiffrement

ENCRYPTION_KEY chiffre au repos les colonnes sensibles exigées par le plan (§3.6) : NIR, IBAN, BIC.

openssl rand -base64 32

Elle vit hors de la base : une sauvegarde volée ne doit pas suffire à lire ces colonnes. La perdre rend ces données irrécupérables — la sauvegarder séparément et documenter sa rotation.

Vérifier

pnpm verify      # typecheck + lint + tests unitaires
pnpm test:e2e    # build, serveur standalone, tests de bout en bout

pnpm verify est ce que la CI exécute sur chaque pull request, suivi du build et des tests end-to-end.

Configuration de la base — à ne pas rater

L'application ne doit pas se connecter en superutilisateur PostgreSQL.

Un superutilisateur contourne la row-level security, y compris déclarée en FORCE. Connecter PlanFlow avec un tel compte désactive silencieusement la seconde couche d'isolation multi-tenant : les requêtes fonctionnent, les tests applicatifs passent, et rien n'indique que la protection a disparu — jusqu'au jour où quelqu'un lit les données d'un autre établissement.

CREATE ROLE planflow_app LOGIN PASSWORD '…' NOSUPERUSER NOBYPASSRLS;
GRANT USAGE ON SCHEMA public TO planflow_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO planflow_app;

Les migrations, elles, s'appliquent avec un compte propriétaire distinct.

L'application vérifie ce point au démarrage : elle refuse de démarrer en production sur une base mal configurée, et se contente d'un avertissement en développement. GET /api/sante expose l'état sous tenantIsolation.

Choix structurants

Aucun traceur tiers. L'audit du produit de référence a intercepté 2102 requêtes de traçage — Segment, LinkedIn Ads, Google Ads, DoubleClick, Clarity, Hotjar — et aucune requête métier. Une application RH ne doit pas envoyer un contexte de navigation portant sur des salariés identifiables à des régies publicitaires. Deux garde-fous rendent la règle vérifiable plutôt que déclarative :

  • une Content-Security-Policy qui ne nomme aucune origine externe, posée par requête avec un nonce (src/proxy.ts) ;
  • un test qui échoue si une dépendance de traçage apparaît dans package.json.

Pour de la télémétrie technique, passer par une interface abstraite auto-hébergée.

Le serveur testé est celui qui est déployé. Les tests end-to-end lancent le serveur standalone, celui que l'image Docker exécute — pas next dev, dont la politique de sécurité est volontairement plus permissive.

Structure

src/
├── app/                 écrans (App Router)
├── lib/
│   ├── env.ts           contrat d'environnement, validé à l'import
│   └── security/csp.ts  politique de sécurité, fonction pure et testable
├── server/
│   ├── db.ts            client Prisma — le scoping multi-tenant s'y greffe au WP-01
│   └── health.ts
└── proxy.ts             en-têtes de sécurité par requête
prisma/                  schéma et migrations
tests/
├── unit/                Vitest
└── e2e/                 Playwright

Écarts assumés par rapport au plan

Trois choix diffèrent de ce qu'annonçait PLAN.md §2, et le plan a été mis à jour en conséquence.

Sujet Plan initial Retenu Raison
Next.js 15 16.3 Version stable courante ; démarrer un greenfield une majeure en retard n'a pas de contrepartie.
Authentification Auth.js v5 Sessions maison en base Auth.js v5 est encore en beta. Le besoin se limite à identifiants et invitation, sans OAuth, et la matrice de conformité (n° 23) impose la révocation de session — immédiate avec des sessions en base, malaisée avec des jetons JWT.
Convention Next middleware.ts proxy.ts Next 16 a renommé la convention ; middleware est déprécié.
S
Description
No description provided
Readme
15 MiB
0 Stars 1 Watchers 0 Forks
Languages
TypeScript 97%
CSS 1.2%
Shell 0.7%
JavaScript 0.5%
PLpgSQL 0.4%
Other 0.2%