specfuse-loop 0.28.1__tar.gz → 0.29.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.28.1/specfuse_loop.egg-info → specfuse_loop-0.29.0}/PKG-INFO +1 -1
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/pyproject.toml +1 -1
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/diagnose_invoke.py +6 -2
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/providers/findings_autofix.py +26 -2
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/providers/findings_diagnose.py +80 -15
- specfuse_loop-0.29.0/specfuse/loop/data/VERSION +1 -0
- specfuse_loop-0.29.0/specfuse/loop/data/docs/concepts/monitoring-runners.md +199 -0
- specfuse_loop-0.29.0/specfuse/loop/data/docs/concepts/monitoring-schema.md +211 -0
- specfuse_loop-0.29.0/specfuse/loop/data/docs/concepts/monitoring-to-fix.md +249 -0
- specfuse_loop-0.29.0/specfuse/loop/data/docs/flows.html +519 -0
- specfuse_loop-0.29.0/specfuse/loop/data/docs/lifecycles.md +394 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/gitignore.snippet +2 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/labels.py +8 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/loop.py +12 -3
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/scaffold.py +13 -3
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/artifact.py +11 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/cli.py +167 -5
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/diagnose_cli.py +21 -1
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/diagnosis.py +31 -1
- specfuse_loop-0.29.0/specfuse/monitor/issues.py +572 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/providers/azure_app_insights.py +377 -32
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/providers/azure_service_bus.py +157 -1
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/redaction.py +6 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse_loop.egg-info/SOURCES.txt +18 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_app_insights_query_schema_check.py +38 -2
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_app_insights_workspace_schema.py +6 -2
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_caller_check_ratchet.py +0 -1
- specfuse_loop-0.29.0/tests/test_diagnose_fenced_analysis.py +69 -0
- specfuse_loop-0.29.0/tests/test_diagnosis_severity_end_to_end.py +191 -0
- specfuse_loop-0.29.0/tests/test_diagnosis_severity_rubric.py +41 -0
- specfuse_loop-0.29.0/tests/test_flows_page.py +94 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_heartbeat_adapter.py +70 -4
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_init_integration.py +5 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_label_registry.py +4 -2
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_monitor_check_isolation.py +2 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_monitor_cli.py +10 -1
- specfuse_loop-0.29.0/tests/test_monitor_diagnose_auto_warning.py +120 -0
- specfuse_loop-0.29.0/tests/test_monitor_finding_context_end_to_end.py +463 -0
- specfuse_loop-0.29.0/tests/test_monitor_finding_lifecycle_end_to_end.py +268 -0
- specfuse_loop-0.29.0/tests/test_monitor_finding_meta.py +208 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_monitor_issue_lifecycle.py +17 -12
- specfuse_loop-0.29.0/tests/test_monitor_one_issue_per_fingerprint.py +110 -0
- specfuse_loop-0.29.0/tests/test_monitor_quiet_annotation.py +189 -0
- specfuse_loop-0.29.0/tests/test_rearm_close_broad_run.py +124 -0
- specfuse_loop-0.29.0/tests/test_rediagnose_on_reopen.py +151 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_data_in_sync.py +5 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_docs.py +5 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_init.py +5 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_resources.py +5 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_wiring.py +7 -1
- specfuse_loop-0.29.0/tests/test_upgrade_gitignore_lines.py +64 -0
- specfuse_loop-0.28.1/specfuse/loop/data/VERSION +0 -1
- specfuse_loop-0.28.1/specfuse/monitor/issues.py +0 -303
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/LICENSE +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/NOTICE +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/README.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/setup.cfg +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/__init__.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/budget.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/drafting_answers.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/drafting_invoke.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/drafting_questions.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/driver_command.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/driver_invoke.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/invoke.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/monitoring_read.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/providers/__init__.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/providers/answers.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/providers/bugs.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/providers/feature.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/providers/triage.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/queue_read.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/repo_detect.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/run.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/severity_backfill.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/triage_invoke.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/agent/worktree.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/_filelock.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/_wu_sections.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/agent_policy.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/arm_eval.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/arm_sweep.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/arm_txn.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/bug_lane.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/bug_lane_run.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/bug_lane_state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/build_provenance.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/caller_check.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/changelog.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/closing_requirements.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/cost.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/criteria_state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/glossary.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/methodology.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/monitoring.yml.example +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/close-discipline.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/human-output.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/operator-escalation.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/planning-discipline.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/verification-discipline.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/rules-local/README.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/event.schema.json +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/decisions_format.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/driver_edit.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/escalation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/events_stats.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/gate_commands.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/heartbeat.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/judge.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/learnings_distil.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/learnings_query.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/lint_closing.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/lint_monitoring.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/lint_plan.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/lint_roadmap.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/notify.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/notify_escalation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/notify_sla.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/plan_baseline.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/policy_proposals.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/policy_review.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/prerun.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/prerun_capture.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/promotion.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/rearm_migration.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/red_on_base.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/replay_spin.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/roadmap_archive.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/triage.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/__init__.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/adapters.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/autofix.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/autofix_invoke.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/autofix_run.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/autofix_state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/durations.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/fingerprint.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/providers/__init__.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/providers/_azure_auth.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse/monitor/schedule.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_abandon_feature_skill.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_abandoned_work_reported.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_abandoned_wu_strands_gate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_budget.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_bug_selection_dedup.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_default_features_root.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_driver_command.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_driver_invoke.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_driver_restart.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_escalation_on_issue.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_escalation_run_state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_findings_seam.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_invoke_timeout.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_invoke_usage.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_item_worktree.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_lock.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_policy_key_ownership.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_policy_queue.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_policy_schema.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_policy_triage_dial.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_provider_answers.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_provider_bugs.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_provider_feature.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_provider_findings_autofix.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_provider_findings_diagnose.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_provider_isolation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_provider_triage.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_queue_read.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_relays_driver_output.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_repo_detect.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_run.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_run_progress.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_seam.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_snapshot_refresh.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_agent_state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_answer_escalation_skill.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_app_insights_adapters.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_app_insights_harvest_window.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_archive_ref_reconciliation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_arm_eval.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_arm_eval_lint_class.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_arm_eval_wiring.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_arm_sweep.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_arm_sweep_gate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_arm_txn.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_arm_wiring.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_artifact_redaction.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_attempt_note_untracked_files.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_attempt_outcome_contract.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_attention_nonwriting_guard.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_attention_silence_section.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_attention_skill_structure.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_attribution_halt_claims.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autoclose_deferral_visibility.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autoclose_gate_note.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autoclose_stub_states_what_passed.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autofix_invoke.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autofix_live.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autofix_predicate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autofix_run.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autofix_state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autosync_no_cwd_leak.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_azure_credential_resolution.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_backend.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_baseline_escalation_message.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_baseline_persistence.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_baseline_probe.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_baseline_provenance.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_baseline_tree_hash_key.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bash_routing.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bats_suites_gated.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_binary_verdict.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_binding_block_advisory.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_binding_block_allocation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_binding_block_budget.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_blind_run_is_not_drained.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bookkeeping_no_verify.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_brief_covers_every_unit_escalation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_ci_pending.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_guardrails.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_label_nonfatal.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_labels_registered.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_min_severity.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_no_checks_pending.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_policy_contract.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_pr_number_carried.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_preflight_and_dial.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_run.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_spend.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_superseded.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_bug_lane_test_paths.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_build_provenance.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_carried_green_invalidated_by_diff.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_carry_accounting_close_and_judge.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_changed_file_test_selection.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_changelog_append_only.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_changelog_collection.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_changelog_release_wiring.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_changelog_schema.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_ci_conclusion_wait.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_ci_runs_every_gate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_claude_resolution.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_close_b_pending_learnings.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_close_records_feature_oracle.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_close_wu_self_reference.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_closing_guard_contracts.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_closing_guard_prediction.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_closing_learnings_destination.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_closing_requirements.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_closing_sessions_on_sonnet.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_coauthor_trailer_allowlist.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_conditional_reflection.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_console_encoding.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_convergent_iteration.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_convergent_wu_lint.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_correlation_id_override.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_correlation_id_two_surfaces.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_cost_lifetime.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_criteria_per_gate_lint.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_criteria_state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_criteria_worklist.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_decision_citation_lint.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_decision_followups_discharged.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_decision_override_lint.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_decisions_format.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_default_severity_aliases.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_degraded_oracle.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_deliverable_missing_note.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_derive_agent_policy_review_mode.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_derive_agent_policy_skill.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_derive_monitoring_discovery.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_derive_monitoring_skill_registration.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_design_for_diagnosis_rule.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_deterministic_refusal_repeat.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_diagnose_headless.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_diagnose_issue_skill.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_diagnosis.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_diagnosis_roundtrip_live.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_dispatch_model_from_fix_scope.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_dispatch_preamble_rules_pointer.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_dispatch_skeleton.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_dispatch_skills_index.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_distilled_accept_step.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_doctor_command_preflight.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_done_feature_gates.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_draft_feature_answers_mode.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_drafting_answer_gate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_drafting_invoke.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_drafting_question_issue.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_drafting_questions.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_drafting_reply_shape.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_driver_edit_detection.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_driver_event_registry_covers_emitters.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_driver_module_surface.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_driver_restart_halt_surfaced.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_driver_restart_halt_wiring.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_driver_restart_hold.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_driver_staleness_gate_summary.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_driver_staleness_warning.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_dry_run_no_branch_mutation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_ensure_feature_branch_base.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_escalation_assignee_policy.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_escalation_contract.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_escalation_crash_containment.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_escalation_dedup_search.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_escalation_emit.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_event_gate_full_envelope.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_events_redaction.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_events_stats.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_failure_artifact_model.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_failure_signature_fail_prefix.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_failure_signature_names_the_test.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_failure_signature_skips_log_noise.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_feature_branch_auto_sync.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_feature_branch_squash_merge.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_feature_branch_stale_base.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_feature_id_resolution.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_feature_oracle_e2e.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_feature_provider_drafting_dispatch.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_feature_review_accumulation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_filelock_portable.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_files_changed_auto_repair.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_find_pr_no_search_index.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_fingerprint.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_fix_bug_cwd_worktree.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_fix_bug_diff_self_check.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_fix_bug_headless.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_fix_bug_reads_comments.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_fix_unit_insertion_bookkeeping.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_fix_unit_insertion_e2e.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_fix_unit_insertion_refused.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_fold_contract_documented.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_followups_artifact.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_frontmatter_scalar_quoting.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gate_broad_run.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gate_budget.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gate_budget_post_dispatch.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gate_commands_needs.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gate_eval_intermediate_wiring.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gate_eval_repo_flag.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gate_eval_terminal_wiring.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gate_report_verdict.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_gitleaks_pinning.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_groom_backlog_skill.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_guard_refusal_failure_class.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_guard_refusal_failure_excerpt.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_guard_repair_e2e.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_guard_repair_prompt.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_headless_no_background_tasks.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_heartbeat.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_heartbeat_emission_contract.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_http_status_regression_adapter.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_http_status_regression_end_to_end.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_human_output_rule.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_human_work_unit.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_hygiene_boundary_guidance.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_hyphenated_gate_name.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_ignored_cleanup_before_probe.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_installed_copy_driver_e2e.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_interpreter_normalization.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_judge_close_path.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_judge_cost.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_judge_cost_fold_committed.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_judge_module.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_judge_path_registry.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_judge_sees_dropped_produces.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_judge_terminal_flip_state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_judged_event_points_at_findings.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_label_description_length.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_label_provisioning_runner_contract.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_label_registry_covers_consumers.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_language_aware_narrow_selection.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lazy_baseline_e2e.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_learnings_curate_vendored_promote.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_learnings_distil_cli.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_learnings_query.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_learnings_staging.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_learnings_suggest_signature_filter.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_learnings_weights.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lifetime_accounting.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_ac_observable.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_boundary_consistency.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_boundary_extraction.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_close_verdict_not_predecided.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_closing.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_closing_criteria.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_closing_criteria_pristine.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_covers_load_wu_invariants.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_feature_oracle_declared.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_gate_proportionality.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_monitoring.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_plan_contract_fields.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_plan_relative_import.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_plan_verdict_exempt.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_planning_sections.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_produces_satisfiability.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_roadmap.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_lint_verification_reference.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_live_tests_opt_in.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_load_bearing_close_lint.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_attempt_evidence.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_criteria_skeleton.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_criteria_survival.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_gate_needs.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_worklist_injection.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_make_repo_gpgsign_isolation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_marker_dual_shape.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_maven_failure_excerpt.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_max_attempts_configurable.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_max_open_prs.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_monitor_durations.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_monitor_runner_surfaces.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_monitoring_cron_dialect.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_monitoring_example.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_monitoring_fenced_blocks.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_monitoring_seed.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_narrow_fallback_warning.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_needs_decision_outcome.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_no_pytest_subprocess.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_notify.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_notify_escalation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_notify_sla.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_operator_escalation_rule.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_oracle_set_declared.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_oracle_unrunnable_attribution.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_per_run_item_cap_disclosure.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_pick_feature_skill.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_pin_honesty_and_integrity.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_pin_marker_not_inherited.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_pin_marker_spawn_sites.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_plan_baseline.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_planning_cost_floor.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_plugin_prose_cross_refs.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_plugin_qualified_dispatch.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_policy_budgets_applied.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_policy_proposals.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_policy_review.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_pr_checks_json_field.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_pr_reference_in_title.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_prep_halt_operator_surface.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_prepare_feature.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_prepare_scaffold_sync.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_prerun_capture.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_prerun_oracles.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_prerun_wiring.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produced_document_verdict_guard.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produces_amendment_e2e.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produces_bracket_literal.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produces_bracket_wildcard.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produces_in_diff.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produces_justification.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produces_recursive_glob.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produces_repair_note.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produces_semantics_unified.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_produces_shape.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_progress_forward_note.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_progress_lines_end_to_end.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_promoted_issue_not_redispatched.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_promotion_ledger.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_provider_escalation_traces.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_provision_labels.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_queue_stalled_adapter.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_rearm_fold_marker.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_rearm_migration.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_reclose_carries_narrow_greens_e2e.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_red_baseline_never_reused.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_red_on_base_fetch_and_reason.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_red_on_base_guardrail.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_red_on_base_overlay.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_red_on_base_probe.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_red_on_base_real_runner.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_redaction_windows_home.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_reescalation_posts_comment.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_reflection_required_signals.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_replan_end_to_end.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_replan_event_emission.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_replan_note_collision.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_replan_trigger.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_replan_turn_contract.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_replay_spin.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_replay_spin_failing_sets.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_required_checks.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_resolve_base.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_restart_cap_counts_progress.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_result_block_audience.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_result_block_near_miss.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_result_block_scalar.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_resume_command_build.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_retain_on_guard_refusal_docs.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_roadmap_archive_cli.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_roadmap_archive_shim.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_roadmap_archive_skill_delegates.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_roadmap_link_gate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_ruff_verdict_lines.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_rule_import_convergence.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_run_level_failure_breaker.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_doc_hygiene.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_label_provisioning.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_skew_halts.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_schedule_dialect.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_seed_documents_prerun_keys.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_service_bus_dlq_adapter.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_session_env_preflight.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_severity_aliases.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_severity_backfill_apply.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_severity_backfill_bugs_only.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_severity_backfill_end_to_end.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_severity_backfill_limit.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_severity_backfill_marker.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_severity_backfill_run.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_severity_label_projection.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_severity_reconciliation.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_severity_rubric.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_shipped_templates_parse.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_skill_discovery_links.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_skills_vendored_in_sync.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_spec_work_unit_rules.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_spinning_rearm_gate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_spinning_repeat_same_failing_set.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_spinout_brief_end_to_end.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_spinout_brief_replan_option.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_stop_rationale_captured.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_stranded_halt_cause.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_surefire_run_level_signature.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_surefire_verdict_lines.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_terminal_auto_close_changelog.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_terminal_gate_message.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_tiered_verification_e2e.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_timeout_kill_portable.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_triage.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_triage_apply.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_triage_projects_severity_label.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_triage_severity_classify.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_triage_severity_end_to_end.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_triage_severity_write.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_triage_skill_contract.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_triage_skips_agent_escalations.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_tsc_verdict_lines.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_umbrella_version_resolution.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_unreadable_pr_listing_holds_pr_work.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_unterminated_frontmatter.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_untracked_file_absorption.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_validate_event_driver_types.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_windows_gate_exec.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_worktree_restore.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.0}/tests/test_wu_execution_metadata.py +0 -0
- {specfuse_loop-0.28.1 → specfuse_loop-0.29.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.29.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"
|
|
@@ -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
|
)
|
|
@@ -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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.29.0
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Where the monitoring cycle runs — the `runner` dial and its surfaces
|
|
2
|
+
|
|
3
|
+
`.specfuse/monitoring.yml` gives every component a `runner` dial: `local`,
|
|
4
|
+
`gh-actions`, or `in-cluster`. The dial says *where that component's
|
|
5
|
+
diagnose/autofix cycle executes* — it is a routing decision, not a feature
|
|
6
|
+
flag, and it does not gate any code path inside the cycle itself. This
|
|
7
|
+
document covers the two surfaces FEAT-2026-0040 ships and states plainly
|
|
8
|
+
what is still missing.
|
|
9
|
+
|
|
10
|
+
What happens to a finding after it is filed — diagnosis, the autofix decision,
|
|
11
|
+
the fix — and which processes must be scheduled for it is in
|
|
12
|
+
[`monitoring-to-fix.md`](monitoring-to-fix.md).
|
|
13
|
+
`specfuse monitor run` does not diagnose anything itself: when it files or
|
|
14
|
+
updates findings for a component set to `diagnose: auto`, its summary prints one
|
|
15
|
+
line saying those findings wait for `specfuse agent run` (specfuse-agent).
|
|
16
|
+
|
|
17
|
+
## The two shipped surfaces
|
|
18
|
+
|
|
19
|
+
**Local.** An operator, or a cron entry on a machine they control, runs
|
|
20
|
+
`specfuse monitor run`. This is the recommended starting point — the
|
|
21
|
+
schema's own `monitoring.yml.example` advises starting every component at
|
|
22
|
+
`runner: local` before loosening any dial. Nothing extra to install — the
|
|
23
|
+
`specfuse` suite already carries it (`pipx install specfuse`, which
|
|
24
|
+
hard-depends on the `specfuse-loop` distribution this CLI ships in). The CLI
|
|
25
|
+
reads `.specfuse/monitoring.yml` from the current working directory the same
|
|
26
|
+
way on this surface as any other.
|
|
27
|
+
|
|
28
|
+
**GitHub Actions.** A shipped, scheduled workflow template runs the same
|
|
29
|
+
cycle in CI. It lives at
|
|
30
|
+
`specfuse/loop/data/workflows/specfuse-monitor.yml` in the `specfuse-loop`
|
|
31
|
+
distribution — this is a *template*, not a workflow this repository runs
|
|
32
|
+
itself (this repository is a CLI tool with no deployable components and
|
|
33
|
+
will never carry a real `monitoring.yml`; see `verification.yml`). To use
|
|
34
|
+
it in a consumer project:
|
|
35
|
+
|
|
36
|
+
1. Copy `specfuse-monitor.yml` into that project's own `.github/workflows/`.
|
|
37
|
+
You already have it if `specfuse` is installed — it ships inside the
|
|
38
|
+
`specfuse_loop` package data, so
|
|
39
|
+
`python -c "import specfuse.loop, pathlib; print(pathlib.Path(specfuse.loop.__file__).parent / 'data/workflows/specfuse-monitor.yml')"`
|
|
40
|
+
prints its path. It is also readable from this repository's
|
|
41
|
+
`specfuse/loop/data/workflows/`.
|
|
42
|
+
2. Add the secrets it references — one per credential-bearing environment
|
|
43
|
+
variable named in that project's `.specfuse/monitoring.yml` (`api_key`,
|
|
44
|
+
`connection_string`, etc.), plus a token with permission to file issues
|
|
45
|
+
(`GITHUB_TOKEN` is sufficient for issues in the same repository) — under
|
|
46
|
+
the consumer repository's Settings → Secrets and variables → Actions.
|
|
47
|
+
Every value the template consumes is a `${{ secrets.* }}` reference; it
|
|
48
|
+
never embeds a literal credential.
|
|
49
|
+
3. Dial the components that should run here to `runner: gh-actions` in that
|
|
50
|
+
project's `.specfuse/monitoring.yml`.
|
|
51
|
+
|
|
52
|
+
The template declares both `on.schedule` (a cron expression) and
|
|
53
|
+
`on.workflow_dispatch`, so it can be triggered by hand while debugging, not
|
|
54
|
+
only on its schedule. It requests exactly `permissions: {issues: write,
|
|
55
|
+
contents: read}` — least privilege for filing issues, nothing more — rather
|
|
56
|
+
than inheriting the default write-all token.
|
|
57
|
+
|
|
58
|
+
## The dial routes, and skips are reported, not silent
|
|
59
|
+
|
|
60
|
+
`specfuse monitor run` takes a `--runner` flag naming which surface this
|
|
61
|
+
invocation *is* (defaulting to `local`). It enumerates only the components
|
|
62
|
+
whose `runner` matches that flag; every other component is named in the run
|
|
63
|
+
summary along with the surface it belongs to, so a skipped component is
|
|
64
|
+
visible, not silently unmonitored. A component whose `runner` value isn't
|
|
65
|
+
one of `local` / `gh-actions` / `in-cluster` is a clear error naming the bad
|
|
66
|
+
value and the supported set — a typo'd dial must fail loudly, not run
|
|
67
|
+
nothing.
|
|
68
|
+
|
|
69
|
+
## `in-cluster` is out of scope here
|
|
70
|
+
|
|
71
|
+
`in-cluster` is a valid schema value today, but no runner surface
|
|
72
|
+
implements it in this feature. A component dialed to `runner: in-cluster`
|
|
73
|
+
is reported by `specfuse monitor run` as unhandled by design — naming
|
|
74
|
+
**FEAT-2026-0043**, which owns that surface — and is neither dropped
|
|
75
|
+
silently nor treated as an error. Do not dial a component to `in-cluster`
|
|
76
|
+
expecting it to be monitored until that feature ships.
|
|
77
|
+
|
|
78
|
+
## The harvest window
|
|
79
|
+
|
|
80
|
+
Each run reads only what is new since the previous one. The window for an
|
|
81
|
+
environment starts at its watermark (the time the previous non-dry run
|
|
82
|
+
started, kept in `.specfuse/monitor-watermarks/<env>.json`) and ends at this
|
|
83
|
+
run's start. With no readable watermark (a first run, a fresh CI runner, a new
|
|
84
|
+
operator) it starts 24 hours back instead, and the run summary says so.
|
|
85
|
+
|
|
86
|
+
The window applies to the checks that read event history: `error-logs` and
|
|
87
|
+
`http-5xx` filter on `ingestion_time()`, so a row stamped just before the
|
|
88
|
+
previous run but ingested after it is read by the next run rather than lost
|
|
89
|
+
between the two. Three check types are deliberately not bounded by it:
|
|
90
|
+
|
|
91
|
+
- `heartbeat` needs the most recent heartbeat however old it is;
|
|
92
|
+
- `http-status-regression` compares the windows around each deployment and
|
|
93
|
+
carries its own fixed lookback;
|
|
94
|
+
- `invariant` runs the operator's query as written. A query that reads event
|
|
95
|
+
history should bound itself (for example `| where TimeGenerated > ago(1h)`);
|
|
96
|
+
otherwise every matching row in retention is a finding, deduplicated only by
|
|
97
|
+
`fingerprint_by`.
|
|
98
|
+
|
|
99
|
+
A run in which any check errored leaves the watermark where it was, so the
|
|
100
|
+
next run retries the same window.
|
|
101
|
+
|
|
102
|
+
The watermark directory is local runtime state, not something to commit.
|
|
103
|
+
`specfuse init` adds `.specfuse/monitor-watermarks/` to a new project's
|
|
104
|
+
`.gitignore`, and `specfuse upgrade` adds it to a project initialised earlier
|
|
105
|
+
(it appends any snippet line the file lacks, leaving your own lines alone). On a CI
|
|
106
|
+
runner the directory starts empty every run — the shipped workflow template
|
|
107
|
+
does not cache it — so each run there reads the 24-hour fallback window.
|
|
108
|
+
|
|
109
|
+
## What `--dry-run` does and does not gate
|
|
110
|
+
|
|
111
|
+
`--dry-run` performs the read-only `fetch_failures()` calls against real
|
|
112
|
+
telemetry/broker transports — it is not an offline mode. It gates only the
|
|
113
|
+
writes: the watermark file and every `gh` invocation. If you point a dry
|
|
114
|
+
run at a production telemetry backend, it will consume that backend's read
|
|
115
|
+
quota exactly as a real run would.
|
|
116
|
+
|
|
117
|
+
## What a finding is, and how its issue is kept
|
|
118
|
+
|
|
119
|
+
Every finding is one GitHub issue per fingerprint. The fingerprint is the
|
|
120
|
+
`specfuse:finding` marker in the issue body, and it is the only thing that
|
|
121
|
+
identifies a finding: nothing a finding *carries* (its title, its context, the
|
|
122
|
+
time it was seen, a dead-lettered message's id) enters it.
|
|
123
|
+
|
|
124
|
+
### A recurrence reopens; it never refiles, and nothing is ever closed
|
|
125
|
+
|
|
126
|
+
On each run, a fingerprint is matched against issues in three states:
|
|
127
|
+
|
|
128
|
+
- **Open** — the issue is updated, as before.
|
|
129
|
+
- **Closed within the last 90 days** — the issue is **reopened** with exactly one
|
|
130
|
+
comment carrying the new occurrence count and the first and last time it was
|
|
131
|
+
seen, in UTC. No second issue is filed, so the earlier diagnosis and any
|
|
132
|
+
"reopen if…" note stay attached to the finding.
|
|
133
|
+
- **Absent, or closed longer ago than 90 days** — a new issue is filed. After 90
|
|
134
|
+
quiet days that is the honest reading, and the bound keeps the listing under
|
|
135
|
+
GitHub's 100-row limit.
|
|
136
|
+
|
|
137
|
+
The monitor **reopens, never closes.** Closing on silence would erase the evidence
|
|
138
|
+
of an intermittent fault, so closing stays a human decision, and no code in
|
|
139
|
+
`specfuse/monitor` issues a close. A finding-meta marker the monitor cannot fully
|
|
140
|
+
understand (a hand-edited or truncated one) reads as absent rather than failing
|
|
141
|
+
the run.
|
|
142
|
+
|
|
143
|
+
### A finding that goes quiet is annotated, once
|
|
144
|
+
|
|
145
|
+
An open finding whose fingerprint has not been seen for **7 days** gets one
|
|
146
|
+
comment naming it a candidate for close, and a marker in its body so the comment
|
|
147
|
+
is not repeated. The monitor only does this for a component every one of whose
|
|
148
|
+
checks ran without error in that cycle — a broken check never makes a finding
|
|
149
|
+
look quiet — and it still closes nothing. If the fingerprint fires again, the
|
|
150
|
+
issue's body is rewritten without the marker, so a later quiet period is
|
|
151
|
+
annotated again. The pass reuses the cycle's own issue listing; `--dry-run`
|
|
152
|
+
prints the issues it would annotate.
|
|
153
|
+
|
|
154
|
+
### `dlq`: closing an issue means "acknowledged"
|
|
155
|
+
|
|
156
|
+
A dead-lettered message stays in the queue until someone resubmits or purges it,
|
|
157
|
+
so it is seen on every run. The `dlq` finding records the message ids it has seen.
|
|
158
|
+
A closed `dlq` issue whose recorded ids cover every message currently in the queue
|
|
159
|
+
**stays closed**, with no write at all — closing it means "acknowledged, replay
|
|
160
|
+
pending". A message id the issue has not recorded **reopens** it, with a comment
|
|
161
|
+
naming that message. Messages are only ever peeked; the monitor never receives,
|
|
162
|
+
completes or abandons one.
|
|
163
|
+
|
|
164
|
+
### `heartbeat`: a schedule that has not emitted yet is not silent
|
|
165
|
+
|
|
166
|
+
A target whose latest expected firing precedes the component's earliest observed
|
|
167
|
+
heartbeat is *pending its first firing*, and produces no `silent-schedule`
|
|
168
|
+
finding. A target that has emitted before and then missed a firing still does, and
|
|
169
|
+
a component with no heartbeat at all files nothing.
|
|
170
|
+
|
|
171
|
+
### What a finding contains
|
|
172
|
+
|
|
173
|
+
The title reads `<component>: <failure class> in <where>`, with no fingerprint.
|
|
174
|
+
Each issue carries the labels `component:<name>` and `check:<type>` (created on
|
|
175
|
+
first use). Every body states the occurrence count, when it was first seen (the earliest
|
|
176
|
+
occurrence the run's rows show; for `dlq`, the message's enqueued time) and when
|
|
177
|
+
the issue was last updated (UTC), what was seen in this run (count, first, last, and the instances), and the
|
|
178
|
+
**Observed** line. Per check, the sections after **Observed** are:
|
|
179
|
+
|
|
180
|
+
| check | sections |
|
|
181
|
+
| --- | --- |
|
|
182
|
+
| `error-logs` | Where (top in-app stack frame), Request (route and result code), Logger (category and message template), Likely client abort, Samples, Reproduce |
|
|
183
|
+
| `http-5xx` | Exception (type, message), Where, Dependency (target, type, result code, duration), Duration, Samples, Reproduce |
|
|
184
|
+
| `dlq` | Message (id, sequence number, subject, enqueued time, delivery count), Dead-letter reason, or the Exception message and top stack frames when the reason is blank, Body shape (the top-level keys, never the values), Peek it |
|
|
185
|
+
| `heartbeat` | Schedule (cron, dialect, timezone, expected firing), Last run (the function's last execution and result), Host last seen, a Host silent line when the component sent no request telemetry across the expected firing, and Earliest heartbeat (the component's first heartbeat, which the grace rule measures from) |
|
|
186
|
+
| `queue-stalled` | Queue (oldest active message age, depth, `stall_after`), Where |
|
|
187
|
+
|
|
188
|
+
A section with nothing to say is left out rather than printed empty. A
|
|
189
|
+
`queue-stalled` finding cannot show when the subscription last completed a
|
|
190
|
+
message: Service Bus runtime properties carry no completion time (their
|
|
191
|
+
`accessed_at_utc` changes on sends as well as receives, so it would mislead),
|
|
192
|
+
and reading one from the messages themselves would mean receiving them, which
|
|
193
|
+
the check never does.
|
|
194
|
+
|
|
195
|
+
Each check still issues exactly one query. Every correlation (an exception to its
|
|
196
|
+
request, a request to its dependency, a schedule to its host's liveness) is a join
|
|
197
|
+
inside that query. The **Reproduce** and **Peek it** sections are text for a
|
|
198
|
+
human to run, built from configuration and sample ids, and the monitor never runs
|
|
199
|
+
them. Every section is redacted before it leaves the machine.
|