Skip to content

feat(workflows): display full run ids consistently and reshape run cards - #2140

Merged
flora131 merged 11 commits into
mainfrom
fix/full-run-id-display
Aug 2, 2026
Merged

feat(workflows): display full run ids consistently and reshape run cards#2140
flora131 merged 11 commits into
mainfrom
fix/full-run-id-display

Conversation

@flora131

@flora131 flora131 commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Problem

Run identifiers were rendered at three different lengths by four separate constants plus a pile of hardcoded slices, so the same run read differently depending on where you looked:

tui/widget.ts             SHORT_ID_LEN = 6
tui/status-list.ts        SHORT_ID_LEN = 6
tui/run-detail.ts         SHORT_ID_LEN = 6
tui/dispatch-confirm.ts   SHORT_ID_LEN = 8
shared/prompt-attribution.ts  SHORT_RUN_ID_LENGTH = 6
extension/*.ts            runId.slice(0, 8)    (~10 sites)
workflow-targets.ts       id.slice(0, 12)      (ambiguity messages)

Reading a1b2c3 in the BACKGROUND widget and a1b2c3d4 in a tool message meant mentally aligning prefixes to confirm they were the same run.

What ships

Full 36-character UUID in every user-facing surface. All short-id constants and hardcoded truncations are removed rather than unified at some shorter length.

Run cards become two lines, since a full UUID plus a workflow name does not fit beside a glyph at the 80-column collapse breakpoint:

 ●  339e05a4-2289-408e-9076-d1a348f582ae
    stage-output-transcript · chain · 3/8 · 12m

 ? d4e5f6a1-77b2-4c31-9e0a-2f1c8b4d6e5f
    build-check · single · 1/2 · 3m

The prompt card is an attribution banner only. Its border title is just AWAITING INPUT, and the body carries the same two identity rows as the widget through a shared renderer. The prompt question and its options continue to render through the existing prompt UI, unchanged — the banner never duplicates them.

Input now matches display: full UUIDs only. The original revision changed rendering alone and left prefix resolution in place, which made the contract asymmetric — the UI only ever printed a 36-character id while the resolver still answered to six characters of it. Run targets now require the full 8-4-4-4-12 hex UUID everywhere runId is accepted. Prefix scanning is gone from the live resolver, the durable DBOS catalog, the resume-target lookup, and inspectRun.

A malformed target is reported separately from a missing one, so a truncated paste is diagnosable as truncated rather than looking like a stale run:

$ /workflow resume 339e05a4
Run id must be a full 36-character UUID; got "339e05a4" (8 chars).

$ /workflow resume 339e05a4-2289-408e-9076-000000000000
Run not found: 339e05a4-2289-408e-9076-000000000000

Because run ids are unique and now matched exactly, run-level ambiguity is unreachable by construction. The ambiguous result variant was replaced by malformed at every call site rather than deleted, so each existing error branch and its coverage stayed intact.

Stage targeting is exact, but deliberately not 36-bound. Stage identifiers are not uniformly bare UUIDs: virtualNodeId builds a nested stage id as the runId:nodeId composite (73 characters), and a ctx.tool node carries tool:<argsHash>. Applying the length rule to the stage axis would have made every nested stage and every tool node untargetable, so that axis keeps exact id and exact name matching and loses only prefixes and partial names — build no longer selects build-check. Nested-stage and tool-node targeting are unchanged and now have explicit coverage.

The /workflow connect picker keeps prefix search in its filter box on purpose: it narrows a list the user then selects from, so it never targets a run by a truncated id.

Height behaviour

Doubling card height matters because the four-run display cap was removed in #2134, so every qualifying run now renders. Two review findings came directly from that pressure and are fixed:

  • Scroll offsets are computed against the question's own row space. The banner is fixed chrome outside the scrollable region rather than a compensating offset term, so the last question row stays reachable at every height.
  • The banner yields to the question. It degrades two identity rows → one row carrying the run id → no banner, and is never rendered beside a question that has zero visible rows. Decoration does not win a height contest against content.

The primitive input/editor prompt path now walks that same ladder; it previously built a single banner and fell straight through to unattributed, so at the one height where the middle rung applies, attribution vanished even though the run id alone would have fit. Measured for both prompt kinds at width 100: rows ≤ 9 no banner, row 10 a one-row banner with the run id, rows ≥ 11 both identity rows.

Verification

  • npm run check — clean
  • npm run test:unit — 615 files, 5739 passed, 1 skipped
  • npm run test:integration — 36 files, 485 passed
  • npm run test:ci-contracts — 5 files, 38 passed
  • Tests assert the full 36-character value rather than a prefix, so they cannot pass under the old truncating behaviour
  • Box borders are asserted intact across a viewport sweep for every prompt kind

On the fixture churn. Roughly 600 test sites used short ids such as "run-1" and "r1", which are no longer valid input, so 50 files change. They derive stable UUIDs from readable seeds through testRunId(seed) rather than hardcoding opaque literals, so each fixture's intent still reads at the call site. Tests that encoded the old contract were inverted rather than deleted: a typed prefix must now be rejected, and the full id must still resolve. No test-only bypass was added to the resolver.

One unrelated fix is included. mock-extension-api-rendering / mock-extension-api were already failing on this branch before this revision, on the dispatch-card test. The connect hint styles its dim prefix and accent id as separate ANSI spans, so a contiguous regex could not match the styled string; the rendered output was correct for a user the whole time. That assertion now runs against the plain render. Integration therefore goes from 2 failing to fully green.

Note for reviewers

PR #2135 adds a queue counter for concurrent prompts. Under this layout it belongs on the second identity row beside the workflow name (build-check · 1/2), in the same meta position the widget uses for mode, progress and elapsed time. That branch should rebase onto this one.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Greptile Summary

This change displays full workflow-run UUIDs and adjusts workflow cards, prompt attribution, and the connect picker layout for the wider identifiers. The five-run connect-picker viewport is documented to support mouse-wheel scrolling, but wheel input does not move selection, preventing mouse users from reaching runs outside the visible rows.

Confidence Score: 4/5

T-Rex T-Rex Logs

What T-Rex did

  • Reproduced the six-row session-picker mouse-wheel workflow for the posted P1 finding and captured a reproduction source along with baseline and SGR wheel-down actions.
  • Reviewed the second P1 finding comment and noted its validation steps and alignment with the expected behavior.
  • Analyzed the contract-validation notes and confirmed that wheel handling for the session picker is not implemented in the picker itself and that wheel input is forwarded to the overlay handler.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (2)

  1. packages/workflows/src/tui/session-picker.ts, line 402 (link)

    P1 Mouse-wheel navigation is ignored

    The five-row picker is documented to scroll with the mouse wheel, but this handler only recognizes arrow and j/k navigation. With six runs, a terminal SGR wheel-down event (\x1b[<65;10;10M) falls through to this noop return and leaves the selection unchanged, so the off-viewport run cannot be reached with the documented mouse interaction. Parse wheel-up/down events and apply the same bounded selection updates as the keyboard navigation paths.

    Artifacts

    Executable session-picker mouse-wheel reproduction source

    • Directly constructs six picker rows, verifies the five-row viewport, then executes both arrow and SGR wheel input cases against the real handler; it provides a focused reproducible proof.

    Arrow-down baseline executed against the six-row session picker

    • The direct baseline command shows `ESC [ B` changes the selected index from 0 to 1, establishing the handler's expected keyboard navigation path.

    SGR mouse-wheel-down executed against the six-row session picker

    • The direct reproduction command shows `ESC [ < 65 ; 10 ; 10 M` returns noop and keeps selection at 0 despite an off-viewport sixth row, confirming the defect.

    Existing session-picker unit test run

    • The scoped Vitest suite completed with 15 passing tests after the reproduction, showing the current suite lacks this wheel-navigation coverage.

    View artifacts

    T-Rex Ran code and verified through T-Rex

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/workflows/src/tui/session-picker.ts
    Line: 402
    
    Comment:
    **Mouse-wheel navigation is ignored**
    
    The five-row picker is documented to scroll with the mouse wheel, but this handler only recognizes arrow and `j`/`k` navigation. With six runs, a terminal SGR wheel-down event (`\x1b[<65;10;10M`) falls through to this `noop` return and leaves the selection unchanged, so the off-viewport run cannot be reached with the documented mouse interaction. Parse wheel-up/down events and apply the same bounded selection updates as the keyboard navigation paths.
    
    ---
    
    For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
  2. General comment

    P1 Advertised mouse-wheel navigation is ignored by the workflow session picker

    • Bug
      • With six rows, the renderer exposes only runs 0-4 in its five-row viewport. The representative terminal SGR wheel-down input \x1b[<65;10;10M reaches handleSessionPickerInput but returns { kind: "noop" } and leaves selectedIndex at 0, preventing wheel navigation to the additional run.
    • Cause
      • handleSessionPickerInput in packages/workflows/src/tui/session-picker.ts:344-403 recognizes only arrow and j/k navigation at lines 385-394; it neither parses SGR mouse input nor maps vertical wheel events to selection movement. The remaining fallback at line 402 returns noop.
    • Fix
      • Parse supported terminal wheel sequences (at least SGR button 64/65 press events) before the fallback and map up/down to the same bounded selection updates as Key.up/Key.down; add direct tests for both wheel directions with more than five rows.

    T-Rex Ran code and verified through T-Rex

Prompt To Fix All With AI
### Issue 1
packages/workflows/src/tui/session-picker.ts:402
**Mouse-wheel navigation is ignored**

The five-row picker is documented to scroll with the mouse wheel, but this handler only recognizes arrow and `j`/`k` navigation. With six runs, a terminal SGR wheel-down event (`\x1b[<65;10;10M`) falls through to this `noop` return and leaves the selection unchanged, so the off-viewport run cannot be reached with the documented mouse interaction. Parse wheel-up/down events and apply the same bounded selection updates as the keyboard navigation paths.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix(workflows): complete the banner ladd..." | Re-trigger Greptile

@mintlify

mintlify Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
bastani 🟢 Ready View Preview Aug 2, 2026, 5:42 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

flora131 added a commit that referenced this pull request Aug 2, 2026
…olution

PR #2140 made every display surface render the full run UUID but left input
resolution accepting typed prefixes, so the contract was asymmetric: the UI
only ever printed a 36-character id while the resolver still answered to six
characters of it.

Run targets now require the full 8-4-4-4-12 hex UUID. Prefix scanning is gone
from the live resolver, the durable DBOS catalog, the resume-target lookup, and
inspectRun. A target that is not a well-formed UUID is reported as malformed
rather than missing:

  Run id must be a full 36-character UUID; got "339e05a4" (8 chars).

That is deliberately distinct from `Run not found:` so a truncated paste is
diagnosable as truncated instead of looking like a stale run.

Because run ids are unique and now matched exactly, run-level ambiguity is
unreachable by construction. The `ambiguous` result variant is replaced by
`malformed` at every call site rather than deleted, which keeps each existing
error branch and its coverage intact.

Stage targeting is exact but deliberately NOT held to 36 characters. Stage
identifiers are not uniformly bare UUIDs: virtualNodeId builds a nested stage id
as the `runId:nodeId` composite (73 chars), and a ctx.tool node carries
`tool:<argsHash>`. A blanket length rule would have made every nested stage and
every tool node untargetable, so the stage axis keeps exact id and exact name
matching and only loses prefix and partial-name matching.

The session-picker filter keeps prefix search on purpose: it narrows a list the
user then selects from, so it never targets a run by a truncated id.

Fixtures across 50 test files carried short ids like "run-1" that are no longer
valid input. They now derive stable UUIDs from readable seeds via
testRunId(seed) rather than hardcoding opaque literals, so each fixture's intent
still reads at the call site. Tests that encoded the old contract were inverted
rather than deleted: typed prefixes must now be rejected, and full ids must
still resolve.

Also fixes a pre-existing failure on this branch, unrelated to the id contract:
the dispatch card's connect hint styles its prefix and id as separate ANSI
spans, so a contiguous regex could not match the styled string. That assertion
now runs against the plain render.

Verification:
- npm run check clean
- test:unit 615 files, 5739 passed
- test:integration 36 files, 485 passed (baseline had 2 failing)
- test:ci-contracts 5 files, 38 passed
@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown

Too many files changed for review. (115 files found, 100 file limit)

Bypass the limit by tagging @greptile-apps to review.

flora131 added 11 commits August 2, 2026 13:14
Render full UUIDs across workflow surfaces and use the shared two-row identity renderer for run cards and prompt attribution banners. Preserve prefix resolution while wrapping narrow cards without shortening identifiers.

PR body notes
- The prompt card's border title is now exactly `AWAITING INPUT`; the run identity moved off the border line into a two-row attribution banner in the card body, rendered by the new shared `renderRunIdentityRows`.
- PR #2135's queue counter belongs on banner row two beside the workflow name, in the optional `meta` slot that is already wired through `PromptCardIdentity.meta` but left unset here.
- The question and options UI below the banner is byte-identical to before.
The standard prompt surface degrades attribution in three rungs — two identity
rows, then the run id alone, then no banner — but the primitive input/editor
path built a single banner and fell straight through to unattributed. At the
one height where the middle rung applies, attribution vanished entirely even
though the run id alone would have fit.

That path now walks the same ladder. `renderPromptRunIdBanner` is exported so
both surfaces share one definition of the middle rung rather than each deriving
it, and the loop keeps the existing guards: a banner is only kept when the
question still renders and loses no rows against the unattributed layout.

Measured for input and editor kinds at width 100: rows <= 9 shows no banner,
row 10 shows a one-row banner carrying the run id, rows >= 11 show both
identity rows. The suite previously asserted that row 10 omitted attribution,
which encoded the missing rung.
…olution

PR #2140 made every display surface render the full run UUID but left input
resolution accepting typed prefixes, so the contract was asymmetric: the UI
only ever printed a 36-character id while the resolver still answered to six
characters of it.

Run targets now require the full 8-4-4-4-12 hex UUID. Prefix scanning is gone
from the live resolver, the durable DBOS catalog, the resume-target lookup, and
inspectRun. A target that is not a well-formed UUID is reported as malformed
rather than missing:

  Run id must be a full 36-character UUID; got "339e05a4" (8 chars).

That is deliberately distinct from `Run not found:` so a truncated paste is
diagnosable as truncated instead of looking like a stale run.

Because run ids are unique and now matched exactly, run-level ambiguity is
unreachable by construction. The `ambiguous` result variant is replaced by
`malformed` at every call site rather than deleted, which keeps each existing
error branch and its coverage intact.

Stage targeting is exact but deliberately NOT held to 36 characters. Stage
identifiers are not uniformly bare UUIDs: virtualNodeId builds a nested stage id
as the `runId:nodeId` composite (73 chars), and a ctx.tool node carries
`tool:<argsHash>`. A blanket length rule would have made every nested stage and
every tool node untargetable, so the stage axis keeps exact id and exact name
matching and only loses prefix and partial-name matching.

The session-picker filter keeps prefix search on purpose: it narrows a list the
user then selects from, so it never targets a run by a truncated id.

Fixtures across 50 test files carried short ids like "run-1" that are no longer
valid input. They now derive stable UUIDs from readable seeds via
testRunId(seed) rather than hardcoding opaque literals, so each fixture's intent
still reads at the call site. Tests that encoded the old contract were inverted
rather than deleted: typed prefixes must now be rejected, and full ids must
still resolve.

Also fixes a pre-existing failure on this branch, unrelated to the id contract:
the dispatch card's connect hint styles its prefix and id as separate ANSI
spans, so a contiguous regex could not match the styled string. That assertion
now runs against the plain render.

Verification:
- npm run check clean
- test:unit 615 files, 5739 passed
- test:integration 36 files, 485 passed (baseline had 2 failing)
- test:ci-contracts 5 files, 38 passed
@flora131
flora131 force-pushed the fix/full-run-id-display branch from 06a9501 to 1d0caf9 Compare August 2, 2026 20:29
@flora131
flora131 merged commit 9efc803 into main Aug 2, 2026
17 checks passed
@flora131
flora131 deleted the fix/full-run-id-display branch August 2, 2026 20:36
flora131 pushed a commit that referenced this pull request Aug 3, 2026
Add an invalidation-only store channel and one memoized payload-free graph
projection per store version, so a small status or question update no longer
walks complete workflow inputs, authored stage result bodies, child output
values, and tool bodies before the overlay renders.

Rebased onto current main. Two conflicts resolved against #2140, which
reshaped run cards after this commit was written:

- node-card.ts: kept main's meta line, which no longer carries the short run
  id because #2140 moved the full id onto its own wrapped row, and applied
  this commit's payload-free `outputCount ?? Object.keys(outputs).length`.
- session-overlays.ts: main had begun caching a snapshot from the subscribe
  callback for the resume-candidate lookup. The invalidation channel carries
  no snapshot, so that cache would have gone stale and newly-started runs
  would never have appeared in the picker. The picker now reads one memoized
  projection per render via readGraphStoreSnapshot().

Co-authored-by: Ashkan Vedadi Gargary <70602836+ashkanvg@users.noreply.github.com>
flora131 pushed a commit that referenced this pull request Aug 3, 2026
Add an invalidation-only store channel and one memoized payload-free graph
projection per store version, so a small status or question update no longer
walks complete workflow inputs, authored stage result bodies, child output
values, and tool bodies before the overlay renders.

Rebased onto current main. Two conflicts resolved against #2140, which
reshaped run cards after this commit was written:

- node-card.ts: kept main's meta line, which no longer carries the short run
  id because #2140 moved the full id onto its own wrapped row, and applied
  this commit's payload-free `outputCount ?? Object.keys(outputs).length`.
- session-overlays.ts: main had begun caching a snapshot from the subscribe
  callback for the resume-candidate lookup. The invalidation channel carries
  no snapshot, so that cache would have gone stale and newly-started runs
  would never have appeared in the picker. The picker now reads one memoized
  projection per render via readGraphStoreSnapshot().

Co-authored-by: Ashkan Vedadi Gargary <70602836+ashkanvg@users.noreply.github.com>
flora131 pushed a commit that referenced this pull request Aug 3, 2026
Add an invalidation-only store channel and one memoized payload-free graph
projection per store version, so a small status or question update no longer
walks complete workflow inputs, authored stage result bodies, child output
values, and tool bodies before the overlay renders.

Rebased onto current main. Two conflicts resolved against #2140, which
reshaped run cards after this commit was written:

- node-card.ts: kept main's meta line, which no longer carries the short run
  id because #2140 moved the full id onto its own wrapped row, and applied
  this commit's payload-free `outputCount ?? Object.keys(outputs).length`.
- session-overlays.ts: main had begun caching a snapshot from the subscribe
  callback for the resume-candidate lookup. The invalidation channel carries
  no snapshot, so that cache would have gone stale and newly-started runs
  would never have appeared in the picker. The picker now reads one memoized
  projection per render via readGraphStoreSnapshot().

Co-authored-by: Ashkan Vedadi Gargary <70602836+ashkanvg@users.noreply.github.com>
sina85 added a commit to sina85/atomic that referenced this pull request Aug 3, 2026
Bring the node-card model/thinking/fast + durable thinkingLevel persistence
up to date with upstream's tabs reformat, the ModelRuntime refactor (bastani-inc#2064),
and the reshaped graph node cards (bastani-inc#2140). Re-applied thinkingLevel across
the durable serialize/restore path, integrated the model row into the
reshaped card, and preserved the replay model-identity fix. NODE_H stays 6
for the model row; updated the bastani-inc#2100 graph-perf composed-row expectation to
match the taller card.
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