Skip to content

D · Operations — the agent-authored Operation Plan#73

Closed
pskeshu wants to merge 29 commits into
feature/temp-change-tacticfrom
feature/operations-tab
Closed

D · Operations — the agent-authored Operation Plan#73
pskeshu wants to merge 29 commits into
feature/temp-change-tacticfrom
feature/operations-tab

Conversation

@pskeshu

@pskeshu pskeshu commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Sub-project D (replaces #66, recovering CI). The agent emits a typed Operation Plan; Operations renders it ⊕ live telemetry. Declare tool → store → route → execution-linkage → spine renderer + live binding. Includes the post-testing plan-schema fix (pinned kind/scope/state + validator normalization) and the Operations 'set up an operation' CTA. Whole-branch reviewed; ~105 tests; ruff+format clean. Stacked on feature/temp-change-tactic.

pskeshu added 29 commits June 28, 2026 16:34
…ased bursts

Renders a session-level tactic band on the swimlane SVG when any embryo
carries a `temp_protocol`.  A 28px band is inserted just below the time
axis (embryo rows shifted down by the same amount).  The band contains a
tactic pill spanning `temp_protocol.start → end`, "→ N°C" setpoint-change
chips at each `setpoint_changes[].t`, and a gutter label.  Burst lane rects
and balloon backgrounds are tinted blue/amber/green when the phase field is
before/during/after; null phase renders unchanged.  node --check: pass.
Add set_operation_plan / get_operation_plan to FileContextStore under
agent/operation_plans/{session_id}.yaml, mirroring the session_intents
domain (atomic _write_yaml, parse-cached _read_yaml). set fires
_notify_context_change('operation_plan') → CONTEXT_UPDATED so the
Operations UI live-refreshes.

10 tests: round-trip fidelity (tactics list, states, nested structure,
overwrite, independent sessions, missing→None) + CONTEXT_UPDATED fired
on set (kind='operation_plan') and silent on get.
Adds the declare_operation_plan @tool for the agent to author its typed
Operation Plan (tactics: planned/active/done) per experiment session.

- gently/app/tools/operation_plan_tools.py: @tool decorator, signature
  (title, goal, tactics, updated_reason, context), _validate_tactics helper
  (clamps unknown kinds to 'custom', rejects invalid states), stamps
  updated_at, resolves session_id + context_store via agent in context,
  calls store.set_operation_plan(session_id, plan).
- gently/app/tools/__init__.py: registers operation_plan_tools module.
- tests/test_operation_plan_tool.py: 15 TDD tests — happy path round-trip,
  tactics preserved, unknown-kind clamping, three error paths (no agent, no
  store, no session), two validation-error paths, registry discoverability,
  schema required-fields check.
Adds read-only route returning the agent-authored Operation Plan from
FileContextStore.get_operation_plan(). "current" resolves via
gently_store.list_sessions()[0]. Returns {available, plan, session_id};
graceful unavailable when store is absent or plan not yet written.
Registered in routes/__init__.py alongside other route factories.
Add FileContextStore.transition_tactic(session_id, tactic_id, state=None, **bind)
— reads the plan via get_operation_plan, finds the tactic by id, flips its state,
merges bind kwargs into tactic['live'] (creating the dict if absent), stamps
updated_at/updated_reason, and writes back via set_operation_plan so
CONTEXT_UPDATED fires exactly once.  Returns True on success, False (no-op) when
plan or tactic is absent.  18 TDD tests all pass.
Add optional tactic_id param to enable_monitoring_mode, queue_burst,
stop_timelapse, pause_timelapse (timelapse_tools.py) and
run_temp_change_burst_protocol_tool (temperature_protocol_tools.py).
On execute the tool calls cs.transition_tactic(session, tactic_id,
'active'/'done'/'paused') via the same guarded getattr pattern as
declare_operation_plan. tactic_id is threaded into BurstAcquisition
and the orchestration layer so BURST_START/BURST_COMPLETE and
TEMP_PROTOCOL_STARTED/TEMP_PROTOCOL_COMPLETED event data carry it for
Task 8's OperationPlanUpdater. All params default None; existing
callers are unaffected.
…plan tactics

Bus-subscriber Service that listens for BURST_COMPLETE, TEMP_PROTOCOL_COMPLETED,
EMBRYO_CADENCE_CHANGED, and TRIGGER_FIRED; reads tactic_id from each payload and
calls context_store.transition_tactic to advance state (done) or bind live values.
Wired into agent.py beside the temperature sampler. 8/8 TDD tests pass.
…maging spec)

Add `tactics: list[dict]` to ImagingSpec — each entry is a lightweight tactic
descriptor {kind, name, target?, scope?, structure?} planned alongside the imaging
spec and later seeding the session's Operation Plan.  Persists through both the
YAML FileContextStore and SQLite ContextStore paths with zero store/tool changes.
… linked plan item

Adds seed_operation_plan_from_plan_item(context_store, session_id) in
gently/app/tools/operation_plan_seed.py.

Resolution path: session_id → get_campaign_ids_for_session → get_plan_items
per campaign → find PlanItem where session_id ∈ plan_item.session_ids AND
imaging_spec.tactics is non-empty → get_campaign for goal text.

Idempotent: existing plan with active/done tactics is never clobbered;
plans with only planned tactics get missing outline entries merged in.

19 new tests in tests/test_operation_plan_seeding.py (all passing).
Hook point: agent.py ~455-463 after attach_session_to_plan (not yet wired).
…ata-driven)

Replace the swimlane Overview with a typed Operation Plan spine renderer.
The agent declares tactics (kind/state/structure/live_bind); the renderer
draws done/active/planned nodes with per-kind structure (scripted_protocol
phase stepper, standing_timelapse per-embryo cadence strip, reactive_monitor
armed/watching/fired badge, exclusive_burst/oneshot compact).

Audit fixes baked in: colored left edge per state, active card flattened
(no card-in-card, values on panel face + hairline rule), active phase is
the headline (larger/bold amber), queued = "next" badge + cocked blue dot,
copy "queued" not "going to be used", mono instrument values.

Adds window.OPERATIONS_SCENARIOS (7 plan fixtures: temp_strain,
expression_onset, hatching_detect, transmission_survey, decided_plan,
async_multi, idle). Scenario dev mode: ?scenario=<name> renders a fixture
with no fetch. Loads scenarios.js before experiment-overview.js in index.html.
Rules view and monitoring-mode logic untouched.
- ExperimentOverview now subscribes to CONTEXT_UPDATED + 7 tactic-lifecycle
  events (TEMP_PROTOCOL_STARTED/COMPLETED, BURST_START/COMPLETE,
  EMBRYO_CADENCE_CHANGED, TEMPERATURE_SETPOINT_CHANGED, POWER_RAMP_STEP);
  each fires a debounced (500 ms) loadPlan()+render() refetch.

- _debouncedRefresh(): coalesces rapid bursts into a single fetch+render.
  _subscribed flag prevents double-registration on repeated tab re-clicks.
  Subscriptions are skipped entirely in scenario mode (?scenario=).

- _handleTempUpdate(): TEMPERATURE_UPDATE (~1 Hz) binds water_c/setpoint_c
  directly onto the active scripted_protocol tactic's temperature gauge IN
  PLACE (updates .ops-gv only), with no plan refetch.

- _renderOpsReadout(): stamps data-livebind on each gauge div. Explicit
  r.bind key takes priority over normalised r.label, so display labels
  ("stage temp") and semantic bind keys ("temperature") are decoupled.

- operations-scenarios.js: adds bind:'temperature' to the temp_strain
  stage-temp readout so the in-place selector resolves correctly.
…rd + paused state + docs

- templates.py: add OPERATION_PLAN_GUIDANCE block + inject after REACTIVE_MONITORING_MODES
- agent.py exit_plan_mode: call seed_operation_plan_from_plan_item after link_session_campaign
- timelapse_tools.py queue_burst: gate transition_tactic on result.startswith("Burst queued for ")
- operation_plan_tools.py: add "paused" to _VALID_STATES (pause_timelapse uses it)
- file_store.py transition_tactic: add read-modify-write / single-loop-thread note
- spec: document live.readouts/phases + flat bound keys in §1 tactic schema
- test: add test_queue_burst_soft_reject_does_not_transition (Fix #4 coverage)
…, delete dead swimlane code

Fix #1: _renderOpsTactic now collects all flat keys on live{} (excl.
readouts/phases/target/summary/desc) and renders them as a compact
.ops-livefacts key:value strip for active and done tactics.  Bound
telemetry like sustained_hz, mp4_path, last_fired, request_id is now
visible on the card face instead of being swallowed.

Fix #5: added paused to STATE_LABEL; .ops-node.paused gets a muted
grey dot + solid grey left edge distinct from done/active/planned.

Fix #6: deleted 897 lines of unreachable swimlane dead code:
_renderOverviewView, _renderHeader, _renderLegend, _renderSwimlanes,
_renderLaneRow, _startNowTicker, _updateNowMarker.  render() has called
_renderOperationSpine for the overview path since the spine rewrite;
all deleted functions were confirmed unreferenced outside their own chain.
…alize in validator

OPERATION_PLAN_GUIDANCE now enumerates all six kind values with glosses, the
scope object shape (three modes), tactic state enum, phase state enum, and a
concrete JSON example tactic — replacing vague prose that let a real model
produce wrong shapes.

_validate_tactics now normalizes before validating:
- kind: monitor/reactive→reactive_monitor; timelapse/standing→standing_timelapse;
  protocol/scripted→scripted_protocol; burst→exclusive_burst;
  reference→standing_timelapse; unknown→custom
- tactic state: running/in_progress→active; pending/queued→planned;
  complete/completed→done
- scope: bare list→{mode:embryos,embryo_ids:[…]}; "global"/"all"→{mode:global};
  dict missing mode inferred from role/embryo_ids keys
- live.phases[].state: pending/queued→todo; running/in_progress→active;
  complete/completed→done

@tool schema constrained: SKIPPED — ToolParameter.type is a bare string;
no items/sub-schema field exists; guidance + normalization cover it.

25 new/updated tests; 0 new failures in full suite.
…rst + quick-start chips)

Replace the bare "No operation running" empty state with a call-to-action panel:
primary "Brief the agent" button opens the agent panel; three quick-start chips
(temperature-strain run, standing timelapse, watch for hatching) open the agent
pre-populated with a specific intent via AgentChat.togglePanel + runCommand.
CTA is idle-only — the populated tactic spine is unchanged.
@pskeshu

pskeshu commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #72 — the temperature + Operations/tactics suite and the new Operate (bottom-cam→SPIM) surface are consolidated onto feature/temperature-operations-all (now targeting development). This increment's work is included there; its feature branch was deleted. Closing to keep a single main PR.

@pskeshu pskeshu closed this Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant