From e21fda6eb573f9ced6e95badcae1b420d0452425 Mon Sep 17 00:00:00 2001 From: Pierre Date: Fri, 15 Aug 2025 09:42:52 +0200 Subject: [PATCH] chore: remove tracked Python bytecode (.pyc, __pycache__) and update .gitignore --- --others --exclude-standard | 56 ++ .archcode/execution_context.yaml | 16 + .archcode/execution_plan.yaml | 56 ++ .../patches/pl-0001-auth-c61e8a96.patch.txt | 14 + .../patches/pl-0002-auth-4100de5c.patch.txt | 14 + .../patches/pl-0003-auth-a94c0c0e.patch.txt | 14 + .archcode/plan_draft_aggregated.yaml | 36 + .archcode/plan_validated.yaml | 26 + .gitignore | 9 + Git_help.txt | 201 ++++++ agents/__pycache__/acw.cpython-311.pyc | Bin 14312 -> 0 bytes agents/__pycache__/acwp.cpython-311.pyc | Bin 14307 -> 0 bytes .../agent_file_checker.cpython-311.pyc | Bin 7933 -> 0 bytes .../agent_module_checker.cpython-311.pyc | Bin 18510 -> 0 bytes agents/acw.py | 38 ++ agents/acwp.py | 4 +- agents/agent_module_compilator.py | 32 + agents/dir | 0 cli/__pycache__/main.cpython-311.pyc | Bin 14837 -> 0 bytes core/__pycache__/archiver.cpython-311.pyc | Bin 13631 -> 0 bytes core/__pycache__/error_policy.cpython-311.pyc | Bin 1810 -> 0 bytes core/__pycache__/fs_apply.cpython-311.pyc | Bin 9116 -> 0 bytes .../__pycache__/git_diffstats.cpython-311.pyc | Bin 8066 -> 0 bytes core/__pycache__/types.cpython-311.pyc | Bin 7594 -> 0 bytes core/__pycache__/yaml_io.cpython-311.pyc | Bin 9990 -> 0 bytes modules/auth/module_draft.yaml | 15 + project_draft.yaml | 18 + runner/__pycache__/run_plan.cpython-311.pyc | Bin 7961 -> 0 bytes runner/run_plan.py | 614 +++++++++++++----- 29 files changed, 1002 insertions(+), 161 deletions(-) create mode 100644 --others --exclude-standard create mode 100644 .archcode/execution_context.yaml create mode 100644 .archcode/execution_plan.yaml create mode 100644 .archcode/patches/pl-0001-auth-c61e8a96.patch.txt create mode 100644 .archcode/patches/pl-0002-auth-4100de5c.patch.txt create mode 100644 .archcode/patches/pl-0003-auth-a94c0c0e.patch.txt create mode 100644 .archcode/plan_draft_aggregated.yaml create mode 100644 .archcode/plan_validated.yaml create mode 100644 Git_help.txt delete mode 100644 agents/__pycache__/acw.cpython-311.pyc delete mode 100644 agents/__pycache__/acwp.cpython-311.pyc delete mode 100644 agents/__pycache__/agent_file_checker.cpython-311.pyc delete mode 100644 agents/__pycache__/agent_module_checker.cpython-311.pyc create mode 100644 agents/dir delete mode 100644 cli/__pycache__/main.cpython-311.pyc delete mode 100644 core/__pycache__/archiver.cpython-311.pyc delete mode 100644 core/__pycache__/error_policy.cpython-311.pyc delete mode 100644 core/__pycache__/fs_apply.cpython-311.pyc delete mode 100644 core/__pycache__/git_diffstats.cpython-311.pyc delete mode 100644 core/__pycache__/types.cpython-311.pyc delete mode 100644 core/__pycache__/yaml_io.cpython-311.pyc create mode 100644 modules/auth/module_draft.yaml create mode 100644 project_draft.yaml delete mode 100644 runner/__pycache__/run_plan.cpython-311.pyc diff --git a/--others --exclude-standard b/--others --exclude-standard new file mode 100644 index 0000000..a449ff0 --- /dev/null +++ b/--others --exclude-standard @@ -0,0 +1,56 @@ +diff --git a/agents/acw.py b/agents/acw.py +index 9a3617c..109c9be 100644 +--- a/agents/acw.py ++++ b/agents/acw.py +@@ -3,6 +3,8 @@ from __future__ import annotations +  + from typing import Any, Dict, List, Optional, Tuple + from textwrap import indent ++from pathlib import Path ++ + import hashlib + import re +  +@@ -145,6 +147,42 @@ def _hash_payload(s: str) -> str: + return hashlib.sha256(s.encode("utf-8")).hexdigest()[:12] +  +  ++ ++def _sanitize_marker(marker: str) -> str: ++ """ ++ Normalise un marker fourni par l'ACWP/runner. ++ - vide -> "" ++ - supprime espaces superflus et nouvelles lignes ++ - s'assure que le marker commence par un commentaire Python '#' pour sécurité ++ - retourne la chaîne sur une seule ligne (sans \n final) ++ """ ++ if not marker: ++ return "" ++ s = str(marker).strip() ++ # Retirer nouvelles lignes internes ++ s = " ".join(s.splitlines()) ++ # Préfixer par '# ' si l'utilisateur n'a fourni qu'un identifiant nu ++ if not s.startswith("#"): ++ s = "# " + s ++ return s ++ ++ ++def _default_markers(plan_line_id: str, file_path: str) -> tuple[str, str]: ++ """ ++ Génère des marqueurs begin/end sûrs et uniques pour une plan_line. ++ Format lisible et stable, évite collisions courantes. ++ """ ++ # rendre le plan_line_id sûr (chars non-alphanum -> underscore) ++ safe_id = re.sub(r"[^\w]", "_", (plan_line_id or "pl").strip()) ++ # incorporer un fragment de nom de fichier (utile pour debug) ++ fname = Path(file_path).stem if file_path else "file" ++ fname_safe = re.sub(r"[^\w]", "_", fname)[:24] ++ begin = f"# === ARCHCODE BEGIN {safe_id} {fname_safe} ===" ++ end = f"# === ARCHCODE END {safe_id} {fname_safe} ===" ++ return begin, end ++ ++ ++ + def _render_meta_inline(meta: Dict[str, Any]) -> str: + """Rend le dict `meta` en ligne juste après #{begin_meta: ...} avec tri des clés (diff stable).""" + items = [] diff --git a/.archcode/execution_context.yaml b/.archcode/execution_context.yaml new file mode 100644 index 0000000..c7f0f14 --- /dev/null +++ b/.archcode/execution_context.yaml @@ -0,0 +1,16 @@ +bus_message_id: BM-0001 +spec_version: v0.0.1 +title: Test projet minimal +loop_iteration: 0 +user_stories: +- id: US-1 + title: Authentification + description: Login / token +input_sources: [] +output_targets: [] +functional_objectives: +- Authentifier utilisateur +non_functional_constraints: [] +modules: +- auth +plan_validated_id: PV-f4facffe diff --git a/.archcode/execution_plan.yaml b/.archcode/execution_plan.yaml new file mode 100644 index 0000000..7638c70 --- /dev/null +++ b/.archcode/execution_plan.yaml @@ -0,0 +1,56 @@ +execution_plan: + project_name: project + bus_message_id: BM-0001 + plan_validated_id: PV-f4facffe + spec_version_ref: v0.0.1 + loop_iteration: 0 + folder_root: archcode_app/ + generated_at: '2025-08-15T08:35:52' + total_lines: 3 + lines: + - plan_line_id: pl-0001-auth-c61e8a96 + module_name: auth + user_story_id: US-1 + responsibilities: &id001 + - Gérer l'authentification (login, token) + depends_on: [] + priority: haute + file_target: archcode_app/auth/__init__.py + file_kind: code + action: create_or_update + role_hint: null + meta: + bus_message_id: BM-0001 + plan_validated_id: PV-f4facffe + plan_line_ref: pl-0001-auth-c61e8a96 + loop_iteration: 0 + - plan_line_id: pl-0002-auth-4100de5c + module_name: auth + user_story_id: US-1 + responsibilities: *id001 + depends_on: [] + priority: haute + file_target: archcode_app/auth/handlers.py + file_kind: code + action: create_or_update + role_hint: handler + meta: + bus_message_id: BM-0001 + plan_validated_id: PV-f4facffe + plan_line_ref: pl-0002-auth-4100de5c + loop_iteration: 0 + - plan_line_id: pl-0003-auth-a94c0c0e + module_name: auth + user_story_id: US-1 + responsibilities: *id001 + depends_on: [] + priority: haute + file_target: archcode_app/auth/models.py + file_kind: code + action: create_or_update + role_hint: model + meta: + bus_message_id: BM-0001 + plan_validated_id: PV-f4facffe + plan_line_ref: pl-0003-auth-a94c0c0e + loop_iteration: 0 diff --git a/.archcode/patches/pl-0001-auth-c61e8a96.patch.txt b/.archcode/patches/pl-0001-auth-c61e8a96.patch.txt new file mode 100644 index 0000000..10ef953 --- /dev/null +++ b/.archcode/patches/pl-0001-auth-c61e8a96.patch.txt @@ -0,0 +1,14 @@ +#{begin_meta: { content_hash: 9d283c7d8641, file: archcode_app/auth/__init__.py, marker_begin: # === ARCHCODE BEGIN pl_0001_auth_c61e8a96 __init__ ===, marker_end: # === ARCHCODE END pl_0001_auth_c61e8a96 __init__ ===, markers_auto: true, module: archcode_app, plan_line_id: pl-0001-auth-c61e8a96, role: function, status_agent_file_checker: pending, status_agent_module_checker: pending, timestamp: 2025-08-15T09:20:55 }} +# === ARCHCODE BEGIN pl_0001_auth_c61e8a96 __init__ === +def init() -> None: + """mARCHCode/ACW + Rôle: function + Acceptance (rappel): + - Gérer l'authentification (login, token) + NOTE: Implémentation minimale générée automatiquement. + Compléter la logique lors des itérations suivantes. + """ + # TODO: implémenter la logique métier + raise NotImplementedError("À implémenter par itération suivante") +# === ARCHCODE END pl_0001_auth_c61e8a96 __init__ === +#{end_meta} diff --git a/.archcode/patches/pl-0002-auth-4100de5c.patch.txt b/.archcode/patches/pl-0002-auth-4100de5c.patch.txt new file mode 100644 index 0000000..97b7861 --- /dev/null +++ b/.archcode/patches/pl-0002-auth-4100de5c.patch.txt @@ -0,0 +1,14 @@ +#{begin_meta: { content_hash: 9084de6f7771, file: archcode_app/auth/handlers.py, marker_begin: # === ARCHCODE BEGIN pl_0002_auth_4100de5c handlers ===, marker_end: # === ARCHCODE END pl_0002_auth_4100de5c handlers ===, markers_auto: true, module: archcode_app, plan_line_id: pl-0002-auth-4100de5c, role: function, status_agent_file_checker: pending, status_agent_module_checker: pending, timestamp: 2025-08-15T09:20:55 }} +# === ARCHCODE BEGIN pl_0002_auth_4100de5c handlers === +def handlers() -> None: + """mARCHCode/ACW + Rôle: function + Acceptance (rappel): + - Gérer l'authentification (login, token) + NOTE: Implémentation minimale générée automatiquement. + Compléter la logique lors des itérations suivantes. + """ + # TODO: implémenter la logique métier + raise NotImplementedError("À implémenter par itération suivante") +# === ARCHCODE END pl_0002_auth_4100de5c handlers === +#{end_meta} diff --git a/.archcode/patches/pl-0003-auth-a94c0c0e.patch.txt b/.archcode/patches/pl-0003-auth-a94c0c0e.patch.txt new file mode 100644 index 0000000..17bd5f4 --- /dev/null +++ b/.archcode/patches/pl-0003-auth-a94c0c0e.patch.txt @@ -0,0 +1,14 @@ +#{begin_meta: { content_hash: 81f51aca1b11, file: archcode_app/auth/models.py, marker_begin: # === ARCHCODE BEGIN pl_0003_auth_a94c0c0e models ===, marker_end: # === ARCHCODE END pl_0003_auth_a94c0c0e models ===, markers_auto: true, module: archcode_app, plan_line_id: pl-0003-auth-a94c0c0e, role: function, status_agent_file_checker: pending, status_agent_module_checker: pending, timestamp: 2025-08-15T09:20:55 }} +# === ARCHCODE BEGIN pl_0003_auth_a94c0c0e models === +def models() -> None: + """mARCHCode/ACW + Rôle: function + Acceptance (rappel): + - Gérer l'authentification (login, token) + NOTE: Implémentation minimale générée automatiquement. + Compléter la logique lors des itérations suivantes. + """ + # TODO: implémenter la logique métier + raise NotImplementedError("À implémenter par itération suivante") +# === ARCHCODE END pl_0003_auth_a94c0c0e models === +#{end_meta} diff --git a/.archcode/plan_draft_aggregated.yaml b/.archcode/plan_draft_aggregated.yaml new file mode 100644 index 0000000..238cc57 --- /dev/null +++ b/.archcode/plan_draft_aggregated.yaml @@ -0,0 +1,36 @@ +plan_draft_aggregated: + project_name: project + bus_message_id: BM-0001 + spec_version_ref: v0.0.1 + loop_iteration: 0 + modules: + - auth + dependencies: [] + folder_structure: {} + items: + - status: ok + source_path: C:\Users\Utilisateur\Documents\mARCHCode\modules\auth\module_draft.yaml + ingested_at: '2025-08-15T08:31:23' + module_draft: + module_name: auth + user_story_id: US-1 + responsibilities: + - Gérer l'authentification (login, token) + files_expected: + - auth/__init__.py + - auth/handlers.py + - auth/models.py + depends_on: [] + inputs: [] + outputs: [] + validator_status: ok + meta: + priority: haute + warnings: [] + stats: + total_items: 1 + validated: 1 + pending: 0 + rejected: 0 + issued_at: '2025-08-15T08:31:23' + aggregated_at: '2025-08-15T08:31:23' diff --git a/.archcode/plan_validated.yaml b/.archcode/plan_validated.yaml new file mode 100644 index 0000000..4361d00 --- /dev/null +++ b/.archcode/plan_validated.yaml @@ -0,0 +1,26 @@ +plan_validated: + plan_validated_id: PV-f4facffe + bus_message_id: BM-0001 + spec_version_ref: v0.0.1 + loop_iteration: 0 + project_name: project + modules: + - module_name: auth + user_story_id: US-1 + responsibilities: + - Gérer l'authentification (login, token) + inputs: [] + outputs: [] + files_expected: + - auth/__init__.py + - auth/handlers.py + - auth/models.py + depends_on: [] + technical_constraints: [] + meta: + priority: haute + meta: + comment_agent_plan_validator: '' + created_at: '2025-08-15T08:33:36' + validated_at: '2025-08-15T08:33:36' + plan_diffs: [] diff --git a/.gitignore b/.gitignore index 91a415c..b0439d9 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,12 @@ build/ *.tar.gz !.archcode/context_snapshots/ + +cat >> .gitignore <<'EOF' +# Bytecode python +__pycache__/ +*.py[cod] +*$py.class +EOF__pycache__/ +*.py[cod] +*$py.class diff --git a/Git_help.txt b/Git_help.txt new file mode 100644 index 0000000..8b29416 --- /dev/null +++ b/Git_help.txt @@ -0,0 +1,201 @@ + + + +créer environnement .venv + + +dir /b +(si .venv n'est pas listé, alors le créer) + +python -m venv .venv + + +Tester agent_module_compilator (commande à lancer depuis la racine du repo) +.venv\Scripts\python.exe -m agents.agent_module_compilator collect --reset-if-missing --update-ec + + +quand plan_draft_aggregated a été généré, faire + +.\.venv\Scripts\python.exe -m agents.agent_plan_validator validate ^ + --ec .archcode\execution_context.yaml ^ + --pga .archcode\plan_draft_aggregated.yaml ^ + --out .archcode\plan_validated.yaml ^ + --comment-out .archcode\comment_agent_plan_validator.yaml ^ + --update-ec + +quand le plan_validated a été généré, construire l’execution_plan.yaml (transformateur déterministe) + +.\.venv\Scripts\python.exe -m scripts.execution_plan_transformer build ^ + --pv .archcode\plan_validated.yaml ^ + --pd .archcode\project_draft.yaml ^ + --ec .archcode\execution_context.yaml ^ + --out .archcode\execution_plan.yaml + + + + + + + +Git + + +Vérifier ce qui a changé (rapide, sans risque) + +Ça te permet de voir exactement quelles modifications ont été faites avant d’agir. + +REM voir l'état synthétique +git status + +REM voir le diff d'un fichier modifié important +git diff agents\acw.py + +REM lister les fichiers non trackés (pour confirmer) +git ls-files --others --exclude-standard + +##################################################### + +Tu veux aussi committer les modifications de code + +Si tu as volontairement patché agents/acw.py ou runner/run_plan.py et que tu veux les inclure : + +git checkout -b archcode-self/local-run +git add agents\acw.py agents\acwp.py runner\run_plan.py agents\agent_module_compilator.py +git add .archcode\* (ou les fichiers .archcode listés avant) +git commit -m "feat: update ACW/runner + add generated patches from local run" + +##################################################### + +Tu veux annuler / retirer les modifications non désirées + +Si les fichiers listés comme modified sont accidentels (par ex. tu n’as pas voulu toucher agents/*.py) : + +Pour annuler les changements non commités (retour à HEAD) : + +git restore agents\acw.py +git restore agents\acwp.py +git restore agents\agent_module_compilator.py +git restore runner\run_plan.py + + +Pour stocker les modifications temporairement (stash) : + +git stash push -m "wip before applying patches" +############################################################################ +) Si tu veux revenir à un état propre puis refaire le run + +Si tu veux annuler tout et repartir : + +REM nettoyer modifications non commités +git restore . + +REM supprimer les fichiers non trackés créés par le run (danger : destructif) +REM liste d'abord pour vérifier +git clean -n -d +REM puis exécuter si OK +git clean -f -d +############################################################ +stager tout et committer + + +0/ commande unique (à faire dans mARCHCode) +changer éventuellement le nom de la branche archcode-self/local-run + +git checkout -b archcode-self/local-run2 && git add -A && git commit -m "chore(run): commit local run artifacts and fixes" && git push --set-upstream origin archcode-self/local-run + +et vérifier éventuellement avec + +git status +git log -1 --stat +git branch -vv + + + +############################################################ + + + + + + + + + + + + + + +1) (Recommandé) créer une branche de travail + +Cette étape évite de polluer master si tu veux expérimenter : + +git checkout -b archcode-self/local-run + +2) Stager tout et committer +git add -A +git commit -m "chore(run): commit all local changes and generated artifacts from local run" + + +Vérifie le commit effectué : + +git status +git log -1 --stat + + +3) (Optionnel) pousser la branche vers le remote + +Tentative simple (va demander tes identifiants si nécessaire) : + +git push --set-upstream origin archcode-self/local-run + + + + + +Si le push réussit : top. + +Si le push échoue avec 403 / permission denied (cas fréquent avec github-actions[bot] ou PAT manquant), tu as plusieurs options : + +Option A — utiliser Git Credential Manager (recommandé) +Configure GCM et relance le push ; il te demandera ton token une seule fois : + +git config --global credential.helper manager-core +git push --set-upstream origin archcode-self/local-run + + +Quand ça demande username/password, mets ton nom d'utilisateur GitHub et en mot de passe colle ton PAT (token). + +Option B — pousser en ligne de commande en incorporant un token (moins sûr : laisse une trace dans l’historique shell — évite si possible) +Remplace GH_PAT et USER/REPO par tes valeurs : + +git push https://x-access-token:GH_PAT@github.com/USER/REPO.git archcode-self/local-run:archcode-self/local-run + + +Après, restaure l'URL du remote pour ne pas garder le token en clair : + +git remote set-url origin https://github.com/USER/REPO.git + + +Option C — créer un fork / sandbox remote +Si tu préfères, tu peux simplement garder le commit local (pas de push) et nous travaillerons dessus ensuite. + +######################################################## +4) Si tu as commis par erreur et veux annuler le commit + +Pour annuler le dernier commit mais garder les modifications staged (soft): + +git reset --soft HEAD~1 + + +Pour annuler le commit et désindexer (remet tout en unstaged): + +git reset --mixed HEAD~1 + + +Pour annuler tout et revenir à l’état du dernier commit (destructif — perd les changements non commités) : + +git restore . # ou, si git ancien : git checkout -- . + + + diff --git a/agents/__pycache__/acw.cpython-311.pyc b/agents/__pycache__/acw.cpython-311.pyc deleted file mode 100644 index b1d987f9df2b20c9393793f103fe7851e7474f9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14312 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 rc&UHgLLD2;+eYa|*uE6!%RoW1Ac+xWoe=$@Z% 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 str: return hashlib.sha256(s.encode("utf-8")).hexdigest()[:12] + +def _sanitize_marker(marker: str) -> str: + """ + Normalise un marker fourni par l'ACWP/runner. + - vide -> "" + - supprime espaces superflus et nouvelles lignes + - s'assure que le marker commence par un commentaire Python '#' pour sécurité + - retourne la chaîne sur une seule ligne (sans \n final) + """ + if not marker: + return "" + s = str(marker).strip() + # Retirer nouvelles lignes internes + s = " ".join(s.splitlines()) + # Préfixer par '# ' si l'utilisateur n'a fourni qu'un identifiant nu + if not s.startswith("#"): + s = "# " + s + return s + + +def _default_markers(plan_line_id: str, file_path: str) -> tuple[str, str]: + """ + Génère des marqueurs begin/end sûrs et uniques pour une plan_line. + Format lisible et stable, évite collisions courantes. + """ + # rendre le plan_line_id sûr (chars non-alphanum -> underscore) + safe_id = re.sub(r"[^\w]", "_", (plan_line_id or "pl").strip()) + # incorporer un fragment de nom de fichier (utile pour debug) + fname = Path(file_path).stem if file_path else "file" + fname_safe = re.sub(r"[^\w]", "_", fname)[:24] + begin = f"# === ARCHCODE BEGIN {safe_id} {fname_safe} ===" + end = f"# === ARCHCODE END {safe_id} {fname_safe} ===" + return begin, end + + + def _render_meta_inline(meta: Dict[str, Any]) -> str: """Rend le dict `meta` en ligne juste après #{begin_meta: ...} avec tri des clés (diff stable).""" items = [] diff --git a/agents/acwp.py b/agents/acwp.py index 8503e2a..2bf7e55 100644 --- a/agents/acwp.py +++ b/agents/acwp.py @@ -1,4 +1,4 @@ -# agents/agent_code_writer_planner.py +# agents/ACWP.py from __future__ import annotations from typing import Any, Dict, Iterable, List, Optional @@ -6,6 +6,8 @@ import uuid from core.types import PlanLine + + """ agent_code_writer_planner (ACWP) — mARCHCode / Phase 3 ====================================================== diff --git a/agents/agent_module_compilator.py b/agents/agent_module_compilator.py index 595d34c..08128e8 100644 --- a/agents/agent_module_compilator.py +++ b/agents/agent_module_compilator.py @@ -521,6 +521,38 @@ def cmd_show(out: Path) -> None: for m in mods: print(f" - {m:12s} : {statuses.get(m, '∅')}") +def collect_modules( + *, + ec_yaml: Path, + pd_yaml: Path, + out: Path, + roots: List[Path], + patterns: Optional[List[str]], + reset: bool, + reset_if_missing: bool, + allow_non_ok: bool, + accept_untagged: bool, + update_ec: bool, +) -> None: + """ + Wrapper for backward compatibility: forwards call to cmd_collect. + + Ce wrapper existe pour conserver l'API historique (anciennement certains + appels testaient collect_modules(...) directement). Il doit être défini + avant main() pour être disponible lorsque le module est exécuté en script. + """ + return cmd_collect( + ec_yaml=ec_yaml, + pd_yaml=pd_yaml, + out=out, + roots=roots, + patterns=patterns, + reset=reset, + reset_if_missing=reset_if_missing, + allow_non_ok=allow_non_ok, + accept_untagged=accept_untagged, + update_ec=update_ec, + ) # ----------------------------------------------------------------------------- # CLI diff --git a/agents/dir b/agents/dir new file mode 100644 index 0000000..e69de29 diff --git a/cli/__pycache__/main.cpython-311.pyc b/cli/__pycache__/main.cpython-311.pyc deleted file mode 100644 index 5b304afc66cebbea99cff2fffcd0e7b51498e577..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14837 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 awHXvwdh|O-?;K5&znqn_ZGm92Z2vD65S-@# diff --git a/core/__pycache__/archiver.cpython-311.pyc b/core/__pycache__/archiver.cpython-311.pyc deleted file mode 100644 index 092242d6b40e91cacf109ebdd6c44068ec871ca9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13631 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 diff --git a/core/__pycache__/error_policy.cpython-311.pyc b/core/__pycache__/error_policy.cpython-311.pyc deleted file mode 100644 index 4665867c10ab7649c58948fbb8b1bdafcadec953..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1810 zcmbtUOK;mo5MD~ui}5RtpD0{xeJCZgb`GvlAgB$7nRNg?q}Wv!vv>ZrV$RZ-2i%7enYb@zbM3S2_I@ltV;N1rdovEU6Z$L?jeJHxY?GMntAj8hIuo z^b>p`Rb+CB#HmaYG)j^*x)~>_$FWL`D61&b=lv=nq%6DzDS6Hr$IHwzjRt<%{1(@; zE4f=aMkr3-xw|~WsZJW1x7$Nm@?1b=JN;#(uhYGW-`?|3M zEB_ER&q%(Ux4oLtML-~+%mJOg58@C}RFMEh5h7KhBmx+T0wUx}oWv>#60aml0??84 z`~MMu_YtgJYd{c?Vv%46hq-hxIX-f!;p+D-7$mywoX_5@-{&-JSKrgXoN<3RETd-H z2HU2b+6MK9x~X|+GD{aF@nf61t6P4;vv+K^YnL)9KdEW9UZa}kE1F0mU;*?#O?%+! zR(B?$X@nUt9ADiitz|#+hwqfHuNU)Lp_D7%%CF>0xx7ELTrL)J8`|AMxtLumluLf> zMs_V*^b_l)+okfo(h)ip>3X%0yH;HXU^&%w*93?`DLr1j&J3>xsB@}kSg#t4(`pc( z7KSogZ*b^i6tEFcoU6c^XHQ#RY{`r5{&$);+LJRad8|F2X~`q)zAMdqdtkhIQ#^dM z=fO{Oq2eb2%>khNOXu+9#{m!?w~Yv_=#kK)@SQ#bc8DG!w>JawiykJ>POydp8dfjb zoO$~n8?i$`T^r>Q+)ZcpQCoV0-?RJawcRS*X@EM}D}plx17#hD8GtkoI`9oGjE|pU zOTE}4oEofF(`|qxoekXhv7lG@IQaFGyxT6BSXiDx80ABPT^4K@SSG?Jggyz(5gEXk zkAd(f;6@2Sthr3H>7J{F+w*r}dP zkItW@=3A-x^Fwndskv5au056hv2Zdq-Gt5{!Tt;3 zEI>vnBLz`q;(S1yPo~iKT-3vlIxY_b6i4F%fFYPfAg_|2gzdA)--BNP1m^%)Cn`x& zCo&+(9V8e=(pU?Po_{;>xHQp0|7Kt3z8m>djQ{m|a=2d_?;v1LzYN4LbAf2xs|4~t IS#Z+70TmbJU;qFB diff --git a/core/__pycache__/fs_apply.cpython-311.pyc b/core/__pycache__/fs_apply.cpython-311.pyc deleted file mode 100644 index 4bc3d637bb81c275e9e133a4a53bc484f85d2bc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9116 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 diff --git a/core/__pycache__/git_diffstats.cpython-311.pyc b/core/__pycache__/git_diffstats.cpython-311.pyc deleted file mode 100644 index 3c7e4ef397b6fabbfa6e769e7076746f5a50463e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8066 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 diff --git a/runner/run_plan.py b/runner/run_plan.py index 276992b..ea88ffa 100644 --- a/runner/run_plan.py +++ b/runner/run_plan.py @@ -3,197 +3,491 @@ import argparse import os +import re +from dataclasses import dataclass from datetime import datetime from pathlib import Path -from typing import Optional - -from core.yaml_io import load_execution_plan -from core.fs_apply import apply_patchblock_to_file as apply_patch -from core.types import PlanLine -from agents.acwp import build_prompt -from agents.acw import run_acw -from agents.agent_file_checker import check_file -from agents.agent_module_checker import check_module -from core.git_diffstats import ensure_branch, stage_and_commit - -# Injection SHA best-effort : présent chez toi dans adapters/git_adapter -try: - from adapters.git_adapter import inject_commit_sha_into_meta # type: ignore -except Exception: +from typing import Any, Dict, List, Optional - def inject_commit_sha_into_meta(pb, sha: Optional[str]) -> None: - """ - Injecte le SHA de commit dans `pb.meta.commit_sha` si possible (fallback no-op). +import yaml +import sys +import importlib - Args: - pb: PatchBlock (ou objet duck-typed) dont l'attribut `meta` peut contenir `commit_sha`. - sha: SHA de commit à injecter (ou None pour ne rien faire). +# Forcer la racine du repo en tête du PYTHONPATH (évite le conflit avec un paquet "agents" tiers) +PROJECT_ROOT = Path(__file__).resolve().parents[1] +if str(PROJECT_ROOT) not in sys.path: + sys.path.insert(0, str(PROJECT_ROOT)) - Returns: - None. Effet de bord tolérant sur `pb.meta` si l'attribut existe. - """ - return - -from core.archiver import ( - archive_execution_plan, - archive_patch_before, - archive_patch_after, - archive_patch_post_commit, - append_console_log, - archive_run_info, -) """ =============================================================================== -mARCHCode — runner/run_plan.py (MVP Phase 3) +mARCHCode — runner/run_plan.py (Fusion 'bridge local' + 'runner complet') ------------------------------------------------------------------------------- Rôle -- Exécuter un execution_plan YAML : pour chaque PlanLine → ACWP → ACW → checkers - → apply (FS) → commit Git → archivage (.arch_runs/…). +- Lire un execution_plan (Phase 2) au format "lines" (transformer) **ou** + un plan typé "modules/plan_lines" (format historique). +- Construire des PlanLine minimales conformes aux attentes d'ACWP. +- Deux modes : + • --dry-run : ACWP → ACW → écrit les patchs dans .archcode/patches (pas d'effets FS) + • mode normal : ACWP → ACW → checkers → apply FS → commit Git → archives Entrées -- ep_path : chemin du fichier execution_plan.yaml -- repo_root : racine du dépôt cible (écriture FS + Git) - -Sorties & effets -- Fichiers écrits dans repo_root selon pb.meta.file -- Commits Git (si repo initialisé) + SHA injecté dans pb.meta (best-effort) -- Archives lisibles dans .arch_runs// - - plan.yaml, patch_before.yaml, patch_after.yaml, post_commit.yaml, console.log +- --ep : chemin du execution_plan.yaml +- --repo : racine du dépôt cible (pour apply/commit) +- --archive-dir: dossier d’archives (si absent → .arch_runs/) +- --dry-run : désactive checkers/apply/commit/archives, n’émet que les patchs +- --patch-dir : dossier de sortie des patchs (défaut .archcode/patches) Notes -- Pas de “init_run_dir” dans core.archiver : on crée le dossier nous-mêmes (os.makedirs). -- apply_patch : alias de core.fs_apply.apply_patchblock_to_file (signature locale). -- Robustesse : best-effort sur Git (le run continue même si Git est absent). +- Tente d’importer les utilitaires (archiver, git, fs_apply). S’il manque quelque chose, + le runner continue en best-effort (ou échoue proprement selon le mode). =============================================================================== """ +# -----------------------------------------------------------------------------# +# Utils import +# -----------------------------------------------------------------------------# + +def _import_first(candidates: List[str]): + """Importe le premier module disponible dans `candidates`.""" + last_err: Optional[BaseException] = None + for name in candidates: + try: + return importlib.import_module(name) + except BaseException as e: + last_err = e + continue + raise ModuleNotFoundError(f"Impossible d'importer l'un de {candidates}: {last_err}") + +# -----------------------------------------------------------------------------# +# Utils YAML / FS +# -----------------------------------------------------------------------------# + +def _read_yaml(path: Path) -> Dict[str, Any]: + """Charge un YAML en dict ({} si vide).""" + data = yaml.safe_load(path.read_text(encoding="utf-8")) + return data or {} + + +def _ensure_dir(p: Path) -> None: + """Crée le répertoire p si nécessaire (parents inclus).""" + p.mkdir(parents=True, exist_ok=True) + + +def _posix(s: str) -> str: + """Normalise un chemin en séparateurs '/' (utile pour les metas/patchs).""" + return s.replace("\\", "/") + + +# -----------------------------------------------------------------------------# +# Shims & heuristiques PlanLine +# -----------------------------------------------------------------------------# + +@dataclass +class SimplePlanLine: + """ + Duck-typed PlanLine minimale pour ACWP._validate_plan_line(). + Champs requis : + - plan_line_id, file (.py), op ∈ {'create','modify'}, role, target_symbol, signature, acceptance[] + """ + plan_line_id: str + file: str + op: str + role: str + target_symbol: str + signature: str + acceptance: List[str] + + # champs optionnels + path: Optional[str] = None + description: Optional[str] = None + depends_on: Optional[List[str]] = None + constraints: Optional[Dict[str, Any]] = None + allow_create: bool = True + markers: Optional[Dict[str, str]] = None + plan_line_ref: Optional[str] = None + intent_fingerprint: Optional[str] = None + + +def _safe_ident(s: str, default: str = "func") -> str: + """Convertit une chaîne en identifiant Python simple (a-z, 0-9, _)""" + base = re.sub(r"[^\w]", "_", (s or "").strip()) + base = re.sub(r"_+", "_", base).strip("_") + return base or default + + +def _role_from_hint(role_hint: Optional[str], file_kind: Optional[str]) -> str: + """Infère un rôle conservateur : 'dto' si demandé, sinon 'function'.""" + rh = (role_hint or "").lower().strip() + if rh == "dto": + return "dto" + return "function" + -def run_plan(ep_path: str, repo_root: str, *, archive_dir: Optional[str] = None) -> None: +def _derive_sig_and_symbol(role: str, filename: str) -> tuple[str, str]: + """ + Déduit un symbole et une signature exécutable minimale depuis le nom de fichier. + - DTO → def make__dto() -> dict: + - sinon → def () -> None: + """ + stem = Path(filename).stem + ident = _safe_ident(stem, "func") + if role == "dto": + symbol = f"make_{ident}_dto" + sig = f"def {symbol}() -> dict:" + return sig, symbol + symbol = ident + sig = f"def {symbol}() -> None:" + return sig, symbol + + +def _ensure_py_target(path: str) -> str: + """Force une cible .py (si dossier → __init__.py ; si sans suffixe → .py).""" + if path.endswith(".py"): + return _posix(path) + p = Path(path) + if path.endswith("/") or p.suffix == "": + return _posix(str(p / "__init__.py")) + return _posix(str(p.with_suffix(".py"))) + + +# -----------------------------------------------------------------------------# +# Charge/normalise les PlanLines depuis un execution_plan +# -----------------------------------------------------------------------------# + +def _from_ep_lines(ep_root: Dict[str, Any]) -> tuple[List[SimplePlanLine], Dict[str, Any]]: + """ + Lecture du format Phase 2 (scripts.execution_plan_transformer) : + { + execution_plan: { + bus_message_id, loop_iteration, lines: [ { plan_line_id, file_target, role_hint, ... }, ... ] + } + } + """ + ep = ep_root.get("execution_plan") or ep_root + bus_message_id = ep.get("bus_message_id") + loop_iteration = ep.get("loop_iteration") + raw_lines = ep.get("lines") or [] + plan_lines: List[SimplePlanLine] = [] + + for ln in raw_lines: + file_target = str(ln.get("file_target") or "").strip() + if not file_target: + continue + file_posix = _ensure_py_target(file_target) + role = _role_from_hint(ln.get("role_hint"), ln.get("file_kind")) + sig, symbol = _derive_sig_and_symbol(role, Path(file_posix).name) + + responsibilities = ln.get("responsibilities") or [] + acceptance = [str(x) for x in responsibilities if str(x).strip()] + if not acceptance: + acceptance = [f"fonction {symbol} existe", "fichier Python valide (imports ok)"] + + pl = SimplePlanLine( + plan_line_id=str(ln.get("plan_line_id") or ""), + file=file_posix, + op=("create" if str(ln.get("action") or "create").lower().startswith("create") else "modify"), + role=role, + target_symbol=symbol, + signature=sig, + acceptance=acceptance, + path=None, + description=None, + depends_on=list(ln.get("depends_on") or []), + constraints={}, + allow_create=True, + markers=None, + plan_line_ref=str(ln.get("plan_line_id") or None), + intent_fingerprint=None, + ) + if pl.plan_line_id and pl.file.endswith(".py"): + plan_lines.append(pl) + + return plan_lines, {"bus_message_id": bus_message_id, "loop_iteration": loop_iteration} + + +def _from_module_plan(ep_root: Dict[str, Any]) -> tuple[List[SimplePlanLine], Dict[str, Any]]: + """ + Lecture d’un format historique : + { + execution_plan_id, modules: [ + { module: "auth", plan_lines: [ {plan_line_id, file, role, signature, ...}, ... ] } + ] + } + On convertit vers SimplePlanLine en comblant les trous (signature/rôle si manquants). + """ + bus_message_id = ep_root.get("bus_message_id") + loop_iteration = ep_root.get("loop_iteration") + modules = ep_root.get("modules") or [] + plan_lines: List[SimplePlanLine] = [] + + for mod in modules: + for ln in mod.get("plan_lines") or []: + file_path = _ensure_py_target(str(ln.get("file") or "").strip()) + role = (ln.get("role") or "function").lower() + if role not in ("dto", "function"): + role = "function" + sig = str(ln.get("signature") or "") + symbol = str(ln.get("target_symbol") or "") + if not sig or not symbol: + sig, symbol = _derive_sig_and_symbol(role, Path(file_path).name) + + acc = ln.get("acceptance") or [f"fonction {symbol} existe"] + pl = SimplePlanLine( + plan_line_id=str(ln.get("plan_line_id") or ""), + file=file_path, + op=("modify" if ln.get("op") == "modify" else "create"), + role=role, + target_symbol=symbol, + signature=sig, + acceptance=[str(a) for a in acc], + path=ln.get("path"), + description=ln.get("description"), + depends_on=list(ln.get("depends_on") or []), + constraints=dict(ln.get("constraints") or {}), + allow_create=bool(ln.get("allow_create", True)), + markers=dict(ln.get("markers") or {}) or None, + plan_line_ref=ln.get("plan_line_ref"), + intent_fingerprint=ln.get("intent_fingerprint"), + ) + if pl.plan_line_id and pl.file.endswith(".py"): + plan_lines.append(pl) + + return plan_lines, {"bus_message_id": bus_message_id, "loop_iteration": loop_iteration} + + +def _load_plan_lines(ep_path: Path) -> tuple[List[SimplePlanLine], Dict[str, Any]]: + """ + Charge un execution_plan et renvoie (plan_lines, meta). + Supporte les deux formats (Phase 2 “lines” et format “modules/plan_lines”). """ - Exécute un plan d’exécution mARCHCode de bout en bout (MVP local). - - Pipeline: - 1) Archive les métadonnées de run et le plan source. - 2) Charge l'ExecutionPlan typé. - 3) (Best-effort) prépare la branche Git de travail. - 4) Pour chaque PlanLine: - - ACWP → prompt - - ACW → PatchBlock - - Checkers fichier & module - - (si OK) apply FS + commit (best-effort) + archivage post-commit + root = _read_yaml(ep_path) + # Heuristique : présence d'une clé 'execution_plan' avec 'lines' → format Phase 2 + ep = root.get("execution_plan") or root + if isinstance(ep.get("lines"), list): + return _from_ep_lines(root) + # Sinon : format historique (modules/plan_lines) + return _from_module_plan(ep) + + +# -----------------------------------------------------------------------------# +# Archiver / Git / FS (best-effort) +# -----------------------------------------------------------------------------# + +# archiver (best-effort) +try: + from core.archiver import ( # type: ignore + archive_execution_plan, + archive_patch_before, + archive_patch_after, + archive_patch_post_commit, + append_console_log, + archive_run_info, + ) +except Exception: + def archive_execution_plan(text: str, run_dir: str) -> None: ... + def archive_patch_before(pb, run_dir: str) -> None: ... + def archive_patch_after(pb, run_dir: str) -> None: ... + def archive_patch_post_commit(pb, run_dir: str) -> None: ... + def append_console_log(msg: str, run_dir: str) -> None: ... + def archive_run_info(run_dir: str, **kwargs) -> None: ... + +# git adapter (best-effort) +try: + from core.git_diffstats import ensure_branch, stage_and_commit # type: ignore +except Exception: + def ensure_branch(*, repo_root: str) -> None: ... + def stage_and_commit(paths: List[str], message: str, *, repo_root: str) -> Optional[str]: + return None + +try: + from adapters.git_adapter import inject_commit_sha_into_meta # type: ignore +except Exception: + def inject_commit_sha_into_meta(pb, sha: Optional[str]) -> None: ... + + +# fs apply (obligatoire si mode apply) +try: + from core.fs_apply import apply_patchblock_to_file as apply_patch # type: ignore +except Exception: + apply_patch = None # type: ignore + + +# -----------------------------------------------------------------------------# +# Pipeline d'exécution +# -----------------------------------------------------------------------------# + +def run_plan( + ep_path: str, + repo_root: str, + *, + archive_dir: Optional[str] = None, + dry_run: bool = False, + patch_dir: Optional[str] = None, +) -> None: + """ + Exécute un execution_plan YAML. + + Modes: + - dry_run=True → ACWP → ACW, écrit les patchs dans patch_dir, pas d'effets FS. + - dry_run=False → ACWP → ACW → checkers → apply FS → commit Git → archives. Args: - ep_path: Chemin vers le fichier `execution_plan.yaml`. - repo_root: Racine du dépôt cible (chemins d'écriture/commit relatifs). - archive_dir: Dossier des artefacts du run (créé si absent). Si None, - un dossier `.arch_runs/` sera créé. - - Returns: - None. Les effets se matérialisent sur le FS, Git (si dispo) et dans `archive_dir`. - - Raises: - FileNotFoundError: si `ep_path` est introuvable (via chargeur). - ValueError: si la structure du plan est invalide (via chargeur). - Toute autre exception est capturée localement lors des étapes best-effort - (ex. Git non initialisé) pour permettre la poursuite du run. + ep_path: Chemin du execution_plan.yaml. + repo_root: Racine du repo (pour apply/commit). + archive_dir: Dossier d’archive (créé si absent). Ignoré si dry_run. + dry_run: Active le mode “bridge local” sans side-effects. + patch_dir: Dossier de sortie des patchs (défaut: .archcode/patches). """ - # --- Prépare le répertoire d’archives du run --- - if not archive_dir: - ts = datetime.now().strftime("%Y%m%d-%H%M%S") - archive_dir = f".arch_runs/{ts}" - os.makedirs(archive_dir, exist_ok=True) - archive_run_info(archive_dir, started_at=datetime.now().isoformat(timespec="seconds")) - append_console_log(f"[arch] start run, archive_dir={archive_dir}", run_dir=archive_dir) - - # Archive le plan source (copie brute) - try: - ep_text = Path(ep_path).read_text(encoding="utf-8") - except Exception as e: - ep_text = f"# [warn] impossible de lire {ep_path}: {e}" - archive_execution_plan(ep_text, run_dir=archive_dir) + ep_p = Path(ep_path) + plan_lines, meta = _load_plan_lines(ep_p) + if not plan_lines: + raise ValueError("Aucune PlanLine valide n’a été trouvée dans le plan.") - # Charge le plan typé - ep = load_execution_plan(ep_path) - print(f"[ExecutionPlan] → {ep.execution_plan_id}") - append_console_log(f"[plan] id={ep.execution_plan_id}", run_dir=archive_dir) + # Imports agents (ACWP/ACW) avec alias -> fallback + ACWP = _import_first(["agents.acwp", "agents.agent_code_writer_planner"]) + ACW = _import_first(["agents.acw", "agents.agent_code_writer"]) - # Branche de travail (si Git est initialisé) - try: - # NOTE: impl réelle dans core.git_diffstats ; appel conservé (best-effort). - ensure_branch(repo_root=repo_root) # type: ignore[call-arg] - print("• Branche de travail prête (archcode-self/… ou équivalent)") - append_console_log("[git] ensure_branch ok", run_dir=archive_dir) - except Exception as e: - print("• Git indisponible — on continue sans commit (MVP)") - append_console_log(f"[git] ensure_branch skipped: {e}", run_dir=archive_dir) - - # Contexte d’exécution : écrire depuis repo_root + # Checkers si pas dry-run + if not dry_run: + mod_file_checker = _import_first(["agents.agent_file_checker"]) + mod_module_checker = _import_first(["agents.agent_module_checker"]) + check_file = getattr(mod_file_checker, "check_file") + check_module = getattr(mod_module_checker, "check_module") + + # Prépare archiver si mode apply + run_dir = None + if not dry_run: + if not archive_dir: + ts = datetime.now().strftime("%Y%m%d-%H%M%S") + archive_dir = f".arch_runs/{ts}" + run_dir = archive_dir + os.makedirs(run_dir, exist_ok=True) + archive_run_info(run_dir, started_at=datetime.now().isoformat(timespec="seconds")) + try: + ep_text = Path(ep_path).read_text(encoding="utf-8") + except Exception as e: + ep_text = f"# [warn] lecture échouée {ep_path}: {e}" + archive_execution_plan(ep_text, run_dir=run_dir) + + # Patch dir (toujours, pour inspection) + patch_dir_p = Path(patch_dir or ".archcode/patches") + _ensure_dir(patch_dir_p) + + # Branche Git (best-effort) si mode apply + if not dry_run: + try: + ensure_branch(repo_root=repo_root) # type: ignore[call-arg] + if run_dir: + append_console_log("[git] ensure_branch ok", run_dir=run_dir) + print("• Branche de travail prête (best-effort)") + except Exception as e: + if run_dir: + append_console_log(f"[git] ensure_branch skipped: {e}", run_dir=run_dir) + print("• Git indisponible — on continue sans commit") + + # Exécution prev_cwd = os.getcwd() os.chdir(repo_root) try: - for mod in ep.modules: - module_name = mod.get("module", "unknown") - plan_lines = mod.get("plan_lines", []) - print(f"→ Module: {module_name} ({len(plan_lines)} plan_lines)") - append_console_log(f"[module] {module_name} ({len(plan_lines)})", run_dir=archive_dir) - - for pl_data in plan_lines: - pl = PlanLine(**pl_data) - print(f" • PlanLine {pl.plan_line_id} ({pl.file})") - append_console_log(f"[plan_line] {pl.plan_line_id} file={pl.file}", run_dir=archive_dir) - - # ACWP → prompt - prompt = build_prompt(pl) - - # ACW : PatchBlock - pb = run_acw(pl, prompt) - archive_patch_before(pb, run_dir=archive_dir) - - # Checkers - pb = check_file(pb) - pb = check_module(pb) - archive_patch_after(pb, run_dir=archive_dir) - - if pb.global_status == "ok": - # Applique le patch sur FS (signature locale : pb → (path, count)) - try: - apply_patch(pb) - append_console_log("[apply] file written", run_dir=archive_dir) - except Exception as e: - print(f" → APPLY FAILED: {e}") - append_console_log(f"[apply] failed: {e}", run_dir=archive_dir) - break - - # Commit Git (best-effort) - message = f"feat(mARCH): {pl.plan_line_id} {pl.role} {pl.target_symbol} (status={pb.global_status})" - try: - sha = stage_and_commit([pb.meta.file], message, repo_root=repo_root) # type: ignore[arg-type] - inject_commit_sha_into_meta(pb, sha) - archive_patch_post_commit(pb, run_dir=archive_dir) - short = (sha or "")[:7] if sha else "∅" - print(f" → OK: fichier écrit & commit {short}") - append_console_log(f"[git] commit {sha}", run_dir=archive_dir) - except Exception as e: - print(f" → OK: fichier écrit (commit non effectué: {e})") - append_console_log(f"[git] commit skipped: {e}", run_dir=archive_dir) - else: - reason = pb.error_trace or "module checker" - print(f" → REJECTED: {reason}") - append_console_log(f"[reject] {pl.plan_line_id}: {reason}", run_dir=archive_dir) + # writer tasks depuis ACWP + tasks = ACWP.plan_to_writer_tasks( + plan_lines, + execution_context=None, + bus_message_id=meta.get("bus_message_id"), + user_story_id=None, + user_story=None, + loop_iteration=meta.get("loop_iteration"), + ) + + produced = 0 + for wt in tasks: + # ACW + pb = ACW.write_code(wt) + + # Toujours sauver le patch (y compris dry-run) + patch_path = patch_dir_p / f"{wt['plan_line_id']}.patch.txt" + patch_path.write_text(pb.code, encoding="utf-8") + produced += 1 + print(f"[patch] {patch_path}") + + if dry_run: + # Pas de checkers, pas d’apply + continue + + # Archive avant checks + if run_dir: + archive_patch_before(pb, run_dir=run_dir) + + # Checkers + pb = check_file(pb) + pb = check_module(pb) + + # Archive après checks + if run_dir: + archive_patch_after(pb, run_dir=run_dir) + + if pb.global_status == "ok": + # Apply (FS) + if apply_patch is None: + raise RuntimeError("apply_patch indisponible (core.fs_apply manquant).") + try: + apply_patch(pb) # type: ignore[misc] + except Exception as e: + print(f" → APPLY FAILED: {e}") + if run_dir: + append_console_log(f"[apply] failed: {e}", run_dir=run_dir) break + + # Commit (best-effort) + msg = f"feat(mARCH): {wt['plan_line_id']} {wt.get('role')} {wt.get('target_symbol')}" + try: + sha = stage_and_commit([pb.meta.file], msg, repo_root=repo_root) # type: ignore[arg-type] + inject_commit_sha_into_meta(pb, sha) + if run_dir: + archive_patch_post_commit(pb, run_dir=run_dir) + short = (sha or "")[:7] if sha else "∅" + print(f" → OK: fichier écrit & commit {short}") + except Exception as e: + print(f" → OK: fichier écrit (commit non effectué: {e})") + if run_dir: + append_console_log(f"[git] commit skipped: {e}", run_dir=run_dir) + else: + reason = getattr(pb, "error_trace", None) or "module checker" + print(f" → REJECTED: {reason}") + if run_dir: + append_console_log(f"[reject] {wt['plan_line_id']}: {reason}", run_dir=run_dir) + break + + if dry_run: + print(f"[DONE] dry-run : {produced} patch(s) écrit(s) dans {patch_dir_p}") + else: + print(f"[DONE] run complet : {produced} patch(s) traités") finally: os.chdir(prev_cwd) -if __name__ == "__main__": - ap = argparse.ArgumentParser(description="Exécute un execution_plan YAML (Phase 3 mARCHCode)") +# -----------------------------------------------------------------------------# +# CLI +# -----------------------------------------------------------------------------# + +def _build_parser() -> argparse.ArgumentParser: + """Construit le parseur CLI (compatible ancien runner + options dry-run).""" + ap = argparse.ArgumentParser(description="Exécute un execution_plan (ACWP → ACW → checkers → apply)") ap.add_argument("--ep", required=True, help="Chemin vers execution_plan.yaml") ap.add_argument("--repo", default=".", help="Racine du repo (où écrire/committer)") - ap.add_argument( - "--archive-dir", - default=None, - help="Dossier d’archive des artefacts (défaut: .arch_runs/)" - ) - args = ap.parse_args() - run_plan(args.ep, args.repo, archive_dir=args.archive_dir) + ap.add_argument("--archive-dir", default=None, help="Dossier d’archives (ignoré en --dry-run)") + ap.add_argument("--dry-run", action="store_true", help="N’émettre que les patchs (pas de checkers/apply/git)") + ap.add_argument("--patch-dir", default=".archcode/patches", help="Dossier de sortie des patchs") + return ap + + +def main(argv: Optional[List[str]] = None) -> None: + """Point d’entrée CLI.""" + parser = _build_parser() + args = parser.parse_args(argv) + run_plan(args.ep, args.repo, archive_dir=args.archive_dir, dry_run=bool(args.dry_run), patch_dir=args.patch_dir) + + +if __name__ == "__main__": + main()