# Orchestration Safeguards

This document defines the step-not-found safeguard used by orchestration paths in
Shell code. The intent is strict: missing steps must produce explicit failure and
operator-facing escalation details, never a silent success path.

## Step-Not-Found Safeguard Flow

Primary entry points:

- `lattice.shell.orchestration.resolve_orchestration_step`
- `lattice.shell.orchestration.run_orchestration_step`

Execution flow for `run_orchestration_step`:

1. Resolve requested step ID against `step_handlers`.
2. If missing, call `lattice.shell.reconciliation.reconcile_missing_step`.
3. If reconciliation fails, build escalation text with
   `lattice.shell.error_handler.build_step_not_found_escalation`.
4. If reconciliation resolves to a step without a registered handler, treat as
   hard failure and escalate.
5. If a valid handler is resolved, execute it and return the handler result.

Invariants:

- Missing step cannot yield implicit success.
- Reconciliation is explicit and auditable via `actions` in reconciliation output.
- Escalation formatting is deterministic for operators and tests.

## Failure Classification

The safeguard distinguishes three operational classes:

1. **Critical step missing (fail-fast):**
   `reconcile_missing_step` returns `Failure[str]` immediately when `step_id` is in
   `critical_steps`; no fallback substitution is permitted.
2. **Recoverable missing step (reconciled):**
   direct fallback (`step_id -> fallback`) or phase default (`<phase>.default`) can
   resolve the step.
3. **Unrecoverable missing step (escalation required):**
   no fallback available, or reconciliation resolves to an ID that has no runtime
   handler in `step_handlers`.

Return semantics from `run_orchestration_step` are strict:

- success path: `Success(handler_output)`
- safeguard failure path: `Failure(str)` containing escalation-ready details

## Escalation Semantics

Escalation payloads are composed by
`lattice.shell.error_handler.build_step_not_found_escalation` and include:

- workflow ID
- phase
- missing step ID
- reason text (critical violation, no fallback, or handler mismatch)
- attempted actions list (or `none`)

If `escalation_handler` is provided to orchestration APIs, the message is emitted
through that callback. Otherwise callers can route message output via
`lattice.shell.error_handler.emit_escalation`.

## API Boundaries and Ownership

Boundary split:

- `lattice.shell.orchestration` owns orchestration-time resolution + execution.
- `lattice.shell.reconciliation` owns fallback/phase-default strategy logic.
- `lattice.shell.error_handler` owns deterministic escalation message formatting.

Contract boundary:

- Step handlers conform to `StepHandler = Callable[[], Result[Any, str]]`.
- Orchestration safeguard APIs return `Result[..., str]` and do not raise for normal
  missing-step control flow.

Operational note:

- `src/lattice/shell/orchestration_errors.py` contains related error datatypes and
  helper flows used for explicit step-not-found modeling in Shell.

## Implementation and Test References

Implementation:

- `src/lattice/shell/orchestration.py`
- `src/lattice/shell/reconciliation.py`
- `src/lattice/shell/error_handler.py`
- `src/lattice/shell/orchestration_errors.py`

Verification tests:

- `tests/shell/test_orchestration_step_not_found.py`
  - registered-step success path
  - fallback reconciliation path
  - critical-step fail-fast escalation path
  - reconciled-without-handler escalation path

## Operational Usage

For new orchestration call sites:

1. Define `step_handlers` with concrete `Result`-returning handlers.
2. Define `fallback_registry` (including optional `<phase>.default` entries).
3. Mark hard requirements in `critical_steps`.
4. Invoke `run_orchestration_step(...)` and branch only on `Success` vs `Failure`.
5. Route `Failure` messages to operator logs/CLI via `emit_escalation` or a custom
   escalation callback.

Recommended local checks:

- `invar guard --changed`
- `python -m pytest tests/shell/test_orchestration_step_not_found.py`
