# v0.2 Dogfood Findings

This file records redacted TP-016 read-only validation findings. It must never contain API keys, athlete IDs, exact private training-load values, raw activity comments, raw tool payloads, or personally identifying training data.

## Run log

| Run ID               | Date       | Runner                                       | Account                                                                                     | Prompt set                         | Notes                                                                                                                                                               |
| -------------------- | ---------- | -------------------------------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| solo-codex-001       | 2026-05-12 | Codex CLI via local `bin/icuvisor` MCP stdio | maintainer account, credentials loaded from untracked parent `.env` without printing values | `docs/dogfood/v0.2-prompts.md`     | 27 prompts executed in one ephemeral Codex run; temporary raw event/output files stored under `/tmp/icuvisor-tp016-*` for measurement and removed after extraction. |
| invited-deferral-001 | 2026-05-15 | Operator-approved TP-016 acceptance change   | invited-athlete validation deferred; no participant accounts accessed                       | documented protocol/template below | Solo dogfood plus invited-athlete protocol/template is accepted for this batch. Real 2–3 athlete validation remains maintainer follow-up action.                    |

## Solo dogfood prompt results

| Prompt | Tools called                                                                                | Status  | Scale / unit / provenance / Strava observations                    | Redacted result note                                                                                                                                 |
| ------ | ------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| CJ-01  | `get_activities`, `get_training_summary`, `get_fitness`, `get_wellness_data`                | Pass    | Metric units preserved; exact loads redacted                       | Recent-load and recovery guidance was possible without inventing missing recovery data.                                                              |
| CJ-02  | `get_fitness`, `get_events`, `get_training_plan`, `get_workout_library`                     | Pass    | No writes suggested                                                | Taper/block guidance was possible from trend, calendar, and library reads; active assignment was unavailable.                                        |
| CJ-03  | `get_wellness_data`                                                                         | Pass    | Missing subjective/sleep fields were not interpreted as zero       | Wellness rows lacked many sleep/HRV/RHR fields in the checked window; missing/stale device values surfaced as unavailable context.                   |
| CJ-04  | `get_activities`, `get_events`, `get_fitness`, `get_wellness_data`                          | Pass    | Coach target redacted; local-account-only limitation stated        | Coach triage was possible for the local account and did not imply external athlete access.                                                           |
| T-01   | `get_athlete_profile`                                                                       | Pass    | Metric units and timezone surfaced; private thresholds redacted    | Profile shape exposed unit/timezone/sport/threshold presence.                                                                                        |
| T-02   | `get_fitness`                                                                               | Pass    | Exact CTL/ATL/TSB values redacted                                  | Six-week trend was summarized correctly at a directional level.                                                                                      |
| T-03   | `get_training_summary`                                                                      | Pass    | No inferred zone data                                              | Four-week aggregate returned sport totals and upstream zone-order totals.                                                                            |
| T-04   | `get_best_efforts`                                                                          | Pass    | Metric pace convention preserved; missing buckets reported         | Efforts returned where present; absent sport/family buckets were not invented.                                                                       |
| T-05   | `get_power_curves`                                                                          | Pass    | Watts exposed; exact curve values redacted                         | Recent curve information was available; long-term comparison limits were stated.                                                                     |
| T-06   | `get_activities`                                                                            | Pass    | Metric distance/pace/speed fields preserved; Strava stubs detected | Recent list included direct-device rows and multiple structured `strava_tos` unavailable rows.                                                       |
| T-07   | `get_activity_details`                                                                      | Pass    | Metric units and source/device fields preserved                    | Redacted activity detail included sport, time, duration, distance, calories, source/device, and load.                                                |
| T-08   | `get_activity_intervals`                                                                    | Fail    | Tool returned short actionable fetch error                         | No intervals were invented after `could not fetch activity intervals; check activity_id and intervals.icu credentials`.                              |
| T-09   | `get_activity_streams`                                                                      | Fail    | Requested snake_case minimal keys, but fetch failed                | Stream metadata/samples could not be validated because details lookup failed.                                                                        |
| T-10   | `get_activity_splits`                                                                       | Fail    | Metric split unit requested, but fetch failed                      | Manual vs virtual split provenance could not be validated because details lookup failed.                                                             |
| T-11   | `get_activity_messages`                                                                     | Pass    | No private message text quoted                                     | Message response had no rows for the chosen activity; timezone handling was not strongly exercised.                                                  |
| T-12   | `get_extended_metrics`                                                                      | Fail    | Tool returned short actionable fetch error; no metrics computed    | Extended metrics were not fabricated after fetch failure.                                                                                            |
| T-13   | `get_events`                                                                                | Pass    | Date/load details redacted; `truncated=false` preserved            | Next-14-day calendar returned workout-event summaries.                                                                                               |
| T-14   | `get_event_by_id`                                                                           | Pass    | Exact event details redacted                                       | Detail result matched the list-level event shape; fallback unavailable path was not triggered.                                                       |
| T-15   | `get_training_plan`                                                                         | Pass    | Periodization parameters not exposed                               | Active assignment returned `no_active_training_plan`; calendar workouts remained available through events.                                           |
| T-16   | `get_workout_library`                                                                       | Pass    | Names/counts redacted                                              | Library folders/plans listed with counts and visibility; no full workout steps requested.                                                            |
| T-17   | `get_workouts_in_folder`                                                                    | Pass    | Exact loads redacted                                               | Folder workouts returned terse summaries; descriptions included more text than ideal for a terse response.                                           |
| T-18   | `get_custom_items`                                                                          | Pass    | Raw custom content omitted                                         | Custom-item index returned item types, names, visibility/index metadata, and counts.                                                                 |
| T-19   | `get_custom_item_by_id`                                                                     | Pass    | Personal labels/thresholds redacted                                | Full content shape was available; v0.4 schema-resource note surfaced.                                                                                |
| T-20   | `get_training_plan`, `get_events`, `get_workout_library`                                    | Pass    | Deferred athlete-level periodization parameters were not invented  | Closest read-only evidence was calendar/library structure, not athlete-level ramp/taper/cadence preferences.                                         |
| A-01   | `get_athlete_profile`, `get_activity_streams`, `get_activity_splits`                        | Pass    | Correctly refused exact normalized graded pace computation         | Required grade/altitude/distance/time stream evidence was unavailable.                                                                               |
| A-02   | `get_activities`, `get_activity_details`, `get_activity_intervals`, `get_activity_messages` | Pass    | Strava `unavailable.reason` and workaround preserved               | Sparse Strava-blocked values were not treated as real data.                                                                                          |
| A-03   | `get_wellness_data`                                                                         | Blocked | Sleep scales preserved conceptually; target row absent             | No row with both `sleepQuality` and `sleepScore` was found in the checked window, so dual-scale reporting could not be fully validated on solo data. |

## Solo dogfood tool response sizes

Sizes are measured from Codex JSONL MCP tool-call result objects after the run, in bytes of compact JSON. Token estimates use a conservative bytes/4 approximation for checking the PRD §7.2.D 30k-token soft ceiling.

| Tool                     | Calls | Max response bytes | Approx. tokens | Tool-call errors | Notes                                                                     |
| ------------------------ | ----: | -----------------: | -------------: | ---------------: | ------------------------------------------------------------------------- |
| `get_activities`         |     1 |              7,011 |         ~1,753 |                0 | Includes structured Strava-unavailable rows.                              |
| `get_activity_details`   |     2 |              1,557 |           ~390 |                0 | One direct activity and one Strava-related check.                         |
| `get_activity_intervals` |     3 |                148 |            ~37 |                3 | Fetch failures returned short public errors.                              |
| `get_activity_messages`  |     2 |                273 |            ~69 |                0 | Empty/no-message shape was small.                                         |
| `get_activity_splits`    |     2 |                146 |            ~37 |                2 | Fetch failures returned short public errors.                              |
| `get_activity_streams`   |     2 |                146 |            ~37 |                2 | Fetch failures returned short public errors.                              |
| `get_athlete_profile`    |     1 |              4,549 |         ~1,138 |                0 | Profile shape remained below ceiling.                                     |
| `get_best_efforts`       |     1 |              7,965 |         ~1,992 |                0 | Multiple sport/family buckets.                                            |
| `get_custom_item_by_id`  |     1 |              3,913 |           ~979 |                0 | Full content shape below ceiling.                                         |
| `get_custom_items`       |     1 |              8,747 |         ~2,187 |                0 | Index shape below ceiling.                                                |
| `get_event_by_id`        |     1 |              2,456 |           ~614 |                0 | Detail shape below ceiling.                                               |
| `get_events`             |     2 |             10,345 |         ~2,587 |                0 | Calendar summaries below ceiling.                                         |
| `get_extended_metrics`   |     2 |                146 |            ~37 |                2 | Fetch failures returned short public errors.                              |
| `get_fitness`            |     1 |              1,491 |           ~373 |                0 | Fitness trend below ceiling.                                              |
| `get_power_curves`       |     1 |              1,665 |           ~417 |                0 | Curve summary below ceiling.                                              |
| `get_training_plan`      |     1 |              1,327 |           ~332 |                0 | No-active-plan shape below ceiling.                                       |
| `get_training_summary`   |     1 |              3,689 |           ~923 |                0 | Summary below ceiling.                                                    |
| `get_wellness_data`      |     2 |             41,459 |        ~10,365 |                0 | Largest solo response; below 30k-token soft ceiling but worth monitoring. |
| `get_workout_library`    |     1 |              4,600 |         ~1,150 |                0 | Folder/library summary below ceiling.                                     |
| `get_workouts_in_folder` |     1 |             16,557 |         ~4,140 |                0 | Below ceiling; descriptions may be verbose for terse default.             |

Largest single measured response: `get_wellness_data` at 41,459 bytes (~10.4k tokens), below the 30k-token soft ceiling.

## Invited athlete dogfood

Status: operator-approved deferral for this batch. Solo dogfood plus the documented invited-athlete protocol/template is accepted as the TP-016 completion evidence. Real 2–3 invited-athlete validation remains a maintainer follow-up action and must be performed only with participant consent and redacted results. No outreach, forum posting, participant recruitment, external account access, raw athlete data collection, or participant-result fabrication was attempted by the worker.

### Participant protocol and inclusion targets

Target 2–3 invited athletes without naming people in the repository:

- Participant A: any willing athlete who can run the canonical prompt set end-to-end.
- Participant B: ideally an imperial/miles user so unit normalization can be checked outside the maintainer's metric account.
- Participant C: ideally an athlete with Polar, Oura, Whoop, Apple Health, or another non-Garmin wellness source so provenance/staleness and dual sleep-scale handling can be exercised.

Each participant runs icuvisor locally against their own intervals.icu account. The maintainer must not receive API keys, athlete IDs, raw exports, raw MCP/Codex/Claude transcripts, screenshots containing values, exact dates, exact training-load/threshold values, activity names, private comments/messages, or unredacted logs.

Consent/privacy note to send with the invite: "This dogfood asks you to run read-only prompts locally and send back only a redacted summary table. Your raw training data, API key, athlete ID, activity names, exact dates, exact thresholds/load values, private comments, and transcripts stay on your machine. The repository will contain only broad labels such as `[INVITED_ATHLETE_1]`, client used, broad unit system, broad device/provenance category, pass/fail/blocked per prompt, local response-size maxima if you choose to measure them, and short redacted observations about scale/unit/provenance/Strava behavior."

### Run instructions for invited participants

Participants should use either Claude Desktop or Codex with the same local `bin/icuvisor` build and the canonical prompt set in `docs/dogfood/v0.2-prompts.md`. The maintainer should include the exact release artifact or git revision to build/run in each invite so participant results are comparable.

- Claude Desktop path: use <https://icuvisor.app/connect/claude-desktop/> for manual config. Start a fresh Claude conversation after configuration or rebuild so cached MCP schemas do not contaminate results.
- Codex path: use `docs/clients/codex-local.md`. Require ephemeral per-command MCP config, `env_vars` pass-through rather than credential values in config, `approval_policy="never"` for non-interactive runs, and cleanup per the TP-006 recipe. Start a fresh Codex session after configuration or rebuild.
- Participants may mark a prompt `blocked` when their account lacks the required data, such as no Strava-blocked activity, no dual `sleepQuality` + `sleepScore` row, no suitable activity with streams/splits/intervals, or no active training plan.
- Do not ask participants to paste environment variables, API keys, config contents, raw tool payloads, event JSONL, transcripts, or screenshots into chats/issues.

### Redacted findings template

Participants should return only this template, with one row per prompt and optional tool-size summary if locally measurable:

| Participant           | Client                  | Broad units               | Broad device/provenance category                               | Prompt ID | Tools called               | Status (`pass`/`fail`/`blocked`) | Largest local response bytes (optional) | Redacted scale/unit/provenance/Strava observations |
| --------------------- | ----------------------- | ------------------------- | -------------------------------------------------------------- | --------- | -------------------------- | -------------------------------- | --------------------------------------: | -------------------------------------------------- |
| `[INVITED_ATHLETE_N]` | Claude Desktop or Codex | metric / imperial / mixed | Garmin / Polar / Oura / Whoop / Apple Health / mixed / unknown | `CJ-01`   | backticked tool names only | pass/fail/blocked                |                   optional local number | no raw values, IDs, dates, names, or comments      |

Forbidden in returned findings: raw tool payloads, exact dates, exact training-load/threshold values, activity names, private comments/messages, athlete IDs, API keys, unredacted logs, screenshots with values, environment variables, or MCP client config contents.

### Local measurement and cleanup

If participants use Codex and want response-byte measurements, they may redirect `--json` output to a local temporary JSONL file, compute compact JSON byte lengths for `item.completed` records with `item.type == "mcp_tool_call"`, copy only per-tool maximum byte counts into the redacted template, and delete the temporary JSONL and final-response files. They must not send JSONL or transcripts to the maintainer.

Claude Desktop users who cannot measure response bytes locally may leave the response-size column blank; prompt pass/fail and qualitative observations are still useful.

### Coverage, fallback, and triage boundaries

The invited-athlete runs should use the same 27-prompt set. Step 3 should specifically try to resolve Step 2 open questions: whether activity intervals/streams/splits/extended-metrics failures reproduce on other accounts or were data-selection issues; whether `get_workouts_in_folder` is too verbose for terse default; and whether an account with both `sleepQuality` and `sleepScore` confirms dual-scale reporting. Concrete failures go into the findings and triage queue only; GitHub issue classification, launch-blocking decisions, and any code fixes are deferred to Step 4 or separate tested commits.

Aggregation result for this batch: no external participant rows exist because invited-athlete execution was explicitly deferred by the operator. The protocol, instructions, template, forbidden-data list, and fallback rules above are preserved so the maintainer can run real 2–3 athlete validation later. No outreach, posting, impersonation, external account access, raw athlete data collection, participant-run collection, or participant-result fabrication was attempted by the worker.

## Triage queue

Revised Step 4 triage for the operator-approved deferral uses the completed solo dogfood run. Invited-athlete-specific triage is deferred until real participant results exist.

- `get_activity_intervals`, `get_activity_streams`, `get_activity_splits`, and `get_extended_metrics` returned fetch errors for selected real activity IDs during the solo run. Follow-up issue: https://github.com/ricardocabral/icuvisor/issues/11. Invited-athlete evidence is still needed later to decide whether these are data-selection issues, upstream availability limitations, or tool bugs.
- `get_workouts_in_folder` stayed below the token ceiling but returned descriptions that may be more verbose than ideal for terse default. Follow-up issue: https://github.com/ricardocabral/icuvisor/issues/12.
- A solo wellness row with both `sleepQuality` and `sleepScore` was not found; the maintainer follow-up participant run should recruit or select an athlete/provider mix likely to exercise the dual-scale sleep case.
- Launch-blocking call for this revised batch: no additional v0.5 blocker beyond existing issues #11 and #12, because external participant validation is explicitly deferred rather than treated as failed evidence.
