Files
vscode/.github/skills/add-policy/SKILL.md
Josh Spicer fcbabe4c73 Merge extension-provided policy (#306874)
* Add merge script for extension configuration policies from distro

Adds build/lib/policies/mergeExtensionPolicies.ts which fetches the
distro's product.json (at the commit pinned in package.json) and merges
extensionConfigurationPolicy entries into policyData.jsonc.

The --export-policy-data step only captures policies from the OSS
configuration registry. Extension policies (e.g., Copilot settings)
are defined in vscode-distro's product.json and are not available in
the OSS dev build. This merge script bridges that gap.

Workflow: ./scripts/code.sh --export-policy-data && node build/lib/policies/mergeExtensionPolicies.ts

Also updates:
- policyExport integration test to run the merge step
- add-policy skill documentation with extension policy lifecycle

* Simplify extension policy format: drop localization block from distro

- mergeExtensionPolicies.ts now reads only description/category from
  the distro and synthesizes localization key/value pairs internally
- Integration test gracefully skips merge step when distro is unavailable
- Updated SKILL.md to document the simplified distro format

* docs: use gh auth token in policy export command

* Handle old distro format missing description/category fields

* docs: add troubleshooting section for merge step in policy skill

* Require description and category fields in extension policy entries

* Add extension configuration policies to policyData.jsonc

* Address PR feedback: fix disclaimer, log targetPath, strip GITHUB_TOKEN from test

* fix: revert extension policies from policyData.jsonc, remove console.log from test

Extension policies should only be added after the distro PR merges.
Test runner disallows console output in tests.

* Add mock distro test for mergeExtensionPolicies, remove merge from export test

- mergeExtensionPolicies.ts: add DISTRO_PRODUCT_JSON env var to
  override the distro path (for testing without private repo access)
- New test: creates a mock product.json, runs the merge script,
  verifies policies are added correctly and idempotently
- Export test: no longer runs the merge step (avoids needing
  distro access or GITHUB_TOKEN in CI)

* Update distro and regenerate policyData.jsonc with extension policies

* Fix CI: add extension policy fixture for export test

The export test now runs mergeExtensionPolicies using a checked-in
fixture file instead of requiring distro access or GITHUB_TOKEN.
When extension policies change in the distro, regenerate both
policyData.jsonc and the fixture file.

* Integrate extension policy merge into --export-policy-data

- Removed mergeExtensionPolicies.ts — no separate CLI tool needed
- --export-policy-data now reads build/lib/policies/extensionPolicies.json
  and merges extension policies into the output automatically
- Single command workflow: ./scripts/code.sh --export-policy-data
- Simplified test — no merge step, no GITHUB_TOKEN, no distro access
- Updated SKILL.md to document the simplified workflow

* Dynamic distro reading: --export-policy-data fetches from distro directly

- policyExport.contribution.ts reads distro product.json dynamically:
  1. DISTRO_PRODUCT_JSON env var (for testing)
  2. .build/distro/mixin/stable/product.json (local checkout)
  3. GitHub API with GITHUB_TOKEN (remote fetch)
- New scripts/export-policy-data.sh wrapper: sets up GITHUB_TOKEN
  via gh CLI and runs the export
- Deleted build/lib/policies/extensionPolicies.json (no static copy)
- Test uses DISTRO_PRODUCT_JSON with a fixture file
- Uses sandbox process.env and VSBuffer (renderer-safe)

* Replace bash wrapper with cross-platform TS script

- New build/lib/policies/exportPolicyData.ts: handles transpilation,
  GITHUB_TOKEN setup via gh CLI, and runs --export-policy-data
- Added 'npm run export-policy-data' script to package.json
- Removed scripts/export-policy-data.sh
- Updated SKILL.md and test error message

* Use OAuth device flow for GitHub token acquisition

exportPolicyData.ts now acquires tokens via:
1. GITHUB_TOKEN env var (if set)
2. gh CLI (fast, non-interactive)
3. GitHub OAuth device flow (interactive, no dependencies)

Based on vscode-copilot-chat's getToken.mts pattern.

* Fix error messages to reference npm run export-policy-data

* Fix disclaimer to reference npm run export-policy-data, clean up stale refs

* Clarify test fixture is static and not expected to track distro changes

* Add inline comment in test clarifying fixture is static

* SKILL.md: add step to update distro commit hash in package.json

* Remove inline comment per PR feedback

* Remove local .build/distro/ fallback path per PR feedback
2026-04-01 00:07:47 +00:00

9.2 KiB

name, description
name description
add-policy Use when adding, modifying, or reviewing VS Code configuration policies. Covers the full policy lifecycle from registration to export to platform-specific artifacts. Run on ANY change that adds a `policy:` field to a configuration property.

Adding a Configuration Policy

Policies allow enterprise administrators to lock configuration settings via OS-level mechanisms (Windows Group Policy, macOS managed preferences, Linux config files) or via Copilot account-level policy data. This skill covers the complete procedure.

When to Use

  • Adding a new policy: field to any configuration property
  • Modifying an existing policy (rename, category change, etc.)
  • Reviewing a PR that touches policy registration
  • Adding account-based policy support via IPolicyData

Architecture Overview

Policy Sources (layered, last writer wins)

Source Implementation How it reads policies
OS-level (Windows registry, macOS plist) NativePolicyService via @vscode/policy-watcher Watches Software\Policies\Microsoft\{productName} (Windows) or bundle identifier prefs (macOS)
Linux file FilePolicyService Reads /etc/vscode/policy.json
Account/GitHub AccountPolicyService Reads IPolicyData from IDefaultAccountService.policyData, applies value() function
Multiplex MultiplexPolicyService Combines OS-level + account policy services; used in desktop main

Key Files

File Purpose
src/vs/base/common/policy.ts PolicyCategory enum, IPolicy interface
src/vs/platform/policy/common/policy.ts IPolicyService, AbstractPolicyService, PolicyDefinition
src/vs/platform/configuration/common/configurations.ts PolicyConfiguration — bridges policies to configuration values
src/vs/workbench/services/policies/common/accountPolicyService.ts Account/GitHub-based policy evaluation
src/vs/workbench/services/policies/common/multiplexPolicyService.ts Combines multiple policy services
src/vs/workbench/contrib/policyExport/electron-browser/policyExport.contribution.ts --export-policy-data CLI handler
src/vs/base/common/defaultAccount.ts IPolicyData interface for account-level policy fields
build/lib/policies/policyData.jsonc Auto-generated policy catalog (DO NOT edit manually)
build/lib/policies/policyGenerator.ts Generates ADMX/ADML (Windows), plist (macOS), JSON (Linux)
build/lib/test/policyConversion.test.ts Tests for policy artifact generation

Procedure

Step 1 — Add the policy field to the configuration property

Find the configuration registration (typically in a *.contribution.ts file) and add a policy object to the property schema.

Required fields:

Determining minimumVersion: Always read version from the root package.json and use the major.minor portion. For example, if package.json has "version": "1.112.0", use minimumVersion: '1.112'. Never hardcode an old version like '1.99'.

policy: {
    name: 'MyPolicyName',                          // PascalCase, unique across all policies
    category: PolicyCategory.InteractiveSession,    // From PolicyCategory enum
    minimumVersion: '1.112',                        // Use major.minor from package.json version
    localization: {
        description: {
            key: 'my.config.key',                   // NLS key for the description
            value: nls.localize('my.config.key', "Human-readable description."),
        }
    }
}

Optional: value function for account-based policy:

If this policy should also be controllable via Copilot account policy data (from IPolicyData), add a value function:

policy: {
    name: 'MyPolicyName',
    category: PolicyCategory.InteractiveSession,
    minimumVersion: '1.112',                        // Use major.minor from package.json version
    value: (policyData) => policyData.my_field === false ? false : undefined,
    localization: { /* ... */ }
}

The value function receives IPolicyData (from src/vs/base/common/defaultAccount.ts) and should:

  • Return a concrete value to override the user's setting
  • Return undefined to not apply any account-level override (falls through to OS policy or user setting)

If you need a new field on IPolicyData, add it to the interface in src/vs/base/common/defaultAccount.ts.

Optional: enumDescriptions for enum/string policies:

localization: {
    description: { key: '...', value: nls.localize('...', "...") },
    enumDescriptions: [
        { key: 'opt.none', value: nls.localize('opt.none', "No access.") },
        { key: 'opt.all', value: nls.localize('opt.all', "Full access.") },
    ]
}

Step 2 — Ensure PolicyCategory is imported

import { PolicyCategory } from '../../../../base/common/policy.js';

Existing categories in the PolicyCategory enum:

  • Extensions
  • IntegratedTerminal
  • InteractiveSession (used for all chat/Copilot policies)
  • Telemetry
  • Update

If you need a new category, add it to PolicyCategory in src/vs/base/common/policy.ts and add corresponding PolicyCategoryData localization.

Step 3 — Validate TypeScript compilation

Check the VS Code - Build watch task output, or run:

npm run compile-check-ts-native

Step 4 — Export the policy data

Regenerate the auto-generated policy catalog:

npm run export-policy-data

This script handles transpilation, sets up GITHUB_TOKEN (via gh CLI or GitHub OAuth device flow), and runs --export-policy-data. The export command reads extension configuration policies from the distro's product.json via the GitHub API and merges them into the output.

This updates build/lib/policies/policyData.jsonc. Never edit this file manually. Verify your new policy appears in the output. You will need code review from a codeowner to merge the change to main.

Policy for extension-provided settings

Extension authors cannot add policy: fields directly—their settings are defined in the extension's package.json, not in VS Code core. Instead, policies for extension settings are defined in vscode-distro's product.json under the extensionConfigurationPolicy key.

How it works

  1. Source of truth: The extensionConfigurationPolicy map lives in vscode-distro under mixin/{quality}/product.json (stable, insider, exploration).
  2. Runtime: When VS Code starts with a distro-mixed product.json, configurationExtensionPoint.ts reads extensionConfigurationPolicy and attaches matching policy objects to extension-contributed configuration properties.
  3. Export/build: The --export-policy-data command fetches the distro's product.json at the commit pinned in package.json and merges extension policies into the output. Use npm run export-policy-data which sets up authentication automatically.

Distro format

Each entry in extensionConfigurationPolicy must include:

"extensionConfigurationPolicy": {
    "publisher.extension.settingName": {
        "name": "PolicyName",
        "category": "InteractiveSession",
        "minimumVersion": "1.99",
        "description": "Human-readable description."
    }
}
  • name: PascalCase policy name, unique across all policies
  • category: Must be a valid PolicyCategory enum value (e.g., InteractiveSession, Extensions)
  • minimumVersion: The VS Code version that first shipped this policy
  • description: Human-readable description string used to generate localization key/value pairs for ADMX/ADML/macOS/Linux policy artifacts

Adding a new extension policy

  1. Add the entry to extensionConfigurationPolicy in all three quality product.json files in vscode-distro (mixin/stable/, mixin/insider/, mixin/exploration/)
  2. Update the distro commit hash in package.json to point to the distro commit that includes your new entry — the export command fetches extension policies from the pinned distro commit
  3. Regenerate policyData.jsonc by running npm run export-policy-data (see Step 4 above)
  4. Update the test fixture at src/vs/workbench/contrib/policyExport/test/node/extensionPolicyFixture.json with the new entry

Test fixtures

The file src/vs/workbench/contrib/policyExport/test/node/extensionPolicyFixture.json is a test fixture that must stay in sync with the extension policies in the checked-in policyData.jsonc. When extension policies are added or changed in the distro, this fixture must be updated to match — otherwise the integration test will fail because the test output (generated from the fixture) won't match the checked-in file (generated from the real distro).

Downstream consumers

Consumer What it reads Output
policyGenerator.ts policyData.jsonc ADMX/ADML (Windows GP), .mobileconfig (macOS), policy.json (Linux)
vscode-website (gulpfile.policies.js) policyData.jsonc Enterprise policy reference table at code.visualstudio.com/docs/enterprise/policies
vscode-docs Generated from website build docs/enterprise/policies.md

Examples

Search the codebase for policy: to find all the examples of different policy configurations.