Skip to content

lifecycle.mdx's healthChecks TypeScript example does not match PluginHealthCheckSchema — the real config is checkMethod #11811

Description

@os-steve

Found while implementing #11787 (PR for the JSON block in the same section). Out of that card's scope — #11787 measured the JSON report sketch and explicitly recorded that PluginHealthCheckParsed "was not audited against its schema". It has now been audited incidentally, and it diverges. Filed rather than fixed, because the right shape is a design question rather than a mechanical correction (see below).

What was measured

content/docs/protocol/kernel/lifecycle.mdx, ### Custom Health Checks, the TypeScript example immediately above the JSON block:

exportdefault{name: '@vendor/salesforce',healthChecks: {salesforce_connection: async({ context })=>{
...
return{status: 'healthy',latency_ms: response.duration,records_synced_last_hour: ... };},},};

The real configuration type is PluginHealthCheckSchema (packages/spec/src/kernel/plugin-lifecycle-advanced.zod.ts:36-84):

interval: z.number().int().min(1000).default(30000),timeout: z.number().int().min(100).default(5000),failureThreshold: z.number().int().min(1).default(3),successThreshold: z.number().int().min(1).default(1),checkMethod: z.string().optional().describe('Method name to call for health check'),autoRestart: z.boolean().default(false),maxRestartAttempts: z.number().int().min(0).default(3),restartBackoff: z.enum(['fixed','linear','exponential']).default('exponential'),

and its only consumer, packages/core/src/health-monitor.ts:109-122:

if(config.checkMethod&&typeof(pluginasany)[config.checkMethod]==='function'){constcheckResult=awaitthis.raceCheckTimeout((pluginasany)[config.checkMethod](),config.timeout, ...);if(checkResult===false||(checkResult&&checkResult.status==='unhealthy')){status='unhealthy'; ... checks.push({name: config.checkMethod,status: 'failed', message });}else{checks.push({name: config.checkMethod,status: 'passed'});}}
aspectdoc examplereal config + consumer
declarationhealthChecks, a map of named async functions on the plugin default exporta single checkMethodstring naming a method on the plugin object
call signatureasync ({ context }) => ...plugin[checkMethod]()called with no arguments; nothing supplies a context
named checksone entry per key (salesforce_connection)exactly one check per plugin, named after checkMethod (or 'plugin-loaded' when none is configured)
return value{ status: 'healthy', latency_ms, records_synced_last_hour }only false and { status: 'unhealthy' } are read; 'healthy', latency_ms and every other key are ignored

healthChecks as an author-writable field: 0 occurrences. The 5 hits in packages/ are PluginHealthMonitor's own private Map<string, PluginHealthCheckParsed> (health-monitor.ts:19,35,51,301) plus one example reading that private map (packages/core/examples/phase2-integration.ts:151) — none of them a declared plugin field. Positive control in the same sweep: checkMethod = 8 hits, spanning the schema field, the consumer, and two test files (plugin-lifecycle-advanced.test.ts:52, health-monitor.test.ts:108), so the zero is a reading rather than a dead probe.

Why this is filed and not fixed

The <Callout> above the section already says these checks are "not yet exposed as a dedicated HTTP endpoint or as a declarative plugin field" — so the example is flagged as aspirational, and a reader who reads the callout is not being told a lie about healthChecks existing.

That is also exactly why the correction is not mechanical. Two readings, and they teach opposite things:

  • A — rewrite the example against checkMethod. The page then shows a shape a plugin author can actually write today: a healthCheck() method on the plugin plus the PluginHealthCheckSchema config that names it. Costs the per-check naming the current example illustrates (the real model supports exactly one check per plugin).
  • B — keep healthChecks as the declared intent and mark it explicitly unimplemented. Defensible if the map-of-named-checks shape is the direction the model is meant to grow toward — the callout's "not yet" reads that way. Then the fix is a sharper "not implemented" marker on the block, not a rewrite.

Answering that needs someone who knows whether healthChecks is planned or abandoned, which is above the pay grade of a docs-accuracy pass.

Not asserting more than was checked

Only the TypeScript example, PluginHealthCheckSchema and health-monitor.ts were compared. The JSON report block in the same section is #11787's subject and is being fixed there. No other page was examined, and raceCheckTimeout's behaviour was read only as far as "the check is invoked with no arguments".


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions