diff --git a/scripts/check-changelog-invariants.sh b/scripts/check-changelog-invariants.sh index 48b73ddd..fab7e531 100644 --- a/scripts/check-changelog-invariants.sh +++ b/scripts/check-changelog-invariants.sh @@ -522,8 +522,10 @@ grep -q "🔩 Internal / build plumbing" <<<"$out" || err "28: 'Changelog-Major: # sentence addressed to whoever tracks the fork graph, shown to someone deciding # whether their handheld stutters less. Fork/maintainer handles stay in the # commit body and the technical changelog; they never reach the hero, on either -# tier. Host types and protocols (Artemis, Apollo, Sunshine, Moonlight) are a -# real user-facing choice and must NOT be caught by the same rule. +# tier -- and the handle is REWRITTEN to "upstream" wherever it does appear, so +# the name is nowhere on the release page while the changelog entry survives. +# Host types and protocols (Artemis, Apollo, Sunshine, Moonlight) are a real +# user-facing choice and must NOT be caught by the same rule. mkrepo commit "fix(vrr): adopt the upstream active-wait bound" \ "Changelog: Adopt Nonary VRR10 active-wait fix: remove the fixed yield-count limit (4096)" @@ -532,10 +534,12 @@ commit "feat: show the host type on each computer card" \ out=$(gen 0.1.0-beta.002) awk '/^## 🚧/{exit} {print}' <<<"$out" | grep -qi "nonary" \ && err "29: an upstream fork attribution reached the hero (the 0.5.0 Nonary bullet)" -grep -qi "nonary" <<<"$out" || err "29: the attribution was dropped entirely — the technical changelog must keep it" +grep -qi "nonary" <<<"$out" && err "29: a fork handle survived somewhere on the release page" +grep -q "VRR10 active-wait fix" <<<"$out" \ + || err "29: the entry was DELETED rather than rewritten — the changelog must keep the change, minus the name" awk '/^## 🚧/{exit} {print}' <<<"$out" | grep -q "Apollo, Sunshine or Artemis" \ || err "29: the attribution filter is over-broad — host types are a user-facing choice, not fork provenance" -[ "$fail" = "$t0" ] && ok "fork/maintainer attribution stays out of the hero; host types are untouched" +[ "$fail" = "$t0" ] && ok "fork handles are rewritten out of the whole page, entries survive, host types untouched" # --- 30. the stable BODY shape: hero first and uncollapsed, everything technical -- # plumbing included -- inside one
that is properly closed. An unclosed @@ -554,6 +558,26 @@ det_line=$(grep -n "Full technical changelog" <<<"$out" | head -1 | cut -d: -f1) grep -q "🔩 Internal / build plumbing" <<<"$out" || err "30: plumbing vanished instead of moving inside the collapsed block" [ "$fail" = "$t0" ] && ok "stable body: uncollapsed hero, one closed
holding the full changelog and plumbing" +# --- 31. the scrub reaches the COLLAPSED PLUMBING block too, and reads as English on +# both sides of the one wrinkle: a subject that already says "upstream" must not +# come out as "...with upstream VRR10 upstream", and one that doesn't must gain +# the word rather than losing the sense ("Guard VRR features" reads as if we +# guard our own). All four spellings below are real 0.5.0 subjects. +mkrepo +commit "chore(vrr): eliminate all divergences with Nonary VRR10 upstream" +commit "test(vrr): restore Nonary latch tests, ratify trade-off" +commit "build: guard Nonary VRR features behind version checks" +commit "ci: port the gamescope hook from moonlight-qt" +out=$(gen 0.1.0-beta.002) +grep -qi "nonary\|moonlight-qt" <<<"$out" && err "31: a handle survived inside the collapsed plumbing block" +grep -qF -- "- Eliminate all divergences with VRR10 upstream (" <<<"$out" \ + || err "31: a subject that already says 'upstream' was doubled up instead of just dropping the handle" +grep -qF -- "- Restore upstream latch tests, ratify trade-off (" <<<"$out" \ + || err "31: the handle was deleted where the sentence needed the word 'upstream' to still make sense" +grep -qF -- "- Guard upstream VRR features behind version checks (" <<<"$out" || err "31: scrub mangled a plumbing subject" +grep -qF -- "- Port the gamescope hook from upstream (" <<<"$out" || err "31: a hyphenated handle (moonlight-qt) was not scrubbed cleanly" +[ "$fail" = "$t0" ] && ok "handles are scrubbed inside the plumbing block and each rewrite reads as English" + cd "$ROOT" if [ "$fail" = 0 ]; then echo "changelog invariants: OK" diff --git a/scripts/gen-changelog.sh b/scripts/gen-changelog.sh index d64538fd..5c90259b 100644 --- a/scripts/gen-changelog.sh +++ b/scripts/gen-changelog.sh @@ -85,11 +85,33 @@ is_stable_cut() { # $1 = version -> 0 if this cut is a production release # 0.5.0's hero opened its second bullet with "Adopt Nonary VRR10 active-wait fix: remove # the fixed yield-count limit (4096)..." -- a sentence addressed to whoever tracks the # fork graph, published to people who wanted to know if their handheld stutters less. -# The attribution belongs in the commit body and the technical changelog, both of which -# keep it; the hero is the one place it does not belong. Deliberately narrow: only fork -# and maintainer handles. "Artemis", "Apollo", "Sunshine" and "Moonlight" are host types -# and protocols a user genuinely picks between, so they are NOT listed here. -HERO_ATTRIBUTION_RE='(^|[^[:alnum:]])(nonary|wjbeckett|cgutman|moonlight-qt)([^[:alnum:]]|$)' +# +# Deliberately narrow: fork and maintainer HANDLES only. "Artemis", "Apollo", "Sunshine" +# and "Moonlight" are host types and protocols a user genuinely picks between, so they +# are NOT listed here and must never be. +ATTRIBUTION_HANDLES='nonary|wjbeckett|cgutman|moonlight-qt' +HERO_ATTRIBUTION_RE="(^|[^[:alnum:]])(${ATTRIBUTION_HANDLES})([^[:alnum:]]|\$)" + +# A handle is REWRITTEN, not deleted, and everywhere -- hero, technical sections and the +# collapsed plumbing block alike. Deleting the bullet would lose a real changelog entry; +# leaving the handle in the technical section still puts a fork maintainer's name on the +# release page, which is what was asked to stop. "Upstream" says the same thing about +# where the change came from without naming anyone. +# +# The one wrinkle worth the code: a subject that ALREADY says "upstream" ("Eliminate all +# divergences with Nonary VRR10 upstream") would come out as "...with upstream VRR10 +# upstream". So substitute the word in only when the sentence does not already carry it, +# and simply drop the handle when it does. Runs BEFORE sentence_case, so a handle at the +# start of a subject still yields a capitalized bullet. +scrub_attribution() { + local s="$1" repl='upstream ' + if printf '%s' "$s" | grep -qiE '(^|[^[:alnum:]])upstream([^[:alnum:]]|$)'; then + repl='' + fi + printf '%s' "$s" \ + | sed -E "s/(^|[^[:alnum:]-])(${ATTRIBUTION_HANDLES})([^[:alnum:]-]|\$)/\1${repl}\3/gI" \ + | sed -E 's/ +/ /g; s/ +([,.;:])/\1/g; s/^[[:space:]]+//; s/[[:space:]]+$//' +} # Only a real version tag may anchor a range. tier_rank() calls anything without a # pre-release infix "stable" (rank 4), which is right for `0.4.3` and catastrophically @@ -323,11 +345,11 @@ while IFS='|' read -r hash subject; do if [ "$force_internal" = true ] \ || { [ "$force_user_facing" != true ] \ && { printf '%s' "$subject" | grep -qiE "$INTERNAL_SUBJECT_RE" || ! ships "$hash"; }; }; then - INTERNAL="${INTERNAL}- $(md_escape "$(sentence_case "$(strip_md_structure "$clean")")") (\`${hash}\`)"$'\n' + INTERNAL="${INTERNAL}- $(md_escape "$(sentence_case "$(scrub_attribution "$(strip_md_structure "$clean")")")") (\`${hash}\`)"$'\n' continue fi - entry=$(md_escape "$(sentence_case "$(strip_md_structure "${note:-$clean}")")") + entry=$(md_escape "$(sentence_case "$(scrub_attribution "$(strip_md_structure "${note:-$clean}")")")") # Classified ONCE, and read by both the hero gate and the technical routing below. # These used to be one if/elif chain at the bottom; the stable hero needs to know @@ -341,8 +363,12 @@ while IFS='|' read -r hash subject; do # ── Does this note earn a place in the hero? ───────────────────────────────── hero=true + # Tested against the RAW note, not $entry: $entry has already been through + # scrub_attribution, so the handle is gone and this would never match. A note that + # NEEDED scrubbing is provenance-flavoured writing, which is the signal being read + # here -- the scrub neutralises the name, this keeps the sentence out of the hero. if printf '%s' "$note" | grep -qiE "$HERO_ATTRIBUTION_RE"; then - # Fork provenance, not user-facing news. Kept in the technical changelog. + # Fork provenance, not user-facing news. Kept, rewritten, in the technical changelog. hero=false echo "Note: hero bullet from ${hash} names an upstream fork/maintainer; technical section only." >&2 elif [ "$STABLE_CUT" = true ] && [ "$kind" != feature ] && [ "$is_major" != true ]; then