1. What is the issue? Please be detailed.
when I validate a form I am getting the famous
XForm is invalid.
: Cycle detected in form's relevant and calculation logic!
But that should not be possible because that form is build with tricc that stoip in case of cycle
2. What steps can we take to reproduce this issue?
Simply use that form
3. What have you tried to fix the issue?
this is a super complex form so I ask LLM to make a mermaid visualisation of the cycle
import pandas as pd
import re
# ========================= CONFIG =========================
file_path = "/mnt/data/Development/tricc-mage/output/adult.xlsx"
sheet_name = "survey"
# βββ YOUR PREFERENCES
only_core_nodes = False # False = full expanded graph (recommended)
# True = ONLY your 9 core nodes
# βββ NEW: SPACING CONTROLS (this is what you asked for!)
node_spacing = 180 # β Increase this for MORE horizontal space between nodes
rank_spacing = 220 # β Increase this for MORE vertical space between levels
# (try 200β300 if your graph feels crowded)
core_names = [
"flag_dehydration_ap",
"ask_ooUbYRn_3wWAAqequ05F_0_Vv_3",
"flag_dehydration_bp"
]
# =========================================================
df = pd.read_excel(file_path, sheet_name=sheet_name)
relevant_col = next((c for c in df.columns if str(c).lower() in ['relevant', 'relevance']), 'relevant')
calculation_col = next((c for c in df.columns if str(c).lower() in ['calculation', 'calculate']), 'calculation')
constraint_col = next((c for c in df.columns if str(c).lower() in ['constraint', 'constrains']), 'constraint')
def extract_refs(text):
if pd.isna(text) or not isinstance(text, str):
return []
return re.findall(r'\$\{([^\}]+)\}', text)
core_set = set(core_names)
if only_core_nodes:
all_names = core_set
nodes_df = df[df['name'].isin(core_set)].copy()
mode = "CORE ONLY"
else:
all_names = set(core_names)
for _, row in df[df['name'].isin(core_names)].iterrows():
for col in [relevant_col, calculation_col]:
for ref in extract_refs(row.get(col, '')):
if ref in df['name'].values:
all_names.add(ref)
nodes_df = df[df['name'].isin(all_names)].copy()
mode = "EXPANDED"
print(f"β
Mode: {mode} β {len(nodes_df)} nodes")
# ========================= MERMAID GENERATION =========================
mermaid_lines = [
# βββ SPACING CONFIG (this ensures nodes are more spaced)
f"%%{{init: {{'flowchart': {{'nodeSpacing': {node_spacing}, 'rankSpacing': {rank_spacing}}}}}}}%%",
"flowchart TD"
]
# Nodes with two styles
label_col = next((c for c in df.columns if str(c).lower().startswith('label')), None)
for _, row in nodes_df.iterrows():
name = row['name']
raw_label = row.get(label_col, '') if label_col else ''
display = str(raw_label).strip()[:120] if pd.notna(raw_label) and str(raw_label).strip() else name
display = display.replace('"', '\\"').replace('\n', ' ')
node_text = f"{name}\\n{display}" if display != name else name
style = ":::core" if name in core_set else ":::dep"
mermaid_lines.append(f' {name}["{node_text}"]{style}')
# Edges (no duplicates)
seen = set()
for _, row in nodes_df.iterrows():
target = row['name']
if pd.notna(row.get(relevant_col)):
for source in extract_refs(row[relevant_col]):
if source in all_names and source != target:
key = (source, target, 'r')
if key not in seen:
seen.add(key)
mermaid_lines.append(f' {source} --r--> {target}')
if pd.notna(row.get(calculation_col)):
for source in extract_refs(row[calculation_col]):
if source in all_names and source != target:
key = (source, target, 'c')
if key not in seen:
seen.add(key)
mermaid_lines.append(f' {source} --c--> {target}')
if pd.notna(row.get(calculation_col)):
for source in extract_refs(row[constraint_col]):
if source in all_names and source != target:
key = (source, target, 'x')
if key not in seen:
seen.add(key)
mermaid_lines.append(f' {source} --x--> {target}')
# Style definitions
mermaid_lines.append('')
mermaid_lines.append('classDef core fill:#ffeb3b,stroke:#d32f2f,stroke-width:4px,color:#000,font-weight:bold;')
mermaid_lines.append('classDef dep fill:#bbdefb,stroke:#1976d2,stroke-width:2px,color:#333;')
mermaid_code = '\n'.join(mermaid_lines)
print("\n" + "="*70)
print(f"MERMAID CODE ({mode} β {len(nodes_df)} nodes, {len(seen)} edges)")
print("="*70)
print(mermaid_code)
print("="*70)
# Save
output_path = "/mnt/data/Development/tricc-mage/output/dependency_graph.mmd"
with open(output_path, "w", encoding="utf-8") as f:
f.write(mermaid_code)
print(f"β
Saved to: {output_path}")
print(" Paste into https://mermaid.live")
print("\nπ¨ Spacing is now controlled by the two numbers at the top.")
print(" Want even more space? Just increase node_spacing / rank_spacing and run again!")
this gives me that result : NO CYCLE DISPLAYED
Is it possible that I reach the limitation of the validator , if not do you have tool to investigate ?
4. Upload any forms or screenshots you can share publicly below.
adult.xlsx (131.4 KB)
