Files
OrcaSlicer/scripts/ladder-all.sh
T
Tommaso BianchiandClaude Opus 5 96f9261425 The offer table is generated again, and its last verb was unreachable
snaporca-ziam said gen_offer_table.py would silently delete the model-mode "Constrain sketch"
row, because that row lived in the generated header and not in tool_atlas.json. Running it found
more than that: FOUR rows existed only in the header — constrain, rename, and the three typed-
value rows sk_length / sk_radius / sk_angdist — and sk_delete's action had drifted, pointing the
sketch row at btn:delete, the FEATURE delete.

All five are now in the atlas, so the header regenerates byte-identically from it. Verbs may carry
a `note`, emitted as a C++ comment above the row: a rationale written into a generated file is
deleted by the next regeneration, which is how this started.

snaporca-z8rs (P1), found by making that true: after the atlas held all 92 verbs, the regenerated
header differed from the checked-in one by EXACTLY ONE LINE — kOfferVerbCount, 91 against 92.
Every consumer loops i < kOfferVerbCount, so the last row of the table was invisible: never listed
by show_offer_menu, never findable by mcp_run_verb. The verb that fell off the end is sk_angdist,
"Angle / distance…" — the typed-value row for a two-entity selection. On the one selection where
you would ask for the angle between two lines, the row that types it was not in the menu.

It survived because nothing compared the Sk2Ent menu against the table: sk_angdist accepts Sk2Ent
and nothing else, so an off-by-one that dropped the LAST verb was invisible from every other
selection. The vocabulary rung now covers Sk2Ent too, and picking the pair taught it one more
rig fact — shift-clicking a circle at its +X point grabs the RADIUS GRIP, which replaces the
selection with that one entity, so the pair silently collapsed to one and the offer answered
SkLine. Correctly, for the selection that actually existed.

gen_offer_table.py --check proves header == atlas and changes nothing; it is now the first step of
scripts/ladder-all.sh, and the only one that needs no rig.

Offer ladder 107/107, gesture ladder 93/93, both on the rig.

snaporca-ziam snaporca-z8rs

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MrMzTpAf78U4NG2M8jfvHY
2026-08-23 06:14:48 +02:00

67 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# Every ladder, in one command, as the gate before a push that touched the Design tab.
#
# WHY A SCRIPT AND NOT CI. Three of the four rungs need a running application with an OpenGL
# canvas and synthetic input; GitHub's runners have neither. So the gate is local and explicit:
# run this, read the last line, and do not push a red one. The kernel suite is the only part CI
# can carry, and it already does.
#
# scripts/ladder-all.sh # kernel + engine + corpus (every 20th) + gestures + offer
# FULL=1 scripts/ladder-all.sh # corpus over ALL 997 sheets (~25 min)
# SKIP_GUI=1 scripts/ladder-all.sh # kernel only, for a machine with no rig
#
# The rig container is expected to be up with the app running and SNAPORCA_MCP set; bring it up
# with scripts/gui-session.sh inside it. The corpus lives at /corpus in that container.
set -uo pipefail
cd "$(dirname "$0")/.."
C="${C:-snaporca-gui}"
CORPUS="${CORPUS:-/corpus}"
STEP="${STEP:-20}"
[ -n "${FULL:-}" ] && STEP=1
fail=0
step() {
local name="$1"; shift
echo
echo "=== $name ==="
if "$@"; then echo "--- $name OK"; else echo "--- $name FAILED"; fail=1; fi
}
run_in_rig() { # copy the script in fresh, then run it there
docker cp "$1" "$C:/tmp/$(basename "$1")" >/dev/null || return 1
shift
docker exec "$C" python3 "$@"
}
# FIRST, and it needs no rig: the offer table the menu is compiled from must be what the atlas
# says. The header calls itself GENERATED and had been hand-edited anyway — which cost four rows
# that existed only in the header, one row wired to the wrong action, and a count of 91 for a
# 92-row array, so the last verb was unreachable (snaporca-z8rs, snaporca-ziam).
step "offer table matches the atlas" python3 docs/ux/mockups/gen_offer_table.py --check
step "kernel suite" scripts/kernel-test.sh --vol "${KVOL:-snaporca_kerneltest}"
if [ -z "${SKIP_GUI:-}" ]; then
step "engine ladder (rungs 1-8, scripted geometry)" \
run_in_rig scripts/sketch-ladder.py /tmp/sketch-ladder.py
step "corpus rung (real drawings, every ${STEP}th)" \
run_in_rig scripts/ladder-corpus.py /tmp/ladder-corpus.py --corpus "$CORPUS" --step "$STEP"
step "corpus scale rung (the heaviest sheets)" \
run_in_rig scripts/ladder-corpus.py /tmp/ladder-corpus.py --corpus "$CORPUS" --scale
step "gesture ladder (mouse and keyboard)" \
run_in_rig scripts/gui-ladder.py /tmp/gui-ladder.py
# The offer ladder needs TWO extra things the others do not: the app must have been launched
# with SNAPORCA_KEYTRACE=1 (its [OFFER] lines are the whole instrument), and it reads the
# generated offer table to predict what each selection should show — which is not in the
# container's own baked source tree, so it is copied in beside the script — /tmp, where
# run_in_rig puts the script, is one of the paths the ladder looks in.
docker cp src/slic3r/GUI/CAD/DesignOffer.hpp "$C:/tmp/DesignOffer.hpp" >/dev/null
step "offer ladder (right-click, the menu, the verbs behind it)" \
run_in_rig scripts/offer-ladder.py /tmp/offer-ladder.py
fi
echo
if [ "$fail" -eq 0 ]; then echo "ALL LADDERS HELD"; else echo "AT LEAST ONE LADDER FAILED"; fi
exit "$fail"