feat(web/console): show run provenance — input message + platform icons#1881
Conversation
Console run cards/rows/detail showed status, workflow, cost, and elapsed but
never what input started a run — the prompt was parsed into the Run primitive
(toRun) yet rendered nowhere, and the origin badge was text-only. This surfaces it:
- OriginBadge: a Lucide icon per platform (web/cli/slack/telegram/discord/github;
none for unknown), mirroring the old dashboard's PLATFORM_ICONS.
- ActiveRunCard: an `input` row folded into the activity grid (truncated, full
text on hover) for running + paused cards.
- RecentRunRow: the message truncated inline beside the workflow name; the row
stays h-9 (title tooltip for the full text).
- RunStartedLine: a blockquote input line under "Workflow X started".
- RunDetailHeader: a full-width `input` sub-row.
- run.test.ts: first test for the primitive — normalizeOrigin (all platforms +
case-insensitive + unknown fallback), userMessage default, pending→running,
conversationPlatformId passthrough. (normalizeOrigin is now exported.)
Deviation from plan (documented): the "from chat →" link is DEFERRED, not built.
The console has no conversation-deep-link route — ChatPage is mounted at
p/:projectId/chat and manages its active conversation internally, so a chat link
has no valid destination. Per the plan's "do not invent a route" constraint, the
link AND the workerPlatformId/parentPlatformId fields that only existed to feed it
were dropped (they'd be unread data). This ships the unambiguous wins that fully
address the stated need ("what did I enter… cli/chat"); the link moves to a
follow-up once console chat supports conversation deep-linking.
|
Caution Review failedPull request was closed or merged during review No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR surfaces run provenance (userMessage) across several console components, adds per-origin icons to origin badges, and exports normalizeOrigin with expanded Bun tests for normalization and parsing. ChangesProvenance Tracking in Console UI
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Review Summary — multi-agent review5 agents reviewed this PR (code, docs, tests, comments, simplify). I skipped Critical Issues (0)None. (CI was still running at review time — verify it's green before merge.) Important Issues (3) — all quick, none hard-blocks (presentational PR)
Suggestions (5)
Strengths
DocumentationNone needed — purely intra-experiment change; CLAUDE.md's Verdict: READY TO MERGE (after a quick layout fix)0 critical. The only material item is I1 — a real but cosmetic layout regression for runs without an input message (the common case for non-Claude/historical runs); it truncates long workflow names rather than breaking anything, so it doesn't hard-block, but it's a 2-line conditional-className fix worth doing before merge. I2/I3 are one-line comment-accuracy rewords. S1-S5 are optional polish + test backfill. Recommended actions
🤖 Multi-agent review via Claude Code |
I1 RecentRunRow layout regression: when userMessage is empty the workflow name
now fills the full width (flex-1) again instead of being capped at 55% with
blank space + needless truncation; capped at 55% only when a message shares
the line.
I2 ActiveRunCard comment: "always" → "when present" (the input row is gated on a
non-empty message).
I3 OriginBadge comment: "mirrors" → "extends" — the icon map is a 7-origin
superset of the old 5-entry map, adds discord, and renders no icon for unknown
(vs the old Globe fallback).
S1 Extracted a local `hasValue` type-guard to replace the repeated
null/undefined/'' chains on currentNode/lastTool (also narrows to string).
S2 Named the grid guard `showDetailGrid` (matches the file's elapsed/canOpen idiom).
S3 RecentRunRow: rerun-param guard uses `!== ''` to match the render guard.
S4 Backfilled run.ts coverage: unknown-status→running, readCost (positive, the
$0.00 >0-guard, and non-numeric→null), and approval-metadata parsing
(well-formed, message default, malformed→null). 29 → 36 console tests.
S5 RunDetailHeader: the deferred "from chat" link now has a tracked anchor —
TODO(#1882) (filed the follow-up issue).
|
Addressed in `0ad090a7` — thanks, I1 was a real regression I introduced. Fixed
|
…1890) * feat(web/console): settings core — assistant config + system panel Adds the console's first settings surface (/console/settings, global) — the parity floor before cutover. PR #4 of the console sequence (after #1878/#1881/#1885). - skills/settings.ts: getConfig/updateAssistantConfig/getHealth/getUpdateCheck plus the pure buildAssistantUpdate(form) transform (8 unit tests). skills/providers.ts: listProviders. Types from @/lib/api.generated (console isolation boundary). - store/keys.ts: config/health/providers/updateCheck keys (health reuses the literal 'health' so it shares lib/health's cache entry). - lib/health.ts: full HealthResponse + useHealth(); useIsDocker derives from it. - AssistantConfigPanel: default-assistant picker (registered providers) + free-text model per provider + codex reasoning/web-search; dirty-gated Save → PATCH /api/config/assistants → ~/.archon/config.yaml → invalidate(K.config) re-seeds. Model is free-text for every provider (Archon does not validate model strings). - SystemPanel: status/adapter/db/version, concurrency (active/maxConcurrent, coerced defensively — concurrency is an open record), running workflows, platform badges, update-check. - ConsoleApp: /console/settings route, gear header link, ',' global keybinding; shortcuts.ts catalogue entry. Honors the error-is-undefined cache contract throughout (the #1885 gotcha). Excludes the GitHub device-flow panel (PR #5) and env-var editing (project-scoped). Validation: web type-check / lint / format:check clean; 59 console tests pass (8 new). Verified end-to-end on an isolated server: read APIs return the expected shapes, save round-trips to config.yaml (binary paths preserved via the server deep-merge), and a browser smoke of /console/settings renders both panels (5 providers, codex effort/web-search, system grid, Save dirty-gated). * fix(web/console): address PR #1890 review — comments + update-check silent failure I1: correct the buildAssistantUpdate JSDoc. Verified the PATCH route does NOT safe-filter per field on the write path — it validates only provider ids and merges the body into config.yaml unfiltered (safe-filtering is read-path only). The real invariant is that this function only ever attaches codex-only fields to the codex entry; the comment now says that instead of the false "server safe-filters" claim. I2: rewrite the K.health note. This PR routes lib/health through K.health, so the old "lib/health already caches under this literal" premise is stale; the invariant is that both consumers read via useHealth() to share one cache entry. I3 (silent failure): SystemPanel showed "checking…" forever on a failed update-check (the error was destructured away). Surface updateError via an UpdateStatus helper → "update check unavailable". S1: SettingsSection children typed ReactNode (ReactElement|ReactElement[] fought the `cond && <el/>` pattern). S5: replaced the nested update-status ternary with the UpdateStatus helper + early returns for the health loading/error states. S6: extracted the shared SettingsSection card shell (PR #5 is the 3rd consumer), a SELECT_CLASS const for the two codex selects, and bound activePlatforms once. Docs: added /console/settings to the console README routes. Deferred (with rationale): - S2 (re-seed on providers identity): latent only — nothing invalidates K.providers, and a ref-snapshot "fix" introduces a config-vs-providers load-order race. Keep the simple [config, providers] effect. - S3 (literal-union effort/webSearch types): kept bare string so seedForm tolerates an out-of-enum value in config.yaml; the <select> is the write-side enforcement. - S4 (show both load errors): an error panel showing the first error is acceptable. Validation: web type-check / lint / format:check clean; 59 console tests pass.
Summary
Runprimitive (toRun) yet rendered nowhere; the origin badge was text-only. Two CLI runs of the same workflow looked identical./consolethe default UI.user_messageonActiveRunCard,RecentRunRow,RunStartedLine,RunDetailHeader; add per-platform Lucide icons toOriginBadge; add the first test for therun.tsprimitive.api.generated.d.tschange. No new renderers beyond text rows. Old UI untouched. The "from chat →" link is deferred (see Deviation).UX Journey
Before
After
Architecture Diagram
Connection inventory:
OriginBadgelucide-reactRun.userMessageprimitives/run.tsrun.test.tsnormalizeOriginexportedLabel Snapshot
risk: lowsize: Swebweb:consoleChange Metadata
featurewebLinked Issue
Validation Evidence (required)
bun run validateis green end-to-end.run.test.tscoversnormalizeOrigin(all 6 platforms + case-insensitive + null/undefined/unknown fallback),userMessagedefault/passthrough, origin derivation,conversationPlatformIdpassthrough, andpending→running.Security Impact (required)
Compatibility / Migration
Human Verification (required)
inputrow into ActiveRunCard's existing activity grid (no double grid); confirmed RecentRunRow staysh-9with the message sharing the workflow line +titletooltip; RunDetailHeader sub-row usesw-fullto wrap onto its own line in the flex-wrap header.userMessage→ no row/blockquote/tooltip anywhere; originunknown→ no icon, label—; long message → truncates with full text on hover.@archon/webhas no jsdom/testing-library, consistent with fix(web/console): gate /console behind auth + correct run-event normalizer #1878's accepted constraint).Side Effects / Blast Radius (required)
toRunexportingnormalizeOrigin; everything else is additive rendering of an existing field.run.test.tsnow covers the normalizer.Rollback Plan (required)
git revert <merge-commit>— 7 files, web-only.inputrow renders for an empty message (guarded against).Risks and Mitigations
max-w-[55%], messagemin-w-0 truncate; row height fixed ath-9; full text viatitle.Deviation from the plan (documented)
The plan's "from chat →" link is deferred, not built. Investigation found the console has no conversation-deep-link route —
ChatPagemounts atp/:projectId/chatand manages its active conversation internally, so the link has no valid destination. Per the plan's explicit "do not invent a route" constraint, the link and theworkerPlatformId/parentPlatformIdfields that only existed to feed it were dropped (they'd be unread data — the unused-field smell the #1878 review flagged). The link moves to a follow-up once the console chat route supports deep-linking to a specific conversation. This PR ships the unambiguous wins that fully address the stated need.Summary by CodeRabbit
New Features
Bug Fixes
Tests