fix(ops): format list fields as JSON arrays, filter out placeholder strings, and update prompt template rules
This commit is contained in:
parent
1fbe2a746d
commit
0ac85f8b92
|
|
@ -23,3 +23,8 @@ TEST_ESEGUITI:
|
||||||
BLOCCO_DATI: si | no
|
BLOCCO_DATI: si | no
|
||||||
NOTE:
|
NOTE:
|
||||||
- <nota>
|
- <nota>
|
||||||
|
|
||||||
|
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'.
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,39 @@ import json
|
||||||
import re
|
import re
|
||||||
import argparse
|
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:
|
def parse_output(text: str) -> dict:
|
||||||
fields = {
|
fields = {
|
||||||
"task_id": None,
|
"task_id": None,
|
||||||
|
|
@ -13,11 +46,11 @@ def parse_output(text: str) -> dict:
|
||||||
"commit": None,
|
"commit": None,
|
||||||
"consolidated_unit_id": None,
|
"consolidated_unit_id": None,
|
||||||
"blocco_dati": None,
|
"blocco_dati": None,
|
||||||
"dates_used": None,
|
"dates_used": [],
|
||||||
"test_eseguiti": None,
|
"test_eseguiti": [],
|
||||||
"note": None,
|
"note": [],
|
||||||
"absorbed_legacy_fragments": None,
|
"absorbed_legacy_fragments": [],
|
||||||
"open_legacy_fragments": None,
|
"open_legacy_fragments": [],
|
||||||
"raw_text": text
|
"raw_text": text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,33 +60,71 @@ def parse_output(text: str) -> dict:
|
||||||
data = json.loads(json_match.group(0))
|
data = json.loads(json_match.group(0))
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
source_dict = data.get("parsed", data)
|
source_dict = data.get("parsed", data)
|
||||||
for k in fields:
|
if isinstance(source_dict, dict):
|
||||||
if k in source_dict and source_dict[k] is not None:
|
for k in fields:
|
||||||
fields[k] = source_dict[k]
|
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:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
patterns = {
|
HEADER_MAP = {
|
||||||
"task_id": r"(?:TASK_ID|task_id):\s*(.+)",
|
"TASK_ID": "task_id",
|
||||||
"esito_205": r"(?:ESITO_205|esito_205):\s*(.+)",
|
"ESITO_205": "esito_205",
|
||||||
"repository": r"(?:REPOSITORY|repository):\s*(.+)",
|
"REPOSITORY": "repository",
|
||||||
"branch": r"(?:BRANCH|branch):\s*(.+)",
|
"BRANCH": "branch",
|
||||||
"commit": r"(?:COMMIT|commit):\s*(.+)",
|
"COMMIT": "commit",
|
||||||
"consolidated_unit_id": r"(?:CONSOLIDATED_UNIT_ID|UNITA_ID|consolidated_unit_id):\s*(.+)",
|
"CONSOLIDATED_UNIT_ID": "consolidated_unit_id",
|
||||||
"blocco_dati": r"(?:BLOCCO_DATI|blocco_dati):\s*(.+)",
|
"UNITA_ID": "consolidated_unit_id",
|
||||||
"dates_used": r"(?:DATES_USED|dates_used):\s*(.+)",
|
"BLOCCO_DATI": "blocco_dati",
|
||||||
"test_eseguiti": r"(?:TEST_ESEGUITI|test_eseguiti):\s*(.+)",
|
"ABSORBED_LEGACY_FRAGMENTS": "absorbed_legacy_fragments",
|
||||||
"note": r"(?:NOTE|note):\s*(.+)",
|
"OPEN_LEGACY_FRAGMENTS": "open_legacy_fragments",
|
||||||
"absorbed_legacy_fragments": r"(?:ABSORBED_LEGACY_FRAGMENTS|absorbed_legacy_fragments):\s*(.+)",
|
"DATES_USED": "dates_used",
|
||||||
"open_legacy_fragments": r"(?:OPEN_LEGACY_FRAGMENTS|open_legacy_fragments):\s*(.+)"
|
"TEST_ESEGUITI": "test_eseguiti",
|
||||||
|
"NOTE": "note",
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, pat in patterns.items():
|
lines = text.splitlines()
|
||||||
if fields[key] is None:
|
current_key = None
|
||||||
m = re.search(pat, text, re.IGNORECASE)
|
|
||||||
|
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:
|
if m:
|
||||||
val = m.group(1).strip()
|
header_match = (field_key, m.group(1).strip())
|
||||||
fields[key] = val
|
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
|
return fields
|
||||||
|
|
||||||
|
|
@ -81,9 +152,6 @@ def validate_fields(fields: dict, expected_repo: str = None, expected_branch: st
|
||||||
"esito_205",
|
"esito_205",
|
||||||
"consolidated_unit_id",
|
"consolidated_unit_id",
|
||||||
"blocco_dati",
|
"blocco_dati",
|
||||||
"dates_used",
|
|
||||||
"test_eseguiti",
|
|
||||||
"note",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for key in mandatory_single_fields:
|
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()):
|
if val is None or (isinstance(val, str) and not val.strip()):
|
||||||
missing.append(key)
|
missing.append(key)
|
||||||
|
|
||||||
abs_frag = fields.get("absorbed_legacy_fragments")
|
mandatory_list_fields = [
|
||||||
open_frag = fields.get("open_legacy_fragments")
|
"dates_used",
|
||||||
has_abs = abs_frag is not None and (not isinstance(abs_frag, str) or bool(str(abs_frag).strip()))
|
"test_eseguiti",
|
||||||
has_open = open_frag is not None and (not isinstance(open_frag, str) or bool(str(open_frag).strip()))
|
"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")
|
missing.append("absorbed_legacy_fragments|open_legacy_fragments")
|
||||||
|
|
||||||
dedup_missing = []
|
dedup_missing = []
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,7 @@ REAL_COMMIT="$(git -C "$REPO_DIR" rev-parse HEAD)"
|
||||||
|
|
||||||
echo "=== ESECUZIONE TEST AUTOMATICI WRAPPER E PARSER AGY 1.1.11 ==="
|
echo "=== ESECUZIONE TEST AUTOMATICI WRAPPER E PARSER AGY 1.1.11 ==="
|
||||||
|
|
||||||
# -------------------------------------------------------------
|
|
||||||
# Test 1: Output Jetski negato -> wrapper fallisce
|
# Test 1: Output Jetski negato -> wrapper fallisce
|
||||||
# -------------------------------------------------------------
|
|
||||||
echo -n "Test 1: Output Jetski negato -> "
|
echo -n "Test 1: Output Jetski negato -> "
|
||||||
TEST_BIN_DIR="$(mktemp -d)"
|
TEST_BIN_DIR="$(mktemp -d)"
|
||||||
cat << 'EOF' > "$TEST_BIN_DIR/agy-ultra"
|
cat << 'EOF' > "$TEST_BIN_DIR/agy-ultra"
|
||||||
|
|
@ -37,9 +35,7 @@ else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# -------------------------------------------------------------
|
|
||||||
# Test 2: Output vuoto -> wrapper fallisce
|
# Test 2: Output vuoto -> wrapper fallisce
|
||||||
# -------------------------------------------------------------
|
|
||||||
echo -n "Test 2: Output vuoto -> "
|
echo -n "Test 2: Output vuoto -> "
|
||||||
TEST_BIN_DIR="$(mktemp -d)"
|
TEST_BIN_DIR="$(mktemp -d)"
|
||||||
cat << 'EOF' > "$TEST_BIN_DIR/agy-ultra"
|
cat << 'EOF' > "$TEST_BIN_DIR/agy-ultra"
|
||||||
|
|
@ -62,20 +58,22 @@ else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# -------------------------------------------------------------
|
|
||||||
# Test 3: task_id null -> parser fallisce
|
# Test 3: task_id null -> parser fallisce
|
||||||
# -------------------------------------------------------------
|
|
||||||
echo -n "Test 3: task_id null -> "
|
echo -n "Test 3: task_id null -> "
|
||||||
INCOMPLETE_REPORT="ESITO_205: riuscito
|
INCOMPLETE_REPORT="ESITO_205: riuscito
|
||||||
REPOSITORY: ${REAL_REPO}
|
REPOSITORY: ${REAL_REPO}
|
||||||
BRANCH: ${REAL_BRANCH}
|
BRANCH: ${REAL_BRANCH}
|
||||||
COMMIT: ${REAL_COMMIT}
|
COMMIT: ${REAL_COMMIT}
|
||||||
CONSOLIDATED_UNIT_ID: 1545
|
CONSOLIDATED_UNIT_ID: 1545
|
||||||
DATES_USED: 2026-08-10
|
DATES_USED:
|
||||||
TEST_ESEGUITI: ./vendor/bin/pest
|
- 2026-08-10
|
||||||
|
TEST_ESEGUITI:
|
||||||
|
- pest
|
||||||
BLOCCO_DATI: no
|
BLOCCO_DATI: no
|
||||||
NOTE: Incompleto
|
NOTE:
|
||||||
ABSORBED_LEGACY_FRAGMENTS: id_cond=12"
|
- Incompleto
|
||||||
|
ABSORBED_LEGACY_FRAGMENTS:
|
||||||
|
- id_cond=12"
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
TEST3_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$INCOMPLETE_REPORT" 2>&1)"
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# -------------------------------------------------------------
|
# Test 4: '- nessuno' viene scartato e restituisce lista vuota []
|
||||||
# Test 4: Report completo -> passa
|
echo -n "Test 4: '- nessuno' viene scartato -> "
|
||||||
# -------------------------------------------------------------
|
NONE_REPORT="TASK_ID: CT-2026-08-10-TEST-NONE
|
||||||
echo -n "Test 4: Report completo -> "
|
|
||||||
COMPLETE_REPORT="TASK_ID: CT-2026-08-10-TEST-001
|
|
||||||
ESITO_205: riuscito
|
ESITO_205: riuscito
|
||||||
REPOSITORY: ${REAL_REPO}
|
REPOSITORY: ${REAL_REPO}
|
||||||
BRANCH: ${REAL_BRANCH}
|
BRANCH: ${REAL_BRANCH}
|
||||||
COMMIT: ${REAL_COMMIT}
|
COMMIT: ${REAL_COMMIT}
|
||||||
CONSOLIDATED_UNIT_ID: 1545
|
CONSOLIDATED_UNIT_ID: 1545
|
||||||
DATES_USED: 2026-08-10
|
ABSORBED_LEGACY_FRAGMENTS:
|
||||||
TEST_ESEGUITI: ./vendor/bin/pest
|
- artisan-ultra-real-24825cbb
|
||||||
|
OPEN_LEGACY_FRAGMENTS:
|
||||||
|
- nessuno
|
||||||
|
DATES_USED:
|
||||||
|
- 2026-08-09
|
||||||
|
TEST_ESEGUITI:
|
||||||
|
- test
|
||||||
BLOCCO_DATI: no
|
BLOCCO_DATI: no
|
||||||
NOTE: Task completato correttamente
|
NOTE:
|
||||||
ABSORBED_LEGACY_FRAGMENTS: id_cond=12"
|
- ARTISAN_ULTRA_OK"
|
||||||
|
|
||||||
set +e
|
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=$?
|
TEST4_EXIT=$?
|
||||||
set -e
|
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)"
|
echo "PASS (Exit Code: $TEST4_EXIT)"
|
||||||
else
|
else
|
||||||
echo "FAIL (Expected exit code 0 with ok: true)"
|
echo "FAIL (Expected open_legacy_fragments to be empty array [])"
|
||||||
echo "$TEST4_OUT"
|
echo "$TEST4_OUT"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# -------------------------------------------------------------
|
# Test 5: Liste restituite come array e smoke completo produce ok=true
|
||||||
# Test 5: Git diverso dal reale -> fallisce
|
echo -n "Test 5: Smoke completo con liste array -> "
|
||||||
# -------------------------------------------------------------
|
COMPLETE_REPORT="TASK_ID: task-348c024293
|
||||||
echo -n "Test 5: Git diverso dal reale -> "
|
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
|
MISMATCH_REPORT="TASK_ID: CT-2026-08-10-TEST-001
|
||||||
ESITO_205: riuscito
|
ESITO_205: riuscito
|
||||||
REPOSITORY: ssh://git@wrong-repo.git
|
REPOSITORY: ssh://git@wrong-repo.git
|
||||||
BRANCH: wrong-branch
|
BRANCH: wrong-branch
|
||||||
COMMIT: deadbeef1234
|
COMMIT: deadbeef1234
|
||||||
CONSOLIDATED_UNIT_ID: 1545
|
CONSOLIDATED_UNIT_ID: 1545
|
||||||
DATES_USED: 2026-08-10
|
DATES_USED:
|
||||||
TEST_ESEGUITI: ./vendor/bin/pest
|
- 2026-08-10
|
||||||
|
TEST_ESEGUITI:
|
||||||
|
- pest
|
||||||
BLOCCO_DATI: no
|
BLOCCO_DATI: no
|
||||||
NOTE: Mismatch test
|
NOTE:
|
||||||
ABSORBED_LEGACY_FRAGMENTS: id_cond=12"
|
- Mismatch test
|
||||||
|
ABSORBED_LEGACY_FRAGMENTS:
|
||||||
|
- id_cond=12"
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
TEST5_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$MISMATCH_REPORT" 2>&1)"
|
TEST6_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$MISMATCH_REPORT" 2>&1)"
|
||||||
TEST5_EXIT=$?
|
TEST6_EXIT=$?
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ $TEST5_EXIT -eq 1 ] && [[ "$TEST5_OUT" == *'"ok": false'* ]] && [[ "$TEST5_OUT" == *'"repository"'* ]]; then
|
if [ $TEST6_EXIT -eq 1 ] && [[ "$TEST6_OUT" == *'"ok": false'* ]] && [[ "$TEST6_OUT" == *'"repository"'* ]]; then
|
||||||
echo "PASS (Exit Code: $TEST5_EXIT)"
|
echo "PASS (Exit Code: $TEST6_EXIT)"
|
||||||
else
|
else
|
||||||
echo "FAIL (Expected exit code 1 with git mismatch)"
|
echo "FAIL (Expected exit code 1 with git mismatch)"
|
||||||
echo "$TEST5_OUT"
|
echo "$TEST6_OUT"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user