Skip to content

Rectify: Recipe-Level Feature Gate Enablement — Unified Visibility Axis#1859

Merged
Trecek merged 4 commits into
developfrom
planner-skills-suppressed-by-feature-gate-in-headless-l1-ses/1852
May 5, 2026
Merged

Rectify: Recipe-Level Feature Gate Enablement — Unified Visibility Axis#1859
Trecek merged 4 commits into
developfrom
planner-skills-suppressed-by-feature-gate-in-headless-l1-ses/1852

Conversation

@Trecek
Copy link
Copy Markdown
Collaborator

@Trecek Trecek commented May 5, 2026

Summary

The skill visibility system has two independent, uncoordinated gating axes: the pack gate (via PACK_REGISTRY + _resolve_effective_disabled) and the feature gate (via FEATURE_REGISTRY + _is_skill_disabled). The pack gate has a recipe-level override mechanism (requires_packs on Recipe flows through ToolContext.active_recipe_packs into _resolve_effective_disabled where it subtracts from the disabled set). The feature gate has no equivalent — session_features is derived exclusively from config.features, and recipes cannot declare feature requirements.

This asymmetry means any FEATURE_REGISTRY entry with default_enabled=False and non-empty skill_categories silently suppresses its skills in all headless L1 sessions, even when the active recipe dispatches those skills. The planner is the first (and currently only) feature hitting this gap, but the architecture makes it inevitable for any future feature following the same pattern.

The fix adds a requires_features field to the Recipe schema, flows it through the same path as requires_packs, and merges it into session_features in init_session. A new static validation rule catches recipes that reference feature-gated skills without declaring the corresponding feature requirement. An architecture test enforces the invariant that every feature with non-empty skill_categories has a tested recipe-level enablement path.

Closes #1852

Implementation Plan

Plan file: /home/talon/projects/autoskillit-runs/remediation-20260504-163437-353221/.autoskillit/temp/rectify/rectify_planner_feature_gate_immunity_2026-05-04_163437.md

🤖 Generated with Claude Code via AutoSkillit

Trecek added 3 commits May 4, 2026 17:16
Fixes the asymmetry between pack gate (which has recipe-level override)
and feature gate (which had no recipe-level input). Recipes can now declare
requires_features: [planner] to ensure their skill-category features are
enabled in init_session, matching the pattern used by requires_packs.

Key changes:
- Recipe schema: adds requires_features: list[str] field with FEATURE_REGISTRY validation
- recipe/io.py: parses requires_features from YAML
- recipe/_recipe_composition.py: union-merges requires_features from sub-recipes
- recipe/_api.py: propagates requires_features into LoadRecipeResult
- pipeline/context.py: adds active_recipe_features: frozenset[str] | None
- tools_kitchen.py: sets/clears active_recipe_features on open/close
- tools_execution.py: passes recipe_features to init_session
- server/_lifespan.py: initializes active_recipe_features = frozenset()
- workspace/session_skills.py: accepts recipe_features param, merges into
  session_features when feat not already set by user config (explicit user
  config wins over recipe-level defaults)
- disabled_feature_tags now uses session_features instead of config.features
  for consistency with the merged state
- recipe/rules/rules_features.py: adds undeclared-feature-requirement rule
  that flags recipes using feature-gated skills without requires_features
- recipes/planner.yaml: adds requires_features: [planner]
- Tests: 7 new tests in test_session_skills_features.py, 3 in test_schema.py,
  2 in test_rules_features.py, 1 arch invariant in test_feature_registry.py
Both LoadRecipeResult and OpenKitchenResult now include requires_features,
so both _FMT_LOAD_RECIPE_SUPPRESSED and _FMT_OPEN_KITCHEN_SUPPRESSED must
declare it to satisfy the field coverage contract tests.
Copy link
Copy Markdown
Collaborator Author

@Trecek Trecek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoSkillit PR Review — Verdict: approved_with_comments

Comment thread src/autoskillit/recipe/io.py Outdated
Comment thread src/autoskillit/recipe/io.py
Copy link
Copy Markdown
Collaborator Author

@Trecek Trecek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoSkillit review: warning-only findings detected. See inline comments — no blocking changes required.

…idation for requires_features

- Replace `or []` coercion with `_rf_val if _rf_val is not None else []` so
  falsy non-None values (False, 0, "") raise via the isinstance guard instead
  of silently becoming []
- Add element-level check: non-string entries in requires_features_raw now
  raise ValueError before reaching Recipe.__post_init__

Addresses review comments 3185460963 and 3185460965.
@Trecek Trecek added this pull request to the merge queue May 5, 2026
Merged via the queue into develop with commit f3ef62b May 5, 2026
2 checks passed
@Trecek Trecek deleted the planner-skills-suppressed-by-feature-gate-in-headless-l1-ses/1852 branch May 5, 2026 01:36
Trecek added a commit that referenced this pull request May 8, 2026
…is (#1859)

## Summary

The skill visibility system has two independent, uncoordinated gating
axes: the **pack gate** (via `PACK_REGISTRY` +
`_resolve_effective_disabled`) and the **feature gate** (via
`FEATURE_REGISTRY` + `_is_skill_disabled`). The pack gate has a
recipe-level override mechanism (`requires_packs` on `Recipe` flows
through `ToolContext.active_recipe_packs` into
`_resolve_effective_disabled` where it subtracts from the disabled set).
The feature gate has no equivalent — `session_features` is derived
exclusively from `config.features`, and recipes cannot declare feature
requirements.

This asymmetry means any `FEATURE_REGISTRY` entry with
`default_enabled=False` and non-empty `skill_categories` silently
suppresses its skills in all headless L1 sessions, even when the active
recipe dispatches those skills. The planner is the first (and currently
only) feature hitting this gap, but the architecture makes it inevitable
for any future feature following the same pattern.

The fix adds a `requires_features` field to the `Recipe` schema, flows
it through the same path as `requires_packs`, and merges it into
`session_features` in `init_session`. A new static validation rule
catches recipes that reference feature-gated skills without declaring
the corresponding feature requirement. An architecture test enforces the
invariant that every feature with non-empty `skill_categories` has a
tested recipe-level enablement path.

Closes #1852

## Implementation Plan

Plan file:
`/home/talon/projects/autoskillit-runs/remediation-20260504-163437-353221/.autoskillit/temp/rectify/rectify_planner_feature_gate_immunity_2026-05-04_163437.md`

🤖 Generated with [Claude Code](https://claude.com/claude-code) via
AutoSkillit
<!-- autoskillit:pipeline-signature
steps=prepare_pr,run_arch_lenses,compose_pr,annotate_pr_diff,review_pr
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant