Skip to content

owner ticks aren't inspectable like playbook runs (no session id / transcript captured) #75

@anshulsao

Description

@anshulsao

Problem

An owner tick runs a fresh headless claude but captures no session id and discards the model output, so there is no way to answer "what did the last tick actually do / why did it fail?" — unlike a playbook run, which has a pinned session id and a full transcript.

playbook run owner tick
invocation AutoRunArgv: claude --session-id <id> -p … SkipPermissionsRun: claude -p --dangerously-skip-permissions
session id ✅ pinned + recorded ❌ none (sessionless)
transcript ✅ jsonl on disk → flow transcript <task> ❌ none written
model output ✅ rendered cmd.Stdout/Stderr = io.Discard (runSkipPermissions)
status run task status + close-out sweep last_tick_status: ok/error/dead
audit trail full transcript + brief + updates only the journal note the tick writes

Today a tick captures only tick_pid, tick_started, last_tick_at, last_tick_status, and a owners/<slug>/ticks/<ts>.log that holds only flow's own supervisor prints — the actual claude run's stdout/stderr is discarded.

What we lose

  1. No replay of the orchestration decision — why a tick dispatched task X, or decided "nothing to do", is unanswerable.
  2. Opaque failures (sharpest loss) — on last_tick_status='error' the output is discarded, so the failure reason is gone.
  3. No per-tick token/cost attribution.

What softens it (why this is P2, not P1)

The tick's real work is orchestrated through tasks/playbook-runs (orchestrate-never-inline), and those each have a session id + transcript (inspectable via flow transcript / flow show playbook). So downstream work is auditable; only the thin orchestration tick itself is a black box. The journal note (owners/<slug>/updates/<today>-tick.md) is the intended human trail, but it's self-reported and may be empty on error.

Proposed fix

The "fresh session each tick" invariant does not require discarding the id — each tick can mint its own new session id (still fresh, never resumed) and record it:

  • Switch the headless tick from SkipPermissionsRun (discard) → a session-pinned headless run (like AutoRunArgv: claude --session-id <new-id> -p …).
  • Record last_tick_session_id on the owner row.
  • Surface it: flow transcript owner <slug> renders the last tick; flow owner show shows it.

Scoping

  • Minimal: pin + record last_tick_session_id, expose via flow transcript owner <slug>.
  • Fuller: a per-tick run record (session id + status + timestamp) shown as a "recent ticks" list in flow owner show, mirroring how playbook runs render.

Relevant code

  • internal/harness/claude/claude.gorunSkipPermissions (discards output), AutoRunArgv (the session-pinned pattern to mirror)
  • internal/app/owner_tick.goownerTickRunner, cmdOwnerTick, recordOwnerTick
  • internal/flowdb/owners.go — owner row (would add last_tick_session_id)

Context: surfaced while reviewing the owner harness (#71).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions