specfuse-loop 0.30.0__tar.gz → 0.31.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {specfuse_loop-0.30.0/specfuse_loop.egg-info → specfuse_loop-0.31.0}/PKG-INFO +4 -4
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/README.md +3 -3
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/pyproject.toml +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/__init__.py +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/agent_policy.py +12 -8
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/arm_eval.py +112 -2
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/changelog.py +5 -1
- specfuse_loop-0.31.0/specfuse/loop/data/VERSION +1 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +13 -6
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/getting-started.md +8 -2
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/glossary.md +3 -2
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/methodology.md +34 -7
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/roadmap.template.md +4 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules/correlation-ids.md +14 -2
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/schemas/driver-event.schema.json +4 -2
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/schemas/event.schema.json +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/schemas/events/spec_validated.schema.json +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/gate_eval.py +11 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/gh_features.py +13 -3
- specfuse_loop-0.31.0/specfuse/loop/ids.py +879 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/layout.py +29 -4
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/lint_plan.py +44 -3
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/lint_roadmap.py +33 -4
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/loop.py +196 -7
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/promotion.py +2 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/upgrade_merge_gate.py +3 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0/specfuse_loop.egg-info}/PKG-INFO +4 -4
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse_loop.egg-info/SOURCES.txt +14 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_policy_queue.py +4 -2
- specfuse_loop-0.31.0/tests/test_arm_eval_terminal.py +191 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_arm_wiring.py +2 -2
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_policy_contract.py +4 -2
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_caller_check_ratchet.py +0 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_decision_followups_discharged.py +2 -2
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_decision_override_lint.py +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_done_feature_gates.py +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_escalation_crash_containment.py +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_feature_id_resolution.py +24 -18
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_eval_calibration.py +7 -6
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gh_features.py +6 -1
- specfuse_loop-0.31.0/tests/test_ids.py +128 -0
- specfuse_loop-0.31.0/tests/test_ids_aliases.py +127 -0
- specfuse_loop-0.31.0/tests/test_ids_call_sites.py +151 -0
- specfuse_loop-0.31.0/tests/test_ids_migrate.py +139 -0
- specfuse_loop-0.31.0/tests/test_ids_migrate_all.py +244 -0
- specfuse_loop-0.31.0/tests/test_judge_editing_terminal_docs.py +67 -0
- specfuse_loop-0.31.0/tests/test_keyed_feature_ids_docs.py +44 -0
- specfuse_loop-0.31.0/tests/test_keyed_feature_ids_end_to_end.py +297 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_layout_no_layout_goldens.py +2 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_close_wu.py +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_correlation_id.py +13 -11
- specfuse_loop-0.31.0/tests/test_lint_judge_edit_acknowledged.py +82 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_plan_errors.py +1 -1
- specfuse_loop-0.31.0/tests/test_lint_roadmap_unkeyed_rows.py +55 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_sections.py +1 -1
- specfuse_loop-0.31.0/tests/test_migrate_all_repo_stays_valid.py +337 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_miniyaml_equivalence.py +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_replan_turn_contract.py +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_roadmap_add_skill.py +28 -3
- specfuse_loop-0.31.0/tests/test_terminal_judge_editing_end_to_end.py +338 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_unterminated_frontmatter.py +1 -1
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_validate_event.py +17 -5
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_validate_event_driver_types.py +1 -1
- specfuse_loop-0.31.0/tests/test_validate_feature_dirs_only.py +37 -0
- specfuse_loop-0.30.0/specfuse/loop/data/VERSION +0 -1
- specfuse_loop-0.30.0/tests/test_judge_editing_terminal_docs.py +0 -58
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/LICENSE +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/NOTICE +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/setup.cfg +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/budget.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/diagnose_invoke.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/drafting_answers.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/drafting_invoke.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/drafting_questions.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/driver_command.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/driver_invoke.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/invoke.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/monitoring_read.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/providers/__init__.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/providers/answers.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/providers/bugs.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/providers/feature.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/providers/findings_autofix.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/providers/findings_diagnose.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/providers/triage.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/queue_read.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/repo_detect.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/run.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/severity_backfill.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/triage_invoke.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/agent/worktree.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/_filelock.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/_wu_sections.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/arm_sweep.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/arm_txn.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/bug_lane.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/bug_lane_run.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/bug_lane_state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/build_provenance.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/caller_check.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/closing_requirements.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/cost.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/criteria_state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/concepts/monitoring-runners.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/concepts/monitoring-schema.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/concepts/monitoring-to-fix.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/flows.html +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/lifecycles.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/gitignore.snippet +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/monitoring.yml.example +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules/close-discipline.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules/human-output.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules/operator-escalation.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules/planning-discipline.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules/verification-discipline.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/rules-local/README.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/decisions_format.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/driver_edit.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/escalation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/events_stats.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/gate_commands.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/gen_contract.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/heartbeat.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/judge.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/labels.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/learnings_distil.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/learnings_query.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/lint_closing.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/lint_monitoring.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/notify.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/notify_escalation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/notify_sla.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/plan_baseline.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/policy_proposals.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/policy_review.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/prerun.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/prerun_capture.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/rearm_migration.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/red_on_base.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/replay_spin.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/roadmap_archive.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/scaffold.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/triage.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/__init__.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/adapters.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/artifact.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/autofix.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/autofix_invoke.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/autofix_run.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/autofix_state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/cli.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/diagnose_cli.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/diagnosis.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/durations.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/fingerprint.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/issues.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/providers/__init__.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/providers/_azure_auth.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/providers/azure_app_insights.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/providers/azure_service_bus.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/redaction.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/monitor/schedule.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_abandon_feature_skill.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_abandoned_work_reported.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_abandoned_wu_strands_gate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_budget.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_bug_selection_dedup.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_default_features_root.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_driver_command.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_driver_invoke.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_driver_restart.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_escalation_on_issue.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_escalation_run_state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_findings_seam.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_invoke_timeout.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_invoke_usage.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_item_worktree.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_lock.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_policy_key_ownership.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_policy_schema.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_policy_triage_dial.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_provider_answers.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_provider_bugs.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_provider_feature.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_provider_findings_autofix.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_provider_findings_diagnose.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_provider_isolation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_provider_triage.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_queue_read.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_relays_driver_output.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_repo_detect.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_run.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_run_progress.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_seam.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_snapshot_refresh.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_agent_state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_answer_escalation_skill.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_app_insights_adapters.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_app_insights_harvest_window.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_app_insights_query_schema_check.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_app_insights_workspace_schema.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_archive_ref_reconciliation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_arm_eval.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_arm_eval_lint_class.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_arm_eval_wiring.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_arm_sweep.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_arm_sweep_gate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_arm_txn.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_artifact_redaction.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_attempt_note_untracked_files.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_attempt_outcome_contract.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_attention_nonwriting_guard.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_attention_silence_section.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_attention_skill_structure.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_attribution_halt_claims.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autoclose_deferral_visibility.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autoclose_gate_note.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autoclose_stub_states_what_passed.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autofix_invoke.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autofix_live.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autofix_predicate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autofix_run.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autofix_state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autosync_no_cwd_leak.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_azure_credential_resolution.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_backend.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_baseline_escalation_message.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_baseline_persistence.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_baseline_probe.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_baseline_provenance.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_baseline_tree_hash_key.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bash_routing.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bats_suites_gated.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_binary_verdict.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_binding_block_advisory.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_binding_block_allocation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_binding_block_budget.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_blind_run_is_not_drained.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_block_feature_layout.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bookkeeping_no_verify.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_brief_covers_every_unit_escalation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_ci_pending.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_guardrails.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_label_nonfatal.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_labels_registered.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_min_severity.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_no_checks_pending.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_pr_number_carried.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_preflight_and_dial.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_run.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_spend.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_superseded.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_bug_lane_test_paths.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_build_provenance.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_carried_green_invalidated_by_diff.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_carry_accounting_close_and_judge.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_changed_file_test_selection.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_changelog_append_only.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_changelog_collection.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_changelog_release_wiring.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_changelog_schema.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_ci_conclusion_wait.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_ci_runs_every_gate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_claude_resolution.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_close_b_pending_learnings.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_close_records_feature_oracle.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_close_wu_self_reference.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_closed_escalation_labels.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_closing_guard_contracts.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_closing_guard_prediction.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_closing_learnings_destination.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_closing_requirements.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_closing_sessions_on_sonnet.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_coauthor_trailer_allowlist.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_conditional_reflection.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_console_encoding.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_convergent_iteration.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_convergent_wu_lint.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_correlation_id_override.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_correlation_id_two_surfaces.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_cost_lifetime.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_criteria_per_gate_lint.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_criteria_state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_criteria_worklist.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_decision_citation_lint.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_decisions_format.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_default_severity_aliases.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_degraded_oracle.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_deliverable_missing_note.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_derive_agent_policy_review_mode.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_derive_agent_policy_skill.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_derive_monitoring_discovery.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_derive_monitoring_skill_registration.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_design_for_diagnosis_rule.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_deterministic_refusal_repeat.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_diagnose_fenced_analysis.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_diagnose_headless.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_diagnose_issue_skill.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_diagnosis.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_diagnosis_roundtrip_live.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_diagnosis_severity_end_to_end.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_diagnosis_severity_rubric.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_dispatch_model_from_fix_scope.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_dispatch_preamble_rules_pointer.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_dispatch_skeleton.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_dispatch_skills_index.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_distilled_accept_step.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_doctor_command_preflight.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_draft_feature_answers_mode.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_drafting_answer_gate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_drafting_invoke.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_drafting_question_issue.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_drafting_questions.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_drafting_reply_shape.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_driver_edit_detection.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_driver_event_registry_covers_emitters.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_driver_module_surface.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_driver_restart_halt_surfaced.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_driver_restart_halt_wiring.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_driver_restart_hold.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_driver_staleness_gate_summary.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_driver_staleness_warning.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_dry_run_no_branch_mutation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_ensure_feature_branch_base.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_escalation_assignee_policy.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_escalation_contract.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_escalation_dedup_search.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_escalation_emit.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_event_gate_full_envelope.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_events_redaction.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_events_stats.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_failure_artifact_model.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_failure_signature_fail_prefix.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_failure_signature_names_the_test.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_failure_signature_skips_log_noise.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_feature_branch_auto_sync.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_feature_branch_squash_merge.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_feature_branch_stale_base.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_feature_oracle_e2e.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_feature_provider_drafting_dispatch.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_feature_review_accumulation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_filelock_portable.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_files_changed_auto_repair.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_find_pr_no_search_index.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_fingerprint.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_fix_bug_cwd_worktree.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_fix_bug_diff_self_check.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_fix_bug_headless.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_fix_bug_reads_comments.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_fix_unit_insertion_bookkeeping.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_fix_unit_insertion_e2e.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_fix_unit_insertion_refused.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_floor_skip_summary.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_flows_page.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_fold_contract_documented.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_followups_artifact.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_frontmatter_scalar_quoting.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_broad_run.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_budget.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_budget_post_dispatch.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_commands_needs.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_eval_intermediate_wiring.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_eval_repo_flag.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_eval_terminal_wiring.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_log_size_budget.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gate_report_verdict.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gen_contract.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_gitleaks_pinning.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_groom_backlog_skill.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_guard_refusal_failure_class.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_guard_refusal_failure_excerpt.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_guard_repair_e2e.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_guard_repair_prompt.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_headless_no_background_tasks.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_heartbeat.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_heartbeat_adapter.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_heartbeat_emission_contract.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_http_status_regression_adapter.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_http_status_regression_end_to_end.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_human_output_rule.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_human_work_unit.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_hygiene_boundary_guidance.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_hyphenated_gate_name.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_ignored_cleanup_before_probe.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_init_integration.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_installed_copy_driver_e2e.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_interpreter_normalization.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_judge_close_path.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_judge_cost.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_judge_cost_fold_committed.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_judge_module.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_judge_path_registry.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_judge_sees_dropped_produces.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_judge_terminal_flip_state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_judged_event_points_at_findings.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_label_description_length.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_label_provisioning_runner_contract.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_label_registry.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_label_registry_covers_consumers.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_language_aware_narrow_selection.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_layout.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_layout_end_to_end.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_layout_link.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_layout_validation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lazy_baseline_e2e.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_learnings_curate_vendored_promote.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_learnings_distil_cli.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_learnings_query.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_learnings_staging.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_learnings_suggest_signature_filter.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_learnings_weights.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lifetime_accounting.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_ac_observable.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_boundary_consistency.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_boundary_extraction.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_close_verdict_not_predecided.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_closing.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_closing_criteria.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_closing_criteria_pristine.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_covers_load_wu_invariants.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_feature_oracle_declared.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_gate_proportionality.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_monitoring.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_plan_contract_fields.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_plan_relative_import.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_plan_verdict_exempt.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_planning_sections.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_produces_satisfiability.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_roadmap.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_lint_verification_reference.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_live_tests_opt_in.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_load_bearing_close_lint.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_attempt_evidence.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_criteria_skeleton.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_criteria_survival.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_gate_needs.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_worklist_injection.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_make_repo_gpgsign_isolation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_marker_dual_shape.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_maven_failure_excerpt.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_max_attempts_configurable.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_max_open_prs.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_check_isolation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_cli.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_diagnose_auto_warning.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_durations.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_finding_context_end_to_end.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_finding_lifecycle_end_to_end.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_finding_meta.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_issue_lifecycle.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_one_issue_per_fingerprint.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_quiet_annotation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitor_runner_surfaces.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitoring_cron_dialect.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitoring_example.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitoring_fenced_blocks.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_monitoring_seed.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_narrow_fallback_warning.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_narrow_non_py_produces.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_needs_decision_outcome.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_no_pytest_subprocess.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_notify.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_notify_escalation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_notify_sla.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_operator_escalation_rule.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_oracle_set_declared.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_oracle_unrunnable_attribution.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_per_run_item_cap_disclosure.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_pick_feature_skill.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_pin_honesty_and_integrity.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_pin_marker_not_inherited.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_pin_marker_spawn_sites.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_plan_baseline.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_planning_cost_floor.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_plugin_prose_cross_refs.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_plugin_qualified_dispatch.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_policy_budgets_applied.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_policy_proposals.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_policy_review.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_pr_checks_json_field.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_pr_reference_in_title.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_prep_halt_operator_surface.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_prepare_feature.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_prepare_scaffold_sync.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_prerun_capture.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_prerun_oracles.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_prerun_wiring.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produced_document_verdict_guard.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produces_amendment_e2e.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produces_bracket_literal.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produces_bracket_wildcard.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produces_in_diff.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produces_justification.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produces_recursive_glob.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produces_repair_note.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produces_semantics_unified.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_produces_shape.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_progress_forward_note.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_progress_lines_end_to_end.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_promoted_issue_not_redispatched.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_promotion_ledger.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_provider_escalation_traces.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_provision_labels.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_queue_stalled_adapter.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_rearm_close_broad_run.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_rearm_fold_marker.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_rearm_migration.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_rearm_retained_new_files.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_reclose_carries_narrow_greens_e2e.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_red_baseline_never_reused.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_red_on_base_fetch_and_reason.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_red_on_base_guardrail.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_red_on_base_overlay.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_red_on_base_probe.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_red_on_base_real_runner.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_redaction_windows_home.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_rediagnose_on_reopen.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_reescalation_posts_comment.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_reflection_required_signals.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_replan_end_to_end.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_replan_event_emission.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_replan_note_collision.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_replan_trigger.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_replay_spin.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_replay_spin_failing_sets.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_required_checks.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_resolve_base.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_restart_cap_counts_progress.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_result_block_audience.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_result_block_near_miss.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_result_block_scalar.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_resume_command_build.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_retain_on_guard_refusal_docs.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_roadmap_archive_cli.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_roadmap_archive_shim.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_roadmap_archive_skill_delegates.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_roadmap_link_gate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_ruff_verdict_lines.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_rule_import_convergence.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_run_level_failure_breaker.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_data_in_sync.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_doc_hygiene.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_docs.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_init.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_label_provisioning.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_resources.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_skew_halts.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_scaffold_wiring.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_schedule_dialect.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_seed_documents_prerun_keys.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_service_bus_dlq_adapter.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_session_env_preflight.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_severity_aliases.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_severity_backfill_apply.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_severity_backfill_bugs_only.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_severity_backfill_end_to_end.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_severity_backfill_limit.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_severity_backfill_marker.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_severity_backfill_run.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_severity_label_projection.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_severity_reconciliation.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_severity_rubric.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_shipped_templates_parse.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_skill_discovery_links.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_skills_vendored_in_sync.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_spec_work_unit_rules.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_spend_counts_cache_tokens.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_spinning_rearm_gate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_spinning_repeat_same_failing_set.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_spinout_brief_end_to_end.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_spinout_brief_replan_option.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_stop_rationale_captured.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_stranded_halt_cause.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_surefire_run_level_signature.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_surefire_verdict_lines.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_terminal_auto_close_changelog.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_terminal_gate_message.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_tiered_verification_e2e.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_timeout_kill_portable.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_triage.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_triage_apply.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_triage_projects_severity_label.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_triage_reads_comments.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_triage_severity_classify.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_triage_severity_end_to_end.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_triage_severity_label_conflict.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_triage_severity_write.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_triage_skill_contract.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_triage_skips_agent_escalations.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_tsc_verdict_lines.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_umbrella_version_resolution.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_unreadable_pr_listing_holds_pr_work.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_untracked_file_absorption.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_upgrade_gitignore_lines.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_windows_gate_exec.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_worktree_restore.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_wu_execution_metadata.py +0 -0
- {specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/tests/test_wu_template_renders_lintable.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specfuse-loop
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.31.0
|
|
4
4
|
Summary: Local-first executor for the Specfuse Plan + Work Unit gate-cycle methodology.
|
|
5
5
|
Author: Specfuse contributors
|
|
6
6
|
License: Apache-2.0
|
|
@@ -152,10 +152,10 @@ specfuse run # the real run
|
|
|
152
152
|
|
|
153
153
|
This repository is also a **self-demonstrating reference installation**: its own
|
|
154
154
|
`.specfuse/` contains a worked example feature
|
|
155
|
-
(`features/FEAT-2026-0001-health-endpoint/`). From the repo root you can run:
|
|
155
|
+
(`features/FEAT-LOOP-2026-0001-health-endpoint/`). From the repo root you can run:
|
|
156
156
|
|
|
157
157
|
```bash
|
|
158
|
-
python .specfuse/scripts/lint_plan.py .specfuse/features/FEAT-2026-0001-health-endpoint
|
|
158
|
+
python .specfuse/scripts/lint_plan.py .specfuse/features/FEAT-LOOP-2026-0001-health-endpoint
|
|
159
159
|
python .specfuse/scripts/loop.py --dry-run
|
|
160
160
|
```
|
|
161
161
|
|
|
@@ -180,7 +180,7 @@ specfuse-loop/
|
|
|
180
180
|
├── skills/verification/SKILL.md
|
|
181
181
|
├── scripts/{loop.py, lint_plan.py, gh_features.py, adopt_feature.py, gh_backend.py}
|
|
182
182
|
├── templates/{PLAN,GATE,WU}.template.md
|
|
183
|
-
└── features/FEAT-2026-0001-health-endpoint/ (the worked example)
|
|
183
|
+
└── features/FEAT-LOOP-2026-0001-health-endpoint/ (the worked example)
|
|
184
184
|
```
|
|
185
185
|
|
|
186
186
|
`specfuse init` also ships the durable docs — `methodology.md`, `skills.md`, and
|
|
@@ -119,10 +119,10 @@ specfuse run # the real run
|
|
|
119
119
|
|
|
120
120
|
This repository is also a **self-demonstrating reference installation**: its own
|
|
121
121
|
`.specfuse/` contains a worked example feature
|
|
122
|
-
(`features/FEAT-2026-0001-health-endpoint/`). From the repo root you can run:
|
|
122
|
+
(`features/FEAT-LOOP-2026-0001-health-endpoint/`). From the repo root you can run:
|
|
123
123
|
|
|
124
124
|
```bash
|
|
125
|
-
python .specfuse/scripts/lint_plan.py .specfuse/features/FEAT-2026-0001-health-endpoint
|
|
125
|
+
python .specfuse/scripts/lint_plan.py .specfuse/features/FEAT-LOOP-2026-0001-health-endpoint
|
|
126
126
|
python .specfuse/scripts/loop.py --dry-run
|
|
127
127
|
```
|
|
128
128
|
|
|
@@ -147,7 +147,7 @@ specfuse-loop/
|
|
|
147
147
|
├── skills/verification/SKILL.md
|
|
148
148
|
├── scripts/{loop.py, lint_plan.py, gh_features.py, adopt_feature.py, gh_backend.py}
|
|
149
149
|
├── templates/{PLAN,GATE,WU}.template.md
|
|
150
|
-
└── features/FEAT-2026-0001-health-endpoint/ (the worked example)
|
|
150
|
+
└── features/FEAT-LOOP-2026-0001-health-endpoint/ (the worked example)
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
`specfuse init` also ships the durable docs — `methodology.md`, `skills.md`, and
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfuse-loop"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.31.0"
|
|
8
8
|
description = "Local-first executor for the Specfuse Plan + Work Unit gate-cycle methodology."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
3
3
|
"""The specfuse-agent runner (FEAT-2026-0049): a thin conductor over the
|
|
4
4
|
existing loop driver and skills. See `.specfuse/features/
|
|
5
|
-
FEAT-2026-0049-specfuse-agent-runner/PLAN.md` for why this is a sibling
|
|
5
|
+
FEAT-LOOP-2026-0049-specfuse-agent-runner/PLAN.md` for why this is a sibling
|
|
6
6
|
package to `specfuse/loop/` and `specfuse/monitor/` rather than living in
|
|
7
7
|
either.
|
|
8
8
|
"""
|
|
@@ -33,6 +33,7 @@ import re
|
|
|
33
33
|
from pathlib import Path
|
|
34
34
|
|
|
35
35
|
from . import _miniyaml
|
|
36
|
+
from . import ids as _ids
|
|
36
37
|
from .lint_roadmap import roadmap_statuses
|
|
37
38
|
|
|
38
39
|
__all__ = (
|
|
@@ -62,7 +63,7 @@ PROVIDER_VALUES = frozenset({"discord", "slack", "teams", "none"})
|
|
|
62
63
|
|
|
63
64
|
REQUIRED_TOP_LEVEL_FIELDS = ("version", "queue", "rules", "budgets", "escalation")
|
|
64
65
|
|
|
65
|
-
_FEAT_ID_RE =
|
|
66
|
+
_FEAT_ID_RE = _ids.FEATURE_ID_RE
|
|
66
67
|
|
|
67
68
|
_REQUIRED_BUDGET_FIELDS = ("max_tokens_per_run", "max_open_prs", "max_items_per_day")
|
|
68
69
|
_REQUIRED_ESCALATION_FIELDS = ("webhook_env", "assignee", "quiet_hours", "sla_hours")
|
|
@@ -644,7 +645,7 @@ def resolve_severity_aliases(path: "str | Path | None" = None) -> dict:
|
|
|
644
645
|
return resolved
|
|
645
646
|
|
|
646
647
|
|
|
647
|
-
def validate_agent_policy(path: str | Path | None = None) -> list[str]:
|
|
648
|
+
def validate_agent_policy(path: str | Path | None = None, repo_root: str | Path | None = None) -> list[str]:
|
|
648
649
|
"""Validate *path* (default ``.specfuse/agent-policy.yml``); return findings.
|
|
649
650
|
|
|
650
651
|
Empty list means valid. A missing file, a parse failure, and a non-mapping
|
|
@@ -684,12 +685,12 @@ def validate_agent_policy(path: str | Path | None = None) -> list[str]:
|
|
|
684
685
|
findings.extend(_check_escalation(parsed["escalation"]))
|
|
685
686
|
|
|
686
687
|
if "queue" in parsed and isinstance(parsed["queue"], list):
|
|
687
|
-
findings.extend(_check_queue_against_roadmap(parsed["queue"]))
|
|
688
|
+
findings.extend(_check_queue_against_roadmap(parsed["queue"], repo_root))
|
|
688
689
|
|
|
689
690
|
return findings
|
|
690
691
|
|
|
691
692
|
|
|
692
|
-
def _check_queue_against_roadmap(queue: list) -> list[str]:
|
|
693
|
+
def _check_queue_against_roadmap(queue: list, repo_root: "str | Path | None" = None) -> list[str]:
|
|
693
694
|
"""Cross-check queue entries against roadmap.md's FEAT-ID status.
|
|
694
695
|
|
|
695
696
|
ERROR when a queue entry has no roadmap row at all (unresolvable, a
|
|
@@ -699,15 +700,18 @@ def _check_queue_against_roadmap(queue: list) -> list[str]:
|
|
|
699
700
|
per the roadmap's own status legend. Skipped without error when
|
|
700
701
|
roadmap.md is absent, so a policy file may exist before a roadmap does.
|
|
701
702
|
"""
|
|
702
|
-
statuses = roadmap_statuses()
|
|
703
|
+
statuses = roadmap_statuses(repo_root)
|
|
703
704
|
if not statuses:
|
|
704
705
|
return []
|
|
706
|
+
root = Path(repo_root) if repo_root is not None else Path.cwd()
|
|
705
707
|
|
|
706
708
|
findings: list[str] = []
|
|
707
709
|
for entry in queue:
|
|
708
710
|
if not isinstance(entry, str) or not _FEAT_ID_RE.match(entry):
|
|
709
711
|
continue
|
|
710
712
|
status = statuses.get(entry)
|
|
713
|
+
if status is None:
|
|
714
|
+
status = statuses.get(_ids.resolve(root, entry))
|
|
711
715
|
if status is None:
|
|
712
716
|
findings.append(f"ERROR: queue: {entry!r} has no row in roadmap.md")
|
|
713
717
|
elif status in _DONE_OR_ABANDONED:
|
|
@@ -725,7 +729,7 @@ def _check_version(version: object) -> list[str]:
|
|
|
725
729
|
|
|
726
730
|
def _check_queue(queue: object) -> list[str]:
|
|
727
731
|
if not isinstance(queue, list):
|
|
728
|
-
return ["ERROR: 'queue' must be a list of FEAT-YYYY-NNNN strings"]
|
|
732
|
+
return ["ERROR: 'queue' must be a list of FEAT-YYYY-NNNN or FEAT-<KEY>-YYYY-NNNN strings"]
|
|
729
733
|
|
|
730
734
|
findings: list[str] = []
|
|
731
735
|
seen: set[str] = set()
|
|
@@ -733,7 +737,7 @@ def _check_queue(queue: object) -> list[str]:
|
|
|
733
737
|
if not isinstance(entry, str) or not _FEAT_ID_RE.match(entry):
|
|
734
738
|
findings.append(
|
|
735
739
|
f"ERROR: queue[{index}]: {entry!r} does not match "
|
|
736
|
-
f"FEAT-YYYY-NNNN"
|
|
740
|
+
f"FEAT-YYYY-NNNN or FEAT-<KEY>-YYYY-NNNN"
|
|
737
741
|
)
|
|
738
742
|
continue
|
|
739
743
|
if entry in seen:
|
|
@@ -884,7 +888,7 @@ def _check_feature_overrides(overrides: object) -> list[str]:
|
|
|
884
888
|
if not _FEAT_ID_RE.match(str(feat_id)):
|
|
885
889
|
findings.append(
|
|
886
890
|
f"ERROR: rules.features.overrides: key {feat_id!r} does not "
|
|
887
|
-
f"match FEAT-YYYY-NNNN"
|
|
891
|
+
f"match FEAT-YYYY-NNNN or FEAT-<KEY>-YYYY-NNNN"
|
|
888
892
|
)
|
|
889
893
|
if value not in GATE_REVIEW_VALUES:
|
|
890
894
|
findings.append(
|
|
@@ -10,8 +10,8 @@ structured per-class verdict. Mirrors the shape of `gate_eval.py`'s
|
|
|
10
10
|
`evaluate_auto_close`: pure evaluation over files, a structured decision
|
|
11
11
|
object, a formatter for the event trail, no side effects. Arming and closing
|
|
12
12
|
are deliberately separate decisions with different inputs — this module does
|
|
13
|
-
not fold into `gate_eval.py
|
|
14
|
-
from `loop.py` (the dependency points the other way: `loop.py` will call
|
|
13
|
+
not fold into `gate_eval.py` (it reads only the `NON_SUBSTANTIVE_TYPES`
|
|
14
|
+
constant from it) and does not import from `loop.py` (the dependency points the other way: `loop.py` will call
|
|
15
15
|
into this module, T04).
|
|
16
16
|
|
|
17
17
|
The organizing principle: model-authored signals may only veto; only
|
|
@@ -38,6 +38,7 @@ from pathlib import Path
|
|
|
38
38
|
from . import _miniyaml
|
|
39
39
|
from .closing_requirements import gate_review_filename
|
|
40
40
|
from .cost import wu_lifetime_cost_usd
|
|
41
|
+
from .gate_eval import NON_SUBSTANTIVE_TYPES
|
|
41
42
|
from .plan_baseline import load_baseline, load_plan_graph
|
|
42
43
|
|
|
43
44
|
PREDICATE_VERSION = "v1"
|
|
@@ -110,6 +111,7 @@ JUDGE_MODULES = tuple(
|
|
|
110
111
|
"criteria_state.py",
|
|
111
112
|
"lint_closing.py",
|
|
112
113
|
"lint_plan.py",
|
|
114
|
+
"ids.py", # supplies lint_plan's CORRELATION_ID_RE; an edit changes what the close's lint accepts
|
|
113
115
|
"plan_baseline.py", # the baseline retroactive_edits compares to
|
|
114
116
|
"roadmap_archive.py", # CLI entry for auto_archive_feature; assert_terminal_flips_fired depends on it
|
|
115
117
|
# The driver: dispatch guards, halt classification, the produces gate,
|
|
@@ -629,6 +631,114 @@ def evaluate_arm_predicate(feature_dir: Path, just_closed_gate: int) -> ArmDecis
|
|
|
629
631
|
)
|
|
630
632
|
|
|
631
633
|
|
|
634
|
+
# Literal acknowledgement entry covering any `produces_driver_helper` hit.
|
|
635
|
+
DRIVER_HELPER_ACK_KEY = "produces_driver_helper"
|
|
636
|
+
# Reason prefix of a `clean` verdict that is clean only because of an acknowledgement.
|
|
637
|
+
ACKNOWLEDGED_REASON_PREFIX = "acknowledged by operator: "
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def _terminal_judge_hits(
|
|
641
|
+
feature_dir: Path, gate_number: int
|
|
642
|
+
) -> "tuple[list[tuple[str, str]], dict | None]":
|
|
643
|
+
"""The judge-surface hits of `gate_number`'s done substantive units, each as
|
|
644
|
+
``(acknowledgement key, rendered hit)``, plus the close unit's
|
|
645
|
+
acknowledgement (or None). Shared by the verdict and the brief's key list so
|
|
646
|
+
the two cannot disagree on what an acknowledgement must name."""
|
|
647
|
+
feature_dir = Path(feature_dir)
|
|
648
|
+
plan = load_plan_graph(feature_dir)
|
|
649
|
+
gate = next(
|
|
650
|
+
(g for g in plan.get("gates", []) or [] if g.get("gate") == gate_number),
|
|
651
|
+
None,
|
|
652
|
+
)
|
|
653
|
+
hits: list[tuple[str, str]] = [] # (acknowledgement key, rendered hit)
|
|
654
|
+
acknowledgement: "dict | None" = None
|
|
655
|
+
for ref in (gate.get("work_units", []) or []) if gate else []:
|
|
656
|
+
wu = _read_wu(feature_dir, ref)
|
|
657
|
+
if wu is None:
|
|
658
|
+
continue
|
|
659
|
+
if wu["type"] == "close" and acknowledgement is None:
|
|
660
|
+
acknowledgement = _read_judge_edit_acknowledgement(feature_dir / ref["file"])
|
|
661
|
+
if wu["type"] in NON_SUBSTANTIVE_TYPES or wu["status"] != "done":
|
|
662
|
+
continue
|
|
663
|
+
for p in wu["produces"]:
|
|
664
|
+
if _matches_judge_path(p):
|
|
665
|
+
hits.append((p, f"{wu['id']} produces {p}"))
|
|
666
|
+
if wu["produces_driver_helper"]:
|
|
667
|
+
hits.append((
|
|
668
|
+
DRIVER_HELPER_ACK_KEY,
|
|
669
|
+
f"{wu['id']} declares produces_driver_helper "
|
|
670
|
+
f"{wu['produces_driver_helper']!r}",
|
|
671
|
+
))
|
|
672
|
+
return hits, acknowledgement
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def terminal_judge_edit_ack_keys(feature_dir: Path, gate_number: int) -> "list[str]":
|
|
676
|
+
"""The sorted entries a `judge_edit_acknowledged.paths` list must contain to
|
|
677
|
+
cover every current hit of `gate_number`: each judge-surface path, plus
|
|
678
|
+
`DRIVER_HELPER_ACK_KEY` when any unit declares `produces_driver_helper`.
|
|
679
|
+
Pure; empty when nothing hits."""
|
|
680
|
+
hits, _ = _terminal_judge_hits(Path(feature_dir), gate_number)
|
|
681
|
+
return sorted({key for key, _ in hits})
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def evaluate_terminal_judge_editing(feature_dir: Path, gate_number: int) -> ClassVerdict:
|
|
685
|
+
"""Report which done substantive units of `gate_number` touch a judge surface.
|
|
686
|
+
|
|
687
|
+
Not a successor-arm class: it is absent from `CLASS_NAMES` and `would_arm`
|
|
688
|
+
never reads it. It is the terminal gate's counterpart of class 2 -- the arm
|
|
689
|
+
predicate looks at *drafted* units of the next gate; nothing arms after the
|
|
690
|
+
terminal gate, so this looks at the units that already ran and are about to
|
|
691
|
+
be closed over. Pure: reads PLAN.md and the gate's unit files only.
|
|
692
|
+
"""
|
|
693
|
+
hits, acknowledgement = _terminal_judge_hits(feature_dir, gate_number)
|
|
694
|
+
if not hits:
|
|
695
|
+
return ClassVerdict(
|
|
696
|
+
"clean", "no done substantive unit touches judge-path surfaces"
|
|
697
|
+
)
|
|
698
|
+
covered = set(acknowledgement["paths"]) if acknowledgement else set()
|
|
699
|
+
uncovered = [text for key, text in hits if key not in covered]
|
|
700
|
+
if uncovered:
|
|
701
|
+
return ClassVerdict("fired", "; ".join(uncovered))
|
|
702
|
+
names = sorted({key for key, _ in hits})
|
|
703
|
+
return ClassVerdict("clean", f"{ACKNOWLEDGED_REASON_PREFIX}{', '.join(names)}")
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
def _read_judge_edit_acknowledgement(close_file: Path) -> "dict | None":
|
|
707
|
+
"""The operator-authored `judge_edit_acknowledged` field of a close unit, or
|
|
708
|
+
None when absent or malformed (a malformed field covers nothing; lint_plan
|
|
709
|
+
reports it). The driver never writes this field."""
|
|
710
|
+
fm, _ = _parse_frontmatter(close_file.read_text())
|
|
711
|
+
raw = fm.get("judge_edit_acknowledged")
|
|
712
|
+
if not isinstance(raw, dict):
|
|
713
|
+
return None
|
|
714
|
+
paths, reason = raw.get("paths"), raw.get("reason")
|
|
715
|
+
if (not isinstance(paths, list) or not paths
|
|
716
|
+
or not all(isinstance(p, str) for p in paths)
|
|
717
|
+
or not isinstance(reason, str) or not reason.strip()):
|
|
718
|
+
return None
|
|
719
|
+
return {"paths": paths, "reason": reason}
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
def terminal_judge_edit_acknowledgement(
|
|
723
|
+
feature_dir: Path, gate_number: int
|
|
724
|
+
) -> "dict | None":
|
|
725
|
+
"""The acknowledgement on `gate_number`'s close unit (``{"paths", "reason"}``)
|
|
726
|
+
or None."""
|
|
727
|
+
feature_dir = Path(feature_dir)
|
|
728
|
+
plan = load_plan_graph(feature_dir)
|
|
729
|
+
gate = next(
|
|
730
|
+
(g for g in plan.get("gates", []) or [] if g.get("gate") == gate_number),
|
|
731
|
+
None,
|
|
732
|
+
)
|
|
733
|
+
for ref in (gate.get("work_units", []) or []) if gate else []:
|
|
734
|
+
wu = _read_wu(feature_dir, ref)
|
|
735
|
+
if wu is not None and wu["type"] == "close":
|
|
736
|
+
ack = _read_judge_edit_acknowledgement(feature_dir / ref["file"])
|
|
737
|
+
if ack is not None:
|
|
738
|
+
return ack
|
|
739
|
+
return None
|
|
740
|
+
|
|
741
|
+
|
|
632
742
|
def _format_decision(decision: ArmDecision) -> str:
|
|
633
743
|
"""Render an ArmDecision to the canonical event-trail block shape."""
|
|
634
744
|
lines: list[str] = [
|
|
@@ -29,6 +29,8 @@ from __future__ import annotations
|
|
|
29
29
|
import re
|
|
30
30
|
from dataclasses import dataclass, field
|
|
31
31
|
|
|
32
|
+
from specfuse.loop import ids
|
|
33
|
+
|
|
32
34
|
ENTRY_CLASSES = ("added", "changed", "fixed", "breaking")
|
|
33
35
|
|
|
34
36
|
_UNRELEASED_HEADING_RE = re.compile(r"^##\s+\[Unreleased\]\s*$")
|
|
@@ -36,7 +38,9 @@ _RELEASED_HEADING_RE = re.compile(r"^##\s+\[(?P<version>[^\]]+)\]\s*-\s*(?P<date
|
|
|
36
38
|
_ANY_H2_RE = re.compile(r"^##\s+")
|
|
37
39
|
_CLASS_HEADING_RE = re.compile(r"^###\s+(?P<name>\S.*?)\s*$")
|
|
38
40
|
_ENTRY_RE = re.compile(r"^-\s+(?P<summary>.+?)\s+\((?P<trace>[^()]+)\)\s*$")
|
|
39
|
-
_TRACE_RE = re.compile(
|
|
41
|
+
_TRACE_RE = re.compile(
|
|
42
|
+
r"^(" + ids.FEATURE_ID_PATTERN + r"(?:/[A-Za-z0-9-]+)?|#\d+)$"
|
|
43
|
+
)
|
|
40
44
|
|
|
41
45
|
|
|
42
46
|
@dataclass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.31.0
|
|
@@ -48,12 +48,19 @@ cleared by editing driver code; it clears by changing the plan's numbers.
|
|
|
48
48
|
file under a judge-path surface — paths that would let a work unit edit its
|
|
49
49
|
own verification.
|
|
50
50
|
|
|
51
|
-
**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
**Terminal gate.** The class reads only the successor gate's drafted units,
|
|
52
|
+
and a terminal gate has no successor. A separate terminal judge-editing check
|
|
53
|
+
covers it: it halts before the close when a done substantive unit of the last
|
|
54
|
+
gate declares a judge-surface path, at every autonomy level. It is not one of
|
|
55
|
+
the eight arm classes and does not touch `would_arm`. The operator clears the
|
|
56
|
+
stop by writing `judge_edit_acknowledged` (`paths` and `reason`) on the close
|
|
57
|
+
unit and running `/unblock-wu`. It uses the same `JUDGE_PATHS`, so the
|
|
58
|
+
universal entries (`.specfuse/verification.yml`, `.specfuse/hooks/`,
|
|
59
|
+
`.specfuse/rules/`, `.github/workflows/`, `pyproject.toml`) also reach consumer
|
|
60
|
+
repositories: a feature there whose last gate edits its CI workflow or
|
|
61
|
+
`pyproject.toml` halts once. The check reads declared `produces:` paths only,
|
|
62
|
+
so an edit a unit never declares is not seen, and `pyproject.toml` matches
|
|
63
|
+
whole-file, so a dependency bump fires it.
|
|
57
64
|
|
|
58
65
|
**Fires when:** a drafted WU's `produces:` list contains a path matching one
|
|
59
66
|
of `JUDGE_PATHS` — `.specfuse/verification.yml`, `.specfuse/hooks/`,
|
|
@@ -112,7 +112,7 @@ Two ways to create a feature folder under `.specfuse/features/`:
|
|
|
112
112
|
- **By hand:** start from the bare templates in `.specfuse/templates/`
|
|
113
113
|
(`PLAN`, `GATE`, `WU`) and fill in a small first feature. (The
|
|
114
114
|
`specfuse/loop` source repo also carries a worked example,
|
|
115
|
-
`FEAT-2026-0001-health-endpoint`, if you want a complete reference to copy
|
|
115
|
+
`FEAT-LOOP-2026-0001-health-endpoint`, if you want a complete reference to copy
|
|
116
116
|
from.)
|
|
117
117
|
|
|
118
118
|
A feature folder holds:
|
|
@@ -141,7 +141,13 @@ specfuse lint .specfuse/features/FEAT-YYYY-NNNN-your-feature
|
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
The linter checks structure: every WU has the five mandatory sections, the closing
|
|
144
|
-
sequence is present and well-formed, dependencies resolve, IDs are well-formed
|
|
144
|
+
sequence is present and well-formed, dependencies resolve, IDs are well-formed
|
|
145
|
+
(both `FEAT-YYYY-NNNN` and the keyed `FEAT-<KEY>-YYYY-NNNN` form).
|
|
146
|
+
|
|
147
|
+
Running several repos? Optionally set `ids.key` in `.specfuse/layout.yml` so new
|
|
148
|
+
IDs read `FEAT-<KEY>-YYYY-NNNN`. Pick a short key distinct across the repos of
|
|
149
|
+
one project (uniqueness is by convention). Existing IDs are never renamed;
|
|
150
|
+
`ids migrate --planned-only` re-keys folderless planned rows only.
|
|
145
151
|
Fix anything it flags before dispatching — it's far cheaper than a failed
|
|
146
152
|
dispatch.
|
|
147
153
|
|
|
@@ -13,7 +13,8 @@ reference.
|
|
|
13
13
|
intends to build, one row each. Add to it with `/roadmap-add`; pick what to
|
|
14
14
|
build next with `/pick-feature`.
|
|
15
15
|
|
|
16
|
-
**Feature** — one substantial piece of work, `FEAT-YYYY-NNNN
|
|
16
|
+
**Feature** — one substantial piece of work, `FEAT-YYYY-NNNN` (`FEAT-<KEY>-YYYY-NNNN` when
|
|
17
|
+
`ids.key` is set in `.specfuse/layout.yml`; legacy IDs are never renamed), with a folder
|
|
17
18
|
under `.specfuse/features/`. Its `PLAN.md` holds the goal, the gates, and the
|
|
18
19
|
budget. Roughly: a thing you would describe in a sentence to someone who does
|
|
19
20
|
not work on the project.
|
|
@@ -24,7 +25,7 @@ and you decide** — everything inside one runs unattended; the boundary between
|
|
|
24
25
|
two is yours.
|
|
25
26
|
|
|
26
27
|
**Work unit (WU)** — the smallest dispatchable piece: one file, one job, one
|
|
27
|
-
fresh session. `FEAT-YYYY-NNNN/T01
|
|
28
|
+
fresh session. `FEAT-YYYY-NNNN/T01` (keyed: `FEAT-<KEY>-YYYY-NNNN/T01`). Everything the session needs is in that
|
|
28
29
|
file, because it starts with no memory of anything before it.
|
|
29
30
|
|
|
30
31
|
**Verification** — the commands in `.specfuse/verification.yml` that decide
|
|
@@ -34,7 +34,8 @@ on both surfaces.
|
|
|
34
34
|
decision or dependency. The absence of a named blocker is the entire
|
|
35
35
|
difference from `blocked`.
|
|
36
36
|
- **Feature** — a spec-driven *or directly-authored* unit of value, identified by
|
|
37
|
-
a correlation ID `FEAT-YYYY-NNNN
|
|
37
|
+
a correlation ID `FEAT-YYYY-NNNN` (or `FEAT-<KEY>-YYYY-NNNN` when the repo sets
|
|
38
|
+
`ids.key`; see *Project key* below). A feature owns an ordered list of gates.
|
|
38
39
|
- **Gate** — a milestone partition of a feature: an ordered batch of substantive
|
|
39
40
|
work units followed by a mandatory closing sequence and a human review-and-arm
|
|
40
41
|
checkpoint. Gates are numbered within a feature.
|
|
@@ -49,6 +50,30 @@ on both surfaces.
|
|
|
49
50
|
It carries its own prompt and is the contract between the planner and the
|
|
50
51
|
executor.
|
|
51
52
|
|
|
53
|
+
**Project key.** A repo may set an optional `ids.key` in `.specfuse/layout.yml`.
|
|
54
|
+
With a key set, new features are allocated as `FEAT-<KEY>-YYYY-NNNN`, and the
|
|
55
|
+
folder anchor uses the lower-cased key (`FEAT-<key>-YYYY-NNNN-slug`). Numbering
|
|
56
|
+
stays per repo and continues from the highest ordinal of either form. Legacy
|
|
57
|
+
unkeyed IDs are permanent: existing IDs are never renamed. The one exception to
|
|
58
|
+
leaving old rows alone is `ids migrate --planned-only`, which re-keys folderless
|
|
59
|
+
planned roadmap rows. Pick a short key distinct across the repos of one project;
|
|
60
|
+
uniqueness is by convention, not enforced. No prompt asks for the key; it is read
|
|
61
|
+
from `layout.yml`. The ID shape lives in `specfuse.loop.ids`.
|
|
62
|
+
|
|
63
|
+
**Renaming existing features.** `ids migrate --all` is the opt-in full rename: it
|
|
64
|
+
re-keys every feature of the repository, rewriting folder names, PLAN and WU
|
|
65
|
+
frontmatter, roadmap rows and anchors, and `events.jsonl` correlation IDs, and
|
|
66
|
+
records each old-to-new pair in `.specfuse/id-aliases.yml`. It cannot reach git
|
|
67
|
+
history, GitHub issue and PR titles, or other repositories, which keep the old
|
|
68
|
+
IDs. The loop resolves an old ID through the alias map (one hop) wherever an
|
|
69
|
+
operator types one: `--feature`, `gate_eval`, and `[FEAT-…]` issue titles. Prose
|
|
70
|
+
mentions are listed for review (`--include-prose` rewrites them) rather than
|
|
71
|
+
rewritten silently.
|
|
72
|
+
`--rehearse` proves a rename first: it applies it (with post-apply validation) on a
|
|
73
|
+
throwaway worktree of `HEAD`, runs the `tests` gate command there (or
|
|
74
|
+
`--rehearse-command`), reports the failing tests, and removes the worktree; the real
|
|
75
|
+
tree is never written and must have no uncommitted tracked changes.
|
|
76
|
+
|
|
52
77
|
The correlation ID threads the entire lifecycle — it appears in the feature
|
|
53
78
|
folder name, the WU file, every event-log entry, the branch, the commit trailer
|
|
54
79
|
(`Feature: FEAT-YYYY-NNNN/TNN`), and (in the orchestrator) the GitHub issue.
|
|
@@ -406,7 +431,7 @@ Every dispatched attempt emits exactly one `attempt_outcome` event to
|
|
|
406
431
|
`outcome`, `failure_class`, `failure_signature`, `failure_excerpt`,
|
|
407
432
|
`failing_tests`, `cost_usd`, `duration_seconds`, `attempt`, and
|
|
408
433
|
`re_arm_count`. For the field-by-field schema see
|
|
409
|
-
`.specfuse/features/FEAT-2026-0016-attempt-outcome-rearm-contract/PLAN.md`
|
|
434
|
+
`.specfuse/features/FEAT-LOOP-2026-0016-attempt-outcome-rearm-contract/PLAN.md`
|
|
410
435
|
§ "Event payload shape — `attempt_outcome` v1". The full payload is
|
|
411
436
|
not restated here (one fact, one home).
|
|
412
437
|
|
|
@@ -796,11 +821,13 @@ single flip site. What changed is what an armed gate is armed *on*. Before the
|
|
|
796
821
|
judge, an `auto` feature let the session that did the work write the verdict
|
|
797
822
|
that advanced the feature; now a fresh evaluator that never sees that session's
|
|
798
823
|
prose writes it, and can only lower it. The `judge_editing` stop class vetoes
|
|
799
|
-
arming a successor gate whose drafted units would edit a judge surface.
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
`
|
|
824
|
+
arming a successor gate whose drafted units would edit a judge surface. The
|
|
825
|
+
terminal gate has no successor, so a separate check covers it: when a done
|
|
826
|
+
substantive unit of the last gate declares a judge-surface path, the driver
|
|
827
|
+
halts before the close, at every autonomy level, and the operator clears the
|
|
828
|
+
stop by writing `judge_edit_acknowledged` on the close unit and running
|
|
829
|
+
`/unblock-wu`. `review` adds no terminal checkpoint; choose it only when a
|
|
830
|
+
wrong arm is expensive to undo.
|
|
804
831
|
|
|
805
832
|
Tighten to `review` when the judge cannot do its job — a feature whose criteria
|
|
806
833
|
are not judgeable from the evidence bundle (see `judge_disabled` in §3), or one
|
|
@@ -46,7 +46,10 @@ flips `blocked` → `active` (or `planned`) and removes the `**Blocked by.**` bl
|
|
|
46
46
|
|
|
47
47
|
## Notes
|
|
48
48
|
|
|
49
|
-
- Correlation IDs are allocated here, sequentially per year: `FEAT-YYYY-NNNN
|
|
49
|
+
- Correlation IDs are allocated here, sequentially per year per repo: `FEAT-YYYY-NNNN`,
|
|
50
|
+
or `FEAT-<KEY>-YYYY-NNNN` when `ids.key` is set in `.specfuse/layout.yml` (numbering
|
|
51
|
+
continues from the highest ordinal of either form; legacy IDs are never renamed;
|
|
52
|
+
`ids migrate --planned-only` re-keys folderless planned rows). Work
|
|
50
53
|
units take `FEAT-YYYY-NNNN/TNN`. The same scheme threads commits (trailer
|
|
51
54
|
`Feature: FEAT-YYYY-NNNN/TNN`), the per-feature event log, and — at fold-in —
|
|
52
55
|
GitHub issues across repos.
|
|
@@ -24,6 +24,16 @@ Two namespaces, four canonical shapes:
|
|
|
24
24
|
- `YYYY` is the four-digit calendar year in which the feature was created.
|
|
25
25
|
- `NNNN` is a zero-padded four-digit ordinal, unique within that year's roadmap.
|
|
26
26
|
- Example: `FEAT-2026-0042`.
|
|
27
|
+
- **Keyed feature-level (optional):** `FEAT-<KEY>-YYYY-NNNN`
|
|
28
|
+
- `KEY` is a per-repository project key: an uppercase letter followed by one to
|
|
29
|
+
five uppercase letters or digits (`[A-Z][A-Z0-9]{1,5}`). A repository opts in by
|
|
30
|
+
setting it once (in the loop, `ids.key` in `.specfuse/layout.yml`); without a key,
|
|
31
|
+
IDs keep the unkeyed shape above.
|
|
32
|
+
- The key makes an ID say which repository it belongs to, so the same ordinal in
|
|
33
|
+
two repositories of one project no longer collides. Numbering stays per
|
|
34
|
+
repository: the ordinal continues from the highest `NNNN` of either shape.
|
|
35
|
+
- Unkeyed IDs that already exist stay valid permanently and are never renamed.
|
|
36
|
+
- Example: `FEAT-ACME-2026-0042`; anchor `feat-acme-2026-0042`.
|
|
27
37
|
- **Task-level:** `FEAT-YYYY-NNNN/<task-id>`
|
|
28
38
|
- The feature-level ID, a literal `/`, then the task identifier.
|
|
29
39
|
- Substantive units use `TNN` — `T` followed by a zero-padded two-digit ordinal,
|
|
@@ -66,7 +76,7 @@ Collisions between the two namespaces are structurally impossible.
|
|
|
66
76
|
The combined pattern accepted across all shapes is:
|
|
67
77
|
|
|
68
78
|
```
|
|
69
|
-
^(FEAT
|
|
79
|
+
^(FEAT-(?:[A-Z][A-Z0-9]{1,5}-)?\d{4}-\d{4}(/(T\d{2}(H\d*)?|G\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE)))?|INIT-\d{4}-\d{4}/F\d{2}(/(T\d{2}(H\d*)?|G\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE)))?)$
|
|
70
80
|
```
|
|
71
81
|
|
|
72
82
|
A string that does not match is malformed.
|
|
@@ -123,7 +133,9 @@ that materially describes the work, err toward including the ID.
|
|
|
123
133
|
## Generating the next ID
|
|
124
134
|
|
|
125
135
|
**Feature-level.** The next ordinal for year `YYYY` is one greater than the largest
|
|
126
|
-
`NNNN` that currently appears in `.specfuse/features/FEAT-YYYY
|
|
136
|
+
`NNNN` that currently appears in `.specfuse/features/FEAT-YYYY-*/` or, in a keyed
|
|
137
|
+
repository, `.specfuse/features/FEAT-<KEY>-YYYY-*/` (both shapes count). A keyed
|
|
138
|
+
repository emits the keyed shape. If no feature
|
|
127
139
|
for `YYYY` exists yet, start at `0001`. Padding is always four digits: `0001`,
|
|
128
140
|
`0042`, `1234`. Year rollover does not continue the previous year's counter — each
|
|
129
141
|
year starts fresh at `0001`.
|
{specfuse_loop-0.30.0 → specfuse_loop-0.31.0}/specfuse/loop/data/schemas/driver-event.schema.json
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"$id": "https://specfuse.dev/loop/schemas/driver-event.schema.json",
|
|
4
4
|
"title": "Driver-local envelope registry",
|
|
5
5
|
"description": "Additive to the vendored orchestrator envelope (event.schema.json): shapes the loop driver emits with no orchestrator-side counterpart. validate_event.py resolves each shape against the vendored envelope first; only when absent there does it fall through to this registry. `event_types` (FEAT-2026-0060) widens the event_type enum; task_started, task_completed, and human_escalation are already sanctioned in the vendored enum and must not be duplicated here. `correlation_id` (FEAT-2026-0073) widens the correlation_id pattern to admit the closing-sequence and hygiene shapes `.specfuse/rules/correlation-ids.md` documents; `closing_names` and `hygiene_suffix_pattern` here must state the same set of shapes as that rules file's combined pattern (and as specfuse/loop/lint_plan.py's CORRELATION_ID_RE, which independently enforces the same contract on PLAN.md graphs).",
|
|
6
|
-
"$comment": "FEAT-2026-0060/T01: event_types derived from the union of every build_event(...) call site in specfuse/loop/loop.py and a corpus sweep of every .specfuse/features/*/events.jsonl, subtracting the vendored envelope enum. attempt_outcome and gate_reached were already-known precedent (loop.py's build_arm_predicate_event docstring); auto_close_decision, arm_predicate_evaluated, re_arm_dispatched, plan_next_draft_lint, and unsandboxed_dispatch are additional driver-local mechanics the orchestrator has no concept of. gate_auto_armed (loop.py build_event call under gate_events.append) and re_arm_rejected (loop.py build_event call under the spinning_reproduction_missing re-arm check) are emitted by real code paths that have never fired in a recorded run, so a corpus-only sweep cannot see them; they are included because call-site derivation does. FEAT-2026-0073/T01: correlation_id added — closing_names and hygiene_suffix_pattern mirror correlation-ids.md's documented shapes and lint_plan.py's CORRELATION_ID_RE verbatim, so the envelope, the rules file, and the PLAN-graph linter state one contract. FEAT-2026-0075/T03: driver_staleness_detected added — emitted at gate completion when a work unit's squash diff touched the driver's own importable surface, naming the editing unit, the driver paths it touched, and the IDs of units this process dispatched afterward (which ran the pre-edit module). FEAT-2026-0100/T02: judged added — emitted once per terminal close after the closing-deliverable guards, carrying the close's own verdict, the judge session's verdict (null when the judge was disabled, timed out, or returned nothing parseable, with the reason beside it), and whether the judge lowered it. FEAT-2026-0109/T01: baseline_attribution added — emitted when the retroactive per-attempt-failure baseline probe (attribute_failure_to_baseline) runs fresh, carrying the gate, the failing-gate set, and the unit whose failed attempt triggered the probe. FEAT-2026-0109/T06: broad_run_result added — emitted whenever the once-per-gate broad verification pass (gate_broad_run_check) actually runs (never on a tree-unchanged dedup skip), carrying the gate, the tree it was measured on, and its pass/fail outcome; a failing broad run additionally raises the existing human_escalation event type with reason: broad_run_gate_failure, which needs no schema addition since human_escalation is already sanctioned in the vendored envelope enum. FEAT-2026-0109/T08: driver_build_pinned added — emitted once per process, before any work unit is dispatched, when this process is itself a materialized pin (`_reexec_pinned`); carries the pin's tree hash and its path so an operator or test can read what build actually ran without inferring it from `ps` output. `driver_staleness_detected` needed no addition here: this unit reuses it with `halted: false` for the pinned, non-halting case (the registry constrains the type list, not the payload), adding `pinned_tree` / `next_pin_tree` fields alongside the existing `halted: true` shape. FEAT-2026-0104/T04: replan added — emitted once a re-plan turn (T03's run_replan_turn) rewrites a unit's body ahead of its last permitted attempt, so gate_eval.py's evaluate_auto_close (check 2, already reading this event_type since FEAT-2026-0018/T02) can disable auto-close for a gate that needed one. FEAT-2026-0115/T01: fix_unit_inserted added — emitted once a blocked RESULT's `blocked_next:` names a drafted fix unit that `insert_fix_unit` validated and inserted ahead of the blocked unit, carrying the fix unit's id, its file, the gate, and the insertion count; the blocked unit's own re-arm (an existing `re_arm_dispatched`, reason `fix_unit_inserted`) rides alongside it, so this is the only genuinely new event_type the mechanic needs.",
|
|
6
|
+
"$comment": "FEAT-2026-0060/T01: event_types derived from the union of every build_event(...) call site in specfuse/loop/loop.py and a corpus sweep of every .specfuse/features/*/events.jsonl, subtracting the vendored envelope enum. attempt_outcome and gate_reached were already-known precedent (loop.py's build_arm_predicate_event docstring); auto_close_decision, arm_predicate_evaluated, re_arm_dispatched, plan_next_draft_lint, and unsandboxed_dispatch are additional driver-local mechanics the orchestrator has no concept of. gate_auto_armed (loop.py build_event call under gate_events.append) and re_arm_rejected (loop.py build_event call under the spinning_reproduction_missing re-arm check) are emitted by real code paths that have never fired in a recorded run, so a corpus-only sweep cannot see them; they are included because call-site derivation does. FEAT-2026-0073/T01: correlation_id added — closing_names and hygiene_suffix_pattern mirror correlation-ids.md's documented shapes and lint_plan.py's CORRELATION_ID_RE verbatim, so the envelope, the rules file, and the PLAN-graph linter state one contract. FEAT-2026-0075/T03: driver_staleness_detected added — emitted at gate completion when a work unit's squash diff touched the driver's own importable surface, naming the editing unit, the driver paths it touched, and the IDs of units this process dispatched afterward (which ran the pre-edit module). FEAT-2026-0100/T02: judged added — emitted once per terminal close after the closing-deliverable guards, carrying the close's own verdict, the judge session's verdict (null when the judge was disabled, timed out, or returned nothing parseable, with the reason beside it), and whether the judge lowered it. FEAT-2026-0109/T01: baseline_attribution added — emitted when the retroactive per-attempt-failure baseline probe (attribute_failure_to_baseline) runs fresh, carrying the gate, the failing-gate set, and the unit whose failed attempt triggered the probe. FEAT-2026-0109/T06: broad_run_result added — emitted whenever the once-per-gate broad verification pass (gate_broad_run_check) actually runs (never on a tree-unchanged dedup skip), carrying the gate, the tree it was measured on, and its pass/fail outcome; a failing broad run additionally raises the existing human_escalation event type with reason: broad_run_gate_failure, which needs no schema addition since human_escalation is already sanctioned in the vendored envelope enum. FEAT-2026-0109/T08: driver_build_pinned added — emitted once per process, before any work unit is dispatched, when this process is itself a materialized pin (`_reexec_pinned`); carries the pin's tree hash and its path so an operator or test can read what build actually ran without inferring it from `ps` output. `driver_staleness_detected` needed no addition here: this unit reuses it with `halted: false` for the pinned, non-halting case (the registry constrains the type list, not the payload), adding `pinned_tree` / `next_pin_tree` fields alongside the existing `halted: true` shape. FEAT-2026-0104/T04: replan added — emitted once a re-plan turn (T03's run_replan_turn) rewrites a unit's body ahead of its last permitted attempt, so gate_eval.py's evaluate_auto_close (check 2, already reading this event_type since FEAT-2026-0018/T02) can disable auto-close for a gate that needed one. FEAT-2026-0115/T01: fix_unit_inserted added — emitted once a blocked RESULT's `blocked_next:` names a drafted fix unit that `insert_fix_unit` validated and inserted ahead of the blocked unit, carrying the fix unit's id, its file, the gate, and the insertion count; the blocked unit's own re-arm (an existing `re_arm_dispatched`, reason `fix_unit_inserted`) rides alongside it, so this is the only genuinely new event_type the mechanic needs. FEAT-2026-0246/T01: terminal_judge_editing_evaluated added — emitted on every terminal gate immediately before the close (auto or dispatched), carrying the gate, the `arm_eval.evaluate_terminal_judge_editing` verdict status and reason; a `fired` verdict additionally raises the existing human_escalation event type with reason: judge_editing_terminal, which needs no schema addition. FEAT-2026-0246/T02: terminal_judge_editing_acknowledged added — emitted when that check is clean only because the close unit carries an operator-authored judge_edit_acknowledged field covering every hit, carrying the gate, close unit id, acknowledged paths, and the operator's reason. FEAT-2026-0247/T06: the correlation-ID shape (legacy FEAT-YYYY-NNNN and the keyed FEAT-<KEY>-YYYY-NNNN form) now comes from specfuse.loop.ids.",
|
|
7
7
|
"event_types": [
|
|
8
8
|
"attempt_outcome",
|
|
9
9
|
"gate_reached",
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
"broad_run_result",
|
|
22
22
|
"driver_build_pinned",
|
|
23
23
|
"replan",
|
|
24
|
-
"fix_unit_inserted"
|
|
24
|
+
"fix_unit_inserted",
|
|
25
|
+
"terminal_judge_editing_evaluated",
|
|
26
|
+
"terminal_judge_editing_acknowledged"
|
|
25
27
|
],
|
|
26
28
|
"correlation_id": {
|
|
27
29
|
"closing_names": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"correlation_id": {
|
|
25
25
|
"type": "string",
|
|
26
26
|
"description": "Correlation ID at any level of the unit hierarchy (docs/naming-convention.md): initiative INIT-YYYY-NNNN, dispatched feature INIT-YYYY-NNNN/FNN, work unit INIT-YYYY-NNNN/FNN/<task-id>; or the legacy/component-local feature FEAT-YYYY-NNNN and task FEAT-YYYY-NNNN/<task-id>. The task-id segment carries all three documented work-unit shapes: substantive TNN, hygiene TNNH[N...] (the target unit's ordinal, a literal H, and an optional ordinal), and closing-sequence G<n>-<NAME> where <NAME> is RETRO, LESSONS, DOCS, PLAN, CLOSE-INTERMEDIATE, or CLOSE. Existing FEAT- entries validate unchanged.",
|
|
27
|
-
"pattern": "^(FEAT
|
|
27
|
+
"pattern": "^(FEAT-(?:[A-Z][A-Z0-9]{1,5}-)?|INIT-)\\d{4}-\\d{4}(/F\\d{2})?(/(T\\d{2}(H\\d*)?|G\\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE)))?$"
|
|
28
28
|
},
|
|
29
29
|
"event_type": {
|
|
30
30
|
"type": "string",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"original_issue_correlation_id": {
|
|
16
16
|
"type": "string",
|
|
17
17
|
"description": "The task-level correlation ID from the triggering task that filed the spec issue (e.g. `FEAT-2026-0042/T09`). Links the resolution event back to the original `spec_issue_raised` event and the downstream task that discovered the problem. This is the task-level ID, not the feature-level ID, because spec issues are raised in the context of a specific task's work. The task-id segment accepts every documented work-unit shape: substantive TNN, hygiene TNNH[N...], and closing-sequence G<n>-<NAME> (RETRO, LESSONS, DOCS, PLAN, CLOSE-INTERMEDIATE, CLOSE). See .specfuse/rules/correlation-ids.md.",
|
|
18
|
-
"pattern": "^((FEAT
|
|
18
|
+
"pattern": "^((FEAT-(?:[A-Z][A-Z0-9]{1,5}-)?\\d{4}-\\d{4})|(INIT-\\d{4}-\\d{4}/F\\d{2}))/(T\\d{2}(H\\d*)?|G\\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE))$"
|
|
19
19
|
},
|
|
20
20
|
"affected_files": {
|
|
21
21
|
"type": "array",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"original_issue_correlation_id": {
|
|
16
16
|
"type": "string",
|
|
17
17
|
"description": "The task-level correlation ID from the triggering task that filed the spec issue (e.g. `FEAT-2026-0042/T09`). Links the routing event back to the original `spec_issue_raised` event and the downstream task that discovered the problem. The task-id segment accepts every documented work-unit shape: substantive TNN, hygiene TNNH[N...], and closing-sequence G<n>-<NAME> (RETRO, LESSONS, DOCS, PLAN, CLOSE-INTERMEDIATE, CLOSE). See .specfuse/rules/correlation-ids.md.",
|
|
18
|
-
"pattern": "^((FEAT
|
|
18
|
+
"pattern": "^((FEAT-(?:[A-Z][A-Z0-9]{1,5}-)?\\d{4}-\\d{4})|(INIT-\\d{4}-\\d{4}/F\\d{2}))/(T\\d{2}(H\\d*)?|G\\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE))$"
|
|
19
19
|
},
|
|
20
20
|
"target_project": {
|
|
21
21
|
"type": "string",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"feature_correlation_id": {
|
|
18
18
|
"type": "string",
|
|
19
19
|
"description": "The feature-level correlation ID of the feature whose specs were validated. Duplicated from the envelope `correlation_id` for payload self-containment — downstream consumers that index payloads without re-reading the envelope benefit from the redundancy.",
|
|
20
|
-
"pattern": "^(FEAT
|
|
20
|
+
"pattern": "^(FEAT-(?:[A-Z][A-Z0-9]{1,5}-)?|INIT-)\\d{4}-\\d{4}(/F\\d{2})?$"
|
|
21
21
|
},
|
|
22
22
|
"pass": {
|
|
23
23
|
"type": "boolean",
|
|
@@ -23,6 +23,7 @@ from dataclasses import dataclass
|
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
|
|
25
25
|
from . import _miniyaml
|
|
26
|
+
from . import ids as _ids
|
|
26
27
|
|
|
27
28
|
PREDICATE_VERSION = "v1"
|
|
28
29
|
PER_WU_COST_RATIO_CEILING = 1.5
|
|
@@ -441,6 +442,7 @@ def _resolve_feature_dir(feature_id: str, repo_root: Path) -> "Path | None":
|
|
|
441
442
|
return None
|
|
442
443
|
|
|
443
444
|
candidates = sorted(d for d in features_dir.iterdir() if d.is_dir())
|
|
445
|
+
feature_id = _ids.resolve(repo_root, feature_id)
|
|
444
446
|
|
|
445
447
|
# Priority 1: exact directory name
|
|
446
448
|
exact = [d for d in candidates if d.name == feature_id]
|
|
@@ -458,8 +460,16 @@ def _resolve_feature_dir(feature_id: str, repo_root: Path) -> "Path | None":
|
|
|
458
460
|
partial: list[Path] = []
|
|
459
461
|
for d in candidates:
|
|
460
462
|
name = d.name
|
|
463
|
+
parsed = _ids.parse(name)
|
|
464
|
+
if parsed is not None:
|
|
465
|
+
# FEAT-[KEY-]YYYY-NNNN-slug: compare the ordinal and the slug
|
|
466
|
+
head = str(parsed)
|
|
467
|
+
nnnn = f"{parsed.ordinal:04d}"
|
|
468
|
+
slug = name[len(head) + 1:]
|
|
469
|
+
if nnnn == feature_id or slug == feature_id:
|
|
470
|
+
partial.append(d)
|
|
471
|
+
continue
|
|
461
472
|
parts = name.split("-")
|
|
462
|
-
# FEAT-YYYY-NNNN-slug → parts[2] is the NNNN part
|
|
463
473
|
if len(parts) >= 4:
|
|
464
474
|
nnnn = parts[2]
|
|
465
475
|
slug = "-".join(parts[3:])
|