r-io suiter-io suitededuction docs
← All strategies
Deduction strategy / Drools / default profile

Drools Simple Satisfaction

The default rule-driven deduction strategy of r-io suite. Three Drools rules decide which objectives to keep, which function graph to prefer and which actor to assign; the surrounding Java campaign explores risk configurations, calls the Default planner and publishes unique collaborative processes.

identifierdrools:drools-default-simple-satisfaction-strategy:1
Two strategies, four Drools profiles

r-io exposes two AbstractProcessGenerator implementations: the native Basic Bloc Satisfaction (native pertinence and expertise ranking, one sequential sub-task per objective) and this Drools generator. Crisis, Healthcare and Production are published profiles of the same Drools pipeline with different scoring rules, not separate algorithms. Simple Satisfaction is the profile the generator requires to exist and to be published.

00 · At a glance

What goes in, what is decided, what comes out

Inputs

From the model and the dialog

  • The Objectives_Manager selected in r-ioded, r-iota or r-ioga and its Plans edges.
  • Satisfies, Needs, Implies, Supports, Provides, Consumes relations of the collaborative model.
  • satisfactionProperties: planner, actor selection mode, resource-combination cap, time limit, campaign configuration (preventive and corrective potentialities).
Decisions

Taken by the rules

  • Objective selection: accept or reject each DroolsObjectiveFact.
  • Function ranking: score every DroolsFunctionCandidateFact, here by minimizing the graph KPI duration.
  • Actor ranking: score every DroolsActorCandidateFact, here by minimizing the graph KPI cost.
Outputs

Persisted by the campaign

  • Campaign solutions (campaignSolution) holding a SolutionProcessTask/Sequence_Flow model with expected dates and assigned resources.
  • Progress, scenario and error results streamed to the caller through the deduction callback.
  • In first-good mode, the first unique solution is also published to the graph synchronously.
01 · Positioning

Where the strategy sits in the deduction chain

The strategy never receives HTTP calls directly. It is instantiated by the deduction service of the governance container, which discovers it through ServiceLoader and runs it inside a campaign.

UIDeduction dialogcreateDroolsStrategyDialog opens the Simple Satisfaction input component (r-ioded, r-iota, generic app).
RESTSolutionResourcePOST …/r-ioga/solution/deduceSolutionASync builds a GJaxbDeduceASync with the callback port.
SOAPGovDeductionDeductionImpl.deduceASync: single operation of the governance container.
SPIDroolsProcessGeneratorOne variant per published registry definition; identifier drools:<id>:<version>.
CampaignDroolsStrategyDeductionCampaignBuilds facts, fires rules, explores combinations, drives the 4-task pipeline.
PlanRioDefaultPlannerActor ranking injected by the strategy; resources, scheduling, expected dates.
StoreCampaign manageraddSolution into solutionsCampaigns; progress sent back through deductionASyncCallBackResponse.

Because variants are enumerated from DroolsStrategyRegistry.listPublishedDefinitions(), publishing a new rule version in r-ioded immediately adds a selectable strategy without redeploying any Java module. The generator refuses to start if the Simple Satisfaction definition is missing or unpublished (IllegalStateException: The default Drools strategy is not published).

02 · Pipeline

The decision path, step by step

Entry point: doDeduceProcessFromOneObjectivesManager(campaign, mode, collaboration, knowledgeSpace, objectivesManager, data, project, resultsQueue).

01

Read the execution parameters

The satisfactionProperties array is parsed into the planner type (selectedPlanningSolver, Default by default), the actor selection mode (actorsSelectionMode, PERSON_FIRST), the resource-combination cap, the time limit, the move-improver flag and the campaign configuration that carries the preventive and corrective potentialities chosen by the user.

02

Filter objectives with the rules

Every Plans edge of the objectives manager becomes a DroolsObjectiveFact(id, name, priority). The rules run once; if they emit no decision at all, every objective is kept, otherwise only accepted objectives survive (objectivesManager.filterPlans). An empty result stops the campaign with an IllegalStateException. Each decision is traced in the campaign observation.

03

Expand the risk configurations

For each potentiality or actuality of the campaign configuration, the objectives it groups receive one selected function each (inner Cartesian product), then a NONE alternative is added per group and the outer product enumerates the campaign combinations. Preventive and corrective candidates are ordered with the same Drools rules; if scoring fails, the order sent by the front end is kept.

04

Find feasible function graphs

For each retained objective, Satisfies relations give the candidate functions; Needs, Implies and Supports expand each candidate into a recursive function graph. A candidate is discarded when no actor Provides at least one function of its graph.

05

Rank function candidates

Each candidate is exposed as a DroolsFunctionCandidateFact with pertinence, priority, the consumed resources and a calculateGraphKPI(name) method. The rules score or reject candidates through the collector; the ranking is a descending sort on the merged score, with natural pertinence as fallback when no rule scored anything.

06

Rank actors and allocate resources

The strategy injects rankActorCandidates into the planner (planner.setActorCandidateRanker). For every function of a plan, actors that provide it become DroolsActorCandidateFacts with their expertise; the rules score them, expertise is the fallback. ResourceAssigner then resolves fixed and movable resources, either the best assignment or every permitted one.

07

Create, plan and value the process

Chosen functions and assignments become Solution, Process, Task and Sequence_Flow elements; solution.planify() and the Default planner compute expected start and end dates (never below one minute per task) and KPIL indicators. Resource-assignment failures are reported as errors without crashing the campaign.

08

Deduplicate and publish

A campaign-wide fingerprint set (CampaignSolutionHelper.createSignature) drops equivalent function, actor, resource and procedure combinations. Unique solutions are stored through the campaign manager; in FIRST_GOOD_SOLUTION mode the first one is also pushed to the graph and the campaign ends.

High-level pseudo-codeDroolsStrategyDeductionCampaign
definition   = registry.published("drools-default-simple-satisfaction-strategy")
objectives   = rules(definition, ObjectiveFact(plans.objective, priority))     # keep all if no decision
combinations = cartesian(groupedPotentialityChoices + NONE per group)
if mode == FIRST_GOOD_SOLUTION: combinations = reverse(combinations)           # start with the fully specified one

single-thread executor, for combination in combinations:
  plansQueue, solutionQueue = new LinkedBlockingQueue()
  task1: every second report RUNNING / ENDED / CRASHED with counts and estimate
  task2: candidates = findBestOrAllPlansWithFunctions(objectives, combination)
         ranked     = rules(FunctionCandidateFact(...)) or pertinence
         plansQueue <- best plan | every plan (ALL_SOLUTIONS) ; PLAN_POISON_PILL
  task3: for plan in plansQueue:
           planner = RioDefaultPlanner(actorRanker = rules(ActorCandidateFact(...)))
           for assignment in planner.solve(plan): solutionQueue <- createSolution(plan, assignment)
         SOLUTION_POISON_PILL
  task4: for solution in solutionQueue:
           if fingerprints.add(signature(solution)): campaignManager.addSolution(solution); results <- ScenarioResult
           if mode == FIRST_GOOD_SOLUTION: publishModelSynchronously(solution); stop
03 · Rules

The three rules, exactly as shipped

The rules are not .drl files: each profile is a Java text block (STRATEGY_RULES) inside default_strategies/, seeded into the registry at first start. They share one package, three fact imports and a single global, collector. No salience, no agenda group, no insert or modify: rules only talk to the collector.

Objective selectionDroolsObjectiveFact
rule "Accept every objective"
when
  $objective : DroolsObjectiveFact()
then
  collector.accept($objective.getId());
end
Every planned objective is kept. Replace the pattern by a condition on getPriority() or getName() to filter.
Function rankingDroolsFunctionCandidateFact
rule "Rank functions by duration"
when
  $candidate : DroolsFunctionCandidateFact()
then
  collector.minimizeScoreCandidate($candidate,
    $candidate.calculateGraphKPI("duration"));
end
The shortest function graph wins. duration is evaluated on a temporary process rebuilt from the graph (section 05).
Actor rankingDroolsActorCandidateFact
rule "Rank actors by cost"
when
  $candidate : DroolsActorCandidateFact()
then
  collector.minimizeScoreCandidate($candidate,
    $candidate.calculateGraphKPI("cost"));
end
The least expensive actor wins. Note: the Java description of the profile still says “actors by expertise”; the executable rule is cost.
FactData exposed to DRLCollector callsFallback
DroolsObjectiveFactgetId(), getName(), getPriority()accept(id), rejectObjective(id)No decision at all → every objective retained
DroolsFunctionCandidateFactcandidate, objective and function ids/names, getPertinence(), getPriority(), getAllGraphConsumedResources(), calculateGraphKPI(name)maximizeScoreCandidate, minimizeScoreCandidate, rejectFunctionCandidateNo score → natural pertinence
DroolsActorCandidateFactcandidate (= actor id), objective and function ids/names, getExpertise(), calculateGraphKPI(name)maximizeScoreCandidate, minimizeScoreCandidate, rejectActorCandidateNo score → natural expertise
Score normalizationDroolsDecisionCollector
minimizeScoreCandidate(c, v) = scoreCandidate(c, -v)
maximizeScoreCandidate(c, v) = scoreCandidate(c, v)
score[c] = max(score[c], v)         # merged with Math::max
reject*(c)                          # removes the score; a later rule may re-score
ranked = candidates with a score, sorted descending
unknown fact type → IllegalArgumentException
04 · Profiles

Same pipeline, four published scoring policies

All four definitions are system defaults (systemDefault=true, version 1 PUBLISHED, SHA-256 checksum, one history entry). Only the DRL text and the favorite domain differ; campaign algorithm, facts, planner and persistence are shared.

ProfileIdentifier · domainFunction ruleActor rule
Simple Satisfaction defaultdrools-default-simple-satisfaction-strategy
collaborative
Rank functions by durationminimize calculateGraphKPI("duration")Rank actors by costminimize calculateGraphKPI("cost")
Crisis Satisfactiondrools-default-crisis-satisfaction-strategy
crisis
Rank functions by pertinencemaximize getPertinence()Rank actors by expertisemaximize getExpertise()
Healthcare Satisfactiondrools-default-healthcare-satisfaction-strategy
health
Multi-criteriamaximize 0.7 × pertinence + 0.3 / (1 + duration / 60)Multi-criteriamaximize 0.6 × expertise + 0.4 / (1 + cost / 100)
Production Satisfaction caveatsdrools-default-production-satisfaction-strategy
production
Pertinence and resource parsimonymaximize 0.5 × pertinence + 0.5 / (1 + consumedResources / 5)As written in sourcemaximize calculateGraphKPI("cost")

The Simple Satisfaction text block is the only public one: r-ioded uses it as the template when a new draft is created. The Production profile carries two source-level caveats detailed in the watch points.

05 · Graph KPI

Computing duration or cost before the process exists

A ranking rule may need duration or cost while no process has been generated yet. DroolsFunctionCandidateFact.calculateGraphKPI therefore rebuilds a lightweight temporary process from the recursive function graph, copies the theoretical indicators of each function into simulated task values, and evaluates the requested KPIL indicator on the main task with HLIndicatorManager.

StartStart_Event
Pre-functionNeeds
SupportSupports
Main functionTask evaluated by HLIndicatorManager
Post-functionImplies
EndEnd_Event
Model relationTemporary process translation
main Needs preTask(pre) → Task(main)
main Implies postTask(main) → Task(post)
main Supports supportTask(support) → Task(main)
No incoming / outgoing flowConnected to Start_Event / End_Event
Temporary graph reconstructionbuildProcessFromFunctionRecursively
functions = unique(main + main.allFunctionsRecursively)
for f in functions:
  task[f] = Task(f.name); copy f.theoretical indicators → task.simulated
for f in functions:
  for pre in f.needs:        edge(task[pre], task[f])
  for post in f.implies:     edge(task[f], task[post])
  for s in f.supports:       edge(task[s], task[f])
connect Start_Event → tasks without incoming flow
connect tasks without outgoing flow → End_Event
process = Process.buildFromGenericModel(nodes, edges)
return indicatorManager.evaluateIndicatorOnTask("Function:" + name, mainTask)

The actor fact reuses the same mechanism: DroolsActorCandidateFact.calculateGraphKPI checks that a Resource:<name> indicator exists, then evaluates Function:<name> on the main task. That mixed lookup is listed in the watch points.

06 · Combinatorics

Potentialities, actualities and solution modes

The campaign configuration lists the potentialities (preventive) and actualities (corrective) the user activated, each with the objectives it groups and, per objective, the ordered candidate functions. The inner product keeps the objectives of one group together: an activated group gives exactly one function to each of its objectives. The outer product combines those bundles and adds a NONE alternative per group, so a solution may simply ignore a risk.

FIRST_GOOD_SOLUTION — candidates are ranked and reduced to the best function per objective. Combinations are processed from the last, fully specified one, so an easier NONE combination cannot silently drop a procedure the user selected. The campaign stops after the first unique persisted solution, which is also published to the graph.

ALL_SOLUTIONS — every ranked candidate and every group combination is explored; plans and actor/resource assignments add further Cartesian products (CartesianProduct), and the fingerprint set removes equivalent solutions across the whole campaign. The estimate published to the UI (estimateCampaignSolutionsCount, saturating multiplication) is revised as unique solutions are processed and never decreases.

Combination modelexample
Risk A:
  Objective A1 → [Function A, Function B]
  Objective A2 → [Function C, Function D]
inner bundles(A) = [(A,C), (A,D), (B,C), (B,D)]
column(A)        = [NONE, (A,C), (A,D), (B,C), (B,D)]
combinations     = product(column(A), column(B), column(actuality C), ...)

FIRST_GOOD_SOLUTION : iterate reversed(combinations), stop at first unique solution
ALL_SOLUTIONS       : iterate all, expand plans × assignments, dedupe by signature
07 · Runtime

A sequential campaign hosting a four-task pipeline

deduce() returns immediately. A single-thread executor (combinationsExecutor) processes combinations one after another, which prevents concurrent writes on the same campaign and graph. Inside one combination, Executors.newFixedThreadPool(4) runs four tasks that communicate through two LinkedBlockingQueues closed by poison pills.

TASK 1Progress reporterevery 1 sReconciles generated and persisted counts (countSolutions in the campaign manager) and emits RUNNING; switches to ENDED only on the last combination or once the first good solution is satisfied, CRASHED on fatal error.
TASK 2Plan producer→ plansQueueFinds function candidates, fires the function rules, sorts by score and pushes one plan (first-good) or every plan combination (all solutions), then PLAN_POISON_PILL.
QUEUEplansQueuePlanWithFunctions instances
TASK 3Solution producer→ solutionQueueCreates the RioDefaultPlanner with the Drools actor ranker, solves each plan, builds Solution/Process models and planifies them; resource shortages become error results, not crashes. Ends with SOLUTION_POISON_PILL.
QUEUEsolutionQueue — planned solutions
TASK 4Solution consumerpersistsDeduplicates by fingerprint, increments the processed counter, stores the solution and emits a ScenarioResult; publishes the model synchronously in first-good mode. The “move nodes improver” block is present but commented out.

Three campaign steps are reported to the UI: “At least one function has been found by objectives”, “At least one actor provides each function”, “All solutions have been deduced”. Cumulative counters (cumulativeSolutionsFound, cumulativeSolutionsProcessed, campaignEstimatedSolutionsCount) survive combination boundaries.

08 · Rule lifecycle

Draft, validate, publish, cache

Definitions live in DroolsStrategyRegistry, a singleton persisted as JSON (gind.drools.strategy-store system property, GIND_DROOLS_STRATEGY_STORE environment variable, or <resources>/../DroolsStrategy/drools-strategies.json). Writes go through a temporary file and an atomic move; if persistence fails, defaults stay in memory with a warning.

01

Draft

saveDraft creates a DRAFT version (published version + 1); cloneStrategy suffixes “- copy”; importStrategy validates the imported DRL and suffixes the id with “-import”.

02

Validate

DroolsRuleEngine.validate rejects an empty source, scans the deny-list below, then compiles with KieHelper and ExecutableModelProject; errors are returned as a DroolsRuleValidation.

03

Publish

publish re-validates, freezes the version as PUBLISHED with publishedAt and a SHA-256 checksum, appends it to the history, removes the draft and exposes a new generator variant.

04

Execute

execute(checksum, drl, facts) re-runs the deny-list on every call, compiles once per checksum into a ConcurrentHashMap<String, KieBase>, opens a fresh KieSession, sets the collector global, inserts the facts, fireAllRules() and always disposes the session.

Security boundary: an 8-token deny-list, not a sandbox

Sources containing any of the tokens below are refused before compilation. Rules remain trusted project assets executed with the rights of the governance service.

Runtime.getRuntimeProcessBuilderSystem.exitjava.io.java.nio.Class.forNamejava.lang.reflectgetClass()
REST endpoint (r-ioded)MethodEffect
/{app}/r-ioded/drools/strategiesGET · POSTList definitions · save a draft
…/strategies/{id}GETRead one definition with versions and history
…/strategies/{id}/clonePOSTDuplicate a definition
…/strategies/{id}/export · …/strategies/importGET · POSTZip archive <id>-maven-test.zip: pom.xml, README.md, src/test/resources/drools-strategy.json, ExportedDroolsStrategyTest.java (import: max 100 entries, 5 MB JSON, exactly one descriptor)
…/strategies/{id}/validate · …/publishPOSTValidate the draft · publish it
…/strategies/{id}/testPOSTRun DRAFT or PUBLISHED rules on user-provided objective, function and actor facts; returns accepted/rejected objectives, candidate scores and executionTimeMs
09 · Parameters & UI

What the dialog sends and where rules are edited

satisfactionProperties keyDefaultUsed for
selectedPlanningSolverDefaultPlanner type; Choco and Opta are refused
actorsSelectionModePERSON_FIRSTHow actors are preferred during assignment
numberOfResourceCombinationsCap on enumerated resource assignments
numberOfSolutionsRequested number of solutions in ALL_SOLUTIONS
terminationSpentLimitTime limit of the campaign
campaignConfigurationActivated potentialities/actualities and candidate functions
activeMoveImproverfalseRead and passed to the planner; the improver call itself is commented out
objectivesManager · embeddedObjectivesManagerModelRoot of the deduction

Deduction dialog. createDroolsStrategyDialog (exposed through getJavascriptFunction() with showInputs, showReport, deploy, droolsStrategyId, droolsStrategyVersion) delegates to DroolsSimpleSatisfactionstrategyComponent, whose deduce() serializes exactly the keys on the left.

r-ioded workspace. DeductionPanelComponent offers four views — CATALOG (native and Drools strategies), EDITOR (three rule sections: objective selection, function ranking, actor ranking), TESTS (facts loaded from the current model, scores returned by the test endpoint) and HISTORY — backed by DroolsStrategiesService (list, saveDraft, clone, export, import, validate, publish, test).

10 · Watch points

What the code does that the descriptions do not say

Simple profile description vs rule

The Java description announces “actors by expertise”; the executable rule minimizes the graph KPI cost. This page follows the rule.

Actor KPI lookup mismatch

DroolsActorCandidateFact.calculateGraphKPI checks for Resource:<name> but evaluates Function:<name>; the default actor rule (cost) depends on this mixed path.

Production actor direction

The Production actor rule calls maximizeScoreCandidate(cost): as written, the most expensive actor ranks first, contrary to the “actors by cost” intent.

Production consumed resources

getAllGraphConsumedResources() returns an empty list until the temporary process has been rebuilt by a previous calculateGraphKPI call, so the resource term may stay constant.

Planner limitation

createPlannerResolver throws UnsupportedOperationException for any solver other than Default; Choco and OptaPlanner modules are also excluded from the governance build.

Move improver disabled

The ProcessImproversManager.improveOnlyWithSelectImprovers("move nodes improver") block of task 4 is commented out although the flag is still read.

11 · Sources

Where to continue reading

Module java/backend/gind-governance/gind-governance-workflow-deduction-strategy-drools, package fr.emac.gind.workflow.deduction.drools.

DroolsProcessGenerator.javaSPI entry, variants per published definition, identifier, default-profile requirement.
DroolsStrategyDeductionCampaign.javaParameters, objective filtering, combinations, four-task pipeline, actor ranker, estimates, publication.
DroolsStrategyRegistry.javaDefaults, drafts, clone, import/export, validate, publish, history, JSON store.
runtime/DroolsRuleEngine.javaDeny-list, KieHelper compilation, checksum cache, session execution.
runtime/DroolsDecisionCollector.javaObjective decisions, normalized scores, rejections.
runtime/Drools{Objective,FunctionCandidate,ActorCandidate}Fact.javaDRL-facing data and temporary graph KPI evaluation.
default_strategies/Drools*SatisfactionStrategy.javaSimple, Crisis, Healthcare and Production text-block rules.
DroolsStrategyProjectArchive.javaMaven test archive format for export and import.
gind-webapp-r-ioded/…/DroolsStrategiesResource.javaREST façade used by the r-ioded workspace.
gind-governance-workflow-planner-default/…/ResourceAssigner.javaConsumes the injected actor ranker during assignment.
Stratégie de déduction / Drools / profil par défaut

Drools Simple Satisfaction

La stratégie de déduction par défaut de r-io suite, pilotée par règles. Trois règles Drools décident des objectifs à garder, du graphe de fonctions à préférer et de l’acteur à affecter ; la campagne Java qui les entoure explore les configurations de risques, appelle le planificateur Default et publie des processus collaboratifs uniques.

identifiantdrools:drools-default-simple-satisfaction-strategy:1
Deux stratégies, quatre profils Drools

r-io expose deux implémentations d’AbstractProcessGenerator : la stratégie native Basic Bloc Satisfaction (classement natif par pertinence et expertise, une sous-tâche séquentielle par objectif) et ce générateur Drools. Crisis, Healthcare et Production sont des profils publiés du même pipeline Drools avec d’autres règles de score, pas des algorithmes distincts. Simple Satisfaction est le profil dont le générateur exige l’existence et la publication.

00 · En un coup d’œil

Ce qui entre, ce qui est décidé, ce qui sort

Entrées

Depuis le modèle et le dialogue

  • L’Objectives_Manager sélectionné dans r-ioded, r-iota ou r-ioga et ses arêtes Plans.
  • Les relations Satisfies, Needs, Implies, Supports, Provides, Consumes du modèle collaboratif.
  • satisfactionProperties : planificateur, mode de sélection des acteurs, plafond de combinaisons de ressources, limite de temps, configuration de campagne (potentialités préventives et correctives).
Décisions

Prises par les règles

  • Sélection des objectifs : accepter ou rejeter chaque DroolsObjectiveFact.
  • Classement des fonctions : noter chaque DroolsFunctionCandidateFact, ici en minimisant le KPI de graphe duration.
  • Classement des acteurs : noter chaque DroolsActorCandidateFact, ici en minimisant le KPI de graphe cost.
Sorties

Persistées par la campagne

  • Des solutions de campagne (campaignSolution) portant un modèle SolutionProcessTask/Sequence_Flow avec dates attendues et ressources affectées.
  • Des résultats de progression, de scénario et d’erreur transmis à l’appelant via le rappel de déduction.
  • En mode « première bonne solution », la première solution unique est aussi publiée dans le graphe en synchrone.
01 · Positionnement

Où se place la stratégie dans la chaîne de déduction

La stratégie ne reçoit jamais d’appel HTTP directement. Elle est instanciée par le service de déduction du conteneur de gouvernance, qui la découvre via ServiceLoader et l’exécute dans une campagne.

UIDialogue de déductioncreateDroolsStrategyDialog ouvre le composant de saisie Simple Satisfaction (r-ioded, r-iota, application générique).
RESTSolutionResourcePOST …/r-ioga/solution/deduceSolutionASync construit un GJaxbDeduceASync avec le port de rappel.
SOAPGovDeductionDeductionImpl.deduceASync : unique opération du conteneur de gouvernance.
SPIDroolsProcessGeneratorUne variante par définition publiée du registre ; identifiant drools:<id>:<version>.
CampagneDroolsStrategyDeductionCampaignConstruit les faits, exécute les règles, explore les combinaisons, pilote le pipeline à 4 tâches.
PlanRioDefaultPlannerClassement des acteurs injecté par la stratégie ; ressources, ordonnancement, dates attendues.
StockageGestionnaire de campagnesaddSolution dans solutionsCampaigns ; progression renvoyée par deductionASyncCallBackResponse.

Les variantes étant énumérées depuis DroolsStrategyRegistry.listPublishedDefinitions(), publier une nouvelle version de règles dans r-ioded ajoute immédiatement une stratégie sélectionnable sans redéployer de module Java. Le générateur refuse de démarrer si la définition Simple Satisfaction est absente ou non publiée (IllegalStateException: The default Drools strategy is not published).

02 · Pipeline

Le chemin de décision, étape par étape

Point d’entrée : doDeduceProcessFromOneObjectivesManager(campaign, mode, collaboration, knowledgeSpace, objectivesManager, data, project, resultsQueue).

01

Lire les paramètres d’exécution

Le tableau satisfactionProperties est analysé pour obtenir le type de planificateur (selectedPlanningSolver, Default par défaut), le mode de sélection des acteurs (actorsSelectionMode, PERSON_FIRST), le plafond de combinaisons de ressources, la limite de temps, l’indicateur d’amélioration par déplacement et la configuration de campagne qui porte les potentialités préventives et correctives choisies par l’utilisateur.

02

Filtrer les objectifs avec les règles

Chaque arête Plans du gestionnaire d’objectifs devient un DroolsObjectiveFact(id, name, priority). Les règles s’exécutent une fois ; si elles n’émettent aucune décision, tous les objectifs sont conservés, sinon seuls les objectifs acceptés survivent (objectivesManager.filterPlans). Un résultat vide arrête la campagne par une IllegalStateException. Chaque décision est tracée dans l’observation de la campagne.

03

Développer les configurations de risques

Pour chaque potentialité ou actualité de la configuration de campagne, les objectifs qu’elle regroupe reçoivent chacun une fonction sélectionnée (produit cartésien interne), puis une alternative NONE est ajoutée par groupe et le produit externe énumère les combinaisons de la campagne. Les candidats préventifs et correctifs sont ordonnés avec les mêmes règles Drools ; en cas d’échec du score, l’ordre envoyé par le front est conservé.

04

Trouver les graphes de fonctions réalisables

Pour chaque objectif retenu, les relations Satisfies donnent les fonctions candidates ; Needs, Implies et Supports développent chaque candidat en graphe récursif de fonctions. Un candidat est écarté quand aucun acteur ne Provides au moins une fonction de son graphe.

05

Classer les fonctions candidates

Chaque candidat est exposé comme DroolsFunctionCandidateFact avec pertinence, priorité, ressources consommées et une méthode calculateGraphKPI(name). Les règles notent ou rejettent les candidats via le collecteur ; le classement est un tri décroissant sur le score fusionné, avec la pertinence naturelle en repli quand aucune règle n’a noté.

06

Classer les acteurs et allouer les ressources

La stratégie injecte rankActorCandidates dans le planificateur (planner.setActorCandidateRanker). Pour chaque fonction d’un plan, les acteurs qui la fournissent deviennent des DroolsActorCandidateFact avec leur expertise ; les règles les notent, l’expertise est le repli. ResourceAssigner résout ensuite ressources fixes et mobiles, soit la meilleure affectation, soit toutes celles permises.

07

Créer, planifier et valoriser le processus

Fonctions choisies et affectations deviennent des éléments Solution, Process, Task et Sequence_Flow ; solution.planify() et le planificateur Default calculent dates de début et de fin attendues (jamais moins d’une minute par tâche) et indicateurs KPIL. Les échecs d’affectation de ressources sont rapportés comme erreurs sans faire crasher la campagne.

08

Dédupliquer et publier

Un ensemble d’empreintes à l’échelle de la campagne (CampaignSolutionHelper.createSignature) écarte les combinaisons équivalentes de fonctions, acteurs, ressources et procédures. Les solutions uniques sont stockées via le gestionnaire de campagnes ; en mode FIRST_GOOD_SOLUTION, la première est aussi poussée dans le graphe et la campagne s’arrête.

Pseudo-code de haut niveauDroolsStrategyDeductionCampaign
definition   = registre.publiee("drools-default-simple-satisfaction-strategy")
objectifs    = regles(definition, ObjectiveFact(plans.objectif, priorite))     # tout garder si aucune décision
combinaisons = cartesien(choixDePotentialitesGroupes + NONE par groupe)
si mode == FIRST_GOOD_SOLUTION : combinaisons = inverser(combinaisons)         # partir de la plus spécifiée

exécuteur mono-thread, pour combinaison dans combinaisons :
  plansQueue, solutionQueue = new LinkedBlockingQueue()
  tâche1 : chaque seconde, rapporter RUNNING / ENDED / CRASHED avec compteurs et estimation
  tâche2 : candidats = findBestOrAllPlansWithFunctions(objectifs, combinaison)
           classes   = regles(FunctionCandidateFact(...)) ou pertinence
           plansQueue <- meilleur plan | tous les plans (ALL_SOLUTIONS) ; PLAN_POISON_PILL
  tâche3 : pour plan dans plansQueue :
             planificateur = RioDefaultPlanner(classeurActeurs = regles(ActorCandidateFact(...)))
             pour affectation dans planificateur.solve(plan) : solutionQueue <- createSolution(plan, affectation)
           SOLUTION_POISON_PILL
  tâche4 : pour solution dans solutionQueue :
             si empreintes.add(signature(solution)) : gestionnaireCampagnes.addSolution(solution) ; résultats <- ScenarioResult
             si mode == FIRST_GOOD_SOLUTION : publierModeleEnSynchrone(solution) ; stop
03 · Règles

Les trois règles, telles que livrées

Les règles ne sont pas des fichiers .drl : chaque profil est un bloc de texte Java (STRATEGY_RULES) dans default_strategies/, injecté dans le registre au premier démarrage. Elles partagent un package, trois imports de faits et un seul global, collector. Pas de salience, pas d’agenda group, pas d’insert ni de modify : les règles ne parlent qu’au collecteur.

Sélection des objectifsDroolsObjectiveFact
rule "Accept every objective"
when
  $objective : DroolsObjectiveFact()
then
  collector.accept($objective.getId());
end
Tout objectif planifié est conservé. Remplacer le motif par une condition sur getPriority() ou getName() pour filtrer.
Classement des fonctionsDroolsFunctionCandidateFact
rule "Rank functions by duration"
when
  $candidate : DroolsFunctionCandidateFact()
then
  collector.minimizeScoreCandidate($candidate,
    $candidate.calculateGraphKPI("duration"));
end
Le graphe de fonctions le plus court l’emporte. duration est évaluée sur un processus temporaire reconstruit depuis le graphe (section 05).
Classement des acteursDroolsActorCandidateFact
rule "Rank actors by cost"
when
  $candidate : DroolsActorCandidateFact()
then
  collector.minimizeScoreCandidate($candidate,
    $candidate.calculateGraphKPI("cost"));
end
L’acteur le moins coûteux l’emporte. Note : la description Java du profil parle encore d’« acteurs par expertise » ; la règle exécutable est le coût.
FaitDonnées exposées au DRLAppels au collecteurRepli
DroolsObjectiveFactgetId(), getName(), getPriority()accept(id), rejectObjective(id)Aucune décision → tous les objectifs conservés
DroolsFunctionCandidateFactids/noms du candidat, de l’objectif et de la fonction, getPertinence(), getPriority(), getAllGraphConsumedResources(), calculateGraphKPI(name)maximizeScoreCandidate, minimizeScoreCandidate, rejectFunctionCandidateAucun score → pertinence naturelle
DroolsActorCandidateFactcandidat (= id de l’acteur), ids/noms de l’objectif et de la fonction, getExpertise(), calculateGraphKPI(name)maximizeScoreCandidate, minimizeScoreCandidate, rejectActorCandidateAucun score → expertise naturelle
Normalisation des scoresDroolsDecisionCollector
minimizeScoreCandidate(c, v) = scoreCandidate(c, -v)
maximizeScoreCandidate(c, v) = scoreCandidate(c, v)
score[c] = max(score[c], v)         # fusion par Math::max
reject*(c)                          # retire le score ; une règle ultérieure peut re-noter
classes = candidats notés, triés par score décroissant
type de fait inconnu → IllegalArgumentException
04 · Profils

Même pipeline, quatre politiques de score publiées

Les quatre définitions sont des valeurs système par défaut (systemDefault=true, version 1 PUBLISHED, somme SHA-256, une entrée d’historique). Seuls le texte DRL et le domaine privilégié diffèrent ; algorithme de campagne, faits, planificateur et persistance sont partagés.

ProfilIdentifiant · domaineRègle fonctionsRègle acteurs
Simple Satisfaction défautdrools-default-simple-satisfaction-strategy
collaborative
Rank functions by durationminimize calculateGraphKPI("duration")Rank actors by costminimize calculateGraphKPI("cost")
Crisis Satisfactiondrools-default-crisis-satisfaction-strategy
crisis
Rank functions by pertinencemaximize getPertinence()Rank actors by expertisemaximize getExpertise()
Healthcare Satisfactiondrools-default-healthcare-satisfaction-strategy
health
Multicritèremaximize 0,7 × pertinence + 0,3 / (1 + duration / 60)Multicritèremaximize 0,6 × expertise + 0,4 / (1 + cost / 100)
Production Satisfaction réservesdrools-default-production-satisfaction-strategy
production
Pertinence et parcimonie des ressourcesmaximize 0,5 × pertinence + 0,5 / (1 + ressourcesConsommées / 5)Tel qu’écrit dans le codemaximize calculateGraphKPI("cost")

Le bloc de texte Simple Satisfaction est le seul public : r-ioded l’utilise comme gabarit à la création d’un brouillon. Le profil Production porte deux réserves au niveau du code, détaillées dans les points de vigilance.

05 · KPI de graphe

Calculer une durée ou un coût avant l’existence du processus

Une règle de classement peut avoir besoin de duration ou cost alors qu’aucun processus n’a encore été généré. DroolsFunctionCandidateFact.calculateGraphKPI reconstruit donc un processus temporaire léger depuis le graphe récursif de fonctions, copie les indicateurs théoriques de chaque fonction dans les valeurs simulées des tâches et évalue l’indicateur KPIL demandé sur la tâche principale avec HLIndicatorManager.

DébutStart_Event
Pré-fonctionNeeds
SupportSupports
Fonction principaleTâche évaluée par HLIndicatorManager
Post-fonctionImplies
FinEnd_Event
Relation du modèleTraduction en processus temporaire
main Needs preTask(pre) → Task(main)
main Implies postTask(main) → Task(post)
main Supports supportTask(support) → Task(main)
Aucun flux entrant / sortantRelié à Start_Event / End_Event
Reconstruction du graphe temporairebuildProcessFromFunctionRecursively
fonctions = unique(principale + principale.toutesFonctionsRecursivement)
pour f dans fonctions :
  tache[f] = Task(f.nom) ; copier indicateurs théoriques de f → tache.simules
pour f dans fonctions :
  pour pre dans f.needs :       arete(tache[pre], tache[f])
  pour post dans f.implies :    arete(tache[f], tache[post])
  pour s dans f.supports :      arete(tache[s], tache[f])
relier Start_Event → tâches sans flux entrant
relier tâches sans flux sortant → End_Event
processus = Process.buildFromGenericModel(noeuds, aretes)
retourner indicatorManager.evaluateIndicatorOnTask("Function:" + nom, tachePrincipale)

Le fait acteur réutilise le même mécanisme : DroolsActorCandidateFact.calculateGraphKPI vérifie qu’un indicateur Resource:<nom> existe, puis évalue Function:<nom> sur la tâche principale. Cette recherche mixte figure dans les points de vigilance.

06 · Combinatoire

Potentialités, actualités et modes de solution

La configuration de campagne liste les potentialités (préventives) et actualités (correctives) activées par l’utilisateur, chacune avec les objectifs qu’elle regroupe et, par objectif, les fonctions candidates ordonnées. Le produit interne garde ensemble les objectifs d’un groupe : un groupe activé donne exactement une fonction à chacun de ses objectifs. Le produit externe combine ces lots et ajoute une alternative NONE par groupe, si bien qu’une solution peut simplement ignorer un risque.

FIRST_GOOD_SOLUTION — les candidats sont classés et réduits à la meilleure fonction par objectif. Les combinaisons sont traitées à partir de la dernière, la plus spécifiée, pour qu’une combinaison NONE plus facile n’omette pas silencieusement une procédure choisie par l’utilisateur. La campagne s’arrête après la première solution unique persistée, également publiée dans le graphe.

ALL_SOLUTIONS — tous les candidats classés et toutes les combinaisons de groupes sont explorés ; plans et affectations acteurs/ressources ajoutent d’autres produits cartésiens (CartesianProduct), et l’ensemble d’empreintes retire les solutions équivalentes sur toute la campagne. L’estimation publiée à l’interface (estimateCampaignSolutionsCount, multiplication saturante) est révisée au fil des solutions uniques traitées et ne diminue jamais.

Modèle de combinaisonexemple
Risque A :
  Objectif A1 → [Fonction A, Fonction B]
  Objectif A2 → [Fonction C, Fonction D]
lots internes(A) = [(A,C), (A,D), (B,C), (B,D)]
colonne(A)       = [NONE, (A,C), (A,D), (B,C), (B,D)]
combinaisons     = produit(colonne(A), colonne(B), colonne(actualité C), ...)

FIRST_GOOD_SOLUTION : parcourir inverse(combinaisons), stop à la première solution unique
ALL_SOLUTIONS       : tout parcourir, développer plans × affectations, dédoublonner par signature
07 · Exécution

Une campagne séquentielle hébergeant un pipeline à quatre tâches

deduce() rend la main immédiatement. Un exécuteur mono-thread (combinationsExecutor) traite les combinaisons l’une après l’autre, ce qui évite les écritures concurrentes sur la même campagne et le même graphe. Dans une combinaison, Executors.newFixedThreadPool(4) exécute quatre tâches qui communiquent par deux LinkedBlockingQueue fermées par des pilules empoisonnées.

TÂCHE 1Rapporteur de progressiontoutes les 1 sRéconcilie compteurs générés et persistés (countSolutions dans le gestionnaire de campagnes) et émet RUNNING ; passe à ENDED seulement sur la dernière combinaison ou dès que la première bonne solution est satisfaite, CRASHED sur erreur fatale.
TÂCHE 2Producteur de plans→ plansQueueTrouve les fonctions candidates, exécute les règles fonctions, trie par score et pousse un plan (première bonne solution) ou toutes les combinaisons de plans (toutes solutions), puis PLAN_POISON_PILL.
FILEplansQueue — instances de PlanWithFunctions
TÂCHE 3Producteur de solutions→ solutionQueueCrée le RioDefaultPlanner avec le classeur d’acteurs Drools, résout chaque plan, construit les modèles Solution/Process et les planifie ; les pénuries de ressources deviennent des résultats d’erreur, pas des crashs. Se termine par SOLUTION_POISON_PILL.
FILEsolutionQueue — solutions planifiées
TÂCHE 4Consommateur de solutionspersisteDédoublonne par empreinte, incrémente le compteur traité, stocke la solution et émet un ScenarioResult ; publie le modèle en synchrone en mode première bonne solution. Le bloc « move nodes improver » est présent mais commenté.

Trois étapes de campagne sont rapportées à l’interface : « At least one function has been found by objectives », « At least one actor provides each function », « All solutions have been deduced ». Les compteurs cumulés (cumulativeSolutionsFound, cumulativeSolutionsProcessed, campaignEstimatedSolutionsCount) survivent aux frontières de combinaison.

08 · Cycle de vie des règles

Brouillon, validation, publication, cache

Les définitions vivent dans DroolsStrategyRegistry, un singleton persisté en JSON (propriété système gind.drools.strategy-store, variable d’environnement GIND_DROOLS_STRATEGY_STORE, sinon <ressources>/../DroolsStrategy/drools-strategies.json). Les écritures passent par un fichier temporaire et un déplacement atomique ; si la persistance échoue, les valeurs par défaut restent en mémoire avec un avertissement.

01

Brouillon

saveDraft crée une version DRAFT (version publiée + 1) ; cloneStrategy suffixe « - copy » ; importStrategy valide le DRL importé et suffixe l’identifiant par « -import ».

02

Validation

DroolsRuleEngine.validate rejette une source vide, parcourt la liste d’interdiction ci-dessous, puis compile avec KieHelper et ExecutableModelProject ; les erreurs reviennent dans une DroolsRuleValidation.

03

Publication

publish revalide, fige la version en PUBLISHED avec publishedAt et une somme SHA-256, l’ajoute à l’historique, supprime le brouillon et expose une nouvelle variante du générateur.

04

Exécution

execute(checksum, drl, facts) rejoue la liste d’interdiction à chaque appel, compile une seule fois par somme de contrôle dans une ConcurrentHashMap<String, KieBase>, ouvre une KieSession neuve, positionne le global collector, insère les faits, fireAllRules() et libère toujours la session.

Frontière de sécurité : une liste d’interdiction de 8 jetons, pas un bac à sable

Les sources contenant l’un des jetons ci-dessous sont refusées avant compilation. Les règles restent des ressources de projet de confiance, exécutées avec les droits du service de gouvernance.

Runtime.getRuntimeProcessBuilderSystem.exitjava.io.java.nio.Class.forNamejava.lang.reflectgetClass()
Endpoint REST (r-ioded)MéthodeEffet
/{app}/r-ioded/drools/strategiesGET · POSTLister les définitions · enregistrer un brouillon
…/strategies/{id}GETLire une définition avec versions et historique
…/strategies/{id}/clonePOSTDupliquer une définition
…/strategies/{id}/export · …/strategies/importGET · POSTArchive zip <id>-maven-test.zip : pom.xml, README.md, src/test/resources/drools-strategy.json, ExportedDroolsStrategyTest.java (import : 100 entrées max, 5 Mo de JSON, exactement un descripteur)
…/strategies/{id}/validate · …/publishPOSTValider le brouillon · le publier
…/strategies/{id}/testPOSTExécuter les règles DRAFT ou PUBLISHED sur des faits objectifs, fonctions et acteurs fournis ; renvoie objectifs acceptés/rejetés, scores des candidats et executionTimeMs
09 · Paramètres & UI

Ce que le dialogue envoie et où les règles s’éditent

Clé de satisfactionPropertiesDéfautUsage
selectedPlanningSolverDefaultType de planificateur ; Choco et Opta sont refusés
actorsSelectionModePERSON_FIRSTPréférence d’acteurs pendant l’affectation
numberOfResourceCombinationsPlafond d’affectations de ressources énumérées
numberOfSolutionsNombre de solutions demandé en ALL_SOLUTIONS
terminationSpentLimitLimite de temps de la campagne
campaignConfigurationPotentialités/actualités activées et fonctions candidates
activeMoveImproverfalseLu et transmis au planificateur ; l’appel à l’improver est lui-même commenté
objectivesManager · embeddedObjectivesManagerModelRacine de la déduction

Dialogue de déduction. createDroolsStrategyDialog (exposée par getJavascriptFunction() avec showInputs, showReport, deploy, droolsStrategyId, droolsStrategyVersion) délègue à DroolsSimpleSatisfactionstrategyComponent, dont deduce() sérialise exactement les clés de gauche.

Espace de travail r-ioded. DeductionPanelComponent propose quatre vues — CATALOG (stratégies natives et Drools), EDITOR (trois sections de règles : sélection des objectifs, classement des fonctions, classement des acteurs), TESTS (faits chargés depuis le modèle courant, scores renvoyés par l’endpoint de test) et HISTORY — appuyées sur DroolsStrategiesService (list, saveDraft, clone, export, import, validate, publish, test).

10 · Points de vigilance

Ce que fait le code et que les descriptions ne disent pas

Description du profil Simple vs règle

La description Java annonce « acteurs par expertise » ; la règle exécutable minimise le KPI de graphe cost. Cette page suit la règle.

Recherche mixte du KPI acteur

DroolsActorCandidateFact.calculateGraphKPI vérifie Resource:<nom> mais évalue Function:<nom> ; la règle acteurs par défaut (cost) dépend de ce chemin mixte.

Sens de la règle acteurs Production

La règle acteurs Production appelle maximizeScoreCandidate(cost) : telle quelle, l’acteur le plus coûteux passe en tête, à l’inverse de l’intention « acteurs par coût ».

Ressources consommées en Production

getAllGraphConsumedResources() renvoie une liste vide tant que le processus temporaire n’a pas été reconstruit par un appel préalable à calculateGraphKPI ; le terme ressources peut donc rester constant.

Limitation du planificateur

createPlannerResolver lève UnsupportedOperationException pour tout solveur autre que Default ; les modules Choco et OptaPlanner sont aussi exclus du build de la gouvernance.

Improver de déplacement désactivé

Le bloc ProcessImproversManager.improveOnlyWithSelectImprovers("move nodes improver") de la tâche 4 est commenté bien que l’indicateur soit toujours lu.

11 · Sources

Où poursuivre la lecture

Module java/backend/gind-governance/gind-governance-workflow-deduction-strategy-drools, package fr.emac.gind.workflow.deduction.drools.

DroolsProcessGenerator.javaEntrée SPI, variantes par définition publiée, identifiant, exigence du profil par défaut.
DroolsStrategyDeductionCampaign.javaParamètres, filtrage des objectifs, combinaisons, pipeline à quatre tâches, classeur d’acteurs, estimations, publication.
DroolsStrategyRegistry.javaValeurs par défaut, brouillons, clonage, import/export, validation, publication, historique, stockage JSON.
runtime/DroolsRuleEngine.javaListe d’interdiction, compilation KieHelper, cache par somme de contrôle, exécution de session.
runtime/DroolsDecisionCollector.javaDécisions d’objectifs, scores normalisés, rejets.
runtime/Drools{Objective,FunctionCandidate,ActorCandidate}Fact.javaDonnées exposées au DRL et évaluation du KPI de graphe temporaire.
default_strategies/Drools*SatisfactionStrategy.javaRègles en blocs de texte Simple, Crisis, Healthcare et Production.
DroolsStrategyProjectArchive.javaFormat d’archive de test Maven pour l’export et l’import.
gind-webapp-r-ioded/…/DroolsStrategiesResource.javaFaçade REST utilisée par l’espace de travail r-ioded.
gind-governance-workflow-planner-default/…/ResourceAssigner.javaConsomme le classeur d’acteurs injecté pendant l’affectation.