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] = []