Standard Operating Procedures for recurring tasks in the PowerCSharp repository. These SOPs are the concrete, step-by-step companion to
CLAUDE.md. Follow them in order; do not skip verification steps to save time.
When to use: adding a method to an existing PowerCSharp.Extensions, PowerCSharp.Utilities,
PowerCSharp.Helpers, or PowerCSharp.Extensions.AspNetCore class (or a new class within one of
those packages). This is the PowerCSharpVersion family.
- Locate the right home. Match the method to an existing namespace/class by convention
(e.g. string helpers →
PowerCSharp.ExtensionsStringExtensions; validation →PowerCSharp.UtilitiesValidationUtility). Read the target file fully before adding to it — checkdocs/PowerCSharp.<Package>.mdfor the documented surface anddocs/EDGE_CASES_AND_SECURITY.mdfor existing null/edge-case conventions in that area. - Match existing signatures and null-handling style. This codebase favors "safe" variants
that don't throw (
SafeSubstring,FirstOrDefaultSafe) — follow the established pattern for the class you're extending rather than introducing a new error-handling style. - Target frameworks. Confirm the package's
<TargetFrameworks>in its.csproj(net8.0;netstandard2.0for most Core-family packages). If the API you need doesn't exist onnetstandard2.0, either polyfill it or guard with#if NET8_0_OR_GREATER— do not silently drop a target framework. - Nullable + EditorConfig.
Nullableisenablerepo-wide (Directory.Build.props). Follow.editorconfigmember-ordering and style rules; rundotnet formatif unsure. - Write the method with full XML doc comments (
<summary>,<param>,<returns>,<exception>where relevant) — these packages ship withGenerateDocumentationFileand their docs pages are hand-maintained from these comments. - Add tests in the matching
tests/PowerCSharp.<Package>.Testsproject (xunit). Cover: happy path, null/empty input, and at least one boundary case, mirroring the style already used for sibling methods in the same test file. - Update package docs. Add a short usage example to
docs/PowerCSharp.<Package>.mdand, if the method is a headline addition, to the "Usage Examples" section of the rootREADME.md. - Update
CHANGELOG.mdunder an[Unreleased]or next-version heading, following the Keep a Changelog format already in use. - Verify locally:
dotnet build PowerCSharp.sln --configuration Release dotnet test tests/PowerCSharp.<Package>.Tests --configuration Release
- Version family: this change belongs to
PowerCSharpVersion(orPowerCSharpCompatibilityVersionif the target isPowerCSharp.Compatibility— see that package's ownCLAUDE.mdfirst). Do not bump the version yourself; that happens in SOP-03 at release time viaworkflow_dispatch. Note the intended family in your summary to the reviewer. - Branch and hand off. Work on
feature/<short-description>branched fromdevelop. Do not merge or push without explicit approval — present the diff for peer review per the working agreement in place for this repository.
When to use: standing up a new feature under the Features Framework — for example, the
roadmapped PowerCSharp.Feature.Sitecore. Read docs/PowerCSharp.Features.Architecture.md and
docs/PowerCSharp.Features.Authoring-Guide.md in full before starting; this SOP sequences that
guidance into an actionable checklist. Do not begin writing code before both documents have been
read for this session.
- Classify the tier. Run the decision tree in the Authoring Guide §1. A feature that pulls a
third-party SDK (a Sitecore GraphQL/Content Serialization client, for example) or carries
non-trivial implementation is always Pluggable (Group 2) — it never belongs in
PowerCSharp.BuiltInFeatures. - Decide single package vs. family. If there is exactly one implementation, use a single
PowerCSharp.Feature.<Name>package. If there are or will be swappable backends (as with Cache: BitFaster vs. Disk), split intoPowerCSharp.Feature.<Name>(contracts + module) plus onePowerCSharp.Feature.<Name>.<Provider>package per backend. Ask the maintainer if this is ambiguous — do not guess for a feature with real architectural weight like Sitecore. - Scaffold the project(s) under
src/Features/PowerCSharp.Feature.<Name>[.{Provider}]/, matchingdocs/PowerCSharp.Features.PackageLayout.md§1 folder conventions:- Contracts-only packages target
netstandard2.0;net8.0and take zero third-party dependencies beyondMicrosoft.Extensions.Logging.Abstractions. - Implementation packages target
net8.0and isolate their third-party SDK dependency entirely within that package (Section 3.2 of the rootCLAUDE.md).
- Contracts-only packages target
- Define the anatomy (Authoring Guide §2), all four/five pieces:
- a stable
FeatureKeystring (e.g."Sitecore"), used in config asPowerFeatures:<Key>; - the contracts other code will depend on;
- a
FeatureOptionsBasesubclass bound fromPowerFeatures:<Key>; - an
IFeatureModuleimplementation for auto-discovery, plus (optionally) an explicitAdd<Name>Feature()extension method for opt-in registration; - a safe-off (NoOp) behavior for when the flag is disabled — dependents must still resolve.
- a stable
- Wire
ConfigureServicesusing the Cache module (CacheFeatureModule, insrc/Features/PowerCSharp.Feature.Cache/) as the literal template: checkcontext.Flags.IsEnabled(FeatureKey)first, register the NoOp implementation and return early if disabled, otherwise register the real implementation. AddConfigurePipelineonly if the feature contributes middleware. - Add the project(s) to
PowerCSharp.slnunder the existingFeaturessolution folder, plus a matchingtests/PowerCSharp.Feature.<Name>.Testsproject referencingxunit+Microsoft.NET.Test.Sdkat the versions already used by sibling test projects. - Add a new version-family property to
Directory.Build.props(PowerCSharpFeature<Name>Version, e.g.PowerCSharpFeatureSitecoreVersion), starting at1.0.0. Set every new package's<PackageVersion>to that property. Add the family to thepackage_familychoice list in.github/workflows/ci-cd.yml'sworkflow_dispatchinputs. - Write tests covering: flag-off → NoOp is resolved; flag-on → real implementation is
resolved; option binding; and the feature's own core logic. Mirror
tests/PowerCSharp.Feature.Cache.Testsstructure. - Document it: a package
README.md(packed viaPackageReadmeFile, see the Cache.csprojfor the pattern), an entry in the rootREADME.mdpackage table, and adocs/PowerCSharp.Feature.<Name>.mdpage following the existing per-package doc format. - Verify dependency isolation manually. Reference only the new package from a scratch console
app and confirm the third-party SDK does not appear in
dotnet list package --include-transitiveoutput unless that specific package was referenced. - Branch and hand off. Work on
feature/<name>-featurefromdevelop. Present the plan (tier decision, package layout, version-family name) as a<plan>block perCLAUDE.mdSection 5 before writing substantial code, since this is an architectural addition — confirm with the maintainer before scaffolding if any step above required a judgment call.
When to use: a develop-branch feature set is ready to ship to NuGet.org and GitHub Packages
for one or more version families. Full reference: docs/WORKFLOW.md.
- Confirm
developis green.dotnet build PowerCSharp.sln --configuration Releaseanddotnet test PowerCSharp.sln --configuration Releasemust both pass locally before starting; CI will re-verify on push but don't rely on CI to catch avoidable failures. - Identify which version family(ies) changed since the last release:
core(PowerCSharpVersion),features(PowerCSharpFeaturesVersion),cache(PowerCSharpFeatureCacheVersion), or a newly-introduced family from SOP-02. Cross-check againstCHANGELOG.md's[Unreleased]section. - Create the release branch from
develop:git checkout develop git pull origin develop git checkout -b release/v<X.Y.Z>
- Update documentation and changelog on the release branch: move
[Unreleased]entries inCHANGELOG.mdunder the new version heading with today's date, and confirm the rootREADME.mdNuGet badge table doesn't need a new row (new package) or wording updates. - Bump the version(s). Preferred path is the
workflow_dispatchtrigger on.github/workflows/ci-cd.yml(release_type: patch/minor/major,package_family: core/features/cache/<new family>) — this both editsDirectory.Build.propsand creates thev<X.Y.Z>/features-v<X.Y.Z>/cache-v<X.Y.Z>tag automatically. If multiple families changed, run it once per family. Manual edits toDirectory.Build.propsare the fallback only ifworkflow_dispatchis unavailable. - Open the PR:
release/v<X.Y.Z>→main, including release notes, a summary of testing performed, and any breaking changes called out explicitly (perCLAUDE.mdSection 2.9). - On merge to
main, the pipeline automatically: builds and tests, packs (dotnet pack PowerCSharp.sln --configuration Release --output ./packages), publishes to NuGet.org and GitHub Packages, and deletes therelease/*branch. Confirm the packages appear on nuget.org/GitHub Packages after the workflow completes — do not assume a green pipeline run equals a successful publish without checking. - Verify Codecov picked up the coverage report for the release build (badge in
README.md). - Do not commit, tag, or push any step above without explicit sign-off unless the user has directed you to execute the release directly — treat release actions as high-blast-radius by default and confirm before executing steps 3, 5, 6, and 7.