Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude/skills/compass-manifest-maintenance/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ test -f CLAUDE.md && echo "✓ repo root" || echo "✗ wrong directory"
1. **Resolve** `<pack>` and `<skill-name>` — confirm `<pack>/skills/<skill-name>/SKILL.md` exists.

2. **Read golden sources** (precedence):
- `<pack>/<pack>-plugin.yaml` — `spec.lifecycle` (default for new skill manifest; see [relationship-rules.md](references/relationship-rules.md) Lifecycle)
- `<pack>/<pack>-plugin.yaml` — `spec.lifecycle` (default for new skill manifest; see [LIFECYCLE.md](../../../LIFECYCLE.md) and [relationship-rules.md](references/relationship-rules.md) Lifecycle)
- `SKILL.md` frontmatter: `name`, `description`, `allowed-tools`
- `SKILL.md` body: `Required MCP Servers`, `/skill-name` invocations, Dependencies, validator prerequisites
- `<pack>/mcps.json` — server keys (map via [mcp-mapping.md](references/mcp-mapping.md))
Expand All @@ -77,7 +77,7 @@ test -f CLAUDE.md && echo "✓ repo root" || echo "✗ wrong directory"
4. **Set `spec.lifecycle`** (do not hardcode `beta`):
- Read `spec.lifecycle` from `<pack>/<pack>-plugin.yaml` — use as the **default** for the skill.
- **Human in the loop:** ask whether to change it. The skill may match the plugin or use a **less mature** value only (e.g. plugin `beta` → skill `development` is OK; plugin `development` → skill `beta` is **not** allowed).
- See [relationship-rules.md](references/relationship-rules.md) Lifecycle.
- Allowed values: `development`, `beta`, `GA`, `deprecated`, `archived` — see [LIFECYCLE.md](../../../LIFECYCLE.md).

5. **Write** `<pack>/skills/<skill-name>/catalog-info.yaml` from [assets/skill-catalog-info.yaml](assets/skill-catalog-info.yaml):
- `namespace: ai5-marketplace`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ All skills, plugins, and owned MCPs use `metadata.namespace: ai5-marketplace`.

## Lifecycle (`spec.lifecycle`)

- **New skill:** copy `spec.lifecycle` from `<pack>/<pack>-plugin.yaml` (ask before changing). Skill must not exceed plugin maturity (`development` < `beta` < `production`).
Canonical model: [LIFECYCLE.md](../../../../LIFECYCLE.md) at the repository root.

Allowed values: `development`, `beta`, `GA`, `deprecated`, `archived`.

- **New skill:** copy `spec.lifecycle` from `<pack>/<pack>-plugin.yaml` (ask before changing). Skill must not exceed plugin maturity (`development` < `beta` < `GA`).
- **New pack:** default plugin to `development`.
- **Retirement:** use `deprecated` then `archived` when sunsetting a component (see LIFECYCLE.md).
- **Distribution:** only `beta` and `GA` with `distribution: external` are published externally; `development`, `deprecated`, and `archived` are always internal-only.

## Files to touch when adding a skill

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ All entities (skills, plugins, and MCP servers) share a single namespace: `ai5-m

#### Adding Compass Manifests for a New Skill

When adding a skill, create `skills/<skill-name>/catalog-info.yaml`. Set `spec.lifecycle` from the pack plugin (`<pack>/<pack>-plugin.yaml`); default to the plugin value and ask the user before changing it — a skill may match the plugin or use a **less mature** lifecycle only (never above the plugin). New packs default the plugin to `development`.
When adding a skill, create `skills/<skill-name>/catalog-info.yaml`. Set `spec.lifecycle` from the pack plugin (`<pack>/<pack>-plugin.yaml`); default to the plugin value and ask the user before changing it — a skill may match the plugin or use a **less mature** lifecycle only (never above the plugin). New packs default the plugin to `development`. Valid values and publication rules: [LIFECYCLE.md](LIFECYCLE.md) (`development` < `beta` < `GA`; plus `deprecated` / `archived`).

```yaml
apiVersion: backstage.io/v1alpha1
Expand Down
91 changes: 91 additions & 0 deletions LIFECYCLE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Component Lifecycle Model

Canonical reference for `spec.lifecycle` on skills, pack plugins, and MCP servers in this repository.

Compass manifests (`catalog-info.yaml`, `*-plugin.yaml`, `mcps/*.yaml`) declare lifecycle so owners, reviewers, and the catalog publication pipeline share one maturity vocabulary.

## Valid lifecycle values

| Value | Meaning |
|-------|---------|
| `development` | In active development, not ready for consumption |
| `beta` | Functional and available for early adoption; may have rough edges |
| `GA` | Generally Available — production-ready, fully supported |
| `deprecated` | Still functional but no longer maintained — consumers should migrate |
| `archived` | End of life — preserved for reference only, not maintained |

These are the **only** allowed values for `spec.lifecycle`. CI rejects any other string (including the former name `production`; use `GA` instead).

### Maturity ordering (ceiling)

For the skill-vs-pack ceiling rule, maturity ranks as:

```text
development < beta < GA
```

`deprecated` and `archived` are retirement states. They are not ranked against the ceiling (see [Ceiling rule](#ceiling-rule)).

## How owners change lifecycle

1. Edit `spec.lifecycle` in the component’s Compass manifest:
- Skill: `<pack>/skills/<skill-name>/catalog-info.yaml`
- Pack plugin: `<pack>/<pack>-plugin.yaml`
- MCP server: `mcps/<server-name>.yaml`
2. Open a pull request with the change.
3. Reviewers approve the transition; merge is the governance gate.

There is **no enforced state machine**. Owners may move between any allowed values (for example `development` → `GA`) when the PR is justified. Prefer gradual promotion (`development` → `beta` → `GA`) and explicit retirement (`GA`/`beta` → `deprecated` → `archived`) when that matches product reality.

### Defaults when authoring

- **New pack plugin:** default `development` (confirm before raising maturity).
- **New skill:** copy `spec.lifecycle` from the parent pack plugin. The skill may match the plugin or use a **less mature** value only — never above the plugin (see ceiling rule).

## Ceiling rule

A skill’s lifecycle must not exceed its parent pack plugin’s lifecycle:

```text
skill lifecycle ≤ pack lifecycle
```

Examples:

| Pack | Skill | Result |
|------|-------|--------|
| `beta` | `beta` | Allowed |
| `GA` | `development` | Allowed |
| `development` | `beta` | **Rejected** |
| `beta` | `GA` | **Rejected** |

Retirement exemptions: if the skill or the pack plugin is `deprecated` or `archived`, the ceiling comparison is skipped for that entity (deprecated/archived plugins skip enforcement for all of their skills).

Enforced by `scripts/validate_lifecycle_ceiling.py` (`make validate-lifecycle-ceiling`, included in `make validate-structure`).

## Interaction with `distribution`

Manifests may set `metadata.labels.distribution` (commonly `external`). Lifecycle and distribution together control external publication:

| Lifecycle | `distribution: external` | External publication |
|-----------|--------------------------|----------------------|
| `development` | any | **Never** — always internal-only |
| `beta` | `external` | Eligible for external publish |
| `GA` | `external` | Eligible for external publish |
| `deprecated` | any | **Never** — always internal-only |
| `archived` | any | **Never** — always internal-only |

Notes:

- Only `beta` and `GA` components with `distribution: external` are published externally.
- `deprecated` and `archived` are always internal-only, regardless of the distribution label.
- `development` is always internal-only. CI emits a **non-blocking warning** when `distribution: external` is paired with `lifecycle: development`, because the label will not cause external publication.

## Catalog publication pipeline

The catalog build/publication pipeline (APPENG-6026) uses lifecycle (and distribution) when routing components for internal vs external publication. Lifecycle awareness for that pipeline is tracked under APPENG-6026; this repository’s role is to keep `spec.lifecycle` accurate and CI-valid so the pipeline can trust the field.

## Related documentation

- Compass relationship and authoring rules: [`.claude/skills/compass-manifest-maintenance/references/relationship-rules.md`](.claude/skills/compass-manifest-maintenance/references/relationship-rules.md)
- Validator: [`scripts/validate_lifecycle_ceiling.py`](scripts/validate_lifecycle_ceiling.py)
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ help:
@echo " validate-collection-schema - Schema + roster + banners (subset of compliance)"
@echo " validate-collection-compliance - Full .catalog compliance (includes collection.json drift)"
@echo " validate-compass-manifests - Compass manifests, roster, refs, and skill references/ layout"
@echo " validate-lifecycle-ceiling - Compass lifecycle ceiling (skill <= plugin lifecycle) + unit tests"
@echo " validate-lifecycle-ceiling - Compass lifecycle (allowed values, ceiling, warnings) + unit tests"
@echo " validate-skill-design - Validate all skills (use PACK=rh-sre for a specific pack)"
@echo " validate-skill-design-changed - Validate only changed skills (staged + unstaged, for local dev)"
@echo " validate-mcp-tools - Validate allowed-tools against live MCP servers (requires podman)"
Expand Down Expand Up @@ -63,9 +63,9 @@ validate: check-uv
uv run python scripts/validate_collection_compliance.py || EXIT=1; \
echo "=== Validating Compass manifests..."; \
uv run python scripts/validate_compass_manifests.py || EXIT=1; \
echo "=== Validating Compass lifecycle ceiling (skill <= plugin lifecycle)..."; \
echo "=== Validating Compass lifecycle (allowed values + ceiling)..."; \
uv run python scripts/validate_lifecycle_ceiling.py || EXIT=1; \
echo "=== Running lifecycle ceiling unit tests..."; \
echo "=== Running lifecycle unit tests..."; \
uv run python scripts/test_validate_lifecycle_ceiling.py || EXIT=1; \
echo "=== Validating MCP tool references (skips gracefully without podman)..."; \
uv run python scripts/validate_mcp_tools.py --summary-only --log-file .validate/mcp-tools.log || EXIT=1; \
Expand Down Expand Up @@ -93,9 +93,9 @@ validate-structure: check-uv
uv run python scripts/validate_collection_compliance.py || EXIT=1; \
echo "=== Validating Compass manifests..."; \
uv run python scripts/validate_compass_manifests.py || EXIT=1; \
echo "=== Validating Compass lifecycle ceiling (skill <= plugin lifecycle)..."; \
echo "=== Validating Compass lifecycle (allowed values + ceiling)..."; \
uv run python scripts/validate_lifecycle_ceiling.py || EXIT=1; \
echo "=== Running lifecycle ceiling unit tests..."; \
echo "=== Running lifecycle unit tests..."; \
uv run python scripts/test_validate_lifecycle_ceiling.py || EXIT=1; \
echo "=== Validating MCP tool references (skips gracefully without podman)..."; \
uv run python scripts/validate_mcp_tools.py --summary-only --log-file .validate/mcp-tools.log || EXIT=1; \
Expand Down
Loading
Loading