Skip to content

feat(coach): make the AI coach agentic and the app's home-screen core - #96

Merged
YKDBontekoe merged 7 commits into
mainfrom
cursor/agentic-coach-integration-4a43
Jul 13, 2026
Merged

YKDBontekoe merged 7 commits into
mainfrom
cursor/agentic-coach-integration-4a43

Conversation

@YKDBontekoe

@YKDBontekoe YKDBontekoe commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

Makes the AI coach genuinely agentic and puts it at the center of the app's home screen, per the KYNOS product vision of an AI-first running coach.

Agentic tool calling — the coach can now reason over runner data on demand instead of relying only on a fixed context dump that gets truncated for token budget:

  • 7 tools: get_recent_runs, get_run_detail (per-km splits), get_health_trend, get_training_load, get_character_progress, get_personal_bests, compute_pace_plan
  • ReAct-style loop in CoachChat._streamAgenticAnswer: the model emits a TOOL_CALL: {...} directive, the app executes it and feeds the result back, up to 2 tool calls per turn before it's forced to answer directly
  • Non-tool-call answers still stream live token-by-token (the loop only buffers while a response could be a tool call)
  • Tool syntax is always stripped from what the athlete sees, even if the model misbehaves past the budget
  • Every tool is gated by the same CoachDataSource privacy permissions used for the static context — tool calling can never surface more than the athlete already consented to share (Zero-Knowledge invariant, AGENTS.md §11)
  • On-device tool instructions only ship on GemmaInferenceTier.full to respect the tiny prompt budget on constrained devices; cloud always gets them
  • New AgentToolStepList chat UI shows which tools ran (running/success/error), so it's not a black box

Home screen redesign — added CoachAgentHeroCard to the top of the Today dashboard: a live insight line plus one-tap prompts ("How's my training load?", "Plan my pace for a 10K", "Any personal bests lately?") that open Coach chat pre-filled, making the coach the app's primary entry point instead of a hidden modal.

Testing

  • flutter analyze — zero issues
  • flutter test — 220/220 passing, including new coverage:
    • Pure-domain parser tests (coach_tool_call_parser_test.dart)
    • ExecuteCoachToolUseCase unit tests for all 7 tools, including a GPS-non-leakage check
    • A full-stack ProviderContainer integration test exercising the real CoachChat notifier + a scripted AiCoachRepository fake, proving: a TOOL_CALL directive is executed and the result is fed into the follow-up prompt; plain answers stream live with no tool steps; the loop stops after the tool budget and never leaks raw TOOL_CALL syntax
    • Widget tests for the new tool-step chip UI
  • flutter build web — succeeds
  • bash scripts/check_design_system.sh / check_architecture.sh — pass
  • dart run tool/generate_codemap.dart — CODEMAP.md updated
  • ✅ Manual GUI walkthrough (Chrome, flutter build web): confirmed the new Coach card renders correctly with all 3 prompt chips fully visible, and tapping a chip navigates into Coach chat with the question pre-filled

kynos_coach_hero_card_final_demo.mp4

New "KYNOS Coach" hero card on the Today dashboard, with tap-through into Coach chat.

Note for reviewers

While testing in a real Chrome browser I found a pre-existing, unrelated bug: on wide/desktop web viewports the floating bottom nav bar scrolls with page content instead of staying fixed, eventually overlapping the header. I reproduced this before touching any code and confirmed it's unaffected by this PR's changes (it's in ResponsiveCenter/ShellPage's web layout, not anything coach-related). Left out of scope here to keep this diff focused — flagging for a follow-up.

Checklist

  • flutter analyze — zero issues
  • flutter test — zero failures
  • dart run tool/generate_codemap.dart — CODEMAP.md updated
  • bash scripts/check_design_system.sh — passes
  • bash scripts/check_architecture.sh — passes
  • flutter build web — succeeds
  • Visual proof attached
  • PR title uses Conventional Commits with lowercase subject

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features
    • Agentic coach chat now supports tool-powered steps, using tool results to continue the conversation.
    • Tool-step progress is now visible in chat (running, success, error).
    • Added a KYNOS Coach hero card on the dashboard with an insight line and quick prompts.
  • Bug Fixes
    • Prevented raw tool-call markup from appearing in assistant responses.
    • Added safeguards to limit repeated tool actions within a single response.
  • Tests
    • Added unit, widget, and end-to-end coverage for tool parsing/execution and updated UI behavior.

cursoragent and others added 5 commits July 13, 2026 08:56
Adds the pure-Dart building blocks for the coach agent to reason over
runner data on demand instead of relying only on a fixed context dump:

- CoachToolCall/CoachToolResult/CoachToolStep entities
- CoachAgentToolCatalog: 7 tools (recent runs, run detail/splits,
  health trend, training load, character progress, personal bests,
  pace-plan math) with a compact system-prompt block
- CoachToolCallParser: ReAct-style `TOOL_CALL: {...}` directive parsing
- ExecuteCoachToolUseCase (+ health/context query helpers): executes
  tool calls gated by the same CoachDataSource privacy permissions used
  for the static coach context

Co-authored-by: Youri Bontekoe <YKDBontekoe@users.noreply.github.com>
CoachChat._streamAgenticAnswer wraps each assistant turn: it detects a
TOOL_CALL directive (peeking the buffered response so plain answers
still stream live token-by-token), executes it via
ExecuteCoachToolUseCase, feeds the result back as a follow-up prompt,
and repeats up to 2 tool calls before forcing a final answer. Tool
syntax is always stripped so it never leaks to the athlete, even if
the model keeps trying past the budget.

- ChatMessage gains a toolSteps field (+ codec persistence) so the UI
  can show which tools were used
- Both the on-device (full tier only, to respect the tiny token
  budget) and cloud system prompts now advertise the tool catalog
- executeCoachToolUseCaseProvider wires HealthRepository into the
  use case via shared/providers/

Co-authored-by: Youri Bontekoe <YKDBontekoe@users.noreply.github.com>
AgentToolStepList renders a small running/success/error chip per tool
call above the assistant's answer, so athletes can see what data the
coach looked up instead of a hidden black box. Wired through
AssistantBubble and MessageBubble.

Co-authored-by: Youri Bontekoe <YKDBontekoe@users.noreply.github.com>
Adds CoachAgentHeroCard at the top of the dashboard: a live insight
line plus one-tap prompts ('How's my training load?', 'Plan my pace
for a 10K', 'Any personal bests lately?') that showcase the coach's
new tool-calling capabilities and open Coach chat with the question
pre-filled. Makes the coach the app's home-screen entry point rather
than a hidden modal, per the KYNOS product goal of an AI-first
running coach.

Co-authored-by: Youri Bontekoe <YKDBontekoe@users.noreply.github.com>
Co-authored-by: Youri Bontekoe <YKDBontekoe@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b95f3f36-2b24-4ca2-a34a-d44eeebef56a

📥 Commits

Reviewing files that changed from the base of the PR and between d0707cc and aa7e722.

📒 Files selected for processing (15)
  • CODEMAP.md
  • lib/domain/entities/coach/coach_tool_call.dart
  • lib/domain/entities/coach/coach_tool_call.freezed.dart
  • lib/domain/usecases/coach/coach_tool_context_queries.dart
  • lib/domain/usecases/coach/coach_tool_health_queries.dart
  • lib/domain/usecases/coach/execute_coach_tool_usecase.dart
  • lib/domain/utils/coach_tool_call_parser.dart
  • lib/domain/utils/coach_tool_result_helpers.dart
  • lib/features/coach_chat/providers/coach_chat_provider.dart
  • lib/features/coach_chat/providers/coach_chat_provider.g.dart
  • lib/infrastructure/coach/coach_conversation_codec.dart
  • test/domain/usecases/coach/execute_coach_tool_usecase_test.dart
  • test/domain/utils/coach_tool_call_parser_test.dart
  • test/features/coach_chat/coach_chat_agentic_tool_test.dart
  • tool/generate_codemap.dart
🚧 Files skipped from review as they are similar to previous changes (12)
  • lib/features/coach_chat/providers/coach_chat_provider.g.dart
  • test/domain/utils/coach_tool_call_parser_test.dart
  • lib/domain/usecases/coach/execute_coach_tool_usecase.dart
  • lib/domain/utils/coach_tool_result_helpers.dart
  • lib/infrastructure/coach/coach_conversation_codec.dart
  • lib/domain/utils/coach_tool_call_parser.dart
  • lib/domain/usecases/coach/coach_tool_health_queries.dart
  • test/features/coach_chat/coach_chat_agentic_tool_test.dart
  • lib/domain/usecases/coach/coach_tool_context_queries.dart
  • test/domain/usecases/coach/execute_coach_tool_usecase_test.dart
  • lib/features/coach_chat/providers/coach_chat_provider.dart
  • CODEMAP.md

📝 Walkthrough

Walkthrough

Agentic coach tooling now spans domain models, tool parsing and execution, AI streaming, conversation persistence, coach-chat UI, dashboard entry points, tests, and regenerated project documentation.

Changes

Agentic coach tools

Layer / File(s) Summary
Tool contracts and parsing
lib/domain/entities/..., lib/domain/utils/...
Messages now carry tool steps; tool calls, results, statuses, catalog definitions, parsing, and result helpers were added.
Context and health tool execution
lib/domain/usecases/coach/..., lib/shared/providers/...
Context and health tools validate inputs and permissions, query data, format results, and dispatch by tool name.
Agentic streaming and persistence
lib/features/coach_chat/providers/..., lib/infrastructure/ai/..., lib/infrastructure/coach/...
Streaming detects and executes tool calls, limits tool rounds, re-prompts the model, updates statuses, and persists tool steps.
Tool-step and dashboard presentation
lib/features/coach_chat/presentation/widgets/..., lib/features/dashboard/...
Tool statuses are rendered in assistant bubbles, and the dashboard gains a coach hero card with seeded prompts.
Validation and project map
test/..., CODEMAP.md, tool/generate_codemap.dart
Tests cover execution, parsing, streaming limits, widget states, and dashboard assertions; project metadata was regenerated.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: released

Poem

A rabbit hops where tool calls flow,
With tiny steps that spin and glow.
The coach now asks, the tools reply,
While prompts and answers gently fly.
“Binky!” says Bun, “the loop is spry!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the two main changes: agentic AI coach behavior and the new coach-focused home-screen card.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

build_runner embeds a content hash in coach_chat_provider.g.dart that
was stale after editing the notifier for the agentic tool loop.

Co-authored-by: Youri Bontekoe <YKDBontekoe@users.noreply.github.com>
@YKDBontekoe
YKDBontekoe marked this pull request as ready for review July 13, 2026 09:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (2)
CODEMAP.md (1)

271-271: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

MD037 false positive from underscores in class names in auto-generated table.

The markdownlint warning at line 271 is triggered by _RunRouteScaffold and _RouteContent in the third column — markdownlint interprets the underscores as emphasis markers with a space inside. This is a false positive since these are Dart private class names. The fix belongs in the codemap generator (tool/generate_codemap.dart): wrap the symbols column in backticks to suppress the warning.

Generator fix — wrap symbols column in backticks

In tool/generate_codemap.dart, when emitting the third column of the layer-map table, wrap the exported-symbols list in backticks:

- | `$path` | $lineCount | $symbols |
+ | `$path` | $lineCount | `$symbols` |

This prevents markdownlint from interpreting underscores in class names as emphasis markers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CODEMAP.md` at line 271, Update the codemap generator’s layer-map table
emission in generate_codemap.dart to wrap the exported-symbols column value in
Markdown backticks, preserving the listed Dart symbol names while preventing
underscores from being parsed as emphasis markers.

Source: Linters/SAST tools

lib/features/coach_chat/providers/coach_chat_provider.dart (1)

278-435: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Extract the agent loop from this oversized notifier.

This provider is now 575 lines and mixes state mutation, streaming protocol handling, tool execution, and prompt construction. Move the agent-loop orchestration into a focused collaborator/use case, leaving the notifier responsible for Riverpod state updates.

As per coding guidelines, “Keep hand-written source files to roughly 250 lines or less.” <coding_guidelines>

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/coach_chat/providers/coach_chat_provider.dart` around lines 278
- 435, The oversized notifier should no longer own agent-loop orchestration.
Extract _streamAgenticAnswer and _buildToolFollowUpPrompt, including streaming,
tool-step coordination, retry limits, and prompt construction, into a focused
collaborator or use case; keep _appendAssistantContent, _setAssistantContent,
and _setAssistantToolSteps in the notifier for Riverpod state updates, and wire
the collaborator to report stream/content/tool-step updates through a suitable
callback interface.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/domain/entities/coach/coach_tool_call.dart`:
- Around line 4-29: Replace the manually implemented CoachToolCall model and
related tool-contract models with typed `@freezed` models, changing JSON-like
fields from Map<String, dynamic> to Map<String, Object?>. Remove custom equality
and hash-code helpers such as _mapEquals, and rely on Freezed-generated
immutable collection equality and model behavior across the affected contracts.

In `@lib/domain/usecases/coach/coach_tool_context_queries.dart`:
- Around line 19-40: Update the context formatting logic after the initial
availability check so each field is gated by its owning CoachDataSource
permission: add ACWR only when readinessAcwr is enabled, weekly momentum only
when weeklyMomentum is enabled, and adjustment hints only when their
corresponding training-insights permission is enabled. Preserve the existing
formatting and initial disabled-result behavior.
- Around line 116-125: Update computePacePlan to parse distance_km without the
5.0 fallback and validate it alongside target_time_minutes. Return the existing
error result when either required value is missing or non-positive, while
preserving the current range validation and pace-plan flow for valid inputs.

In `@lib/domain/usecases/coach/coach_tool_health_queries.dart`:
- Around line 112-128: Validate the normalized metric in the coach health query
flow before calling _health.getSummaries, accepting only hrv, rhr, sleep, and
steps; return the existing error result for unsupported values. Update the
validation in the method containing the shown metric handling so _metricValue
and _metricUnit never receive an unknown metric and cannot report HRV under
another label.

In `@lib/domain/utils/coach_tool_call_parser.dart`:
- Around line 14-18: Update CoachToolCallParser.tryParse and its related
detection logic to accept a TOOL_CALL marker only when it begins at the first
non-whitespace position, matching isDefinitelyNotToolCall’s prefix rule. Ensure
embedded directives are rejected rather than parsed, preventing them from
entering the streaming path; preserve valid leading-whitespace tool calls.

In `@lib/features/coach_chat/providers/coach_chat_provider.dart`:
- Around line 370-375: Update the multi-step tool-resolution flow around
_buildToolFollowUpPrompt so each iteration preserves previously generated
tool-result summaries or appends equivalent synthetic tool-result messages to
the follow-up history. Ensure the final inference receives results from every
tool execution, not only the most recent one, while retaining the existing
final-attempt behavior.
- Around line 368-377: Prevent another inference from starting after
cancellation in the tool-step loop. In the flow around _setAssistantToolSteps
and executeTool, check _cancelRequested immediately after updating the tool
status and exit the current operation before building the follow-up prompt; also
check it at the top of each iteration so cancellation is honored before any
sendCoach request.

In `@lib/infrastructure/coach/coach_conversation_codec.dart`:
- Around line 178-185: Update CoachConversationCodec._toolStepFromMap so
unrecognized non-null status strings do not make deserialization throw. Safely
resolve the mapped value against CoachToolStatus.values and fall back to
CoachToolStatus.success when lookup fails, while preserving the existing success
fallback for null values.

---

Nitpick comments:
In `@CODEMAP.md`:
- Line 271: Update the codemap generator’s layer-map table emission in
generate_codemap.dart to wrap the exported-symbols column value in Markdown
backticks, preserving the listed Dart symbol names while preventing underscores
from being parsed as emphasis markers.

In `@lib/features/coach_chat/providers/coach_chat_provider.dart`:
- Around line 278-435: The oversized notifier should no longer own agent-loop
orchestration. Extract _streamAgenticAnswer and _buildToolFollowUpPrompt,
including streaming, tool-step coordination, retry limits, and prompt
construction, into a focused collaborator or use case; keep
_appendAssistantContent, _setAssistantContent, and _setAssistantToolSteps in the
notifier for Riverpod state updates, and wire the collaborator to report
stream/content/tool-step updates through a suitable callback interface.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b3f7a756-b7dc-4923-9ff2-b810cb1a9a54

📥 Commits

Reviewing files that changed from the base of the PR and between 8b4f342 and d0707cc.

📒 Files selected for processing (25)
  • CODEMAP.md
  • lib/domain/entities/chat_message.dart
  • lib/domain/entities/coach/coach_tool_call.dart
  • lib/domain/entities/coach/coach_tool_definition.dart
  • lib/domain/usecases/coach/coach_tool_context_queries.dart
  • lib/domain/usecases/coach/coach_tool_health_queries.dart
  • lib/domain/usecases/coach/execute_coach_tool_usecase.dart
  • lib/domain/utils/coach_tool_call_parser.dart
  • lib/domain/utils/coach_tool_result_helpers.dart
  • lib/features/coach_chat/presentation/widgets/agent_tool_step_list.dart
  • lib/features/coach_chat/presentation/widgets/assistant_bubble.dart
  • lib/features/coach_chat/presentation/widgets/message_list.dart
  • lib/features/coach_chat/providers/coach_chat_provider.dart
  • lib/features/coach_chat/providers/coach_chat_provider.g.dart
  • lib/features/dashboard/presentation/pages/dashboard_page.dart
  • lib/features/dashboard/presentation/widgets/coach_agent_hero_card.dart
  • lib/infrastructure/ai/gemma/gemma_inference_session.dart
  • lib/infrastructure/ai/hybrid_ai_coach_repository.dart
  • lib/infrastructure/coach/coach_conversation_codec.dart
  • lib/shared/providers/coach_usecase_providers.dart
  • test/domain/usecases/coach/execute_coach_tool_usecase_test.dart
  • test/domain/utils/coach_tool_call_parser_test.dart
  • test/features/coach_chat/agent_tool_step_list_test.dart
  • test/features/coach_chat/coach_chat_agentic_tool_test.dart
  • test/features/dashboard/dashboard_page_test.dart

Comment thread lib/domain/entities/coach/coach_tool_call.dart Outdated
Comment thread lib/domain/usecases/coach/coach_tool_context_queries.dart
Comment thread lib/domain/usecases/coach/coach_tool_context_queries.dart
Comment thread lib/domain/usecases/coach/coach_tool_health_queries.dart
Comment thread lib/domain/utils/coach_tool_call_parser.dart
Comment thread lib/features/coach_chat/providers/coach_chat_provider.dart
Comment thread lib/features/coach_chat/providers/coach_chat_provider.dart
Comment thread lib/infrastructure/coach/coach_conversation_codec.dart
Convert coach tool models to @freezed with Map<String, Object?>, gate
training-load fields by data-source permissions, tighten pace-plan and
health-metric validation, reject embedded TOOL_CALL parsing, accumulate
multi-step tool results with cancellation checks, and harden codec enum
deserialization.

Co-authored-by: Youri Bontekoe <YKDBontekoe@users.noreply.github.com>
@YKDBontekoe
YKDBontekoe merged commit cce79a5 into main Jul 13, 2026
13 checks passed
@YKDBontekoe
YKDBontekoe deleted the cursor/agentic-coach-integration-4a43 branch July 13, 2026 09:48
kynos-release-bot Bot pushed a commit that referenced this pull request Jul 13, 2026
# [1.18.0](v1.17.0...v1.18.0) (2026-07-13)

### Features

* **coach:** make the AI coach agentic and the app's home-screen core ([#96](#96)) ([cce79a5](cce79a5))
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