docs: importer zacus_conversation_bundle_v3 et clôturer le blocage ZIP intake #106
@@ -1,4 +1,4 @@
|
||||
# Architecture d'intégration ZIP — implémentation minimale (G1)
|
||||
# Architecture d'intégration du conversation bundle — baseline (G1)
|
||||
|
||||
## Entrée / sortie
|
||||
- **Entrée**: bundle conversationnel `scenario-ai-coherence/zacus_conversation_bundle_v3/*`.
|
||||
@@ -15,7 +15,8 @@
|
||||
- Bootstrap dépendances validateurs: `tools/setup/install_validators.sh`.
|
||||
|
||||
## Prochaine évolution proposée
|
||||
- Ajouter `tools/scenario/validate_runtime_bundle.py` pour vérifier la cohérence entre `scenario_runtime.json`, `scenario_canonical.yaml` et `scenario_promptable_template.yaml`.
|
||||
- Validateur ajouté: `tools/scenario/validate_runtime_bundle.py` (cohérence entre `scenario_runtime.json`, `scenario_canonical.yaml` et `scenario_promptable_template.yaml`).
|
||||
- Prochaine itération: intégration CI de ce contrôle dans la gate G3.
|
||||
|
||||
## Statut Gates
|
||||
- G1 Arch Freeze : **satisfait** pour ce lot d'import documentaire/data.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Artifact QA/Test — runtime bundle validator
|
||||
|
||||
- Date: 2026-02-25
|
||||
- Script ajouté: `tools/scenario/validate_runtime_bundle.py`
|
||||
- Cible: cohérence `scenario_runtime.json` / `scenario_canonical.yaml` / `scenario_promptable_template.yaml`.
|
||||
- Doc mise à jour: README bundle + workflow global.
|
||||
@@ -6,3 +6,5 @@
|
||||
3. **Exporter les briefings Markdown** via `tools/scenario/export_md.py` pour mettre à jour `kit-maitre-du-jeu/_generated/` et `docs/_generated/SCENARIO_BRIEF.md`.
|
||||
4. **Valider l’audio** avec `tools/audio/validate_manifest.py audio/manifests/zacus_v1_audio.yaml` afin que chaque piste corresponde à un fichier de `game/prompts/audio/`.
|
||||
5. **Valider les printables** avec `tools/printables/validate_manifest.py printables/manifests/zacus_v1_printables.yaml` pour s’assurer que chaque ID a un prompt accessible.
|
||||
|
||||
6. **Valider le conversation bundle** avec `tools/scenario/validate_runtime_bundle.py` pour vérifier la cohérence `scenario_runtime.json` / `scenario_canonical.yaml` / `scenario_promptable_template.yaml`.
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
{
|
||||
"date": "2026-02-25",
|
||||
"agent": "pm-spec",
|
||||
"agent": "qa-test",
|
||||
"changes": [
|
||||
"tools/scenario/validate_runtime_bundle.py",
|
||||
"scenario-ai-coherence/zacus_conversation_bundle_v3/README.md",
|
||||
"docs/WORKFLOWS.md",
|
||||
"specs/CONVERSATION_BUNDLE_INTAKE.md",
|
||||
"architecture/CONVERSATION_BUNDLE_ARCHITECTURE.md",
|
||||
"tools/requirements/validators.txt",
|
||||
"tools/setup/install_validators.sh",
|
||||
"docs/WORKFLOWS.md"
|
||||
"tools/setup/install_validators.sh"
|
||||
],
|
||||
"artifacts": [
|
||||
"artifacts/pm-spec/2026-02-25/conversation-bundle-intake.md",
|
||||
"artifacts/qa-test/2026-02-25/validator-bootstrap.md"
|
||||
"artifacts/qa-test/2026-02-25/validator-bootstrap.md",
|
||||
"artifacts/qa-test/2026-02-25/runtime-bundle-validator.md"
|
||||
],
|
||||
"gates": {
|
||||
"G0": "satisfied",
|
||||
@@ -20,5 +23,5 @@
|
||||
"G4": "pending",
|
||||
"G5": "partial"
|
||||
},
|
||||
"note": "Terminologie harmonisée autour du conversation bundle; validateurs installables et exécutés."
|
||||
"note": "Validateur runtime bundle ajouté + documentation mise à jour; contrôles projet exécutés."
|
||||
}
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
# Zacus Conversation Bundle (v3)
|
||||
|
||||
Files:
|
||||
- scenario_promptable_template.yaml : your editable template (prompt_input)
|
||||
- scenario_canonical.yaml : canonical YAML (copy into docs/... target)
|
||||
- scenario_runtime.json : runtime JSON (copy into data/story/scenarios/DEFAULT.json)
|
||||
- fsm_mermaid.md : FSM diagram
|
||||
- CODEX_PROMPT.md : ready-to-paste Codex prompt
|
||||
- zacus_v2.yaml : narrative scenario aligned with runtime + gameplay reality
|
||||
- CONVERSATION_SUMMARY.md : what changed / key decisions
|
||||
## Purpose
|
||||
Bundle de travail conversationnel/IA conservé en dehors de la source canon gameplay (`game/scenarios/*.yaml`).
|
||||
|
||||
Typical flow:
|
||||
1) Update prompt_input in scenario_promptable_template.yaml if needed.
|
||||
2) Use canonical/runtime files to update repo targets.
|
||||
3) Use zacus_v2.yaml as the printed narrative / MJ reference for the real game.
|
||||
## Files
|
||||
- `scenario_promptable_template.yaml`: template éditable (input prompt)
|
||||
- `scenario_canonical.yaml`: YAML canonique conversation bundle
|
||||
- `scenario_runtime.json`: JSON runtime dérivé
|
||||
- `fsm_mermaid.md`: diagramme FSM
|
||||
- `CODEX_PROMPT.md`: prompt Codex prêt à l'emploi
|
||||
- `zacus_v2.yaml`: narration MJ alignée bundle
|
||||
- `CONVERSATION_SUMMARY.md`: synthèse des décisions
|
||||
|
||||
## Validation workflow
|
||||
1. Installer les dépendances validateurs :
|
||||
- `bash tools/setup/install_validators.sh`
|
||||
2. Valider la cohérence interne du bundle :
|
||||
- `python3 tools/scenario/validate_runtime_bundle.py`
|
||||
3. Lancer les validateurs projet (non-régression canon/audio/printables) :
|
||||
- `python3 tools/scenario/validate_scenario.py game/scenarios/zacus_v1.yaml`
|
||||
- `python3 tools/scenario/export_md.py game/scenarios/zacus_v1.yaml`
|
||||
- `python3 tools/audio/validate_manifest.py audio/manifests/zacus_v1_audio.yaml`
|
||||
- `python3 tools/printables/validate_manifest.py printables/manifests/zacus_v1_printables.yaml`
|
||||
|
||||
## Notes
|
||||
- Ce bundle n'est pas promu automatiquement dans `game/scenarios/*.yaml`.
|
||||
- Toute promotion doit passer par une revue fonctionnelle dédiée.
|
||||
|
||||
Generated: 2026-02-25
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ZIP intake — statut après réception bundle (G0 Spec Freeze)
|
||||
# Conversation bundle intake — statut courant (G0 Spec Freeze)
|
||||
|
||||
## Contexte
|
||||
Le bundle annoncé par l'utilisateur est désormais disponible sous :
|
||||
Le bundle conversationnel est maintenu dans :
|
||||
`scenario-ai-coherence/zacus_conversation_bundle_v3/`.
|
||||
|
||||
## Critères d'acceptation (AC)
|
||||
@@ -21,7 +21,8 @@ Le bundle annoncé par l'utilisateur est désormais disponible sous :
|
||||
- Les formats `scenario_runtime.json`/templates peuvent nécessiter une validation dédiée ultérieure.
|
||||
|
||||
## Prochaine étape proposée
|
||||
- Ajouter un validateur dédié pour `scenario-ai-coherence/zacus_conversation_bundle_v3/scenario_runtime.json` et son template promptable, puis l'inclure dans les gates G3.
|
||||
- Validateur dédié implémenté: `tools/scenario/validate_runtime_bundle.py` (cohérence runtime/canonical/template).
|
||||
- Étape suivante: brancher ce validateur dans l'automatisation CI (gate G3 systématique).
|
||||
|
||||
## Statut Gates
|
||||
- G0 Spec Freeze : **satisfait** pour l'import brut du bundle.
|
||||
|
||||
Executable
+187
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate coherence of Zacus conversation bundle artifacts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError: # pragma: no cover
|
||||
sys.exit("Missing dependency: install PyYAML (pip install pyyaml) to validate bundle YAML files.")
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def read_json(path: Path) -> dict:
|
||||
try:
|
||||
return json.loads(path.read_text())
|
||||
except OSError as exc:
|
||||
raise ValidationError(f"Cannot read JSON file {path}: {exc}")
|
||||
except json.JSONDecodeError as exc:
|
||||
raise ValidationError(f"Invalid JSON in {path}: {exc}")
|
||||
|
||||
|
||||
def read_yaml(path: Path) -> dict:
|
||||
try:
|
||||
return yaml.safe_load(path.read_text())
|
||||
except OSError as exc:
|
||||
raise ValidationError(f"Cannot read YAML file {path}: {exc}")
|
||||
except yaml.YAMLError as exc:
|
||||
raise ValidationError(f"Invalid YAML in {path}: {exc}")
|
||||
|
||||
|
||||
def extract_template_scenario(template: dict) -> tuple[str, int]:
|
||||
prompt_input = template.get("prompt_input")
|
||||
if not isinstance(prompt_input, dict):
|
||||
raise ValidationError("template missing `prompt_input` mapping")
|
||||
|
||||
scenario = prompt_input.get("scenario")
|
||||
if not isinstance(scenario, dict):
|
||||
raise ValidationError("template missing `prompt_input.scenario` mapping")
|
||||
|
||||
scenario_id = scenario.get("id")
|
||||
version = scenario.get("version")
|
||||
if not isinstance(scenario_id, str) or not scenario_id:
|
||||
raise ValidationError("template `prompt_input.scenario.id` must be a non-empty string")
|
||||
if not isinstance(version, int):
|
||||
raise ValidationError("template `prompt_input.scenario.version` must be an integer")
|
||||
return scenario_id, version
|
||||
|
||||
|
||||
def extract_canonical(canonical: dict) -> tuple[str, int, list[str], str]:
|
||||
scenario = canonical.get("scenario")
|
||||
if not isinstance(scenario, dict):
|
||||
raise ValidationError("canonical missing `scenario` mapping")
|
||||
|
||||
scenario_id = scenario.get("id")
|
||||
version = scenario.get("version")
|
||||
initial_step = scenario.get("initial_step")
|
||||
steps = canonical.get("steps_runtime_order")
|
||||
|
||||
if not isinstance(scenario_id, str) or not scenario_id:
|
||||
raise ValidationError("canonical `scenario.id` must be a non-empty string")
|
||||
if not isinstance(version, int):
|
||||
raise ValidationError("canonical `scenario.version` must be an integer")
|
||||
if not isinstance(initial_step, str) or not initial_step:
|
||||
raise ValidationError("canonical `scenario.initial_step` must be a non-empty string")
|
||||
if not isinstance(steps, list) or not steps:
|
||||
raise ValidationError("canonical `steps_runtime_order` must be a non-empty list")
|
||||
|
||||
step_ids: list[str] = []
|
||||
for idx, step in enumerate(steps, start=1):
|
||||
if not isinstance(step, dict):
|
||||
raise ValidationError(f"canonical step #{idx} must be a mapping")
|
||||
step_id = step.get("step_id")
|
||||
if not isinstance(step_id, str) or not step_id:
|
||||
raise ValidationError(f"canonical step #{idx} missing non-empty `step_id`")
|
||||
step_ids.append(step_id)
|
||||
|
||||
if initial_step not in step_ids:
|
||||
raise ValidationError("canonical initial_step not found in canonical steps_runtime_order")
|
||||
|
||||
return scenario_id, version, step_ids, initial_step
|
||||
|
||||
|
||||
def extract_runtime(runtime: dict) -> tuple[str, int, list[str], str]:
|
||||
scenario = runtime.get("scenario")
|
||||
if not isinstance(scenario, dict):
|
||||
raise ValidationError("runtime missing `scenario` mapping")
|
||||
|
||||
scenario_id = scenario.get("id")
|
||||
version = scenario.get("version")
|
||||
initial_step = scenario.get("initial_step")
|
||||
steps = runtime.get("steps_runtime_order")
|
||||
|
||||
if not isinstance(scenario_id, str) or not scenario_id:
|
||||
raise ValidationError("runtime `scenario.id` must be a non-empty string")
|
||||
if not isinstance(version, int):
|
||||
raise ValidationError("runtime `scenario.version` must be an integer")
|
||||
if not isinstance(initial_step, str) or not initial_step:
|
||||
raise ValidationError("runtime `scenario.initial_step` must be a non-empty string")
|
||||
if not isinstance(steps, list) or not steps:
|
||||
raise ValidationError("runtime `steps_runtime_order` must be a non-empty list")
|
||||
|
||||
step_ids: list[str] = []
|
||||
for idx, step in enumerate(steps, start=1):
|
||||
if not isinstance(step, dict):
|
||||
raise ValidationError(f"runtime step #{idx} must be a mapping")
|
||||
step_id = step.get("step_id")
|
||||
if not isinstance(step_id, str) or not step_id:
|
||||
raise ValidationError(f"runtime step #{idx} missing non-empty `step_id`")
|
||||
step_ids.append(step_id)
|
||||
|
||||
if initial_step not in step_ids:
|
||||
raise ValidationError("runtime initial_step not found in runtime steps_runtime_order")
|
||||
|
||||
return scenario_id, version, step_ids, initial_step
|
||||
|
||||
|
||||
def validate_bundle(runtime_path: Path, canonical_path: Path, template_path: Path) -> None:
|
||||
runtime = read_json(runtime_path)
|
||||
canonical = read_yaml(canonical_path)
|
||||
template = read_yaml(template_path)
|
||||
|
||||
canonical_id, canonical_version, canonical_steps, canonical_initial = extract_canonical(canonical)
|
||||
runtime_id, runtime_version, runtime_steps, runtime_initial = extract_runtime(runtime)
|
||||
template_id, template_version = extract_template_scenario(template)
|
||||
|
||||
errors: list[str] = []
|
||||
|
||||
if canonical_id != runtime_id:
|
||||
errors.append(f"scenario id mismatch: canonical={canonical_id} runtime={runtime_id}")
|
||||
if canonical_version != runtime_version:
|
||||
errors.append(f"scenario version mismatch: canonical={canonical_version} runtime={runtime_version}")
|
||||
if canonical_initial != runtime_initial:
|
||||
errors.append(f"initial_step mismatch: canonical={canonical_initial} runtime={runtime_initial}")
|
||||
if canonical_steps != runtime_steps:
|
||||
errors.append("steps_runtime_order mismatch between canonical and runtime")
|
||||
|
||||
if template_id != canonical_id:
|
||||
errors.append(f"template id mismatch: template={template_id} canonical={canonical_id}")
|
||||
if template_version != canonical_version:
|
||||
errors.append(f"template version mismatch: template={template_version} canonical={canonical_version}")
|
||||
|
|
||||
|
||||
if errors:
|
||||
raise ValidationError("; ".join(errors))
|
||||
|
||||
print(
|
||||
"[runtime-bundle-validate] ok "
|
||||
f"id={canonical_id} version={canonical_version} steps={len(canonical_steps)} initial={canonical_initial}"
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Validate conversation bundle runtime/canonical/template coherence.")
|
||||
parser.add_argument(
|
||||
"--runtime",
|
||||
default="scenario-ai-coherence/zacus_conversation_bundle_v3/scenario_runtime.json",
|
||||
help="Path to scenario_runtime.json",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--canonical",
|
||||
default="scenario-ai-coherence/zacus_conversation_bundle_v3/scenario_canonical.yaml",
|
||||
help="Path to scenario_canonical.yaml",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--template",
|
||||
default="scenario-ai-coherence/zacus_conversation_bundle_v3/scenario_promptable_template.yaml",
|
||||
help="Path to scenario_promptable_template.yaml",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
validate_bundle(Path(args.runtime), Path(args.canonical), Path(args.template))
|
||||
except ValidationError as exc:
|
||||
print(f"[runtime-bundle-validate] error -> {exc}")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user
validate_bundleonly checks scenario identity/order metadata and then returns success, so it misses integration breakers that already exist in the tracked bundle data (for example, stalecurrent_firmware_snapshot.source_to_update_filespaths inscenario_promptable_template.yamland theFORCE_WIN_ETAPE2action event mismatch inscenario_runtime.jsonvshardware/firmware/data/story/actions/ACTION_FORCE_ETAPE2.json). Because this commit also wires this script into.github/workflows/validate.yml, CI can now report the runtime-bundle gate as green while the bundle is still non-integrable.Useful? React with 👍 / 👎.