diff --git a/docs/releases/RELEASE_NOTES_v6.4.0-rc.10.md b/docs/releases/RELEASE_NOTES_v6.4.0-rc.10.md index 787773811..0d03b22fc 100644 --- a/docs/releases/RELEASE_NOTES_v6.4.0-rc.10.md +++ b/docs/releases/RELEASE_NOTES_v6.4.0-rc.10.md @@ -8,7 +8,7 @@ - **Better notification control** - Alerts can be snoozed individually, maintenance can recur by scope, escalation repeats can target specific destinations, delivery routes can filter by severity, and informational events remain distinct from warnings. - **Earlier capacity warnings** - Rolling metric windows and predictive storage forecasts surface sustained pressure and likely exhaustion before a single threshold breach becomes an outage. - **More accurate host disk health** - SMART sector, media, endurance, spare, and CRC thresholds can be tuned per host, while empty Unraid slots remain neutral and Proxmox-linked agents avoid duplicate disk-risk alerts. -- **External availability monitoring** - Dead-man checks can notify when an expected external signal stops arriving, with configuration and recovery state persisted. +- **External monitoring for Pulse itself** - Pulse can send a health signal to a Healthchecks-compatible watchdog on another host, with configuration and recovery state persisted. - **Clearer infrastructure details** - Resource drawers, Proxmox backup views, and alert timelines present more complete and consistent context across desktop and narrow layouts. - **Safer governed actions** - Docker action results now carry independently observed post-action state, while deployment enrollment and credential changes persist atomically. diff --git a/docs/releases/RELEASE_NOTES_v6.4.0-rc.11.md b/docs/releases/RELEASE_NOTES_v6.4.0-rc.11.md index 60011582d..46260b3ab 100644 --- a/docs/releases/RELEASE_NOTES_v6.4.0-rc.11.md +++ b/docs/releases/RELEASE_NOTES_v6.4.0-rc.11.md @@ -13,7 +13,7 @@ - **Better notification control** - Alerts can be snoozed individually, maintenance can recur by scope, escalation repeats can target specific destinations, delivery routes can filter by severity, and informational events remain distinct from warnings. - **Earlier capacity warnings** - Rolling metric windows and predictive storage forecasts surface sustained pressure and likely exhaustion before a single threshold breach becomes an outage. - **More accurate host disk health** - SMART sector, media, endurance, spare, and CRC thresholds can be tuned per host, while empty Unraid slots remain neutral and Proxmox-linked agents avoid duplicate disk-risk alerts. -- **External availability monitoring** - Dead-man checks can notify when an expected external signal stops arriving, with configuration and recovery state persisted. +- **External monitoring for Pulse itself** - Pulse can send a health signal to a Healthchecks-compatible watchdog on another host, with configuration and recovery state persisted. - **Clearer infrastructure details** - Resource drawers, backup views, and alert timelines present fuller context, while phone Settings use a searchable index, sticky section title, compact controls, and full-size touch targets. - **Safer governed actions** - Docker action results now carry independently observed post-action state, while deployment enrollment and credential changes persist atomically. diff --git a/docs/releases/RELEASE_NOTES_v6.4.0-rc.12.md b/docs/releases/RELEASE_NOTES_v6.4.0-rc.12.md index db02d5d24..8b71aeb71 100644 --- a/docs/releases/RELEASE_NOTES_v6.4.0-rc.12.md +++ b/docs/releases/RELEASE_NOTES_v6.4.0-rc.12.md @@ -17,7 +17,7 @@ - **Better notification control** - Alerts can be snoozed individually, maintenance can recur by scope, escalation repeats can target specific destinations, delivery routes can filter by severity, and informational events remain distinct from warnings. - **Earlier capacity warnings** - Rolling metric windows and predictive storage forecasts surface sustained pressure and likely exhaustion before a single threshold breach becomes an outage. - **More accurate host disk health** - SMART sector, media, endurance, spare, and CRC thresholds can be tuned per host, while empty Unraid slots remain neutral and Proxmox-linked agents avoid duplicate disk-risk alerts. -- **External availability monitoring** - Dead-man checks can notify when an expected external signal stops arriving, with configuration and recovery state persisted. +- **External monitoring for Pulse itself** - Pulse can send a health signal to a Healthchecks-compatible watchdog on another host, with configuration and recovery state persisted. - **Clearer infrastructure details** - Resource drawers, Proxmox backup views, and alert timelines present more complete and consistent context across desktop and narrow layouts. - **Phone-friendly Settings** - On narrow layouts, phone Settings use a searchable index, sticky section title, compact controls, and full-size touch targets. - **Safer governed actions** - Docker action results carry independently observed post-action state, while deployment enrollment and credential changes persist atomically. diff --git a/docs/releases/RELEASE_NOTES_v6.4.0-rc.13.md b/docs/releases/RELEASE_NOTES_v6.4.0-rc.13.md index 25e223069..614259a18 100644 --- a/docs/releases/RELEASE_NOTES_v6.4.0-rc.13.md +++ b/docs/releases/RELEASE_NOTES_v6.4.0-rc.13.md @@ -16,7 +16,7 @@ - **Better notification control** - Alerts can be snoozed individually, maintenance can recur by scope, escalation repeats can target specific destinations, delivery routes can filter by severity, and informational events remain distinct from warnings. - **Earlier capacity warnings** - Rolling metric windows and predictive storage forecasts surface sustained pressure and likely exhaustion before a single threshold breach becomes an outage. - **More accurate host disk health** - SMART sector, media, endurance, spare, and CRC thresholds can be tuned per host, while empty Unraid slots remain neutral and Proxmox-linked agents avoid duplicate disk-risk alerts. -- **External availability monitoring** - Dead-man checks can notify when an expected external signal stops arriving, with configuration and recovery state persisted. +- **External monitoring for Pulse itself** - Pulse can send a health signal to a Healthchecks-compatible watchdog on another host, with configuration and recovery state persisted. - **Clearer infrastructure details** - Resource drawers, Proxmox backup views, and alert timelines present more complete and consistent context across desktop and narrow layouts. - **Phone-friendly Settings** - On narrow layouts, phone Settings use a searchable index, sticky section title, compact controls, and full-size touch targets. - **Safer governed actions** - Docker action results carry independently observed post-action state, while deployment enrollment and credential changes persist atomically. diff --git a/docs/releases/RELEASE_NOTES_v6.4.0-rc.7.md b/docs/releases/RELEASE_NOTES_v6.4.0-rc.7.md index 1a89b21b3..a7a89386f 100644 --- a/docs/releases/RELEASE_NOTES_v6.4.0-rc.7.md +++ b/docs/releases/RELEASE_NOTES_v6.4.0-rc.7.md @@ -8,7 +8,7 @@ - **Better notification control** - Alerts can be snoozed individually, maintenance can recur by scope, escalation repeats can target specific destinations, and delivery routes can filter by severity. - **Earlier capacity warnings** - Rolling metric windows and predictive storage forecasts surface sustained pressure and likely exhaustion before a single threshold breach becomes an outage. - **Host disk policies** - SMART sector, media, endurance, spare, and CRC thresholds can be tuned per host while Proxmox-linked agents continue to avoid duplicate disk-risk alerts. -- **External availability monitoring** - Dead-man checks can notify when an expected external signal stops arriving, with configuration and recovery state persisted. +- **External monitoring for Pulse itself** - Pulse can send a health signal to a Healthchecks-compatible watchdog on another host, with configuration and recovery state persisted. - **Clearer infrastructure details** - Resource drawers, Proxmox backup views, and alert timelines present more complete and consistent context across desktop and narrow layouts. - **Safer governed actions** - Docker action results now carry independently observed post-action state, while deployment enrollment and credential changes persist atomically. diff --git a/docs/releases/RELEASE_NOTES_v6.4.0-rc.8.md b/docs/releases/RELEASE_NOTES_v6.4.0-rc.8.md index 4f42b4d69..d51d9105b 100644 --- a/docs/releases/RELEASE_NOTES_v6.4.0-rc.8.md +++ b/docs/releases/RELEASE_NOTES_v6.4.0-rc.8.md @@ -8,7 +8,7 @@ - **Better notification control** - Alerts can be snoozed individually, maintenance can recur by scope, escalation repeats can target specific destinations, delivery routes can filter by severity, and informational events remain distinct from warnings. - **Earlier capacity warnings** - Rolling metric windows and predictive storage forecasts surface sustained pressure and likely exhaustion before a single threshold breach becomes an outage. - **Host disk policies** - SMART sector, media, endurance, spare, and CRC thresholds can be tuned per host while Proxmox-linked agents continue to avoid duplicate disk-risk alerts. -- **External availability monitoring** - Dead-man checks can notify when an expected external signal stops arriving, with configuration and recovery state persisted. +- **External monitoring for Pulse itself** - Pulse can send a health signal to a Healthchecks-compatible watchdog on another host, with configuration and recovery state persisted. - **Clearer infrastructure details** - Resource drawers, Proxmox backup views, and alert timelines present more complete and consistent context across desktop and narrow layouts. - **Safer governed actions** - Docker action results now carry independently observed post-action state, while deployment enrollment and credential changes persist atomically. diff --git a/docs/releases/RELEASE_NOTES_v6.4.0-rc.9.md b/docs/releases/RELEASE_NOTES_v6.4.0-rc.9.md index 0c3df85d1..f90e163b4 100644 --- a/docs/releases/RELEASE_NOTES_v6.4.0-rc.9.md +++ b/docs/releases/RELEASE_NOTES_v6.4.0-rc.9.md @@ -8,7 +8,7 @@ - **Better notification control** - Alerts can be snoozed individually, maintenance can recur by scope, escalation repeats can target specific destinations, delivery routes can filter by severity, and informational events remain distinct from warnings. - **Earlier capacity warnings** - Rolling metric windows and predictive storage forecasts surface sustained pressure and likely exhaustion before a single threshold breach becomes an outage. - **More accurate host disk health** - SMART sector, media, endurance, spare, and CRC thresholds can be tuned per host, while empty Unraid slots remain neutral and Proxmox-linked agents avoid duplicate disk-risk alerts. -- **External availability monitoring** - Dead-man checks can notify when an expected external signal stops arriving, with configuration and recovery state persisted. +- **External monitoring for Pulse itself** - Pulse can send a health signal to a Healthchecks-compatible watchdog on another host, with configuration and recovery state persisted. - **Clearer infrastructure details** - Resource drawers, Proxmox backup views, and alert timelines present more complete and consistent context across desktop and narrow layouts. - **Safer governed actions** - Docker action results now carry independently observed post-action state, while deployment enrollment and credential changes persist atomically. diff --git a/scripts/check_public_docs.py b/scripts/check_public_docs.py index 3e7b091e7..be2bc98d7 100644 --- a/scripts/check_public_docs.py +++ b/scripts/check_public_docs.py @@ -32,10 +32,18 @@ MARKDOWN_LINK = re.compile(r"!?\[[^\]]*\]\(([^)]+)\)") HTML_ASSET = re.compile(r"(?:src|href)=[\"']([^\"']+)[\"']") HEADING = re.compile(r"^#{1,6}\s+(.+?)\s*#*\s*$", re.MULTILINE) +MISLEADING_PUBLIC_CLAIMS = { + r"dead-man checks can notify when an expected external signal stops arriving": ( + "dead-man direction is reversed; Pulse sends its own health signal to an " + "external watchdog" + ), +} + def public_markdown_files() -> list[Path]: files = [ROOT / name for name in ROOT_DOCS if (ROOT / name).exists()] files.extend(sorted((ROOT / "docs").glob("*.md"))) + files.extend(sorted((ROOT / "docs" / "releases").glob("*.md"))) files.extend(sorted((ROOT / "docs" / "i18n").glob("**/*.md"))) files.extend(sorted((ROOT / ".github" / "ISSUE_TEMPLATE").glob("*.md"))) return files @@ -110,6 +118,19 @@ def check_links(files: list[Path]) -> list[str]: return errors +def check_public_claims(files: list[Path]) -> list[str]: + """Reject product claims whose direction changes the advertised capability.""" + + errors: list[str] = [] + for path in files: + text = path.read_text(encoding="utf-8", errors="replace") + display_path = path.relative_to(ROOT) + for pattern, label in MISLEADING_PUBLIC_CLAIMS.items(): + if re.search(pattern, text, flags=re.IGNORECASE): + errors.append(f"{display_path}: {label}") + return errors + + def check_current_claims() -> list[str]: errors: list[str] = [] retired_patterns = { @@ -152,6 +173,7 @@ def check_current_claims() -> list[str]: def main() -> int: files = public_markdown_files() errors = check_links(files) + errors.extend(check_public_claims(files)) errors.extend(check_current_claims()) if errors: diff --git a/scripts/tests/test_public_docs_claims.py b/scripts/tests/test_public_docs_claims.py new file mode 100644 index 000000000..1dfaeb9e1 --- /dev/null +++ b/scripts/tests/test_public_docs_claims.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""Guard public capability claims that can reverse the product contract.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path +import sys +import tempfile +import unittest +from unittest import mock + + +ROOT = Path(__file__).resolve().parents[2] +SCRIPT = ROOT / "scripts" / "check_public_docs.py" +SPEC = importlib.util.spec_from_file_location("check_public_docs", SCRIPT) +assert SPEC is not None and SPEC.loader is not None +public_docs = importlib.util.module_from_spec(SPEC) +sys.modules[SPEC.name] = public_docs +SPEC.loader.exec_module(public_docs) + + +class PublicDocsClaimsTest(unittest.TestCase): + def test_release_notes_are_in_the_public_claim_surface(self) -> None: + files = public_docs.public_markdown_files() + + self.assertIn(ROOT / "docs/releases/RELEASE_NOTES_v6.4.0-rc.7.md", files) + + def test_rejects_reversed_dead_man_direction(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + note = root / "note.md" + note.write_text( + "Dead-man checks can notify when an expected external signal " + "stops arriving.\n", + encoding="utf-8", + ) + with mock.patch.object(public_docs, "ROOT", root): + errors = public_docs.check_public_claims([note]) + + self.assertEqual(len(errors), 1) + self.assertIn("dead-man direction is reversed", errors[0]) + + def test_accepts_outbound_watchdog_description(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + note = root / "note.md" + note.write_text( + "Pulse sends its own health signal to a watchdog on another host.\n", + encoding="utf-8", + ) + with mock.patch.object(public_docs, "ROOT", root): + errors = public_docs.check_public_claims([note]) + + self.assertEqual(errors, []) + + +if __name__ == "__main__": + unittest.main()