Skip to content

patch: Graceful error when an unsupported project type has no manifest #50

@rdwj

Description

@rdwj

Follow-up to #48. After the manifest loader lands, the failure mode for projects scaffolded from a template that does not (yet) ship a `.fips-template.yaml` is a Python traceback rather than a clean error.

Reproduction

After #48 merges, scaffold a gateway / ui / sandbox project with a CLI version whose template repo ships no manifest yet:

```
$ fips-agents patch check
...
ValueError: Patching is not supported for project type 'gateway'.
Supported types: mcp-server, agent, workflow.
```

Same outcome if the manifest is present but missing required fields — `_resolve_categories` warns about the malformed manifest and then falls through to `get_categories_for_type(project_type)`, which raises for any type outside the built-in three.

Why this happens

`tools/patching.py:_resolve_categories`:

```python
def _resolve_categories(template_root, template_info):
manifest = _load_template_manifest(template_root)
if manifest is not None:
resolved = _categories_from_manifest(manifest)
if resolved is not None:
return resolved
console.print("[yellow]⚠[/yellow] manifest malformed; falling back...")
project_type = get_project_type(template_info)
return get_categories_for_type(project_type) # raises for gateway / ui / sandbox
```

The `ValueError` from the constants lookup bubbles up through `check_for_updates` and `patch_category` as an unhandled exception.

Severity

Pre-existing behavior is the same (the old code raised the same `ValueError` from `patch_category` even before manifest support), so #48 doesn't regress anyone. But once manifests start landing in the template repos, the user expectation shifts — "my gateway project should support patch." A user who upgrades the CLI before the gateway-template manifest merge ships will hit this.

Proposed fix

Define a sentinel exception in `tools/patching.py`:

```python
class PatchUnsupportedForProjectType(Exception):
"""Raised when patch is invoked on a project whose template type
has no built-in category set and ships no usable .fips-template.yaml."""
```

Wrap the fallback in `_resolve_categories`:

```python
project_type = get_project_type(template_info)
try:
return get_categories_for_type(project_type)
except ValueError as e:
raise PatchUnsupportedForProjectType(
f"This template type ({project_type}) requires a .fips-template.yaml "
f"manifest to support patching, and none was found in the cloned "
f"template. See https://github.com/fips-agents/fips-agents-cli/issues/45.\"
) from e
```

Catch in the two call sites:

  • `check_for_updates` returns `{}` after printing a clean error so `patch check` exits 0 with "no patchable categories" rather than a traceback.
  • `patch_category` returns `(False, str(exc))` so the existing red ✗ rendering kicks in.

Test plan

  • Unit test: `_resolve_categories` raises `PatchUnsupportedForProjectType` for project_type `gateway` when no manifest is present.
  • Unit test: same when manifest is present but malformed (i.e., `_categories_from_manifest` returns `None`).
  • Integration test: `patch check` against a fake gateway project (no manifest) exits 0 with a friendly message, no traceback.
  • Integration test: `patch chart` against the same project exits 1 with the friendly message.

Out of scope

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions