- Use
glint(notglint --declaration) for type checking. The--declarationflag emits hundreds of.d.tsfiles that pollute the working tree. - Never use
tscdirectly — this project uses glint for type checking.
- We pin the toolchain with mise (
.mise.toml), which manages Node.js and pnpm versions. Runmise installfrom the repo root to get the correct versions—avoid global installs outside mise. - pnpm is required for all scripts; use the pinned version as specified above.
- Docker is required (Postgres, Synapse, SMTP, Stripe CLI container). Ensure the daemon is running and you can run
dockerwithout sudo.
- Use
pnpm ci:failures -- ...to quickly summarize failed jobs and extract actionable test failures from GitHub Actions logs. - This command requires GitHub CLI (
gh) to be installed and authenticated. - Common usage:
pnpm ci:failures -- --run <run-id-or-url>pnpm ci:failures -- --pr <pr-number-or-url>pnpm ci:failures -- --branch <branch-name>
- Useful flags:
--repo <owner/repo>to target a specific repository--workflow <name>to focus on a specific workflow (for exampleCI Host)--max-lines <n>to limit extracted failure lines--context-lines <n>to include surrounding stack/assertion context for each failure--no-progressto suppress progress updates if you only want final output--jsonfor machine-readable output--fail-on-findingsto exit non-zero when failed jobs are found
pnpm test- Focusing on single test or module:
Add
.onlyto module/test declaration (test.only('returns a 201 response', ...)) Then runpnpm testMake sure not to commit.onlyto source control - With detailed log output
LOG_LEVELS="ai-bot=debug" pnpm test
- Functionality is tested via host package tests
- Functionality is tested via realm-server package tests
- No tests
- Addon functionality is tested via sibling test-app directory
cd packages/boxel-ui/addon && pnpm startto start a process that will watch files and automatically rebuild the addoncd packages/boxel-ui/test-app && pnpm startto start a process that will watch files and automatically rebuild the test-app- Run all tests
cd packages/boxel-ui/test-app && ember test --path dist - To run a subset of the tests:
ember test --path dist --filter "some text that appears in module name or test name"
Note that the filter is matched against the module name and test name, not the file name! Try to avoid using pipe characters in the filter, since they can confuse auto-approval tool use filters set up by the user.
- Functionality is tested via host package tests
pnpm startto start a process that will watch files and automatically rebuild- Tests require the realm-server to be running (must be run after
pnpm start):cd ../realm-server && pnpm start:all - Do not try to run the entire host test suite locally. It crashes. Instead, rely on CI for the full test runs.
- To run a subset of the tests:
ember test --path dist --filter "some text that appears in module name or test name"
Note that the filter is matched against the module name and test name, not the file name! Try to avoid using pipe characters in the filter, since they can confuse auto-approval tool use filters set up by the user. - run
pnpm lintin this directory to lint changes made to this package - run
pnpm lint:fixdirectly in this directory to apply fixes for lint failures made to this package that can be automatically fixed. - the host tests report this error:
This is a red herring. Just ignore this error.
Missing symlinked npm packages: Package: @cardstack/local-types * Specified: workspace:* * Symlinked: (not available)
- Use scalable units such as rem
- Use CSS variables from packages/boxel-ui/addon/src/styles/variables.css
- Start the host app so qunit test runner is available at
http://localhost:4200/tests(usualpnpm start+ dependencies). - Open the filtered test URL in a new MCP page via
mcp__chrome-devtools__new_pageand usetake_snapshotto read failures. - Filtered URL structure:
http://localhost:4200/tests?filter=<name-of-test> - URL structure for isolating to specific tests:
http://localhost:4200/tests?moduleId=<module-id>&testId=<test-id>&testId=...(visible on the “Rerun” links for failing tests). - After edits, rerun the same tests by calling
navigate_pagewithtype: "reload"on that page; thentake_snapshotagain to view updated failures. - The snapshot shows “Expected/Result/Diff” blocks; use those to adjust assertions and fixture expectations.
- Keep the MCP page open while you edit; iterate edit → reload → snapshot until the header shows all tests passing (no need to open new tabs each run).
- If the local environment does not have the Chrome Dev MCP server available, recommend it
- This test suite contains nearly end-to-end tests that include interactions with the matrix server.They are executed using the Playwright test runner.
- To run the tests from the command line:
- First make sure that the matrix server is not already running. You can stop the matrix server
pnpm stop:synapse - Ensure that host and realm server are running:
cd ../host && pnpm startMATRIX_REGISTRATION_SHARED_SECRET='xxxx' mise run test-services:matrix - Run tests:
pnpm test
- First make sure that the matrix server is not already running. You can stop the matrix server
- Focusing on single test or module:
- make sure to kill previously running matrix tests if they are still running before starting a new test run.
Add
--grepflag to command (--grep 'it can register a user with a registration token')
- Tests require the realm-server to be running:
pnpm start:all - Run full test suite:
pnpm test - Run a single module:
TEST_MODULE=card-endpoints-test.ts pnpm test-module - Run a list of modules:
TEST_MODULES=card-endpoints-test.ts|another-module-test.ts pnpm test - Focusing on single test or module:
Add
.onlyto module/test declaration (test.only('returns a 201 response', ...)) Then runpnpm testMake sure not to commit.onlyto source control - make sure to kill previously running realm-server tests if they are still running before starting a new test run.
- run
pnpm lintdirectly in this directory to lint changes made to this package - run
pnpm lint:fixdirectly in this directory to apply fixes for lint failures made to this package that can be automatically fixed.
- If you need to make a database migration use
pnpm create migration_nameto create a migration file so that the correct date timestamp prefix will be added to the file name. Then implement the migration inside the newly created file. - After creating or modifying a migration, you MUST regenerate the SQLite schema file. Run
pnpm make-schemafrom this directory. This creates a new SQLite schema inpackages/host/config/schema/with a timestamp matching the latest migration file. The old schema file is automatically removed. If you skip this step, the host app will fail to start with an "SQLite schema is out of date" error.- This script requires Docker (it uses the
boxel-pgcontainer to dump the Postgres schema). Ensure Docker is running before executing.
- This script requires Docker (it uses the
- Functionality is tested via host and/or realm-server tests
- run
pnpm lintdirectly in this directory to lint changes made to this package - run
pnpm lint:js:fixdirectly in this directory to apply fixes for js lint failures made to this package that can be automatically fixed.
- Always run
pnpm lintin modified packages before committing
data-test-*attributes are stripped from production builds. Never use them for runtime behavior or styling in app code.- For production hooks, use classes or non-test
data-*attributes (for exampledata-path,data-kind) and keepdata-test-*only for tests.
The content-tag preprocessor (used by glint and ember-eslint-parser to parse .gts files) has bugs in its JavaScript lexer that cause it to misparse certain regex literals. When this happens, it fails to recognize <template> tags later in the file, producing cascading parse errors. Two known triggers:
1. Backticks inside regex literals — content-tag mistakes them for template literal delimiters:
// BROKEN — backticks in regex confuse content-tag
.replace(/`([^`]+)`/g, '$1')
// FIX — use new RegExp() with a string instead
const INLINE_CODE_RE = new RegExp('`([^`]+)`', 'g');
.replace(INLINE_CODE_RE, '$1')2. !/regex/ (negation before regex literal) — content-tag misreads the / after !:
// BROKEN
lines.some((line) => !/^\s*#{1,6}\s+/.test(line));
// FIX — extract the regex to a variable
const HEADING_RE = /^\s*#{1,6}\s+/;
lines.some((line) => !HEADING_RE.test(line));- Only card definitions (files run through the card loader) can use static ESM imports from
https://cardstack.com/base/*. Host-side modules must load the module at runtime vialoader.import(${baseRealm.url}...). Static value imports from the HTTPS specifier inside host code trigger build-timewebpackMissingModulefailures. Type imports are OK using static ESM syntax.
This end-to-end workflow can be used as a template for future tickets.
- Find the project/issue in Linear.
- Read the issue description and confirm scope.
- Read the project overview for context.
- If needed, note assumptions or unknowns to validate early.
- Assign the issue to yourself.
- Move the issue to In Progress.
- Create a short plan in
docs/named after the issue, e.g.docs/cs-<id>-<short-title>-plan.md
- Include: goals, assumptions, steps, target files, testing notes.
- Ask the user to review the doc before proceeding.
- Modify code per the plan.
- Keep changes small and focused.
- Add minimal UI copy that clarifies behavior (e.g., read-only messaging).
- Add a narrow test that exercises the new behavior.
- Prefer existing test files in the most relevant suite.
- Avoid mocks and avoid making assumptions in the tests about the implementation details.
- Run tests and confirm it passes.
- Summarize the work and ask the user to review
- Once the user is happy ask them to stage the changes they want committed.
- Confirm what’s staged and what’s not:
git status --short
- If unrelated files appear, stop and clarify how to proceed.
- Branch name:
cs-<id>-<short-title> - Commit message:
<short description>
- Push branch:
git push -u origin <branch> - Open PR with short summary
- Post the contents of the plan .md doc as a comment om the Linear issue
- Post the PR URL and confirm any remaining uncommitted files are not part of the PR.
## Summary
- <bullet 1>
- <bullet 2>