Skip to content

feat(web): reposition UI as viewer + light CRUD#8

Merged
z23cc merged 4 commits intomainfrom
fn-18-reposition-web-ui-as-viewer-light-crud
Apr 5, 2026
Merged

feat(web): reposition UI as viewer + light CRUD#8
z23cc merged 4 commits intomainfrom
fn-18-reposition-web-ui-as-viewer-light-crud

Conversation

@z23cc
Copy link
Copy Markdown
Owner

@z23cc z23cc commented Apr 5, 2026

Summary

Aligns the web UI with architectural truth: Claude Code terminal runs agents, web UI browses state. Removes a fake-promise button, documents the divide of labor, and adds viewer features that justify the web UI's existence.

The Fake Promise Fix

EpicDetail.tsx had a prominent "Start Work" button that called POST /epics/:id/work. The daemon handler only updated task status in SQLite — it never spawned Claude Code workers. Agent execution requires a live Claude Code session (via the Agent() tool). Replaced the button with "Copy /flow-code:work " that copies to clipboard, plus an explainer: "Agent execution happens in your Claude Code terminal."

Changes

Task 1 — Replace Start Work → Copy command

  • Removed handleStartWork + apiPost('/epics/:id/work') call
  • New button copies the exact work command, sonner toast confirms
  • Inline explainer text clarifying where agents actually run

Task 2 — README Web UI section

  • New ## Web UI section documenting responsibility split
  • Launch instructions: flowctl serve --port 3737
  • Explicit statement that web UI never manages agents

Task 3 — Dashboard search + status filter

  • New EpicFilters component (search by id/title + status dropdown)
  • Shows "N/M" count when filters active, clear button
  • Empty-state when filter matches zero
  • Domain filter skipped (epic-level domain not in API)

Task 4 — Evidence viewer modal

  • New GET /api/v1/tasks/:id endpoint (merges Task + Evidence + RuntimeState)
  • EvidenceModal shows: status, duration, diff stats, commits, tests, review_iterations, workspace_changes
  • Done task titles become clickable; non-done tasks plain
  • Escape / backdrop / close button all dismiss

Verification

Check Result
cargo build --release -p flowctl-daemon
bun run typecheck
bun run build ✓ (231ms)
bun test ✓ 10 pass

Architecture note

The daemon's POST /epics/:id/work endpoint is left intact — MCP clients and scripts may depend on it. The web UI simply stops calling it. Future epic (deferred) could add real execution via Rust claude-agent-sdk or a conductor skill, but that was deemed out of scope until real user demand exists.

Epic: fn-18-reposition-web-ui-as-viewer-light-crud

z23cc added 4 commits April 5, 2026 15:35
The 'Start Work' button posted to /epics/:id/work which only updated
task status in SQLite — it never spawned Claude Code workers. That
was a fake promise. Replaced with a 'Copy /flow-code:work <id>' action
plus explainer text making clear that agent execution belongs in the
Claude Code terminal.

fn-18-reposition-web-ui-as-viewer-light-crud.1
Documents the divide of labor between Claude Code terminal (agent
execution), web UI (browse + light CRUD), and flowctl CLI (scripting).
Makes it explicit that the web UI is a read-only dashboard, not an
orchestrator — you copy the work command from the UI and run it in
your Claude Code session.

fn-18-reposition-web-ui-as-viewer-light-crud.2
When epic count grows past ~10, scanning the grid becomes painful.
Added a search input (matches id or title, case-insensitive) and a
status dropdown derived from live epic statuses. Shows filtered/total
count when any filter is active, with a clear-filters button.

Domain filter deliberately skipped — domain is per-task, not per-epic,
so filtering epics by domain would require pulling task lists into
the /epics response. Not worth the cost now.

fn-18-reposition-web-ui-as-viewer-light-crud.3
Adds GET /api/v1/tasks/{id} endpoint returning task + evidence +
runtime state (including duration_seconds). Frontend exposes a
clickable title on done tasks that opens an EvidenceModal showing
status, duration, diff stats, commits, tests, review iterations,
and workspace_changes if recorded.

Backend: new get_task_handler in handlers/task.rs merges TaskRepo,
EvidenceRepo, and RuntimeRepo into a single response payload.

Frontend: EvidenceModal component with Escape/backdrop close,
loading/error states, and conditional rendering per evidence field.

fn-18-reposition-web-ui-as-viewer-light-crud.4
@z23cc z23cc marked this pull request as ready for review April 5, 2026 07:44
Copilot AI review requested due to automatic review settings April 5, 2026 07:44
@z23cc z23cc merged commit 30ef328 into main Apr 5, 2026
1 check failed
@z23cc z23cc deleted the fn-18-reposition-web-ui-as-viewer-light-crud branch April 5, 2026 07:44
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR repositions the web UI as a state/evidence viewer with light CRUD (not an agent orchestrator), removes a misleading “Start Work” action, adds dashboard filtering, and introduces a task evidence/detail viewer backed by a new daemon endpoint.

Changes:

  • Replace “Start Work” with a “Copy /flow-code:work …” affordance + inline execution explainer on Epic detail.
  • Add dashboard epic search + status filtering via a new EpicFilters component.
  • Add a task detail/evidence modal in the frontend and a new daemon endpoint GET /api/v1/tasks/{id} to serve task+evidence+runtime details.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
README.md Documents the Web UI vs terminal/CLI responsibilities and launch instructions.
frontend/src/pages/EpicDetail.tsx Removes the fake “Start Work” call; adds copy-to-clipboard and evidence modal entry point.
frontend/src/pages/Dashboard.tsx Adds in-memory epic filtering (search + status) and empty/clear states.
frontend/src/components/EvidenceModal.tsx New modal for task details/evidence fetched from the daemon.
frontend/src/components/EpicFilters.tsx New reusable filter UI (search, status dropdown, counts, clear).
flowctl/crates/flowctl-daemon/src/server.rs Registers the new REST endpoint GET /api/v1/tasks/{id}.
flowctl/crates/flowctl-daemon/src/handlers/task.rs Implements get_task_handler combining task + evidence + runtime (+ duration).
flowctl/crates/flowctl-daemon/src/handlers/mod.rs Exports the new task handler.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +308 to +313
| Surface | Responsibility |
|---|---|
| **Claude Code terminal** | Run agents: `/flow-code:plan`, `/flow-code:work`, `/flow-code:brainstorm`, reviews, worker spawning, permission prompts |
| **Web UI** | Browse epics/tasks/specs/DAG/memory/evidence; light CRUD (create epic, edit deps, add gaps, archive epics) |
| **`flowctl` CLI** | Scripting, CI, automation, anything headless |

Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The markdown table in this section uses double leading pipes (||) on each row, which renders as an extra empty column (or breaks table rendering depending on the renderer). Use single leading/ending pipes (|) for standard GitHub-flavored markdown tables.

Copilot uses AI. Check for mistakes.
Comment on lines +62 to +63
(evidence.tests?.length ?? 0) > 0 ||
(evidence.files_changed ?? 0) > 0 ||
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasEvidence doesn't consider evidence fields that the modal can display (e.g. insertions, deletions, review_iterations). If those are present while files_changed is null/0 and there are no commits/tests, the UI will incorrectly show “No evidence recorded…”. Include all displayed fields (and optionally prs) in the hasEvidence check.

Suggested change
(evidence.tests?.length ?? 0) > 0 ||
(evidence.files_changed ?? 0) > 0 ||
(evidence.tests?.length ?? 0) > 0 ||
(evidence.prs?.length ?? 0) > 0 ||
(evidence.files_changed ?? 0) > 0 ||
(evidence.insertions ?? 0) > 0 ||
(evidence.deletions ?? 0) > 0 ||
(evidence.review_iterations ?? 0) > 0 ||

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +74
return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4"
onClick={onClose}
>
<div
className="relative max-h-[85vh] w-full max-w-2xl overflow-y-auto rounded-lg border border-border bg-bg-secondary shadow-2xl"
onClick={(e) => e.stopPropagation()}
>
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This modal is built from raw <div>s without dialog semantics or focus management, which hurts keyboard/screen-reader accessibility (no role="dialog"/aria-modal, no initial focus, no focus trap). Consider using the existing <dialog>-based modal component pattern (or add the missing ARIA + focus handling here).

Copilot uses AI. Check for mistakes.
Comment on lines +146 to +152
<button
onClick={() => setEvidenceTaskId(t.id)}
className="text-left font-medium hover:text-accent transition-colors"
title="View evidence"
>
{t.title}
</button>
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clickable task title is rendered as a <button> without an explicit type. If this component ever ends up nested inside a <form>, the default type="submit" can cause accidental form submissions. Set type="button" on this button.

Copilot uses AI. Check for mistakes.
Comment on lines 89 to 95
// ── New RESTful endpoints ──────────────────────────────
.route("/api/v1/epics/{id}/plan", post(handlers::set_epic_plan_handler))
.route("/api/v1/epics/{id}/work", post(handlers::start_epic_work_handler))
.route("/api/v1/tasks/{id}", get(handlers::get_task_handler))
.route("/api/v1/tasks/{id}/start", post(handlers::start_task_rest_handler))
.route("/api/v1/tasks/{id}/done", post(handlers::done_task_rest_handler))
.route("/api/v1/tasks/{id}/block", post(handlers::block_task_rest_handler))
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new GET route for fetching task details was added, but there’s no corresponding handler/router test coverage alongside the existing server/router tests in this crate. Add a test that creates a task (and optionally evidence/runtime rows) and asserts GET /api/v1/tasks/{id} returns the expected shape (including nulls when rows don’t exist).

Copilot uses AI. Check for mistakes.
z23cc added a commit that referenced this pull request Apr 5, 2026
Adds a lightweight per-task narrative dump at `.flow/outputs/<task-id>.md`
that workers write in new Phase 5c and the next worker auto-reads during
Phase 1 re-anchor. Gated on a new `outputs.enabled` config key (default
true), fully independent from `memory.enabled` — outputs is a narrative
handoff layer, not part of the verified memory system.

- flowctl-core: new `OutputEntry` protocol type (convention #8)
- flowctl-service: new `OutputsStore` (file-system native, no libSQL)
- flowctl-cli: new `flowctl outputs write|list|show` commands
- Phase 5c `outputs_dump` added to worker sequence dynamically based on
  `outputs.enabled` config; worker.md Phase 1 extended to list+show prior
  outputs (limit 3) and Phase 5c added between Phase 5 and Phase 5b
- `done_task()` in lifecycle.rs intentionally unchanged — worker owns the
  dump explicitly in Phase 5c, keeping runtime agnostic of narrative shape
- Missing `## Surprises`/`## Decisions` sections allowed (pitfall #2)
z23cc added a commit that referenced this pull request Apr 9, 2026
P0 fixes (state loss — root cause of 5 issues):
- get_flow_dir() now walks up directory tree (FLOW_STATE_DIR env → walk-up → CWD)
  Fixes: #1 state loss, #3 state not persistent, #5 worker parallel fail,
  #9 .flow symlink issues. Same pattern as git finding .git.
- flowctl recover --epic <id> [--dry-run]: rebuilds task completion status
  from git log. Fixes #11 no recovery after state loss.

P1 fixes (guard + review):
- Guard graceful fallback: missing tools → "skipped" (not "failed").
  Only actual failures block pipeline. Fixes #8.
- Review-backend availability check: if rp-cli/codex not in PATH,
  auto-fallback to "none" with warning. Fixes #7.

P2 fixes (UX):
- Slug max length 40→20 chars. "Django+React platform with account
  management" → "fn-3-django-react-plat" not 40-char monster. Fixes #2 #12.
- Brainstorm auto-skip: trivial tasks (≤10 words, contains "fix"/"typo"/etc)
  skip brainstorm entirely. Fixes #6.
- --interactive flag: pause at key decisions. Fixes #10.

370 tests pass. Zero new dependencies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

2 participants