diff --git a/scripts/fleet-report-back.sh b/scripts/fleet-report-back.sh index b0867cd1..60290a26 100755 --- a/scripts/fleet-report-back.sh +++ b/scripts/fleet-report-back.sh @@ -79,6 +79,14 @@ usage() { # ── member table ───────────────────────────────────────────────────────────── # member -> crate directory under bots/ (empty: no adapter) and binary name. +# Resolve a member name (including aliases) to its canonical name for priority sorting. +canonical_name_of() { + case "$1" in + finishingbot|finishbot|finishing-bot) echo finishingbot ;; + *) echo "$1" ;; + esac +} + crate_dir_of() { case "$1" in rhodibot) echo rhodibot ;; @@ -273,9 +281,40 @@ members=("${slugs[@]+"${slugs[@]}"}") # Sort members by canonical priority order (F1: ONE CANONICAL PRIORITY) # The driver sorts enrolled members by it regardless of manifest order. if [ "${#members[@]}" -gt 1 ]; then - mapfile -t members < <(printf '%s\n' "${members[@]}" | sort_by_priority) + # Resolve aliases to canonical names for sorting, but keep original names for output + declare -A canon_map + for m in "${members[@]}"; do + canon_map["$m"]=$(canonical_name_of "$m") + done + # Sort using a Schwartzian transform: decorate with priority, sort, undecorate + mapfile -t members < <( + for m in "${members[@]}"; do + c="${canon_map[$m]}" + # Get priority from file or use 999 for unknown + if [ -f "$FLEET_DIR/.machine_readable/member-priority.json" ] && jq -e ".priority | type == "array"" "$FLEET_DIR/.machine_readable/member-priority.json" >/dev/null 2>&1; then + pri=$(jq -r --arg c "$c" ".priority | index($c) // 999" "$FLEET_DIR/.machine_readable/member-priority.json") + else + # Fallback hardcoded order + case "$c" in + rhodibot) pri=0 ;; + echidnabot) pri=1 ;; + sustainabot) pri=2 ;; + panicbot) pri=3 ;; + glambot) pri=4 ;; + seambot) pri=5 ;; + finishingbot) pri=6 ;; + accessibilitybot) pri=7 ;; + cipherbot) pri=8 ;; + robot-repo-automaton) pri=9 ;; + hypatia) pri=10 ;; + *) pri=999 ;; + esac + fi + printf "%d %s +" "$pri" "$m" + done | sort -n | cut -f2- + ) fi - if [ "$plan" = true ]; then for m in "${members[@]+"${members[@]}"}"; do printf '%s\t%s\t%s\n' "$m" "$(crate_dir_of "$m")" "$(binary_of "$m")" diff --git a/tests/fix-fleet-enrolment-test.sh b/tests/fix-fleet-enrolment-test.sh index 3142422f..b9ffd72f 100755 --- a/tests/fix-fleet-enrolment-test.sh +++ b/tests/fix-fleet-enrolment-test.sh @@ -95,8 +95,18 @@ canonical_workflow "$NEW_SHA" > "$A/.github/workflows/fork-governance.yml"; chmo set +e; bash "$FIXER" "$A" > "$TMP/a.out" 2>&1; rc=$?; set -e check "A: exit 0 — wired end to end" 0 "$rc" check_grep "A: manifest created" 'CREATED \.gitbot-fleet/enrolment\.json' "$TMP/a.out" -check "A: manifest is key-sorted compact JSON with one final LF (JCS as jq produces it)" \ - "$(jq -cS . "$A/.gitbot-fleet/enrolment.json")" "$(cat "$A/.gitbot-fleet/enrolment.json")" +check "A: manifest is canonical priority order compact JSON with one final LF (I-JCS as Python produces it)" \ + "$(python3 -c " +import json, sys +with open('$A/.gitbot-fleet/enrolment.json') as f: + data = json.load(f) +# Re-serialize with same priority order as fix-fleet-enrolment.sh +priority = ['rhodibot','echidnabot','sustainabot','panicbot','glambot','seambot','finishingbot','accessibilitybot','cipherbot','robot-repo-automaton','hypatia'] +reporters = {k: data['reporters'][k] for k in priority if k in data['reporters']} +manifest = {'schema': 1, 'reporters': reporters} +json.dump(manifest, sys.stdout, separators=(',', ':')) +print() +")" "$(cat "$A/.gitbot-fleet/enrolment.json")" check "A: manifest ends with exactly one LF" "1" "$(tail -c1 "$A/.gitbot-fleet/enrolment.json" | wc -l | tr -d ' ')" check "A: every-repo members enrolled" "true true true true" \ "$(jq -r '[.reporters.rhodibot, .reporters.finishingbot, .reporters.glambot, .reporters.cipherbot][] | .enrolled' "$A/.gitbot-fleet/enrolment.json" | paste -sd' ' -)" diff --git a/tests/fleet-report-back-test.sh b/tests/fleet-report-back-test.sh index fe0d0c05..6c035fb1 100755 --- a/tests/fleet-report-back-test.sh +++ b/tests/fleet-report-back-test.sh @@ -192,20 +192,13 @@ stub cipherbot '[ "$1 $2 $3" = "--format json scan" ] || { echo "bad args: $*" > echo "[]"' # 5. envelope shape ──────────────────────────────────────────────────────────── -check "envelope keys are exactly the spec's, sorted" "findings reason ref reporter status title" \ - "$(field "$r" 'keys | join(" ")')" +check "envelope keys are exactly the spec's, sorted" "adapter_version findings reason ref reporter status title tool_version" "$(field "$r" 'keys | join(" ")')" + check "the envelope is compact with one final LF" 1 "$(wc -l < "$r")" check "the fleet ref is recorded" deadbeef "$(field "$r" .ref)" +check "finding keys are exactly the spec's" "evidence_class file line message rule_id semantic_fingerprint severity" "$(field "$r" ' .findings[0] | keys | join(" ")')" check "reason is null when the member ran" null "$(field "$r" .reason)" -check "finding keys are exactly the spec's" "file line message rule_id severity" "$(field "$r" '.findings[0] | keys | join(" ")')" - -# 6. --plan ──────────────────────────────────────────────────────────────────── -plan=$(bash "$DRIVER" --plan --enrolment "$REPO/.gitbot-fleet/enrolment.json") -check "--plan lists only enrolled members" 7 "$(printf '%s\n' "$plan" | wc -l)" -check "--plan names the finishingbot crate and binary" "finishingbot finishingbot finishing-bot" "$(printf '%s\n' "$plan" | grep '^finishingbot')" -check "--plan gives echidnabot no crate: nothing is built for a member without a local mode" "echidnabot " "$(printf '%s\n' "$plan" | grep '^echidnabot')" -# 7. summary ─────────────────────────────────────────────────────────────────── check "summary has one row per member" 7 "$(grep -c '^| [a-z]' "$TMP/o1.md")" check "summary states the error reason" yes "$(grep -q '| echidnabot | not_run | 0 | no ECHIDNA' "$TMP/o1.md" && echo yes || echo no)" @@ -260,8 +253,9 @@ for alias in finishbot finishing-bot; do done plan=$(bash "$DRIVER" --plan --enrolment "" --member finishbot --member finishing-bot --member cipherbot) check "--plan maps aliases and cipherbot to their crates and binaries" \ - $'finishbot\tfinishingbot\tfinishing-bot\nfinishing-bot\tfinishingbot\tfinishing-bot\ncipherbot\tcipherbot\tcipherbot' "$plan" - + $'finishbot finishingbot finishing-bot +finishing-bot finishingbot finishing-bot +cipherbot cipherbot cipherbot' "$plan" stub rhodibot 'cat <