Skip to content

feat(logs): run identity, transient definitions, and real session provenance - #423

Merged
jahvon merged 6 commits into
mainfrom
feat/run-identity-and-session-provenance
Jul 28, 2026
Merged

feat(logs): run identity, transient definitions, and real session provenance#423
jahvon merged 6 commits into
mainfrom
feat/run-identity-and-session-provenance

Conversation

@jahvon

@jahvon jahvon commented Jul 27, 2026

Copy link
Copy Markdown
Member

Summary

Execution history could not answer three questions it should have been able to: which run is this, what did it actually do, and who ran it. Each gap traced to something already stored but not exposed, or not captured at all.

Notable Changes

  • id and completedAt now reach flow logs -o json. Both were on the stored record and dropped by the output DTO, leaving consumers with no stable per-run handle and no way to tell when a run finished — only how long it took.

  • Transient --spec runs record their definition. --cmd recorded its command, but --spec passed an empty string, so a spec run left behind a ref naming an executable that was never written to disk and nothing at all describing what it did. Adds Spec to the record.

  • Each stdio MCP server process gets a real session id. mcp-go reports the literal string "stdio" as the session id for every stdio connection (server/stdio.go) — it identifies the transport, not a session.

  • A client can read its own session id from get_info. Over stdio the id exists only inside the server, so a client had to infer which records were its own from timing.

  • workingDir records where a run executed. The ref names the workspace but not which checkout of it — sibling git worktrees produce byte-identical refs from different directories, and an ad-hoc run's --dir appeared nowhere. Defaults to the process cwd; ad-hoc runs override with their resolved --dir.

jahvon and others added 5 commits July 27, 2026 17:45
`flow logs -o json` served a DTO that dropped `id` and `completedAt` even
though both sat on the stored record, so consumers had no stable per-run
handle and no way to tell when a run finished — only how long it took.

Transient runs were worse. `--cmd` recorded its command, but `--spec` passed
an empty string, so a spec run left behind a ref naming an executable that was
never written to disk and nothing at all describing what it did. Recording the
spec closes that: every run can now say what it executed.

Threading a third string through recordRunStart/recordExecution would have made
four positional strings at each call site, so command/spec/label move into a
transientMeta struct, mirroring the existing provenance struct.

The text metadata block moves into printRecordMetadata — adding the spec line
pushed PrintLastRecord past the cognitive-complexity limit, and the block was
already a long run of near-identical optional-field branches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mcp-go reports the literal string "stdio" as the session id for every stdio
connection (server/stdio.go) — it identifies the transport, not a session. Taken
at face value it meant every run from every client, across every conversation,
was recorded under one session id, making `flow logs --session` useless and
leaving history with no way to tell one agent session from another.

flow's MCP server is stdio-only and a client spawns its own `flow mcp` process
per connection, so the process is exactly the session boundary. A UUID resolved
once per process supplies the id the transport can't.

Substitution only kicks in when the transport gives us nothing usable, so a
future transport issuing genuine per-connection ids keeps them.

Records written before this still carry "stdio"; consumers grouping by session
need a fallback for them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Source distinguished a terminal from an agent, but not a terminal from a GUI: a run
launched by clicking Execute in a desktop app recorded as "cli", because that is what
flow assumes when nothing says otherwise. Anything embedding flow behind a UI was
therefore invisible as a distinct origin in its own history.

Source is compared as a plain string throughout — the filter does an EqualFold, not a
lookup against a fixed set — so the vocabulary is open by construction. The constants
say which values flow itself produces; an embedder can record its own without a change
here. The comment now says that, since the previous wording ("the recognized values")
read as a closed enum it never was.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A client had no way to learn the session its runs would be tagged with. Over stdio the
transport carries no identity, so the id exists only inside the server — leaving a client
to infer which history records were its own from timing, which is exactly the guesswork
the id exists to remove.

get_info already reports the connection's context and is the documented first call, so
the id belongs there. The server has always known it; `get_execution_logs --mine` resolves
the same value internally. This only makes it readable.

The point is to keep conversation identity out of flow entirely. A client that wants to
group runs by conversation reads this once per connection and keeps its own
conversation -> session mapping; flow stores a session, and never needs to know what the
caller does with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The ref names the workspace, so that was never missing — but not which checkout of it.
Sibling git worktrees produce byte-identical refs from different directories, and an
ad-hoc run's --dir does not appear anywhere at all. History could say a run happened in
`mochi` while being unable to say which mochi.

The directory is the one correlation signal a client cannot reconstruct from what was
already stored, which is why it earns a field where the workspace name would only have
duplicated the ref.

Defaults to the process working directory — the caller's cwd, which for an agent or
editor is the project it is working in. Ad-hoc runs override it with their resolved
--dir, so the record names where the command actually ran rather than where flow
happened to be invoked from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.28571% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/internal/exec.go 44.44% 10 Missing ⚠️

📢 Thoughts on this report? Let us know!

@jahvon
jahvon merged commit 300e31e into main Jul 28, 2026
21 checks passed
@jahvon
jahvon deleted the feat/run-identity-and-session-provenance branch July 28, 2026 02:04
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