specfuse-loop 0.3.19__tar.gz → 0.3.21__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.3.19/specfuse_loop.egg-info → specfuse_loop-0.3.21}/PKG-INFO +1 -1
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/pyproject.toml +1 -1
- specfuse_loop-0.3.21/specfuse/loop/data/VERSION +1 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/docs/methodology.md +7 -1
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/lint_plan.py +27 -1
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/loop.py +416 -34
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse_loop.egg-info/SOURCES.txt +3 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_attempt_outcome_emission.py +50 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_gate_eval_intermediate_wiring.py +11 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_gate_eval_terminal_wiring.py +111 -0
- specfuse_loop-0.3.21/tests/test_lifetime_accounting.py +240 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_planned_cost_lint.py +72 -0
- specfuse_loop-0.3.21/tests/test_produces_in_diff.py +264 -0
- specfuse_loop-0.3.21/tests/test_spinning_rearm_gate.py +308 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_terminal_flips.py +85 -0
- specfuse_loop-0.3.19/specfuse/loop/data/VERSION +0 -1
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/LICENSE +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/NOTICE +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/README.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/setup.cfg +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/_filelock.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/gitignore.snippet +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/rules/verification-discipline.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/schemas/event.schema.json +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/scaffold.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_autoclose_deferral_visibility.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_backend.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_bash_routing.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_bookkeeping_no_verify.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_claude_resolution.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_close_wu_self_reference.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_console_encoding.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_degraded_oracle.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_ensure_feature_branch_base.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_events_redaction.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_filelock_portable.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_init_integration.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_interpreter_normalization.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_learnings_query.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_plan_relative_import.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_lint_verification_reference.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_attempt_evidence.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_prepare_feature.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_prepare_scaffold_sync.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_redaction_windows_home.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_resolve_base.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_scaffold_data_in_sync.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_scaffold_doc_hygiene.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_scaffold_docs.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_scaffold_init.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_scaffold_resources.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_scaffold_wiring.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_skills_vendored_in_sync.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_timeout_kill_portable.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_untracked_file_absorption.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_windows_gate_exec.py +0 -0
- {specfuse_loop-0.3.19 → specfuse_loop-0.3.21}/tests/test_wu_execution_metadata.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfuse-loop"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.21"
|
|
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"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.3.21
|
|
@@ -139,7 +139,13 @@ auto-close only eliminates the reflective overhead, not the forward-design step.
|
|
|
139
139
|
full close ceremony regardless of predicate outcome. Alternatively, set
|
|
140
140
|
`auto_close_disabled: true` in a feature's `PLAN.md` frontmatter to disable
|
|
141
141
|
auto-close permanently for that feature (e.g., for features that are inherently
|
|
142
|
-
exploratory and expect off-plan behavior).
|
|
142
|
+
exploratory and expect off-plan behavior). For a single **substantive close**
|
|
143
|
+
— a `close` (or `close-intermediate`) WU whose acceptance criteria assert on the
|
|
144
|
+
source tree, reconcile prior gates, or observe runtime behaviour rather than
|
|
145
|
+
just writing the retrospective stub — set `auto_close_disabled: true` in **that
|
|
146
|
+
WU's** frontmatter. The predicate keys on the implementation WUs' cost and
|
|
147
|
+
plan-conformance and cannot see what a close verifies, so a substantive close
|
|
148
|
+
must be marked must-run or its verification is silently skipped (#189).
|
|
143
149
|
|
|
144
150
|
**Predicate-version transparency** — every `auto_close_decision` event in
|
|
145
151
|
`events.jsonl` carries a `predicate_version` field (e.g., `predicate_version:
|
|
@@ -248,12 +248,23 @@ def _find_task_graph_block(body: str) -> dict | None:
|
|
|
248
248
|
return None
|
|
249
249
|
|
|
250
250
|
|
|
251
|
+
# Ceremony WU types systematically under-estimated in the field: close and
|
|
252
|
+
# plan-next WUs ran 2.8-5.2x over on $2-3 estimates across FEAT-2026-0049
|
|
253
|
+
# (clabonte/generator), poisoning every calibration built on the plans (#201).
|
|
254
|
+
# The floor is a WARN threshold, not a cap — estimates below it are almost
|
|
255
|
+
# certainly wishful, not cheap.
|
|
256
|
+
CEREMONY_COST_FLOOR_USD = 5.0
|
|
257
|
+
_CEREMONY_TYPES = frozenset({"close", "close-intermediate", "plan-next"})
|
|
258
|
+
|
|
259
|
+
|
|
251
260
|
def check_planned_cost(feature_dir: Path, plan_fm: dict, gates: list) -> None:
|
|
252
261
|
"""Emit WARN for missing planned_cost_usd on WUs and PLAN.md.
|
|
253
262
|
|
|
254
263
|
Sealed WUs (wu status=done AND plan status=done) are skipped silently —
|
|
255
264
|
backfilling cost estimates on history is pointless. Active or draft WUs
|
|
256
|
-
get the WARN.
|
|
265
|
+
get the WARN. Ceremony-type WUs (close/close-intermediate/plan-next)
|
|
266
|
+
with a planned cost below CEREMONY_COST_FLOOR_USD get a floor WARN
|
|
267
|
+
(#201). PLAN.md is compared against the sum of WU planned costs;
|
|
257
268
|
delta > 10% emits a separate WARN naming the delta. Never raises or
|
|
258
269
|
appends to an errors list — all findings are WARN-only (exit code 0).
|
|
259
270
|
"""
|
|
@@ -283,6 +294,21 @@ def check_planned_cost(feature_dir: Path, plan_fm: dict, gates: list) -> None:
|
|
|
283
294
|
)
|
|
284
295
|
if planned is not None:
|
|
285
296
|
wu_sum += float(planned)
|
|
297
|
+
# Floor applies to POSITIVE estimates only: the observed
|
|
298
|
+
# failure is a real-but-wishful $2-3 estimate anchoring
|
|
299
|
+
# calibration, not an explicit 0.00 (the scaffold's
|
|
300
|
+
# "unestimated" placeholder, already visible as such).
|
|
301
|
+
if (not is_sealed
|
|
302
|
+
and wfm.get("type") in _CEREMONY_TYPES
|
|
303
|
+
and 0 < float(planned) < CEREMONY_COST_FLOOR_USD):
|
|
304
|
+
print(
|
|
305
|
+
f"WARN: {wfile}: planned_cost_usd "
|
|
306
|
+
f"${float(planned):.2f} is below the "
|
|
307
|
+
f"${CEREMONY_COST_FLOOR_USD:.2f} floor for "
|
|
308
|
+
f"'{wfm.get('type')}' WUs. Ceremony WUs ran 2.8-5.2x "
|
|
309
|
+
f"over on $2-3 estimates (FEAT-2026-0049); raise the "
|
|
310
|
+
f"estimate rather than anchoring calibration on it."
|
|
311
|
+
)
|
|
286
312
|
|
|
287
313
|
wu_sum = round(wu_sum, 2)
|
|
288
314
|
|