syntaxai/tdd.md · main · tools / sama-cli / src / c14_graph.sh
# c14 — adapter: import-graph rendering. Walks ALL_FILES,
# resolves their import edges via collect_imports, emits a graphviz
# .dot file, and (if `dot` is on PATH) renders a PNG. The boundary
# call to `dot` happens here, so this file is correctly Layer 2 —
# its prefix declares it as such, and the §4.4 Modeled (boundary)
# rule permits filesystem + tool boundaries in Layer 2.
# sama-import: a31_constants.sh
# sama-import: b32_utils.sh
# Emit a .dot graph of the import topology to stdout.
#
# Node attributes are layer-coloured so the rendered PNG makes the
# layer stratification visible. Edges flow from importer → imported
# (matching the natural reading direction); the .dot `rankdir=LR`
# puts Layer 0 on the left and Layer 3 on the right.
emit_dot() {
local repo_root="$1"
local src_dir="$2"
echo "digraph sama {"
echo " rankdir = LR;"
echo " node [shape=box, style=\"rounded,filled\", fontname=\"Helvetica\"];"
echo " graph [splines=ortho, nodesep=0.4];"
local color_l0="#cfe8d4"
local color_l1="#cfd8e8"
local color_l2="#e8e0c4"
local color_l3="#e8c4c4"
# Nodes
local f
for f in "${ALL_FILES[@]}"; do
if is_sama_file "$f" || is_test_file "$f"; then
:
else
continue
fi
local label="${f##*/}"
local info
local layer="?"
local color="#dddddd"
if info="$(declared_layer "$f")"; then
layer="$(echo "$info" | awk '{print $1}')"
case "$layer" in
0) color="$color_l0" ;;
1) color="$color_l1" ;;
2) color="$color_l2" ;;
3) color="$color_l3" ;;
esac
fi
echo " \"$f\" [label=\"$label\\nL$layer\", fillcolor=\"$color\"];"
done
# Edges
for f in "${ALL_FILES[@]}"; do
if is_sama_file "$f" || is_test_file "$f"; then
:
else
continue
fi
local imp
while IFS= read -r imp; do
[ -z "$imp" ] && continue
# Only emit edges into files we actually loaded.
local found=0
local x
for x in "${ALL_FILES[@]}"; do
if [ "$x" = "$imp" ]; then found=1; break; fi
done
[ "$found" = "1" ] || continue
echo " \"$f\" -> \"$imp\";"
done < <(collect_imports "$f" "$repo_root")
done
echo "}"
}
# Wrapper: write .dot to a path; render PNG if dot is available.
render_graph() {
local repo_root="$1"
local src_dir="$2"
local out_dot="${3:-/tmp/sama-graph.dot}"
local out_png="${4:-/tmp/sama-graph.png}"
emit_dot "$repo_root" "$src_dir" > "$out_dot"
echo "Wrote: $out_dot"
if command -v dot > /dev/null 2>&1; then
if dot -Tpng "$out_dot" -o "$out_png" 2>/dev/null; then
echo "Wrote: $out_png"
return 0
fi
echo "warning: \`dot\` failed to render — keeping .dot only" >&2
return 0
fi
echo "info: \`dot\` (graphviz) not installed — wrote .dot only" >&2
return 0
}