agentleFS
MicrosoftKnown publishermicrosoft/WinMIDL/.github/copilot-instructions.md

WinMIDL

Copilot instructions0 starsChanged 4 months agoMITView on GitHub

E2E proxy/stub tests require an MSVC x64 dev prompt: The VS extension must be built with MSBuild from VS, not dotnet build. WinMIDL is a MIDL3 compiler with an LSP server, two editor extensions, and custom proxy/stub codegen. Pipeline: IDL source → Lexer → Parser (lossless green tree) → Binder → InterfaceSynthesizer → VersionPropagator → AttributeValidator → CodeGen (WinMD / Header / IID / Proxy) Key projects (in src/): - Midl.Syntax — shared token/node types - Midl.Lexer — hand-written lexer,…

  • Where files are headingNew Copilot instructions files have more rules about branches and pull requests, and fewer about code style.

    Of the Copilot instructions files we read, the 2,336 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

  • In this repositoryOne other file it gives its agents.

The file

We split this copilot-instructions.md into the rules it gives an agent

11 rules. About 0 are common, 4 are distinctive, and 7 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 7

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Distinctive:

    Every change must be evaluated across 12 aspects: lexer/parser, analysis, diagnostics, autocomplete, signature help, hover/goto-def, WinMD …

    line 52
  2. Very specific:

    All codes defined in src/Midl.Analysis/Diagnostics/DiagnosticDescriptors.cs

    line 66
  3. Very specific:

    All attributes must be emitted to WinMD.

    line 83
  4. Distinctive:

    Use C# partial classes to minimize merge conflicts

    line 95
  5. Distinctive:

    Never use regex to edit source code — use exact string matching

    line 96
  6. Very specific:

    Never hardcode IIDs — use generated _i.c files

    line 97
  7. Distinctive:

    Every agent must run tests before committing

    line 98
  8. Very specific:

    Never push after running only a subset.

    line 110
  9. Very specific:

    All changes must follow TDD: write tests first, verify they fail, implement, verify they pass.

    line 114
  10. Very specific:

    Method params/retvals: use marshal_interface/unmarshal_interface (COM marshaling, preserves object identity)

    line 134
  11. Very specific:

    Struct fields: use marshal_nullable<T>/unmarshal_nullable<T> (inline [uint8 hasValue][T value], uses winrt::box_value/winrt::unbox_value)

    line 135

≈ “Common” is an estimate: at least 1 in 5 of the 3,779 Copilot instructions files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
copilot-instructions.md

Copilot Instructions — WinMIDL

Build & Test

dotnet build                                    # Build all projects
dotnet test                                     # Run all ~1,900 tests (xUnit)
dotnet test tests/Midl.Analysis.Tests           # Single project
dotnet test --filter "FullyQualifiedName~Parse" # Single test by name pattern
scripts/publish-optimized.ps1                   # NativeAOT binary + smoke test
scripts/package-vsix.ps1                        # VS Code VSIX (auto-infers version from git tag)
scripts/package-vs-vsix.ps1                     # VS VSIX

E2E proxy/stub tests require an MSVC x64 dev prompt:

tests/e2e-custom-marshal/comprehensive/build-and-test.ps1   # In-proc (89 tests)
tests/e2e-custom-marshal/outofproc/build-and-test.ps1       # Cross-process (108 tests)

The VS extension must be built with MSBuild from VS, not dotnet build.

Architecture

WinMIDL is a MIDL3 compiler with an LSP server, two editor extensions, and custom proxy/stub codegen.

Pipeline: IDL source → Lexer → Parser (lossless green tree) → Binder → InterfaceSynthesizer → VersionPropagator → AttributeValidator → CodeGen (WinMD / Header / IID / Proxy)

Key projects (in src/):

  • Midl.Syntax — shared token/node types
  • Midl.Lexer — hand-written lexer, built-in C preprocessor
  • Midl.Parser — error-recovering recursive-descent parser (Roslyn green tree pattern)
  • Midl.Analysis — binder, symbol table, type resolver, validators, WinMD reader
  • Midl.CodeGen — WinMD emitter, C header, IID, proxy/stub generators, decompiler
  • Midl.Lsp — JSON-RPC LSP server (no external NuGet deps)
  • Midl.Cli — CLI entry point, NativeAOT-compatible

Extensions (in editors/):

  • editors/vscode/ — VS Code extension (TextMate grammar, LSP client, TypeScript)
  • editors/vs/Midl.VsExtension/ — Visual Studio extension (traditional VSSDK, ILanguageClient)

Proxy/stub (in src/Midl.CodeGen/Proxy/):

  • Generates direct C++ proxy/stub code (no NDR) for COM cross-apartment/cross-process marshaling
  • Payload format: simple packed binary with null-terminated HSTRING
  • Interface pointer marshaling: typed IID first, IID_IUnknown fallback for system objects
  • Runtime helpers in include/winmidl/proxy_helpers.h

Conventions

Feature/bug checklist (see CONTRIBUTING.md)

Every change must be evaluated across 12 aspects: lexer/parser, analysis, diagnostics, autocomplete, signature help, hover/goto-def, WinMD emission (on ALL targets: types, methods, properties, events, params, fields), header/IID, proxy/stub codegen, decompiler round-trip, VS Code ↔ VS parity, and testing.

Compilation modes

  • CompilationMode.Strict — MIDL3 only, classic constructs produce errors
  • CompilationMode.Compat — allows classic MIDL2 constructs (coclass, typedef, etc.)
  • WITH_COMPAT define — enables compat-mode code paths
  • FEATURE_STAGING define — enables feature-staging support (feature_name declarations, [feature] gating)

Diagnostics

  • MIDL1xxx — semantic errors and warnings
  • MIDL2xxx — strict-mode errors (classic constructs, C-only types)
  • MIDL3xxx — code quality hints (naming, empty types, etc.)
  • All codes defined in src/Midl.Analysis/Diagnostics/DiagnosticDescriptors.cs

Interface synthesis

Runtimeclasses get synthesized exclusive interfaces:

  • Instance members → I<Class> (default interface)
  • Constructors → I<Class>Factory
  • Static members → I<Class>Statics
  • Protected → I<Class>Protected
  • Overridable → I<Class>Overrides

Parameterized types

IReference<T>, IVector<T>, IMap<K,V>, EventHandler<T> etc. emit as IInspectable* in generated headers and proxy code. PIIDs computed via UUID v5.

WinMD attribute emission

All attributes must be emitted to WinMD. Well-known attributes use dedicated Add*Attribute methods in CustomAttributeEmitter.cs. Everything else uses AddGenericCustomAttribute via EmitRemainingAttributes. Default values (threading(both), marshaling_behavior(agile)) are suppressed in decompiled output.

Proxy/stub marshaling

  • CoMarshalInterface/CoUnmarshalInterface use the typed IID (needed for delegate callbacks)
  • Falls back to IID_IUnknown for system WinRT objects (PropertyValue, etc.)
  • MSHCTX_LOCAL for both inproc and cross-process
  • Object type → IID_IUnknown (not IID_IInspectable — no proxy/stub for IInspectable)

Agent workflow

  • Background agents get their own dedicated git worktree + branch
  • Use C# partial classes to minimize merge conflicts
  • Never use regex to edit source code — use exact string matching
  • Never hardcode IIDs — use generated _i.c files
  • Every agent must run tests before committing
  • Audit sub-agent changes: always diff sub-agent commits in src/ and tests/ against the design intent before accepting. Sub-agents may change the design (e.g., modify test assertions) to make their code pass rather than fixing actual bugs. Verify the diff explicitly — do not trust "all tests pass" from a sub-agent.

Pre-push verification

ALL of the following must pass locally before pushing:

dotnet test --filter "Category!=E2E"                          # Unit tests (~2500)
pwsh tests/e2e-custom-marshal/comprehensive/build-and-test.ps1  # In-proc E2E (133 tests)
pwsh tests/e2e-custom-marshal/outofproc/build-and-test.ps1      # Cross-process E2E (123 tests)
dotnet test tests/Midl.E2E.Tests                                # C# E2E runner (runs build scripts from C#)

Never push after running only a subset. The C# E2E runner (Midl.E2E.Tests) exercises different code paths than the pwsh scripts (different INCLUDE setup, different process management).

TDD

All changes must follow TDD: write tests first, verify they fail, implement, verify they pass. For proxy/stub changes, this means unit tests in Midl.CodeGen.Tests first, then E2E tests that exercise actual C++ compilation and cross-apartment round-trip.

VS extension limitations

  • VS doesn't render markdown in LSP hover — hover handler strips markdown for VS
  • VS doesn't support LSP onTypeFormatting — use native ISmartIndent instead
  • VS built-in C++ project system hijacks .idl files — use .idlrt as primary extension
  • VS extension uses traditional VSSDK (AsyncPackage + ILanguageClient), not VisualStudio.Extensibility SDK

proxy_helpers.h compilation paths

include/winmidl/proxy_helpers.h is compiled in multiple contexts. When adding new #include directives or dependencies, ALL of these must be updated:

  • tests/e2e-custom-marshal/comprehensive/build-and-test.ps1 — $env:INCLUDE
  • tests/e2e-custom-marshal/outofproc/build-and-test.ps1 — $includes array
  • tests/Midl.E2E.Tests/E2ETestHelper.cs — MsvcToolchain record + Environment["INCLUDE"]
  • tests/e2e-custom-marshal/comprehensive/generated/winmidl/proxy_helpers.h — tracked copy

IReference<T> encoding

IReference<T> (nullable value types) has different encoding depending on context:

  • Method params/retvals: use marshal_interface/unmarshal_interface (COM marshaling, preserves object identity)
  • Struct fields: use marshal_nullable<T>/unmarshal_nullable<T> (inline [uint8 hasValue][T value], uses winrt::box_value/winrt::unbox_value)

This distinction exists because method-level IReference<T> is an IInspectable* that may carry custom behavior, while struct-field IReference<T> is conceptually just "T or null" with no identity to preserve.

Testing patterns

  • CompilationHelper.Compile(source) — compile IDL without SDK refs
  • CompilationHelper.Compile(source, new[] { sdk.WindowsWinMdPath }) — with SDK
  • CompileAndEmit(source) — compile + emit WinMD bytes
  • OpenMetadataReader(bytes) — read WinMD metadata
  • E2E tests use Check(condition, "TestName") pattern with pass/fail counting

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.