wade-cli 1.1.0__tar.gz → 1.1.2__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.
- {wade_cli-1.1.0 → wade_cli-1.1.2}/AGENTS.md +7 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/CHANGELOG.md +12 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/KNOWLEDGE.ratings.jsonl +2 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/PKG-INFO +25 -4
- {wade_cli-1.1.0 → wade_cli-1.1.2}/README.md +24 -3
- {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/architecture.md +26 -2
- {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/workflows-and-skills.md +27 -1
- {wade_cli-1.1.0 → wade_cli-1.1.2}/pyproject.toml +1 -1
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/__init__.py +1 -1
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/repo.py +12 -0
- wade_cli-1.1.2/src/wade/models/review_cycle.py +69 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/workflow.py +4 -4
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/done.py +13 -0
- wade_cli-1.1.2/src/wade/services/review_cycle_service.py +192 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/review_delegation_service.py +382 -85
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/review_record_service.py +70 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/review_service.py +108 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/materializer.py +9 -3
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/safe_state.py +46 -0
- wade_cli-1.1.2/templates/prompts/review-code.md +9 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/_partials/review-budget.md +7 -3
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/implementation.md +3 -3
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/review-pr-comments.md +3 -3
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_implement_work_done_contract.py +6 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_skill_install.py +18 -1
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_done_gates.py +2 -2
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_done_idempotent.py +26 -6
- wade_cli-1.1.2/tests/unit/test_services/test_review_cycle_service.py +235 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_review_delegation_service.py +173 -0
- wade_cli-1.1.2/tests/unit/test_services/test_review_receipts.py +999 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_review_records.py +82 -3
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_review_status_block.py +2 -2
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_review_budget_partial.py +13 -0
- wade_cli-1.1.0/templates/prompts/review-code.md +0 -7
- wade_cli-1.1.0/tests/unit/test_services/test_review_receipts.py +0 -497
- {wade_cli-1.1.0 → wade_cli-1.1.2}/.gitattributes +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/.github/workflows/auto-version.yml +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/.github/workflows/ci.yml +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/.github/workflows/pr-title-lint.yml +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/.github/workflows/publish.yml +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/.github/workflows/release.yml +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/.gitignore +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/.pre-commit-config.yaml +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/.wade.yml +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/CONTRIBUTING.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/KNOWLEDGE.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/assets/wade.png +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/docker-compose.e2e.yml +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/documentation-policies.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/extending.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/skills-system.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/testing.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/install.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/auto_version.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/changelog.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/check-all.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/check.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/fmt.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/hooks/pre-push +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/install-hooks.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/release.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/setup-worktree.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-concurrency.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-e2e-docker.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-e2e.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-live-ai-taskr.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-live-ai.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-live-gh.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/__main__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/admin.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/autocomplete.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/deps_session.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/hook.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/implementation_session.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/knowledge.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/main.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/plan_session.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/review.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/review_pr_comments_session.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/session.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/session_shared.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/skills.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/task.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/worktree.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/config/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/config/loader.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/config/migrations.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/config/schema.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/branch.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/hooks.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/pr.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/stash.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/sync.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/worktree.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/hooks/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/hooks/cli.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/hooks/policies.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/logging/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/logging/context.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/logging/setup.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/batch.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/config.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/delegation.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/deps.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/events.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/hooks.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/permission.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/readiness.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/review.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/session.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/session_manifest.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/skill.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/task.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/worktree.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/_pr_delegate.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/base.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/clickup.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/github.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/markdown.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/registry.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/ai_resolution.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/batch_review_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/bot_trigger.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/check_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/delegation_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/deps_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/documentation_receipt_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/_shared.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/batch.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/bootstrap.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/cleanup.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/core.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/draft_pr.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/lifecycle.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/sync.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/usage_tracking.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/auth.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/commands.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/config_io.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/manifest.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/migrations.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/prompts_ai.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/prompts_setup.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/shell.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/knowledge_recovery.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/knowledge_search.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/knowledge_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/plan_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/prompt_delivery.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/review_settle.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/session_composition_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/skill_diagnostics_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/skill_invocation_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/smart_start.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/task_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/catalog.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/discovery.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/doc_targets.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/installer.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/pointer.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/resolver.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/validation.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/ui/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/ui/console.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/ui/prompts.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/body_markers.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/clipboard.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/conventional.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/filelock.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/gitref.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/http.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/install.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/knowledge_file.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/markdown.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/markers.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/paths.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/plan_validation.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/process.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/runtime_env.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/slug.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/stale_base.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/templates.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/terminal.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/token_usage_markdown.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/update_check.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/agents-pointer.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/hooks/commit-msg +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/hooks/pre-commit +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/hooks/pre-push +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/deps-analysis.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/implement-context.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/plan-session.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/review-batch.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/review-plan.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/review-pr-comments.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/session-start-implement.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/session-start-plan.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/session-start-review.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/setup-worktree.sh.example +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/batch-review/SKILL.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/code-review/SKILL.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/dependency-analysis/SKILL.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/implementation/SKILL.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/knowledge/SKILL.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/plan-review/SKILL.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/planning/SKILL.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/review-comments/SKILL.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/task/SKILL.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/task/examples.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/task/plan-format.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/task/reference/session-summary-format.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/statusline-command.sh +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/_partials/completion.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/_partials/documentation-step.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/_partials/interaction-policy.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/_partials/knowledge-step.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/plan.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/documentation.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/new-plan.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/plan-output-contract.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/pr-summary-format.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/recovery.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/session-summary-format.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/tracking-issues.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/concurrency/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/concurrency/test_batch_multi_pr.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/concurrency/test_knowledge_merge.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/concurrency/test_lock_retry.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/concurrency/test_stash_race.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/conftest.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/_support.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/conftest.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/mock_gh_script.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_admin_contract.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_check_contract.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_knowledge_contract.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_mock_gh_contract.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_plan_contract.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_review_contract.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_smart_start_contract.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_task_contract.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_work_sync_list_contract.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/live/taskr_header_hi_body.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/live/taskr_header_howdy_body.md +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/transcripts/claude_session.txt +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/transcripts/codex_session.txt +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/transcripts/copilot_session.txt +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/transcripts/generic_session.txt +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_autostash.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_check.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_dynamic_skill_sessions.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_git.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_implementation_lifecycle.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_init.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_knowledge.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_markdown_provider.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_skill_context_budget.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/live/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/live/test_wade_live_ai.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/live/test_wade_live_ai_taskr.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/live/test_wade_live_gh.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/test_cli_basics.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/conftest.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_autocomplete.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_done_doc_pass_advisory.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_done_review_hint.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_knowledge_cli.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_review_cli.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_session_subapps.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_shell_init.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_skills_session_cli.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_task_cli.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_work_ai_flags.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_work_cli.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_bot_review_config.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_dynamic_skill_config.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_loader.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_merge_strategy_migration.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_migrations.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_migrations_atomic.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_console.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_effort.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_all_patches_present.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_branch_list.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_branch_slugify.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_detached_worktree.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_is_merged_into.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_main_checkout_root.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_merge_count.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_pr_base.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_pr_lookup.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_pr_retry.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_pr_title.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_repo.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_reset_branch.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_scaffold_commit.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_stash_drop.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_sync_conflicted_files.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_tip_commit_is_empty.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_worktree_remove.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_hooks/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_hooks/test_hook_cli.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_hooks/test_policies.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_hooks/test_post_tool_use.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_hooks/test_session_start.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_batch.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_config_models.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_delegation.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_deps_models.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_deps_partition.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_review.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_review_expectation.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_task_models.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_workflow_models.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_permission_mode.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_providers/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_providers/test_clickup.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_providers/test_github.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_providers/test_github_pr.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_providers/test_markdown.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_scripts/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_scripts/test_changelog.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_ai_resolution.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_batch_build_cmd.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_batch_pr_index.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_batch_review_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_allowlist.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_guard_matcher.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_hook_timeout.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_post_tool_use.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_pre_push.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_session_start.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bot_trigger.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_catchup.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_catchup_reconcile.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_check.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_cleanup_loss_guard.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_codex_worktree_launch_context.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_delegation_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_deps_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_documentation_receipt.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_done_plan_flag.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_done_pr_title_sync.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_draft_pr_base_fetch.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_draft_pr_retarget.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_implementation_done_sync.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_implementation_launch_context.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_implementation_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_implementation_service_resume.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_init.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_init_shell_integration.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_knowledge_recovery.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_knowledge_search.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_knowledge_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_managed_git_hooks.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_no_direct_git.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_plan_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_poll_for_reviews.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_post_implementation_lifecycle.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_pr_lookup_guards.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_pr_summary_path.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_pre_push_backstop.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_preserve_session_data.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_prompt_delivery.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_push_recovery.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_resolve_task_branch.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_resume_autonomy.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_review_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_skill_diagnostics_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_smart_start.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_smart_start_resume.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_stale_base_surface.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_sync_base_branch.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_task_service.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_tracked_managed_files.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_worktree_gitignore.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_crossby_contract.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_doc_targets.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_dynamic_skills.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_generic_builtin_boundaries.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_knowledge_attributes.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_managed_gitignore.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_pointer.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/__init__.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_body_markers.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_conventional.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_filelock.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_gitref.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_http.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_install.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_markdown.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_markers.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_plan_validation.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_process.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_prompts.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_runtime_env.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_slug.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_stale_base.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_terminal.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_terminal_launch.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_update_check.py +0 -0
- {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_yolo.py +0 -0
|
@@ -72,6 +72,13 @@ No circular dependencies. Models are pure data. Services orchestrate. **Never im
|
|
|
72
72
|
> GitHub (PR/issue body markers, labels) and worktree files, **not** SQLite. The
|
|
73
73
|
> code still exists but is inert; removal is tracked as #357 C5.
|
|
74
74
|
|
|
75
|
+
> **Closing-review state** lives in worktree files, not the immutable session
|
|
76
|
+
> bundle: binding-aware receipts are under `.wade/reviews/`, and an active
|
|
77
|
+
> PR-comment feedback cycle is under `.wade/review-cycles/`. The cycle preserves
|
|
78
|
+
> its pre-edit baseline and feedback history across refreshes and is cleared
|
|
79
|
+
> only after a successful PR-comment completion; unsafe state must fall back to
|
|
80
|
+
> a full review.
|
|
81
|
+
|
|
75
82
|
> **Deterministic git-hook install/reconcile/build logic lives in `git/hooks.py`**
|
|
76
83
|
> (git layer), not `skills/installer.py`. Packaged template-asset loaders
|
|
77
84
|
> (prompt/skill/hook templates) live in `utils/templates.py`, a leaf module.
|
|
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Conventional Commits](https://conventionalcommits.org/).
|
|
6
6
|
|
|
7
|
+
## [v1.1.2] — 2026-09-08
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- make closing reviews converge on final incremental changes (#503) (5599fb1)
|
|
12
|
+
|
|
13
|
+
## [v1.1.1] — 2026-09-08
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
- review limitation on reviews (#500) (3479794)
|
|
18
|
+
|
|
7
19
|
## [v1.1.0] — 2026-09-04
|
|
8
20
|
|
|
9
21
|
### Features
|
|
@@ -170,3 +170,5 @@
|
|
|
170
170
|
{"dir": "stale", "event_id": "71f6ebba04cc4c86bc4e2d02ed19ea9d", "id": "65ee7eb64d81", "ts": "2026-09-04T22:14:03.707575+00:00"}
|
|
171
171
|
{"id": "1f23c00660aa", "superseded_by": "4668ee29b835", "ts": "2026-09-04T22:14:10.938819+00:00"}
|
|
172
172
|
{"id": "65ee7eb64d81", "superseded_by": "54236b4243bc", "ts": "2026-09-04T22:14:17.907524+00:00"}
|
|
173
|
+
{"dir": "up", "event_id": "bc0cace277f74e4490f28148eafb4070", "id": "4d419ec6629c", "ts": "2026-09-07T22:30:30.110983+00:00"}
|
|
174
|
+
{"dir": "up", "event_id": "0901f7e84f80422488ee976fb7f54934", "id": "fb5582aa12e0", "ts": "2026-09-08T10:26:25.322775+00:00"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: wade-cli
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: WADE — Workflow for AI-Driven Engineering
|
|
5
5
|
Project-URL: Homepage, https://github.com/ivanviragine/wade
|
|
6
6
|
Project-URL: Repository, https://github.com/ivanviragine/wade
|
|
@@ -325,7 +325,7 @@ WADE splits review into an **AI review pass** you or the agent can invoke, and
|
|
|
325
325
|
| Command | What it reviews |
|
|
326
326
|
|---------|-----------------|
|
|
327
327
|
| `wade review plan <file>` | A plan file, before it becomes tasks |
|
|
328
|
-
| `wade review implementation` | The current implementation
|
|
328
|
+
| `wade review implementation` | The current implementation change (mandatory gate before `done`) |
|
|
329
329
|
| `wade review batch <N>` | Coherence across parallel implementation branches |
|
|
330
330
|
| `wade review pr-comments <N>` | Starts a session to address human/bot PR comments |
|
|
331
331
|
| `wade review trigger <N>` | Posts configured bot-review trigger comments on the task's PR |
|
|
@@ -335,12 +335,33 @@ printing it does not certify that a review happened. After performing that
|
|
|
335
335
|
self-review and addressing its findings, run
|
|
336
336
|
`wade review implementation --ack-self-review` to write the current commit and
|
|
337
337
|
frozen-review-binding receipt. Successful headless or interactive reviews write
|
|
338
|
-
the same receipt directly.
|
|
338
|
+
the same receipt directly. In an implementation session, once the active frozen
|
|
339
|
+
review binding has consumed `done.max_review_passes` (default 2), `wade review
|
|
340
|
+
implementation` exits successfully without launching another reviewer and directs
|
|
341
|
+
you to `wade implementation-session done`. The dispatch cap does not block a
|
|
342
|
+
valid `--ack-self-review`, which still writes its active-binding receipt.
|
|
343
|
+
|
|
344
|
+
The first implementation review for a frozen reviewer binding receives the
|
|
345
|
+
complete branch change. After a successful review and a new fix commit, a
|
|
346
|
+
re-review receives only the committed delta since that successful same-binding
|
|
347
|
+
review, plus any staged or unstaged changes and the local `PLAN.md` when safely
|
|
348
|
+
available. This narrows repeated review work without weakening `done`: the final
|
|
349
|
+
receipt is still written for the exact current commit. An unreadable record,
|
|
350
|
+
changed binding, unreachable baseline, or merge on the baseline-to-HEAD path
|
|
351
|
+
falls back to the complete branch change.
|
|
339
352
|
|
|
340
353
|
To fetch the unresolved comments themselves during a review session, the agent
|
|
341
354
|
runs `wade review-pr-comments-session fetch <N>`; it resolves individual threads with
|
|
342
355
|
`wade review-pr-comments-session resolve`.
|
|
343
356
|
|
|
357
|
+
When a PR-comment session starts, WADE records its pre-edit commit and a feedback
|
|
358
|
+
snapshot under local `.wade/review-cycles/` state, outside the immutable session
|
|
359
|
+
bundle. Re-fetching retains prior feedback while adding new feedback, but never
|
|
360
|
+
advances that baseline. Its closing review receives the accumulated feedback and
|
|
361
|
+
the cycle delta; unsafe, mismatched, stale, or merged lineage uses the full
|
|
362
|
+
branch change instead. A successful `review-pr-comments-session done` clears
|
|
363
|
+
the cycle so later feedback starts from the then-current PR head.
|
|
364
|
+
|
|
344
365
|
### The auto-launched review session
|
|
345
366
|
|
|
346
367
|
The auto-launched **review session** — the one started when you pick **"Wait
|
|
@@ -545,7 +566,7 @@ Decision and integrity gates deliberately require an explicit valid outcome:
|
|
|
545
566
|
| PR-SUMMARY | `PR-SUMMARY.md` is missing, empty, or still a template placeholder (both change-producing session types) | `done.require_pr_summary: false` |
|
|
546
567
|
| Documentation decision | neither an `--updated` nor reasoned `--not-needed` receipt exists for current HEAD | *no disable toggle; `docs --not-needed "<reason>"` records the explicit no-change outcome* |
|
|
547
568
|
| Sync | the branch is behind main — auto-syncs first, refuses only on conflict (both change-producing session types) | `done.require_sync: false` |
|
|
548
|
-
| Review ran | `wade review implementation` has no successful external-review or explicitly acknowledged self-review record for the current commit **and frozen REVIEW binding**, or the frozen workflow/skill bundle no longer matches its manifest. Bundle validation happens before receipts or pass counts are trusted. Prompt emission alone never satisfies this gate. **Implementation sessions bound this loop:**
|
|
569
|
+
| Review ran | `wade review implementation` has no successful external-review or explicitly acknowledged self-review record for the current commit **and frozen REVIEW binding**, or the frozen workflow/skill bundle no longer matches its manifest. Bundle validation happens before receipts or pass counts are trusted. Prompt emission alone never satisfies this gate. **Implementation sessions bound this loop:** at `done.max_review_passes` (default 2), `wade review implementation` stops before launching an excess reviewer, while a valid `--ack-self-review` may still record its receipt; `done` remains authoritative and completes an unreviewed later commit with a cap-reached status instead of looping forever | `--skip-review`, `done.require_review: false` (auto-off when `ai.review_implementation.enabled: false`) |
|
|
549
570
|
| Resolved threads | unresolved PR review threads remain (review-comments only) | `done.require_resolved_threads: false` |
|
|
550
571
|
| Conventional title | the task title is not a conventional-commit title (the PR title is derived from it, so it would fail `PR Title Lint`) — blocks; when valid but the open PR's title differs, syncs the PR title to match (both session types) — if that sync fails while the PR's current title is itself non-conventional (lint would fail), `done` fails so it can be retried | `done.require_conventional_title: false` |
|
|
551
572
|
| Knowledge valid | the knowledge file is structurally corrupt — duplicate entry IDs or unresolved conflict markers (e.g. from a `merge=union` merge) | *none — gated by `knowledge.enabled`; no `done.*` hatch* |
|
|
@@ -289,7 +289,7 @@ WADE splits review into an **AI review pass** you or the agent can invoke, and
|
|
|
289
289
|
| Command | What it reviews |
|
|
290
290
|
|---------|-----------------|
|
|
291
291
|
| `wade review plan <file>` | A plan file, before it becomes tasks |
|
|
292
|
-
| `wade review implementation` | The current implementation
|
|
292
|
+
| `wade review implementation` | The current implementation change (mandatory gate before `done`) |
|
|
293
293
|
| `wade review batch <N>` | Coherence across parallel implementation branches |
|
|
294
294
|
| `wade review pr-comments <N>` | Starts a session to address human/bot PR comments |
|
|
295
295
|
| `wade review trigger <N>` | Posts configured bot-review trigger comments on the task's PR |
|
|
@@ -299,12 +299,33 @@ printing it does not certify that a review happened. After performing that
|
|
|
299
299
|
self-review and addressing its findings, run
|
|
300
300
|
`wade review implementation --ack-self-review` to write the current commit and
|
|
301
301
|
frozen-review-binding receipt. Successful headless or interactive reviews write
|
|
302
|
-
the same receipt directly.
|
|
302
|
+
the same receipt directly. In an implementation session, once the active frozen
|
|
303
|
+
review binding has consumed `done.max_review_passes` (default 2), `wade review
|
|
304
|
+
implementation` exits successfully without launching another reviewer and directs
|
|
305
|
+
you to `wade implementation-session done`. The dispatch cap does not block a
|
|
306
|
+
valid `--ack-self-review`, which still writes its active-binding receipt.
|
|
307
|
+
|
|
308
|
+
The first implementation review for a frozen reviewer binding receives the
|
|
309
|
+
complete branch change. After a successful review and a new fix commit, a
|
|
310
|
+
re-review receives only the committed delta since that successful same-binding
|
|
311
|
+
review, plus any staged or unstaged changes and the local `PLAN.md` when safely
|
|
312
|
+
available. This narrows repeated review work without weakening `done`: the final
|
|
313
|
+
receipt is still written for the exact current commit. An unreadable record,
|
|
314
|
+
changed binding, unreachable baseline, or merge on the baseline-to-HEAD path
|
|
315
|
+
falls back to the complete branch change.
|
|
303
316
|
|
|
304
317
|
To fetch the unresolved comments themselves during a review session, the agent
|
|
305
318
|
runs `wade review-pr-comments-session fetch <N>`; it resolves individual threads with
|
|
306
319
|
`wade review-pr-comments-session resolve`.
|
|
307
320
|
|
|
321
|
+
When a PR-comment session starts, WADE records its pre-edit commit and a feedback
|
|
322
|
+
snapshot under local `.wade/review-cycles/` state, outside the immutable session
|
|
323
|
+
bundle. Re-fetching retains prior feedback while adding new feedback, but never
|
|
324
|
+
advances that baseline. Its closing review receives the accumulated feedback and
|
|
325
|
+
the cycle delta; unsafe, mismatched, stale, or merged lineage uses the full
|
|
326
|
+
branch change instead. A successful `review-pr-comments-session done` clears
|
|
327
|
+
the cycle so later feedback starts from the then-current PR head.
|
|
328
|
+
|
|
308
329
|
### The auto-launched review session
|
|
309
330
|
|
|
310
331
|
The auto-launched **review session** — the one started when you pick **"Wait
|
|
@@ -509,7 +530,7 @@ Decision and integrity gates deliberately require an explicit valid outcome:
|
|
|
509
530
|
| PR-SUMMARY | `PR-SUMMARY.md` is missing, empty, or still a template placeholder (both change-producing session types) | `done.require_pr_summary: false` |
|
|
510
531
|
| Documentation decision | neither an `--updated` nor reasoned `--not-needed` receipt exists for current HEAD | *no disable toggle; `docs --not-needed "<reason>"` records the explicit no-change outcome* |
|
|
511
532
|
| Sync | the branch is behind main — auto-syncs first, refuses only on conflict (both change-producing session types) | `done.require_sync: false` |
|
|
512
|
-
| Review ran | `wade review implementation` has no successful external-review or explicitly acknowledged self-review record for the current commit **and frozen REVIEW binding**, or the frozen workflow/skill bundle no longer matches its manifest. Bundle validation happens before receipts or pass counts are trusted. Prompt emission alone never satisfies this gate. **Implementation sessions bound this loop:**
|
|
533
|
+
| Review ran | `wade review implementation` has no successful external-review or explicitly acknowledged self-review record for the current commit **and frozen REVIEW binding**, or the frozen workflow/skill bundle no longer matches its manifest. Bundle validation happens before receipts or pass counts are trusted. Prompt emission alone never satisfies this gate. **Implementation sessions bound this loop:** at `done.max_review_passes` (default 2), `wade review implementation` stops before launching an excess reviewer, while a valid `--ack-self-review` may still record its receipt; `done` remains authoritative and completes an unreviewed later commit with a cap-reached status instead of looping forever | `--skip-review`, `done.require_review: false` (auto-off when `ai.review_implementation.enabled: false`) |
|
|
513
534
|
| Resolved threads | unresolved PR review threads remain (review-comments only) | `done.require_resolved_threads: false` |
|
|
514
535
|
| Conventional title | the task title is not a conventional-commit title (the PR title is derived from it, so it would fail `PR Title Lint`) — blocks; when valid but the open PR's title differs, syncs the PR title to match (both session types) — if that sync fails while the PR's current title is itself non-conventional (lint would fail), `done` fails so it can be retried | `done.require_conventional_title: false` |
|
|
515
536
|
| Knowledge valid | the knowledge file is structurally corrupt — duplicate entry IDs or unresolved conflict markers (e.g. from a `merge=union` merge) | *none — gated by `knowledge.enabled`; no `done.*` hatch* |
|
|
@@ -87,6 +87,7 @@ src/wade/
|
|
|
87
87
|
│ ├── skill_invocation_service.py # Bind methods to bounded operation contracts
|
|
88
88
|
│ ├── skill_diagnostics_service.py # Discovery and precedence diagnostics
|
|
89
89
|
│ ├── review_record_service.py # Binding-aware durable review outcomes
|
|
90
|
+
│ ├── review_cycle_service.py # Safe PR-comment feedback-cycle context
|
|
90
91
|
│ ├── documentation_receipt_service.py # Current-commit documentation decision
|
|
91
92
|
│ ├── batch_review_service.py # Batch issue review
|
|
92
93
|
│ ├── deps_service.py # Dependency analysis
|
|
@@ -800,7 +801,30 @@ The implementation-review pass cap distinguishes an attempted, bounded review
|
|
|
800
801
|
from a reviewer that never launched: success and a true headless timeout count
|
|
801
802
|
toward `done.max_review_passes`, but missing credentials/PATH or a sandbox exec
|
|
802
803
|
denial do not. Otherwise an unavailable reviewer could exhaust the cap and let
|
|
803
|
-
`done` bypass its review gate without any review attempt.
|
|
804
|
+
`done` bypass its review gate without any review attempt. For an implementation
|
|
805
|
+
session, `wade review implementation` consults that same active frozen-binding
|
|
806
|
+
count before dispatch and skips an excess launch; `implementation-session done`
|
|
807
|
+
remains the authoritative classifier for accepting an unreviewed later commit
|
|
808
|
+
and projecting its cap-reached PR review status. A valid `--ack-self-review`
|
|
809
|
+
still records the current binding receipt because it acknowledges work already
|
|
810
|
+
performed rather than dispatching a reviewer. Plan and PR-comment reviews are
|
|
811
|
+
uncapped.
|
|
812
|
+
|
|
813
|
+
Review input converges without weakening completion evidence. The first mapped
|
|
814
|
+
implementation review uses the full branch diff; later reviews may select the
|
|
815
|
+
nearest satisfying record for the same frozen REVIEW binding that is an ancestor
|
|
816
|
+
of `HEAD`, then inspect only the delta from that commit. A Git error, malformed
|
|
817
|
+
or foreign record, non-ancestor, or merge on that ancestry path forces the full
|
|
818
|
+
branch diff. Staged and unstaged changes remain separate inputs in either case,
|
|
819
|
+
and every review writes its ordinary exact-HEAD receipt.
|
|
820
|
+
|
|
821
|
+
`review_cycle_service.py` stores the PR-comment session's issue/PR identity,
|
|
822
|
+
pre-edit baseline, and normalized feedback in `.wade/review-cycles/`, outside
|
|
823
|
+
the immutable session bundle. Re-fetching retains prior feedback and adds new
|
|
824
|
+
feedback while preserving the baseline; a successful PR-comment `done` removes
|
|
825
|
+
it. The closing review receives the accumulated feedback and cycle delta only
|
|
826
|
+
after safe state, identity, ancestry, and merge-path checks. Any failed check
|
|
827
|
+
emits a diagnostic and reviews the complete branch diff instead.
|
|
804
828
|
|
|
805
829
|
The capability remediation is intentionally tool-neutral: retain the sandbox
|
|
806
830
|
and grant only the worktree Git metadata paths, GitHub credential/API route, or
|
|
@@ -911,7 +935,7 @@ includes the active binding's pass count. The structured worktree records under
|
|
|
911
935
|
`.wade/reviews/` provide deterministic gate evidence; the PR-body projection is
|
|
912
936
|
the durable human-readable outcome.
|
|
913
937
|
|
|
914
|
-
Binding-aware review records and documentation receipts use the descriptor-safe
|
|
938
|
+
Binding-aware review records, review-cycle contexts, and documentation receipts use the descriptor-safe
|
|
915
939
|
state primitives in `utils/safe_state.py`: `.wade` and intermediate directories
|
|
916
940
|
must be real directories, final files are opened relative to trusted directory
|
|
917
941
|
handles, and malformed/symlinked/unreadable state is treated as absent. Record
|
|
@@ -175,6 +175,14 @@ An implementation-to-PR-comment transition is a new session and replaces the
|
|
|
175
175
|
manifest/workflow. Durable review records survive outside the session bundle,
|
|
176
176
|
but apply only if the new session's review binding is identical.
|
|
177
177
|
|
|
178
|
+
The implementation and PR-comment definitions currently use workflow revision
|
|
179
|
+
2. Their authoritative order is verification, documentation decision, knowledge,
|
|
180
|
+
then closing method review; an old frozen revision is rejected until an explicit
|
|
181
|
+
refresh replaces it. Expected tracked documentation and knowledge changes are
|
|
182
|
+
therefore normally present in the final review. If a review finding creates a
|
|
183
|
+
new tracked commit, the session repeats affected verification/documentation work
|
|
184
|
+
and reviews that new final commit.
|
|
185
|
+
|
|
178
186
|
## Bounded invocation contract
|
|
179
187
|
|
|
180
188
|
`skill_invocation_service.py` composes bounded prompts structurally in this
|
|
@@ -228,7 +236,25 @@ active binding. Switching A to B preserves A's history but makes it inapplicable
|
|
|
228
236
|
switching back to byte-identical A at the same commit reuses its record. Refresh
|
|
229
237
|
of only WORK does not invalidate a REVIEW record.
|
|
230
238
|
|
|
231
|
-
|
|
239
|
+
The first implementation review for a REVIEW binding uses the full branch diff.
|
|
240
|
+
A later one may use the nearest same-binding satisfying record that is a safe
|
|
241
|
+
ancestor of `HEAD`, reviewing only its committed delta while retaining staged and
|
|
242
|
+
unstaged sections. Git errors, malformed or other-binding records, non-ancestors,
|
|
243
|
+
and any merge on the selected ancestry path fail open to the full branch diff.
|
|
244
|
+
The record is only an input baseline: every completed review still writes the
|
|
245
|
+
normal exact-`HEAD`, binding-aware receipt.
|
|
246
|
+
|
|
247
|
+
PR-comment cycles persist separately at
|
|
248
|
+
`.wade/review-cycles/review-cycle@<issue>.json`, rather than in immutable
|
|
249
|
+
`.wade/session/` methodology snapshots. A cycle captures the open PR identity,
|
|
250
|
+
the pre-edit baseline, and normalized fetched feedback. Starting or resuming a
|
|
251
|
+
cycle preserves its baseline and retains prior feedback while adding newly
|
|
252
|
+
fetched feedback; successful `review-pr-comments-session done` removes the
|
|
253
|
+
context. The closing review uses that accumulated feedback plus the cycle delta
|
|
254
|
+
only after identity, ancestry, and merge-path validation; unreadable, mismatched,
|
|
255
|
+
malformed, or unsafe state falls back to a full diff.
|
|
256
|
+
|
|
257
|
+
Manifest, review, review-cycle, and documentation-gate state uses descriptor-relative,
|
|
232
258
|
no-follow filesystem operations. Unsafe/malformed/unreadable state is absent for
|
|
233
259
|
gate purposes and fails toward re-review, never toward completion. SHA marker
|
|
234
260
|
files are never accepted as review evidence. The completion classifier also
|
|
@@ -686,6 +686,18 @@ def diff_between_checked(repo_root: Path, base: str, head: str) -> str:
|
|
|
686
686
|
return _run_git("diff", f"{base}...{head}", cwd=repo_root).stdout
|
|
687
687
|
|
|
688
688
|
|
|
689
|
+
def has_merge_commit_between(repo_root: Path, base: str, head: str) -> bool:
|
|
690
|
+
"""Return whether the ancestry path from *base* to *head* contains a merge.
|
|
691
|
+
|
|
692
|
+
Callers first establish that ``base`` is an ancestor of ``head``. Keeping
|
|
693
|
+
this query in the git layer lets review-input selection fail open when git
|
|
694
|
+
cannot establish a trustworthy incremental lineage.
|
|
695
|
+
"""
|
|
696
|
+
|
|
697
|
+
result = _run_git("rev-list", "--merges", f"{base}..{head}", cwd=repo_root)
|
|
698
|
+
return bool(result.stdout.strip())
|
|
699
|
+
|
|
700
|
+
|
|
689
701
|
def diff_worktree(repo_root: Path, *, staged: bool = False) -> str:
|
|
690
702
|
"""Return ``git diff`` output for the working tree (or the staged index).
|
|
691
703
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Persisted PR-comment review-cycle context."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
9
|
+
|
|
10
|
+
REVIEW_CYCLE_SCHEMA_VERSION = 1
|
|
11
|
+
# A persisted context includes JSON syntax and other fields, so its actual
|
|
12
|
+
# serialized byte length remains the authoritative limit.
|
|
13
|
+
MAX_REVIEW_CYCLE_PAYLOAD_BYTES = 256 * 1024
|
|
14
|
+
MAX_REVIEW_FEEDBACK_CHARS = MAX_REVIEW_CYCLE_PAYLOAD_BYTES
|
|
15
|
+
_COMMIT_RE = re.compile(r"^[0-9a-f]{7,64}$")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ReviewCycleContext(BaseModel, frozen=True):
|
|
19
|
+
"""The immutable baseline and mutable feedback snapshot for one PR cycle."""
|
|
20
|
+
|
|
21
|
+
schema_version: int = Field(default=REVIEW_CYCLE_SCHEMA_VERSION)
|
|
22
|
+
issue_number: str
|
|
23
|
+
pr_number: int = Field(gt=0)
|
|
24
|
+
baseline_commit: str
|
|
25
|
+
feedback: str = Field(min_length=1, max_length=MAX_REVIEW_FEEDBACK_CHARS)
|
|
26
|
+
|
|
27
|
+
@field_validator("schema_version")
|
|
28
|
+
@classmethod
|
|
29
|
+
def _schema(cls, value: int) -> int:
|
|
30
|
+
if value != REVIEW_CYCLE_SCHEMA_VERSION:
|
|
31
|
+
raise ValueError(f"Unsupported review-cycle schema {value}")
|
|
32
|
+
return value
|
|
33
|
+
|
|
34
|
+
@field_validator("issue_number")
|
|
35
|
+
@classmethod
|
|
36
|
+
def _issue_number(cls, value: str) -> str:
|
|
37
|
+
normalized = value.strip().lstrip("#")
|
|
38
|
+
if not normalized.isdigit() or int(normalized) <= 0:
|
|
39
|
+
raise ValueError("Review-cycle issue number must be positive")
|
|
40
|
+
return str(int(normalized))
|
|
41
|
+
|
|
42
|
+
@field_validator("baseline_commit")
|
|
43
|
+
@classmethod
|
|
44
|
+
def _commit(cls, value: str) -> str:
|
|
45
|
+
if not _COMMIT_RE.fullmatch(value):
|
|
46
|
+
raise ValueError("Review-cycle baseline must be a lowercase hexadecimal git object id")
|
|
47
|
+
return value
|
|
48
|
+
|
|
49
|
+
@field_validator("feedback")
|
|
50
|
+
@classmethod
|
|
51
|
+
def _feedback(cls, value: str) -> str:
|
|
52
|
+
normalized = value.replace("\r\n", "\n").replace("\r", "\n").strip()
|
|
53
|
+
if not normalized:
|
|
54
|
+
raise ValueError("Review-cycle feedback cannot be empty")
|
|
55
|
+
return normalized
|
|
56
|
+
|
|
57
|
+
def serialized_payload(self) -> bytes:
|
|
58
|
+
"""Return the exact UTF-8 state-file representation for this context."""
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
json.dumps(self.model_dump(mode="json"), indent=2, sort_keys=True, ensure_ascii=True)
|
|
62
|
+
+ "\n"
|
|
63
|
+
).encode("utf-8")
|
|
64
|
+
|
|
65
|
+
@model_validator(mode="after")
|
|
66
|
+
def _serialized_payload_fits_state_file(self) -> ReviewCycleContext:
|
|
67
|
+
if len(self.serialized_payload()) > MAX_REVIEW_CYCLE_PAYLOAD_BYTES:
|
|
68
|
+
raise ValueError("Review-cycle serialized payload exceeds the 256 KiB limit")
|
|
69
|
+
return self
|
|
@@ -178,7 +178,7 @@ SESSION_DEFINITIONS: dict[SessionKind, SessionDefinition] = {
|
|
|
178
178
|
kind=SessionKind.IMPLEMENTATION,
|
|
179
179
|
ai_command=AICommandKey.IMPLEMENT,
|
|
180
180
|
workflow_template="implementation.md",
|
|
181
|
-
workflow_revision=
|
|
181
|
+
workflow_revision=2,
|
|
182
182
|
launch_prompt="implement-context.md",
|
|
183
183
|
session_phase=SessionPhase.IMPLEMENT,
|
|
184
184
|
steps=(
|
|
@@ -187,9 +187,9 @@ SESSION_DEFINITIONS: dict[SessionKind, SessionDefinition] = {
|
|
|
187
187
|
"understand-plan",
|
|
188
188
|
"implement",
|
|
189
189
|
"verify",
|
|
190
|
-
"method-review",
|
|
191
190
|
"documentation",
|
|
192
191
|
"knowledge",
|
|
192
|
+
"method-review",
|
|
193
193
|
"pr-summary",
|
|
194
194
|
"sync",
|
|
195
195
|
"done",
|
|
@@ -214,7 +214,7 @@ SESSION_DEFINITIONS: dict[SessionKind, SessionDefinition] = {
|
|
|
214
214
|
kind=SessionKind.REVIEW_PR_COMMENTS,
|
|
215
215
|
ai_command=AICommandKey.REVIEW_PR_COMMENTS,
|
|
216
216
|
workflow_template="review-pr-comments.md",
|
|
217
|
-
workflow_revision=
|
|
217
|
+
workflow_revision=2,
|
|
218
218
|
launch_prompt="review-pr-comments.md",
|
|
219
219
|
session_phase=SessionPhase.REVIEW,
|
|
220
220
|
steps=(
|
|
@@ -223,9 +223,9 @@ SESSION_DEFINITIONS: dict[SessionKind, SessionDefinition] = {
|
|
|
223
223
|
"verify-feedback",
|
|
224
224
|
"address-feedback",
|
|
225
225
|
"verify",
|
|
226
|
-
"method-review",
|
|
227
226
|
"documentation",
|
|
228
227
|
"knowledge",
|
|
228
|
+
"method-review",
|
|
229
229
|
"pr-summary",
|
|
230
230
|
"sync",
|
|
231
231
|
"resolve-threads",
|
|
@@ -46,6 +46,7 @@ from wade.services.implementation_service.lifecycle import (
|
|
|
46
46
|
_strip_summary_section,
|
|
47
47
|
)
|
|
48
48
|
from wade.services.implementation_service.usage_tracking import IMPL_USAGE_MARKER_START
|
|
49
|
+
from wade.services.review_cycle_service import clear_review_cycle
|
|
49
50
|
from wade.services.review_record_service import (
|
|
50
51
|
count_binding_passes,
|
|
51
52
|
has_other_satisfying_binding,
|
|
@@ -387,6 +388,18 @@ def done(
|
|
|
387
388
|
# block + skip-worktree still in place) so the user can fix and re-run.
|
|
388
389
|
return False
|
|
389
390
|
|
|
391
|
+
if session_type == "review-pr-comments" and not clear_review_cycle(
|
|
392
|
+
worktree_root, issue_number=issue_number
|
|
393
|
+
):
|
|
394
|
+
# The PR may already be finalized, but completion remains retryable
|
|
395
|
+
# until the active review-cycle state is gone or durably invalidated.
|
|
396
|
+
# Otherwise a later feedback cycle could inherit an obsolete baseline.
|
|
397
|
+
console.error(
|
|
398
|
+
"Could not clear the completed PR-comment review-cycle state safely; "
|
|
399
|
+
"restore filesystem access and re-run done."
|
|
400
|
+
)
|
|
401
|
+
return False
|
|
402
|
+
|
|
390
403
|
# Success only: strip the worktree gitignore block and restore .gitignore
|
|
391
404
|
# visibility now that there is nothing left to retry. The PR is already
|
|
392
405
|
# updated at this point, so a filesystem cleanup failure (read-only file,
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Safe PR-comment review-cycle state outside immutable session bundles."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from pydantic import ValidationError
|
|
10
|
+
|
|
11
|
+
from wade.models.review_cycle import ReviewCycleContext
|
|
12
|
+
from wade.utils.safe_state import (
|
|
13
|
+
MAX_STATE_FILE_BYTES,
|
|
14
|
+
atomic_write_state_file,
|
|
15
|
+
delete_state_file,
|
|
16
|
+
read_state_file,
|
|
17
|
+
state_file_present,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
_DIRECTORIES = ("review-cycles",)
|
|
21
|
+
_INVALIDATION_DIRECTORIES = ("review-cycle-invalidations",)
|
|
22
|
+
_PREFIX = "review-cycle@"
|
|
23
|
+
_FEEDBACK_SECTION = re.compile(r"(?m)(?=^### )")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class ReviewCycleLookup:
|
|
28
|
+
"""A context lookup that preserves an unsafe-state diagnostic."""
|
|
29
|
+
|
|
30
|
+
context: ReviewCycleContext | None
|
|
31
|
+
diagnostic: str | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def review_cycle_filename(issue_number: str) -> str:
|
|
35
|
+
"""Return the one canonical context filename for an issue's active cycle."""
|
|
36
|
+
|
|
37
|
+
normalized = issue_number.strip().lstrip("#")
|
|
38
|
+
if not normalized.isdigit() or int(normalized) <= 0:
|
|
39
|
+
raise ValueError("Review-cycle issue number must be positive")
|
|
40
|
+
return f"{_PREFIX}{int(normalized)}.json"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def read_review_cycle(
|
|
44
|
+
root: Path,
|
|
45
|
+
*,
|
|
46
|
+
issue_number: str,
|
|
47
|
+
pr_number: int,
|
|
48
|
+
) -> ReviewCycleLookup:
|
|
49
|
+
"""Read a current identity-matched cycle, never trusting malformed state."""
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
filename = review_cycle_filename(issue_number)
|
|
53
|
+
except ValueError as exc:
|
|
54
|
+
return ReviewCycleLookup(None, str(exc))
|
|
55
|
+
if state_file_present(root, _INVALIDATION_DIRECTORIES, filename):
|
|
56
|
+
return ReviewCycleLookup(
|
|
57
|
+
None,
|
|
58
|
+
"PR-comment review-cycle state was invalidated after an unsafe update",
|
|
59
|
+
)
|
|
60
|
+
raw = read_state_file(root, _DIRECTORIES, filename)
|
|
61
|
+
if raw is None:
|
|
62
|
+
if state_file_present(root, _DIRECTORIES, filename):
|
|
63
|
+
return ReviewCycleLookup(
|
|
64
|
+
None,
|
|
65
|
+
"PR-comment review-cycle state is unreadable or unsafe",
|
|
66
|
+
)
|
|
67
|
+
return ReviewCycleLookup(None)
|
|
68
|
+
try:
|
|
69
|
+
context = ReviewCycleContext.model_validate_json(raw)
|
|
70
|
+
except (ValidationError, ValueError):
|
|
71
|
+
return ReviewCycleLookup(None, "PR-comment review-cycle state is malformed")
|
|
72
|
+
if context.issue_number != str(int(issue_number.lstrip("#"))):
|
|
73
|
+
return ReviewCycleLookup(None, "PR-comment review-cycle issue identity does not match")
|
|
74
|
+
if context.pr_number != pr_number:
|
|
75
|
+
return ReviewCycleLookup(None, "PR-comment review-cycle PR identity does not match")
|
|
76
|
+
return ReviewCycleLookup(context)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _feedback_sections(feedback: str) -> tuple[str, ...]:
|
|
80
|
+
"""Return individually rendered review entries from a feedback snapshot."""
|
|
81
|
+
|
|
82
|
+
return tuple(
|
|
83
|
+
section.strip() for section in _FEEDBACK_SECTION.split(feedback)[1:] if section.strip()
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _merge_feedback(existing_feedback: str, refreshed_feedback: str) -> str:
|
|
88
|
+
"""Retain feedback already addressed while adding new feedback from a refresh."""
|
|
89
|
+
|
|
90
|
+
if refreshed_feedback in existing_feedback:
|
|
91
|
+
return existing_feedback
|
|
92
|
+
|
|
93
|
+
refreshed_sections = _feedback_sections(refreshed_feedback)
|
|
94
|
+
if refreshed_sections:
|
|
95
|
+
existing_sections = set(_feedback_sections(existing_feedback))
|
|
96
|
+
additions = [section for section in refreshed_sections if section not in existing_sections]
|
|
97
|
+
if not additions:
|
|
98
|
+
return existing_feedback
|
|
99
|
+
return f"{existing_feedback}\n\n## Feedback added during refresh\n\n" + "\n\n".join(
|
|
100
|
+
additions
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return f"{existing_feedback}\n\n{refreshed_feedback}"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def initialize_or_refresh_review_cycle(
|
|
107
|
+
root: Path,
|
|
108
|
+
*,
|
|
109
|
+
issue_number: str,
|
|
110
|
+
pr_number: int,
|
|
111
|
+
baseline_commit: str,
|
|
112
|
+
feedback: str,
|
|
113
|
+
) -> ReviewCycleLookup:
|
|
114
|
+
"""Create a cycle or retain its baseline and all feedback across refreshes."""
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
filename = review_cycle_filename(issue_number)
|
|
118
|
+
except ValueError as exc:
|
|
119
|
+
return ReviewCycleLookup(None, f"Could not validate PR-comment review-cycle state: {exc}")
|
|
120
|
+
invalidation = _discard_invalidated_review_cycle(root, filename)
|
|
121
|
+
if invalidation is not None:
|
|
122
|
+
return ReviewCycleLookup(None, invalidation)
|
|
123
|
+
|
|
124
|
+
existing = read_review_cycle(root, issue_number=issue_number, pr_number=pr_number)
|
|
125
|
+
if existing.diagnostic is not None:
|
|
126
|
+
return existing
|
|
127
|
+
try:
|
|
128
|
+
context = (
|
|
129
|
+
ReviewCycleContext(
|
|
130
|
+
**(
|
|
131
|
+
existing.context.model_dump()
|
|
132
|
+
| {"feedback": _merge_feedback(existing.context.feedback, feedback)}
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
if existing.context is not None
|
|
136
|
+
else ReviewCycleContext(
|
|
137
|
+
issue_number=issue_number,
|
|
138
|
+
pr_number=pr_number,
|
|
139
|
+
baseline_commit=baseline_commit,
|
|
140
|
+
feedback=feedback,
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
except (ValidationError, ValueError) as exc:
|
|
144
|
+
return ReviewCycleLookup(None, f"Could not validate PR-comment review-cycle state: {exc}")
|
|
145
|
+
payload = context.serialized_payload()
|
|
146
|
+
if len(payload) > MAX_STATE_FILE_BYTES:
|
|
147
|
+
return ReviewCycleLookup(
|
|
148
|
+
None,
|
|
149
|
+
"Could not persist PR-comment review-cycle state: serialized payload exceeds "
|
|
150
|
+
"the 256 KiB limit",
|
|
151
|
+
)
|
|
152
|
+
if not atomic_write_state_file(root, _DIRECTORIES, filename, payload):
|
|
153
|
+
if clear_review_cycle(root, issue_number=issue_number):
|
|
154
|
+
return ReviewCycleLookup(
|
|
155
|
+
None,
|
|
156
|
+
"Could not persist PR-comment review-cycle state safely; the prior state was "
|
|
157
|
+
"invalidated",
|
|
158
|
+
)
|
|
159
|
+
return ReviewCycleLookup(None, "Could not persist PR-comment review-cycle state safely")
|
|
160
|
+
return ReviewCycleLookup(context)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def clear_review_cycle(root: Path, *, issue_number: str) -> bool:
|
|
164
|
+
"""Remove an active cycle, or durably invalidate it when removal is unsafe."""
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
filename = review_cycle_filename(issue_number)
|
|
168
|
+
except ValueError:
|
|
169
|
+
return False
|
|
170
|
+
if not state_file_present(root, _DIRECTORIES, filename):
|
|
171
|
+
return True
|
|
172
|
+
if delete_state_file(root, _DIRECTORIES, filename):
|
|
173
|
+
return True
|
|
174
|
+
# A successful marker makes every later read fail closed, even if the old
|
|
175
|
+
# regular JSON file remains readable after a failed deletion. It lives in
|
|
176
|
+
# a sibling directory so a read-only review-cycles directory can still be
|
|
177
|
+
# invalidated through its writable .wade parent.
|
|
178
|
+
return atomic_write_state_file(root, _INVALIDATION_DIRECTORIES, filename, b"")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _discard_invalidated_review_cycle(root: Path, filename: str) -> str | None:
|
|
182
|
+
"""Clear a previous invalidation before beginning a fresh feedback cycle."""
|
|
183
|
+
|
|
184
|
+
if not state_file_present(root, _INVALIDATION_DIRECTORIES, filename):
|
|
185
|
+
return None
|
|
186
|
+
if state_file_present(root, _DIRECTORIES, filename) and not delete_state_file(
|
|
187
|
+
root, _DIRECTORIES, filename
|
|
188
|
+
):
|
|
189
|
+
return "Could not discard invalidated PR-comment review-cycle state safely"
|
|
190
|
+
if not delete_state_file(root, _INVALIDATION_DIRECTORIES, filename):
|
|
191
|
+
return "Could not clear PR-comment review-cycle invalidation safely"
|
|
192
|
+
return None
|