From 9ad0f245020180e6419d9a68fa285db786da5d24 Mon Sep 17 00:00:00 2001 From: cyberdocs120 Date: Wed, 21 Oct 2026 12:37:33 +0100 Subject: [PATCH 1/3] chore: remove PR_INFO.md and block handoff artifacts in CI - Delete PR_INFO.md (one-off handoff artifact for #134) - Move durable note about pre-existing Stellar test failures into docs/CONTRIBUTING.md under a new 'Known pre-existing test failures' section so reviewers don't flag them as regressions - Add 'Ensure handoff artifacts are not tracked' step to the CI build job; blocks PR_INFO.md, PR_DESCRIPTION.md, PR_BODY.md, HANDOFF.md, HANDOFF_*.md and lowercase/hyphenated variants Closes #195 --- .github/workflows/ci.yml | 23 +++++++ PR_INFO.md | 145 --------------------------------------- docs/CONTRIBUTING.md | 7 ++ 3 files changed, 30 insertions(+), 145 deletions(-) delete mode 100644 PR_INFO.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e16102..df62e7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,29 @@ jobs: exit 1 fi + - name: Ensure handoff artifacts are not tracked + run: | + patterns=( + 'PR_INFO.md' + 'PR_DESCRIPTION.md' + 'PR_BODY.md' + 'HANDOFF.md' + 'HANDOFF_*.md' + 'pr-info.md' + 'pr_info.md' + 'pr-body.md' + 'pr_body.md' + ) + found=0 + for p in "${patterns[@]}"; do + matches=$(git ls-files "$p" 2>/dev/null) + if [ -n "$matches" ]; then + echo "::error::Handoff artifact must not be committed: $matches" + found=1 + fi + done + if [ "$found" -eq 1 ]; then exit 1; fi + serverless-smoke: name: Serverless Smoke if: github.event_name == 'pull_request' diff --git a/PR_INFO.md b/PR_INFO.md deleted file mode 100644 index 1ab5237..0000000 --- a/PR_INFO.md +++ /dev/null @@ -1,145 +0,0 @@ -# Pull Request Information - -## Summary - -Successfully completed issue #134: Added Portuguese (PT) locale and established translation contribution workflow. - -## PR Details - -**Title:** `feat(i18n): add Portuguese locale and translation contribution flow` - -**Base Branch:** `develop` (in wraith-protocol/www) -**Head Branch:** `feat/add-portuguese-locale` (in code3ks/www) - -**PR Body:** - -````markdown -## Summary - -This PR adds Portuguese (PT) as a third locale to the website and establishes a documented, repeatable translation contribution workflow. - -## Changes - -### Core Implementation - -- ✅ Added `src/i18n/pt.json` with complete Portuguese translations (241 keys) -- ✅ Extended `scripts/lint-i18n.mjs` to enforce parity across all locales - - Validates all locale files have identical key structures - - Fails CI on missing or extraneous keys - - Detects hardcoded strings in components -- ✅ Updated `src/i18n/index.ts` to register Portuguese locale -- ✅ Language switcher automatically picks up PT from locale list - -### Documentation - -- ✅ Created `TRANSLATIONS.md` at repo root documenting: - - How to add a new locale - - How to keep translations in sync - - What strings not to translate (brand names, code snippets) - - Review process and contributor recognition - -### Contributors - -- ✅ Created `src/data/team.json` for contributor credits - -## Testing - -### Locale Parity Validation - -```bash -pnpm run lint:i18n -``` -```` - -Output: - -``` -✅ All locales have matching key structures! -✅ No hardcoded JSX strings found in 14 component(s). -✅ All i18n checks passed! -``` - -### Build Verification - -```bash -pnpm run build -``` - -- ✅ TypeScript compilation passes -- ✅ Vite build successful -- ✅ All assets generated correctly - -### Automated Tests - -```bash -pnpm test -``` - -- ✅ All i18n-related tests pass -- ✅ Footer, NotFound, and A11y tests pass (11 total) -- ⚠️ 2 pre-existing Stellar page test failures (unrelated to i18n) - -## Screenshots - -The Portuguese locale is automatically available in the language switcher and can be selected by users with PT browser settings or manually via the header dropdown. - -## Acceptance Criteria - -- [x] Every page renders in EN, ES, PT with no missing-key fallbacks -- [x] CI parity gate fails on introduced drift -- [x] TRANSLATIONS.md covers the contribution loop end-to-end -- [x] Build passes -- [x] Lighthouse Perf/SEO remain high (no regressions) - -Closes #134 - -```` - -## How to Create the PR - -1. Go to: https://github.com/wraith-protocol/www/compare/develop...code3ks:www:feat/add-portuguese-locale - -2. Click "Create pull request" - -3. Fill in the title and description from above - -4. Submit the PR - -## Changes Made - -### Files Added -- `src/i18n/pt.json` - Complete Portuguese translations -- `TRANSLATIONS.md` - Translation contribution guide -- `src/data/team.json` - Contributor credits - -### Files Modified -- `src/i18n/index.ts` - Registered Portuguese locale -- `scripts/lint-i18n.mjs` - Enhanced to enforce locale parity - -## Verification Commands - -Run these to verify the implementation: - -```bash -# Check locale parity -pnpm run lint:i18n - -# Build the project -pnpm run build - -# Run tests -pnpm test -```` - -## Next Steps - -1. Create the PR on GitHub using the link above -2. Wait for CI to run and verify all checks pass -3. Request review from maintainers -4. Get native Portuguese speaker review (optional but recommended) -5. Address any feedback -6. Get PR merged - -## Issue Link - -Closes: https://github.com/wraith-protocol/www/issues/134 diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 9b454b0..ee8932c 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -140,6 +140,13 @@ The footer badge reads those values at build time and will automatically reflect - Trigger a manual incident or synthetic failure in the provider to verify the badge flips to yellow/red. - Confirm the badge links back to the public incident history and subscribe flow. +## Known pre-existing test failures + +The Stellar page (`/stellar`) currently has 2 failing unit tests that pre-date +the i18n work introduced in #134. They are unrelated to translations or locale +switching. Do not treat them as regressions when reviewing PRs — track them +under their own issue and fix them independently. + ## Commit conventions Commits follow [Conventional Commits](https://www.conventionalcommits.org/) From 7e41de6f7ec4cc6f5431f653e12c7b86b62ab9b4 Mon Sep 17 00:00:00 2001 From: cyberdocs120 Date: Thu, 22 Oct 2026 03:45:28 +0100 Subject: [PATCH 2/3] fix(ci): normalise handoff-artifact gate and remove stale Stellar failure note - Add missing lowercase patterns (handoff.md, handoff_*.md, pr-description.md, pr_description.md) so the gate blocks the full PR/handoff artifact family regardless of case or separator - Group HANDOFF and PR_DESCRIPTION pairs together for readability - Remove 'Known pre-existing test failures' section from CONTRIBUTING.md; the Stellar Unit Tests job is green on this head so the note was documenting failures that cannot be reproduced - Add tests/handoff-gate.test.sh: shell fixture that runs the exact gate logic against a throw-away git repo, covering 15 blocking variants and 5 negative cases (20/20 pass); exposed as pnpm test:gate --- .github/workflows/ci.yml | 8 +- docs/CONTRIBUTING.md | 7 -- package.json | 1 + tests/handoff-gate.test.sh | 153 +++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 9 deletions(-) create mode 100755 tests/handoff-gate.test.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df62e7b..4a0a8cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,13 +52,17 @@ jobs: - name: Ensure handoff artifacts are not tracked run: | patterns=( + 'HANDOFF.md' + 'HANDOFF_*.md' + 'handoff.md' + 'handoff_*.md' 'PR_INFO.md' 'PR_DESCRIPTION.md' 'PR_BODY.md' - 'HANDOFF.md' - 'HANDOFF_*.md' 'pr-info.md' 'pr_info.md' + 'pr-description.md' + 'pr_description.md' 'pr-body.md' 'pr_body.md' ) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index ee8932c..9b454b0 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -140,13 +140,6 @@ The footer badge reads those values at build time and will automatically reflect - Trigger a manual incident or synthetic failure in the provider to verify the badge flips to yellow/red. - Confirm the badge links back to the public incident history and subscribe flow. -## Known pre-existing test failures - -The Stellar page (`/stellar`) currently has 2 failing unit tests that pre-date -the i18n work introduced in #134. They are unrelated to translations or locale -switching. Do not treat them as regressions when reviewing PRs — track them -under their own issue and fix them independently. - ## Commit conventions Commits follow [Conventional Commits](https://www.conventionalcommits.org/) diff --git a/package.json b/package.json index a92f3dd..43e20ac 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "test:a11y": "vitest run src/__tests__/a11y.test.tsx", "test:a11y:playwright": "playwright test tests/a11y", "test:link-check": "vitest run tests/link-check", + "test:gate": "bash tests/handoff-gate.test.sh", "check:links": "tsx scripts/check-links.ts", "format": "prettier --write .", "format:check": "prettier --check .", diff --git a/tests/handoff-gate.test.sh b/tests/handoff-gate.test.sh new file mode 100755 index 0000000..6620ee3 --- /dev/null +++ b/tests/handoff-gate.test.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash +# Tests for the "Ensure handoff artifacts are not tracked" CI gate. +# +# Strategy: initialise a throw-away git repo in a temp directory, stage one +# artifact at a time, run the gate logic, and assert it exits non-zero. +# Then verify that the matching lowercase / hyphen / underscore variants that +# were historically missing also trigger the gate. + +set -euo pipefail + +PASS=0 +FAIL=0 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +# Colours (suppressed when not a TTY) +if [ -t 1 ]; then + GREEN='\033[0;32m'; RED='\033[0;31m'; RESET='\033[0m' +else + GREEN=''; RED=''; RESET='' +fi + +ok() { echo -e "${GREEN} PASS${RESET} $1"; PASS=$((PASS+1)); } +fail() { echo -e "${RED} FAIL${RESET} $1"; FAIL=$((FAIL+1)); } + +# Run the gate logic against a git repo that has $1 tracked. +# Returns the gate's exit code (0 = clean, 1 = blocked). +run_gate_with_file() { + local file="$1" + local tmpdir + tmpdir=$(mktemp -d) + # shellcheck disable=SC2064 + trap "rm -rf '$tmpdir'" RETURN + + git -C "$tmpdir" init -q + git -C "$tmpdir" config user.email "test@example.com" + git -C "$tmpdir" config user.name "Test" + + # Create parent dirs if the path contains a slash (subdirectory variant) + mkdir -p "$tmpdir/$(dirname "$file")" + echo "stub" > "$tmpdir/$file" + git -C "$tmpdir" add "$file" + + # Replicate the exact gate logic from ci.yml + ( + cd "$tmpdir" + patterns=( + 'HANDOFF.md' + 'HANDOFF_*.md' + 'handoff.md' + 'handoff_*.md' + 'PR_INFO.md' + 'PR_DESCRIPTION.md' + 'PR_BODY.md' + 'pr-info.md' + 'pr_info.md' + 'pr-description.md' + 'pr_description.md' + 'pr-body.md' + 'pr_body.md' + ) + found=0 + for p in "${patterns[@]}"; do + matches=$(git ls-files "$p" 2>/dev/null) + if [ -n "$matches" ]; then + found=1 + fi + done + exit "$found" + ) + return $? +} + +# Assert that staging $1 causes the gate to block (exit 1). +assert_blocked() { + local file="$1" + if run_gate_with_file "$file"; then + fail "gate should block '$file' but it passed" + else + ok "gate blocks '$file'" + fi +} + +# Assert that staging $1 does NOT trigger the gate (exit 0). +assert_allowed() { + local file="$1" + if run_gate_with_file "$file"; then + ok "gate allows '$file'" + else + fail "gate should allow '$file' but it blocked" + fi +} + +# --------------------------------------------------------------------------- +# HANDOFF family +# --------------------------------------------------------------------------- +echo +echo "--- HANDOFF family ---" +assert_blocked "HANDOFF.md" +assert_blocked "HANDOFF_v2.md" +assert_blocked "HANDOFF_final.md" +assert_blocked "handoff.md" +assert_blocked "handoff_v2.md" +assert_blocked "handoff_final.md" + +# --------------------------------------------------------------------------- +# PR_INFO / pr-info / pr_info family +# --------------------------------------------------------------------------- +echo +echo "--- PR_INFO family ---" +assert_blocked "PR_INFO.md" +assert_blocked "pr-info.md" +assert_blocked "pr_info.md" + +# --------------------------------------------------------------------------- +# PR_DESCRIPTION / pr-description / pr_description family +# --------------------------------------------------------------------------- +echo +echo "--- PR_DESCRIPTION family ---" +assert_blocked "PR_DESCRIPTION.md" +assert_blocked "pr-description.md" +assert_blocked "pr_description.md" + +# --------------------------------------------------------------------------- +# PR_BODY / pr-body / pr_body family +# --------------------------------------------------------------------------- +echo +echo "--- PR_BODY family ---" +assert_blocked "PR_BODY.md" +assert_blocked "pr-body.md" +assert_blocked "pr_body.md" + +# --------------------------------------------------------------------------- +# Negative cases — files that must NOT be blocked +# --------------------------------------------------------------------------- +echo +echo "--- Negative cases (should pass the gate) ---" +assert_allowed "README.md" +assert_allowed "docs/CONTRIBUTING.md" +assert_allowed "CHANGELOG.md" +assert_allowed "src/handoff-utils.ts" # not a .md at root +assert_allowed "notes/handoff-notes.txt" # wrong extension + +# --------------------------------------------------------------------------- +# Summary +# --------------------------------------------------------------------------- +echo +echo "Results: ${PASS} passed, ${FAIL} failed" +if [ "$FAIL" -gt 0 ]; then + exit 1 +fi From 5be5d1e5f1420e11e85054a272f49d9d1a0a763d Mon Sep 17 00:00:00 2001 From: cyberdocs120 Date: Thu, 22 Oct 2026 03:55:18 +0100 Subject: [PATCH 3/3] ci: retrigger after npm audit registry timeout