From d99b4bb2cfff3ebd09d10fc9c4e59d4ab7576bf3 Mon Sep 17 00:00:00 2001 From: jawwad-ali Date: Fri, 31 Jul 2026 11:37:23 +0500 Subject: [PATCH] fix(bundler): resolve built-in step types when checking bundle references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_resolved_locally` gives three of the four component kinds a "is it bundled with Spec Kit?" check before the installed-in-project one: presets -> _locate_bundled_preset or PresetManager.get_pack extensions -> _locate_bundled_extension or ExtensionManager...is_installed workflows -> _locate_bundled_workflow or WorkflowRegistry.is_installed steps -> StepRegistry.is_installed <-- no bundled check `StepRegistry` tracks *community* step types installed under `.specify/workflows/steps/`. Spec Kit ships 11 step types as built-ins registered in `STEP_REGISTRY`, so every one of them looked unresolved: steps/shell -> False steps/gate -> False steps/command -> False steps/if -> False A bundle declaring a dependency on any built-in step type was therefore reported as an unresolved reference — an error online, a warning offline. There is no `_locate_bundled_step` to mirror, because step types are not an on-disk asset directory; `STEP_REGISTRY` is the equivalent check, and is what `specify workflow step info` reports as "built-in". Co-Authored-By: Claude Opus 5 (1M context) --- .../bundler/services/references.py | 9 ++++++ tests/unit/test_bundler_references.py | 32 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/specify_cli/bundler/services/references.py b/src/specify_cli/bundler/services/references.py index 3dd0f3d010..ca31e1e352 100644 --- a/src/specify_cli/bundler/services/references.py +++ b/src/specify_cli/bundler/services/references.py @@ -40,8 +40,17 @@ def _resolved_locally(root: Path, component: ComponentRef) -> bool: return True return WorkflowRegistry(root).is_installed(component.id) if kind == "steps": + from ...workflows import STEP_REGISTRY from ...workflows.catalog import StepRegistry + # Step types ship with Spec Kit as built-ins (shell, gate, if, ...) + # rather than as an on-disk asset directory, so there is no + # ``_locate_bundled_step`` to mirror the three lookups above. + # ``STEP_REGISTRY`` is the bundled-with-Spec-Kit check for this kind + # -- it is what ``specify workflow step info`` reports as + # "built-in". Without it every built-in step type looked unresolved. + if component.id in STEP_REGISTRY: + return True return StepRegistry(root).is_installed(component.id) except Exception: # noqa: BLE001 - resolution is best-effort return False diff --git a/tests/unit/test_bundler_references.py b/tests/unit/test_bundler_references.py index 1291ba08bd..111779e9ad 100644 --- a/tests/unit/test_bundler_references.py +++ b/tests/unit/test_bundler_references.py @@ -24,6 +24,38 @@ def test_bundled_extension_resolves(tmp_path: Path): assert warnings == [] +def test_builtin_step_type_resolves(tmp_path: Path): + """A built-in step type must resolve, like a bundled extension. + + Spec Kit ships 11 step types as built-ins registered in ``STEP_REGISTRY`` + rather than as on-disk asset directories, so there is no + ``_locate_bundled_step``. The ``steps`` branch of ``_resolved_locally`` only + asked ``StepRegistry(root).is_installed()``, which tracks *community* step + types installed under ``.specify/workflows/steps/`` — so every built-in step + type was reported as an unresolved reference. + """ + from specify_cli.workflows import STEP_REGISTRY + + root = make_project(tmp_path) + warnings: list[str] = [] + check = make_reference_checker(root, allow_network=True, warnings=warnings) + + for step_id in ("shell", "gate", "command", "if"): + assert step_id in STEP_REGISTRY, step_id + assert check(_ref("steps", step_id)) is None, step_id + assert warnings == [] + + +def test_unknown_step_type_still_errors_online(tmp_path: Path): + """The guard must not make every step id resolve.""" + root = make_project(tmp_path) + warnings: list[str] = [] + check = make_reference_checker(root, allow_network=True, warnings=warnings) + problem = check(_ref("steps", "no-such-step-type")) + assert problem is not None + assert "no-such-step-type" in problem + + def test_unknown_reference_errors_online(tmp_path: Path): root = make_project(tmp_path) warnings: list[str] = []