Restore returned demo recovery candidate for complete footprint repair

Retain the independently rejected distribution-state rollback evidence and repair the operation in this assigned worktree; canonical publication remains separate.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-03 02:46:21 +01:00
16 changed files with 1773 additions and 824 deletions
@@ -2306,7 +2306,11 @@ func TestUpdateDemoWorkflowUsesGovernedNetworkPath(t *testing.T) {
if err != nil {
t.Fatalf("read demo runtime profile resolver: %v", err)
}
workflow := string(workflowBytes) + "\n" + string(profileBytes)
transactionBytes, err := os.ReadFile(repoFile(".github", "scripts", "demo-runtime-transaction.py"))
if err != nil {
t.Fatal(err)
}
workflow := string(workflowBytes) + "\n" + string(profileBytes) + "\n" + string(transactionBytes)
required := []string{
`- name: Tailscale`,
`uses: tailscale/github-action@780049a30b6ff5c378a9e7b389d15ece7a204888 # v4.1.3`,
@@ -2319,7 +2323,6 @@ func TestUpdateDemoWorkflowUsesGovernedNetworkPath(t *testing.T) {
`workflow_call:`,
`verify_only:`,
`Waiting for activated release assets to be available`,
`bash /tmp/pulse-install.sh --version "$TAG"`,
`Refuse mutation during verification-only checks`,
`uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0`,
`go run ./scripts/release_update_key.go public-key-ssh`,
@@ -2327,15 +2330,10 @@ func TestUpdateDemoWorkflowUsesGovernedNetworkPath(t *testing.T) {
`Verify target host identity`,
`bash .github/scripts/setup-demo-ssh.sh`,
`Demo environment points at host $REMOTE_HOSTNAME but expected $DEMO_EXPECTED_HOSTNAME.`,
`Prepare demo host storage`,
`KEEP_BACKUPS=2`,
`Removing demo backup to restore install headroom: %s`,
`Pruning demo volatile runtime stores to restore install headroom.`,
`sudo find "$CONFIG_DIR" -xdev -type f`,
`-name "metrics.db"`,
`Removing demo volatile store: %s`,
`Demo host does not have enough free space to back up $CONFIG_DIR before install.`,
`Restore demo runtime configuration`,
`Apply guarded demo transaction`,
`python3 .github/scripts/dispatch-demo-runtime.py "$MODE"`,
`Retain guarded demo transaction evidence`,
`.forward.elapsed_seconds >= 300`,
`Resolve target-compatible demo runtime profile`,
`git grep -q 'mockEagerHistoryPVEGuestLimit'`,
`git grep -q 'UpdateMetricCohort'`,
@@ -2361,22 +2359,13 @@ func TestUpdateDemoWorkflowUsesGovernedNetworkPath(t *testing.T) {
`MOCK_SEED_DURATION=2h`,
`MOCK_SAMPLE_INTERVAL=5m`,
`MOCK_UPDATE_INTERVAL=15s`,
`resolve_config_dir`,
`set_env_value DEMO_MODE true`,
`set_env_value PULSE_MOCK_MODE true`,
`set_env_value PULSE_MOCK_NODES "$MOCK_NODES"`,
`set_env_value PULSE_MOCK_VMS_PER_NODE "$MOCK_VMS_PER_NODE"`,
`set_env_value PULSE_MOCK_LXCS_PER_NODE "$MOCK_LXCS_PER_NODE"`,
`set_env_value PULSE_MOCK_DOCKER_HOSTS "$MOCK_DOCKER_HOSTS"`,
`set_env_value PULSE_MOCK_K8S_PODS "$MOCK_K8S_PODS"`,
`set_env_value PULSE_MOCK_SEED_METRICS_STORE false`,
`set_env_value PULSE_MOCK_TRENDS_SEED_DURATION "$MOCK_SEED_DURATION"`,
`set_env_value PULSE_MOCK_TRENDS_SAMPLE_INTERVAL "$MOCK_SAMPLE_INTERVAL"`,
`set_env_value PULSE_MOCK_UPDATE_INTERVAL "$MOCK_UPDATE_INTERVAL"`,
`ensure_demo_fixture_entitlement`,
`"demo_fixtures"`,
`del(.integrity)`,
`Demo fixture entitlement ensured in governed demo billing state.`,
`WINDOW = 300`,
`/var/lib/pulse-deploy/relay/deploy.lock`,
`snapshot-headroom`,
`restored-estate-identity`,
`demo_fixtures`,
`PULSE_MOCK_SEED_METRICS_STORE`,
`installer-identity`,
`/api/license/runtime-capabilities`,
`Mock mode enabled`,
`Demo server mock mode did not enable after entitlement sync`,
@@ -0,0 +1,93 @@
package installtests
import (
"os"
"os/exec"
"strings"
"testing"
)
func TestDemoTransactionConnectedRecoveryControls(t *testing.T) {
cmd := exec.Command("python3", repoFile(".github", "scripts", "tests", "test_demo_runtime_transaction.py"))
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("demo connected transaction controls failed: %v\n%s", err, output)
}
if !strings.Contains(string(output), "\nOK\n") {
t.Fatalf("demo controls did not return a complete unittest verdict: %s", output)
}
}
func TestDemoTransactionKeepsSignedAdmissionAndVerificationOnlyReadOnly(t *testing.T) {
workflow, err := os.ReadFile(repoFile(".github", "workflows", "update-demo-server.yml"))
if err != nil {
t.Fatal(err)
}
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 end < 0 {
t.Fatal("missing retained demo transaction entry")
}
entry := text[start : start+end]
if !strings.Contains(entry, "if: inputs.verify_only != true") {
t.Fatal("verification-only invocation must never submit a transaction")
}
for _, forbidden := range []string{"Pruning demo volatile runtime stores", "Removing demo backup to restore install headroom", "sudo systemctl restart"} {
if strings.Contains(text, forbidden) {
t.Fatalf("unguarded demo mutation remains: %s", forbidden)
}
}
for _, required := range []string{"Require exact committed activation marker for mutation", "release-activation.json", "Stable demo mutation refuses mutable, inactive, or prerelease tag", "PINNED_RELEASE_SSH_PUBLIC_KEY", "git show \"refs/tags/${TAG}:install.sh\"", "Verify public browser smoke"} {
if !strings.Contains(text, required) {
t.Fatalf("lost signed/admission/customer verification boundary: %s", required)
}
}
recovery, err := os.ReadFile(repoFile(".github", "workflows", "recover-demo-server.yml"))
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(recovery), "sudo systemctl stop pulse") || strings.Contains(string(recovery), "Compensate failed mutated recovery") {
t.Fatal("later workflow failure must not stop a committed or unrelated service")
}
if !strings.Contains(string(recovery), ".forward.elapsed_seconds >= 300") || !strings.Contains(string(recovery), "if: always()") {
t.Fatal("recovery must validate and retain the actual terminal observation")
}
}
func TestDemoNativeAcceptanceIsSecretFreeDisposableAndKeepsRealWindows(t *testing.T) {
data, err := os.ReadFile(repoFile(".github", "workflows", "demo-runtime-native.yml"))
if err != nil {
t.Fatal(err)
}
text := string(data)
for _, required := range []string{
"pull_request:", "branches: [main]", "contents: read", "runs-on: ubuntu-24.04", "timeout-minutes: 35",
"persist-credentials: false", "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1",
"demo_runtime_native.py", "if: always()", "demo-native-result.json", "if-no-files-found: error",
} {
if !strings.Contains(text, required) {
t.Fatalf("lost disposable native CI/source or terminal-receipt boundary: %s", required)
}
}
for _, forbidden := range []string{"secrets.", "pull_request_target", "self-hosted", "contents: write", "id-token: write"} {
if strings.Contains(text, forbidden) {
t.Fatalf("native fixture gained credentials, privileged trigger or non-disposable target: %s", forbidden)
}
}
driver, err := os.ReadFile(repoFile(".github", "scripts", "tests", "demo_runtime_native.py"))
if err != nil {
t.Fatal(err)
}
for _, required := range []string{"not-an-empty-disposable-public-ci-runner", "dispatcher.BOOTSTRAP", "proc.terminate()", "--signal=TERM", "original_engine_sha256", "fixture_engine_sha256", "cleanup_complete", "signed_published_installer_acceptance", "elapsed_seconds\"] >= 300"} {
if !strings.Contains(string(driver), required) {
t.Fatalf("lost native fixture observation or limitation: %s", required)
}
}
if strings.Contains(string(driver), "WINDOW =") || strings.Contains(string(driver), "time.monotonic =") || strings.Contains(string(driver), "time.sleep =") {
t.Fatal("native acceptance must not shorten production windows or replace real clocks")
}
}
@@ -47,6 +47,32 @@ class RegistryAuditTest(unittest.TestCase):
args = parse_args(["--check", "--repo-scope", "pulse"])
self.assertEqual(args.repo_scope, ["pulse"])
def test_live_registry_path_lists_are_casefold_sorted(self) -> None:
# Fixture-only audit tests cannot catch ordering mistakes in the
# committed registry. Check its real path lists using the same
# case-insensitive contract as candidate preflight, not ASCII order.
registry = registry_audit.load_registry_payload()
for subsystem in registry["subsystems"]:
lists = {
field: subsystem[field]
for field in ("owned_prefixes", "owned_files")
}
verification = subsystem["verification"]
lists.update({
f"verification.{field}": verification[field]
for field in ("test_prefixes", "exact_files")
})
for policy in verification["path_policies"]:
lists.update({
f"{policy['id']}.{field}": policy[field]
for field in (
"match_prefixes", "match_files", "test_prefixes", "exact_files"
)
})
for field, paths in lists.items():
with self.subTest(subsystem=subsystem["id"], field=field):
self.assertEqual(paths, sorted(paths, key=str.casefold))
def test_trust_gate_regression_proofs_are_registered_to_exact_policies(self) -> None:
registry = registry_audit.load_registry_payload()
rules = {rule["id"]: rule for rule in registry["subsystems"]}
@@ -2589,7 +2589,9 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
self.assertIn("DEMO_EXPECTED_HOSTNAME", demo)
self.assertIn("Verify target host identity", demo)
self.assertIn("Demo environment points at host $REMOTE_HOSTNAME but expected $DEMO_EXPECTED_HOSTNAME.", demo)
self.assertIn("Restore demo runtime configuration", demo)
self.assertIn("Apply guarded demo transaction", demo)
self.assertIn("dispatch-demo-runtime.py", demo)
self.assertIn("Retain guarded demo transaction evidence", demo)
self.assertIn("Resolve target-compatible demo runtime profile", demo)
self.assertIn("mockEagerHistoryPVEGuestLimit", demo_profile)
self.assertIn("UpdateMetricCohort", demo_profile)
@@ -2608,19 +2610,17 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
self.assertIn("MOCK_SEED_DURATION=2h", demo_profile)
self.assertIn("MOCK_SAMPLE_INTERVAL=5m", demo_profile)
self.assertIn("MOCK_UPDATE_INTERVAL=15s", demo_profile)
self.assertIn("resolve_config_dir", demo)
self.assertIn("set_env_value DEMO_MODE true", demo)
self.assertIn("set_env_value PULSE_MOCK_MODE true", demo)
self.assertIn('set_env_value PULSE_MOCK_NODES "$MOCK_NODES"', demo)
self.assertIn("set_env_value PULSE_MOCK_SEED_METRICS_STORE false", demo)
self.assertIn('set_env_value PULSE_MOCK_TRENDS_SEED_DURATION "$MOCK_SEED_DURATION"', demo)
self.assertIn('set_env_value PULSE_MOCK_TRENDS_SAMPLE_INTERVAL "$MOCK_SAMPLE_INTERVAL"', demo)
self.assertIn('set_env_value PULSE_MOCK_UPDATE_INTERVAL "$MOCK_UPDATE_INTERVAL"', demo)
self.assertIn("ensure_demo_fixture_entitlement", demo)
self.assertIn('"demo_fixtures"', demo)
self.assertIn("del(.integrity)", demo)
self.assertIn("Demo fixture entitlement ensured in governed demo billing state.", demo)
self.assertIn("Demo service restarted with governed demo runtime configuration.", demo)
# Profile application moved into the owned transaction; keep its
# runtime settings and entitlement, not the superseded shell snippets.
transaction = read(".github/scripts/demo-runtime-transaction.py")
for required in ('"DEMO_MODE": "true"', '"PULSE_MOCK_MODE": "true"',
'"PULSE_MOCK_SEED_METRICS_STORE": "false"',
'"seed_duration": "trends_seed_duration"', '"sample_interval": "trends_sample_interval"',
'"PULSE_MOCK_" + key.upper()', '["demo_fixtures"]', 'billing.pop("integrity", None)',
'self.host.profile(self.request, not healthy_baseline)', 'self.host.start()'):
self.assertIn(required, transaction)
self.assertIn("MOCK_NODES: ${{ needs.resolve.outputs.mock_nodes }}", demo)
self.assertIn("MOCK_UPDATE_INTERVAL: ${{ needs.resolve.outputs.mock_update_interval }}", demo)
self.assertIn("/api/license/runtime-capabilities", demo)
self.assertIn("Mock mode enabled", demo)
self.assertIn("Demo server mock mode did not enable after entitlement sync", demo)
@@ -2749,73 +2749,42 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
def test_stable_demo_recovery_is_fixed_current_bits_only(self) -> None:
workflow = read(".github/workflows/recover-demo-server.yml")
recovery = read(".github/scripts/recover-demo-runtime.sh")
transaction = read(".github/scripts/demo-runtime-transaction.py")
dispatcher = read(".github/scripts/dispatch-demo-runtime.py")
self.assertIn("workflow_dispatch:", workflow)
self.assertNotIn("workflow_call:", workflow)
self.assertNotIn("inputs:", workflow)
self.assertIn("environment: demo-stable", workflow)
self.assertIn("contents: read", workflow)
self.assertIn("cancel-in-progress: false", workflow)
self.assertIn('gh api "repos/${GITHUB_REPOSITORY}/releases/latest"', workflow)
self.assertIn("Resolve target-compatible recovery profile", workflow)
self.assertIn("resolve-demo-runtime-profile.sh", workflow)
self.assertIn(".github/scripts/recover-demo-runtime.sh", workflow)
self.assertIn("Verify public health", workflow)
self.assertIn("Verify public frontend parity", workflow)
self.assertIn("./scripts/run_demo_public_browser_smoke.sh", workflow)
self.assertIn("Compensate failed mutated recovery", workflow)
self.assertIn("Capture bounded Pulse failure diagnostics", workflow)
self.assertIn("demo-runtime-diagnostics.txt", workflow)
self.assertIn("journalctl -u pulse --since '-30 minutes'", workflow)
self.assertIn("journalctl -u pulse --since '-10 minutes'", workflow)
self.assertIn("tail -400", workflow)
self.assertIn("sudo systemctl stop pulse", workflow)
self.assertIn("Retain bounded recovery evidence", workflow)
self.assertNotIn("release-convergence.yml", workflow)
self.assertNotIn("install.sh", workflow)
self.assertNotIn("scp ", workflow)
for required in ("workflow_dispatch:", "environment: demo-stable", "contents: read",
"cancel-in-progress: false", 'gh api "repos/${GITHUB_REPOSITORY}/releases/latest"',
"Resolve target-compatible recovery profile", "resolve-demo-runtime-profile.sh",
".github/scripts/recover-demo-runtime.sh", "Verify public health",
"Verify public frontend parity", "./scripts/run_demo_public_browser_smoke.sh",
"Capture bounded Pulse failure diagnostics", "demo-runtime-diagnostics.txt",
"journalctl -u pulse --since '-30 minutes'", "journalctl -u pulse --since '-10 minutes'",
"tail -400", "Retain bounded recovery evidence", ".forward.elapsed_seconds >= 300"):
self.assertIn(required, workflow)
for forbidden in ("workflow_call:", "inputs:", "release-convergence.yml", "install.sh", "scp ",
"Compensate failed mutated recovery", "sudo systemctl stop pulse"):
self.assertNotIn(forbidden, workflow)
self.assertIn('exec python3 .github/scripts/dispatch-demo-runtime.py recover', recovery)
self.assertIn("if mode == 'update' else b''", dispatcher)
self.assertIn("/var/lib/pulse-deploy/relay/deploy.lock", transaction)
self.assertIn('if self.request["mode"] == "update":', transaction)
self.assertIn('self.host.install(self.attempt, self.request)', transaction)
self.assertIn('raise Failure("recovery-source-mismatch")', transaction)
self.assertIn('copy_path(path, snapshot / label)', transaction)
self.assertIn('copy_path(snapshot / label, PATHS[label])', transaction)
self.assertIn('estate_hash(PATHS) != self.receipt["snapshot_sha256"]', transaction)
self.assertIn('WINDOW = 300', transaction)
self.assertIn('self.watch("recovery", original_version, cursors)', transaction)
self.assertIn('signal.signal(sig, signal.SIG_IGN)', transaction)
self.assertIn('self.save("rolled_back")', transaction)
self.assertIn('self.save("rollback_failed")', transaction)
for required in ("alerts/events.db", "alerts/alert-history.json.imported",
"alerts/alert-history.backup.json.imported", "ai_incidents.json", "snapshot_retained"):
self.assertIn(required, transaction)
self.assertNotIn('kill -QUIT', transaction + recovery + workflow)
self.assertNotIn('eval ', transaction + recovery)
self.assertNotIn("'--wait'", dispatcher)
self.assertIn('if [ "$#" -ne 16 ]', recovery)
self.assertIn('SERVICE_NAME="pulse"', recovery)
self.assertIn('RELAY_SERVICE_NAME="pulse-relay"', recovery)
self.assertIn('EXPECTED_BINARY="/opt/pulse/bin/pulse"', recovery)
self.assertIn('EXPECTED_UNIT="/etc/systemd/system/pulse.service"', recovery)
self.assertIn('set_env_value PULSE_MOCK_NODES "$MOCK_NODES"', recovery)
self.assertIn('set_env_value PULSE_MOCK_VMS_PER_NODE "$MOCK_VMS_PER_NODE"', recovery)
self.assertIn('set_env_value PULSE_MOCK_LXCS_PER_NODE "$MOCK_LXCS_PER_NODE"', recovery)
self.assertIn('set_env_value PULSE_MOCK_DOCKER_HOSTS "$MOCK_DOCKER_HOSTS"', recovery)
self.assertIn('set_env_value PULSE_MOCK_DOCKER_CONTAINERS "$MOCK_DOCKER_CONTAINERS"', recovery)
self.assertIn('set_env_value PULSE_MOCK_GENERIC_HOSTS "$MOCK_GENERIC_HOSTS"', recovery)
self.assertIn('set_env_value PULSE_MOCK_K8S_CLUSTERS "$MOCK_K8S_CLUSTERS"', recovery)
self.assertIn('set_env_value PULSE_MOCK_K8S_NODES "$MOCK_K8S_NODES"', recovery)
self.assertIn('set_env_value PULSE_MOCK_K8S_PODS "$MOCK_K8S_PODS"', recovery)
self.assertIn('set_env_value PULSE_MOCK_K8S_DEPLOYMENTS "$MOCK_K8S_DEPLOYMENTS"', recovery)
self.assertIn("set_env_value PULSE_MOCK_SEED_METRICS_STORE false", recovery)
self.assertIn('grep -Fxq "PULSE_MOCK_SEED_METRICS_STORE=false"', recovery)
self.assertIn('set_env_value PULSE_MOCK_TRENDS_SEED_DURATION "$MOCK_SEED_DURATION"', recovery)
self.assertIn('set_env_value PULSE_MOCK_TRENDS_SAMPLE_INTERVAL "$MOCK_SAMPLE_INTERVAL"', recovery)
self.assertIn('set_env_value PULSE_MOCK_UPDATE_INTERVAL "$MOCK_UPDATE_INTERVAL"', recovery)
self.assertIn('sudo systemctl restart "$SERVICE_NAME"', recovery)
self.assertIn('sudo systemctl stop "$SERVICE_NAME"', recovery)
self.assertIn('sudo kill -QUIT "$failed_pid"', recovery)
self.assertIn("clear_demo_operational_history", recovery)
self.assertIn("restore_demo_operational_history", recovery)
self.assertIn("alerts/events.db", recovery)
self.assertIn("alerts/alert-history.json.imported", recovery)
self.assertIn("alerts/alert-history.backup.json.imported", recovery)
self.assertIn("ai_incidents.json", recovery)
self.assertIn("demo-operational-history.tar", recovery)
self.assertNotIn('rm -f "/etc/pulse/', recovery)
self.assertIn('AFTER_RELAY_PID" = "$BEFORE_RELAY_PID', recovery)
self.assertIn('AFTER_BINARY_SHA" = "$BEFORE_BINARY_SHA', recovery)
self.assertIn('AFTER_UNIT_SHA" = "$BEFORE_UNIT_SHA', recovery)
self.assertIn('AFTER_DROPINS_SHA" = "$BEFORE_DROPINS_SHA', recovery)
self.assertNotIn('AFTER_CONFIG_SHA" = "$BEFORE_CONFIG_SHA', recovery)
self.assertIn("/etc/pulse/.env /etc/pulse/billing.json", recovery)
self.assertIn("restore_runtime_config", recovery)
self.assertIn("sudo cp --archive", recovery)
self.assertNotIn("eval ", recovery)
self.assertNotIn("sudo bash", recovery)
def test_blocked_record_tracks_current_target_and_candidate_version(self) -> None:
blocked_record_surface = {
@@ -100,6 +100,21 @@ 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",
".github/scripts/tests/demo_runtime_native.py", ".github/workflows/demo-runtime-native.yml"):
with self.subTest(path=path):
result = lookup_paths([path])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual({item["subsystem"] for item in result["impacted_subsystems"]}, {"deployment-installability"})
requirement = result["files"][0]["matches"][0]["verification_requirement"]
self.assertEqual(requirement["id"], "demo-runtime-transaction")
self.assertEqual(sorted(requirement["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],