Skip to content

Commit be7360c

Browse files
authored
chore(plugins,services): declare providesServices on the remaining init-time providers (ADR-0116, #4131) (#4185)
ADR-0116 gave the kernel a declared ordering contract, but only ObjectQLPlugin and MetadataPlugin had declared what their init() registers — so the pre-Phase-1 ordering check could only name a provider for two plugins' worth of services. An audit of every plugin's init() body (brace-matched, comments stripped, each call classified by whether it sits inside a try/if) found 20 plugins that register a service on every path without declaring it. All 20 now declare providesServices. Purely additive: no ordering changes and no new failure modes — a providesServices entry only lets the kernel say WHO provides a service when it reports a misordering, and enriches the Phase-1 getService miss diagnostic. cache/queue/job needed a closer read before declaring, because they register the same service from several branches: every early-return branch plus the fallback registers it, so every path does. ADR-0116's rule that a conditionally registered service must never be declared is unchanged and was applied throughout. The consumer half of the same audit is tracked in #4187.
1 parent 99ffc04 commit be7360c

21 files changed

Lines changed: 150 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
"@objectstack/core": patch
3+
"@objectstack/plugin-auth": patch
4+
"@objectstack/plugin-email": patch
5+
"@objectstack/plugin-hono-server": patch
6+
"@objectstack/plugin-security": patch
7+
"@objectstack/service-storage": patch
8+
"@objectstack/service-settings": patch
9+
"@objectstack/service-realtime": patch
10+
"@objectstack/service-i18n": patch
11+
"@objectstack/service-analytics": patch
12+
"@objectstack/service-messaging": patch
13+
"@objectstack/service-cluster": patch
14+
"@objectstack/service-sms": patch
15+
"@objectstack/service-cache": patch
16+
"@objectstack/service-queue": patch
17+
"@objectstack/service-job": patch
18+
"@objectstack/service-datasource": patch
19+
"@objectstack/service-automation": patch
20+
"@objectstack/mcp": patch
21+
---
22+
23+
chore(plugins,services): declare `providesServices` on the 20 remaining init-time service providers (ADR-0116 follow-up, #4131)
24+
25+
ADR-0116 gave the kernel a declared ordering contract, but only
26+
`ObjectQLPlugin` and `MetadataPlugin` had declared what their `init()`
27+
registers. The pre-Phase-1 ordering check can only *name a provider* for
28+
services someone declared, so its coverage was two plugins wide.
29+
30+
An audit of every plugin's `init()` body (brace-matched, comments stripped,
31+
each call classified by whether it sits inside a `try`/`if`) found 20 plugins
32+
that register a service on every path without declaring it. All 20 now
33+
declare `providesServices`. Purely additive: no ordering changes, no new
34+
failure modes — a `providesServices` entry only lets the kernel say *who*
35+
provides a service when it reports a misordering, and enriches the Phase-1
36+
`getService` miss diagnostic.
37+
38+
Three needed a closer read before declaring, because they register the same
39+
service from several branches (`cache`, `queue`, `job`): each early-return
40+
branch plus the fallback registers it, so every path does — the declaration
41+
is honest. ADR-0116's rule that a *conditionally* registered service must
42+
never be declared is unchanged and was applied throughout.
43+
44+
The same audit found 12 plugins that hard-resolve a service during `init()`
45+
(11 of them `manifest`) without declaring `requiresServices`. None is a live
46+
exposure — every one already declares a hard `dependencies` entry on the
47+
provider, so the kernel orders them correctly today. Those are tracked
48+
separately: with a hard dependency in place, `requiresServices` mostly
49+
restates what the kernel already enforces, and its real value is on
50+
*soft*-dependency consumers, of which `AppPlugin` is currently the only one.

packages/core/src/api-registry-plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ export function createApiRegistryPlugin(
6666

6767
return {
6868
name: 'com.objectstack.core.api-registry',
69+
/**
70+
* Services init() registers on every path (ADR-0116, #4131) — lets the
71+
* kernel name this plugin when a consumer requires one before it inits.
72+
*/
73+
providesServices: ['api-registry'],
6974
type: 'standard',
7075
version: '1.0.0',
7176

packages/mcp/src/plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ export interface MCPServerPluginOptions {
9393
*/
9494
export class MCPServerPlugin implements Plugin {
9595
name = 'com.objectstack.mcp';
96+
/**
97+
* Services init() registers on every path (ADR-0116, #4131) — lets the
98+
* kernel name this plugin when a consumer requires one before it inits.
99+
*/
100+
providesServices = ['mcp'];
96101
version = '1.0.0';
97102
type = 'standard' as const;
98103
dependencies: string[] = [];

packages/plugins/plugin-auth/src/auth-plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ export interface AuthPluginOptions extends Partial<AuthConfig> {
137137
*/
138138
export class AuthPlugin implements Plugin {
139139
name = 'com.objectstack.auth';
140+
/**
141+
* Services init() registers on every path (ADR-0116, #4131) — lets the
142+
* kernel name this plugin when a consumer requires one before it inits.
143+
*/
144+
providesServices = ['auth', 'tenancy'];
140145
type = 'standard';
141146
version = '1.0.0';
142147
dependencies: string[] = ['com.objectstack.engine.objectql']; // manifest service required

packages/plugins/plugin-email/src/email-plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ export interface EmailServicePluginOptions {
5757
*/
5858
export class EmailServicePlugin implements Plugin {
5959
name = 'com.objectstack.service.email';
60+
/**
61+
* Services init() registers on every path (ADR-0116, #4131) — lets the
62+
* kernel name this plugin when a consumer requires one before it inits.
63+
*/
64+
providesServices = ['email'];
6065
version = '1.0.0';
6166
type = 'standard';
6267
dependencies = ['com.objectstack.engine.objectql'];

packages/plugins/plugin-hono-server/src/hono-plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ const DISCOVERY_ROUTE_SEGMENTS: Partial<Record<keyof ApiRoutes, string>> = {
242242
*/
243243
export class HonoServerPlugin implements Plugin {
244244
name = 'com.objectstack.server.hono';
245+
/**
246+
* Services init() registers on every path (ADR-0116, #4131) — lets the
247+
* kernel name this plugin when a consumer requires one before it inits.
248+
*/
249+
providesServices = ['http.server', 'http-server'];
245250
type = 'server';
246251
version = '0.9.0';
247252

packages/plugins/plugin-security/src/security-plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,11 @@ export { describeHighPrivilegeBits } from '@objectstack/spec/security';
296296

297297
export class SecurityPlugin implements Plugin {
298298
name = 'com.objectstack.security';
299+
/**
300+
* Services init() registers on every path (ADR-0116, #4131) — lets the
301+
* kernel name this plugin when a consumer requires one before it inits.
302+
*/
303+
providesServices = ['security.permissions', 'security.rls', 'security.fieldMasker', 'security.bootstrapPermissionSets', 'security.fallbackPermissionSet'];
299304
type = 'standard';
300305
version = '1.0.0';
301306
dependencies = ['com.objectstack.engine.objectql'];

packages/services/service-analytics/src/plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ export interface AnalyticsServicePluginOptions {
153153
*/
154154
export class AnalyticsServicePlugin implements Plugin {
155155
name = 'com.objectstack.service-analytics';
156+
/**
157+
* Services init() registers on every path (ADR-0116, #4131) — lets the
158+
* kernel name this plugin when a consumer requires one before it inits.
159+
*/
160+
providesServices = ['analytics'];
156161
version = '1.0.0';
157162
type = 'standard' as const;
158163
dependencies: string[] = [];

packages/services/service-automation/src/plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,11 @@ export function findInertDeclaredConnectors(
313313
*/
314314
export class AutomationServicePlugin implements Plugin {
315315
name = 'com.objectstack.service-automation';
316+
/**
317+
* Services init() registers on every path (ADR-0116, #4131) — lets the
318+
* kernel name this plugin when a consumer requires one before it inits.
319+
*/
320+
providesServices = ['automation'];
316321
version = '1.0.0';
317322
type = 'standard' as const;
318323
// Soft dependency on metadata: we look it up at start() and tolerate absence.

packages/services/service-cache/src/cache-service-plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ export interface CacheServicePluginOptions {
6161
*/
6262
export class CacheServicePlugin implements Plugin {
6363
name = 'com.objectstack.service.cache';
64+
/**
65+
* Services init() registers on every path (ADR-0116, #4131) — lets the
66+
* kernel name this plugin when a consumer requires one before it inits.
67+
*/
68+
providesServices = ['cache'];
6469
version = '1.0.0';
6570
type = 'standard';
6671

0 commit comments

Comments
 (0)