Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .claude/agents/e2e-test-engineer.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ You are an elite E2E Test Engineer specializing in browser-based end-to-end test
You are the `e2e-test-engineer` agent on the Cornerstone project team. In all commits, use this trailer:

```
Co-Authored-By: Claude e2e-test-engineer (<model>) <noreply@anthropic.com>
Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com>
```

Replace `<model>` with your actual model identifier (e.g., `Opus 4.6`, `Sonnet 4.5`).

In all GitHub comments (issues, PRs, discussions), prefix your first line with:

```
Expand Down
22 changes: 22 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Summary

<!-- Brief description of the changes -->

## Related Issues

<!-- Link to GitHub Issues: Fixes #N, Related: #N -->

## Quality Gates

- [ ] `npm run lint` passes
- [ ] `npm run typecheck` passes
- [ ] `npm test` passes
- [ ] `npm run format:check` passes
- [ ] `npm run build` passes
- [ ] `npm audit` shows 0 fixable vulnerabilities

## Agent Reviews

- [ ] `security-engineer` reviewed
- [ ] `product-architect` reviewed
- [ ] `product-owner` reviewed
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
run: npm run build

- name: Security audit
run: npm audit --audit-level=moderate
run: npm audit --audit-level=low

docker:
name: Docker
Expand Down
22 changes: 20 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,11 @@ scripts/worktree-remove.sh feat/42-work-item-crud --delete-branch
| Client | React | 19.x | ADR-002 |
| Client Routing | React Router | 7.x | ADR-002 |
| Database | SQLite (better-sqlite3) | -- | ADR-003 |
| ORM | Drizzle ORM | 0.38.x | ADR-003 |
| ORM | Drizzle ORM | 0.45.x | ADR-003 |
| Bundler (client) | Webpack | 5.x | ADR-004 |
| Styling | CSS Modules | -- | ADR-006 |
| Testing (unit/integration) | Jest (ts-jest) | 30.x | ADR-005 |
| Testing (E2E) | Playwright | TBD | ADR-005 |
| Testing (E2E) | Playwright | 1.58.x | ADR-005 |
| Language | TypeScript | ~5.9 | -- |
| Runtime | Node.js | 24 LTS | -- |
| Container | Docker (DHI Alpine) | -- | -- |
Expand All @@ -364,6 +364,9 @@ cornerstone/
.prettierrc # Prettier config
jest.config.ts # Jest config (all packages)
Dockerfile # Multi-stage Docker build
docker-compose.yml # Docker Compose for end-user deployment
.env.example # Example environment variables
.releaserc.json # semantic-release configuration
CLAUDE.md # This file
plan/ # Requirements document
shared/ # @cornerstone/shared - TypeScript types
Expand Down Expand Up @@ -399,13 +402,23 @@ cornerstone/
lib/ # Utilities, API client
types/ # Type declarations (CSS modules, etc.)
styles/ # Global CSS (index.css)
e2e/ # @cornerstone/e2e - Playwright E2E tests
package.json
tsconfig.json
playwright.config.ts # Playwright configuration
auth.setup.ts # Authentication setup for tests
containers/ # Testcontainers setup modules
fixtures/ # Test fixtures and helpers
pages/ # Page Object Models
tests/ # Test files organized by feature/epic
```

### Package Dependency Graph

```
@cornerstone/shared <-- @cornerstone/server
<-- @cornerstone/client
@cornerstone/e2e (standalone — runs against built app via testcontainers)
```

### Build Order
Expand Down Expand Up @@ -465,6 +478,7 @@ Unit and integration testing is owned by the `qa-integration-tester` agent. E2E
- **Unit & integration tests**: Jest with ts-jest (co-located with source: `foo.test.ts` next to `foo.ts`)
- **API integration tests**: Fastify's `app.inject()` method (no HTTP server needed)
- **E2E tests**: Playwright (owned by `e2e-test-engineer` agent, runs against built app)
- E2E test files live in `e2e/tests/` (separate workspace, not co-located with source)
- E2E tests run against **desktop, tablet, and mobile** viewports via Playwright projects
- Test environment managed by **testcontainers**: app, OIDC provider, upstream proxy
- **Test command**: `npm test` (runs all Jest tests across all workspaces via `--experimental-vm-modules` for ESM)
Expand Down Expand Up @@ -555,3 +569,7 @@ Additional variables for OIDC, Paperless-ngx, and sessions will be added as thos
## Cross-Team Convention

Any agent making a decision that affects other agents (e.g., a new naming convention, a shared pattern, a configuration change) must update this file so the convention is documented in one place.

### Agent Memory Maintenance

When a code change invalidates information in agent memory (e.g., fixing a bug documented in memory, changing a public API, updating routes), the implementing agent must update the relevant agent memory files. During the refinement phase, the orchestrator should verify that no stale memory entries exist for completed work.
20 changes: 5 additions & 15 deletions server/src/plugins/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,27 +80,17 @@ export function loadConfig(env: Record<string, string | undefined>): AppConfig {

// Parse and validate SECURE_COOKIES
const secureCookiesStr = (getValue('SECURE_COOKIES') ?? 'true').toLowerCase();
let secureCookies: boolean;
if (secureCookiesStr === 'true') {
secureCookies = true;
} else if (secureCookiesStr === 'false') {
secureCookies = false;
} else {
if (secureCookiesStr !== 'true' && secureCookiesStr !== 'false') {
errors.push(`SECURE_COOKIES must be 'true' or 'false', got: ${getValue('SECURE_COOKIES')}`);
secureCookies = true; // Default fallback
}
const secureCookies = secureCookiesStr === 'true';

// Parse TRUST_PROXY (boolean, default false)
// Parse and validate TRUST_PROXY
const trustProxyStr = (getValue('TRUST_PROXY') ?? 'false').toLowerCase();
let trustProxy: boolean;
if (trustProxyStr === 'true') {
trustProxy = true;
} else if (trustProxyStr === 'false') {
trustProxy = false;
} else {
if (trustProxyStr !== 'true' && trustProxyStr !== 'false') {
errors.push(`TRUST_PROXY must be 'true' or 'false', got: ${getValue('TRUST_PROXY')}`);
trustProxy = false; // Default fallback
}
const trustProxy = trustProxyStr === 'true';

// OIDC configuration (all optional)
const oidcIssuer = getValue('OIDC_ISSUER');
Expand Down