specfuse-loop 0.22.1__tar.gz → 0.22.3__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.22.1/specfuse_loop.egg-info → specfuse_loop-0.22.3}/PKG-INFO +1 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/pyproject.toml +1 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/severity_backfill.py +96 -10
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/agent_policy.py +12 -1
- specfuse_loop-0.22.3/specfuse/loop/data/VERSION +1 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/labels.py +69 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/loop.py +189 -10
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/triage.py +87 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse_loop.egg-info/SOURCES.txt +7 -0
- specfuse_loop-0.22.3/tests/test_attribution_halt_claims.py +139 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_baseline_escalation_message.py +18 -5
- specfuse_loop-0.22.3/tests/test_hygiene_boundary_guidance.py +88 -0
- specfuse_loop-0.22.3/tests/test_ignored_cleanup_before_probe.py +292 -0
- specfuse_loop-0.22.3/tests/test_per_run_item_cap_disclosure.py +80 -0
- specfuse_loop-0.22.3/tests/test_plugin_prose_cross_refs.py +80 -0
- specfuse_loop-0.22.3/tests/test_severity_backfill_end_to_end.py +215 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_backfill_run.py +158 -0
- specfuse_loop-0.22.3/tests/test_severity_label_projection.py +138 -0
- specfuse_loop-0.22.3/tests/test_severity_reconciliation.py +177 -0
- specfuse_loop-0.22.1/specfuse/loop/data/VERSION +0 -1
- specfuse_loop-0.22.1/tests/test_severity_backfill_end_to_end.py +0 -86
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/LICENSE +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/NOTICE +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/README.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/setup.cfg +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/__init__.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/budget.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/diagnose_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/drafting_answers.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/drafting_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/drafting_questions.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/driver_command.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/driver_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/monitoring_read.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/__init__.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/answers.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/bugs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/feature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/findings_autofix.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/findings_diagnose.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/triage.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/queue_read.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/repo_detect.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/triage_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/worktree.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/_filelock.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/_wu_sections.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/arm_eval.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/arm_sweep.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/arm_txn.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/bug_lane.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/bug_lane_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/bug_lane_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/build_provenance.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/caller_check.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/changelog.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/closing_requirements.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/cost.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/criteria_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/glossary.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/methodology.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/gitignore.snippet +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/monitoring.yml.example +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/close-discipline.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/human-output.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/operator-escalation.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/planning-discipline.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/verification-discipline.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules-local/README.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/event.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/decisions_format.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/driver_edit.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/escalation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/events_stats.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/gate_commands.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/heartbeat.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/judge.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/learnings_distil.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/learnings_query.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/lint_closing.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/lint_monitoring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/lint_plan.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/lint_roadmap.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/notify.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/notify_escalation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/notify_sla.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/plan_baseline.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/policy_proposals.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/policy_review.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/prerun.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/prerun_capture.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/promotion.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/rearm_migration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/replay_spin.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/roadmap_archive.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/scaffold.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/__init__.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/adapters.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/artifact.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/autofix.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/autofix_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/autofix_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/autofix_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/cli.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/diagnose_cli.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/diagnosis.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/fingerprint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/issues.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/providers/__init__.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/providers/_azure_auth.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/providers/azure_app_insights.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/providers/azure_service_bus.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/redaction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/schedule.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_abandon_feature_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_abandoned_work_reported.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_abandoned_wu_strands_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_budget.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_bug_selection_dedup.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_default_features_root.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_driver_command.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_driver_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_driver_restart.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_escalation_on_issue.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_escalation_run_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_findings_seam.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_invoke_timeout.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_invoke_usage.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_item_worktree.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_lock.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_policy_key_ownership.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_policy_queue.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_policy_schema.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_policy_triage_dial.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_answers.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_bugs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_feature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_findings_autofix.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_findings_diagnose.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_isolation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_triage.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_queue_read.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_relays_driver_output.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_repo_detect.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_run_progress.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_seam.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_snapshot_refresh.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_answer_escalation_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_app_insights_adapters.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_archive_ref_reconciliation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_eval.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_eval_lint_class.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_eval_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_sweep.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_sweep_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_txn.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_artifact_redaction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attempt_note_untracked_files.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attempt_outcome_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attention_nonwriting_guard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attention_silence_section.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attention_skill_structure.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autoclose_deferral_visibility.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autoclose_gate_note.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autoclose_stub_states_what_passed.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autofix_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autofix_live.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autofix_predicate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autofix_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autofix_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autosync_no_cwd_leak.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_azure_credential_resolution.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_backend.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_baseline_persistence.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_baseline_probe.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_baseline_provenance.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_baseline_tree_hash_key.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bash_routing.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bats_suites_gated.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_binary_verdict.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_binding_block_advisory.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_binding_block_allocation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_binding_block_budget.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bookkeeping_no_verify.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_brief_covers_every_unit_escalation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_ci_pending.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_guardrails.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_label_nonfatal.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_labels_registered.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_min_severity.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_policy_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_pr_number_carried.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_preflight_and_dial.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_test_paths.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_build_provenance.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_caller_check_ratchet.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_changed_file_test_selection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_changelog_append_only.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_changelog_collection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_changelog_release_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_changelog_schema.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_ci_conclusion_wait.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_ci_runs_every_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_claude_resolution.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_close_b_pending_learnings.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_close_records_feature_oracle.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_close_wu_self_reference.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_closing_guard_contracts.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_closing_guard_prediction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_closing_learnings_destination.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_closing_requirements.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_coauthor_trailer_allowlist.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_conditional_reflection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_console_encoding.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_convergent_iteration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_convergent_wu_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_correlation_id_override.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_correlation_id_two_surfaces.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_cost_lifetime.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_criteria_per_gate_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_criteria_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_criteria_worklist.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_decision_citation_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_decision_followups_discharged.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_decision_override_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_decisions_format.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_default_severity_aliases.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_degraded_oracle.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_deliverable_missing_note.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_derive_agent_policy_review_mode.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_derive_agent_policy_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_derive_monitoring_discovery.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_derive_monitoring_skill_registration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_design_for_diagnosis_rule.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_deterministic_refusal_repeat.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_diagnose_headless.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_diagnose_issue_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_diagnosis.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_diagnosis_roundtrip_live.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_dispatch_skeleton.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_distilled_accept_step.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_doctor_command_preflight.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_done_feature_gates.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_draft_feature_answers_mode.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_drafting_answer_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_drafting_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_drafting_question_issue.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_drafting_questions.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_drafting_reply_shape.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_edit_detection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_event_registry_covers_emitters.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_module_surface.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_restart_halt_surfaced.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_restart_halt_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_restart_hold.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_staleness_gate_summary.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_staleness_warning.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_dry_run_no_branch_mutation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_ensure_feature_branch_base.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_escalation_assignee_policy.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_escalation_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_escalation_crash_containment.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_escalation_dedup_search.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_escalation_emit.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_event_gate_full_envelope.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_events_redaction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_events_stats.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_failure_artifact_model.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_failure_signature_fail_prefix.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_failure_signature_skips_log_noise.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_branch_auto_sync.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_branch_squash_merge.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_branch_stale_base.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_id_resolution.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_oracle_e2e.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_provider_drafting_dispatch.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_review_accumulation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_filelock_portable.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_files_changed_auto_repair.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_find_pr_no_search_index.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_fingerprint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_fix_bug_diff_self_check.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_fix_bug_headless.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_fix_bug_reads_comments.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_fold_contract_documented.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_followups_artifact.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_frontmatter_scalar_quoting.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_broad_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_budget.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_budget_post_dispatch.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_commands_needs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_eval_intermediate_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_eval_repo_flag.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_eval_terminal_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_report_verdict.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gitleaks_pinning.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_groom_backlog_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_guard_refusal_failure_class.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_guard_refusal_failure_excerpt.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_guard_repair_e2e.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_guard_repair_prompt.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_heartbeat.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_heartbeat_adapter.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_human_output_rule.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_human_work_unit.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_hyphenated_gate_name.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_init_integration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_installed_copy_driver_e2e.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_interpreter_normalization.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_close_path.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_cost.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_cost_fold_committed.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_module.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_path_registry.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_terminal_flip_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_label_provisioning_runner_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_label_registry.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_label_registry_covers_consumers.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_language_aware_narrow_selection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lazy_baseline_e2e.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_curate_vendored_promote.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_distil_cli.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_query.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_staging.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_suggest_signature_filter.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_weights.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lifetime_accounting.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_ac_observable.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_boundary_consistency.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_boundary_extraction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_close_verdict_not_predecided.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_closing.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_closing_criteria.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_closing_criteria_pristine.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_covers_load_wu_invariants.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_feature_oracle_declared.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_gate_proportionality.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_monitoring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_plan_contract_fields.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_plan_relative_import.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_plan_verdict_exempt.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_planning_sections.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_produces_satisfiability.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_roadmap.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_verification_reference.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_live_tests_opt_in.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_load_bearing_close_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_attempt_evidence.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_criteria_skeleton.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_criteria_survival.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_gate_needs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_worklist_injection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_make_repo_gpgsign_isolation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_marker_dual_shape.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_maven_failure_excerpt.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_max_attempts_configurable.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_max_open_prs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitor_cli.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitor_issue_lifecycle.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitor_runner_surfaces.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitoring_cron_dialect.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitoring_example.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitoring_fenced_blocks.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitoring_seed.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_narrow_fallback_warning.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_no_pytest_subprocess.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_notify.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_notify_escalation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_notify_sla.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_operator_escalation_rule.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_oracle_set_declared.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_oracle_unrunnable_attribution.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pick_feature_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pin_honesty_and_integrity.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pin_marker_not_inherited.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pin_marker_spawn_sites.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_plan_baseline.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_planning_cost_floor.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_plugin_qualified_dispatch.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_policy_budgets_applied.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_policy_proposals.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_policy_review.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pr_checks_json_field.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pr_reference_in_title.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prep_halt_operator_surface.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prepare_feature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prepare_scaffold_sync.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prerun_capture.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prerun_oracles.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prerun_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produced_document_verdict_guard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_bracket_literal.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_bracket_wildcard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_in_diff.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_justification.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_recursive_glob.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_semantics_unified.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_shape.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_progress_forward_note.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_progress_lines_end_to_end.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_promotion_ledger.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_provider_escalation_traces.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_provision_labels.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_queue_stalled_adapter.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_rearm_fold_marker.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_rearm_migration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_red_baseline_never_reused.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_redaction_windows_home.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_reflection_required_signals.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replan_end_to_end.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replan_event_emission.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replan_note_collision.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replan_trigger.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replan_turn_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replay_spin.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_required_checks.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_resolve_base.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_restart_cap_counts_progress.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_result_block_audience.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_resume_command_build.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_retain_on_guard_refusal_docs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_archive_cli.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_archive_shim.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_archive_skill_delegates.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_link_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_ruff_verdict_lines.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_rule_import_convergence.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_run_level_failure_breaker.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_data_in_sync.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_doc_hygiene.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_docs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_init.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_label_provisioning.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_resources.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_skew_halts.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_schedule_dialect.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_seed_documents_prerun_keys.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_service_bus_dlq_adapter.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_session_env_preflight.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_aliases.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_backfill_apply.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_backfill_limit.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_backfill_marker.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_rubric.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_shipped_templates_parse.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_skill_discovery_links.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_skills_vendored_in_sync.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_spinning_rearm_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_spinout_brief_end_to_end.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_spinout_brief_replan_option.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_stop_rationale_captured.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_stranded_halt_cause.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_surefire_run_level_signature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_surefire_verdict_lines.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_terminal_gate_message.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_tiered_verification_e2e.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_timeout_kill_portable.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_apply.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_severity_classify.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_severity_end_to_end.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_severity_write.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_skill_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_skips_agent_escalations.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_tsc_verdict_lines.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_umbrella_version_resolution.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_unterminated_frontmatter.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_untracked_file_absorption.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_validate_event_driver_types.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_windows_gate_exec.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_worktree_restore.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_wu_execution_metadata.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/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.22.
|
|
7
|
+
version = "0.22.3"
|
|
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"
|
|
@@ -37,9 +37,13 @@ from typing import Callable, Optional
|
|
|
37
37
|
|
|
38
38
|
from specfuse.agent.invoke import run_claude
|
|
39
39
|
from specfuse.agent.triage_invoke import build_invocation, classify_severity
|
|
40
|
-
from specfuse.loop.labels import
|
|
40
|
+
from specfuse.loop.labels import (
|
|
41
|
+
list_existing_labels,
|
|
42
|
+
read_severity_rubric,
|
|
43
|
+
severity_label_projection,
|
|
44
|
+
)
|
|
41
45
|
|
|
42
|
-
from specfuse.loop import triage
|
|
46
|
+
from specfuse.loop import agent_policy, triage
|
|
43
47
|
from specfuse.loop.build_provenance import warn_if_out_of_tree
|
|
44
48
|
|
|
45
49
|
#: Stands in for T10's classification session (gate 3, walking skeleton).
|
|
@@ -50,7 +54,13 @@ def _default_runner(argv: list, check: bool = False):
|
|
|
50
54
|
return subprocess.run(argv, check=check, capture_output=True, text=True)
|
|
51
55
|
|
|
52
56
|
|
|
53
|
-
def apply_severity_backfill(
|
|
57
|
+
def apply_severity_backfill(
|
|
58
|
+
runner: Callable,
|
|
59
|
+
repo: str,
|
|
60
|
+
decisions: list,
|
|
61
|
+
*,
|
|
62
|
+
label_projection: "dict | None" = None,
|
|
63
|
+
) -> list:
|
|
54
64
|
"""Record each decision in `decisions` against its GitHub issue, marker
|
|
55
65
|
first, label second.
|
|
56
66
|
|
|
@@ -65,6 +75,13 @@ def apply_severity_backfill(runner: Callable, repo: str, decisions: list) -> lis
|
|
|
65
75
|
amended marker in place -- the marker is the authoritative record, the
|
|
66
76
|
label a projection re-derived from it (`PLAN.md`'s "Record
|
|
67
77
|
precedence").
|
|
78
|
+
|
|
79
|
+
`label_projection` maps a vocabulary value to the label this repository
|
|
80
|
+
spells it with (`labels.severity_label_projection`, #3353). The marker
|
|
81
|
+
always records the vocabulary value, so it stays comparable across
|
|
82
|
+
repositories; only the label takes the operator's own word. Absent or
|
|
83
|
+
missing a value, the vocabulary label is written -- the behaviour before
|
|
84
|
+
the projection existed.
|
|
68
85
|
"""
|
|
69
86
|
results = []
|
|
70
87
|
for decision in decisions:
|
|
@@ -88,6 +105,7 @@ def apply_severity_backfill(runner: Callable, repo: str, decisions: list) -> lis
|
|
|
88
105
|
"number": number,
|
|
89
106
|
"severity": severity,
|
|
90
107
|
"skipped": False,
|
|
108
|
+
"severity_source": decision.get("severity_source", "classifier"),
|
|
91
109
|
}
|
|
92
110
|
|
|
93
111
|
new_body = triage.amend_marker_severity(body, severity)
|
|
@@ -104,7 +122,19 @@ def apply_severity_backfill(runner: Callable, repo: str, decisions: list) -> lis
|
|
|
104
122
|
continue
|
|
105
123
|
row["marker_written"] = True
|
|
106
124
|
|
|
107
|
-
|
|
125
|
+
if decision.get("severity_source") == "label":
|
|
126
|
+
# Reconciled (#3360): the label IS the source of this severity, so
|
|
127
|
+
# it is already on the issue. Writing one would at best re-apply
|
|
128
|
+
# what is there and at worst add a second spelling beside the
|
|
129
|
+
# person's, which is the collision this path exists to avoid.
|
|
130
|
+
row["label_written"] = False
|
|
131
|
+
row["label_skipped_reason"] = "severity read from the issue's own label"
|
|
132
|
+
results.append(row)
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
label = (label_projection or {}).get(
|
|
136
|
+
severity, triage.severity_label_for(severity)
|
|
137
|
+
)
|
|
108
138
|
try:
|
|
109
139
|
runner(
|
|
110
140
|
["gh", "issue", "edit", str(number), "--repo", repo, "--add-label", label],
|
|
@@ -156,7 +186,14 @@ def run_backfill(
|
|
|
156
186
|
once `apply_severity_backfill` has run over it, that function's own
|
|
157
187
|
`skipped`/`marker_written`/`label_written` fields.
|
|
158
188
|
"""
|
|
159
|
-
rubric
|
|
189
|
+
# One `gh label list` feeds both the rubric and the write-side
|
|
190
|
+
# projection (#3353): the once-per-run listing this run is asserted on.
|
|
191
|
+
existing, _listing_reason = list_existing_labels(
|
|
192
|
+
working_dir, runner=runner, repo=repo
|
|
193
|
+
)
|
|
194
|
+
rubric = read_severity_rubric(
|
|
195
|
+
working_dir, runner=runner, repo=repo, existing=existing
|
|
196
|
+
)
|
|
160
197
|
if not rubric:
|
|
161
198
|
return {
|
|
162
199
|
"rubric": {},
|
|
@@ -180,6 +217,8 @@ def run_backfill(
|
|
|
180
217
|
),
|
|
181
218
|
}
|
|
182
219
|
|
|
220
|
+
aliases = agent_policy.resolve_severity_aliases()
|
|
221
|
+
|
|
183
222
|
rows = []
|
|
184
223
|
decisions = []
|
|
185
224
|
for issue in candidates:
|
|
@@ -187,6 +226,31 @@ def run_backfill(
|
|
|
187
226
|
title = issue.get("title", "")
|
|
188
227
|
body = issue.get("body") or ""
|
|
189
228
|
|
|
229
|
+
# The label is consulted BEFORE any classification session (#3360): an
|
|
230
|
+
# issue a person already labelled must have its marker written from
|
|
231
|
+
# that label, never from a fresh opinion about it. This also means a
|
|
232
|
+
# reconciled issue costs no `claude` invocation at all.
|
|
233
|
+
action, labelled_severity, reason = triage.severity_decision(
|
|
234
|
+
issue.get("labels"), aliases)
|
|
235
|
+
|
|
236
|
+
if action == triage.SEVERITY_SKIP:
|
|
237
|
+
rows.append({
|
|
238
|
+
"number": number, "classified": False, "severity": None,
|
|
239
|
+
"severity_source": "label", "skipped_reason": reason,
|
|
240
|
+
})
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
if action == triage.SEVERITY_RECONCILE:
|
|
244
|
+
rows.append({
|
|
245
|
+
"number": number, "classified": True,
|
|
246
|
+
"severity": labelled_severity, "severity_source": "label",
|
|
247
|
+
})
|
|
248
|
+
decisions.append({
|
|
249
|
+
"number": number, "body": body,
|
|
250
|
+
"severity": labelled_severity, "severity_source": "label",
|
|
251
|
+
})
|
|
252
|
+
continue
|
|
253
|
+
|
|
190
254
|
argv, prompt = build_invocation(
|
|
191
255
|
number, title, body, repo, working_dir, model=model, effort=effort, rubric=rubric
|
|
192
256
|
)
|
|
@@ -194,14 +258,24 @@ def run_backfill(
|
|
|
194
258
|
severity = classify_severity(invoked.text, rubric)
|
|
195
259
|
|
|
196
260
|
if severity is None:
|
|
197
|
-
rows.append({"number": number, "classified": False, "severity": None
|
|
261
|
+
rows.append({"number": number, "classified": False, "severity": None,
|
|
262
|
+
"severity_source": "classifier"})
|
|
198
263
|
continue
|
|
199
264
|
|
|
200
|
-
rows.append({"number": number, "classified": True, "severity": severity
|
|
201
|
-
|
|
265
|
+
rows.append({"number": number, "classified": True, "severity": severity,
|
|
266
|
+
"severity_source": "classifier"})
|
|
267
|
+
decisions.append({"number": number, "body": body, "severity": severity,
|
|
268
|
+
"severity_source": "classifier"})
|
|
202
269
|
|
|
203
270
|
if apply and decisions:
|
|
204
|
-
|
|
271
|
+
# A property of the repository's label set, not of any one issue
|
|
272
|
+
# (#3353) -- derived from the listing already read above.
|
|
273
|
+
projection = severity_label_projection(
|
|
274
|
+
[item.get("name", "") for item in (existing or [])]
|
|
275
|
+
)
|
|
276
|
+
write_results = apply_severity_backfill(
|
|
277
|
+
runner, repo, decisions, label_projection=projection
|
|
278
|
+
)
|
|
205
279
|
by_number = {result["number"]: result for result in write_results}
|
|
206
280
|
for row in rows:
|
|
207
281
|
result = by_number.get(row["number"])
|
|
@@ -238,7 +312,19 @@ def _print_run_report(report: dict) -> None:
|
|
|
238
312
|
return
|
|
239
313
|
for row in report["rows"]:
|
|
240
314
|
if not row["classified"]:
|
|
241
|
-
|
|
315
|
+
# A refusal to overwrite a person is not a classification failure,
|
|
316
|
+
# and #3360's re-run condition is read off these lines: it asks for
|
|
317
|
+
# zero rows the run would contradict, which is unanswerable if both
|
|
318
|
+
# print the same sentence.
|
|
319
|
+
reason = row.get("skipped_reason")
|
|
320
|
+
if reason:
|
|
321
|
+
print(f"#{row['number']}: left alone — {reason}")
|
|
322
|
+
else:
|
|
323
|
+
print(f"#{row['number']}: no usable classification, skipped")
|
|
324
|
+
continue
|
|
325
|
+
if row.get("severity_source") == "label":
|
|
326
|
+
print(f"#{row['number']}: severity={row['severity']} "
|
|
327
|
+
f"(reconciled from its own label, not classified)")
|
|
242
328
|
continue
|
|
243
329
|
print(f"#{row['number']}: severity={row['severity']}")
|
|
244
330
|
|
|
@@ -363,10 +363,21 @@ def describe_budget_sources(**caps) -> str:
|
|
|
363
363
|
that is not capped: the operator cannot tell which of the two they have.
|
|
364
364
|
Each value is a `(value, source)` pair, source being `flag`, `policy` or
|
|
365
365
|
`none`.
|
|
366
|
+
|
|
367
|
+
`max_items` carries an explicit `per run` (#3340). The policy key it comes
|
|
368
|
+
from is named `max_items_per_day` and is applied per run -- enforcing it
|
|
369
|
+
across a day needs state that survives a process. Printing `max_items=62
|
|
370
|
+
(policy)` against that key invites the reading the name suggests, and the
|
|
371
|
+
wrong reading is the unsafe one: a day of runs can exceed the number the
|
|
372
|
+
operator believes they set. Only this cap is annotated, because only this
|
|
373
|
+
one has a name promising something wider than it delivers; an unset cap
|
|
374
|
+
prints `unbounded` and needs no qualifier.
|
|
366
375
|
"""
|
|
367
376
|
parts = []
|
|
368
377
|
for name, (value, source) in sorted(caps.items()):
|
|
369
|
-
|
|
378
|
+
rendered = "unbounded" if value is None else str(value)
|
|
379
|
+
qualifier = " per run" if name == "max_items" and value is not None else ""
|
|
380
|
+
parts.append(f"{name}={rendered} ({source}{qualifier})")
|
|
370
381
|
return "budgets: " + ", ".join(parts)
|
|
371
382
|
|
|
372
383
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.22.3
|
|
@@ -402,11 +402,75 @@ def _ensure_severity_labels(target, *, runner, repo=None) -> list:
|
|
|
402
402
|
return failed
|
|
403
403
|
|
|
404
404
|
|
|
405
|
+
def severity_label_projection(label_names, *, aliases: Optional[dict] = None) -> dict:
|
|
406
|
+
"""`{severity_value: label_name}` for the values a repository spells in its
|
|
407
|
+
own word (#3353), given the `severity:*` labels it defines.
|
|
408
|
+
|
|
409
|
+
`severity_aliases` (#3349) are honoured by the `min_severity` floor, which
|
|
410
|
+
reads an aliased label through `read_severity_label`. The classifier
|
|
411
|
+
FEAT-2026-0113 added answers in the published vocabulary instead, so the
|
|
412
|
+
write path projected `severity:high` at a repository whose issues all say
|
|
413
|
+
`severity:major` -- both spellings in circulation, and nothing reporting
|
|
414
|
+
the asymmetry.
|
|
415
|
+
|
|
416
|
+
This is the write-side half. The classifier is deliberately left answering
|
|
417
|
+
in the vocabulary so the marker's `severity=` field stays comparable across
|
|
418
|
+
repositories; only the *label* takes the repository's own word.
|
|
419
|
+
|
|
420
|
+
Pure over *label_names* rather than listing them itself, so the one caller
|
|
421
|
+
that needs both this and `read_severity_rubric` pays for a single
|
|
422
|
+
`gh label list` -- the once-per-run listing that run is asserted on.
|
|
423
|
+
|
|
424
|
+
Entries appear only where the projection actually differs, so an empty
|
|
425
|
+
result means "write the vocabulary label" and callers need no special case.
|
|
426
|
+
Two bounds keep it honest:
|
|
427
|
+
|
|
428
|
+
* **A label the repository does not define is never projected.** The
|
|
429
|
+
shipped `DEFAULT_SEVERITY_ALIASES` name words (`blocker`, `urgent`,
|
|
430
|
+
`trivial`) most repositories have never created; projecting one would
|
|
431
|
+
write a label that does not exist.
|
|
432
|
+
* **An in-vocabulary label always wins.** A repository defining both
|
|
433
|
+
`severity:high` and `severity:major` gets no entry for `high` -- the same
|
|
434
|
+
precedence `read_severity_rubric` applies when it builds the rubric.
|
|
435
|
+
|
|
436
|
+
Two aliases onto one value is the operator's own ambiguity. The lowest
|
|
437
|
+
label name sorts first, so a run is at least reproducible; defining the
|
|
438
|
+
vocabulary label is what removes the ambiguity.
|
|
439
|
+
"""
|
|
440
|
+
prefix = agent_policy.SEVERITY_LABEL_PREFIX
|
|
441
|
+
try:
|
|
442
|
+
severity_names = sorted(
|
|
443
|
+
name for name in label_names if name.startswith(prefix)
|
|
444
|
+
)
|
|
445
|
+
except TypeError:
|
|
446
|
+
return {}
|
|
447
|
+
|
|
448
|
+
in_vocabulary = {
|
|
449
|
+
name[len(prefix):] for name in severity_names
|
|
450
|
+
if name[len(prefix):] in agent_policy.SEVERITY_VALUES
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
aliases = aliases if aliases is not None else agent_policy.resolve_severity_aliases()
|
|
454
|
+
projection: dict = {}
|
|
455
|
+
for name in severity_names:
|
|
456
|
+
word = name[len(prefix):]
|
|
457
|
+
if word in agent_policy.SEVERITY_VALUES:
|
|
458
|
+
continue
|
|
459
|
+
mapped = aliases.get(word.strip().lower())
|
|
460
|
+
if mapped not in agent_policy.SEVERITY_VALUES:
|
|
461
|
+
continue
|
|
462
|
+
if mapped in in_vocabulary or mapped in projection:
|
|
463
|
+
continue
|
|
464
|
+
projection[mapped] = name
|
|
465
|
+
return projection
|
|
466
|
+
|
|
467
|
+
|
|
405
468
|
def read_severity_rubric(
|
|
406
469
|
target: str | Path,
|
|
407
470
|
*,
|
|
408
471
|
runner: Optional[Callable] = None,
|
|
409
472
|
repo: Optional[str] = None,
|
|
473
|
+
existing: Optional[list] = None,
|
|
410
474
|
) -> dict:
|
|
411
475
|
"""`{severity_value: description}` from the repo's own `severity:*` labels,
|
|
412
476
|
or specfuse's `DEFAULT_SEVERITY_RUBRIC` when it has defined none (#3352).
|
|
@@ -428,7 +492,11 @@ def read_severity_rubric(
|
|
|
428
492
|
"""
|
|
429
493
|
runner = runner if runner is not None else _default_runner
|
|
430
494
|
|
|
431
|
-
existing
|
|
495
|
+
# `existing` lets a caller that already listed the repository's labels pass
|
|
496
|
+
# them in rather than pay for a second `gh label list` (#3353). Absent, the
|
|
497
|
+
# listing happens here exactly as it always did.
|
|
498
|
+
if existing is None:
|
|
499
|
+
existing, _reason = list_existing_labels(target, runner=runner, repo=repo)
|
|
432
500
|
if existing is None:
|
|
433
501
|
return {}
|
|
434
502
|
|
|
@@ -125,7 +125,7 @@ SPECFUSE_DIR = Path(".specfuse")
|
|
|
125
125
|
REPO_ROOT = SPECFUSE_DIR.parent
|
|
126
126
|
FEATURES_DIR = SPECFUSE_DIR / "features"
|
|
127
127
|
VERIFICATION_PATH = SPECFUSE_DIR / "verification.yml"
|
|
128
|
-
DRIVER_VERSION = "0.22.
|
|
128
|
+
DRIVER_VERSION = "0.22.3"
|
|
129
129
|
# Oldest scaffold layout this driver can drive. init.sh stamps the scaffold's own
|
|
130
130
|
# version into `.specfuse/VERSION`; check_scaffold_version() fails loud at startup if
|
|
131
131
|
# the consumer's scaffold is older than this, pointing at `specfuse upgrade`. Bump
|
|
@@ -2244,6 +2244,34 @@ def untracked_paths() -> set[str]:
|
|
|
2244
2244
|
return {line.strip() for line in out.splitlines() if line.strip()}
|
|
2245
2245
|
|
|
2246
2246
|
|
|
2247
|
+
def ignored_paths() -> set[str]:
|
|
2248
|
+
"""Repo-relative paths git reports as IGNORED, file by file.
|
|
2249
|
+
|
|
2250
|
+
Twin of :func:`untracked_paths`, snapshotted per-WU at dispatch so the
|
|
2251
|
+
attempt-failure path can tell "this ignored file was already here" from
|
|
2252
|
+
"this attempt produced it" (#3330). A `git reset --hard` restores tracked
|
|
2253
|
+
content and leaves ignored files in place, so without this the baseline
|
|
2254
|
+
attribution probe measures the failed attempt's own build output and
|
|
2255
|
+
reports the failure as pre-existing.
|
|
2256
|
+
|
|
2257
|
+
`--ignored` is deliberately paired with `ls-files` rather than read off
|
|
2258
|
+
`git status --porcelain --ignored`: status collapses a wholly-ignored
|
|
2259
|
+
directory to a single entry, so `!! out/` is byte-identical before and
|
|
2260
|
+
after an attempt adds files underneath it. That is precisely the reported
|
|
2261
|
+
case -- `out/` already existed and the attempt wrote 11 new files into it --
|
|
2262
|
+
and a snapshot diff over the status listing would have seen nothing.
|
|
2263
|
+
|
|
2264
|
+
Can be large on a repository carrying vendored dependency trees, so it is
|
|
2265
|
+
called only on the failure path that actually probes, where it is cheap
|
|
2266
|
+
beside the full gate-set run it precedes.
|
|
2267
|
+
"""
|
|
2268
|
+
out = subprocess.run(
|
|
2269
|
+
["git", "ls-files", "--others", "--ignored", "--exclude-standard"],
|
|
2270
|
+
capture_output=True, text=True, check=True,
|
|
2271
|
+
).stdout
|
|
2272
|
+
return {line.strip() for line in out.splitlines() if line.strip()}
|
|
2273
|
+
|
|
2274
|
+
|
|
2247
2275
|
def _tracked_dirty_paths() -> set[str]:
|
|
2248
2276
|
"""Paths with TRACKED, uncommitted changes (staged or unstaged), repo-relative.
|
|
2249
2277
|
|
|
@@ -3787,10 +3815,70 @@ def _clean_attempt_untracked(
|
|
|
3787
3815
|
continue
|
|
3788
3816
|
|
|
3789
3817
|
|
|
3818
|
+
#: Ignored path prefixes the attempt-ignored clean never deletes. `work/` holds
|
|
3819
|
+
#: the per-attempt verify-output notes `persist_attempt_notes` writes (#168) --
|
|
3820
|
+
#: gitignored, and written AFTER the reset, so at the next attempt's reset they
|
|
3821
|
+
#: look exactly like the build output this clean exists to remove. The snapshot
|
|
3822
|
+
#: is taken once per WU rather than per attempt, so without this keep-list
|
|
3823
|
+
#: attempt 2 would delete attempt 1's evidence.
|
|
3824
|
+
IGNORED_CLEAN_KEEP_PREFIXES = ("work/", ".specfuse/")
|
|
3825
|
+
|
|
3826
|
+
|
|
3827
|
+
def _clean_attempt_ignored(
|
|
3828
|
+
ignored_before: "set[str] | frozenset[str]", events_path: Path,
|
|
3829
|
+
) -> None:
|
|
3830
|
+
"""Delete gitignored files that appeared since the *ignored_before* snapshot.
|
|
3831
|
+
|
|
3832
|
+
Defect 1 of #3330. `_clean_attempt_untracked` above deliberately leaves
|
|
3833
|
+
ignored paths alone, and that carve-out is load-bearing -- the driver's own
|
|
3834
|
+
gitignored `work/` notes have to survive the reset. But it also leaves the
|
|
3835
|
+
failed attempt's build output on disk, and the baseline attribution probe
|
|
3836
|
+
then runs against it: in the reported incident a unit's own generated files
|
|
3837
|
+
failed the probe, and the driver concluded the failure predated the feature
|
|
3838
|
+
and halted a green branch.
|
|
3839
|
+
|
|
3840
|
+
Only files that appeared SINCE the snapshot are removed, which is what makes
|
|
3841
|
+
this safe for the expensive case raised on #3371: a build cache present at
|
|
3842
|
+
dispatch is in the snapshot and is never touched. Only what this attempt
|
|
3843
|
+
produced goes.
|
|
3844
|
+
|
|
3845
|
+
Never deletes: paths in *ignored_before*, anything under
|
|
3846
|
+
`IGNORED_CLEAN_KEEP_PREFIXES`, *events_path*, or a per-criterion close-state
|
|
3847
|
+
file -- the same exclusions its untracked twin honours.
|
|
3848
|
+
|
|
3849
|
+
Best-effort: any git or filesystem error is swallowed, reverting to the old
|
|
3850
|
+
leftover behaviour rather than crashing the run.
|
|
3851
|
+
"""
|
|
3852
|
+
try:
|
|
3853
|
+
root = Path(git("rev-parse", "--show-toplevel"))
|
|
3854
|
+
candidates = ignored_paths() - set(ignored_before)
|
|
3855
|
+
except (subprocess.CalledProcessError, OSError):
|
|
3856
|
+
return
|
|
3857
|
+
try:
|
|
3858
|
+
keep = events_path.resolve()
|
|
3859
|
+
except OSError:
|
|
3860
|
+
keep = None
|
|
3861
|
+
for rel in sorted(candidates):
|
|
3862
|
+
if any(rel.startswith(p) or f"/{p}" in rel
|
|
3863
|
+
for p in IGNORED_CLEAN_KEEP_PREFIXES):
|
|
3864
|
+
continue
|
|
3865
|
+
target = root / rel
|
|
3866
|
+
try:
|
|
3867
|
+
if keep is not None and target.resolve() == keep:
|
|
3868
|
+
continue
|
|
3869
|
+
if criteria_state.CRITERIA_FILENAME_RE.match(target.name):
|
|
3870
|
+
continue
|
|
3871
|
+
if target.is_file() or target.is_symlink():
|
|
3872
|
+
target.unlink()
|
|
3873
|
+
except OSError:
|
|
3874
|
+
continue
|
|
3875
|
+
|
|
3876
|
+
|
|
3790
3877
|
def reset_preserving_events(
|
|
3791
3878
|
head_before: str,
|
|
3792
3879
|
events_path: Path,
|
|
3793
3880
|
untracked_before: "set[str] | frozenset[str] | None" = None,
|
|
3881
|
+
ignored_before: "set[str] | frozenset[str] | None" = None,
|
|
3794
3882
|
) -> None:
|
|
3795
3883
|
"""`git reset --hard <head_before>` without losing events.jsonl content.
|
|
3796
3884
|
|
|
@@ -3828,6 +3916,12 @@ def reset_preserving_events(
|
|
|
3828
3916
|
nothing untracked" and delete the entire untracked working tree; `None`
|
|
3829
3917
|
means "no snapshot available, do not clean" and preserves the pre-#162
|
|
3830
3918
|
behavior for callers that cannot supply one.
|
|
3919
|
+
|
|
3920
|
+
`ignored_before` (#3330 defect 1) does the same for GITIGNORED paths, and
|
|
3921
|
+
is supplied by the one call site whose reset is followed by the baseline
|
|
3922
|
+
attribution probe. Same inert-on-`None` contract. It is not passed on every
|
|
3923
|
+
reset: the listing it diffs walks ignored trees, which is cheap beside the
|
|
3924
|
+
gate-set run the probe performs and not worth paying where nothing probes.
|
|
3831
3925
|
"""
|
|
3832
3926
|
saved = events_path.read_text() if events_path.is_file() else None
|
|
3833
3927
|
git("reset", "--hard", head_before)
|
|
@@ -3835,6 +3929,8 @@ def reset_preserving_events(
|
|
|
3835
3929
|
events_path.write_text(saved)
|
|
3836
3930
|
if untracked_before is not None:
|
|
3837
3931
|
_clean_attempt_untracked(untracked_before, events_path)
|
|
3932
|
+
if ignored_before is not None:
|
|
3933
|
+
_clean_attempt_ignored(ignored_before, events_path)
|
|
3838
3934
|
|
|
3839
3935
|
|
|
3840
3936
|
def retain_tree_after_refusal(head_before: str, events_path: Path) -> str:
|
|
@@ -6485,6 +6581,7 @@ def baseline_evidence_diffstat(feat_fm: dict) -> "str | None":
|
|
|
6485
6581
|
def format_preexisting_gate_failure(
|
|
6486
6582
|
gate_number: int, failing_gates: list[dict], feat_fm: dict,
|
|
6487
6583
|
done_unit_ids: "list[str] | None" = None,
|
|
6584
|
+
attributed_to: "str | None" = None,
|
|
6488
6585
|
) -> str:
|
|
6489
6586
|
"""Render the `preexisting_gate_failure` halt as a message a non-expert
|
|
6490
6587
|
operator can act on without reading driver source (FEAT-2026-0051/T03).
|
|
@@ -6497,6 +6594,22 @@ def format_preexisting_gate_failure(
|
|
|
6497
6594
|
defer the feature. There is no "proceed anyway" option in v1: the waiver
|
|
6498
6595
|
is future work tracked as FEAT-2026-0052, never offered here as if it
|
|
6499
6596
|
already existed.
|
|
6597
|
+
|
|
6598
|
+
`attributed_to` names the work unit whose failed attempt triggered the
|
|
6599
|
+
probe (#3330 defect 2). This message was written for a gate-entry probe,
|
|
6600
|
+
where "zero work units were dispatched" was true; FEAT-2026-0109/T01 then
|
|
6601
|
+
removed that probe, leaving attribution as the only path that renders it --
|
|
6602
|
+
and on that path a unit *was* dispatched and its failure is what caused the
|
|
6603
|
+
re-run. Left unsaid, every fresh-gate claim here is false exactly when the
|
|
6604
|
+
message is shown.
|
|
6605
|
+
|
|
6606
|
+
The proof is softened on every path (#3371). A `git diff --stat` compares
|
|
6607
|
+
TRACKED files, so it cannot see a check's gitignored inputs; `git reset
|
|
6608
|
+
--hard` restores tracked files and leaves ignored ones behind, which is how
|
|
6609
|
+
a failed attempt's own build output ends up failing the probe that is
|
|
6610
|
+
supposed to exonerate it. Stating "the failure predates this feature" from
|
|
6611
|
+
that diff reads as conclusive and cost one reported incident a bisect
|
|
6612
|
+
across three commits.
|
|
6500
6613
|
"""
|
|
6501
6614
|
# A RESUMED gate -- one where work units already ran and committed -- has a
|
|
6502
6615
|
# baseline that INCLUDES this feature's own landed work, so the fresh-gate
|
|
@@ -6505,8 +6618,17 @@ def format_preexisting_gate_failure(
|
|
|
6505
6618
|
# that a previous unit of the same gate had added two commits earlier.
|
|
6506
6619
|
done_unit_ids = list(done_unit_ids or [])
|
|
6507
6620
|
resumed = bool(done_unit_ids)
|
|
6621
|
+
attributed = bool(attributed_to) and not resumed
|
|
6508
6622
|
|
|
6509
|
-
if
|
|
6623
|
+
if attributed:
|
|
6624
|
+
lines = [
|
|
6625
|
+
f"Gate {gate_number} is blocked: the automated checks it depends "
|
|
6626
|
+
f"on failed on a probe the driver ran AFTER {attributed_to}'s "
|
|
6627
|
+
f"attempt failed and its changes were reset.",
|
|
6628
|
+
"",
|
|
6629
|
+
"Failing check(s) found by that probe:",
|
|
6630
|
+
]
|
|
6631
|
+
elif resumed:
|
|
6510
6632
|
lines = [
|
|
6511
6633
|
f"Gate {gate_number} is blocked: the automated checks it depends on "
|
|
6512
6634
|
f"are failing, and this gate has ALREADY LANDED WORK.",
|
|
@@ -6526,7 +6648,14 @@ def format_preexisting_gate_failure(
|
|
|
6526
6648
|
f"(signature: {g['failure_signature']})"
|
|
6527
6649
|
)
|
|
6528
6650
|
lines.append("")
|
|
6529
|
-
if
|
|
6651
|
+
if attributed:
|
|
6652
|
+
lines.append(
|
|
6653
|
+
f"{attributed_to} WAS dispatched and its attempt failed. The "
|
|
6654
|
+
f"driver then reset the tree and re-ran the checks to ask whether "
|
|
6655
|
+
f"the failure predated that attempt; the result above is what "
|
|
6656
|
+
f"that probe found."
|
|
6657
|
+
)
|
|
6658
|
+
elif resumed:
|
|
6530
6659
|
lines.append(
|
|
6531
6660
|
f"{len(done_unit_ids)} work unit(s) in this gate are already `done` "
|
|
6532
6661
|
f"and committed, so the baseline these checks measured includes this "
|
|
@@ -6554,12 +6683,16 @@ def format_preexisting_gate_failure(
|
|
|
6554
6683
|
)
|
|
6555
6684
|
else:
|
|
6556
6685
|
lines.append(
|
|
6557
|
-
"
|
|
6558
|
-
"the integration branch (
|
|
6559
|
-
"
|
|
6560
|
-
"files, not code the failing check(s) read):"
|
|
6686
|
+
"The failing check(s)' TRACKED input files are unchanged vs "
|
|
6687
|
+
"the integration branch (the diff below is the feature's own "
|
|
6688
|
+
"plan/scaffold files, not code the failing check(s) read):"
|
|
6561
6689
|
)
|
|
6562
6690
|
lines.append(diffstat)
|
|
6691
|
+
elif attributed:
|
|
6692
|
+
lines.append(
|
|
6693
|
+
"Base-tree comparison unavailable (no integration branch could "
|
|
6694
|
+
"be resolved, or the comparison itself failed)."
|
|
6695
|
+
)
|
|
6563
6696
|
else:
|
|
6564
6697
|
lines.append(
|
|
6565
6698
|
"Base-tree comparison unavailable (no integration branch could "
|
|
@@ -6567,9 +6700,41 @@ def format_preexisting_gate_failure(
|
|
|
6567
6700
|
"check(s) above were still measured before any work unit ran, "
|
|
6568
6701
|
"so the conclusion stands even without this proof."
|
|
6569
6702
|
)
|
|
6703
|
+
if not resumed:
|
|
6704
|
+
# The limit of the proof above, stated wherever it is offered (#3371).
|
|
6705
|
+
# A tracked diff is consistent with BOTH "pre-existing" and "left
|
|
6706
|
+
# behind by this run", so naming what it cannot see is the difference
|
|
6707
|
+
# between a reader checking `out/` first and bisecting the branch.
|
|
6708
|
+
lines.append("")
|
|
6709
|
+
lines.append(
|
|
6710
|
+
"What that comparison does NOT cover: untracked and gitignored "
|
|
6711
|
+
"paths. `git reset --hard` restores tracked files and leaves "
|
|
6712
|
+
"ignored ones in place, so a check that reads a build or output "
|
|
6713
|
+
"directory (`out/`, `target/`, generated-code trees, caches) can "
|
|
6714
|
+
"fail on state this feature's own failed attempt produced. The "
|
|
6715
|
+
"diff above cannot distinguish that from pre-existing breakage."
|
|
6716
|
+
)
|
|
6570
6717
|
lines.append("")
|
|
6571
6718
|
lines.append("What to do next:")
|
|
6572
|
-
if
|
|
6719
|
+
if attributed:
|
|
6720
|
+
lines.append(
|
|
6721
|
+
" 1. Check for leftover untracked/ignored state from the reset "
|
|
6722
|
+
"attempt FIRST — it is the cheapest to rule out and the most "
|
|
6723
|
+
"likely cause. `git status --ignored --short` and `git clean -ndX` "
|
|
6724
|
+
"list what survived; if a listed path feeds a failing check, that "
|
|
6725
|
+
"is your answer."
|
|
6726
|
+
)
|
|
6727
|
+
lines.append(
|
|
6728
|
+
" 2. Only if the tree is clean of those is this pre-existing "
|
|
6729
|
+
"debt — confirm by running the failing check(s) on the "
|
|
6730
|
+
"integration branch in a fresh checkout before assuming it."
|
|
6731
|
+
)
|
|
6732
|
+
lines.append(
|
|
6733
|
+
f" 3. If it was leftover state, remove it and re-run; "
|
|
6734
|
+
f"{attributed_to} is back at its previous attempt count and will "
|
|
6735
|
+
f"be retried."
|
|
6736
|
+
)
|
|
6737
|
+
elif resumed:
|
|
6573
6738
|
lines.append(
|
|
6574
6739
|
" 1. Find which of this gate's landed work units introduced the "
|
|
6575
6740
|
"failing check, and fix it ON THIS BRANCH. `git log -S<signature> "
|
|
@@ -10671,6 +10836,13 @@ def run(
|
|
|
10671
10836
|
# (#150). Captured here, not inside squash_commit, because by
|
|
10672
10837
|
# then the agent's own new files are indistinguishable from it.
|
|
10673
10838
|
untracked_before = untracked_paths()
|
|
10839
|
+
# Ignored counterpart (#3330 defect 1), taken in the same
|
|
10840
|
+
# breath: only the attempt-failure reset that precedes the
|
|
10841
|
+
# baseline attribution probe consumes it, but it has to be
|
|
10842
|
+
# captured HERE, before the agent runs, or the attempt's own
|
|
10843
|
+
# build output is already indistinguishable from what was
|
|
10844
|
+
# on disk when it started.
|
|
10845
|
+
ignored_before = ignored_paths()
|
|
10674
10846
|
_is_rearm = detect_rearm_dispatch(wu)
|
|
10675
10847
|
if _is_rearm:
|
|
10676
10848
|
fold_cumulative_on_rearm(wu, backend)
|
|
@@ -11646,9 +11818,15 @@ def run(
|
|
|
11646
11818
|
untracked_before=untracked_before)
|
|
11647
11819
|
print(" no FINDINGS metric — reset to base")
|
|
11648
11820
|
else:
|
|
11821
|
+
# `ignored_before` is passed HERE and nowhere else: this
|
|
11822
|
+
# is the one reset whose tree is then measured by the
|
|
11823
|
+
# attribution probe below, so it is the one place where
|
|
11824
|
+
# the attempt's own gitignored output would be read as
|
|
11825
|
+
# pre-existing breakage (#3330 defect 1).
|
|
11649
11826
|
reset_preserving_events(
|
|
11650
11827
|
head_before, events_path,
|
|
11651
|
-
untracked_before=untracked_before
|
|
11828
|
+
untracked_before=untracked_before,
|
|
11829
|
+
ignored_before=ignored_before)
|
|
11652
11830
|
# Retroactive baseline attribution (FEAT-2026-0109/T01):
|
|
11653
11831
|
# decide, right after the reset above restores the
|
|
11654
11832
|
# tree the unit was dispatched against, whether this
|
|
@@ -11685,7 +11863,8 @@ def run(
|
|
|
11685
11863
|
gate.number, _attr_failing, feat_fm,
|
|
11686
11864
|
done_unit_ids=[
|
|
11687
11865
|
u.wu_id for u in units
|
|
11688
|
-
if u.status == "done"]
|
|
11866
|
+
if u.status == "done"],
|
|
11867
|
+
attributed_to=wu.wu_id))
|
|
11689
11868
|
wu_events.append(build_event(
|
|
11690
11869
|
"human_escalation", feature_id, {
|
|
11691
11870
|
"reason": "preexisting_gate_failure",
|