Skip to content

M1: Assistant config + skill feature flag enforcement#10152

Merged
noanflaherty merged 2 commits into
feature/skill-feature-flags-gatewayfrom
swarm/skill-flag-gateway/task-1
Feb 27, 2026
Merged

M1: Assistant config + skill feature flag enforcement#10152
noanflaherty merged 2 commits into
feature/skill-feature-flags-gatewayfrom
swarm/skill-flag-gateway/task-1

Conversation

@noanflaherty
Copy link
Copy Markdown
Contributor

@noanflaherty noanflaherty commented Feb 27, 2026

Add featureFlags config section to assistant and enforce feature flags at all skill exposure code paths. When a skill flag is OFF, the skill is hidden from UI lists, model prompts, skill_load, and active tool projection. Part of #10145.


Open with Devin

Co-Authored-By: Claude <noreply@anthropic.com>
@noanflaherty
Copy link
Copy Markdown
Contributor Author

👀 Where to focus your review

  • assistant/src/config/skill-state.ts: New isSkillFeatureEnabled helper and the feature-flag gate in resolveSkillStates. This is the foundational primitive that all other enforcement points depend on. Verify the defaulting semantics (missing key = enabled, explicit false = disabled).
  • assistant/src/daemon/session-skill-tools.ts: Feature flag filter applied to the active skill ID set. This ensures flag-OFF skills are dropped even when old conversation markers exist. The getConfig() call here runs on every projection — verify this is acceptable for the hot path.

Risk level: Medium — New enforcement layer that silently removes skills from multiple surfaces. Misconfigured flags would make skills invisible with no error in the UI list or model prompt.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

…t section

Add isSkillFeatureEnabled checks in skill_load for child skills in both
the body-loading loop and the loaded_skill marker loop, so flag-OFF child
skills are fully hidden. Also filter hardcoded browser/twitter references
in buildDynamicSkillWorkflowSection through isSkillFeatureEnabled so the
system prompt does not advertise disabled skills.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@noanflaherty
Copy link
Copy Markdown
Contributor Author

Review feedback addressed

Pushed fix commit b333506d3 directly to swarm/skill-flag-gateway/task-1.

Issue 1: Enforce feature flags on included child skills in skill_load

File: assistant/src/tools/skills/load.ts

Added isSkillFeatureEnabled(childId, config) checks in both child skill loops:

  1. Body-loading loop (lines ~95-107): After resolving the child from catalogIndex, the loop now skips (continue) if the child's feature flag is OFF. This prevents loading the child's body content and listing it in the "Included Skills (immediate)" section.
  2. Marker-emitting loop (lines ~124-137): After the existing !child check, the loop now also skips if the child's feature flag is OFF. This prevents emitting <loaded_skill> markers for disabled child skills.

The config variable was already in scope from the top-level feature flag gate.

Issue 2: Hide hardcoded disabled skills from system prompt guidance

File: assistant/src/config/system-prompt.ts

Modified buildDynamicSkillWorkflowSection() to accept the AssistantConfig and conditionally include the "Browser Skill Prerequisite" and "X (Twitter) Skill" subsections only when their respective feature flags (browser, twitter) are enabled via isSkillFeatureEnabled. Updated the call site in appendSkillsCatalog() to pass the existing config.

Files modified

  • assistant/src/tools/skills/load.ts
  • assistant/src/config/system-prompt.ts

Concerns

None. Both fixes are minimal and use the existing isSkillFeatureEnabled utility that was already imported in both files.

@noanflaherty
Copy link
Copy Markdown
Contributor Author

@codex review this PR again — the previous issues have been fixed in commit b333506

@noanflaherty
Copy link
Copy Markdown
Contributor Author

@devin review this PR again — the previous issues have been fixed in commit b333506

@noanflaherty noanflaherty merged commit fcdbfd1 into feature/skill-feature-flags-gateway Feb 27, 2026
2 checks passed
@noanflaherty noanflaherty deleted the swarm/skill-flag-gateway/task-1 branch February 27, 2026 00:38
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

noanflaherty added a commit that referenced this pull request Feb 27, 2026
* M1: Assistant config + skill feature flag enforcement (#10152)

* feat: add skill feature flags config and enforcement

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: enforce feature flags on included child skills and dynamic prompt section

Add isSkillFeatureEnabled checks in skill_load for child skills in both
the body-loading loop and the loaded_skill marker loop, so flag-OFF child
skills are fully hidden. Also filter hardcoded browser/twitter references
in buildDynamicSkillWorkflowSection through isSkillFeatureEnabled so the
system prompt does not advertise disabled skills.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>

* M2: Gateway feature-flags API + persistence (#10165)

* feat: add gateway feature-flags REST API with config persistence

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: handle malformed URI encoding and config parse errors in feature-flags

- Wrap decodeURIComponent in try/catch in gateway/src/index.ts to return
  400 Bad Request on malformed percent-encoding instead of crashing to
  the global error handler with a misleading 500.

- Refactor readConfigFile to use a discriminated union result type that
  distinguishes "file doesn't exist" (returns empty config) from "file
  exists but can't be parsed" (returns error). The PATCH handler now
  returns 500 with a descriptive message when the config file is
  malformed, preventing silent data loss. The GET handler gracefully
  degrades to empty flags on parse errors (no data loss risk).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: validate parsed config is a plain object in readConfigFile

---------

Co-authored-by: Claude <noreply@anthropic.com>

* M3: Client-only PATCH auth split for feature flags (#10171)

* feat: add client-only PATCH auth split for feature-flags

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: remove rate-limit on 403 and handle identical token edge case

- Remove authRateLimiter.recordFailure() from 403 path to avoid penalizing
  legitimately authenticated clients who used the wrong token type (Issue 1)
- Only record failures on 401 (truly invalid authentication)
- Skip runtime-token rejection when FEATURE_FLAG_TOKEN is identical to
  runtimeBearerToken to support single-token deployments (Issue 2)

Addresses review feedback on PR #10171

---------

Co-authored-by: Claude <noreply@anthropic.com>

* M4: Client token plumbing (macOS/iOS) (#10175)

* feat: add feature-flag client token plumbing for macOS/iOS

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: throw on unavailable transport, always use gateway on macOS, clear stale token

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>

* docs: add feature-flags architecture documentation (#10181)

Co-authored-by: Claude <noreply@anthropic.com>

* fix: use active transport for macOS remote mode, preserve token on re-pair

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review feedback on final PR #10183

1. [P1] Fix gateway type-check: remove dead `not_found` variant from
   ConfigReadResult union (fixes narrowing bug on `result.detail`), make
   `featureFlagToken` optional in GatewayConfig type.

2. [P1] Add featureFlagToken to pairing responses: runtime now reads
   ~/.vellum/feature-flag-token and includes it in approval responses
   so iOS receives the token during QR pairing.

3. [P1] Fix PATCH auth bypass when tokens equal: loadConfig() now detects
   token collision and regenerates the feature-flag token to ensure the
   auth split is always enforceable.

4. [P2] Gate guardian-verification section on feature flags: system prompt
   skips buildGuardianVerificationRoutingSection() when guardian-verify-setup
   skill flag is OFF.

5. [P2] Fix failing integration test: removed incorrect assertion that
   <available_skills> disappears when only browser/twitter flags are OFF
   (bundled skills remain visible).

6. [P2] Fix macOS local HTTP transport: setFeatureFlag now checks if
   httpTransport.baseURL is localhost before delegating — local HTTP mode
   (localHttpEnabled) falls through to the direct gateway call.

7. [P2] Accept dotted skill IDs in feature-flag keys: updated regex from
   [a-z0-9_-]+ to [a-z0-9][a-z0-9._-]* to match managed skill ID validation.

* Migrate hatch visibility to skill flag and remove deprecated client flags

---------

Co-authored-by: Claude <noreply@anthropic.com>
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