Operators Guide

Operate Action-owned Observed State

Operate raw admission, typed collections, difference policies and independent execution verification.

Operate action-owned observed state

Development status: raw admission, typed normalization, durable decisions, approval/automatic dispatch and shared REST/GraphQL/UI reads are implemented. Synthetic runtime/server/DNS workflows are verified; native execution, real-provider and release qualification remain separate. Unsupported persisted state refuses startup without conversion.

Start with the action declarations. Acquisition belongs to your runtime or app; normalization belongs to the controller. Neither successful HTTP admission nor successful normalization proves a provider operation has finished.

Unsupported stores and orphan execution records refuse startup. Do not delete state or patch compatibility stamps to bypass refusal.

Observation intake is HTTP-only. Result uploads keep their ordinary storage and state-file behavior; filenames and JSON contents do not select producers or grant scope. Submit raw inputs explicitly. Verification claims belong only on the raw-input endpoint; result uploads reject that header before reading the body. A runner failure or disconnect does not invalidate unrelated independently acquired observations.

Prepare raw evidence

The example uses invented provider data and no credentials:

{
  "version": 1,
  "producer": "cloud-servers:actions/inventory.toml",
  "submission_id": "11111111-1111-4111-8111-111111111111",
  "read_at": 1793692800,
  "collections": {
    "server": {
      "coverage": "complete"
    }
  },
  "payload": {
    "instances": [
      {
        "id": "vm-739",
        "name": "server-a",
        "vcpus": 4,
        "network_ids": [
          "network-9"
        ]
      }
    ]
  }
}

Use a new UUID for every independent read, and use the time the provider was read. The example helper creates both without contacting a provider:

"""Emit a fresh synthetic raw input, without provider or controller calls."""
import json
import time
import uuid
from pathlib import Path

report = json.loads(Path(__file__).with_name("report.json").read_text())
report["submission_id"] = str(uuid.uuid4())
report["read_at"] = int(time.time())
print(json.dumps(report))

Submit JSON to POST /api/v1/observation-inputs on the existing authenticated listener on this development branch. A 202 means durable raw admission only. It is not normalization success, approval, dispatch or finality. Retry an uncertain admission with the same UUID and unchanged envelope, rather than creating a new read or changing its time.

Do not submit desired values as an observation, turn acquisition errors into empty inventory, or claim completeness before pagination finishes.

Coverage and failures

  • complete replaces collection membership, including explicit complete empty.
  • partial replaces reported rows and retains unreported rows with their old timestamps and validity.
  • A missing collection does not refresh its evidence.
  • Failed/invalid collections retain unusable historical rows.
  • Recovering a reported partial row does not resurrect unreported failed rows.
  • Older read times cannot replace newer facts; controller sequence breaks equal-time ties.

Desired matching requires exact typed keys. Duplicate names/keys, missing matching fields, incompatible values, stale evidence and configuration changes must remain explicit rather than becoming correction commands. Matching uses row read times, not the latest partial collection timestamp. Comparison freshness defaults to 60 seconds unless action-local finality specifies max_age.

Review without causing work

Reads must not normalize, acquire provider data, approve, enqueue or dispatch. Use /drift, GET /api/v1/observations/{kind} (collections, resources, differences, decisions), or GraphQL observedState. All share pinned context, typed values/provenance and revision-bound pagination. Use collection/provider-key filters for runtime dependency lookup. There is no raw-payload read endpoint.

Desired-only and observed-only rows are inventory information, not permission to adopt, create or delete resources. DNS Push and Pull are separate actions with shared conflict protection: Push changes provider state; Pull changes intent through an explicit mutation contract. Merely reading inventory must do neither.

Enable difference policy deliberately

CE/EE default to report_only. The --reconciliation-ceiling approval setting permits explicit approval and caps auto declarations at approval; automatic permits action-declared automatic correction. This ceiling applies only to difference policy, not ordinary manual/calendar/startup auto_execute work.

The controller persists decisions before dispatch. Approval binds configuration, watched values, provider identity and independent finality target. Use the displayed configuration/material/revision for approve/reject commands. Changed context requires refresh and review, not automatic command retry. Pause restricts work; it cannot raise the ceiling. Equivalent reports/restart do not duplicate work. Rejection suppresses the episode, including changed observations, until fresh valid convergence rearms it. Previous possible work must also have closed. Competing automatic actions for one logical scope are held rather than choosing a winner.

Approving or enabling automatic policy can execute infrastructure-changing module code. Qualified protected runtime, explicit resource scope, current evidence and action-local finality are still required. See the API reference.

Protect possible work

Preserve queue records, execution receipts, frozen context, observation history and scope journals together. Never clear a hold because values match or an action exited, timed out, disconnected or was cancelled.

Finality must attribute fresh independent evidence to the exact captured execution, including provider terminality and runner/root/descendant/resumable terminality in the same read. Finalization and journal scope closure are separate durable decisions. No automatic resend/retry is allowed after possible work.

Action-local finality, protected dispatch and independent read attribution use captured action-owned terms. A desired-only intent update does not prevent original execution verification when captured observation/static material is unchanged. Frozen identity, recipe/software, barriers/windows and independent terminal assertions still apply; approval/dispatch remain current-full-configuration bound. Changing producer/runtime material can block new proof for held work: preserve compatible material rather than renewing terms or resending. Unsupported state is refused without rewriting it. Frozen normalization recipes now carry evaluator compatibility stamps. A different source/dependency/compiler/target build can refuse retained recipes before reopen, pruning or replay; a mismatched worker cannot replace existing collections. Older development state without the stamp is refused too. Compatibility stamps are not binary attestation. Do not edit stamps or discard retained inputs to bypass refusal.

Changing declaration syntax is not a state migration. Keep the complete prior state and compatible controller; do not remove state files to bypass refusal.

See the reference for implemented limits and the authoring guide for declaration placement.