diff --git a/.github/workflows/validate-examples.yml b/.github/workflows/validate-examples.yml new file mode 100644 index 0000000..c3dc265 --- /dev/null +++ b/.github/workflows/validate-examples.yml @@ -0,0 +1,57 @@ +name: Validate Examples + +on: + push: + paths: + - "examples/**" + - "references/**" + - "scripts/validate_specula.py" + - ".github/workflows/validate-examples.yml" + pull_request: + paths: + - "examples/**" + - "references/**" + - "scripts/validate_specula.py" + - ".github/workflows/validate-examples.yml" + workflow_dispatch: + +jobs: + validate-examples: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements.txt + + - name: Validate every example (constitution + state machine + integration) + shell: bash + run: | + set -euo pipefail + status=0 + for dir in examples/*/; do + const="${dir}constitution.json" + sm="${dir}state-machine.json" + if [[ -f "$const" && -f "$sm" ]]; then + echo "::group::Validating ${dir}" + if ! python scripts/validate_specula.py \ + --constitution "$const" \ + --state-machine "$sm"; then + status=1 + fi + echo "::endgroup::" + fi + done + if [[ "$status" -ne 0 ]]; then + echo "One or more examples failed schema/consistency validation." + exit 1 + fi + echo "All examples passed schema and consistency validation." diff --git a/examples/community-space-brand/state-machine.json b/examples/community-space-brand/state-machine.json index 940d3c6..f5b27c6 100644 --- a/examples/community-space-brand/state-machine.json +++ b/examples/community-space-brand/state-machine.json @@ -9,52 +9,71 @@ "id": "receive_request", "name": "Receive Request", "type": "normal", - "allowed_transitions": ["classify_persona_mode"] + "allowed_transitions": [ + "classify_persona_mode" + ] }, "classify_persona_mode": { "id": "classify_persona_mode", "name": "Classify Persona Mode", "description": "Identify whether the interaction is from Esploratori (discovering, energy in movement), Attivatori (producing, energy structures), or Garanti (enabling, energy stabilises) mode.", "type": "normal", - "allowed_transitions": ["non_judgment_check"] + "allowed_transitions": [ + "non_judgment_check" + ] }, "non_judgment_check": { "id": "non_judgment_check", "name": "Non-Judgment Check", "type": "normal", - "allowed_transitions": ["enabling_check", "block_stigmatizing_output"] + "allowed_transitions": [ + "enabling_check", + "block_stigmatizing_output" + ] }, "enabling_check": { "id": "enabling_check", "name": "Enabling Check", "description": "Verify response provides infrastructure (tools, access, resources), not just reassurance or assistance.", "type": "normal", - "allowed_transitions": ["space_state_selection", "escalate_community_guardian"] + "allowed_transitions": [ + "space_state_selection", + "escalate_community_guardian" + ] }, "space_state_selection": { "id": "space_state_selection", "name": "Space State Selection", "description": "Select the appropriate activation state: essenziale, modulare, denso, or gestuale. Each state carries different intensity, color weight, and interaction mode.", "type": "normal", - "allowed_transitions": ["generate_response"] + "allowed_transitions": [ + "generate_response" + ] }, "generate_response": { "id": "generate_response", "name": "Generate Response", "type": "normal", - "allowed_transitions": ["response_ready", "escalate_community_guardian"] + "allowed_transitions": [ + "response_ready", + "escalate_community_guardian" + ] }, "block_stigmatizing_output": { "id": "block_stigmatizing_output", "name": "Block Stigmatizing Output", "type": "escalation", - "allowed_transitions": ["generate_response"] + "allowed_transitions": [ + "generate_response" + ] }, "escalate_community_guardian": { "id": "escalate_community_guardian", "name": "Escalate to Community Guardian", "type": "escalation", - "allowed_transitions": ["response_ready"] + "allowed_transitions": [ + "response_ready" + ] }, "response_ready": { "id": "response_ready", @@ -64,66 +83,72 @@ }, "transitions": [ { - "id": "t_receive_to_classify", + "id": "transition_receive_to_classify", "from_state": "receive_request", "to_state": "classify_persona_mode", "trigger": "request_received" }, { - "id": "t_classify_to_judgment", + "id": "transition_classify_to_judgment", "from_state": "classify_persona_mode", "to_state": "non_judgment_check", "trigger": "persona_mode_identified" }, { - "id": "t_judgment_pass", + "id": "transition_judgment_pass", "from_state": "non_judgment_check", "to_state": "enabling_check", "trigger": "judgment_guard_passed", - "guard_ids": ["guard_non_judgment"] + "guard_ids": [ + "guard_non_judgment" + ] }, { - "id": "t_judgment_fail", + "id": "transition_judgment_fail", "from_state": "non_judgment_check", "to_state": "block_stigmatizing_output", "trigger": "judgment_guard_failed", "is_fallback": true }, { - "id": "t_enabling_pass", + "id": "transition_enabling_pass", "from_state": "enabling_check", "to_state": "space_state_selection", "trigger": "enabling_guard_passed", - "guard_ids": ["guard_enabling"] + "guard_ids": [ + "guard_enabling" + ] }, { - "id": "t_enabling_escalate", + "id": "transition_enabling_escalate", "from_state": "enabling_check", "to_state": "escalate_community_guardian", "trigger": "enabling_guard_failed", "is_fallback": true }, { - "id": "t_state_to_generate", + "id": "transition_state_to_generate", "from_state": "space_state_selection", "to_state": "generate_response", "trigger": "space_state_selected" }, { - "id": "t_generate_to_ready", + "id": "transition_generate_to_ready", "from_state": "generate_response", "to_state": "response_ready", "trigger": "response_validated", - "guard_ids": ["guard_no_performance_pressure"] + "guard_ids": [ + "guard_no_performance_pressure" + ] }, { - "id": "t_block_to_retry", + "id": "transition_block_to_retry", "from_state": "block_stigmatizing_output", "to_state": "generate_response", "trigger": "output_revised" }, { - "id": "t_escalation_to_ready", + "id": "transition_escalation_to_ready", "from_state": "escalate_community_guardian", "to_state": "response_ready", "trigger": "guardian_approved" @@ -157,7 +182,7 @@ "applies_at_state": "generate_response", "condition": "content_non_competitive AND no_achievement_framing AND no_ranking", "principle": "principle_imagination_in_practice", - "action_on_violation": "revise", + "action_on_violation": "retry", "severity": "high" } ], @@ -165,22 +190,45 @@ "essenziale": { "description": "When only what matters remains. Minimal activation.", "tone": "quiet, essential, grounding", - "color_weight": {"sand": 0.75, "white": 0.2, "green": 0.05, "red": 0.0} + "color_weight": { + "sand": 0.75, + "white": 0.2, + "green": 0.05, + "red": 0.0 + } }, "modulare": { "description": "When the community builds together. Structured activation.", "tone": "collaborative, structured, enabling", - "color_weight": {"sand": 0.6, "white": 0.15, "green": 0.2, "black": 0.05, "red": 0.0} + "color_weight": { + "sand": 0.6, + "white": 0.15, + "green": 0.2, + "black": 0.05, + "red": 0.0 + } }, "denso": { "description": "When the space is full of relationships. High presence.", "tone": "warm, present, relational", - "color_weight": {"sand": 0.55, "white": 0.15, "green": 0.15, "black": 0.1, "red": 0.05} + "color_weight": { + "sand": 0.55, + "white": 0.15, + "green": 0.15, + "black": 0.1, + "red": 0.05 + } }, "gestuale": { "description": "When something spontaneous happens. Emergent activation.", "tone": "spontaneous, energetic, open", - "color_weight": {"sand": 0.5, "white": 0.15, "green": 0.15, "black": 0.1, "red": 0.1} + "color_weight": { + "sand": 0.5, + "white": 0.15, + "green": 0.15, + "black": 0.1, + "red": 0.1 + } } }, "metadata": {