Repository navigation
Release 0.86.5 - #4272
Merged
Merged
Release 0.86.5#4272
Conversation
…roMasters id no letter (#4198) * Return a null program letter generate URL for certificates without a MicroMasters id /api/v0/program_certificates/ fails with NoReverseMatch for any learner holding a certificate whose micromasters_program_id is null, because program-letter-intercept only accepts an integer program id. Those are the DEDP Public Policy track and the DEDP umbrella program, which only ever ran on MITx Online and so have no MicroMasters program. In production that is 292 certificates across 282 learners, and each of them loses every letter link on the dashboard to the 500 (Sentry MIT-LEARN-4VG). program_letter_generate_url is now null for those certificates, and nullable in the v0 schema. program_letter_share_url does not depend on the MicroMasters id and is still returned. Fixes MIT-LEARN-4VG Claude-Session: https://claude.ai/code/session_01FGZebHWZHphHig5GqMDJnd * Give certificates without a MicroMasters id no program letter The previous commit stopped the 500, which exposes a second problem. A certificate with a null micromasters_program_id (DEDP Public Policy and the DEDP umbrella program) still got a share URL, and the letter page looks up its content in the MicroMasters CMS by that id. requests drops a None param, so the CMS returns every ProgramPage and the letter renders the first one: another program's letter under the learner's name. Until the content source for those programs is decided, they get no letter. ProgramCertificate.has_program_letter is the rule. The certificate list returns a null program_letter_share_url and creates no ProgramLetter for them, and the v1 program letter endpoint 404s for letters already issued against them. The share URL becomes nullable in the v0 schema, and both nullable letter URLs are allowlisted for oasdiff. Claude-Session: https://claude.ai/code/session_01FGZebHWZHphHig5GqMDJnd * Test that a certificate with no letter shows no Program Letter item useProgramLetterMenuItem already returns null for a missing share URL, so a null one from the API needs no code change. This covers it so the behavior holds if the hook changes. Claude-Session: https://claude.ai/code/session_01FGZebHWZHphHig5GqMDJnd * Say which certificates the share URL comment covers Claude-Session: https://claude.ai/code/session_01FGZebHWZHphHig5GqMDJnd * Put the revoked check in has_program_letter ProgramLetterViewSet checked is_revoked in get_object and the missing MicroMasters id in its queryset, so the rule for who gets a letter was split across two places. has_program_letter now covers both and get_object is the only check. Claude-Session: https://claude.ai/code/session_01FGZebDFdzPzFjrHohNavU5
… spacing (#4238) * Apply the card design tweaks to the redesigned resource card Titles clamp at two lines and reserve no height, so a lone card is only as tall as its title while the results grid and carousels still stretch each card to the tallest beside it. Metadata flows in a row, stacking only in a narrow (<=240px) card. The card takes a list of actions, and passes the learning-path action alongside the bookmark for learning-path editors, as the legacy card does. A vertical card stacks them as 28px circles down the image's corner; a horizontal card lines them up as bare 18px icons at the end of the metadata row. Every button is a 32px box whose negative margin lays it out at its drawn size, so the targets grow without moving anything; a vertical button's circle is its background clipped to the content box. A horizontal card with actions but no metadata keeps that row's height free, so a long title or parent-course line stops above the icons. The price panel no longer hosts a button, so its first-line reserve and floor go, and it centres its lines with flex. Search rows move their spacing from the results container's `ul > li + li` rule, which outranked PlainList's `itemSpacing` and made CardGrid undo it, onto a ResultsList: 16px apart (24px below `sm`) with the redesign flag on, 8px as before with it off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show a resource already in a learning path by shape, not only color On horizontal cards the learning-path action told its two states apart only by color: the same menu-add icon in dark gray or red, which fails WCAG 1.4.1 for color-blind users. Remix Icon's menu-add has no distinct filled form (RiMenuAddFill renders identically), and has no checked form, so MenuCheckIcon draws one: menu-add's three lines with the plus replaced by the check from list-check-3, at the same stroke weight. The card uses it when the resource is in a learning path, in both layouts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Add an Openly Licensed Content section to the Terms of Service Adds the CC BY-NC-SA 4.0 summary and the permitted-use-for-AI-training terms as a source-neutral "Openly Licensed Content" section with an #open-license anchor, and points the content-rights and Trademarks sections to it. Refs mitodl/hq#13892 Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ainer build (#4259) - python-tests sets PYTEST_ADDOPTS=--no-cov and jest runs with --coverage=false. Neither report had a consumer: the tokenless Codecov uploads (Python and JS) were rejected on main and on PR branches ("Token required because branch is protected") while the step reported success, and Codecov's newest mit-learn data is from 2024-03. Remove both upload steps and codecov.yml. - Move `manage.py check`, the two migration checks and the OpenAPI spec check into a python-checks job, one step each, so they run beside pytest instead of ahead of it. collectstatic stays in python-tests as its own step. - Delete the scripts/test/python_tests.sh wrapper and the unused scripts/test/test_suite.sh (it called Compose v1's `docker-compose`); CI calls pytest and the leaf check scripts directly. - build-nextjs-container no longer needs javascript-tests. It shares no artifacts with it; the Docker build does its own install and `next build`. - ci-gate requires python-checks. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
REDIS_VIEW_CACHE_DURATION defaulted to 24h, but the clear-views-cache beat task wiped every cached view (a delete_pattern SCAN over the Redis keyspace shared with the Celery broker) every hour, so the hourly clear was the real lifetime and the TTL only mattered if beat stopped. Set the TTL default to 1h and drop the beat entry and its task. Staleness stays bounded at an hour, and Redis expires keys on its own instead of a SCAN. RedBeat removes static entries absent from beat_schedule when beat starts, so the old clear-views-cache entry is cleaned up on deploy. CLEAR_VIEWS_CACHE_SCHEDULE_SECONDS is no longer read; no environment set it. QA sets REDIS_VIEW_CACHE_DURATION=0 and is unaffected. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Anchor the login and logout URL patterns The ^login and ^logout patterns were unanchored, so any path starting with either (e.g. /loginfoo, /login/foo) reached the login or logout view. The gateway only requires authentication on /login (and /login/* in deployed environments, but only /login in the local APISIX config), so a request on another variant could reach CustomLoginView as an anonymous user, which then redirected them to `next` without logging them in. This anchors both patterns to an optional trailing slash, and sends an anonymous user on any path other than LOGIN_URL to LOGIN_URL with their query string so the gateway authenticates them. Claude-Session: https://claude.ai/code/session_01FGZebE9MVPhw6MmBqPQzhG * Check that every login param survives the redirect to LOGIN_URL Claude-Session: https://claude.ai/code/session_01FGZebE9MVPhw6MmBqPQzhG
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…4247) * Say analytics are on their way for an org or contract not loaded yet A manager of a new organization or contract passes MITx Online's manager check right away, but the warehouse the analytics API reads only picks the org or contract up on its next load. Until then the API's contract gate answered 403, which the query client throws to the error boundary as a generic failure. The analytics API now answers that case with a 404 whose detail.code is organization_not_loaded or contract_not_loaded (mitodl/ol-analytics-api, branch b2b-data-not-loaded). This reads the code and shows a notice saying the data will be available once it has been loaded, on the analytics page and on the contract's learners page. Any other 404 still reads as a failed section. Claude-Session: https://claude.ai/code/session_01FGZebDGdX8oHGMzSsqf3J9 * Disable learner export while the contract is not loaded Export reads the same learner-progress endpoint, so for a contract the warehouse has not loaded it 404s and reported "Could not export learners. Please try again." under a notice saying retrying won't help. Claude-Session: https://claude.ai/code/session_01FGZebDGdX8oHGMzSsqf3J9
…d of bootstrapping (#4236) * Upgrade posthog-js to 1.442.0 and read flags through useFeatureFlag Since posthog-js 1.350.0 (PostHog/posthog-js#2895), an init with bootstrap.featureFlags never requests /flags, so the internal sentinel useFeatureFlagsLoaded watched never cleared and flag-gated pages spun forever on any deployed env. Stop bootstrapping, and fall back to env defaults in a hook of our own instead. Add `useFeatureFlag(flag)`, which reads posthog-js through `useSyncExternalStore`: - During SSR and hydration it returns the flag's `NEXT_PUBLIC_FEATURE_*` env default ("true" in any case is on; anything else, unset included, is off). - In the browser it returns PostHog's value when PostHog has one (this load's /flags, or flags stored from an earlier visit), and the env default otherwise: before PostHog answers, when the request fails, and for a flag PostHog doesn't return (disabled, deleted, never created, or server-only / limited to evaluation contexts, which PostHog leaves out of browser responses). So disabling a flag in PostHog hands it back to its env default; a 0% rollout, which PostHog returns as off, is how to switch one off. This matches `useFeatureFlagEnabled(flag, envDefault)` outside hydration. - Passing the env default as `useFeatureFlagEnabled`'s `defaultValue` doesn't avoid the mismatch below: it applies only while posthog-js has no value. - It always returns a boolean. Not posthog-js/react's `useFeatureFlagEnabled`: it takes its first value from `useState(() => client.isFeatureEnabled(flag))`, with no server snapshot. A component that hydrates after posthog-js has flags (a `<Suspense>` boundary that hydrates after the provider's effect, or streams in after `/flags` answers) renders PostHog's value where the server rendered the default, and React throws away and client-renders that boundary. Reproduced on Next 16.4.4 / React 19.3.1 / posthog-js 1.442.0: returning visitors mismatched in plain and streamed Suspense boundaries, first visits in streamed ones, and with init in instrumentation-client.ts the whole shell mismatched. A `useSyncExternalStore` reader hydrated cleanly in every position. - Pin posthog-js to 1.442.0 (with @posthog/browser-common 0.10.1, core 1.61.2, types 1.418.0), so later versions arrive as their own Renovate PRs rather than in lockfile maintenance, and drop the Renovate freeze from #4230. - useFeatureFlagsLoaded reads a context ConfiguredPostHogProvider sets. It turns true once flags are final for the identity the page ends up with: the first reloadFeatureFlagsAsync() after init, then /me, then the reload identify (new distinct id) or reset starts. A `cancelled` result (posthog-js resetting for its own identity change) waits on the next load. A 10s cap ends the wait with a console.warn. Without an api key it is always true. It never flips back. - The provider keeps the current identity's flag load in a ref and acts on a load only while it is still current. So an anonymous answer that lands after a /me refetch has identified the visitor doesn't mark flags loaded (Copilot), and a /me refetch that keeps the identity (new profile data, so syncIdentity returns null) keeps waiting on that identity's pending load rather than the page's first one (Codex). Strict Mode's effect replay keeps a load the identity effect already set (Copilot). - posthog.init moves into bootstrap/posthog.ts, which instrumentation-client.ts calls (in its own try/catch) before hydration. posthog-js tells onFeatureFlags subscribers nothing when init makes stored flags readable, so with init in the provider's effect, components that subscribed first kept their env default until /flags answered. Before hydration, every read sees them; identify can't run before init either. bootstrapPostHog starts the first flag load and the provider adopts it (checked in a browser that the two share the module). PosthogIdentifier folds into the provider. - No PostHog inside an iframe (window.self !== window.top): third-party cookies are blocked or partitioned there, and the embed shows none of our privacy notices. Embeds on other sites get no PostHog, as before; an embed URL opened directly in a tab now does. With an api key but posthog not started, the provider still hydrates with flags not loaded (the server can't tell), then marks them loaded. - init: no bootstrap; remote_config_refresh_interval_ms: 0 (no 5-minute refresh, as on 1.297); feature_flag_request_max_retries: 0 (give up after 3s, as on 1.297, rather than 6.5s per request, which on a first load after login would hold gated pages until the 10s cap). - Calling reloadFeatureFlagsAsync() right after init sends /flags with config.js instead of after it; posthog-js's own load then skips, so it's still one request. - Lint: ban posthog-js/react's flag hooks, as a `patterns` entry so it survives #4062's usePostHog `paths` entry for the same module. - Delete EnvFlagsProvider, getBootstrapFeatureFlags, parseFlagValue and INTERNAL_BOOTSTRAPPING_FLAG. Gates keep their notFound()/ForbiddenError shape: they now throw only after flags are final, so Next's sticky not-found boundary only matters when a flag changes mid-page or the cap fires. The call sites move to useFeatureFlag in the next commit, so this one fails lint, typecheck and the call-site tests on its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Route call sites through useFeatureFlag Mechanical follow-through on the previous commit, in 63 files: - Swap all 37 `useFeatureFlagEnabled` reads (28 files) for `useFeatureFlag`, and point the 34 call-site tests' mocks at `@/common/useFeatureFlag` instead of `posthog-js/react`. - `useFeatureFlag` always returns a boolean, so the `=== true` / `!== true` comparisons and `!!` coercions on flag values go, as do the test mocks' `undefined` "not loaded" values and ContractAdminPage's "flag is absent" case, which is now the same as its "flag is disabled" case. - Comments that described a bootstrapped `false` or an `undefined` flag now describe the env default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#4265) Cancel now just closes the dialog (or returns to dashboard home from the contract page) without sending consented: false to MITx Online. Only "Agree and continue" records a response. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ds (#4240) * Show run-specific actions only in run rows on multi-run dashboard cards When a card has a Course runs accordion and the multiple-run-context-menus flag is on, the card's root menu drops Email Settings, Unenroll and Receipt; each run row's menu still offers them. Single-run cards are unchanged. Refs mitodl/hq#14234 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Reset the per-run menus flag mock before each card test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the multiple-run-context-menus flag It is rolled out to everyone, so the per-run row menus, the run label in the email settings and unenroll dialogs, and the card menu change are now unconditional. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Wait for the receipt lookup before asserting the card omits Receipt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Actually hide a dashboard card menu button marked hidden The button's display: inline-flex overrode the browser's [hidden] rule, so an empty card menu still showed its trigger. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
) On a wide screen the title column ran the full container width, so a long department name and the Follow pill after it were drawn over the banner photograph. Over a photograph, from md up, the title area is now held to the distance between the column's left edge and the photograph's, measured in container query units against the hero so it tracks the photo's placement at every width. Co-authored-by: Ahtesham Quraish <ahtesham.quraish@arbisoft.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Stop the resource card provider writing to the query cache during render useLearningResourcesBulkList wrote every resource into its detail query from inside `select`, which runs during render and, being inline, on every render. LearningResourceProvider read those detail queries back through useQueries, so each write re-rendered it and the render wrote again. With react-query 5.105 the loop often runs until React gives up, so news and article pages with several cards hit "Maximum update depth exceeded" and showed the error page. The provider now builds its map from the bulk list's own data, and `select` only picks out the results. Fixes #4268 Fixes MIT-LEARN-4ZT Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep editor cards on screen while an added card loads, or if it fails Adding a card changes the bulk list's query key. The provider now keeps the resources earlier lists returned, so the cards already on the canvas stay put while the new list loads and if that request fails. Loading is worked out per card, so only the new card shows a skeleton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check that only the added card shows a skeleton while its list loads Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Derive card loading from the list query's isLoading, not isFetching `isFetching` is also true during a background refetch of a list that already has data, so a card whose resource the list no longer returns would flash a skeleton on every refetch. The query's `isLoading` (pending and fetching) is true only while a key has no data yet, which is the added-card case the context needs. The failure test waited a fixed 100 ms, which also passes while the request is still pending. It now waits for the added card to settle with no skeleton, which only happens once the request has failed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Keep the previous list as placeholder data while an added card's list loads Adding a card in the editor changes the bulk list's key. The provider asks the hook for `keepPreviousData`, so the previous list stands in until the new one loads and the provider no longer needs its own copy of earlier results. Placeholder data reports `success`, so the provider counts a card the placeholder lacks as loading. The "keeps the cards when the list fails" test goes with the state it covered: once the request has failed after its retries, the cards blank until the next refetch, like any other query. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Chris Chudzicki <christopher.chudzicki@gmail.com>
OpenAPI Changes2 changes: 2 error, 0 warning, 0 info Unexpected changes? Ensure your branch is up-to-date with |
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.
Carey P Gumaer
Ahtesham Quraish
Danielle Frappier
Chris Chudzicki
Tobias Macey
renovate[bot]
Anastasia Beglova
Peter Pinch