diff --git a/CHANGELOG.md b/CHANGELOG.md index 5db40bb..3eed488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ All notable repository-level changes are recorded here. Application history befo - Cleared CI Ruff findings in CSP middleware and upgrade-archive version validation without changing runtime behavior. - Corrected source-release verification so revision versions such as `1.3.5.3` are not mistaken for global IPv4 literals while real global IP addresses remain blocked. +- Corrected release verification to treat changelog markdown headings (for example, `## 1.3.5.5 - ...`) as version labels instead of IPv4 candidates while continuing to block actual global IPv4 literals. - Made forensic inventory generation deterministic across Windows and Linux by canonicalizing text line endings, normalizing aggregate byte counts, and excluding ignored runtime state from source audits. - Advanced the pinned Django 5.2 LTS runtime from 5.2.16 to 5.2.17 after RC2 `pip-audit` identified the upstream security advisory, and synchronized active dependency, deployment verification, and security-test contracts without changing application behavior. - Renamed CSS parser token-kind locals to avoid Bandit B105 credential-name false positives; no Bandit rule, exclusion, sanitizer policy, or runtime behavior is weakened. diff --git a/MEMORY.md b/MEMORY.md index 5e23140..3011c3f 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -203,12 +203,29 @@ This memory file should be treated as the canonical context snapshot for future - Regenerated `documents/DOCUMENTATION_MANIFEST.json` after the Admin Guide workflow update so the repository forensic documentation gate sees current hashes and metadata. +### [2026-09-18] Documentation policy gate repair for release verification maintenance + +- CI `quality-and-security` failed because `scripts/verify_release.py` and its focused test changed + without the required maintained-document updates. +- Added synchronized maintenance entries in `CHANGELOG.md` and + `documents/phases/PHASE-004-RELEASE-UPGRADE-AND-OPERATIONAL-RELIABILITY.md` so the + documentation-policy gate records the changelog-heading false-positive correction in release + verification. + ### [2026-09-18] Forensic inventory CI synchronization - Regenerated `docs/FORENSIC_FILE_INVENTORY.json` with `scripts/generate_inventory.py` after the repository metadata changes made the generated inventory stale. - Validation: `generate_inventory.py --check` passed with 475 maintained files and 41,057 text lines. +### [2026-09-18] Documentation contracts inventory refresh + +- The `documentation-contracts` workflow failed at the inventory freshness gate because + `documents/DOCUMENTATION_MANIFEST.json` changed without a matching refresh of + `docs/FORENSIC_FILE_INVENTORY.json`. +- Regenerating the forensic inventory restored the documentation workflow and the repository + forensic audit to a passing state. + ### [2026-09-17] System Update production-readiness scope - Fixed the System Update page CSP mismatch by adding a per-response nonce to the page's inline script and existing inline styles. @@ -424,6 +441,10 @@ This memory file should be treated as the canonical context snapshot for future ### [2026-09-18] CI release verification false-positive correction +- GitHub Actions release run `35318392855` failed in `build-verified-source` because `scripts/verify_release.py` treated the changelog heading `## 1.3.5.4 - ...` as a global IPv4 literal. +- Updated `is_version_literal` to treat markdown changelog headings of the form `## - ...` as version literals, preserving global-IP blocking elsewhere. +- Extended `scripts/test_release_workflow.py` with a regression assertion for changelog heading version detection; targeted release workflow tests pass (8). + ### [2026-09-18] Permanent release-version IP-scan rule - Added a durable `AGENTS.md` contract: semantic release versions, including four-component values such as `1.3.5.3`, must never be treated as IP literals by forensic or release verification. Real global IP addresses must remain blocked and the exemption must stay regression-tested. diff --git a/docs/FORENSIC_FILE_INVENTORY.json b/docs/FORENSIC_FILE_INVENTORY.json index d00cf46..1518520 100644 --- a/docs/FORENSIC_FILE_INVENTORY.json +++ b/docs/FORENSIC_FILE_INVENTORY.json @@ -135,10 +135,10 @@ }, { "kind": "text", - "lines": 257, + "lines": 258, "path": "CHANGELOG.md", - "sha256": "a78ca3e9a35cb20a3a45b4391cec0636a2c8e181ee66e4f2615f295c01c2dcea", - "size_bytes": 23802 + "sha256": "8e4fcf1cabb187c7f0027b12b146c0eb705eaba9c55d43310a748beab5bd9c1a", + "size_bytes": 24006 }, { "kind": "text", @@ -170,10 +170,10 @@ }, { "kind": "text", - "lines": 463, + "lines": 484, "path": "MEMORY.md", - "sha256": "576d53ac667c3e25b56a6e50aa166e3a36c64da8f173693b179e8fa094495408", - "size_bytes": 34814 + "sha256": "ff593f9ed67d55bbfd296301cd78cd21109fbd48ccc0cd07506b1905f847e3c9", + "size_bytes": 36221 }, { "kind": "text", @@ -910,7 +910,7 @@ "kind": "text", "lines": 269, "path": "documents/DOCUMENTATION_MANIFEST.json", - "sha256": "e0bae5b3a4444a8a8ac3d09fbf8e88a768af0a31bce515b7f476e1c3536eeb72", + "sha256": "78945e290a073ccc46563a692390ac2fdebaba98130d23703246918067dce1cf", "size_bytes": 10580 }, { @@ -1020,10 +1020,10 @@ }, { "kind": "text", - "lines": 153, + "lines": 160, "path": "documents/phases/PHASE-004-RELEASE-UPGRADE-AND-OPERATIONAL-RELIABILITY.md", - "sha256": "407559ca84178cb96667f676472f9b575f2822f551d6185688019d7e7f35e7bc", - "size_bytes": 10063 + "sha256": "ad7cb03b349335b3f20f50862dbb93474be403efe43b387718e31f92c7ce8062", + "size_bytes": 10439 }, { "kind": "text", @@ -6065,7 +6065,7 @@ }, { "kind": "text", - "lines": 259, + "lines": 264, "path": "scripts/test_release_workflow.py", "python": { "classes": [], @@ -6093,8 +6093,8 @@ ], "methods": [] }, - "sha256": "87062aa985a025d5e1beba7de7c8148dd60abe571f318b27e590b2b24d2d0e66", - "size_bytes": 8288 + "sha256": "03134e56f6c46aa0232c3dec8bdd1e83f719398c12165229412ab80232da27c7", + "size_bytes": 8563 }, { "kind": "text", @@ -6207,7 +6207,7 @@ }, { "kind": "text", - "lines": 204, + "lines": 207, "path": "scripts/verify_release.py", "python": { "classes": [], @@ -6229,8 +6229,8 @@ ], "methods": [] }, - "sha256": "0f5c9770a83f5dab4266f0b53fae0152e8dc15fb486353be67fd245b090b2517", - "size_bytes": 8008 + "sha256": "848bb3ec8b9b4bcf9895a2f931de2121a4d553b74788de4e6babad666dd729c5", + "size_bytes": 8146 } ], "release_version": "1.3.5.5", @@ -6250,7 +6250,7 @@ "shell_files": 15, "shell_functions": 42, "text_files": 450, - "total_bytes": 32267088, - "total_text_lines": 41063 + "total_bytes": 32269488, + "total_text_lines": 41100 } } diff --git a/documents/DOCUMENTATION_MANIFEST.json b/documents/DOCUMENTATION_MANIFEST.json index d24b9c7..4218c8c 100644 --- a/documents/DOCUMENTATION_MANIFEST.json +++ b/documents/DOCUMENTATION_MANIFEST.json @@ -198,10 +198,10 @@ "audience": "users-operators-and-maintainers", "document_id": "phase-004-release-upgrade-and-operational-reliability", "document_type": "phase", - "last_reviewed": "2026-08-18", + "last_reviewed": "2026-09-18", "path": "documents/phases/PHASE-004-RELEASE-UPGRADE-AND-OPERATIONAL-RELIABILITY.md", "phase_id": "PHASE-004", - "sha256": "407559ca84178cb96667f676472f9b575f2822f551d6185688019d7e7f35e7bc", + "sha256": "ad7cb03b349335b3f20f50862dbb93474be403efe43b387718e31f92c7ce8062", "status": "active", "title": "Release, Upgrade and Operational Reliability", "version": "1.3.5.5" diff --git a/documents/phases/PHASE-004-RELEASE-UPGRADE-AND-OPERATIONAL-RELIABILITY.md b/documents/phases/PHASE-004-RELEASE-UPGRADE-AND-OPERATIONAL-RELIABILITY.md index 07352d8..15b0429 100644 --- a/documents/phases/PHASE-004-RELEASE-UPGRADE-AND-OPERATIONAL-RELIABILITY.md +++ b/documents/phases/PHASE-004-RELEASE-UPGRADE-AND-OPERATIONAL-RELIABILITY.md @@ -5,7 +5,7 @@ document_type: phase audience: users-operators-and-maintainers status: active version: 1.3.5.5 -last_reviewed: 2026-08-18 +last_reviewed: 2026-09-18 phase_id: PHASE-004 --- @@ -140,6 +140,13 @@ the deterministic source ZIP/checksum. The resulting immutable official baseline `517778967ca491974f315d231dfd43b3dba85fe86b47dcfc63e4c7051d1010bd`. Existing-VPS execution was intentionally deferred so PHASE-005A can be qualified first. +## 2026-09-18 release verification maintenance correction + +The release verification global-IPv4 guard now ignores changelog markdown heading prefixes such as +`## 1.3.5.5 - ...` and validates the extracted version token instead. This preserves the fail-closed +rule against real global IPv4 literals while preventing false positives from maintained changelog +section headings. + ## Documentation impact PHASE-004A updated the root/application changelogs, build/release examples, README status wording, diff --git a/scripts/test_release_workflow.py b/scripts/test_release_workflow.py index f604a15..f1a9eae 100644 --- a/scripts/test_release_workflow.py +++ b/scripts/test_release_workflow.py @@ -60,6 +60,11 @@ def test_release_version_is_not_global_ip() -> None: changelog_line = f"- Added release identity coverage for `{REVISION_VERSION}`.\n".encode() start = changelog_line.index(REVISION_VERSION.encode()) assert VERIFY.is_version_literal(changelog_line, start, start + len(REVISION_VERSION)) + changelog_heading = f"## {REVISION_VERSION} - Release hardening\n".encode() + heading_start = changelog_heading.index(REVISION_VERSION.encode()) + assert VERIFY.is_version_literal( + changelog_heading, heading_start, heading_start + len(REVISION_VERSION) + ) def test_tag_identity_and_manual_mode() -> None: diff --git a/scripts/verify_release.py b/scripts/verify_release.py index 8115bda..d2d32ed 100644 --- a/scripts/verify_release.py +++ b/scripts/verify_release.py @@ -61,6 +61,9 @@ def is_version_literal(data: bytes, start: int, end: int) -> bool: if line_end == -1: line_end = len(data) line = data[line_start:line_end].strip().lower() + candidate = data[start:end].lower() + if line.startswith(b"## ") and line[3:].split(b" - ", 1)[0] == candidate: + return True if any( marker in line for marker in (