Repository navigation
Harden the hub, polish the UX, and add Builder medals - #63
Merged
Merged
Conversation
- ga.js configures only G-C5V0X2SGD3, passes page_location so GA keeps incoming campaigns, then drops utm_* from the address bar. - Links to the team's other sites on this domain get utm_source=hub&utm_medium=referral&utm_campaign=crosslink and the spot they were clicked from, open in the same tab unless a new tab was asked for, and send webbook_click or team_site_click. - Hub pages (game, lab, builders, badges) are not treated as team sites.
- Articles: markdown is rendered through DOMPurify; marked and DOMPurify are served from assets/vendor instead of a CDN. - scripts/check_data.py replaces the JSON syntax check in the deploy workflow: plain-token ids, integer counts, valid slugs, and only http(s) or site-relative links. - Builder pages: invalid slugs stop the build; every interpolated value is escaped; LinkedIn and record links must be http(s); no inline onerror on avatars. - Hub: data is cleaned on load (links, ids, counts), dates are escaped, stored streak values are read as numbers, and a malformed #station= link no longer stops the page from starting. - Content-Security-Policy meta on every page; Pretendard from the npm path with SRI. - GA config turns off Google signals and ad personalization; privacy copy matches what is sent. - Workflow: actions pinned to commits, checkout without persisted credentials, stats push with an explicit token; update_stats.py only calls api.github.com and drops Authorization on cross-host redirects. - Remove the empty .github/workflows/cd file.
- Same five-item nav and skip link on every page, with the current page marked - Quiz becomes the last goal chip; game and lab links move into the daily band - Picking a route hides the weekly block so the route shelf comes first - Add a marketing route and send marketing quiz answers to it - One tenure format for Builders across the hub and Builder pages - Builder dialog: '자료 열기' and a way back to the person - Upcoming Data Forum first, with a clear note when sign-up is not open yet - Stamp toast shows above an open dialog - Lab: challenge on top, sticky chart on small screens, notes open once solved - Sort chapters in the game review; small layout fixes
This was referenced Oct 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces #62, which was branched from an older
mainand conflicted with the sharedassets/js/ga.jsandassets/js/track.jsthat landed in #61. Rebuilt on currentmain; no conflicts.Four commits, reviewable one at a time.
1. GA4 and team-site links (5db79be)
ga.jsconfigures only the hub propertyG-C5V0X2SGD3.page_locationis passed so incoming campaigns are kept, thenutm_*is removed from the address bar. Google signals and ad personalization are off.utm_source=hub&utm_medium=referral&utm_campaign=crosslink&utm_content=<spot>, open in the same tab unless a new tab was asked for, and sendwebbook_clickorteam_site_click(site,placement,link_url). Hub pages (game, lab, builders, badges) are excluded.station_openfrom Use the hub's own GA4 property and track visits across team sites #62 is not added:open_repoalready covers it, withsource: "deeplink"for#station=links.Pairs with CausalInferenceLab/Causal-Inference-with-Python#91 on the web book side.
2. Security hardening (35ecb8c)
markedandDOMPurifyare vendored underassets/vendor/instead of loaded from a CDN.[a-z0-9]+(-[a-z0-9]+)*(a bad slug could write outsidebuilders/). Every value is escaped, links must be http(s), andonerrorattributes are replaced by a shared image fallback.data/*.jsonare cleaned on load. Values read back fromlocalStorageand#station=are parsed defensively.scripts/check_data.pyruns in the deploy workflow and stops a build on non-plain ids, non-integer counts, bad slugs, or links that are not http(s) or site paths.script-src 'self'+ Google Tag Manager,object-src 'none',base-uri 'self',form-action 'self'). Pretendard is pinned with SRI.persist-credentials: false, and the stats push uses the token explicitly.update_stats.pykeeps the token on api.github.com when following redirects. Removed the empty.github/workflows/cd.3. UX polish (b2a9d19)
4. Builder medals and academic events (abaeb8a)
TIERS/SPECIALSinhub.jsand the same lists inbuild_builders.py.build_builders.pyalso writesbadges/<slug>-medal.svg. Builder pages, README badges and the share images (assets/og/builders/*.jpg, regenerated) show the medal.data/stages.json.check_data.pyvalidates it.medal_card_open(builder,tier) andclick_conference(conference,placement).sharealso getscontent: "medal_card".Checked
img onerrorandjavascript:links, event dates, repo cohorts, Builder LinkedIn, stored streak,#station=%E0.check_data.pyrejects each of them.After merge
placement,site,chapter,control,tier,conference.CODEOWNERSonly applies if "Require review from Code Owners" is on in branch protection.main. Ifmainstarts requiring PRs, that push will fail.https://claude.ai/code/session_01HXKUXMwRWZBebGVTt4yncM
Generated by Claude Code