specfuse-loop 0.26.0__tar.gz → 0.27.0__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.
- {specfuse_loop-0.26.0/specfuse_loop.egg-info → specfuse_loop-0.27.0}/PKG-INFO +1 -1
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/pyproject.toml +1 -1
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/bugs.py +37 -13
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/run.py +43 -21
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/severity_backfill.py +4 -4
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/state.py +3 -1
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/bug_lane_run.py +180 -19
- specfuse_loop-0.27.0/specfuse/loop/data/VERSION +1 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/escalation.py +27 -8
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/loop.py +102 -1
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/red_on_base.py +94 -13
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/triage.py +42 -5
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/autofix_invoke.py +11 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse_loop.egg-info/SOURCES.txt +15 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_escalation_on_issue.py +9 -2
- specfuse_loop-0.27.0/tests/test_bug_lane_no_checks_pending.py +99 -0
- specfuse_loop-0.27.0/tests/test_bug_lane_spend.py +160 -0
- specfuse_loop-0.27.0/tests/test_bug_lane_superseded.py +216 -0
- specfuse_loop-0.27.0/tests/test_fix_bug_cwd_worktree.py +81 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_broad_run.py +10 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_eval_intermediate_wiring.py +4 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_eval_terminal_wiring.py +13 -0
- specfuse_loop-0.27.0/tests/test_headless_no_background_tasks.py +57 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lifecycle_integration.py +10 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_max_open_prs.py +6 -5
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_amendment_e2e.py +10 -0
- specfuse_loop-0.27.0/tests/test_promoted_issue_not_redispatched.py +102 -0
- specfuse_loop-0.27.0/tests/test_red_on_base_fetch_and_reason.py +173 -0
- specfuse_loop-0.27.0/tests/test_red_on_base_overlay.py +164 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_red_on_base_probe.py +22 -18
- specfuse_loop-0.27.0/tests/test_red_on_base_real_runner.py +105 -0
- specfuse_loop-0.27.0/tests/test_reescalation_posts_comment.py +104 -0
- specfuse_loop-0.27.0/tests/test_result_block_near_miss.py +54 -0
- specfuse_loop-0.27.0/tests/test_result_block_scalar.py +72 -0
- specfuse_loop-0.27.0/tests/test_severity_backfill_bugs_only.py +57 -0
- specfuse_loop-0.27.0/tests/test_terminal_auto_close_changelog.py +97 -0
- specfuse_loop-0.27.0/tests/test_unreadable_pr_listing_holds_pr_work.py +91 -0
- specfuse_loop-0.26.0/specfuse/loop/data/VERSION +0 -1
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/LICENSE +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/NOTICE +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/README.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/setup.cfg +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/__init__.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/budget.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/diagnose_invoke.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/drafting_answers.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/drafting_invoke.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/drafting_questions.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/driver_command.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/driver_invoke.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/invoke.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/monitoring_read.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/__init__.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/answers.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/feature.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/findings_autofix.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/findings_diagnose.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/triage.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/queue_read.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/repo_detect.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/triage_invoke.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/worktree.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/_filelock.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/_wu_sections.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/agent_policy.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/arm_eval.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/arm_sweep.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/arm_txn.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/bug_lane.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/bug_lane_state.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/build_provenance.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/caller_check.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/changelog.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/closing_requirements.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/cost.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/criteria_state.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/glossary.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/methodology.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/gitignore.snippet +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/monitoring.yml.example +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/close-discipline.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/human-output.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/operator-escalation.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/planning-discipline.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/verification-discipline.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules-local/README.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/event.schema.json +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/decisions_format.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/driver_edit.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/events_stats.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/gate_commands.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/heartbeat.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/judge.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/labels.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/learnings_distil.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/learnings_query.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/lint_closing.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/lint_monitoring.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/lint_plan.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/lint_roadmap.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/notify.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/notify_escalation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/notify_sla.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/plan_baseline.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/policy_proposals.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/policy_review.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/prerun.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/prerun_capture.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/promotion.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/rearm_migration.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/replay_spin.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/roadmap_archive.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/scaffold.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/__init__.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/adapters.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/artifact.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/autofix.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/autofix_run.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/autofix_state.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/cli.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/diagnose_cli.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/diagnosis.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/fingerprint.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/issues.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/providers/__init__.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/providers/_azure_auth.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/providers/azure_app_insights.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/providers/azure_service_bus.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/redaction.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/schedule.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_abandon_feature_skill.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_abandoned_work_reported.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_abandoned_wu_strands_gate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_budget.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_bug_selection_dedup.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_default_features_root.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_driver_command.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_driver_invoke.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_driver_restart.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_escalation_run_state.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_findings_seam.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_invoke_timeout.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_invoke_usage.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_item_worktree.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_lock.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_policy_key_ownership.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_policy_queue.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_policy_schema.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_policy_triage_dial.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_answers.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_bugs.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_feature.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_findings_autofix.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_findings_diagnose.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_isolation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_triage.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_queue_read.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_relays_driver_output.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_repo_detect.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_run.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_run_progress.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_seam.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_snapshot_refresh.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_state.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_answer_escalation_skill.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_app_insights_adapters.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_archive_ref_reconciliation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_eval.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_eval_lint_class.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_eval_wiring.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_sweep.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_sweep_gate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_txn.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_wiring.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_artifact_redaction.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attempt_note_untracked_files.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attempt_outcome_contract.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attention_nonwriting_guard.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attention_silence_section.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attention_skill_structure.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attribution_halt_claims.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autoclose_deferral_visibility.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autoclose_gate_note.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autoclose_stub_states_what_passed.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autofix_invoke.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autofix_live.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autofix_predicate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autofix_run.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autofix_state.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autosync_no_cwd_leak.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_azure_credential_resolution.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_backend.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_baseline_escalation_message.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_baseline_persistence.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_baseline_probe.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_baseline_provenance.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_baseline_tree_hash_key.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bash_routing.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bats_suites_gated.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_binary_verdict.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_binding_block_advisory.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_binding_block_allocation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_binding_block_budget.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_blind_run_is_not_drained.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bookkeeping_no_verify.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_brief_covers_every_unit_escalation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_ci_pending.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_guardrails.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_label_nonfatal.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_labels_registered.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_min_severity.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_policy_contract.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_pr_number_carried.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_preflight_and_dial.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_run.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_state.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_test_paths.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_build_provenance.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_caller_check_ratchet.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_carried_green_invalidated_by_diff.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_carry_accounting_close_and_judge.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_changed_file_test_selection.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_changelog_append_only.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_changelog_collection.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_changelog_release_wiring.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_changelog_schema.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ci_conclusion_wait.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ci_runs_every_gate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_claude_resolution.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_close_b_pending_learnings.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_close_records_feature_oracle.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_close_wu_self_reference.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_guard_contracts.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_guard_prediction.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_learnings_destination.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_requirements.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_sessions_on_sonnet.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_coauthor_trailer_allowlist.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_conditional_reflection.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_console_encoding.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_convergent_iteration.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_convergent_wu_lint.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_correlation_id_override.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_correlation_id_two_surfaces.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_cost_lifetime.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_criteria_per_gate_lint.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_criteria_state.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_criteria_worklist.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_decision_citation_lint.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_decision_followups_discharged.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_decision_override_lint.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_decisions_format.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_default_severity_aliases.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_degraded_oracle.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_deliverable_missing_note.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_derive_agent_policy_review_mode.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_derive_agent_policy_skill.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_derive_monitoring_discovery.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_derive_monitoring_skill_registration.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_design_for_diagnosis_rule.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_deterministic_refusal_repeat.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_diagnose_headless.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_diagnose_issue_skill.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_diagnosis.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_diagnosis_roundtrip_live.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_dispatch_model_from_fix_scope.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_dispatch_preamble_rules_pointer.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_dispatch_skeleton.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_dispatch_skills_index.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_distilled_accept_step.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_doctor_command_preflight.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_done_feature_gates.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_draft_feature_answers_mode.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_drafting_answer_gate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_drafting_invoke.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_drafting_question_issue.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_drafting_questions.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_drafting_reply_shape.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_edit_detection.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_event_registry_covers_emitters.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_module_surface.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_restart_halt_surfaced.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_restart_halt_wiring.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_restart_hold.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_staleness_gate_summary.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_staleness_warning.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_dry_run_no_branch_mutation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ensure_feature_branch_base.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_escalation_assignee_policy.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_escalation_contract.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_escalation_crash_containment.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_escalation_dedup_search.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_escalation_emit.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_event_gate_full_envelope.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_events_redaction.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_events_stats.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_failure_artifact_model.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_failure_signature_fail_prefix.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_failure_signature_names_the_test.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_failure_signature_skips_log_noise.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_branch_auto_sync.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_branch_squash_merge.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_branch_stale_base.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_id_resolution.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_oracle_e2e.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_provider_drafting_dispatch.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_review_accumulation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_filelock_portable.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_files_changed_auto_repair.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_find_pr_no_search_index.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fingerprint.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_bug_diff_self_check.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_bug_headless.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_bug_reads_comments.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_unit_insertion_bookkeeping.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_unit_insertion_e2e.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_unit_insertion_refused.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fold_contract_documented.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_followups_artifact.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_frontmatter_scalar_quoting.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_budget.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_budget_post_dispatch.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_commands_needs.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_eval_repo_flag.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_report_verdict.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gitleaks_pinning.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_groom_backlog_skill.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_guard_refusal_failure_class.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_guard_refusal_failure_excerpt.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_guard_repair_e2e.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_guard_repair_prompt.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_heartbeat.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_heartbeat_adapter.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_human_output_rule.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_human_work_unit.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_hygiene_boundary_guidance.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_hyphenated_gate_name.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ignored_cleanup_before_probe.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_init_integration.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_installed_copy_driver_e2e.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_interpreter_normalization.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_close_path.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_cost.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_cost_fold_committed.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_module.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_path_registry.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_sees_dropped_produces.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_terminal_flip_state.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judged_event_points_at_findings.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_label_description_length.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_label_provisioning_runner_contract.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_label_registry.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_label_registry_covers_consumers.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_language_aware_narrow_selection.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lazy_baseline_e2e.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_curate_vendored_promote.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_distil_cli.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_query.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_staging.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_suggest_signature_filter.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_weights.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lifetime_accounting.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_ac_observable.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_boundary_consistency.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_boundary_extraction.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_close_verdict_not_predecided.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_closing.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_closing_criteria.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_closing_criteria_pristine.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_covers_load_wu_invariants.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_feature_oracle_declared.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_gate_proportionality.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_monitoring.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_plan_contract_fields.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_plan_relative_import.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_plan_verdict_exempt.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_planning_sections.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_produces_satisfiability.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_roadmap.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_verification_reference.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_live_tests_opt_in.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_load_bearing_close_lint.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_attempt_evidence.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_criteria_skeleton.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_criteria_survival.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_gate_needs.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_worklist_injection.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_make_repo_gpgsign_isolation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_marker_dual_shape.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_maven_failure_excerpt.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_max_attempts_configurable.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitor_cli.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitor_issue_lifecycle.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitor_runner_surfaces.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitoring_cron_dialect.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitoring_example.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitoring_fenced_blocks.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitoring_seed.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_narrow_fallback_warning.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_needs_decision_outcome.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_no_pytest_subprocess.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_notify.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_notify_escalation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_notify_sla.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_operator_escalation_rule.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_oracle_set_declared.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_oracle_unrunnable_attribution.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_per_run_item_cap_disclosure.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pick_feature_skill.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pin_honesty_and_integrity.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pin_marker_not_inherited.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pin_marker_spawn_sites.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_plan_baseline.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_planning_cost_floor.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_plugin_prose_cross_refs.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_plugin_qualified_dispatch.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_policy_budgets_applied.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_policy_proposals.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_policy_review.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pr_checks_json_field.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pr_reference_in_title.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prep_halt_operator_surface.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prepare_feature.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prepare_scaffold_sync.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prerun_capture.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prerun_oracles.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prerun_wiring.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produced_document_verdict_guard.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_bracket_literal.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_bracket_wildcard.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_in_diff.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_justification.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_recursive_glob.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_repair_note.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_semantics_unified.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_shape.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_progress_forward_note.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_progress_lines_end_to_end.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_promotion_ledger.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_provider_escalation_traces.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_provision_labels.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_queue_stalled_adapter.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_rearm_fold_marker.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_rearm_migration.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_reclose_carries_narrow_greens_e2e.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_red_baseline_never_reused.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_red_on_base_guardrail.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_redaction_windows_home.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_reflection_required_signals.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replan_end_to_end.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replan_event_emission.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replan_note_collision.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replan_trigger.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replan_turn_contract.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replay_spin.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replay_spin_failing_sets.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_required_checks.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_resolve_base.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_restart_cap_counts_progress.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_result_block_audience.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_resume_command_build.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_retain_on_guard_refusal_docs.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_archive_cli.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_archive_shim.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_archive_skill_delegates.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_link_gate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ruff_verdict_lines.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_rule_import_convergence.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_run_level_failure_breaker.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_data_in_sync.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_doc_hygiene.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_docs.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_init.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_label_provisioning.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_resources.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_skew_halts.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_wiring.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_schedule_dialect.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_seed_documents_prerun_keys.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_service_bus_dlq_adapter.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_session_env_preflight.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_aliases.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_backfill_apply.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_backfill_end_to_end.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_backfill_limit.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_backfill_marker.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_backfill_run.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_label_projection.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_reconciliation.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_rubric.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_shipped_templates_parse.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_skill_discovery_links.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_skills_vendored_in_sync.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_spec_work_unit_rules.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_spinning_rearm_gate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_spinning_repeat_same_failing_set.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_spinout_brief_end_to_end.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_spinout_brief_replan_option.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_stop_rationale_captured.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_stranded_halt_cause.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_surefire_run_level_signature.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_surefire_verdict_lines.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_terminal_gate_message.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_tiered_verification_e2e.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_timeout_kill_portable.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_apply.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_projects_severity_label.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_severity_classify.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_severity_end_to_end.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_severity_write.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_skill_contract.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_skips_agent_escalations.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_tsc_verdict_lines.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_umbrella_version_resolution.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_unterminated_frontmatter.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_untracked_file_absorption.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_validate_event_driver_types.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_windows_gate_exec.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_worktree_restore.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_wu_execution_metadata.py +0 -0
- {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_wu_template_renders_lintable.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfuse-loop"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.27.0"
|
|
8
8
|
description = "Local-first executor for the Specfuse Plan + Work Unit gate-cycle methodology."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -16,7 +16,7 @@ import subprocess
|
|
|
16
16
|
import time
|
|
17
17
|
from typing import Any, Callable, Optional, Sequence
|
|
18
18
|
|
|
19
|
-
from specfuse.agent.invoke import resolve_item_timeout_seconds
|
|
19
|
+
from specfuse.agent.invoke import resolve_item_timeout_seconds, usage_spend
|
|
20
20
|
from specfuse.agent.run import (
|
|
21
21
|
KIND_BUG,
|
|
22
22
|
STATUS_COMPLETED,
|
|
@@ -41,6 +41,7 @@ from specfuse.loop.bug_lane_run import (
|
|
|
41
41
|
OUTCOME_DECLINED,
|
|
42
42
|
OUTCOME_MERGED,
|
|
43
43
|
OUTCOME_REFUSED,
|
|
44
|
+
OUTCOME_SUPERSEDED,
|
|
44
45
|
REASON_PR_NOT_FOUND,
|
|
45
46
|
pr_closes_issue,
|
|
46
47
|
run_bug_lane,
|
|
@@ -63,7 +64,9 @@ _FIX_BUG_STOPPED_OUTCOMES = (OUTCOME_REFUSED, OUTCOME_COULD_NOT_PROCEED)
|
|
|
63
64
|
#: "fix" its own "PR was declined by the merge guardrails" report.
|
|
64
65
|
_HUMAN_OWNED_LABELS = frozenset({NEEDS_HUMAN_LABEL, "blocked-wu"})
|
|
65
66
|
|
|
66
|
-
def _bounded_runner(
|
|
67
|
+
def _bounded_runner(
|
|
68
|
+
runner: Callable, timeout_seconds: float, working_dir: str = "."
|
|
69
|
+
) -> Callable:
|
|
67
70
|
"""Wrap *runner* so the headless `claude` dispatch inside `run_bug_lane`
|
|
68
71
|
(`specfuse.loop.bug_lane_run:595`, off-limits to this WU) gets a real
|
|
69
72
|
wall-clock timeout without editing that module.
|
|
@@ -72,12 +75,24 @@ def _bounded_runner(runner: Callable, timeout_seconds: float) -> Callable:
|
|
|
72
75
|
`claude` -- `run_bug_lane` reuses this same runner for every `gh` read
|
|
73
76
|
and write along the way, and those must not inherit an item-scale
|
|
74
77
|
deadline meant for one long-running session.
|
|
78
|
+
|
|
79
|
+
The same call is started with `cwd=working_dir` (#3420). The item's
|
|
80
|
+
worktree used to reach the session only as a `Working directory:` line in
|
|
81
|
+
its prompt, so a session that followed `/fix-bug`'s own branch workflow
|
|
82
|
+
from where it was launched branched and committed in the operator's
|
|
83
|
+
primary checkout. The process boundary is what isolates it, not the text.
|
|
84
|
+
|
|
85
|
+
Any other keyword a caller passes is forwarded untouched (#3452). The
|
|
86
|
+
red-on-base probe runs its command with `cwd=<base worktree>`; a wrapper
|
|
87
|
+
that accepted only `check` raised `TypeError`, which the probe's
|
|
88
|
+
fail-closed `except` turned into `red_on_base_unverified` on every PR.
|
|
75
89
|
"""
|
|
76
90
|
|
|
77
|
-
def wrapped(argv, check: bool = False):
|
|
78
|
-
kwargs
|
|
91
|
+
def wrapped(argv, check: bool = False, **kwargs):
|
|
92
|
+
kwargs["check"] = check
|
|
79
93
|
if argv and argv[0] == "claude":
|
|
80
94
|
kwargs["timeout"] = timeout_seconds
|
|
95
|
+
kwargs.setdefault("cwd", working_dir)
|
|
81
96
|
return runner(argv, **kwargs)
|
|
82
97
|
|
|
83
98
|
return wrapped
|
|
@@ -725,7 +740,7 @@ class BugsProvider:
|
|
|
725
740
|
started = time.monotonic()
|
|
726
741
|
try:
|
|
727
742
|
result = run_bug_lane(
|
|
728
|
-
_bounded_runner(self._runner, timeout_seconds),
|
|
743
|
+
_bounded_runner(self._runner, timeout_seconds, self._working_dir),
|
|
729
744
|
self._repo,
|
|
730
745
|
issue_number,
|
|
731
746
|
working_dir=self._working_dir,
|
|
@@ -738,9 +753,11 @@ class BugsProvider:
|
|
|
738
753
|
status=STATUS_ESCALATED,
|
|
739
754
|
detail=(
|
|
740
755
|
f"could_not_proceed: headless /fix-bug session timed out "
|
|
741
|
-
f"after {elapsed:.0f}s"
|
|
756
|
+
f"after {elapsed:.0f}s (its token spend is not counted)"
|
|
742
757
|
),
|
|
743
758
|
escalation=_timed_out_payload(issue_number, elapsed),
|
|
759
|
+
# A session cut off by the timeout printed no usage envelope,
|
|
760
|
+
# so its spend is unknown, not zero (#3419) -- said in `detail`.
|
|
744
761
|
spend=0,
|
|
745
762
|
)
|
|
746
763
|
|
|
@@ -750,17 +767,23 @@ class BugsProvider:
|
|
|
750
767
|
# and therefore unevaluable — the one point the proposal called out as
|
|
751
768
|
# mattering most.
|
|
752
769
|
profile_note = _dispatch_profile_note(result.dispatch_profile)
|
|
770
|
+
# #3419: what the headless `/fix-bug` session spent, on every outcome.
|
|
771
|
+
# Was a hard-coded 0, so `max_tokens` bounded triage alone.
|
|
772
|
+
spend = usage_spend(result.usage)
|
|
753
773
|
|
|
754
774
|
if result.outcome == OUTCOME_MERGED:
|
|
755
|
-
# `run_bug_lane` (`specfuse/loop/bug_lane_run.py`, T04's file) does
|
|
756
|
-
# not surface the headless `/fix-bug` session's usage envelope on
|
|
757
|
-
# `BugLaneResult` -- reporting the bug lane's real spend needs a
|
|
758
|
-
# change there, out of this WU's reach (`specfuse/loop/` is
|
|
759
|
-
# off-limits except what T01 declares). Explicit `spend=0` rather
|
|
760
|
-
# than a value this module cannot actually measure.
|
|
761
775
|
return ActionOutcome(
|
|
762
776
|
status=STATUS_COMPLETED,
|
|
763
|
-
detail=f"{result.reason or ''}{profile_note}", spend=
|
|
777
|
+
detail=f"{result.reason or ''}{profile_note}", spend=spend)
|
|
778
|
+
|
|
779
|
+
if result.outcome == OUTCOME_SUPERSEDED:
|
|
780
|
+
# #3447: the issue was closed elsewhere and the lane closed its own
|
|
781
|
+
# duplicate PR. Done, with nothing for a human to merge or decide.
|
|
782
|
+
return ActionOutcome(
|
|
783
|
+
status=STATUS_COMPLETED,
|
|
784
|
+
detail=f"{result.reason}; PR #{result.pr_number} closed{profile_note}",
|
|
785
|
+
spend=spend,
|
|
786
|
+
)
|
|
764
787
|
|
|
765
788
|
detail = result.reason if result.reason is not None else result.outcome
|
|
766
789
|
|
|
@@ -825,6 +848,7 @@ class BugsProvider:
|
|
|
825
848
|
detail=f"{detail}{profile_note}",
|
|
826
849
|
escalation=escalation,
|
|
827
850
|
environmental=not result.outcome_named,
|
|
851
|
+
spend=spend,
|
|
828
852
|
)
|
|
829
853
|
|
|
830
854
|
def reconcile(self, item: ActionItem, outcome: ActionOutcome) -> None:
|
|
@@ -389,15 +389,23 @@ def _resolve_bugs_preempt(policy_path: Optional[str]) -> bool:
|
|
|
389
389
|
return bugs.get("preempt") is True
|
|
390
390
|
|
|
391
391
|
|
|
392
|
-
def _default_runner(
|
|
392
|
+
def _default_runner(
|
|
393
|
+
argv: list,
|
|
394
|
+
check: bool = False,
|
|
395
|
+
timeout: Optional[float] = None,
|
|
396
|
+
cwd: Optional[str] = None,
|
|
397
|
+
):
|
|
393
398
|
"""The real subprocess runner. Forwards *timeout* to `subprocess.run`
|
|
394
399
|
(FEAT-2026-0108/T03) so a caller that bounds one call's wall-clock time --
|
|
395
400
|
`BugsProvider.execute`'s headless `/fix-bug` dispatch, the one call this
|
|
396
401
|
exists for -- gets a real `subprocess.TimeoutExpired` when the process
|
|
397
402
|
outruns it, rather than the session's own turn or wall limit being the
|
|
398
|
-
only thing that ever ends it (#3178).
|
|
403
|
+
only thing that ever ends it (#3178).
|
|
404
|
+
|
|
405
|
+
Forwards *cwd* for the same dispatch (#3420): the session is started in
|
|
406
|
+
the item's worktree, not the run's own directory."""
|
|
399
407
|
return subprocess.run(
|
|
400
|
-
argv, check=check, capture_output=True, text=True, timeout=timeout
|
|
408
|
+
argv, check=check, capture_output=True, text=True, timeout=timeout, cwd=cwd
|
|
401
409
|
)
|
|
402
410
|
|
|
403
411
|
|
|
@@ -855,17 +863,15 @@ def run_agent(
|
|
|
855
863
|
report=report,
|
|
856
864
|
)
|
|
857
865
|
|
|
858
|
-
# The PR listing failing is NOT a refusal:
|
|
859
|
-
#
|
|
860
|
-
#
|
|
861
|
-
#
|
|
862
|
-
#
|
|
863
|
-
|
|
864
|
-
if snapshot.prs_error:
|
|
866
|
+
# The PR listing failing is NOT a refusal (#3412): work that opens no
|
|
867
|
+
# PR still runs. Under a declared `max_open_prs`, the PR-opening kinds
|
|
868
|
+
# are held back in the dispatch loop below until a refresh reads the
|
|
869
|
+
# listing; without a cap there is nothing to enforce, and the run says
|
|
870
|
+
# only that the listing is unreadable.
|
|
871
|
+
if snapshot.prs_error and agent_policy.resolve_max_open_prs(policy_path) is None:
|
|
865
872
|
report(
|
|
866
|
-
"snapshot:
|
|
867
|
-
"
|
|
868
|
-
"measure against"
|
|
873
|
+
"snapshot: the PR listing could not be read — no "
|
|
874
|
+
"max_open_prs is declared, so nothing is held back"
|
|
869
875
|
)
|
|
870
876
|
|
|
871
877
|
budget_kwargs = {}
|
|
@@ -916,7 +922,9 @@ def run_agent(
|
|
|
916
922
|
#: only so the report fires on each transition rather than every pass:
|
|
917
923
|
#: `snapshot.prs` is re-read between items (#3338), so a PR merged
|
|
918
924
|
#: mid-run genuinely lifts this and the operator should see both edges.
|
|
919
|
-
|
|
925
|
+
# Why PR-opening kinds are held: "" (not held), "unreadable" or
|
|
926
|
+
# "cap" -- so each change of reason is reported once (#3412).
|
|
927
|
+
open_prs_suppressed = ""
|
|
920
928
|
#: (signature, consecutive count) for the run-level failure breaker
|
|
921
929
|
#: (#3343). Reset by an escalation with a different cause and by any
|
|
922
930
|
#: completed item — a run making progress is not in the state this
|
|
@@ -956,26 +964,40 @@ def run_agent(
|
|
|
956
964
|
snapshot_stale = False
|
|
957
965
|
|
|
958
966
|
# A failed PR listing leaves `snapshot.prs` empty with
|
|
959
|
-
# `prs_error` set
|
|
960
|
-
#
|
|
961
|
-
#
|
|
967
|
+
# `prs_error` set -- not zero open PRs, an unknown number (#3412).
|
|
968
|
+
# Reading it as zero turned the cap off for the run, exactly when
|
|
969
|
+
# the run could see least. Under a cap the PR-opening kinds wait,
|
|
970
|
+
# the way reaching the cap already makes them wait, until a refresh
|
|
971
|
+
# reads the listing. #3338's refresh carries a previously read
|
|
972
|
+
# listing forward, so `prs_error` stays set only while this run has
|
|
973
|
+
# never read one.
|
|
962
974
|
suppressed_kinds = frozenset()
|
|
963
|
-
if _open_pr_cap is not None and
|
|
975
|
+
if _open_pr_cap is not None and snapshot.prs_error:
|
|
976
|
+
suppressed_kinds = PR_OPENING_KINDS
|
|
977
|
+
if open_prs_suppressed != "unreadable":
|
|
978
|
+
report(
|
|
979
|
+
f"max_open_prs NOT measurable — the PR listing could "
|
|
980
|
+
f"not be read ({snapshot.prs_error}); not starting "
|
|
981
|
+
f"{', '.join(sorted(PR_OPENING_KINDS))} items until it "
|
|
982
|
+
f"can be"
|
|
983
|
+
)
|
|
984
|
+
open_prs_suppressed = "unreadable"
|
|
985
|
+
elif _open_pr_cap is not None and len(snapshot.prs) >= _open_pr_cap:
|
|
964
986
|
suppressed_kinds = PR_OPENING_KINDS
|
|
965
|
-
if
|
|
987
|
+
if open_prs_suppressed != "cap":
|
|
966
988
|
report(
|
|
967
989
|
f"max_open_prs reached — {len(snapshot.prs)} open PR(s) "
|
|
968
990
|
f"at a cap of {_open_pr_cap}; not starting "
|
|
969
991
|
f"{', '.join(sorted(PR_OPENING_KINDS))} items until one "
|
|
970
992
|
f"closes"
|
|
971
993
|
)
|
|
972
|
-
open_prs_suppressed =
|
|
994
|
+
open_prs_suppressed = "cap"
|
|
973
995
|
elif open_prs_suppressed:
|
|
974
996
|
report(
|
|
975
997
|
f"max_open_prs cleared — {len(snapshot.prs)} open PR(s) "
|
|
976
998
|
f"against a cap of {_open_pr_cap}"
|
|
977
999
|
)
|
|
978
|
-
open_prs_suppressed =
|
|
1000
|
+
open_prs_suppressed = ""
|
|
979
1001
|
|
|
980
1002
|
action, a, b = _select_next(
|
|
981
1003
|
providers,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Copyright 2026 Specfuse Contributors
|
|
2
2
|
# Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
3
|
-
"""`specfuse-backfill-severity` -- amend already-marked, severity-less
|
|
3
|
+
"""`specfuse-backfill-severity` -- amend already-marked, severity-less bugs
|
|
4
4
|
with a severity, for issues the normal triage path will never revisit
|
|
5
5
|
(FEAT-2026-0113).
|
|
6
6
|
|
|
@@ -15,7 +15,7 @@ script is one deliberate command away, not one argv typo on the binary an
|
|
|
15
15
|
unattended run uses.
|
|
16
16
|
|
|
17
17
|
`run_backfill` (T10) is the real run shape: `triage.list_severity_backfill_candidates`
|
|
18
|
-
(T08) selects the open, marked, severity-less
|
|
18
|
+
(T08) selects the open, marked, severity-less bugs; `labels.read_severity_rubric`
|
|
19
19
|
is read exactly once per run; each candidate is classified through
|
|
20
20
|
`triage_invoke.build_invocation` + `triage_invoke.classify_severity` -- the same
|
|
21
21
|
classifier a fresh triage uses, so a low-confidence or out-of-rubric answer fails
|
|
@@ -161,7 +161,7 @@ def run_backfill(
|
|
|
161
161
|
effort: str = "medium",
|
|
162
162
|
) -> dict:
|
|
163
163
|
"""Select, classify, and (only under `apply=True`) write severities for
|
|
164
|
-
every open, marked, severity-less
|
|
164
|
+
every open, marked, severity-less bug in `repo` -- the real run shape
|
|
165
165
|
T07's single-issue tracer bullet stood in for.
|
|
166
166
|
|
|
167
167
|
The rubric is read exactly once, before any candidate is even selected:
|
|
@@ -289,7 +289,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
289
289
|
parser = argparse.ArgumentParser(
|
|
290
290
|
prog="specfuse-backfill-severity",
|
|
291
291
|
description=(
|
|
292
|
-
"Amend every open, marked, severity-less
|
|
292
|
+
"Amend every open, marked, severity-less bug with a severity, "
|
|
293
293
|
"marker first, label projected after."
|
|
294
294
|
),
|
|
295
295
|
)
|
|
@@ -47,7 +47,7 @@ from typing import Callable, Optional
|
|
|
47
47
|
from specfuse.loop import agent_policy
|
|
48
48
|
from specfuse.loop.lint_plan import read_frontmatter
|
|
49
49
|
from specfuse.loop.plan_baseline import load_plan_graph
|
|
50
|
-
from specfuse.loop.triage import parse_marker
|
|
50
|
+
from specfuse.loop.triage import effective_category, parse_marker
|
|
51
51
|
|
|
52
52
|
DEFAULT_ISSUE_LIST_LIMIT = 100
|
|
53
53
|
DEFAULT_PR_LIST_LIMIT = 100
|
|
@@ -166,6 +166,8 @@ def _to_issue_summary(raw: dict) -> IssueSummary:
|
|
|
166
166
|
)
|
|
167
167
|
marker = parse_marker(raw.get("body") or "")
|
|
168
168
|
category, confidence = marker if marker is not None else (None, None)
|
|
169
|
+
# #3415: an operator's relabel overrides the marker triage wrote.
|
|
170
|
+
category = effective_category(category, labels)
|
|
169
171
|
return IssueSummary(
|
|
170
172
|
number=raw.get("number"),
|
|
171
173
|
title=raw.get("title", ""),
|
|
@@ -34,7 +34,7 @@ import json
|
|
|
34
34
|
import tempfile
|
|
35
35
|
import re
|
|
36
36
|
import time
|
|
37
|
-
from dataclasses import dataclass
|
|
37
|
+
from dataclasses import dataclass, replace
|
|
38
38
|
from typing import Any, Callable, Optional
|
|
39
39
|
|
|
40
40
|
from specfuse.loop.agent_policy import (
|
|
@@ -51,13 +51,15 @@ from specfuse.loop.bug_lane import (
|
|
|
51
51
|
REASON_DIFF_TOO_LARGE,
|
|
52
52
|
REASON_JUDGE_PATH_TOUCHED,
|
|
53
53
|
REASON_NO_TEST_EVIDENCE,
|
|
54
|
+
REASON_RED_ON_BASE_UNVERIFIED,
|
|
54
55
|
evaluate_merge_guardrails,
|
|
55
56
|
evaluate_pr_shape_guardrails,
|
|
56
57
|
judge_paths_touched,
|
|
57
58
|
)
|
|
58
59
|
from specfuse.loop.bug_lane_state import GitHubMergeCapState, record_merge
|
|
59
60
|
from specfuse.loop.labels import provision_labels
|
|
60
|
-
from specfuse.loop.
|
|
61
|
+
from specfuse.loop.loop import parse_claude_json_output
|
|
62
|
+
from specfuse.loop.red_on_base import probe_red_on_base, select_test_files
|
|
61
63
|
from specfuse.loop.triage import parse_marker
|
|
62
64
|
from specfuse.monitor.diagnosis import DiagnosisParseError
|
|
63
65
|
from specfuse.monitor.diagnosis import parse as parse_diagnosis
|
|
@@ -76,6 +78,7 @@ __all__ = (
|
|
|
76
78
|
"OUTCOME_MERGED",
|
|
77
79
|
"OUTCOME_DECLINED",
|
|
78
80
|
"OUTCOME_AUTOMERGE_OFF",
|
|
81
|
+
"OUTCOME_SUPERSEDED",
|
|
79
82
|
"run_bug_lane",
|
|
80
83
|
"pr_ci_conclusion",
|
|
81
84
|
"add_guardrail_label",
|
|
@@ -104,6 +107,12 @@ OUTCOME_DECLINED = "declined"
|
|
|
104
107
|
#: when the guardrails had passed and the dial was the only thing in the way.
|
|
105
108
|
OUTCOME_AUTOMERGE_OFF = "automerge_off"
|
|
106
109
|
|
|
110
|
+
#: The issue was closed while the item ran -- by another PR, or by hand -- so
|
|
111
|
+
#: the lane's PR duplicates work already done and was closed (#3447). Not an
|
|
112
|
+
#: escalation: nothing is left for a human to decide, and asking one to merge
|
|
113
|
+
#: a duplicate of a closed fix was the bug.
|
|
114
|
+
OUTCOME_SUPERSEDED = "superseded"
|
|
115
|
+
|
|
107
116
|
#: `needs_decision` escalates like the other two: the session opened no PR,
|
|
108
117
|
#: so there is nothing for the guardrails to evaluate, and what the operator
|
|
109
118
|
#: owes is an answer rather than a review (#3390).
|
|
@@ -182,6 +191,10 @@ class BugLaneResult:
|
|
|
182
191
|
#: telling them apart is the difference between "fix this by hand" and
|
|
183
192
|
#: "push the branch that already exists".
|
|
184
193
|
unpushed_work: Optional[tuple] = None
|
|
194
|
+
#: The headless session's usage envelope (#3419), `None` when the CLI
|
|
195
|
+
#: printed none. `BugsProvider` turns it into `ActionOutcome.spend`;
|
|
196
|
+
#: without it `max_tokens` never bounded the lane that spends.
|
|
197
|
+
usage: Optional[dict] = None
|
|
185
198
|
|
|
186
199
|
|
|
187
200
|
#: How long to wait for a freshly-opened PR's checks to reach a conclusion,
|
|
@@ -216,6 +229,10 @@ _BUCKET_PENDING = "pending"
|
|
|
216
229
|
|
|
217
230
|
_CI_FAILING = "fail"
|
|
218
231
|
|
|
232
|
+
#: What `gh pr checks` prints on stderr, with empty stdout and exit 1, for a PR
|
|
233
|
+
#: that has no check runs at all (#3445).
|
|
234
|
+
_NO_CHECKS_REPORTED = "no checks reported"
|
|
235
|
+
|
|
219
236
|
|
|
220
237
|
def _required_checks_verdict(rows: list, required) -> str:
|
|
221
238
|
"""`""` when every required check is present and passing, else why not.
|
|
@@ -272,6 +289,12 @@ def _read_ci_conclusion_once(
|
|
|
272
289
|
# only a fallback when there is no parseable output.
|
|
273
290
|
stdout = getattr(result, "stdout", None)
|
|
274
291
|
if not stdout:
|
|
292
|
+
# #3445: `gh` never prints `[]` for a PR with no check runs. It prints
|
|
293
|
+
# nothing, says `no checks reported on the '<branch>' branch` on stderr
|
|
294
|
+
# and exits 1. That is the "no check registered yet" state below, not
|
|
295
|
+
# an unreadable read, and must not decline as `ci_not_green`.
|
|
296
|
+
if _NO_CHECKS_REPORTED in (getattr(result, "stderr", None) or ""):
|
|
297
|
+
return _CI_PENDING
|
|
275
298
|
return _CI_UNKNOWN
|
|
276
299
|
|
|
277
300
|
try:
|
|
@@ -586,6 +609,70 @@ def _issue_provenance(runner: Callable, repo: str, issue_number: int) -> Optiona
|
|
|
586
609
|
return {"kind": "triaged_issue", "ref": str(issue_number)}
|
|
587
610
|
|
|
588
611
|
|
|
612
|
+
def _superseded_by(
|
|
613
|
+
runner: Callable, repo: str, issue_number: int, own_pr: int
|
|
614
|
+
) -> tuple[bool, Optional[int]]:
|
|
615
|
+
"""Whether *issue_number* was closed by something other than *own_pr*,
|
|
616
|
+
and the PR that closed it when GitHub knows one (#3447).
|
|
617
|
+
|
|
618
|
+
Fails open: an unreadable issue reads as not superseded, so a `gh` hiccup
|
|
619
|
+
leaves the lane exactly where it was before this check existed rather than
|
|
620
|
+
closing a PR that may be the only fix.
|
|
621
|
+
"""
|
|
622
|
+
result = runner(
|
|
623
|
+
[
|
|
624
|
+
"gh", "issue", "view", str(issue_number), "--repo", repo,
|
|
625
|
+
"--json", "state,closedByPullRequestsReferences",
|
|
626
|
+
],
|
|
627
|
+
check=False,
|
|
628
|
+
)
|
|
629
|
+
if getattr(result, "returncode", 1) != 0 or not getattr(result, "stdout", None):
|
|
630
|
+
return False, None
|
|
631
|
+
try:
|
|
632
|
+
data = json.loads(result.stdout)
|
|
633
|
+
except ValueError:
|
|
634
|
+
return False, None
|
|
635
|
+
if not isinstance(data, dict) or data.get("state") != "CLOSED":
|
|
636
|
+
return False, None
|
|
637
|
+
closers = [
|
|
638
|
+
ref.get("number")
|
|
639
|
+
for ref in data.get("closedByPullRequestsReferences") or ()
|
|
640
|
+
if isinstance(ref, dict)
|
|
641
|
+
]
|
|
642
|
+
closers = [n for n in closers if isinstance(n, int) and not isinstance(n, bool)]
|
|
643
|
+
if own_pr in closers:
|
|
644
|
+
return False, None
|
|
645
|
+
return True, (closers[0] if closers else None)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def _close_superseded(
|
|
649
|
+
runner: Callable,
|
|
650
|
+
repo: str,
|
|
651
|
+
issue_number: int,
|
|
652
|
+
pr_number: int,
|
|
653
|
+
by_pr: Optional[int],
|
|
654
|
+
dispatch_profile: tuple,
|
|
655
|
+
) -> BugLaneResult:
|
|
656
|
+
"""Close the lane's own PR as a duplicate of a fix already done (#3447)."""
|
|
657
|
+
by = f"PR #{by_pr}" if by_pr else "another change"
|
|
658
|
+
runner(
|
|
659
|
+
[
|
|
660
|
+
"gh", "pr", "close", str(pr_number), "--repo", repo,
|
|
661
|
+
"--comment",
|
|
662
|
+
f"Superseded: issue #{issue_number} was closed by {by} while the "
|
|
663
|
+
"bug lane was evaluating this PR. Closing it as a duplicate.",
|
|
664
|
+
],
|
|
665
|
+
check=False,
|
|
666
|
+
)
|
|
667
|
+
return BugLaneResult(
|
|
668
|
+
outcome=OUTCOME_SUPERSEDED,
|
|
669
|
+
reason=f"superseded by {by}",
|
|
670
|
+
pr_number=pr_number,
|
|
671
|
+
evidence=(f"Issue #{issue_number} was closed by {by}.",),
|
|
672
|
+
dispatch_profile=dispatch_profile,
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
|
|
589
676
|
#: Branch shape `/fix-bug` uses for an issue it is fixing.
|
|
590
677
|
_WORK_BRANCH_GLOB = "fix/issue-{number}-*"
|
|
591
678
|
|
|
@@ -641,9 +728,21 @@ def unpushed_work_for_issue(runner: Callable, issue_number: int) -> Optional[tup
|
|
|
641
728
|
|
|
642
729
|
|
|
643
730
|
def _evidence_for(
|
|
644
|
-
reason: str,
|
|
731
|
+
reason: str,
|
|
732
|
+
changed_files: list,
|
|
733
|
+
diff_lines: int,
|
|
734
|
+
limits: dict,
|
|
735
|
+
*,
|
|
736
|
+
red_on_base_reason: str = "",
|
|
645
737
|
) -> tuple:
|
|
646
738
|
"""Render the measurement behind *reason* as sentences a human can read."""
|
|
739
|
+
if reason == REASON_RED_ON_BASE_UNVERIFIED and red_on_base_reason:
|
|
740
|
+
# #3416: which step of the probe gave up. Without it `unverified` is
|
|
741
|
+
# the one decline nobody can act on short of re-running it by hand.
|
|
742
|
+
return (
|
|
743
|
+
f"The red-on-base probe could not run the PR's tests at the merge "
|
|
744
|
+
f"base: {red_on_base_reason}.",
|
|
745
|
+
)
|
|
647
746
|
if reason == REASON_JUDGE_PATH_TOUCHED:
|
|
648
747
|
hits = judge_paths_touched(changed_files)
|
|
649
748
|
if not hits:
|
|
@@ -699,30 +798,37 @@ def _declined(
|
|
|
699
798
|
)
|
|
700
799
|
|
|
701
800
|
|
|
702
|
-
def
|
|
703
|
-
"""
|
|
801
|
+
def _pr_base_and_head(runner: Callable, repo: str, pr_number: int) -> tuple[str, str]:
|
|
802
|
+
"""`(baseRefOid, headRefOid)` for the PR, `""` for whichever is unknown.
|
|
704
803
|
|
|
705
804
|
`baseRefOid` is the tip of the base branch as GitHub sees it now, which is
|
|
706
|
-
what the PR would merge into
|
|
707
|
-
|
|
708
|
-
|
|
805
|
+
what the PR would merge into -- the tree its new test must fail on
|
|
806
|
+
(#3377). `headRefOid` is the PR's own commit, whose test files the probe
|
|
807
|
+
runs on that tree (#3417). An empty string makes the probe decline rather
|
|
808
|
+
than guess a commit.
|
|
709
809
|
"""
|
|
710
810
|
try:
|
|
711
811
|
result = runner(
|
|
712
812
|
["gh", "pr", "view", str(pr_number), "--repo", repo,
|
|
713
|
-
"--json", "baseRefOid"],
|
|
813
|
+
"--json", "baseRefOid,headRefOid"],
|
|
714
814
|
check=False,
|
|
715
815
|
)
|
|
716
816
|
except Exception: # noqa: BLE001 - a lookup failure declines, never raises
|
|
717
|
-
return ""
|
|
817
|
+
return "", ""
|
|
718
818
|
if getattr(result, "returncode", 1) != 0 or not getattr(result, "stdout", None):
|
|
719
|
-
return ""
|
|
819
|
+
return "", ""
|
|
720
820
|
try:
|
|
721
821
|
payload = json.loads(result.stdout)
|
|
722
822
|
except ValueError:
|
|
723
|
-
return ""
|
|
724
|
-
|
|
725
|
-
|
|
823
|
+
return "", ""
|
|
824
|
+
if not isinstance(payload, dict):
|
|
825
|
+
return "", ""
|
|
826
|
+
|
|
827
|
+
def _sha(key: str) -> str:
|
|
828
|
+
value = payload.get(key)
|
|
829
|
+
return value.strip() if isinstance(value, str) else ""
|
|
830
|
+
|
|
831
|
+
return _sha("baseRefOid"), _sha("headRefOid")
|
|
726
832
|
|
|
727
833
|
|
|
728
834
|
def dispatch_profile_for(
|
|
@@ -813,6 +919,33 @@ def run_bug_lane(
|
|
|
813
919
|
always accurate) and merges only when the dial is `on` *and* the
|
|
814
920
|
guardrails are eligible -- the module's single merge call site.
|
|
815
921
|
"""
|
|
922
|
+
session: dict = {}
|
|
923
|
+
result = _run_bug_lane(
|
|
924
|
+
runner, repo, issue_number,
|
|
925
|
+
working_dir=working_dir, policy_path=policy_path, now=now,
|
|
926
|
+
ci_sleep=ci_sleep, ci_clock=ci_clock,
|
|
927
|
+
ci_deadline_seconds=ci_deadline_seconds,
|
|
928
|
+
pr_lookup_sleep=pr_lookup_sleep, session=session,
|
|
929
|
+
)
|
|
930
|
+
# #3419: every outcome carries the session's usage, whichever of the
|
|
931
|
+
# lane's many return paths produced it.
|
|
932
|
+
return replace(result, usage=session.get("usage"))
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
def _run_bug_lane(
|
|
936
|
+
runner: Callable,
|
|
937
|
+
repo: str,
|
|
938
|
+
issue_number: int,
|
|
939
|
+
*,
|
|
940
|
+
working_dir: str,
|
|
941
|
+
policy_path: Any,
|
|
942
|
+
now: Optional[float],
|
|
943
|
+
ci_sleep: Callable,
|
|
944
|
+
ci_clock: Callable,
|
|
945
|
+
ci_deadline_seconds: Optional[float],
|
|
946
|
+
pr_lookup_sleep: Callable,
|
|
947
|
+
session: dict,
|
|
948
|
+
) -> BugLaneResult:
|
|
816
949
|
model, effort, profile_source = dispatch_profile_for(
|
|
817
950
|
runner, repo, issue_number, policy_path
|
|
818
951
|
)
|
|
@@ -820,8 +953,13 @@ def run_bug_lane(
|
|
|
820
953
|
argv, prompt = build_invocation(
|
|
821
954
|
issue_number, repo, working_dir, model=model, effort=effort
|
|
822
955
|
)
|
|
823
|
-
|
|
824
|
-
|
|
956
|
+
# #3419: ask for the CLI's JSON envelope so the session's usage is known.
|
|
957
|
+
# Its `result` is the same final text plain `-p` printed, so everything
|
|
958
|
+
# below reads `session_output` exactly as before.
|
|
959
|
+
invocation = runner(argv + ["--output-format", "json", prompt], check=False)
|
|
960
|
+
session_output, session["usage"] = parse_claude_json_output(
|
|
961
|
+
getattr(invocation, "stdout", "") or ""
|
|
962
|
+
)
|
|
825
963
|
outcome = classify_outcome(session_output)
|
|
826
964
|
|
|
827
965
|
if outcome in _ESCALATING_OUTCOMES:
|
|
@@ -855,6 +993,13 @@ def run_bug_lane(
|
|
|
855
993
|
outcome=OUTCOME_DECLINED, reason=_REASON_PR_NOT_FOUND,
|
|
856
994
|
pr_number=None, dispatch_profile=_profile)
|
|
857
995
|
|
|
996
|
+
# #3447: another session may have fixed the issue while this one ran.
|
|
997
|
+
superseded, by_pr = _superseded_by(runner, repo, issue_number, pr_number)
|
|
998
|
+
if superseded:
|
|
999
|
+
return _close_superseded(
|
|
1000
|
+
runner, repo, issue_number, pr_number, by_pr, _profile,
|
|
1001
|
+
)
|
|
1002
|
+
|
|
858
1003
|
changed_files, diff_lines = _pr_changed_files_and_diff_lines(runner, repo, pr_number)
|
|
859
1004
|
limits = bug_lane_limits(policy_path)
|
|
860
1005
|
provenance = _issue_provenance(runner, repo, issue_number)
|
|
@@ -902,14 +1047,18 @@ def run_bug_lane(
|
|
|
902
1047
|
# test run.
|
|
903
1048
|
require_red = resolve_require_red_on_base(policy_path)
|
|
904
1049
|
red_on_base = None
|
|
1050
|
+
red_on_base_reason = ""
|
|
905
1051
|
if require_red:
|
|
906
|
-
|
|
1052
|
+
base_sha, head_sha = _pr_base_and_head(runner, repo, pr_number)
|
|
1053
|
+
probe = probe_red_on_base(
|
|
907
1054
|
runner,
|
|
908
|
-
base_sha=
|
|
1055
|
+
base_sha=base_sha,
|
|
1056
|
+
head_sha=head_sha,
|
|
909
1057
|
test_files=select_test_files(changed_files, limits["test_paths"]),
|
|
910
1058
|
command_template=resolve_red_on_base_command(policy_path),
|
|
911
1059
|
worktree_root=tempfile.gettempdir(),
|
|
912
1060
|
)
|
|
1061
|
+
red_on_base, red_on_base_reason = probe.verdict, probe.reason
|
|
913
1062
|
|
|
914
1063
|
decision = evaluate_merge_guardrails(
|
|
915
1064
|
changed_files=changed_files,
|
|
@@ -925,6 +1074,15 @@ def run_bug_lane(
|
|
|
925
1074
|
)
|
|
926
1075
|
dial = resolve_bug_automerge(policy_path)
|
|
927
1076
|
|
|
1077
|
+
# #3447: asked again after the CI wait, the longest stretch an item spends
|
|
1078
|
+
# -- the observed duplicates were closed elsewhere during exactly this
|
|
1079
|
+
# window. Neither merging nor escalating a duplicate of a closed fix.
|
|
1080
|
+
superseded, by_pr = _superseded_by(runner, repo, issue_number, pr_number)
|
|
1081
|
+
if superseded:
|
|
1082
|
+
return _close_superseded(
|
|
1083
|
+
runner, repo, issue_number, pr_number, by_pr, _profile,
|
|
1084
|
+
)
|
|
1085
|
+
|
|
928
1086
|
if dial and decision.eligible:
|
|
929
1087
|
runner(
|
|
930
1088
|
["gh", "pr", "merge", str(pr_number), "--repo", repo, "--squash", "--delete-branch"],
|
|
@@ -950,6 +1108,9 @@ def run_bug_lane(
|
|
|
950
1108
|
# case returned above.
|
|
951
1109
|
return _declined(
|
|
952
1110
|
runner, repo, pr_number, decision.reason, working_dir,
|
|
953
|
-
evidence=_evidence_for(
|
|
1111
|
+
evidence=_evidence_for(
|
|
1112
|
+
decision.reason, changed_files, diff_lines, limits,
|
|
1113
|
+
red_on_base_reason=red_on_base_reason,
|
|
1114
|
+
),
|
|
954
1115
|
dispatch_profile=_profile,
|
|
955
1116
|
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.27.0
|