Skip to content

GitHub Copilot support

steer is built for Claude Code, but teammates who use GitHub Copilot - either the Copilot CLI or Copilot in VS Code - can pick up the same org engineering standards. This page explains how all surfaces share one source of truth and how to install and refresh the Copilot side.

Scope

The Copilot target covers the always-on standards (.github/copilot-instructions.md, read by both the CLI and VS Code), the skills (as cross-tool SKILL.md on the CLI, and as .agents/skills/steer-*/ in the open Agent Skills format, read by every non-Claude agent), custom agents (.github/agents/*.agent.md - the steer-reviewer port), path-scoped instructions (.github/instructions/*.instructions.md), MCP servers (.vscode/mcp.json), an opt-in cloud coding-agent setup workflow (copilot-setup-steps.yml). It does not cover hooks: the Copilot CLI hook variant retired, so hook enforcement is guaranteed on Claude Code only. Copilot Chat in VS Code runs hooks/hooks.json directly and gets them incidentally, with no parity promise. Skill enforcement still differs from Claude Code - see the sections below for the caveats.

Surfaces at a glance

Capability Claude Code Copilot CLI Copilot in VS Code
Always-on standards SessionStart hook -> raw stdout, in parts .github/copilot-instructions.md (no hooks - the CLI variant retired) SessionStart hook (the plugin's hooks/hooks.json, run by VS Code) -> hookSpecificOutput.additionalContext, incidental and unguaranteed; .github/copilot-instructions.md as the reliable path
Path-scoped standards rule inject-when traits not delivered - emitted only to .github/instructions/ (see below) .github/instructions/*.instructions.md (applyTo glob)
Skills plugin skills/ (/steer:<skill>) plugin skills/ via Copilot manifest .agents/skills/steer-*/ (/steer-<skill>)
Subagents plugin agents/ not declared - the Copilot manifest carries skills only .github/agents/*.agent.md (agent picker)
MCP servers plugin .mcp.json not declared - the Copilot manifest has no mcpServers key .vscode/mcp.json
Cloud coding agent - (Claude @claude workflow) - .github/workflows/copilot-setup-steps.yml (opt-in)
Gate hooks hooks/hooks.json (deny on version pins, ask on the trunk-push gate) none - the CLI hook variant retired hooks/hooks.json as-is, incidentally - VS Code runs the Claude-format hooks (hard deny on version pins), with no parity promise
Source of truth rules/*.md + skills/ + agents/ the same rules/ + skills/ + agents/ the same rules/ + skills/ + agents/

Every one of those artifacts - instructions, the cross-tool .agents/skills/ tree, custom agents, the VS Code mcp.json, and the plugin + marketplace manifest versions - is generated from that one source and guarded by a build-time drift gate (see below) that fails the build the moment a committed artifact drifts. A symmetry meta-gate (check_copilot_symmetry.py, part of plugin-check) further asserts the same wiring across both generated families - *_copilot_* (the Copilot-only artifacts) and *_agent_* (the cross-tool .agents/skills/ tree): every gen_*.py in either is wired into gen:copilot, and every check_*.py into plugin-check - so a generator no task runs, or a gate no task invokes, fails the build. It asserts wiring, not generator<->gate pairing: gen_copilot_manifests.py has no check_copilot_manifests.py counterpart, because the manifest versions are gated by check_plugin.py's version-sync check instead. None of them is hand-maintained.

Why the surfaces differ

On every surface, steer's rules reach the session through the SessionStart hook (inject-standards.sh) - but the three harnesses want its stdout in three shapes, and the script tells them apart (steer_hook_host in hooks/lib/json.sh):

  • Claude Code takes raw text and caps one hook command's stdout at 10,000 characters, so the ruleset arrives in parts (one registration of the script per part in hooks/hooks.json).
  • Copilot CLI runs no steer hooks: the Copilot manifest declares skills only, and the always-on standards reach it through the committed .github/copilot-instructions.md. Its hook surface can take injected context (a top-level additionalContext under the camelCase sessionStart event), and steer shipped a generated manifest for it until hook parity retired - a second manifest, a generator, a drift gate and a per-hook porting decision, for a surface nothing guaranteed.
  • Copilot Chat in VS Code does not read the Copilot manifest at all: it detects steer as a Claude-format plugin (.claude-plugin/plugin.json) and runs hooks/hooks.json as-is. It injects only from hookSpecificOutput.additionalContext and logs everything else as "returned non-JSON output". The script recognises VS Code's payload (snake_case SessionStart carrying model and timestamp, and no permission_mode - the documented Claude Code field) and emits the same envelope from part 1, staying silent on the other eight registrations.

One JSON object carries both keys, so a single code path serves both Copilot surfaces: the CLI reads the top-level key and ignores the nested one, VS Code the reverse. A Claude Code payload, or any shape the script does not recognise, keeps the raw parted output - a mis-read can only ever fall back to today's behaviour, never lose the ruleset on Claude. The inject-when scoping, knowledge-work mode and the missing-rules fallback banner all ride inside the envelope.

The static custom-instructions file, .github/copilot-instructions.md, stays as the fallback: the Copilot cloud coding agent and Copilot code review load no plugins (the cloud agent runs hooks only from a repo's own .github/hooks/*.json), so for them the committed file is the whole standards surface, and it also covers a session where the hook cannot run. A build-time generator (mise run gen:copilot) concatenates the rules into that committed artifact, and a sync gate (check_copilot_instructions.py, part of plugin-check) fails the build if the artifact ever drifts from the rules. The same generator step also renders the cross-tool skill tree (below), with its own drift gate (check_agent_skills.py).

Why .github/copilot-instructions.md, not AGENTS.md

Copilot reads several repository instruction files and merges them - including AGENTS.md and CLAUDE.md/GEMINI.md - resolving conflicts non-deterministically. Emitting an AGENTS.md would therefore double-load the org standards alongside a repo's existing CLAUDE.md, while Claude Code (which does not read AGENTS.md) would ignore it entirely.

.github/copilot-instructions.md is Copilot's primary instructions file, is never read by Claude Code, and lives under .github/ so it does not compete at the repo root with CLAUDE.md. That keeps each surface reading exactly one copy of the standards.

Using it as a Copilot teammate

The standards file and the skill tree are installed by /steer:setup init (new repos) or /steer:setup adopt (existing repos), run from Claude Code during bootstrap - see the Adopt workflow. Copilot teammates only consume the files; they do not need to generate them.

Copilot CLI

copilot plugin marketplace add element22llc/e22-plugins
copilot plugin install steer@e22-plugins

The CLI loads the skills via the Copilot plugin manifest and reads the standards from .github/copilot-instructions.md in the repo.

Copilot in VS Code

VS Code does not use the Copilot CLI plugin marketplace, so there is nothing to install - it reads the committed repo files directly:

  • Standards - .github/copilot-instructions.md is read automatically as the repository's custom instructions (governed by the github.copilot.chat.codeGeneration.useInstructionFiles setting, default-on in recent VS Code). To confirm it loaded, expand the References section of a Copilot Chat response - the file is listed there (or right-click the Chat view -> Diagnostics).
  • Skills - every steer skill ships as a real SKILL.md under .agents/skills/steer-<skill>/, one of the three project-skill locations VS Code discovers (alongside .github/skills/ and .claude/skills/). Each is surfaced in Chat as a /steer-<skill> slash-command. Type /steer- in Chat to see them. Nothing in .vscode/settings.json gates this - skill discovery is on by default.

The bundled .vscode/settings.json sets both settings explicitly, so the standards load regardless of a teammate's VS Code defaults.

Refreshing after a steer update

In VS Code the always-on rules need no refresh: the SessionStart hook reads them from the installed plugin, so a plugin update is the whole upgrade path there (the Extensions view). On the Copilot CLI, which runs no steer hooks, the rules arrive only through the committed .github/copilot-instructions.md, so a copilot plugin update steer refreshes the skills and the instructions file must be regenerated to match. The committed Copilot files - the instructions fallback, the cross-tool skill tree, custom agents, path-scoped instructions - are a static snapshot, so they go stale when steer's rules or skills change. Refresh them with /steer:setup sync from Claude Code:

copilot plugin update steer       # CLI only: pull the new plugin version
# then, from Claude Code in the repo:
/steer:setup sync                 # re-copies copilot-instructions.md,
                                  # .agents/skills/, agents/, instructions/
/steer:setup sync --check         # read-only: reports the surface as mis-wired
                                  # when it has fallen behind

/steer:setup sync owns this because the refresh is a capability repair: agent-surface-current is wired only when every generated file is byte-identical to its plugin source and no retired steer-*.prompt.md lingers under .github/prompts/. The repair is a verbatim re-copy plus the deletion of any lingering steer--prefixed prompt file - a copy cannot remove one, so without that half the capability reports mis-wired after every repair. A prompt file the team wrote themselves is theirs and stays. /steer:setup init is not the refresh path - it installs the surface at bootstrap and then deliberately stops on an already-initialized repo, so re-running it does nothing.

For the Copilot cloud coding agent and code review, which load no plugins, this static set is the entire standards surface - so a repo that never refreshes leaves those surfaces working against the rules of whatever plugin version bootstrapped it, while Copilot CLI, VS Code and Claude Code sessions are current. Put the refresh on whoever owns plugin updates; the launch checklist carries it as a rollout item.

The files are fully steer-managed - overwritten on refresh and never hand-edited. Repo-specific Copilot guidance belongs in a separate *.instructions.md file, not in these; the re-copy never touches a file you own.

Skills on Copilot

steer's skills are authored as SKILL.md files. They reach the two Copilot surfaces differently:

  • Copilot CLI reads SKILL.md natively (an open cross-tool standard). A Copilot-specific plugin manifest (plugins/steer/.github/plugin/plugin.json, which Copilot prefers over the .claude-plugin/ manifest Claude Code uses) points Copilot at skills/. Its version - and the Copilot marketplace manifest's - is stamped from the source plugin.json by gen_copilot_manifests.py (mise run gen:copilot), so no Copilot manifest is hand-versioned either.
  • Copilot in VS Code reads the committed .agents/skills/ tree. This is not a Copilot-specific rendering: .agents/skills/ is one of the interoperable locations defined by the Agent Skills open standard, so the same tree is discovered by Cursor, Gemini CLI and Codex without any further work.

The build renders one .agents/skills/steer-<skill>/ directory per skill - including every user-invocable: false gateway, which the model can reach even though no one can type them - carrying the real skill body and its supporting mode files, not a summary. A body has to be rewritten to work off Claude Code (gen_agent_skills.py):

In the authored skill In the portable copy Why
${CLAUDE_PLUGIN_ROOT}/skills/<self>/modes/x.md modes/x.md The file travels with the skill, which is exactly the spec's colocation convention.
${CLAUDE_PLUGIN_ROOT}/templates/reference/..., and relative ../../templates/... links a blob/main URL on this repo Shared by many skills; vendoring several hundred KB - MIGRATIONS.md alone is the largest single file - into every consumer repo is not worth it, and the repo is public. These URLs are not currently fetchable - see Known limitations.
/steer:<skill> /steer-<skill> Plugin namespacing is Claude Code's; the slash name here is the skill's directory name.

Three differences from Claude Code remain on the Copilot surfaces - the first two on both, the third on VS Code only (which runs the plugin's hooks.json incidentally, per the table above; the CLI runs no steer hooks at all). Their mitigations do not: both notes below are injected by the generator into the portable .agents/skills/ tree, so the VS Code surface carries them. The Copilot CLI loads the authored skills/ directly, where context: fork and disallowed-tools are present-but-unhonoured and no note appears - read the two bullets there as caveats you apply yourself.

  • Forked skills are not forked here. context: fork names a Claude Code execution mode no other agent implements, so the portable copy drops it - but the two skills that use it (steer-explain, /steer-status) argue from forked execution in their bodies - steer-explain most sharply, telling the reader "this skill runs forked, and AskUserQuestion is removed from every subagent". That premise is false on this surface, and it would forbid a correct action. Both portable copies therefore open with a note saying the fork passages describe Claude Code, and that where a step says it cannot ask, you may.
  • Tool-permission scoping is inert. No non-Claude agent honors steer's allowed-tools / disallowed-tools, and their values are Claude tool syntax anyway - so the portable copy drops both fields rather than shipping a grant that means nothing. A skill that was frontmatter-restricted upstream instead opens with an explicit note that the restriction is now enforced by instruction, not by tooling, so a body reading "the edit tools are unavailable" is not mistaken for a guarantee. Treat those skills as advisory there.
  • No steer hook gates a skill mid-run on either Copilot surface - the CLI runs none, and the hooks VS Code picks up from hooks.json guard writes and Bash calls, not skill bodies.

Custom agents on Copilot

steer's subagents in the plugin's agents/ reach VS Code as custom agents - .github/agents/<name>.agent.md, selectable from the Copilot Chat agent picker (this is the format formerly called "custom chat modes"/.chatmode.md). Today that is steer-reviewer, the read-only reviewer that /steer-audit and /steer-work --reviewed delegate a single bounded slice to. The scheduled loop delegates to it too, but it is not a slash-command there: a user-invocable: false skill gets no /steer-<name> command.

The build renders one .agent.md per subagent (gen_copilot_agents.py, drift gate check_copilot_agents.py). The subagent's Claude tools (Read/Grep/ Glob) are mapped to Copilot's read-only built-in tool sets (codebase, search), so the ported reviewer stays write-free on VS Code the same way it is in Claude Code.

Scope preconditions in the flat file

A scoped rule that is not area-specific stays in copilot-instructions.md, because there is no applyTo glob that means "this repo is on the e22 org pack" or "this repo's tracker is GitHub". The flat file cannot test a repo trait, so the generator states the trait as a precondition the reader can check instead: SCOPE_PRECONDITIONS in gen_copilot_instructions.py emits a blockquote under the rule's heading -

Applies only to a repo on the e22 org pack - policy/org.yml with pack: e22, which is also what an absent file means. [...]

Five sections carry one today (the org pack's stack and commands rules, the OpenSpec backend, issue-first, and the autonomous-loop rule). The map is keyed by token, not by rule, so a new trait-scoped rule inherits the qualification rather than depending on its author writing a conditional first sentence - and check_copilot_instructions.py fails when a token appears in neither SCOPE_PRECONDITIONS nor the deliberate UNQUALIFIED_TOKENS exemption list. That gate exists because of #577, where rule 33 told native spec/ repos "This repo carries an openspec/ spine".

A path-scoped rule (next section) needs none of this: applyTo already gates the file, so the sentence would be redundant.

Path-scoped instructions

Most rules are repo-wide and live in the flat copilot-instructions.md. A rule that is genuinely area-specific - currently the infra/IaC stack rule - is emitted instead as a path-scoped instruction file, .github/instructions/<name>.instructions.md, carrying an applyTo glob so Copilot loads it only when working on matching files (e.g. **/*.tf, infra/**). This is the Copilot analog of the Claude SessionStart hook's inject-when trait gating; the same rule source drives both, and a scoped rule is excluded from the flat file so it never double-loads. The same generator + drift gate as the flat instructions (gen_copilot_instructions.py / check_copilot_instructions.py) keeps them in sync.

A scoped rule reaches VS Code only

Because the exclusion is unconditional (iter_rule_files filters SCOPED_RULES), a path-scoped rule is not in .github/copilot-instructions.md - today that means rule 12-stack-infra, the IaC stack standards. That directory is read by Copilot in VS Code and by the cloud coding agent; whether the Copilot CLI reads it is unverified here, so a CLI teammate working on Terraform may receive no IaC standards. If you need them there, load the file explicitly.

Do not "fix" this by dropping the rule from SCOPED_RULES: that key drives both the flat-file exclusion and the scoped emission, and main() prunes the orphaned file - so you would move the rule into every consumer's always-on context and delete infra.instructions.md, not resolve the gap.

Repo-specific Copilot guidance you author yourself also goes in a separate *.instructions.md you own - never edit the steer-generated ones.

MCP servers in VS Code

Copilot in VS Code does not read the plugin's .mcp.json (that wires Claude Code only). So the scaffold ships .vscode/mcp.json - VS Code's servers schema - mirroring the same servers: the GitHub MCP server that the tracker gateway (tracker-sync, reached through /steer-work and its issues mode - it is user-invocable: false, so no one types it directly) is built around, and context7 for current library docs. The GitHub server prompts once for a PAT (stored in VS Code secret storage). Without it, Copilot's tracker workflow falls back to gh only.

Like the other Copilot artifacts, this file is generated - gen_copilot_mcp.py renders it from the plugin's .mcp.json (mise run gen:copilot), translating the one sanctioned difference: the auth placeholder (env var -> prompted input, mapped in the generator's AUTH_INPUTS). A byte-equality drift gate (check_copilot_mcp.py, part of plugin-check) fails the build if the committed mirror falls out of sync. Edit .mcp.json and regenerate - never hand-edit the template in this repo.

That byte-gate governs the plugin-side template only. Unlike the four artifacts under .github/, the installed .vscode/mcp.json is not steer-managed: it sits outside /steer:setup sync's agent-surface-current capability, so a consumer owns their copy and is expected to merge additively and remove servers they don't use. Nothing re-copies it over their edits; only a one-shot ledger migration amends it.

Cloud coding agent (opt-in)

The GitHub-side Copilot coding agent (assign it an issue, it works in an ephemeral environment and opens a PR) reads the same .github/copilot-instructions.md + .github/instructions/ for standards. To make it boot a steer repo correctly, the scaffold carries .github/workflows/copilot-setup-steps.yml - it installs the pinned mise toolchain and runs dev:setup. The job name copilot-setup-steps is required; MCP + firewall for the agent are set in repo Settings -> Copilot -> Coding agent, not in-repo.

It is opt-in - /steer:setup init does not install it automatically; add it only for repos that use the coding agent. It fits steer's autonomous-loop rules: the coding agent opens draft PRs and never merges, so the human merge gate stands. Point it only at PR-flow repos (protected main), never solo-trunk.

Gate hooks on Copilot

Hook enforcement is guaranteed on Claude Code only. The Copilot CLI hook variant - hooks/copilot-hooks.json, its generator, its drift gate and the STEER_HOOK_TARGET=copilot branches inside the hook scripts - retired: it cost a second manifest, a per-hook porting decision and a softened envelope for a surface steer never promised parity on, and the gates it carried were ported, not proven (whether the CLI exports CLAUDE_PLUGIN_ROOT was never verified). The standards themselves still reach the CLI, through .github/copilot-instructions.md.

Copilot Chat in VS Code gets the hooks incidentally. It detects steer as a Claude-format plugin and runs hooks/hooks.json directly, with Claude's envelope and matcher syntax (matchers are ignored there, so every hook runs on every matching event). So the PreToolUse gates do fire in VS Code, as Claude's hard deny on version pins, and the SessionStart and Stop hooks run too: the ruleset injector recognises VS Code's payload shape and answers with the JSON envelope it reads (see Why the surfaces differ), while the advisory notices (session-checks.sh, orient-session.sh) emit raw text that VS Code discards. None of that is a promise - it is what the surface happens to do today, and a VS Code change could stop it without steer being at fault.

The advisory spec-first / issue-first nudges, and the issue-create contract guard in check-bash-actions.sh, were never ported anywhere: their intent is carried by the standards in .github/copilot-instructions.md.

Known limitations

  • The rewritten shared-file URLs are not fetchable. The rewrite in the table above points at GitHub's HTML blob/ view rather than raw.githubusercontent.com, and it is applied inside runnable command lines too, so those ship as sh "https://...". Because the rewrite is unconditional, any step that depends on reading a shared file or running a shared script is affected - for some skills that costs a link, for others the whole procedure. Detail, and what is unaffected, in Known limitations.
  • Tool-permission scoping is inert. See Skills on Copilot - the bodies themselves port in full, but neither frontmatter tool field does anything here. disallowed-tools removes nothing from the pool, where Claude Code at least removes those tools for the invoking turn; allowed-tools pre-approves nothing, though it grants without restricting in Claude Code either. Both limits port as instructions only. (The steer-reviewer subagent does port as a custom agent.)
  • No hook guarantee off Claude Code. The CLI runs no steer hooks at all; VS Code runs hooks/hooks.json incidentally, so it gets Claude's hard deny on version pins rather than anything tuned for it. The advisory nudges live in the standards text, not as hooks, and the advisory SessionStart notices emit raw text that VS Code discards.
  • Invocation form differs by surface, and the instructions file is shared. .github/copilot-instructions.md carries the rules verbatim, so every skill cross-reference in them reads /steer:<skill>. In VS Code the invocable form is /steer-<skill> (the .agents/skills/ tree); on the CLI skills load from the plugin manifest. Because one file serves both surfaces, a blanket rewrite would be wrong for one of them - the generated file therefore opens with a note stating the mapping. The skill-tree artifacts are rewritten to the hyphen form by gen_agent_skills.py.
  • Standards delivery on the CLI is the committed file, not a hook. Nothing steer ships executes on the Copilot CLI, so .github/copilot-instructions.md is the whole always-on surface there. It is installed by /steer:setup init and refreshed by /steer:setup sync; a repo that never ran either gets no standards on that surface.
  • Polyrepo topology is Claude-only. Workspace/member role detection is emitted by the orient-session.sh SessionStart hook as raw text, which the Copilot surfaces discard (only the ruleset injector emits their JSON envelope so far). There is deliberately no always-on polyrepo rule for the generator to carry, so a Copilot session gets no topology note. The full topology is in the polyrepo reference, which Claude Code loads (/steer:reference polyrepo).
  • Worktree mise trust inheritance is Claude-only. check-worktree-trust.sh runs on two Claude-Code registrations - SessionStart and CwdChanged - so a Copilot session started in or entered into a linked worktree does not inherit the primary checkout's trust, and its first mise run ... fails on trust, not on the task - the cost a polyrepo pays per member per feature. The standards carry the remedy instead of a hook: rule 45-delivery § Parallel worktrees tells the agent to run mise trust in the worktree before its first mise run ... - only when the primary checkout is already trusted, the same condition the hook applies - and otherwise to ask the human to run mise trust && mise install there, so no surface creates first-time trust. mise trust is idempotent, so the instruction is also free on Claude Code where the check already ran.
  • Worktree teardown is Claude-only too. Stopping a worktree's Docker stack is now done by two Claude-Code lifecycle hooks (SessionEnd -> docker:down, WorktreeRemove -> docker:clean), and neither event exists on a Copilot surface - so Copilot gets neither. This is exactly the trap this page exists to avoid: an unscoped rule asserting a safety net that is not there. Rules 45-delivery § Parallel worktrees and 50-done § End-of-session checklist therefore scope the hook claim to Claude Code and leave mise run docker:clean as the agent's own job everywhere else. (On Claude Code only the WorktreeRemove half is dependable; the SessionEnd half is best-effort - see Hooks -> Lifecycle events.) What is surface-agnostic is the per-worktree COMPOSE_PROJECT_NAME/port offset: it comes from the scaffold's mise config (scripts/worktree-env.sh), not from a hook.
  • SessionStart notices never arrive, so the rules no longer promise them. Beyond the worktree check above, three always-on notices used to tell the agent a SessionStart hook would flag a condition: a missing /spec spine (rule 00-router), an in-progress spec/BUILD-STATUS.md (rule 05-roles), and recorded hook faults (00-router § When steer itself misbehaves - two of the three now live in the router, which absorbed the self-report rule in 6.6). Copilot's sessionStart ignores stdout, so the notice never comes - and its absence reads as "condition not present," which is worse than no promise at all. Each rule now scopes the flag to Claude Code and, where there is something a reader could look for themselves (rules 00-router and 05-roles), says to do that instead; rule 00-router § When steer itself misbehaves only scopes, because recorded hook faults exist on no other surface. Likewise rule 10-stack no longer claims a hook denies stale image-major pins without qualification: the ported gate only asks on the Copilot CLI (VS Code, which runs the Claude hooks as-is, does deny), so the rule now says to keep the pins current yourself.
  • One further rule scoped, two whose detail moved out, plus one skill. The same sweep, finished. Rule 90-design-sources pointed at the frontend-design plugin, which the Copilot marketplace does not list, so it was scoped to Claude Code inline (the rule has since left the always-on payload for DESIGN-SOURCES.md, which carries the same qualification). Two others no longer need scoping because the surface-specific detail left the rule entirely: rule 61-gates § Hotfix is now surface-neutral about the hotfix/<n>-slug prefix (the reconciliation it used to name is the Stop hook reconcile-issue-first.sh, which is not ported - no Stop hook is, so on Copilot the prefix carries the convention alone), and rule 36-issue-first no longer enumerates the allow/ask permission tiers. Those tiers are Claude Code's - they live in .claude/settings.json and Claude skill frontmatter, and are documented in the plugin's ISSUE-WORKFLOW.md, which the flat Copilot standards file does not carry; Copilot applies its own host permissions instead, which is what the rule's surviving text describes. And /steer:spec questions leaned on check-open-questions.sh for both the backlog nudge and the 14-day blocking escalation with no alternative - its body now tells any other surface to apply that age test by hand.
  • Manual refresh. Unlike Claude Code's live injection, the Copilot files must be regenerated after a plugin update (see above).
  • Hooks are Preview. Copilot's plugin hooks are Preview and can be disabled by org policy, so the standards delivery never depends on them; the Copilot hook is hardened to fail open (it can never block an edit on error).