specfuse-loop 0.3.1__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {specfuse_loop-0.3.1/specfuse_loop.egg-info → specfuse_loop-0.3.2}/PKG-INFO +1 -1
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/pyproject.toml +1 -1
- specfuse_loop-0.3.2/specfuse/loop/data/VERSION +1 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/templates/GATE.template.md +0 -6
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/templates/PLAN.template.md +0 -6
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/templates/WU.template.md +3 -6
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/loop.py +15 -1
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse_loop.egg-info/SOURCES.txt +2 -1
- specfuse_loop-0.3.2/tests/test_wu_execution_metadata.py +115 -0
- specfuse_loop-0.3.1/specfuse/loop/data/VERSION +0 -1
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/LICENSE +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/NOTICE +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/README.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/setup.cfg +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/docs/methodology.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/gitignore.snippet +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/lint_plan.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/scaffold.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_backend.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_gate_eval_intermediate_wiring.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_gate_eval_terminal_wiring.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_init_integration.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_scaffold_data_in_sync.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_scaffold_docs.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_scaffold_init.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_scaffold_resources.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_scaffold_wiring.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/tests/test_version_skew.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.2"
|
|
8
8
|
description = "Local-first executor for the Specfuse Plan + Work Unit gate-cycle methodology."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.3.2
|
|
@@ -14,12 +14,6 @@ status: open # open | awaiting_review | passed
|
|
|
14
14
|
# set it in the successor gate to exercise the brake for the first time.
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
<!--
|
|
18
|
-
Copyright 2026 Specfuse Contributors
|
|
19
|
-
Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
20
|
-
-->
|
|
21
|
-
|
|
22
|
-
|
|
23
17
|
# Gate 1 — <name of the milestone this gate proves>
|
|
24
18
|
|
|
25
19
|
## Definition of done
|
|
@@ -11,12 +11,6 @@ status: active # active | done | abandoned
|
|
|
11
11
|
# planned_cost_usd: 0.00 # OPTIONAL — sum of WU planned costs; lint warns when missing or when delta from WU sum > 10%
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
<!--
|
|
15
|
-
Copyright 2026 Specfuse Contributors
|
|
16
|
-
Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
17
|
-
-->
|
|
18
|
-
|
|
19
|
-
|
|
20
14
|
# Plan: <short feature title>
|
|
21
15
|
|
|
22
16
|
<One or two paragraphs of human-facing intent. The work units carry the executable
|
|
@@ -10,12 +10,6 @@ generated_surfaces: [] # OPTIONAL — paths to generated files this unit's a
|
|
|
10
10
|
# oracle_env: macos_local # OPTIONAL — environment where this WU's verifying oracle runs; see frontmatter notes
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
<!--
|
|
14
|
-
Copyright 2026 Specfuse Contributors
|
|
15
|
-
Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
16
|
-
-->
|
|
17
|
-
|
|
18
|
-
|
|
19
13
|
<!--
|
|
20
14
|
Frontmatter notes (single-repo):
|
|
21
15
|
|
|
@@ -48,6 +42,9 @@ DRIVER-OWNED FIELDS — the driver writes these at outcome time; authors leave t
|
|
|
48
42
|
<!-- driver-owned: attempts, cost_usd, input_tokens, output_tokens, duration_seconds,
|
|
49
43
|
cumulative_cost_usd, cumulative_duration_seconds, cumulative_input_tokens,
|
|
50
44
|
cumulative_output_tokens, re_arm_count, re_arm_history -->
|
|
45
|
+
<!-- driver-stamped at dispatch (resolved execution metadata, visible in this .md):
|
|
46
|
+
model, effort (override or type default), gate_set (the verification.yml set
|
|
47
|
+
that is this WU's exit oracle), driver_version, started_at (UTC ISO). -->
|
|
51
48
|
<!-- Full field semantics in docs/methodology.md §2 and events.jsonl outcome payloads. -->
|
|
52
49
|
|
|
53
50
|
Dependencies live in PLAN.md's `gates[].work_units[].depends_on` graph, not
|
|
@@ -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.2"
|
|
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
|
|
@@ -3156,6 +3156,20 @@ def run(
|
|
|
3156
3156
|
if _is_rearm:
|
|
3157
3157
|
fold_cumulative_on_rearm(wu, backend)
|
|
3158
3158
|
backend.set_wu(wu, "status", "in_progress")
|
|
3159
|
+
# Stamp the resolved execution metadata into the WU frontmatter so
|
|
3160
|
+
# it is visible when you read the .md (not only on the console /
|
|
3161
|
+
# in events.jsonl): the model + effort it runs with (override or
|
|
3162
|
+
# type default), which verification gate set is its exit oracle,
|
|
3163
|
+
# the driver version that executed it, and when it was dispatched.
|
|
3164
|
+
# Written after head_before so they ride the WU's squash commit;
|
|
3165
|
+
# idempotent on retries/re-arms (same values overwrite cleanly,
|
|
3166
|
+
# started_at refreshes to the latest dispatch).
|
|
3167
|
+
backend.set_wu(wu, "model", wu.model)
|
|
3168
|
+
backend.set_wu(wu, "effort", wu.effort)
|
|
3169
|
+
backend.set_wu(wu, "gate_set", GATES_FOR_TYPE.get(wu.type, "code"))
|
|
3170
|
+
backend.set_wu(wu, "driver_version", DRIVER_VERSION)
|
|
3171
|
+
backend.set_wu(wu, "started_at",
|
|
3172
|
+
dt.datetime.now(dt.timezone.utc).isoformat())
|
|
3159
3173
|
# Events and per-attempt notes are buffered in memory during the
|
|
3160
3174
|
# WU's lifecycle and flushed at outcome time. This prevents the
|
|
3161
3175
|
# `git reset --hard` between failed attempts from silently
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2026 Specfuse contributors
|
|
3
|
+
# Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
4
|
+
#
|
|
5
|
+
"""The driver stamps resolved execution metadata into a WU's frontmatter.
|
|
6
|
+
|
|
7
|
+
So the planned/effective model, effort, exit-oracle gate set, driver version,
|
|
8
|
+
and dispatch time are visible when you read the WU `.md` — not only on the
|
|
9
|
+
console or in events.jsonl. Written at dispatch (after status -> in_progress),
|
|
10
|
+
so they ride the WU's squash commit.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
import subprocess
|
|
17
|
+
import unittest
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
from tests._loop_loader import load_loop
|
|
21
|
+
from tests.test_driver_integration import (
|
|
22
|
+
integration_workspace,
|
|
23
|
+
write_minimal_feature,
|
|
24
|
+
_read_frontmatter,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
loop = load_loop()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _write_verification_yml(root: Path) -> None:
|
|
31
|
+
(root / ".specfuse/verification.yml").write_text(
|
|
32
|
+
"code:\n - name: noop\n command: \"true\"\n"
|
|
33
|
+
"doc:\n - name: noop\n command: \"true\"\n"
|
|
34
|
+
"plannext:\n - name: noop\n command: \"true\"\n"
|
|
35
|
+
)
|
|
36
|
+
subprocess.run(["git", "-C", str(root), "add", ".specfuse/verification.yml"],
|
|
37
|
+
check=True)
|
|
38
|
+
subprocess.run(["git", "-C", str(root), "commit", "-q", "-m", "verif"], check=True)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TestWUExecutionMetadata(unittest.TestCase):
|
|
42
|
+
|
|
43
|
+
def setUp(self):
|
|
44
|
+
self._cwd = os.getcwd()
|
|
45
|
+
self._patches: list[tuple[str, object]] = []
|
|
46
|
+
|
|
47
|
+
def tearDown(self):
|
|
48
|
+
os.chdir(self._cwd)
|
|
49
|
+
for name, original in self._patches:
|
|
50
|
+
setattr(loop, name, original)
|
|
51
|
+
|
|
52
|
+
def _patch(self, name: str, replacement) -> None:
|
|
53
|
+
self._patches.append((name, getattr(loop, name)))
|
|
54
|
+
setattr(loop, name, replacement)
|
|
55
|
+
|
|
56
|
+
def test_dispatch_stamps_model_effort_gateset_driver_started(self):
|
|
57
|
+
with integration_workspace() as root:
|
|
58
|
+
os.chdir(root)
|
|
59
|
+
_write_verification_yml(root)
|
|
60
|
+
write_minimal_feature(
|
|
61
|
+
root, "FEAT-2026-9301", "exec-meta", "feat/exec-meta",
|
|
62
|
+
[("FEAT-2026-9301/T01", "implementation", "pending")],
|
|
63
|
+
)
|
|
64
|
+
self._patch("dispatch", lambda wu, fn, cost_tracking=True: ("(stub)\n", None))
|
|
65
|
+
self._patch("verify", lambda wu, fd, cfg=None: (True, "(stub)"))
|
|
66
|
+
|
|
67
|
+
rc = loop.run(None, dry_run=False)
|
|
68
|
+
self.assertEqual(rc, 0)
|
|
69
|
+
|
|
70
|
+
fdir = root / ".specfuse/features/FEAT-2026-9301-exec-meta"
|
|
71
|
+
fm = _read_frontmatter(fdir / "WU-T01.md")
|
|
72
|
+
|
|
73
|
+
# model = the WU's resolved model (the integration helper authors a
|
|
74
|
+
# concrete haiku id); effort defaults to EFFORT_BY_TYPE[implementation]
|
|
75
|
+
# = medium (helper sets no effort); gate_set = GATES_FOR_TYPE = code.
|
|
76
|
+
self.assertEqual(fm.get("model"), "claude-haiku-4-5-20251001")
|
|
77
|
+
self.assertEqual(fm.get("effort"), "medium")
|
|
78
|
+
self.assertEqual(fm.get("gate_set"), "code")
|
|
79
|
+
self.assertEqual(fm.get("driver_version"), loop.DRIVER_VERSION)
|
|
80
|
+
# started_at is a UTC ISO timestamp.
|
|
81
|
+
self.assertIn("started_at", fm)
|
|
82
|
+
self.assertRegex(fm["started_at"], r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}")
|
|
83
|
+
|
|
84
|
+
def test_author_model_effort_override_is_preserved(self):
|
|
85
|
+
"""An explicit model/effort override survives the stamp (same value)."""
|
|
86
|
+
with integration_workspace() as root:
|
|
87
|
+
os.chdir(root)
|
|
88
|
+
_write_verification_yml(root)
|
|
89
|
+
write_minimal_feature(
|
|
90
|
+
root, "FEAT-2026-9302", "exec-ovr", "feat/exec-ovr",
|
|
91
|
+
[("FEAT-2026-9302/T01", "implementation", "pending")],
|
|
92
|
+
)
|
|
93
|
+
# Override the helper's default model with an alias + set effort.
|
|
94
|
+
wu_path = root / ".specfuse/features/FEAT-2026-9302-exec-ovr/WU-T01.md"
|
|
95
|
+
text = wu_path.read_text()
|
|
96
|
+
text = text.replace("model: claude-haiku-4-5-20251001\n",
|
|
97
|
+
"model: opus\neffort: high\n", 1)
|
|
98
|
+
wu_path.write_text(text)
|
|
99
|
+
subprocess.run(["git", "-C", str(root), "add", "."], check=True)
|
|
100
|
+
subprocess.run(["git", "-C", str(root), "commit", "-q", "-m", "override"],
|
|
101
|
+
check=True)
|
|
102
|
+
|
|
103
|
+
self._patch("dispatch", lambda wu, fn, cost_tracking=True: ("(stub)\n", None))
|
|
104
|
+
self._patch("verify", lambda wu, fd, cfg=None: (True, "(stub)"))
|
|
105
|
+
rc = loop.run(None, dry_run=False)
|
|
106
|
+
self.assertEqual(rc, 0)
|
|
107
|
+
|
|
108
|
+
fm = _read_frontmatter(
|
|
109
|
+
root / ".specfuse/features/FEAT-2026-9302-exec-ovr/WU-T01.md")
|
|
110
|
+
self.assertEqual(fm.get("model"), "opus")
|
|
111
|
+
self.assertEqual(fm.get("effort"), "high")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
if __name__ == "__main__":
|
|
115
|
+
unittest.main()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.3.1
|
|
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.1 → specfuse_loop-0.3.2}/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
|
{specfuse_loop-0.3.1 → specfuse_loop-0.3.2}/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
|