microduck-cli 0.9.5__tar.gz → 0.9.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/CHANGELOG.md +15 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/PKG-INFO +2 -2
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/README.md +1 -1
- microduck_cli-0.9.6/docs/verification/2026-09-08-thor-orin-tutorial-run.md +106 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/doctor.py +18 -8
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/env/doctor.py +41 -9
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/pyproject.toml +1 -1
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_env_doctor.py +59 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/uv.lock +1 -1
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/agent-config/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/agent-config/scripts/show.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/ask-colleague/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/ask-colleague/prompts/review.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/ask-colleague/prompts/write.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/challenge/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/cicd/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/cicd/scripts/workflow.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/deviate/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/operate-microduck/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/recall/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/recall/scripts/recall.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/remember/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/remember/scripts/remember.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/run-tests/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/scope/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/sonarclaude/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/spec-to-plan/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/summarize-delivery/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/think/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/think/scripts/think.sh +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/validate-delivery/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/version-bump/SKILL.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/version-bump/scripts/bump.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills.local.yaml.example +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/current +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/current_plan +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/deliveries/jetson-retest-and-ai-lab-tutorial.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/deliveries/microduck-cli-env-teach-operate-rules.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/deliveries/orin-sanity.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/deliveries/readme-as-proof.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/deliveries/thor-sanity.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/frames/jetson-retest-and-ai-lab-tutorial.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/frames/microduck-cli-env-teach-operate-rules.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/frames/orin-sanity.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/frames/readme-as-proof.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/frames/thor-sanity.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/plans/jetson-retest-and-ai-lab-tutorial.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/plans/microduck-cli-env-teach-operate-rules.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/plans/orin-sanity.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/plans/readme-as-proof.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/plans/thor-sanity.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.eidetic/memory/microduck-cli__public.jsonl +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.flake8 +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.github/workflows/publish.yml +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.github/workflows/tests.yml +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.gitignore +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/.markdownlint-cli2.yaml +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/AGENTS.colleague.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/CLAUDE.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/LICENSE +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/culture.yaml +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/deliveries/2026-09-03-microduck-cli-env-teach-operate-rules.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/deliveries/2026-09-04-orin-sanity.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/deliveries/2026-09-04-readme-as-proof.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/deliveries/2026-09-07-jetson-retest-and-ai-lab-tutorial.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/operating-the-duck.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/plans/2026-09-03-microduck-cli-env-teach-operate-rules.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/plans/2026-09-04-orin-sanity.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/plans/2026-09-04-thor-sanity.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/plans/2026-09-07-jetson-retest-and-ai-lab-tutorial.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/skill-sources.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/specs/2026-09-03-microduck-cli-env-teach-operate-rules.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/specs/2026-09-04-orin-sanity.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/specs/2026-09-04-readme-as-proof.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/specs/2026-09-04-thor-sanity.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/specs/2026-09-07-jetson-retest-and-ai-lab-tutorial.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/tools/check_tutorial.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/upstream-pins.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/verification/2026-09-04-orin-sanity.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/verification/2026-09-04-sim-bringup.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/verification/2026-09-04-thor-sanity.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/docs/verification/2026-09-07-spark-retest.md +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/__main__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/compose.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/default_rules.toml +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/defaults.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/engine.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/human_gate.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/idle.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/intents.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/liveness.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/model.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/release.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/replay.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/rule_engine.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/rules.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/sense.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/senselog.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/sink.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/behavior/skills.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/cli.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/duck.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/env.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/explain.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/learn.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/overview.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/policy.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/rules.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_commands/whoami.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_errors.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/cli/_output.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/duck/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/duck/addressing.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/duck/gate.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/duck/record.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/env/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/env/hosts.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/env/params.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/env/stack.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/explain/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/explain/catalog.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/explain/duck.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/explain/env.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/explain/policy.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/explain/rules.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/ipc/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/ipc/client.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/ipc/proto.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/train/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/train/artifacts.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/microduck_cli/train/lane.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/sonar-project.properties +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/fake_robotd.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/fixtures/duck_ipc_proto.json +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/live/__init__.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/live/test_live_cli.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_addressing.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_check_tutorial.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_cli.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_cli_introspection.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_client.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_default_rules.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_duck.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_engine.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_env.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_fake_robotd.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_gate.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_hosts.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_human_gate.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_idle.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_intents.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_lane.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_learn_skill.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_links.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_liveness.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_lockstep.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_model.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_no_config_writes.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_no_hardware_paths.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_no_secrets_in_output.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_params.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_policy.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_proto.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_record.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_release.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_replay.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_rule_engine.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_rules.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_rules_cli.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_sense.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_senselog.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_sink.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_skills.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_stack.py +0 -0
- {microduck_cli-0.9.5 → microduck_cli-0.9.6}/tests/test_zero_deps.py +0 -0
|
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/). This project
|
|
6
6
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.9.6] - 2026-09-08
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `docs/verification/2026-09-08-thor-orin-tutorial-run.md` — the tutorial's operator run on Jetson AGX Thor (PyPI 0.9.4 wheel): the first smoke failed on a torch the clone had re-synced to `+cu129` after slipping back to the pin, the second passed in 26 s beside the production stack; the simulation's memory footprint on Thor (`duck-body` 933 MB, `robotd` 34 MB) and Orin (590 MB / 41 MB, +0.9 GB box-wide, headless); Orin's clone at `a30a9e4` resolves torch from the SBSA index with a plain `uv sync`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- `README.md`: the Spark re-test row's `[FAIL]`-under-healthy caveat now points at this fix.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- `env doctor` on a wheel install (`uv tool install microduck-cli`) reported both pin checks as "pinned commit unknown" because it read `docs/upstream-pins.md` from a path the wheel does not ship (defect d5 from the 2026-09-07 Spark re-test). The pinned commits are now carried inside the package as `env/doctor.py`'s `PACKAGED_PINS`; a checkout still reads the docs table first, a wheel falls back to the packaged copy, and `tests/test_env_doctor.py` asserts the two never drift.
|
|
21
|
+
- `env doctor` and `doctor` text output print `[WARN]` for a failing warning-severity check and reserve `[FAIL]` for errors, so a `healthy` verdict is never followed by a `[FAIL]` line (a Thor clone on the `microduck_rl#39` branch reads `[WARN] rl_pinned_commit`, as the tutorial says).
|
|
22
|
+
|
|
8
23
|
## [0.9.5] - 2026-09-07
|
|
9
24
|
|
|
10
25
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: microduck-cli
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.6
|
|
4
4
|
Summary: Agent-agnostic CLI for controlling the MicroDuck robot. Any agent, any human, one CLI. Sim-first: every verb is exercised against the real robotd daemon and the MuJoCo simulation.
|
|
5
5
|
Project-URL: Homepage, https://github.com/agentculture/microduck-cli
|
|
6
6
|
Project-URL: Issues, https://github.com/agentculture/microduck-cli/issues
|
|
@@ -150,7 +150,7 @@ before trusting a number here.
|
|
|
150
150
|
| Box | Reached | Result | Caveat |
|
|
151
151
|
|---|---|---|---|
|
|
152
152
|
| **DGX Spark** (GB10, aarch64) | all six checks + train smoke | pass; live suite **12 passed, 0 failed** | walking `xfail` |
|
|
153
|
-
| **DGX Spark**, re-test 2026-09-07 following the [Jetson AI Lab tutorial](docs/verification/2026-09-07-spark-retest.md) on the **PyPI 0.9.4 wheel**, viewer **windowed** | tutorial steps 1 and 5–11 executed on the wheel (steps 2–4, clone/build/venv, checked against pre-existing state, not re-run) + both live suites + smoke | pass; **12 passed, 1 xfailed** windowed | the wheel's `env doctor`
|
|
153
|
+
| **DGX Spark**, re-test 2026-09-07 following the [Jetson AI Lab tutorial](docs/verification/2026-09-07-spark-retest.md) on the **PyPI 0.9.4 wheel**, viewer **windowed** | tutorial steps 1 and 5–11 executed on the wheel (steps 2–4, clone/build/venv, checked against pre-existing state, not re-run) + both live suites + smoke | pass; **12 passed, 1 xfailed** windowed | the wheel's `env doctor` printed two `[FAIL]` pin lines under a `healthy` verdict (pins file not shipped — fixed in 0.9.6: the pins are packaged and warnings render `[WARN]`); the smoke needs the GPU free — it failed OOM beside a resident vLLM engine and passed with it paused |
|
|
154
154
|
| **Jetson AGX Thor** (JetPack 7) | all six checks, three tiers, headless | pass; **12 passed, 1 xfailed** | ran on an *uncommitted* local torch override; the upstream fix is still open as [microduck_rl#39](https://github.com/pollen-robotics/microduck_rl/pull/39) (issue [#38](https://github.com/pollen-robotics/microduck_rl/issues/38)), so `env doctor`'s `rl_pinned_commit` fails there **by design** until it merges and this repo re-pins |
|
|
155
155
|
| **Jetson AGX Orin** (L4T R39) | checks 1–4 | pass | the SBSA torch wheel carries no `sm_87` kernels — GPU training is **not available** on Orin at this pin |
|
|
156
156
|
|
|
@@ -133,7 +133,7 @@ before trusting a number here.
|
|
|
133
133
|
| Box | Reached | Result | Caveat |
|
|
134
134
|
|---|---|---|---|
|
|
135
135
|
| **DGX Spark** (GB10, aarch64) | all six checks + train smoke | pass; live suite **12 passed, 0 failed** | walking `xfail` |
|
|
136
|
-
| **DGX Spark**, re-test 2026-09-07 following the [Jetson AI Lab tutorial](docs/verification/2026-09-07-spark-retest.md) on the **PyPI 0.9.4 wheel**, viewer **windowed** | tutorial steps 1 and 5–11 executed on the wheel (steps 2–4, clone/build/venv, checked against pre-existing state, not re-run) + both live suites + smoke | pass; **12 passed, 1 xfailed** windowed | the wheel's `env doctor`
|
|
136
|
+
| **DGX Spark**, re-test 2026-09-07 following the [Jetson AI Lab tutorial](docs/verification/2026-09-07-spark-retest.md) on the **PyPI 0.9.4 wheel**, viewer **windowed** | tutorial steps 1 and 5–11 executed on the wheel (steps 2–4, clone/build/venv, checked against pre-existing state, not re-run) + both live suites + smoke | pass; **12 passed, 1 xfailed** windowed | the wheel's `env doctor` printed two `[FAIL]` pin lines under a `healthy` verdict (pins file not shipped — fixed in 0.9.6: the pins are packaged and warnings render `[WARN]`); the smoke needs the GPU free — it failed OOM beside a resident vLLM engine and passed with it paused |
|
|
137
137
|
| **Jetson AGX Thor** (JetPack 7) | all six checks, three tiers, headless | pass; **12 passed, 1 xfailed** | ran on an *uncommitted* local torch override; the upstream fix is still open as [microduck_rl#39](https://github.com/pollen-robotics/microduck_rl/pull/39) (issue [#38](https://github.com/pollen-robotics/microduck_rl/issues/38)), so `env doctor`'s `rl_pinned_commit` fails there **by design** until it merges and this repo re-pins |
|
|
138
138
|
| **Jetson AGX Orin** (L4T R39) | checks 1–4 | pass | the SBSA torch wheel carries no `sm_87` kernels — GPU training is **not available** on Orin at this pin |
|
|
139
139
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Jetson AGX Thor and AGX Orin, 2026-09-08: the tutorial's operator run
|
|
2
|
+
|
|
3
|
+
The Jetson AI Lab tutorial draft
|
|
4
|
+
([NVIDIA-AI-IOT/jetson-ai-lab#444](https://github.com/NVIDIA-AI-IOT/jetson-ai-lab/pull/444))
|
|
5
|
+
was followed by the operator on Jetson AGX Thor on the **PyPI 0.9.4 wheel**
|
|
6
|
+
(`~/.local/bin/microduck`, `uv tool install`), and its review comments asked
|
|
7
|
+
for numbers this record supplies: the Thor smoke on the wheel, and the
|
|
8
|
+
simulation's memory footprint on both Jetson boards. Everything below was
|
|
9
|
+
run over SSH by the agent at the operator's request, except the first smoke
|
|
10
|
+
attempt, which the operator ran and saved as `1.json`.
|
|
11
|
+
|
|
12
|
+
| | Jetson AGX Thor | Jetson AGX Orin |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| host | "NVIDIA Thor", JetPack 7 / L4T R38.2.2, kernel `6.8.12-tegra`, CUDA 13.0, 122 GB unified | "Orin (nvgpu)" sm_87, L4T R39, kernel `6.8.12-1021-tegra`, 61 GB unified |
|
|
15
|
+
| CLI | `microduck-cli 0.9.4` from PyPI | `microduck-cli 0.9.4` from PyPI |
|
|
16
|
+
| `microduck` clone | `0cd676d` (the pin) | `0cd676d` (the pin) |
|
|
17
|
+
| `microduck_rl` clone | `a30a9e4` (`fix/jetson-thor-torch-source`, [microduck_rl#39](https://github.com/pollen-robotics/microduck_rl/pull/39)) | `a30a9e4` (same branch) |
|
|
18
|
+
| shared box | the production stack (`prod-*`, `model-gear-*` vLLM engines) up throughout | `model-gear-*` and `prod-*` containers up throughout; 8 GB available |
|
|
19
|
+
|
|
20
|
+
## Thor: the smoke failed first, on a torch the clone had re-synced
|
|
21
|
+
|
|
22
|
+
The operator's first `policy smoke` (`1.json`, 01:31) failed in
|
|
23
|
+
`ManagerBasedRlEnv.__init__` with `torch.AcceleratorError: CUDA error: no
|
|
24
|
+
kernel image is available for execution on the device`, after torch's own
|
|
25
|
+
warning that the installed build supports `sm_80 sm_90 sm_100 sm_120` and
|
|
26
|
+
Thor is `sm_110`. The clone's reflog showed it had been checked out at
|
|
27
|
+
`a30a9e4` and then moved back to the pinned `29e887e`; the next `uv run
|
|
28
|
+
train` re-synced torch to the `2.9.1+cu129` wheel from PyTorch's index
|
|
29
|
+
(`Uninstalled 1 package … Installed 1 package` at the top of stderr), which
|
|
30
|
+
carries no Thor kernels. The three extra wheels the fix branch adds
|
|
31
|
+
(`nvpl-blas`, `nvpl-lapack`, `nvidia-cudss-cu13`) were still in the venv.
|
|
32
|
+
|
|
33
|
+
Fix: back to the tutorial's Thor tab.
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
$ git checkout a30a9e4 && UV_HTTP_TIMEOUT=600 uv sync # in ~/git/microduck_rl
|
|
37
|
+
$ .venv/bin/python -c "import torch"
|
|
38
|
+
ImportError: libnvpl_lapack_lp64_gomp.so.0: cannot open shared object file
|
|
39
|
+
$ .venv/bin/python -c "import mjlab_microduck.tasks, torch; print(torch.__version__, torch.cuda.get_arch_list(), torch.cuda.is_available())"
|
|
40
|
+
2.9.1 ['sm_110', 'sm_121'] True
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The bare `import torch` failure is expected on this branch: `a30a9e4` does
|
|
44
|
+
not use `LD_LIBRARY_PATH`; `mjlab_microduck/_torch_libs.py` pre-loads the two
|
|
45
|
+
undeclared libraries when `mjlab_microduck.tasks` is imported, which every
|
|
46
|
+
train/play path does before torch. A hand probe must import the package
|
|
47
|
+
first. The 2026-09-04 Thor record used `LD_LIBRARY_PATH` because that module
|
|
48
|
+
did not exist yet.
|
|
49
|
+
|
|
50
|
+
## Thor: the smoke on the wheel, second attempt
|
|
51
|
+
|
|
52
|
+
`free -g` immediately before: `Mem: 122 total, 86 used, 36 available` (the
|
|
53
|
+
tutorial's floor is 20 GB).
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
$ WANDB_MODE=offline timeout 30m microduck policy smoke Mjlab-Velocity-Flat-MicroDuck --json
|
|
57
|
+
Warp 1.12.0 initialized: CUDA Toolkit 12.9, Driver 13.0
|
|
58
|
+
"cuda:0" : "NVIDIA Thor" (123 GiB, sm_101, mempool enabled)
|
|
59
|
+
...
|
|
60
|
+
Iteration time: 2.05s
|
|
61
|
+
Time elapsed: 00:00:09
|
|
62
|
+
{"argv": ["uv", "run", "train", "Mjlab-Velocity-Flat-MicroDuck", "--env.scene.num-envs", "64", "--agent.max_iterations", "5"], "ok": true, "returncode": 0, ...}
|
|
63
|
+
exit 0 elapsed 0:26.02 max RSS 2.8 GB
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Pass.** 26 s wall-clock on a warm Warp kernel cache (the 2026-09-04 cold
|
|
67
|
+
run took 144 s). `docker ps` afterwards: every `prod-*` and `model-gear-*`
|
|
68
|
+
container still `Up`, the three vLLM engines `(healthy)`. `free -g` after:
|
|
69
|
+
`36 available`, unchanged.
|
|
70
|
+
|
|
71
|
+
## Memory footprint of the simulation
|
|
72
|
+
|
|
73
|
+
RSS of the two processes `env up --sim` starts, read from `/proc/<pid>/status`
|
|
74
|
+
while the duck was standing (`duck init --apply`, `duck enable --apply`):
|
|
75
|
+
|
|
76
|
+
| process | Thor (windowed, up since the operator's run) | Orin (`--headless`, 3 s to healthy) |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `duck-body` (`python -m mjlab_microduck.sim.body_server`) | 933 MB | 590 MB |
|
|
79
|
+
| `robotd --sim` | 34 MB | 41 MB |
|
|
80
|
+
| box `used` delta while up | not measured (already running) | +0.9 GB (`free -m`: 54355 → 55268 MB) |
|
|
81
|
+
|
|
82
|
+
So the simulation and the daemon together cost about one gigabyte, an order
|
|
83
|
+
of magnitude below the training smoke's 2.8 GB peak RSS and far below
|
|
84
|
+
Thor's 20 GB floor for the smoke. On Orin the stack ran and stood the duck
|
|
85
|
+
up with 8 GB available beside the production containers; `env down` stopped
|
|
86
|
+
both processes (`body: terminated`; the "something is still listening on
|
|
87
|
+
port 7801" warning cleared on the next check, nothing left running).
|
|
88
|
+
|
|
89
|
+
## Orin: the fix branch resolves torch without `UV_INDEX`
|
|
90
|
+
|
|
91
|
+
The Orin clone is also at `a30a9e4`, and its `uv.lock` resolves `torch`
|
|
92
|
+
2.9.1 from `pypi.jetson-ai-lab.io/sbsa/cu130` (the branch keys the SBSA
|
|
93
|
+
index on `'tegra' in platform_release`, which Orin's `6.8.12-1021-tegra`
|
|
94
|
+
kernel matches); `torch-2.9.1.dist-info` is what is installed. The tutorial's
|
|
95
|
+
Orin `UV_INDEX=…` line is only needed on the pinned `develop` commit. Nothing
|
|
96
|
+
changes for training: that torch has `sm_110`/`sm_121` kernels, not Orin's
|
|
97
|
+
`sm_87`, exactly as the 2026-09-04 Orin record found.
|
|
98
|
+
|
|
99
|
+
## What this changed
|
|
100
|
+
|
|
101
|
+
- The 0.9.6 `env doctor` fix in this repo (`PACKAGED_PINS`, `[WARN]`): Thor
|
|
102
|
+
on the wheel hit the two `[FAIL] … unknown` lines the Spark re-test filed as
|
|
103
|
+
`d5`, and the tutorial reviewer asked for it to just work.
|
|
104
|
+
- The tutorial: Step 2 tells Jetson readers to check out `a30a9e4` up front
|
|
105
|
+
(the flip back to the pin is what broke the first smoke); the Thor and Orin
|
|
106
|
+
tabs carry these numbers; Step 4's Spark tab warns about the sync's size.
|
|
@@ -98,20 +98,30 @@ def _diagnose() -> dict[str, object]:
|
|
|
98
98
|
return {"healthy": healthy, "checks": checks}
|
|
99
99
|
|
|
100
100
|
|
|
101
|
+
def _mark(check: dict) -> str:
|
|
102
|
+
"""`ok` for a pass, `WARN` for a failing warning, `FAIL` for a failing error."""
|
|
103
|
+
if check["passed"]:
|
|
104
|
+
return "ok"
|
|
105
|
+
return "WARN" if check["severity"] == "warning" else "FAIL"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _render_text(report: dict) -> str:
|
|
109
|
+
status = "healthy" if report["healthy"] else "unhealthy"
|
|
110
|
+
lines = [f"microduck-cli doctor: {status}", ""]
|
|
111
|
+
for check in report["checks"]:
|
|
112
|
+
lines.append(f"[{_mark(check)}] {check['id']}: {check['message']}")
|
|
113
|
+
if not check["passed"] and check["remediation"]:
|
|
114
|
+
lines.append(f" hint: {check['remediation']}")
|
|
115
|
+
return "\n".join(lines)
|
|
116
|
+
|
|
117
|
+
|
|
101
118
|
def cmd_doctor(args: argparse.Namespace) -> int:
|
|
102
119
|
report = _diagnose()
|
|
103
120
|
json_mode = bool(getattr(args, "json", False))
|
|
104
121
|
if json_mode:
|
|
105
122
|
emit_result(report, json_mode=True)
|
|
106
123
|
else:
|
|
107
|
-
|
|
108
|
-
lines = [f"microduck-cli doctor: {status}", ""]
|
|
109
|
-
for check in report["checks"]:
|
|
110
|
-
mark = "ok" if check["passed"] else "FAIL"
|
|
111
|
-
lines.append(f"[{mark}] {check['id']}: {check['message']}")
|
|
112
|
-
if not check["passed"] and check["remediation"]:
|
|
113
|
-
lines.append(f" hint: {check['remediation']}")
|
|
114
|
-
emit_result("\n".join(lines), json_mode=False)
|
|
124
|
+
emit_result(_render_text(report), json_mode=False)
|
|
115
125
|
return 0 if report["healthy"] else 1
|
|
116
126
|
|
|
117
127
|
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Diagnoses whether this box is ready to run the simulation stack (t13) and
|
|
4
4
|
the training lane (t14): the upstream clones checked out at the pinned
|
|
5
|
-
commits recorded in ``docs/upstream-pins.md
|
|
5
|
+
commits recorded in ``docs/upstream-pins.md`` (carried inside the package as
|
|
6
|
+
``PACKAGED_PINS`` too, so a wheel install can verify them), a cargo toolchain new enough
|
|
6
7
|
to build ``robotd``/``robotctl``/``tof``/``sounds``, the built daemons
|
|
7
8
|
themselves, the ``microduck_rl`` virtualenv with ``onnxruntime`` installed,
|
|
8
9
|
a state directory short enough for unix sockets, a free port for
|
|
@@ -65,6 +66,18 @@ _MICRODUCK_RL_HF_README_URL = (
|
|
|
65
66
|
"https://github.com/pollen-robotics/microduck_rl/blob/develop/scripts/hf/README.md"
|
|
66
67
|
)
|
|
67
68
|
|
|
69
|
+
# --- the pinned upstream commits, carried inside the wheel -------------
|
|
70
|
+
# docs/upstream-pins.md is the authoritative ledger, but a wheel does not
|
|
71
|
+
# ship docs/, so a `uv tool install` used to report both pin checks as
|
|
72
|
+
# "unknown" (a [FAIL] line under a healthy verdict — the 2026-09-07 Spark
|
|
73
|
+
# re-test, defect d5). These are the same commits, hardcoded on purpose;
|
|
74
|
+
# tests/test_env_doctor.py asserts they equal the table so a re-pin that
|
|
75
|
+
# forgets one of the two fails CI rather than drifting.
|
|
76
|
+
PACKAGED_PINS: dict[str, str] = {
|
|
77
|
+
"pollen-robotics/microduck": "0cd676d6fbb6e90a762c84aa63abe7a02dbc9495",
|
|
78
|
+
"pollen-robotics/microduck_rl": "29e887ecfbf5d37144759e5a9f8a176dfb83d547",
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
_MIN_CARGO_VERSION = (1, 89)
|
|
69
82
|
|
|
70
83
|
# duck-body's default port, matching upstream's DUCK_SIM_PORT (env/stack.py's
|
|
@@ -181,6 +194,17 @@ def _parse_pins(pins_path: Path) -> dict[str, str]:
|
|
|
181
194
|
return pins
|
|
182
195
|
|
|
183
196
|
|
|
197
|
+
def load_pins(pins_path: Path) -> dict[str, str]:
|
|
198
|
+
"""The pinned commits: the docs table when it is readable, else the packaged copy.
|
|
199
|
+
|
|
200
|
+
A checkout reads ``docs/upstream-pins.md`` (so an in-flight re-pin is seen
|
|
201
|
+
before the constant is updated); a wheel, which does not ship ``docs/``,
|
|
202
|
+
falls back to :data:`PACKAGED_PINS` row by row. Either way the result
|
|
203
|
+
carries both repos whenever either source knows them.
|
|
204
|
+
"""
|
|
205
|
+
return {**PACKAGED_PINS, **_parse_pins(pins_path)}
|
|
206
|
+
|
|
207
|
+
|
|
184
208
|
def _find_onnxruntime(rl_clone: str) -> str | None:
|
|
185
209
|
venv = Path(rl_clone) / ".venv"
|
|
186
210
|
if not venv.is_dir():
|
|
@@ -296,7 +320,7 @@ def default_probe() -> EnvProbe:
|
|
|
296
320
|
env = _probe_environ()
|
|
297
321
|
|
|
298
322
|
pins_path = Path(__file__).resolve().parents[2] / "docs" / "upstream-pins.md"
|
|
299
|
-
pins =
|
|
323
|
+
pins = load_pins(pins_path)
|
|
300
324
|
|
|
301
325
|
microduck_clone, rl_clone = resolve_clone_paths(env)
|
|
302
326
|
microduck_clone_commit = _git_head(microduck_clone) if microduck_clone else None
|
|
@@ -377,9 +401,9 @@ def _pinned_commit_check(
|
|
|
377
401
|
check_id,
|
|
378
402
|
False,
|
|
379
403
|
"warning",
|
|
380
|
-
f"pinned commit for {label} is unknown (docs/upstream-pins.md
|
|
381
|
-
"
|
|
382
|
-
"
|
|
404
|
+
f"pinned commit for {label} is unknown (no row in docs/upstream-pins.md and none "
|
|
405
|
+
"packaged)",
|
|
406
|
+
"add the repo's row to docs/upstream-pins.md and PACKAGED_PINS in env/doctor.py",
|
|
383
407
|
)
|
|
384
408
|
if not clone_commit:
|
|
385
409
|
return _check(
|
|
@@ -621,17 +645,25 @@ def diagnose(probe: EnvProbe) -> dict[str, object]:
|
|
|
621
645
|
return {"healthy": healthy, "checks": checks}
|
|
622
646
|
|
|
623
647
|
|
|
648
|
+
def _mark(check: Mapping[str, object]) -> str:
|
|
649
|
+
if check["passed"]:
|
|
650
|
+
return "ok"
|
|
651
|
+
return "WARN" if check["severity"] == "warning" else "FAIL"
|
|
652
|
+
|
|
653
|
+
|
|
624
654
|
def render_text(report: Mapping[str, object]) -> str:
|
|
625
655
|
"""Render `report` the way `microduck-cli doctor`'s text output looks.
|
|
626
656
|
|
|
627
|
-
``"[ok] id: message"``
|
|
628
|
-
|
|
657
|
+
``"[ok] id: message"`` for a passing check, ``"[WARN] id: message"`` for a
|
|
658
|
+
failing ``warning``-severity one (surfaced, never blocking the verdict) and
|
|
659
|
+
``"[FAIL] id: message"`` for a failing ``error``, with a ``" hint:
|
|
660
|
+
remediation"`` line under any failing check that carries one. The prefix
|
|
661
|
+
agrees with the verdict line: a healthy report never shows ``[FAIL]``.
|
|
629
662
|
"""
|
|
630
663
|
status = "healthy" if report["healthy"] else "unhealthy"
|
|
631
664
|
lines = [f"microduck-cli env doctor: {status}", ""]
|
|
632
665
|
for check in report["checks"]: # type: ignore[union-attr]
|
|
633
|
-
|
|
634
|
-
lines.append(f"[{mark}] {check['id']}: {check['message']}")
|
|
666
|
+
lines.append(f"[{_mark(check)}] {check['id']}: {check['message']}")
|
|
635
667
|
if not check["passed"] and check["remediation"]:
|
|
636
668
|
lines.append(f" hint: {check['remediation']}")
|
|
637
669
|
return "\n".join(lines)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "microduck-cli"
|
|
3
|
-
version = "0.9.
|
|
3
|
+
version = "0.9.6"
|
|
4
4
|
description = "Agent-agnostic CLI for controlling the MicroDuck robot. Any agent, any human, one CLI. Sim-first: every verb is exercised against the real robotd daemon and the MuJoCo simulation."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
+
from pathlib import Path
|
|
6
7
|
|
|
7
8
|
from microduck_cli.env.doctor import (
|
|
9
|
+
PACKAGED_PINS,
|
|
8
10
|
EnvProbe,
|
|
11
|
+
_parse_pins,
|
|
9
12
|
default_probe,
|
|
10
13
|
diagnose,
|
|
14
|
+
load_pins,
|
|
11
15
|
render_text,
|
|
12
16
|
)
|
|
13
17
|
from microduck_cli.env.hosts import HostInfo
|
|
@@ -335,3 +339,58 @@ def test_default_probe_never_raises_and_returns_env_probe():
|
|
|
335
339
|
# Must be diagnosable without raising regardless of what's installed here.
|
|
336
340
|
report = diagnose(probe)
|
|
337
341
|
assert isinstance(report["healthy"], bool)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
# --- packaged pins (d5: a wheel ships no docs/) ---------------------------
|
|
345
|
+
|
|
346
|
+
_PINS_DOC = Path(__file__).resolve().parents[1] / "docs" / "upstream-pins.md"
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def test_packaged_pins_match_the_docs_table():
|
|
350
|
+
# A re-pin that updates one source and not the other must fail here.
|
|
351
|
+
assert _parse_pins(_PINS_DOC) == PACKAGED_PINS
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def test_load_pins_falls_back_to_packaged_when_docs_absent(tmp_path):
|
|
355
|
+
assert load_pins(tmp_path / "missing" / "upstream-pins.md") == PACKAGED_PINS
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def test_load_pins_prefers_a_readable_docs_table(tmp_path):
|
|
359
|
+
doc = tmp_path / "upstream-pins.md"
|
|
360
|
+
doc.write_text(
|
|
361
|
+
"| repo | ref | commit |\n|---|---|---|\n"
|
|
362
|
+
"| `pollen-robotics/microduck` | branch `x` | `abcdef1234567` |\n",
|
|
363
|
+
encoding="utf-8",
|
|
364
|
+
)
|
|
365
|
+
pins = load_pins(doc)
|
|
366
|
+
assert pins["pollen-robotics/microduck"] == "abcdef1234567"
|
|
367
|
+
assert pins["pollen-robotics/microduck_rl"] == PACKAGED_PINS["pollen-robotics/microduck_rl"]
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def test_default_probe_knows_both_pins():
|
|
371
|
+
probe = default_probe()
|
|
372
|
+
assert probe.microduck_pinned_commit == PACKAGED_PINS["pollen-robotics/microduck"]
|
|
373
|
+
assert probe.rl_pinned_commit == PACKAGED_PINS["pollen-robotics/microduck_rl"]
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
# --- [WARN] vs [FAIL] -----------------------------------------------------
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def test_render_text_marks_warnings_warn_not_fail():
|
|
380
|
+
probe = EnvProbe(
|
|
381
|
+
**{
|
|
382
|
+
**COMPLETE_PROBE.__dict__,
|
|
383
|
+
"rl_clone_commit": "a30a9e4ffc161c90d19181f2815d1b50064182bb",
|
|
384
|
+
}
|
|
385
|
+
)
|
|
386
|
+
report = diagnose(probe)
|
|
387
|
+
text = render_text(report)
|
|
388
|
+
assert text.startswith("microduck-cli env doctor: healthy")
|
|
389
|
+
assert "[WARN] rl_pinned_commit:" in text
|
|
390
|
+
assert "[FAIL]" not in text
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def test_render_text_marks_errors_fail():
|
|
394
|
+
text = render_text(diagnose(EMPTY_PROBE))
|
|
395
|
+
assert text.startswith("microduck-cli env doctor: unhealthy")
|
|
396
|
+
assert "[FAIL] microduck_clone_present:" in text
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/ask-colleague/scripts/ask-colleague.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/scripts/fetch-issues.sh
RENAMED
|
File without changes
|
{microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/scripts/mesh-message.sh
RENAMED
|
File without changes
|
{microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/scripts/post-comment.sh
RENAMED
|
File without changes
|
{microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/communicate/scripts/post-issue.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/doc-test-alignment/scripts/check.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/pypi-maintainer/scripts/switch-source.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microduck_cli-0.9.5 → microduck_cli-0.9.6}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/frames/jetson-retest-and-ai-lab-tutorial.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microduck_cli-0.9.5 → microduck_cli-0.9.6}/.devague/plans/jetson-retest-and-ai-lab-tutorial.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|