diff --git a/mqlaunch/lib/network.sh b/mqlaunch/lib/network.sh index b6202079..eb552166 100755 --- a/mqlaunch/lib/network.sh +++ b/mqlaunch/lib/network.sh @@ -210,6 +210,7 @@ run_network_quality() { pause_enter } +# Shows network info. show_network_info() { local report diff --git a/release-check.sh b/release-check.sh index f290ff9a..c28ef8f3 100755 --- a/release-check.sh +++ b/release-check.sh @@ -25,10 +25,14 @@ done : "$DRY_RUN" BLOCKERS=() +# Coordinates say behavior. say() { [[ "$JSON" -eq 1 ]] || echo "$1"; } +# Coordinates ok behavior. ok() { [[ "$JSON" -eq 1 ]] || echo " ok: $1"; } +# Marks a failing check. fail() { BLOCKERS+=("$1"); [[ "$JSON" -eq 1 ]] || echo "FAIL: $1" >&2; } +# Coordinates run behavior. run() { local label="$1"; shift local out diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index 5e44cbda..9b32d56f 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -528,6 +528,7 @@ start_dev_mode() { sleep 1 } +# Renders the menu view for terminal output. function render_menu() { local git_state host_name local repo_label branch_label diff --git a/terminal/launchers/mqlaunch-command-mode.sh b/terminal/launchers/mqlaunch-command-mode.sh index 85448f29..0c389ae8 100644 --- a/terminal/launchers/mqlaunch-command-mode.sh +++ b/terminal/launchers/mqlaunch-command-mode.sh @@ -91,6 +91,7 @@ nearest_cli_command() { ' } +# Prints unknown command error. print_unknown_command_error() { local command_name="${1:-}" local nearest diff --git a/terminal/menus/mq-obsidian-menu.sh b/terminal/menus/mq-obsidian-menu.sh index 4d4d162b..055a39d8 100755 --- a/terminal/menus/mq-obsidian-menu.sh +++ b/terminal/menus/mq-obsidian-menu.sh @@ -345,31 +345,55 @@ mq_obsidian_triage_learning_inbox() { printf " mq-agent\n" } -# Regenerates memory views when a supported script exists. +# mqobsidian's view renderers, repo-relative. Each reads local memory data and +# rewrites markdown under its own views/ directory. They sit next to their data +# rather than in mqobsidian/scripts/ on purpose: scripts/ is the export surface +# other repos consume, and these render nothing anyone else reads. Rendering is +# not curation — promotion, scoring and aging stay manual passes in mqobsidian. +MQ_OBSIDIAN_VIEW_RENDERERS=( + "memory/commands/build_views.py" + "memory/workflows/build_workflow_views.py" +) + +# Regenerates memory views by running every renderer the vault actually ships. +# Returns the first nonzero status so a broken renderer is not reported as a +# successful regeneration. mq_obsidian_regenerate_views() { - local python + # `rc`, not `status`: menus run under zsh, where $status is read-only. + local python renderer rc=0 ran=0 - if [[ -x "$MQ_OBSIDIAN_DIR/scripts/regenerate-memory-views.py" || -f "$MQ_OBSIDIAN_DIR/scripts/regenerate-memory-views.py" ]]; then - python="$(mq_obsidian_python)" || return - mq_obsidian_run "$python" scripts/regenerate-memory-views.py - else - printf "────────────────────────────────────────────────────────────\n" - printf " mqlaunch · Option 13 · Regenerate memory views\n" - printf "────────────────────────────────────────────────────────────\n\n" - printf "Status\n" - printf " not implemented yet\n\n" - printf "Current state\n" - printf " No regenerate-memory-views script is registered.\n\n" - printf "Expected owner\n" - printf " mq-agent orchestration\n\n" - printf "Local producer\n" - printf " mqobsidian regenerate-memory-views script\n\n" - printf "mqlaunch role\n" - printf " menu entry and read-only status only\n\n" - printf "Available here\n" - printf " Enter return to menu\n" - printf " x exit mqlaunch\n" + for renderer in "${MQ_OBSIDIAN_VIEW_RENDERERS[@]}"; do + [[ -f "$MQ_OBSIDIAN_DIR/$renderer" ]] || continue + if [[ "$ran" -eq 0 ]]; then + python="$(mq_obsidian_python)" || return 1 + fi + ran=1 + printf "── %s\n" "$renderer" + if ! mq_obsidian_run "$python" "$renderer"; then + rc=1 + fi + done + + if [[ "$ran" -eq 1 ]]; then + return "$rc" fi + + printf "────────────────────────────────────────────────────────────\n" + printf " mqlaunch · Option 13 · Regenerate memory views\n" + printf "────────────────────────────────────────────────────────────\n\n" + printf "Status\n" + printf " not implemented yet\n\n" + printf "Current state\n" + printf " This vault ships no view renderer.\n\n" + printf "Local producers\n" + for renderer in "${MQ_OBSIDIAN_VIEW_RENDERERS[@]}"; do + printf " %s\n" "$renderer" + done + printf "\nmqlaunch role\n" + printf " runs the renderers; mqobsidian owns curation\n\n" + printf "Available here\n" + printf " Enter return to menu\n" + printf " x exit mqlaunch\n" } # Runs the MQ Obsidian menu loop. diff --git a/terminal/release/mq-release-check.sh b/terminal/release/mq-release-check.sh index 86da721f..0300b3fa 100755 --- a/terminal/release/mq-release-check.sh +++ b/terminal/release/mq-release-check.sh @@ -101,6 +101,7 @@ else fi section "MQOBSIDIAN MANIFEST CONTRACT" +# Coordinates check mqobsidian manifest contract behavior. check_mqobsidian_manifest_contract() { local manifest="$BASE_DIR/mqlaunch/config/mqobsidian/views.json" diff --git a/tests/apps-menu-surface-smoke.sh b/tests/apps-menu-surface-smoke.sh index dd7dce74..6cbec32d 100755 --- a/tests/apps-menu-surface-smoke.sh +++ b/tests/apps-menu-surface-smoke.sh @@ -17,6 +17,7 @@ echo "SMOKE: apps menu surface" echo "[1/4] syntax check" bash -n "$MENU" +# Coordinates check loop behavior. check_loop() { local label="$1" panel="$2" loop="$3" limit="$4" diff --git a/tests/command-discovery-inventory-smoke.sh b/tests/command-discovery-inventory-smoke.sh index 2727e7ce..5ff712d7 100755 --- a/tests/command-discovery-inventory-smoke.sh +++ b/tests/command-discovery-inventory-smoke.sh @@ -93,6 +93,7 @@ cat >"$probe_dir/collide.sh" <<'PROBE' #!/usr/bin/env bash # Same handler names the real menus use, wired to the dispatcher instead. open_system_menu() { "$BASE_DIR/bin/mqlaunch" system; } +# Runs network ghost. run_network_ghost() { "$BASE_DIR/bin/mqlaunch" ghost; } PROBE with_untracked="$("$TOOL" --json)" @@ -170,6 +171,7 @@ echo "[10/10] the inventory still notices a bypass and a duplication" PLANT_MENU="$ROOT/terminal/menus/mq-net-menu.sh" PLANT_BACKUP="$(mktemp)" cp "$PLANT_MENU" "$PLANT_BACKUP" +# Restores plant from saved script state. restore_plant() { cp "$PLANT_BACKUP" "$PLANT_MENU"; rm -f "$PLANT_BACKUP"; } trap restore_plant EXIT diff --git a/tests/command-registry-smoke.sh b/tests/command-registry-smoke.sh index 830537ee..b1196b57 100755 --- a/tests/command-registry-smoke.sh +++ b/tests/command-registry-smoke.sh @@ -68,6 +68,7 @@ json.dump(doc, open(sys.argv[2], "w")) PY } +# Coordinates expect reject behavior. expect_reject() { # expect_reject local fixture="$1" what="$2" reason="$3" out diff --git a/tests/command-word-normalization-smoke.sh b/tests/command-word-normalization-smoke.sh index fb271395..8a36fe9f 100755 --- a/tests/command-word-normalization-smoke.sh +++ b/tests/command-word-normalization-smoke.sh @@ -16,6 +16,7 @@ trap 'rm -rf "$TMPDIR_TEST"' EXIT echo "SMOKE: command word normalization" +# Runs launcher. run_launcher() { set +e MACOS_SCRIPTS_HOME="$ROOT" MQ_NO_TUI=1 MQLAUNCH_HEADLESS=1 \ @@ -60,7 +61,9 @@ echo "[3/4] srm routes its agent verbs case-insensitively" export MACOS_SCRIPTS_HOME="$ROOT" # shellcheck source=/dev/null source "$COMMAND_MODE" +# Pauses until Enter is pressed. pause_enter() { return 0; } +# Runs agent command. run_agent_command() { printf '%s\n' "$1" > "$TMPDIR_TEST/verb" return 0 @@ -83,7 +86,9 @@ echo "[4/4] normalization does not reach past the command word" export MACOS_SCRIPTS_HOME="$ROOT" # shellcheck source=/dev/null source "$COMMAND_MODE" +# Pauses until Enter is pressed. pause_enter() { return 0; } +# Runs agent command. run_agent_command() { printf '%s\n' "$*" > "$TMPDIR_TEST/args" } diff --git a/tests/delegate-missing-message-smoke.sh b/tests/delegate-missing-message-smoke.sh index 599746dd..69535fb7 100755 --- a/tests/delegate-missing-message-smoke.sh +++ b/tests/delegate-missing-message-smoke.sh @@ -40,6 +40,7 @@ run_agent_missing() { MQ_AGENT_BIN="/nonexistent-delegate-for-this-test" # Stubs for the UI helpers the menu expects from mq-ui.sh. ui_err() { echo "$*" >&2; } +# Pauses until Enter is pressed. pause_enter() { :; } source "'"$AGENT_MENU"'" >/dev/null 2>&1 || true _run_agent doctor diff --git a/tests/delegated-exit-code-smoke.sh b/tests/delegated-exit-code-smoke.sh index aa49b812..58bad070 100755 --- a/tests/delegated-exit-code-smoke.sh +++ b/tests/delegated-exit-code-smoke.sh @@ -10,11 +10,13 @@ export MACOS_SCRIPTS_HOME="$ROOT" # shellcheck source=/dev/null source "$COMMAND_MODE" +# Pauses until Enter is pressed. pause_enter() { printf 'pause-called\n' >> "$TMPDIR_TEST/pause.log" return 0 } +# Coordinates assert status behavior. assert_status() { local expected="$1" shift @@ -38,17 +40,20 @@ assert_status 1 dispatch_cli_command review grep -q 'bridge not loaded' "$TMPDIR_TEST/stderr" echo "[2/12] usage and runtime failures propagate" +# Runs agent command. run_agent_command() { return "${MQ_TEST_BACKEND_STATUS:-0}"; } MQ_TEST_BACKEND_STATUS=2 assert_status 2 dispatch_cli_command review MQ_TEST_BACKEND_STATUS=42 assert_status 42 dispatch_cli_command stack status echo "[3/12] HAL pause does not overwrite backend status" +# Coordinates mq hal run behavior. mq_hal_run() { return "${MQ_TEST_BACKEND_STATUS:-0}"; } rm -f "$TMPDIR_TEST/pause.log" MQ_TEST_BACKEND_STATUS=42 assert_status 42 dispatch_cli_command hal brief [[ -s "$TMPDIR_TEST/pause.log" ]] echo "[4/12] JSON stdout stays clean" +# Coordinates mq hal run behavior. mq_hal_run() { printf '{"schema":"hal.test.v1"}\n' return 42 @@ -210,6 +215,7 @@ echo "[8/12] the brain bridge's exit status reaches the caller" brain_status() { local code="$1" verb="$2" ( +# Coordinates mq brain run behavior. mq_brain_run() { return "$code"; } dispatch_cli_command "$verb" note-arg >/dev/null 2>&1 ) @@ -257,6 +263,7 @@ fn_status() { ) } +# Coordinates expect status behavior. expect_status() { local want="$1" fn="$2" shift 2 @@ -325,6 +332,7 @@ exit 7 STUB chmod +x "$apps_fake/tools/scripts/hal-terminal-guide.sh" +# Coordinates apps status behavior. apps_status() { local got=0 ( diff --git a/tests/dev-menu-smoke.sh b/tests/dev-menu-smoke.sh index 5a6d717d..fc3b5b3a 100755 --- a/tests/dev-menu-smoke.sh +++ b/tests/dev-menu-smoke.sh @@ -150,9 +150,13 @@ echo "[6/6] each grouped row actually opens its submenu" routes="$(timeout 60 bash -c ' export MACOS_SCRIPTS_HOME="'"$ROOT"'" BASE_DIR="'"$ROOT"'" MQ_NO_TUI=1 source "'"$ROOT"'/terminal/launchers/mqlaunch.sh" >/dev/null 2>&1 || true +# Reads menu choice from user input or stdin. read_menu_choice() { IFS= read -r REPLY || return 1; return 0; } +# Reads main choice from user input or stdin. read_main_choice() { IFS= read -r choice || return 1; return 0; } +# Prints header. print_header() { :; } +# Pauses until Enter is pressed. pause_enter() { :; } for choice in 1 4 10; do printf "%s\nb\nb\n" "$choice" \ diff --git a/tests/doctor-status-contract-smoke.sh b/tests/doctor-status-contract-smoke.sh index 30438746..0f64abc4 100755 --- a/tests/doctor-status-contract-smoke.sh +++ b/tests/doctor-status-contract-smoke.sh @@ -249,6 +249,7 @@ build_world "$eza_and_launcher" git gh uv python3 node fzf jq gitleaks pbcopy doctor_run "$only_eza" key next-eza >/dev/null doctor_run "$eza_and_launcher" key next-launcher >/dev/null +# Coordinates next step behavior. next_step() { # next_step python3 - "$1" <<'PY' diff --git a/tests/git-restore-to-base-smoke.sh b/tests/git-restore-to-base-smoke.sh index e8c25a9d..6452e371 100755 --- a/tests/git-restore-to-base-smoke.sh +++ b/tests/git-restore-to-base-smoke.sh @@ -14,8 +14,10 @@ GIT_MENU="$ROOT/terminal/menus/mq-git-menu.sh" TMP="$(mktemp -d)" trap 'rm -rf "$TMP"' EXIT +# Coordinates git behavior. _git() { git -C "$1" "${@:2}"; } +# Coordinates assert restored behavior. assert_restored() { local repo="$1" diff --git a/tests/hal-args-no-eval-smoke.sh b/tests/hal-args-no-eval-smoke.sh index 4c0a300d..3b0e36a1 100755 --- a/tests/hal-args-no-eval-smoke.sh +++ b/tests/hal-args-no-eval-smoke.sh @@ -7,6 +7,7 @@ set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# Marks a failing check. fail() { printf '[FAIL] %s\n' "$1" >&2; exit 1; } for f in terminal/launchers/mqlaunch-repl.sh terminal/menus/mq-main-menu.sh; do diff --git a/tests/install-contract-smoke.sh b/tests/install-contract-smoke.sh index 0570dbbe..5bb7af94 100755 --- a/tests/install-contract-smoke.sh +++ b/tests/install-contract-smoke.sh @@ -41,6 +41,7 @@ done) echo "[2/6] a dry run changes nothing" tmp_bin="$(mktemp -d)" tmp_state="$(mktemp -d)" +# Coordinates cleanup behavior. cleanup() { rm -rf "$tmp_bin" "$tmp_state"; } trap cleanup EXIT diff --git a/tests/manifest.tsv b/tests/manifest.tsv index 1f821d05..550e0b1b 100644 --- a/tests/manifest.tsv +++ b/tests/manifest.tsv @@ -74,6 +74,7 @@ mq-flow-routing-smoke.sh active - mq-obsidian-menu-no-promotion-smoke.sh active - mq-obsidian-python-smoke.sh active - mq-obsidian-regenerate-placeholder-ui-smoke.sh active - +mq-obsidian-regenerate-views-smoke.sh active - mq-obsidian-triage-ui-smoke.sh active - brain-bridge-smoke.sh active - mq-agent-routing-smoke.sh active - diff --git a/tests/monolith-delayer-smoke.sh b/tests/monolith-delayer-smoke.sh index ad7e50a3..16e80a1c 100755 --- a/tests/monolith-delayer-smoke.sh +++ b/tests/monolith-delayer-smoke.sh @@ -10,6 +10,7 @@ set -uo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" LAUNCHER="$ROOT/terminal/launchers/mqlaunch.sh" +# Marks a failing check. fail() { printf '[FAIL] %s\n' "$1" >&2; exit 1; } # Each concern: "lib_relpath : space-separated function names" diff --git a/tests/mq-agent-routing-smoke.sh b/tests/mq-agent-routing-smoke.sh index 16d396bf..7e62750f 100755 --- a/tests/mq-agent-routing-smoke.sh +++ b/tests/mq-agent-routing-smoke.sh @@ -59,6 +59,7 @@ translate() { ( # shellcheck source=/dev/null source "$AGENT_MENU" >/dev/null 2>&1 +# Coordinates run agent behavior. _run_agent() { printf 'mq-agent' printf ' %s' "$@" @@ -68,6 +69,7 @@ translate() { ) } +# Coordinates expect translation behavior. expect_translation() { local want="$1" shift @@ -105,6 +107,7 @@ dispatch() { ( # shellcheck source=/dev/null source "$AGENT_MENU" >/dev/null 2>&1 +# Coordinates run agent behavior. _run_agent() { printf 'mq-agent' printf ' %s' "$@" @@ -115,6 +118,7 @@ dispatch() { ) } +# Coordinates expect dispatch behavior. expect_dispatch() { local want="$1" shift @@ -172,17 +176,20 @@ cli() { export MACOS_SCRIPTS_HOME="$ROOT" # shellcheck source=/dev/null source "$COMMAND_MODE" >/dev/null 2>&1 +# Runs agent command. run_agent_command() { printf 'mq-agent' printf ' %s' "$@" printf '\n' return "${STUB_EXIT:-0}" } +# Pauses until Enter is pressed. pause_enter() { :; } dispatch_cli_command "$@" ) } +# Coordinates expect cli behavior. expect_cli() { local want="$1" shift diff --git a/tests/mq-debug-smoke.sh b/tests/mq-debug-smoke.sh index d938ffb4..8612bd8d 100755 --- a/tests/mq-debug-smoke.sh +++ b/tests/mq-debug-smoke.sh @@ -6,6 +6,7 @@ set -uo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# Marks a failing check. fail() { printf '[FAIL] %s\n' "$1" >&2; exit 1; } # shellcheck source=/dev/null diff --git a/tests/mq-memory-cochange-routing-smoke.sh b/tests/mq-memory-cochange-routing-smoke.sh index d0a19176..31eb15e0 100755 --- a/tests/mq-memory-cochange-routing-smoke.sh +++ b/tests/mq-memory-cochange-routing-smoke.sh @@ -35,7 +35,9 @@ echo "[3/7] command mode intercepts 'memory cochange' and routes to the agent br # shellcheck source=/dev/null source "$COMMAND_MODE" +# Pauses until Enter is pressed. pause_enter() { return 0; } +# Runs agent command. run_agent_command() { printf '%s\n' "$*" >"$DELEGATE_DIR/call"; return 0; } dispatch_cli_command memory cochange >/dev/null 2>&1 diff --git a/tests/mq-obsidian-regenerate-placeholder-ui-smoke.sh b/tests/mq-obsidian-regenerate-placeholder-ui-smoke.sh index 3ad6aacf..f8561833 100755 --- a/tests/mq-obsidian-regenerate-placeholder-ui-smoke.sh +++ b/tests/mq-obsidian-regenerate-placeholder-ui-smoke.sh @@ -17,17 +17,17 @@ source "$ROOT/terminal/menus/mq-obsidian-menu.sh" echo "[1/2] option 13 has a stable command label" test "$(surface_choice_summary mqobsidian 13)" = "option 13: regenerate memory views" -echo "[2/2] missing producer is presented as a planned placeholder" +echo "[2/2] a vault with no renderer is presented as a planned placeholder" +# The tmp vault ships no renderer, which is what puts option 13 on this path. +# Which renderers it looks for is covered by mq-obsidian-regenerate-views-smoke.sh; +# this asserts the placeholder still reads as operator UI. output="$(mq_obsidian_regenerate_views)" grep -q "mqlaunch · Option 13 · Regenerate memory views" <<<"$output" grep -q " not implemented yet" <<<"$output" -grep -q " No regenerate-memory-views script is registered." <<<"$output" -grep -q "Expected owner" <<<"$output" -grep -q " mq-agent orchestration" <<<"$output" -grep -q "Local producer" <<<"$output" -grep -q " mqobsidian regenerate-memory-views script" <<<"$output" +grep -q " This vault ships no view renderer." <<<"$output" +grep -q "Local producers" <<<"$output" grep -q "mqlaunch role" <<<"$output" -grep -q " menu entry and read-only status only" <<<"$output" +grep -q " runs the renderers; mqobsidian owns curation" <<<"$output" grep -q " Enter return to menu" <<<"$output" grep -q " x exit mqlaunch" <<<"$output" diff --git a/tests/mq-obsidian-regenerate-views-smoke.sh b/tests/mq-obsidian-regenerate-views-smoke.sh new file mode 100755 index 00000000..b4f59ec8 --- /dev/null +++ b/tests/mq-obsidian-regenerate-views-smoke.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Option 13 used to look for scripts/regenerate-memory-views.py, a file mqobsidian +# never shipped, so the option was a permanent placeholder. The renderers that do +# exist live next to the data they render: memory/commands/build_views.py and +# memory/workflows/build_workflow_views.py. This locks the option to those, and +# keeps the placeholder for a vault that has neither. + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TMP_ROOT="$(mktemp -d)" +trap 'rm -rf "$TMP_ROOT"' EXIT + +export BASE_DIR="$ROOT" +export MQ_OBSIDIAN_DIR="$TMP_ROOT/mqobsidian" + +# shellcheck source=../terminal/menus/mq-main-menu.sh +source "$ROOT/terminal/menus/mq-main-menu.sh" +# shellcheck source=../terminal/menus/mq-obsidian-menu.sh +source "$ROOT/terminal/menus/mq-obsidian-menu.sh" + +echo "SMOKE: mqobsidian regenerate memory views" + +# A stub python that records what it was asked to run, so the test asserts +# routing without depending on mqobsidian's real renderers. +mkdir -p "$TMP_ROOT/bin" +cat >"$TMP_ROOT/bin/python3" <<'STUB' +#!/usr/bin/env bash +printf 'RAN %s\n' "$1" +STUB +chmod +x "$TMP_ROOT/bin/python3" +export PATH="$TMP_ROOT/bin:$PATH" + +echo "[1/6] both renderers present: both run" +mkdir -p "$MQ_OBSIDIAN_DIR/memory/commands" "$MQ_OBSIDIAN_DIR/memory/workflows" +touch "$MQ_OBSIDIAN_DIR/memory/commands/build_views.py" +touch "$MQ_OBSIDIAN_DIR/memory/workflows/build_workflow_views.py" +output="$(mq_obsidian_regenerate_views)" +grep -q "RAN memory/commands/build_views.py" <<<"$output" +grep -q "RAN memory/workflows/build_workflow_views.py" <<<"$output" + +echo "[2/6] only the command renderer present: the other is not invented" +rm "$MQ_OBSIDIAN_DIR/memory/workflows/build_workflow_views.py" +output="$(mq_obsidian_regenerate_views)" +grep -q "RAN memory/commands/build_views.py" <<<"$output" +! grep -q "build_workflow_views" <<<"$output" + +echo "[3/6] neither present: placeholder, and it names the real producers" +rm "$MQ_OBSIDIAN_DIR/memory/commands/build_views.py" +output="$(mq_obsidian_regenerate_views)" +grep -q "mqlaunch · Option 13 · Regenerate memory views" <<<"$output" +grep -q " not implemented yet" <<<"$output" +grep -q "memory/commands/build_views.py" <<<"$output" +grep -q "memory/workflows/build_workflow_views.py" <<<"$output" + +echo "[4/6] a failing renderer surfaces as a nonzero status" +cat >"$TMP_ROOT/bin/python3" <<'STUB' +#!/usr/bin/env bash +printf 'boom\n' >&2 +exit 3 +STUB +chmod +x "$TMP_ROOT/bin/python3" +touch "$MQ_OBSIDIAN_DIR/memory/commands/build_views.py" +set +e +mq_obsidian_regenerate_views >/dev/null 2>&1 +status=$? +set -e +test "$status" -ne 0 + +echo "[5/6] option 13 keeps its command label" +test "$(surface_choice_summary mqobsidian 13)" = "option 13: regenerate memory views" + +echo "[6/6] it runs under zsh, which is the shell the menus actually use" +# Caught a real one: a local named `status` is fine in bash and fatal in zsh, +# where $status is read-only. Testing only under bash hid it. +cat >"$TMP_ROOT/bin/python3" <<'STUB' +#!/usr/bin/env bash +printf 'RAN %s\n' "$1" +STUB +chmod +x "$TMP_ROOT/bin/python3" +output="$(zsh -c " + export BASE_DIR='$ROOT' MQ_OBSIDIAN_DIR='$MQ_OBSIDIAN_DIR' PATH='$PATH' + source '$ROOT/terminal/menus/mq-main-menu.sh' + source '$ROOT/terminal/menus/mq-obsidian-menu.sh' + mq_obsidian_regenerate_views +" 2>&1)" +grep -q "RAN memory/commands/build_views.py" <<<"$output" +! grep -qi "read-only variable" <<<"$output" + +echo "OK: mqobsidian regenerate memory views smoke passed" diff --git a/tests/mq-route-entrypoint-smoke.sh b/tests/mq-route-entrypoint-smoke.sh index 6cfc3042..0071b4a6 100755 --- a/tests/mq-route-entrypoint-smoke.sh +++ b/tests/mq-route-entrypoint-smoke.sh @@ -10,6 +10,7 @@ DOC="$ROOT/docs/COMMANDS.md" echo "SMOKE: mqlaunch route thin entrypoint" +# Coordinates dispatch behavior. dispatch() { ( export MACOS_SCRIPTS_HOME="$ROOT" @@ -17,12 +18,14 @@ dispatch() { source "$AGENT_MENU" >/dev/null 2>&1 # shellcheck source=/dev/null source "$COMMAND_MODE" >/dev/null 2>&1 +# Coordinates run agent behavior. _run_agent() { printf 'mq-agent' printf ' <%s>' "$@" printf '\n' return "${STUB_EXIT:-0}" } +# Pauses until Enter is pressed. pause_enter() { :; } dispatch_cli_command "$@" ) diff --git a/tests/namespace-help-smoke.sh b/tests/namespace-help-smoke.sh index 16acc932..e77be160 100755 --- a/tests/namespace-help-smoke.sh +++ b/tests/namespace-help-smoke.sh @@ -14,6 +14,7 @@ TIMEOUT_BIN="$(command -v timeout || command -v gtimeout || true)" exit 1 } +# Runs help. run_help() { local namespace="$1" local flag="$2" diff --git a/tests/operator-usage-message-smoke.sh b/tests/operator-usage-message-smoke.sh index d9d15d35..9dbe8ffe 100755 --- a/tests/operator-usage-message-smoke.sh +++ b/tests/operator-usage-message-smoke.sh @@ -127,6 +127,7 @@ routed="$( MACOS_SCRIPTS_HOME="$ROOT" MQ_NO_TUI=1 bash -c ' source "$1" >/dev/null 2>&1 BASE_DIR="$2" +# Pauses until Enter is pressed. pause_enter() { :; } dispatch_cli_command skills audit --repo /tmp/x ' _ "$COMMAND_MODE" "$skills_fake" 2>&1 @@ -243,6 +244,7 @@ for probe in "list" "status --short" "diff-summary"; do MACOS_SCRIPTS_HOME="$ROOT" MQ_NO_TUI=1 bash -c ' source "$1" >/dev/null 2>&1 BASE_DIR="$2" +# Pauses until Enter is pressed. pause_enter() { :; } shift 2 dispatch_cli_command repos "$@" diff --git a/tests/output-mode-parity-smoke.sh b/tests/output-mode-parity-smoke.sh index 2528016f..83ecbb63 100755 --- a/tests/output-mode-parity-smoke.sh +++ b/tests/output-mode-parity-smoke.sh @@ -316,6 +316,7 @@ json.dump(doc, open(sys.argv[2], "w")) PY } +# Coordinates expect parity failure behavior. expect_parity_failure() { # expect_parity_failure [observed] local fixture="$1" what="$2" reason="$3" observed="${4:-$run_dir/observed.txt}" out diff --git a/tests/performance-screens-golden-smoke.sh b/tests/performance-screens-golden-smoke.sh index 7a87b54b..0607b160 100755 --- a/tests/performance-screens-golden-smoke.sh +++ b/tests/performance-screens-golden-smoke.sh @@ -121,6 +121,7 @@ render_screen() { return "$rc" } +# Coordinates capture all behavior. capture_all() { local perf_file="$1" dest="$2" fn rc so se : >"$dest" diff --git a/tests/plain-output-contract-smoke.sh b/tests/plain-output-contract-smoke.sh index 61230e17..da9ffc8f 100755 --- a/tests/plain-output-contract-smoke.sh +++ b/tests/plain-output-contract-smoke.sh @@ -351,11 +351,13 @@ printf 'x\n' > "$repo_probe/tracked.txt" git -C "$repo_probe" add tracked.txt git -C "$repo_probe" -c user.email=t@t -c user.name=t commit -qm init +# Coordinates state of behavior. state_of() { bash -c 'source "$1/ui/terminal-ui/mq-ui.sh" >/dev/null 2>&1; mq_repo_state "$2"' \ _ "$ROOT" "$1" } +# Coordinates expect state behavior. expect_state() { # expect_state local got diff --git a/tests/prompt-lib-smoke.sh b/tests/prompt-lib-smoke.sh index 479ff26c..86e33ad0 100755 --- a/tests/prompt-lib-smoke.sh +++ b/tests/prompt-lib-smoke.sh @@ -7,6 +7,7 @@ LAUNCHER="$ROOT/terminal/launchers/mqlaunch.sh" TMPDIR_TEST="$(mktemp -d)" trap 'rm -rf "$TMPDIR_TEST"' EXIT +# Marks a failing check. fail() { printf '[FAIL] %s\n' "$1" >&2; exit 1; } echo "[1/5] shared prompt library exists and is sourced" diff --git a/tests/pulse-cli-color-contract-smoke.sh b/tests/pulse-cli-color-contract-smoke.sh index 827dce88..9cce6f29 100755 --- a/tests/pulse-cli-color-contract-smoke.sh +++ b/tests/pulse-cli-color-contract-smoke.sh @@ -71,6 +71,7 @@ EOF chmod +x "$STUB"/networksetup "$STUB"/wdutil "$STUB"/ping "$STUB"/route +# Coordinates esc count behavior. esc_count() { # Count ESC bytes, not lines: several escapes share a line. LC_ALL=C python3 -c 'import sys; sys.stdout.write(str(open(sys.argv[1],"rb").read().count(b"\x1b")))' "$1" diff --git a/tests/release-check-contract-smoke.sh b/tests/release-check-contract-smoke.sh index ce964d06..a6601423 100755 --- a/tests/release-check-contract-smoke.sh +++ b/tests/release-check-contract-smoke.sh @@ -38,12 +38,14 @@ STUB chmod +x "$TMPDIR_TEST/release-check.sh" \ "$TMPDIR_TEST/terminal/release/mq-release-check.sh" +# Coordinates dispatch behavior. dispatch() { ( export MACOS_SCRIPTS_HOME="$ROOT" export STUB_LOG="$TMPDIR_TEST/calls.log" # shellcheck source=/dev/null source "$COMMAND_MODE" +# Pauses until Enter is pressed. pause_enter() { return 0; } # Read by the dispatcher sourced above — this is what points it at the stub # tree instead of the real one. ShellCheck cannot follow the source. diff --git a/tests/release-menu-smoke.sh b/tests/release-menu-smoke.sh index 87e86ad3..b999d2ca 100755 --- a/tests/release-menu-smoke.sh +++ b/tests/release-menu-smoke.sh @@ -155,8 +155,11 @@ echo "[6/6] each grouped row actually opens its submenu" routes="$(timeout 60 bash -c ' export MACOS_SCRIPTS_HOME="'"$ROOT"'" BASE_DIR="'"$ROOT"'" MQ_NO_TUI=1 source "'"$ROOT"'/terminal/launchers/mqlaunch.sh" >/dev/null 2>&1 || true +# Reads menu choice from user input or stdin. read_menu_choice() { IFS= read -r REPLY || return 1; return 0; } +# Prints header. print_header() { :; } +# Pauses until Enter is pressed. pause_enter() { :; } for choice in 8 9; do printf "%s\nb\nb\n" "$choice" \ diff --git a/tests/release-pull-request-mode-smoke.sh b/tests/release-pull-request-mode-smoke.sh index 3a372da5..994ff4e2 100755 --- a/tests/release-pull-request-mode-smoke.sh +++ b/tests/release-pull-request-mode-smoke.sh @@ -23,6 +23,7 @@ export GIT_CONFIG_GLOBAL="$WORK/gitconfig" echo "SMOKE: release.sh pull_request mode" +# Marks a failing check. fail() { echo "FAIL: $*" >&2 exit 1 @@ -78,6 +79,7 @@ exit 0 STUB chmod +x "$WORK/bin/gh" +# Runs release. run_release() { local repo="$1" shift diff --git a/tests/runtime-authority-freeze-smoke.sh b/tests/runtime-authority-freeze-smoke.sh index f2ec1536..713ba240 100755 --- a/tests/runtime-authority-freeze-smoke.sh +++ b/tests/runtime-authority-freeze-smoke.sh @@ -78,6 +78,7 @@ plants=( "automation/login/mqlogin.sh" "tools/scripts/doctor.sh" ) +# Restores plants from saved script state. restore_plants() { local rel for rel in "${plants[@]}"; do diff --git a/tests/system-menu-smoke.sh b/tests/system-menu-smoke.sh index bab6f36c..776d8dbc 100755 --- a/tests/system-menu-smoke.sh +++ b/tests/system-menu-smoke.sh @@ -174,8 +174,11 @@ echo "[7/7] each grouped row actually opens its submenu" routes="$(timeout 60 bash -c ' export MACOS_SCRIPTS_HOME="'"$ROOT"'" BASE_DIR="'"$ROOT"'" MQ_NO_TUI=1 source "'"$ROOT"'/terminal/launchers/mqlaunch.sh" >/dev/null 2>&1 || true +# Reads menu choice from user input or stdin. read_menu_choice() { IFS= read -r REPLY || return 1; return 0; } +# Prints header. print_header() { :; } +# Pauses until Enter is pressed. pause_enter() { :; } for choice in 2 5 7 8; do printf "%s\nb\nb\n" "$choice" \ diff --git a/tests/terminal-width-smoke.sh b/tests/terminal-width-smoke.sh index bcdc3574..76b74bec 100755 --- a/tests/terminal-width-smoke.sh +++ b/tests/terminal-width-smoke.sh @@ -25,6 +25,7 @@ trap 'rm -rf "$WORK"' EXIT echo "SMOKE: terminal width" +# Marks a failing check. fail() { echo "FAIL: $*" >&2 exit 1 diff --git a/tests/test-inventory-smoke.sh b/tests/test-inventory-smoke.sh index 231a08d8..042a5688 100755 --- a/tests/test-inventory-smoke.sh +++ b/tests/test-inventory-smoke.sh @@ -17,6 +17,7 @@ SUITE="$ROOT/tools/scripts/test-all.sh" echo "SMOKE: test inventory" +# Marks a failing check. fail() { echo "FAIL: $*" >&2 exit 1 diff --git a/tests/theme-command-surface-smoke.sh b/tests/theme-command-surface-smoke.sh index 6d041f7f..95898c6f 100755 --- a/tests/theme-command-surface-smoke.sh +++ b/tests/theme-command-surface-smoke.sh @@ -26,18 +26,22 @@ source "$COMMAND_MODE" CALLS="$TMPDIR_TEST/calls.log" +# Reads or applies the theme cmd setting. theme_cmd() { printf 'theme_cmd %s\n' "$*" >> "$CALLS" return 0 } +# Opens themes menu. open_themes_menu() { printf 'menu\n' >> "$CALLS" return 0 } +# Pauses until Enter is pressed. pause_enter() { return 0; } +# Coordinates assert call behavior. assert_call() { local expected="$1" shift @@ -97,6 +101,7 @@ theme_cmd() { return "${MQ_TEST_THEME_STATUS:-0}" } +# Coordinates assert status behavior. assert_status() { local expected="$1" shift diff --git a/tests/unknown-command-contract-smoke.sh b/tests/unknown-command-contract-smoke.sh index 3dcb99b4..e919f67d 100755 --- a/tests/unknown-command-contract-smoke.sh +++ b/tests/unknown-command-contract-smoke.sh @@ -17,6 +17,7 @@ EOF chmod +x "$TMPDIR_TEST/bin/$command_name" done +# Runs unknown. run_unknown() { local label="$1" local unknown="${2:-definitely-not-a-command}" @@ -46,6 +47,7 @@ run_unknown() { grep -q 'mqlaunch ask' "$stderr_file" } +# Runs tty unknown. run_tty_unknown() { local output_file="$TMPDIR_TEST/tty.output" local status @@ -90,6 +92,7 @@ PY grep -q 'Unknown command: doctro' "$output_file" } +# Runs discover input contract. run_discover_input_contract() { local output expected exit_output @@ -100,14 +103,20 @@ BASE_DIR="$ROOT_UNDER_TEST" APP_TITLE="MQLAUNCH" source "$ROOT_UNDER_TEST/terminal/menus/mq-main-menu.sh" +# Opens command palette or help. open_command_palette_or_help() { printf 'palette\n'; } +# Opens help or index. open_help_or_index() { printf 'help\n'; } +# Runs main shell command. run_main_shell_command() { printf 'shell:%s\n' "$1"; } +# Runs mqworkflows. run_mqworkflows() { printf 'command:workflows\n'; } +# Routes cli command to the matching command handler. dispatch_cli_command() { printf 'unknown:%s\n' "$*" return 0 } +# Pauses until Enter is pressed. pause_enter() { :; } for original in "/" "/." "/ Palette" "/. Palette" "?" "?." "? Help" "?. Help index" "!printf contract-shell" "workflows"; do diff --git a/tests/wiki-command-ref-smoke.sh b/tests/wiki-command-ref-smoke.sh index cae4d908..6a34de97 100755 --- a/tests/wiki-command-ref-smoke.sh +++ b/tests/wiki-command-ref-smoke.sh @@ -17,6 +17,7 @@ trap 'rm -rf "$WORK"' EXIT echo "SMOKE: wiki Command-Reference metadata" +# Marks a failing check. fail() { echo "FAIL: $*" >&2 exit 1 diff --git a/tools/scripts/test-all.sh b/tools/scripts/test-all.sh index 69bd2e0f..d870d1f7 100755 --- a/tools/scripts/test-all.sh +++ b/tools/scripts/test-all.sh @@ -62,6 +62,7 @@ echo "== Running mqlaunch headless checks ==" "$PROJECT_ROOT/tests/mq-obsidian-menu-no-promotion-smoke.sh" "$PROJECT_ROOT/tests/mq-obsidian-python-smoke.sh" "$PROJECT_ROOT/tests/mq-obsidian-regenerate-placeholder-ui-smoke.sh" +"$PROJECT_ROOT/tests/mq-obsidian-regenerate-views-smoke.sh" "$PROJECT_ROOT/tests/mq-obsidian-triage-ui-smoke.sh" "$PROJECT_ROOT/tests/brain-bridge-smoke.sh" "$PROJECT_ROOT/tests/system-menu-smoke.sh"