Skip to content

Fix giscus like button not persisting first reaction (#1312) - #58

Open
soyalejolopez wants to merge 17 commits into
masterfrom
soyalejolopez-fix-report-like-button
Open

Fix giscus like button not persisting first reaction (#1312)#58
soyalejolopez wants to merge 17 commits into
masterfrom
soyalejolopez-fix-report-like-button

Conversation

@soyalejolopez

Copy link
Copy Markdown
Owner

Problem

On a resource detail page (e.g. #purview-audit-copilot-report), clicking the giscus 👍 "like" made it vanish and not stick — it only appeared after a manual page refresh.

Root cause: The like is the embedded giscus reaction, mapped to a GitHub Discussion in the "Resource Votes" category. On resources with no backing Discussion yet, the first reaction hits giscus bug #1312: giscus creates the discussion and records the reaction server-side, but the client never refetches, so the 👍 visually reverts.

There was already a workaround, but it was self-defeating: the remount timer re-checked giscusState.hasDiscussion, which flips true the instant the discussion is created — cancelling the very remount meant to make the like stick.

Fix

All changes are in the giscus block of index.html:

  • Event-driven remount — track whether a discussion existed at mount time (existedAtMount); when emit-metadata reports the discussion was just created by the user's reaction, remount giscus once to pull the persisted 👍.
  • Fixed the blur-timer fallback — removed the counterproductive hasDiscussion re-check from the fired callback so the remount actually fires; delay bumped 2500ms → 3000ms so the server write settles first.
  • Re-arm safeguard — if a speculative (blur-triggered) refetch lands and there's still no discussion (e.g. the user focused the widget to sign in rather than react), refetched is cleared so a later genuine first reaction can still recover. Guarded by a speculativeRefetch flag so genuine reactions stay single-remount (no loops, no double-remount).

Validation

  • Loaded the page over a local HTTP server: zero console errors, giscus mounts, detail view renders.
  • Reviewed by GPT 5.6 Sol: first pass caught the wasted-refetch edge case; after the re-arm fix → PASS, no remaining issues.

Net change: 1 file changed, 35 insertions(+), 4 deletions(-).

soyalejolopez and others added 6 commits July 22, 2026 11:50
The card upvote count was baked into a static resource-stats.json that is
only regenerated by a daily CI job (cron 0 6 * * *), so a fresh reaction
would not show up for up to ~24h. Combined with the fact that only one
resource currently has a backing discussion, the counter was misleading.

This removes the upvote count *display* from both the comfortable and
compact card views (and the compact 'Votes' header column + orphaned CSS).
Voting itself is unchanged: the giscus-backed 'Community & upvotes' panel
on the detail page still works, and the upvotes value is still loaded in
getStats and used by the Popular/Trending/Engaging sort logic.


Copilot-Session: 888b1353-6309-4a90-bb0f-9b7b716a32dc

Co-authored-by: soyalejolopez <soyalejolopez@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The Engaging sort used a smoothed upvotes-per-view rate, which buried high-traffic resources so a resource with many views and a single reaction never surfaced. Rank by raw reaction/upvote count (views as tiebreaker, then name) so reactions drive the sort as expected.

Also count all GitHub Discussion reaction types (not just THUMBS_UP) when computing upvotes in build-stats.js, so any reaction registers.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 8ee4765a-3082-46e1-8848-c29193ffd00f

Co-authored-by: soyalejolopez <88358406+soyalejolopez@users.noreply.github.com>
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 7.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](actions/setup-node@v4...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](actions/checkout@v4...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…microsoft#487)

* chore(deps): bump js-yaml from 4.3.0 to 5.2.1 in /tools/catalog-build

Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.3.0 to 5.2.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](nodeca/js-yaml@4.3.0...5.2.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 5.2.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(catalog-build): use namespace import for js-yaml v5

js-yaml v5 is ESM-only and no longer ships a default export, so `import yaml from 'js-yaml'` throws at load time and breaks `npm run check`. Switch to a namespace import; yaml.load and yaml.JSON_SCHEMA remain available as named exports.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alejandro Lopez <alejanl@microsoft.com>
The report detail page embeds giscus for likes. On resources with no backing Discussion yet, the first reaction hit giscus bug #1312: giscus creates the discussion and adds the reaction server-side but never refetches, so the like visually reverts until a manual refresh.

Fix the existing workaround so the remount reliably fires:

- Event-driven remount: track whether a discussion existed at mount time and remount once when metadata reports it was just created by the user's reaction.

- Remove the self-defeating hasDiscussion re-check from the blur-timer callback (it cancelled the very remount meant to persist the like).

- Re-arm recovery when a speculative (blur-triggered) refetch finds no discussion, so a later genuine first reaction can still stick.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 61593e5a-6c5f-46eb-bb43-08ee66fce301
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

🛰️ PR Sweeper report

Risk: 🟠 MEDIUM · Security gate: ✅ passing · Files: 9

🔒 Automated guardrails (authoritative)

Category Location Finding
⚠️ scope Touches security-sensitive path(s): .github/workflows/build-catalog.yml, .github/workflows/content-spring-cleaning.yml, .github/workflows/traffic-stats.yml, .github/workflows/update-message-center.yml. Maintainer + security review required.
ℹ️ scope Change(s) outside known content roots: index.html.

🤖 Dual-model AI review (advisory)

AI review unavailable for this run (models not reachable or no diff). Guardrails above are unaffected.

  • GPT-class reviewer (openai/gpt-5): skipped — GitHub Models HTTP 413: {"error":{"code":"tokens_limit_reached","message":"Request body too large for gpt-5 model. Max size: 4000 tokens.","details":"Request body too large for gpt-5 model. Max size: 4000 tokens."}}
  • Deep-reasoning reviewer (openai/o3): skipped — GitHub Models HTTP 413: {"error":{"code":"tokens_limit_reached","message":"Request body too large for o3 model. Max size: 4000 tokens.","details":"Request body too large for o3 model. Max size: 4000 tokens."}}

The automated guardrails are authoritative and gate the security status. The AI review is advisory and never auto-merges. Thanks for contributing to FastTrack! 🛩️

@github-actions github-actions Bot added sweeper:ai-reviewed PR Sweeper: dual-model AI review attached sweeper:risk-low PR Sweeper: low risk sweeper:scope-review PR Sweeper: scope/sensitive-path review labels Jul 23, 2026
soyalejolopez and others added 4 commits July 23, 2026 16:59
…ft#498)

The report detail page embeds giscus for likes. On resources with no backing Discussion yet, the first reaction hit giscus bug #1312: giscus creates the discussion and adds the reaction server-side but never refetches, so the like visually reverts until a manual refresh.

Fix the existing workaround so the remount reliably fires:

- Event-driven remount: track whether a discussion existed at mount time and remount once when metadata reports it was just created by the user's reaction.

- Remove the self-defeating hasDiscussion re-check from the blur-timer callback (it cancelled the very remount meant to persist the like).

- Re-arm recovery when a speculative (blur-triggered) refetch finds no discussion, so a later genuine first reaction can still stick.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Co-authored-by: soyalejolopez <88358406+soyalejolopez@users.noreply.github.com>
Copilot-Session: 61593e5a-6c5f-46eb-bb43-08ee66fce301
Gate remount recovery on whether the discussion has reactions instead of
whether it exists. A resource whose Discussion already exists with 0
reactions (a #1312 ghost created by an earlier reverted first-reaction)
now arms the remount so the first reaction sticks, matching the brand-new
discussion path. Active discussions (reactionCount > 0) are left untouched.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 61593e5a-6c5f-46eb-bb43-08ee66fce301
Gate remount recovery on whether the discussion has reactions instead of
whether it exists. A resource whose Discussion already exists with 0
reactions (a #1312 ghost created by an earlier reverted first-reaction)
now arms the remount so the first reaction sticks, matching the brand-new
discussion path. Active discussions (reactionCount > 0) are left untouched.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 61593e5a-6c5f-46eb-bb43-08ee66fce301
@soyalejolopez
soyalejolopez force-pushed the soyalejolopez-fix-report-like-button branch from bb2b122 to fd75c5d Compare July 27, 2026 14:00
soyalejolopez and others added 5 commits July 27, 2026 09:17
microsoft#500)

Gate remount recovery on whether the discussion has reactions instead of
whether it exists. A resource whose Discussion already exists with 0
reactions (a #1312 ghost created by an earlier reverted first-reaction)
now arms the remount so the first reaction sticks, matching the brand-new
discussion path. Active discussions (reactionCount > 0) are left untouched.

Co-authored-by: soyalejolopez <88358406+soyalejolopez@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 61593e5a-6c5f-46eb-bb43-08ee66fce301
…per (microsoft#502)

* Surface the real error when traffic collection fails

The Collect Traffic Data workflow has failed every day since 7/6 with no
usable diagnostic. Two bugs conspired to hide the cause:

- gh_api_retry sent gh's stderr to /dev/null, so the retry warnings said
  "attempt N/3 failed" and nothing else.
- fetch_required_json echoed its ::error:: to stdout while running inside
  a command substitution, so the error text was captured into the caller's
  variable instead of reaching the log.

Now the real gh stderr is preserved and included in the retry warning,
errors route to stderr and return instead of exiting from a subshell, and
an empty TRAFFIC_TOKEN fails immediately with a message naming the secret
and the administration:read scope it needs.

Also skips the pointless sleep after the final attempt.

The underlying failure is an expired TRAFFIC_TOKEN. This does not fix
that, but it makes the next failure legible in one glance.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 68fa07a3-7ce8-442f-a9e8-f6b1775d8a1f

* Stop the message center refresh dying on the Actions PR policy

Every weekly run since 7/20 has pushed its branch successfully and then
failed on gh pr create with:

  GitHub Actions is not permitted to create or approve pull requests

That is a repo setting, not a bug in the run, and it should not throw away
a branch that already landed. The step now recognises that specific error,
emits a warning plus a job summary with a compare URL, and exits 0. Any
other create failure still fails the job.

Two supporting fixes:

- --force-with-lease was failing with "stale info" because actions/checkout
  only fetches the default branch, so refs/remotes/origin/<branch> never
  existed on a same-day rerun. The ref is now seeded before the push.
- GH_TOKEN falls back to AUTOMATION_PAT when present, which sidesteps the
  policy entirely for repos that add one.

Also adds set -euo pipefail and drops an || true that was masking gh pr
list failures.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 68fa07a3-7ce8-442f-a9e8-f6b1775d8a1f

* Route catalog rebuilds through a PR when master is protected

Half the recent Build catalog runs failed with:

  remote: error: GH006: Protected branch update failed for refs/heads/master
  - Changes must be made through a pull request.

The publish job pushes catalog.json, resource-stats.json,
resource-discussions.json and traffic-data/clarity-views.json directly to
master. Branch protection now forbids that, so the rebuild has been
silently not landing. The runs that appeared to pass only did so through
the "no changes" early exit.

The step now escalates: direct push, then pull --rebase and retry for a
plain race, then fall back to a catalog-refresh/<date>-<sha> branch with a
PR and auto-merge. If the repo also blocks Actions from opening PRs, it
warns with a compare URL and exits 0 rather than failing.

Errors are no longer swallowed. gh pr list dropped its || true, an
unconfirmed PR after a successful branch push is a hard failure, and a
failed auto-merge is a loud warning with the PR URL.

The PR number is parsed from gh pr create's output URL rather than by
re-listing, because listing immediately after creation races GitHub's PR
index and can report a phantom failure.

No retrigger loop: all four generated files sit outside this workflow's
paths filters, so merging the catalog PR will not start another run.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 68fa07a3-7ce8-442f-a9e8-f6b1775d8a1f

* Give the PR sweeper time to resolve a fork PR number

Three runs on 7/22 aborted with "Could not resolve a trusted PR number
from the workflow_run event". Fork PRs leave
workflow_run.pull_requests[0].number empty, so the job falls back to
looking the PR up by head SHA, and that lookup ran exactly once.

PR microsoft#495 was created at 16:49:09Z and the report job queried at 16:49:37Z.
Twenty-eight seconds. GitHub's commit-to-PR association index had not
propagated yet; the same SHA resolves fine now. An earlier run the same
afternoon hit the same empty value and resolved fine, which is what a
propagation race looks like.

The lookup now retries with backoff at 0, 3, 8 and 15 seconds and logs why
each attempt came back empty.

If it still cannot resolve, the job no longer exits 1. It warns, still
posts the commit status, and returns. This does not weaken the gate:
setStatus only needs the trusted head SHA, and the deterministic
guardrails remain the authoritative merge check. Only the sticky comment
is lost, and a red X on a healthy PR is worse than a missing comment.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 68fa07a3-7ce8-442f-a9e8-f6b1775d8a1f

---------

Co-authored-by: soyalejolopez <88358406+soyalejolopez@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 68fa07a3-7ce8-442f-a9e8-f6b1775d8a1f
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
The per-resource giscus remount workaround for bug #1312 was itself
destroying reactions. It remounted the iframe (container.innerHTML='')
the instant a first reaction created the backing Discussion, at
reactionCount 0, while the addReaction mutation was still in flight,
aborting the commit before it landed. Every discussion created after the
workaround shipped ended up with zero reactions; only a resource reacted
to before it shipped kept its like.

giscus persists reactions natively server-side, so remove the remount
apparatus entirely: the emit-metadata listener, the blur speculative
refetch, giscusClearPending, the data-emit-metadata attribute, and the
isRefetch path. mountGiscus now just mounts. The residual #1312 in-session
visual revert is cosmetic; the reaction is saved and correct on reload.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 61593e5a-6c5f-46eb-bb43-08ee66fce301
@github-actions github-actions Bot added sweeper:risk-medium PR Sweeper: medium risk and removed sweeper:risk-low PR Sweeper: low risk sweeper:ai-reviewed PR Sweeper: dual-model AI review attached labels Jul 27, 2026
soyalejolopez and others added 2 commits July 27, 2026 14:24
The per-resource giscus remount workaround for bug #1312 was itself
destroying reactions. It remounted the iframe (container.innerHTML='')
the instant a first reaction created the backing Discussion, at
reactionCount 0, while the addReaction mutation was still in flight,
aborting the commit before it landed. Every discussion created after the
workaround shipped ended up with zero reactions; only a resource reacted
to before it shipped kept its like.

giscus persists reactions natively server-side, so remove the remount
apparatus entirely: the emit-metadata listener, the blur speculative
refetch, giscusClearPending, the data-emit-metadata attribute, and the
isRefetch path. mountGiscus now just mounts. The residual #1312 in-session
visual revert is cosmetic; the reaction is saved and correct on reload.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 61593e5a-6c5f-46eb-bb43-08ee66fce301
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sweeper:risk-medium PR Sweeper: medium risk sweeper:scope-review PR Sweeper: scope/sensitive-path review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants