diff --git a/application/single_app/config.py b/application/single_app/config.py index 8dd84f2b2..e342eef4a 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -96,7 +96,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.260.008" +VERSION = "0.260.009" IS_DEVELOPMENT = is_development_env_enabled() SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax') diff --git a/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md b/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md new file mode 100644 index 000000000..8addedc3e --- /dev/null +++ b/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md @@ -0,0 +1,154 @@ +# Admin Settings IA rework — handoff + +Everything a new conversation needs to continue this work. The session-local +scratch scripts do **not** survive, so the parts that mattered were moved into +the repository. + +--- + +## Where things stand + +Staging branch `feature/admin-settings-ia` on `microsoft/simplechat`, cut from +`Development`. Nothing reaches `Development` until the whole rework is tested +and merged in one final reviewed PR. + +``` +Development ─── feature/admin-settings-ia ──┬── #1297 stage 1 MERGED + ├── #1304 stage A+B MERGED + ├── stage C next + ├── stage D + └── stage E + └──► final PR ──► Development +``` + +Current version: **0.260.009**. Fingerprint: **462 field names / 110 card ids**. + +### Shipped + +| Stage | Content | +|---|---| +| 1 | Template split into 18 per-tab partials under `templates/admin/_panes/`; Latest Features moved last and General made the landing tab; Global Identities heading; File Sync sidebar submenu; `sectionMap` pruned 72 → 6; classification banner preview fixed; CodeQL XSS sink fixed | +| A | 12 cross-tab links converted from tab-coupled `switchTab` to `data-admin-link` card targets; two were already broken; `switchTab` removed | +| B | `data-requires` dependency announcements with inline mirror and link; File Sync and Permissions wired | +| — | Form field contract enforced in CI | + +--- + +## Guardrails — these now live in the repo + +The single most important rule: **never rename a `name=` attribute.** Admin +Settings posts one form and the backend reads by field name, so a rename +silently stops a setting from saving with no error anywhere. + +| Check | How to run | +|---|---| +| **Field contract** | `python -m pytest functional_tests/test_admin_settings_field_contract.py` | +| Regenerate baseline *(only for a deliberate removal)* | `python functional_tests/test_admin_settings_field_contract.py --update-baseline` | +| Composition contract | `python -m pytest functional_tests/test_admin_settings_template_composition.py` | +| Card links | `python -m pytest functional_tests/test_admin_card_links.py` | +| Dependencies | `python -m pytest functional_tests/test_admin_settings_dependencies.py` | +| Sidebar parity | `python -m pytest functional_tests/test_admin_settings_sidebar_card_parity.py` | +| XSS | `python scripts/check_xss_sinks.py --full-file ` | + +**Regression baseline:** the 75 functional test files that reference +`admin_settings.html` sit at **33 pre-existing failures**. That number must not +change. To reproduce the list: + +``` +cd functional_tests +python -m pytest $(grep -rl admin_settings.html test_*.py) -q -p no:randomly +``` + +Exclude `test_enhanced_pii_analysis_standalone.py`, which needs live Azure +credentials. + +### Reading templates in tests + +`admin_settings.html` is only a shell now. Tests must compose it: + +```python +from test_support.templates import read_admin_settings_template +markup = read_admin_settings_template() +``` + +`test_admin_settings_template_composition.py` fails any test that reads the +template uncomposed while referencing a partial-backed card or field. + +--- + +## Next: Stage C + +Add the group navigation level **against the current 18 tabs**, so grouping is +proven before any card moves. + +1. Group row in the top-tab strip; collapsible group headers in + `_sidebar_nav.html` with persisted state. +2. Three-level sidebar search (group → tab → card). +3. Legacy hash redirect map inside `showAdminTab`. + +A server-side nav map is worth introducing here, since both navs need to render +the same group structure. Note it is **not** needed for link resolution — +`admin_card_links.js` resolves card → tab from the DOM via +`closest('.tab-pane')`, which is why links already survive any IA change. + +## Then Stage D — the risky one + +Re-home cards into the 14 groups, **one group per commit**, running the field +contract test on each. Three complications found while splitting: + +- **I1** Modals are interleaved *between* cards, not collected at the end. Each + modal moves with the tab that owns its trigger. +- **I2** Three cards are nested inside other cards and must move with their + parents: `conversation-contents-drawer-section` (inside chat file uploads), + `content-understanding-section` and `office-embedded-image-section` (inside + document intelligence). All three land in the same target tab as their parent. +- **I4** The Data Management migration workflow is ~985 lines of non-card markup + forming one unit. Split it across Backup/Migrate/Restore/Jobs **last and on + its own**. + +## Then Stage E + +- Split `system-settings-section`: `max_file_size_mb` → Workspaces; + `conversation_history_limit` and `default_system_prompt` → Chat; idle timeout + fields → Security; `access_denied_message` → Security. **No renames.** +- Access and Roles roster mirroring the 10 `require_member_of_*` toggles, built + on the Stage B proxy handling. Mirrors carry **no `name` attribute** — the + field contract test enforces this. + +--- + +## Decisions already locked + +| | Decision | +|---|---| +| D1 | Split `system-settings-section` across Chat / Security / Workspaces | +| D2 | Workflow becomes its own group | +| D3 | Mirror role toggles rather than move them | +| D4 | Legacy hash redirect map | +| D5 | Per-tab partials | +| Q2 | File Sync warns rather than blocks; hard-disable elsewhere | +| — | Land each stage as its own PR into the staging branch | + +## Target structure — 14 groups + +`Appearance · Chat · AI Models · Agents & Actions · Workspaces · Workflow · +Knowledge · Security · Governance · Data Lifecycle · Backup & Recovery · Scale · +Operations · Help` + +Full card-by-card placement is in the PR description of #1304 and in the +stage 2 plan. Key rationale: grouping follows *capability weight*, not card +titles — Governance has 196 KB of backing JS so it cannot be a Security +sub-tab; Video and audio are extraction pipelines, not chat features; Retention +is data lifecycle, not a workspace toggle. + +--- + +## Before the final merge to Development + +Revert the CI branch filters added in #1297. Six workflows list +`feature/admin-settings-ia` in their `pull_request` branch filters so staged +PRs are gated; `Development` should end up with its original configuration. + +Files: `codeql.yml`, `xss-sink-check.yml`, `broken-access-control-check.yml`, +`malicious-pr-security-review.yml`, `python-syntax-check.yml`, +`swagger-route-check.yml`. diff --git a/docs/explanation/release_notes.md b/docs/explanation/release_notes.md index 5bfcd0b91..a1d4772bc 100644 --- a/docs/explanation/release_notes.md +++ b/docs/explanation/release_notes.md @@ -2,6 +2,16 @@ For feature-focused and fix-focused drill-downs by version, see [Features by Version](/explanation/features/) and [Fixes by Version](/explanation/fixes/). +### **(v0.260.009)** + +#### New Features + +* **Admin Settings Form Field Contract Is Now Enforced** + * Admin Settings submits one form and the backend reads every value by field name, so the set of `name` attributes is the real contract between the template and the settings backend. Renaming or dropping one silently stops that setting from saving, with no error anywhere. + * A new test pins every field name against a committed baseline, and fails the build if one disappears. Adding settings is unaffected; removing one now requires regenerating the baseline in the same commit, which makes it a visible, reviewed decision. + * The same test rejects duplicate field names, which is what prevents a mirrored control from submitting a value twice. + * (Ref: `test_admin_settings_field_contract.py`, `admin_settings_field_baseline.json`) + ### **(v0.260.008)** #### New Features diff --git a/functional_tests/test_admin_settings_field_contract.py b/functional_tests/test_admin_settings_field_contract.py new file mode 100644 index 000000000..9f450f545 --- /dev/null +++ b/functional_tests/test_admin_settings_field_contract.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +# test_admin_settings_field_contract.py +""" +Functional test pinning the Admin Settings form field contract. +Version: 0.260.009 +Implemented in: 0.260.009 + +Admin Settings submits a single form, and the backend reads every value by +field name. That makes the set of `name` attributes the real contract between +the template and `route_frontend_admin_settings.py`. Moving a card between tabs +does not change it; renaming or dropping a `name` silently stops a setting from +saving, with no error anywhere. + +The information architecture rework moves most cards between tabs, so this test +holds that contract still: + + 1. Every field name recorded in the baseline still exists. + 2. No field name is duplicated except the known radio and checkbox groups, + which is what stops a mirrored control from submitting a value twice. + +New field names are allowed and reported, because adding settings is ordinary +work. Removing or renaming one requires regenerating the baseline in the same +commit: + + python functional_tests/test_admin_settings_field_contract.py --update-baseline + +That makes losing a setting a visible, reviewed decision rather than an +accident. +""" + +import json +import re +import sys +from collections import Counter +from pathlib import Path + +from test_support.templates import read_admin_settings_template +from test_support.versioning import assert_app_version_at_least + + +BASELINE_PATH = ( + Path(__file__).resolve().parent + / "test_support" + / "admin_settings_field_baseline.json" +) + +FIELD_RE = re.compile(r'\sname="([^"]+)"') + +# Jinja-templated names cannot be compared literally. +TEMPLATED = re.compile(r"\{\{|\{%") + + +def collect_field_names(markup): + """Return a Counter of literal form field names in the composed template.""" + return Counter( + name for name in FIELD_RE.findall(markup) if not TEMPLATED.search(name) + ) + + +def load_baseline(): + """Read the committed field-name contract.""" + assert BASELINE_PATH.is_file(), ( + f"Missing baseline at {BASELINE_PATH}. Generate it with " + "--update-baseline." + ) + return json.loads(BASELINE_PATH.read_text(encoding="utf-8")) + + +def test_no_admin_settings_field_is_lost(): + """A dropped or renamed field name silently stops a setting from saving.""" + print("Testing Admin Settings field name contract...") + + assert_app_version_at_least("0.260.009") + baseline = load_baseline() + current = collect_field_names(read_admin_settings_template()) + + required = set(baseline["field_names"]) + present = set(current) + + missing = sorted(required - present) + assert not missing, ( + "These form fields disappeared from Admin Settings, so the settings " + "they carry can no longer be saved. If a removal is intended, " + "regenerate the baseline in the same commit with --update-baseline:\n " + + "\n ".join(missing) + ) + + added = sorted(present - required) + if added: + print(f" {len(added)} new field(s) since the baseline: {added[:5]}") + + print(f"All {len(required)} baseline field(s) still present.") + + +def test_no_unexpected_duplicate_field_names(): + """Two controls sharing a name submit the value twice.""" + print("Testing Admin Settings duplicate field names...") + + baseline = load_baseline() + current = collect_field_names(read_admin_settings_template()) + + allowed = baseline["allowed_duplicates"] + unexpected = { + name: count + for name, count in current.items() + if count > 1 and name not in allowed + } + + assert not unexpected, ( + "These field names appear more than once, so the form would submit " + "each of them multiple times. Mirrored controls must omit the name " + "attribute rather than repeat it:\n " + + "\n ".join(f"{name} x{count}" for name, count in sorted(unexpected.items())) + ) + + # A group that stops being a group is also a contract change worth noticing. + shrunk = sorted( + name for name, count in allowed.items() if current.get(name, 0) < count + ) + if shrunk: + print(f" note: known group(s) now smaller than baseline: {shrunk}") + + print(f"No unexpected duplicates; {len(allowed)} known group(s) intact.") + + +def update_baseline(): + """Rewrite the baseline from the current template, deliberately.""" + current = collect_field_names(read_admin_settings_template()) + payload = { + "_comment": ( + "Contract for Admin Settings form field names. Regenerate with " + "test_admin_settings_field_contract.py --update-baseline only when " + "a field is intentionally removed or renamed." + ), + "field_names": sorted(current), + "allowed_duplicates": { + name: count for name, count in sorted(current.items()) if count > 1 + }, + } + BASELINE_PATH.write_text( + json.dumps(payload, indent=2) + "\n", encoding="utf-8" + ) + print(f"Baseline written: {len(payload['field_names'])} field(s), " + f"{len(payload['allowed_duplicates'])} known group(s)") + + +if __name__ == "__main__": + if "--update-baseline" in sys.argv: + update_baseline() + sys.exit(0) + + tests = [ + test_no_admin_settings_field_is_lost, + test_no_unexpected_duplicate_field_names, + ] + + results = [] + for test in tests: + try: + test() + results.append(True) + except Exception as exc: + print(f"FAILED {test.__name__}: {exc}") + import traceback + traceback.print_exc() + results.append(False) + + print(f"\nResults: {sum(results)}/{len(results)} passed") + sys.exit(0 if all(results) else 1) diff --git a/functional_tests/test_support/admin_settings_field_baseline.json b/functional_tests/test_support/admin_settings_field_baseline.json new file mode 100644 index 000000000..3c9fe0019 --- /dev/null +++ b/functional_tests/test_support/admin_settings_field_baseline.json @@ -0,0 +1,463 @@ +{ + "_comment": "Contract for Admin Settings form field names. Regenerate with test_admin_settings_field_contract.py --update-baseline only when a field is intentionally removed or renamed.", + "field_names": [ + "access_denied_message", + "agent_templates_allow_user_submission", + "agent_templates_require_approval", + "agents_page_disclaimer_markdown", + "agents_page_hero_color_mode", + "agents_page_hero_primary_color", + "agents_page_hero_secondary_color", + "agents_page_promoted_popular_agents_json", + "agents_page_promoted_popular_order", + "agents_page_promoted_popular_tag_enabled", + "agents_page_promoted_popular_tag_label", + "agents_page_show_instructions_in_details", + "agents_page_subtitle", + "agents_page_title", + "ai_notice_frequency", + "ai_notice_message", + "allow_group_agents", + "allow_group_custom_endpoints", + "allow_group_plugins", + "allow_group_workflows", + "allow_group_workspace_file_downloads", + "allow_personal_workspace_file_downloads", + "allow_public_workspace_file_downloads", + "allow_user_agents", + "allow_user_custom_endpoints", + "allow_user_plugins", + "allow_user_workflows", + "app_title", + "audio_files_storage_account_url", + "azure_ai_search_authentication_type", + "azure_ai_search_endpoint", + "azure_ai_search_key", + "azure_apim_ai_search_endpoint", + "azure_apim_ai_search_subscription_key", + "azure_apim_content_safety_endpoint", + "azure_apim_content_safety_subscription_key", + "azure_apim_document_intelligence_endpoint", + "azure_apim_document_intelligence_subscription_key", + "azure_apim_embedding_api_version", + "azure_apim_embedding_deployment", + "azure_apim_embedding_endpoint", + "azure_apim_embedding_subscription_key", + "azure_apim_gpt_api_version", + "azure_apim_gpt_deployment", + "azure_apim_gpt_endpoint", + "azure_apim_gpt_subscription_key", + "azure_apim_image_gen_api_version", + "azure_apim_image_gen_deployment", + "azure_apim_image_gen_endpoint", + "azure_apim_image_gen_subscription_key", + "azure_content_understanding_analyzer_id", + "azure_content_understanding_api_version", + "azure_content_understanding_authentication_type", + "azure_content_understanding_endpoint", + "azure_content_understanding_image_analyzer_id", + "azure_content_understanding_key", + "azure_document_intelligence_authentication_type", + "azure_document_intelligence_endpoint", + "azure_document_intelligence_key", + "azure_openai_embedding_api_version", + "azure_openai_embedding_authentication_type", + "azure_openai_embedding_endpoint", + "azure_openai_embedding_key", + "azure_openai_embedding_resource_group", + "azure_openai_embedding_subscription_id", + "azure_openai_gpt_api_version", + "azure_openai_gpt_authentication_type", + "azure_openai_gpt_endpoint", + "azure_openai_gpt_key", + "azure_openai_gpt_resource_group", + "azure_openai_gpt_subscription_id", + "azure_openai_image_gen_api_version", + "azure_openai_image_gen_authentication_type", + "azure_openai_image_gen_endpoint", + "azure_openai_image_gen_key", + "azure_openai_image_gen_resource_group", + "azure_openai_image_gen_subscription_id", + "chunk_size_csv", + "chunk_size_doc", + "chunk_size_docm", + "chunk_size_docx", + "chunk_size_excel", + "chunk_size_html", + "chunk_size_json", + "chunk_size_log", + "chunk_size_md", + "chunk_size_pdf", + "chunk_size_pptx", + "chunk_size_transcript", + "chunk_size_txt", + "chunk_size_xml", + "chunk_size_yaml", + "chunk_size_yml", + "classification_banner_color", + "classification_banner_enabled", + "classification_banner_text", + "classification_banner_text_color", + "content_safety_authentication_type", + "content_safety_endpoint", + "content_safety_include_trigger_information", + "content_safety_key", + "content_safety_violation_message", + "control_center_auto_refresh_enabled", + "control_center_auto_refresh_time", + "control_center_auto_refresh_timezone", + "conversation_cache_ttl_seconds", + "conversation_history_limit", + "cosmos_throughput_account_name", + "cosmos_throughput_auto_scale_down_enabled", + "cosmos_throughput_auto_scale_up_enabled", + "cosmos_throughput_autoscale_enabled", + "cosmos_throughput_container_policies_json", + "cosmos_throughput_convert_manual_to_autoscale_enabled", + "cosmos_throughput_database_name", + "cosmos_throughput_enforce_container_defaults", + "cosmos_throughput_ignore_max_limit", + "cosmos_throughput_ignore_min_limit", + "cosmos_throughput_max_ru", + "cosmos_throughput_metrics_window_minutes", + "cosmos_throughput_min_ru", + "cosmos_throughput_resource_group", + "cosmos_throughput_scale_down_cooldown_minutes", + "cosmos_throughput_scale_down_step_ru", + "cosmos_throughput_scale_down_threshold_percent", + "cosmos_throughput_scale_up_cooldown_minutes", + "cosmos_throughput_scale_up_step_ru", + "cosmos_throughput_scale_up_threshold_percent", + "cosmos_throughput_subscription_id", + "custom_pages_force_menu", + "custom_pages_menu_name", + "custom_pages_restart_acknowledged", + "data_management_migration_groups_mode_choice", + "data_management_migration_mode", + "data_management_migration_public_workspaces_mode_choice", + "data_management_migration_users_mode_choice", + "debug_timer_unit", + "debug_timer_value", + "deep_research_enable_ledger_artifact", + "deep_research_enable_query_planning", + "deep_research_max_search_queries_per_turn", + "deep_research_max_user_urls_per_turn", + "default_model_selection_json", + "default_retention_conversation_group", + "default_retention_conversation_personal", + "default_retention_conversation_public", + "default_retention_document_group", + "default_retention_document_personal", + "default_retention_document_public", + "default_system_prompt", + "disable_group_creation", + "document_access_index_backfill_batch_size", + "document_access_index_cache_ttl_seconds", + "document_access_index_repair_batch_size", + "document_action_analyze_chat_max_documents", + "document_action_analyze_enabled", + "document_action_analyze_workflow_max_documents", + "document_action_comparison_chat_max_documents", + "document_action_comparison_enabled", + "document_action_comparison_workflow_max_documents", + "document_classification_categories_json", + "document_intelligence_auto_sample_pages", + "document_intelligence_pdf_image_extraction_mode", + "embedding_model_json", + "enable_agent_template_gallery", + "enable_ai_notice", + "enable_ai_search_apim", + "enable_appinsights_global_logging", + "enable_audio_file_support", + "enable_chat_completion_audio_cues", + "enable_chat_file_uploads", + "enable_chunk_size_override", + "enable_content_safety", + "enable_content_safety_apim", + "enable_conversation_archiving", + "enable_conversation_cache", + "enable_conversation_contents_drawer", + "enable_custom_pages", + "enable_dark_mode_default", + "enable_debug_logging", + "enable_debug_logging_timer", + "enable_deep_source_review", + "enable_default_embedding_model_plugin", + "enable_desktop_notifications", + "enable_document_access_index_cache", + "enable_document_access_index_reads", + "enable_document_access_index_shadow_validation", + "enable_document_access_index_write_through", + "enable_document_classification", + "enable_document_intelligence_apim", + "enable_document_intelligence_formula_extraction", + "enable_embedding_apim", + "enable_enhanced_citations", + "enable_enhanced_extraction", + "enable_external_healthcheck", + "enable_external_links", + "enable_extract_meta_data", + "enable_fact_memory_plugin", + "enable_file_processing_logs", + "enable_file_processing_logs_timer", + "enable_file_sharing", + "enable_file_sync", + "enable_file_sync_group", + "enable_file_sync_personal", + "enable_file_sync_public", + "enable_front_door", + "enable_gpt_apim", + "enable_group_workspaces", + "enable_http_plugin", + "enable_idle_timeout", + "enable_image_gen_apim", + "enable_image_generation", + "enable_inbound_mcp_rate_limits", + "enable_inbound_mcp_server", + "enable_key_vault_secret_expiration_reminders", + "enable_key_vault_secret_storage", + "enable_landing_page_editor", + "enable_left_nav_default", + "enable_math_plugin", + "enable_mcp_destination_governance", + "enable_multi_model_endpoints", + "enable_multimodal_vision", + "enable_no_auth_external_healthcheck", + "enable_office_embedded_image_analysis", + "enable_public_workspaces", + "enable_redis_cache", + "enable_retention_policy_group", + "enable_retention_policy_personal", + "enable_retention_policy_public", + "enable_semantic_kernel", + "enable_source_review", + "enable_speech_to_text_input", + "enable_startup_document_access_index_backfill", + "enable_support_latest_feature_documentation_links", + "enable_support_latest_features", + "enable_support_menu", + "enable_support_send_feedback", + "enable_swagger", + "enable_tabular_durable_run_confirmation", + "enable_terms_of_use", + "enable_text_plugin", + "enable_text_to_speech", + "enable_thoughts", + "enable_time_plugin", + "enable_url_access", + "enable_user_agreement", + "enable_user_agreement_daily", + "enable_user_feedback", + "enable_user_workspace", + "enable_video_file_support", + "enable_wait_plugin", + "enable_web_search", + "enable_web_search_user_notice", + "enforce_workspace_scope_lock", + "external_links_force_menu", + "external_links_json", + "external_links_menu_name", + "favicon_file", + "file_download_allowed_group_ids", + "file_download_allowed_public_workspace_ids", + "file_sync_allow_recursive_sources", + "file_sync_allowed_group_ids", + "file_sync_allowed_public_workspace_ids", + "file_sync_group_admin_only", + "file_sync_max_concurrent_runs", + "file_sync_max_files_per_run", + "file_sync_max_gb_per_run", + "file_sync_max_sources_per_scope", + "file_sync_min_schedule_interval_minutes", + "file_sync_personal_admin_only", + "file_sync_personal_require_app_role", + "file_sync_public_admin_only", + "file_sync_visible_source_types", + "file_timer_unit", + "file_timer_value", + "front_door_url", + "governance_global_actions_usage", + "governance_global_agents_usage", + "governance_global_endpoints", + "governance_group_actions", + "governance_group_agents", + "governance_group_endpoints", + "governance_user_actions", + "governance_user_agents", + "governance_user_endpoints", + "gpt_model_json", + "group_workflow_allowed_group_ids", + "hide_app_title", + "idle_timeout_minutes", + "idle_warning_message", + "idle_warning_minutes", + "image_gen_model_json", + "inbound_mcp_allow_all_source_ids", + "inbound_mcp_allow_external_tenants", + "inbound_mcp_allowed_client_app_entries_json", + "inbound_mcp_allowed_source_entries_json", + "inbound_mcp_allowed_tenant_entries_json", + "inbound_mcp_max_request_bytes", + "inbound_mcp_rate_limit_read_per_window", + "inbound_mcp_rate_limit_search_per_window", + "inbound_mcp_rate_limit_window_seconds", + "inbound_mcp_rate_limit_write_per_window", + "inbound_mcp_required_app_role", + "inbound_mcp_required_scope", + "inbound_mcp_required_user_role", + "inbound_mcp_source_header", + "key_vault_identity", + "key_vault_name", + "key_vault_secret_expiration_admin_roles", + "key_vault_secret_expiration_default_contact_email", + "key_vault_secret_expiration_default_lead_days", + "key_vault_secret_expiration_emit_contact_email_in_telemetry", + "key_vault_secret_expiration_require_expiration", + "key_vault_secret_expiration_scan_interval_seconds", + "landing_page_alignment", + "landing_page_logo_scale_percent", + "landing_page_text", + "latest_features_enable_enhanced_citations_proxy", + "latest_features_enable_redis_cache_proxy", + "latest_features_enable_thoughts_proxy", + "latest_features_office_docs_authentication_type_proxy", + "latest_features_office_docs_storage_account_blob_endpoint_proxy", + "latest_features_office_docs_storage_account_url_proxy", + "latest_features_redis_auth_type_proxy", + "latest_features_redis_key_proxy", + "latest_features_redis_url_proxy", + "latest_features_tabular_preview_max_blob_size_mb_proxy", + "logo_dark_file", + "logo_file", + "max_file_size_mb", + "max_rounds_per_agent", + "mcp_block_unsafe_destinations", + "merge_global_semantic_kernel_with_workspace", + "metadata_extraction_model", + "metadata_extraction_model_selection_json", + "model_endpoint_identity_header_enabled", + "model_endpoint_identity_header_name", + "model_endpoint_identity_header_value_type", + "model_endpoints_json", + "multimodal_vision_model", + "office_docs_authentication_type", + "office_docs_storage_account_blob_endpoint", + "office_docs_storage_account_url", + "office_embedded_image_max_per_document", + "office_embedded_image_min_pixels", + "orchestration_type", + "per_user_semantic_kernel", + "public_workspace_display_name", + "redis_auth_type", + "redis_key", + "redis_url", + "release_notifications_email", + "release_notifications_name", + "release_notifications_org", + "release_notifications_registered", + "release_notifications_registered_at", + "release_notifications_updated_at", + "require_group_assignment_for_file_downloads", + "require_group_assignment_for_file_sync", + "require_group_assignment_for_group_workflows", + "require_member_of_chat_file_upload_user", + "require_member_of_control_center_admin", + "require_member_of_control_center_dashboard_reader", + "require_member_of_create_group", + "require_member_of_create_public_workspace", + "require_member_of_deep_research_user", + "require_member_of_feedback_admin", + "require_member_of_safety_violation_admin", + "require_member_of_url_access_user", + "require_member_of_workflow_user", + "require_owner_for_group_agent_management", + "require_public_workspace_assignment_for_file_downloads", + "require_public_workspace_assignment_for_file_sync", + "require_shared_conversation_file_approval", + "retention_policy_execution_hour", + "send_feedback_bug_details", + "send_feedback_bug_email", + "send_feedback_bug_name", + "send_feedback_bug_org", + "send_feedback_feature_details", + "send_feedback_feature_email", + "send_feedback_feature_name", + "send_feedback_feature_org", + "show_logo", + "source_review_allow_internal_hosts", + "source_review_allow_js_rendering", + "source_review_audit_logging", + "source_review_default_mode", + "source_review_enable_llm_planning", + "source_review_js_load_more_clicks", + "source_review_max_bytes_per_page_mb", + "source_review_max_depth", + "source_review_max_pages_per_turn", + "source_review_max_redirects", + "source_review_max_seed_pages_per_turn", + "source_review_respect_robots_txt", + "source_review_timeout_seconds", + "speech_service_authentication_type", + "speech_service_endpoint", + "speech_service_key", + "speech_service_locale", + "speech_service_location", + "speech_service_resource_group", + "speech_service_resource_id", + "speech_service_resource_name", + "speech_service_subscription_id", + "support_feedback_recipient_email", + "support_menu_name", + "tabular_durable_run_confirmation_threshold_batches", + "tabular_durable_run_confirmation_threshold_rows", + "tabular_generated_output_chunk_model_deployment", + "tabular_generated_output_chunk_model_mode", + "tabular_preview_max_blob_size_mb", + "terms_of_use_accept_button_text", + "terms_of_use_decline_button_text", + "terms_of_use_decline_redirect_url", + "terms_of_use_frequency", + "terms_of_use_message", + "terms_of_use_title", + "url_access_allowed_domains", + "url_access_blocked_domains", + "url_access_max_chat_urls_per_turn", + "url_access_max_workflow_urls_per_run", + "user_agreement_apply_chat", + "user_agreement_apply_group", + "user_agreement_apply_personal", + "user_agreement_apply_public", + "user_agreement_text", + "video_files_storage_account_url", + "video_index_timeout", + "video_indexer_account_id", + "video_indexer_account_name", + "video_indexer_arm_api_version", + "video_indexer_endpoint", + "video_indexer_location", + "video_indexer_resource_group", + "video_indexer_subscription_id", + "web_search_consent_accepted", + "web_search_foundry_agent_id", + "web_search_foundry_api_version", + "web_search_foundry_auth_type", + "web_search_foundry_authority", + "web_search_foundry_client_id", + "web_search_foundry_client_secret", + "web_search_foundry_cloud", + "web_search_foundry_endpoint", + "web_search_foundry_managed_identity_client_id", + "web_search_foundry_managed_identity_type", + "web_search_foundry_tenant_id", + "web_search_user_notice_text", + "workflow_max_auto_invoke_attempts", + "workflow_max_tasks" + ], + "allowed_duplicates": { + "data_management_migration_groups_mode_choice": 3, + "data_management_migration_mode": 3, + "data_management_migration_public_workspaces_mode_choice": 3, + "data_management_migration_users_mode_choice": 3, + "file_sync_visible_source_types": 3 + } +}