Learn

Observability without routine approval

How can a human remain informed about autonomous work without becoming a permission gate for every action?

Removing routine approvals should not mean removing visibility. A system can allow work to continue without synchronous human permission while independently exposing enough state for a human to inspect what is happening.

Observation is a control in its own right: a way to inspect autonomous work. It is not proof of safety, and not a substitute for containment or for human acceptance of results.

Approval is not observation

Human control over autonomous systems is often collapsed into a single phrase (“human in the loop”). That phrase hides four different controls. They answer different questions and act at different moments.

Four control types. Not every deployment exposes every intervention mechanism.
Control What happens to execution
Approval Waits for human permission before proceeding
Observation Continues while a human can inspect
Intervention Human may stop or change the run where control exists
Acceptance Decision occurs after work exists

The central distinction:

observation is not approval · execution is not acceptance

Why live visibility matters

OpenTendril’s current Objective requires that a Botanist can grant an LLM full freedom inside a Terrarium, be asked for nothing while it works, and still receive reviewable Git output, with nothing outside the declared boundary touched. One of the conditions that series of runs must carry is whether the run could be watched while it happened, not only reconstructed afterwards.

The Objective states the tension deliberately:

stop being asked + keep being able to look

These are opposite operational requirements. Being asked for nothing is only tolerable if looking remains available. An unobservable run is not framed here as automatically insecure. It fails the current OpenTendril delegation objective: it is the blindness that makes people return to approving every step. Details of measurement live on the experiment page.

What useful execution observability contains

For governed autonomous systems, useful observation tends to fall into durable categories. These are general categories, not a claim that every product field exists in every system, and not a claim that OpenTendril currently records every one of them completely.

Lifecycle state

Whether work has started, is running, finished successfully, failed, or is stalled.

Actions and commands

What tools or commands ran, with arguments and outcomes where recorded.

Files affected

Which paths changed, when the workspace can measure it.

Model and provider

Which mind carried the run, where resolution is known.

Network attempts

Whether external reach was attempted, and under which policy.

Resource usage

Time, tokens, memory, process limits, where measured.

Credentials and capability activity

Which operation classes were authorised or denied, where audited.

Outcome and history

Terminal verdict of the run, plus a durable record for later inspection.

Desired measurements in an objective document are not the same thing as implemented telemetry. The next sections describe only what OpenTendril currently exposes.

OpenTendril’s current observation model

In OpenTendril, sprout.grow is a synchronous capability: it answers when the run matures or withers. While that wait proceeds, observation surfaces can be watched:

  • the /ws EventBus stream;
  • Sprout-run history endpoints.

sprout.watch is the delegated view authority for that observation. Those surfaces are independent of routine step approval. A run can execute without pausing for human permission on each action while still emitting lifecycle and action events.

Watching is independent of approval; handoff and acceptance come after work exists. See Git and human acceptance.

Current implementation surfaces (verified on the product default branch):

  • EventBus: in-process pub-sub with typed events (for example sprout-emerged, sprout-matured, sprout-withered, sprout-detached, sprout-dormant, tool-invoked, sprout-transcript, terrarium resource events, delegation authorised/denied, hormonal-trigger blocked). Sinks persist events and feed remote transporters; a slow sink does not block publish.
  • /ws: WebSocket gateway over the EventBus. Optional ?replay=N prepends a short in-memory recent history (bounded, not a full archive). The same surface is mounted on the main API mux and a standalone gateway listener.
  • History store: SQLite at .tendril/history.db when enabled (default). Records sessions, chat messages, EventBus rows, and Sprout-run records (status, transcript, model, genotype, substrate, dispatching Pollen, timings, output/error). Can be disabled for headless runs; can prune by configurable retention.
  • REST observation: per-session (phytomer) endpoints for chat history, persisted events, and sprout-run lists. When history logging is off, those history reads return not implemented rather than inventing state.
  • sprout.watch: a real operation-class used for delegated observation authority: who may watch stored run records, persisted events, and the live stream for phytomers they own. It is deliberately not a command projected onto every surface the way sprout.grow is; it authorises the view without authorising growth. The operator (Stem key, no Pollen) retains the full view.

Run results also report lifecycle status (matured / withered), a refined outcome (for example complete, no-changes, failed, timed-out), files modified when measurable, and the resolved provider/model. That is what a surface records, not a guarantee that every useful category above is always fully populated.

Greenhouse

Greenhouse is the Command Center UI: the observation surface a Botanist watches through. Taxonomy: external observability UI, not the Stem and not the worker.

The current UI (React app under ui/) is a decoupled client of the Stem’s HTTP and WebSocket contracts. What it actually presents today:

  • connection onboarding with Botanist bearer key;
  • session (phytomer) rail: list, create, preference updates, prune;
  • live EventBus connection status and event ticker;
  • garden-style visualisation driven by streamed events;
  • chat panel posting into a session;
  • drill-down on a Sprout run from history: status, genotype, model, origin, step id, timings, output/error text, and related phenotypic-selection fitness fields when present.

It re-hydrates cold state from REST after load or reconnect, and uses /ws?replay=… as a bounded supplement for recent session-less sequence telemetry. It is not a full command-replay product, cost dashboard, credential graph, universal kill switch, or forensic timeline. Those should not be assumed from the Greenhouse concept alone.

Observation and intervention are different

Seeing a run does not grant its next action, and does not accept its result.

observe ≠ approve · observe ≠ accept · observe ≠ necessarily intervene

Current OpenTendril observation surfaces are read-oriented: stream, history, and Greenhouse drill-down. There is no verified universal operator “kill switch” in the Greenhouse UI for arbitrary live intervention.

Lifecycle machinery does include internal endings: growth budgets that stop waiting (detach) rather than always killing work immediately, orphan reaping when nothing still waits on a run, terrarium timeouts, and Hormonal Triggers that can abort before a Sprout emerges. Those are system controls and pre-execution gates, not a general claim that every human observer can stop or rewrite any running Sprout from the dashboard.

If you need the acceptance half of the story (whether the resulting code becomes project state), that is Git handoff, not observation.

Historical record versus live stream

Both matter, and they answer different questions:

  • Live: what is happening now? (/ws EventBus feed)
  • History: what happened, and what was the outcome? (history.db + REST)

Live stream alone is ephemeral without a sink. History alone is retrospective; under the current Objective it is not a full substitute for being able to watch while work runs. The in-memory bus window used for ?replay= is short and lossy by design. Durable rows live in SQLite when logging is enabled, and optional retention pruning means those rows are not described here as immutable audit logs.

Report, never decide

Observation reports; acceptance decides

OpenTendril’s job under the Objective is to hold the boundary and to say what happened. It does not decide whether the work was good. A Botanist may observe a perfectly contained run that still produces bad code. That is a work-quality issue, not necessarily a containment failure.

Conflating “we saw the run finish” with “we should keep the change” is the same scope error as conflating containment with correctness. See the experiment, Git and human acceptance, and containment.

Limitations

  • Visibility is only as good as the events and records actually emitted.
  • Telemetry can have blind spots; absence of an event is not proof of absence of action.
  • Retrospective records alone do not satisfy the Objective’s live-watch requirement.
  • History can be disabled or pruned; do not assume complete or immutable auditability.
  • Observation does not guarantee containment.
  • Observation does not make output correct or worth accepting.
  • Delegated watchers see only what ownership and sprout.watch grants allow, not necessarily the whole fleet.