diff --git a/.github/workflows/canonical-governance.yml b/.github/workflows/canonical-governance.yml index 51446d8d4..4f6e6e197 100644 --- a/.github/workflows/canonical-governance.yml +++ b/.github/workflows/canonical-governance.yml @@ -86,6 +86,19 @@ jobs: cache: true cache-dependency-path: repos/pulse/go.sum + - name: Set up Node.js for frontend governance + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: '20' + cache: npm + cache-dependency-path: repos/pulse/frontend-modern/package-lock.json + + - name: Install locked frontend governance dependencies + working-directory: repos/pulse/frontend-modern + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' + run: npm ci --ignore-scripts --no-audit --no-fund + - name: Stub embedded frontend assets for Go tests run: bash scripts/ensure_test_assets.sh diff --git a/docs/release-control/v6/internal/CANONICAL_DEVELOPMENT_PROTOCOL.md b/docs/release-control/v6/internal/CANONICAL_DEVELOPMENT_PROTOCOL.md index e3ae2f067..cb206464c 100644 --- a/docs/release-control/v6/internal/CANONICAL_DEVELOPMENT_PROTOCOL.md +++ b/docs/release-control/v6/internal/CANONICAL_DEVELOPMENT_PROTOCOL.md @@ -253,6 +253,14 @@ print a non-passing receipt skeleton with `python3 scripts/release_control/browser_verification_guard.py --print-template` after staging the intended frontend paths. +A source diff that is byte-for-byte the locked Prettier output of its parent +has no rendered behavior or visual delta and does not require a new browser +receipt. The exemption fails closed when the formatter is unavailable or the +formatted output differs by even one token. Canonical Governance must install +the lockfile-pinned frontend formatter before evaluating changed commits so a +clean runner applies the same deterministic exemption as the commit-time +guard; its guard tests must run with that formatter present rather than skip. + ## Reporter Test Image Validation Path When a reviewed fix needs live confirmation from one or a few Docker-based diff --git a/internal/repoctl/canonical_development_protocol_test.go b/internal/repoctl/canonical_development_protocol_test.go index c788a993b..1e13050f8 100644 --- a/internal/repoctl/canonical_development_protocol_test.go +++ b/internal/repoctl/canonical_development_protocol_test.go @@ -1863,6 +1863,10 @@ func TestCanonicalGovernanceRunsInCI(t *testing.T) { "repository: rcourtman/pulse-pro", "repository: rcourtman/pulse-enterprise", "repository: rcourtman/pulse-mobile", + "Set up Node.js for frontend governance", + "cache-dependency-path: repos/pulse/frontend-modern/package-lock.json", + "Install locked frontend governance dependencies", + "npm ci --ignore-scripts --no-audit --no-fund", "PULSE_REPO_ROOT_PULSE_PRO", "PULSE_REPO_ROOT_PULSE_ENTERPRISE", "PULSE_REPO_ROOT_PULSE_MOBILE", @@ -1885,6 +1889,12 @@ func TestCanonicalGovernanceRunsInCI(t *testing.T) { "python3 scripts/release_control/status_audit_test.py", "python3 scripts/release_control/subsystem_lookup_test.py", }) + + install := strings.Index(workflow, "Install locked frontend governance dependencies") + guard := strings.Index(workflow, "Run canonical completion guard against changed commits") + if install < 0 || guard < 0 || install > guard { + t.Fatal("locked frontend governance dependencies must be installed before the per-commit browser guard") + } } func TestLegacyReleaseControlOrchestratorIsRemoved(t *testing.T) {