Gardes nommées, reprise de revue : « nommée » jamais « déclarée », exclusions, registre du refus
gardes du contrat / conformite (pull_request) Successful in 17s

Les trois corrections retenues de la revue extérieure d'openathle#275 —
que j'avais amendées sur la branche d'une PR déjà fusionnée, donc jamais
livrées : les champs disent ce qu'ils mesurent (nommee_par_parcours,
_par_adr, _par_epreuve — un parcours peut porter la règle en prose sans
nommer la classe) ; une absence n'est pas une règle (gardes_exclues, par
motif — exclue = non observée) ; décider que non a un réceptacle
(gardes_ecartees, registre de la juridiction, lu jamais écrit). Le
journal porte ses motifs : la limite est dite là où la mesure s'écrit.
La triade est mesurée : sans_epreuve.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
temoin-agent
2026-09-21 21:13:02 +02:00
co-authored by Claude Fable 5.1
parent 8670f57b16
commit 654042d0fa
5 changed files with 88 additions and 22 deletions
+21 -6
View File
@@ -10,7 +10,7 @@
*
* Exit codes: 0 sound, 1 broken. A refusal names its cause.
*/
import { cpSync, mkdtempSync, readFileSync, rmSync, unlinkSync, existsSync } from 'node:fs';
import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, existsSync, writeFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { tmpdir } from 'node:os';
import { fileURLToPath } from 'node:url';
@@ -47,12 +47,18 @@ try {
assert((byInv(first, 'INV-001')?.rangs_constates ?? []).join(',') === '4,3', 'INV-001 carries the third-party ranks its tuning declares, in stratum order');
assert(byInv(first, 'INV-003')?.rangs_constates === undefined, 'INV-003 (no locus) carries no ranks — nothing observed maps to nothing');
assert(JSON.stringify(journal().correspondance_rangs) === JSON.stringify({ domaine: 4, application: 3, interface: 1 }), 'journal publishes the full rank correspondence for consumers joining old findings');
// Named guards — candidates, never findings: a domain exception nobody declared.
// Named guards — candidates, never findings: a domain exception nobody NAMES
// in a journey. Fields say « named », not « declared » : a journey may hold
// the rule in prose without naming the class — that is the instrument's limit.
const g = journal().gardes_nommees;
assert(g && g.total === 1 && g.sans_declaration === 1, 'the fixture has one named domain guard, and no journey declares it');
assert(g.candidats[0].nom === 'RuleViolatedException' && g.candidats[0].strate === 'domaine', 'the candidate carries its name and its stratum');
assert(g.candidats[0].dans_parcours === false && g.candidats[0].dans_adr === true && g.candidats[0].eprouve === false, 'the candidate says where it is cited — ADR yes, journey no, test no');
assert(!('candidats' in journal()) , 'candidates live under gardes_nommees, not at the journal root');
// Excluded by pattern = not observed at all: the NotFound one is neither counted nor listed.
assert(g && g.total === 1 && g.non_nommees_par_un_parcours === 1, 'the fixture has one observed named guard (the NotFound one is excluded), and no journey names it');
assert(JSON.stringify(g.motifs) === JSON.stringify(['src/main/java/**/Domain/**/*Exception.java']), 'the journal says which patterns it looked at — the lamppost is written down');
assert(g.candidats.length === 1 && g.candidats[0].nom === 'RuleViolatedException', 'the NotFound one is excluded by pattern; the other is a candidate');
const c = g.candidats[0];
assert(c.strate === 'domaine' && c.nommee_par_parcours === false && c.nommee_par_adr === true && c.nommee_par_epreuve === false, 'the candidate says who names it — ADR yes, journey no, test no');
assert(g.sans_epreuve === 1, 'the triad is measured, not asserted: the observed guard lacks a test');
assert(g.ecartees === 0 && g.differees === 0, 'no decision register yet: nothing set aside');
// Backward compatibility: a tuning without the key gets no section at all.
// On its OWN copy of the fixture: a second run on `repo` would rewrite the
// journal and falsify the assertions that follow (caught by the self-test
@@ -62,6 +68,15 @@ try {
execFileSync(process.execPath, [join(here, 'run.mjs'), '--config', 'reglage-sans-gardes.yaml', '--repo', repo2], { encoding: 'utf8', stdio: 'pipe' });
const journal2 = parseYaml(readFileSync(join(repo2, 'docs/parcours/_mesures/derniere-execution.yaml'), 'utf8'));
assert(!('gardes_nommees' in journal2), 'a tuning that does not ask for named guards changes nothing');
// The refusal register: what the jurisdiction set aside does not come back
// every night. Read by the instrument, never written by it.
const repo3 = mkdtempSync(join(tmpdir(), 'instrument-selftest-registre-'));
cpSync(join(here, 'selftest/fixture'), repo3, { recursive: true });
mkdirSync(join(repo3, 'docs/parcours/_meta'), { recursive: true });
writeFileSync(join(repo3, 'docs/parcours/_meta/gardes-ecartees.yaml'), "- nom: RuleViolatedException\n decision: ecarte\n motif: une plomberie, pas une règle\n");
execFileSync(process.execPath, [join(here, 'run.mjs'), '--config', 'reglage.yaml', '--repo', repo3], { encoding: 'utf8', stdio: 'pipe' });
const j3 = parseYaml(readFileSync(join(repo3, 'docs/parcours/_mesures/derniere-execution.yaml'), 'utf8')).gardes_nommees;
assert(j3.total === 1 && j3.ecartees === 1 && j3.candidats.length === 0, 'a guard set aside leaves the candidates and stays in the count');
assert(byInv(first, 'INV-002')?.etat === 'mal_domicilie', 'INV-002 mal_domicilie — proven only away from home');
assert(byInv(first, 'INV-003')?.etat === 'aucun_locus', 'INV-003 aucun_locus — named by no test');
assert(journal().constats_nouveaux === 3 && journal().invariants === 3, 'journal counts the run');