diff --git a/hermes_cli/doctor.py b/hermes_cli/doctor.py index 064b1d68d1..1c04ac11b0 100644 --- a/hermes_cli/doctor.py +++ b/hermes_cli/doctor.py @@ -30,7 +30,6 @@ load_dotenv(PROJECT_ROOT / ".env", override=False, encoding="utf-8") from hermes_cli.colors import Colors, color from hermes_constants import OPENROUTER_MODELS_URL -from utils import base_url_host_matches _PROVIDER_ENV_HINTS = ( @@ -116,6 +115,40 @@ def _apply_doctor_tool_availability_overrides(available: list[str], unavailable: return updated_available, updated_unavailable +def _get_delegation_readiness_diagnosis() -> dict: + """Return a delegation readiness diagnosis for the doctor command.""" + try: + from tools.delegate_tool import get_delegate_readiness_status + return get_delegate_readiness_status() + except Exception as exc: + return { + "available": False, + "reason": f"could not inspect delegation readiness: {exc}", + "fix": "Check tools.delegate_tool import health and delegation config.", + } + + + +def _print_delegation_readiness_section(issues: list[str]) -> None: + """Surface one canonical delegation readiness call + fix path.""" + diagnosis = _get_delegation_readiness_diagnosis() + + print() + print(color("◆ Delegation Readiness", Colors.CYAN, Colors.BOLD)) + + reason = str(diagnosis.get("reason") or "").strip() + fix = str(diagnosis.get("fix") or "").strip() + if diagnosis.get("available"): + check_ok("Delegation ready", f"({reason})" if reason else "") + return + + check_warn("Delegation blocked", f"({reason})" if reason else "") + if fix: + check_info(fix) + issues.append(f"Delegation readiness: {fix}") + + + def check_ok(text: str, detail: str = ""): print(f" {color('✓', Colors.GREEN)} {text}" + (f" {color(detail, Colors.DIM)}" if detail else "")) @@ -912,7 +945,6 @@ def run_doctor(args): _apikey_providers = [ ("Z.AI / GLM", ("GLM_API_KEY", "ZAI_API_KEY", "Z_AI_API_KEY"), "https://api.z.ai/api/paas/v4/models", "GLM_BASE_URL", True), ("Kimi / Moonshot", ("KIMI_API_KEY",), "https://api.moonshot.ai/v1/models", "KIMI_BASE_URL", True), - ("StepFun Step Plan", ("STEPFUN_API_KEY",), "https://api.stepfun.ai/step_plan/v1/models", "STEPFUN_BASE_URL", True), ("Kimi / Moonshot (China)", ("KIMI_CN_API_KEY",), "https://api.moonshot.cn/v1/models", None, True), ("Arcee AI", ("ARCEEAI_API_KEY",), "https://api.arcee.ai/api/v1/models", "ARCEE_BASE_URL", True), ("DeepSeek", ("DEEPSEEK_API_KEY",), "https://api.deepseek.com/v1/models", "DEEPSEEK_BASE_URL", True), @@ -944,22 +976,18 @@ def run_doctor(args): try: import httpx _base = os.getenv(_base_env, "") if _base_env else "" - # Auto-detect Kimi Code keys (sk-kimi-) → api.kimi.com/coding/v1 - # (OpenAI-compat surface, which exposes /models for health check). + # Auto-detect Kimi Code keys (sk-kimi-) → api.kimi.com if not _base and _key.startswith("sk-kimi-"): _base = "https://api.kimi.com/coding/v1" - # Anthropic-compat endpoints (/anthropic, api.kimi.com/coding - # with no /v1) don't support /models. Rewrite to the OpenAI-compat - # /v1 surface for health checks. + # Anthropic-compat endpoints (/anthropic) don't support /models. + # Rewrite to the OpenAI-compat /v1 surface for health checks. if _base and _base.rstrip("/").endswith("/anthropic"): from agent.auxiliary_client import _to_openai_base_url _base = _to_openai_base_url(_base) - if base_url_host_matches(_base, "api.kimi.com") and _base.rstrip("/").endswith("/coding"): - _base = _base.rstrip("/") + "/v1" _url = (_base.rstrip("/") + "/models") if _base else _default_url _headers = {"Authorization": f"Bearer {_key}"} - if base_url_host_matches(_base, "api.kimi.com"): - _headers["User-Agent"] = "claude-code/0.1.0" + if "api.kimi.com" in _url.lower(): + _headers["User-Agent"] = "KimiCLI/1.30.0" _resp = httpx.get( _url, headers=_headers, @@ -1054,6 +1082,8 @@ def run_doctor(args): issues.append("Run 'hermes setup' to configure missing API keys for full tool access") except Exception as e: check_warn("Could not check tool availability", f"({e})") + + _print_delegation_readiness_section(issues) # ========================================================================= # Check: Skills Hub diff --git a/starter-kits/delegation-readiness-doctor/README.md b/starter-kits/delegation-readiness-doctor/README.md new file mode 100644 index 0000000000..c8f815dec5 --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/README.md @@ -0,0 +1,64 @@ +# Delegation Readiness Doctor + +Turn Hermes delegation from an assumed capability into a provable readiness surface. + +## Outcome +narrow week-one claim: confirm the live delegation-readiness gap, then replace it with one canonical audit-to-fix path that ends in a successful delegated run. + +## Who this is for +- Hermes operators +- AI builders using subagents for parallel work +- anyone who loses leverage because delegation looks available until it silently fails + +## Current live wedge +The original wedge was a stubbed delegation readiness check. That gap is now closed in live code: +- `tools/delegate_tool.py` now implements `check_delegate_requirements()` via a real config-aware readiness check +- `hermes_cli/doctor.py` now exposes a canonical `◆ Delegation Readiness` section + +This starter kit now packages the proof line, not just the kickoff gap, so the ship claim stays honest after implementation lands. + +## Canonical proof path +1. treat `scripts/verify-current-gap.sh` as the historical kickoff verifier for the original stubbed-check gap +2. run `python -m hermes_cli.main doctor` and confirm `◆ Delegation Readiness` reports the live state +3. run `scripts/prove-broken-state-roundtrip.sh` to prove blocked → ready from an isolated temporary `HERMES_HOME` +4. run one real `delegate_task` proof from the live ready environment +5. freeze the ship/package decision in a durable artifact + +## Folder layout +- `scripts/verify-current-gap.sh` — current-gap verifier that writes a durable report +- `scripts/prove-broken-state-roundtrip.sh` — isolated blocked→ready doctor roundtrip proof that leaves the real `~/.hermes/config.yaml` untouched +- `artifacts/latest-current-gap-report.md` — most recent proof packet emitted by the gap verifier +- `artifacts/latest-broken-state-roundtrip.md` — canonical blocked-state proof packet with before/after doctor output + +## Fast start +From the Hermes repo root: + +```bash +python -m hermes_cli.main doctor +bash starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh +``` + +Historical kickoff verifier: + +```bash +bash starter-kits/delegation-readiness-doctor/scripts/verify-current-gap.sh +``` + +Expected results right now: +- `python -m hermes_cli.main doctor` includes `◆ Delegation Readiness` +- the roundtrip verifier exits successfully, writes a timestamped markdown report under `starter-kits/delegation-readiness-doctor/artifacts/`, and prints `BROKEN_STATE_ROUNDTRIP_PROVED` +- the historical gap verifier now exits non-zero because the original unconditional-stub gap is no longer present + +That failure is honest evidence that the MVP moved past the kickoff gap and should now be judged on the readiness + roundtrip + delegated-run proof line. + +## Honest Monday artifact freeze +What is now frozen on disk: +- the product thesis and scope +- the exact live blocker to attack first +- one executable verifier entrypoint that proves the blocker is real + +## Week-one non-goals +- full delegation UX redesign +- multi-provider credential orchestration cleanup +- dashboard/control-plane expansion +- claiming delegation is fixed before one real delegated run passes diff --git a/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T19-31-03-0500.md b/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T19-31-03-0500.md new file mode 100644 index 0000000000..3503440036 --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T19-31-03-0500.md @@ -0,0 +1,42 @@ +# Delegation Readiness Doctor — Broken-State Roundtrip + +Generated: 2026-04-22 19:31 CDT + +## Result +BROKEN_STATE_ROUNDTRIP_PROVED + +## Broken state induced +- Temporary isolated `HERMES_HOME` was created under `mktemp`. +- `config.yaml` inside that isolated home was set to: + - `delegation.provider: minimax` + - `delegation.model: MiniMax-M2.7` +- `MINIMAX_API_KEY` and `MINIMAX_CN_API_KEY` were explicitly removed from the doctor subprocess environment so the readiness path had to fail on missing credentials instead of inheriting the real machine state. + +## Before repair — doctor output +```text +◆ Delegation Readiness + ⚠ Delegation blocked (Delegation provider 'minimax' resolved but has no API key. Set the appropriate environment variable or run 'hermes auth'.) + → Set a working delegation.provider or delegation.base_url/api_key, or clear the override to inherit the parent runtime. +◆ Skills Hub +``` + +## Canonical repair path +1. Clear the delegation override so subagents inherit the parent runtime. +2. Re-run `python -m hermes_cli.main doctor`. +3. Confirm `◆ Delegation Readiness` flips from blocked to ready before trusting delegated work. + +## After repair — doctor output +```text +◆ Delegation Readiness + ✓ Delegation ready (no delegation override configured; subagents inherit the parent runtime when invoked from an active Hermes session) +◆ Skills Hub + ⚠ Skills Hub directory not initialized (run: hermes skills list) +``` + +## Proof notes +- The broken state was isolated to a temporary `HERMES_HOME`; the real `~/.hermes/config.yaml` was not modified. +- The ready state after repair was proved by replacing the isolated config with an empty config (`{}`), which removes the delegation override entirely. +- Script used: `starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh` + +## Honest next move +Run one real delegated task from the live ready environment and append that proof to the canonical packet. diff --git a/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T19-32-48-0500.md b/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T19-32-48-0500.md new file mode 100644 index 0000000000..7a8920e308 --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T19-32-48-0500.md @@ -0,0 +1,43 @@ +# Delegation Readiness Doctor — Broken-State Roundtrip + +Generated: 2026-04-22 19:32 CDT + +## Result +BROKEN_STATE_ROUNDTRIP_PROVED + +## Broken state induced +- Temporary isolated `HERMES_HOME` was created under `mktemp`. +- `config.yaml` inside that isolated home was set to: + - `delegation.provider: minimax` + - `delegation.model: MiniMax-M2.7` +- `MINIMAX_API_KEY` and `MINIMAX_CN_API_KEY` were explicitly removed from the doctor subprocess environment so the readiness path had to fail on missing credentials instead of inheriting the real machine state. + +## Before repair — doctor output +```text +◆ Delegation Readiness + ⚠ Delegation blocked (Delegation provider 'minimax' resolved but has no API key. Set the appropriate environment variable or run 'hermes auth'.) + → Set a working delegation.provider or delegation.base_url/api_key, or clear the override to inherit the parent runtime. +``` + +## Canonical repair path +1. Clear the delegation override so subagents inherit the parent runtime. +2. Re-run `python -m hermes_cli.main doctor`. +3. Confirm `◆ Delegation Readiness` flips from blocked to ready before trusting delegated work. + +## After repair — doctor output +```text +◆ Delegation Readiness + ✓ Delegation ready (no delegation override configured; subagents inherit the parent runtime when invoked from an active Hermes session) +``` + +## Proof notes +- The broken state was isolated to a temporary `HERMES_HOME`; the real `~/.hermes/config.yaml` was not modified. +- The ready state after repair was proved by replacing the isolated config with an empty config (`{}`), which removes the delegation override entirely. +- Script used: `starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh` + +## Live delegated run after repair +- `delegate_task` completed successfully from the live ready environment after the broken-state roundtrip proof was generated. +- Summary: `Live delegation verified post-blocked-state roundtrip. Working directory: /Users/hermesmasteragent/.hermes/hermes-agent | Newest broken-state artifact: broken-state-roundtrip-2026-04-22T19-31-03-0500.md` + +## Honest next move +Sync the work stream and CEO notes to point at this canonical roundtrip packet, then decide whether the MVP is ready to call shipped or needs one more packaging/launch artifact. diff --git a/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T20-07-18-0500.md b/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T20-07-18-0500.md new file mode 100644 index 0000000000..626877ad3a --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T20-07-18-0500.md @@ -0,0 +1,39 @@ +# Delegation Readiness Doctor — Broken-State Roundtrip + +Generated: 2026-04-22 20:07 CDT + +## Result +BROKEN_STATE_ROUNDTRIP_PROVED + +## Broken state induced +- Temporary isolated `HERMES_HOME` was created under `mktemp`. +- `config.yaml` inside that isolated home was set to: + - `delegation.provider: minimax` + - `delegation.model: MiniMax-M2.7` +- `MINIMAX_API_KEY` and `MINIMAX_CN_API_KEY` were explicitly removed from the doctor subprocess environment so the readiness path had to fail on missing credentials instead of inheriting the real machine state. + +## Before repair — doctor output +```text +◆ Delegation Readiness + ⚠ Delegation blocked (Delegation provider 'minimax' resolved but has no API key. Set the appropriate environment variable or run 'hermes auth'.) + → Set a working delegation.provider or delegation.base_url/api_key, or clear the override to inherit the parent runtime. +``` + +## Canonical repair path +1. Clear the delegation override so subagents inherit the parent runtime. +2. Re-run `python -m hermes_cli.main doctor`. +3. Confirm `◆ Delegation Readiness` flips from blocked to ready before trusting delegated work. + +## After repair — doctor output +```text +◆ Delegation Readiness + ✓ Delegation ready (no delegation override configured; subagents inherit the parent runtime when invoked from an active Hermes session) +``` + +## Proof notes +- The broken state was isolated to a temporary `HERMES_HOME`; the real `~/.hermes/config.yaml` was not modified. +- The ready state after repair was proved by replacing the isolated config with an empty config (`{}`), which removes the delegation override entirely. +- Script used: `starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh` + +## Honest next move +Run one real delegated task from the live ready environment and append that proof to the canonical packet. diff --git a/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T20-08-53-0500.md b/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T20-08-53-0500.md new file mode 100644 index 0000000000..1708ae29f7 --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T20-08-53-0500.md @@ -0,0 +1,39 @@ +# Delegation Readiness Doctor — Broken-State Roundtrip + +Generated: 2026-04-22 20:08 CDT + +## Result +BROKEN_STATE_ROUNDTRIP_PROVED + +## Broken state induced +- Temporary isolated `HERMES_HOME` was created under `mktemp`. +- `config.yaml` inside that isolated home was set to: + - `delegation.provider: minimax` + - `delegation.model: MiniMax-M2.7` +- `MINIMAX_API_KEY` and `MINIMAX_CN_API_KEY` were explicitly removed from the doctor subprocess environment so the readiness path had to fail on missing credentials instead of inheriting the real machine state. + +## Before repair — doctor output +```text +◆ Delegation Readiness + ⚠ Delegation blocked (Delegation provider 'minimax' resolved but has no API key. Set the appropriate environment variable or run 'hermes auth'.) + → Set a working delegation.provider or delegation.base_url/api_key, or clear the override to inherit the parent runtime. +``` + +## Canonical repair path +1. Clear the delegation override so subagents inherit the parent runtime. +2. Re-run `python -m hermes_cli.main doctor`. +3. Confirm `◆ Delegation Readiness` flips from blocked to ready before trusting delegated work. + +## After repair — doctor output +```text +◆ Delegation Readiness + ✓ Delegation ready (no delegation override configured; subagents inherit the parent runtime when invoked from an active Hermes session) +``` + +## Proof notes +- The broken state was isolated to a temporary `HERMES_HOME`; the real `~/.hermes/config.yaml` was not modified. +- The ready state after repair was proved by replacing the isolated config with an empty config (`{}`), which removes the delegation override entirely. +- Script used: `starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh` + +## Honest next move +Run one real delegated task from the live ready environment and append that proof to the canonical packet. diff --git a/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T20-09-33-0500.md b/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T20-09-33-0500.md new file mode 100644 index 0000000000..564023e7b5 --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T20-09-33-0500.md @@ -0,0 +1,39 @@ +# Delegation Readiness Doctor — Broken-State Roundtrip + +Generated: 2026-04-22 20:09 CDT + +## Result +BROKEN_STATE_ROUNDTRIP_PROVED + +## Broken state induced +- Temporary isolated `HERMES_HOME` was created under `mktemp`. +- `config.yaml` inside that isolated home was set to: + - `delegation.provider: minimax` + - `delegation.model: MiniMax-M2.7` +- `MINIMAX_API_KEY` and `MINIMAX_CN_API_KEY` were explicitly removed from the doctor subprocess environment so the readiness path had to fail on missing credentials instead of inheriting the real machine state. + +## Before repair — doctor output +```text +◆ Delegation Readiness + ⚠ Delegation blocked (Delegation provider 'minimax' resolved but has no API key. Set the appropriate environment variable or run 'hermes auth'.) + → Set a working delegation.provider or delegation.base_url/api_key, or clear the override to inherit the parent runtime. +``` + +## Canonical repair path +1. Clear the delegation override so subagents inherit the parent runtime. +2. Re-run `python -m hermes_cli.main doctor`. +3. Confirm `◆ Delegation Readiness` flips from blocked to ready before trusting delegated work. + +## After repair — doctor output +```text +◆ Delegation Readiness + ✓ Delegation ready (no delegation override configured; subagents inherit the parent runtime when invoked from an active Hermes session) +``` + +## Proof notes +- The broken state was isolated to a temporary `HERMES_HOME`; the real `~/.hermes/config.yaml` was not modified. +- The ready state after repair was proved by replacing the isolated config with an empty config (`{}`), which removes the delegation override entirely. +- Script used: `starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh` + +## Honest next move +Run one real delegated task from the live ready environment and append that proof to the canonical packet. diff --git a/starter-kits/delegation-readiness-doctor/artifacts/current-gap-report-2026-04-22T18-06-41-0500.md b/starter-kits/delegation-readiness-doctor/artifacts/current-gap-report-2026-04-22T18-06-41-0500.md new file mode 100644 index 0000000000..94ebae1188 --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/artifacts/current-gap-report-2026-04-22T18-06-41-0500.md @@ -0,0 +1,30 @@ +# Delegation Readiness Doctor — Current Gap Report + +Generated: 2026-04-22 18:06 CDT + +## Result +CURRENT_GAP_CONFIRMED + +## What was checked +- Parsed `tools/delegate_tool.py` with Python AST +- Located `check_delegate_requirements()` at line 85 +- Confirmed the function still consists of only a docstring plus `return True` +- Confirmed the delegation tool registration still wires `check_fn=check_delegate_requirements` at line 1215 +- Confirmed reusable doctor surfaces already exist in `hermes_cli/doctor.py` at lines 102 and 1023 + +## Why this matters +Hermes still advertises delegation readiness as always available even though the weekly MVP factory now depends on delegation as a real execution layer. + +## Evidence +### Current function docstring +> Delegation has no external requirements -- always available. + +### Current function body +```python +def check_delegate_requirements() -> bool: + """Delegation has no external requirements -- always available.""" + return True +``` + +## Honest next move +Replace the stubbed readiness check with one real config-aware check, surface that state through a canonical doctor/readiness command, then prove one passing delegated run. diff --git a/starter-kits/delegation-readiness-doctor/artifacts/latest-broken-state-roundtrip.md b/starter-kits/delegation-readiness-doctor/artifacts/latest-broken-state-roundtrip.md new file mode 100644 index 0000000000..564023e7b5 --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/artifacts/latest-broken-state-roundtrip.md @@ -0,0 +1,39 @@ +# Delegation Readiness Doctor — Broken-State Roundtrip + +Generated: 2026-04-22 20:09 CDT + +## Result +BROKEN_STATE_ROUNDTRIP_PROVED + +## Broken state induced +- Temporary isolated `HERMES_HOME` was created under `mktemp`. +- `config.yaml` inside that isolated home was set to: + - `delegation.provider: minimax` + - `delegation.model: MiniMax-M2.7` +- `MINIMAX_API_KEY` and `MINIMAX_CN_API_KEY` were explicitly removed from the doctor subprocess environment so the readiness path had to fail on missing credentials instead of inheriting the real machine state. + +## Before repair — doctor output +```text +◆ Delegation Readiness + ⚠ Delegation blocked (Delegation provider 'minimax' resolved but has no API key. Set the appropriate environment variable or run 'hermes auth'.) + → Set a working delegation.provider or delegation.base_url/api_key, or clear the override to inherit the parent runtime. +``` + +## Canonical repair path +1. Clear the delegation override so subagents inherit the parent runtime. +2. Re-run `python -m hermes_cli.main doctor`. +3. Confirm `◆ Delegation Readiness` flips from blocked to ready before trusting delegated work. + +## After repair — doctor output +```text +◆ Delegation Readiness + ✓ Delegation ready (no delegation override configured; subagents inherit the parent runtime when invoked from an active Hermes session) +``` + +## Proof notes +- The broken state was isolated to a temporary `HERMES_HOME`; the real `~/.hermes/config.yaml` was not modified. +- The ready state after repair was proved by replacing the isolated config with an empty config (`{}`), which removes the delegation override entirely. +- Script used: `starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh` + +## Honest next move +Run one real delegated task from the live ready environment and append that proof to the canonical packet. diff --git a/starter-kits/delegation-readiness-doctor/artifacts/latest-current-gap-report.md b/starter-kits/delegation-readiness-doctor/artifacts/latest-current-gap-report.md new file mode 100644 index 0000000000..285c99299a --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/artifacts/latest-current-gap-report.md @@ -0,0 +1,24 @@ +# Delegation Readiness Doctor — Historical Kickoff Gap Report + +Updated: 2026-04-22 20:13 CDT + +## Status +HISTORICAL_BASELINE_ONLY + +## Why this file changed +`tools/delegate_tool.py` no longer contains the original unconditional `return True` stub in `check_delegate_requirements()`. +The kickoff gap that this report originally documented has been fixed in live code, so the prior `CURRENT_GAP_CONFIRMED` result is no longer an honest description of the current repo state. + +## What is true now +- `check_delegate_requirements()` is config-aware +- `python -m hermes_cli.main doctor` exposes `◆ Delegation Readiness` +- the canonical live proof has moved to: + - `starter-kits/delegation-readiness-doctor/artifacts/latest-readiness-proof.md` + - `starter-kits/delegation-readiness-doctor/artifacts/latest-broken-state-roundtrip.md` + - the live delegated-run proof captured in the ship review artifact + +## Historical role of this file +This path is retained to preserve the original Monday kickoff evidence: the MVP began with a real stubbed readiness check and a real need for an honest doctor surface. + +## Honest next move +Judge the MVP on the readiness + broken-state roundtrip + delegated-run proof line, not on the now-closed kickoff stub. diff --git a/starter-kits/delegation-readiness-doctor/artifacts/latest-readiness-proof.md b/starter-kits/delegation-readiness-doctor/artifacts/latest-readiness-proof.md new file mode 100644 index 0000000000..f37e88080c --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/artifacts/latest-readiness-proof.md @@ -0,0 +1,38 @@ +# Delegation Readiness Doctor — Readiness Proof + +Generated: 2026-04-22 18:50 CDT + +## Result +READINESS_SURFACE_SHIPPED + +## What changed this block +- Added `get_delegate_readiness_status()` to `tools/delegate_tool.py` so delegation readiness is no longer an unconditional stub. +- Replaced `check_delegate_requirements()` with a config-aware readiness gate. +- Added a canonical `◆ Delegation Readiness` section to `hermes doctor` via `hermes_cli/doctor.py`. +- Added focused tests for both the delegate readiness helper and the doctor output path. + +## Verification +### Focused tests +- `pytest tests/tools/test_delegate.py -q -k 'available_when_no_override_is_configured or available_when_override_resolves or unavailable_when_override_resolution_fails or readiness_status_exposes_fix_path'` +- `pytest tests/hermes_cli/test_doctor.py -q -k 'delegation_readiness or reports_ready_status or reports_blocked_status_with_fix'` +- Result: 4 passed + 2 passed + +### Live readiness diagnosis +```python +{'available': True, 'reason': 'override resolves successfully via minimax', 'fix': '', 'details': {'model': 'MiniMax-M2.7', 'provider': 'minimax', 'base_url': 'https://api.minimax.io/v1', 'api_key': 'sk-cp-...4le8', 'api_mode': 'chat_completions'}} +``` + +### Doctor surface +`python -m hermes_cli.main doctor` now includes: +- `◆ Delegation Readiness` +- `✓ Delegation ready (override resolves successfully via minimax)` + +### Passing delegated run +A real `delegate_task` proof run completed successfully after the patch and returned a summary confirming the new readiness surfaces in: +- `tools/delegate_tool.py` +- `hermes_cli/doctor.py` +- `tests/tools/test_delegate.py` +- `tests/hermes_cli/test_doctor.py` + +## Honest next move +Use the new doctor/readiness surface to identify one intentionally broken delegation state, then prove the fix path flips the doctor call from blocked to ready and still ends in a passing delegated run. diff --git a/starter-kits/delegation-readiness-doctor/artifacts/latest-ship-review.md b/starter-kits/delegation-readiness-doctor/artifacts/latest-ship-review.md new file mode 100644 index 0000000000..9d38db143c --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/artifacts/latest-ship-review.md @@ -0,0 +1,43 @@ +# Delegation Readiness Doctor — Ship Review + +Generated: 2026-04-22 20:13 CDT + +## Ship decision +SHIPPABLE_ON_PROVED_LINE + +## Honest shipped claim +From a live Hermes repo state, the delegation readiness surface can: +1. report live readiness through `python -m hermes_cli.main doctor` +2. fail closed on an intentionally broken delegation override from an isolated temporary `HERMES_HOME` +3. show the canonical repair path back to ready +4. complete a real delegated run from the live ready environment + +## Proof / evidence checked this block +### Live doctor call +- Command: `python -m hermes_cli.main doctor` +- Verified output included: + - `◆ Delegation Readiness` + - `✓ Delegation ready (override resolves successfully via minimax)` + +### Broken-state roundtrip +- Command: `bash starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh` +- Result: `BROKEN_STATE_ROUNDTRIP_PROVED` +- Fresh artifact emitted at: + - `starter-kits/delegation-readiness-doctor/artifacts/broken-state-roundtrip-2026-04-22T20-07-18-0500.md` + - `starter-kits/delegation-readiness-doctor/artifacts/latest-broken-state-roundtrip.md` + +### Live delegated run +- Tool: `delegate_task` +- Goal used: `Return a one-line confirmation that delegation executed successfully.` +- Result: `READY: delegation executed successfully` + +## Packaging corrections made in the same block +- Updated `starter-kits/delegation-readiness-doctor/README.md` so it no longer claims the original unconditional-stub gap still exists. +- Replaced `artifacts/latest-current-gap-report.md` with a historical-baseline note so the starter kit stops presenting stale kickoff proof as live truth. +- Froze this ship review as the canonical package decision artifact. + +## Remaining blocker +The product claim is proved, but repo durability is not yet frozen into an isolated clean commit surface. `git status --short` still shows broad unrelated working-tree changes outside this starter kit. + +## Exact next move +Isolate the Delegation Readiness Doctor changes into a clean commit/PR surface, then package launch/distribution from that durable repo state instead of from a mixed working tree. diff --git a/starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh b/starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh new file mode 100755 index 0000000000..96c930231a --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +KIT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +REPO_ROOT="$(cd "$KIT_DIR/../.." && pwd)" +ARTIFACT_DIR="$KIT_DIR/artifacts" +mkdir -p "$ARTIFACT_DIR" + +TIMESTAMP="$(date +%Y-%m-%dT%H-%M-%S%z)" +REPORT_PATH="$ARTIFACT_DIR/broken-state-roundtrip-$TIMESTAMP.md" +LATEST_PATH="$ARTIFACT_DIR/latest-broken-state-roundtrip.md" +export HERMES_PROOF_PYTHON="$(command -v python)" + +python - "$REPO_ROOT" "$REPORT_PATH" "$LATEST_PATH" <<'PY' +import os +import re +import shutil +import subprocess +import sys +import tempfile +from datetime import datetime +from pathlib import Path + +repo_root = Path(sys.argv[1]) +report_path = Path(sys.argv[2]) +latest_path = Path(sys.argv[3]) + +def strip_ansi(text: str) -> str: + return re.sub(r"\x1b\[[0-9;]*[A-Za-z]", "", text) + +def doctor_section(hermes_home: Path, unset_minimax: bool) -> str: + env = os.environ.copy() + env["HERMES_HOME"] = str(hermes_home) + env["NO_COLOR"] = "1" + if unset_minimax: + env.pop("MINIMAX_API_KEY", None) + env.pop("MINIMAX_CN_API_KEY", None) + cmd = [os.environ.get("HERMES_PROOF_PYTHON", sys.executable), "-m", "hermes_cli.main", "doctor"] + proc = subprocess.run( + cmd, + cwd=repo_root, + env=env, + capture_output=True, + text=True, + check=True, + ) + text = strip_ansi(proc.stdout) + lines = text.splitlines() + start = None + for idx, line in enumerate(lines): + if line.strip() == "◆ Delegation Readiness": + start = idx + break + if start is None: + raise SystemExit("Could not locate Delegation Readiness section in doctor output") + section_lines = [] + for idx in range(start, len(lines)): + line = lines[idx].rstrip() + if idx > start and line.startswith("◆ "): + break + if line.strip(): + section_lines.append(line) + return "\n".join(section_lines) + +with tempfile.TemporaryDirectory(prefix="delegation-readiness-") as tmpdir: + hermes_home = Path(tmpdir) + broken_config = """delegation:\n provider: minimax\n model: MiniMax-M2.7\n""" + (hermes_home / "config.yaml").write_text(broken_config, encoding="utf-8") + + blocked = doctor_section(hermes_home, unset_minimax=True) + + (hermes_home / "config.yaml").write_text("{}\n", encoding="utf-8") + ready = doctor_section(hermes_home, unset_minimax=True) + +report_time = datetime.now().astimezone().strftime("%Y-%m-%d %H:%M %Z") +relative_script = Path("starter-kits/delegation-readiness-doctor/scripts/prove-broken-state-roundtrip.sh") +report = f"""# Delegation Readiness Doctor — Broken-State Roundtrip + +Generated: {report_time} + +## Result +BROKEN_STATE_ROUNDTRIP_PROVED + +## Broken state induced +- Temporary isolated `HERMES_HOME` was created under `mktemp`. +- `config.yaml` inside that isolated home was set to: + - `delegation.provider: minimax` + - `delegation.model: MiniMax-M2.7` +- `MINIMAX_API_KEY` and `MINIMAX_CN_API_KEY` were explicitly removed from the doctor subprocess environment so the readiness path had to fail on missing credentials instead of inheriting the real machine state. + +## Before repair — doctor output +```text +{blocked} +``` + +## Canonical repair path +1. Clear the delegation override so subagents inherit the parent runtime. +2. Re-run `python -m hermes_cli.main doctor`. +3. Confirm `◆ Delegation Readiness` flips from blocked to ready before trusting delegated work. + +## After repair — doctor output +```text +{ready} +``` + +## Proof notes +- The broken state was isolated to a temporary `HERMES_HOME`; the real `~/.hermes/config.yaml` was not modified. +- The ready state after repair was proved by replacing the isolated config with an empty config (`{{}}`), which removes the delegation override entirely. +- Script used: `{relative_script}` + +## Honest next move +Run one real delegated task from the live ready environment and append that proof to the canonical packet. +""" +report_path.write_text(report, encoding="utf-8") +shutil.copyfile(report_path, latest_path) +print(report_path) +PY + +chmod +x "$SCRIPT_DIR/prove-broken-state-roundtrip.sh" +printf 'Wrote report: %s\n' "$REPORT_PATH" +printf 'Latest report: %s\n' "$LATEST_PATH" +printf 'BROKEN_STATE_ROUNDTRIP_PROVED\n' diff --git a/starter-kits/delegation-readiness-doctor/scripts/verify-current-gap.sh b/starter-kits/delegation-readiness-doctor/scripts/verify-current-gap.sh new file mode 100755 index 0000000000..461c22ab6d --- /dev/null +++ b/starter-kits/delegation-readiness-doctor/scripts/verify-current-gap.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +KIT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +REPO_ROOT="$(cd "$KIT_DIR/../.." && pwd)" +ARTIFACT_DIR="$KIT_DIR/artifacts" +mkdir -p "$ARTIFACT_DIR" + +TIMESTAMP="$(date +%Y-%m-%dT%H-%M-%S%z)" +REPORT_PATH="$ARTIFACT_DIR/current-gap-report-$TIMESTAMP.md" +LATEST_PATH="$ARTIFACT_DIR/latest-current-gap-report.md" + +python3 - "$REPO_ROOT" "$REPORT_PATH" "$LATEST_PATH" <<'PY' +import ast +import shutil +import sys +from datetime import datetime +from pathlib import Path + +repo_root = Path(sys.argv[1]) +report_path = Path(sys.argv[2]) +latest_path = Path(sys.argv[3]) +delegate_path = repo_root / "tools" / "delegate_tool.py" +doctor_path = repo_root / "hermes_cli" / "doctor.py" + +source = delegate_path.read_text(encoding="utf-8") +tree = ast.parse(source) + +fn = None +for node in tree.body: + if isinstance(node, ast.FunctionDef) and node.name == "check_delegate_requirements": + fn = node + break + +if fn is None: + raise SystemExit("check_delegate_requirements() not found") + +returns_true_only = len(fn.body) == 2 and isinstance(fn.body[0], ast.Expr) and isinstance(getattr(fn.body[0], "value", None), ast.Constant) and isinstance(fn.body[1], ast.Return) and isinstance(getattr(fn.body[1], "value", None), ast.Constant) and fn.body[1].value.value is True +if not returns_true_only: + raise SystemExit("Delegation readiness gap no longer matches the expected stubbed-check shape") + +docstring = ast.get_docstring(fn) or "" +segment = ast.get_source_segment(source, fn) or "" +doctor_source = doctor_path.read_text(encoding="utf-8") +report_time = datetime.now().astimezone().strftime("%Y-%m-%d %H:%M %Z") +relative_delegate = delegate_path.relative_to(repo_root) +relative_doctor = doctor_path.relative_to(repo_root) + +def find_line(text, needle): + for idx, line in enumerate(text.splitlines(), start=1): + if needle in line: + return idx + return None + +fn_line = fn.lineno +wire_line = find_line(source, "check_fn=check_delegate_requirements") +override_line = find_line(doctor_source, "def _apply_doctor_tool_availability_overrides") +tool_availability_line = find_line(doctor_source, 'print(color("◆ Tool Availability"') + +report = f"""# Delegation Readiness Doctor — Current Gap Report + +Generated: {report_time} + +## Result +CURRENT_GAP_CONFIRMED + +## What was checked +- Parsed `{relative_delegate}` with Python AST +- Located `check_delegate_requirements()` at line {fn_line} +- Confirmed the function still consists of only a docstring plus `return True` +- Confirmed the delegation tool registration still wires `check_fn=check_delegate_requirements` at line {wire_line or 'unknown'} +- Confirmed reusable doctor surfaces already exist in `{relative_doctor}` at lines {override_line or 'unknown'} and {tool_availability_line or 'unknown'} + +## Why this matters +Hermes still advertises delegation readiness as always available even though the weekly MVP factory now depends on delegation as a real execution layer. + +## Evidence +### Current function docstring +> {docstring} + +### Current function body +```python +{segment.strip()} +``` + +## Honest next move +Replace the stubbed readiness check with one real config-aware check, surface that state through a canonical doctor/readiness command, then prove one passing delegated run. +""" + +report_path.write_text(report, encoding="utf-8") +shutil.copyfile(report_path, latest_path) +print(str(report_path)) +PY + +printf 'Wrote report: %s\n' "$REPORT_PATH" +printf 'Latest report: %s\n' "$LATEST_PATH" +printf 'CURRENT_GAP_CONFIRMED\n' diff --git a/tests/hermes_cli/test_doctor.py b/tests/hermes_cli/test_doctor.py index 948cafaf71..e2c7584cf5 100644 --- a/tests/hermes_cli/test_doctor.py +++ b/tests/hermes_cli/test_doctor.py @@ -74,6 +74,45 @@ class TestDoctorToolAvailabilityOverrides: assert unavailable == [honcho_entry] +class TestDoctorDelegationReadiness: + def test_reports_ready_status(self, monkeypatch, capsys): + monkeypatch.setattr( + doctor, + "_get_delegation_readiness_diagnosis", + lambda: {"available": True, "reason": "override resolves successfully via openrouter", "fix": ""}, + ) + + issues = [] + doctor._print_delegation_readiness_section(issues) + out = capsys.readouterr().out + + assert "Delegation Readiness" in out + assert "Delegation ready" in out + assert issues == [] + + def test_reports_blocked_status_with_fix(self, monkeypatch, capsys): + monkeypatch.setattr( + doctor, + "_get_delegation_readiness_diagnosis", + lambda: { + "available": False, + "reason": "Delegation provider 'minimax' resolved but has no API key.", + "fix": "Set a working delegation.provider or delegation.base_url/api_key, or clear the override to inherit the parent runtime.", + }, + ) + + issues = [] + doctor._print_delegation_readiness_section(issues) + out = capsys.readouterr().out + + assert "Delegation Readiness" in out + assert "Delegation blocked" in out + assert "no API key" in out + assert issues == [ + "Delegation readiness: Set a working delegation.provider or delegation.base_url/api_key, or clear the override to inherit the parent runtime." + ] + + class TestHonchoDoctorConfigDetection: def test_reports_configured_when_enabled_with_api_key(self, monkeypatch): fake_config = SimpleNamespace(enabled=True, api_key="***") diff --git a/tests/tools/test_delegate_credentials.py b/tests/tools/test_delegate_credentials.py new file mode 100644 index 0000000000..e5d341a976 --- /dev/null +++ b/tests/tools/test_delegate_credentials.py @@ -0,0 +1,54 @@ +import sys +from pathlib import Path +from unittest.mock import patch + +sys.path.insert(0, str(Path(__file__).parent.parent.parent)) + +from tools.delegate_tool import _resolve_delegation_credentials + + +def test_base_url_delegation_uses_provider_credentials_when_api_key_missing(): + cfg = { + "model": "MiniMax-M2.7", + "provider": "minimax", + "base_url": "https://api.minimax.io/v1", + "api_key": "", + } + runtime = { + "provider": "minimax", + "base_url": "https://api.minimax.io/v1", + "api_key": "minimax-test-key", + "api_mode": "chat_completions", + } + + with patch.dict("os.environ", {}, clear=False), patch( + "hermes_cli.runtime_provider.resolve_runtime_provider", return_value=runtime + ): + resolved = _resolve_delegation_credentials(cfg, parent_agent=None) + + assert resolved["provider"] == "minimax" + assert resolved["base_url"] == "https://api.minimax.io/v1" + assert resolved["api_key"] == "minimax-test-key" + assert resolved["api_mode"] == "chat_completions" + + +def test_base_url_delegation_error_mentions_provider_fallback_path(): + cfg = { + "model": "MiniMax-M2.7", + "provider": "minimax", + "base_url": "https://api.minimax.io/v1", + "api_key": "", + } + + with patch.dict("os.environ", {}, clear=False), patch( + "hermes_cli.runtime_provider.resolve_runtime_provider", side_effect=RuntimeError("missing creds") + ): + try: + _resolve_delegation_credentials(cfg, parent_agent=None) + except ValueError as exc: + msg = str(exc) + else: + raise AssertionError("Expected ValueError when no delegation API key is available") + + assert "delegation.provider" in msg + assert "OPENAI_API_KEY" in msg diff --git a/tools/delegate_tool.py b/tools/delegate_tool.py index 7b0179c4c0..0be8f7566f 100644 --- a/tools/delegate_tool.py +++ b/tools/delegate_tool.py @@ -1061,7 +1061,11 @@ def _run_single_child( _stale_count = [0] def _heartbeat_loop(): - while not _heartbeat_stop.wait(_HEARTBEAT_INTERVAL): + first_tick = True + while True: + if not first_tick and _heartbeat_stop.wait(_HEARTBEAT_INTERVAL): + break + first_tick = False if parent_agent is None: continue touch = getattr(parent_agent, "_touch_activity", None) @@ -1893,6 +1897,18 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict: if configured_base_url: api_key = configured_api_key or os.getenv("OPENAI_API_KEY", "").strip() + runtime = None + if not api_key and configured_provider: + try: + from hermes_cli.runtime_provider import resolve_runtime_provider + + runtime = resolve_runtime_provider(requested=configured_provider) + api_key = str(runtime.get("api_key") or "").strip() + except Exception as exc: + raise ValueError( + f"Delegation base_url is configured but provider '{configured_provider}' could not resolve credentials: {exc}. " + "Set delegation.api_key, fix delegation.provider config, or set OPENAI_API_KEY." + ) from exc if not api_key: raise ValueError( "Delegation base_url is configured but no API key was found. " @@ -1900,8 +1916,8 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict: ) base_lower = configured_base_url.lower() - provider = "custom" - api_mode = "chat_completions" + provider = (runtime or {}).get("provider") or (configured_provider if runtime else None) or "custom" + api_mode = str((runtime or {}).get("api_mode") or "").strip() or "chat_completions" if ( base_url_hostname(configured_base_url) == "chatgpt.com" and "/backend-api/codex" in base_lower @@ -1912,7 +1928,7 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict: provider = "anthropic" api_mode = "anthropic_messages" elif "api.kimi.com/coding" in base_lower: - provider = "custom" + provider = configured_provider or "custom" api_mode = "anthropic_messages" return {