From 22ba6a2bcccad4dfc89c61b16a6e31ec8b2b7217 Mon Sep 17 00:00:00 2001 From: Ajay Merchia Date: Fri, 9 Jan 2026 02:09:59 -0800 Subject: [PATCH 1/4] feat: Add V3 schema foundation (PR #1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ci/config.yaml with enum definitions for availability, agent_capabilities, solution_tags - Add ci/config_loader.py utility for loading config values - Update ci/model.py with V3 constants: - Required/optional field definitions for connectors and plugins - REMOVED_FIELDS set for strict validation - Regex patterns for kebab-case and Title Case validation - Description prefix/suffix constants Key changes: - Config.yaml stores ONLY enums (validation logic stays in code) - Dropped fidelity, difficulty_level, time_in_minutes from V3 - Added strict field validation sets (ALL_CONNECTOR_FIELDS, ALL_PLUGIN_FIELDS) - V3 will ban any fields not in allowed sets 🤖 Generated with Claude Code Co-Authored-By: Claude Sonnet 4.5 --- ci/config.yaml | 23 ++++++++++++ ci/config_loader.py | 58 ++++++++++++++++++++++++++++++ ci/model.py | 87 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 ci/config.yaml create mode 100644 ci/config_loader.py diff --git a/ci/config.yaml b/ci/config.yaml new file mode 100644 index 00000000..ff0a070b --- /dev/null +++ b/ci/config.yaml @@ -0,0 +1,23 @@ +# V3 Schema Configuration +# This file defines valid enum values for schema fields +# Validation rules and logic live in validate_v3.py + +version: 3 + +# Valid availability states for plugins and connectors +availability: + - IDEA + - VALIDATED + - BUILT_IN + - INSTALLABLE + - IMPOSSIBLE + +# Valid agent capabilities (multi-select for plugins) +agent_capabilities: + - Ambient Agent + - Proactive Agent + - Conversational Agent + +# Valid solution tags (business categorization, multi-select for plugins) +# NOTE: This will be populated in PR #2 by extracting from existing files +solution_tags: [] diff --git a/ci/config_loader.py b/ci/config_loader.py new file mode 100644 index 00000000..df4328e1 --- /dev/null +++ b/ci/config_loader.py @@ -0,0 +1,58 @@ +""" +Configuration loader for V3 schema validation. +Loads and caches config.yaml to provide enum value lists. +""" + +import yaml +import os +from typing import List, Dict, Any + +# Path to config file relative to this module +CONFIG_FILE = os.path.join(os.path.dirname(__file__), "config.yaml") + +# Cache the loaded config +_config_cache = None + + +def load_config() -> Dict[str, Any]: + """Load and cache config.yaml""" + global _config_cache + if _config_cache is None: + with open(CONFIG_FILE, 'r', encoding='utf-8') as f: + _config_cache = yaml.safe_load(f) + return _config_cache + + +def get_valid_values(field: str) -> List[str]: + """ + Get valid enum values for a config field. + + Args: + field: Field name (e.g., 'availability', 'agent_capabilities', 'solution_tags') + + Returns: + List of valid values, or empty list if field not in config + """ + config = load_config() + return config.get(field, []) + + +def is_valid_value(field: str, value: str) -> bool: + """ + Check if a value is valid for a given field. + + Args: + field: Field name + value: Value to check + + Returns: + True if value is in the valid list, False otherwise + """ + valid_values = get_valid_values(field) + return value in valid_values + + +def get_config_version() -> int: + """Get the schema version from config""" + config = load_config() + return config.get('version', 3) diff --git a/ci/model.py b/ci/model.py index 46d888a5..787aca10 100644 --- a/ci/model.py +++ b/ci/model.py @@ -1,5 +1,7 @@ from enum import Enum +import re +# V2 Schema Enums (kept for backwards compatibility during migration) class Fidelity(Enum): IDEA = "Idea" VALIDATED = "Validated" @@ -20,14 +22,89 @@ class DifficultyLevel(Enum): ADVANCED = "Advanced" +class Availability(Enum): + IDEA = "Idea" + VALIDATED = "Validated" + BUILT_IN = "Built-In" + INSTALLABLE = "Installable" + + +# Common constants DIRECTORY_MAP = {ContentTypes.CONNECTOR: "connectors", ContentTypes.PLUGIN: "plugins"} MARKDOWN_EXTENSION = '.md' README_FILENAME = f"README{MARKDOWN_EXTENSION}" LOGO_FILE = "logo.png" -class Availability(Enum): - IDEA = "Idea" - VALIDATED = "Validated" - BUILT_IN = "Built-In" - INSTALLABLE = "Installable" \ No newline at end of file + +# ============================================================================ +# V3 Schema Constants +# ============================================================================ + +# V3 Schema Version +V3_SCHEMA_VERSION = 3 + +# Path to config file +CONFIG_FILE = "ci/config.yaml" + +# Regex patterns for V3 validation +KEBAB_CASE_PATTERN = r'^[a-z0-9]+(?:-[a-z0-9]+)*$' +TITLE_CASE_PATTERN = r'^[A-Z][a-z]*(?:\s+[A-Z][a-z]*)*$' # Simplified, actual validation more complex + +# Description validation constants +PLUGIN_DESCRIPTION_PREFIX = "A plugin that" +CONNECTOR_DESCRIPTION_PREFIX = "A connector for" +DESCRIPTION_SUFFIX = "." + +# Purple Chat Link validation +PURPLE_CHAT_URL_PREFIX = "https://marketplace.moveworks.com/purple-chat?conversation=" + +# V3 Required Fields by Content Type +CONNECTOR_REQUIRED_FIELDS = { + "name", # Title Case name + "availability", # Enum from config.yaml + # Note: logo is required as a file, not a YAML field +} + +PLUGIN_REQUIRED_FIELDS = { + "name", # Title Case name + "description", # Must start with prefix and end with period + "availability", # Enum from config.yaml + "systems", # List of connector slugs (must exist as directories) + "purple_chat_link", # Must be valid URL + "solution_tags", # List from config.yaml +} + +# V3 Optional Fields by Content Type +CONNECTOR_OPTIONAL_FIELDS = { + "description", # Should end with period if present + "video", # Video URL + "redirects", # List of old slugs + "num_implementations", # Integer count +} + +PLUGIN_OPTIONAL_FIELDS = { + "agent_capabilities", # List from config.yaml + "installation_asset_uuid", # UUID for installable plugins + "video", # Video URL + "redirects", # List of old slugs + "domain", # Deprecated but still accepted + "num_implementations", # Integer count + "accreditations", # List of author usernames +} + +# V3 Removed Fields (validation will fail if these are present) +REMOVED_FIELDS = { + "drop_accreditations", # No longer needed + "design_pattern_id", # Internal field deprecated + "installation_link", # Rare override no longer supported + "custom_tags", # Migrated to agent_capabilities + "resources", # Computed field, not stored + "fidelity", # Removed in V3 + "difficulty_level", # Removed in V3 + "time_in_minutes", # Removed in V3 +} + +# All valid V3 fields (for strict schema validation) +ALL_CONNECTOR_FIELDS = CONNECTOR_REQUIRED_FIELDS | CONNECTOR_OPTIONAL_FIELDS +ALL_PLUGIN_FIELDS = PLUGIN_REQUIRED_FIELDS | PLUGIN_OPTIONAL_FIELDS \ No newline at end of file From 4e6416693050b6c71f643a1cef799db739521b3d Mon Sep 17 00:00:00 2001 From: Ajay Merchia Date: Fri, 9 Jan 2026 02:12:58 -0800 Subject: [PATCH 2/4] feat: Populate solution_tags in config.yaml - Extracted 34 unique tags from existing solution_tags and domain fields - Combined and deduplicated all tags across 890 files - Tags cover all business categories: HR, Finance, IT, Sales, etc. This completes the foundation for V3 schema validation. --- ci/config.yaml | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/ci/config.yaml b/ci/config.yaml index ff0a070b..e95060ec 100644 --- a/ci/config.yaml +++ b/ci/config.yaml @@ -19,5 +19,39 @@ agent_capabilities: - Conversational Agent # Valid solution tags (business categorization, multi-select for plugins) -# NOTE: This will be populated in PR #2 by extracting from existing files -solution_tags: [] +# Extracted from existing solution_tags and domain fields across all files +solution_tags: + - Access Management + - Approvals + - Customer Success + - Data Analysis + - Engineering + - Facilities + - Finance - Expense Management + - Finance - Other + - Finance - Payroll + - Finance - Procurement + - General + - HR - Benefits + - HR - Employee Records + - HR - Learning & Development + - HR - Onboarding + - HR - Other + - HR - Performance Management + - HR - Recruiting & Talent + - HR - Talent Management + - HR - Time & Absence + - HR - Workplace Culture + - IT + - Legal + - Manager + - Marketing + - Product + - Product Management + - Productivity + - Project Management + - Sales + - Support + - Ticketing + - Troubleshoot + - Workday From c03879e0ff81ae5f1cecea51b800034b8ef8bb12 Mon Sep 17 00:00:00 2001 From: Ajay Merchia Date: Fri, 9 Jan 2026 02:15:00 -0800 Subject: [PATCH 3/4] fix: Remove 'Polling Required' from agent_capabilities - 'Polling Required' will be migrated to 'Ambient Agent' in all files - Final agent_capabilities: Ambient Agent, Structured Data Analyzer - This aligns with v3 migration plan to remove custom_tags --- ci/config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/config.yaml b/ci/config.yaml index e95060ec..3146a926 100644 --- a/ci/config.yaml +++ b/ci/config.yaml @@ -13,10 +13,10 @@ availability: - IMPOSSIBLE # Valid agent capabilities (multi-select for plugins) +# Note: "Polling Required" is being migrated to "Ambient Agent" agent_capabilities: - Ambient Agent - - Proactive Agent - - Conversational Agent + - Structured Data Analyzer # Valid solution tags (business categorization, multi-select for plugins) # Extracted from existing solution_tags and domain fields across all files From f088026683df57cdc1f812384f94b2dd19bdca97 Mon Sep 17 00:00:00 2001 From: Ajay Merchia Date: Fri, 9 Jan 2026 02:19:15 -0800 Subject: [PATCH 4/4] refactor: Simplify model.py V3 schema - Remove REMOVED_FIELDS set - validation will fail for any field not in allowed sets - Remove deprecated fields: num_implementations, domain, accreditations - Add logo as required field for connectors (URL, not file) - Remove unused 're' import - Fix linting errors (line length, spacing, newline at EOF) --- ci/model.py | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/ci/model.py b/ci/model.py index 787aca10..2aa30292 100644 --- a/ci/model.py +++ b/ci/model.py @@ -1,5 +1,5 @@ from enum import Enum -import re + # V2 Schema Enums (kept for backwards compatibility during migration) class Fidelity(Enum): @@ -30,7 +30,10 @@ class Availability(Enum): # Common constants -DIRECTORY_MAP = {ContentTypes.CONNECTOR: "connectors", ContentTypes.PLUGIN: "plugins"} +DIRECTORY_MAP = { + ContentTypes.CONNECTOR: "connectors", + ContentTypes.PLUGIN: "plugins" +} MARKDOWN_EXTENSION = '.md' README_FILENAME = f"README{MARKDOWN_EXTENSION}" @@ -49,7 +52,8 @@ class Availability(Enum): # Regex patterns for V3 validation KEBAB_CASE_PATTERN = r'^[a-z0-9]+(?:-[a-z0-9]+)*$' -TITLE_CASE_PATTERN = r'^[A-Z][a-z]*(?:\s+[A-Z][a-z]*)*$' # Simplified, actual validation more complex +# Simplified Title Case pattern - actual validation more complex +TITLE_CASE_PATTERN = r'^[A-Z][a-z]*(?:\s+[A-Z][a-z]*)*$' # Description validation constants PLUGIN_DESCRIPTION_PREFIX = "A plugin that" @@ -57,13 +61,15 @@ class Availability(Enum): DESCRIPTION_SUFFIX = "." # Purple Chat Link validation -PURPLE_CHAT_URL_PREFIX = "https://marketplace.moveworks.com/purple-chat?conversation=" +PURPLE_CHAT_URL_PREFIX = ( + "https://marketplace.moveworks.com/purple-chat?conversation=" +) # V3 Required Fields by Content Type CONNECTOR_REQUIRED_FIELDS = { - "name", # Title Case name - "availability", # Enum from config.yaml - # Note: logo is required as a file, not a YAML field + "name", # Title Case name + "availability", # Enum from config.yaml + "logo", # Logo URL (required) } PLUGIN_REQUIRED_FIELDS = { @@ -77,10 +83,9 @@ class Availability(Enum): # V3 Optional Fields by Content Type CONNECTOR_OPTIONAL_FIELDS = { - "description", # Should end with period if present - "video", # Video URL - "redirects", # List of old slugs - "num_implementations", # Integer count + "description", # Should end with period if present + "video", # Video URL + "redirects", # List of old slugs } PLUGIN_OPTIONAL_FIELDS = { @@ -88,23 +93,9 @@ class Availability(Enum): "installation_asset_uuid", # UUID for installable plugins "video", # Video URL "redirects", # List of old slugs - "domain", # Deprecated but still accepted - "num_implementations", # Integer count - "accreditations", # List of author usernames -} - -# V3 Removed Fields (validation will fail if these are present) -REMOVED_FIELDS = { - "drop_accreditations", # No longer needed - "design_pattern_id", # Internal field deprecated - "installation_link", # Rare override no longer supported - "custom_tags", # Migrated to agent_capabilities - "resources", # Computed field, not stored - "fidelity", # Removed in V3 - "difficulty_level", # Removed in V3 - "time_in_minutes", # Removed in V3 } # All valid V3 fields (for strict schema validation) +# Any fields NOT in these sets will cause validation to FAIL ALL_CONNECTOR_FIELDS = CONNECTOR_REQUIRED_FIELDS | CONNECTOR_OPTIONAL_FIELDS -ALL_PLUGIN_FIELDS = PLUGIN_REQUIRED_FIELDS | PLUGIN_OPTIONAL_FIELDS \ No newline at end of file +ALL_PLUGIN_FIELDS = PLUGIN_REQUIRED_FIELDS | PLUGIN_OPTIONAL_FIELDS