From 2c49980f6a4d6bf1b4784505d6c00636acc85d77 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Mon, 31 Aug 2026 10:00:55 +0530 Subject: [PATCH] bug: isolate application status by model (#351) --- CHANGELOG.md | 2 ++ lib/bash/app/README.md | 5 ++++ lib/bash/app/lib_app.sh | 12 ++++++--- lib/bash/app/tests/lib_app.bats | 46 +++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df97058..6af7598 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ and versions are tracked in the repo-root `VERSION` file. including canonical-name, alias, option-name, and option-token conflicts. - Rejected application declaration attributes outside each public API's documented context instead of silently accepting and discarding them. +- Isolated application run status per model while retaining the process-global + last-status value as a compatibility view of the most recently active model. - Prevented list, CLI, and application call paths from creating or overwriting caller-visible variables through undeclared internal scratch assignments. - Eliminated an intermittent macOS Bash process-group race in supervised diff --git a/lib/bash/app/README.md b/lib/bash/app/README.md index 9cbc8aa..b1e8b87 100644 --- a/lib/bash/app/README.md +++ b/lib/bash/app/README.md @@ -70,6 +70,11 @@ dispatched at most once per `base_app_run`, and the application status is preserved even if a hook fails. `INT`, `TERM`, and `HUP` map to statuses 130, 143, and 129 respectively. +`base_app_status MODEL RESULT_VARIABLE` returns that model's most recent run or +signal-derived status. A newly initialized model that has not run reports `0`. +`BASE_BASH_LIBS_APP_LAST_STATUS` remains the compatibility view of the most +recently active model, but model-aware callers should use `base_app_status`. + The policy module owns no global trap or shell-code strings itself. It uses the stdlib's shared cleanup dispatcher and can therefore coexist with other cleanup paths and hooks. diff --git a/lib/bash/app/lib_app.sh b/lib/bash/app/lib_app.sh index 0f91378..b105dc3 100644 --- a/lib/bash/app/lib_app.sh +++ b/lib/bash/app/lib_app.sh @@ -258,6 +258,7 @@ __base_bash_libs_app_cleanup_dispatch__() { *) __base_bash_libs_app_hook_dispatch__ "$model" fatal "$status" ;; esac __base_bash_libs_app_hook_dispatch__ "$model" cleanup "$status" + __base_bash_libs_app_models["$model|last-status"]="$status" BASE_BASH_LIBS_APP_LAST_STATUS="$status" return "$status" } @@ -310,6 +311,7 @@ base_app_init() { __base_bash_libs_app_models["$model|name"]="${__base_bash_libs_app_attrs[name]-$model}" __base_bash_libs_app_models["$model|description"]="${__base_bash_libs_app_attrs[description]-}" __base_bash_libs_app_models["$model|config-keys"]="" + __base_bash_libs_app_models["$model|last-status"]=0 for key in normal fatal int term hup cleanup; do __base_bash_libs_app_models["$model|hooks|$key"]="" done @@ -720,19 +722,21 @@ base_app_run() { __base_bash_libs_app_cleanup_dispatch__ "$status" base_std_unregister_cleanup_hook __base_bash_libs_app_cleanup_dispatch__ || true BASE_BASH_LIBS_APP_ACTIVE_MODEL="" + __base_bash_libs_app_models["$model|last-status"]="$status" + # shellcheck disable=SC2034 # Published compatibility status for callers. BASE_BASH_LIBS_APP_LAST_STATUS="$status" return "$status" } -# base_app_status - Copies the last application status. +# base_app_status - Copies the model's last run status; never-run models are 0. base_app_status() { - local result_name="${2-}" + local model="${1-}" result_name="${2-}" (($# == 2)) || { __base_bash_libs_app_error__ 'base_app_status: usage: base_app_status MODEL RESULT_VARIABLE' return 2 } __base_bash_libs_std_assert_public_variable_names__ base_app_status "$result_name" || return 1 __base_bash_libs_std_assert_writable_output__ base_app_status "$result_name" || return 1 - __base_bash_libs_app_model_exists__ "$1" || return 1 - printf -v "$result_name" '%s' "$BASE_BASH_LIBS_APP_LAST_STATUS" + __base_bash_libs_app_model_exists__ "$model" || return 1 + printf -v "$result_name" '%s' "${__base_bash_libs_app_models["$model|last-status"]-0}" } diff --git a/lib/bash/app/tests/lib_app.bats b/lib/bash/app/tests/lib_app.bats index bd40cc3..902176e 100644 --- a/lib/bash/app/tests/lib_app.bats +++ b/lib/bash/app/tests/lib_app.bats @@ -257,6 +257,52 @@ assert_demo_snapshot() { [ "$status" -eq 7 ] } +@test "application status is isolated across normal failure signal and never-run models" { + succeeds() { return 0; } + fails() { return 7; } + local alpha_status beta_status signal_status never_status + + base_app_init alpha + base_app_init beta + base_app_init signaled + base_app_init never + + base_app_status never never_status + [ "$never_status" -eq 0 ] + + base_app_run alpha succeeds + base_app_status alpha alpha_status + base_app_status beta beta_status + [ "$alpha_status" -eq 0 ] + [ "$beta_status" -eq 0 ] + + if base_app_run beta fails; then + false + else + [ "$?" -eq 7 ] + fi + base_app_status alpha alpha_status + base_app_status beta beta_status + [ "$alpha_status" -eq 0 ] + [ "$beta_status" -eq 7 ] + [ "$BASE_BASH_LIBS_APP_LAST_STATUS" -eq 7 ] + + BASE_BASH_LIBS_APP_ACTIVE_MODEL=signaled + if __base_bash_libs_app_cleanup_dispatch__ 143; then + false + else + [ "$?" -eq 143 ] + fi + BASE_BASH_LIBS_APP_ACTIVE_MODEL="" + base_app_status signaled signal_status + base_app_status beta beta_status + base_app_status never never_status + [ "$signal_status" -eq 143 ] + [ "$beta_status" -eq 7 ] + [ "$never_status" -eq 0 ] + [ "$BASE_BASH_LIBS_APP_LAST_STATUS" -eq 143 ] +} + @test "source is idempotent" { source "$BASE_BASH_DIR/app/lib_app.sh" [ "$(type -t base_app_run)" = function ]