specfuse-loop 0.28.0__tar.gz → 0.28.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.0/specfuse_loop.egg-info → specfuse_loop-0.28.1}/PKG-INFO +1 -1
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/pyproject.toml +1 -1
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/severity_backfill.py +0 -3
- specfuse_loop-0.28.1/specfuse/loop/data/VERSION +1 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/loop.py +1 -1
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/cli.py +53 -11
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/providers/azure_app_insights.py +38 -4
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse_loop.egg-info/SOURCES.txt +2 -0
- specfuse_loop-0.28.1/tests/test_app_insights_harvest_window.py +132 -0
- specfuse_loop-0.28.1/tests/test_monitor_check_isolation.py +178 -0
- specfuse_loop-0.28.0/specfuse/loop/data/VERSION +0 -1
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/LICENSE +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/NOTICE +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/README.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/setup.cfg +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/__init__.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/budget.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/diagnose_invoke.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/drafting_answers.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/drafting_invoke.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/drafting_questions.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/driver_command.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/driver_invoke.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/invoke.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/monitoring_read.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/providers/__init__.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/providers/answers.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/providers/bugs.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/providers/feature.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/providers/findings_autofix.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/providers/findings_diagnose.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/providers/triage.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/queue_read.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/repo_detect.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/run.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/triage_invoke.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/agent/worktree.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/_filelock.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/_wu_sections.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/agent_policy.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/arm_eval.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/arm_sweep.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/arm_txn.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/bug_lane.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/bug_lane_run.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/bug_lane_state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/build_provenance.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/caller_check.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/changelog.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/closing_requirements.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/cost.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/criteria_state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/docs/glossary.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/docs/methodology.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/gitignore.snippet +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/monitoring.yml.example +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules/close-discipline.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules/human-output.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules/operator-escalation.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules/planning-discipline.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules/verification-discipline.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/rules-local/README.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/schemas/event.schema.json +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/decisions_format.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/driver_edit.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/escalation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/events_stats.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/gate_commands.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/heartbeat.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/judge.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/labels.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/learnings_distil.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/learnings_query.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/lint_closing.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/lint_monitoring.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/lint_plan.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/lint_roadmap.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/notify.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/notify_escalation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/notify_sla.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/plan_baseline.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/policy_proposals.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/policy_review.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/prerun.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/prerun_capture.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/promotion.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/rearm_migration.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/red_on_base.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/replay_spin.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/roadmap_archive.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/scaffold.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/triage.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/__init__.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/adapters.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/artifact.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/autofix.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/autofix_invoke.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/autofix_run.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/autofix_state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/diagnose_cli.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/diagnosis.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/durations.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/fingerprint.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/issues.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/providers/__init__.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/providers/_azure_auth.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/providers/azure_service_bus.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/redaction.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/schedule.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_abandon_feature_skill.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_abandoned_work_reported.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_abandoned_wu_strands_gate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_budget.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_bug_selection_dedup.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_default_features_root.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_driver_command.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_driver_invoke.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_driver_restart.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_escalation_on_issue.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_escalation_run_state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_findings_seam.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_invoke_timeout.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_invoke_usage.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_item_worktree.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_lock.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_policy_key_ownership.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_policy_queue.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_policy_schema.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_policy_triage_dial.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_provider_answers.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_provider_bugs.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_provider_feature.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_provider_findings_autofix.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_provider_findings_diagnose.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_provider_isolation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_provider_triage.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_queue_read.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_relays_driver_output.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_repo_detect.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_run.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_run_progress.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_seam.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_snapshot_refresh.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_agent_state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_answer_escalation_skill.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_app_insights_adapters.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_app_insights_query_schema_check.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_app_insights_workspace_schema.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_archive_ref_reconciliation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_arm_eval.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_arm_eval_lint_class.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_arm_eval_wiring.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_arm_sweep.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_arm_sweep_gate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_arm_txn.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_arm_wiring.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_artifact_redaction.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_attempt_note_untracked_files.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_attempt_outcome_contract.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_attention_nonwriting_guard.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_attention_silence_section.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_attention_skill_structure.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_attribution_halt_claims.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autoclose_deferral_visibility.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autoclose_gate_note.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autoclose_stub_states_what_passed.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autofix_invoke.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autofix_live.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autofix_predicate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autofix_run.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autofix_state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autosync_no_cwd_leak.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_azure_credential_resolution.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_backend.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_baseline_escalation_message.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_baseline_persistence.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_baseline_probe.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_baseline_provenance.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_baseline_tree_hash_key.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bash_routing.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bats_suites_gated.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_binary_verdict.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_binding_block_advisory.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_binding_block_allocation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_binding_block_budget.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_blind_run_is_not_drained.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bookkeeping_no_verify.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_brief_covers_every_unit_escalation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_ci_pending.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_guardrails.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_label_nonfatal.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_labels_registered.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_min_severity.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_no_checks_pending.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_policy_contract.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_pr_number_carried.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_preflight_and_dial.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_run.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_spend.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_superseded.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_bug_lane_test_paths.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_build_provenance.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_caller_check_ratchet.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_carried_green_invalidated_by_diff.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_carry_accounting_close_and_judge.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_changed_file_test_selection.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_changelog_append_only.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_changelog_collection.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_changelog_release_wiring.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_changelog_schema.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_ci_conclusion_wait.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_ci_runs_every_gate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_claude_resolution.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_close_b_pending_learnings.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_close_records_feature_oracle.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_close_wu_self_reference.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_closing_guard_contracts.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_closing_guard_prediction.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_closing_learnings_destination.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_closing_requirements.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_closing_sessions_on_sonnet.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_coauthor_trailer_allowlist.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_conditional_reflection.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_console_encoding.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_convergent_iteration.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_convergent_wu_lint.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_correlation_id_override.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_correlation_id_two_surfaces.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_cost_lifetime.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_criteria_per_gate_lint.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_criteria_state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_criteria_worklist.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_decision_citation_lint.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_decision_followups_discharged.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_decision_override_lint.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_decisions_format.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_default_severity_aliases.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_degraded_oracle.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_deliverable_missing_note.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_derive_agent_policy_review_mode.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_derive_agent_policy_skill.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_derive_monitoring_discovery.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_derive_monitoring_skill_registration.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_design_for_diagnosis_rule.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_deterministic_refusal_repeat.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_diagnose_headless.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_diagnose_issue_skill.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_diagnosis.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_diagnosis_roundtrip_live.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_dispatch_model_from_fix_scope.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_dispatch_preamble_rules_pointer.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_dispatch_skeleton.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_dispatch_skills_index.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_distilled_accept_step.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_doctor_command_preflight.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_done_feature_gates.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_draft_feature_answers_mode.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_drafting_answer_gate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_drafting_invoke.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_drafting_question_issue.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_drafting_questions.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_drafting_reply_shape.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_driver_edit_detection.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_driver_event_registry_covers_emitters.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_driver_module_surface.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_driver_restart_halt_surfaced.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_driver_restart_halt_wiring.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_driver_restart_hold.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_driver_staleness_gate_summary.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_driver_staleness_warning.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_dry_run_no_branch_mutation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_ensure_feature_branch_base.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_escalation_assignee_policy.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_escalation_contract.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_escalation_crash_containment.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_escalation_dedup_search.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_escalation_emit.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_event_gate_full_envelope.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_events_redaction.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_events_stats.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_failure_artifact_model.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_failure_signature_fail_prefix.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_failure_signature_names_the_test.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_failure_signature_skips_log_noise.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_feature_branch_auto_sync.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_feature_branch_squash_merge.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_feature_branch_stale_base.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_feature_id_resolution.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_feature_oracle_e2e.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_feature_provider_drafting_dispatch.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_feature_review_accumulation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_filelock_portable.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_files_changed_auto_repair.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_find_pr_no_search_index.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_fingerprint.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_fix_bug_cwd_worktree.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_fix_bug_diff_self_check.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_fix_bug_headless.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_fix_bug_reads_comments.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_fix_unit_insertion_bookkeeping.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_fix_unit_insertion_e2e.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_fix_unit_insertion_refused.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_fold_contract_documented.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_followups_artifact.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_frontmatter_scalar_quoting.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gate_broad_run.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gate_budget.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gate_budget_post_dispatch.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gate_commands_needs.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gate_eval_intermediate_wiring.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gate_eval_repo_flag.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gate_eval_terminal_wiring.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gate_report_verdict.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_gitleaks_pinning.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_groom_backlog_skill.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_guard_refusal_failure_class.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_guard_refusal_failure_excerpt.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_guard_repair_e2e.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_guard_repair_prompt.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_headless_no_background_tasks.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_heartbeat.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_heartbeat_adapter.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_heartbeat_emission_contract.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_http_status_regression_adapter.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_http_status_regression_end_to_end.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_human_output_rule.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_human_work_unit.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_hygiene_boundary_guidance.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_hyphenated_gate_name.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_ignored_cleanup_before_probe.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_init_integration.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_installed_copy_driver_e2e.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_interpreter_normalization.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_judge_close_path.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_judge_cost.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_judge_cost_fold_committed.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_judge_module.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_judge_path_registry.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_judge_sees_dropped_produces.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_judge_terminal_flip_state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_judged_event_points_at_findings.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_label_description_length.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_label_provisioning_runner_contract.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_label_registry.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_label_registry_covers_consumers.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_language_aware_narrow_selection.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lazy_baseline_e2e.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_learnings_curate_vendored_promote.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_learnings_distil_cli.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_learnings_query.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_learnings_staging.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_learnings_suggest_signature_filter.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_learnings_weights.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lifetime_accounting.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_ac_observable.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_boundary_consistency.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_boundary_extraction.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_close_verdict_not_predecided.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_closing.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_closing_criteria.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_closing_criteria_pristine.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_covers_load_wu_invariants.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_feature_oracle_declared.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_gate_proportionality.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_monitoring.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_plan_contract_fields.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_plan_relative_import.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_plan_verdict_exempt.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_planning_sections.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_produces_satisfiability.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_roadmap.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_lint_verification_reference.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_live_tests_opt_in.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_load_bearing_close_lint.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_attempt_evidence.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_criteria_skeleton.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_criteria_survival.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_gate_needs.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_worklist_injection.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_make_repo_gpgsign_isolation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_marker_dual_shape.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_maven_failure_excerpt.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_max_attempts_configurable.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_max_open_prs.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_monitor_cli.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_monitor_durations.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_monitor_issue_lifecycle.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_monitor_runner_surfaces.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_monitoring_cron_dialect.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_monitoring_example.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_monitoring_fenced_blocks.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_monitoring_seed.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_narrow_fallback_warning.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_needs_decision_outcome.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_no_pytest_subprocess.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_notify.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_notify_escalation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_notify_sla.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_operator_escalation_rule.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_oracle_set_declared.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_oracle_unrunnable_attribution.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_per_run_item_cap_disclosure.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_pick_feature_skill.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_pin_honesty_and_integrity.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_pin_marker_not_inherited.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_pin_marker_spawn_sites.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_plan_baseline.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_planning_cost_floor.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_plugin_prose_cross_refs.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_plugin_qualified_dispatch.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_policy_budgets_applied.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_policy_proposals.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_policy_review.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_pr_checks_json_field.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_pr_reference_in_title.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_prep_halt_operator_surface.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_prepare_feature.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_prepare_scaffold_sync.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_prerun_capture.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_prerun_oracles.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_prerun_wiring.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produced_document_verdict_guard.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produces_amendment_e2e.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produces_bracket_literal.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produces_bracket_wildcard.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produces_in_diff.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produces_justification.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produces_recursive_glob.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produces_repair_note.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produces_semantics_unified.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_produces_shape.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_progress_forward_note.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_progress_lines_end_to_end.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_promoted_issue_not_redispatched.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_promotion_ledger.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_provider_escalation_traces.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_provision_labels.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_queue_stalled_adapter.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_rearm_fold_marker.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_rearm_migration.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_reclose_carries_narrow_greens_e2e.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_red_baseline_never_reused.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_red_on_base_fetch_and_reason.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_red_on_base_guardrail.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_red_on_base_overlay.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_red_on_base_probe.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_red_on_base_real_runner.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_redaction_windows_home.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_reescalation_posts_comment.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_reflection_required_signals.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_replan_end_to_end.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_replan_event_emission.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_replan_note_collision.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_replan_trigger.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_replan_turn_contract.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_replay_spin.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_replay_spin_failing_sets.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_required_checks.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_resolve_base.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_restart_cap_counts_progress.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_result_block_audience.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_result_block_near_miss.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_result_block_scalar.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_resume_command_build.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_retain_on_guard_refusal_docs.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_roadmap_archive_cli.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_roadmap_archive_shim.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_roadmap_archive_skill_delegates.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_roadmap_link_gate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_ruff_verdict_lines.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_rule_import_convergence.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_run_level_failure_breaker.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_data_in_sync.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_doc_hygiene.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_docs.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_init.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_label_provisioning.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_resources.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_skew_halts.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_scaffold_wiring.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_schedule_dialect.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_seed_documents_prerun_keys.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_service_bus_dlq_adapter.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_session_env_preflight.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_severity_aliases.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_severity_backfill_apply.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_severity_backfill_bugs_only.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_severity_backfill_end_to_end.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_severity_backfill_limit.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_severity_backfill_marker.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_severity_backfill_run.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_severity_label_projection.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_severity_reconciliation.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_severity_rubric.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_shipped_templates_parse.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_skill_discovery_links.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_skills_vendored_in_sync.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_spec_work_unit_rules.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_spinning_rearm_gate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_spinning_repeat_same_failing_set.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_spinout_brief_end_to_end.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_spinout_brief_replan_option.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_stop_rationale_captured.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_stranded_halt_cause.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_surefire_run_level_signature.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_surefire_verdict_lines.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_terminal_auto_close_changelog.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_terminal_gate_message.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_tiered_verification_e2e.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_timeout_kill_portable.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_triage.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_triage_apply.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_triage_projects_severity_label.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_triage_severity_classify.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_triage_severity_end_to_end.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_triage_severity_write.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_triage_skill_contract.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_triage_skips_agent_escalations.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_tsc_verdict_lines.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_umbrella_version_resolution.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_unreadable_pr_listing_holds_pr_work.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_unterminated_frontmatter.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_untracked_file_absorption.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_validate_event_driver_types.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_windows_gate_exec.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_worktree_restore.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/tests/test_wu_execution_metadata.py +0 -0
- {specfuse_loop-0.28.0 → specfuse_loop-0.28.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.28.
|
|
7
|
+
version = "0.28.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"
|
|
@@ -46,9 +46,6 @@ from specfuse.loop.labels import (
|
|
|
46
46
|
from specfuse.loop import agent_policy, triage
|
|
47
47
|
from specfuse.loop.build_provenance import warn_if_out_of_tree
|
|
48
48
|
|
|
49
|
-
#: Stands in for T10's classification session (gate 3, walking skeleton).
|
|
50
|
-
_STUB_SEVERITY = "medium"
|
|
51
|
-
|
|
52
49
|
|
|
53
50
|
def _default_runner(argv: list, check: bool = False):
|
|
54
51
|
return subprocess.run(argv, check=check, capture_output=True, text=True)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.28.1
|
|
@@ -127,7 +127,7 @@ SPECFUSE_DIR = Path(".specfuse")
|
|
|
127
127
|
REPO_ROOT = SPECFUSE_DIR.parent
|
|
128
128
|
FEATURES_DIR = SPECFUSE_DIR / "features"
|
|
129
129
|
VERIFICATION_PATH = SPECFUSE_DIR / "verification.yml"
|
|
130
|
-
DRIVER_VERSION = "0.28.
|
|
130
|
+
DRIVER_VERSION = "0.28.1"
|
|
131
131
|
# Oldest scaffold layout this driver can drive. init.sh stamps the scaffold's own
|
|
132
132
|
# version into `.specfuse/VERSION`; check_scaffold_version() fails loud at startup if
|
|
133
133
|
# the consumer's scaffold is older than this, pointing at `specfuse upgrade`. Bump
|
|
@@ -241,7 +241,16 @@ def _default_transport_resolver(module, check_type: str, binding: Mapping[str, o
|
|
|
241
241
|
)
|
|
242
242
|
|
|
243
243
|
|
|
244
|
-
def _build_adapter(
|
|
244
|
+
def _build_adapter(
|
|
245
|
+
cls,
|
|
246
|
+
*,
|
|
247
|
+
component: str,
|
|
248
|
+
environment: Mapping[str, object],
|
|
249
|
+
check: Mapping[str, object],
|
|
250
|
+
transport,
|
|
251
|
+
now: datetime,
|
|
252
|
+
since: Optional[datetime] = None,
|
|
253
|
+
):
|
|
245
254
|
kwargs = dict(
|
|
246
255
|
component=component,
|
|
247
256
|
environment=environment,
|
|
@@ -255,6 +264,10 @@ def _build_adapter(cls, *, component: str, environment: Mapping[str, object], ch
|
|
|
255
264
|
deploy_signal=check.get("deploy_signal"),
|
|
256
265
|
window_after_deploy=check.get("window_after_deploy"),
|
|
257
266
|
codes=check.get("codes"),
|
|
267
|
+
# The harvest window (#3480): adapters that read event history
|
|
268
|
+
# accept it; those that need older history by design do not.
|
|
269
|
+
since=since,
|
|
270
|
+
until=now if since is not None else None,
|
|
258
271
|
)
|
|
259
272
|
return _call_filtered(cls, **kwargs)
|
|
260
273
|
|
|
@@ -266,6 +279,7 @@ def _process_check(
|
|
|
266
279
|
environment: Mapping[str, object],
|
|
267
280
|
transport_resolver: Callable,
|
|
268
281
|
now: datetime,
|
|
282
|
+
since: Optional[datetime] = None,
|
|
269
283
|
):
|
|
270
284
|
check_type = check.get("type")
|
|
271
285
|
if check_type not in CHECK_TYPES:
|
|
@@ -290,7 +304,13 @@ def _process_check(
|
|
|
290
304
|
adapter_cls = _find_adapter_class(module, check_type)
|
|
291
305
|
transport = transport_resolver(module, check_type, binding)
|
|
292
306
|
adapter = _build_adapter(
|
|
293
|
-
adapter_cls,
|
|
307
|
+
adapter_cls,
|
|
308
|
+
component=component_name,
|
|
309
|
+
environment=environment,
|
|
310
|
+
check=check,
|
|
311
|
+
transport=transport,
|
|
312
|
+
now=now,
|
|
313
|
+
since=since,
|
|
294
314
|
)
|
|
295
315
|
artifacts = list(adapter.fetch_failures())
|
|
296
316
|
skipped = list(getattr(adapter, "skipped_targets", []))
|
|
@@ -454,8 +474,10 @@ def run_cycle(
|
|
|
454
474
|
|
|
455
475
|
summary: list = []
|
|
456
476
|
total_findings = 0
|
|
477
|
+
any_errored = False
|
|
457
478
|
|
|
458
479
|
for env_name in sorted(selected_envs):
|
|
480
|
+
env_errored = False
|
|
459
481
|
environment = selected_envs[env_name]
|
|
460
482
|
watermark_path = watermark_dir / f"{env_name}.json"
|
|
461
483
|
since, fallback_reason = _read_watermark(watermark_path, lookback=lookback, now=now)
|
|
@@ -487,13 +509,27 @@ def run_cycle(
|
|
|
487
509
|
findings: list = []
|
|
488
510
|
skipped: list = []
|
|
489
511
|
for check in component.get("checks") or []:
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
512
|
+
# #3478: one check's failure — a provider SDK error, a missing
|
|
513
|
+
# credential, a permission the running identity lacks — must
|
|
514
|
+
# not blind every other check in the run. Report it, keep the
|
|
515
|
+
# environment's watermark where it was so the window is
|
|
516
|
+
# retried, and exit non-zero once the summary is printed.
|
|
517
|
+
try:
|
|
518
|
+
artifacts, check_skipped = _process_check(
|
|
519
|
+
check=check,
|
|
520
|
+
component=component,
|
|
521
|
+
environment=environment,
|
|
522
|
+
transport_resolver=transport_resolver,
|
|
523
|
+
now=now,
|
|
524
|
+
since=since,
|
|
525
|
+
)
|
|
526
|
+
except Exception as exc: # noqa: BLE001 — isolation is the point
|
|
527
|
+
env_errored = True
|
|
528
|
+
summary.append(
|
|
529
|
+
f"{env_name}/{component_name}: check {check.get('type')} errored — "
|
|
530
|
+
f"{type(exc).__name__}: {exc}"
|
|
531
|
+
)
|
|
532
|
+
continue
|
|
497
533
|
findings.extend(artifacts)
|
|
498
534
|
skipped.extend(check_skipped)
|
|
499
535
|
|
|
@@ -528,14 +564,20 @@ def run_cycle(
|
|
|
528
564
|
f"{getattr(skip, 'reason', 'unknown reason')}"
|
|
529
565
|
)
|
|
530
566
|
|
|
531
|
-
if
|
|
567
|
+
if env_errored:
|
|
568
|
+
any_errored = True
|
|
569
|
+
summary.append(
|
|
570
|
+
f"environment {env_name}: watermark not advanced — a check errored; "
|
|
571
|
+
"its window is retried next cycle"
|
|
572
|
+
)
|
|
573
|
+
elif not dry_run:
|
|
532
574
|
_write_watermark(watermark_path, now=now)
|
|
533
575
|
|
|
534
576
|
summary.append(f"total: {total_findings} finding(s) across {len(selected_components)} component(s)")
|
|
535
577
|
for line in summary:
|
|
536
578
|
print(line, file=out)
|
|
537
579
|
|
|
538
|
-
return 0
|
|
580
|
+
return 1 if any_errored else 0
|
|
539
581
|
|
|
540
582
|
|
|
541
583
|
# ---------------------------------------------------------------------------
|
{specfuse_loop-0.28.0 → specfuse_loop-0.28.1}/specfuse/monitor/providers/azure_app_insights.py
RENAMED
|
@@ -69,7 +69,29 @@ class AppInsightsTransport(Protocol):
|
|
|
69
69
|
...
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
def
|
|
72
|
+
def _kql_datetime(moment: datetime) -> str:
|
|
73
|
+
"""A KQL ``datetime()`` literal at full precision. ``moment`` comes from
|
|
74
|
+
the harvester's own clock or watermark file, never from a returned row."""
|
|
75
|
+
text = moment.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
|
76
|
+
return f"datetime({text})"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _harvest_window(since: Optional[datetime], until: Optional[datetime]) -> str:
|
|
80
|
+
"""The harvest-window predicate (#3480), or nothing when no window was
|
|
81
|
+
given. Bounded on ``ingestion_time()`` rather than ``TimeGenerated`` so a
|
|
82
|
+
row stamped before the previous run but ingested after it is read by the
|
|
83
|
+
next run instead of falling between two windows."""
|
|
84
|
+
if since is None or until is None:
|
|
85
|
+
return ""
|
|
86
|
+
return (
|
|
87
|
+
f"| where ingestion_time() >= {_kql_datetime(since)} "
|
|
88
|
+
f"and ingestion_time() < {_kql_datetime(until)} "
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _error_logs_query(
|
|
93
|
+
component: str, since: Optional[datetime] = None, until: Optional[datetime] = None
|
|
94
|
+
) -> str:
|
|
73
95
|
"""Built only from *component*, an operator-configured name — never from
|
|
74
96
|
a telemetry query's returned rows.
|
|
75
97
|
|
|
@@ -81,16 +103,20 @@ def _error_logs_query(component: str) -> str:
|
|
|
81
103
|
return (
|
|
82
104
|
"AppExceptions "
|
|
83
105
|
f"| where AppRoleName == '{component}' "
|
|
106
|
+
f"{_harvest_window(since, until)}"
|
|
84
107
|
"| project timestamp = TimeGenerated, operation_Id = OperationId, "
|
|
85
108
|
"operation_ParentId = ParentId, type = ExceptionType, "
|
|
86
109
|
"outerMessage = OuterMessage"
|
|
87
110
|
)
|
|
88
111
|
|
|
89
112
|
|
|
90
|
-
def _http_5xx_query(
|
|
113
|
+
def _http_5xx_query(
|
|
114
|
+
component: str, since: Optional[datetime] = None, until: Optional[datetime] = None
|
|
115
|
+
) -> str:
|
|
91
116
|
return (
|
|
92
117
|
"AppRequests "
|
|
93
118
|
f"| where AppRoleName == '{component}' and ResultCode startswith '5' "
|
|
119
|
+
f"{_harvest_window(since, until)}"
|
|
94
120
|
"| project timestamp = TimeGenerated, operation_Id = OperationId, "
|
|
95
121
|
"name = Name, resultCode = ResultCode"
|
|
96
122
|
)
|
|
@@ -105,13 +131,17 @@ class ErrorLogsAdapter:
|
|
|
105
131
|
component: str,
|
|
106
132
|
environment: Mapping[str, object],
|
|
107
133
|
transport: AppInsightsTransport,
|
|
134
|
+
since: Optional[datetime] = None,
|
|
135
|
+
until: Optional[datetime] = None,
|
|
108
136
|
) -> None:
|
|
109
137
|
self._component = component
|
|
110
138
|
self._telemetry = resolve_telemetry(component, environment)
|
|
111
139
|
self._transport = transport
|
|
140
|
+
self._since = since
|
|
141
|
+
self._until = until
|
|
112
142
|
|
|
113
143
|
def fetch_failures(self) -> Iterator[FailureArtifact]:
|
|
114
|
-
query = _error_logs_query(self._component)
|
|
144
|
+
query = _error_logs_query(self._component, self._since, self._until)
|
|
115
145
|
rows = self._transport.run_query(query)
|
|
116
146
|
for row in rows:
|
|
117
147
|
yield self._build_artifact(row)
|
|
@@ -140,13 +170,17 @@ class Http5xxAdapter:
|
|
|
140
170
|
component: str,
|
|
141
171
|
environment: Mapping[str, object],
|
|
142
172
|
transport: AppInsightsTransport,
|
|
173
|
+
since: Optional[datetime] = None,
|
|
174
|
+
until: Optional[datetime] = None,
|
|
143
175
|
) -> None:
|
|
144
176
|
self._component = component
|
|
145
177
|
self._telemetry = resolve_telemetry(component, environment)
|
|
146
178
|
self._transport = transport
|
|
179
|
+
self._since = since
|
|
180
|
+
self._until = until
|
|
147
181
|
|
|
148
182
|
def fetch_failures(self) -> Iterator[FailureArtifact]:
|
|
149
|
-
query = _http_5xx_query(self._component)
|
|
183
|
+
query = _http_5xx_query(self._component, self._since, self._until)
|
|
150
184
|
rows = self._transport.run_query(query)
|
|
151
185
|
for row in rows:
|
|
152
186
|
yield self._build_artifact(row)
|
|
@@ -182,6 +182,7 @@ tests/test_agent_snapshot_refresh.py
|
|
|
182
182
|
tests/test_agent_state.py
|
|
183
183
|
tests/test_answer_escalation_skill.py
|
|
184
184
|
tests/test_app_insights_adapters.py
|
|
185
|
+
tests/test_app_insights_harvest_window.py
|
|
185
186
|
tests/test_app_insights_query_schema_check.py
|
|
186
187
|
tests/test_app_insights_workspace_schema.py
|
|
187
188
|
tests/test_archive_ref_reconciliation.py
|
|
@@ -482,6 +483,7 @@ tests/test_max_open_prs.py
|
|
|
482
483
|
tests/test_migrate_legacy.py
|
|
483
484
|
tests/test_miniyaml_equivalence.py
|
|
484
485
|
tests/test_miniyaml_negative.py
|
|
486
|
+
tests/test_monitor_check_isolation.py
|
|
485
487
|
tests/test_monitor_cli.py
|
|
486
488
|
tests/test_monitor_durations.py
|
|
487
489
|
tests/test_monitor_issue_lifecycle.py
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Copyright 2026 Specfuse Contributors
|
|
2
|
+
# Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
3
|
+
"""#3480: the App Insights `error-logs` and `http-5xx` checks must read only
|
|
4
|
+
the harvest window `run_cycle` computes — from the environment's watermark,
|
|
5
|
+
or `now - lookback` without one — not the workspace's whole retention.
|
|
6
|
+
|
|
7
|
+
The window is bounded on `ingestion_time()`, not `TimeGenerated`: a row
|
|
8
|
+
stamped just before the previous run but ingested after it is still picked
|
|
9
|
+
up by the next run instead of being skipped forever. `heartbeat` and
|
|
10
|
+
`http-status-regression` are deliberately not bounded by it: one needs the
|
|
11
|
+
last heartbeat however old, the other compares windows around a deployment.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import io
|
|
17
|
+
import tempfile
|
|
18
|
+
import unittest
|
|
19
|
+
from datetime import datetime, timedelta, timezone
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
import specfuse.monitor.cli as cli
|
|
23
|
+
|
|
24
|
+
_NOW = datetime(2026, 10, 16, 10, 0, 0, 123456, tzinfo=timezone.utc)
|
|
25
|
+
_ENVIRONMENT = {
|
|
26
|
+
"telemetry": {"provider": "azure_app_insights", "credentials": {"api_key": "ACME_API_KEY"}},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _config(checks):
|
|
31
|
+
return {
|
|
32
|
+
"environments": {"production": _ENVIRONMENT},
|
|
33
|
+
"components": [{
|
|
34
|
+
"name": "web-api",
|
|
35
|
+
"type": "api",
|
|
36
|
+
"runner": "local",
|
|
37
|
+
"diagnose": "manual",
|
|
38
|
+
"autofix": "off",
|
|
39
|
+
"checks": checks,
|
|
40
|
+
}],
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class _Recorder:
|
|
45
|
+
def __init__(self):
|
|
46
|
+
self.queries: dict[str, str] = {}
|
|
47
|
+
|
|
48
|
+
def resolver(self, module, check_type, binding):
|
|
49
|
+
recorder = self
|
|
50
|
+
|
|
51
|
+
class _Transport:
|
|
52
|
+
def run_query(self, query):
|
|
53
|
+
recorder.queries[check_type] = query
|
|
54
|
+
return []
|
|
55
|
+
|
|
56
|
+
return _Transport()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
_BOUNDED = [{"type": "error-logs"}, {"type": "http-5xx"}]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class TestTelemetryQueriesAreBoundedToTheHarvestWindow(unittest.TestCase):
|
|
63
|
+
def setUp(self):
|
|
64
|
+
self._tmpdir = tempfile.TemporaryDirectory()
|
|
65
|
+
self.addCleanup(self._tmpdir.cleanup)
|
|
66
|
+
self.watermark_dir = Path(self._tmpdir.name)
|
|
67
|
+
|
|
68
|
+
def _run(self, checks):
|
|
69
|
+
recorder = _Recorder()
|
|
70
|
+
cli.run_cycle(
|
|
71
|
+
_config(checks),
|
|
72
|
+
dry_run=True,
|
|
73
|
+
transport_resolver=recorder.resolver,
|
|
74
|
+
now=_NOW,
|
|
75
|
+
watermark_dir=self.watermark_dir,
|
|
76
|
+
out=io.StringIO(),
|
|
77
|
+
)
|
|
78
|
+
return recorder.queries
|
|
79
|
+
|
|
80
|
+
def _window(self, since):
|
|
81
|
+
return (
|
|
82
|
+
f"| where ingestion_time() >= datetime({since}) "
|
|
83
|
+
f"and ingestion_time() < datetime(2026-10-16T10:00:00.123456Z)"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def test_no_watermark_bounds_to_the_lookback(self):
|
|
87
|
+
queries = self._run(_BOUNDED)
|
|
88
|
+
since = "2026-10-15T10:00:00.123456Z" # _NOW - DEFAULT_LOOKBACK (24h)
|
|
89
|
+
for check_type in ("error-logs", "http-5xx"):
|
|
90
|
+
with self.subTest(check_type=check_type):
|
|
91
|
+
self.assertIn(self._window(since), queries[check_type])
|
|
92
|
+
|
|
93
|
+
def test_watermark_bounds_to_the_previous_run(self):
|
|
94
|
+
previous = _NOW - timedelta(minutes=15)
|
|
95
|
+
cli._write_watermark(self.watermark_dir / "production.json", now=previous)
|
|
96
|
+
queries = self._run(_BOUNDED)
|
|
97
|
+
for check_type in ("error-logs", "http-5xx"):
|
|
98
|
+
with self.subTest(check_type=check_type):
|
|
99
|
+
self.assertIn(self._window("2026-10-16T09:45:00.123456Z"), queries[check_type])
|
|
100
|
+
|
|
101
|
+
def test_window_filter_follows_the_table_scope(self):
|
|
102
|
+
# Bounded before projection, so the predicate reads the source table.
|
|
103
|
+
query = self._run([{"type": "error-logs"}])["error-logs"]
|
|
104
|
+
self.assertLess(query.index("ingestion_time()"), query.index("| project"))
|
|
105
|
+
|
|
106
|
+
def test_heartbeat_and_status_regression_are_not_window_bounded(self):
|
|
107
|
+
queries = self._run([
|
|
108
|
+
{"type": "heartbeat", "targets": [
|
|
109
|
+
{"name": "nightly", "cron": "0 2 * * *", "dialect": "standard-5", "timezone": "Etc/UTC"},
|
|
110
|
+
]},
|
|
111
|
+
{"type": "http-status-regression", "deploy_signal": {"kind": "app-version"},
|
|
112
|
+
"window_after_deploy": "1h"},
|
|
113
|
+
])
|
|
114
|
+
for check_type in ("heartbeat", "http-status-regression"):
|
|
115
|
+
with self.subTest(check_type=check_type):
|
|
116
|
+
self.assertNotIn("ingestion_time()", queries[check_type])
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class TestBoundedQueriesMatchWorkspaceSchema(unittest.TestCase):
|
|
120
|
+
def test_bounded_queries_use_only_workspace_names(self):
|
|
121
|
+
from specfuse.monitor.providers import azure_app_insights as aai
|
|
122
|
+
from tests.test_app_insights_query_schema_check import schema_violations
|
|
123
|
+
|
|
124
|
+
since, until = _NOW - timedelta(hours=1), _NOW
|
|
125
|
+
for build in (aai._error_logs_query, aai._http_5xx_query):
|
|
126
|
+
with self.subTest(build=build.__name__):
|
|
127
|
+
query = build("backend-api", since, until)
|
|
128
|
+
self.assertEqual(schema_violations(query), [], query)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if __name__ == "__main__":
|
|
132
|
+
unittest.main()
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Copyright 2026 Specfuse Contributors
|
|
2
|
+
# Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
3
|
+
"""#3478: one failing check must not abort the whole `specfuse monitor run`
|
|
4
|
+
cycle. Each check is isolated: its error is reported in the summary, the
|
|
5
|
+
other checks still run and file, the environment's watermark is not advanced
|
|
6
|
+
(so the failed window is retried), and the run exits non-zero."""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import io
|
|
11
|
+
import tempfile
|
|
12
|
+
import unittest
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from types import SimpleNamespace
|
|
16
|
+
|
|
17
|
+
import specfuse.monitor.cli as cli
|
|
18
|
+
|
|
19
|
+
_NOW = datetime(2026, 10, 16, 10, 0, 0, tzinfo=timezone.utc)
|
|
20
|
+
_REPO = "acme-widget/repo"
|
|
21
|
+
_ENVIRONMENT = {
|
|
22
|
+
"telemetry": {"provider": "azure_app_insights", "credentials": {"api_key": "ACME_API_KEY"}},
|
|
23
|
+
"broker": {"provider": "azure_service_bus", "credentials": {"connection_string": "ACME_CONN"}},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class HttpResponseError(Exception):
|
|
28
|
+
"""Stands in for `azure.core.exceptions.HttpResponseError`: any provider
|
|
29
|
+
SDK exception that is not a `MonitorCliError`."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _component(name, checks):
|
|
33
|
+
return {
|
|
34
|
+
"name": name,
|
|
35
|
+
"type": "api",
|
|
36
|
+
"runner": "local",
|
|
37
|
+
"diagnose": "manual",
|
|
38
|
+
"autofix": "off",
|
|
39
|
+
"checks": checks,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _config():
|
|
44
|
+
# order-worker: error-logs succeeds, http-5xx fails; web-api is processed
|
|
45
|
+
# after the failing check and must still be checked.
|
|
46
|
+
return {
|
|
47
|
+
"environments": {"production": _ENVIRONMENT},
|
|
48
|
+
"components": [
|
|
49
|
+
_component("order-worker", [{"type": "http-5xx"}, {"type": "error-logs"}]),
|
|
50
|
+
_component("web-api", [{"type": "error-logs"}]),
|
|
51
|
+
],
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class _Transport:
|
|
56
|
+
def __init__(self, rows=None, error=None):
|
|
57
|
+
self._rows = rows or []
|
|
58
|
+
self._error = error
|
|
59
|
+
|
|
60
|
+
def run_query(self, query):
|
|
61
|
+
if self._error is not None:
|
|
62
|
+
raise self._error
|
|
63
|
+
return list(self._rows)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
_ERROR_ROWS = [{"type": "NullReferenceException", "outerMessage": "boom at line 5"}]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _failing_resolver(module, check_type, binding):
|
|
70
|
+
if check_type == "http-5xx":
|
|
71
|
+
return _Transport(error=HttpResponseError(
|
|
72
|
+
"Failed to resolve table or column expression named 'requests'"
|
|
73
|
+
))
|
|
74
|
+
return _Transport(rows=_ERROR_ROWS)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _clean_resolver(module, check_type, binding):
|
|
78
|
+
return _Transport(rows=_ERROR_ROWS if check_type == "error-logs" else [])
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class _GhRecorder:
|
|
82
|
+
def __init__(self):
|
|
83
|
+
self.created = 0
|
|
84
|
+
|
|
85
|
+
def __call__(self, args, check=True):
|
|
86
|
+
if args[:3] == ["gh", "issue", "list"]:
|
|
87
|
+
return SimpleNamespace(returncode=0, stdout="[]", stderr="")
|
|
88
|
+
if args[:3] == ["gh", "issue", "create"]:
|
|
89
|
+
self.created += 1
|
|
90
|
+
return SimpleNamespace(
|
|
91
|
+
returncode=0, stdout=f"https://github.com/{_REPO}/issues/{self.created}\n", stderr=""
|
|
92
|
+
)
|
|
93
|
+
raise AssertionError(f"unexpected gh call: {args}")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class TestOneFailingCheckIsIsolated(unittest.TestCase):
|
|
97
|
+
def setUp(self):
|
|
98
|
+
self._tmpdir = tempfile.TemporaryDirectory()
|
|
99
|
+
self.addCleanup(self._tmpdir.cleanup)
|
|
100
|
+
self.watermark_dir = Path(self._tmpdir.name)
|
|
101
|
+
|
|
102
|
+
def _run(self, resolver, gh=None):
|
|
103
|
+
out = io.StringIO()
|
|
104
|
+
gh = gh or _GhRecorder()
|
|
105
|
+
code = cli.run_cycle(
|
|
106
|
+
_config(),
|
|
107
|
+
dry_run=False,
|
|
108
|
+
repo=_REPO,
|
|
109
|
+
transport_resolver=resolver,
|
|
110
|
+
gh_runner=gh,
|
|
111
|
+
now=_NOW,
|
|
112
|
+
watermark_dir=self.watermark_dir,
|
|
113
|
+
out=out,
|
|
114
|
+
)
|
|
115
|
+
return code, out.getvalue(), gh
|
|
116
|
+
|
|
117
|
+
def test_other_checks_still_run_and_file(self):
|
|
118
|
+
_, output, gh = self._run(_failing_resolver)
|
|
119
|
+
# error-logs on order-worker (same component, after the failure) and
|
|
120
|
+
# on web-api (the next component) both filed.
|
|
121
|
+
self.assertEqual(gh.created, 2)
|
|
122
|
+
self.assertIn("production/order-worker: 1 finding(s)", output)
|
|
123
|
+
self.assertIn("production/web-api: 1 finding(s)", output)
|
|
124
|
+
self.assertIn("total: 2 finding(s)", output)
|
|
125
|
+
|
|
126
|
+
def test_error_is_reported_in_the_summary(self):
|
|
127
|
+
_, output, _ = self._run(_failing_resolver)
|
|
128
|
+
self.assertIn(
|
|
129
|
+
"production/order-worker: check http-5xx errored — HttpResponseError: "
|
|
130
|
+
"Failed to resolve table or column expression named 'requests'",
|
|
131
|
+
output,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def test_run_exits_non_zero(self):
|
|
135
|
+
code, _, _ = self._run(_failing_resolver)
|
|
136
|
+
self.assertEqual(code, 1)
|
|
137
|
+
|
|
138
|
+
def test_watermark_is_not_advanced_for_the_environment(self):
|
|
139
|
+
self._run(_failing_resolver)
|
|
140
|
+
self.assertFalse((self.watermark_dir / "production.json").exists())
|
|
141
|
+
|
|
142
|
+
def test_setup_error_inside_one_check_is_isolated_too(self):
|
|
143
|
+
def resolver(module, check_type, binding):
|
|
144
|
+
if check_type == "http-5xx":
|
|
145
|
+
raise cli.MonitorCliError("credential env var 'ACME_API_KEY' is not set")
|
|
146
|
+
return _Transport(rows=_ERROR_ROWS)
|
|
147
|
+
|
|
148
|
+
code, output, gh = self._run(resolver)
|
|
149
|
+
self.assertEqual(code, 1)
|
|
150
|
+
self.assertEqual(gh.created, 2)
|
|
151
|
+
self.assertIn(
|
|
152
|
+
"production/order-worker: check http-5xx errored — MonitorCliError: "
|
|
153
|
+
"credential env var 'ACME_API_KEY' is not set",
|
|
154
|
+
output,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def test_clean_run_exits_zero_and_advances_the_watermark(self):
|
|
158
|
+
code, output, gh = self._run(_clean_resolver)
|
|
159
|
+
self.assertEqual(code, 0)
|
|
160
|
+
self.assertEqual(gh.created, 2)
|
|
161
|
+
self.assertNotIn("errored", output)
|
|
162
|
+
self.assertTrue((self.watermark_dir / "production.json").exists())
|
|
163
|
+
|
|
164
|
+
def test_run_level_errors_stay_fatal(self):
|
|
165
|
+
with self.assertRaises(cli.MonitorCliError):
|
|
166
|
+
cli.run_cycle(
|
|
167
|
+
_config(),
|
|
168
|
+
env_filter="staging",
|
|
169
|
+
dry_run=True,
|
|
170
|
+
transport_resolver=_clean_resolver,
|
|
171
|
+
now=_NOW,
|
|
172
|
+
watermark_dir=self.watermark_dir,
|
|
173
|
+
out=io.StringIO(),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == "__main__":
|
|
178
|
+
unittest.main()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.28.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|