-
Notifications
You must be signed in to change notification settings - Fork 3
feat: claude init #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release-teak
Are you sure you want to change the base?
Changes from all commits
64e67f1
31bbd44
333d512
8fda70b
7fda05d
bb5e8b7
0ede482
e5a1808
8184ee4
e63ff50
4a91e26
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| Run all pre-PR validation checks and report results. | ||
|
|
||
| > Note: The build step uses `npm run build:validate` (with a stub `env.config`) rather than `npm run build`, so the build succeeds without the private edX plugin packages required in production. All other checks match CI. | ||
|
|
||
| Execute the following checks **in order**, capturing output from each. Continue through all checks even if one fails — collect all failures before reporting. | ||
|
|
||
| ## Checks to run | ||
|
|
||
| ### 1. Commit messages | ||
| Run: `git log release-teak..HEAD --format="%H %s"` | ||
|
|
||
| > Note: `release-teak` is the current base branch for PRs in the `edx` fork. Update this (and the matching allow-list entry in `.claude/settings.json`) when the default branch changes. | ||
|
|
||
| For each commit, validate the subject line against the conventional commits format: | ||
| `<type>(<optional scope>): <description>` | ||
|
|
||
| Valid types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert` | ||
|
|
||
| Flag any commit whose subject does not match this pattern. | ||
|
|
||
| ### 2. Lint | ||
| Run: `npm run lint -- --max-warnings 0` | ||
|
|
||
| ### 3. Type checking | ||
| Run: `npm run types` | ||
|
|
||
| ### 4. Tests | ||
| Run: `npm test -- --passWithNoTests` | ||
|
|
||
| ### 5. Build | ||
| Run: `npm run build:validate` | ||
|
|
||
| ### 6. Bundle size | ||
| Run: `npm run bundlewatch` | ||
|
|
||
| ## Report | ||
|
|
||
| After all checks complete, output a summary table: | ||
|
|
||
| | Check | Status | | ||
| |-------|--------| | ||
| | Commit messages | ✅ PASS / ❌ FAIL | | ||
| | Lint | ✅ PASS / ❌ FAIL | | ||
| | Types | ✅ PASS / ❌ FAIL | | ||
| | Tests | ✅ PASS / ❌ FAIL | | ||
| | Build | ✅ PASS / ❌ FAIL | | ||
| | Bundle size | ✅ PASS / ❌ FAIL | | ||
|
|
||
| For each failed check, show the specific errors and a brief suggested fix. | ||
| If all checks pass, confirm the branch is ready for a PR. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "permissions": { | ||
| "allow": [ | ||
| "Bash(npm run lint)", | ||
| "Bash(npm run lint -- --max-warnings 0)", | ||
| "Bash(npm run types)", | ||
| "Bash(npm test -- --passWithNoTests)", | ||
| "Bash(npm run build)", | ||
| "Bash(npm run build:validate)", | ||
| "Bash(git log -1 --pretty=%B)", | ||
|
nsprenkle marked this conversation as resolved.
|
||
| "Bash(git log release-teak..HEAD --format=\"%H %s\")", | ||
| "Bash(npm run bundlewatch)" | ||
| ] | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| # CLAUDE.md | ||
|
|
||
| This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | ||
|
|
||
| ## Commands | ||
|
|
||
| Run `/validate` to check all pre-PR requirements locally (lint, types, tests, build, bundle size, commit messages) and get a pass/fail summary with guidance on any failures. | ||
|
|
||
| ```bash | ||
| # Development | ||
| npm start # Start dev server (standard) | ||
| npm run dev # Start dev server with local OpenEdX config (host: apps.local.openedx.io) | ||
| npm run build # Production webpack build | ||
|
|
||
| # Testing | ||
| npm test # Run all tests with coverage | ||
| npm run test:watch # Watch mode | ||
| npm run snapshot # Update snapshots | ||
|
|
||
| # Run a single test file | ||
| NODE_ENV=test npx jest path/to/file.test.jsx | ||
|
|
||
| # Linting | ||
| npm run lint # Check | ||
| npm run lint:fix # Auto-fix | ||
|
|
||
| # Type checking | ||
| npm run types # tsc --noEmit (TypeScript only) | ||
|
|
||
| # i18n | ||
| npm run i18n_extract # Extract translation strings | ||
| ``` | ||
|
|
||
| ## Fork and Upgrade Strategy | ||
|
|
||
| This repo is a fork of [openedx/frontend-app-learning](https://github.com/openedx/frontend-app-learning) maintained in the `edx` GitHub org. Understanding this relationship is important when authoring changes. | ||
|
|
||
| ### Release cadence | ||
|
|
||
| The open source community authors changes on `openedx/master`. Every ~6 months, these are grouped into a named release branch, tested, and offered to forks. Because we run a large production instance, we pull in upstream changes only when named releases are available — not from `openedx/master` directly. | ||
|
|
||
| ### Authoring changes | ||
|
|
||
| We also author our own changes on this fork to ship features faster than the upstream review process allows. The guiding principle is: **every change should ideally be compatible with upstream and eventually contributed back.** | ||
|
|
||
| In practice this means: | ||
|
|
||
| - **Prefer plugin slots** — proprietary UI additions should use `@openedx/frontend-plugin-framework` plugin slots ([src/plugin-slots/](src/plugin-slots/)) so they can be injected without modifying core code. | ||
| - **Prefer feature toggles disabled by default** — any behavior that isn't appropriate for the broader open source community should be gated behind a config flag (via `getConfig()` from `@edx/frontend-platform`) that defaults to off, making the change safe to contribute upstream. | ||
| - **Avoid proprietary logic in core paths** — changes that can't be upstreamed should be isolated at the edges (plugin slots, config-gated branches) rather than embedded in shared data/API/redux code. | ||
|
|
||
| ## Architecture | ||
|
|
||
| This is an OpenEdX Micro-Frontend (MFE) built on `@edx/frontend-platform` and `@openedx/frontend-build`. It serves the learner-facing course experience. | ||
|
|
||
| ### Routing | ||
|
|
||
| Routes are defined in [src/constants.ts](src/constants.ts) as `DECODE_ROUTES` and `ROUTES`. The main route structure is: | ||
| - `/course/:courseId/home` — Course outline/home tab | ||
| - `/course/:courseId/dates` — Dates tab | ||
| - `/course/:courseId/progress` — Progress tab | ||
| - `/course/:courseId/discussion/...` — Discussion tab | ||
| - `/course/:courseId/:sequenceId/:unitId` — Courseware (unit content) | ||
| - `/course/:courseId/course-end` — Course exit | ||
|
|
||
| All routes under `DECODE_ROUTES` are wrapped in `<DecodePageRoute>` (URL decoding support). The app entry point is [src/index.jsx](src/index.jsx). | ||
|
|
||
| ### Redux Store Structure | ||
|
|
||
| The Redux store ([src/store.ts](src/store.ts)) has these slices: | ||
|
|
||
| | Key | Purpose | | ||
| |-----|---------| | ||
| | `models` | Normalized model store (courses, sequences, units by ID) | | ||
| | `courseware` | Current courseId, sequenceId, unit IDs and loading state | | ||
| | `courseHome` | Course home tab data | | ||
| | `specialExams` | Special exam state (external lib) | | ||
| | `learningAssistant` | AI chat assistant state (external lib) | | ||
| | `recommendations` | Course exit recommendations | | ||
| | `tours` | Product tour state | | ||
| | `plugins` | Plugin framework overrides | | ||
|
|
||
| **Model Store pattern**: API data is normalized and stored flat in `state.models.<type>[id]`. Slices store IDs, not full objects. Access via `state.models.courses[state.courseware.courseId]`. See [docs/decisions/0004-model-store.md](docs/decisions/0004-model-store.md). | ||
|
|
||
| ### Key Source Directories | ||
|
|
||
| - [src/courseware/](src/courseware/) — Unit content delivery: sequences, units, iframes, sidebar | ||
| - [src/courseware/data/](src/courseware/data/) — Redux slice, thunks, API, selectors for courseware | ||
| - [src/courseware/course/sequence/](src/courseware/course/sequence/) — Sequence navigation & unit rendering | ||
| - [src/courseware/course/new-sidebar/](src/courseware/course/new-sidebar/) — Sidebar (discussions, notifications) | ||
| - [src/course-home/](src/course-home/) — Outline, dates, progress, discussion tabs | ||
| - Each tab has its own `data/` subdirectory with slice, thunks, and API | ||
| - [src/generic/](src/generic/) — Domain-agnostic reusable code (model-store, hooks, notices, user-messages). Do not add app-specific logic here. | ||
| - [src/shared/](src/shared/) — App-specific shared code used across multiple top-level components | ||
| - [src/plugin-slots/](src/plugin-slots/) — `@openedx/frontend-plugin-framework` plugin slots for extensibility | ||
| - [src/tab-page/](src/tab-page/) — `TabContainer` wrapper that handles tab loading state | ||
| - [src/product-tours/](src/product-tours/) — Onboarding product tours | ||
|
|
||
| ### Naming Conventions | ||
|
|
||
| From [docs/decisions/0006-thunk-and-api-naming.md](docs/decisions/0006-thunk-and-api-naming.md): | ||
|
|
||
| - **API functions** use HTTP verb prefixes: `getCourseBlocks`, `postSequencePosition` | ||
| - **Redux thunks** use semantic prefixes: `fetchCourse`, `fetchSequence`, `saveSequencePosition`, `checkBlockCompletion` | ||
|
|
||
| ### Data Flow Pattern | ||
|
|
||
| Each major feature follows this pattern: | ||
| ``` | ||
| data/api.js — Raw API calls (HTTP verb naming) | ||
| data/thunks.js — Redux thunks that call APIs and dispatch to model-store (fetch/save naming) | ||
| data/slice.js — Redux slice (state shape + reducers) | ||
| data/selectors.js — Reselect selectors | ||
| data/__factories__/ — Rosie factories for test data | ||
| ``` | ||
|
|
||
| ### Loading State | ||
|
|
||
| Components own their own loading state (LOADING/LOADED/FAILED/DENIED constants from [src/constants.ts](src/constants.ts)). Components render spinners/skeletons themselves rather than relying on parents to gate rendering. See [docs/decisions/0005-components-own-their-loading-state.md](docs/decisions/0005-components-own-their-loading-state.md). | ||
|
|
||
| ### Plugin Slots | ||
|
|
||
| The app exposes many plugin slots via `@openedx/frontend-plugin-framework` in [src/plugin-slots/](src/plugin-slots/). Each slot has a README. Slots allow operators to inject/replace UI components without forking. | ||
|
|
||
| ### Testing Approach | ||
|
|
||
| From [docs/decisions/0007-testing.md](docs/decisions/0007-testing.md): | ||
|
|
||
| - Use **React Testing Library** — query by labels, text, roles; use `data-testid` as last resort | ||
| - Mock HTTP with **axios-mock-adapter**; build test data with **Rosie factories** in `data/__factories__/` | ||
| - Test non-obvious behavior (error states, interactions, corner cases) — not happy-path rendering | ||
| - **Avoid snapshots** for complex components; they're too brittle. Snapshots are acceptable for data/redux tests and tiny isolated components. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| const path = require('path'); | ||
| const { merge } = require('webpack-merge'); | ||
| const prodConfig = require('./webpack.prod.config'); | ||
|
|
||
|
Comment on lines
+2
to
+4
|
||
| /** | ||
| * Webpack config used by `npm run build:validate`. | ||
| * | ||
| * Identical to the prod build except env.config is replaced with a stub so | ||
| * that the build succeeds without the private edX plugin packages that are | ||
| * only available in the local development monorepo. | ||
| */ | ||
| module.exports = merge(prodConfig, { | ||
| resolve: { | ||
| alias: { | ||
| 'env.config': path.resolve(__dirname, './env.config.validate.jsx'), | ||
| // TsconfigPathsPlugin doesn't hook correctly on the merged config, so | ||
| // replicate the tsconfig "@src/*" path mapping explicitly. | ||
| '@src': path.resolve(__dirname, 'src'), | ||
| }, | ||
| }, | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.