fabro/lib/crates/fabro-api/tests
Bryan Helmkamp d17599898e
feat(web): show creator avatar on run "Created by" cell (#319)
## Summary

The run Overview tab's "Created by" cell rendered every user as a
colored circle with the first letter of their login. Reviewers and run
owners expected the same GitHub avatar shown on `/profile` and in the
top-right nav. The cell only had `login` to work with — the
`PrincipalUser` schema carried no avatar URL.

This threads an optional `avatar_url` through `UserPrincipal`
end-to-end: schema, server auth, and frontend. The avatar is captured at
action time from the request's auth context and persisted with the run's
`created_by` principal — a point-in-time snapshot, the same pattern as
audit logs and chat apps.

## What changed

- **`fabro-types`** — `UserPrincipal` gains `avatar_url: Option<String>`
with `#[serde(default, skip_serializing_if)]`, plus a
`Principal::user_with_avatar` constructor. The existing
`Principal::user` constructor is unchanged (sets `None`), so test
fixtures and CLI/replay call sites need no edits.
- **OpenAPI** — `PrincipalUser` gains an optional nullable `avatar_url`;
Rust (progenitor) and TypeScript clients regenerated.
- **`fabro-server`** — `auth_context_from_session` (cookie auth) and
`classify_user_token` (JWT auth) populate the principal's avatar from
the session/JWT, treating an empty string as `None`.
- **`fabro-web`** — the `run-summary-panel` "Created by" cell renders an
`<img>` when `avatar_url` is present, falling back to the initial circle
otherwise.

## Compatibility

The field is optional with serde defaults, so old persisted runs and
`RunEvent.actor` payloads deserialize unchanged — they show the
initial-circle fallback. No migration or backfill.

## Known gap

CLI-initiated runs (`fabro run ...`) still show the initial circle: the
CLI auth flow hardcodes an empty `avatar_url` in the JWT subject
(`cli_flow.rs:508`). Wiring the avatar through CLI login
(`~/.fabro/auth.json`, JWT claims, refresh-token chain) is a deliberate
follow-up. Web-initiated runs get the avatar today.

## Test plan

- `cargo nextest run --workspace` — 5,832 tests pass, including new
`principal.rs` and `principal_round_trip.rs` cases covering avatar
serialization and legacy-JSON (no-field) deserialization.
- `cd apps/fabro-web && bun test run-summary-panel` — 13 tests pass,
including a new case asserting the `<img>` renders with the avatar src.
- `bun run typecheck`, `cargo +nightly-2026-04-14 fmt --check --all`,
and `clippy --workspace --all-targets -- -D warnings` all clean.
- Manual: restart `fabro server`, create a run from the web UI, confirm
the real avatar renders on the Overview tab; confirm an older run falls
back to the initial circle.

---

[![Compound Engineering
v2.60.0](https://img.shields.io/badge/Compound_Engineering-v2.60.0-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
🤖 Generated with Claude Opus 4.7 (1M context, extended thinking) via
[Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:11:57 -04:00
..
billed_token_counts_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
command_termination_round_trip.rs feat(command): distinguish cancelled commands from timeouts 2026-04-30 22:45:37 -04:00
diff_stats_round_trip.rs refactor: unify duplicate API types via with_replacement 2026-04-28 12:29:37 -07:00
diff_summary_round_trip.rs feat(runs): surface diff summary counts 2026-05-07 17:34:32 -07:00
event_envelope_round_trip.rs refactor(api): reuse canonical run events 2026-04-29 20:36:59 -04:00
git_context_round_trip.rs refactor: unify duplicate API types via with_replacement 2026-04-28 12:29:37 -07:00
interview_option_round_trip.rs refactor(api): align run projection types 2026-04-29 20:32:50 -04:00
interview_question_record_round_trip.rs refactor(api): align run projection types 2026-04-29 20:32:50 -04:00
model_costs_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
model_features_round_trip.rs refactor(llm): split provider identity from adapters (#280) 2026-05-16 13:13:41 -04:00
model_limits_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
model_round_trip.rs refactor(llm): split provider identity from adapters (#280) 2026-05-16 13:13:41 -04:00
model_test_mode_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
pending_interview_record_round_trip.rs refactor(runs): simplify run projection shape 2026-05-09 23:31:43 -04:00
permission_level_round_trip.rs feat(api): require typed PermissionLevel on session create (#300) 2026-05-17 17:15:20 -04:00
pre_run_push_outcome_round_trip.rs refactor: unify duplicate API types via with_replacement 2026-04-28 12:29:37 -07:00
principal_round_trip.rs feat(web): show creator avatar on run "Created by" cell (#319) 2026-05-20 09:11:57 -04:00
provider_id_round_trip.rs feat(server): add GET /api/v1/providers and /settings/models page (#321) 2026-05-20 09:05:00 -04:00
provider_round_trip.rs feat(server): add GET /api/v1/providers and /settings/models page (#321) 2026-05-20 09:05:00 -04:00
pull_request_round_trip.rs feat(pr): support GitHub pull request associations (#270) 2026-05-16 12:47:27 -04:00
question_type_round_trip.rs refactor(api): unify leaf API types 2026-04-29 20:21:23 -04:00
run_billing_stage_round_trip.rs feat(llm): add catalog controls and speed billing (#249) 2026-05-13 14:12:16 -04:00
run_event_round_trip.rs refactor(workflow): remove local worktree mode 2026-05-09 11:04:23 -04:00
run_failure_round_trip.rs feat(errors): add structured failure diagnostics (#277) 2026-05-16 13:25:07 -04:00
run_integrations_round_trip.rs Move GitHub token permissions to [run.integrations.github.permissions] (#215) 2026-05-05 15:33:31 -04:00
run_projection_round_trip.rs feat(api): unify public run shape 2026-05-10 20:48:55 -04:00
run_sandbox_round_trip.rs feat(sandbox): prepare clone layout for multi-repo runs (#250) 2026-05-14 09:38:20 -04:00
run_summary_round_trip.rs fix(server): persist manifest metadata names (#302) 2026-05-18 08:31:23 -04:00
sandbox_details_round_trip.rs feat(sandbox): surface provider links and network policy (#275) 2026-05-16 10:16:18 -04:00
sandbox_services_round_trip.rs fix(server): improve sandbox service discovery 2026-05-10 12:40:38 -04:00
secret_metadata_round_trip.rs refactor(auth): split credential sources and vault schemas (#306) 2026-05-18 11:07:42 -04:00
secret_type_round_trip.rs refactor(auth): split credential sources and vault schemas (#306) 2026-05-18 11:07:42 -04:00
server_settings_round_trip.rs feat(server): support stdout log destination 2026-04-26 14:52:15 -04:00
stage_completion_round_trip.rs refactor(run-projection): use stage vocabulary 2026-05-01 19:56:22 -04:00
stage_handler_round_trip.rs feat(api): expose stage handlers on run stages 2026-05-08 13:36:02 -07:00
stage_outcome_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
stage_projection_round_trip.rs feat(billing): project live stage token usage 2026-05-09 15:25:51 -04:00
stage_state_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
status_round_trip.rs feat(api): unify public run shape 2026-05-10 20:48:55 -04:00
submit_answer_request_round_trip.rs fix(api): type interview answer submissions 2026-05-08 09:18:35 -07:00
workflow_settings_round_trip.rs fix(workflow): ignore deprecated project directory 2026-05-09 10:55:56 -04:00