Imported from anderson-webops/classes.jacobdanderson.net (
AGENTS.md). Install upstream withnpx skills add anderson-webops/classes.jacobdanderson.net. Copyright stays with the author.
Repository Guidelines
Project Structure & Module Organization
front-end/hosts the Vite-powered Vue 3 client. Routing views live insrc/pages, shared UI insrc/components, state insrc/stores, and feature logic insrc/modules. Static assets belong inpublic/andsrc/assets/, while i18n copy sits underlocales/.- Front-end unit specs reside in
front-end/test/*.spec.test.ts(snapshots in__snapshots__/). End-to-end workflows live infront-end/cypress/. back-end/contains the Express + Mongoose API. Keep request handling insrc/controllers/, validation insrc/middleware/, schemas insrc/models/, and route wiring undersrc/routes/.- Monorepo-wide configuration (ESLint, TypeScript base config, workspace scripts) is defined at the repository root; update these when adjusting tooling for either project.
Build, Test, and Development Commands
npm install(root) installs all workspace dependencies using supported Node 24–26 and the pinnednpm@12.0.1toolchain. Avoid mixing package managers.npm run devstarts the front-end dev server on port 3333;npm run serveruns the same build with--hostenabled for LAN previews.npm run serverlaunches the API with live reload viatsx watch -r dotenv/configon port 3008.npm run buildproduces optimized client + server bundles (front-end/dist/,back-end/dist/).npm run -w front-end test/test:unitrun Vitest suites;npm run -w front-end test:e2eopens Cypress.npm run lint(orlint-fix) runs the shared ESLint configuration across both workspaces; pre-commit hooks runlint-stagedautomatically.
Coding Style & Naming Conventions
- ESLint extends
@antfu/eslint-configand enforces Prettier with tab indentation, double quotes, semicolons, 80-character lines, and LF endings. Runnpm run lint-fixbefore pushing. - Vue single-file components use PascalCase filenames (
TheHeader.vue), composables use theuseFeaturepattern, and Pinia stores live insrc/stores/. - TypeScript modules should export camelCase functions and PascalCase classes/types. Keep front-end route files lowercase to match the generated router.
- Prefer descriptive directory names (
controllers/common/,controllers/users/) and colocate feature-specific assets alongside their modules.
Testing Guidelines
- Write unit tests with Vitest and follow the
*.spec.test.tsnaming used infront-end/test/. Snapshot updates belong in__snapshots__/and should be reviewed line-by-line. - Cypress specs should stub network calls against the Express test server; store fixtures under
front-end/cypress/fixtures/. - Back-end tests live under
back-end/test/and run withnpm run -w back-end test. - Aim to cover new endpoints, Pinia stores, and critical user flows before requesting review; document any intentionally skipped scenarios in the PR.
Commit & Pull Request Guidelines
- Follow the existing history: present-tense, concise subjects (
Add tutor availability routes). Keep summaries under 72 characters and expand details in the body when needed. - Reference GitHub issues with
Fixes #123orRefs #123in the description. - Before opening a PR, ensure
npm run lintand relevant tests pass, and include screenshots or screen recordings for UI-facing changes. - PR descriptions should outline scope, testing evidence, migration steps (if any), and rollout considerations.
Security & Configuration Tips
- The API expects secrets via environment variables:
SESSION_SECRET, Mongo credentials (MONGODB_URIor Vault viaVAULT_ROLE_ID/VAULT_SECRET_ID), and optionalCROSS_SITEto adjust cookie policy. Load them through.envfiles excluded from version control. npm run serveralready loadsdotenv/configand will attempt Vault retrieval viasrc/vaultClient.ts; validate both code paths when changing auth or persistence.- Never commit real credentials or production endpoints. Scrub logs before sharing, and verify rate limiting when exposing new routes under
/admin-mailor other sensitive prefixes.
Native Production Authority
- The canonical custom-host path is native Nginx plus exactly one loopback-only systemd API. Do not infer or introduce a Docker deployment.
- Prepare a candidate from a clean checkout at the exact annotated release tag with
scripts/prepare-native-release.sh, then activate it withscripts/promote-native-release.shand the same tagged checkout. - Preserve the strict static-route boundary: unknown pages use the generated branded
404.htmlwith status 404, unknown API paths remain JSON 404s, and neither/release.jsonnor/api/releaseis a public identity endpoint. - Promotion must install the same release's Nginx snippets and systemd unit, switch atomically, pass readiness and loopback TLS smoke gates, and restore the prior configuration and release on failure. Do not change DNS, TLS records, data, credentials, or backups as part of application deployment.
Local Course Material Paths
- Place local starter/solution packs and source course working folders under
/Users/jacobanderson/Documents/Work/Juni, not under/Users/jacobanderson/Work/Juni. - Use the top-level course folder layout already present in that directory (for example
Low Level Security,Intro to Swift App Development,Linux Systems, andWeb Development Foundations) unless the user explicitly requests a different local structure.
Agent Delivery Workflow
- Do not leave completed work uncommitted. After each coherent, validated change set, create a commit and push it in the same session.
- Use multiple commits and pushes when that keeps unrelated changes, partial validations, or follow-up fixes clearly separated. Prefer small, logically grouped commits over one mixed commit.
- Keep both
package-lock.jsonandback-end/package-lock.jsonsynchronized before every commit or push. - Use lowercase annotated semver tags only. Do not invent ad-hoc labels such as
V1,torca-r07,pre-lfs-migration-*, or similar one-off names. - This repo follows the stable
v2.xline. Stay onv2for routine work; only cutv3for an intentional breaking application or API change. - Before creating a new tag, check the latest tag in the active semver line and decide whether the new commit is still the same release milestone. If it is, move that existing tag forward to the new validated commit instead of minting a new version number.
- Keep the GitHub release aligned with that decision: when the commit still belongs to the same milestone, update or recreate the existing release so it points at the moved tag/current commit; only create a brand-new release when the change creates a genuinely new milestone.
- Cut a fresh semver tag and release only when the work crosses a real release boundary, such as a new deployable milestone, a materially different operator/user-facing state, or a version-line change that deserves its own notes and rollback point.
- Create an annotated tag when a deployable course-catalog, front-end UX, back-end API, health/deploy, performance, or security change is ready to ship.
- Create a GitHub release when that tag represents a real site milestone for users, admins, or operators. Release notes should summarize scope, validation, rollout notes, and any migration or recovery steps.
- If the existing tag or release history contains stale drafts, redundant entries, or ad-hoc labels, clean that history up instead of preserving clutter.
- Skip tags and releases for trivial doc-only edits, formatting-only changes, or routine housekeeping unless they change deployment, operations, or a consumer-facing contract.
Dependency & Lockfile Discipline
- Treat the repo-root
npm cipath as the source of truth for deploy readiness. - Any time
package.json, any workspacepackage.json, dependency ranges,package-lock.json, or dependency update tooling changes, verify lockfile parity from the repo root before committing. - Do not rely on
npm installfallback as success. A change is not deploy-ready unless rootnpm cisucceeds. - Do not run
npm install,npm update,npm ci, Playwright/Cypress installs, or dependency-refresh commands automatically unless dependencies or lockfiles changed, dependencies are missing, or the user explicitly asks for a clean dependency gate.
Task-Spawning Guardrails
- Before spawning heavy tasks, check for existing work and avoid duplicate churn.
- Use the repo-provided shared lock wrapper for dependency installs, build setup, browser installs, and other heavy local jobs:
node scripts/classes-family-heavy-task.mjs -- <command>. For example, run clean installs asnode scripts/classes-family-heavy-task.mjs -- npm ci, not barenpm ci, unless CI or the user explicitly requires the bare command. - Never run dependency installs concurrently across
classes.jacobdanderson.netandscheduler.classes.jacobdanderson.net. Use a shared lock such as/tmp/classes-family-dependency-install.lockor/tmp/classes-family-heavy-task.lock. - Ignore generated/heavy directories in file watching, searches, indexing, audits, and task triggers:
node_modules,front-end/node_modules,back-end/node_modules,.git,.next,dist,build,coverage,.cache,.turbo,.vite,playwright-report, andtest-results. - If a task creates or refreshes dependency trees, mark generated dependency directories as non-indexable where possible:
touch node_modules/.metadata_never_index, and do the same forfront-end/node_modulesandback-end/node_moduleswhen present. - Enforce concurrency caps: max 1 dependency install across both repos, max 1 browser automation run per repo, and max 2 total heavy tasks across both repos. Queue extra tasks instead of spawning immediately. For install/setup commands, prefer a shared lock wrapper equivalent to:
On macOS, use a Node/Python lockfile or installlock=/tmp/classes-family-heavy-task.lock ( flock -n 9 || { echo "Another classes/scheduler.classes heavy task is already running; skipping." exit 0 } npm ci ) 9>"$lock"flockvia Homebrew ifflockis unavailable. - Every spawned process must log a clear parent task id, cwd, command, pid, start time, end time, exit code, timeout, and child-process-group cleanup on failure or cancellation.
- Browser tasks must close pages, contexts, browser processes, and any dev servers they started in
finally. - Avoid broad filesystem scans from the repo root unless filtered. Use
rgwith explicit excludes and never scannode_modules. - Do not start a dev server if the needed port is already served by a healthy process. Reuse it or fail clearly.
- If macOS load average is already high, defer non-urgent jobs. Avoid new heavy jobs when load average is above 8, avoid dependency/file-generation jobs while
mds_storeshas high CPU, and avoid installs/builds when available disk is under 40 GB unless required.
Required production/dev dependency update flow before every dependency commit:
- Check production and development dependency freshness from the repository root with
npm outdated --workspaces --longor the repo's documented equivalent. - Review both
dependenciesanddevDependenciesin the root and every workspace package; do not limit updates to production-only packages. - Apply needed updates with the narrowest command that updates the relevant manifest and lockfile together, such as
npm install -w <workspace> <package>@<version>ornpm install -D -w <workspace> <package>@<version>. - If the update is only a lockfile/security refresh, regenerate from the root with
npm install --package-lock-only --ignore-scripts --no-fund --no-audit. - Run
npm auditfrom the repository root and resolve remaining production or dev advisories before committing unless a documented upstream limitation prevents it.
Required dependency verification before dependency/lockfile commits and deploy-critical clean-install checks:
- Run
node scripts/classes-family-heavy-task.mjs -- npm cifrom the repository root through the shared classes-family install lock. - Run
npm run lint. - Run
npm run typecheck. - Run
npm run build. - If API or back-end behavior changed and the repo has a back-end workspace, run
npm run -w back-end testor the repo's equivalent API test command.
For non-dependency source or documentation changes, do not run npm ci automatically. Use existing dependencies for the relevant lightweight checks, and skip generated-tree churn unless dependencies are missing, lockfiles changed, or the user explicitly requests a clean dependency gate.
If npm ci fails because package.json and package-lock.json are out of sync:
- Run
npm install --package-lock-only --ignore-scripts --no-fund --no-auditfrom the repository root. - Re-run
npm cifrom the repository root. - Commit the resulting
package-lock.jsonchange with the related dependency/package change.
Never commit or push dependency/package changes if root npm ci fails.