Skip to main content

Governed runtime + skills for a multi-domain personal health agent (recovery, running, sleep, stress, strength, nutrition).

Project description

Health Agent Infra

Health Agent Infra is a local governed runtime for personal-health agents. It implements an AgentSpec-style contract: an agent can inspect hai capabilities, operate only through validated CLI commands, and rely on deterministic Python for policy, state, validation, and commits. You talk to the agent, the agent invokes the local hai CLI, and the runtime defines + enforces what the agent is allowed to do.

It is a working single-user package — dogfooded daily by the maintainer — and a reference architecture for the code/skill split. v0.1.15.1 is published as the foreign-user-ready package after a Linux keyring hotfix; the recorded non-maintainer validation session is post-publish empirical evidence feeding v0.1.16. It is not a chatbot or a hosted coaching app; it is the boundary that lets an LLM work over health data without owning the policy engine, the database, or the final write path.

PyPI Tests Python License

What this is

A Claude Code agent is the intended operator. You ask it to check readiness, log a gym session, explain yesterday's recommendation, or record how the day went. The agent maps that request onto validated hai commands, reads a governed snapshot, posts one bounded proposal per domain, and lets the runtime commit the final plan atomically to local SQLite.

The core rule:

Skills never mutate actions; code never improvises coaching prose.

Python owns classification bands, R-rules, X-rules, schema validation, supersession, review linkage, and transactions. Markdown skills own rationale, uncertainty, clarification, and natural-language handoff back to the user.

The package stores state locally and has no telemetry path. Pull commands only call the configured data source, currently intervals.icu or Garmin Connect. If you drive the runtime with a hosted LLM agent, any context you send to that host is governed by that host's data policy; Health Agent Infra does not control the model provider.

For technical users who want the convenience of a conversational health agent without handing the model unchecked authority over personal health data.

What ships today

Surface Shipped shape
Domains 6: recovery, running, sleep, stress, strength, nutrition
Skills 14 packaged markdown skills, including intent-router and expert-explainer
CLI contract 60 annotated hai commands with mutation class, idempotency, JSON mode, exit codes, and agent-safety metadata
State 25 SQLite migrations, local-only by default
Synthesis 10 X-rule evaluators across two phases, committed in one transaction
Verification 2631 passing tests, 13-persona harness, 5-path trusted-first-value acceptance matrix

Why it is different

  • Natural-language front end, deterministic write path. The normal product loop is conversational, but every mutation routes through validated CLI commands and local transactions.
  • Local-first runtime. State lives in SQLite under your home directory. No Health Agent Infra account, no daemon, no hosted backend.
  • Governed, not generative. Python owns deterministic policy; skills only narrate and ask for clarification over already-constrained actions.
  • Agent-native by contract. hai capabilities --json exposes every subcommand's mutation class, idempotency, JSON behavior, exit codes, and agent-safe flag. The intent-router skill maps natural-language intent to that contract, so the CLI is the agent's tool surface rather than a list of commands the user must memorize.
  • Auditable by construction. Pulls, accepted state, proposals, X-rule firings, final recommendations, and review outcomes persist in typed tables. Inspect with hai today, hai explain --operator, hai doctor, and hai stats; these surfaces reconcile supersede chains and hide schema churn that raw SQL will not.

Current state. v0.1.15.1 (2026-05-03) is the current PyPI package: the v0.1.15 foreign-user-ready release plus a Linux keyring fall-through hotfix. The v0.1.15 base shipped gym set-id collision repair, CSV-fixture default-deny on hai pull and hai daily, hai intake gaps presence signals, hai target nutrition over the existing target table, partial-day nutrition suppression, and a merge-human-inputs skill update. Migration head is 25. The dogfooded single-user system works; the non-maintainer recorded session is still pending as empirical validation feeding v0.1.16. The release-by-release audit index is in AUDIT.md; the shortest current-truth map is reporting/docs/current_system_state.md.

What the loop looks like

User:  "Plan today, but I slept badly and my legs feel heavy."
Agent: Reads `hai capabilities`, logs the readiness note, runs `hai daily`.
Runtime: Pulls evidence, projects state, classifies six domains, applies R-rules.
Agent: Invokes domain skills and posts `DomainProposal` rows with `hai propose`.
Runtime: Applies X-rules, commits the plan atomically, schedules review.
User:  Reads `hai today`; asks "why did you soften the run?"
Agent: Runs `hai explain --operator` and answers from persisted rows.

How the daily loop completes

hai daily is the orchestrator the agent drives. It does not finish the full judgment loop in one call:

  1. pull fetches evidence from the configured source and writes a sync_run_log row for freshness telemetry.
  2. clean normalizes evidence into typed accepted-state rows. v0.1.9 makes this fail-closed: a DB projection failure exits non-OK rather than silently leaving downstream callers with stale state.
  3. snapshot builds the per-domain bundle, with classified_state and policy_result populated on every domain regardless of whether the caller passed an evidence bundle.
  4. gaps enumerates user-closeable intake gaps.
  5. proposal_gate reports awaiting_proposals, incomplete, or complete.

When the gate is not complete, the agent invokes the per-domain readiness skills, posts one DomainProposal per expected domain with hai propose --domain <d>, then re-runs hai daily. --domains <csv> narrows the expected set for partial-day runs. Direct hai synthesize enforces the same six-domain completeness gate by default — pass --domains '' to opt out (rare; matches pre-v0.1.9 permissive behavior).

The full contract is in reporting/docs/agent_integration.md.

Install

The commands below are the agent-operable surface. You can run them by hand, but the intended daily loop is natural language first: tell the agent what you want, let it inspect hai capabilities, and let it invoke the right validated command.

# First-install canonical (bypasses the brief PyPI CDN cache window):
pipx install --force \
  --pip-args="--no-cache-dir --index-url https://pypi.org/simple/" \
  'health-agent-infra==0.1.15.1'
# OR for a dev checkout: pip install -e .
hai init                                       # scaffolds state + config + skills
hai auth intervals-icu                         # preferred live source
hai capabilities --human                       # one-page overview of every command
hai doctor                                     # check setup; --deep probes the live API
hai daily                                      # orchestrates pull -> clean ->
                                               # snapshot -> gaps -> proposal gate;
                                               # the agent then posts proposals
hai today                                      # read today's plan in plain language

After roughly two minutes the bare pipx install health-agent-infra works; the --no-cache-dir form is for the immediately-post-publish window.

--source defaults to intervals_icu when credentials are configured, else csv for the committed fixture. Garmin Connect live scraping remains best-effort and rate-limited; use --source garmin_live only when you explicitly want it.

Run hai capabilities --human (v0.1.13+) for a one-page workflow-grouped overview of every hai command. The agent-facing JSON manifest is hai capabilities --json.

On macOS, credentials are stored in the OS keyring. The first hai pull may ask for access; choose Always Allow if you want scripted runs such as hai daily to continue without hanging on a prompt.

Full agent wiring notes live in reporting/docs/agent_integration.md.

Where your data lives

Everything the runtime stores stays on your machine. Three locations matter:

What Default path Override
State DB ~/.local/share/health_agent_infra/state.db $HAI_STATE_DB, --db-path
Intake / proposal JSONL ~/.health_agent/ $HAI_BASE_DIR, --base-dir
Config (thresholds.toml) macOS: ~/Library/Application Support/hai/; Linux: ~/.config/hai/ hai config init --path <p>

Run hai doctor to confirm resolved paths, schema version, source freshness, and skill installation status. It also warns when the applied migration set has gaps even if MAX(version) looks current.

Troubleshooting

The five most common gotchas a new user hits, in order:

  1. hai today says "no plan for "hai daily hasn't run yet today, or the proposal gate stopped at awaiting_proposals because the agent didn't post per-domain proposals. Run hai daily (let the agent drive it conversationally), or check hai stats --funnel to see where the pipeline stalled.

  2. hai pull returns HTTP 403 — two distinct root causes look identical at the IntervalsIcuError boundary. Run hai doctor --deep (v0.1.13+) — it classifies the failure into one of five outcome classes:

    • CAUSE_1_CLOUDFLARE_UA — Cloudflare bot-protection blocked the request at the edge (the credentials never reached intervals.icu). Patched in v0.1.12.1; if it re-fires, file an issue.
    • CAUSE_2_CREDS — intervals.icu rejected the credentials. Run hai auth intervals-icu to refresh.
    • NETWORK — DNS / TCP / TLS layer; verify connectivity.
    • OTHER — unclassified; consult reporting/docs/intervals_icu_403_triage.md.
  3. hai doctor says auth is OK but pulls fail — the default hai doctor checks credential presence in the keyring, not whether the live API accepts them. Always run hai doctor --deep before a demo or after rotating keys; the deep check makes one live API call.

  4. A USER_INPUT exit with no useful message — every USER_INPUT exit should print an actionable next-step (v0.1.13 W-AD audit pinned this surface). If you hit one without a hint, the message itself is a bug — file an issue.

  5. hai today looks confident but you have only a few days of data — the cold-start window is the first 14 days for running, strength, and stress; recovery and sleep continue to defer when signal is thin. Bands and trends carry real signal around day 90. The plan calibration table below is the durable reference.

Reading your plan

hai today is the non-agent-mediated user surface. It resolves supersede chains and renders the canonical plan for a date:

hai today                         # today, markdown on TTY / plain elsewhere
hai today --as-of 2026-04-23      # specific date
hai today --domain recovery       # narrow to one domain
hai today --format json           # machine-readable

For dense audit output, use hai explain --operator or hai explain. Both reconstruct the plan from persisted rows; they do not recompute the runtime state.

Recording your day

After the next day's run schedules review events, record how yesterday went:

hai review record --outcome-json <path>
hai review summary [--domain recovery]

Outcomes are append-only and re-link when a plan has been superseded. If you recorded an outcome against the morning plan but re-authored the day after lunch, hai review record routes the outcome to the canonical leaf's matching-domain recommendation.

followed_recommendation and self_reported_improvement must be strict booleans (true / false), not "yes", 1, or truthy strings.

Manual intake lives under:

hai intake gym|exercise|nutrition|stress|note|readiness ...

Nutrition is a daily total, not per-meal. Re-calling within the same day creates a supersede chain; log it once at the end of the day.

Six domains in v1

recovery - running - sleep - stress - strength - nutrition

Each domain ships schemas, classification bands, policy rules, and a readiness skill. Synthesis reconciles proposals through 10 X-rule evaluators across two phases. Nutrition is macros-only in v1; see reporting/docs/non_goals.md.

Calibration timeline

A fresh install can produce recommendations on day one, but several signals need history before they carry much meaning:

Window What works
Days 1-14 Cold-start mode for running, strength, and stress. Expect to review flags consciously.
Day 14 Cold-start window closes. HRV and RHR rolling baselines begin to stabilize.
Days 14-28 Recovery, sleep, and stress become more calibrated against trailing-7d trend.
Day 28 ACWR's chronic-load denominator is full. Strength volume_ratio stops mechanically reading as 4x.
Day 60+ Trend bands start carrying real signal.
Around day 90 Steady state; remaining uncertainty is structural rather than history-bounded.

Code-derived marker: COLD_START_THRESHOLD_DAYS = 14 in src/health_agent_infra/core/state/snapshot.py. Cold-start relaxation is asymmetric by design: running, strength, and stress can soften some coverage blocks; recovery, sleep, and nutrition do not. Nutrition keeps deferring on insufficient evidence rather than relaxing into a low-confidence guess.

Permanent caveats:

  • intervals.icu does not expose sleep efficiency, body battery, or Garmin all-day stress.
  • v1 nutrition is macros-only, so micronutrient coverage is unavailable at source.

What the system refuses to do

  • No medical claims or diagnosis-shaped language.
  • No autonomous training-plan or diet-plan generation.
  • No state mutation without the relevant validated CLI path and, for agent-proposed intent/target activation, explicit user commit.
  • No package telemetry or hosted state backend.
  • No skill-side arithmetic for bands, scores, R-rules, or X-rules.

Full scope boundaries are in reporting/docs/non_goals.md and SECURITY.md.

CLI surface

This is the contract an agent operates after translating user intent from natural language. Humans can use it directly for setup, debugging, and audit; the normal product loop is still conversational.

# Evidence + intake
hai pull [--source intervals_icu|garmin_live|csv] --date <d>
hai clean --evidence-json <p>
hai intake gym|exercise|nutrition|stress|note|readiness ...

# Agent flow
hai daily [--domains <csv>]
hai propose --domain <d> --proposal-json <p>
hai synthesize --as-of <d> --user-id <u>
hai synthesize --bundle-only

# State + audit
hai state init | migrate | read | snapshot | reproject [--cascade-synthesis]
hai capabilities [--json | --markdown]
hai explain --for-date <d> --user-id <u>
hai today | hai doctor | hai stats

# Review, memory, intent, targets
hai review schedule | record | summary
hai memory set | list | archive
hai intent training add-session | training list | sleep set-window | list | archive
hai target set | nutrition | list | archive

# Ops + research + evals
hai auth intervals-icu | garmin | status
hai config init | show | validate | diff
hai planned-session-types
hai research topics | search --topic <t>
hai eval run --domain <d> | --synthesis [--json]
hai setup-skills

The authoritative surface is hai capabilities --markdown or reporting/docs/agent_cli_contract.md.

Roadmap

Now / Next / Later lives in ROADMAP.md. The detailed release plan lives in reporting/plans/tactical_plan_v0_1_x.md; the strategic vision in reporting/plans/strategic_plan_v1.md. The earlier reporting/plans/historical/multi_release_roadmap.md is superseded as of 2026-04-27.

Where to read next

Citing this work

See CITATION.cff. If you are writing about the project's claims rather than the package itself, use HYPOTHESES.md as the canonical statement of the bets and their falsification criteria.

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

health_agent_infra-0.1.15.1.tar.gz (526.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

health_agent_infra-0.1.15.1-py3-none-any.whl (650.8 kB view details)

Uploaded Python 3

File details

Details for the file health_agent_infra-0.1.15.1.tar.gz.

File metadata

  • Download URL: health_agent_infra-0.1.15.1.tar.gz
  • Upload date:
  • Size: 526.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for health_agent_infra-0.1.15.1.tar.gz
Algorithm Hash digest
SHA256 1d09243339042980f029ea5403c6f65b19c7a2392f19c25d18d2c4818f57d3dd
MD5 2a59fc62e20130cc105e52a67508a421
BLAKE2b-256 5c8b0fe96b18834ca5ee1f95e973302f39847acfb69a3404e4c79f4283657474

See more details on using hashes here.

File details

Details for the file health_agent_infra-0.1.15.1-py3-none-any.whl.

File metadata

File hashes

Hashes for health_agent_infra-0.1.15.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ca8f9f664ad009482d919f350938afd189802a8894ce15720256d2906df0dfae
MD5 a4fba5f363fd2ca6516d8396288bc086
BLAKE2b-256 feca719e34a864c3afc2d759f977e5c78b106a2c1336c40a2f8a3d854be5e942

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page