From 751fb67523012658c305fd0dae12fba4cfe67c4c Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Sat, 3 Oct 2026 01:12:47 +0100 Subject: [PATCH] Bind demo controls without broadening unrelated release proof Keep new source paths on their own connected proof policy after the full lookup suite exposed six broadened metadata expectations. Preserve terminal no-op cancellation evidence and the runtime file owner. Contract-Neutral: Correct connected observation and verification routing within the existing demo operations and authority. Change-source: pulse-maintainer --- .github/scripts/demo-runtime-transaction.py | 9 ++++++--- .../tests/test_demo_runtime_transaction.py | 13 ++++++++++--- .../v6/internal/subsystems/registry.json | 19 +++++++++++++++---- scripts/installtests/demo_transaction_test.go | 5 ++++- .../release_control/subsystem_lookup_test.py | 12 ++++++++++++ 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/.github/scripts/demo-runtime-transaction.py b/.github/scripts/demo-runtime-transaction.py index 60193e378e..5fa81dda04 100644 --- a/.github/scripts/demo-runtime-transaction.py +++ b/.github/scripts/demo-runtime-transaction.py @@ -275,6 +275,8 @@ class Host: pattern = re.compile(r"^[ \t]*" + re.escape(key) + r"=.*$", re.M) text = pattern.sub(key + "=" + value, text) if pattern.search(text) else text.rstrip("\n") + "\n" + key + "=" + value + "\n" env_file.write_text(text) + data_owner = PATHS["data"].stat() + os.chown(env_file, data_owner.st_uid, data_owner.st_gid) os.chmod(env_file, 0o600) billing = json.loads(billing_file.read_text()) if not isinstance(billing, dict) or not isinstance(billing.get("capabilities", []), list): @@ -282,6 +284,7 @@ class Host: billing["capabilities"] = sorted(set(billing.get("capabilities", []) + ["demo_fixtures"])) billing.pop("integrity", None) billing_file.write_text(json.dumps(billing)) + os.chown(billing_file, data_owner.st_uid, data_owner.st_gid) os.chmod(billing_file, 0o600) if unhealthy: for relative in DEMO_HISTORY: @@ -408,14 +411,14 @@ class Transaction: elif self.request["mode"] == "update": raise Failure("update-baseline-unhealthy") self.receipt["healthy_baseline"] = healthy_baseline + old_handlers = {sig: signal.getsignal(sig) for sig in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP)} + for sig in old_handlers: + signal.signal(sig, cancel) # The no-op is observed too; an immediate healthy sample is not success. if self.request["mode"] == "recover" and healthy_baseline and self.host.profile_matches(self.request): self.watch("forward", original_version, self.host.cursors()) self.save("healthy_noop") return 0 - old_handlers = {sig: signal.getsignal(sig) for sig in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP)} - for sig in old_handlers: - signal.signal(sig, cancel) self.save("capturing") stopped = True self.host.stop() diff --git a/.github/scripts/tests/test_demo_runtime_transaction.py b/.github/scripts/tests/test_demo_runtime_transaction.py index 36bb810f44..6050a380c8 100644 --- a/.github/scripts/tests/test_demo_runtime_transaction.py +++ b/.github/scripts/tests/test_demo_runtime_transaction.py @@ -1,4 +1,3 @@ -import copy import hashlib import importlib.util import json @@ -195,6 +194,16 @@ class TransactionTest(unittest.TestCase): self.assertFalse(receipt["mutated"]) self.assertEqual(self.host.stops, 0) + def test_noop_cancellation_retains_terminal_failure_without_stopping_service(self): + self.host.profile(request(), False) + self.host.cancel_at = 55 + code, receipt = self.run_transaction() + self.assertEqual(code, 1) + self.assertEqual(receipt["status"], "refused") + self.assertEqual(receipt["failure"], "cancelled-forward") + self.assertEqual(self.host.stops, 0) + self.assertFalse(receipt["mutated"]) + def test_update_restores_complete_changed_executable_unit_and_data_after_late_failure(self): self.host.fail_at = 55 code, receipt = self.run_transaction("update", "v6.4.6") @@ -404,8 +413,6 @@ class DispatchLifecycleTest(unittest.TestCase): for uncertain in (False, True): with self.subTest(uncertain=uncertain), tempfile.TemporaryDirectory() as directory: root = Path(directory) / "state" - source = dispatcher.BOOTSTRAP.replace("pathlib.Path('/var/lib/pulse-deploy/demo')", repr(root)) - # repr(Path) is not a Python literal: bind just the fixed path. source = dispatcher.BOOTSTRAP.replace("'/var/lib/pulse-deploy/demo'", repr(str(root))) source = source.replace("root.stat().st_uid == 0", "root.stat().st_uid == os.getuid()") clock = [0] diff --git a/docs/release-control/v6/internal/subsystems/registry.json b/docs/release-control/v6/internal/subsystems/registry.json index 69c3b883c3..9cf02a0271 100644 --- a/docs/release-control/v6/internal/subsystems/registry.json +++ b/docs/release-control/v6/internal/subsystems/registry.json @@ -4751,8 +4751,6 @@ "match_files": [ ".github/ISSUE_TEMPLATE/v6_rc_feedback.yml", ".github/scripts/check-demo-reachability.sh", - ".github/scripts/demo-runtime-transaction.py", - ".github/scripts/dispatch-demo-runtime.py", ".github/scripts/recover-demo-runtime.sh", ".github/scripts/resolve-demo-runtime-profile.sh", ".github/scripts/setup-demo-ssh.sh", @@ -4813,13 +4811,11 @@ "allow_same_subsystem_tests": false, "test_prefixes": [], "exact_files": [ - ".github/scripts/tests/test_demo_runtime_transaction.py", "frontend-modern/src/components/__tests__/whatsNewModel.test.ts", "frontend-modern/src/utils/__tests__/docsLinks.test.ts", "pulse-enterprise:scripts/validate-pro-release-line_test.sh", "pulse-pro:scripts/tests/test_validate_paid_runtime_distribution.py", "scripts/installtests/build_release_assets_test.go", - "scripts/installtests/demo_transaction_test.go", "scripts/release_control/internal/record_rc_to_ga_rehearsal_test.py", "scripts/release_control/internal/release_preflight_test.py", "scripts/release_control/mobile_release_gate_test.py", @@ -5173,6 +5169,21 @@ "scripts/installtests/install_sh_test.go", "scripts/installtests/safe_profile_migration_test.go" ] + }, + { + "id": "demo-runtime-transaction", + "label": "demo shared-host transaction and retained recovery proof", + "match_prefixes": [], + "match_files": [ + ".github/scripts/demo-runtime-transaction.py", + ".github/scripts/dispatch-demo-runtime.py" + ], + "allow_same_subsystem_tests": false, + "test_prefixes": [], + "exact_files": [ + "scripts/installtests/demo_transaction_test.go", + ".github/scripts/tests/test_demo_runtime_transaction.py" + ] } ], "match_files": null diff --git a/scripts/installtests/demo_transaction_test.go b/scripts/installtests/demo_transaction_test.go index f53ed0abc0..ae800da558 100644 --- a/scripts/installtests/demo_transaction_test.go +++ b/scripts/installtests/demo_transaction_test.go @@ -25,8 +25,11 @@ func TestDemoTransactionKeepsSignedAdmissionAndVerificationOnlyReadOnly(t *testi } text := string(workflow) start := strings.Index(text, "- name: Apply guarded demo transaction") + if start < 0 { + t.Fatal("missing retained demo transaction entry") + } end := strings.Index(text[start:], "- name: Retain guarded demo transaction evidence") - if start < 0 || end < 0 { + if end < 0 { t.Fatal("missing retained demo transaction entry") } entry := text[start : start+end] diff --git a/scripts/release_control/subsystem_lookup_test.py b/scripts/release_control/subsystem_lookup_test.py index a4dc01c378..d77aece65c 100644 --- a/scripts/release_control/subsystem_lookup_test.py +++ b/scripts/release_control/subsystem_lookup_test.py @@ -100,6 +100,18 @@ def _contract_reference(contract_path: str, needle: str, runtime_path: str) -> d class SubsystemLookupTest(unittest.TestCase): + + def test_demo_transaction_sources_have_connected_proof_without_widening_release_metadata(self): + for path in (".github/scripts/demo-runtime-transaction.py", ".github/scripts/dispatch-demo-runtime.py"): + with self.subTest(path=path): + result = lookup_paths([path]) + subsystem = next(item for item in result["subsystems"] if item["id"] == "deployment-installability") + self.assertEqual(subsystem["matched_verification_policies"][0]["id"], "demo-runtime-transaction") + self.assertEqual(sorted(subsystem["required_verification"]["exact_files"]), [ + ".github/scripts/tests/test_demo_runtime_transaction.py", + "scripts/installtests/demo_transaction_test.go", + ]) + def git(self, repo_root: Path, *args: str) -> None: subprocess.run( ["git", *args],