specfuse-loop 0.28.2__tar.gz → 0.29.1__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.28.2/specfuse_loop.egg-info → specfuse_loop-0.29.1}/PKG-INFO +1 -1
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/pyproject.toml +1 -1
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/diagnose_invoke.py +6 -2
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/invoke.py +14 -3
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/providers/findings_autofix.py +26 -2
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/providers/findings_diagnose.py +80 -15
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/providers/triage.py +13 -6
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/run.py +45 -2
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/triage_invoke.py +25 -0
- specfuse_loop-0.29.1/specfuse/loop/data/VERSION +1 -0
- specfuse_loop-0.29.1/specfuse/loop/data/docs/concepts/monitoring-runners.md +199 -0
- specfuse_loop-0.29.1/specfuse/loop/data/docs/concepts/monitoring-schema.md +211 -0
- specfuse_loop-0.29.1/specfuse/loop/data/docs/concepts/monitoring-to-fix.md +249 -0
- specfuse_loop-0.29.1/specfuse/loop/data/docs/flows.html +519 -0
- specfuse_loop-0.29.1/specfuse/loop/data/docs/lifecycles.md +394 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/gitignore.snippet +2 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/escalation.py +65 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/labels.py +8 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/loop.py +41 -4
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/scaffold.py +13 -3
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/triage.py +34 -13
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/artifact.py +11 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/cli.py +154 -3
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/diagnose_cli.py +6 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/diagnosis.py +31 -1
- specfuse_loop-0.29.1/specfuse/monitor/issues.py +572 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/providers/azure_app_insights.py +377 -32
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/providers/azure_service_bus.py +157 -1
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/redaction.py +6 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse_loop.egg-info/SOURCES.txt +22 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_invoke_usage.py +2 -2
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_app_insights_query_schema_check.py +38 -2
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_app_insights_workspace_schema.py +6 -2
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_spend.py +3 -3
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_caller_check_ratchet.py +0 -1
- specfuse_loop-0.29.1/tests/test_closed_escalation_labels.py +148 -0
- specfuse_loop-0.29.1/tests/test_diagnosis_severity_end_to_end.py +191 -0
- specfuse_loop-0.29.1/tests/test_diagnosis_severity_rubric.py +41 -0
- specfuse_loop-0.29.1/tests/test_floor_skip_summary.py +94 -0
- specfuse_loop-0.29.1/tests/test_flows_page.py +94 -0
- specfuse_loop-0.29.1/tests/test_gate_log_size_budget.py +67 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_heartbeat_adapter.py +70 -4
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_init_integration.py +5 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_label_registry.py +4 -2
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitor_check_isolation.py +2 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitor_cli.py +10 -1
- specfuse_loop-0.29.1/tests/test_monitor_diagnose_auto_warning.py +120 -0
- specfuse_loop-0.29.1/tests/test_monitor_finding_context_end_to_end.py +463 -0
- specfuse_loop-0.29.1/tests/test_monitor_finding_lifecycle_end_to_end.py +268 -0
- specfuse_loop-0.29.1/tests/test_monitor_finding_meta.py +208 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitor_issue_lifecycle.py +17 -12
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitor_one_issue_per_fingerprint.py +2 -0
- specfuse_loop-0.29.1/tests/test_monitor_quiet_annotation.py +189 -0
- specfuse_loop-0.29.1/tests/test_rearm_close_broad_run.py +124 -0
- specfuse_loop-0.29.1/tests/test_rediagnose_on_reopen.py +151 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_data_in_sync.py +5 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_docs.py +5 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_init.py +5 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_resources.py +5 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_wiring.py +7 -1
- specfuse_loop-0.29.1/tests/test_spend_counts_cache_tokens.py +62 -0
- specfuse_loop-0.29.1/tests/test_triage_reads_comments.py +111 -0
- specfuse_loop-0.29.1/tests/test_triage_severity_label_conflict.py +165 -0
- specfuse_loop-0.29.1/tests/test_upgrade_gitignore_lines.py +64 -0
- specfuse_loop-0.28.2/specfuse/loop/data/VERSION +0 -1
- specfuse_loop-0.28.2/specfuse/monitor/issues.py +0 -303
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/LICENSE +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/NOTICE +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/README.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/setup.cfg +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/__init__.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/budget.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/drafting_answers.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/drafting_invoke.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/drafting_questions.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/driver_command.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/driver_invoke.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/monitoring_read.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/providers/__init__.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/providers/answers.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/providers/bugs.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/providers/feature.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/queue_read.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/repo_detect.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/severity_backfill.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/agent/worktree.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/_filelock.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/_wu_sections.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/agent_policy.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/arm_eval.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/arm_sweep.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/arm_txn.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/bug_lane.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/bug_lane_run.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/bug_lane_state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/build_provenance.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/caller_check.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/changelog.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/closing_requirements.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/cost.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/criteria_state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/docs/glossary.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/docs/methodology.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/monitoring.yml.example +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules/close-discipline.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules/human-output.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules/operator-escalation.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules/planning-discipline.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules/verification-discipline.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/rules-local/README.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/schemas/event.schema.json +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/decisions_format.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/driver_edit.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/events_stats.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/gate_commands.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/heartbeat.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/judge.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/learnings_distil.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/learnings_query.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/lint_closing.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/lint_monitoring.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/lint_plan.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/lint_roadmap.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/notify.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/notify_escalation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/notify_sla.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/plan_baseline.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/policy_proposals.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/policy_review.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/prerun.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/prerun_capture.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/promotion.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/rearm_migration.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/red_on_base.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/replay_spin.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/roadmap_archive.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/__init__.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/adapters.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/autofix.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/autofix_invoke.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/autofix_run.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/autofix_state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/durations.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/fingerprint.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/providers/__init__.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/providers/_azure_auth.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse/monitor/schedule.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_abandon_feature_skill.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_abandoned_work_reported.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_abandoned_wu_strands_gate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_budget.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_bug_selection_dedup.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_default_features_root.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_driver_command.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_driver_invoke.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_driver_restart.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_escalation_on_issue.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_escalation_run_state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_findings_seam.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_invoke_timeout.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_item_worktree.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_lock.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_policy_key_ownership.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_policy_queue.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_policy_schema.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_policy_triage_dial.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_provider_answers.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_provider_bugs.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_provider_feature.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_provider_findings_autofix.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_provider_findings_diagnose.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_provider_isolation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_provider_triage.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_queue_read.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_relays_driver_output.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_repo_detect.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_run.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_run_progress.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_seam.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_snapshot_refresh.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_agent_state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_answer_escalation_skill.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_app_insights_adapters.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_app_insights_harvest_window.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_archive_ref_reconciliation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_arm_eval.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_arm_eval_lint_class.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_arm_eval_wiring.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_arm_sweep.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_arm_sweep_gate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_arm_txn.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_arm_wiring.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_artifact_redaction.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_attempt_note_untracked_files.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_attempt_outcome_contract.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_attention_nonwriting_guard.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_attention_silence_section.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_attention_skill_structure.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_attribution_halt_claims.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autoclose_deferral_visibility.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autoclose_gate_note.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autoclose_stub_states_what_passed.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autofix_invoke.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autofix_live.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autofix_predicate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autofix_run.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autofix_state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autosync_no_cwd_leak.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_azure_credential_resolution.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_backend.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_baseline_escalation_message.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_baseline_persistence.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_baseline_probe.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_baseline_provenance.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_baseline_tree_hash_key.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bash_routing.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bats_suites_gated.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_binary_verdict.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_binding_block_advisory.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_binding_block_allocation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_binding_block_budget.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_blind_run_is_not_drained.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bookkeeping_no_verify.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_brief_covers_every_unit_escalation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_ci_pending.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_guardrails.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_label_nonfatal.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_labels_registered.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_min_severity.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_no_checks_pending.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_policy_contract.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_pr_number_carried.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_preflight_and_dial.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_run.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_superseded.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_bug_lane_test_paths.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_build_provenance.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_carried_green_invalidated_by_diff.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_carry_accounting_close_and_judge.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_changed_file_test_selection.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_changelog_append_only.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_changelog_collection.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_changelog_release_wiring.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_changelog_schema.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_ci_conclusion_wait.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_ci_runs_every_gate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_claude_resolution.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_close_b_pending_learnings.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_close_records_feature_oracle.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_close_wu_self_reference.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_closing_guard_contracts.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_closing_guard_prediction.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_closing_learnings_destination.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_closing_requirements.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_closing_sessions_on_sonnet.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_coauthor_trailer_allowlist.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_conditional_reflection.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_console_encoding.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_convergent_iteration.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_convergent_wu_lint.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_correlation_id_override.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_correlation_id_two_surfaces.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_cost_lifetime.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_criteria_per_gate_lint.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_criteria_state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_criteria_worklist.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_decision_citation_lint.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_decision_followups_discharged.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_decision_override_lint.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_decisions_format.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_default_severity_aliases.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_degraded_oracle.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_deliverable_missing_note.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_derive_agent_policy_review_mode.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_derive_agent_policy_skill.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_derive_monitoring_discovery.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_derive_monitoring_skill_registration.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_design_for_diagnosis_rule.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_deterministic_refusal_repeat.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_diagnose_fenced_analysis.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_diagnose_headless.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_diagnose_issue_skill.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_diagnosis.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_diagnosis_roundtrip_live.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_dispatch_model_from_fix_scope.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_dispatch_preamble_rules_pointer.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_dispatch_skeleton.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_dispatch_skills_index.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_distilled_accept_step.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_doctor_command_preflight.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_done_feature_gates.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_draft_feature_answers_mode.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_drafting_answer_gate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_drafting_invoke.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_drafting_question_issue.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_drafting_questions.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_drafting_reply_shape.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_driver_edit_detection.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_driver_event_registry_covers_emitters.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_driver_module_surface.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_driver_restart_halt_surfaced.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_driver_restart_halt_wiring.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_driver_restart_hold.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_driver_staleness_gate_summary.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_driver_staleness_warning.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_dry_run_no_branch_mutation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_ensure_feature_branch_base.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_escalation_assignee_policy.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_escalation_contract.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_escalation_crash_containment.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_escalation_dedup_search.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_escalation_emit.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_event_gate_full_envelope.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_events_redaction.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_events_stats.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_failure_artifact_model.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_failure_signature_fail_prefix.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_failure_signature_names_the_test.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_failure_signature_skips_log_noise.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_feature_branch_auto_sync.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_feature_branch_squash_merge.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_feature_branch_stale_base.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_feature_id_resolution.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_feature_oracle_e2e.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_feature_provider_drafting_dispatch.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_feature_review_accumulation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_filelock_portable.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_files_changed_auto_repair.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_find_pr_no_search_index.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_fingerprint.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_fix_bug_cwd_worktree.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_fix_bug_diff_self_check.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_fix_bug_headless.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_fix_bug_reads_comments.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_fix_unit_insertion_bookkeeping.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_fix_unit_insertion_e2e.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_fix_unit_insertion_refused.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_fold_contract_documented.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_followups_artifact.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_frontmatter_scalar_quoting.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gate_broad_run.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gate_budget.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gate_budget_post_dispatch.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gate_commands_needs.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gate_eval_intermediate_wiring.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gate_eval_repo_flag.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gate_eval_terminal_wiring.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gate_report_verdict.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_gitleaks_pinning.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_groom_backlog_skill.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_guard_refusal_failure_class.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_guard_refusal_failure_excerpt.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_guard_repair_e2e.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_guard_repair_prompt.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_headless_no_background_tasks.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_heartbeat.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_heartbeat_emission_contract.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_http_status_regression_adapter.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_http_status_regression_end_to_end.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_human_output_rule.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_human_work_unit.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_hygiene_boundary_guidance.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_hyphenated_gate_name.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_ignored_cleanup_before_probe.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_installed_copy_driver_e2e.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_interpreter_normalization.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_judge_close_path.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_judge_cost.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_judge_cost_fold_committed.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_judge_module.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_judge_path_registry.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_judge_sees_dropped_produces.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_judge_terminal_flip_state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_judged_event_points_at_findings.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_label_description_length.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_label_provisioning_runner_contract.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_label_registry_covers_consumers.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_language_aware_narrow_selection.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lazy_baseline_e2e.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_learnings_curate_vendored_promote.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_learnings_distil_cli.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_learnings_query.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_learnings_staging.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_learnings_suggest_signature_filter.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_learnings_weights.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lifetime_accounting.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_ac_observable.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_boundary_consistency.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_boundary_extraction.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_close_verdict_not_predecided.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_closing.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_closing_criteria.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_closing_criteria_pristine.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_covers_load_wu_invariants.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_feature_oracle_declared.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_gate_proportionality.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_monitoring.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_plan_contract_fields.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_plan_relative_import.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_plan_verdict_exempt.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_planning_sections.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_produces_satisfiability.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_roadmap.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_lint_verification_reference.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_live_tests_opt_in.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_load_bearing_close_lint.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_attempt_evidence.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_criteria_skeleton.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_criteria_survival.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_gate_needs.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_worklist_injection.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_make_repo_gpgsign_isolation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_marker_dual_shape.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_maven_failure_excerpt.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_max_attempts_configurable.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_max_open_prs.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitor_durations.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitor_runner_surfaces.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitoring_cron_dialect.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitoring_example.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitoring_fenced_blocks.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_monitoring_seed.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_narrow_fallback_warning.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_needs_decision_outcome.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_no_pytest_subprocess.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_notify.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_notify_escalation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_notify_sla.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_operator_escalation_rule.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_oracle_set_declared.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_oracle_unrunnable_attribution.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_per_run_item_cap_disclosure.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_pick_feature_skill.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_pin_honesty_and_integrity.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_pin_marker_not_inherited.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_pin_marker_spawn_sites.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_plan_baseline.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_planning_cost_floor.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_plugin_prose_cross_refs.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_plugin_qualified_dispatch.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_policy_budgets_applied.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_policy_proposals.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_policy_review.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_pr_checks_json_field.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_pr_reference_in_title.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_prep_halt_operator_surface.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_prepare_feature.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_prepare_scaffold_sync.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_prerun_capture.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_prerun_oracles.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_prerun_wiring.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produced_document_verdict_guard.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produces_amendment_e2e.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produces_bracket_literal.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produces_bracket_wildcard.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produces_in_diff.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produces_justification.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produces_recursive_glob.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produces_repair_note.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produces_semantics_unified.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_produces_shape.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_progress_forward_note.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_progress_lines_end_to_end.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_promoted_issue_not_redispatched.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_promotion_ledger.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_provider_escalation_traces.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_provision_labels.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_queue_stalled_adapter.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_rearm_fold_marker.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_rearm_migration.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_reclose_carries_narrow_greens_e2e.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_red_baseline_never_reused.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_red_on_base_fetch_and_reason.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_red_on_base_guardrail.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_red_on_base_overlay.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_red_on_base_probe.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_red_on_base_real_runner.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_redaction_windows_home.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_reescalation_posts_comment.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_reflection_required_signals.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_replan_end_to_end.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_replan_event_emission.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_replan_note_collision.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_replan_trigger.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_replan_turn_contract.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_replay_spin.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_replay_spin_failing_sets.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_required_checks.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_resolve_base.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_restart_cap_counts_progress.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_result_block_audience.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_result_block_near_miss.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_result_block_scalar.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_resume_command_build.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_retain_on_guard_refusal_docs.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_roadmap_archive_cli.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_roadmap_archive_shim.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_roadmap_archive_skill_delegates.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_roadmap_link_gate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_ruff_verdict_lines.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_rule_import_convergence.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_run_level_failure_breaker.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_doc_hygiene.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_label_provisioning.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_skew_halts.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_schedule_dialect.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_seed_documents_prerun_keys.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_service_bus_dlq_adapter.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_session_env_preflight.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_severity_aliases.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_severity_backfill_apply.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_severity_backfill_bugs_only.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_severity_backfill_end_to_end.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_severity_backfill_limit.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_severity_backfill_marker.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_severity_backfill_run.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_severity_label_projection.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_severity_reconciliation.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_severity_rubric.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_shipped_templates_parse.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_skill_discovery_links.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_skills_vendored_in_sync.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_spec_work_unit_rules.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_spinning_rearm_gate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_spinning_repeat_same_failing_set.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_spinout_brief_end_to_end.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_spinout_brief_replan_option.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_stop_rationale_captured.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_stranded_halt_cause.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_surefire_run_level_signature.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_surefire_verdict_lines.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_terminal_auto_close_changelog.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_terminal_gate_message.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_tiered_verification_e2e.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_timeout_kill_portable.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_triage.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_triage_apply.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_triage_projects_severity_label.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_triage_severity_classify.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_triage_severity_end_to_end.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_triage_severity_write.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_triage_skill_contract.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_triage_skips_agent_escalations.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_tsc_verdict_lines.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_umbrella_version_resolution.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_unreadable_pr_listing_holds_pr_work.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_unterminated_frontmatter.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_untracked_file_absorption.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_validate_event_driver_types.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_windows_gate_exec.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_worktree_restore.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/tests/test_wu_execution_metadata.py +0 -0
- {specfuse_loop-0.28.2 → specfuse_loop-0.29.1}/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.29.1"
|
|
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"
|
|
@@ -23,7 +23,7 @@ from __future__ import annotations
|
|
|
23
23
|
|
|
24
24
|
from specfuse.agent.invoke import build_claude_argv
|
|
25
25
|
from specfuse.monitor.diagnose_cli import AnalysisParseError, render_headless
|
|
26
|
-
from specfuse.monitor.diagnosis import FIX_SCOPES
|
|
26
|
+
from specfuse.monitor.diagnosis import FIX_SCOPES, SEVERITY_RUBRIC
|
|
27
27
|
|
|
28
28
|
__all__ = ("AnalysisParseError", "build_invocation", "read_result")
|
|
29
29
|
|
|
@@ -69,7 +69,11 @@ def build_invocation(
|
|
|
69
69
|
"partial evidence gets a low number.\n"
|
|
70
70
|
f"- fix_scope: exactly one of {', '.join(FIX_SCOPES)} -- do not "
|
|
71
71
|
"invent or default this value; if none plainly fits, say so in "
|
|
72
|
-
"candidate_fix and still pick the closest of the three.\n
|
|
72
|
+
"candidate_fix and still pick the closest of the three.\n"
|
|
73
|
+
"- severity (optional): how serious the finding is, judged from its "
|
|
74
|
+
"evidence against this rubric; omit it if you cannot tell:\n"
|
|
75
|
+
+ "".join(f" - {line}\n" for line in SEVERITY_RUBRIC)
|
|
76
|
+
+ "\n"
|
|
73
77
|
"Do not post anything and do not run `gh issue comment` yourself; "
|
|
74
78
|
"the caller renders and posts the diagnosis this session produces."
|
|
75
79
|
)
|
|
@@ -112,13 +112,24 @@ def _parse_envelope(raw: str) -> tuple:
|
|
|
112
112
|
return result_text, (usage if usage else None)
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
#: Every token field `budgets.max_tokens_per_run` is compared against (#3522).
|
|
116
|
+
SPEND_TOKEN_FIELDS = (
|
|
117
|
+
"input_tokens",
|
|
118
|
+
"output_tokens",
|
|
119
|
+
"cache_read_input_tokens",
|
|
120
|
+
"cache_creation_input_tokens",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
115
124
|
def usage_spend(usage: Optional[dict]) -> int:
|
|
116
|
-
"""`spend` for `ActionOutcome`:
|
|
117
|
-
|
|
125
|
+
"""`spend` for `ActionOutcome`: all four usage token fields, cache reads
|
|
126
|
+
and cache creation included (#3522). With prompt caching nearly all of a
|
|
127
|
+
headless session's input is cache traffic, so leaving it out reported a
|
|
128
|
+
small fraction of the real volume and `max_tokens_per_run` never bound.
|
|
118
129
|
Zero for a `None` usage, never a `KeyError`."""
|
|
119
130
|
if not usage:
|
|
120
131
|
return 0
|
|
121
|
-
return int(usage.get(
|
|
132
|
+
return sum(int(usage.get(key, 0)) for key in SPEND_TOKEN_FIELDS)
|
|
122
133
|
|
|
123
134
|
|
|
124
135
|
def run_claude(
|
|
@@ -7,7 +7,8 @@ confidence threshold, `fix_scope` routing, fingerprint rate limit, and daily
|
|
|
7
7
|
cap all stay in `specfuse.monitor.autofix`/`autofix_state`, read only
|
|
8
8
|
through `run_autofix`.
|
|
9
9
|
|
|
10
|
-
`advertise` returns
|
|
10
|
+
`advertise` returns, most severe diagnosis first (stable within a severity),
|
|
11
|
+
one `kind="finding-autofix"` item per open snapshot
|
|
11
12
|
issue carrying `specfuse.monitor.issues.FINDING_LABEL` that already has a
|
|
12
13
|
diagnosis comment -- the mirror of T10's filter, read the same way via
|
|
13
14
|
`gh issue view N --json body,comments` and `specfuse.monitor.diagnosis.parse`.
|
|
@@ -63,6 +64,7 @@ from specfuse.monitor.issues import FINDING_LABEL
|
|
|
63
64
|
|
|
64
65
|
_ITEM_ID_PREFIX = "finding-autofix-"
|
|
65
66
|
_DEFAULT_MONITORING_CONFIG_PATH = ".specfuse/monitoring.yml"
|
|
67
|
+
_SEVERITY_ORDER = ("critical", "high", "medium", "low")
|
|
66
68
|
_FAILING_OUTCOMES = ("refused", "could_not_proceed")
|
|
67
69
|
|
|
68
70
|
|
|
@@ -91,6 +93,24 @@ def _has_diagnosis(comments: list) -> bool:
|
|
|
91
93
|
return False
|
|
92
94
|
|
|
93
95
|
|
|
96
|
+
def _latest_diagnosis(comments: list):
|
|
97
|
+
latest = None
|
|
98
|
+
for comment in comments or []:
|
|
99
|
+
body = comment.get("body", "") if isinstance(comment, dict) else ""
|
|
100
|
+
try:
|
|
101
|
+
diagnosis = parse(body)
|
|
102
|
+
except DiagnosisParseError:
|
|
103
|
+
continue
|
|
104
|
+
if diagnosis is not None:
|
|
105
|
+
latest = diagnosis
|
|
106
|
+
return latest
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _severity_rank(diagnosis) -> int:
|
|
110
|
+
severity = diagnosis.severity if diagnosis is not None else None
|
|
111
|
+
return _SEVERITY_ORDER.index(severity) if severity in _SEVERITY_ORDER else len(_SEVERITY_ORDER)
|
|
112
|
+
|
|
113
|
+
|
|
94
114
|
def _known_components(monitoring_config: Any) -> set:
|
|
95
115
|
components = monitoring_config.get("components") if isinstance(monitoring_config, dict) else None
|
|
96
116
|
if not isinstance(components, list):
|
|
@@ -171,6 +191,7 @@ class FindingsAutofixProvider:
|
|
|
171
191
|
known_components = _known_components(monitoring_config)
|
|
172
192
|
|
|
173
193
|
items = []
|
|
194
|
+
ranks = {}
|
|
174
195
|
for issue in snapshot.issues:
|
|
175
196
|
if FINDING_LABEL not in issue.labels:
|
|
176
197
|
continue
|
|
@@ -183,7 +204,8 @@ class FindingsAutofixProvider:
|
|
|
183
204
|
if component is None or component not in known_components:
|
|
184
205
|
continue
|
|
185
206
|
|
|
186
|
-
|
|
207
|
+
diagnosis = _latest_diagnosis(comments)
|
|
208
|
+
if diagnosis is None:
|
|
187
209
|
continue
|
|
188
210
|
|
|
189
211
|
item_id = f"{_ITEM_ID_PREFIX}{issue.number}"
|
|
@@ -192,6 +214,7 @@ class FindingsAutofixProvider:
|
|
|
192
214
|
"component": component,
|
|
193
215
|
"monitoring_config": monitoring_config,
|
|
194
216
|
}
|
|
217
|
+
ranks[item_id] = _severity_rank(diagnosis)
|
|
195
218
|
items.append(
|
|
196
219
|
ActionItem(
|
|
197
220
|
item_id=item_id,
|
|
@@ -200,6 +223,7 @@ class FindingsAutofixProvider:
|
|
|
200
223
|
queue_key=None,
|
|
201
224
|
)
|
|
202
225
|
)
|
|
226
|
+
items.sort(key=lambda item: ranks[item.item_id])
|
|
203
227
|
return items
|
|
204
228
|
|
|
205
229
|
def execute(self, item: ActionItem) -> ActionOutcome:
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
`AgentSnapshot` cannot answer.
|
|
8
8
|
|
|
9
9
|
`advertise` returns one `kind="finding-diagnose"` item per open snapshot
|
|
10
|
-
issue carrying `specfuse.monitor.issues.FINDING_LABEL` that
|
|
11
|
-
|
|
10
|
+
issue carrying `specfuse.monitor.issues.FINDING_LABEL` that needs a diagnosis:
|
|
11
|
+
none yet, a monitor reopen comment newer than its latest diagnosis, or the
|
|
12
|
+
`rediagnose` label (#3487). The snapshot's `IssueSummary` carries no body, so this
|
|
12
13
|
provider reads each candidate's body and comments itself with
|
|
13
14
|
`gh issue view N --json body,comments` -- the same read
|
|
14
15
|
`autofix_run._read_finding_issue` performs and the same softening of "the
|
|
@@ -25,8 +26,8 @@ The rendered comment body is `diagnosis.render`'s, unaltered -- this module
|
|
|
25
26
|
holds no heading template and no marker string of its own. An unparseable
|
|
26
27
|
analysis result posts nothing: `execute` escalates with the parse error's
|
|
27
28
|
own message and never retries, defaults a field, or posts a partial body.
|
|
28
|
-
This provider performs no git mutation
|
|
29
|
-
diagnosis
|
|
29
|
+
This provider performs no git mutation. Its only label write is the
|
|
30
|
+
`severity:<value>` label of a diagnosis that carries a severity.
|
|
30
31
|
"""
|
|
31
32
|
|
|
32
33
|
from __future__ import annotations
|
|
@@ -51,14 +52,16 @@ from specfuse.agent.run import (
|
|
|
51
52
|
_default_runner,
|
|
52
53
|
)
|
|
53
54
|
from specfuse.agent.state import AgentSnapshot
|
|
55
|
+
from specfuse.loop.agent_policy import SEVERITY_LABEL_PREFIX
|
|
54
56
|
from specfuse.monitor.diagnosis import DiagnosisParseError, parse
|
|
55
|
-
from specfuse.monitor.issues import FINDING_LABEL
|
|
57
|
+
from specfuse.monitor.issues import FINDING_LABEL, REDIAGNOSE_LABEL, is_reopen_comment
|
|
56
58
|
|
|
57
59
|
_ITEM_ID_PREFIX = "finding-diagnose-"
|
|
58
60
|
_DEFAULT_MONITORING_CONFIG_PATH = ".specfuse/monitoring.yml"
|
|
59
61
|
_DIAGNOSE_DIAL_AUTO = "auto"
|
|
60
62
|
|
|
61
63
|
|
|
64
|
+
|
|
62
65
|
def _read_issue(runner: Callable, repo: str, issue_number: int) -> dict:
|
|
63
66
|
result = runner(
|
|
64
67
|
["gh", "issue", "view", str(issue_number), "--repo", repo, "--json", "body,comments"],
|
|
@@ -73,15 +76,25 @@ def _read_issue(runner: Callable, repo: str, issue_number: int) -> dict:
|
|
|
73
76
|
return parsed if isinstance(parsed, dict) else {}
|
|
74
77
|
|
|
75
78
|
|
|
76
|
-
def
|
|
77
|
-
|
|
79
|
+
def _is_diagnosis(body: str) -> bool:
|
|
80
|
+
try:
|
|
81
|
+
return parse(body) is not None
|
|
82
|
+
except DiagnosisParseError:
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _needs_diagnosis(comments: list) -> bool:
|
|
87
|
+
"""True when the finding has no diagnosis yet, or the monitor reopened it
|
|
88
|
+
after its latest diagnosis (#3487: a regression after a fix is diagnosed
|
|
89
|
+
again, once per reopen). `gh issue view` lists comments oldest first."""
|
|
90
|
+
last_diagnosis = last_reopen = -1
|
|
91
|
+
for index, comment in enumerate(comments or []):
|
|
78
92
|
body = comment.get("body", "") if isinstance(comment, dict) else ""
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return False
|
|
93
|
+
if _is_diagnosis(body):
|
|
94
|
+
last_diagnosis = index
|
|
95
|
+
elif is_reopen_comment(body):
|
|
96
|
+
last_reopen = index
|
|
97
|
+
return last_diagnosis < 0 or last_reopen > last_diagnosis
|
|
85
98
|
|
|
86
99
|
|
|
87
100
|
def _unparseable_diagnosis_payload(number: int, error: str) -> EscalationPayload:
|
|
@@ -168,11 +181,15 @@ class FindingsDiagnoseProvider:
|
|
|
168
181
|
if dial != _DIAGNOSE_DIAL_AUTO:
|
|
169
182
|
continue
|
|
170
183
|
|
|
171
|
-
|
|
184
|
+
relabelled = REDIAGNOSE_LABEL in issue.labels
|
|
185
|
+
if not relabelled and not _needs_diagnosis(comments):
|
|
172
186
|
continue
|
|
173
187
|
|
|
174
188
|
item_id = f"{_ITEM_ID_PREFIX}{issue.number}"
|
|
175
|
-
self._rows[item_id] = {
|
|
189
|
+
self._rows[item_id] = {
|
|
190
|
+
"number": issue.number, "title": issue.title, "body": body,
|
|
191
|
+
"relabelled": relabelled, "labels": tuple(issue.labels),
|
|
192
|
+
}
|
|
176
193
|
items.append(
|
|
177
194
|
ActionItem(
|
|
178
195
|
item_id=item_id,
|
|
@@ -221,11 +238,59 @@ class FindingsDiagnoseProvider:
|
|
|
221
238
|
],
|
|
222
239
|
check=False,
|
|
223
240
|
)
|
|
241
|
+
self._label_severity(number, rendered, row.get("labels", ()))
|
|
242
|
+
if row.get("relabelled"):
|
|
243
|
+
# Consume the request so the next run does not diagnose again.
|
|
244
|
+
self._runner(
|
|
245
|
+
["gh", "issue", "edit", str(number), "--repo", self._repo,
|
|
246
|
+
"--remove-label", REDIAGNOSE_LABEL],
|
|
247
|
+
check=False,
|
|
248
|
+
)
|
|
224
249
|
return ActionOutcome(
|
|
225
250
|
status=STATUS_COMPLETED,
|
|
226
251
|
detail=f"issue #{number} diagnosed",
|
|
227
252
|
spend=spend,
|
|
228
253
|
)
|
|
229
254
|
|
|
255
|
+
def _read_labels(self, number: int) -> set:
|
|
256
|
+
result = self._runner(
|
|
257
|
+
["gh", "issue", "view", str(number), "--repo", self._repo, "--json", "labels"],
|
|
258
|
+
check=False,
|
|
259
|
+
)
|
|
260
|
+
if getattr(result, "returncode", 1) != 0 or not getattr(result, "stdout", None):
|
|
261
|
+
return set()
|
|
262
|
+
try:
|
|
263
|
+
parsed = json.loads(result.stdout)
|
|
264
|
+
except ValueError:
|
|
265
|
+
return set()
|
|
266
|
+
entries = parsed.get("labels") if isinstance(parsed, dict) else None
|
|
267
|
+
return {
|
|
268
|
+
e["name"] for e in entries or []
|
|
269
|
+
if isinstance(e, dict) and isinstance(e.get("name"), str)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
def _label_severity(self, number: int, rendered: str, labels) -> None:
|
|
273
|
+
"""Give the finding exactly one `severity:<value>` label, replacing any
|
|
274
|
+
previous one. No severity on the diagnosis, no label write."""
|
|
275
|
+
diagnosis = parse(rendered)
|
|
276
|
+
severity = diagnosis.severity if diagnosis is not None else None
|
|
277
|
+
if severity is None:
|
|
278
|
+
return
|
|
279
|
+
wanted = f"{SEVERITY_LABEL_PREFIX}{severity}"
|
|
280
|
+
self._runner(
|
|
281
|
+
["gh", "label", "create", wanted, "--force", "--repo", self._repo],
|
|
282
|
+
check=False,
|
|
283
|
+
)
|
|
284
|
+
current = set(labels) | self._read_labels(number)
|
|
285
|
+
stale = [
|
|
286
|
+
label for label in sorted(current)
|
|
287
|
+
if label.startswith(SEVERITY_LABEL_PREFIX) and label != wanted
|
|
288
|
+
]
|
|
289
|
+
argv = ["gh", "issue", "edit", str(number), "--repo", self._repo]
|
|
290
|
+
for label in stale:
|
|
291
|
+
argv += ["--remove-label", label]
|
|
292
|
+
argv += ["--add-label", wanted]
|
|
293
|
+
self._runner(argv, check=False)
|
|
294
|
+
|
|
230
295
|
def reconcile(self, item: ActionItem, outcome: ActionOutcome) -> None:
|
|
231
296
|
return None
|
|
@@ -303,7 +303,8 @@ class TriageProvider:
|
|
|
303
303
|
|
|
304
304
|
rubric = self._rubric_for_run()
|
|
305
305
|
argv, prompt = build_invocation(
|
|
306
|
-
number, title, body, self._repo, self._working_dir, rubric=rubric
|
|
306
|
+
number, title, body, self._repo, self._working_dir, rubric=rubric,
|
|
307
|
+
comments=row.get("comments") or [],
|
|
307
308
|
)
|
|
308
309
|
invoked = run_claude(argv, prompt, runner=self._runner)
|
|
309
310
|
spend = usage_spend(invoked.usage)
|
|
@@ -368,6 +369,9 @@ class TriageProvider:
|
|
|
368
369
|
"category": category,
|
|
369
370
|
"confidence": confidence,
|
|
370
371
|
"severity": severity,
|
|
372
|
+
# #3521: without the issue's labels `apply_triage` cannot see
|
|
373
|
+
# a person's `severity:*` label, and adds a second one beside it.
|
|
374
|
+
"labels": row.get("labels") or [],
|
|
371
375
|
}
|
|
372
376
|
]
|
|
373
377
|
results = apply_triage(
|
|
@@ -391,11 +395,14 @@ class TriageProvider:
|
|
|
391
395
|
spend=spend,
|
|
392
396
|
)
|
|
393
397
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
detail
|
|
397
|
-
|
|
398
|
-
|
|
398
|
+
detail = f"issue #{number} triaged as {row_result.get('category')}"
|
|
399
|
+
if row_result.get("severity_conflict"):
|
|
400
|
+
detail += (
|
|
401
|
+
f"; severity label left as "
|
|
402
|
+
f"{', '.join(row_result['severity_conflict'])} "
|
|
403
|
+
f"(triage judged {severity})"
|
|
404
|
+
)
|
|
405
|
+
return ActionOutcome(status=STATUS_COMPLETED, detail=detail, spend=spend)
|
|
399
406
|
|
|
400
407
|
def reconcile(self, item: ActionItem, outcome: ActionOutcome) -> None:
|
|
401
408
|
return None
|
|
@@ -62,6 +62,7 @@ from specfuse.loop.build_provenance import warn_if_out_of_tree
|
|
|
62
62
|
from specfuse.loop.escalation import (
|
|
63
63
|
CREATED_NUMBER_UNKNOWN,
|
|
64
64
|
annotate_escalation,
|
|
65
|
+
clear_closed_escalation_labels,
|
|
65
66
|
emit_escalation,
|
|
66
67
|
)
|
|
67
68
|
|
|
@@ -198,8 +199,8 @@ class ActionOutcome:
|
|
|
198
199
|
`spend` is real tokens, not a proxy — a provider that dispatches a
|
|
199
200
|
headless `claude` session reports it through
|
|
200
201
|
`specfuse.agent.invoke.usage_spend(invoke_result.usage)`
|
|
201
|
-
(FEAT-2026-0108/T01):
|
|
202
|
-
|
|
202
|
+
(FEAT-2026-0108/T01): all four usage token fields, cache reads and cache
|
|
203
|
+
creation included (#3522). It defaults to zero so a
|
|
203
204
|
provider that dispatches nothing, or whose session reported no usage
|
|
204
205
|
envelope, leaves the run's total spend untouched. `escalation`, when set
|
|
205
206
|
on a `STATUS_ESCALATED` outcome, is recorded — on `target_issue` when the
|
|
@@ -299,6 +300,10 @@ class RunSummary:
|
|
|
299
300
|
#: re-run, because a triage-only run otherwise looks like a bug lane that
|
|
300
301
|
#: declined everything.
|
|
301
302
|
newly_triaged_bugs_undispatched: tuple = ()
|
|
303
|
+
#: `(number, reason)` for bugs this run triaged that the severity floor
|
|
304
|
+
#: holds back (#3521). Kept apart from the re-run list above: a re-run
|
|
305
|
+
#: would not dispatch them either, so the summary names the floor instead.
|
|
306
|
+
newly_triaged_bugs_below_floor: tuple = ()
|
|
302
307
|
|
|
303
308
|
|
|
304
309
|
def _bug_marked_numbers(snapshot: AgentSnapshot) -> set:
|
|
@@ -306,6 +311,30 @@ def _bug_marked_numbers(snapshot: AgentSnapshot) -> set:
|
|
|
306
311
|
return {issue.number for issue in snapshot.issues if issue.triage_category == "bug"}
|
|
307
312
|
|
|
308
313
|
|
|
314
|
+
def _split_by_severity_floor(numbers, snapshot: AgentSnapshot,
|
|
315
|
+
policy_path: Optional[str]) -> tuple:
|
|
316
|
+
"""`(re-runnable, [(number, reason), ...])` for bugs triaged and never
|
|
317
|
+
dispatched (#3521).
|
|
318
|
+
|
|
319
|
+
Reads the floor exactly as `BugsProvider.advertise` does, so a bug this
|
|
320
|
+
returns as held back is one the next run's lane would skip too -- telling
|
|
321
|
+
the operator to re-run for it was a promise the lane would not keep.
|
|
322
|
+
"""
|
|
323
|
+
floor = agent_policy.resolve_min_severity(policy_path)
|
|
324
|
+
aliases = agent_policy.resolve_severity_aliases(policy_path)
|
|
325
|
+
labels_by_number = {issue.number: issue.labels for issue in snapshot.issues}
|
|
326
|
+
rerun, held = [], []
|
|
327
|
+
for number in numbers:
|
|
328
|
+
severity, via = agent_policy.read_severity_label(
|
|
329
|
+
labels_by_number.get(number), aliases)
|
|
330
|
+
ok, why = agent_policy.meets_severity_floor(severity, floor, via=via)
|
|
331
|
+
if ok:
|
|
332
|
+
rerun.append(number)
|
|
333
|
+
else:
|
|
334
|
+
held.append((number, why))
|
|
335
|
+
return rerun, held
|
|
336
|
+
|
|
337
|
+
|
|
309
338
|
def _refresh_snapshot(
|
|
310
339
|
previous: AgentSnapshot,
|
|
311
340
|
*,
|
|
@@ -838,6 +867,13 @@ def run_agent(
|
|
|
838
867
|
policy_path=policy_path,
|
|
839
868
|
features_root=features_root,
|
|
840
869
|
)
|
|
870
|
+
# #3523: a closed issue keeps no escalation labels the lane wrote.
|
|
871
|
+
cleared = clear_closed_escalation_labels(runner, repo)
|
|
872
|
+
if cleared:
|
|
873
|
+
report(
|
|
874
|
+
"cleared needs-human from closed escalated issue(s): "
|
|
875
|
+
+ ", ".join(f"#{number}" for number in cleared)
|
|
876
|
+
)
|
|
841
877
|
bugs_preempt = _resolve_bugs_preempt(policy_path)
|
|
842
878
|
report(
|
|
843
879
|
f"snapshot: {len(snapshot.issues)} open issues, "
|
|
@@ -1179,6 +1215,10 @@ def run_agent(
|
|
|
1179
1215
|
for number in _bug_marked_numbers(snapshot) - bugs_marked_at_start
|
|
1180
1216
|
if f"bug-{number}" not in handled_ids
|
|
1181
1217
|
)
|
|
1218
|
+
newly_triaged, below_floor = _split_by_severity_floor(
|
|
1219
|
+
newly_triaged, snapshot, policy_path)
|
|
1220
|
+
for number, why in below_floor:
|
|
1221
|
+
report(f"#{number} newly triaged as bug, not dispatched — {why}")
|
|
1182
1222
|
if newly_triaged:
|
|
1183
1223
|
report(
|
|
1184
1224
|
f"{len(newly_triaged)} issue(s) newly triaged as bug and not "
|
|
@@ -1200,6 +1240,7 @@ def run_agent(
|
|
|
1200
1240
|
escalations=tuple(escalations),
|
|
1201
1241
|
wip_refs=tuple(wip_refs),
|
|
1202
1242
|
newly_triaged_bugs_undispatched=tuple(newly_triaged),
|
|
1243
|
+
newly_triaged_bugs_below_floor=tuple(below_floor),
|
|
1203
1244
|
)
|
|
1204
1245
|
finally:
|
|
1205
1246
|
lock_fd.close()
|
|
@@ -1229,6 +1270,8 @@ def _format_summary(summary: RunSummary) -> str:
|
|
|
1229
1270
|
f" {len(summary.newly_triaged_bugs_undispatched)} newly triaged "
|
|
1230
1271
|
f"as bug, not dispatched — re-run to fix them: {numbers}"
|
|
1231
1272
|
)
|
|
1273
|
+
for number, why in summary.newly_triaged_bugs_below_floor:
|
|
1274
|
+
lines.append(f" #{number} newly triaged as bug, not dispatched — {why}")
|
|
1232
1275
|
return "\n".join(lines)
|
|
1233
1276
|
|
|
1234
1277
|
|
|
@@ -47,6 +47,7 @@ def build_invocation(
|
|
|
47
47
|
model: str = "sonnet",
|
|
48
48
|
effort: str = "medium",
|
|
49
49
|
rubric: Optional[dict] = None,
|
|
50
|
+
comments: Optional[list] = None,
|
|
50
51
|
):
|
|
51
52
|
"""Build argv and prompt text for a headless triage-classification
|
|
52
53
|
session against one issue. Returns a `(argv, prompt)` tuple; runs
|
|
@@ -60,6 +61,12 @@ def build_invocation(
|
|
|
60
61
|
session -- and the marker instruction grows a third `severity=` field.
|
|
61
62
|
The prompt is built from `rubric`'s own mapping alone; it holds no copy
|
|
62
63
|
of any shipped default definitions.
|
|
64
|
+
|
|
65
|
+
`comments` is the issue's comment list as `gh issue list --json comments`
|
|
66
|
+
returns it (#3520): a maintainer often routes an issue -- folds it into a
|
|
67
|
+
feature, marks it a duplicate -- in a comment, and a classifier reading
|
|
68
|
+
the body alone labels it `bug` anyway. No comments leaves the prompt
|
|
69
|
+
byte-identical to the one built without them.
|
|
63
70
|
"""
|
|
64
71
|
argv = build_claude_argv(model, effort)
|
|
65
72
|
if rubric:
|
|
@@ -88,6 +95,7 @@ def build_invocation(
|
|
|
88
95
|
f"Issue number: {issue_number}\n"
|
|
89
96
|
f"Issue title: {title}\n\n"
|
|
90
97
|
f"Issue body:\n{body}\n\n"
|
|
98
|
+
f"{_comments_block(comments)}"
|
|
91
99
|
"Classify this issue into exactly one of the categories: "
|
|
92
100
|
f"{', '.join(CATEGORIES)}.\n\n"
|
|
93
101
|
f"{marker_instruction}"
|
|
@@ -95,6 +103,23 @@ def build_invocation(
|
|
|
95
103
|
return argv, prompt
|
|
96
104
|
|
|
97
105
|
|
|
106
|
+
def _comments_block(comments: Optional[list]) -> str:
|
|
107
|
+
if not comments:
|
|
108
|
+
return ""
|
|
109
|
+
rendered = []
|
|
110
|
+
for comment in comments:
|
|
111
|
+
author = (comment.get("author") or {}).get("login") or "unknown"
|
|
112
|
+
association = comment.get("authorAssociation") or "NONE"
|
|
113
|
+
rendered.append(f"--- @{author} ({association}):\n{comment.get('body') or ''}")
|
|
114
|
+
return (
|
|
115
|
+
"Issue comments, oldest first:\n"
|
|
116
|
+
+ "\n\n".join(rendered)
|
|
117
|
+
+ "\n\nIf a maintainer comment already routes this issue -- folds it "
|
|
118
|
+
"into a feature or another issue, or marks it a duplicate -- classify "
|
|
119
|
+
"it by that decision, not by the body alone.\n\n"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
98
123
|
def classify_result(result_text: str) -> Optional[tuple]:
|
|
99
124
|
"""Return the `(category, confidence)` pair a classification session's
|
|
100
125
|
output names, or `None` if the output carries no parseable triage
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.29.1
|