specfuse-loop 0.22.1__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.1/specfuse_loop.egg-info → specfuse_loop-0.22.2}/PKG-INFO +1 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/pyproject.toml +1 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/severity_backfill.py +38 -5
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/agent_policy.py +12 -1
- specfuse_loop-0.22.2/specfuse/loop/data/VERSION +1 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/labels.py +69 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/loop.py +1 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/SOURCES.txt +4 -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.1 → 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.1/specfuse/loop/data/VERSION +0 -1
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/LICENSE +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/NOTICE +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/README.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/setup.cfg +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/__init__.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/budget.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/diagnose_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/drafting_answers.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/drafting_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/drafting_questions.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/driver_command.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/driver_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/monitoring_read.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/__init__.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/answers.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/bugs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/feature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/findings_autofix.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/findings_diagnose.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/triage.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/queue_read.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/repo_detect.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/triage_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/worktree.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/_filelock.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/_wu_sections.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/arm_eval.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/arm_sweep.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/arm_txn.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/bug_lane.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/bug_lane_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/bug_lane_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/build_provenance.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/caller_check.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/changelog.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/closing_requirements.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/cost.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/criteria_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/glossary.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/methodology.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/gitignore.snippet +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/monitoring.yml.example +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/close-discipline.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/human-output.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/operator-escalation.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/planning-discipline.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/verification-discipline.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules-local/README.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/event.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/decisions_format.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/driver_edit.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/escalation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/events_stats.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/gate_commands.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/heartbeat.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/judge.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/learnings_distil.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/learnings_query.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/lint_closing.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/lint_monitoring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/lint_plan.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/lint_roadmap.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/notify.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/notify_escalation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/notify_sla.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/plan_baseline.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/policy_proposals.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/policy_review.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/prerun.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/prerun_capture.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/promotion.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/rearm_migration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/replay_spin.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/roadmap_archive.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/scaffold.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/triage.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/__init__.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/adapters.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/artifact.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/autofix.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/autofix_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/autofix_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/autofix_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/cli.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/diagnose_cli.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/diagnosis.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/fingerprint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/issues.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/providers/__init__.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/providers/_azure_auth.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/providers/azure_app_insights.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/providers/azure_service_bus.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/redaction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/schedule.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_abandon_feature_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_abandoned_work_reported.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_abandoned_wu_strands_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_budget.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_bug_selection_dedup.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_default_features_root.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_driver_command.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_driver_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_driver_restart.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_escalation_on_issue.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_escalation_run_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_findings_seam.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_invoke_timeout.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_invoke_usage.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_item_worktree.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_lock.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_policy_key_ownership.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_policy_queue.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_policy_schema.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_policy_triage_dial.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_answers.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_bugs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_feature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_findings_autofix.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_findings_diagnose.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_isolation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_triage.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_queue_read.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_relays_driver_output.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_repo_detect.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_run_progress.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_seam.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_snapshot_refresh.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_answer_escalation_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_app_insights_adapters.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_archive_ref_reconciliation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_eval.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_eval_lint_class.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_eval_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_sweep.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_sweep_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_txn.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_artifact_redaction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attempt_note_untracked_files.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attempt_outcome_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attention_nonwriting_guard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attention_silence_section.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attention_skill_structure.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autoclose_deferral_visibility.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autoclose_gate_note.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autoclose_stub_states_what_passed.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autofix_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autofix_live.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autofix_predicate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autofix_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autofix_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autosync_no_cwd_leak.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_azure_credential_resolution.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_backend.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_baseline_escalation_message.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_baseline_persistence.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_baseline_probe.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_baseline_provenance.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_baseline_tree_hash_key.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bash_routing.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bats_suites_gated.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_binary_verdict.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_binding_block_advisory.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_binding_block_allocation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_binding_block_budget.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bookkeeping_no_verify.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_brief_covers_every_unit_escalation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_ci_pending.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_guardrails.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_label_nonfatal.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_labels_registered.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_min_severity.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_policy_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_pr_number_carried.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_preflight_and_dial.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_test_paths.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_build_provenance.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_caller_check_ratchet.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_changed_file_test_selection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_changelog_append_only.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_changelog_collection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_changelog_release_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_changelog_schema.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_ci_conclusion_wait.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_ci_runs_every_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_claude_resolution.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_close_b_pending_learnings.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_close_records_feature_oracle.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_close_wu_self_reference.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_closing_guard_contracts.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_closing_guard_prediction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_closing_learnings_destination.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_closing_requirements.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_coauthor_trailer_allowlist.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_conditional_reflection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_console_encoding.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_convergent_iteration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_convergent_wu_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_correlation_id_override.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_correlation_id_two_surfaces.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_cost_lifetime.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_criteria_per_gate_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_criteria_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_criteria_worklist.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_decision_citation_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_decision_followups_discharged.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_decision_override_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_decisions_format.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_default_severity_aliases.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_degraded_oracle.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_deliverable_missing_note.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_derive_agent_policy_review_mode.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_derive_agent_policy_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_derive_monitoring_discovery.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_derive_monitoring_skill_registration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_design_for_diagnosis_rule.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_deterministic_refusal_repeat.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_diagnose_headless.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_diagnose_issue_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_diagnosis.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_diagnosis_roundtrip_live.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_dispatch_skeleton.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_distilled_accept_step.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_doctor_command_preflight.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_done_feature_gates.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_draft_feature_answers_mode.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_drafting_answer_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_drafting_invoke.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_drafting_question_issue.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_drafting_questions.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_drafting_reply_shape.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_edit_detection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_event_registry_covers_emitters.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_module_surface.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_restart_halt_surfaced.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_restart_halt_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_restart_hold.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_staleness_gate_summary.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_staleness_warning.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_dry_run_no_branch_mutation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_ensure_feature_branch_base.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_escalation_assignee_policy.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_escalation_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_escalation_crash_containment.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_escalation_dedup_search.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_escalation_emit.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_event_gate_full_envelope.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_events_redaction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_events_stats.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_failure_artifact_model.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_failure_signature_fail_prefix.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_failure_signature_skips_log_noise.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_branch_auto_sync.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_branch_squash_merge.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_branch_stale_base.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_id_resolution.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_oracle_e2e.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_provider_drafting_dispatch.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_review_accumulation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_filelock_portable.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_files_changed_auto_repair.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_find_pr_no_search_index.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_fingerprint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_fix_bug_diff_self_check.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_fix_bug_headless.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_fix_bug_reads_comments.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_fold_contract_documented.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_followups_artifact.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_frontmatter_scalar_quoting.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_broad_run.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_budget.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_budget_post_dispatch.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_commands_needs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_eval_intermediate_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_eval_repo_flag.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_eval_terminal_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_report_verdict.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gitleaks_pinning.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_groom_backlog_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_guard_refusal_failure_class.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_guard_refusal_failure_excerpt.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_guard_repair_e2e.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_guard_repair_prompt.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_heartbeat.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_heartbeat_adapter.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_human_output_rule.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_human_work_unit.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_hyphenated_gate_name.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_init_integration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_installed_copy_driver_e2e.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_interpreter_normalization.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_close_path.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_cost.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_cost_fold_committed.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_module.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_path_registry.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_terminal_flip_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_label_provisioning_runner_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_label_registry.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_label_registry_covers_consumers.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_language_aware_narrow_selection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lazy_baseline_e2e.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_curate_vendored_promote.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_distil_cli.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_query.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_staging.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_suggest_signature_filter.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_weights.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lifetime_accounting.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_ac_observable.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_boundary_consistency.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_boundary_extraction.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_close_verdict_not_predecided.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_closing.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_closing_criteria.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_closing_criteria_pristine.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_covers_load_wu_invariants.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_feature_oracle_declared.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_gate_proportionality.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_monitoring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_plan_contract_fields.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_plan_relative_import.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_plan_verdict_exempt.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_planning_sections.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_produces_satisfiability.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_roadmap.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_verification_reference.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_live_tests_opt_in.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_load_bearing_close_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_attempt_evidence.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_criteria_skeleton.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_criteria_survival.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_gate_needs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_worklist_injection.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_make_repo_gpgsign_isolation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_marker_dual_shape.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_maven_failure_excerpt.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_max_attempts_configurable.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_max_open_prs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitor_cli.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitor_issue_lifecycle.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitor_runner_surfaces.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitoring_cron_dialect.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitoring_example.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitoring_fenced_blocks.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitoring_seed.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_narrow_fallback_warning.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_no_pytest_subprocess.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_notify.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_notify_escalation.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_notify_sla.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_operator_escalation_rule.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_oracle_set_declared.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_oracle_unrunnable_attribution.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pick_feature_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pin_honesty_and_integrity.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pin_marker_not_inherited.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pin_marker_spawn_sites.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_plan_baseline.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_planning_cost_floor.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_plugin_qualified_dispatch.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_policy_budgets_applied.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_policy_proposals.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_policy_review.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pr_checks_json_field.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pr_reference_in_title.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prep_halt_operator_surface.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prepare_feature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prepare_scaffold_sync.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prerun_capture.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prerun_oracles.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prerun_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produced_document_verdict_guard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_bracket_literal.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_bracket_wildcard.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_in_diff.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_justification.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_recursive_glob.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_semantics_unified.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_shape.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_progress_forward_note.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_progress_lines_end_to_end.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_promotion_ledger.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_provider_escalation_traces.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_provision_labels.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_queue_stalled_adapter.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_rearm_fold_marker.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_rearm_migration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_red_baseline_never_reused.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_redaction_windows_home.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_reflection_required_signals.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replan_end_to_end.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replan_event_emission.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replan_note_collision.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replan_trigger.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replan_turn_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replay_spin.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_required_checks.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_resolve_base.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_restart_cap_counts_progress.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_result_block_audience.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_resume_command_build.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_retain_on_guard_refusal_docs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_cli.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_shim.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_skill_delegates.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_link_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_ruff_verdict_lines.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_rule_import_convergence.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_run_level_failure_breaker.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_data_in_sync.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_doc_hygiene.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_docs.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_init.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_label_provisioning.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_resources.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_skew_halts.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_wiring.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_schedule_dialect.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_seed_documents_prerun_keys.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_service_bus_dlq_adapter.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_session_env_preflight.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_aliases.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_backfill_apply.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_backfill_end_to_end.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_backfill_limit.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_backfill_marker.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_rubric.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_shipped_templates_parse.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_skill_discovery_links.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_skills_vendored_in_sync.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_spinning_rearm_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_spinout_brief_end_to_end.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_spinout_brief_replan_option.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_stop_rationale_captured.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_stranded_halt_cause.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_surefire_run_level_signature.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_surefire_verdict_lines.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_terminal_gate_message.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_tiered_verification_e2e.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_timeout_kill_portable.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_apply.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_severity_classify.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_severity_end_to_end.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_severity_write.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_skill_contract.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_skips_agent_escalations.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_tsc_verdict_lines.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_umbrella_version_resolution.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_unterminated_frontmatter.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_untracked_file_absorption.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_validate_event_driver_types.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_windows_gate_exec.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_worktree_restore.py +0 -0
- {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_wu_execution_metadata.py +0 -0
- {specfuse_loop-0.22.1 → 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"
|
|
@@ -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"])
|
|
@@ -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.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
|
|
@@ -554,6 +557,7 @@ tests/test_severity_backfill_end_to_end.py
|
|
|
554
557
|
tests/test_severity_backfill_limit.py
|
|
555
558
|
tests/test_severity_backfill_marker.py
|
|
556
559
|
tests/test_severity_backfill_run.py
|
|
560
|
+
tests/test_severity_label_projection.py
|
|
557
561
|
tests/test_severity_rubric.py
|
|
558
562
|
tests/test_shipped_templates_parse.py
|
|
559
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()
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2026 Specfuse Contributors
|
|
3
|
+
# Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
4
|
+
#
|
|
5
|
+
"""Published prose cross-references this project only (#3378).
|
|
6
|
+
|
|
7
|
+
Two surfaces are published content: everything under ``plugins/``, which ships
|
|
8
|
+
in a ``specfuse-loop`` release, is mirrored into the umbrella marketplace repo
|
|
9
|
+
and lands in every consumer's checkout when they install the plugin; and
|
|
10
|
+
``CHANGELOG.md``, which ships in the pip package. Both are a far wider surface
|
|
11
|
+
than the commit bodies, PR bodies, roadmap prose and ``events.jsonl`` notes the
|
|
12
|
+
leak-scan discipline already guards.
|
|
13
|
+
|
|
14
|
+
The failure this guards against is #3378: ``fix-bug/SKILL.md`` cited a real
|
|
15
|
+
downstream consumer's ``org/repo#issue`` while explaining the ``Closes #<n>``
|
|
16
|
+
rule, and the 0.22.1 changelog entry describing that same fix cited it again.
|
|
17
|
+
Both shipped before anyone noticed.
|
|
18
|
+
|
|
19
|
+
`leak_scan` could not catch it and still cannot. A bare ``org/repo#n`` token is
|
|
20
|
+
indistinguishable from the many legitimate ``specfuse/loop#NNNN``
|
|
21
|
+
cross-references the same prose uses, and the denylist is the wrong instrument
|
|
22
|
+
here for a sharper reason: the consumer's name legitimately appears in
|
|
23
|
+
``.specfuse/agent-policy.yml`` (it is the repo the bug lane is pointed at) and
|
|
24
|
+
in this repo's own tests. A denylist entry cannot tell published prose from
|
|
25
|
+
local configuration, so adding one would red the whole repository rather than
|
|
26
|
+
the one file that was actually wrong.
|
|
27
|
+
|
|
28
|
+
So the invariant is scoped to the published surfaces instead, and stated
|
|
29
|
+
positively: prose under ``plugins/`` and in ``CHANGELOG.md`` may cross-reference
|
|
30
|
+
this project's own repositories and nothing else. Any other ``org/repo#n`` is someone else's
|
|
31
|
+
identity riding out in our release.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import re
|
|
37
|
+
import unittest
|
|
38
|
+
from pathlib import Path
|
|
39
|
+
|
|
40
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
41
|
+
PLUGINS_DIR = REPO_ROOT / "plugins"
|
|
42
|
+
CHANGELOG = REPO_ROOT / "CHANGELOG.md"
|
|
43
|
+
|
|
44
|
+
# `owner/repo#123`. Deliberately broad on the owner/repo halves: the point is to
|
|
45
|
+
# catch an owner we have never seen, so the pattern cannot enumerate them.
|
|
46
|
+
CROSS_REF_RE = re.compile(r"\b([A-Za-z0-9][A-Za-z0-9._-]*)/([A-Za-z0-9._-]+)#\d+")
|
|
47
|
+
|
|
48
|
+
# The only owner published prose may name. This project's own repositories are
|
|
49
|
+
# the ones a reader of a shipped skill can be expected to follow.
|
|
50
|
+
ALLOWED_OWNERS = frozenset({"specfuse"})
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class PluginProseCrossRefs(unittest.TestCase):
|
|
54
|
+
|
|
55
|
+
def test_published_prose_references_only_this_project(self):
|
|
56
|
+
offenders = []
|
|
57
|
+
published = sorted(PLUGINS_DIR.rglob("*.md")) + [CHANGELOG]
|
|
58
|
+
for path in published:
|
|
59
|
+
for lineno, line in enumerate(
|
|
60
|
+
path.read_text(encoding="utf-8").splitlines(), start=1
|
|
61
|
+
):
|
|
62
|
+
for owner, repo in CROSS_REF_RE.findall(line):
|
|
63
|
+
if owner not in ALLOWED_OWNERS:
|
|
64
|
+
rel = path.relative_to(REPO_ROOT)
|
|
65
|
+
offenders.append(f"{rel}:{lineno}: {owner}/{repo}#…")
|
|
66
|
+
|
|
67
|
+
self.assertEqual(
|
|
68
|
+
offenders,
|
|
69
|
+
[],
|
|
70
|
+
"published plugin prose names a repository outside "
|
|
71
|
+
f"{sorted(ALLOWED_OWNERS)}. This content ships to every consumer, "
|
|
72
|
+
"so another party's org/repo must not ride along in it — describe "
|
|
73
|
+
"the repo instead ('a downstream consumer repo') and keep the "
|
|
74
|
+
"traceable reference on this project's own issue. Offenders:\n "
|
|
75
|
+
+ "\n ".join(offenders),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__": # pragma: no cover
|
|
80
|
+
unittest.main()
|
|
@@ -222,5 +222,65 @@ class BackfillRun(unittest.TestCase):
|
|
|
222
222
|
self.assertEqual([], label_create_calls)
|
|
223
223
|
|
|
224
224
|
|
|
225
|
+
class AliasedSchemeWritesTheRepositorysOwnLabel(unittest.TestCase):
|
|
226
|
+
"""End to end for #3353: a repository whose severity labels are its own
|
|
227
|
+
words gets those words written back, not the vocabulary spelling.
|
|
228
|
+
|
|
229
|
+
The unit tests for `severity_label_projection` prove the mapping; this
|
|
230
|
+
proves the wiring, which is where the defect actually lived -- the
|
|
231
|
+
projection existed nowhere and `apply_severity_backfill` wrote
|
|
232
|
+
`severity_label_for(value)` unconditionally.
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
ALIASED_LABELS = [
|
|
236
|
+
{"name": "severity:major", "description": "Wrong behavior."},
|
|
237
|
+
{"name": "severity:minor", "description": "Cosmetic."},
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
def test_the_label_written_is_the_repositorys_word(self):
|
|
241
|
+
candidates = [_candidate(1)]
|
|
242
|
+
runner, calls = _make_runner(
|
|
243
|
+
candidates=candidates,
|
|
244
|
+
label_rows=self.ALIASED_LABELS,
|
|
245
|
+
claude_answers={1: _marker(1, "bug", "high", "high")},
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
report = run_backfill(runner, _REPO, apply=True)
|
|
249
|
+
|
|
250
|
+
self.assertEqual({"high", "low"}, set(report["rubric"]))
|
|
251
|
+
|
|
252
|
+
label_calls = [c for c in _issue_edit_calls(calls) if "--add-label" in c]
|
|
253
|
+
self.assertEqual(1, len(label_calls))
|
|
254
|
+
written = label_calls[0][label_calls[0].index("--add-label") + 1]
|
|
255
|
+
self.assertEqual("severity:major", written)
|
|
256
|
+
|
|
257
|
+
def test_the_marker_still_records_the_vocabulary_value(self):
|
|
258
|
+
candidates = [_candidate(1)]
|
|
259
|
+
runner, calls = _make_runner(
|
|
260
|
+
candidates=candidates,
|
|
261
|
+
label_rows=self.ALIASED_LABELS,
|
|
262
|
+
claude_answers={1: _marker(1, "bug", "high", "high")},
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
run_backfill(runner, _REPO, apply=True)
|
|
266
|
+
|
|
267
|
+
body_calls = [c for c in _issue_edit_calls(calls) if "--body" in c]
|
|
268
|
+
self.assertEqual(1, len(body_calls))
|
|
269
|
+
body = body_calls[0][body_calls[0].index("--body") + 1]
|
|
270
|
+
self.assertIn("severity=high", body)
|
|
271
|
+
|
|
272
|
+
def test_the_listing_is_still_read_exactly_once(self):
|
|
273
|
+
candidates = [_candidate(1)]
|
|
274
|
+
runner, calls = _make_runner(
|
|
275
|
+
candidates=candidates,
|
|
276
|
+
label_rows=self.ALIASED_LABELS,
|
|
277
|
+
claude_answers={1: _marker(1, "bug", "high", "high")},
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
run_backfill(runner, _REPO, apply=True)
|
|
281
|
+
|
|
282
|
+
self.assertEqual(1, len(_label_list_calls(calls)))
|
|
283
|
+
|
|
284
|
+
|
|
225
285
|
if __name__ == "__main__":
|
|
226
286
|
unittest.main()
|