specfuse-loop 0.3.3__tar.gz → 0.3.4__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.3/specfuse_loop.egg-info → specfuse_loop-0.3.4}/PKG-INFO +1 -1
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/pyproject.toml +1 -1
- specfuse_loop-0.3.4/specfuse/loop/data/VERSION +1 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/loop.py +27 -9
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_orchestration.py +2 -2
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_prepare_feature.py +39 -1
- specfuse_loop-0.3.3/specfuse/loop/data/VERSION +0 -1
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/LICENSE +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/NOTICE +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/README.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/setup.cfg +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/docs/methodology.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/gitignore.snippet +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/lint_plan.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/scaffold.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse_loop.egg-info/SOURCES.txt +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_backend.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_gate_eval_intermediate_wiring.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_gate_eval_terminal_wiring.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_init_integration.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_scaffold_data_in_sync.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_scaffold_docs.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_scaffold_init.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_scaffold_resources.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_scaffold_wiring.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/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.4"
|
|
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.4
|
|
@@ -62,7 +62,7 @@ SPECFUSE_DIR = Path(".specfuse")
|
|
|
62
62
|
REPO_ROOT = SPECFUSE_DIR.parent
|
|
63
63
|
FEATURES_DIR = SPECFUSE_DIR / "features"
|
|
64
64
|
VERIFICATION_PATH = SPECFUSE_DIR / "verification.yml"
|
|
65
|
-
DRIVER_VERSION = "0.3.
|
|
65
|
+
DRIVER_VERSION = "0.3.4"
|
|
66
66
|
# Oldest scaffold layout this driver can drive. init.sh stamps the scaffold's own
|
|
67
67
|
# version into `.specfuse/VERSION`; check_scaffold_version() fails loud at startup if
|
|
68
68
|
# the consumer's scaffold is older than this, pointing at `specfuse upgrade`. Bump
|
|
@@ -728,7 +728,8 @@ def _branch_prep_hint(feature_dir: "Path", feat_fm: dict, feature_id: str) -> st
|
|
|
728
728
|
lines = [
|
|
729
729
|
"",
|
|
730
730
|
"Easiest — let the loop create the branch and commit for you:",
|
|
731
|
-
" specfuse-loop --prepare",
|
|
731
|
+
" specfuse-loop --prepare # …then run",
|
|
732
|
+
" specfuse-loop --prepare-only # …then stop, so you can review first",
|
|
732
733
|
"",
|
|
733
734
|
"Or do it manually:",
|
|
734
735
|
]
|
|
@@ -926,9 +927,15 @@ def ensure_feature_branch(feat_fm: dict, feature_dir: "Path | None" = None) -> N
|
|
|
926
927
|
).returncode == 0
|
|
927
928
|
if not is_ancestor:
|
|
928
929
|
raise FeatureBranchError(
|
|
929
|
-
f"branch '{branch}' exists and
|
|
930
|
-
f"
|
|
931
|
-
f"
|
|
930
|
+
f"branch '{branch}' already exists and has diverged from your "
|
|
931
|
+
f"current branch '{current}' (it carries commits HEAD does not). "
|
|
932
|
+
f"Refusing to silently check it out — that could build on a stale "
|
|
933
|
+
f"base or lose work. Pick one, then re-run:\n"
|
|
934
|
+
f" - resume that branch's work: git checkout {branch}\n"
|
|
935
|
+
f" - rebase it onto here: git checkout {branch} && "
|
|
936
|
+
f"git rebase {current}\n"
|
|
937
|
+
f" - discard it (work not needed): git branch -D {branch} "
|
|
938
|
+
f"(the loop recreates it from HEAD)"
|
|
932
939
|
)
|
|
933
940
|
_checked_checkout(["checkout", branch], f"checkout of existing branch '{branch}'")
|
|
934
941
|
print(f"Switched to feature branch '{branch}' (was on '{current}').")
|
|
@@ -2974,6 +2981,7 @@ def run(
|
|
|
2974
2981
|
dry_run: bool,
|
|
2975
2982
|
force_full_close: str | None = None,
|
|
2976
2983
|
prepare: bool = False,
|
|
2984
|
+
prepare_only: bool = False,
|
|
2977
2985
|
) -> int:
|
|
2978
2986
|
# Fail-fast on a malformed verification.yml BEFORE we touch any WU state.
|
|
2979
2987
|
# The per-gate `verify()` call lazy-loads the same file; if it's malformed,
|
|
@@ -3027,10 +3035,16 @@ def run(
|
|
|
3027
3035
|
)
|
|
3028
3036
|
return 1
|
|
3029
3037
|
require_git_ready()
|
|
3030
|
-
# --prepare: create the feature branch + commit the
|
|
3031
|
-
# the guards below pass (the do-it-for-me path
|
|
3032
|
-
|
|
3038
|
+
# --prepare / --prepare-only: create the feature branch + commit the
|
|
3039
|
+
# folder up front so the guards below pass (the do-it-for-me path
|
|
3040
|
+
# draft-feature can't take). --prepare-only stops after, so you can
|
|
3041
|
+
# review the commit before the loop dispatches anything.
|
|
3042
|
+
if prepare or prepare_only:
|
|
3033
3043
|
prepare_feature(feat_fm, feature_dir, feature_id)
|
|
3044
|
+
if prepare_only:
|
|
3045
|
+
print("Prepared: feature is on its branch and committed. "
|
|
3046
|
+
"Re-run `specfuse-loop` to start the gate.")
|
|
3047
|
+
return 0
|
|
3034
3048
|
# Pre-flight guards — both run BEFORE ensure_feature_branch so the
|
|
3035
3049
|
# refusal happens before any branch mutation, and both protect against
|
|
3036
3050
|
# the per-attempt `git reset --hard` destroying uncommitted state:
|
|
@@ -4014,12 +4028,16 @@ def main() -> int:
|
|
|
4014
4028
|
"'branch:') and commit its folder, then run — the do-it-for-me "
|
|
4015
4029
|
"path after /draft-feature, which leaves the folder "
|
|
4016
4030
|
"uncommitted on the current branch.")
|
|
4031
|
+
ap.add_argument("--prepare-only", action="store_true",
|
|
4032
|
+
help="Like --prepare (create branch + commit the folder) but "
|
|
4033
|
+
"STOP afterwards without dispatching — review the commit, then "
|
|
4034
|
+
"re-run `specfuse-loop` to start.")
|
|
4017
4035
|
args = ap.parse_args()
|
|
4018
4036
|
if not FEATURES_DIR.exists():
|
|
4019
4037
|
sys.exit(f"No {FEATURES_DIR}. Run from your repo root.")
|
|
4020
4038
|
auto_sync(dry_run=args.dry_run, no_autosync=args.no_autosync)
|
|
4021
4039
|
return run(args.feature, args.dry_run, force_full_close=args.force_full_close,
|
|
4022
|
-
prepare=args.prepare)
|
|
4040
|
+
prepare=args.prepare, prepare_only=args.prepare_only)
|
|
4023
4041
|
|
|
4024
4042
|
|
|
4025
4043
|
if __name__ == "__main__":
|
|
@@ -706,7 +706,7 @@ class TestMainArgparse(unittest.TestCase):
|
|
|
706
706
|
with patch.object(sys, "argv", ["loop", "--feature", "my-feature"]):
|
|
707
707
|
result = loop.main()
|
|
708
708
|
self.assertEqual(result, 0)
|
|
709
|
-
mock_run.assert_called_once_with("my-feature", False, force_full_close=None, prepare=False)
|
|
709
|
+
mock_run.assert_called_once_with("my-feature", False, force_full_close=None, prepare=False, prepare_only=False)
|
|
710
710
|
|
|
711
711
|
def test_dry_run_flag_forwarded_to_run(self):
|
|
712
712
|
"""--dry-run is forwarded as dry_run=True to run()."""
|
|
@@ -718,7 +718,7 @@ class TestMainArgparse(unittest.TestCase):
|
|
|
718
718
|
with patch.object(loop, "run", return_value=0) as mock_run:
|
|
719
719
|
with patch.object(sys, "argv", ["loop", "--dry-run"]):
|
|
720
720
|
loop.main()
|
|
721
|
-
mock_run.assert_called_once_with(None, True, force_full_close=None, prepare=False)
|
|
721
|
+
mock_run.assert_called_once_with(None, True, force_full_close=None, prepare=False, prepare_only=False)
|
|
722
722
|
|
|
723
723
|
def test_multi_active_error_propagates_from_run(self):
|
|
724
724
|
"""main() with no args propagates the find_feature multi-active SystemExit."""
|
|
@@ -36,6 +36,13 @@ def _init_git(root: Path) -> None:
|
|
|
36
36
|
subprocess.run(["git", "-C", str(root), "config", "commit.gpgSign", "false"],
|
|
37
37
|
check=True)
|
|
38
38
|
(root / "README.md").write_text("# fixture\n")
|
|
39
|
+
specfuse = root / ".specfuse"
|
|
40
|
+
specfuse.mkdir()
|
|
41
|
+
(specfuse / "verification.yml").write_text(
|
|
42
|
+
"code:\n - name: noop\n command: \"true\"\n"
|
|
43
|
+
"doc:\n - name: noop\n command: \"true\"\n"
|
|
44
|
+
"plannext:\n - name: noop\n command: \"true\"\n"
|
|
45
|
+
)
|
|
39
46
|
subprocess.run(["git", "-C", str(root), "add", "."], check=True)
|
|
40
47
|
subprocess.run(["git", "-C", str(root), "commit", "-q", "-m", "init"], check=True)
|
|
41
48
|
|
|
@@ -46,7 +53,10 @@ def _write_uncommitted_feature(root: Path) -> Path:
|
|
|
46
53
|
fdir.mkdir(parents=True)
|
|
47
54
|
(fdir / "PLAN.md").write_text(
|
|
48
55
|
f"---\nfeature_id: {_FEAT_ID}\ntitle: T\nbranch: {_BRANCH}\n"
|
|
49
|
-
f"roadmap_goal: t\nstatus: active\n---\n\n# Plan\n"
|
|
56
|
+
f"roadmap_goal: t\nstatus: active\n---\n\n# Plan\n\n```yaml\n"
|
|
57
|
+
f"gates:\n - gate: 1\n file: GATE-01.md\n work_units:\n"
|
|
58
|
+
f" - id: {_FEAT_ID}/T01\n file: WU-01.md\n"
|
|
59
|
+
f" depends_on: []\n```\n"
|
|
50
60
|
)
|
|
51
61
|
(fdir / "GATE-01.md").write_text("---\ngate: 1\nstatus: open\n---\n\n# Gate 1\n")
|
|
52
62
|
(fdir / "WU-01.md").write_text(
|
|
@@ -85,6 +95,34 @@ class TestPrepareFeature(unittest.TestCase):
|
|
|
85
95
|
capture_output=True, text=True).stdout
|
|
86
96
|
self.assertIn("PLAN.md", tracked)
|
|
87
97
|
|
|
98
|
+
def test_prepare_only_prepares_then_stops(self):
|
|
99
|
+
"""--prepare-only branches + commits but does NOT dispatch (rc=0)."""
|
|
100
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
101
|
+
root = Path(tmp)
|
|
102
|
+
_init_git(root)
|
|
103
|
+
_write_uncommitted_feature(root)
|
|
104
|
+
os.chdir(root)
|
|
105
|
+
|
|
106
|
+
called = {"dispatch": False}
|
|
107
|
+
|
|
108
|
+
def _no_dispatch(*a, **k):
|
|
109
|
+
called["dispatch"] = True
|
|
110
|
+
raise AssertionError("must not dispatch under --prepare-only")
|
|
111
|
+
|
|
112
|
+
orig = loop.dispatch
|
|
113
|
+
loop.dispatch = _no_dispatch
|
|
114
|
+
try:
|
|
115
|
+
rc = loop.run(None, dry_run=False, prepare_only=True)
|
|
116
|
+
finally:
|
|
117
|
+
loop.dispatch = orig
|
|
118
|
+
|
|
119
|
+
self.assertEqual(rc, 0)
|
|
120
|
+
self.assertFalse(called["dispatch"])
|
|
121
|
+
self.assertEqual(loop._current_branch(), _BRANCH)
|
|
122
|
+
# folder committed (tree clean)
|
|
123
|
+
rel = Path(".specfuse/features") / f"{_FEAT_ID}-thing"
|
|
124
|
+
self.assertEqual(loop.untracked_feature_files(rel), [])
|
|
125
|
+
|
|
88
126
|
def test_committed_guard_message_suggests_prepare_and_branch(self):
|
|
89
127
|
"""On the default branch, the untracked-folder refusal names --prepare
|
|
90
128
|
AND the `git checkout -b <branch>` step."""
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.3.3
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/specfuse/loop/data/docs/concepts/ralph-lineage.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specfuse_loop-0.3.3 → specfuse_loop-0.3.4}/tests/test_lint_correlation_id_close_intermediate.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|