From ac305e03e28771109e2fe450bada7e41c1266635 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 29 Jun 2026 14:46:08 +0000 Subject: [PATCH] ci: add wrapper output-forwarding gate to prevent v0.3.x-class regressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing wrapper-forwarding job checked that wrappers expose every variable from their core module, but had no equivalent check for outputs. The v0.3.x regression (post_patch_ssm_document_name, redis_endpoint, redis_mode silently missing from HA wrappers) slipped through exactly because of this gap. Add a "Diff wrapper outputs against their core modules" step in the same job. It mirrors the variable-diff logic: for each of the 12 wrapper/core pairs, compare output names with comm -23 and fail if the wrapper is missing any output the core declares. No new dependencies — pure shell. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01JiqETiysB7AxP9ypY1LetL --- .github/workflows/ci.yml | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c50db34..9684a4a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -432,6 +432,47 @@ jobs: fi echo "All wrapper modules forward their core surface area cleanly." + # The v0.3.x regression was missing outputs (post_patch_ssm_document_name, + # redis_endpoint, redis_mode) that operators read from the wrapper. This + # step is the output-layer equivalent of the variable check above: if the + # core module adds a new output, the wrapper must re-export it too. + - name: Diff wrapper outputs against their core modules + run: | + set -eo pipefail + declare -A wrappers=( + ["sat-aws-single"]="single-vm/aws" + ["asm-aws-single"]="single-vm/aws" + ["sat-aws-ha"]="ha-hot-hot/aws" + ["asm-aws-ha"]="ha-hot-hot/aws" + ["sat-aws-autoscale"]="unlimited-scale/aws" + ["asm-aws-autoscale"]="unlimited-scale/aws" + ["sat-azure-single"]="single-vm/azure" + ["asm-azure-single"]="single-vm/azure" + ["sat-azure-ha"]="ha-hot-hot/azure" + ["asm-azure-ha"]="ha-hot-hot/azure" + ["sat-azure-autoscale"]="unlimited-scale/azure" + ["asm-azure-autoscale"]="unlimited-scale/azure" + ) + + fail=0 + for wrapper in "${!wrappers[@]}"; do + core="${wrappers[$wrapper]}" + core_outputs=$(grep -h '^output ' "modules/${core}/outputs.tf" | awk '{print $2}' | tr -d '"' | sort) + wrapper_outputs=$(grep -h '^output ' "modules/${wrapper}/outputs.tf" | awk '{print $2}' | tr -d '"' | sort) + missing=$(comm -23 <(echo "$core_outputs") <(echo "$wrapper_outputs") || true) + if [ -n "$missing" ]; then + echo "::error::Wrapper modules/${wrapper} is missing outputs defined by core modules/${core}:" + echo "$missing" | sed 's|^|::error:: - |' + fail=1 + fi + done + + if [ "${fail}" = "1" ]; then + echo "::error::Wrappers must re-export every core output (the v0.3.x regression was exactly this class of omission)." + exit 1 + fi + echo "All wrapper modules re-export their core output surface cleanly." + # Non-blocking: warn (don't fail) when a forwarded variable's # description/type/default has drifted from the core module, so the # public-API docs stay faithful to the implementation.