agentleFS
VPDPersonalVPDPersonal/Aspid.MVVM/.claude/skills/aspid-code-style/SKILL.md

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".

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
  1. Very specific:

    No trailing whitespace, no whitespace-only lines.

    line 23
  2. Very specific:

    Default constructor access: protected on non-sealed generic types (a subclass needs a reachable base ctor for Unity serialization).

    line 43
  3. Very specific:

    Never ==/!= between float/double (ReSharper CompareOfFloatsByEqualityOperator): zero via value is 0d / is not 0f, integrality via value % …

    line 76
  4. Very specific:

    Delete // comments entirely when they restate the XML doc or the obvious.

    line 81
  5. Distinctive:

    Keep only what the code cannot be understood without, as one short phrase.

    line 82
  6. Very specific:

    Prefer one attribute with a params parameter over a family of marker attributes.

    line 92

≈ “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.

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

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 enable as the first line when the file has ? annotations. Exception: files with MonoBehaviour classes (views, initializers, Mono binders) have no #nullable enable and no ? annotations.
  • File name = type name. One public type per file.
  • // ReSharper disable once CheckNamespace above namespace when 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, Source included, is compiled by Unity only: no #if UNITY_2023_1_OR_NEWER, UNITY_2022_1_OR_NEWER or TMP guards, no stand-ins for UnityEngine types. UNITY_EDITOR and DEBUG guards stay. The other conditional defines are the integrations ASPID_MVVM_UNITY_LOCALIZATION_INTEGRATION, ASPID_MVVM_ZENJECT_INTEGRATION, ASPID_MVVM_VCONTAINER_INTEGRATION, ASPID_MVVM_ADDRESSABLES_INTEGRATION, ASPID_MVVM_UNITASK_INTEGRATION and the opt-outs ASPID_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 and using (….Auto()) / using (this.Marker()) sits under #if ASPID_MVVM_PROFILER. Generated code spells the same condition out (Defines.ASPID_MVVM_PROFILER in the generator). ENABLE_PROFILER keeps the marker fields and classes out of a release player. The alias keeps its ASPID_MVVM_ prefix: a file-local #define cannot undo a project-wide symbol, so a bare PROFILER in 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, not Centre).

Member ordering (StyleCop SA1201 with one deviation)

  1. Serialized fields ([SerializeField], [SerializeReference])
  2. Other fields
  3. Auto-properties (including protected abstract T X { get; set; })
  4. Constructors
  5. Events
  6. Properties with bodies / expression-bodied
  7. 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 (never if (x is not null)), so the field default survives omission.
  • Default constructor access: protected on non-sealed generic types (a subclass needs a reachable base ctor for Unity serialization); private on sealed types when an empty instance fails at runtime; public with <remarks>Default: …</remarks> when an empty instance is valid.
  • Invalid arguments throw (ArgumentNullException, ArgumentOutOfRangeException with the parameter name); never accept silently.

Methods and expressions

  • Method parameters go one per line only when the signature exceeds 120 columns.
  • Short guard if with return on one line: if (!IsMissing(value)) return value;
  • Brace-less if whose body is an action (assignment, call): body on the next line, indented. One line only for return.
  • if whose body is a single multi-line return …( … ) 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 or return it stays on one line.
  • A chain of if … return over one value becomes a switch expression 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 ==/!= between float/double (ReSharper CompareOfFloatsByEqualityOperator): zero via value is 0d / is not 0f, integrality via value % 1d is 0d, equality of two floats via Mathf.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.
  • typeparam is T, not TFrom, 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 params parameter 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.

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.