aspid-code-style
Skill65 starsChanged 18 days agoMITView on GitHub
Formatting and layout rules for C# code in Aspid.MVVM — member ordering, `#nullable`, line breaks in expression-bodied members and ternaries, column-aligned parameters, named arguments for fallback/log helpers, float comparison, comments, `#if` guards. Use when writing or reviewing any `.cs` file in the repo (Source, StarterKit, Unity, Editor, tests), when deciding how to format something, or when the user asks to "tidy up", "align with our style", "do it like the rest of the code".
In this repository9 other files it gives its agents.
- AGENTS.mdAGENTS.md
- asp-branchSkill
- asp-commitSkill
- asp-mvvm-releaseSkill
- asp-prSkill
- docs-siteSkill
- starterkit-binder-authoringSkill
- starterkit-converter-authoringSkill
- starterkit-layoutSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 1 is distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 23
No trailing whitespace, no whitespace-only lines.
- Very specific: line 43
Default constructor access: protected on non-sealed generic types (a subclass needs a reachable base ctor for Unity serialization).
- Very specific: line 76
Never ==/!= between float/double (ReSharper CompareOfFloatsByEqualityOperator): zero via value is 0d / is not 0f, integrality via value % …
- Very specific: line 81
Delete // comments entirely when they restate the XML doc or the obvious.
- Distinctive: line 82
Keep only what the code cannot be understood without, as one short phrase.
- Very specific: line 92
Prefer one attribute with a params parameter over a family of marker attributes.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill 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.
Files
Aspid.MVVM code style
These rules apply to all new and edited code without asking. Breaking public API is allowed, but members are never renamed for style alone: rename only when the name misrepresents behavior.
File
#nullable enableas the first line when the file has?annotations. Exception: files withMonoBehaviourclasses (views, initializers, Mono binders) have no#nullable enableand no?annotations.- File name = type name. One public type per file.
// ReSharper disable once CheckNamespaceabovenamespacewhen the folder differs from the namespace (the package default).- No
#region/#endregion, even with dozens of explicit interface implementations. - The package targets Unity
6000.0, and every assembly,Sourceincluded, is compiled by Unity only: no#if UNITY_2023_1_OR_NEWER,UNITY_2022_1_OR_NEWERor TMP guards, no stand-ins forUnityEnginetypes.UNITY_EDITORandDEBUGguards stay. The other conditional defines are the integrationsASPID_MVVM_UNITY_LOCALIZATION_INTEGRATION,ASPID_MVVM_ZENJECT_INTEGRATION,ASPID_MVVM_VCONTAINER_INTEGRATION,ASPID_MVVM_ADDRESSABLES_INTEGRATION,ASPID_MVVM_UNITASK_INTEGRATIONand the opt-outsASPID_MVVM_UNITY_PROFILER_DISABLED,ASPID_MVVM_EDITOR_DISABLED. - Profiler markers anywhere in the package: the file opens with the header below (after
#nullable enable, if any), and every marker field,Markers<T>class andusing (….Auto())/using (this.Marker())sits under#if ASPID_MVVM_PROFILER. Generated code spells the same condition out (Defines.ASPID_MVVM_PROFILERin the generator).ENABLE_PROFILERkeeps the marker fields and classes out of a release player. The alias keeps itsASPID_MVVM_prefix: a file-local#definecannot undo a project-wide symbol, so a barePROFILERin Scripting Define Symbols would override the opt-out.#if ENABLE_PROFILER && !ASPID_MVVM_UNITY_PROFILER_DISABLED #define ASPID_MVVM_PROFILER #endif - No trailing whitespace, no whitespace-only lines. No em/en dashes (
—,–) in code, comments or docs: use:or,. - American spelling, including API names (
Center, notCentre).
Member ordering (StyleCop SA1201 with one deviation)
- Serialized fields (
[SerializeField],[SerializeReference]) - Other fields
- Auto-properties (including
protected abstract T X { get; set; }) - Constructors
- Events
- Properties with bodies / expression-bodied
- Methods
The single deviation from StyleCop: auto-properties sit above the constructor. Within fields, serialized ones come first.
Constructors
- 2+ parameters: one parameter per line, opening parenthesis on the name line.
- Assignments in the body ordered by increasing line length, the longest (
?? throw) last. - Optional fallback:
_fallback = fallback ?? _fallback;on one line (neverif (x is not null)), so the field default survives omission. - Default constructor access:
protectedon non-sealed generic types (a subclass needs a reachable base ctor for Unity serialization);privateon sealed types when an empty instance fails at runtime;publicwith<remarks>Default: …</remarks>when an empty instance is valid. - Invalid arguments throw (
ArgumentNullException,ArgumentOutOfRangeExceptionwith the parameter name); never accept silently.
Methods and expressions
- Method parameters go one per line only when the signature exceeds 120 columns.
- Short guard
ifwithreturnon one line:if (!IsMissing(value)) return value; - Brace-less
ifwhose body is an action (assignment, call): body on the next line, indented. One line only forreturn. ifwhose body is a single multi-linereturn …(…)gets braces.- Expression-bodied method: body on the next line after
=>:string IConverter<int, string>.Convert(int value) => Convert(value); - Ternary as an expression body: the condition stays on the
=>line, branches on the following lines:public string? Convert(string? value) => string.IsNullOrWhiteSpace(value) ? value : Wrap(value, _color, _includeAlpha); - Ternary in a
var x = …assignment: three lines (condition /? a/: b); inside a call argument orreturnit stays on one line. - A chain of
if … returnover one value becomes aswitchexpression with relational patterns. - Array initializers are not wrapped for line width.
- Multi-line string concatenation:
+at the end of the line, not at the start of the next. - Calls with 2+ arguments to fallback and log helpers (
UseFallback,Fallback,Fail,LogError) use named arguments, one per line:return this.UseFallback( fallback: _fallback, problem: value.Expected("an index"));
Numbers
- Never
==/!=betweenfloat/double(ReSharper CompareOfFloatsByEqualityOperator): zero viavalue is 0d/is not 0f, integrality viavalue % 1d is 0d, equality of two floats viaMathf.Approximately. - A value outside the target type's range saturates at the bound (
NumericSaturation); it is never dropped silently.
Comments
- Delete
//comments entirely when they restate the XML doc or the obvious. Shortening instead of deleting counts as half-done work. - Keep only what the code cannot be understood without, as one short phrase.
- XML docs: global skill
asp-xmldoc.
Generics and inheritance
- Generic classes are never
sealed: they are extension points. typeparamisT, notTFrom, when there is a single parameter.- Shared bases must not narrow the set of interfaces their subclasses implement.
- Duplicates and unused members left over from a refactor are deleted, not kept "for compatibility".
- Helpers and wrappers (loggers,
*Text,*Math) stay thin: the operation and one message format, no profiler markers, caches or editor machinery. A logger exposes only logging methods; formatting helpers live in a separate type. - Prefer one attribute with a
paramsparameter over a family of marker attributes; the name states a fact ([UsedInModes(BindMode.TwoWay, BindMode.OneWayToSource)]), not an editor behavior.
Tests
- Assertion messages and comments in tests are in English.
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
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.

