diff --git a/Programme/contenus_pedagogiques/cycle_3/mathematiques/cm1/01_nombres_calcul_resolution/01B_fractions/kit_adaptatif/svg/parcours_fraction_support.svg b/Programme/contenus_pedagogiques/cycle_3/mathematiques/cm1/01_nombres_calcul_resolution/01B_fractions/kit_adaptatif/svg/parcours_fraction_support.svg
index 9295ec4..1df1b71 100644
--- a/Programme/contenus_pedagogiques/cycle_3/mathematiques/cm1/01_nombres_calcul_resolution/01B_fractions/kit_adaptatif/svg/parcours_fraction_support.svg
+++ b/Programme/contenus_pedagogiques/cycle_3/mathematiques/cm1/01_nombres_calcul_resolution/01B_fractions/kit_adaptatif/svg/parcours_fraction_support.svg
@@ -29,7 +29,7 @@
0
1
- 3
-
- 4
+ 3
+
+ 4
diff --git a/backend/app/__pycache__/curriculum.cpython-313.pyc b/backend/app/__pycache__/curriculum.cpython-313.pyc
index 773605b..18c5975 100644
Binary files a/backend/app/__pycache__/curriculum.cpython-313.pyc and b/backend/app/__pycache__/curriculum.cpython-313.pyc differ
diff --git a/backend/app/__pycache__/database.cpython-313.pyc b/backend/app/__pycache__/database.cpython-313.pyc
index 72d85ae..2ec09f5 100644
Binary files a/backend/app/__pycache__/database.cpython-313.pyc and b/backend/app/__pycache__/database.cpython-313.pyc differ
diff --git a/backend/app/__pycache__/main.cpython-313.pyc b/backend/app/__pycache__/main.cpython-313.pyc
index 969dd21..74f471c 100644
Binary files a/backend/app/__pycache__/main.cpython-313.pyc and b/backend/app/__pycache__/main.cpython-313.pyc differ
diff --git a/backend/app/__pycache__/models.cpython-313.pyc b/backend/app/__pycache__/models.cpython-313.pyc
index 66e37e4..6d7a174 100644
Binary files a/backend/app/__pycache__/models.cpython-313.pyc and b/backend/app/__pycache__/models.cpython-313.pyc differ
diff --git a/backend/app/__pycache__/schemas.cpython-313.pyc b/backend/app/__pycache__/schemas.cpython-313.pyc
index 5a728ad..0c429fc 100644
Binary files a/backend/app/__pycache__/schemas.cpython-313.pyc and b/backend/app/__pycache__/schemas.cpython-313.pyc differ
diff --git a/backend/app/__pycache__/services.cpython-313.pyc b/backend/app/__pycache__/services.cpython-313.pyc
index 175f5fb..7e10901 100644
Binary files a/backend/app/__pycache__/services.cpython-313.pyc and b/backend/app/__pycache__/services.cpython-313.pyc differ
diff --git a/backend/app/main.py b/backend/app/main.py
index 6797270..b35b9f2 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -19,6 +19,7 @@ from .auth import (
)
from .curriculum import QUESTIONS
from .program_content import (
+ apply_program_review_updates,
build_program_tree,
decode_asset_token,
format_adaptive_kit_context,
@@ -52,6 +53,7 @@ async def lifespan(app: FastAPI):
seed_admin_user(db)
finally:
db.close()
+ apply_program_review_updates()
yield
diff --git a/backend/app/program_content.py b/backend/app/program_content.py
index ccb01e0..b7fa893 100644
--- a/backend/app/program_content.py
+++ b/backend/app/program_content.py
@@ -29,6 +29,7 @@ def default_content_root() -> Path:
CONTENT_ROOT = Path(os.getenv("PROGRAM_CONTENT_ROOT", default_content_root())).resolve()
LESSON_BACKGROUND = "#f8fafc"
REVIEW_STATE_PATH = Path(os.getenv("PROGRAM_REVIEW_STATE_PATH", CONTENT_ROOT / ".program_review_state.json"))
+REVIEW_UPDATES_PATH = Path(os.getenv("PROGRAM_REVIEW_UPDATES_PATH", CONTENT_ROOT / "review_updates"))
GRADE_ALIASES = {
"CM1": "cm1",
@@ -298,6 +299,71 @@ def save_review_state(state: dict) -> None:
)
+def iter_review_update_files() -> list[Path]:
+ candidates = []
+ single_file = CONTENT_ROOT / ".program_review_updates.json"
+ if single_file.exists():
+ candidates.append(single_file)
+ if REVIEW_UPDATES_PATH.exists():
+ candidates.extend(sorted(REVIEW_UPDATES_PATH.glob("*.json")))
+ return candidates
+
+
+def apply_program_review_updates() -> dict:
+ state = load_review_state()
+ applied = 0
+ skipped = 0
+ errors = []
+
+ for path in iter_review_update_files():
+ try:
+ payload = json.loads(path.read_text(encoding="utf-8"))
+ except json.JSONDecodeError as exc:
+ errors.append({"file": str(path), "error": f"JSON invalide: {exc}"})
+ continue
+
+ if payload.get("schema") != "professeur-top-review-updates-v1":
+ skipped += 1
+ continue
+
+ payload_updated_at = payload.get("updated_at") or datetime.utcnow().isoformat(timespec="seconds") + "Z"
+ payload_reviewer = payload.get("updated_by") or "codex"
+ for index, update in enumerate(payload.get("updates") or []):
+ node_key = update.get("node_key") or update.get("path")
+ if not node_key:
+ skipped += 1
+ continue
+
+ update_id = update.get("update_id") or f"{path.name}:{index}:{node_key}"
+ existing = state.get(node_key, {})
+ if existing.get("codex_update_id") == update_id:
+ skipped += 1
+ continue
+
+ next_review = {
+ **existing,
+ "status": update.get("status") or existing.get("status") or "treated",
+ "comment": update.get("comment", existing.get("comment", "")),
+ "reviewer": update.get("reviewer") or payload_reviewer,
+ "updated_at": update.get("updated_at") or payload_updated_at,
+ "codex_update_id": update_id,
+ }
+ if update.get("treated_note"):
+ next_review["treated_note"] = update["treated_note"]
+ state[node_key] = next_review
+ applied += 1
+
+ if applied:
+ save_review_state(state)
+
+ return {
+ "applied": applied,
+ "skipped": skipped,
+ "errors": errors,
+ "files": [str(path) for path in iter_review_update_files()],
+ }
+
+
def collect_lesson_status(lesson: dict) -> dict:
lesson_dir = CONTENT_ROOT / lesson["id"]
kit_dir = lesson_dir / "kit_adaptatif"
diff --git a/backend/contenus_pedagogiques/cycle_3/mathematiques/cm1/01_nombres_calcul_resolution/01B_fractions/kit_adaptatif/svg/parcours_fraction_support.svg b/backend/contenus_pedagogiques/cycle_3/mathematiques/cm1/01_nombres_calcul_resolution/01B_fractions/kit_adaptatif/svg/parcours_fraction_support.svg
index 9295ec4..1df1b71 100644
--- a/backend/contenus_pedagogiques/cycle_3/mathematiques/cm1/01_nombres_calcul_resolution/01B_fractions/kit_adaptatif/svg/parcours_fraction_support.svg
+++ b/backend/contenus_pedagogiques/cycle_3/mathematiques/cm1/01_nombres_calcul_resolution/01B_fractions/kit_adaptatif/svg/parcours_fraction_support.svg
@@ -29,7 +29,7 @@
0
1
- 3
-
- 4
+ 3
+
+ 4
diff --git a/backend/contenus_pedagogiques/review_updates/codex-2026-05-07-fractions-kit.json b/backend/contenus_pedagogiques/review_updates/codex-2026-05-07-fractions-kit.json
new file mode 100644
index 0000000..142b825
--- /dev/null
+++ b/backend/contenus_pedagogiques/review_updates/codex-2026-05-07-fractions-kit.json
@@ -0,0 +1,16 @@
+{
+ "schema": "professeur-top-review-updates-v1",
+ "updated_by": "codex",
+ "updated_at": "2026-05-07T19:55:00Z",
+ "updates": [
+ {
+ "update_id": "codex-2026-05-07-fractions-kit-parcours-fraction-support",
+ "node_key": "cycle_3/mathematiques/cm1/01_nombres_calcul_resolution/01B_fractions/kit_adaptatif/svg/parcours_fraction_support.svg",
+ "status": "treated",
+ "comment": "Aucune fraction ne doit etre representee avec un trait oblique, il faut un trait horizontal avec les enfants de cet age",
+ "reviewer": "codex",
+ "updated_at": "2026-05-07T19:55:00Z",
+ "treated_note": "Fractions visibles remplacees par une disposition numerateur, barre horizontale, denominateur. Etiquette de la demi-droite remontee au-dessus du point vert."
+ }
+ ]
+}