specfuse-loop 0.22.0__tar.gz → 0.22.2__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.0/specfuse_loop.egg-info → specfuse_loop-0.22.2}/PKG-INFO +1 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/pyproject.toml +1 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/providers/bugs.py +12 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/run.py +33 -5
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/severity_backfill.py +38 -5
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/agent_policy.py +52 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/bug_lane_run.py +52 -2
- specfuse_loop-0.22.2/specfuse/loop/data/VERSION +1 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/labels.py +69 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/loop.py +1 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/SOURCES.txt +5 -0
- specfuse_loop-0.22.2/tests/test_hygiene_boundary_guidance.py +88 -0
- specfuse_loop-0.22.2/tests/test_per_run_item_cap_disclosure.py +80 -0
- specfuse_loop-0.22.2/tests/test_plugin_prose_cross_refs.py +80 -0
- specfuse_loop-0.22.2/tests/test_required_checks.py +161 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_run_level_failure_breaker.py +113 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_severity_backfill_run.py +60 -0
- specfuse_loop-0.22.2/tests/test_severity_label_projection.py +138 -0
- specfuse_loop-0.22.0/specfuse/loop/data/VERSION +0 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/LICENSE +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/NOTICE +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/README.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/setup.cfg +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/__init__.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/budget.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/diagnose_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/drafting_answers.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/drafting_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/drafting_questions.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/driver_command.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/driver_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/monitoring_read.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/providers/__init__.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/providers/answers.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/providers/feature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/providers/findings_autofix.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/providers/findings_diagnose.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/providers/triage.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/queue_read.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/repo_detect.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/triage_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/agent/worktree.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/_filelock.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/_wu_sections.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/arm_eval.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/arm_sweep.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/arm_txn.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/bug_lane.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/bug_lane_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/build_provenance.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/caller_check.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/changelog.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/closing_requirements.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/cost.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/criteria_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/glossary.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/methodology.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/gitignore.snippet +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/monitoring.yml.example +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/close-discipline.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/human-output.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/operator-escalation.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/planning-discipline.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/verification-discipline.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/rules-local/README.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/event.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/decisions_format.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/driver_edit.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/escalation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/events_stats.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/gate_commands.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/heartbeat.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/judge.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/learnings_distil.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/learnings_query.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/lint_closing.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/lint_monitoring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/lint_plan.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/lint_roadmap.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/notify.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/notify_escalation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/notify_sla.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/plan_baseline.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/policy_proposals.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/policy_review.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/prerun.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/prerun_capture.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/promotion.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/rearm_migration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/replay_spin.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/roadmap_archive.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/scaffold.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/triage.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/__init__.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/adapters.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/artifact.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/autofix.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/autofix_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/autofix_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/autofix_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/cli.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/diagnose_cli.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/diagnosis.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/fingerprint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/issues.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/providers/__init__.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/providers/_azure_auth.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/providers/azure_app_insights.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/providers/azure_service_bus.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/redaction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse/monitor/schedule.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_abandon_feature_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_abandoned_work_reported.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_abandoned_wu_strands_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_budget.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_bug_selection_dedup.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_default_features_root.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_driver_command.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_driver_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_driver_restart.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_escalation_on_issue.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_escalation_run_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_findings_seam.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_invoke_timeout.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_invoke_usage.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_item_worktree.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_lock.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_policy_key_ownership.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_policy_queue.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_policy_schema.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_policy_triage_dial.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_provider_answers.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_provider_bugs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_provider_feature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_provider_findings_autofix.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_provider_findings_diagnose.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_provider_isolation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_provider_triage.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_queue_read.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_relays_driver_output.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_repo_detect.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_run_progress.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_seam.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_snapshot_refresh.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_agent_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_answer_escalation_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_app_insights_adapters.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_archive_ref_reconciliation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_arm_eval.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_arm_eval_lint_class.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_arm_eval_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_arm_sweep.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_arm_sweep_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_arm_txn.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_arm_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_artifact_redaction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_attempt_note_untracked_files.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_attempt_outcome_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_attention_nonwriting_guard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_attention_silence_section.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_attention_skill_structure.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autoclose_deferral_visibility.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autoclose_gate_note.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autoclose_stub_states_what_passed.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autofix_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autofix_live.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autofix_predicate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autofix_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autofix_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autosync_no_cwd_leak.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_azure_credential_resolution.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_backend.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_baseline_escalation_message.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_baseline_persistence.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_baseline_probe.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_baseline_provenance.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_baseline_tree_hash_key.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bash_routing.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bats_suites_gated.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_binary_verdict.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_binding_block_advisory.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_binding_block_allocation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_binding_block_budget.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bookkeeping_no_verify.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_brief_covers_every_unit_escalation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_ci_pending.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_guardrails.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_label_nonfatal.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_labels_registered.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_min_severity.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_policy_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_pr_number_carried.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_preflight_and_dial.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_bug_lane_test_paths.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_build_provenance.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_caller_check_ratchet.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_changed_file_test_selection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_changelog_append_only.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_changelog_collection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_changelog_release_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_changelog_schema.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_ci_conclusion_wait.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_ci_runs_every_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_claude_resolution.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_close_b_pending_learnings.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_close_records_feature_oracle.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_close_wu_self_reference.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_closing_guard_contracts.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_closing_guard_prediction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_closing_learnings_destination.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_closing_requirements.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_coauthor_trailer_allowlist.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_conditional_reflection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_console_encoding.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_convergent_iteration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_convergent_wu_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_correlation_id_override.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_correlation_id_two_surfaces.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_cost_lifetime.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_criteria_per_gate_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_criteria_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_criteria_worklist.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_decision_citation_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_decision_followups_discharged.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_decision_override_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_decisions_format.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_default_severity_aliases.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_degraded_oracle.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_deliverable_missing_note.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_derive_agent_policy_review_mode.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_derive_agent_policy_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_derive_monitoring_discovery.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_derive_monitoring_skill_registration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_design_for_diagnosis_rule.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_deterministic_refusal_repeat.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_diagnose_headless.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_diagnose_issue_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_diagnosis.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_diagnosis_roundtrip_live.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_dispatch_skeleton.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_distilled_accept_step.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_doctor_command_preflight.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_done_feature_gates.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_draft_feature_answers_mode.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_drafting_answer_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_drafting_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_drafting_question_issue.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_drafting_questions.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_drafting_reply_shape.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_driver_edit_detection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_driver_event_registry_covers_emitters.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_driver_module_surface.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_driver_restart_halt_surfaced.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_driver_restart_halt_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_driver_restart_hold.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_driver_staleness_gate_summary.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_driver_staleness_warning.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_dry_run_no_branch_mutation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_ensure_feature_branch_base.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_escalation_assignee_policy.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_escalation_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_escalation_crash_containment.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_escalation_dedup_search.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_escalation_emit.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_event_gate_full_envelope.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_events_redaction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_events_stats.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_failure_artifact_model.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_failure_signature_fail_prefix.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_failure_signature_skips_log_noise.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_feature_branch_auto_sync.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_feature_branch_squash_merge.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_feature_branch_stale_base.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_feature_id_resolution.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_feature_oracle_e2e.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_feature_provider_drafting_dispatch.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_feature_review_accumulation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_filelock_portable.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_files_changed_auto_repair.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_find_pr_no_search_index.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_fingerprint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_fix_bug_diff_self_check.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_fix_bug_headless.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_fix_bug_reads_comments.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_fold_contract_documented.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_followups_artifact.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_frontmatter_scalar_quoting.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gate_broad_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gate_budget.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gate_budget_post_dispatch.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gate_commands_needs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gate_eval_intermediate_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gate_eval_repo_flag.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gate_eval_terminal_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gate_report_verdict.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_gitleaks_pinning.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_groom_backlog_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_guard_refusal_failure_class.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_guard_refusal_failure_excerpt.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_guard_repair_e2e.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_guard_repair_prompt.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_heartbeat.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_heartbeat_adapter.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_human_output_rule.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_human_work_unit.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_hyphenated_gate_name.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_init_integration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_installed_copy_driver_e2e.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_interpreter_normalization.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_judge_close_path.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_judge_cost.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_judge_cost_fold_committed.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_judge_module.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_judge_path_registry.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_judge_terminal_flip_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_label_provisioning_runner_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_label_registry.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_label_registry_covers_consumers.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_language_aware_narrow_selection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lazy_baseline_e2e.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_learnings_curate_vendored_promote.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_learnings_distil_cli.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_learnings_query.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_learnings_staging.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_learnings_suggest_signature_filter.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_learnings_weights.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lifetime_accounting.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_ac_observable.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_boundary_consistency.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_boundary_extraction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_close_verdict_not_predecided.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_closing.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_closing_criteria.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_closing_criteria_pristine.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_covers_load_wu_invariants.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_feature_oracle_declared.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_gate_proportionality.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_monitoring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_plan_contract_fields.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_plan_relative_import.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_plan_verdict_exempt.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_planning_sections.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_produces_satisfiability.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_roadmap.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_lint_verification_reference.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_live_tests_opt_in.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_load_bearing_close_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_attempt_evidence.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_criteria_skeleton.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_criteria_survival.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_gate_needs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_worklist_injection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_make_repo_gpgsign_isolation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_marker_dual_shape.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_maven_failure_excerpt.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_max_attempts_configurable.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_max_open_prs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_monitor_cli.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_monitor_issue_lifecycle.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_monitor_runner_surfaces.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_monitoring_cron_dialect.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_monitoring_example.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_monitoring_fenced_blocks.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_monitoring_seed.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_narrow_fallback_warning.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_no_pytest_subprocess.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_notify.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_notify_escalation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_notify_sla.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_operator_escalation_rule.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_oracle_set_declared.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_oracle_unrunnable_attribution.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_pick_feature_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_pin_honesty_and_integrity.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_pin_marker_not_inherited.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_pin_marker_spawn_sites.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_plan_baseline.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_planning_cost_floor.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_plugin_qualified_dispatch.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_policy_budgets_applied.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_policy_proposals.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_policy_review.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_pr_checks_json_field.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_pr_reference_in_title.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_prep_halt_operator_surface.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_prepare_feature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_prepare_scaffold_sync.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_prerun_capture.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_prerun_oracles.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_prerun_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_produced_document_verdict_guard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_produces_bracket_literal.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_produces_bracket_wildcard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_produces_in_diff.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_produces_justification.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_produces_recursive_glob.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_produces_semantics_unified.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_produces_shape.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_progress_forward_note.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_progress_lines_end_to_end.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_promotion_ledger.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_provider_escalation_traces.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_provision_labels.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_queue_stalled_adapter.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_rearm_fold_marker.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_rearm_migration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_red_baseline_never_reused.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_redaction_windows_home.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_reflection_required_signals.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_replan_end_to_end.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_replan_event_emission.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_replan_note_collision.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_replan_trigger.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_replan_turn_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_replay_spin.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_resolve_base.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_restart_cap_counts_progress.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_result_block_audience.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_resume_command_build.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_retain_on_guard_refusal_docs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_cli.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_shim.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_skill_delegates.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_roadmap_link_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_ruff_verdict_lines.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_rule_import_convergence.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_data_in_sync.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_doc_hygiene.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_docs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_init.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_label_provisioning.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_resources.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_skew_halts.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_scaffold_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_schedule_dialect.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_seed_documents_prerun_keys.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_service_bus_dlq_adapter.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_session_env_preflight.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_severity_aliases.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_severity_backfill_apply.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_severity_backfill_end_to_end.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_severity_backfill_limit.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_severity_backfill_marker.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_severity_rubric.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_shipped_templates_parse.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_skill_discovery_links.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_skills_vendored_in_sync.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_spinning_rearm_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_spinout_brief_end_to_end.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_spinout_brief_replan_option.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_stop_rationale_captured.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_stranded_halt_cause.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_surefire_run_level_signature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_surefire_verdict_lines.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_terminal_gate_message.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_tiered_verification_e2e.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_timeout_kill_portable.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_triage.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_triage_apply.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_triage_severity_classify.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_triage_severity_end_to_end.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_triage_severity_write.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_triage_skill_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_triage_skips_agent_escalations.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_tsc_verdict_lines.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_umbrella_version_resolution.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_unterminated_frontmatter.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_untracked_file_absorption.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_validate_event_driver_types.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_windows_gate_exec.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_worktree_restore.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/tests/test_wu_execution_metadata.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.2}/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.2"
|
|
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"
|
|
@@ -788,7 +788,18 @@ class BugsProvider:
|
|
|
788
788
|
label_written=result.label_written,
|
|
789
789
|
)
|
|
790
790
|
|
|
791
|
-
|
|
791
|
+
# #3372: only a session that produced NO outcome marker can have an
|
|
792
|
+
# environmental cause — a dispatch that failed, an empty result, a
|
|
793
|
+
# crash. `outcome_named` is exactly that distinction, already carried
|
|
794
|
+
# from the session's own text (#3343). A `refused` is `/fix-bug`
|
|
795
|
+
# reading the issue and judging it out of scope; three of those say the
|
|
796
|
+
# queue holds three feature-scoped issues, not that anything is broken.
|
|
797
|
+
return ActionOutcome(
|
|
798
|
+
status=STATUS_ESCALATED,
|
|
799
|
+
detail=detail,
|
|
800
|
+
escalation=escalation,
|
|
801
|
+
environmental=not result.outcome_named,
|
|
802
|
+
)
|
|
792
803
|
|
|
793
804
|
def reconcile(self, item: ActionItem, outcome: ActionOutcome) -> None:
|
|
794
805
|
return None
|
|
@@ -220,6 +220,24 @@ class ActionOutcome:
|
|
|
220
220
|
#: same shape as `NON_JUDGE_MODULES` and
|
|
221
221
|
#: `DEPENDENCY_MANIFEST_NAMED_UNCOVERED`.
|
|
222
222
|
escalation_waived: str = ""
|
|
223
|
+
#: Whether this escalation could have an environmental cause (#3372).
|
|
224
|
+
#:
|
|
225
|
+
#: Only an outcome reached WITHOUT reading the item can be about the run's
|
|
226
|
+
#: environment: a dispatch that failed, a session that produced no outcome
|
|
227
|
+
#: marker, a crash. An outcome reached BY reading the item is a judgement
|
|
228
|
+
#: about that item — `/fix-bug`'s `refused` says the work is not bug-sized,
|
|
229
|
+
#: `ci_not_green` and `pr_not_found` are facts about that issue's PR — and
|
|
230
|
+
#: three in a row says the queue holds three such issues, not that anything
|
|
231
|
+
#: is broken.
|
|
232
|
+
#:
|
|
233
|
+
#: #3343's breaker counted every escalation and stopped a run that was
|
|
234
|
+
#: working: two fixes merged, one held on red CI, six correctly declined,
|
|
235
|
+
#: 19 items never reached, and an escalation asserting an environment
|
|
236
|
+
#: cause a human then had to disprove.
|
|
237
|
+
#:
|
|
238
|
+
#: **Defaults False so a provider must opt in.** A provider that says
|
|
239
|
+
#: nothing never trips the breaker, rather than tripping it by omission.
|
|
240
|
+
environmental: bool = False
|
|
223
241
|
|
|
224
242
|
|
|
225
243
|
class ActionProvider(Protocol):
|
|
@@ -1003,12 +1021,22 @@ def run_agent(
|
|
|
1003
1021
|
# #3343: an escalation whose cause keeps repeating is not about
|
|
1004
1022
|
# the item. Counted here rather than in a provider, because only
|
|
1005
1023
|
# the run can see that the SAME thing failed N times.
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1024
|
+
#
|
|
1025
|
+
# #3372: but only an outcome the provider marks `environmental`
|
|
1026
|
+
# can have an environmental cause. A per-item judgement —
|
|
1027
|
+
# `refused`, `ci_not_green`, `pr_not_found` — RESETS the count
|
|
1028
|
+
# rather than being ignored: a lane still reaching verdicts on
|
|
1029
|
+
# items is not one whose environment is broken.
|
|
1030
|
+
if not outcome.environmental:
|
|
1031
|
+
last_failure_signature = None
|
|
1032
|
+
consecutive_identical_failures = 0
|
|
1009
1033
|
else:
|
|
1010
|
-
|
|
1011
|
-
|
|
1034
|
+
signature = failure_signature(item, outcome)
|
|
1035
|
+
if signature == last_failure_signature:
|
|
1036
|
+
consecutive_identical_failures += 1
|
|
1037
|
+
else:
|
|
1038
|
+
last_failure_signature = signature
|
|
1039
|
+
consecutive_identical_failures = 1
|
|
1012
1040
|
|
|
1013
1041
|
if consecutive_identical_failures >= IDENTICAL_FAILURE_LIMIT:
|
|
1014
1042
|
run_reason = (
|
|
@@ -37,7 +37,11 @@ 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
46
|
from specfuse.loop import triage
|
|
43
47
|
from specfuse.loop.build_provenance import warn_if_out_of_tree
|
|
@@ -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:
|
|
@@ -104,7 +121,9 @@ def apply_severity_backfill(runner: Callable, repo: str, decisions: list) -> lis
|
|
|
104
121
|
continue
|
|
105
122
|
row["marker_written"] = True
|
|
106
123
|
|
|
107
|
-
label =
|
|
124
|
+
label = (label_projection or {}).get(
|
|
125
|
+
severity, triage.severity_label_for(severity)
|
|
126
|
+
)
|
|
108
127
|
try:
|
|
109
128
|
runner(
|
|
110
129
|
["gh", "issue", "edit", str(number), "--repo", repo, "--add-label", label],
|
|
@@ -156,7 +175,14 @@ def run_backfill(
|
|
|
156
175
|
once `apply_severity_backfill` has run over it, that function's own
|
|
157
176
|
`skipped`/`marker_written`/`label_written` fields.
|
|
158
177
|
"""
|
|
159
|
-
rubric
|
|
178
|
+
# One `gh label list` feeds both the rubric and the write-side
|
|
179
|
+
# projection (#3353): the once-per-run listing this run is asserted on.
|
|
180
|
+
existing, _listing_reason = list_existing_labels(
|
|
181
|
+
working_dir, runner=runner, repo=repo
|
|
182
|
+
)
|
|
183
|
+
rubric = read_severity_rubric(
|
|
184
|
+
working_dir, runner=runner, repo=repo, existing=existing
|
|
185
|
+
)
|
|
160
186
|
if not rubric:
|
|
161
187
|
return {
|
|
162
188
|
"rubric": {},
|
|
@@ -201,7 +227,14 @@ def run_backfill(
|
|
|
201
227
|
decisions.append({"number": number, "body": body, "severity": severity})
|
|
202
228
|
|
|
203
229
|
if apply and decisions:
|
|
204
|
-
|
|
230
|
+
# A property of the repository's label set, not of any one issue
|
|
231
|
+
# (#3353) -- derived from the listing already read above.
|
|
232
|
+
projection = severity_label_projection(
|
|
233
|
+
[item.get("name", "") for item in (existing or [])]
|
|
234
|
+
)
|
|
235
|
+
write_results = apply_severity_backfill(
|
|
236
|
+
runner, repo, decisions, label_projection=projection
|
|
237
|
+
)
|
|
205
238
|
by_number = {result["number"]: result for result in write_results}
|
|
206
239
|
for row in rows:
|
|
207
240
|
result = by_number.get(row["number"])
|
|
@@ -46,6 +46,7 @@ __all__ = (
|
|
|
46
46
|
"SEVERITY_VALUES",
|
|
47
47
|
"read_severity_label",
|
|
48
48
|
"resolve_severity_aliases",
|
|
49
|
+
"resolve_required_checks",
|
|
49
50
|
"DEFAULT_SEVERITY_ALIASES",
|
|
50
51
|
"AUTOMERGE_VALUES",
|
|
51
52
|
"GATE_REVIEW_VALUES",
|
|
@@ -295,6 +296,45 @@ def resolve_max_items(flag: "int | None",
|
|
|
295
296
|
return _positive_int_budget(path, "max_items_per_day")
|
|
296
297
|
|
|
297
298
|
|
|
299
|
+
def resolve_required_checks(path: "str | Path | None" = None) -> tuple:
|
|
300
|
+
"""`rules.bugs.required_checks` as a tuple of check names, or `()` (#3373).
|
|
301
|
+
|
|
302
|
+
The names that must be **present and passing** before the bug lane may
|
|
303
|
+
auto-merge. Empty when absent or unusable, so a deployment that never
|
|
304
|
+
declared it behaves exactly as before: any `success` conclusion is accepted.
|
|
305
|
+
|
|
306
|
+
Why it exists. `evaluate_merge_guardrails` accepted any `success`, and a
|
|
307
|
+
repository whose PR job is a deliberate fast lane — excluding a test group
|
|
308
|
+
that guards committed expectations — gave a `success` that **could not
|
|
309
|
+
fail** on that class of defect. A bug-lane PR merged under `automerge: "on"`
|
|
310
|
+
and left `main` red for an hour.
|
|
311
|
+
|
|
312
|
+
The split those repositories rely on assumes every change already passed
|
|
313
|
+
the full gate set before its PR opened. That holds for driver work-unit
|
|
314
|
+
PRs and does **not** hold for bug-lane PRs, where a headless session
|
|
315
|
+
chooses which tests to run. This key is how a repository points the lane at
|
|
316
|
+
the complete check rather than the fast one.
|
|
317
|
+
|
|
318
|
+
A string is accepted as a one-element list — the common single-check case —
|
|
319
|
+
and anything else unusable resolves to `()`. Requiring nothing is the
|
|
320
|
+
pre-existing behaviour; inventing a requirement from a malformed value
|
|
321
|
+
would block every merge on a typo.
|
|
322
|
+
"""
|
|
323
|
+
try:
|
|
324
|
+
policy = load_policy(path)
|
|
325
|
+
except (FileNotFoundError, OSError):
|
|
326
|
+
return ()
|
|
327
|
+
rules = policy.get("rules") if isinstance(policy, dict) else None
|
|
328
|
+
bugs = rules.get("bugs") if isinstance(rules, dict) else None
|
|
329
|
+
raw = bugs.get("required_checks") if isinstance(bugs, dict) else None
|
|
330
|
+
if isinstance(raw, str):
|
|
331
|
+
name = raw.strip()
|
|
332
|
+
return (name,) if name else ()
|
|
333
|
+
if not isinstance(raw, list):
|
|
334
|
+
return ()
|
|
335
|
+
return tuple(str(n).strip() for n in raw if str(n).strip())
|
|
336
|
+
|
|
337
|
+
|
|
298
338
|
def resolve_max_open_prs(path: "str | Path | None" = None) -> "int | None":
|
|
299
339
|
"""`budgets.max_open_prs`, or None when unset or unusable (#3340).
|
|
300
340
|
|
|
@@ -323,10 +363,21 @@ def describe_budget_sources(**caps) -> str:
|
|
|
323
363
|
that is not capped: the operator cannot tell which of the two they have.
|
|
324
364
|
Each value is a `(value, source)` pair, source being `flag`, `policy` or
|
|
325
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.
|
|
326
375
|
"""
|
|
327
376
|
parts = []
|
|
328
377
|
for name, (value, source) in sorted(caps.items()):
|
|
329
|
-
|
|
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})")
|
|
330
381
|
return "budgets: " + ", ".join(parts)
|
|
331
382
|
|
|
332
383
|
|
|
@@ -38,6 +38,7 @@ from typing import Any, Callable, Optional
|
|
|
38
38
|
|
|
39
39
|
from specfuse.loop.agent_policy import (
|
|
40
40
|
bug_lane_ci_wait_seconds,
|
|
41
|
+
resolve_required_checks,
|
|
41
42
|
bug_lane_limits,
|
|
42
43
|
resolve_bug_automerge,
|
|
43
44
|
)
|
|
@@ -197,7 +198,40 @@ _BUCKET_PENDING = "pending"
|
|
|
197
198
|
_CI_FAILING = "fail"
|
|
198
199
|
|
|
199
200
|
|
|
200
|
-
def
|
|
201
|
+
def _required_checks_verdict(rows: list, required) -> str:
|
|
202
|
+
"""`""` when every required check is present and passing, else why not.
|
|
203
|
+
|
|
204
|
+
A required check that **never ran** is the case #3373 is about: the
|
|
205
|
+
repository's PR job was green and the check that would have caught the
|
|
206
|
+
defect was not in the list at all. It is not a failure — nothing failed —
|
|
207
|
+
and it is equally not the green the operator asked the lane to require.
|
|
208
|
+
|
|
209
|
+
A required check that was **skipped** is treated the same way. Skipping is
|
|
210
|
+
not a failure in general, which is why `_BUCKETS_OK` tolerates it, but a
|
|
211
|
+
check an operator named as required and that did not run cannot establish
|
|
212
|
+
what it was required to establish.
|
|
213
|
+
"""
|
|
214
|
+
if not required:
|
|
215
|
+
return ""
|
|
216
|
+
by_name = {}
|
|
217
|
+
for row in rows:
|
|
218
|
+
name = row.get("name")
|
|
219
|
+
if isinstance(name, str):
|
|
220
|
+
by_name[name] = str(row.get("bucket") or "").lower()
|
|
221
|
+
for name in required:
|
|
222
|
+
bucket = by_name.get(name)
|
|
223
|
+
if bucket is None:
|
|
224
|
+
return f"required check never ran: {name}"
|
|
225
|
+
if bucket == _BUCKET_PENDING:
|
|
226
|
+
return _CI_PENDING
|
|
227
|
+
if bucket != "pass":
|
|
228
|
+
return f"required check not passing: {name} ({bucket})"
|
|
229
|
+
return ""
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _read_ci_conclusion_once(
|
|
233
|
+
runner: Callable, repo: str, pr_number: int, required=()
|
|
234
|
+
) -> str:
|
|
201
235
|
"""One `gh pr checks` read, mapped through `bucket`.
|
|
202
236
|
|
|
203
237
|
`_CI_PENDING` when any check is queued or running, or when no check is
|
|
@@ -244,6 +278,14 @@ def _read_ci_conclusion_once(runner: Callable, repo: str, pr_number: int) -> str
|
|
|
244
278
|
|
|
245
279
|
if _BUCKET_PENDING in buckets:
|
|
246
280
|
return _CI_PENDING
|
|
281
|
+
|
|
282
|
+
# #3373: the operator's own list, checked before the aggregate verdict.
|
|
283
|
+
# A repository whose PR job deliberately excludes a test group can point
|
|
284
|
+
# the lane at the complete check instead of the fast one.
|
|
285
|
+
verdict = _required_checks_verdict(rows, required)
|
|
286
|
+
if verdict:
|
|
287
|
+
return verdict
|
|
288
|
+
|
|
247
289
|
if buckets <= _BUCKETS_OK:
|
|
248
290
|
return "success"
|
|
249
291
|
return _CI_FAILING
|
|
@@ -258,6 +300,7 @@ def pr_ci_conclusion(
|
|
|
258
300
|
clock: Callable = time.monotonic,
|
|
259
301
|
deadline_seconds: float = CI_WAIT_SECONDS,
|
|
260
302
|
poll_seconds: float = CI_POLL_SECONDS,
|
|
303
|
+
required_checks=(),
|
|
261
304
|
) -> str:
|
|
262
305
|
"""Wait for `pr_number`'s CI to reach a conclusion, then return it.
|
|
263
306
|
|
|
@@ -284,7 +327,9 @@ def pr_ci_conclusion(
|
|
|
284
327
|
"""
|
|
285
328
|
started = clock()
|
|
286
329
|
while True:
|
|
287
|
-
conclusion = _read_ci_conclusion_once(
|
|
330
|
+
conclusion = _read_ci_conclusion_once(
|
|
331
|
+
runner, repo, pr_number, required_checks
|
|
332
|
+
)
|
|
288
333
|
if conclusion != _CI_PENDING:
|
|
289
334
|
return conclusion
|
|
290
335
|
if clock() - started >= deadline_seconds:
|
|
@@ -720,6 +765,11 @@ def run_bug_lane(
|
|
|
720
765
|
ci_conclusion = pr_ci_conclusion(
|
|
721
766
|
runner, repo, pr_number,
|
|
722
767
|
sleep=ci_sleep, clock=ci_clock, deadline_seconds=deadline_seconds,
|
|
768
|
+
# #3373: the checks this repository says must be green before the lane
|
|
769
|
+
# may merge unattended. Empty unless declared, so behaviour is
|
|
770
|
+
# unchanged for anyone who has not pointed the lane at a complete
|
|
771
|
+
# check.
|
|
772
|
+
required_checks=resolve_required_checks(policy_path),
|
|
723
773
|
)
|
|
724
774
|
state_reader = GitHubMergeCapState(runner=runner, repo=repo, now=now)
|
|
725
775
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.22.2
|
|
@@ -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.2"
|
|
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
|
|
@@ -367,6 +367,7 @@ tests/test_heartbeat.py
|
|
|
367
367
|
tests/test_heartbeat_adapter.py
|
|
368
368
|
tests/test_human_output_rule.py
|
|
369
369
|
tests/test_human_work_unit.py
|
|
370
|
+
tests/test_hygiene_boundary_guidance.py
|
|
370
371
|
tests/test_hyphenated_gate_name.py
|
|
371
372
|
tests/test_init_integration.py
|
|
372
373
|
tests/test_installed_copy_driver_e2e.py
|
|
@@ -469,6 +470,7 @@ tests/test_notify_sla.py
|
|
|
469
470
|
tests/test_operator_escalation_rule.py
|
|
470
471
|
tests/test_oracle_set_declared.py
|
|
471
472
|
tests/test_oracle_unrunnable_attribution.py
|
|
473
|
+
tests/test_per_run_item_cap_disclosure.py
|
|
472
474
|
tests/test_pick_feature_skill.py
|
|
473
475
|
tests/test_pin_honesty_and_integrity.py
|
|
474
476
|
tests/test_pin_marker_not_inherited.py
|
|
@@ -476,6 +478,7 @@ tests/test_pin_marker_spawn_sites.py
|
|
|
476
478
|
tests/test_plan_baseline.py
|
|
477
479
|
tests/test_planned_cost_lint.py
|
|
478
480
|
tests/test_planning_cost_floor.py
|
|
481
|
+
tests/test_plugin_prose_cross_refs.py
|
|
479
482
|
tests/test_plugin_qualified_dispatch.py
|
|
480
483
|
tests/test_policy_budgets_applied.py
|
|
481
484
|
tests/test_policy_proposals.py
|
|
@@ -516,6 +519,7 @@ tests/test_replan_note_collision.py
|
|
|
516
519
|
tests/test_replan_trigger.py
|
|
517
520
|
tests/test_replan_turn_contract.py
|
|
518
521
|
tests/test_replay_spin.py
|
|
522
|
+
tests/test_required_checks.py
|
|
519
523
|
tests/test_resolve_base.py
|
|
520
524
|
tests/test_restart_cap_counts_progress.py
|
|
521
525
|
tests/test_result_block.py
|
|
@@ -553,6 +557,7 @@ tests/test_severity_backfill_end_to_end.py
|
|
|
553
557
|
tests/test_severity_backfill_limit.py
|
|
554
558
|
tests/test_severity_backfill_marker.py
|
|
555
559
|
tests/test_severity_backfill_run.py
|
|
560
|
+
tests/test_severity_label_projection.py
|
|
556
561
|
tests/test_severity_rubric.py
|
|
557
562
|
tests/test_shipped_templates_parse.py
|
|
558
563
|
tests/test_skill_discovery_links.py
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Copyright 2026 Specfuse Contributors
|
|
2
|
+
# Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
3
|
+
"""The authoring rule teaches the reword the lint asks for (#3357).
|
|
4
|
+
|
|
5
|
+
`lint_plan` WARNs when a work unit declares a `produces:` path that also matches
|
|
6
|
+
one of its own Do-not-touch patterns, and since #3357 that warning names the
|
|
7
|
+
fix: reword the boundary to name the *surfaces* it protects rather than the file
|
|
8
|
+
this unit also produces.
|
|
9
|
+
|
|
10
|
+
The shape that trips it is not an edge case. A hygiene unit exists to make a
|
|
11
|
+
bounded fix inside a file a previous unit built, so it has to declare that file
|
|
12
|
+
in `produces:` *and* protect almost all of it — and §7 of the authoring skill,
|
|
13
|
+
which is where an author is told how to write a hygiene unit, said to name the
|
|
14
|
+
broken file in `produces:` without saying anything about how to word the
|
|
15
|
+
boundary. The author met the warning with no guidance in the rule that
|
|
16
|
+
generated the shape.
|
|
17
|
+
|
|
18
|
+
Worse than unhelpful: FEAT-2026-0113's `T08H` wrote "every other function in
|
|
19
|
+
`specfuse/loop/triage.py`", which read literally forbids editing the one
|
|
20
|
+
function that unit existed to fix. It passed only because the acceptance
|
|
21
|
+
criteria named the target unambiguously, so the boundary and the criteria
|
|
22
|
+
contradicted each other and only the criteria were load-bearing.
|
|
23
|
+
|
|
24
|
+
This guards the pairing rather than the prose: whatever wording either side
|
|
25
|
+
uses, the authoring rule must teach surface-naming, so the lint's recommended
|
|
26
|
+
fix and the rule that produces the shape cannot drift apart again.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import re
|
|
32
|
+
import unittest
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
|
|
35
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
36
|
+
SKILL = REPO_ROOT / "plugins/specfuse/skills/authoring-work-units/SKILL.md"
|
|
37
|
+
MIRROR = REPO_ROOT / ".specfuse/skills/authoring-work-units/SKILL.md"
|
|
38
|
+
LINT = REPO_ROOT / "specfuse/loop/lint_plan.py"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _section_7(text: str) -> str:
|
|
42
|
+
"""§7, the hygiene-work-unit section, up to the next top-level heading."""
|
|
43
|
+
match = re.search(r"^## 7\. .*?$(.*?)(?=^## \d+\. )", text, re.S | re.M)
|
|
44
|
+
assert match, "authoring skill has no '## 7.' section"
|
|
45
|
+
return match.group(1)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class HygieneBoundaryGuidance(unittest.TestCase):
|
|
49
|
+
|
|
50
|
+
def test_the_lint_still_recommends_naming_surfaces(self):
|
|
51
|
+
# If this fails the lint changed its advice, and the rule below is
|
|
52
|
+
# now teaching the wrong reword rather than merely a stale one.
|
|
53
|
+
self.assertIn("surfaces it protects", LINT.read_text(encoding="utf-8"))
|
|
54
|
+
|
|
55
|
+
def test_section_7_tells_the_author_how_to_word_the_boundary(self):
|
|
56
|
+
body = _section_7(SKILL.read_text(encoding="utf-8"))
|
|
57
|
+
|
|
58
|
+
self.assertIn(
|
|
59
|
+
"surface", body.lower(),
|
|
60
|
+
"§7 tells an author to put the broken file in `produces:`, which is "
|
|
61
|
+
"exactly the shape that trips the Do-not-touch collision warning. "
|
|
62
|
+
"It must also tell them to word the boundary by naming the "
|
|
63
|
+
"surfaces it protects, or the rule generates a warning it gives no "
|
|
64
|
+
"way to answer (#3357).",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def test_section_7_warns_against_the_whole_file_wording(self):
|
|
68
|
+
body = _section_7(SKILL.read_text(encoding="utf-8")).lower()
|
|
69
|
+
|
|
70
|
+
self.assertTrue(
|
|
71
|
+
"every other function in" in body or "whole file" in body,
|
|
72
|
+
"§7 must show the wording that actually went wrong — a boundary "
|
|
73
|
+
"phrased over a whole file contradicts the criteria of a unit that "
|
|
74
|
+
"exists to edit one function in it (FEAT-2026-0113/T08H).",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def test_the_mirrored_copy_agrees(self):
|
|
78
|
+
# plugins/ is canonical; .specfuse/ is the synced scaffold copy.
|
|
79
|
+
self.assertEqual(
|
|
80
|
+
SKILL.read_text(encoding="utf-8"),
|
|
81
|
+
MIRROR.read_text(encoding="utf-8"),
|
|
82
|
+
"plugins/ and .specfuse/ copies of the authoring skill diverged — "
|
|
83
|
+
"run scripts/sync-scaffold.sh",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__": # pragma: no cover
|
|
88
|
+
unittest.main()
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Copyright 2026 Specfuse Contributors
|
|
2
|
+
# Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
3
|
+
"""`max_items_per_day` is enforced per run, and now says so (#3340).
|
|
4
|
+
|
|
5
|
+
Most of #3340 landed: budgets resolve from the policy when no flag is given,
|
|
6
|
+
`max_open_prs` gates bug-lane dispatch, and the run opens with a line naming
|
|
7
|
+
each effective cap and its source.
|
|
8
|
+
|
|
9
|
+
One expectation was left. The policy key is named `max_items_per_day` and is
|
|
10
|
+
applied *per run* -- enforcing it across runs within a day needs state that
|
|
11
|
+
survives a process, which is a feature, not this fix. `resolve_max_items`
|
|
12
|
+
records that honestly in its docstring, but a docstring is not a surface an
|
|
13
|
+
operator reads. The issue offered the alternative explicitly: enforce it across
|
|
14
|
+
runs, **or** document it as per-run.
|
|
15
|
+
|
|
16
|
+
So the operator-facing surfaces carry it. A run that prints `max_items=62
|
|
17
|
+
(policy)` against a policy whose key says `per_day` invites exactly the wrong
|
|
18
|
+
reading -- and the wrong reading is the unsafe one, since a day of runs can
|
|
19
|
+
exceed the number the operator believes they set.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import unittest
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
from specfuse.loop import agent_policy
|
|
28
|
+
|
|
29
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
30
|
+
SKILL = REPO_ROOT / "plugins/specfuse/skills/derive-agent-policy/SKILL.md"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PerRunItemCapDisclosure(unittest.TestCase):
|
|
34
|
+
|
|
35
|
+
def test_the_run_line_says_the_item_cap_is_per_run(self):
|
|
36
|
+
line = agent_policy.describe_budget_sources(
|
|
37
|
+
max_items=(62, "policy"),
|
|
38
|
+
max_tokens=(2800000, "policy"),
|
|
39
|
+
max_minutes=(None, "none"),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
self.assertIn("per run", line)
|
|
43
|
+
|
|
44
|
+
def test_a_flag_sourced_item_cap_says_it_too(self):
|
|
45
|
+
# The cap is per-run whoever set it; the policy key's name is what
|
|
46
|
+
# misleads, but a flag run is bounded the same way.
|
|
47
|
+
line = agent_policy.describe_budget_sources(max_items=(5, "flag"))
|
|
48
|
+
|
|
49
|
+
self.assertIn("per run", line)
|
|
50
|
+
|
|
51
|
+
def test_other_caps_are_not_annotated(self):
|
|
52
|
+
# Only the item cap has a name that promises something wider than it
|
|
53
|
+
# delivers. Annotating the rest would be noise.
|
|
54
|
+
line = agent_policy.describe_budget_sources(
|
|
55
|
+
max_tokens=(2800000, "policy"),
|
|
56
|
+
max_open_prs=(3, "policy"),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
self.assertNotIn("per run", line)
|
|
60
|
+
|
|
61
|
+
def test_an_absent_item_cap_is_not_annotated(self):
|
|
62
|
+
line = agent_policy.describe_budget_sources(max_items=(None, "none"))
|
|
63
|
+
|
|
64
|
+
self.assertIn("unbounded", line)
|
|
65
|
+
self.assertNotIn("per run", line)
|
|
66
|
+
|
|
67
|
+
def test_the_drafting_skill_discloses_the_semantics(self):
|
|
68
|
+
body = SKILL.read_text(encoding="utf-8")
|
|
69
|
+
|
|
70
|
+
self.assertIn(
|
|
71
|
+
"per run", body,
|
|
72
|
+
"/derive-agent-policy drafts `max_items_per_day` into an "
|
|
73
|
+
"operator's policy file. It must say that the key is applied per "
|
|
74
|
+
"run, or the operator writes a number believing it bounds a day "
|
|
75
|
+
"(#3340).",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__": # pragma: no cover
|
|
80
|
+
unittest.main()
|