From a7083bb1655087e439209f092e79b8e87567c721 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Fri, 6 Mar 2026 13:11:26 -0500 Subject: [PATCH] Update Rust code for nested API schema types Update all struct constructions in demo data and server handlers to match the new nested sub-schemas. Fix aggregate usage handler to use AggregateUsageTotals wrapper, ModelReference, and TokenUsage. Update test assertions from flat field paths to nested paths. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/arc-api/src/demo/mod.rs | 558 +++++++++++---------------------- crates/arc-api/src/server.rs | 39 ++- 2 files changed, 204 insertions(+), 393 deletions(-) diff --git a/crates/arc-api/src/demo/mod.rs b/crates/arc-api/src/demo/mod.rs index 52cdfb7fd..b48d558cb 100644 --- a/crates/arc-api/src/demo/mod.rs +++ b/crates/arc-api/src/demo/mod.rs @@ -443,7 +443,7 @@ pub async fn list_workflow_runs( ) -> Response { let items: Vec<_> = runs::list_items() .into_iter() - .filter(|r| r.workflow == name) + .filter(|r| r.workflow.slug == name) .collect(); paginated_response(items, &pagination) } @@ -508,7 +508,7 @@ pub async fn create_session_stub( let now = "2026-03-06T16:00:00Z"; ( StatusCode::CREATED, - Json(serde_json::json!({"id": id, "title": "New session", "model": "Opus 4.6", "created_at": now, "updated_at": now})), + Json(serde_json::json!({"id": id, "title": "New session", "model": {"id": "Opus 4.6"}, "created_at": now, "updated_at": now})), ) .into_response() } @@ -702,357 +702,191 @@ mod runs { vec![ RunListItem { id: "run-1".into(), - repo: "api-server".into(), + repository: RepositoryReference { name: "api-server".into() }, title: "Add rate limiting to auth endpoints".into(), - workflow: "implement".into(), + workflow: WorkflowReference { slug: "implement".into() }, status: BoardColumn::Working, - number: None, - additions: None, - deletions: None, - checks: vec![], - elapsed_secs: Some(420.0), - elapsed_warning: Some(false), - resources: Some("4 CPU / 8 GB".into()), - comments: Some(0), + pull_request: None, + timings: Some(RunTimings { elapsed_secs: 420.0, elapsed_warning: Some(false) }), + sandbox: Some(RunSandbox { id: "sb-a1b2c3d4".into(), resources: Some(SandboxResources { cpu: 4, memory: 8 }) }), question: None, - sandbox_id: Some("sb-a1b2c3d4".into()), created_at: ts("2026-03-06T14:30:00Z"), }, RunListItem { id: "run-2".into(), - repo: "web-dashboard".into(), + repository: RepositoryReference { name: "web-dashboard".into() }, title: "Migrate to React Router v7".into(), - workflow: "implement".into(), + workflow: WorkflowReference { slug: "implement".into() }, status: BoardColumn::Working, - number: None, - additions: None, - deletions: None, - checks: vec![], - elapsed_secs: Some(8100.0), - elapsed_warning: Some(false), - resources: Some("8 CPU / 16 GB".into()), - comments: Some(0), + pull_request: None, + timings: Some(RunTimings { elapsed_secs: 8100.0, elapsed_warning: Some(false) }), + sandbox: Some(RunSandbox { id: "sb-e5f6g7h8".into(), resources: Some(SandboxResources { cpu: 8, memory: 16 }) }), question: None, - sandbox_id: Some("sb-e5f6g7h8".into()), created_at: ts("2026-03-06T12:00:00Z"), }, RunListItem { id: "run-3".into(), - repo: "cli-tools".into(), + repository: RepositoryReference { name: "cli-tools".into() }, title: "Fix config parsing for nested values".into(), - workflow: "fix_build".into(), + workflow: WorkflowReference { slug: "fix_build".into() }, status: BoardColumn::Working, - number: None, - additions: None, - deletions: None, - checks: vec![], - elapsed_secs: Some(2700.0), - elapsed_warning: Some(false), - resources: Some("2 CPU / 4 GB".into()), - comments: Some(0), + pull_request: None, + timings: Some(RunTimings { elapsed_secs: 2700.0, elapsed_warning: Some(false) }), + sandbox: Some(RunSandbox { id: "sb-i9j0k1l2".into(), resources: Some(SandboxResources { cpu: 2, memory: 4 }) }), question: None, - sandbox_id: Some("sb-i9j0k1l2".into()), created_at: ts("2026-03-05T09:20:00Z"), }, RunListItem { id: "run-4".into(), - repo: "api-server".into(), + repository: RepositoryReference { name: "api-server".into() }, title: "Update OpenAPI spec for v3".into(), - workflow: "expand".into(), + workflow: WorkflowReference { slug: "expand".into() }, status: BoardColumn::Pending, - number: None, - additions: Some(567), - deletions: Some(234), - checks: vec![], - elapsed_secs: Some(4320.0), - elapsed_warning: Some(false), - resources: None, - comments: Some(0), - question: Some("Accept or push for another round?".into()), - sandbox_id: Some("sb-q7r8s9t0".into()), + pull_request: Some(RunPullRequest { number: 0, additions: Some(567), deletions: Some(234), comments: Some(0), checks: vec![] }), + timings: Some(RunTimings { elapsed_secs: 4320.0, elapsed_warning: Some(false) }), + sandbox: Some(RunSandbox { id: "sb-q7r8s9t0".into(), resources: None }), + question: Some(RunQuestion { text: "Accept or push for another round?".into() }), created_at: ts("2026-03-04T15:00:00Z"), }, RunListItem { id: "run-5".into(), - repo: "shared-types".into(), + repository: RepositoryReference { name: "shared-types".into() }, title: "Add pipeline event types".into(), - workflow: "implement".into(), + workflow: WorkflowReference { slug: "implement".into() }, status: BoardColumn::Pending, - number: None, - additions: Some(145), - deletions: Some(23), - checks: vec![], - elapsed_secs: Some(1680.0), - elapsed_warning: Some(false), - resources: None, - comments: Some(0), - question: Some("Proceed from investigation to fix?".into()), - sandbox_id: Some("sb-u1v2w3x4".into()), + pull_request: Some(RunPullRequest { number: 0, additions: Some(145), deletions: Some(23), comments: Some(0), checks: vec![] }), + timings: Some(RunTimings { elapsed_secs: 1680.0, elapsed_warning: Some(false) }), + sandbox: Some(RunSandbox { id: "sb-u1v2w3x4".into(), resources: None }), + question: Some(RunQuestion { text: "Proceed from investigation to fix?".into() }), created_at: ts("2026-03-04T10:00:00Z"), }, RunListItem { id: "run-6".into(), - repo: "web-dashboard".into(), + repository: RepositoryReference { name: "web-dashboard".into() }, title: "Add dark mode toggle".into(), - workflow: "implement".into(), + workflow: WorkflowReference { slug: "implement".into() }, status: BoardColumn::Review, - number: Some(889), - additions: Some(234), - deletions: Some(67), - checks: vec![ - CheckRun { - name: "lint".into(), - status: CheckRunStatus::Success, - duration_secs: Some(23.0), - }, - CheckRun { - name: "typecheck".into(), - status: CheckRunStatus::Success, - duration_secs: Some(72.0), - }, - CheckRun { - name: "unit-tests".into(), - status: CheckRunStatus::Success, - duration_secs: Some(154.0), - }, - CheckRun { - name: "integration-tests".into(), - status: CheckRunStatus::Failure, - duration_secs: Some(296.0), - }, - CheckRun { - name: "e2e / chrome".into(), - status: CheckRunStatus::Failure, - duration_secs: Some(182.0), - }, - CheckRun { - name: "build".into(), - status: CheckRunStatus::Success, - duration_secs: Some(105.0), - }, - CheckRun { - name: "coverage".into(), - status: CheckRunStatus::Skipped, - duration_secs: None, - }, - ], - elapsed_secs: Some(2100.0), - elapsed_warning: Some(false), - resources: None, - comments: Some(4), + pull_request: Some(RunPullRequest { + number: 889, + additions: Some(234), + deletions: Some(67), + comments: Some(4), + checks: vec![ + CheckRun { name: "lint".into(), status: CheckRunStatus::Success, duration_secs: Some(23.0) }, + CheckRun { name: "typecheck".into(), status: CheckRunStatus::Success, duration_secs: Some(72.0) }, + CheckRun { name: "unit-tests".into(), status: CheckRunStatus::Success, duration_secs: Some(154.0) }, + CheckRun { name: "integration-tests".into(), status: CheckRunStatus::Failure, duration_secs: Some(296.0) }, + CheckRun { name: "e2e / chrome".into(), status: CheckRunStatus::Failure, duration_secs: Some(182.0) }, + CheckRun { name: "build".into(), status: CheckRunStatus::Success, duration_secs: Some(105.0) }, + CheckRun { name: "coverage".into(), status: CheckRunStatus::Skipped, duration_secs: None }, + ], + }), + timings: Some(RunTimings { elapsed_secs: 2100.0, elapsed_warning: Some(false) }), + sandbox: Some(RunSandbox { id: "sb-m3n4o5p6".into(), resources: None }), question: None, - sandbox_id: Some("sb-m3n4o5p6".into()), created_at: ts("2026-03-03T16:45:00Z"), }, RunListItem { id: "run-7".into(), - repo: "infrastructure".into(), + repository: RepositoryReference { name: "infrastructure".into() }, title: "Terraform module for Redis cluster".into(), - workflow: "implement".into(), + workflow: WorkflowReference { slug: "implement".into() }, status: BoardColumn::Review, - number: Some(156), - additions: Some(412), - deletions: Some(0), - checks: vec![ - CheckRun { - name: "lint".into(), - status: CheckRunStatus::Success, - duration_secs: Some(18.0), - }, - CheckRun { - name: "typecheck".into(), - status: CheckRunStatus::Success, - duration_secs: Some(56.0), - }, - CheckRun { - name: "unit-tests".into(), - status: CheckRunStatus::Pending, - duration_secs: None, - }, - CheckRun { - name: "integration-tests".into(), - status: CheckRunStatus::Queued, - duration_secs: None, - }, - CheckRun { - name: "build".into(), - status: CheckRunStatus::Pending, - duration_secs: None, - }, - ], - elapsed_secs: Some(720.0), - elapsed_warning: Some(false), - resources: None, - comments: Some(1), + pull_request: Some(RunPullRequest { + number: 156, + additions: Some(412), + deletions: Some(0), + comments: Some(1), + checks: vec![ + CheckRun { name: "lint".into(), status: CheckRunStatus::Success, duration_secs: Some(18.0) }, + CheckRun { name: "typecheck".into(), status: CheckRunStatus::Success, duration_secs: Some(56.0) }, + CheckRun { name: "unit-tests".into(), status: CheckRunStatus::Pending, duration_secs: None }, + CheckRun { name: "integration-tests".into(), status: CheckRunStatus::Queued, duration_secs: None }, + CheckRun { name: "build".into(), status: CheckRunStatus::Pending, duration_secs: None }, + ], + }), + timings: Some(RunTimings { elapsed_secs: 720.0, elapsed_warning: Some(false) }), + sandbox: Some(RunSandbox { id: "sb-y5z6a7b8".into(), resources: None }), question: None, - sandbox_id: Some("sb-y5z6a7b8".into()), created_at: ts("2026-03-03T11:00:00Z"), }, RunListItem { id: "run-8".into(), - repo: "api-server".into(), + repository: RepositoryReference { name: "api-server".into() }, title: "Implement webhook retry logic".into(), - workflow: "implement".into(), + workflow: WorkflowReference { slug: "implement".into() }, status: BoardColumn::Merge, - number: Some(1249), - additions: Some(189), - deletions: Some(45), - checks: vec![ - CheckRun { - name: "lint".into(), - status: CheckRunStatus::Success, - duration_secs: Some(21.0), - }, - CheckRun { - name: "typecheck".into(), - status: CheckRunStatus::Success, - duration_secs: Some(68.0), - }, - CheckRun { - name: "unit-tests".into(), - status: CheckRunStatus::Success, - duration_secs: Some(192.0), - }, - CheckRun { - name: "integration-tests".into(), - status: CheckRunStatus::Success, - duration_secs: Some(334.0), - }, - CheckRun { - name: "e2e / chrome".into(), - status: CheckRunStatus::Success, - duration_secs: Some(262.0), - }, - CheckRun { - name: "e2e / firefox".into(), - status: CheckRunStatus::Success, - duration_secs: Some(285.0), - }, - CheckRun { - name: "build".into(), - status: CheckRunStatus::Success, - duration_secs: Some(121.0), - }, - CheckRun { - name: "deploy-preview".into(), - status: CheckRunStatus::Success, - duration_secs: Some(93.0), - }, - CheckRun { - name: "security-scan".into(), - status: CheckRunStatus::Skipped, - duration_secs: None, - }, - CheckRun { - name: "performance".into(), - status: CheckRunStatus::Success, - duration_secs: Some(138.0), - }, - CheckRun { - name: "bundle-size".into(), - status: CheckRunStatus::Success, - duration_secs: Some(34.0), - }, - CheckRun { - name: "accessibility".into(), - status: CheckRunStatus::Success, - duration_secs: Some(72.0), - }, - ], - elapsed_secs: Some(259200.0), - elapsed_warning: Some(true), - resources: None, - comments: Some(7), + pull_request: Some(RunPullRequest { + number: 1249, + additions: Some(189), + deletions: Some(45), + comments: Some(7), + checks: vec![ + CheckRun { name: "lint".into(), status: CheckRunStatus::Success, duration_secs: Some(21.0) }, + CheckRun { name: "typecheck".into(), status: CheckRunStatus::Success, duration_secs: Some(68.0) }, + CheckRun { name: "unit-tests".into(), status: CheckRunStatus::Success, duration_secs: Some(192.0) }, + CheckRun { name: "integration-tests".into(), status: CheckRunStatus::Success, duration_secs: Some(334.0) }, + CheckRun { name: "e2e / chrome".into(), status: CheckRunStatus::Success, duration_secs: Some(262.0) }, + CheckRun { name: "e2e / firefox".into(), status: CheckRunStatus::Success, duration_secs: Some(285.0) }, + CheckRun { name: "build".into(), status: CheckRunStatus::Success, duration_secs: Some(121.0) }, + CheckRun { name: "deploy-preview".into(), status: CheckRunStatus::Success, duration_secs: Some(93.0) }, + CheckRun { name: "security-scan".into(), status: CheckRunStatus::Skipped, duration_secs: None }, + CheckRun { name: "performance".into(), status: CheckRunStatus::Success, duration_secs: Some(138.0) }, + CheckRun { name: "bundle-size".into(), status: CheckRunStatus::Success, duration_secs: Some(34.0) }, + CheckRun { name: "accessibility".into(), status: CheckRunStatus::Success, duration_secs: Some(72.0) }, + ], + }), + timings: Some(RunTimings { elapsed_secs: 259200.0, elapsed_warning: Some(true) }), + sandbox: Some(RunSandbox { id: "sb-c9d0e1f2".into(), resources: None }), question: None, - sandbox_id: Some("sb-c9d0e1f2".into()), created_at: ts("2026-02-28T14:00:00Z"), }, RunListItem { id: "run-9".into(), - repo: "cli-tools".into(), + repository: RepositoryReference { name: "cli-tools".into() }, title: "Add --verbose flag to run command".into(), - workflow: "expand".into(), + workflow: WorkflowReference { slug: "expand".into() }, status: BoardColumn::Merge, - number: Some(430), - additions: Some(56), - deletions: Some(12), - checks: vec![ - CheckRun { - name: "lint".into(), - status: CheckRunStatus::Success, - duration_secs: Some(15.0), - }, - CheckRun { - name: "typecheck".into(), - status: CheckRunStatus::Success, - duration_secs: Some(48.0), - }, - CheckRun { - name: "unit-tests".into(), - status: CheckRunStatus::Success, - duration_secs: Some(116.0), - }, - CheckRun { - name: "build".into(), - status: CheckRunStatus::Success, - duration_secs: Some(82.0), - }, - CheckRun { - name: "coverage".into(), - status: CheckRunStatus::Success, - duration_secs: Some(124.0), - }, - CheckRun { - name: "bundle-size".into(), - status: CheckRunStatus::Skipped, - duration_secs: None, - }, - ], - elapsed_secs: Some(3900.0), - elapsed_warning: Some(false), - resources: None, - comments: Some(2), + pull_request: Some(RunPullRequest { + number: 430, + additions: Some(56), + deletions: Some(12), + comments: Some(2), + checks: vec![ + CheckRun { name: "lint".into(), status: CheckRunStatus::Success, duration_secs: Some(15.0) }, + CheckRun { name: "typecheck".into(), status: CheckRunStatus::Success, duration_secs: Some(48.0) }, + CheckRun { name: "unit-tests".into(), status: CheckRunStatus::Success, duration_secs: Some(116.0) }, + CheckRun { name: "build".into(), status: CheckRunStatus::Success, duration_secs: Some(82.0) }, + CheckRun { name: "coverage".into(), status: CheckRunStatus::Success, duration_secs: Some(124.0) }, + CheckRun { name: "bundle-size".into(), status: CheckRunStatus::Skipped, duration_secs: None }, + ], + }), + timings: Some(RunTimings { elapsed_secs: 3900.0, elapsed_warning: Some(false) }), + sandbox: Some(RunSandbox { id: "sb-g3h4i5j6".into(), resources: None }), question: None, - sandbox_id: Some("sb-g3h4i5j6".into()), created_at: ts("2026-02-27T09:00:00Z"), }, RunListItem { id: "run-10".into(), - repo: "shared-types".into(), + repository: RepositoryReference { name: "shared-types".into() }, title: "Export utility type helpers".into(), - workflow: "sync_drift".into(), + workflow: WorkflowReference { slug: "sync_drift".into() }, status: BoardColumn::Merge, - number: Some(76), - additions: Some(34), - deletions: Some(8), - checks: vec![ - CheckRun { - name: "lint".into(), - status: CheckRunStatus::Success, - duration_secs: Some(12.0), - }, - CheckRun { - name: "typecheck".into(), - status: CheckRunStatus::Success, - duration_secs: Some(34.0), - }, - CheckRun { - name: "unit-tests".into(), - status: CheckRunStatus::Success, - duration_secs: Some(75.0), - }, - CheckRun { - name: "build".into(), - status: CheckRunStatus::Success, - duration_secs: Some(58.0), - }, - ], - elapsed_secs: Some(2880.0), - elapsed_warning: Some(false), - resources: None, - comments: Some(0), + pull_request: Some(RunPullRequest { + number: 76, + additions: Some(34), + deletions: Some(8), + comments: Some(0), + checks: vec![ + CheckRun { name: "lint".into(), status: CheckRunStatus::Success, duration_secs: Some(12.0) }, + CheckRun { name: "typecheck".into(), status: CheckRunStatus::Success, duration_secs: Some(34.0) }, + CheckRun { name: "unit-tests".into(), status: CheckRunStatus::Success, duration_secs: Some(75.0) }, + CheckRun { name: "build".into(), status: CheckRunStatus::Success, duration_secs: Some(58.0) }, + ], + }), + timings: Some(RunTimings { elapsed_secs: 2880.0, elapsed_warning: Some(false) }), + sandbox: Some(RunSandbox { id: "sb-k7l8m9n0".into(), resources: None }), question: None, - sandbox_id: Some("sb-k7l8m9n0".into()), created_at: ts("2026-02-26T08:00:00Z"), }, ] @@ -1137,36 +971,28 @@ mod runs { RunUsage { stages: vec![ UsageStage { - stage: "Detect Drift".into(), - model: "Opus 4.6".into(), - input_tokens: 12480, - output_tokens: 3210, + stage: UsageStageRef { id: "detect-drift".into(), name: "Detect Drift".into() }, + model: ModelReference { id: "Opus 4.6".into() }, + usage: TokenUsage { input_tokens: 12480, output_tokens: 3210, cost: 0.48 }, runtime_secs: 72.0, - cost: 0.48, }, UsageStage { - stage: "Propose Changes".into(), - model: "Gemini 3.1".into(), - input_tokens: 28640, - output_tokens: 8750, + stage: UsageStageRef { id: "propose-changes".into(), name: "Propose Changes".into() }, + model: ModelReference { id: "Gemini 3.1".into() }, + usage: TokenUsage { input_tokens: 28640, output_tokens: 8750, cost: 0.72 }, runtime_secs: 154.0, - cost: 0.72, }, UsageStage { - stage: "Review Changes".into(), - model: "Codex 5.3".into(), - input_tokens: 9120, - output_tokens: 2640, + stage: UsageStageRef { id: "review-changes".into(), name: "Review Changes".into() }, + model: ModelReference { id: "Codex 5.3".into() }, + usage: TokenUsage { input_tokens: 9120, output_tokens: 2640, cost: 0.19 }, runtime_secs: 45.0, - cost: 0.19, }, UsageStage { - stage: "Apply Changes".into(), - model: "Opus 4.6".into(), - input_tokens: 21300, - output_tokens: 6480, + stage: UsageStageRef { id: "apply-changes".into(), name: "Apply Changes".into() }, + model: ModelReference { id: "Opus 4.6".into() }, + usage: TokenUsage { input_tokens: 21300, output_tokens: 6480, cost: 0.87 }, runtime_secs: 118.0, - cost: 0.87, }, ], totals: UsageTotals { @@ -1177,25 +1003,19 @@ mod runs { }, by_model: vec![ UsageByModel { - model: "Opus 4.6".into(), + model: ModelReference { id: "Opus 4.6".into() }, stages: 2, - input_tokens: 33780, - output_tokens: 9690, - cost: 1.35, + usage: TokenUsage { input_tokens: 33780, output_tokens: 9690, cost: 1.35 }, }, UsageByModel { - model: "Gemini 3.1".into(), + model: ModelReference { id: "Gemini 3.1".into() }, stages: 1, - input_tokens: 28640, - output_tokens: 8750, - cost: 0.72, + usage: TokenUsage { input_tokens: 28640, output_tokens: 8750, cost: 0.72 }, }, UsageByModel { - model: "Codex 5.3".into(), + model: ModelReference { id: "Codex 5.3".into() }, stages: 1, - input_tokens: 9120, - output_tokens: 2640, - cost: 0.19, + usage: TokenUsage { input_tokens: 9120, output_tokens: 2640, cost: 0.19 }, }, ], } @@ -1255,32 +1075,28 @@ mod usage { pub fn aggregate() -> AggregateUsage { AggregateUsage { - total_runs: 9, - total_input_tokens: 643_860, - total_output_tokens: 189_720, - total_cost: 20.34, - total_runtime_secs: 3_501.0, + totals: AggregateUsageTotals { + runs: 9, + input_tokens: 643_860, + output_tokens: 189_720, + cost: 20.34, + runtime_secs: 3_501.0, + }, by_model: vec![ UsageByModel { - model: "Opus 4.6".into(), + model: ModelReference { id: "Opus 4.6".into() }, stages: 18, - input_tokens: 304_020, - output_tokens: 87_210, - cost: 12.15, + usage: TokenUsage { input_tokens: 304_020, output_tokens: 87_210, cost: 12.15 }, }, UsageByModel { - model: "Gemini 3.1".into(), + model: ModelReference { id: "Gemini 3.1".into() }, stages: 9, - input_tokens: 257_760, - output_tokens: 78_750, - cost: 6.48, + usage: TokenUsage { input_tokens: 257_760, output_tokens: 78_750, cost: 6.48 }, }, UsageByModel { - model: "Codex 5.3".into(), + model: ModelReference { id: "Codex 5.3".into() }, stages: 9, - input_tokens: 82_080, - output_tokens: 23_760, - cost: 1.71, + usage: TokenUsage { input_tokens: 82_080, output_tokens: 23_760, cost: 1.71 }, }, ], } @@ -1296,33 +1112,29 @@ mod workflows { name: "Fix Build".into(), slug: "fix_build".into(), filename: "fix_build.dot".into(), - last_run: Some("2 hours ago".into()), + last_run: Some(WorkflowLastRun { label: "2 hours ago".into() }), schedule: None, - next_run: None, }, WorkflowListItem { name: "Implement Feature".into(), slug: "implement".into(), filename: "implement.dot".into(), - last_run: Some("4 days ago".into()), + last_run: Some(WorkflowLastRun { label: "4 days ago".into() }), schedule: None, - next_run: None, }, WorkflowListItem { name: "Sync Drift".into(), slug: "sync_drift".into(), filename: "sync_drift.dot".into(), - last_run: Some("1 day ago".into()), + last_run: Some(WorkflowLastRun { label: "1 day ago".into() }), schedule: None, - next_run: None, }, WorkflowListItem { name: "Expand Product".into(), slug: "expand".into(), filename: "expand.dot".into(), - last_run: Some("2 weeks ago".into()), + last_run: Some(WorkflowLastRun { label: "2 weeks ago".into() }), schedule: None, - next_run: None, }, ] } @@ -2212,9 +2024,8 @@ mod verifications { fn recent_results_from_def(defs: &[RecentResultDef]) -> Vec { defs.iter() .map(|r| RecentControlResult { - run_id: r.run_id.into(), - run_title: r.run_title.into(), - workflow: r.workflow.into(), + run: RunReference { id: r.run_id.into(), title: r.run_title.into() }, + workflow: WorkflowReference { slug: r.workflow.into() }, result: r.result, timestamp: r.timestamp.into(), }) @@ -2293,7 +2104,7 @@ mod verifications { slug: ctrl.slug.into(), description: ctrl.description.into(), type_: ctrl.type_, - category: cat.name.into(), + category: CategoryReference { name: cat.name.into() }, }, performance: ControlPerformance { mode: ctrl.mode, @@ -2345,9 +2156,8 @@ mod retros { pub fn list_items() -> Vec { vec![ RetroListItem { - run_id: "run-1".into(), - workflow_name: "implement".into(), - goal: "Add rate limiting to auth endpoints".into(), + run: RunReference { id: "run-1".into(), title: "Add rate limiting to auth endpoints".into() }, + workflow: WorkflowReference { slug: "implement".into() }, timestamp: ts("2026-02-28T14:32:00Z"), smoothness: Some(SmoothnessRating::Smooth), stats: RetroStats { @@ -2366,9 +2176,8 @@ mod retros { friction_point_count: 0, }, RetroListItem { - run_id: "run-2".into(), - workflow_name: "implement".into(), - goal: "Migrate to React Router v7".into(), + run: RunReference { id: "run-2".into(), title: "Migrate to React Router v7".into() }, + workflow: WorkflowReference { slug: "implement".into() }, timestamp: ts("2026-02-28T10:15:00Z"), smoothness: Some(SmoothnessRating::Bumpy), stats: RetroStats { @@ -2390,9 +2199,8 @@ mod retros { friction_point_count: 2, }, RetroListItem { - run_id: "run-6".into(), - workflow_name: "implement".into(), - goal: "Add dark mode toggle".into(), + run: RunReference { id: "run-6".into(), title: "Add dark mode toggle".into() }, + workflow: WorkflowReference { slug: "implement".into() }, timestamp: ts("2026-02-27T16:45:00Z"), smoothness: Some(SmoothnessRating::Effortless), stats: RetroStats { @@ -2410,9 +2218,8 @@ mod retros { friction_point_count: 0, }, RetroListItem { - run_id: "run-3".into(), - workflow_name: "fix_build".into(), - goal: "Fix config parsing for nested values".into(), + run: RunReference { id: "run-3".into(), title: "Fix config parsing for nested values".into() }, + workflow: WorkflowReference { slug: "fix_build".into() }, timestamp: ts("2026-02-27T09:20:00Z"), smoothness: Some(SmoothnessRating::Struggled), stats: RetroStats { @@ -2431,9 +2238,8 @@ mod retros { friction_point_count: 3, }, RetroListItem { - run_id: "run-8".into(), - workflow_name: "implement".into(), - goal: "Implement webhook retry logic".into(), + run: RunReference { id: "run-8".into(), title: "Implement webhook retry logic".into() }, + workflow: WorkflowReference { slug: "implement".into() }, timestamp: ts("2026-02-26T11:00:00Z"), smoothness: Some(SmoothnessRating::Smooth), stats: RetroStats { @@ -2478,7 +2284,7 @@ mod sessions { SessionListItem { id: uid(S1), title: "Add rate limiting to auth endpoints".into(), - model: "Opus 4.6".into(), + model: ModelReference { id: "Opus 4.6".into() }, last_message_preview: "Done. I've created the rate limiter and wired it up...".into(), created_at: ts("2026-03-06T14:30:00Z"), updated_at: ts("2026-03-06T15:45:00Z"), @@ -2486,7 +2292,7 @@ mod sessions { SessionListItem { id: uid(S2), title: "Fix config parsing for nested values".into(), - model: "Sonnet 4.6".into(), + model: ModelReference { id: "Sonnet 4.6".into() }, last_message_preview: "Fixed. The parser now tracks the current section header...".into(), created_at: ts("2026-03-06T12:30:00Z"), updated_at: ts("2026-03-06T13:15:00Z"), @@ -2494,7 +2300,7 @@ mod sessions { SessionListItem { id: uid(S3), title: "Migrate to React Router v7".into(), - model: "Opus 4.6".into(), + model: ModelReference { id: "Opus 4.6".into() }, last_message_preview: "You're on React Router 6.22. The migration to v7 involves...".into(), created_at: ts("2026-03-05T10:00:00Z"), updated_at: ts("2026-03-05T11:30:00Z"), @@ -2502,7 +2308,7 @@ mod sessions { SessionListItem { id: uid(S4), title: "Add dark mode toggle".into(), - model: "Sonnet 4.6".into(), + model: ModelReference { id: "Sonnet 4.6".into() }, last_message_preview: "Added a dark mode toggle to the settings panel with system preference detection.".into(), created_at: ts("2026-03-05T09:00:00Z"), updated_at: ts("2026-03-05T09:45:00Z"), @@ -2510,7 +2316,7 @@ mod sessions { SessionListItem { id: uid(S5), title: "Update OpenAPI spec for v3".into(), - model: "Opus 4.6".into(), + model: ModelReference { id: "Opus 4.6".into() }, last_message_preview: "Updated all endpoint schemas to v3 format with discriminated unions.".into(), created_at: ts("2026-03-05T08:00:00Z"), updated_at: ts("2026-03-05T08:30:00Z"), @@ -2518,7 +2324,7 @@ mod sessions { SessionListItem { id: uid(S6), title: "Terraform module for Redis cluster".into(), - model: "Opus 4.6".into(), + model: ModelReference { id: "Opus 4.6".into() }, last_message_preview: "Created the module with 3-node cluster, automatic failover, and encryption at rest.".into(), created_at: ts("2026-03-03T15:00:00Z"), updated_at: ts("2026-03-03T16:00:00Z"), @@ -2526,7 +2332,7 @@ mod sessions { SessionListItem { id: uid(S7), title: "Add pipeline event types".into(), - model: "Sonnet 4.6".into(), + model: ModelReference { id: "Sonnet 4.6".into() }, last_message_preview: "Added PipelineStarted, StageCompleted, and PipelineFailed event types.".into(), created_at: ts("2026-03-01T11:00:00Z"), updated_at: ts("2026-03-01T12:00:00Z"), @@ -2534,7 +2340,7 @@ mod sessions { SessionListItem { id: uid(S8), title: "Implement webhook retry logic".into(), - model: "Opus 4.6".into(), + model: ModelReference { id: "Opus 4.6".into() }, last_message_preview: "Implemented exponential backoff with jitter, max 5 retries over 24 hours.".into(), created_at: ts("2026-02-28T09:00:00Z"), updated_at: ts("2026-02-28T10:00:00Z"), @@ -2546,7 +2352,7 @@ mod sessions { let parsed = id.parse::().ok()?; match parsed.as_u128() { S1 => Some(SessionDetail { - id: uid(S1), title: "Add rate limiting to auth endpoints".into(), model: "Opus 4.6".into(), created_at: ts("2026-03-06T14:30:00Z"), updated_at: ts("2026-03-06T15:45:00Z"), + id: uid(S1), title: "Add rate limiting to auth endpoints".into(), model: ModelReference { id: "Opus 4.6".into() }, created_at: ts("2026-03-06T14:30:00Z"), updated_at: ts("2026-03-06T15:45:00Z"), turns: vec![ SessionTurn::UserTurn(UserTurn { kind: UserTurnKind::User, content: "Add rate limiting to the auth endpoints. We're getting hit with brute force attempts on /api/auth/login and /api/auth/register. Use a sliding window approach with Redis, 10 requests per minute per IP.".into(), created_at: ts("2026-02-28T10:00:00Z") }), SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "I'll implement sliding window rate limiting using Redis. Let me first look at the existing auth routes and middleware setup.".into(), created_at: ts("2026-02-28T10:00:15Z") }), @@ -2565,7 +2371,7 @@ mod sessions { ], }), S2 => Some(SessionDetail { - id: uid(S2), title: "Fix config parsing for nested values".into(), model: "Sonnet 4.6".into(), created_at: ts("2026-03-06T12:30:00Z"), updated_at: ts("2026-03-06T13:15:00Z"), + id: uid(S2), title: "Fix config parsing for nested values".into(), model: ModelReference { id: "Sonnet 4.6".into() }, created_at: ts("2026-03-06T12:30:00Z"), updated_at: ts("2026-03-06T13:15:00Z"), turns: vec![ SessionTurn::UserTurn(UserTurn { kind: UserTurnKind::User, content: "The CLI crashes when parsing nested TOML config values like [database.connection]. Can you debug and fix this?".into(), created_at: ts("2026-02-28T10:00:00Z") }), SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "Let me look at the config parser to understand how nested keys are handled.".into(), created_at: ts("2026-02-28T10:00:12Z") }), @@ -2581,7 +2387,7 @@ mod sessions { ], }), S3 => Some(SessionDetail { - id: uid(S3), title: "Migrate to React Router v7".into(), model: "Opus 4.6".into(), created_at: ts("2026-03-05T10:00:00Z"), updated_at: ts("2026-03-05T11:30:00Z"), + id: uid(S3), title: "Migrate to React Router v7".into(), model: ModelReference { id: "Opus 4.6".into() }, created_at: ts("2026-03-05T10:00:00Z"), updated_at: ts("2026-03-05T11:30:00Z"), turns: vec![ SessionTurn::UserTurn(UserTurn { kind: UserTurnKind::User, content: "Help me migrate our app from React Router v6 to v7. We're using createBrowserRouter with data loaders.".into(), created_at: ts("2026-02-26T10:00:00Z") }), SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "I'll audit your current router setup and identify what needs to change for v7. Let me scan the codebase.".into(), created_at: ts("2026-02-26T10:00:10Z") }), diff --git a/crates/arc-api/src/server.rs b/crates/arc-api/src/server.rs index d6ee64b95..67a94fe5f 100644 --- a/crates/arc-api/src/server.rs +++ b/crates/arc-api/src/server.rs @@ -315,19 +315,23 @@ async fn get_aggregate_usage( .by_model .iter() .map(|(model, totals)| arc_types::UsageByModel { - model: model.clone(), + model: arc_types::ModelReference { id: model.clone() }, stages: totals.stages, - input_tokens: totals.input_tokens, - output_tokens: totals.output_tokens, - cost: totals.cost, + usage: arc_types::TokenUsage { + input_tokens: totals.input_tokens, + output_tokens: totals.output_tokens, + cost: totals.cost, + }, }) .collect(); let response = arc_types::AggregateUsage { - total_runs: agg.total_runs, - total_input_tokens: by_model.iter().map(|m| m.input_tokens).sum(), - total_output_tokens: by_model.iter().map(|m| m.output_tokens).sum(), - total_cost: by_model.iter().map(|m| m.cost).sum(), - total_runtime_secs: agg.total_runtime_secs, + totals: arc_types::AggregateUsageTotals { + runs: agg.total_runs, + input_tokens: by_model.iter().map(|m| m.usage.input_tokens).sum(), + output_tokens: by_model.iter().map(|m| m.usage.output_tokens).sum(), + cost: by_model.iter().map(|m| m.usage.cost).sum(), + runtime_secs: agg.total_runtime_secs, + }, by_model, }; (StatusCode::OK, Json(response)).into_response() @@ -377,7 +381,7 @@ async fn list_runs( .map(|(id, managed_run)| RunStatusResponse { id: id.clone(), status: managed_run.status, - error: managed_run.error.clone(), + error: managed_run.error.as_ref().map(|msg| arc_types::RunError { message: msg.clone() }), queue_position: queue_positions.get(id).copied(), created_at: managed_run.created_at, }) @@ -458,6 +462,7 @@ async fn start_run( queue_position: None, created_at, }), + ) .into_response() } @@ -690,7 +695,7 @@ async fn get_run_status( Json(RunStatusResponse { id: id.clone(), status: managed_run.status, - error: managed_run.error.clone(), + error: managed_run.error.as_ref().map(|msg| arc_types::RunError { message: msg.clone() }), created_at: managed_run.created_at, queue_position, }), @@ -1506,11 +1511,11 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); let body = body_json(response.into_body()).await; - assert_eq!(body["total_runs"].as_i64().unwrap(), 0); - assert_eq!(body["total_input_tokens"].as_i64().unwrap(), 0); - assert_eq!(body["total_output_tokens"].as_i64().unwrap(), 0); - assert_eq!(body["total_cost"].as_f64().unwrap(), 0.0); - assert_eq!(body["total_runtime_secs"].as_f64().unwrap(), 0.0); + assert_eq!(body["totals"]["runs"].as_i64().unwrap(), 0); + assert_eq!(body["totals"]["input_tokens"].as_i64().unwrap(), 0); + assert_eq!(body["totals"]["output_tokens"].as_i64().unwrap(), 0); + assert_eq!(body["totals"]["cost"].as_f64().unwrap(), 0.0); + assert_eq!(body["totals"]["runtime_secs"].as_f64().unwrap(), 0.0); assert!(body["by_model"].as_array().unwrap().is_empty()); } @@ -1562,7 +1567,7 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); let body = body_json(response.into_body()).await; - assert_eq!(body["total_runs"].as_i64().unwrap(), 1); + assert_eq!(body["totals"]["runs"].as_i64().unwrap(), 1); } #[tokio::test]