diff --git a/scripts/ops/antigravity-cli/205_followup_prompt.template.txt b/scripts/ops/antigravity-cli/205_followup_prompt.template.txt index 0fa8d98..a628aeb 100644 --- a/scripts/ops/antigravity-cli/205_followup_prompt.template.txt +++ b/scripts/ops/antigravity-cli/205_followup_prompt.template.txt @@ -23,3 +23,8 @@ TEST_ESEGUITI: BLOCCO_DATI: si | no NOTE: - + +REGOLE RIGIDE PER LE LISTE (ABSORBED_LEGACY_FRAGMENTS, OPEN_LEGACY_FRAGMENTS, DATES_USED, TEST_ESEGUITI, NOTE): +- Ogni elemento della lista deve iniziare con '- '. +- Quando una lista non ha elementi: lasciare soltanto l'intestazione (es. OPEN_LEGACY_FRAGMENTS:) e non inserire alcuna riga con trattino. +- E VIETATO tassativamente scrivere 'none', 'nessuno', 'nessuna', 'n/a' o 'non applicabile'. diff --git a/scripts/ops/antigravity-cli/parse_205_runner_output.py b/scripts/ops/antigravity-cli/parse_205_runner_output.py index 480422a..37eca6f 100755 --- a/scripts/ops/antigravity-cli/parse_205_runner_output.py +++ b/scripts/ops/antigravity-cli/parse_205_runner_output.py @@ -4,6 +4,39 @@ import json import re import argparse +PLACEHOLDERS = {"none", "nessuno", "nessuna", "n/a", "non applicabile", "null"} + +def clean_list_item(item: str) -> str: + item = item.strip() + if item.startswith("- "): + item = item[2:].strip() + elif item.startswith("-"): + item = item[1:].strip() + return item + +def is_placeholder(item: str) -> bool: + clean = clean_list_item(item).lower() + return clean in PLACEHOLDERS or not clean + +def parse_list_field(val) -> list: + if val is None: + return [] + items = [] + if isinstance(val, list): + for el in val: + s = str(el).strip() + if not is_placeholder(s): + items.append(clean_list_item(s)) + elif isinstance(val, str): + lines = val.strip().splitlines() + for line in lines: + s = line.strip() + if not s: + continue + if not is_placeholder(s): + items.append(clean_list_item(s)) + return items + def parse_output(text: str) -> dict: fields = { "task_id": None, @@ -13,11 +46,11 @@ def parse_output(text: str) -> dict: "commit": None, "consolidated_unit_id": None, "blocco_dati": None, - "dates_used": None, - "test_eseguiti": None, - "note": None, - "absorbed_legacy_fragments": None, - "open_legacy_fragments": None, + "dates_used": [], + "test_eseguiti": [], + "note": [], + "absorbed_legacy_fragments": [], + "open_legacy_fragments": [], "raw_text": text } @@ -27,33 +60,71 @@ def parse_output(text: str) -> dict: data = json.loads(json_match.group(0)) if isinstance(data, dict): source_dict = data.get("parsed", data) - for k in fields: - if k in source_dict and source_dict[k] is not None: - fields[k] = source_dict[k] + if isinstance(source_dict, dict): + for k in fields: + if k in ["raw_text"]: + continue + if k in ["dates_used", "test_eseguiti", "note", "absorbed_legacy_fragments", "open_legacy_fragments"]: + if k in source_dict and source_dict[k] is not None: + fields[k] = parse_list_field(source_dict[k]) + else: + if k in source_dict and source_dict[k] is not None: + fields[k] = str(source_dict[k]).strip() except Exception: pass - patterns = { - "task_id": r"(?:TASK_ID|task_id):\s*(.+)", - "esito_205": r"(?:ESITO_205|esito_205):\s*(.+)", - "repository": r"(?:REPOSITORY|repository):\s*(.+)", - "branch": r"(?:BRANCH|branch):\s*(.+)", - "commit": r"(?:COMMIT|commit):\s*(.+)", - "consolidated_unit_id": r"(?:CONSOLIDATED_UNIT_ID|UNITA_ID|consolidated_unit_id):\s*(.+)", - "blocco_dati": r"(?:BLOCCO_DATI|blocco_dati):\s*(.+)", - "dates_used": r"(?:DATES_USED|dates_used):\s*(.+)", - "test_eseguiti": r"(?:TEST_ESEGUITI|test_eseguiti):\s*(.+)", - "note": r"(?:NOTE|note):\s*(.+)", - "absorbed_legacy_fragments": r"(?:ABSORBED_LEGACY_FRAGMENTS|absorbed_legacy_fragments):\s*(.+)", - "open_legacy_fragments": r"(?:OPEN_LEGACY_FRAGMENTS|open_legacy_fragments):\s*(.+)" + HEADER_MAP = { + "TASK_ID": "task_id", + "ESITO_205": "esito_205", + "REPOSITORY": "repository", + "BRANCH": "branch", + "COMMIT": "commit", + "CONSOLIDATED_UNIT_ID": "consolidated_unit_id", + "UNITA_ID": "consolidated_unit_id", + "BLOCCO_DATI": "blocco_dati", + "ABSORBED_LEGACY_FRAGMENTS": "absorbed_legacy_fragments", + "OPEN_LEGACY_FRAGMENTS": "open_legacy_fragments", + "DATES_USED": "dates_used", + "TEST_ESEGUITI": "test_eseguiti", + "NOTE": "note", } - for key, pat in patterns.items(): - if fields[key] is None: - m = re.search(pat, text, re.IGNORECASE) + lines = text.splitlines() + current_key = None + + for line in lines: + stripped = line.strip() + if not stripped: + continue + + header_match = None + for h_name, field_key in HEADER_MAP.items(): + pattern = rf"^(?:{h_name}|{field_key}):\s*(.*)$" + m = re.match(pattern, stripped, re.IGNORECASE) if m: - val = m.group(1).strip() - fields[key] = val + header_match = (field_key, m.group(1).strip()) + break + + if header_match: + field_key, inline_val = header_match + current_key = field_key + if field_key in ["dates_used", "test_eseguiti", "note", "absorbed_legacy_fragments", "open_legacy_fragments"]: + if inline_val and not is_placeholder(inline_val): + fields[field_key].append(clean_list_item(inline_val)) + else: + if not fields[field_key] and inline_val: + fields[field_key] = inline_val + elif current_key and current_key in ["dates_used", "test_eseguiti", "note", "absorbed_legacy_fragments", "open_legacy_fragments"]: + if not is_placeholder(stripped): + fields[current_key].append(clean_list_item(stripped)) + + for k in ["dates_used", "test_eseguiti", "note", "absorbed_legacy_fragments", "open_legacy_fragments"]: + cleaned = [] + for el in fields[k]: + c = clean_list_item(str(el)) + if c and not is_placeholder(c) and c not in cleaned: + cleaned.append(c) + fields[k] = cleaned return fields @@ -81,9 +152,6 @@ def validate_fields(fields: dict, expected_repo: str = None, expected_branch: st "esito_205", "consolidated_unit_id", "blocco_dati", - "dates_used", - "test_eseguiti", - "note", ] for key in mandatory_single_fields: @@ -91,12 +159,21 @@ def validate_fields(fields: dict, expected_repo: str = None, expected_branch: st if val is None or (isinstance(val, str) and not val.strip()): missing.append(key) - abs_frag = fields.get("absorbed_legacy_fragments") - open_frag = fields.get("open_legacy_fragments") - has_abs = abs_frag is not None and (not isinstance(abs_frag, str) or bool(str(abs_frag).strip())) - has_open = open_frag is not None and (not isinstance(open_frag, str) or bool(str(open_frag).strip())) + mandatory_list_fields = [ + "dates_used", + "test_eseguiti", + "note", + ] - if not (has_abs or has_open): + for key in mandatory_list_fields: + val = fields.get(key) + if not isinstance(val, list) or len(val) == 0: + missing.append(key) + + abs_frag = fields.get("absorbed_legacy_fragments") or [] + open_frag = fields.get("open_legacy_fragments") or [] + + if len(abs_frag) == 0 and len(open_frag) == 0: missing.append("absorbed_legacy_fragments|open_legacy_fragments") dedup_missing = [] diff --git a/scripts/ops/antigravity-cli/test_runner_and_parser.sh b/scripts/ops/antigravity-cli/test_runner_and_parser.sh index 70e4a96..579e4de 100755 --- a/scripts/ops/antigravity-cli/test_runner_and_parser.sh +++ b/scripts/ops/antigravity-cli/test_runner_and_parser.sh @@ -12,9 +12,7 @@ REAL_COMMIT="$(git -C "$REPO_DIR" rev-parse HEAD)" echo "=== ESECUZIONE TEST AUTOMATICI WRAPPER E PARSER AGY 1.1.11 ===" -# ------------------------------------------------------------- # Test 1: Output Jetski negato -> wrapper fallisce -# ------------------------------------------------------------- echo -n "Test 1: Output Jetski negato -> " TEST_BIN_DIR="$(mktemp -d)" cat << 'EOF' > "$TEST_BIN_DIR/agy-ultra" @@ -37,9 +35,7 @@ else exit 1 fi -# ------------------------------------------------------------- # Test 2: Output vuoto -> wrapper fallisce -# ------------------------------------------------------------- echo -n "Test 2: Output vuoto -> " TEST_BIN_DIR="$(mktemp -d)" cat << 'EOF' > "$TEST_BIN_DIR/agy-ultra" @@ -62,20 +58,22 @@ else exit 1 fi -# ------------------------------------------------------------- # Test 3: task_id null -> parser fallisce -# ------------------------------------------------------------- echo -n "Test 3: task_id null -> " INCOMPLETE_REPORT="ESITO_205: riuscito REPOSITORY: ${REAL_REPO} BRANCH: ${REAL_BRANCH} COMMIT: ${REAL_COMMIT} CONSOLIDATED_UNIT_ID: 1545 -DATES_USED: 2026-08-10 -TEST_ESEGUITI: ./vendor/bin/pest +DATES_USED: +- 2026-08-10 +TEST_ESEGUITI: +- pest BLOCCO_DATI: no -NOTE: Incompleto -ABSORBED_LEGACY_FRAGMENTS: id_cond=12" +NOTE: +- Incompleto +ABSORBED_LEGACY_FRAGMENTS: +- id_cond=12" set +e TEST3_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$INCOMPLETE_REPORT" 2>&1)" @@ -90,61 +88,99 @@ else exit 1 fi -# ------------------------------------------------------------- -# Test 4: Report completo -> passa -# ------------------------------------------------------------- -echo -n "Test 4: Report completo -> " -COMPLETE_REPORT="TASK_ID: CT-2026-08-10-TEST-001 +# Test 4: '- nessuno' viene scartato e restituisce lista vuota [] +echo -n "Test 4: '- nessuno' viene scartato -> " +NONE_REPORT="TASK_ID: CT-2026-08-10-TEST-NONE ESITO_205: riuscito REPOSITORY: ${REAL_REPO} BRANCH: ${REAL_BRANCH} COMMIT: ${REAL_COMMIT} CONSOLIDATED_UNIT_ID: 1545 -DATES_USED: 2026-08-10 -TEST_ESEGUITI: ./vendor/bin/pest +ABSORBED_LEGACY_FRAGMENTS: +- artisan-ultra-real-24825cbb +OPEN_LEGACY_FRAGMENTS: +- nessuno +DATES_USED: +- 2026-08-09 +TEST_ESEGUITI: +- test BLOCCO_DATI: no -NOTE: Task completato correttamente -ABSORBED_LEGACY_FRAGMENTS: id_cond=12" +NOTE: +- ARTISAN_ULTRA_OK" set +e -TEST4_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$COMPLETE_REPORT" 2>&1)" +TEST4_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$NONE_REPORT" 2>&1)" TEST4_EXIT=$? set -e -if [ $TEST4_EXIT -eq 0 ] && [[ "$TEST4_OUT" == *'"ok": true'* ]]; then +if [ $TEST4_EXIT -eq 0 ] && [[ "$TEST4_OUT" == *'"open_legacy_fragments": []'* ]]; then echo "PASS (Exit Code: $TEST4_EXIT)" else - echo "FAIL (Expected exit code 0 with ok: true)" + echo "FAIL (Expected open_legacy_fragments to be empty array [])" echo "$TEST4_OUT" exit 1 fi -# ------------------------------------------------------------- -# Test 5: Git diverso dal reale -> fallisce -# ------------------------------------------------------------- -echo -n "Test 5: Git diverso dal reale -> " +# Test 5: Liste restituite come array e smoke completo produce ok=true +echo -n "Test 5: Smoke completo con liste array -> " +COMPLETE_REPORT="TASK_ID: task-348c024293 +ESITO_205: riuscito +REPOSITORY: ${REAL_REPO} +BRANCH: ${REAL_BRANCH} +COMMIT: ${REAL_COMMIT} +CONSOLIDATED_UNIT_ID: SMOKE-24825CBB +ABSORBED_LEGACY_FRAGMENTS: +- artisan-ultra-real-24825cbb +OPEN_LEGACY_FRAGMENTS: +DATES_USED: +- 2026-08-09 +TEST_ESEGUITI: +- lettura AGENTS.md e parser rigoroso +BLOCCO_DATI: no +NOTE: +- ARTISAN_ULTRA_24825CBB_REAL_OK" + +set +e +TEST5_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$COMPLETE_REPORT" 2>&1)" +TEST5_EXIT=$? +set -e + +if [ $TEST5_EXIT -eq 0 ] && [[ "$TEST5_OUT" == *'"ok": true'* ]] && [[ "$TEST5_OUT" == *'"absorbed_legacy_fragments": ['* ]]; then + echo "PASS (Exit Code: $TEST5_EXIT)" +else + echo "FAIL (Expected ok: true with array fields)" + echo "$TEST5_OUT" + exit 1 +fi + +# Test 6: Git diverso dal reale -> fallisce +echo -n "Test 6: Git diverso dal reale -> " MISMATCH_REPORT="TASK_ID: CT-2026-08-10-TEST-001 ESITO_205: riuscito REPOSITORY: ssh://git@wrong-repo.git BRANCH: wrong-branch COMMIT: deadbeef1234 CONSOLIDATED_UNIT_ID: 1545 -DATES_USED: 2026-08-10 -TEST_ESEGUITI: ./vendor/bin/pest +DATES_USED: +- 2026-08-10 +TEST_ESEGUITI: +- pest BLOCCO_DATI: no -NOTE: Mismatch test -ABSORBED_LEGACY_FRAGMENTS: id_cond=12" +NOTE: +- Mismatch test +ABSORBED_LEGACY_FRAGMENTS: +- id_cond=12" set +e -TEST5_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$MISMATCH_REPORT" 2>&1)" -TEST5_EXIT=$? +TEST6_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$MISMATCH_REPORT" 2>&1)" +TEST6_EXIT=$? set -e -if [ $TEST5_EXIT -eq 1 ] && [[ "$TEST5_OUT" == *'"ok": false'* ]] && [[ "$TEST5_OUT" == *'"repository"'* ]]; then - echo "PASS (Exit Code: $TEST5_EXIT)" +if [ $TEST6_EXIT -eq 1 ] && [[ "$TEST6_OUT" == *'"ok": false'* ]] && [[ "$TEST6_OUT" == *'"repository"'* ]]; then + echo "PASS (Exit Code: $TEST6_EXIT)" else echo "FAIL (Expected exit code 1 with git mismatch)" - echo "$TEST5_OUT" + echo "$TEST6_OUT" exit 1 fi