AGcaRO — Auto Generated control as ReadOnlyA Visual Studio extension that opens generated source files as read-only, so nobody wastes an afternoon editing a file the next code generation will overwrite.
The problemYou open Generated files look exactly like hand-written ones in the editor. The What AGcaRO does
InstallingFrom the Visual Studio MarketplaceSearch for AGcaRO under Extensions ▸ Manage Extensions ▸ Online. From source
The package lands in Build prerequisite: Visual Studio with the Visual Studio extension development workload
(the Working on the extensionOpen How detection worksRules are evaluated in this order, and the first one that decides ends the evaluation:
Steps 3 to 5 come before the read precisely so the common case never touches the disk. Once the document is open, step 6 uses the text the editor already holds — zero I/O. Only the header counts. By default the first 40 lines or 8 KB, whichever comes first. That is what keeps detection cheap, and what stops a file that merely mentions the tag halfway down from being locked. What is recognised out of the boxContent markers (case-insensitive, any comment syntax)
File name patterns
Generated folders and never-protected patternsFolders: Never protected: Using itWhen a file opens lockedAn info bar appears at the top of the document with the exact reason and offers Unlock for this session, Always allow this file, Settings and Stop showing this. Try to type and the status bar explains the block and reminds you of the shortcut. CommandsUnder Extensions ▸ AGcaRO, on the editor context menu and on the Solution Explorer context menu:
Opting a file out from inside the filePut ConfigurationPer userTools ▸ Options ▸ AGcaRO, across two pages: General (behaviour, feedback, diagnostics) and Detection (the marker and pattern lists). Per repository —
|
| Setting | Default | What it does |
|---|---|---|
enabled |
true |
Master switch |
showInfoBar |
true |
Info bar at the top of protected documents |
allowUnlock |
true |
Enables the unlock commands |
enforcement |
Shell |
Shell or ShellAndReadOnlyRegion |
strictMode |
false |
Unlocks do not survive closing the document; the in-file opt-out is ignored |
applyFileSystemAttribute |
false |
Also sets the on-disk read-only attribute |
respectAllowMarker |
true |
Honours AGcaRO:allow-edit |
allowMarker |
AGcaRO:allow-edit |
The opt-out text |
headerLineLimit |
40 |
Header lines inspected |
headerByteLimit |
8192 |
Bytes read per file |
cacheCapacity |
2048 |
Memoised decisions |
logLevel |
Errors |
Off, Errors, Info, Verbose |
Design decisions worth explaining
Protection lives in the editor, not on disk. The file system read-only attribute is
available as an option (applyFileSystemAttribute) but ships off: marking the file on disk
makes some generators fail when they try to rewrite it. Protecting the developer must not cost
you the generator.
The default mechanism is Visual Studio's own. BSF_USER_READONLY blocks typing, pasting,
formatting, snippets and refactorings — and, decisively, the shell knows how to suspend it
while reloading the document after the generator rewrites the file. ShellAndReadOnlyRegion
adds a direct buffer lock for anyone who wants maximum enforcement, at the cost of sometimes
needing to close and reopen the file after regeneration.
Failing means allowing, never locking. An I/O error, an unreachable network share, a denied permission, an unreadable configuration: in every one of those cases the file is treated as editable. A protection extension that locks the developer out because of its own error is worse than no extension at all.
The header window is bounded on purpose. Scanning whole files would find markers anywhere, but it would also lock any file that merely quotes the tag in a comment — and it would make opening a large generated file noticeably slower.
FAQ
Does it stop the generator from writing the file?
No. The lock lives in the editor's buffer, not on disk. The generator writes freely, and AGcaRO re-evaluates the file afterwards.
I unlocked a file, edited it, and the generator wiped my changes. Whose fault is that?
Yours, and AGcaRO said so before you started. Unlocking is deliberately explicit and is written to the audit log for exactly this conversation.
Why is my hand-written file locked?
Run Extensions ▸ AGcaRO ▸ Diagnose Current File: the Output window will name the rule and,
for a content marker, the line number. The usual culprit is a DO NOT EDIT phrase in the
header. Add the file to excludeGlobs, or put AGcaRO:allow-edit in its header.
Does it slow down opening a solution?
No. Nothing is scanned up front — evaluation happens lazily, per document, as each one opens. The package itself loads in the background and only when a solution is present.
Can I make protection mandatory for my team?
Yes. Commit an .agcaro.json with "allowUnlock": false and "strictMode": true. The unlock
commands disappear and the in-file opt-out marker stops being honoured.
The file stayed locked after the generator ran. Why?
Almost certainly enforcement is set to ShellAndReadOnlyRegion, whose direct buffer lock can
block the reload. Switch back to Shell (the default), or close and reopen the file.
Which languages are supported?
All of them. Detection reads the header as plain text and looks for markers regardless of comment syntax, so it works for any text-based format — including ones that did not exist when this was written.
Development
src/AGcaRO/
Core/ detection engine - plain .NET, no Visual Studio dependency at all
Editor/ editor integration: locking, info bar, command filter
Services/ logging, configuration and the coordinator
Options/ the Tools > Options pages
Commands/ menu commands
tests/ 111 tests over Core, runnable with `dotnet test`
samples/ sample files + manual verification walkthrough
build/ helper scripts: build, publishing and asset generation
publish/ Marketplace manifest and store page
docs/ requirements, publishing guide and the .agcaro.json schema
.github/workflows/ CI and release
New-Vsix.ps1 produces the .vsix packages - at the root, being the release entry point
Core/ deliberately does not reference the VSSDK: the test project compiles those sources
straight against net8.0, which is what lets the whole suite run without Visual Studio.
Scripts
| Script | Purpose |
|---|---|
New-Vsix.ps1 |
Packaging — tests, builds, validates the .vsix and publishes it to artifacts\ with a SHA-256 |
build\Build.ps1 |
Development loop: build and test |
build\Publish-Vsix.ps1 |
Publishes to the Marketplace. Start with -Validate, then -WhatIf |
build\Test-Scripts.ps1 |
Checks BOM, syntax and curly quotes in the .ps1 files. -Fix adds the BOM |
build\Test-MefExports.ps1 |
Verifies the MEF composition by reading the assembly metadata |
build\New-Assets.ps1 |
Regenerates the icons and the preview image |
build\New-SampleFiles.ps1 |
Generates the sample files |
.\New-Vsix.ps1 # bump the build and produce Release into artifacts\
.\New-Vsix.ps1 -Configuration Both # Debug and Release sharing one version
.\New-Vsix.ps1 -NoVersionBump # rebuild without bumping
.\New-Vsix.ps1 -Version 1.1.0 # pinned version, repository untouched
.\build\Build.ps1 # build and test, without packaging
.\build\Publish-Vsix.ps1 -Validate # check everything without contacting the store
Versioning
MAJOR.MINOR.BUILD, starting at 1.0.123. New-Vsix.ps1 bumps BUILD on every packaging
run and writes it to source.extension.vsixmanifest — commit that change along with the rest.
The manifest is the single source of the version: the .csproj reads it from there (target
SetVersionFromManifest), so AssemblyVersion, FileVersion and the VSIX version can never
diverge. Never set the version anywhere else.
| Situation | What to do |
|---|---|
| Ordinary build | Nothing — 1.0.123 → 1.0.124 automatically |
| Raise MINOR or MAJOR | Edit the manifest by hand (1.0.150 → 1.1.0) and commit; the next run continues from 1.1.1 |
| Rebuild without advancing | -NoVersionBump |
| In CI | -NoVersionBump — an ephemeral runner has nowhere to commit the bump |
Working on the PowerShell scripts
The scripts run on both Windows PowerShell 5.1 and PowerShell 7 — except
Test-MefExports.ps1, which needs 7 (it uses System.Reflection.Metadata, absent from .NET
Framework) and refuses to run on 5.1 with a clear message.
build\Test-Scripts.ps1 checks for the two traps below, and CI runs it:
UTF-8 with a BOM is not optional. Without it, Windows PowerShell 5.1 reads the file as ANSI and any accented character breaks the parser before the first line executes. The
.editorconfigpinscharset = utf-8-bom, but automated tooling strips BOMs easily —Test-Scripts.ps1 -Fixputs them back.
Never use curly quotes (
“ ”) inside literals. PowerShell accepts them as string delimiters equivalent to", so one in the middle of a message ends the string there and the error surfaces several lines later with a misleading message. Quote with straight single quotes.
Troubleshooting the build
| Symptom | Cause | Fix |
|---|---|---|
VSSDK1048 during the deploy step |
Stale incremental state in obj\ |
Run with -Clean |
Build produces the DLL but no .vsix |
-t:Restore,Build in one invocation evaluates the project before the VSSDK targets exist |
Run Restore and Build separately (the scripts already do) |
VsixPublisher.exe not found |
Extensibility workload missing | Install Visual Studio extension development |
Publishing
The full walkthrough — creating the publisher, generating the PAT, validating and publishing —
is in docs/PUBLISHING.md, with a release checklist and the common
pitfalls.
Before the first publish, replace the placeholder values (
publisher, repository URLs) listed in the table at the top of that document.
Documents
docs/REQUIREMENTS.md— the expanded requirements, with traceable IDs (FR-*/NFR-*) referenced from the code comments.docs/PUBLISHING.md— publishing to the Marketplace.samples/README.md— manual verification walkthrough inside VS.CHANGELOG.md— change history.
Compatibility
| Visual Studio | 2022 (17.x) and 2026 (18.x) — Community, Professional and Enterprise |
| Manifest | InstallationTarget [17.0,) — the API-version-based compatibility model |
| Runtime | .NET Framework 4.7.2 (in-process, like every VSIX) |
| Dependencies | none beyond the VSSDK itself |
Contributing
Issues and pull requests are welcome. Before opening a PR:
.\build\Test-Scripts.ps1 # if you touched any .ps1
.\build\Build.ps1 # build + 111 tests
New detection rules should come with a test in tests/AGcaRO.Core.Tests and, when they are
worth demonstrating, a file under samples/ plus a row in its walkthrough table.
Licence
MIT — see LICENSE.txt.