* docs(plugins): draft Host data API proto (read + write DTOs)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(plugins): ADR + spec + plan for the Host data API
Plugins access kandev data via capability-gated Host gRPC RPCs returning
hand-mapped DTOs, never the DB directly. ADR 0042.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(plugins): add Host data API RPCs and DTOs to plugin proto
Extends the existing service Host (per ADR 0042) with read RPCs
(ListTasks/GetTask, ListWorkspaces, ListWorkflows, ListWorkflowSteps,
ListAgentProfiles, ListRepositories, ListSessions, ListSessionCodeStats)
and declares the deferred phase-2 write RPCs (CreateTask, UpdateTask,
CreateComment) so the contract is stable for later tasks. Regenerates
plugin.pb.go/plugin_grpc.pb.go via `make -C apps/backend proto`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(plugins): Host data API SDK accessors + session LOC aggregation
Wave 1-2 of ADR 0042: pkg/pluginsdk gains typed reader accessors (host.Tasks(),
host.Sessions().CodeStats(), host.Workflows(), ...) over the extended Host gRPC
service, with proto<->native DTO conversions and an UnimplementedHostData seam
for the kandev-side impl. internal/analytics gains the per-session LOC
aggregation those readers surface. Backend handlers + capability gating land in
the next wave.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(plugins): capability-gated Host data read handlers (ADR 0042)
Implement pluginHost Tasks/Sessions/Workspaces/Workflows/AgentProfiles/
Repositories readers: each gated on api_read:<resource> (PermissionDenied
otherwise), backed by the service layer via narrow injected interfaces, and
hand-mapped to the pluginsdk DTOs. Sessions().CodeStats routes through the
analytics aggregation; acp_session_id preserves the metadata->resume_token
fallback. Wired through plugins.Service.SetDataSources + backendapp.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(plugins): wire test for Host data reads + docs (ADR 0042 accepted)
End-to-end test over the real go-plugin transport: session data round-trips,
per-resource api_read gating returns PermissionDenied over the wire, cursor
pagination. Document the Host data API + SDK accessors in GRPC-CONTRACT.md,
reconcile the spec to shipped code, mark ADR 0042 accepted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(plugins): full session history + snapshot profile name in data API
Sessions().List now fetches tasks with includeArchived=true — an archived
task's sessions are still real sessions, and CodeStats already made the
same call at the SQL layer, so the two session reads were inconsistent
(26 of 359 sessions visible on a real instance). Tasks().List keeps
archived tasks excluded.
Session gains agent_profile_name (proto field 10) sourced from the
profile snapshot, so sessions of since-uninstalled agents keep a human
label — the AgentProfiles registry only lists live agents.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(plugins): renumber Host data API ADR 0042 -> 0043
main merged its own ADR 0042 (project shell output); renumber this one to the
next free slot and update all references.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(plugins): address Host data API review (perf, pagination, filters, proto)
- paginate before DTO conversion so per-session ACP lookups run <=limit times
- fetch all source pages (no silent 1000/workspace truncation); accurate HasMore
- stable id tiebreaker on paginated sorts
- AND SessionFilter.TaskIDs with WorkspaceIDs; exclude config-mode sessions from
CodeStats (shared dialect.ExcludeConfigModePredicate) so List/CodeStats match
- Get returns codes.NotFound consistently in-process and over the wire
- wrap bare-DTO RPC responses (GetTaskResponse etc.) for Buf response uniqueness
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(plugins): mark Host data API plan tasks done
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(stats): add avg turn duration and messages per turn to averages card
Adds two new metrics to the workspace stats Averages card: mean turn
duration and mean message count per turn. Both exclude outliers — turns
shorter than 1s (no-op/aborted), longer than 1h (zombie turns where
completed_at was backfilled across an agent restart), and turns with no
messages — so the displayed means reflect typical work rather than tail
skew. On a ~4k-turn sample the filter drops avg duration from ~1062s to
~289s.
* fix(stats): exclude exact 1h turns and use Tooltip for averages help text
Address PR feedback: the BETWEEN bound was inclusive so turns exactly at
the 1h ceiling were retained, contradicting the documented half-open
range. Also swap the title attribute on the new Averages rows for the
shared Tooltip component so the help text is reachable via keyboard and
screen readers.
* fix(stats): align turn-row zero state with em-dash convention
Address PR feedback: avg_messages_per_turn rendered "0.0" while
avg_turn_duration_ms rendered "—" under the same empty-data condition.
Also corrects the GetGlobalStats docstring to acknowledge that
clean_turn_agg adds one correlated per-turn lookup on top of the four
single-scan CTEs.
* test(stats): cover exact 1h turn boundary in clean-turn exclusion test
Add a turn of exactly 3,600,000ms (cleanTurnMaxDurationMs) to
TestGetGlobalStats_CleanTurnExcludesOutliers so the exclusive upper
bound is exercised directly, not just via the 2h zombie turn that
sits well above the boundary.
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
* perf(stats): split stats endpoint per-section and rewrite GetGlobalStats
Stats page used to wait on a single endpoint that ran 7 SQL queries in parallel
and rendered nothing until the slowest one returned. Cold first-paint was the
bottleneck users actually hit.
- Backend: split /api/v1/workspaces/:id/stats into 7 per-section endpoints
(global, tasks, daily-activity, completed-activity, agent-usage,
repositories, git). Each handler runs one repo call and returns its DTO.
- Backend: rewrote GetGlobalStats from 9 independent correlated subqueries
into 4 CTEs (task / session / turn / message). Each underlying table is now
scanned at most once per request instead of 1-3 times.
- Frontend: replaced the monolithic fetchStats + single PanelState with
useStatsSections hook (7 parallel fetches, per-section SectionStatus).
Each panel renders skeleton -> data -> error independently; light queries
(overview, agent usage, git) paint immediately, heavy ones stream in.
- Tests: added TestGetGlobalStats_AggregatesAcrossTables to lock the
CTE-based query against regressions for turns, user/tool messages, and
total duration.
* fix(stats): address PR review feedback
- Restore cache: "no-store" on section fetches; Gin emits no Cache-Control
headers so the browser could otherwise serve stale stats on revisit
(Greptile P1, Cubic).
- Stop blanking the Overview panel when /git fails: render OverviewCards as
soon as `global` is ready and treat `git` as optional, falling back to the
Averages card. Updates GitOrAveragesCard / OverviewCards types (Claude).
- Fix TaskStatsHasMore off-by-one at exactly taskStatsLimit rows: probe
taskStatsLimit+1, slice back to the page size, only flag hasMore on a
confirmed extra row (Claude, Cubic).
- Rename anyError → firstError with a comment documenting the deterministic
insertion-order iteration; clearer signal that callers should use it as
"any section failed?" and surface per-section errors in their own panels
(Claude).
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
* feat(stats): show active time and elapsed span per task
* fix(stats): address review feedback on task duration metrics
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
* fix: include archived tasks in completed tasks over time chart
Archived tasks without a session completed_at were excluded from the
Completed Tasks Over Time chart. Use COALESCE(session.completed_at,
task.archived_at) as the completion date so archived tasks fall back
to their archive timestamp.
* fix: address review feedback - isolate completion paths in test
- Remove archived_at from task-regular so it only counts via session
completed_at (CodeRabbit nitpick)
- Add task-last-step-no-dates to verify COALESCE IS NOT NULL filter
excludes tasks with neither date (Claude suggestion)
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>