# Background child health

The exported `@netscript/plugin/health` contract describes the background child that performs durable work. Its health listener runs under the service identity, independently of an open phone, browser, or owner session. CLI and MCP diagnostics read this same contract through the [shared plugin health aggregate](https://rickylabs.github.io/netscript/reference/plugin/#plugin-runtime-health-aggregation).

The exported state union contains: `starting`, `ready`, `degraded`, `crash-looping`, `stopped`, `failed`.

## `ChildHealthState`

Closed background child lifecycle vocabulary.

- `starting`: Bootstrap is in progress; the child cannot accept durable work yet.
- `ready`: Listeners are running, the registry is loaded, and dependencies are ready.
- `degraded`: Listeners lack registry or dependency readiness, or a restart is underway.
- `crash-looping`: The restart threshold is latched; brief successful starts stay unhealthy.
- `stopped`: Clean shutdown completed; processing has stopped.
- `failed`: A fatal bootstrap or listener failure prevents processing.

Example:

```ts
import type { ChildHealthState } from '@netscript/plugin/health';
const state: ChildHealthState = 'starting';
```

## `ChildHealthSnapshot`

Bounded JSON health payload for one background child.

`registryReady` confirms the generated definitions loaded and validated. `dependencyReady` confirms startup checks succeeded and no listener failure is active. `restartCount` is cumulative for this child instance. `lastFatalError` contains only a fixed redacted message and an epoch timestamp; the raw exception and stack never leave the process.

Example:

```ts
import type { ChildHealthSnapshot } from '@netscript/plugin/health';
const health: ChildHealthSnapshot = {
  state: 'starting',
  registryReady: false,
  dependencyReady: false,
  restartCount: 0,
  lastFatalError: null,
};
```

## `ChildFatalError`

Public incident metadata; the original message and stack are deliberately withheld.

Example:

```ts
import type { ChildFatalError } from '@netscript/plugin/health';
const incident: ChildFatalError = {
  message: 'Background child failed (details redacted).',
  timestamp: 0,
};
```

## `CHILD_CRASH_LOOP_THRESHOLD`

Restarts at or above the threshold within the window latch crash-looping.

Recovery requires a full window of uninterrupted running with registry and dependencies ready. A restart or fatal failure resets that running window; a brief ready interval cannot hide a storm.

Example:

```ts
import { CHILD_CRASH_LOOP_THRESHOLD } from '@netscript/plugin/health';
console.log(CHILD_CRASH_LOOP_THRESHOLD.restarts); // 3
```

The current threshold is **3 restarts within 60 seconds**. The window includes its boundary. Only `ready` with both readiness flags true returns HTTP 200 from `/health`; other states return HTTP 503. A healthy API with a stopped or crash-looping child yields `degraded` in plugin doctor and MCP health. A child-only plugin with a dead child is `unhealthy`.

## Authoring and migration in 0.0.8

`declareHealthChecks` and its unused `HealthCheckSpec`, `SagasHealthCheckSpec`, and `TriggersHealthCheckSpec` types were removed. Remove those overrides and type imports from custom Aspire contributions. Keep resource contribution and environment declarations.

The author scaffold declares the API's `doctor.healthEndpoint` in adapter metadata. The generated AppHost uses the service's `HealthCheckPath` appsettings value, defaulting to `/health`. The AppHost generator probes enabled Deno background entries with `RequiresKv: true` at `/health`, regardless of their resource name or entrypoint spelling. Custom children that declare this capability must serve the shared payload. Service health paths remain metadata-driven.

See [Author a plugin](https://rickylabs.github.io/netscript/orchestration-runtime/how-to/author-a-plugin/) for the two-tier scaffold. Dashboard panel rendering and installed-consumer failure experiments are separate acceptance work.
