Ekklesia had no notion of a module being on or off: the accepted module set was exactly "whatever discovery found on disk", and `ModuleLifecycleStage` named nine states nothing produced or consumed. This replaces that vocabulary with two axes that real code produces — persistent, administrator-owned configuration (`enabled`/`disabled`) and per-boot runtime observation (`running`/`disabled`/`blocked`) — and wires them through startup. - One Core table (`ekklesia_core_module_state`, migration 0004) holding only `module_id` and `enabled`. A module with no record is enabled by default, so deploying a module is still sufficient to run it and discovery never needs an initialization write. A record for an undeployed module is retained and ignored, so redeploying restores the decision. - `resolveModuleEligibility` is pure: a disabled module transitively blocks every module that *requires* it (module and capability edges alike) with a structured reason naming the provider; optional dependencies never block; and a missing, incompatible, or cyclic dependency stays discovery's concern. - `startApi` starts only the eligible set and builds the UI publication registry from that same list, so routes, artifacts, browser inventory, and Dashboard navigation all follow one authoritative set. A disabled module is never imported, so there is no per-request enablement check anywhere. - Startup stays all-or-nothing, deliberately; there is consequently no `failed` runtime state. `pnpm modules:enable`/`modules:disable` is the out-of-band recovery path for a module that breaks startup. - Changes take effect at the next start, and the read model reports configuration, runtime, and `restartRequired` separately rather than claiming a module stopped when it has not. - Migration participation and permission composition still span the deployed and discovered inventories respectively, unchanged. - New restricted platform permission `core.modules.lifecycle.manage` gates both reading and changing enablement, at a sibling prefix (`/api/v1/module-lifecycle`) that cannot collide with the module route namespace. - `pnpm doctor` gains a `modules.lifecycle` check so "why is this module unavailable?" is answerable without reading logs. See ADR 0028. Co-Authored-By: Claude Opus 5 <[email protected]>
52 lines
2.7 KiB
JSON
52 lines
2.7 KiB
JSON
{
|
|
"name": "ekklesia",
|
|
"version": "0.1.0",
|
|
"private": true,
|
|
"type": "module",
|
|
"description": "Open, modular church technology platform",
|
|
"engines": {
|
|
"node": ">=24.11.0 <25",
|
|
"pnpm": ">=11.12.0 <12"
|
|
},
|
|
"packageManager": "[email protected]",
|
|
"scripts": {
|
|
"api:start": "turbo run build && node --env-file-if-exists=.env apps/api/dist/server.js",
|
|
"authorization:bootstrap-owner": "turbo run build && node --env-file-if-exists=.env core/dist/cli/bootstrap-platform-owner.js",
|
|
"boundaries": "node scripts/check-boundaries.ts",
|
|
"build": "turbo run build",
|
|
"ci:verify": "pnpm verify && pnpm infrastructure:config",
|
|
"doctor": "node scripts/run-doctor.ts",
|
|
"format": "biome format --write .",
|
|
"format:check": "biome format .",
|
|
"identity:schema:validate": "turbo run build && node core/dist/cli/validate-identity-schema.js",
|
|
"infrastructure:config": "docker compose --env-file .env.example -f infrastructure/compose.yaml config --quiet",
|
|
"infrastructure:down": "docker compose --env-file .env -f infrastructure/compose.yaml down",
|
|
"infrastructure:logs": "docker compose --env-file .env -f infrastructure/compose.yaml logs --follow",
|
|
"infrastructure:smoke": "bash scripts/smoke-infrastructure.sh",
|
|
"infrastructure:up": "docker compose --env-file .env -f infrastructure/compose.yaml up --detach --wait",
|
|
"lint": "turbo run lint lint:root",
|
|
"lint:root": "biome lint .",
|
|
"migrations:apply": "turbo run build && node --env-file-if-exists=.env core/dist/cli/apply-migrations.js",
|
|
"migrations:plan": "turbo run build && node --env-file-if-exists=.env core/dist/cli/plan-migrations.js",
|
|
"migrations:validate": "turbo run build && node core/dist/cli/validate-migrations.js",
|
|
"modules:disable": "turbo run build && node --env-file-if-exists=.env core/dist/cli/set-module-enabled.js --disable",
|
|
"modules:enable": "turbo run build && node --env-file-if-exists=.env core/dist/cli/set-module-enabled.js --enable",
|
|
"modules:validate": "turbo run build && node core/dist/cli/validate-modules.js",
|
|
"test": "turbo run test test:root",
|
|
"test:migrations:integration": "pnpm build && bash scripts/test-migrations-integration.sh",
|
|
"test:root": "vitest run --config vitest.config.ts",
|
|
"test:watch": "vitest",
|
|
"typecheck": "turbo run typecheck typecheck:root",
|
|
"typecheck:root": "tsc --noEmit --project tsconfig.json",
|
|
"verify": "pnpm format:check && pnpm lint && pnpm boundaries && pnpm typecheck && pnpm test && pnpm modules:validate && pnpm migrations:validate && pnpm identity:schema:validate && pnpm build"
|
|
},
|
|
"devDependencies": {
|
|
"@biomejs/biome": "2.5.3",
|
|
"@types/node": "24.13.3",
|
|
"turbo": "2.10.4",
|
|
"typescript": "7.0.2",
|
|
"vitest": "4.1.10",
|
|
"yaml": "2.9.0"
|
|
}
|
|
}
|