Files
Ekklesia/CLAUDE.md
T
ekklesiaandClaude Opus 5.5 06bad2fe36 docs: bring CLAUDE.md in line with the current repositories and product rules
Core's agent guidance still described modules living in this monorepo and
manifest v7, and said nothing about the product rules that now govern every
change. Record that first-party modules live in their own Zayin repositories
and install as Module Packages, that the parent ROADMAP.md and DESIGN.md are
authoritative for product direction, the ownership, branding, token, dark-mode
and no-native-dialog rules, that display pairing is transitional until Stage 1,
and the branch-per-PR, no-push-to-main, and no-release-tag workflow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 21:31:36 -05:00

9.5 KiB

CLAUDE.md

Guidance for Claude Code (and other coding agents) working in this repository. This file is intentionally short and durable — it points at authoritative sources instead of restating their contents, because restated details drift.

What this repository is

Ekklesia is a modular, self-hosted church platform. This repository is the platform — Core, the API, the Dashboard and display shell, and the public @ekklesia/* module-development packages — built as a strictly layered pnpm/TypeScript monorepo:

  • Core (core/) owns platform infrastructure: identity, database connectivity, migration execution, module discovery/lifecycle, devices, and other feature-agnostic services. Core never imports a feature module, a surface, or an app.
  • Modules are independently versioned product packages. First-party modules live in their own repositories (Signage is Zayin/Signage), build against the published @ekklesia/* packages (ADR 0053), and reach an installation as a Module Package (ADR 0049) installed into module storage outside the image (ADR 0048). No module source lives in this repository; templates/module is the example, and modules/ is only an optional, ignored local installed-modules directory. A module owns its data, migrations, and its own domain UI (ADR 0021), declares capabilities/permissions through @ekklesia/contracts and @ekklesia/module-sdk, and never imports Core, another module, or a surface. Manifest v8 permits independently optional primary and full-viewport display artifacts, authored in React against the role-neutral @ekklesia/module-ui-sdk ABI; database access is the Module Database ABI v1 (ADR 0050).
  • Surfaces (surfaces/*) own the containing application shell, navigation, and every presentation concern a module does not. The shell hosts module UI through the generic loader; it never rebuilds a module's domain screens. Surfaces consume public API/client contracts only.

These boundaries are enforced by pnpm boundaries (scripts/check-boundaries.ts), not just documented. Read docs/architecture/repository-boundaries.md and docs/architecture/platform-architecture.md before proposing any change that crosses a layer.

Product direction lives beside this repository

The current roadmap and product/interface design contract are ROADMAP.md and DESIGN.md in the parent development directory (../ROADMAP.md and ../DESIGN.md from a checkout in the standard ~/development/ekklesia/ layout). They are deliberately not committed here; do not copy them into this repository to make them available. They are authoritative for product direction: read the relevant stages and sections before designing a change. If this repository's documentation disagrees with them, flag the conflict, correct the repository-local documentation where appropriate, and never silently follow the stale text. Architectural decisions still change only through ADRs (below).

Rules from those documents that every change here must respect:

  • Ownership. Modules own their domain data, behaviour, and UI. Shared resources — displays and devices, accounts and identity, roles and authorization, branding, mail, push, storage providers, and module installation/lifecycle — belong to the platform, and modules consume them.
  • Branding. Normal user-facing UI never hard-codes "Ekklesia"; product identity comes from installation branding (ADR 0046). Package names, diagnostics, logs, and developer tooling may. Existing hard-coded shell strings are known debt for Stage 1, not a pattern to copy.
  • Visual direction. Simple, minimal, and calm; responsive for phone, tablet, and desktop; light, dark, and system modes. Normal UI colours come only from the shared design tokens (@ekklesia/design-tokens, ADR 0039) — never a hard-coded colour.
  • No browser-native dialogs. window.alert, window.confirm, and window.prompt are prohibited in product UI. Destructive actions use in-product confirmation proportional to their impact.
  • Displays are transitional. Displays are platform-owned. Today's device pairing (the Dashboard sidebar's Display devices panel) and Signage's own display roster are a transitional implementation that Stage 1 migrates to Settings → Displays. Fix defects in them, but do not build the Stage 1 Settings, global Displays, theme editor, module installer, or responsive shell redesign early, and keep existing paired displays working across any change.

ADRs are authoritative

docs/architecture/decisions/ records durable architectural decisions. Accepted ADRs are immutable except for status and cross-reference corrections — do not edit their content to fit a new change. A changed decision gets a new ADR that supersedes the old one. Read the full ADR index (docs/architecture/decisions/README.md) and every ADR relevant to the area you are touching before designing a change; do not assume this file's summary of any decision is complete or current.

Read before you design

At minimum, read the current versions of:

  • docs/architecture/decisions/README.md and every ADR relevant to your change
  • docs/architecture/platform-architecture.md and docs/architecture/repository-boundaries.md
  • README.md (current platform status — what is and is not implemented)
  • ../ROADMAP.md and ../DESIGN.md (current product direction; see above)
  • docs/development/repository-workflow.md and docs/development/testing.md
  • CONTRIBUTING.md

These files change as the platform evolves. Inspect the actual current repository state (source, tests, package.json scripts, CI workflow) before relying on anything in this file or in your own memory of a prior session — treat stale assumptions as a bug to catch, not a shortcut.

Working conventions

  • The canonical repositories are on the church Gitea under the Zayin organization: ssh://[email protected]:2222/Zayin/Ekklesia.git (this one), Zayin/Signage.git, and Zayin/Classroom_Desktop.git. A local clone may be behind the server: git fetch --prune --tags and branch from origin/main, never from whatever is checked out.
  • One branch per PR, off an up-to-date origin/main. Never commit or push directly to main, never merge your own PR, and never change branch-protection settings.
  • Never create or push a release tag. Release tags belong to the human maintainer.
  • A change that needs both a platform contract change and a module change is two PRs: Core first, on its own branch, with the compatibility boundary stated explicitly; then the module. Do not copy platform logic into a module to avoid making the correct Core change.
  • Keep PRs focused on one bounded slice. Record major architectural changes in an ADR rather than burying them in an implementation.
  • Add or update tests when you change a contract or architectural rule. New behavior needs new test coverage, not just a passing existing suite.
  • Errors that reach a user, log, CLI, or pnpm doctor output must be sanitized: never expose secrets, connection strings, raw SQL, raw driver/exception messages, stack traces, causes, or machine-specific paths. Match the sanitization pattern already used by core/src/migrations/runner.ts and core/src/diagnostics/doctor.ts rather than inventing a new one.
  • Never run a database migration automatically (on import, on startup, in a test that isn't explicitly a migration test, or as a side effect of an unrelated command). Migrations are always an explicit, operator-invoked action.

Canonical verification commands

Run from the repository root; package.json is the source of truth if this list and the file ever disagree:

  • pnpm verify — the full code-only gate (format, lint, boundaries, typecheck, tests, module and migration validation, identity schema drift check, build). Docker-free; never touches a live database.
  • pnpm test:migrations:integration — live, disposable-PostgreSQL evidence: migration discovery, planning, and execution, plus the apps/api HTTP transport (signup, sign-in, principal resolution, sign-out). Requires Docker. Run this for any change touching either area.
  • pnpm test:package-release — builds the public @ekklesia/* package train, proves it reproducible, and proves a standalone module consumes exactly those tarballs. Publishes nothing. Run it for any change to the public packages or their release tooling.
  • pnpm test:package-publication — publishes a release train to a disposable Gitea (Docker) and proves the publication tool. Never the real registry. The real publication is the manual Publish packages workflow only; never run scripts/publish-package-train.ts against the real registry without the user's explicit intent.
  • pnpm doctor — read-only local readiness report. Never treat it as a substitute for pnpm verify or the integration suite.
  • pnpm migrations:plan / pnpm migrations:apply — read-only planning vs. mutating execution against DATABASE_URL. Never run migrations:apply without the user's explicit intent.
  • pnpm api:start — starts the identity HTTP transport against an already-migrated DATABASE_URL. It never applies migrations itself. Never run it without the user's explicit intent.

When you're unsure

If a task would require weakening an existing ADR's guarantee, introducing a second mechanism where one already exists (a second migration runner, a second discovery path, a second config system), or exposing something the boundary checker currently blocks, stop and surface the conflict instead of working around it.