All writing

Optional intelligence needs hard failure boundaries

Keep semantic enrichment from taking down core processing by modeling dependencies, fallbacks, and degraded results explicitly.

An AI enrichment can improve a result without being required to produce it. If the system treats that optional step like a core dependency, a model timeout can block ingestion, reporting, or an otherwise valid user response.

Optional does not mean unobserved. It means the workflow has an explicit degraded mode: core facts still complete, unavailable enrichments are named, and retries do not repeat work that already succeeded.

Draw the dependency graph in code

This section separates stages whose output is required from stages that only add interpretation. The workflow cannot enforce a failure boundary if every task is hidden inside one promise chain.

A typed result makes the dependency graph visible to callers:

export type StageResult<T> =
  | { status: 'available'; value: T }
  | { status: 'unavailable'; reason: string; retryable: boolean };

export type CoreFacts = {
  subjectId: string;
  metrics: Record<string, number>;
  sourceVersion: string;
};

export type EnrichedResult = {
  core: CoreFacts;
  narrative: StageResult<string>;
  semanticLabels: StageResult<string[]>;
};

The core value is not wrapped in StageResult because failure to produce it means the pipeline did not complete. Optional fields are always present as stateful results, so consumers cannot confuse “not attempted,” “failed,” and an empty successful response.

Avoid using null for every degraded case. A timeout that should retry and a rejected response that should not retry demand different operational behavior.

Settle optional stages independently

This section runs enrichments after core processing succeeds. Each optional stage gets its own deadline, error classification, and result state instead of sharing the core failure path.

The core stage remains outside that boundary. A missing or invalid fact set should fail loudly rather than returning a degraded object that appears usable. Optional stages can run concurrently when independent, but their failures should settle separately.

Each optional call also needs a deadline. A fallback that waits indefinitely is not a failure boundary; it is only a different place to hang.

Persist successful work before retrying failures

This section gives retries a checkpoint so one unavailable enrichment does not recompute core facts or another enrichment that already completed.

Record completion independently for the core artifact and each enrichment. On resume, read successful artifacts and run only missing or retryable stages. The checkpoint and artifact write must be coordinated so a crash cannot mark a missing artifact as complete. An idempotent artifact write followed by a checkpoint update is a practical sequence because retrying after either operation remains safe.

Retry policy should depend on failure classification. Rate limits and transient network failures may merit delayed retries, while schema violations usually need investigation or a different model version rather than repeated identical calls.

Let consumers acknowledge degraded state

This section prevents a downstream API from flattening unavailable enrichment into misleading empty content. The response can remain successful while describing which capability is ready, pending, or unavailable.

Do not expose internal provider messages or stack traces in the public response. A bounded capability status is sufficient for an interface to hide a section, show a temporary-state message, or use a deterministic fallback. “No narrative exists” and “narrative generation failed” should remain different states even when neither renders text.

Observe degraded responses as a product metric. Core success can keep availability high while optional intelligence silently disappears for every user; capability-level counts reveal that failure without turning it into a global outage.

Optional intelligence should improve the happy path without owning the system’s availability. Hard boundaries make degradation visible, retryable, and local instead of allowing one probabilistic dependency to become a global outage.