Skip to content

Latest commit

 

History

History
68 lines (50 loc) · 8.6 KB

File metadata and controls

68 lines (50 loc) · 8.6 KB

Project Conventions

Layer-specific conventions live in .claude/rules/, loaded when you touch a matching file: laravel-boost.md (generated by Boost, package versions and framework guidance), php.md (Pint, PHPStan, Rector), frontend.md (Vue/TS comments, generated types, npm tooling), i18n.md (translation mechanics), testing.md (coverage gate, parallelism, browser-suite internals), docs-site.md (the Starlight site).

House rules

  • Activate the relevant skill whenever you work in its domain (.claude/skills/), rather than waiting until you are stuck.
  • Follow the conventions already in the code. Check sibling files for structure, naming, and approach before inventing your own, and look for an existing component to reuse before writing a new one.
  • Do not add or change dependencies, and do not create new base directories, without approval.
  • Only create documentation files when explicitly asked.
  • Do not write verification scripts or tinker one-offs for behaviour a test can prove. Unit and feature tests are worth more.
  • Be concise in explanations. Focus on what matters rather than narrating the obvious.

Commits & PR titles — Conventional Commits drive the release (non-negotiable)

  • Releases run on two branches. develop accumulates features and cuts vX.Y.Z-rc.N release candidates; master cuts the stable releases and is fed by promoting develop. Feature work therefore branches off and targets develop — a PR opened against master bypasses the candidate line, and nothing errors to tell you. Full flow in CONTRIBUTING.mdReleases; the invariants are enforced by tests/Unit/ReleaseFlowTest.php.
  • Feature PRs merge by squash, and the squash commit subject is the PR title (squash_merge_commit_title = PR_TITLE). So the PR title is the commit release-please reads — it MUST be a valid Conventional Commit (type: imperative subject, lower-case type, no trailing period), e.g. feat: edit last message from the composer with ↑. A PR titled like a sentence ("Edit last message…") is silently dropped from the changelog and the version bump — this is the #1 mistake here.
  • The subject must not start with a capital letter (acronyms included: feat: SSO login… is rejected, feat: re-check the SSO policy is fine), must not end with a full stop, and must be at most 90 characters. That length rule is the one you cannot fix after the fact, so get it right before merging (#891).
  • Changelog-relevant types (from release-please-config.json): feat → Features (minor bump), fix → Bug Fixes (patch), perf → Performance, refactor → Code Refactoring, deps → Dependencies (non-bumping, rides along into the next release). A breaking change (feat!: / fix!:, or a BREAKING CHANGE: footer) forces a major bump. docs, test, chore, ci, build and style are allowed but do not appear in the changelog and do not bump the version — only use them for PRs that genuinely ship no user-facing feature/fix.
  • Never hand-edit CHANGELOG.md, VERSION, or either release-please manifest — release-please owns them, and nothing is tagged by a push: merging the release PR is what tags and publishes.
  • Keep individual commit messages Conventional too. commitlint validates the PR's commits; it never reads the PR title, and both must be right.
  • Branch names are cosmetic (release-please ignores them). Never rely on one for the changelog; set the PR title.
  • When opening a PR with gh pr create, always pass a Conventional-Commit --title and reference the issue in the body (Closes #NNN).
  • Hotfixes are the one exception, and only when both halves hold: a released version is broken in production and develop cannot be promoted as it stands. Then branch off master, PR into master with a fix: title, and merge the stable release PR. Everything else — including nearly all fix: work — goes through develop; do not take this route because it is faster. CONTRIBUTING.mdHotfixes has the full path, including the mandatory back-merge into develop.
  • Promote develop to master with a merge commit, never a squash. release-please reads the individual Conventional Commits, so squashing a promotion would collapse a whole release's worth of them into one changelog entry.

Implementing Issues (TDD)

  • Always activate the tdd skill when implementing an issue or building a feature. Drive the work test-first (red → green → refactor): write a failing test that captures the acceptance criterion, make it pass with the minimal change, then refactor. This pairs with the non-negotiable 100% coverage gate below.

Internationalization (i18n) — never hardcode user-facing copy

  • All user-visible copy must go through the translation layer — never hardcode English in a component, controller, or Blade view. Frontend copy goes through $t / useTranslations, backend copy through __(), and the message key is the English source string.
  • Every new key needs its French translation in lang/fr.json in the same change. The helpers, interpolation syntax and locale plumbing are in .claude/rules/i18n.md.

Quality gates — both must be green before you push

  • Backend: ./vendor/bin/sail composer test runs Pint, PHPStan, Rector's dry-run, and php artisan test --parallel --coverage --min=100. 100% coverage is non-negotiable; do not push or open a PR until it reports Total: 100.0 %.
  • Frontend: ./vendor/bin/sail npm run lint:check, ./vendor/bin/sail npm run format:check, ./vendor/bin/sail npm run types:check, ./vendor/bin/sail npm run test:js (the Vitest suite, which the PHP coverage gate cannot reach) and ./vendor/bin/sail npm run build. All five must pass. ./vendor/bin/sail composer ci:check runs both gates at once.
  • Always run Node/npm tooling through Sail, never bare npm on the host: node_modules lives inside the Linux container and its native bindings are Linux-only, so a host run fails with Cannot find native binding.
  • Accessibility is part of a green UI change, and the automated gate does not catch it. Run the repo's axe/contrast checks and match the patterns in the existing a11y browser tests before calling UI work done.

Browser Testing — always headless

  • Every browser you drive runs headless. Never open a visible window. This covers the Pest browser suite, interactive tooling (the Playwright MCP, the browser-use and run skills) and any raw Playwright/Chromium launch. A window popping open steals focus from whoever is at the keyboard, and on a machine running several worktrees at once it is a stream of them.
  • Drive interactive tooling headless too: @playwright/mcp --headless, chromium.launch({ headless: true }). To see the page, take a screenshot and read it.
  • If you genuinely need to watch a browser, ask first and revert it before committing. What that costs inside the Pest suite, and the ->debug() trap in particular, is in .claude/rules/testing.md.

Operator-facing changes ship with their docs

  • A change an operator would have to act on is not done until docs/ is updated in the same PR — a new or changed .env/config setting, a feature toggle, or a change to install, upgrade, reverse-proxy or production-stack steps. The trigger fires on app changes, which is why it lives here; the page map and build steps are in .claude/rules/docs-site.md.

Reporting Bugs Found While Doing Something Else

  • When you discover a bug, broken tooling, or other pre-existing defect while implementing something unrelated, do not fix it inline. Keep the current change focused on its own scope.
  • Check for an existing issue first (gh issue list --state open --search "<keywords>", and closed issues too). If none exists, gh issue create one describing what's broken, how it surfaced, why it matters, and clear acceptance criteria, with a fitting label (e.g. tech-debt).
  • Mention the issue in the PR of the feature you're working on so the discovery is traceable, then carry on. Only fix it inline if it directly blocks you.