From 35508dae929026317734d5f1b9f84f9604987ce4 Mon Sep 17 00:00:00 2001 From: Eric Sibly Date: Thu, 2 Jul 2026 10:50:17 -0700 Subject: [PATCH 01/13] First pass Instructions and Skills split/sync refactor. --- .github/AI-WORKFLOWS.md | 37 +++++++- .github/INSTRUCTION_AUTHORING.md | 53 ++++++++++-- .github/SKILL_AUTHORING.md | 43 +++++++--- .github/agents/README.md | 45 +++++----- .github/agents/coreex-expert.agent.md | 31 +++++-- .github/copilot-instructions.md | 28 ++++-- .../coreex-api-controllers.instructions.md | 9 +- ...oreex-application-services.instructions.md | 16 ++-- .../coreex-contracts.instructions.md | 18 ++-- .../coreex-domain.instructions.md | 13 ++- .../coreex-event-subscribers.instructions.md | 11 ++- .../coreex-host-setup.instructions.md | 7 +- .../coreex-repositories.instructions.md | 16 ++-- .../instructions/coreex-tests.instructions.md | 14 ++- .../coreex-tooling.instructions.md | 86 ++++++++----------- .../coreex-validators.instructions.md | 11 ++- .github/skills/coreex-adapter/SKILL.md | 21 +++-- .github/skills/coreex-aggregate/SKILL.md | 24 ++++-- .../coreex-aggregate/references/workflow.md | 4 +- .github/skills/coreex-api/SKILL.md | 15 ++-- .github/skills/coreex-app-service/SKILL.md | 16 ++-- .github/skills/coreex-contract/SKILL.md | 7 +- .../coreex-contract/references/workflow.md | 4 + .github/skills/coreex-db-migration/SKILL.md | 11 ++- .../references/workflow.md | 23 ++++- .github/skills/coreex-docs-sync/README.md | 4 +- .github/skills/coreex-docs-sync/SKILL.md | 8 +- .github/skills/coreex-policy/SKILL.md | 7 +- .github/skills/coreex-refdata/SKILL.md | 13 ++- .github/skills/coreex-repository/SKILL.md | 9 +- .github/skills/coreex-subscriber/SKILL.md | 30 ++++--- .../coreex-subscriber/references/workflow.md | 12 ++- .github/skills/coreex-test-api/SKILL.md | 16 ++-- .../coreex-test-api/references/workflow.md | 3 + .github/skills/coreex-test-relay/SKILL.md | 17 ++-- .github/skills/coreex-test-subscribe/SKILL.md | 14 ++- .github/skills/coreex-validator/SKILL.md | 8 +- .../coreex-validator/references/workflow.md | 2 + AGENTS.md | 10 ++- src/CoreEx.Template/CoreEx.Template.csproj | 37 +++++++- .../content/CoreEx.Core/AGENTS.md | 2 +- 41 files changed, 532 insertions(+), 223 deletions(-) diff --git a/.github/AI-WORKFLOWS.md b/.github/AI-WORKFLOWS.md index 03fcd96a..9a83b853 100644 --- a/.github/AI-WORKFLOWS.md +++ b/.github/AI-WORKFLOWS.md @@ -42,10 +42,43 @@ Instructions are passive — no action is needed to activate them. The global fi ## Prompts, Skills, and Templates +### Skills vs Prompts vs Instructions + +Three artefact types cooperate, each with a distinct job: + +- **Instruction** (`instructions/*.instructions.md`) — invariant rules that are *auto-injected* whenever an edited file matches the instruction's `applyTo` glob. You never invoke them; they passively shape every edit to a matching file. +- **Skill** (`skills/coreex-*/`) — a reasoning *workflow* you invoke explicitly (`/coreex-` in Claude Code) to create or modify something. Each skill owns a `SKILL.md` and a `references/workflow.md` that drive the interaction. +- **Prompt** (`prompts/coreex-*.prompt.md`) — the GitHub Copilot entry point that *delegates to the matching skill's workflow*. Skills and prompts map 1:1 by name, so `/coreex-contract` (Claude Code skill) and `coreex-contract.prompt.md` (Copilot prompt) run the same workflow. + +**Feature Configuration.** A CoreEx solution's project-wide choices — `data-provider`, `refdata-enabled`, `domain-driven-enabled`, `rop-enabled`, `outbox-enabled`, `messaging-provider` — are persisted in the solution-root `AGENTS.md` **Feature Configuration** block. Skills read that block before asking anything, so recorded decisions are not re-prompted from one skill to the next. + +### Command catalog + | Command | Type | What it does | |---------|------|-------------| -| [`CoreEx.Template`](../src/CoreEx.Template/README.md) | Template pack | Deterministic `dotnet new` scaffolding for a CoreEx solution plus API, relay, and subscriber hosts. Use `dotnet new install CoreEx.Template` and then the `coreex*` templates in a terminal. | +| [`CoreEx.Template`](../src/CoreEx.Template/README.md) | Template pack | Deterministic `dotnet new` scaffolding for a CoreEx solution plus API, relay, and subscriber hosts. Use `dotnet new install CoreEx.Template` and then the `coreex*` templates in a terminal. `dotnet new coreex-ai` installs this full AI workflow set (instructions, prompts, skills, the `coreex-expert` agent, `.claude/commands/`, and the `.github/docs/coreex/` docs cache) into a consuming project. | | [`/acquire-codebase-knowledge`](./skills/acquire-codebase-knowledge/README.md) | Skill | Maps an unfamiliar codebase and produces seven structured onboarding documents. | -| [`/coreex-scaffold`](./skills/solution-scaffolder/README.md) | Skill-backed prompt | Guides greenfield solution scaffolding, chooses the smallest safe CoreEx.Template shape, and runs the matching `dotnet new coreex*` commands. | +| [`/coreex-scaffold`](./skills/solution-scaffolder/README.md) · [prompt](./prompts/coreex-scaffold.prompt.md) | Skill + prompt | Guides greenfield solution scaffolding, chooses the smallest safe CoreEx.Template shape, and runs the matching `dotnet new coreex*` commands. | | [`/coreex-docs-sync`](./skills/coreex-docs-sync/README.md) | Skill | Fetches and caches CoreEx architecture docs and all per-package AI guides locally under `.github/docs/coreex/`. | | [`/aspire`](./skills/aspire/README.md) | Skill | Orchestrates Aspire distributed apps locally: start, stop, logs, debug. | + +#### Per-capability skills (L1) + +Fourteen skills add or modify a single CoreEx capability on an existing solution. Each is invoked as `/coreex-` in Claude Code, or via the matching [`prompts/coreex-.prompt.md`](./prompts/) in Copilot (1:1 by name). Every skill reads the solution-root `AGENTS.md` **Feature Configuration** first to avoid redundant questioning. + +| Skill / prompt | Capability | +|----------------|-----------| +| [`coreex-contract`](./skills/coreex-contract/) | Hand-authored contract (DTO/entity) — root, subordinate, request/response, base class | +| [`coreex-refdata`](./skills/coreex-refdata/) | Reference data type + `ref-data.yaml` entry | +| [`coreex-db-migration`](./skills/coreex-db-migration/) | Database table / DbEx migration | +| [`coreex-repository`](./skills/coreex-repository/) | EF Core repository, mapper, and query configuration | +| [`coreex-adapter`](./skills/coreex-adapter/) | External-integration adapter | +| [`coreex-app-service`](./skills/coreex-app-service/) | Application service orchestration | +| [`coreex-validator`](./skills/coreex-validator/) | Fluent `Validator` | +| [`coreex-policy`](./skills/coreex-policy/) | Authorization / business policy | +| [`coreex-aggregate`](./skills/coreex-aggregate/) | DDD aggregate / domain entity | +| [`coreex-api`](./skills/coreex-api/) | API controller / endpoint | +| [`coreex-subscriber`](./skills/coreex-subscriber/) | Event subscriber | +| [`coreex-test-api`](./skills/coreex-test-api/) | API tests | +| [`coreex-test-subscribe`](./skills/coreex-test-subscribe/) | Subscriber tests | +| [`coreex-test-relay`](./skills/coreex-test-relay/) | Outbox relay tests | diff --git a/.github/INSTRUCTION_AUTHORING.md b/.github/INSTRUCTION_AUTHORING.md index 187eafe4..7e51f34b 100644 --- a/.github/INSTRUCTION_AUTHORING.md +++ b/.github/INSTRUCTION_AUTHORING.md @@ -21,12 +21,49 @@ Write instruction files as you would a concise internal coding guide for a capab - **Be specific.** Use actual type names, package names, and method names from this codebase. - **Be brief.** If content cannot fit on one screen, split into a separate file or move detail to `Further Reading`. - **Do not restate global rules.** If `.github/copilot-instructions.md` already covers it, do not repeat it here. +- **Invariants, not procedures.** An instruction file states the rules that must hold on *any* edit to a matching + file. A multi-step "how to create X from scratch" procedure belongs in a **skill** (`.github/skills/coreex-*/`), + which is invoked explicitly. When an instruction is tempted to describe a creation workflow, replace it with a + one-line pointer to the skill. See [Instructions vs. Skills](#instructions-vs-skills). - **Explicit negation matters.** Copilot's training data contains many common patterns that are wrong for this repo. State anti-patterns explicitly with a `## Do Not` section. - **Scope tightly.** An instruction that is always injected regardless of context wastes tokens. Use the narrowest `applyTo` glob that is still correct. +- **References must resolve in a consumer repo.** Instruction files ship to consumers via `dotnet new coreex-ai`, + where this repo's `samples/` and `src/CoreEx.*` do not exist. For deeper reading, link the docs-sync cache + (`/.github/docs/coreex/.md`, present after `coreex-ai` / `/coreex-docs-sync`) or a full GitHub URL — never + a bare local `../../samples/…` or `../../src/CoreEx…` path. - **Never direct Copilot toward generated files.** Instruction files must never include guidance, examples, or `applyTo` globs that would cause Copilot to create or modify `*.g.cs`, `*.g.sql`, `*.g.pgsql`, or any other generated-output file. All generated files are owned exclusively by their corresponding tooling (Roslyn source generator, `*.Database` project, `*.CodeGen` project). Changes must be made to the source templates or generation configuration, not to the output. See [Generated Code](#generated-code) below. --- +## Instructions vs. Skills + +The AI workflow set has two complementary asset types. Keep the boundary crisp: + +| | Instruction (`.github/instructions/*.instructions.md`) | Skill (`.github/skills/coreex-*/`) | +|---|---|---| +| **Trigger** | Auto-injected when an edited file matches `applyTo` | Invoked explicitly by the developer | +| **Content** | **Invariant rules** that must hold on *any* edit | **Step-by-step procedure** to create/modify something | +| **Shape** | Rule + code example, no multi-step workflow | Interview → phased workflow → checklists | + +**When an instruction starts describing a creation procedure, extract it.** Move the steps into the matching skill's +`references/workflow.md` and leave a one-line pointer in the instruction: + +```markdown +> To scaffold a new reference data type, invoke the [`coreex-refdata`](/.github/skills/coreex-refdata/SKILL.md) skill. +``` + +Keep in the instruction only the invariants that must hold whenever such a file is edited (naming, types, +generated-file ownership, decision gates like "root vs subordinate"). A single decision point ("confirm idempotency +for every POST") is an invariant and stays; a numbered "detect → confirm → scaffold → verify" sequence is a +procedure and moves to the skill. + +**Project-wide choices live in state, not in prompts.** The solution-root `AGENTS.md` "Feature Configuration" +records `data-provider`, `rop-enabled`, `domain-driven-enabled`, `refdata-enabled`, `outbox-enabled`, and +`messaging-provider`. Instructions and skills must **read that recording before asking** and re-state resolved +values for confirmation rather than re-prompting (the global rule lives in `.github/copilot-instructions.md`). + +--- + ## Required Format ```markdown @@ -63,8 +100,8 @@ tags: ["tag1", "tag2"] ## Further Reading -- [`samples/docs/.md`](../../samples/docs/.md) — layer-level walkthrough with sample code references. -- [`src/CoreEx.X/README.md`](../../src/CoreEx.X/README.md) — full API reference for the primary package. +- [`` deep-dive](/.github/docs/coreex/.md) — layer-level walkthrough (docs-sync cache). +- Related skill: [`coreex-`](/.github/skills/coreex-/SKILL.md) — invoke to scaffold. ``` --- @@ -119,13 +156,15 @@ List things Copilot must not generate for this area. Be specific: name the wrong ### `## Further Reading` — required -Link to the layer-level `samples/docs/` walkthrough and any directly relevant `src/*/README.md` files. Copilot will fetch these when it needs deeper context, keeping the instruction file itself lean. +Link the layer-level walkthrough from the docs-sync cache (`/.github/docs/coreex/.md`) and the **related +skill** for creation workflows. These resolve in a consumer repo; a bare `../../samples/docs/…` path does not. +Copilot/Claude fetch these for deeper context, keeping the instruction file itself lean. ```markdown ## Further Reading -- [`samples/docs/application-layer.md`](../../samples/docs/application-layer.md) — application service patterns with sample code. -- [`src/CoreEx/Results/README.md`](../../src/CoreEx/Results/README.md) — `Result` pipeline API reference. +- [Application layer deep-dive](/.github/docs/coreex/application-layer.md) — service patterns (docs-sync cache). +- Related skill: [`coreex-app-service`](/.github/skills/coreex-app-service/SKILL.md) — invoke to scaffold a service. ``` --- @@ -241,6 +280,6 @@ public Task CreateAsync([FromBody] Product product) => ## Further Reading -- [`samples/docs/hosts-layer.md`](../../samples/docs/hosts-layer.md) — API host composition and controller patterns. -- [`src/CoreEx.AspNetCore/README.md`](../../src/CoreEx.AspNetCore/README.md) — `WebApi` helper API reference. +- [Hosts layer deep-dive](/.github/docs/coreex/hosts-layer.md) — API host composition and controller patterns. +- Related skill: [`coreex-api`](/.github/skills/coreex-api/SKILL.md) — invoke to scaffold a controller/endpoint. ``` diff --git a/.github/SKILL_AUTHORING.md b/.github/SKILL_AUTHORING.md index 18653ed8..543a90e1 100644 --- a/.github/SKILL_AUTHORING.md +++ b/.github/SKILL_AUTHORING.md @@ -59,18 +59,36 @@ Each file stays focused on one concern. No file should exceed what is readable i Reusable templates and examples: - **assets/templates/** — boilerplate code, project structures (copy-and-fill files) -- **assets/examples/** — concrete working examples from the repo (links only, no duplicates) +- **assets/examples/** — concrete working examples (links only, no duplicates) -**Important**: Never maintain duplicate copies of sample code. Always link to the canonical source in `samples/` or other repository locations. +**Important**: Never maintain duplicate copies of sample code. -## Cross-Referencing +## Cross-Referencing (consumer-agnostic) -When skills reference each other, instructions, or samples: +> **These skills ship to consumer repositories** via `dotnet new coreex-ai` (the `CoreEx.Template` pack). +> A consumer repo does **not** contain this repository's `samples/` or `src/CoreEx.*` source. A skill must +> therefore be **domain-agnostic** and must not link to local paths that only exist inside the CoreEx repo. +> A link that resolves here but is dead in a consumer repo is a defect. -- **Relative paths**: `../other-skill/references/...` (for other skills) -- **Absolute workspace paths**: `/.github/instructions/coreex-host-setup.instructions.md`, `/samples/src/Contoso.Products.Api/Program.cs` -- Always verify links work before committing -- Prefer workspace-relative links for durability +Reference targets, in priority order: + +1. **Installed instructions** — always present in a consumer repo. Absolute workspace path, markdown link: + `[Application Services](/.github/instructions/coreex-application-services.instructions.md)`. +2. **Sibling skills** — relative markdown link to the other skill: `[coreex-repository](../coreex-repository/SKILL.md)`. + For "invoke this skill instead" mentions, a bare backtick name (`` `coreex-repository` ``) is fine — that is how a + user invokes it. +3. **Labeled illustrative examples** — concrete `samples/` or `src/CoreEx.*` code linked as a **full GitHub URL**, + clearly marked as an external example, e.g. + `[ProductController (CoreEx sample — illustrative, not in your project)](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Api/Controllers)`. +4. **docs-sync cache** (`/.github/docs/coreex/*.md`, `/.github/docs/coreex/agents/*.md`) — optional, secondary. + Present after `dotnet new coreex-ai` (it now ships the cache) and refreshable via `/coreex-docs-sync`. Never the + *only* pointer, so a skill still works if the cache is absent. + +Rules: +- **Never** use a bare local `samples/…` or `src/CoreEx…` path in a skill — use a GitHub URL (target 3) instead. +- Keep skill bodies domain-agnostic: placeholders (`{solution}`, `{domain}`, `{Entity}`) in copyable code; + concrete sample names only in prose, clearly framed as examples ("e.g. …"). +- Verify every link before committing; prefer the always-present targets (1, 2) as the backbone. ## Frontmatter Requirements @@ -132,8 +150,9 @@ For detailed step-by-step guidance, see [`references/workflow.md`](references/wo - [Application Services Instructions](/.github/instructions/coreex-application-services.instructions.md) - [Contracts Instructions](/.github/instructions/coreex-contracts.instructions.md) - [Host Setup Instructions](/.github/instructions/coreex-host-setup.instructions.md) -- [CoreEx Template Pack](../src/CoreEx.Template/README.md) -- [CoreEx Capabilities](./docs/capabilities.md) +- [coreex-app-service](../coreex-app-service/SKILL.md) — related skill +- [Application layer deep-dive](/.github/docs/coreex/application-layer.md) — optional (after `/coreex-docs-sync`) +- [ProductService (CoreEx sample — illustrative)](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Application) ``` ## Quality Gates @@ -146,4 +165,6 @@ Before completing a skill: - [ ] YAML frontmatter is valid - [ ] No inline workflows or checklists in main SKILL.md - [ ] Cross-references to instructions are correct -- [ ] Example links point to real, canonical code locations +- [ ] **No bare local `samples/…` or `src/CoreEx…` paths** — illustrative examples use full GitHub URLs, labeled +- [ ] Body is domain-agnostic — placeholders in code; sample names only as framed prose examples +- [ ] The reference backbone (instructions + sibling skills) resolves in a consumer repo without the docs cache diff --git a/.github/agents/README.md b/.github/agents/README.md index 25a0963e..639d3137 100644 --- a/.github/agents/README.md +++ b/.github/agents/README.md @@ -101,31 +101,28 @@ An earlier design synced only the packages the project already references. This ## Adopting the agent in a consuming project -Copy the following from this repository into any project that references CoreEx NuGet packages: +Do not copy files by hand. The whole AI workflow set — including this agent — is installed by the `coreex-ai` template. From the **repo root** of the project that references CoreEx NuGet packages: +```bash +# Install the template pack once (skip if already installed) +dotnet new install CoreEx.Template + +# Single-repo project: +dotnet new coreex-ai + +# Monorepo — replace with the CoreEx app path (e.g. backend): +dotnet new coreex-ai --app-folder ``` -.github/ - copilot-instructions.md - agents/ - coreex-expert.agent.md - instructions/ - coreex-conventions.instructions.md - coreex-contracts.instructions.md - coreex-application-services.instructions.md - coreex-validators.instructions.md - coreex-repositories.instructions.md - coreex-api-controllers.instructions.md - coreex-event-subscribers.instructions.md - coreex-host-setup.instructions.md - coreex-tooling.instructions.md - coreex-tests.instructions.md - coreex-domain.instructions.md - skills/ - coreex-docs-sync/ - SKILL.md - acquire-codebase-knowledge/ # optional — repo onboarding docs -``` -On first use, run `/coreex-docs-sync` to populate the local cache. Re-run whenever the CoreEx NuGet version is bumped. +`dotnet new coreex-ai` installs everything the agent depends on: + +- `.github/instructions/` — the scoped, auto-injected instruction files +- `.github/prompts/` — the `coreex-scaffold` prompt plus one prompt per per-capability (L1) skill +- `.github/skills/` — the full skill suite (`coreex-docs-sync`, `acquire-codebase-knowledge`, `solution-scaffolder`, `aspire`, and the 14 L1 skills) +- `.github/agents/coreex-expert.agent.md` — this agent +- `.claude/commands/` — the Claude Code equivalents +- `.github/docs/coreex/` — the local docs cache (architecture docs + per-package guides) the expert reads first + +After install, run `/coreex-docs-sync` once to populate the docs cache, then re-run it whenever the CoreEx NuGet version is bumped — the agent recommends this automatically when the cached `coreex-version` no longer matches the project (see the resolution flowchart above). -For deterministic project scaffolding, use the [CoreEx.Template](../src/CoreEx.Template/README.md) `dotnet new` template pack rather than an agent skill. +For deterministic project scaffolding, use the [CoreEx.Template](../src/CoreEx.Template/README.md) `dotnet new coreex*` template pack rather than an agent skill. diff --git a/.github/agents/coreex-expert.agent.md b/.github/agents/coreex-expert.agent.md index 7d86786a..288c280a 100644 --- a/.github/agents/coreex-expert.agent.md +++ b/.github/agents/coreex-expert.agent.md @@ -105,11 +105,32 @@ Do not set up the local cache silently — always offer and wait for confirmatio ## Decision routing -These skills are part of the CoreEx AI workflow set and live in `.github/skills/`. They can be copied from the [CoreEx repository](https://github.com/Avanade/CoreEx/tree/main/.github/skills) into a consuming project: - -- Greenfield domain or host scaffolding → advise using the [CoreEx.Template](../../src/CoreEx.Template/README.md) `dotnet new` templates. -- Retrofit capability on an existing domain → inspect the current code and recommend the smallest manual changes aligned to the samples and instructions. -- Repo mapping or onboarding documentation → advise using `/acquire-codebase-knowledge`. +These skills are part of the CoreEx AI workflow set and live in `.github/skills/` (installed into a consuming project by `dotnet new coreex-ai`). When a request maps to a skill, route to it rather than authoring changes freehand — each skill reads the solution-root `AGENTS.md` **Feature Configuration** first to avoid re-prompting for recorded project choices. + +**Add or modify a single capability** → route to the matching per-capability (L1) skill: + +| Request | Skill | +|---------|-------| +| Contract / DTO / entity | `/coreex-contract` | +| Reference data | `/coreex-refdata` | +| Database table or migration | `/coreex-db-migration` | +| Repository, mapper, query config | `/coreex-repository` | +| External integration / adapter | `/coreex-adapter` | +| Application service | `/coreex-app-service` | +| Validator | `/coreex-validator` | +| Authorization / business policy | `/coreex-policy` | +| DDD aggregate / domain entity | `/coreex-aggregate` | +| API controller / endpoint | `/coreex-api` | +| Event subscriber | `/coreex-subscriber` | +| API tests | `/coreex-test-api` | +| Subscriber tests | `/coreex-test-subscribe` | +| Outbox relay tests | `/coreex-test-relay` | + +**Broader routing:** + +- Greenfield solution or host scaffolding → `/coreex-scaffold` (`solution-scaffolder` skill), which runs the matching [CoreEx.Template](../../src/CoreEx.Template/README.md) `dotnet new coreex*` commands. +- Repo mapping or onboarding documentation → `/acquire-codebase-knowledge`. +- Retrofit that no single skill covers → inspect the current code and recommend the smallest manual changes aligned to the samples and instructions. ## Response format diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 09c7dea0..a3b2ae62 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -9,6 +9,20 @@ tags: ["guidelines", "conventions", "comments"] ## Purpose CoreEx is a modular .NET framework for enterprise APIs and distributed services. Favor CoreEx-native primitives, patterns, and extensions over ad-hoc implementations. +## Project Configuration State (read before asking) + +A CoreEx solution scaffolded with `dotnet new coreex` records its project-wide choices in the **solution-root +`AGENTS.md` "Feature Configuration"** block: `data-provider` (SQL Server / PostgreSQL / None), `refdata-enabled`, +`domain-driven-enabled`, `rop-enabled` (exception vs `Result`), `outbox-enabled`, and `messaging-provider`. + +Before asking the user a project-wide question, **resolve it from that recording first**, cross-checked against the +real artefacts (package references, `dbex.yaml`, presence of the `*.Domain` project). Re-state the resolved values for +confirmation rather than re-prompting; if the recording and artefacts disagree, **stop and flag** rather than guessing. +When a change enables a feature the recording marks disabled (e.g. the first ref-data type in a `refdata-enabled: +false` solution), update the recording. The skills and instructions rely on this — it is what keeps them from +re-asking `data-provider`/`rop-enabled`/etc. on every task. (In this framework repository there is no such generated +solution; the rule applies to consumer solutions where these assets are installed.) + ## Repository Shape - `CoreEx.sln`: main solution for framework + samples. - `src\`: reusable CoreEx libraries (AspNetCore, Database, EntityFrameworkCore, Events, Validation, DomainDriven, RefData, Caching, etc.). @@ -166,14 +180,18 @@ See [INSTRUCTION_AUTHORING.md](INSTRUCTION_AUTHORING.md#generated-code) for full ## Agent Customizations (Prompts, Skills, and Templates) -The following prompts, skills, and templates are available in this repository. Type `/` in chat to invoke prompts and skills. Use `dotnet new` in a terminal for templates. +The following prompts, skills, and templates are available. Type `/` in chat to invoke prompts and skills; use +`dotnet new` in a terminal for templates. For the full catalog and the skills-vs-prompts-vs-instructions distinction, +see [AI-WORKFLOWS.md](./AI-WORKFLOWS.md). | Command | Type | When to use | |---------|------|-------------| -| `CoreEx.Template` | Template pack | Deterministic `dotnet new` scaffolding for solution, API, relay, and subscriber shapes. Use `dotnet new install CoreEx.Template` and then run `dotnet new coreex`, `coreex-api`, `coreex-relay`, or `coreex-subscribe` as needed. | -| `CoreEx Expert` | Agent | Architecture guidance, pattern recommendations, and design review aligned to the samples and repo instructions. | -| `/init` | Prompt | Initialize a new CoreEx solution or workspace. | -| `/setup` | Prompt | Configure an existing CoreEx solution with standard tooling and settings. | +| `CoreEx.Template` | Template pack | Deterministic `dotnet new` scaffolding. `dotnet new install CoreEx.Template`, then `dotnet new coreex` (solution), `coreex-api` / `coreex-relay` / `coreex-subscribe` (hosts), or `coreex-ai` (AI workflow assets). | +| `CoreEx Expert` | Agent | Architecture guidance, pattern recommendations, and design review. Invoke via `/coreex-expert` (or `@coreex-expert`). | +| `/coreex-scaffold` | Skill-backed prompt | Guided greenfield solution scaffolding (chooses the smallest safe shape, runs the `dotnet new coreex*` commands). | +| `/coreex-docs-sync` | Skill | Refresh the local `.github/docs/coreex/` doc cache after a CoreEx version bump. | +| `/coreex-` | Skills (L1) + matching prompts | Add or modify one building block: `coreex-contract`, `coreex-refdata`, `coreex-db-migration`, `coreex-repository`, `coreex-adapter`, `coreex-app-service`, `coreex-validator`, `coreex-policy`, `coreex-aggregate`, `coreex-api`, `coreex-subscriber`, and `coreex-test-api` / `coreex-test-subscribe` / `coreex-test-relay`. Each skill has a `.prompt.md` wrapper for Copilot. | +| `/acquire-codebase-knowledge`, `/aspire` | Skills | Repo onboarding documentation; local Aspire orchestration. | ## Guidance for Authoring Instructions and Skills diff --git a/.github/instructions/coreex-api-controllers.instructions.md b/.github/instructions/coreex-api-controllers.instructions.md index e8248f6b..a3f0c840 100644 --- a/.github/instructions/coreex-api-controllers.instructions.md +++ b/.github/instructions/coreex-api-controllers.instructions.md @@ -6,6 +6,10 @@ tags: ["controllers", "api", "routing", "cqrs", "dependency-injection", "minimal # API Conventions +> **Related skill:** to scaffold a new controller or endpoint, invoke the [`coreex-api`](/.github/skills/coreex-api/SKILL.md) skill. +> This file holds the invariants that must hold on **any** edit to a controller/endpoint file; the skill drives the +> step-by-step **creation** procedure. + > **Precondition — the Api host must exist.** Controllers live in the `*.Api` host, which is **not** part of the base `coreex` solution. Before authoring a controller, confirm an Api host is present (`**/*.Api/*.Api.csproj`); if it is absent, run the scaffolding workflow first (see `coreex-host-setup.instructions.md` → "Scaffolding an API host") — which confirms creation, generates it via the `coreex-api` template using the recorded solution options, and adds the new projects to the solution (`dotnet sln add`) as the final in-session step. Also ensure the entity's application service exists (create per *Service Operations — Confirm Scope* in `coreex-application-services.instructions.md` if not). > **Maintain the API tests alongside the controller.** When you create or change a controller's operations, **offer to create or update the matching `XxxReadTests` / `XxxMutateTests`** (per-entity, one partial file per operation) in the `*.Test.Api` project — see `coreex-tests.instructions.md` → "API Tests — Structure & Generation". If accepted, co-design the seed data, tests, and `.res.json`/`.req.json` resources together; if declined, proceed but note the coverage gap. @@ -324,7 +328,8 @@ All the same rules apply as for MVC controllers: no business logic in the handle ## Further Reading -- [Hosts Layer Guide](https://github.com/Avanade/CoreEx/blob/main/samples/docs/hosts-layer.md) — API host composition, controller patterns, and `Program.cs` shape. -- [CoreEx.AspNetCore README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.AspNetCore/README.md) — `WebApi` helper API reference. +- [Hosts Layer Guide](/.github/docs/coreex/hosts-layer.md) — API host composition, controller patterns, and `Program.cs` shape (docs-sync cache; after `/coreex-docs-sync`). +- [CoreEx.AspNetCore guide](/.github/docs/coreex/agents/CoreEx.AspNetCore.md) — `WebApi` helper API reference (docs-sync cache; after `/coreex-docs-sync`). Source: [CoreEx.AspNetCore README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.AspNetCore/README.md). - [CoreEx.AspNetCore Mvc README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.AspNetCore/Mvc/README.md) — MVC `WebApi` (`IActionResult`-returning), action attributes, and controller patterns. - [CoreEx.AspNetCore Http README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.AspNetCore/Http/README.md) — Minimal API `WebApi` (`IResult`-returning) and `RouteHandlerBuilder` extensions. +- Related skill: [`coreex-api`](/.github/skills/coreex-api/SKILL.md) — invoke to scaffold a controller/endpoint. diff --git a/.github/instructions/coreex-application-services.instructions.md b/.github/instructions/coreex-application-services.instructions.md index baf88c48..72fb7938 100644 --- a/.github/instructions/coreex-application-services.instructions.md +++ b/.github/instructions/coreex-application-services.instructions.md @@ -6,6 +6,11 @@ tags: ["services", "application-layer", "dependency-injection", "validation", "u # Application Service Conventions +> **Related skill:** to scaffold a new application service, invoke the [`coreex-app-service`](/.github/skills/coreex-app-service/SKILL.md) skill; +> for the validator it calls invoke [`coreex-validator`](/.github/skills/coreex-validator/SKILL.md), and for a domain-guard policy invoke [`coreex-policy`](/.github/skills/coreex-policy/SKILL.md). +> This file holds the invariants that must hold on **any** edit to an Application-layer file; the skills drive the +> step-by-step **creation** procedures. + ## NuGet / Project References | Package | Key types provided | @@ -472,9 +477,10 @@ Always call `.ConfigureAwait(false)` on every `await` inside service and reposit ## Further Reading -- [Application Layer Guide](https://github.com/Avanade/CoreEx/blob/main/samples/docs/application-layer.md) — full walkthrough of services, validators, adapters, policies, mapping, and the unit-of-work pattern. -- [Pattern Catalog](https://github.com/Avanade/CoreEx/blob/main/samples/docs/patterns.md) — CQRS, Service, Unit of Work, Validator, Policy, Adapter, and Event patterns with cross-links. -- [Layer Dependencies](https://github.com/Avanade/CoreEx/blob/main/samples/docs/layers.md) — layer dependency rules: Application depends inward only on Contracts and its own interfaces. -- [CoreEx.Validation README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.Validation/README.md) — `Validator`, rule set, `OnValidateAsync`, and `ValidateFurtherAsync`. -- [CoreEx README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx/README.md) — `IUnitOfWork`, `Result`, `[ScopedService]`, and CoreEx exception types. +- [Application Layer Guide](/.github/docs/coreex/application-layer.md) — full walkthrough of services, validators, adapters, policies, mapping, and the unit-of-work pattern (docs-sync cache; after `/coreex-docs-sync`). +- [Pattern Catalog](/.github/docs/coreex/patterns.md) — CQRS, Service, Unit of Work, Validator, Policy, Adapter, and Event patterns with cross-links (docs-sync cache; after `/coreex-docs-sync`). +- [Layer Dependencies](/.github/docs/coreex/layers.md) — layer dependency rules: Application depends inward only on Contracts and its own interfaces (docs-sync cache; after `/coreex-docs-sync`). +- [CoreEx.Validation guide](/.github/docs/coreex/agents/CoreEx.Validation.md) — `Validator`, rule set, `OnValidateAsync`, and `ValidateFurtherAsync` (docs-sync cache; after `/coreex-docs-sync`). Source: [CoreEx.Validation README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.Validation/README.md). +- [CoreEx guide](/.github/docs/coreex/agents/CoreEx.md) — `IUnitOfWork`, `Result`, `[ScopedService]`, and CoreEx exception types (docs-sync cache; after `/coreex-docs-sync`). Source: [CoreEx README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx/README.md). - [CoreEx Results README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx/Results/README.md) — `Result` type, pipeline operators (`.GoAsync`, `.ThenAs`, `.ThenAsAsync`), and error propagation semantics. +- Related skills: [`coreex-app-service`](/.github/skills/coreex-app-service/SKILL.md) (scaffold a service), [`coreex-validator`](/.github/skills/coreex-validator/SKILL.md) (scaffold a validator), [`coreex-policy`](/.github/skills/coreex-policy/SKILL.md) (scaffold a domain-guard policy). diff --git a/.github/instructions/coreex-contracts.instructions.md b/.github/instructions/coreex-contracts.instructions.md index 794a1268..6cbade25 100644 --- a/.github/instructions/coreex-contracts.instructions.md +++ b/.github/instructions/coreex-contracts.instructions.md @@ -6,6 +6,11 @@ tags: ["contracts", "dto", "source-generation", "reference-data", "etag"] # Contract (DTO) Conventions +> **Related skill:** to create or modify a contract/DTO, invoke the [`coreex-contract`](/.github/skills/coreex-contract/SKILL.md) +> skill; for a **reference data** type, invoke [`coreex-refdata`](/.github/skills/coreex-refdata/SKILL.md). This file holds +> the invariants that must hold on **any** contract edit (root-vs-subordinate classification, property type resolution, +> marker interfaces); the skills drive the step-by-step creation procedure. + ## File Placement Contracts live **flat in the root of the `*.Contracts` project** — both hand-authored (`Product.cs`) and generated (`Product.g.cs`) files. **Do not create sub-folders** such as `Entities/`, `Models/`, or `RefData/` to group them; the samples place every contract at the project root regardless of whether it is a root entity, subordinate, request/response DTO, or reference-data type. Only introduce a sub-folder if the **user explicitly asks** for one. The same flat-root convention applies to the matching files in the other layers (validators, services, repositories, mappers) unless their instructions state otherwise. @@ -283,14 +288,11 @@ public partial class ProductLite : ProductBase Reference data contracts are **generated, not hand-authored**. The source of truth is the `entities:` section of `ref-data.yaml` in the domain's `*.CodeGen` project. Running the CodeGen generates all artefacts across every layer -- contract class, API endpoint, service method, repository interface, repository implementation, and mapper -- as `.g.cs` files that must never be edited directly. -> **Agent instruction:** When asked to create or modify a reference data type: -> 1. Edit `ref-data.yaml` in `tools/[domain].CodeGen/` -- add or update the entry under `entities:`. -> 2. Offer to run `dotnet run` from the CodeGen directory on the user's behalf. -> 3. If confirmed, execute it and summarise the generated artefacts on success; on failure relay the **complete output verbatim** — it provides the diagnostic needed to fix the entry. -> 4. On failure, fix the issue in `ref-data.yaml` and offer to re-run -- do not create or edit `.g.cs` files to work around a generation error. -> 5. If the user declines, remind them to run `dotnet run` from the `*.CodeGen` directory before the new types are available. -> -> If the user **explicitly requests** hand-authoring instead of CodeGen, use the pattern shown in [Hand-authored contracts (explicit request only)](#hand-authored-contracts-explicit-request-only) below. +> **Creation procedure → skill.** To create or modify a reference data type, invoke the +> [`coreex-refdata`](/.github/skills/coreex-refdata/SKILL.md) skill — it drives the `ref-data.yaml` edit and the +> `*.CodeGen` run across every layer. Never edit a generated `.g.cs` to work around a generation error — fix +> `ref-data.yaml` and re-run. If the user **explicitly requests** hand-authoring instead of CodeGen, use the pattern in +> [Hand-authored contracts (explicit request only)](#hand-authored-contracts-explicit-request-only) below. ### `ref-data.yaml` -- entity definition diff --git a/.github/instructions/coreex-domain.instructions.md b/.github/instructions/coreex-domain.instructions.md index b4a742c2..fca95147 100644 --- a/.github/instructions/coreex-domain.instructions.md +++ b/.github/instructions/coreex-domain.instructions.md @@ -8,6 +8,10 @@ tags: ["domain", "ddd", "aggregates", "entities", "value-objects", "result"] The Domain layer is **optional**. It is introduced only when a domain contains aggregates with meaningful business rules and invariants that must be enforced at the model level — not in orchestration code. For example, a checkout/basket domain with state-machine transitions and nested item rules benefits from this layer; a simple CRUD-oriented domain (like a product catalog) typically does not. +> **Related skill:** to scaffold a new aggregate root, entity, or value object, invoke the [`coreex-aggregate`](/.github/skills/coreex-aggregate/SKILL.md) skill. +> This file holds the invariants that must hold on **any** edit to a Domain-layer file; the skill drives the +> step-by-step **creation** procedure. + ## NuGet / Project References | Package | Key types provided | @@ -201,8 +205,9 @@ For CRUD-oriented domains, skip the Domain layer entirely and let the Applicatio ## Further Reading -- [Domain Layer Guide](https://github.com/Avanade/CoreEx/blob/main/samples/docs/domain-layer.md) — aggregates, entities, value objects, and `PersistenceState` walkthrough. -- [Pattern Catalog](https://github.com/Avanade/CoreEx/blob/main/samples/docs/patterns.md) — Aggregate, Entity, and Value Object pattern entries with cross-links. -- [Layer Dependencies](https://github.com/Avanade/CoreEx/blob/main/samples/docs/layers.md) — when to introduce the Domain layer and its position in the dependency graph. -- [CoreEx.DomainDriven README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.DomainDriven/README.md) — `Aggregate`, `Entity`, and `PersistenceState`. +- [Domain Layer Guide](/.github/docs/coreex/domain-layer.md) — aggregates, entities, value objects, and `PersistenceState` walkthrough (docs-sync cache; after `/coreex-docs-sync`). +- [Pattern Catalog](/.github/docs/coreex/patterns.md) — Aggregate, Entity, and Value Object pattern entries with cross-links (docs-sync cache; after `/coreex-docs-sync`). +- [Layer Dependencies](/.github/docs/coreex/layers.md) — when to introduce the Domain layer and its position in the dependency graph (docs-sync cache; after `/coreex-docs-sync`). +- [CoreEx.DomainDriven guide](/.github/docs/coreex/agents/CoreEx.DomainDriven.md) — `Aggregate`, `Entity`, and `PersistenceState` (docs-sync cache; after `/coreex-docs-sync`). Source: [CoreEx.DomainDriven README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.DomainDriven/README.md). - [CoreEx Results README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx/Results/README.md) — `Result` type, pipeline operators (`.GoAsync`, `.ThenAs`, `.ThenAsAsync`), and error propagation semantics. +- Related skill: [`coreex-aggregate`](/.github/skills/coreex-aggregate/SKILL.md) — invoke to scaffold an aggregate, entity, or value object. diff --git a/.github/instructions/coreex-event-subscribers.instructions.md b/.github/instructions/coreex-event-subscribers.instructions.md index 63637f9a..ee290a74 100644 --- a/.github/instructions/coreex-event-subscribers.instructions.md +++ b/.github/instructions/coreex-event-subscribers.instructions.md @@ -6,6 +6,10 @@ tags: ["subscribers", "messaging", "service-bus", "event-handling", "integration # Event Subscriber Conventions +> **Related skill:** to scaffold a new event subscriber, invoke the [`coreex-subscriber`](/.github/skills/coreex-subscriber/SKILL.md) skill. +> This file holds the invariants that must hold on **any** edit to a subscriber file; the skill drives the +> step-by-step **creation** procedure. + ## NuGet / Project References | Package | Key types provided | @@ -250,6 +254,7 @@ app.Run(); ## Further Reading -- [Hosts Layer Guide — Subscribe Host](https://github.com/Avanade/CoreEx/blob/main/samples/docs/hosts-layer.md) — Subscribe host architecture, Program.cs shape, and subscriber patterns. -- [Pattern Catalog](https://github.com/Avanade/CoreEx/blob/main/samples/docs/patterns.md) — Subscribe, Publish, Transactional Outbox, and Event-Driven Replication pattern entries. -- [CoreEx.Azure.Messaging.ServiceBus README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.Azure.Messaging.ServiceBus/README.md) — `SubscribedBase`, `ErrorHandler`, and Service Bus receiver configuration. +- [Hosts Layer Guide — Subscribe Host](/.github/docs/coreex/hosts-layer.md) — Subscribe host architecture, Program.cs shape, and subscriber patterns (docs-sync cache; after `/coreex-docs-sync`). +- [Pattern Catalog](/.github/docs/coreex/patterns.md) — Subscribe, Publish, Transactional Outbox, and Event-Driven Replication pattern entries (docs-sync cache; after `/coreex-docs-sync`). +- [CoreEx.Azure.Messaging.ServiceBus guide](/.github/docs/coreex/agents/CoreEx.Azure.Messaging.ServiceBus.md) — `SubscribedBase`, `ErrorHandler`, and Service Bus receiver configuration (docs-sync cache; after `/coreex-docs-sync`). Source: [CoreEx.Azure.Messaging.ServiceBus README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.Azure.Messaging.ServiceBus/README.md). +- Related skill: [`coreex-subscriber`](/.github/skills/coreex-subscriber/SKILL.md) — invoke to scaffold an event subscriber. diff --git a/.github/instructions/coreex-host-setup.instructions.md b/.github/instructions/coreex-host-setup.instructions.md index 898181d0..4fe1fbab 100644 --- a/.github/instructions/coreex-host-setup.instructions.md +++ b/.github/instructions/coreex-host-setup.instructions.md @@ -8,7 +8,12 @@ tags: ["program-cs", "host-setup", "middleware", "dependency-registration", "cac The host is a **composition root only** — no business logic. There are three host types in a CoreEx solution depending on the capabilities required. Each follows the same opening skeleton, then diverges based on its responsibilities. -> **Further Reading**: [Hosts Layer Guide](https://github.com/Avanade/CoreEx/blob/main/samples/docs/hosts-layer.md) · [Layer Dependencies](https://github.com/Avanade/CoreEx/blob/main/samples/docs/layers.md) · [Pattern Catalog](https://github.com/Avanade/CoreEx/blob/main/samples/docs/patterns.md) +> **Related skill:** to scaffold a solution or an additional host (Api / Subscribe / Relay), invoke the [`solution-scaffolder`](/.github/skills/solution-scaffolder/SKILL.md) skill. +> This file holds the invariants that must hold on **any** edit to a host `Program.cs`; the skill drives the +> step-by-step **creation** procedure. (The per-host "Scaffolding an … host" blocks below stay here — they carry the +> Feature Configuration recover/record guardrails that must be honoured whenever a host is added.) + +> **Further Reading**: [Hosts Layer Guide](/.github/docs/coreex/hosts-layer.md) · [Layer Dependencies](/.github/docs/coreex/layers.md) · [Pattern Catalog](/.github/docs/coreex/patterns.md) (docs-sync cache; after `/coreex-docs-sync`) --- diff --git a/.github/instructions/coreex-repositories.instructions.md b/.github/instructions/coreex-repositories.instructions.md index 1a1b8df9..ea498033 100644 --- a/.github/instructions/coreex-repositories.instructions.md +++ b/.github/instructions/coreex-repositories.instructions.md @@ -6,6 +6,11 @@ tags: ["repositories", "infrastructure", "data-access", "efcore", "mapping", "ad # Repository & Infrastructure Conventions +> **Related skill:** to scaffold a new repository, invoke the [`coreex-repository`](/.github/skills/coreex-repository/SKILL.md) skill; +> for an anti-corruption adapter (which also lives in Infrastructure) invoke [`coreex-adapter`](/.github/skills/coreex-adapter/SKILL.md). +> This file holds the invariants that must hold on **any** edit to an Infrastructure-layer file; the skills drive the +> step-by-step **creation** procedures. + ## NuGet / Project References | Package | Key types provided | @@ -328,9 +333,10 @@ Always call `.ConfigureAwait(false)` on every `await` inside repository and adap ## Further Reading -- [Infrastructure Layer Guide](https://github.com/Avanade/CoreEx/blob/main/samples/docs/infrastructure-layer.md) — full walkthrough of persistence models, repositories, mapping, and external client/adapter patterns. -- [Pattern Catalog](https://github.com/Avanade/CoreEx/blob/main/samples/docs/patterns.md) — Adapter, Repository, Mapper, Persistence, and HTTP Client pattern entries. -- [Layer Dependencies](https://github.com/Avanade/CoreEx/blob/main/samples/docs/layers.md) — layer dependency rules and the role of the Infrastructure layer. -- [Tooling Guide](https://github.com/Avanade/CoreEx/blob/main/samples/docs/tooling.md) — `*.Database` project: schema, persistence-model generation, and outbox provisioning. -- [CoreEx.EntityFrameworkCore README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.EntityFrameworkCore/README.md) — `EfDb`, `EfDbModel`, `EfDbMappedModel`, and `EfDbOptions`. +- [Infrastructure Layer Guide](/.github/docs/coreex/infrastructure-layer.md) — full walkthrough of persistence models, repositories, mapping, and external client/adapter patterns (docs-sync cache; after `/coreex-docs-sync`). +- [Pattern Catalog](/.github/docs/coreex/patterns.md) — Adapter, Repository, Mapper, Persistence, and HTTP Client pattern entries (docs-sync cache; after `/coreex-docs-sync`). +- [Layer Dependencies](/.github/docs/coreex/layers.md) — layer dependency rules and the role of the Infrastructure layer (docs-sync cache; after `/coreex-docs-sync`). +- [Tooling Guide](/.github/docs/coreex/tooling.md) — `*.Database` project: schema, persistence-model generation, and outbox provisioning (docs-sync cache; after `/coreex-docs-sync`). +- [CoreEx.EntityFrameworkCore guide](/.github/docs/coreex/agents/CoreEx.EntityFrameworkCore.md) — `EfDb`, `EfDbModel`, `EfDbMappedModel`, and `EfDbOptions` (docs-sync cache; after `/coreex-docs-sync`). Source: [CoreEx.EntityFrameworkCore README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.EntityFrameworkCore/README.md). - [CoreEx Results README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx/Results/README.md) — `Result` type, pipeline operators (`.GoAsync`, `.ThenAs`, `.ThenAsAsync`), and error propagation semantics. +- Related skills: [`coreex-repository`](/.github/skills/coreex-repository/SKILL.md) (scaffold a repository), [`coreex-adapter`](/.github/skills/coreex-adapter/SKILL.md) (scaffold an anti-corruption adapter). diff --git a/.github/instructions/coreex-tests.instructions.md b/.github/instructions/coreex-tests.instructions.md index c1e27f03..f9d39fe1 100644 --- a/.github/instructions/coreex-tests.instructions.md +++ b/.github/instructions/coreex-tests.instructions.md @@ -6,6 +6,13 @@ tags: ["testing", "unit-tests", "integration-tests", "test-helpers", "nunit"] # Test Conventions +> **Related skills:** to author tests, invoke the matching skill — +> [`coreex-test-api`](/.github/skills/coreex-test-api/SKILL.md) (API host integration tests), +> [`coreex-test-subscribe`](/.github/skills/coreex-test-subscribe/SKILL.md) (Subscribe host tests), or +> [`coreex-test-relay`](/.github/skills/coreex-test-relay/SKILL.md) (Outbox Relay host tests). This file holds the +> invariants that must hold on **any** test edit (project types, base classes, isolation, assertion helpers); the +> skills drive the step-by-step creation procedure. + ## NuGet / Project References | Package | Key types provided | @@ -216,10 +223,9 @@ public partial class EmployeeMutateTests **Expected `.req.json` / `.res.json` resources** (the JSON representation of the request/response) live under `Resources/{TestClass}/…` and are referenced via `.ExpectJsonFromResource(...)` / `.AssertJsonFromResource("EmployeeReadTests.Employee_Get_Found.res.json", "etag", "changelog")` (exclude volatile fields). **Pre-author them from the seed values** — you control the seed, so you can write the expected JSON up front (remember to exclude/expect the volatile `id`/`etag`/`changelog`); then **run once and reconcile** any remaining differences from the actual output (they are intentionally copy-paste-friendly). Expect the first run to need a small fix-up; that's normal, not a failure to avoid. They scale better than inline assertions as entities grow. -> **Agent instruction — co-design seed, tests, and resources together.** These three must agree, so author them in order: -> 1. **Seed first** — add/extend the domain's `read-data.seed.yaml` / `mutate-data.seed.yaml` with the known rows the tests will reference, as object rows with deterministic **`^N` ids** (= `N.ToGuid()`); for mutate, the baselines a test needs — e.g. an existing SKU for a duplicate-conflict test, a row to update/delete. -> 2. **Tests next** — one partial file per operation; reference seeded rows via `n.ToGuid()` / known codes; keep mutate tests independent. -> 3. **Resources last** — capture `.res.json`/`.req.json` from the actual run; the response resource must match the seeded values. +> **Creation procedure → skill.** Co-designing the seed, tests, and expected resources (seed first → tests next → +> resources last, so all three agree) is driven by the [`coreex-test-api`](/.github/skills/coreex-test-api/SKILL.md) +> skill. The coverage those tests must achieve is invariant: > > Cover, per operation: **Get** (found, not-found, ETag/not-modified); **Query** (filter, order, paging, field selection); **Create** (success + Location + outbox event, bad-data validation, duplicate/conflict, idempotency-key); **Update** (success + ETag/concurrency, not-found); **Patch** (merge success, not-found); **Delete** (idempotent — always 204, never 404): **get → delete → get → delete** = exists → 204+event → now 404 → 204 with **no** event. Assert outbox events on mutating success paths (provider-specific `ExpectXxxOutboxEvents`) and `ExpectNoXxxOutboxEvents` on failure paths. diff --git a/.github/instructions/coreex-tooling.instructions.md b/.github/instructions/coreex-tooling.instructions.md index e157f336..b267d14f 100644 --- a/.github/instructions/coreex-tooling.instructions.md +++ b/.github/instructions/coreex-tooling.instructions.md @@ -13,6 +13,12 @@ Each domain has two developer-time tooling projects that have **no runtime prese | `*.CodeGen` | Generates reference-data C# artefacts across all layers from `ref-data.yaml` | | `*.Database` | Manages the full database lifecycle — schema, seed data, outbox provisioning, and Infrastructure C# code generation | +> **Related skills:** this file holds the invariants (command reference, YAML structure, table templates, casing, +> generated-file ownership) that must hold on **any** tooling edit. For the step-by-step **creation** procedures, +> invoke the skills: [`coreex-db-migration`](/.github/skills/coreex-db-migration/SKILL.md) (author/apply a migration +> script — new table, alter, outbox) and [`coreex-refdata`](/.github/skills/coreex-refdata/SKILL.md) (add/modify a +> reference data type — `ref-data.yaml` + seed + CodeGen). + --- ## Order of operations (database-first) @@ -113,12 +119,10 @@ Key `entities:` options: | `properties[].type` | Yes (if any) | -- | CLR type; prefix `^` for a ref-data navigation accessor | | `properties[].excludeContract` | No | `false` | Exclude from the generated contract (persistence only) | -> **Agent instruction:** When asked to create or modify a reference data type: -> 1. Add or update the entry under `entities:` in `ref-data.yaml`. -> 2. Offer to run `dotnet run` from the `*.CodeGen` directory on the user's behalf. -> 3. If confirmed, execute it and summarise the generated artefacts on success; on failure relay the **complete output verbatim** — it provides the diagnostic needed to fix the entry. -> 4. On failure, fix the issue in `ref-data.yaml` and offer to re-run -- do not create or edit `.g.cs` files to work around a generation error. -> 5. If the user declines, remind them to run `dotnet run` from the `*.CodeGen` directory before the new types are available. +> **Creation procedure → skill.** To create or modify a reference data type, invoke the +> [`coreex-refdata`](/.github/skills/coreex-refdata/SKILL.md) skill — it drives the `ref-data.yaml` edit, the +> `*.CodeGen` run, and the seed/contract wiring. The invariants in this file still apply on any such edit +> (notably: never edit `.g.cs` to work around a generation error — fix `ref-data.yaml` and re-run). > > **Do not pre-create output directories.** A configured target path (e.g. `apiProjectPath`) that points to a not-yet-existing project directory does **not** cause CodeGen to fail — it simply emits a *warning* and skips that output. Never create an empty directory, stub project, or placeholder file to "unblock" code generation. If a target project is genuinely missing and its artefacts are needed, raise it with the user rather than fabricating the folder. @@ -202,24 +206,11 @@ When a solution is scaffolded with outbox enabled, the `coreex` template **alrea > **Migration scripts are immutable once applied.** A create script runs exactly once and is never re-run, regenerated, or edited. If the outbox schema later needs to change (e.g. a DbEx version bump alters its shape), author a **new** timestamped `ALTER` migration script for the delta — do not modify or re-scaffold the original create script. -> **Agent instruction:** When asked to create the database outbox table(s): -> 1. **Validate `dbex.yaml` first.** All three conditions must hold: -> - Root-level `outbox: true` is set. -> - Root-level `schema:` has a value (call it `xxx`). -> - Root-level `outboxName:` has a value (call it `yyy`). -> If any condition fails, **stop and error** — state which is missing. Do not attempt to scaffold the script. -> 2. **Check for an existing create script.** Look under `Migrations/` for a `*-create-*-outbox-tables.{sql,pgsql}` script (the template ships one when outbox is enabled). If present, **do not scaffold another** — skip to step 4 to apply it. Only when none exists, proceed to step 3. -> 3. **Scaffold the migration script** using the extracted `schema` (`xxx`) and `outboxName` (`yyy`) values: -> ``` -> dotnet run -- script outbox xxx yyy -> ``` -> 4. **Ask** whether the (new or existing) script should be deployed/migrated to the database. -> 5. **If confirmed**, run: -> ``` -> dotnet run -- CreateMigrateAndCodeGen -> ``` -> Summarise the output on success; on failure relay the **complete output verbatim** — it provides the diagnostic needed to resolve the issue. -> 6. **If declined**, remind the user to run `dotnet run -- CreateMigrateAndCodeGen` before the outbox table(s) exist in the database. +> **Creation procedure → skill.** To provision the outbox table(s), invoke the +> [`coreex-db-migration`](/.github/skills/coreex-db-migration/SKILL.md) skill (its **Outbox provisioning** section). +> It validates `dbex.yaml` (`outbox: true`, plus `schema:` and `outboxName:` values), reuses the template-shipped +> create script when present or scaffolds `dotnet run -- script outbox ` when absent, and applies via +> `dotnet run -- CreateMigrateAndCodeGen`. The immutability invariant above still holds. ### `dbex.yaml` structure @@ -424,13 +415,17 @@ When authoring a migration script for an entity that has a corresponding .NET co | `DateTime` | `DATETIME2` | `TIMESTAMP` | | `DateTimeOffset` | `DATETIMEOFFSET` | `TIMESTAMPTZ` | -> **Agent instruction:** When generating a migration script for an entity that has a .NET contract: -> 1. **The `script` scaffold's PK is a placeholder — replace it.** `dotnet run -- script create|refdata` seeds the primary key with a generated-key placeholder: `[{Name}Id] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY` (SQL Server) or `"{name}_id" SERIAL PRIMARY KEY` (PostgreSQL). **Neither is the default to keep** — overwrite it to match the contract's identifier type. -> 2. **Map the primary key column to the contract's identifier type.** The identifier is `string` **by default** → `[{Name}Id] NVARCHAR(50) NOT NULL PRIMARY KEY` (SQL Server) / `"{name}_id" VARCHAR(50) NOT NULL PRIMARY KEY` (PostgreSQL). Use `UNIQUEIDENTIFIER`/`UUID` **only** when the contract identifier is explicitly a `Guid`; `INT`/`INTEGER` for `int`; etc. Never leave the scaffold's `UNIQUEIDENTIFIER` (SQL Server) or `SERIAL` (PostgreSQL) for a `string` identifier. -> 3. **Drop the scaffold's value-generation default.** Remove `DEFAULT (NEWSEQUENTIALID())` (SQL Server) and **replace `SERIAL` with the plain column type** (PostgreSQL — `SERIAL` is an auto-increment sequence, i.e. a DB-assigned value); never add `IDENTITY` / `gen_random_uuid()` either — unless explicitly requested. The application services layer assigns identifier and other values; the database should not default them. -> 4. **Map every other column to its contract property type** per the table above, preserving nullability (`?` → nullable column). For a `[ReferenceData]` property, the contract property is `{Name}Code` (a string) — so the column is `{Name}Code` (e.g. `[GenderCode] NVARCHAR(50) NULL`), **not** `{Name}Id`, and **no foreign key** (see step 4 of the create/alter workflow). Mirror the contract property name exactly. -> 5. **Lock the agreed identifier type — never deviate, especially in fixing loops.** Once the type is agreed (the `string` default, or whatever the user explicitly specified), it is fixed for the whole task. Do **not** silently change it, and do **not** revert to the scaffold's `UNIQUEIDENTIFIER` (or flip it again) while troubleshooting a build/migration failure — a failure is never resolved by changing the PK type. If you believe the agreed type is wrong, **stop and ask** rather than changing it. -> 6. **If in doubt about a type, nullability, or precision/length, ask** rather than guessing. +> **Creation procedure → skill.** Authoring/applying a migration script is driven by the +> [`coreex-db-migration`](/.github/skills/coreex-db-migration/SKILL.md) skill (inspect → script → fill columns → +> apply). The invariants that hold whenever a script is authored — map with the table above: +> - The `script` scaffold's PK is a **placeholder** — replace it to match the contract's identifier type +> (`string` default → `NVARCHAR(50)`/`VARCHAR(50)`; `UNIQUEIDENTIFIER`/`UUID` only for an explicit `Guid`). +> - **Drop the value-generation default** (`NEWSEQUENTIALID()` / `SERIAL` / `IDENTITY` / `gen_random_uuid()`) unless +> explicitly requested — the application layer assigns identifiers. +> - Map every other column to its contract property type, preserving nullability. A `[ReferenceData]` property maps +> to a `{Name}Code` column (never `{Name}Id`), **no foreign key**. +> - **Lock the agreed identifier type** — never flip it in a fixing loop; a failure is never resolved by changing the +> PK type. If you think it is wrong, stop and ask. ### Creating or altering a table for an entity @@ -438,23 +433,18 @@ When asked to create or change a database table for a .NET entity (e.g. *"create > **Inspect first — this is a hard gate.** Inspection is the **first action**, ahead of authoring anything. Do **not** write (or plan to write) a `CREATE`/`ALTER` script before the `Inspect` result is in hand — the result determines *whether* a script is even needed and *which kind*. A plan that scaffolds scripts before inspecting is wrong; fix the plan, don't proceed. -> **Agent instruction:** -> 1. **Identify every table involved.** This includes the entity's own table plus any reference data tables implied by its `[ReferenceData]` properties (each typed reference-data property maps to a lookup table that may need to exist). -> 2. **Bring the database up to date, then inspect — before authoring any script.** First run `dotnet run -- database` (non-destructive: `Create` → `Migrate` → `Schema` → `Data`) so the live database reflects all authored scripts, then run `dotnet run -- inspect [
...]` (read-only). Only proceed to authoring once you know each table's actual state. -> 3. **Branch per table on the `Inspect` result:** -> - **Not found** → scaffold the script with `dotnet run -- script create
` (or `script refdata
` for a reference-data table) so it is correctly named/timestamped/cased, fill in the columns, and register the table under `tables:` in `dbex.yaml`. Map column types per [Mapping contract types to columns](#mapping-contract-types-to-columns). **Do not create a schema-create script** — the schema already exists (template-provided). -> - **Found, Reference Data: Yes** → do **not** recreate or alter it directly. Reference it from the entity table per step 4 below. Any change to the reference data table's own shape flows through `ref-data.yaml` + CodeGen, not a hand-authored script. -> - **Found, schema differs from the contract** → scaffold with `dotnet run -- script alter
` and include the **delta only** (applied scripts are immutable — never edit the original create script). -> - **Found, schema already matches** → no script is needed; say so. -> 4. **Reference-data relationships are stored by `Code`, with no foreign key — this is the default; do not deviate silently.** For each `[ReferenceData]` property on the entity (the contract has a `{Name}Code` string property, e.g. `GenderCode`): -> - **Default — by Code:** create a column that **mirrors the contract property** — same name `{Name}Code` (e.g. `[GenderCode] NVARCHAR(50) NULL` / `gender_code`), typed to match the reference data's `Code` column. **Do not create a foreign key.** Do **not** invent a `{Name}Id` column. -> - **Only if the user explicitly asks for an Id reference** → name it `{Name}Id`, typed to match the reference data identifier; and even then a foreign key is **not** automatic — ask whether one is required and add it only if confirmed. -> -> **Never** default to `{Name}Id` + a `REFERENCES` foreign key — that contradicts both the contract (whose property is `{Name}Code`) and the CoreEx convention (reference data is resolved by code, not FK-joined). -> 5. **Confirm logical-delete support** (for root/aggregate tables). Ask whether the table should support logical (soft) deletes — **default yes**. If yes, add an infrastructure-only column: `[IsDeleted] BIT NOT NULL DEFAULT (0)` (SQL Server) / `is_deleted BOOLEAN NOT NULL DEFAULT FALSE` (PostgreSQL) — it must be **NOT NULL** and default to the DB's `false` (`0` / `FALSE`), **never nullable**. This is a persistence concern only — the .NET contract/entity must **not** declare an equivalent property. -> 6. **Offer to apply.** Offer to run `dotnet run -- CreateMigrateAndCodeGen`. Summarise the output on success; on failure relay the **complete output verbatim**. -> -> **On failure, do not add defensive existence-guards.** Migration scripts are plain DDL — DbEx tracks which have been applied and runs each exactly once, so a `CREATE TABLE` does **not** need `IF NOT EXISTS` / `IF OBJECT_ID(...) IS NULL` wrappers. If a script fails because an object already exists (or differs), that means the `Inspect` step was skipped or stale: **re-inspect** to learn the real state, then either remove the redundant create script (object already correct), or author a separate `ALTER` migration for the delta. Wrapping the DDL in conditional guards to make it "pass" masks the underlying state mismatch and is not the convention — never do it. +> **Creation procedure → skill.** Creating or altering a table for an entity is driven by the +> [`coreex-db-migration`](/.github/skills/coreex-db-migration/SKILL.md) skill, which branches per-table on the +> `Inspect` result: **not found** → `script create`/`script refdata` + register in `dbex.yaml`; **found, reference +> data** → do not recreate/alter directly (changes flow through `ref-data.yaml` + CodeGen); **found, schema differs** +> → `script alter` with the **delta only** (applied scripts are immutable); **found, matches** → no script. The +> Inspect-first gate above and these invariants always apply: +> - Reference-data relationships are stored by `{Name}Code` (mirror the contract property), **no foreign key** — never +> default to `{Name}Id` + `REFERENCES`; use an Id reference only when the user explicitly asks. +> - Logical (soft) delete on root/aggregate tables is `[IsDeleted] BIT NOT NULL DEFAULT (0)` / +> `is_deleted BOOLEAN NOT NULL DEFAULT FALSE` — **NOT NULL**, defaults false, no contract property (default yes; confirm). +> - **Never** add `IF NOT EXISTS` / `IF OBJECT_ID(...)` guards to make a failing script "pass" — re-inspect and author +> an `ALTER` for a real delta instead. ### `Schema` — idempotent objects diff --git a/.github/instructions/coreex-validators.instructions.md b/.github/instructions/coreex-validators.instructions.md index d7eaea85..16dd7188 100644 --- a/.github/instructions/coreex-validators.instructions.md +++ b/.github/instructions/coreex-validators.instructions.md @@ -6,6 +6,10 @@ tags: ["validators", "validation", "fluent-api", "rules", "error-handling", "app # Validator Conventions +> **Related skill:** to scaffold a new validator, invoke the [`coreex-validator`](/.github/skills/coreex-validator/SKILL.md) skill. +> This file holds the invariants that must hold on **any** edit to a validator file; the skill drives the +> step-by-step **creation** procedure. + ## NuGet / Project References | Package | Key types provided | @@ -308,6 +312,7 @@ Property(x => x.Quantity, c => c ## Further Reading -- [Application Layer Guide — Validators](https://github.com/Avanade/CoreEx/blob/main/samples/docs/application-layer.md) — full validator walkthrough including declarative and programmatic phases. -- [Pattern Catalog](https://github.com/Avanade/CoreEx/blob/main/samples/docs/patterns.md) — Validator pattern entry with cross-links. -- [CoreEx.Validation README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.Validation/README.md) — `Validator`, rule set, `OnValidateAsync`, `ValidateFurtherAsync`, and `AbstractValidator`. +- [Application Layer Guide — Validators](/.github/docs/coreex/application-layer.md) — full validator walkthrough including declarative and programmatic phases (docs-sync cache; after `/coreex-docs-sync`). +- [Pattern Catalog](/.github/docs/coreex/patterns.md) — Validator pattern entry with cross-links (docs-sync cache; after `/coreex-docs-sync`). +- [CoreEx.Validation guide](/.github/docs/coreex/agents/CoreEx.Validation.md) — `Validator`, rule set, `OnValidateAsync`, `ValidateFurtherAsync`, and `AbstractValidator` (docs-sync cache; after `/coreex-docs-sync`). Source: [CoreEx.Validation README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.Validation/README.md). +- Related skill: [`coreex-validator`](/.github/skills/coreex-validator/SKILL.md) — invoke to scaffold a validator. diff --git a/.github/skills/coreex-adapter/SKILL.md b/.github/skills/coreex-adapter/SKILL.md index 05822e8f..f83082cf 100644 --- a/.github/skills/coreex-adapter/SKILL.md +++ b/.github/skills/coreex-adapter/SKILL.md @@ -21,7 +21,11 @@ Guides you through adding or modifying an adapter — the boundary that isolates - EF repositories within the same domain — use `coreex-repository` - Application services that consume the adapter — use `coreex-app-service` -- Event subscriber hosts that drive `IXxxSyncAdapter` — see `.github/instructions/coreex-event-subscribers.instructions.md` +- Event subscriber hosts that drive `IXxxSyncAdapter` — use `coreex-subscriber` + +> **Resolve project-wide choices from state before asking.** Read the solution-root `AGENTS.md` +> **Feature Configuration** for `messaging-provider` (does this domain publish/consume events?) and `data-provider` +> (EF-only replication vs HTTP). Only prompt for what is unrecorded; re-state resolved values for confirmation. ## Quick Reference @@ -38,11 +42,10 @@ For full workflow and code examples see [`references/workflow.md`](references/wo ## Key References -- `samples/src/Contoso.Shopping.Application/Adapters/Products/IProductAdapter.cs` — synchronous adapter interface -- `samples/src/Contoso.Shopping.Application/Adapters/Products/IProductSyncAdapter.cs` — replication adapter interface -- `samples/src/Contoso.Shopping.Infrastructure/Adapters/Products/ProductAdapter.cs` — adapter implementation (HTTP + EF + events) -- `samples/src/Contoso.Shopping.Infrastructure/Adapters/Products/ProductSyncAdapter.cs` — replication implementation (EF upsert/delete) -- `samples/src/Contoso.Shopping.Infrastructure/Clients/Products/ProductsHttpClient.cs` — typed HTTP client -- `samples/tests/Contoso.Shopping.Test.Unit/Clients/Products/ProductsHttpClientTests.cs` — HTTP client unit tests -- `.github/instructions/coreex-repositories.instructions.md` — Infrastructure conventions (adapters, clients, mapping) -- `.github/instructions/coreex-application-services.instructions.md` — Application conventions (adapter interfaces, anti-corruption layer) +- [`/.github/instructions/coreex-repositories.instructions.md`](/.github/instructions/coreex-repositories.instructions.md) — Infrastructure conventions (adapters, clients, mapping) +- [`/.github/instructions/coreex-application-services.instructions.md`](/.github/instructions/coreex-application-services.instructions.md) — Application conventions (adapter interfaces, anti-corruption layer) +- Related skills: [`coreex-repository`](../coreex-repository/SKILL.md) (same-domain persistence), [`coreex-app-service`](../coreex-app-service/SKILL.md) (consumes the adapter), [`coreex-subscriber`](../coreex-subscriber/SKILL.md) (drives `IXxxSyncAdapter`) +- [Infrastructure layer deep-dive](/.github/docs/coreex/infrastructure-layer.md) — optional (after `/coreex-docs-sync`) +- Illustrative examples (CoreEx sample — not present in your project): + - [synchronous + replication adapter interfaces](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Application/Adapters/Products) — `IProductAdapter`, `IProductSyncAdapter` + - [adapter + client implementations](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Infrastructure/Adapters/Products) and the [typed HTTP client](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Infrastructure/Clients/Products) diff --git a/.github/skills/coreex-aggregate/SKILL.md b/.github/skills/coreex-aggregate/SKILL.md index 33dec706..a55bf973 100644 --- a/.github/skills/coreex-aggregate/SKILL.md +++ b/.github/skills/coreex-aggregate/SKILL.md @@ -28,6 +28,11 @@ then follows the corresponding pattern from `CoreEx.DomainDriven`. - Infrastructure persistence of the aggregate (EF mapping, `PersistenceState`-driven insert/update/delete) — use `coreex-repository` - Validating primitive request DTOs before they reach the domain — use `coreex-validator` +> **Confirm this skill applies before writing any domain object.** It is gated on +> `domain-driven-enabled`: verify both the solution-root `AGENTS.md` **Feature Configuration** +> (`domain-driven-enabled: true`) **and** the presence of the `*.Domain` project. If either is absent, +> the domain is CRUD-oriented — stop and use `coreex-app-service` against repository interfaces instead. + ## Quick Reference - **Aggregate root** → `Aggregate` (extends `Entity`) — adds `Events`/`AddEvent`/`ClearEvents` for integration-event accumulation @@ -46,10 +51,15 @@ For full workflow and code examples see [`references/workflow.md`](references/wo ## Key References -- `.github/instructions/coreex-domain.instructions.md` — full Domain layer conventions (aggregates, entities, value objects, `PersistenceState`) -- `.github/instructions/coreex-application-services.instructions.md` — how Application services construct, mutate, and map aggregates (`Domain.Xxx.CreateNew(...)`, `Application/Mapping/` `Mapper`) -- `src/CoreEx.DomainDriven/README.md` — package overview, key types, and the "Domain Events — Intentionally Not Supported" rationale -- `src/CoreEx.DomainDriven/Aggregate.cs`, `Entity.cs`, `EntityBase.cs` — actual base-class implementation (`Modify`, `Remove`, `OnCheckCanMutate`, `OnMutate`, `PersistenceState` transitions) -- `src/CoreEx.DomainDriven/PersistenceState.cs`, `DomainDrivenExtensions.cs` — state enum and filter helpers (`IsNew`, `IsModified`, `IsNewOrModified`, `IsNotRemoved`) -- `src/CoreEx.Template/README.md` — `--domain-driven-enabled true` template flag and generated `*.Domain` project shape -- `.github/instructions/coreex-tests.instructions.md` — `*.Test.Unit` conventions (`WithGenericTester`, `Test.Scoped(...)`) reused for aggregate unit tests +- [`/.github/instructions/coreex-domain.instructions.md`](/.github/instructions/coreex-domain.instructions.md) — full Domain layer conventions (aggregates, entities, value objects, `PersistenceState`) +- [`/.github/instructions/coreex-application-services.instructions.md`](/.github/instructions/coreex-application-services.instructions.md) — how Application services construct, mutate, and map aggregates (`Domain.Xxx.CreateNew(...)`, `Application/Mapping/` `Mapper`) +- [`/.github/instructions/coreex-tests.instructions.md`](/.github/instructions/coreex-tests.instructions.md) — `*.Test.Unit` conventions (`WithGenericTester`, `Test.Scoped(...)`) reused for aggregate unit tests +- Related skills: [`coreex-contract`](../coreex-contract/SKILL.md) (maps aggregate ↔ contract), [`coreex-repository`](../coreex-repository/SKILL.md) (persists aggregates via `PersistenceState`), [`coreex-app-service`](../coreex-app-service/SKILL.md) (constructs/mutates aggregates, forwards `Events`) +- Deep-dive (after `/coreex-docs-sync`) — the primary consumer-resolvable pointers into `CoreEx.DomainDriven`: + - [`/.github/docs/coreex/agents/CoreEx.DomainDriven.md`](/.github/docs/coreex/agents/CoreEx.DomainDriven.md) — package AI usage guide (key types, the "Domain Events — Intentionally Not Supported" rationale) + - [`/.github/docs/coreex/domain-layer.md`](/.github/docs/coreex/domain-layer.md) — Domain layer sample architecture doc +- Illustrative examples (CoreEx sample — not present in your project): + - [CoreEx.DomainDriven README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.DomainDriven/README.md) — package overview and rationale + - [Aggregate.cs](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.DomainDriven/Aggregate.cs), [Entity.cs](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.DomainDriven/Entity.cs), [EntityBase.cs](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.DomainDriven/EntityBase.cs) — base-class implementation (`Modify`, `Remove`, `OnCheckCanMutate`, `OnMutate`, `PersistenceState` transitions) + - [PersistenceState.cs](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.DomainDriven/PersistenceState.cs), [DomainDrivenExtensions.cs](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.DomainDriven/DomainDrivenExtensions.cs) — state enum and filter helpers (`IsNew`, `IsModified`, `IsNewOrModified`, `IsNotRemoved`) + - [CoreEx.Template README](https://github.com/Avanade/CoreEx/blob/main/src/CoreEx.Template/README.md) — `--domain-driven-enabled true` template flag and generated `*.Domain` project shape diff --git a/.github/skills/coreex-aggregate/references/workflow.md b/.github/skills/coreex-aggregate/references/workflow.md index 08be953f..e7d3c523 100644 --- a/.github/skills/coreex-aggregate/references/workflow.md +++ b/.github/skills/coreex-aggregate/references/workflow.md @@ -215,7 +215,7 @@ tests. Do not defer this coverage to host-level integration tests; those only pr domain's own branching logic. Follow the same `*.Test.Unit` conventions as validator tests (see -`.github/instructions/coreex-tests.instructions.md`): one test class per aggregate, under +[`/.github/instructions/coreex-tests.instructions.md`](/.github/instructions/coreex-tests.instructions.md)): one test class per aggregate, under `*.Test.Unit/Domains/`, named `{Aggregate}Tests`, extending `WithGenericTester`, with each `[Test]` running inside `Test.Scoped(test => { ... })` — this establishes the ambient `ExecutionContext` that `Runtime.UtcNow` and any `ThrowIfInactive()` reference-data check rely on. (`Runtime.NewId()`/ @@ -342,7 +342,7 @@ Application layer. CoreEx deliberately does not provide in-process domain-event - Add events from within a mutation method using the inherited `AddEvent(...)`. - The Application service is responsible for forwarding `aggregate.Events` into `_unitOfWork.Events` inside the same `TransactionAsync(...)` scope that persists the aggregate — see - `.github/instructions/coreex-application-services.instructions.md` for the Application-layer side + [`/.github/instructions/coreex-application-services.instructions.md`](/.github/instructions/coreex-application-services.instructions.md) for the Application-layer side of this handoff. - If a genuine in-process domain-event use case exists, that is an opt-in extension a consumer adds themselves (e.g., raising events dispatched via MediatR after commit) — not a CoreEx default. diff --git a/.github/skills/coreex-api/SKILL.md b/.github/skills/coreex-api/SKILL.md index c02fc723..d96527bf 100644 --- a/.github/skills/coreex-api/SKILL.md +++ b/.github/skills/coreex-api/SKILL.md @@ -1,6 +1,6 @@ --- name: coreex-api -description: "Add or modify a CoreEx API controller (or Minimal API endpoint) in an *.Api host. USE FOR: scaffolding the MVC controller pair (XxxController + XxxReadController), GET/query/schema endpoints, POST create, PUT + PATCH full-entity update, DELETE, and custom business-action endpoints. Covers both exception-based and Result service styles, and Minimal API as an alternative to MVC. DO NOT USE FOR: Api host setup / Program.cs (use coreex-host-setup), application services (use coreex-app-service), API integration tests (use coreex-test-api)." +description: "Add or modify a CoreEx API controller (or Minimal API endpoint) in an *.Api host. USE FOR: scaffolding the MVC controller pair (XxxController + XxxReadController), GET/query/schema endpoints, POST create, PUT + PATCH full-entity update, DELETE, and custom business-action endpoints. Covers both exception-based and Result service styles, and Minimal API as an alternative to MVC. DO NOT USE FOR: Api host setup / Program.cs (use solution-scaffolder), application services (use coreex-app-service), API integration tests (use coreex-test-api)." argument-hint: "Optional: entity name, operations needed (get/query/create/update/delete/custom), exception-based or Result service style, MVC or Minimal API" tags: ["api", "controller", "mvc", "minimal-api", "webapi", "routing", "cqrs", "coreex"] --- @@ -21,7 +21,7 @@ Guides you through adding or modifying HTTP API endpoints in an `*.Api` host. Co ## When Not to Use -- Api host setup and `Program.cs` composition — use `coreex-host-setup` +- Api host setup and `Program.cs` composition — use `solution-scaffolder` (see [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md)) - Application service creation — use `coreex-app-service` - API integration tests — use `coreex-test-api` (hand off once the endpoint is implemented) - Subscriber or relay hosts — controllers do not belong there @@ -29,6 +29,7 @@ Guides you through adding or modifying HTTP API endpoints in an `*.Api` host. Co ## Quick Reference **Clarifying questions before writing any code:** +0. Resolve `rop-enabled` (exception vs `Result` service style — selects the standard vs `WithResult` WebApi helper variant) from the solution-root `AGENTS.md` **Feature Configuration** before asking the rest; only prompt for what is unrecorded and re-state resolved values for confirmation. 1. What entity / resource is being exposed? (names the controller and route) 2. Which operations? GET / Query / Create / Update / Delete / custom business action? 3. Is the application service exception-based or `Result` pipeline style? (determines WebApi helper variant) @@ -56,7 +57,9 @@ For full workflow and code examples see [`references/workflow.md`](references/wo ## Key References - [`/.github/instructions/coreex-api-controllers.instructions.md`](/.github/instructions/coreex-api-controllers.instructions.md) — authoritative conventions: MVC vs Minimal API, WebApi helpers, attributes, route parameter rules, CQRS split -- [`/samples/src/Contoso.Products.Api/Controllers/`](/samples/src/Contoso.Products.Api/Controllers/) — `ProductController` + `ProductReadController` (exception-based, full CRUD + query + $query) -- [`/samples/src/Contoso.Shopping.Api/Controllers/`](/samples/src/Contoso.Shopping.Api/Controllers/) — `BasketController` + `BasketReadController` (Result<T> style, custom business actions, cross-tagged nested route) -- [`/samples/src/Contoso.Orders.Api/Controllers/`](/samples/src/Contoso.Orders.Api/Controllers/) — `OrderController` (exception-based, custom orchestration action returning 202 Accepted) -- `coreex-test-api` — the integration-test workflow for the endpoints this skill scaffolds +- [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md) — Api host / `Program.cs` composition (scaffolded by `solution-scaffolder`) +- Related skills: [`coreex-app-service`](../coreex-app-service/SKILL.md) (controllers delegate to it), [`coreex-test-api`](../coreex-test-api/SKILL.md) (integration tests for these endpoints), [`coreex-subscriber`](../coreex-subscriber/SKILL.md) (sibling host entry point), [`solution-scaffolder`](../solution-scaffolder/SKILL.md) (host setup) +- Illustrative examples (CoreEx sample — not present in your project): + - [`ProductController` + `ProductReadController`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Api/Controllers) — exception-based, full CRUD + query + $query + - [`BasketController` + `BasketReadController`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Api/Controllers) — Result<T> style, custom business actions, cross-tagged nested route + - [`OrderController`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Orders.Api/Controllers) — exception-based, custom orchestration action returning 202 Accepted diff --git a/.github/skills/coreex-app-service/SKILL.md b/.github/skills/coreex-app-service/SKILL.md index 75fa1a91..25baa1b6 100644 --- a/.github/skills/coreex-app-service/SKILL.md +++ b/.github/skills/coreex-app-service/SKILL.md @@ -1,6 +1,6 @@ --- name: coreex-app-service -description: "Create or modify a CoreEx Application-layer service. USE FOR: new service class (exception-based or Result), adding CRUD/business operations, CQRS read service (XxxReadService), adapter interface in Application/Adapters/, policy class in Application/Policies/, application-level mapper (Domain → Contract). DO NOT USE FOR: Infrastructure repositories (use coreex-repository), validators (use coreex-validator), controller endpoints." +description: "Create or modify a CoreEx Application-layer service. USE FOR: new service class (exception-based or Result), adding CRUD/business operations, CQRS read service (XxxReadService), adapter interface in Application/Adapters/, policy class in Application/Policies/, application-level mapper (Domain → Contract). DO NOT USE FOR: Infrastructure repositories (use coreex-repository), validators (use coreex-validator), controller endpoints (use coreex-api)." argument-hint: "Optional: entity name, operations needed (get/create/update/delete/query/custom), exception-based or Result pipeline, cross-domain calls needed" tags: ["application-service", "application-layer", "unit-of-work", "cqrs", "events", "adapter", "policy", "result", "coreex"] --- @@ -22,12 +22,13 @@ Guides you through creating or modifying a CoreEx Application-layer service in ` - Infrastructure repositories — use the `coreex-repository` skill - Validators — use the `coreex-validator` skill -- Controller endpoints — see the host conventions +- Controller endpoints — use `coreex-api` - Domain aggregates, entities, value objects — those belong in the Domain layer ## Quick Reference **Clarifying questions before writing any code:** +0. Resolve `rop-enabled` (exception vs `Result`), `domain-driven-enabled` (aggregate mapping) and `outbox-enabled` (event publishing) from the solution-root `AGENTS.md` **Feature Configuration** before asking the rest; only prompt for what is unrecorded and re-state resolved values for confirmation. 1. Exception-based or `Result` pipeline style? (→ Path A or B — per-project choice) 2. Which operations? Get / Create / Update / Delete / custom business action? (**never assume Query**) 3. Any cross-domain or external-service calls? (→ adapter interface, Path D) @@ -55,7 +56,10 @@ For full workflow and code examples see [`references/workflow.md`](references/wo ## Key References - [`/.github/instructions/coreex-application-services.instructions.md`](/.github/instructions/coreex-application-services.instructions.md) — full conventions: guard clauses, events, CQRS, adapters, policies, Result<T> operators -- [`/samples/src/Contoso.Products.Application/`](/samples/src/Contoso.Products.Application/) — `ProductService` (exception-based CRUD + business actions), `ProductReadService` (CQRS read) -- [`/samples/src/Contoso.Shopping.Application/`](/samples/src/Contoso.Shopping.Application/) — `BasketService` (Result<T> + adapter + policy), `BasketReadService` -- [`/samples/src/Contoso.Shopping.Application/Policies/`](/samples/src/Contoso.Shopping.Application/Policies/) — `ProductPolicy` example -- [`/samples/src/Contoso.Shopping.Application/Adapters/`](/samples/src/Contoso.Shopping.Application/Adapters/) — `IProductAdapter` example +- Related skills: [`coreex-repository`](../coreex-repository/SKILL.md) (persistence the service injects), [`coreex-validator`](../coreex-validator/SKILL.md) (invoked before mutations), [`coreex-policy`](../coreex-policy/SKILL.md) (I/O guard logic), [`coreex-adapter`](../coreex-adapter/SKILL.md) (cross-domain calls), [`coreex-contract`](../coreex-contract/SKILL.md) (the entity/request types), [`coreex-aggregate`](../coreex-aggregate/SKILL.md) (Domain layer + mapping), [`coreex-api`](../coreex-api/SKILL.md) (consumes the service), [`coreex-test-api`](../coreex-test-api/SKILL.md) (tests the service through the host) +- [Application layer deep-dive](/.github/docs/coreex/application-layer.md) — optional (after `/coreex-docs-sync`) +- Illustrative examples (CoreEx sample — not present in your project): + - [`ProductService` + `ProductReadService`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Application) — exception-based CRUD + business actions, CQRS read + - [`BasketService` + `BasketReadService`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Application) — Result<T> + adapter + policy + - [`ProductPolicy`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Application/Policies) — policy example + - [`IProductAdapter`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Application/Adapters/Products) — adapter interface example diff --git a/.github/skills/coreex-contract/SKILL.md b/.github/skills/coreex-contract/SKILL.md index d4cea214..1cb3e36e 100644 --- a/.github/skills/coreex-contract/SKILL.md +++ b/.github/skills/coreex-contract/SKILL.md @@ -26,6 +26,7 @@ Guides you through creating or modifying a hand-authored contract (DTO/entity) i ## Quick Reference **Clarifying questions to ask before emitting any code:** +0. **Resolve from state first.** Read the solution-root `AGENTS.md` **Feature Configuration** for `refdata-enabled` (whether `[ReferenceData]` wiring applies) and `domain-driven-enabled` (aggregate-mapping context). Only ask for what is unrecorded; re-state resolved values for confirmation. 1. Root or subordinate? (if not explicit) 2. Identifier type? (default `string?` — confirm before using any other type) 3. `IETag` needed? (default yes for root contracts — omit only on explicit request) @@ -46,5 +47,7 @@ For full workflow, decision trees, and code examples see [`references/workflow.m - [`/.github/instructions/coreex-contracts.instructions.md`](/.github/instructions/coreex-contracts.instructions.md) — full contract conventions: interfaces, source generation, property rules, localization, inheritance - [`/.github/instructions/coreex-conventions.instructions.md`](/.github/instructions/coreex-conventions.instructions.md) — XML doc comments, expression bodies, brace style -- [`/samples/src/Contoso.Products.Contracts/`](/samples/src/Contoso.Products.Contracts/) — canonical multi-contract examples (base class, root entity, subordinate, ref-data extension) -- [`/samples/src/Contoso.Shopping.Contracts/`](/samples/src/Contoso.Shopping.Contracts/) — canonical request/response and subordinate contracts +- Related skills: [`coreex-refdata`](../coreex-refdata/SKILL.md) (generated ref-data contracts), [`coreex-aggregate`](../coreex-aggregate/SKILL.md) (DDD domain object the contract maps to), [`coreex-repository`](../coreex-repository/SKILL.md) (mapping), [`coreex-app-service`](../coreex-app-service/SKILL.md) (consumes contracts) +- Illustrative examples (CoreEx sample — not present in your project): + - [multi-contract examples](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Contracts) — base class, root entity, subordinate, ref-data extension + - [request/response and subordinate contracts](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Contracts) diff --git a/.github/skills/coreex-contract/references/workflow.md b/.github/skills/coreex-contract/references/workflow.md index 74aac525..7acaa083 100644 --- a/.github/skills/coreex-contract/references/workflow.md +++ b/.github/skills/coreex-contract/references/workflow.md @@ -115,6 +115,8 @@ Key assembly rules: When the root contract extends a base class: +> Example uses an illustrative Product domain — substitute your own entity/type names. + ```csharp /// Provides the base Product contract properties. [Contract] @@ -158,6 +160,8 @@ public partial class ProductLite : ProductBase A subordinate is accessed only through a parent (e.g. `BasketItem`, `OrderLine`, `Address`). A request/response object has no identity (e.g. `BasketItemAddRequest`, `ProductReserve`). +> Example uses an illustrative Basket domain — substitute your own entity/type names. + ```csharp // Subordinate with identity (nested resource with its own ETag) /// Represents the BasketItem contract. diff --git a/.github/skills/coreex-db-migration/SKILL.md b/.github/skills/coreex-db-migration/SKILL.md index 74e016c8..ada8048e 100644 --- a/.github/skills/coreex-db-migration/SKILL.md +++ b/.github/skills/coreex-db-migration/SKILL.md @@ -23,6 +23,11 @@ Guides you through any database schema change for a CoreEx domain — from choos - Generating CoreEx contracts/services/repositories — that is `*.CodeGen`, not `*.Database` - Runtime or deployment issues +> **Resolve project-wide choices from state before asking.** Read the solution-root `AGENTS.md` +> **Feature Configuration** for `data-provider` (SQL Server / PostgreSQL — gates this whole skill; `None` +> means no database) and `outbox-enabled`. Only prompt for what is unrecorded; re-state resolved values +> for confirmation. + ## Quick Reference All commands run from the `*.Database` project directory. @@ -60,5 +65,7 @@ For the full step-by-step decision tree, SQL column templates, and guardrails se - [`/.github/instructions/coreex-tooling.instructions.md`](/.github/instructions/coreex-tooling.instructions.md) — DbEx command reference, `dbex.yaml` structure, SQL conventions, outbox provisioning - [`/.github/instructions/coreex-repositories.instructions.md`](/.github/instructions/coreex-repositories.instructions.md) — what the generated `*.g.cs` feeds into -- [`/samples/src/Contoso.Products.Database/`](/samples/src/Contoso.Products.Database/) — canonical PostgreSQL domain example -- [`/samples/src/Contoso.Shopping.Database/`](/samples/src/Contoso.Shopping.Database/) — canonical SQL Server domain example +- Related skills: [`coreex-refdata`](../coreex-refdata/SKILL.md) (reference-data tables + CodeGen), [`coreex-repository`](../coreex-repository/SKILL.md) (maps the generated persistence models) +- Illustrative examples (CoreEx sample — not present in your project): + - [PostgreSQL domain database project](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Database) — canonical `.pgsql` / `snake_case` migrations, `dbex.yaml`, seed files + - [SQL Server domain database project](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Database) — canonical `.sql` / `PascalCase` migrations, `dbex.yaml`, seed files diff --git a/.github/skills/coreex-db-migration/references/workflow.md b/.github/skills/coreex-db-migration/references/workflow.md index 7bdf18d9..71d59924 100644 --- a/.github/skills/coreex-db-migration/references/workflow.md +++ b/.github/skills/coreex-db-migration/references/workflow.md @@ -239,6 +239,27 @@ This runs: Create → Migrate → CodeGen → Schema → Data. --- +## Outbox provisioning + +Provision the transactional outbox table(s) when the domain publishes events via the outbox — i.e. `outbox-enabled` +in the solution-root `AGENTS.md` Feature Configuration, and only for a `data-provider` other than `None`. The outbox +table(s) are created by a DbEx-generated migration script — never hand-authored. + +1. **Validate `dbex.yaml`** — all three must hold; if any is missing, stop and state which: + - `outbox: true` + - root-level `schema:` has a value (call it `xxx`) + - root-level `outboxName:` has a value (call it `yyy`) +2. **Check for an existing create script** under `Migrations/` (`*-create-*-outbox-tables.{sql,pgsql}`). The `coreex` + template ships one when outbox was enabled at scaffold time — if present, **do not scaffold another**; skip to step 4. +3. **Scaffold only when none exists:** `dotnet run -- script outbox xxx yyy`. +4. **Apply** (on confirmation): `dotnet run -- CreateMigrateAndCodeGen`. On failure, surface the verbatim output. + +The outbox create script is immutable once applied — a later shape change (e.g. a DbEx version bump) needs a **new** +`ALTER` migration, never an edit to the original. The generated outbox schema objects (stored procedures / functions) +are owned by DbEx; see [`/.github/instructions/coreex-tooling.instructions.md`](/.github/instructions/coreex-tooling.instructions.md). + +--- + ## Guardrails - **Scripts are immutable once applied.** Never modify a migration script that has already been run. Author a new script for any subsequent delta. @@ -250,5 +271,5 @@ This runs: Create → Migrate → CodeGen → Schema → Data. ``` This runs: Drop → Create → Migrate → CodeGen → Schema → Data — a completely fresh slate. **This is destructive — confirm with the user before executing.** The `--accept-prompts` flag bypasses DbEx's built-in confirmation prompt for DROP commands; only pass it after the user has explicitly consented. Never use `dropandall` on a shared or production database. - **Path D filename limit:** full filename ≤255 characters including the timestamp prefix and extension. Keep descriptors to 3–5 words. -- **Outbox provisioning is a separate concern** — use `dotnet run -- script outbox ` as described in `coreex-tooling.instructions.md`. Do not conflate it with this workflow. +- **Outbox provisioning has its own flow** — see the [Outbox provisioning](#outbox-provisioning) section above; do not conflate it with a table create/alter. - **No schema-create script.** The `coreex` template ships the schema-create migration; never emit another `create--schema` script unless an additional schema is explicitly requested. diff --git a/.github/skills/coreex-docs-sync/README.md b/.github/skills/coreex-docs-sync/README.md index e513d62f..6b067209 100644 --- a/.github/skills/coreex-docs-sync/README.md +++ b/.github/skills/coreex-docs-sync/README.md @@ -4,9 +4,9 @@ Fetches the CoreEx architecture docs and per-package AI guides from GitHub and c ## When to run -- **First time** setting up a consuming project — populates the cache from scratch. -- **After bumping a CoreEx NuGet version** — keeps the guides in sync with the version in use. +- **After bumping a CoreEx NuGet version** — refreshes the guides to match the version in use. - **When the CoreEx Expert recommends it** — the agent checks the manifest on every session and flags if the cache is older than 30 days or the recorded version doesn't match the project's current packages. +- **First-time population** — `dotnet new coreex-ai` already ships the cache at install; run this only if the cache is absent (e.g. the assets were copied in manually) or to pull the latest `main`. Do not run this inside the CoreEx repository itself — the docs are already present locally at `samples/docs/` and `src/*/AGENTS.md`. diff --git a/.github/skills/coreex-docs-sync/SKILL.md b/.github/skills/coreex-docs-sync/SKILL.md index fc3ff23b..5d700635 100644 --- a/.github/skills/coreex-docs-sync/SKILL.md +++ b/.github/skills/coreex-docs-sync/SKILL.md @@ -11,10 +11,10 @@ Fetches the CoreEx sample architecture docs and AI usage guides for all CoreEx p ## When to Use -- Setting up the local doc cache for the first time in a consuming project. -- After bumping a CoreEx NuGet package version. -- When the CoreEx Expert recommends a refresh (cache older than 30 days or version mismatch). -- Any time you want to ensure local docs reflect the latest CoreEx `main` branch. +- **Refresh** the cache after bumping a CoreEx NuGet package version. +- When the CoreEx Expert flags the cache stale (older than 30 days, or a version mismatch). +- To pull the latest CoreEx `main` branch docs. +- **First-time population** only when the cache is absent — e.g. a project that installed the AI assets manually. `dotnet new coreex-ai` already ships the cache at install, so most consumers use this skill to refresh, not populate. ## When Not to Use diff --git a/.github/skills/coreex-policy/SKILL.md b/.github/skills/coreex-policy/SKILL.md index 4aa24ac5..ff491665 100644 --- a/.github/skills/coreex-policy/SKILL.md +++ b/.github/skills/coreex-policy/SKILL.md @@ -25,6 +25,7 @@ Guides you through creating or modifying a CoreEx Application-layer policy class ## Quick Reference **Clarifying questions before writing any code:** +0. Resolve `rop-enabled` (`Result` vs exception style — policies compose into `Result` pipelines) from the solution-root `AGENTS.md` **Feature Configuration** before asking the rest; only prompt for what is unrecorded and re-state resolved values for confirmation. 1. What entity / concept is being guarded? (names the policy class) 2. What type of guard? EnsureExists / business rule / state check 3. Which adapter or repository does the policy call? @@ -47,5 +48,7 @@ For full workflow and code examples see [`references/workflow.md`](references/wo ## Key References - [`/.github/instructions/coreex-application-services.instructions.md`](/.github/instructions/coreex-application-services.instructions.md) — policies, adapters, DI registration principle, Result<T> pipeline operators -- [`/samples/src/Contoso.Shopping.Application/Policies/`](/samples/src/Contoso.Shopping.Application/Policies/) — `ProductPolicy` — EnsureExists translating NotFoundError → ValidationError -- [`/samples/src/Contoso.Shopping.Application/BasketService.cs`](/samples/src/Contoso.Shopping.Application/BasketService.cs) — `ItemAddAsync` shows policy in a `Result.GoAsync().ThenAsAsync()` pipeline +- Related skills: [`coreex-app-service`](../coreex-app-service/SKILL.md) (wires the policy into a service), [`coreex-adapter`](../coreex-adapter/SKILL.md) + [`coreex-repository`](../coreex-repository/SKILL.md) (the dependencies a policy calls), [`coreex-validator`](../coreex-validator/SKILL.md) (synchronous, no-I/O guard sibling) +- Illustrative examples (CoreEx sample — not present in your project): + - [`ProductPolicy`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Application/Policies) — EnsureExists translating NotFoundError → ValidationError + - [`BasketService.cs`](https://github.com/Avanade/CoreEx/blob/main/samples/src/Contoso.Shopping.Application/BasketService.cs) — `ItemAddAsync` shows a policy in a `Result.GoAsync().ThenAsAsync()` pipeline diff --git a/.github/skills/coreex-refdata/SKILL.md b/.github/skills/coreex-refdata/SKILL.md index 063f6148..759cf54a 100644 --- a/.github/skills/coreex-refdata/SKILL.md +++ b/.github/skills/coreex-refdata/SKILL.md @@ -26,6 +26,11 @@ Guides you through the complete end-to-end workflow for adding or modifying a re ## Quick Reference +> **Resolve project-wide choices from state before asking.** Read the solution-root `AGENTS.md` +> **Feature Configuration** for `refdata-enabled` (gates this whole skill) and `data-provider` +> (SQL Server / PostgreSQL — drives casing and script extension). Only prompt for what is unrecorded; +> re-state resolved values for confirmation. + **Two separate `dotnet run` steps in two different projects — never conflate them.** | Task | Project | Command / File | @@ -63,7 +68,7 @@ For the full decision tree, YAML examples, seeding rules, and guardrails see [`r - [`/.github/instructions/coreex-tooling.instructions.md`](/.github/instructions/coreex-tooling.instructions.md) — `ref-data.yaml` structure, `dbex.yaml`, seed format, `Script`/`All`/`Data` commands - [`/.github/instructions/coreex-contracts.instructions.md`](/.github/instructions/coreex-contracts.instructions.md) — `[ReferenceData]`, `partial` properties, global usings -- [`/samples/src/Contoso.Products.CodeGen/ref-data.yaml`](/samples/src/Contoso.Products.CodeGen/ref-data.yaml) — canonical PostgreSQL CodeGen definition -- [`/samples/src/Contoso.Shopping.CodeGen/ref-data.yaml`](/samples/src/Contoso.Shopping.CodeGen/ref-data.yaml) — canonical SQL Server CodeGen definition -- [`/samples/src/Contoso.Products.Database/Data/ref-data.seed.yaml`](/samples/src/Contoso.Products.Database/Data/ref-data.seed.yaml) — canonical PostgreSQL seed file -- [`/samples/src/Contoso.Shopping.Database/Data/ref-data.seed.yaml`](/samples/src/Contoso.Shopping.Database/Data/ref-data.seed.yaml) — canonical SQL Server seed file +- Related skills: [`coreex-contract`](../coreex-contract/SKILL.md) (wires `[ReferenceData]` into contracts), [`coreex-db-migration`](../coreex-db-migration/SKILL.md) (ref-data table schema) +- Illustrative examples (CoreEx sample — not present in your project): + - [PostgreSQL CodeGen definition](https://github.com/Avanade/CoreEx/blob/main/samples/src/Contoso.Products.CodeGen/ref-data.yaml) and its [seed file](https://github.com/Avanade/CoreEx/blob/main/samples/src/Contoso.Products.Database/Data/ref-data.seed.yaml) + - [SQL Server CodeGen definition](https://github.com/Avanade/CoreEx/blob/main/samples/src/Contoso.Shopping.CodeGen/ref-data.yaml) and its [seed file](https://github.com/Avanade/CoreEx/blob/main/samples/src/Contoso.Shopping.Database/Data/ref-data.seed.yaml) diff --git a/.github/skills/coreex-repository/SKILL.md b/.github/skills/coreex-repository/SKILL.md index 674b9294..c08db591 100644 --- a/.github/skills/coreex-repository/SKILL.md +++ b/.github/skills/coreex-repository/SKILL.md @@ -27,6 +27,7 @@ Guides you through creating or modifying a CoreEx Infrastructure-layer repositor ## Quick Reference **Clarifying questions before writing any code:** +0. **Resolve from state first.** Read the solution-root `AGENTS.md` **Feature Configuration** for `data-provider` (SQL Server / PostgreSQL — gates this skill) and `domain-driven-enabled` (Domain-mapping vs contract-mapping). Only ask for what is unrecorded; re-state resolved values for confirmation. 1. Which entity? Which database type (PostgreSQL / SQL Server)? Check the project's `Program.cs`. 2. New repository or adding to an existing one? 3. Operations needed: Get / Create / Update / Delete / Query? @@ -48,7 +49,7 @@ For full workflow and code examples see [`references/workflow.md`](references/wo ## Key References - [`/.github/instructions/coreex-repositories.instructions.md`](/.github/instructions/coreex-repositories.instructions.md) — full conventions: EfDb, DbContext, mappers, query config, adapters -- [`/samples/src/Contoso.Products.Infrastructure/Repositories/`](/samples/src/Contoso.Products.Infrastructure/Repositories/) — `ProductRepository` (CRUD + query), `ProductsEfDb`, `ProductsDbContext` -- [`/samples/src/Contoso.Products.Infrastructure/Mapping/`](/samples/src/Contoso.Products.Infrastructure/Mapping/) — `ProductMapper` (BiDirectionMapper) -- [`/samples/src/Contoso.Shopping.Infrastructure/Repositories/`](/samples/src/Contoso.Shopping.Infrastructure/Repositories/) — `BasketRepository` (Result + Domain mapping) -- [`/samples/src/Contoso.Shopping.Infrastructure/Mapping/`](/samples/src/Contoso.Shopping.Infrastructure/Mapping/) — `BasketMapper` (Domain ↔ Persistence one-way mapper) +- Related skills: [`coreex-db-migration`](../coreex-db-migration/SKILL.md) (generates the persistence models this maps), [`coreex-app-service`](../coreex-app-service/SKILL.md) + [`coreex-adapter`](../coreex-adapter/SKILL.md) (consumers), [`coreex-aggregate`](../coreex-aggregate/SKILL.md) (persisting aggregates) +- Illustrative examples (CoreEx sample — not present in your project): + - [contract-mapping repositories](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Infrastructure/Repositories) — `ProductRepository` (CRUD + query), `ProductsEfDb`, `ProductsDbContext` — and [BiDirectionMapper](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Infrastructure/Mapping) (`ProductMapper`) + - [Domain-mapping repositories](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Infrastructure/Repositories) — `BasketRepository` (`Result` + Domain mapping) — and [one-way Domain ↔ Persistence mappers](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Infrastructure/Mapping) (`BasketMapper`) diff --git a/.github/skills/coreex-subscriber/SKILL.md b/.github/skills/coreex-subscriber/SKILL.md index dade84f1..307b67ad 100644 --- a/.github/skills/coreex-subscriber/SKILL.md +++ b/.github/skills/coreex-subscriber/SKILL.md @@ -17,9 +17,9 @@ There are three distinct subscriber scenarios — determine which applies before | Scenario | Trigger | Delegates to | Example | |---|---|---|---| -| **Command** | A command addressed to _this_ domain arrives on the broker | Application service | `ReservationConfirmSubscriber` → `IMovementService` | -| **Event — Data Sync** | An event from _another_ domain/system arrives; maintain a local cached copy | Replication adapter (`IXxxSyncAdapter`) | `ProductModifySubscriber` → `IProductSyncAdapter` | -| **Event — Business Process** | An event from another domain arrives; trigger a choreography step in _this_ domain | Application service | e.g., `OrderPlacedSubscriber` → `IFulfillmentService` | +| **Command** | A command addressed to _this_ domain arrives on the broker | Application service | e.g. `{Entity}ConfirmSubscriber` → `I{Entity}Service` | +| **Event — Data Sync** | An event from _another_ domain/system arrives; maintain a local cached copy | Replication adapter (`IXxxSyncAdapter`) | e.g. `{Entity}ModifySubscriber` → `I{Entity}SyncAdapter` | +| **Event — Business Process** | An event from another domain arrives; trigger a choreography step in _this_ domain | Application service | e.g. `{Entity}PlacedSubscriber` → `I{Entity}Service` | > **Rule:** Never subscribe to a **command addressed to another domain** — only to commands addressed > to this domain, or to events from any domain. Subscribing to another domain's commands means you @@ -30,7 +30,13 @@ There are three distinct subscriber scenarios — determine which applies before - HTTP API controllers — use `coreex-api` - Application services that the subscriber calls — use `coreex-app-service` - Replication adapter implementations (`IXxxSyncAdapter`) — use `coreex-adapter` -- Subscribe host `Program.cs` setup or Service Bus receiver wiring — see `.github/instructions/coreex-host-setup.instructions.md` +- Subscribe host `Program.cs` setup or Service Bus receiver wiring — see [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md) + +> **Resolve project-wide choices from state before asking.** Read the solution-root `AGENTS.md` +> **Feature Configuration**: `messaging-provider` gates this skill (a Subscribe host exists only when a +> messaging provider is configured); `outbox-enabled` determines whether the delegated service publishes +> resulting events transactionally; `rop-enabled` confirms `Result`/`Result` return-style pipelines. +> Only prompt for what is unrecorded; re-state resolved values for confirmation. ## Quick Reference @@ -47,10 +53,12 @@ For full workflow and code examples see [`references/workflow.md`](references/wo ## Key References -- `samples/src/Contoso.Products.Subscribe/Subscribers/ReservationConfirmSubscriber.cs` — command subscriber with `ErrorHandler` -- `samples/src/Contoso.Products.Subscribe/Subscribers/ReservationCancelSubscriber.cs` — command subscriber sharing an `ErrorHandler` -- `samples/src/Contoso.Shopping.Subscribe/Subscribers/ProductModifySubscriber.cs` — typed event-sync subscriber with `ValueValidator` -- `samples/src/Contoso.Shopping.Subscribe/Subscribers/ProductDeleteSubscriber.cs` — untyped event-sync subscriber (key-only delete) -- `coreex-test-subscribe` — full Subscribe-test integration test workflow (test class shape, simulating message receipt, command/event-sync/event-business-process test patterns, unsubscribed-subject test) -- `.github/instructions/coreex-event-subscribers.instructions.md` — full subscriber conventions reference -- `.github/instructions/coreex-host-setup.instructions.md` — Subscribe host `Program.cs` shape +- [`/.github/instructions/coreex-event-subscribers.instructions.md`](/.github/instructions/coreex-event-subscribers.instructions.md) — full subscriber conventions reference +- [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md) — Subscribe host `Program.cs` shape +- Related skills: [`coreex-app-service`](../coreex-app-service/SKILL.md) (command/business-process subscribers delegate to it), [`coreex-adapter`](../coreex-adapter/SKILL.md) (data-sync subscribers drive `IXxxSyncAdapter`), [`coreex-api`](../coreex-api/SKILL.md) (sibling HTTP entry point), [`coreex-test-subscribe`](../coreex-test-subscribe/SKILL.md) (full Subscribe-test integration test workflow — test class shape, simulating message receipt, per-scenario patterns, unsubscribed-subject test) +- [Hosts layer deep-dive](/.github/docs/coreex/hosts-layer.md) — optional (after `/coreex-docs-sync`) +- Illustrative examples (CoreEx sample — not present in your project): + - [ReservationConfirmSubscriber](https://github.com/Avanade/CoreEx/blob/main/samples/src/Contoso.Products.Subscribe/Subscribers/ReservationConfirmSubscriber.cs) — command subscriber with `ErrorHandler` + - [ReservationCancelSubscriber](https://github.com/Avanade/CoreEx/blob/main/samples/src/Contoso.Products.Subscribe/Subscribers/ReservationCancelSubscriber.cs) — command subscriber sharing an `ErrorHandler` + - [ProductModifySubscriber](https://github.com/Avanade/CoreEx/blob/main/samples/src/Contoso.Shopping.Subscribe/Subscribers/ProductModifySubscriber.cs) — typed event-sync subscriber with `ValueValidator` + - [ProductDeleteSubscriber](https://github.com/Avanade/CoreEx/blob/main/samples/src/Contoso.Shopping.Subscribe/Subscribers/ProductDeleteSubscriber.cs) — untyped event-sync subscriber (key-only delete) diff --git a/.github/skills/coreex-subscriber/references/workflow.md b/.github/skills/coreex-subscriber/references/workflow.md index 6641a2af..b9ad2431 100644 --- a/.github/skills/coreex-subscriber/references/workflow.md +++ b/.github/skills/coreex-subscriber/references/workflow.md @@ -161,8 +161,8 @@ Use dot-separated lowercase strings: `{solution}.{domain}.{entity}.{action}[.v{n | Key-only (no payload) → no version suffix | `contoso.products.product.deleted` | | Commands with payload → include `.v{n}` suffix | `contoso.products.reservation.create.v1` | | Commands key-only (no payload) → no version suffix | `contoso.products.reservation.confirm` | -| `EventData.CreateCommand(...)` for commands | `EventData.CreateCommand("products", "reservation", "confirm")` | -| `EventData.CreateEvent(...)` or `new EventData().WithTitle(...)` for events | `new EventData().WithTitle("contoso.products.product.updated.v1")` | +| `EventData.CreateCommand(...)` for commands | `EventData.CreateCommand("{domain}", "{entity}", "{action}")` | +| `EventData.CreateEvent(...)` or `new EventData().WithTitle(...)` for events | `new EventData().WithTitle("{solution}.{domain}.{entity}.updated.v1")` | The version reflects the payload schema — not whether the message is an event or command. A command with a payload would also carry a version; a key-only event would not. @@ -186,6 +186,9 @@ internal static readonly ErrorHandler DefaultErrorHandler = new ErrorHandler() **Share across related subscribers:** +> Example uses an illustrative Reservation domain — substitute your own entity/service/subscriber +> names and `{solution}`/`{domain}` literals. + ```csharp // ReservationCancelSubscriber.cs — reuses the handler defined in ReservationConfirmSubscriber public ReservationCancelSubscriber(IMovementService service) @@ -256,6 +259,9 @@ There are two coordination styles: ### Choreography with CoreEx Subscribers +> Example uses an illustrative Shopping/Products reservation flow — substitute your own domains, +> entities, subjects, and `{solution}` literals. + The Shopping/Products reservation flow is a choreography saga: 1. **Shopping** places an order → publishes `reservation.confirm` command via Outbox relay @@ -273,7 +279,7 @@ restores inventory. - Limit cross-domain coordination where possible — every saga hop adds latency and failure surface area. > **Orchestration note:** When coordination complexity exceeds what choreography can handle cleanly, -> consider a Durable Task-based orchestrator (the CoreEx `samples/aspire` setup includes the +> consider a Durable Task-based orchestrator (the CoreEx samples' Aspire setup includes the > Durable Task emulator). Orchestration is out of scope for the subscriber skill — it lives in a > dedicated orchestrator service, not in subscriber classes. diff --git a/.github/skills/coreex-test-api/SKILL.md b/.github/skills/coreex-test-api/SKILL.md index c99adea4..3572d951 100644 --- a/.github/skills/coreex-test-api/SKILL.md +++ b/.github/skills/coreex-test-api/SKILL.md @@ -26,6 +26,12 @@ setup shared with Subscribe host tests (`coreex-test-subscribe` links back here - Validator/aggregate/adapter unit tests (`*.Test.Unit`) — already covered by `coreex-validator`/`coreex-aggregate`/`coreex-adapter` - Implementing the controller/endpoint itself — use `coreex-api` +> **Resolve project-wide choices from state before asking.** Read the solution-root `AGENTS.md` +> **Feature Configuration**: `data-provider` (PostgreSQL vs SQL Server) selects the migrate/seed and +> outbox helper family used throughout — never mix them; `outbox-enabled` and `messaging-provider` +> determine whether outbox-event assertions (`ExpectXxxOutboxEvents`) apply at all. Only prompt for +> what is unrecorded; re-state resolved values for confirmation. + ## Quick Reference - **One class pair per entity**: `XxxReadTests` (seeds `read-data.seed.yaml`) / `XxxMutateTests` (seeds `mutate-data.seed.yaml`) — each a `partial class`, one operation per sub-file: `Xxx{Read|Mutate}Tests.{Operation}.cs` @@ -46,8 +52,8 @@ For full workflow, decision trees, and code patterns see [`references/workflow.m ## Key References - [`/.github/instructions/coreex-tests.instructions.md`](/.github/instructions/coreex-tests.instructions.md) — full, authoritative test conventions (auto-injected on any `*.Test*/**/*.cs` edit); this skill is a practical workflow layered on top of it, not a replacement -- [`/samples/docs/testing.md`](/samples/docs/testing.md) — test architecture, data seeding, schema isolation, E2E runner -- `samples/tests/Contoso.Products.Test.Api/` — PostgreSQL domain example (outbox + HTTP mock adapter) -- `samples/tests/Contoso.Shopping.Test.Api/` — SQL Server domain example (outbox + ETag/concurrency) -- `coreex-test-subscribe` — Subscribe host tests, shares this skill's setup foundations -- `coreex-api` — the controller/endpoint implementation this skill's tests exercise +- Related skills: [`coreex-api`](../coreex-api/SKILL.md) (the controller/endpoint implementation these tests exercise), [`coreex-app-service`](../coreex-app-service/SKILL.md) (Application logic behind the endpoint), [`coreex-test-subscribe`](../coreex-test-subscribe/SKILL.md) (Subscribe host tests — shares this skill's setup foundations), [`coreex-test-relay`](../coreex-test-relay/SKILL.md) (Outbox Relay host tests) +- [Testing deep-dive](/.github/docs/coreex/testing.md) — test architecture, data seeding, schema isolation, E2E runner; optional (after `/coreex-docs-sync`) +- Illustrative examples (CoreEx sample — not present in your project): + - [Contoso.Products.Test.Api](https://github.com/Avanade/CoreEx/tree/main/samples/tests/Contoso.Products.Test.Api) — PostgreSQL domain example (outbox + HTTP mock adapter) + - [Contoso.Shopping.Test.Api](https://github.com/Avanade/CoreEx/tree/main/samples/tests/Contoso.Shopping.Test.Api) — SQL Server domain example (outbox + ETag/concurrency) diff --git a/.github/skills/coreex-test-api/references/workflow.md b/.github/skills/coreex-test-api/references/workflow.md index 43f345bb..f02bde5e 100644 --- a/.github/skills/coreex-test-api/references/workflow.md +++ b/.github/skills/coreex-test-api/references/workflow.md @@ -88,6 +88,9 @@ If the domain calls another domain over HTTP, add HTTP mocking to `OneTimeSetUp` ## Phase 3 — Author Tests Per Operation +> Examples throughout this phase use an illustrative Product domain — substitute your own entity/type +> names and routes, and replace the `contoso`/`products` subject literals with your `{solution}`/`{domain}`. + Co-design seed → tests → resources **in that order** (seed rows exist before you write assertions against them; resources get captured from the actual run). diff --git a/.github/skills/coreex-test-relay/SKILL.md b/.github/skills/coreex-test-relay/SKILL.md index 9430067d..1606041d 100644 --- a/.github/skills/coreex-test-relay/SKILL.md +++ b/.github/skills/coreex-test-relay/SKILL.md @@ -25,7 +25,13 @@ rarely need new scenarios per domain. - API host integration tests — use `coreex-test-api` - Subscribe host integration tests — use `coreex-test-subscribe` -- Relay host `Program.cs` / hosted-service registration — see `.github/instructions/coreex-host-setup.instructions.md` +- Relay host `Program.cs` / hosted-service registration — see [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md) + +> **Resolve project-wide choices from state before asking.** Read the solution-root `AGENTS.md` +> **Feature Configuration**: `messaging-provider` gates this skill (a Relay host exists only when a +> messaging provider is configured) and `data-provider` (PostgreSQL vs SQL Server) selects the outbox +> publisher family (`PostgresOutboxPublisher`/`SqlServerOutboxPublisher`) — never mix them. Only prompt +> for what is unrecorded; re-state resolved values for confirmation. ## Quick Reference @@ -46,14 +52,14 @@ public class RelayTests : WithApiTester test.Run(async _ => { var pub = ActivatorUtilities.GetServiceOrCreateInstance(test.Services); // or SqlServerOutboxPublisher — provider-specific - pub.Add("contoso", [ce1, ce2]); + pub.Add("{solution}", [ce1, ce2]); await pub.PublishAsync(); for (int i = 0; i < 5; i++) await Task.Delay(TimeSpan.FromSeconds(1)); var list = await Test.GetAndClearAzureServiceBusAsync( - ServiceBusSessionReceiverOptions.CreateForTopicSubscription("contoso", "products")); + ServiceBusSessionReceiverOptions.CreateForTopicSubscription("{solution}", "{domain}")); list.Should().HaveCount(2); }).AssertSuccess(); @@ -84,6 +90,7 @@ that config at startup). Do not "fix" it by editing the test, subject names, or ## Key References - [`/.github/instructions/coreex-tests.instructions.md`](/.github/instructions/coreex-tests.instructions.md) — full, authoritative test conventions, "Outbox Relay Host Tests" section -- `samples/tests/Contoso.Products.Test.Relay/RelayTests.cs` — canonical outbox-forwarding test -- `samples/tests/Contoso.Products.Test.Relay/OtherTests.Health.cs`, `OtherTests.HostedServices.cs` — health and hosted-service management checks - `coreex-test-api` / `coreex-test-subscribe` — the other two integration test skills, both more likely to need per-domain extension than this one +- Illustrative examples (CoreEx sample — not present in your project): + - [RelayTests.cs](https://github.com/Avanade/CoreEx/blob/main/samples/tests/Contoso.Products.Test.Relay/RelayTests.cs) — canonical outbox-forwarding test + - [OtherTests.Health.cs](https://github.com/Avanade/CoreEx/blob/main/samples/tests/Contoso.Products.Test.Relay/OtherTests.Health.cs), [OtherTests.HostedServices.cs](https://github.com/Avanade/CoreEx/blob/main/samples/tests/Contoso.Products.Test.Relay/OtherTests.HostedServices.cs) — health and hosted-service management checks diff --git a/.github/skills/coreex-test-subscribe/SKILL.md b/.github/skills/coreex-test-subscribe/SKILL.md index b6d4b7c6..79f467cf 100644 --- a/.github/skills/coreex-test-subscribe/SKILL.md +++ b/.github/skills/coreex-test-subscribe/SKILL.md @@ -27,6 +27,12 @@ test triggers behavior: a simulated message receipt instead of an HTTP call. - Outbox Relay host tests — use `coreex-test-relay` - Implementing the subscriber class itself — use `coreex-subscriber` +> **Resolve project-wide choices from state before asking.** Read the solution-root `AGENTS.md` +> **Feature Configuration**: `messaging-provider` gates this skill (Subscribe host tests exist only when +> a messaging provider is configured); `data-provider` (PostgreSQL vs SQL Server) selects the +> migrate/seed and outbox helper family used throughout — never mix them; `outbox-enabled` determines +> whether outbox-event assertions apply. Only prompt for what is unrecorded; re-state resolved values. + ## Quick Reference - **Base class**: `WithApiTester<{Domain}.Subscribe.Program>` — same DB/cache/outbox `[OneTimeSetUp]` shape as API tests (migrate + seed via named-file overload → `ClearFusionCacheAsync()` → provider-specific `UseExpected{Postgres|SqlServer}OutboxPublisher()`); Subscribe hosts **do** have FusionCache (reference data, idempotency) @@ -44,7 +50,7 @@ shared DB/cache/outbox setup mechanics (seed data authoring, provider-specific o ## Key References - [`/.github/instructions/coreex-tests.instructions.md`](/.github/instructions/coreex-tests.instructions.md) — full, authoritative test conventions, "Subscribe Host Tests" section -- `coreex-test-api` — shared integration-test setup foundations (DB migrate/seed, cache, outbox) -- `coreex-subscriber` — the subscriber implementation this skill's tests exercise; its three scenarios (command / event-data-sync / event-business-process) map directly to this skill's test shapes -- `samples/tests/Contoso.Products.Test.Subscribe/SubscriberTests.ReservationConfirm.cs` — command subscriber test (outbox assertion + `ErrorHandler`) -- `samples/tests/Contoso.Shopping.Test.Subscribe/SubscriberTests.ProductModify.cs` — event-sync subscriber test (state assertion) +- Related skills: [`coreex-subscriber`](../coreex-subscriber/SKILL.md) (the subscriber implementation under test — its command / event-data-sync / event-business-process scenarios map directly to this skill's test shapes), [`coreex-test-api`](../coreex-test-api/SKILL.md) (shared integration-test setup foundations — DB migrate/seed, cache, outbox), [`coreex-test-relay`](../coreex-test-relay/SKILL.md) (Outbox Relay host tests) +- Illustrative examples (CoreEx sample — not present in your project): + - [SubscriberTests.ReservationConfirm.cs](https://github.com/Avanade/CoreEx/blob/main/samples/tests/Contoso.Products.Test.Subscribe/SubscriberTests.ReservationConfirm.cs) — command subscriber test (outbox assertion + `ErrorHandler`) + - [SubscriberTests.ProductModify.cs](https://github.com/Avanade/CoreEx/blob/main/samples/tests/Contoso.Shopping.Test.Subscribe/SubscriberTests.ProductModify.cs) — event-sync subscriber test (state assertion) diff --git a/.github/skills/coreex-validator/SKILL.md b/.github/skills/coreex-validator/SKILL.md index 10c07788..a415c9d7 100644 --- a/.github/skills/coreex-validator/SKILL.md +++ b/.github/skills/coreex-validator/SKILL.md @@ -55,6 +55,8 @@ For full workflow, rule reference, and code examples see [`references/workflow.m - [`/.github/instructions/coreex-validators.instructions.md`](/.github/instructions/coreex-validators.instructions.md) — full rule set, comparison operators, localization, DependsOn, DI registration - [`/.github/instructions/coreex-tests.instructions.md`](/.github/instructions/coreex-tests.instructions.md) — validator unit test conventions: `Test.Scoped`, `AssertErrors`, expected message text -- [`/samples/src/Contoso.Products.Application/Validators/`](/samples/src/Contoso.Products.Application/Validators/) — `ProductValidator` (simple), `MovementRequestValidator` (injection + dictionary + async) -- [`/samples/src/Contoso.Shopping.Application/Validators/`](/samples/src/Contoso.Shopping.Application/Validators/) — `AbstractValidator` style, plain request validators -- [`/samples/src/Contoso.Orders.Application/Validators/`](/samples/src/Contoso.Orders.Application/Validators/) — `OrderValidator` (collection validator) +- Related skills: [`coreex-app-service`](../coreex-app-service/SKILL.md) (invokes the validator), [`coreex-policy`](../coreex-policy/SKILL.md) (async I/O guard sibling for checks a validator cannot do), [`coreex-contract`](../coreex-contract/SKILL.md) (the type being validated) +- Illustrative examples (CoreEx sample — not present in your project): + - [`ProductValidator` + `MovementRequestValidator`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Application/Validators) — simple, plus injection + dictionary + async + - [Shopping validators](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Application/Validators) — `AbstractValidator` style, plain request validators + - [`OrderValidator`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Orders.Application/Validators) — collection validator diff --git a/.github/skills/coreex-validator/references/workflow.md b/.github/skills/coreex-validator/references/workflow.md index 8447e12a..bd6d4c36 100644 --- a/.github/skills/coreex-validator/references/workflow.md +++ b/.github/skills/coreex-validator/references/workflow.md @@ -215,6 +215,8 @@ When adding to an existing override, respect the existing `context.HasErrors` gu ## Nested Validators +> Examples below use illustrative `Order`/`Address`/`Movement` domains — substitute your own entity/type names. + ### Entity Use `.Entity()` to run a separate validator against a complex sub-property. Errors from the sub-validator are merged into the parent result under the sub-property's JSON path (e.g. `"address.street"`). diff --git a/AGENTS.md b/AGENTS.md index 35cf5011..4de27f43 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -87,11 +87,17 @@ dotnet new coreex-ai --app-folder This installs: - `.github/instructions/` — scoped instruction files auto-injected by Copilot for each file type -- `.github/prompts/` — scaffolding prompt +- `.github/prompts/` — the scaffolding prompt plus one `coreex-.prompt.md` per L1 skill +- `.github/skills/` — the CoreEx skill suite: `coreex-docs-sync`, `solution-scaffolder`, and the L1 skills + (`coreex-contract`, `coreex-refdata`, `coreex-db-migration`, `coreex-repository`, `coreex-adapter`, + `coreex-app-service`, `coreex-validator`, `coreex-policy`, `coreex-aggregate`, `coreex-api`, `coreex-subscriber`, + `coreex-test-api`, `coreex-test-subscribe`, `coreex-test-relay`) - `.github/agents/coreex-expert.agent.md` — architecture guidance agent -- `.github/skills/coreex-docs-sync/` — skill to cache CoreEx docs locally +- `.github/docs/coreex/` — the architecture docs + per-package guides cache (refresh later with `/coreex-docs-sync`) - `.claude/commands/` — equivalent commands for Claude Code +For the full catalog and how these pieces fit together, see [`.github/AI-WORKFLOWS.md`](./.github/AI-WORKFLOWS.md). + ### Step 4 — Reload context and continue After running `dotnet new coreex-ai`, reload the context so the new instruction files are active. diff --git a/src/CoreEx.Template/CoreEx.Template.csproj b/src/CoreEx.Template/CoreEx.Template.csproj index 0f9e9560..646b197d 100644 --- a/src/CoreEx.Template/CoreEx.Template.csproj +++ b/src/CoreEx.Template/CoreEx.Template.csproj @@ -94,7 +94,7 @@ + + <_AiDocFile Include="$(_RepoRoot)src/CoreEx/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.AspNetCore/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.AspNetCore.md" /> + <_AiDocFile Condition="Exists('$(_RepoRoot)src/CoreEx.AspNetCore.NSwag/AGENTS.md')" Include="$(_RepoRoot)src/CoreEx.AspNetCore.NSwag/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.AspNetCore.NSwag.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.Azure.Messaging.ServiceBus/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.Azure.Messaging.ServiceBus.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.Caching.FusionCache/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.Caching.FusionCache.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.CodeGen/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.CodeGen.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.Data/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.Data.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.Database/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.Database.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.Database.Postgres/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.Database.Postgres.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.Database.SqlServer/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.Database.SqlServer.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.DomainDriven/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.DomainDriven.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.EntityFrameworkCore/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.EntityFrameworkCore.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.Events/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.Events.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.RefData/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.RefData.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.UnitTesting/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.UnitTesting.md" /> + <_AiDocFile Include="$(_RepoRoot)src/CoreEx.Validation/AGENTS.md" DestSubPath=".github/docs/coreex/agents/CoreEx.Validation.md" /> + <_AiDocFile Include="$(_RepoRoot)docs/application-scaffolding-guide.md" DestSubPath=".github/docs/coreex/application-scaffolding-guide.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/application-layer.md" DestSubPath=".github/docs/coreex/application-layer.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/aspire.md" DestSubPath=".github/docs/coreex/aspire.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/contracts-layer.md" DestSubPath=".github/docs/coreex/contracts-layer.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/domain-layer.md" DestSubPath=".github/docs/coreex/domain-layer.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/hosts-layer.md" DestSubPath=".github/docs/coreex/hosts-layer.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/infrastructure-layer.md" DestSubPath=".github/docs/coreex/infrastructure-layer.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/layers.md" DestSubPath=".github/docs/coreex/layers.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/local-dev.md" DestSubPath=".github/docs/coreex/local-dev.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/patterns.md" DestSubPath=".github/docs/coreex/patterns.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/testing.md" DestSubPath=".github/docs/coreex/testing.md" /> + <_AiDocFile Include="$(_RepoRoot)samples/docs/tooling.md" DestSubPath=".github/docs/coreex/tooling.md" /> + + + <_CoreExAiGenerated Include="$(_AiAiGenRoot)**/*" /> diff --git a/src/CoreEx.Template/content/CoreEx.Core/AGENTS.md b/src/CoreEx.Template/content/CoreEx.Core/AGENTS.md index 1d3e32c8..45c24c1e 100644 --- a/src/CoreEx.Template/content/CoreEx.Core/AGENTS.md +++ b/src/CoreEx.Template/content/CoreEx.Core/AGENTS.md @@ -79,7 +79,7 @@ app-name/ ## Relevant Docs -After running `/coreex-docs-sync` (or using `coreex-bootstrap`), the following are available locally: +`dotnet new coreex-ai` installs the following under `.github/docs/coreex/` (refresh later with `/coreex-docs-sync`): - `.github/docs/coreex/layers.md` -- full layered architecture and dependency rules - `.github/docs/coreex/patterns.md` -- CoreEx request/response and event patterns From 1d140f3dfbbdb069f89e6790296d995895796ddf Mon Sep 17 00:00:00 2001 From: Eric Sibly Date: Thu, 2 Jul 2026 12:23:05 -0700 Subject: [PATCH 02/13] Review and fix Instructions/Skills split (#151): version-pinned docs-sync, do-not-edit headers, coreex-solution-scaffolder rename Reviews commit 35508dae (Instructions/Skills split) and addresses gaps found: - Repurpose coreex-docs-sync from an unpinned live GitHub-main fetch to a version-pinned refresh, consistent with CoreEx.Template/CoreEx sharing one release version. Rewrote SKILL.md, README.md, and the Claude Code command. - Add version-pin discipline guidance (AGENTS.md, AI-WORKFLOWS.md) so dotnet new coreex* installs are pinned to a specific CoreEx.Template release rather than floating to latest. - Document --force overwrite semantics accurately where referenced. - Stamp a version marker (.github/docs/coreex/manifest.txt) into coreex-ai/coreex-bootstrap output recording which CoreEx.Template release populated the AI workflow assets, enabling informed refresh decisions. - Fix a hardcoded 4.0.0-preview-1 version literal in coreex-expert.agent.md. - Add a dual-audience do-not-hand-edit header across 61 template-sourced files (authored here vs. generated in consumer repos). - Fix three instances of a dotnet new template bug: symbol "replaces" values do an unscoped, global literal text-replace across every packed file, not a scoped placeholder substitution. Replaced human-readable literals ("coreex-version", "app-folder") with sentinel tokens (__COREEX_AI_VERSION_TOKEN__, __COREEX_AI_APP_FOLDER_TOKEN__) to avoid corrupting prose that legitimately mentions those names; removed one unused/dead symbol entirely. - Rename solution-scaffolder skill to coreex-solution-scaffolder for coreex- prefix consistency; update all cross-references. - Fix a real CoreEx.Template packaging gap: coreex-solution-scaffolder was documented as shipping to both CoreEx.Bootstrap and CoreEx.Ai, but the csproj Copy list only shipped it to Bootstrap. Added the missing Copy block. - Document the provenance and intentional divergence of the skill's static docker-compose.local.yml/servicebus-config.template.json fallback assets (a retrofit-safe alternative to CoreEx.Core's conditional, options-aware generated equivalents) via a new assets/README.md. - Fix two now-stale assertions in tools/validate-template-pack.ps1 that contradicted the intentional changes above (.github/docs asserted absent; coreex-solution-scaffolder asserted absent from CoreEx.Ai). Verified via tools/validate-template-pack.ps1: 10/10 scenarios pass. Also manually verified via clean build/pack/install/generate cycles for coreex-ai (single-repo and --app-folder monorepo modes) and coreex-bootstrap. host-setup-extract (splitting remaining host-scaffolding procedures out of coreex-host-setup.instructions.md now that coreex-solution-scaffolder exists) is deliberately deferred to a follow-up PR. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .claude/commands/coreex-docs-sync.md | 103 ++++++++---------- .claude/commands/coreex-expert.md | 8 ++ .github/AI-WORKFLOWS.md | 15 ++- .github/agents/README.md | 2 +- .github/agents/coreex-expert.agent.md | 29 +++-- .../coreex-api-controllers.instructions.md | 8 ++ ...oreex-application-services.instructions.md | 8 ++ .../coreex-contracts.instructions.md | 8 ++ .../coreex-conventions.instructions.md | 8 ++ .../coreex-domain.instructions.md | 8 ++ .../coreex-event-subscribers.instructions.md | 8 ++ .../coreex-host-setup.instructions.md | 10 +- .../coreex-repositories.instructions.md | 8 ++ .../instructions/coreex-tests.instructions.md | 8 ++ .../coreex-tooling.instructions.md | 8 ++ .../coreex-validators.instructions.md | 8 ++ .github/prompts/coreex-adapter.prompt.md | 8 ++ .github/prompts/coreex-aggregate.prompt.md | 8 ++ .github/prompts/coreex-api.prompt.md | 8 ++ .github/prompts/coreex-app-service.prompt.md | 8 ++ .github/prompts/coreex-contract.prompt.md | 8 ++ .github/prompts/coreex-db-migration.prompt.md | 8 ++ .github/prompts/coreex-policy.prompt.md | 8 ++ .github/prompts/coreex-refdata.prompt.md | 8 ++ .github/prompts/coreex-repository.prompt.md | 8 ++ .github/prompts/coreex-scaffold.prompt.md | 12 +- .github/prompts/coreex-subscriber.prompt.md | 8 ++ .github/prompts/coreex-test-api.prompt.md | 8 ++ .github/prompts/coreex-test-relay.prompt.md | 8 ++ .../prompts/coreex-test-subscribe.prompt.md | 8 ++ .github/prompts/coreex-validator.prompt.md | 8 ++ .github/skills/coreex-adapter/SKILL.md | 8 ++ .../coreex-adapter/references/workflow.md | 8 ++ .github/skills/coreex-aggregate/SKILL.md | 8 ++ .../coreex-aggregate/references/workflow.md | 8 ++ .github/skills/coreex-api/SKILL.md | 16 ++- .../skills/coreex-api/references/workflow.md | 8 ++ .github/skills/coreex-app-service/SKILL.md | 8 ++ .../coreex-app-service/references/workflow.md | 8 ++ .github/skills/coreex-contract/SKILL.md | 8 ++ .../coreex-contract/references/workflow.md | 8 ++ .github/skills/coreex-db-migration/SKILL.md | 8 ++ .../references/workflow.md | 8 ++ .github/skills/coreex-docs-sync/README.md | 53 +++++++-- .github/skills/coreex-docs-sync/SKILL.md | 79 +++++++++----- .github/skills/coreex-policy/SKILL.md | 8 ++ .../coreex-policy/references/workflow.md | 8 ++ .github/skills/coreex-refdata/SKILL.md | 8 ++ .../coreex-refdata/references/workflow.md | 8 ++ .github/skills/coreex-repository/SKILL.md | 8 ++ .../coreex-repository/references/workflow.md | 8 ++ .../README.md | 10 +- .../SKILL.md | 15 ++- .../assets/README.md | 46 ++++++++ .../assets/docker-compose.local.yml | 2 + .../assets/servicebus-config.template.json | 0 .../references/workflow.md | 10 +- .github/skills/coreex-subscriber/SKILL.md | 8 ++ .../coreex-subscriber/references/workflow.md | 8 ++ .github/skills/coreex-test-api/SKILL.md | 8 ++ .../coreex-test-api/references/workflow.md | 8 ++ .github/skills/coreex-test-relay/SKILL.md | 8 ++ .github/skills/coreex-test-subscribe/SKILL.md | 8 ++ .../references/workflow.md | 8 ++ .github/skills/coreex-validator/SKILL.md | 8 ++ .../coreex-validator/references/workflow.md | 8 ++ AGENTS.md | 27 ++++- .../.github/copilot-instructions.md | 10 ++ consumer-instructions/README.md | 2 +- src/CoreEx.Template/CoreEx.Template.csproj | 46 ++++++-- .../CoreEx.Ai/.template.config/template.json | 10 +- .../.template.config/template.json | 12 +- tools/validate-template-pack.ps1 | 9 +- 73 files changed, 773 insertions(+), 153 deletions(-) rename .github/skills/{solution-scaffolder => coreex-solution-scaffolder}/README.md (75%) rename .github/skills/{solution-scaffolder => coreex-solution-scaffolder}/SKILL.md (91%) create mode 100644 .github/skills/coreex-solution-scaffolder/assets/README.md rename .github/skills/{solution-scaffolder => coreex-solution-scaffolder}/assets/docker-compose.local.yml (86%) rename .github/skills/{solution-scaffolder => coreex-solution-scaffolder}/assets/servicebus-config.template.json (100%) rename .github/skills/{solution-scaffolder => coreex-solution-scaffolder}/references/workflow.md (94%) diff --git a/.claude/commands/coreex-docs-sync.md b/.claude/commands/coreex-docs-sync.md index 43f0709d..b9dee34e 100644 --- a/.claude/commands/coreex-docs-sync.md +++ b/.claude/commands/coreex-docs-sync.md @@ -1,87 +1,74 @@ --- -description: "Fetch the CoreEx sample architecture docs and all per-package AI usage guides from GitHub and cache them locally under .github/docs/coreex/ for offline, faster expert guidance." -allowed-tools: [Read, Write, Glob, Grep, WebFetch] +description: "Refresh the full CoreEx AI workflow asset bundle (instructions, skills, prompts, the coreex-expert agent, and the .github/docs/coreex/ docs cache) to the CoreEx.Template release matching the project's installed CoreEx NuGet version. Version-pinned — never fetches unreleased GitHub main content." +allowed-tools: [Read, Bash, Glob, Grep] --- -Sync the CoreEx docs to `.github/docs/coreex/`. Follow these steps exactly. + -## Step 1 — Detect current CoreEx version +Refresh the CoreEx AI asset bundle. Follow these steps exactly. + +## Step 1 — Detect the target CoreEx version Search for the `CoreEx` NuGet package version in this order, stopping at the first match: 1. `Directory.Packages.props` — look for `` 2. Any `*.csproj` file — look for `` 3. `Directory.Build.props` -Record the version (or `unknown` if not found). Use it in the manifest. +If none is found, stop and tell the user this project has no `CoreEx` reference yet — there is nothing to pin a refresh to. `CoreEx.Template` and `CoreEx` are released from the same repo at the same version number, so this detected version is also the exact `CoreEx.Template` version to install. -## Step 2 — Detect referenced CoreEx packages (for manifest only) +## Step 2 — Check the current manifest -Scan `Directory.Packages.props` and all `*.csproj` files for `PackageVersion` or `PackageReference` entries whose `Include` attribute starts with `CoreEx`. Collect the distinct package names. This list goes into the manifest so the CoreEx Expert knows which packages the project currently uses — it does not limit which guides are synced. +Read `.github/docs/coreex/manifest.txt` if it exists and extract its `coreex-version` value. -## Step 3 — Create cache directories +- If the manifest is missing, continue to Step 3. +- If the manifest's `coreex-version` already equals the version detected in Step 1, report **already in sync** and stop — nothing to do. +- Otherwise, continue to Step 3. -Ensure both directories exist. Create them if absent: -- `.github/docs/coreex/` -- `.github/docs/coreex/agents/` +## Step 3 — Install the pinned template version -## Step 4 — Fetch and write the sample architecture docs +Run, substituting the version detected in Step 1: -Fetch each URL below and write to the corresponding local path. Report each as it completes. +```bash +dotnet new install CoreEx.Template:: +``` -| URL | Local path | -|---|---| -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/local-dev.md` | `.github/docs/coreex/local-dev.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/layers.md` | `.github/docs/coreex/layers.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/patterns.md` | `.github/docs/coreex/patterns.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/contracts-layer.md` | `.github/docs/coreex/contracts-layer.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/domain-layer.md` | `.github/docs/coreex/domain-layer.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/application-layer.md` | `.github/docs/coreex/application-layer.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/infrastructure-layer.md` | `.github/docs/coreex/infrastructure-layer.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/hosts-layer.md` | `.github/docs/coreex/hosts-layer.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/testing.md` | `.github/docs/coreex/testing.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/tooling.md` | `.github/docs/coreex/tooling.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/samples/docs/aspire.md` | `.github/docs/coreex/aspire.md` | +Never install a bare `dotnet new install CoreEx.Template` — it silently resolves to whatever is latest and can desync the AI assets from the project's actual `CoreEx` version. -## Step 5 — Fetch and write all per-package guides +## Step 4 — Dry-run first -Fetch the `AGENTS.md` for every CoreEx package listed below — regardless of whether the project currently references them. This allows the CoreEx Expert to guide on and recommend any package, including ones not yet adopted. +Run (add `--app-folder ` if this is a monorepo, per the recorded app folder): -| URL | Local path | -|---|---| -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.AspNetCore/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.AspNetCore.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.AspNetCore.NSwag/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.AspNetCore.NSwag.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.Azure.Messaging.ServiceBus/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.Azure.Messaging.ServiceBus.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.Caching.FusionCache/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.Caching.FusionCache.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.CodeGen/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.CodeGen.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.Data/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.Data.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.Database/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.Database.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.Database.Postgres/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.Database.Postgres.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.Database.SqlServer/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.Database.SqlServer.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.DomainDriven/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.DomainDriven.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.EntityFrameworkCore/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.EntityFrameworkCore.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.Events/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.Events.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.RefData/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.RefData.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.UnitTesting/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.UnitTesting.md` | -| `https://raw.githubusercontent.com/Avanade/CoreEx/main/src/CoreEx.Validation/AGENTS.md` | `.github/docs/coreex/agents/CoreEx.Validation.md` | +```bash +dotnet new coreex-ai +``` -If any fetch fails, record the failure, skip that file, and continue. +**Without** `--force`. This is a dry run — it prints the "Overwrite" list of every file that would change and applies nothing. This is the only point at which the pending change set is visible; show it to the user. -## Step 6 — Write the manifest +## Step 5 — Confirm, then apply -Write `.github/docs/coreex/.manifest` with this exact format: +Ask the user to confirm, then re-run with `--force`: -``` -synced: YYYY-MM-DD -coreex-version: -referenced-packages: +```bash +dotnet new coreex-ai --force ``` +`--force` overwrites every file the template's current version emits. It does **not** delete files an older/superseded template version left behind (see Step 6). + +## Step 6 — Flag possible orphans + +Compare the skill folder names under `.github/skills/coreex-*/` against the current known L1 skill set (listed in [`AI-WORKFLOWS.md`](https://github.com/Avanade/CoreEx/blob/main/.github/AI-WORKFLOWS.md)). Any extra folder is very likely left over from a prior template version and should be flagged for manual review, not assumed safe to delete automatically. + ## Step 7 — Report Summarise: -- How many architecture docs were written successfully. -- How many package guides were written successfully (out of 16). -- Any files that failed to fetch (with the error). -- The CoreEx version and referenced packages recorded in the manifest. -- A reminder: *"Re-run `/coreex-docs-sync` after bumping the CoreEx NuGet version or when the CoreEx Expert suggests the cache is stale."* +- The refreshed `coreex-version` (from the new `.github/docs/coreex/manifest.txt`). +- The files changed (from the Step 4 dry-run list). +- Any flagged orphans from Step 6. + +There is no live GitHub `main` fetch anywhere in this flow and no age-based staleness check — a version mismatch (or a missing manifest) is the only signal that triggers a refresh. diff --git a/.claude/commands/coreex-expert.md b/.claude/commands/coreex-expert.md index 286e8183..edcf93f8 100644 --- a/.claude/commands/coreex-expert.md +++ b/.claude/commands/coreex-expert.md @@ -3,6 +3,14 @@ description: "CoreEx Expert — architecture guidance, pattern decisions, and sa allowed-tools: [Read, Glob, Grep, WebFetch, WebSearch, Edit, Write] --- + + Read `.github/agents/coreex-expert.agent.md` and follow the instructions in that file. The user's question or request follows. Apply the CoreEx Expert role, operating rules, and response format defined in the agent file. diff --git a/.github/AI-WORKFLOWS.md b/.github/AI-WORKFLOWS.md index 9a83b853..9f44efb8 100644 --- a/.github/AI-WORKFLOWS.md +++ b/.github/AI-WORKFLOWS.md @@ -52,14 +52,23 @@ Three artefact types cooperate, each with a distinct job: **Feature Configuration.** A CoreEx solution's project-wide choices — `data-provider`, `refdata-enabled`, `domain-driven-enabled`, `rop-enabled`, `outbox-enabled`, `messaging-provider` — are persisted in the solution-root `AGENTS.md` **Feature Configuration** block. Skills read that block before asking anything, so recorded decisions are not re-prompted from one skill to the next. +### Version-pin discipline + +`CoreEx.Template` and the core `CoreEx` package are released from the same repo at the same version number — pinning one always means pinning the other. Every `dotnet new install CoreEx.Template` invocation — in `AGENTS.md`'s Cold Start walkthrough, `coreex-solution-scaffolder`, `coreex-docs-sync`, or anywhere else — must carry an explicit `::`, resolved as follows, and must never fall through to a bare/latest install: + +- **Project already references a `CoreEx` NuGet package** (check `Directory.Packages.props`, `*.csproj`, `Directory.Build.props`): pin to that exact version. This is the common case for a refresh (`/coreex-docs-sync`) or adding a host to an existing solution. +- **No `CoreEx` reference yet** (true first-time adoption): resolve the latest stable release explicitly (e.g. `dotnet package search CoreEx.Template --exact-match`, or the [NuGet.org listing](https://www.nuget.org/packages/CoreEx.Template)) and pin to that specific version rather than letting install resolve silently to "whatever is latest right now." + +A version mismatch between the installed AI-asset bundle (`.github/docs/coreex/manifest.txt` → `coreex-version`) and the project's actual `CoreEx` package version must be surfaced, not silently ignored — `coreex-expert` checks this every session and recommends `/coreex-docs-sync` when it finds one. + ### Command catalog | Command | Type | What it does | |---------|------|-------------| -| [`CoreEx.Template`](../src/CoreEx.Template/README.md) | Template pack | Deterministic `dotnet new` scaffolding for a CoreEx solution plus API, relay, and subscriber hosts. Use `dotnet new install CoreEx.Template` and then the `coreex*` templates in a terminal. `dotnet new coreex-ai` installs this full AI workflow set (instructions, prompts, skills, the `coreex-expert` agent, `.claude/commands/`, and the `.github/docs/coreex/` docs cache) into a consuming project. | +| [`CoreEx.Template`](../src/CoreEx.Template/README.md) | Template pack | Deterministic `dotnet new` scaffolding for a CoreEx solution plus API, relay, and subscriber hosts. Always install with an explicit pinned version — `dotnet new install CoreEx.Template::` (see [Version-pin discipline](#version-pin-discipline)) — then run the `coreex*` templates in a terminal. `dotnet new coreex-ai` installs the full AI workflow set (instructions, prompts, skills, the `coreex-expert` agent, `.claude/commands/`, and the `.github/docs/coreex/` docs cache, self-describing via `manifest.txt`) into a consuming project as one version-pinned bundle. | | [`/acquire-codebase-knowledge`](./skills/acquire-codebase-knowledge/README.md) | Skill | Maps an unfamiliar codebase and produces seven structured onboarding documents. | -| [`/coreex-scaffold`](./skills/solution-scaffolder/README.md) · [prompt](./prompts/coreex-scaffold.prompt.md) | Skill + prompt | Guides greenfield solution scaffolding, chooses the smallest safe CoreEx.Template shape, and runs the matching `dotnet new coreex*` commands. | -| [`/coreex-docs-sync`](./skills/coreex-docs-sync/README.md) | Skill | Fetches and caches CoreEx architecture docs and all per-package AI guides locally under `.github/docs/coreex/`. | +| [`/coreex-scaffold`](./skills/coreex-solution-scaffolder/README.md) · [prompt](./prompts/coreex-scaffold.prompt.md) | Skill + prompt | Guides greenfield solution scaffolding, chooses the smallest safe CoreEx.Template shape, and runs the matching `dotnet new coreex*` commands. | +| [`/coreex-docs-sync`](./skills/coreex-docs-sync/README.md) | Skill | Refreshes the whole AI asset bundle (instructions, skills, prompts, agent, `.claude/commands/`, and the `.github/docs/coreex/` docs cache) as one version-pinned `dotnet new coreex-ai --force` reinstall, matching the project's referenced `CoreEx` NuGet version. No live GitHub `main` fetch. | | [`/aspire`](./skills/aspire/README.md) | Skill | Orchestrates Aspire distributed apps locally: start, stop, logs, debug. | #### Per-capability skills (L1) diff --git a/.github/agents/README.md b/.github/agents/README.md index 639d3137..34d730fb 100644 --- a/.github/agents/README.md +++ b/.github/agents/README.md @@ -118,7 +118,7 @@ dotnet new coreex-ai --app-folder - `.github/instructions/` — the scoped, auto-injected instruction files - `.github/prompts/` — the `coreex-scaffold` prompt plus one prompt per per-capability (L1) skill -- `.github/skills/` — the full skill suite (`coreex-docs-sync`, `acquire-codebase-knowledge`, `solution-scaffolder`, `aspire`, and the 14 L1 skills) +- `.github/skills/` — the full skill suite (`coreex-docs-sync`, `acquire-codebase-knowledge`, `coreex-solution-scaffolder`, `aspire`, and the 14 L1 skills) - `.github/agents/coreex-expert.agent.md` — this agent - `.claude/commands/` — the Claude Code equivalents - `.github/docs/coreex/` — the local docs cache (architecture docs + per-package guides) the expert reads first diff --git a/.github/agents/coreex-expert.agent.md b/.github/agents/coreex-expert.agent.md index 288c280a..7c63615f 100644 --- a/.github/agents/coreex-expert.agent.md +++ b/.github/agents/coreex-expert.agent.md @@ -5,6 +5,15 @@ tools: [read/readFile, read/problems, search/codebase, search/fileSearch, search user-invocable: true argument-hint: Ask for CoreEx pattern guidance, architecture decisions, or sample-aligned implementation advice. --- + + + You are the CoreEx Expert. Your mission: @@ -72,26 +81,26 @@ Check `.github/docs/coreex/` for a local cache first (see [Local doc cache](#loc ## Local doc cache -`/coreex-docs-sync` populates two local folders. Prefer local copies over GitHub URLs or fetches whenever they are present. +`dotnet new coreex-ai` bakes `.github/instructions/`, `.github/skills/`, `.github/prompts/`, this agent, and the doc cache below into the project **at the exact `CoreEx.Template` version installed** — it is a version-pinned snapshot, not a live fetch. `/coreex-docs-sync` re-applies that same install (`dotnet new coreex-ai --force`, pinned to the version matching the project's referenced `CoreEx` NuGet package) to refresh the whole bundle at once. There is no live GitHub `main` fetch anywhere in this flow — prefer local copies over GitHub URLs whenever they are present. | Folder | Contents | |---|---| -| `.github/docs/coreex/` | 10 sample architecture docs (layers, patterns, each layer walkthrough, testing, tooling, Aspire) | +| `.github/docs/coreex/` | 11 sample architecture docs (layers, patterns, each layer walkthrough, testing, tooling, Aspire, application scaffolding) | | `.github/docs/coreex/agents/` | AI usage guides for **all** CoreEx packages — available for guidance even on packages not yet adopted by this project | -A manifest at `.github/docs/coreex/.manifest` records the sync date, CoreEx version, and which packages are currently referenced in the project. +A manifest at `.github/docs/coreex/manifest.txt` records `coreex-version` — stamped directly by the template at generation time, never hand-written. **When you are about to consult a sample architecture doc or a per-package guide:** 1. Check for the file under `.github/docs/coreex/` or `.github/docs/coreex/agents/` respectively. -2. If found, use the local copy. Then read `.github/docs/coreex/.manifest` and check: - - `synced` date: if older than 30 days, recommend running `/coreex-docs-sync`. - - `4.0.0-preview-1`: scan `*.csproj`, `Directory.Packages.props`, and `Directory.Build.props` for the `CoreEx` package version; if it differs from the manifest, recommend running `/coreex-docs-sync`. -3. If no local cache exists and you are about to fetch a GitHub URL, offer first: *"I can run `/coreex-docs-sync` to cache the CoreEx docs and all package guides locally — this avoids repeated GitHub fetches. Want me to do that first?"* +2. If found, use the local copy. Then read `.github/docs/coreex/manifest.txt`'s `coreex-version` and compare it against the `CoreEx` package version scanned from `*.csproj`, `Directory.Packages.props`, or `Directory.Build.props`. If they differ (or the manifest is missing), recommend running `/coreex-docs-sync`. +3. If no local cache exists and you are about to fetch a GitHub URL, offer first: *"I can run `/coreex-docs-sync` to install the version-pinned CoreEx AI asset bundle (instructions, skills, prompts, and this docs cache) locally — this avoids repeated GitHub fetches and keeps everything on one release. Want me to do that first?"* + +There is no age-based staleness check — a version match is the only signal that matters, since a refresh always re-installs the exact matching release rather than re-fetching indefinitely-fresh content. -**At the start of a session involving CoreEx guidance**, read `.github/docs/coreex/.manifest` if it exists. The `referenced-packages` field lists which CoreEx packages this project currently uses — distinguish between guiding on an **already-referenced** package and recommending a **new** one the project would need to add. +**At the start of a session involving CoreEx guidance**, determine which `CoreEx.*` packages this project currently references by scanning its package references live (cheap, not cached) — distinguish between guiding on an **already-referenced** package and recommending a **new** one the project would need to add. -Do not set up the local cache silently — always offer and wait for confirmation. +Do not run `/coreex-docs-sync` silently — always offer and wait for confirmation. ## Operating rules @@ -128,7 +137,7 @@ These skills are part of the CoreEx AI workflow set and live in `.github/skills/ **Broader routing:** -- Greenfield solution or host scaffolding → `/coreex-scaffold` (`solution-scaffolder` skill), which runs the matching [CoreEx.Template](../../src/CoreEx.Template/README.md) `dotnet new coreex*` commands. +- Greenfield solution or host scaffolding → `/coreex-scaffold` (`coreex-solution-scaffolder` skill), which runs the matching [CoreEx.Template](../../src/CoreEx.Template/README.md) `dotnet new coreex*` commands. - Repo mapping or onboarding documentation → `/acquire-codebase-knowledge`. - Retrofit that no single skill covers → inspect the current code and recommend the smallest manual changes aligned to the samples and instructions. diff --git a/.github/instructions/coreex-api-controllers.instructions.md b/.github/instructions/coreex-api-controllers.instructions.md index a3f0c840..c83abe9c 100644 --- a/.github/instructions/coreex-api-controllers.instructions.md +++ b/.github/instructions/coreex-api-controllers.instructions.md @@ -4,6 +4,14 @@ description: "API conventions for CoreEx: MVC ControllerBase and Minimal API app tags: ["controllers", "api", "routing", "cqrs", "dependency-injection", "minimal-api"] --- + + # API Conventions > **Related skill:** to scaffold a new controller or endpoint, invoke the [`coreex-api`](/.github/skills/coreex-api/SKILL.md) skill. diff --git a/.github/instructions/coreex-application-services.instructions.md b/.github/instructions/coreex-application-services.instructions.md index 72fb7938..092f54bf 100644 --- a/.github/instructions/coreex-application-services.instructions.md +++ b/.github/instructions/coreex-application-services.instructions.md @@ -4,6 +4,14 @@ description: "Application service conventions: ScopedService registration, depen tags: ["services", "application-layer", "dependency-injection", "validation", "unit-of-work", "cqrs", "policies", "adapters"] --- + + # Application Service Conventions > **Related skill:** to scaffold a new application service, invoke the [`coreex-app-service`](/.github/skills/coreex-app-service/SKILL.md) skill; diff --git a/.github/instructions/coreex-contracts.instructions.md b/.github/instructions/coreex-contracts.instructions.md index 6cbade25..5b07eeda 100644 --- a/.github/instructions/coreex-contracts.instructions.md +++ b/.github/instructions/coreex-contracts.instructions.md @@ -4,6 +4,14 @@ description: "Contract (DTO) conventions: source generation, marker attributes, tags: ["contracts", "dto", "source-generation", "reference-data", "etag"] --- + + # Contract (DTO) Conventions > **Related skill:** to create or modify a contract/DTO, invoke the [`coreex-contract`](/.github/skills/coreex-contract/SKILL.md) diff --git a/.github/instructions/coreex-conventions.instructions.md b/.github/instructions/coreex-conventions.instructions.md index df0675c9..92a2916a 100644 --- a/.github/instructions/coreex-conventions.instructions.md +++ b/.github/instructions/coreex-conventions.instructions.md @@ -4,6 +4,14 @@ description: "Universal C# coding conventions: nullable, implicit usings, Global tags: ["conventions", "style", "nullable", "usings", "naming"] --- + + # C# Coding Conventions ## Project Configuration diff --git a/.github/instructions/coreex-domain.instructions.md b/.github/instructions/coreex-domain.instructions.md index fca95147..006d3c8f 100644 --- a/.github/instructions/coreex-domain.instructions.md +++ b/.github/instructions/coreex-domain.instructions.md @@ -4,6 +4,14 @@ description: "Domain layer conventions: aggregates, entities, value objects, Per tags: ["domain", "ddd", "aggregates", "entities", "value-objects", "result"] --- + + # Domain Layer Conventions The Domain layer is **optional**. It is introduced only when a domain contains aggregates with meaningful business rules and invariants that must be enforced at the model level — not in orchestration code. For example, a checkout/basket domain with state-machine transitions and nested item rules benefits from this layer; a simple CRUD-oriented domain (like a product catalog) typically does not. diff --git a/.github/instructions/coreex-event-subscribers.instructions.md b/.github/instructions/coreex-event-subscribers.instructions.md index ee290a74..6350e24a 100644 --- a/.github/instructions/coreex-event-subscribers.instructions.md +++ b/.github/instructions/coreex-event-subscribers.instructions.md @@ -4,6 +4,14 @@ description: "Event subscriber conventions: SubscribedBase, SubscribedBase, V tags: ["subscribers", "messaging", "service-bus", "event-handling", "integration", "subscribe-host"] --- + + # Event Subscriber Conventions > **Related skill:** to scaffold a new event subscriber, invoke the [`coreex-subscriber`](/.github/skills/coreex-subscriber/SKILL.md) skill. diff --git a/.github/instructions/coreex-host-setup.instructions.md b/.github/instructions/coreex-host-setup.instructions.md index 4fe1fbab..15dcbef9 100644 --- a/.github/instructions/coreex-host-setup.instructions.md +++ b/.github/instructions/coreex-host-setup.instructions.md @@ -4,11 +4,19 @@ description: "Host setup conventions for Program.cs: API host, Subscribe host, O tags: ["program-cs", "host-setup", "middleware", "dependency-registration", "caching"] --- + + # Host Setup Conventions (Program.cs) The host is a **composition root only** — no business logic. There are three host types in a CoreEx solution depending on the capabilities required. Each follows the same opening skeleton, then diverges based on its responsibilities. -> **Related skill:** to scaffold a solution or an additional host (Api / Subscribe / Relay), invoke the [`solution-scaffolder`](/.github/skills/solution-scaffolder/SKILL.md) skill. +> **Related skill:** to scaffold a solution or an additional host (Api / Subscribe / Relay), invoke the [`coreex-solution-scaffolder`](/.github/skills/coreex-solution-scaffolder/SKILL.md) skill. > This file holds the invariants that must hold on **any** edit to a host `Program.cs`; the skill drives the > step-by-step **creation** procedure. (The per-host "Scaffolding an … host" blocks below stay here — they carry the > Feature Configuration recover/record guardrails that must be honoured whenever a host is added.) diff --git a/.github/instructions/coreex-repositories.instructions.md b/.github/instructions/coreex-repositories.instructions.md index ea498033..3a6306b9 100644 --- a/.github/instructions/coreex-repositories.instructions.md +++ b/.github/instructions/coreex-repositories.instructions.md @@ -4,6 +4,14 @@ description: "Repository and infrastructure conventions: EFCore, mapping, typed tags: ["repositories", "infrastructure", "data-access", "efcore", "mapping", "adapters"] --- + + # Repository & Infrastructure Conventions > **Related skill:** to scaffold a new repository, invoke the [`coreex-repository`](/.github/skills/coreex-repository/SKILL.md) skill; diff --git a/.github/instructions/coreex-tests.instructions.md b/.github/instructions/coreex-tests.instructions.md index f9d39fe1..61cf25df 100644 --- a/.github/instructions/coreex-tests.instructions.md +++ b/.github/instructions/coreex-tests.instructions.md @@ -4,6 +4,14 @@ description: "Test conventions: test project types (Api/Unit/Subscribe/Relay), b tags: ["testing", "unit-tests", "integration-tests", "test-helpers", "nunit"] --- + + # Test Conventions > **Related skills:** to author tests, invoke the matching skill — diff --git a/.github/instructions/coreex-tooling.instructions.md b/.github/instructions/coreex-tooling.instructions.md index b267d14f..98c83aa2 100644 --- a/.github/instructions/coreex-tooling.instructions.md +++ b/.github/instructions/coreex-tooling.instructions.md @@ -4,6 +4,14 @@ description: "Developer tooling conventions: *.CodeGen reference-data C# code ge tags: ["tooling", "codegen", "database", "migrations", "dbex", "reference-data", "outbox"] --- + + # Developer Tooling Conventions Each domain has two developer-time tooling projects that have **no runtime presence**. They run locally during development and in CI/CD pipelines to generate code and manage the database schema. diff --git a/.github/instructions/coreex-validators.instructions.md b/.github/instructions/coreex-validators.instructions.md index 16dd7188..b58d5937 100644 --- a/.github/instructions/coreex-validators.instructions.md +++ b/.github/instructions/coreex-validators.instructions.md @@ -4,6 +4,14 @@ description: "Validator conventions: Validator, AbstractValidator, decl tags: ["validators", "validation", "fluent-api", "rules", "error-handling", "application-layer"] --- + + # Validator Conventions > **Related skill:** to scaffold a new validator, invoke the [`coreex-validator`](/.github/skills/coreex-validator/SKILL.md) skill. diff --git a/.github/prompts/coreex-adapter.prompt.md b/.github/prompts/coreex-adapter.prompt.md index b7b23e43..97d55117 100644 --- a/.github/prompts/coreex-adapter.prompt.md +++ b/.github/prompts/coreex-adapter.prompt.md @@ -3,6 +3,14 @@ mode: agent description: "Create or modify a CoreEx adapter (anti-corruption layer): Application interface, Infrastructure implementation, typed HTTP client, and unit tests." --- + + Your job is to create or modify a CoreEx adapter following the project's established conventions. Read and follow `.github/skills/coreex-adapter/references/workflow.md` exactly. Do not skip steps or invent patterns not shown there. diff --git a/.github/prompts/coreex-aggregate.prompt.md b/.github/prompts/coreex-aggregate.prompt.md index 555a0f59..79910fa8 100644 --- a/.github/prompts/coreex-aggregate.prompt.md +++ b/.github/prompts/coreex-aggregate.prompt.md @@ -3,6 +3,14 @@ mode: agent description: "Add or modify a DDD domain object (aggregate root, entity, or value object) in a CoreEx Domain layer. Interviews the developer to determine which applies, then follows the CoreEx.DomainDriven pattern." --- + + Use the `coreex-aggregate` skill to add or modify a domain object in this CoreEx `*.Domain` project. Read `.github/skills/coreex-aggregate/SKILL.md` first, confirm a Domain layer is warranted (Phase 0), diff --git a/.github/prompts/coreex-api.prompt.md b/.github/prompts/coreex-api.prompt.md index 3aa6974e..0d73a25d 100644 --- a/.github/prompts/coreex-api.prompt.md +++ b/.github/prompts/coreex-api.prompt.md @@ -2,6 +2,14 @@ description: Add or modify a CoreEx API controller (MVC or Minimal API) — CQRS controller pair, CRUD endpoints, custom business actions, Result variants --- + + Guide this workspace through adding or modifying a CoreEx API controller or Minimal API endpoint. Use `.github/skills/coreex-api/SKILL.md` and its referenced workflow as the authoritative workflow when they exist. diff --git a/.github/prompts/coreex-app-service.prompt.md b/.github/prompts/coreex-app-service.prompt.md index 900ad2f3..456d1ee3 100644 --- a/.github/prompts/coreex-app-service.prompt.md +++ b/.github/prompts/coreex-app-service.prompt.md @@ -2,6 +2,14 @@ description: Create or modify a CoreEx Application-layer service — CRUD operations, business actions, unit-of-work + events, CQRS read service, adapter interface, and policy --- + + Guide this workspace through creating or modifying a CoreEx Application-layer service. Use `.github/skills/coreex-app-service/SKILL.md` and its referenced workflow as the authoritative workflow when they exist. diff --git a/.github/prompts/coreex-contract.prompt.md b/.github/prompts/coreex-contract.prompt.md index aed22d2f..7de747e2 100644 --- a/.github/prompts/coreex-contract.prompt.md +++ b/.github/prompts/coreex-contract.prompt.md @@ -2,6 +2,14 @@ description: Create or modify a hand-authored contract (DTO/entity) in a CoreEx domain — root entity, subordinate, request/response, or base class extraction --- + + Guide this workspace through creating or modifying a CoreEx contract. Use `.github/skills/coreex-contract/SKILL.md` and its referenced workflow as the authoritative workflow contract when they exist. diff --git a/.github/prompts/coreex-db-migration.prompt.md b/.github/prompts/coreex-db-migration.prompt.md index 194e6f55..88561efe 100644 --- a/.github/prompts/coreex-db-migration.prompt.md +++ b/.github/prompts/coreex-db-migration.prompt.md @@ -2,6 +2,14 @@ description: Add or change a database table for a CoreEx domain — scaffold migration script, update dbex.yaml, apply, and regenerate EF persistence models --- + + Guide this workspace through a CoreEx database schema change. Use `.github/skills/coreex-db-migration/SKILL.md` and its referenced workflow as the authoritative workflow contract when they exist. diff --git a/.github/prompts/coreex-policy.prompt.md b/.github/prompts/coreex-policy.prompt.md index 52669b2c..1eb76169 100644 --- a/.github/prompts/coreex-policy.prompt.md +++ b/.github/prompts/coreex-policy.prompt.md @@ -2,6 +2,14 @@ description: Create or modify a CoreEx Application-layer policy class — EnsureExists guards, business rule checks, Result pipeline composition --- + + Guide this workspace through creating or modifying a CoreEx Application-layer policy class. Use `.github/skills/coreex-policy/SKILL.md` and its referenced workflow as the authoritative workflow when they exist. diff --git a/.github/prompts/coreex-refdata.prompt.md b/.github/prompts/coreex-refdata.prompt.md index d45a16e3..9087d141 100644 --- a/.github/prompts/coreex-refdata.prompt.md +++ b/.github/prompts/coreex-refdata.prompt.md @@ -2,6 +2,14 @@ description: Add or modify a reference data type in a CoreEx domain — database migration, seed rows, dbex.yaml, CoreEx CodeGen, and contract wiring --- + + Guide this workspace through a CoreEx reference data change. Use `.github/skills/coreex-refdata/SKILL.md` and its referenced workflow as the authoritative workflow contract when they exist. diff --git a/.github/prompts/coreex-repository.prompt.md b/.github/prompts/coreex-repository.prompt.md index 23f37dea..b5b7c6cf 100644 --- a/.github/prompts/coreex-repository.prompt.md +++ b/.github/prompts/coreex-repository.prompt.md @@ -2,6 +2,14 @@ description: Create or modify a CoreEx Infrastructure-layer repository — EfDb CRUD delegates, QueryArgsConfig queries, BiDirectionMapper, and Result pipelines --- + + Guide this workspace through creating or modifying a CoreEx Infrastructure-layer repository. Use `.github/skills/coreex-repository/SKILL.md` and its referenced workflow as the authoritative workflow when they exist. diff --git a/.github/prompts/coreex-scaffold.prompt.md b/.github/prompts/coreex-scaffold.prompt.md index 4eb090c3..c7d38ef5 100644 --- a/.github/prompts/coreex-scaffold.prompt.md +++ b/.github/prompts/coreex-scaffold.prompt.md @@ -2,12 +2,20 @@ description: Guide me through choosing and running the right CoreEx.Template dotnet new commands for a new solution --- + + Guide this workspace through CoreEx scaffolding. -Use `.github/skills/solution-scaffolder/SKILL.md` and its referenced workflow as the authoritative workflow contract when they exist. +Use `.github/skills/coreex-solution-scaffolder/SKILL.md` and its referenced workflow as the authoritative workflow contract when they exist. Operational contract: -- Use `.github/skills/solution-scaffolder/SKILL.md` and its referenced workflow as the sole source of truth for interview order, defaults, and guardrails. +- Use `.github/skills/coreex-solution-scaffolder/SKILL.md` and its referenced workflow as the sole source of truth for interview order, defaults, and guardrails. - Keep the interview deterministic: one scaffold question per turn, exactly one editable field per confirmation card, and wait for confirmation before moving on. - Preserve the skill's multiple-choice format: use a `text` field only for the base solution name and `select` fields for all other interview questions when confirmation cards are available. - Inspect the workspace before scaffolding, run the safest dry-run path before real template commands, and scaffold only the smallest safe CoreEx shape. diff --git a/.github/prompts/coreex-subscriber.prompt.md b/.github/prompts/coreex-subscriber.prompt.md index f8a1fddc..2e9e4efe 100644 --- a/.github/prompts/coreex-subscriber.prompt.md +++ b/.github/prompts/coreex-subscriber.prompt.md @@ -3,6 +3,14 @@ mode: agent description: "Add or modify a CoreEx subscriber (command / event-sync / event-business-process). Covers SubscribedBase, SubscribedBase, ValueValidator, ErrorHandler, subject naming, DI discovery, and integration tests." --- + + Use the `coreex-subscriber` skill to add or modify a subscriber in this CoreEx Subscribe host. Read `.github/skills/coreex-subscriber/SKILL.md` first to determine which scenario applies (command, diff --git a/.github/prompts/coreex-test-api.prompt.md b/.github/prompts/coreex-test-api.prompt.md index c7d94b74..46e47cac 100644 --- a/.github/prompts/coreex-test-api.prompt.md +++ b/.github/prompts/coreex-test-api.prompt.md @@ -3,6 +3,14 @@ mode: agent description: "Write or update an integration test in a CoreEx *.Test.Api project — OneTimeSetUp seeding, CRUD/ETag/soft-delete scenarios, outbox assertions, and HTTP client mocking." --- + + Use the `coreex-test-api` skill to write or update an integration test in this CoreEx `*.Test.Api` project. Read `.github/skills/coreex-test-api/SKILL.md` first, then follow diff --git a/.github/prompts/coreex-test-relay.prompt.md b/.github/prompts/coreex-test-relay.prompt.md index be0407b0..320464cf 100644 --- a/.github/prompts/coreex-test-relay.prompt.md +++ b/.github/prompts/coreex-test-relay.prompt.md @@ -3,6 +3,14 @@ mode: agent description: "Understand, verify, or extend an Outbox Relay host integration test in a CoreEx *.Test.Relay project — mostly templated plumbing checks, rarely extended per domain." --- + + Use the `coreex-test-relay` skill to work with the CoreEx `*.Test.Relay` project. Read `.github/skills/coreex-test-relay/SKILL.md` first — check what the templated `RelayTests.cs` diff --git a/.github/prompts/coreex-test-subscribe.prompt.md b/.github/prompts/coreex-test-subscribe.prompt.md index b31be24e..62d330da 100644 --- a/.github/prompts/coreex-test-subscribe.prompt.md +++ b/.github/prompts/coreex-test-subscribe.prompt.md @@ -3,6 +3,14 @@ mode: agent description: "Write or update an integration test in a CoreEx *.Test.Subscribe project — simulating broker message receipt, ErrorHandler outcomes, and command/event-sync/event-business-process test scenarios." --- + + Use the `coreex-test-subscribe` skill to write or update an integration test in this CoreEx `*.Test.Subscribe` project. diff --git a/.github/prompts/coreex-validator.prompt.md b/.github/prompts/coreex-validator.prompt.md index 8cb54e38..a718f6c2 100644 --- a/.github/prompts/coreex-validator.prompt.md +++ b/.github/prompts/coreex-validator.prompt.md @@ -2,6 +2,14 @@ description: Create or modify a CoreEx validator in the Application layer — Validator, Validator with injection, AbstractValidator, or adding rules to an existing validator --- + + Guide this workspace through creating or modifying a CoreEx validator. Use `.github/skills/coreex-validator/SKILL.md` and its referenced workflow as the authoritative workflow when they exist. diff --git a/.github/skills/coreex-adapter/SKILL.md b/.github/skills/coreex-adapter/SKILL.md index f83082cf..ef9a3b20 100644 --- a/.github/skills/coreex-adapter/SKILL.md +++ b/.github/skills/coreex-adapter/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: external domain name, operations needed (get/reserve/c tags: ["adapter", "anti-corruption", "http-client", "infrastructure", "integration", "coreex"] --- + + # CoreEx: Adapter (Anti-Corruption Layer) Guides you through adding or modifying an adapter — the boundary that isolates your domain from an external system. Covers the Application-layer interface, Infrastructure-layer implementation, typed HTTP client, sub-folder conventions, registration, and unit testing. diff --git a/.github/skills/coreex-adapter/references/workflow.md b/.github/skills/coreex-adapter/references/workflow.md index 4eb69687..727d1793 100644 --- a/.github/skills/coreex-adapter/references/workflow.md +++ b/.github/skills/coreex-adapter/references/workflow.md @@ -1,3 +1,11 @@ + + # coreex-adapter: Workflow Full workflow for adding or modifying an adapter (anti-corruption layer) in a CoreEx `*.Application` + `*.Infrastructure` host pair. diff --git a/.github/skills/coreex-aggregate/SKILL.md b/.github/skills/coreex-aggregate/SKILL.md index a55bf973..8bf28399 100644 --- a/.github/skills/coreex-aggregate/SKILL.md +++ b/.github/skills/coreex-aggregate/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: domain object kind (aggregate root / entity / value ob tags: ["ddd", "domain-driven-design", "aggregate", "entity", "value-object", "domain-layer", "coreex"] --- + + # CoreEx: Aggregate (DDD Domain Object) Guides you through adding or modifying a domain object in a CoreEx Domain layer — aggregate roots, diff --git a/.github/skills/coreex-aggregate/references/workflow.md b/.github/skills/coreex-aggregate/references/workflow.md index e7d3c523..1e4cf05a 100644 --- a/.github/skills/coreex-aggregate/references/workflow.md +++ b/.github/skills/coreex-aggregate/references/workflow.md @@ -1,3 +1,11 @@ + + # coreex-aggregate: Workflow Full workflow for adding or modifying a DDD domain object (aggregate root, entity, or value object) diff --git a/.github/skills/coreex-api/SKILL.md b/.github/skills/coreex-api/SKILL.md index d96527bf..ddb0501c 100644 --- a/.github/skills/coreex-api/SKILL.md +++ b/.github/skills/coreex-api/SKILL.md @@ -1,10 +1,18 @@ --- name: coreex-api -description: "Add or modify a CoreEx API controller (or Minimal API endpoint) in an *.Api host. USE FOR: scaffolding the MVC controller pair (XxxController + XxxReadController), GET/query/schema endpoints, POST create, PUT + PATCH full-entity update, DELETE, and custom business-action endpoints. Covers both exception-based and Result service styles, and Minimal API as an alternative to MVC. DO NOT USE FOR: Api host setup / Program.cs (use solution-scaffolder), application services (use coreex-app-service), API integration tests (use coreex-test-api)." +description: "Add or modify a CoreEx API controller (or Minimal API endpoint) in an *.Api host. USE FOR: scaffolding the MVC controller pair (XxxController + XxxReadController), GET/query/schema endpoints, POST create, PUT + PATCH full-entity update, DELETE, and custom business-action endpoints. Covers both exception-based and Result service styles, and Minimal API as an alternative to MVC. DO NOT USE FOR: Api host setup / Program.cs (use coreex-solution-scaffolder), application services (use coreex-app-service), API integration tests (use coreex-test-api)." argument-hint: "Optional: entity name, operations needed (get/query/create/update/delete/custom), exception-based or Result service style, MVC or Minimal API" tags: ["api", "controller", "mvc", "minimal-api", "webapi", "routing", "cqrs", "coreex"] --- + + # CoreEx: API Controller Guides you through adding or modifying HTTP API endpoints in an `*.Api` host. Covers the MVC controller pair (mutations + reads), all standard CRUD verbs, custom business-action endpoints, and Minimal API as an alternative. @@ -21,7 +29,7 @@ Guides you through adding or modifying HTTP API endpoints in an `*.Api` host. Co ## When Not to Use -- Api host setup and `Program.cs` composition — use `solution-scaffolder` (see [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md)) +- Api host setup and `Program.cs` composition — use `coreex-solution-scaffolder` (see [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md)) - Application service creation — use `coreex-app-service` - API integration tests — use `coreex-test-api` (hand off once the endpoint is implemented) - Subscriber or relay hosts — controllers do not belong there @@ -57,8 +65,8 @@ For full workflow and code examples see [`references/workflow.md`](references/wo ## Key References - [`/.github/instructions/coreex-api-controllers.instructions.md`](/.github/instructions/coreex-api-controllers.instructions.md) — authoritative conventions: MVC vs Minimal API, WebApi helpers, attributes, route parameter rules, CQRS split -- [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md) — Api host / `Program.cs` composition (scaffolded by `solution-scaffolder`) -- Related skills: [`coreex-app-service`](../coreex-app-service/SKILL.md) (controllers delegate to it), [`coreex-test-api`](../coreex-test-api/SKILL.md) (integration tests for these endpoints), [`coreex-subscriber`](../coreex-subscriber/SKILL.md) (sibling host entry point), [`solution-scaffolder`](../solution-scaffolder/SKILL.md) (host setup) +- [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md) — Api host / `Program.cs` composition (scaffolded by `coreex-solution-scaffolder`) +- Related skills: [`coreex-app-service`](../coreex-app-service/SKILL.md) (controllers delegate to it), [`coreex-test-api`](../coreex-test-api/SKILL.md) (integration tests for these endpoints), [`coreex-subscriber`](../coreex-subscriber/SKILL.md) (sibling host entry point), [`coreex-solution-scaffolder`](../coreex-solution-scaffolder/SKILL.md) (host setup) - Illustrative examples (CoreEx sample — not present in your project): - [`ProductController` + `ProductReadController`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Api/Controllers) — exception-based, full CRUD + query + $query - [`BasketController` + `BasketReadController`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Api/Controllers) — Result<T> style, custom business actions, cross-tagged nested route diff --git a/.github/skills/coreex-api/references/workflow.md b/.github/skills/coreex-api/references/workflow.md index b075dade..6e531c53 100644 --- a/.github/skills/coreex-api/references/workflow.md +++ b/.github/skills/coreex-api/references/workflow.md @@ -1,3 +1,11 @@ + + # coreex-api: Workflow Full workflow for adding or modifying HTTP API endpoints in a CoreEx `*.Api` host using MVC controllers or Minimal API handlers. diff --git a/.github/skills/coreex-app-service/SKILL.md b/.github/skills/coreex-app-service/SKILL.md index 25baa1b6..fc9e5806 100644 --- a/.github/skills/coreex-app-service/SKILL.md +++ b/.github/skills/coreex-app-service/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: entity name, operations needed (get/create/update/dele tags: ["application-service", "application-layer", "unit-of-work", "cqrs", "events", "adapter", "policy", "result", "coreex"] --- + + # CoreEx: Application Service Guides you through creating or modifying a CoreEx Application-layer service in `Application/`. Covers CRUD operations, business actions, unit-of-work + events, CQRS read services, adapters, and policies. diff --git a/.github/skills/coreex-app-service/references/workflow.md b/.github/skills/coreex-app-service/references/workflow.md index 7139c27b..db48bc2c 100644 --- a/.github/skills/coreex-app-service/references/workflow.md +++ b/.github/skills/coreex-app-service/references/workflow.md @@ -1,3 +1,11 @@ + + # coreex-app-service: Workflow Full workflow for creating or modifying a CoreEx Application-layer service in `Application/`. Follow the path that matches the request. diff --git a/.github/skills/coreex-contract/SKILL.md b/.github/skills/coreex-contract/SKILL.md index 1cb3e36e..e306891d 100644 --- a/.github/skills/coreex-contract/SKILL.md +++ b/.github/skills/coreex-contract/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: contract name, domain, root or subordinate, properties tags: ["contracts", "dto", "source-generation", "coreex", "partial", "etag", "changelog"] --- + + # CoreEx: Contract Guides you through creating or modifying a hand-authored contract (DTO/entity) in `*.Contracts`. Covers root entities, subordinate types, request/response objects, and base class extraction. diff --git a/.github/skills/coreex-contract/references/workflow.md b/.github/skills/coreex-contract/references/workflow.md index 7acaa083..4592a855 100644 --- a/.github/skills/coreex-contract/references/workflow.md +++ b/.github/skills/coreex-contract/references/workflow.md @@ -1,3 +1,11 @@ + + # coreex-contract: Workflow Full workflow for creating or modifying a hand-authored contract in `*.Contracts`. Follow the path that matches the request; do not mix paths. diff --git a/.github/skills/coreex-db-migration/SKILL.md b/.github/skills/coreex-db-migration/SKILL.md index ada8048e..8d4714ee 100644 --- a/.github/skills/coreex-db-migration/SKILL.md +++ b/.github/skills/coreex-db-migration/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: entity/table name, schema, SQL Server vs PostgreSQL, n tags: ["database", "migration", "dbex", "schema", "efcore", "coreex"] --- + + # CoreEx: DB Migration Guides you through any database schema change for a CoreEx domain — from choosing the right migration script through to regenerated EF persistence models. diff --git a/.github/skills/coreex-db-migration/references/workflow.md b/.github/skills/coreex-db-migration/references/workflow.md index 71d59924..f9840064 100644 --- a/.github/skills/coreex-db-migration/references/workflow.md +++ b/.github/skills/coreex-db-migration/references/workflow.md @@ -1,3 +1,11 @@ + + # DB Migration Workflow All commands run from the `*.Database` project directory for the target domain. diff --git a/.github/skills/coreex-docs-sync/README.md b/.github/skills/coreex-docs-sync/README.md index 6b067209..de3c4e9c 100644 --- a/.github/skills/coreex-docs-sync/README.md +++ b/.github/skills/coreex-docs-sync/README.md @@ -1,14 +1,33 @@ + + # CoreEx Docs Sync -Fetches the CoreEx architecture docs and per-package AI guides from GitHub and caches them locally. Once cached, the `coreex-expert` agent uses the local copies instead of making live GitHub fetches on every question. +Re-runs `dotnet new coreex-ai --force`, pinned to the exact `CoreEx.Template` version matching this +project's referenced `CoreEx` NuGet version, to refresh the **whole** AI workflow asset bundle — +`.github/instructions/`, `.github/skills/`, `.github/prompts/`, `.github/agents/coreex-expert.agent.md`, +`.claude/commands/`, and the `.github/docs/coreex/` docs cache — as one atomic, version-matched unit. +It does **not** fetch anything live from GitHub `main`; there is no "give me unreleased docs" path. +That's deliberate — instructions/skills (behaviour) and the docs cache (reference material) must +always describe the same release, and a live fetch can't guarantee that. ## When to run -- **After bumping a CoreEx NuGet version** — refreshes the guides to match the version in use. -- **When the CoreEx Expert recommends it** — the agent checks the manifest on every session and flags if the cache is older than 30 days or the recorded version doesn't match the project's current packages. -- **First-time population** — `dotnet new coreex-ai` already ships the cache at install; run this only if the cache is absent (e.g. the assets were copied in manually) or to pull the latest `main`. +- **After bumping the `CoreEx` NuGet version** — refreshes the whole bundle to match. +- **When `coreex-expert` recommends it** — the agent compares `.github/docs/coreex/manifest.txt`'s + `coreex-version` against the project's actually-referenced `CoreEx` version each session and flags + a mismatch (or a missing manifest). + +There is no age-based ("stale after N days") trigger anymore — staleness is purely a version mismatch, +since refreshing now means "re-install the matching release," not "re-fetch `main` and hope it lines up." -Do not run this inside the CoreEx repository itself — the docs are already present locally at `samples/docs/` and `src/*/AGENTS.md`. +Do not run this inside the CoreEx repository itself — the docs are already present locally at +`samples/docs/` and `src/*/AGENTS.md`, and there is no NuGet-referenced version to pin to. ## How to invoke @@ -24,11 +43,16 @@ Do not run this inside the CoreEx repository itself — the docs are already pre No arguments required. -## What gets written +## What gets refreshed ``` +.github/instructions/*.instructions.md +.github/skills/** ← coreex-docs-sync, coreex-solution-scaffolder, and all L1 skills +.github/prompts/*.prompt.md +.github/agents/coreex-expert.agent.md +.claude/commands/** .github/docs/coreex/ - .manifest ← sync date, CoreEx version, referenced packages + manifest.txt ← coreex-version, stamped by the template — not hand-written local-dev.md layers.md patterns.md @@ -40,8 +64,9 @@ No arguments required. testing.md tooling.md aspire.md + application-scaffolding-guide.md agents/ - CoreEx.md ← one guide per package, always synced + CoreEx.md ← one guide per package, always present CoreEx.AspNetCore.md CoreEx.AspNetCore.NSwag.md CoreEx.Azure.Messaging.ServiceBus.md @@ -59,9 +84,15 @@ No arguments required. CoreEx.Validation.md ``` -All 16 package guides are synced unconditionally — not just the packages the project currently references. This lets the expert recommend adopting a new package with full knowledge of what it offers. The `referenced-packages` field in the manifest records which packages are actually in the project so the expert can distinguish the two. +All of the above are part of the one `dotnet new coreex-ai --force` bundle — none are fetched or +written independently. All 16 package guides are always present regardless of which packages the +project currently references, so the expert can recommend adopting a new one with full knowledge of +what it offers; it reads the project's actual package references live each session (no longer cached) +to tell "already in use" from "you'd need to add this." ## Reference -- [SKILL.md](./SKILL.md) — full detail on detection logic, manifest format, and re-run triggers. -- [CoreEx Expert agent README](../agents/README.md) — how the agent uses the cache and when it suggests a refresh. +- [SKILL.md](./SKILL.md) — full detail on the version-pinned refresh mechanism, guardrails (`--force` + scoping, orphan files), and the manifest format. +- [CoreEx Expert agent README](../agents/README.md) — how the agent uses the cache and when it + flags a version mismatch. diff --git a/.github/skills/coreex-docs-sync/SKILL.md b/.github/skills/coreex-docs-sync/SKILL.md index 5d700635..4dc84993 100644 --- a/.github/skills/coreex-docs-sync/SKILL.md +++ b/.github/skills/coreex-docs-sync/SKILL.md @@ -1,41 +1,61 @@ --- name: coreex-docs-sync -description: "Fetch the CoreEx sample architecture docs and all per-package AI usage guides from GitHub and cache them locally under .github/docs/coreex/. Guides for all CoreEx packages are synced regardless of which ones the project currently references, enabling the CoreEx Expert to guide on and recommend any package." -argument-hint: No arguments required. Run to set up or refresh the local doc cache. -tags: ["docs", "cache", "sync", "coreex-expert", "offline"] +description: "Refresh the full CoreEx AI workflow asset bundle (instructions, skills, prompts, the coreex-expert agent, and the .github/docs/coreex/ docs cache) to the CoreEx.Template release matching the project's installed CoreEx NuGet version. Version-pinned — never fetches unreleased GitHub main content — so the assets that shape AI behaviour and the reference docs they point to always describe the exact same release." +argument-hint: No arguments required. Run to check for or apply a version-pinned refresh. +tags: ["docs", "cache", "sync", "coreex-expert", "template", "versioning"] --- -# Sync CoreEx Docs + -Fetches the CoreEx sample architecture docs and AI usage guides for all CoreEx packages from GitHub, writing them to `.github/docs/coreex/`. All 16 package guides are synced unconditionally — this enables the CoreEx Expert to give authoritative guidance on any package, including ones not yet adopted by the project. Writes a manifest recording which packages the project currently references so the expert can distinguish "already in use" from "you'd need to add this." +# Sync CoreEx AI Assets + +Keeps `.github/instructions/`, `.github/skills/`, `.github/prompts/`, `.github/agents/coreex-expert.agent.md`, `.claude/commands/`, and the `.github/docs/coreex/` docs cache all pinned to **one** `CoreEx.Template` release — the one matching the `CoreEx` NuGet version this project actually references. It does this by re-running `dotnet new coreex-ai --force` at that exact version — it does **not** fetch anything from GitHub `main` directly. Instructions/skills (behaviour) and the docs cache (reference material) are one bundle from one release; they cannot drift apart from each other under this mechanism. ## When to Use -- **Refresh** the cache after bumping a CoreEx NuGet package version. -- When the CoreEx Expert flags the cache stale (older than 30 days, or a version mismatch). -- To pull the latest CoreEx `main` branch docs. -- **First-time population** only when the cache is absent — e.g. a project that installed the AI assets manually. `dotnet new coreex-ai` already ships the cache at install, so most consumers use this skill to refresh, not populate. +- The `CoreEx` NuGet package version was bumped in the project. +- `coreex-expert` reports the installed AI-asset bundle (`.github/docs/coreex/manifest.txt` → `coreex-version`) no longer matches the project's referenced `CoreEx` version. +- `.github/docs/coreex/manifest.txt` is missing entirely (assets were copied in by hand rather than via `dotnet new coreex-ai`). ## When Not to Use -- In the CoreEx repository itself — the docs are already present at `samples/docs/` and `src/*/AGENTS.md`. -- When you only need one specific doc — fetch the GitHub URL directly instead. +- In the CoreEx repository itself — the docs are already present at `samples/docs/` and `src/*/AGENTS.md`, and there is no NuGet-referenced version to pin to. +- When you only need one specific doc and are not trying to realign the whole bundle — fetch the GitHub URL for that one file directly instead; do not use this as a substitute for the version-pinned refresh. +- To get unreleased/`main`-branch content ahead of a release — not supported. This skill only ever installs a released `CoreEx.Template` version; there is no "give me the latest main" path, by design (see Rationale below). ## What It Does -1. **Detects** the current `CoreEx` NuGet version from `Directory.Packages.props`, `*.csproj`, or `Directory.Build.props`. -2. **Detects** all `CoreEx.*` package references currently in the project — recorded in the manifest for the expert's awareness, not used to limit what is synced. -3. **Creates** `.github/docs/coreex/` and `.github/docs/coreex/agents/` if they do not exist. -4. **Fetches** the 11 sample architecture docs and writes them to `.github/docs/coreex/`. -5. **Fetches** all 16 per-package `AGENTS.md` guides and writes them to `.github/docs/coreex/agents/`. -6. **Writes** `.github/docs/coreex/.manifest` with sync date, CoreEx version, and the list of packages currently referenced in the project. -7. **Reports** success, any fetch failures, and a reminder of when to re-run. +1. **Detects** the `CoreEx` NuGet version this project references, from `Directory.Packages.props`, `*.csproj`, or `Directory.Build.props`. This is the target version — `CoreEx.Template` and `CoreEx` are released from the same repo at the same version number, so the two always match. +2. **Reads** `.github/docs/coreex/manifest.txt` (if present) for the currently-installed `coreex-version`. If it already equals the detected target, **report already in sync and stop** — nothing to do. +3. **Installs the pinned template version explicitly** — `dotnet new install CoreEx.Template::` — never a bare `dotnet new install CoreEx.Template` (which silently resolves to latest and can desync the AI assets from the project's actual CoreEx version). +4. **Dry-runs first.** Runs `dotnet new coreex-ai` (and `--app-folder ` if this is a monorepo, per the recorded app folder) **without** `--force` and shows the "Overwrite" file list it reports — this is the only point at which the pending change set is visible; review it before proceeding. +5. **Confirms, then applies** — re-runs with `--force`. Note: `--force` overwrites every file the template currently emits; it does **not** delete files the template no longer emits (see [Guardrails](#guardrails)). +6. **Flags possible orphans** — compares the skill folder names under `.github/skills/coreex-*/` against the current known L1 skill set (listed in [`AI-WORKFLOWS.md`](/.github/AI-WORKFLOWS.md)); any extra folder is very likely left over from a prior version and should be reviewed for manual removal, not assumed safe. +7. **Reports** the refreshed `coreex-version`, the files changed (from the step-4 dry run), and any flagged orphans. + +## Guardrails + +- **Never install an unpinned/latest template version.** Every `dotnet new install CoreEx.Template` must carry an explicit `::` matching the project's referenced `CoreEx` NuGet version. A bare install is how the two mechanisms (code and AI assets) end up on different releases. +- **`--force` is scoped, not destructive, but also not a cleanup tool.** It only overwrites files the template's *current* version emits — verified empirically: unrelated repo content and files outside the template's manifest are untouched. It does **not** prune files an *older* version emitted that the current version no longer does (e.g. a renamed or retired skill) — those are true orphans and need manual review/removal. +- **Always dry-run before `--force`.** The no-`--force` invocation is the only place the pending "Overwrite" list is printed; running `--force` straight away gives no visibility into what actually changed. +- **Hand-edits to template-sourced files do not survive a refresh.** Every file under `.github/instructions/`, `.github/skills/`, `.github/prompts/`, `.github/agents/coreex-expert.agent.md`, and `.claude/commands/` is regenerated by this process — see the header on each file. +- **Never run this inside the CoreEx framework repository itself.** + +## Rationale — why version-pinned only, no live `main` fetch + +`dotnet new coreex-ai` bakes instructions/skills/prompts/agent/docs content **at `CoreEx.Template` pack time** — it is already an atomic, version-pinned snapshot the moment that NuGet package is built. A live fetch from GitHub `main` is a *second, independent* sync mechanism with no version guarantee at all — it could pull docs describing behaviour from a `CoreEx` version newer (or older) than the one actually referenced, while instructions/skills stay pinned to whichever template version was last installed. That drift is exactly the failure mode this skill exists to prevent, so there is deliberately no "fetch unreleased main" path. ## Cache Layout ``` .github/docs/coreex/ - .manifest # synced date, coreex-version, referenced-packages + manifest.txt # coreex-version — stamped by the CoreEx.Template package at generation time local-dev.md layers.md patterns.md @@ -47,9 +67,10 @@ Fetches the CoreEx sample architecture docs and AI usage guides for all CoreEx p testing.md tooling.md aspire.md + application-scaffolding-guide.md agents/ - CoreEx.md # always present after sync - CoreEx.AspNetCore.md # always present after sync + CoreEx.md + CoreEx.AspNetCore.md CoreEx.AspNetCore.NSwag.md CoreEx.Azure.Messaging.ServiceBus.md CoreEx.Caching.FusionCache.md @@ -66,18 +87,20 @@ Fetches the CoreEx sample architecture docs and AI usage guides for all CoreEx p CoreEx.Validation.md ``` +All of the above are shipped and refreshed as part of the `dotnet new coreex-ai --force` bundle — none are fetched independently. + ## Manifest Format ``` -synced: YYYY-MM-DD -coreex-version: -referenced-packages: CoreEx, CoreEx.Validation, CoreEx.EntityFrameworkCore, ... +coreex-version: ``` -The `referenced-packages` field lets the CoreEx Expert distinguish between packages already in the project and packages it might recommend adding. +Stamped directly by the template (via the same `coreex-version` constant symbol the `coreex`/`coreex-api`/`coreex-relay`/`coreex-subscribe` templates already use to pin `Directory.Packages.props`) — never hand-written, never fetched. `referenced-packages` (which `CoreEx.*` packages the project currently uses) is no longer cached; `coreex-expert` reads that live from the project's package references each session since it is cheap to compute and does not need to survive a refresh. ## Re-run Triggers -- CoreEx NuGet version bumped in the project. -- CoreEx Expert reports cache is older than 30 days. -- CoreEx Expert reports a version mismatch between the manifest and the project's current package version. +- `CoreEx` NuGet version bumped in the project (the manifest's `coreex-version` will then differ from the detected version). +- `coreex-expert` reports the manifest is missing. +- `coreex-expert` reports a version mismatch between the manifest and the project's current package version. + +There is no age-based ("older than N days") trigger — staleness is defined purely by version mismatch now that refresh means "re-install the matching release," not "re-fetch main and hope it lines up." diff --git a/.github/skills/coreex-policy/SKILL.md b/.github/skills/coreex-policy/SKILL.md index ff491665..0b29b28d 100644 --- a/.github/skills/coreex-policy/SKILL.md +++ b/.github/skills/coreex-policy/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: entity being guarded, type of guard (exists-check / bu tags: ["policy", "application-layer", "result", "guard", "adapter", "anti-corruption", "coreex"] --- + + # CoreEx: Policy Guides you through creating or modifying a CoreEx Application-layer policy class in `Application/Policies/`. Policies encapsulate **domain-level guard logic that requires async I/O** — adapter or repository calls — providing a named, independently testable home for rules that cannot live in a synchronous validator or directly in the domain model. diff --git a/.github/skills/coreex-policy/references/workflow.md b/.github/skills/coreex-policy/references/workflow.md index 69ad3cae..6a02e766 100644 --- a/.github/skills/coreex-policy/references/workflow.md +++ b/.github/skills/coreex-policy/references/workflow.md @@ -1,3 +1,11 @@ + + # coreex-policy: Workflow Full workflow for creating or modifying a CoreEx Application-layer policy class in `Application/Policies/`. A policy encapsulates guard logic that requires async I/O and cannot live in a synchronous validator. diff --git a/.github/skills/coreex-refdata/SKILL.md b/.github/skills/coreex-refdata/SKILL.md index 759cf54a..22a4e2d0 100644 --- a/.github/skills/coreex-refdata/SKILL.md +++ b/.github/skills/coreex-refdata/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: entity name(s), domain, extra properties beyond standa tags: ["refdata", "codegen", "database", "contracts", "reference-data", "coreex"] --- + + # CoreEx: Reference Data Guides you through the complete end-to-end workflow for adding or modifying a reference data (Code/Text lookup) type in a CoreEx domain — covering both the database side (`*.Database`) and the CodeGen side (`*.CodeGen`). diff --git a/.github/skills/coreex-refdata/references/workflow.md b/.github/skills/coreex-refdata/references/workflow.md index 9b02fcf2..ad08f3bb 100644 --- a/.github/skills/coreex-refdata/references/workflow.md +++ b/.github/skills/coreex-refdata/references/workflow.md @@ -1,3 +1,11 @@ + + # coreex-refdata: Workflow Full step-by-step workflow for adding or modifying a reference data type. Follow phases in order — the database must be up to date before CodeGen runs. diff --git a/.github/skills/coreex-repository/SKILL.md b/.github/skills/coreex-repository/SKILL.md index c08db591..4fc148a8 100644 --- a/.github/skills/coreex-repository/SKILL.md +++ b/.github/skills/coreex-repository/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: entity name, database type (PostgreSQL/SQL Server), op tags: ["repository", "infrastructure", "efcore", "mapping", "coreex", "data-access", "result"] --- + + # CoreEx: Repository Guides you through creating or modifying a CoreEx Infrastructure-layer repository in `Infrastructure/Repositories/`. Covers CRUD delegates, custom queries, bidirectional mappers, EfDb accessors, and `Result` pipelines. diff --git a/.github/skills/coreex-repository/references/workflow.md b/.github/skills/coreex-repository/references/workflow.md index a8678436..38a64848 100644 --- a/.github/skills/coreex-repository/references/workflow.md +++ b/.github/skills/coreex-repository/references/workflow.md @@ -1,3 +1,11 @@ + + # coreex-repository: Workflow Full workflow for creating or modifying a CoreEx Infrastructure repository in `Infrastructure/Repositories/`. Follow the path that matches the request. diff --git a/.github/skills/solution-scaffolder/README.md b/.github/skills/coreex-solution-scaffolder/README.md similarity index 75% rename from .github/skills/solution-scaffolder/README.md rename to .github/skills/coreex-solution-scaffolder/README.md index 5e06da37..a87eedb8 100644 --- a/.github/skills/solution-scaffolder/README.md +++ b/.github/skills/coreex-solution-scaffolder/README.md @@ -1,3 +1,11 @@ + + # CoreEx Scaffold Guides a developer through selecting the right `CoreEx.Template` scaffolding shape and then runs the matching `dotnet new coreex*` commands. @@ -23,7 +31,7 @@ Guides a developer through selecting the right `CoreEx.Template` scaffolding sha If the prompt file is not present, attach the skill file directly in Copilot Chat: ``` -#file:.github/skills/solution-scaffolder/SKILL.md scaffold a new CoreEx solution for my requirements +#file:.github/skills/coreex-solution-scaffolder/SKILL.md scaffold a new CoreEx solution for my requirements ``` ## What it will do diff --git a/.github/skills/solution-scaffolder/SKILL.md b/.github/skills/coreex-solution-scaffolder/SKILL.md similarity index 91% rename from .github/skills/solution-scaffolder/SKILL.md rename to .github/skills/coreex-solution-scaffolder/SKILL.md index c8923f5a..ff169850 100644 --- a/.github/skills/solution-scaffolder/SKILL.md +++ b/.github/skills/coreex-solution-scaffolder/SKILL.md @@ -1,10 +1,18 @@ --- -name: solution-scaffolder +name: coreex-solution-scaffolder description: "Guide a developer through CoreEx solution shaping after bootstrap, using a short plain-English interview that turns user answers into safe dotnet new template inputs. USE FOR: bootstrap-only repos, deciding API-only vs API plus relay vs API plus subscriber, choosing SQL Server vs Postgres vs no database, choosing refdata/outbox/DDD/ROP options, installing CoreEx.Template, checking current solution shape, adding missing Api/Relay/Subscribe hosts to an existing repo, and optionally preparing a first local runnable state with local dependency assets plus database/code-generation steps. DO NOT USE FOR: unrelated runtime debugging, bootstrap creation, or forcing root re-scaffolding over an existing solution. INVOKES: workspace inspection, ask-questions style interviews, dotnet new install/list, dry-run validation, solution wiring, optional local dependency asset creation, focused build/test validation, and either template generation or manual retrofit work depending on repo shape." argument-hint: "Optional: base solution name, whether this is new or retrofit, required hosts, database choice, and messaging needs." tags: ["coreex", "scaffolding", "retrofit", "template", "hosts"] --- + + # CoreEx Scaffold Guides a repository through the right CoreEx setup path by interviewing the user in simple English and translating the answers into safe CoreEx template commands. @@ -88,7 +96,7 @@ Before this skill runs, the repository must already be in one of these states: - A bootstrap-only shell created by `coreex-bootstrap`; or - An existing CoreEx solution that is missing some runtime hosts. -**AI assets:** If the repository does not yet have `.github/instructions/` or `.github/agents/`, the consumer should run `dotnet new coreex-ai` at the repo root first (optionally with `--app-folder ` for monorepos). This skill will remind the user if AI assets appear to be absent. +**AI assets:** If the repository does not yet have `.github/instructions/` or `.github/agents/`, the consumer should run `dotnet new coreex-ai` at the repo root first (optionally with `--app-folder ` for monorepos), installed from an explicitly pinned `CoreEx.Template::` (see [Version-pin discipline](/.github/AI-WORKFLOWS.md#version-pin-discipline)). This skill will remind the user if AI assets appear to be absent. ## Naming Rules @@ -147,10 +155,11 @@ dotnet new coreex-subscribe -n Company.Product.Domain.Subscribe ... - **Bootstrap is a prereq:** This skill assumes any bootstrap creation has already happened before the workflow starts. - **For retrofit work:** If the repo already contains a solution, `src/`, tooling, or tests, do not re-run the root scaffold unless the current shape is still only the bootstrap shell. - **Template identity conflicts:** If `dotnet new list` or template execution reports duplicate CoreEx template identities, warn the user which template source is being selected before continuing. +- **Version-pin discipline:** If `CoreEx.Template` needs installing or updating here, never run a bare `dotnet new install CoreEx.Template` — resolve and pin an explicit `::` matching the project's referenced `CoreEx` NuGet version (see [AI-WORKFLOWS.md § Version-pin discipline](/.github/AI-WORKFLOWS.md#version-pin-discipline)). - **Pre-flight validation:** Always run a dry-run with the intended base solution name before any real domain/host template invocation. - **Watch for nested roots:** If dry-run output shows paths like `src\\Company.Product.Domain.Api\\src\\Company.Product.Domain.Api\\...`, the output root is wrong; stop and change strategy. - **Naming mismatch:** If dry-run output shows incorrect project names because the repo uses a non-canonical root name, stop and recommend either renaming the solution first or manually creating the missing hosts. -- **Force only for bootstrap replacement:** In a confirmed bootstrap-only repo, `--force` is acceptable only after dry-run validation shows the expected canonical layout. +- **Force only for bootstrap replacement:** In a confirmed bootstrap-only repo, `--force` is acceptable only after dry-run validation shows the expected canonical layout. `--force` overwrites every file the current template version emits but does **not** delete files an older/superseded template version left behind — treat any such leftover as a manual-review orphan, not a safe-to-ignore artifact. - **No force for mismatches:** Do not use `--force` to push through a naming or layout mismatch. The mismatch itself indicates an unsafe operation. ## Validation Rules diff --git a/.github/skills/coreex-solution-scaffolder/assets/README.md b/.github/skills/coreex-solution-scaffolder/assets/README.md new file mode 100644 index 00000000..08905c5a --- /dev/null +++ b/.github/skills/coreex-solution-scaffolder/assets/README.md @@ -0,0 +1,46 @@ + + +# coreex-solution-scaffolder assets + +Static local-dev infrastructure fallbacks bundled with this skill: `docker-compose.local.yml` and +`servicebus-config.template.json`. + +## Why these exist + +`CoreEx.Core`'s own `dotnet new coreex` template already emits a correct, option-aware +`docker-compose.yml` and `servicebus/Config.json` — conditioned on the `data-provider` and +`messaging-provider` the consumer actually selected. That is always the authoritative source **when +it is safe to (re-)run**. + +It is often *not* safe to re-run: per this skill's Existing Repository Guardrails, re-running the root +`coreex` template over an already-evolved solution would overwrite hand-written Application/Domain/ +Infrastructure code. So there is a real retrofit gap — a repo that added `coreex-api`/`coreex-relay` +individually (or that predates these files) can be missing local-dev infrastructure entirely, with no +safe way to regenerate it from the template. These two files exist to fill exactly that gap. + +## Where they came from, and why they intentionally diverge from `CoreEx.Core` + +Both files were adapted from this repository's own root [`docker-compose.yml`](/docker-compose.yml) +and [`servicebus/Config.json`](/servicebus/Config.json) — the same infrastructure used to run the +Products/Shopping samples — not from `CoreEx.Core`'s conditional template output. Two deliberate +changes were made to keep them consumer-safe and domain-agnostic: + +- `servicebus-config.template.json`'s third topic was genericized from this repo's own + `contoso`/`products`/`shopping` sample names to `__TOPIC_NAME__`/`__SUBSCRIPTION_NAME__` placeholders + — per this repo's core principle that skills must not be tied to the sample domains. +- Unlike `CoreEx.Core`'s `docker-compose.yml` (which conditionally includes only the services matching + the selected `data-provider`/`messaging-provider`), these are an unconditional "everything" stack — + SQL Server, Postgres, Redis, Service Bus emulator, Aspire Dashboard, and the DTS emulator — so the + fallback works regardless of which options the retrofit repo actually chose. Trim unused services + when applying them to a specific repo. + +This means the two are **intentionally not** byte-identical to `CoreEx.Core`'s generated output, and +they are not automatically kept in sync with either that template or this repo's own root files. If a +required service, port, or image tag changes in `/docker-compose.yml`, `/servicebus/Config.json`, or +`CoreEx.Core`'s templated equivalents, reconcile these two copies by hand as part of that change. diff --git a/.github/skills/solution-scaffolder/assets/docker-compose.local.yml b/.github/skills/coreex-solution-scaffolder/assets/docker-compose.local.yml similarity index 86% rename from .github/skills/solution-scaffolder/assets/docker-compose.local.yml rename to .github/skills/coreex-solution-scaffolder/assets/docker-compose.local.yml index b9f7f5d4..8ce4ab29 100644 --- a/.github/skills/solution-scaffolder/assets/docker-compose.local.yml +++ b/.github/skills/coreex-solution-scaffolder/assets/docker-compose.local.yml @@ -1,3 +1,5 @@ +# Static, full-featured local-dev fallback bundled with the coreex-solution-scaffolder skill — see +# README.md (this directory) for what this is, where it came from, and when to reconcile it. name: CoreEx services: diff --git a/.github/skills/solution-scaffolder/assets/servicebus-config.template.json b/.github/skills/coreex-solution-scaffolder/assets/servicebus-config.template.json similarity index 100% rename from .github/skills/solution-scaffolder/assets/servicebus-config.template.json rename to .github/skills/coreex-solution-scaffolder/assets/servicebus-config.template.json diff --git a/.github/skills/solution-scaffolder/references/workflow.md b/.github/skills/coreex-solution-scaffolder/references/workflow.md similarity index 94% rename from .github/skills/solution-scaffolder/references/workflow.md rename to .github/skills/coreex-solution-scaffolder/references/workflow.md index 0dfccf7e..9e42065f 100644 --- a/.github/skills/solution-scaffolder/references/workflow.md +++ b/.github/skills/coreex-solution-scaffolder/references/workflow.md @@ -1,3 +1,11 @@ + + # Workflow Use this workflow to guide the user through CoreEx solution shape decisions in plain English, then turn the answers into safe `dotnet new` inputs. @@ -325,7 +333,7 @@ For local development there are two validation modes: - `dotnet test` for `tests/[solution].Test.Unit` should run by default when the project exists. - API, relay, and subscriber tests may depend on local SQL Server or Postgres, Redis, Service Bus, or other host prerequisites. If those are not configured, skip those tests and say why. - A missing local dependency is a deferred setup item, not necessarily a scaffolding failure. -- If the user wants runnable local validation and local dependency files are missing, materialize them first from bundled templates or repo-standard equivalents, for example `.github/skills/solution-scaffolder/assets/docker-compose.local.yml` and `.github/skills/solution-scaffolder/assets/servicebus-config.template.json`. +- If the user wants runnable local validation and local dependency files are missing, materialize them first from bundled templates or repo-standard equivalents, for example `.github/skills/coreex-solution-scaffolder/assets/docker-compose.local.yml` and `.github/skills/coreex-solution-scaffolder/assets/servicebus-config.template.json`. These are a static, options-agnostic fallback for use when it is unsafe to re-run the root scaffold — see `.github/skills/coreex-solution-scaffolder/assets/README.md` for why they exist and how they relate to the scaffold's own generated `docker-compose.yml`/`servicebus/Config.json`. - If the generated shape includes SQL Server, Redis, or Service Bus and the repo lacks a root `docker-compose.yml`, create one before trying to run broader tests. - If Service Bus emulator wiring is needed and the repo lacks `servicebus/Config.json`, create it before starting the dependency stack. - For `tools/[solution].Database`, run from that directory so `dbex.yaml` is discovered correctly. Prefer `dotnet run -- All` for first-run local setup and `dotnet run -- CodeGen` or `dotnet run -- Database` for narrower follow-up work. diff --git a/.github/skills/coreex-subscriber/SKILL.md b/.github/skills/coreex-subscriber/SKILL.md index 307b67ad..c17726b8 100644 --- a/.github/skills/coreex-subscriber/SKILL.md +++ b/.github/skills/coreex-subscriber/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: subscriber scenario (command / event-sync / event-proc tags: ["subscriber", "messaging", "service-bus", "event-handling", "choreography", "saga", "coreex"] --- + + # CoreEx: Subscriber Guides you through adding or modifying a subscriber in a CoreEx Subscribe host. A subscriber is the diff --git a/.github/skills/coreex-subscriber/references/workflow.md b/.github/skills/coreex-subscriber/references/workflow.md index b9ad2431..30e43e69 100644 --- a/.github/skills/coreex-subscriber/references/workflow.md +++ b/.github/skills/coreex-subscriber/references/workflow.md @@ -1,3 +1,11 @@ + + # coreex-subscriber: Workflow Full workflow for adding or modifying a subscriber in a CoreEx Subscribe host. diff --git a/.github/skills/coreex-test-api/SKILL.md b/.github/skills/coreex-test-api/SKILL.md index 3572d951..4f50ac9a 100644 --- a/.github/skills/coreex-test-api/SKILL.md +++ b/.github/skills/coreex-test-api/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Entity/endpoint name, operations to cover (Get/Query/Create/Upda tags: ["testing", "integration-tests", "api", "unittestex", "coreex"] --- + + # CoreEx: API Integration Test Guides you through writing or extending `*.Test.Api` integration tests — the real host, real DB, real diff --git a/.github/skills/coreex-test-api/references/workflow.md b/.github/skills/coreex-test-api/references/workflow.md index f02bde5e..8c367a24 100644 --- a/.github/skills/coreex-test-api/references/workflow.md +++ b/.github/skills/coreex-test-api/references/workflow.md @@ -1,3 +1,11 @@ + + # API Integration Test — Full Workflow ## Phase 0 — Confirm Scope diff --git a/.github/skills/coreex-test-relay/SKILL.md b/.github/skills/coreex-test-relay/SKILL.md index 1606041d..210b4dd8 100644 --- a/.github/skills/coreex-test-relay/SKILL.md +++ b/.github/skills/coreex-test-relay/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: what you're trying to verify (relay forwarding, hosted tags: ["testing", "integration-tests", "relay", "outbox", "service-bus", "unittestex", "coreex"] --- + + # CoreEx: Outbox Relay Host Integration Test Guides you through the `*.Test.Relay` project — the most templated and least-extended of the three diff --git a/.github/skills/coreex-test-subscribe/SKILL.md b/.github/skills/coreex-test-subscribe/SKILL.md index 79f467cf..d5f6389e 100644 --- a/.github/skills/coreex-test-subscribe/SKILL.md +++ b/.github/skills/coreex-test-subscribe/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Subscriber name/scenario (command / event-sync / event-business- tags: ["testing", "integration-tests", "subscriber", "messaging", "unittestex", "coreex"] --- + + # CoreEx: Subscribe Host Integration Test Guides you through writing or extending `*.Test.Subscribe` integration tests — simulating broker diff --git a/.github/skills/coreex-test-subscribe/references/workflow.md b/.github/skills/coreex-test-subscribe/references/workflow.md index ef70ade3..41047525 100644 --- a/.github/skills/coreex-test-subscribe/references/workflow.md +++ b/.github/skills/coreex-test-subscribe/references/workflow.md @@ -1,3 +1,11 @@ + + # Subscribe Host Integration Test — Full Workflow ## Phase 0 — Match the Test Shape to the Subscriber Scenario diff --git a/.github/skills/coreex-validator/SKILL.md b/.github/skills/coreex-validator/SKILL.md index a415c9d7..d0e6c0b1 100644 --- a/.github/skills/coreex-validator/SKILL.md +++ b/.github/skills/coreex-validator/SKILL.md @@ -5,6 +5,14 @@ argument-hint: "Optional: contract type, validator name, list of properties to v tags: ["validators", "validation", "application-layer", "coreex", "fluent-rules", "async"] --- + + # CoreEx: Validator Guides you through creating or modifying a CoreEx validator (`Application/Validators/`) for a contract or request type. Covers declarative rules, ref-data validation, async database checks, collection and dictionary validators. diff --git a/.github/skills/coreex-validator/references/workflow.md b/.github/skills/coreex-validator/references/workflow.md index bd6d4c36..9de7eabb 100644 --- a/.github/skills/coreex-validator/references/workflow.md +++ b/.github/skills/coreex-validator/references/workflow.md @@ -1,3 +1,11 @@ + + # coreex-validator: Workflow Full workflow for creating or modifying a CoreEx validator in `Application/Validators/`. Follow the path that matches the request. diff --git a/AGENTS.md b/AGENTS.md index 4de27f43..e7c86f3f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -72,11 +72,23 @@ Single-repo projects answer "no" (the most common case). Monorepo projects provi ### Step 3 — Install the template and AI workflow assets -Run these commands from the **repo root**: +Resolve the version to pin first — **never run a bare `dotnet new install CoreEx.Template`** (it +silently resolves to whatever is latest at that moment and is never recorded anywhere, so a future +session has nothing to compare against to detect drift): + +- If this repository already references a `CoreEx` NuGet package (check `Directory.Packages.props`, + `*.csproj`, or `Directory.Build.props`), use that exact version — `CoreEx.Template` and `CoreEx` are + released from the same repo at the same version number, so they always match. +- Otherwise (true first-time adoption, no CoreEx reference yet), resolve the latest stable release — + e.g. `dotnet package search CoreEx.Template --exact-match` or check the + [NuGet.org listing](https://www.nuget.org/packages/CoreEx.Template) — and pin to that specific version + explicitly. Record it so later sessions (and `/coreex-docs-sync`) know what was installed. + +Run these commands from the **repo root**, substituting `` for the version resolved above: ```bash -# Install the template pack (skip if already installed) -dotnet new install CoreEx.Template +# Install the exact pinned template version (skip if already installed at that version) +dotnet new install CoreEx.Template:: # Single-repo project: dotnet new coreex-ai @@ -88,15 +100,18 @@ dotnet new coreex-ai --app-folder This installs: - `.github/instructions/` — scoped instruction files auto-injected by Copilot for each file type - `.github/prompts/` — the scaffolding prompt plus one `coreex-.prompt.md` per L1 skill -- `.github/skills/` — the CoreEx skill suite: `coreex-docs-sync`, `solution-scaffolder`, and the L1 skills +- `.github/skills/` — the CoreEx skill suite: `coreex-docs-sync`, `coreex-solution-scaffolder`, and the L1 skills (`coreex-contract`, `coreex-refdata`, `coreex-db-migration`, `coreex-repository`, `coreex-adapter`, `coreex-app-service`, `coreex-validator`, `coreex-policy`, `coreex-aggregate`, `coreex-api`, `coreex-subscriber`, `coreex-test-api`, `coreex-test-subscribe`, `coreex-test-relay`) - `.github/agents/coreex-expert.agent.md` — architecture guidance agent -- `.github/docs/coreex/` — the architecture docs + per-package guides cache (refresh later with `/coreex-docs-sync`) +- `.github/docs/coreex/` — the architecture docs + per-package guides cache, self-describing via + `.github/docs/coreex/manifest.txt` (refresh later, version-pinned, with `/coreex-docs-sync`) - `.claude/commands/` — equivalent commands for Claude Code -For the full catalog and how these pieces fit together, see [`.github/AI-WORKFLOWS.md`](./.github/AI-WORKFLOWS.md). +Every file above comes from **one** `CoreEx.Template` release installed as **one** atomic bundle — +never mix versions across a single install. For the full catalog and how these pieces fit together, +see [`.github/AI-WORKFLOWS.md`](./.github/AI-WORKFLOWS.md). ### Step 4 — Reload context and continue diff --git a/consumer-instructions/.github/copilot-instructions.md b/consumer-instructions/.github/copilot-instructions.md index 1b0799fa..8975b88f 100644 --- a/consumer-instructions/.github/copilot-instructions.md +++ b/consumer-instructions/.github/copilot-instructions.md @@ -1,3 +1,13 @@ + + # CoreEx — AI Coding Context ## What CoreEx Is diff --git a/consumer-instructions/README.md b/consumer-instructions/README.md index 42a43879..9c5066ee 100644 --- a/consumer-instructions/README.md +++ b/consumer-instructions/README.md @@ -16,7 +16,7 @@ The per-capability instruction files live in the repo's root [`.github/instructi 1. Copy `consumer-instructions/.github/copilot-instructions.md` to your project's `.github/copilot-instructions.md`. 2. Copy the instruction files from the repo's `.github/instructions/` that match what you're building into your project's `.github/instructions/` folder. -3. If you want the guided greenfield scaffolding workflow in a non-template repository, copy the canonical files from the repo's [`.github/prompts/`](../.github/prompts/) and [`.github/skills/solution-scaffolder/`](../.github/skills/solution-scaffolder/) folders. +3. If you want the guided greenfield scaffolding workflow in a non-template repository, copy the canonical files from the repo's [`.github/prompts/`](../.github/prompts/) and [`.github/skills/coreex-solution-scaffolder/`](../.github/skills/coreex-solution-scaffolder/) folders. Copilot applies the global instructions to every chat interaction and injects the file-scoped instructions automatically based on which file is open. If you copied the greenfield scaffold prompt and skill, run `/coreex-scaffold` to choose the right `CoreEx.Template` commands before generating the solution. diff --git a/src/CoreEx.Template/CoreEx.Template.csproj b/src/CoreEx.Template/CoreEx.Template.csproj index 646b197d..faeb209f 100644 --- a/src/CoreEx.Template/CoreEx.Template.csproj +++ b/src/CoreEx.Template/CoreEx.Template.csproj @@ -71,8 +71,12 @@ // When absent, preserve the original per-file applyTo pattern unchanged. var colonIdx = line.IndexOf(':'); var originalPattern = line.Substring(colonIdx + 1).Trim().Trim('"'); + // __COREEX_AI_APP_FOLDER_TOKEN__ is a sentinel, not the literal text "app-folder": the + // app-folder symbol's "replaces" value is a global text-replace across every packed file, + // so using the human-readable name here would also corrupt any prose mentioning "app-folder" + // (e.g. "--app-folder" in SKILL.md/README.md) elsewhere in the CoreEx.Ai bundle. sb.AppendLine(""); - sb.AppendLine("applyTo: \"app-folder/" + originalPattern + "\""); + sb.AppendLine("applyTo: \"__COREEX_AI_APP_FOLDER_TOKEN__/" + originalPattern + "\""); sb.AppendLine(""); sb.AppendLine(line); sb.AppendLine(""); @@ -94,13 +98,13 @@ + + + + + <_AiManifestLine Include="# Generated by 'dotnet new coreex-ai' — records which CoreEx.Template release populated" /> + <_AiManifestLine Include="# .github/instructions, .github/skills, .github/prompts, .github/agents, and this docs cache." /> + <_AiManifestLine Include="# Do not hand-edit — refreshed automatically by 'dotnet new coreex-ai --force'." /> + <_AiManifestLine Include="coreex-version: __COREEX_AI_VERSION_TOKEN__" /> + + + <_CoreExAiGenerated Include="$(_AiAiGenRoot)**/*" /> diff --git a/src/CoreEx.Template/content/CoreEx.Ai/.template.config/template.json b/src/CoreEx.Template/content/CoreEx.Ai/.template.config/template.json index deddc496..761ce42b 100644 --- a/src/CoreEx.Template/content/CoreEx.Ai/.template.config/template.json +++ b/src/CoreEx.Template/content/CoreEx.Ai/.template.config/template.json @@ -12,13 +12,21 @@ }, "preferNameDirectory": false, "symbols": { + "coreex-version": { + "type": "generated", + "generator": "constant", + "parameters": { + "value": "COREEX_VERSION" + }, + "replaces": "__COREEX_AI_VERSION_TOKEN__" + }, "app-folder": { "type": "parameter", "datatype": "string", "defaultValue": "", "displayName": "Application folder", "description": "The subfolder where the CoreEx application lives, relative to the repo root (for monorepos, e.g. 'backend' or 'services/api'). Leave blank for single-repo projects.", - "replaces": "app-folder" + "replaces": "__COREEX_AI_APP_FOLDER_TOKEN__" }, "hasAppFolder": { "type": "computed", diff --git a/src/CoreEx.Template/content/CoreEx.Bootstrap/.template.config/template.json b/src/CoreEx.Template/content/CoreEx.Bootstrap/.template.config/template.json index 2439e647..23478374 100644 --- a/src/CoreEx.Template/content/CoreEx.Bootstrap/.template.config/template.json +++ b/src/CoreEx.Template/content/CoreEx.Bootstrap/.template.config/template.json @@ -11,15 +11,5 @@ "type": "project" }, "sourceName": "app-name", - "preferNameDirectory": false, - "symbols": { - "coreex-version": { - "type": "generated", - "generator": "constant", - "parameters": { - "value": "COREEX_VERSION" - }, - "replaces": "coreex-version" - } - } + "preferNameDirectory": false } \ No newline at end of file diff --git a/tools/validate-template-pack.ps1 b/tools/validate-template-pack.ps1 index 867b8eb5..cf3ee9be 100644 --- a/tools/validate-template-pack.ps1 +++ b/tools/validate-template-pack.ps1 @@ -70,18 +70,19 @@ $testScenarios = @( ".github/prompts/coreex-scaffold.prompt.md" ".github/agents/coreex-expert.agent.md" ".github/skills/coreex-docs-sync/SKILL.md" + ".github/skills/coreex-solution-scaffolder/SKILL.md" + ".github/docs/coreex/manifest.txt" ".claude/commands/coreex-expert.md" ".claude/commands/coreex-docs-sync.md" ) FilesAbsent = @( ".github/copilot-instructions.md" - ".github/docs" - ".github/skills/solution-scaffolder" ) FileContains = @{ ".github/instructions/coreex.instructions.md" = 'applyTo: "**"' ".github/instructions/coreex-api-controllers.instructions.md" = "applyTo:" ".github/instructions/coreex-validators.instructions.md" = "applyTo:" + ".github/docs/coreex/manifest.txt" = "coreex-version:" } } Build = $false @@ -96,16 +97,18 @@ $testScenarios = @( ".github/instructions/coreex.instructions.md" ".github/instructions/coreex-validators.instructions.md" ".github/skills/coreex-docs-sync/SKILL.md" + ".github/skills/coreex-solution-scaffolder/SKILL.md" + ".github/docs/coreex/manifest.txt" ".claude/commands/coreex-docs-sync.md" ) FilesAbsent = @( ".github/copilot-instructions.md" - ".github/docs" ) FileContains = @{ ".github/instructions/coreex.instructions.md" = 'applyTo: "backend/' ".github/instructions/coreex-validators.instructions.md" = 'applyTo: "backend/' ".github/instructions/coreex-api-controllers.instructions.md" = 'applyTo: "backend/' + ".github/docs/coreex/manifest.txt" = "coreex-version:" } } Build = $false From 760cf37b74b3b370daac585af6300615ed0af885 Mon Sep 17 00:00:00 2001 From: Eric Sibly Date: Thu, 2 Jul 2026 12:45:21 -0700 Subject: [PATCH 03/13] Address Copilot PR review comments - .github/agents/README.md: pin dotnet new install CoreEx.Template to an explicit :: in the adoption instructions (was unpinned, contradicting the version-pin discipline described elsewhere), and stop describing /coreex-docs-sync as a required first-time population step now that dotnet new coreex-ai already installs .github/docs/coreex/ at the pinned version -- docs-sync is a refresh mechanism for later version bumps only. - .github/skills/coreex-docs-sync/SKILL.md and .claude/commands/coreex-docs-sync.md: fix an inaccurate dry-run claim. Empirically verified that omitting --force is NOT always non-mutating: dotnet new coreex-ai without --force only blocks and lists conflicts when target files already exist and differ; if none of the target files exist yet, it silently creates them for real with no confirmation. Switched Step 4 to use --dry-run explicitly, which is verified to never write regardless of target state, and updated the corresponding guardrail text. - src/CoreEx.Template/CoreEx.Template.csproj: AI-WORKFLOWS.md is linked from many shipped skills (/.github/AI-WORKFLOWS.md) but was not actually packaged into either CoreEx.Bootstrap or CoreEx.Ai -- a real broken-link gap. Added it to both the Bootstrap _AiFile list and the Ai Copy block, added the dual-audience do-not-hand-edit header to the file itself (it now ships to consumers), and updated the packaging comment inventory. - tools/validate-template-pack.ps1: added FilesPresent assertions for .github/AI-WORKFLOWS.md in both coreex-ai scenarios to guard against regressing this packaging gap. Verified via tools/validate-template-pack.ps1 (10/10 scenarios pass) and a manual coreex-bootstrap generation confirming AI-WORKFLOWS.md ships there too. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .claude/commands/coreex-docs-sync.md | 4 ++-- .github/AI-WORKFLOWS.md | 8 ++++++++ .github/agents/README.md | 10 ++++++---- .github/skills/coreex-docs-sync/SKILL.md | 4 ++-- src/CoreEx.Template/CoreEx.Template.csproj | 6 ++++-- tools/validate-template-pack.ps1 | 2 ++ 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/.claude/commands/coreex-docs-sync.md b/.claude/commands/coreex-docs-sync.md index b9dee34e..aed18231 100644 --- a/.claude/commands/coreex-docs-sync.md +++ b/.claude/commands/coreex-docs-sync.md @@ -45,10 +45,10 @@ Never install a bare `dotnet new install CoreEx.Template` — it silently resolv Run (add `--app-folder ` if this is a monorepo, per the recorded app folder): ```bash -dotnet new coreex-ai +dotnet new coreex-ai --dry-run ``` -**Without** `--force`. This is a dry run — it prints the "Overwrite" list of every file that would change and applies nothing. This is the only point at which the pending change set is visible; show it to the user. +Always include `--dry-run` explicitly — it is the only invocation guaranteed never to write anything. Omitting `--force` alone is **not** equivalent: it blocks and lists "Overwrite" conflicts only when existing files would change, but if none of the target files exist yet (e.g. a repo missing most of the bundle) it creates them for real with no confirmation. `--dry-run` prints the "Create"/"Overwrite" list of every file that would change and always applies nothing; show it to the user before proceeding. ## Step 5 — Confirm, then apply diff --git a/.github/AI-WORKFLOWS.md b/.github/AI-WORKFLOWS.md index 9f44efb8..683ae972 100644 --- a/.github/AI-WORKFLOWS.md +++ b/.github/AI-WORKFLOWS.md @@ -1,3 +1,11 @@ + + # AI Workflow Set This folder contains the AI artefacts that give GitHub Copilot and Claude Code authoritative knowledge of CoreEx patterns, conventions, and architecture. They can be used directly in the CoreEx repository or copied into a consuming project. diff --git a/.github/agents/README.md b/.github/agents/README.md index 34d730fb..9d69815b 100644 --- a/.github/agents/README.md +++ b/.github/agents/README.md @@ -103,9 +103,11 @@ An earlier design synced only the packages the project already references. This Do not copy files by hand. The whole AI workflow set — including this agent — is installed by the `coreex-ai` template. From the **repo root** of the project that references CoreEx NuGet packages: +Resolve the version to pin first — **never run a bare `dotnet new install CoreEx.Template`** (it silently resolves to whatever is latest at that moment, which can desync the AI assets from the project's actual CoreEx version — see [AGENTS.md](../../AGENTS.md), Step 3, for the full resolution rule): + ```bash -# Install the template pack once (skip if already installed) -dotnet new install CoreEx.Template +# Install the pinned template version (skip if already installed at that version) +dotnet new install CoreEx.Template:: # Single-repo project: dotnet new coreex-ai @@ -121,8 +123,8 @@ dotnet new coreex-ai --app-folder - `.github/skills/` — the full skill suite (`coreex-docs-sync`, `acquire-codebase-knowledge`, `coreex-solution-scaffolder`, `aspire`, and the 14 L1 skills) - `.github/agents/coreex-expert.agent.md` — this agent - `.claude/commands/` — the Claude Code equivalents -- `.github/docs/coreex/` — the local docs cache (architecture docs + per-package guides) the expert reads first +- `.github/docs/coreex/` — the local docs cache (architecture docs + per-package guides) the expert reads first, already populated at the pinned version — no separate sync step needed on first install -After install, run `/coreex-docs-sync` once to populate the docs cache, then re-run it whenever the CoreEx NuGet version is bumped — the agent recommends this automatically when the cached `coreex-version` no longer matches the project (see the resolution flowchart above). +Later, when the project's `CoreEx` NuGet version is bumped, re-run `/coreex-docs-sync` to refresh the whole bundle (instructions, skills, prompts, this agent, and the docs cache) to the matching `CoreEx.Template` release — the agent recommends this automatically when the cached `coreex-version` no longer matches the project (see the resolution flowchart above). For deterministic project scaffolding, use the [CoreEx.Template](../src/CoreEx.Template/README.md) `dotnet new coreex*` template pack rather than an agent skill. diff --git a/.github/skills/coreex-docs-sync/SKILL.md b/.github/skills/coreex-docs-sync/SKILL.md index 4dc84993..3f72209c 100644 --- a/.github/skills/coreex-docs-sync/SKILL.md +++ b/.github/skills/coreex-docs-sync/SKILL.md @@ -34,7 +34,7 @@ Keeps `.github/instructions/`, `.github/skills/`, `.github/prompts/`, `.github/a 1. **Detects** the `CoreEx` NuGet version this project references, from `Directory.Packages.props`, `*.csproj`, or `Directory.Build.props`. This is the target version — `CoreEx.Template` and `CoreEx` are released from the same repo at the same version number, so the two always match. 2. **Reads** `.github/docs/coreex/manifest.txt` (if present) for the currently-installed `coreex-version`. If it already equals the detected target, **report already in sync and stop** — nothing to do. 3. **Installs the pinned template version explicitly** — `dotnet new install CoreEx.Template::` — never a bare `dotnet new install CoreEx.Template` (which silently resolves to latest and can desync the AI assets from the project's actual CoreEx version). -4. **Dry-runs first.** Runs `dotnet new coreex-ai` (and `--app-folder ` if this is a monorepo, per the recorded app folder) **without** `--force` and shows the "Overwrite" file list it reports — this is the only point at which the pending change set is visible; review it before proceeding. +4. **Dry-runs first.** Runs `dotnet new coreex-ai --dry-run` (and `--app-folder ` if this is a monorepo, per the recorded app folder) and shows the "Create"/"Overwrite" file list it reports — `--dry-run` never writes anything, so this is always safe to run first, review it before proceeding. Do **not** rely on omitting `--force` alone: when none of the target files exist yet (e.g. a repo missing most of the bundle), `dotnet new coreex-ai` without `--force` and without `--dry-run` still writes every file for real — it only blocks and lists conflicts when files it would touch already exist and differ. 5. **Confirms, then applies** — re-runs with `--force`. Note: `--force` overwrites every file the template currently emits; it does **not** delete files the template no longer emits (see [Guardrails](#guardrails)). 6. **Flags possible orphans** — compares the skill folder names under `.github/skills/coreex-*/` against the current known L1 skill set (listed in [`AI-WORKFLOWS.md`](/.github/AI-WORKFLOWS.md)); any extra folder is very likely left over from a prior version and should be reviewed for manual removal, not assumed safe. 7. **Reports** the refreshed `coreex-version`, the files changed (from the step-4 dry run), and any flagged orphans. @@ -43,7 +43,7 @@ Keeps `.github/instructions/`, `.github/skills/`, `.github/prompts/`, `.github/a - **Never install an unpinned/latest template version.** Every `dotnet new install CoreEx.Template` must carry an explicit `::` matching the project's referenced `CoreEx` NuGet version. A bare install is how the two mechanisms (code and AI assets) end up on different releases. - **`--force` is scoped, not destructive, but also not a cleanup tool.** It only overwrites files the template's *current* version emits — verified empirically: unrelated repo content and files outside the template's manifest are untouched. It does **not** prune files an *older* version emitted that the current version no longer does (e.g. a renamed or retired skill) — those are true orphans and need manual review/removal. -- **Always dry-run before `--force`.** The no-`--force` invocation is the only place the pending "Overwrite" list is printed; running `--force` straight away gives no visibility into what actually changed. +- **Always dry-run before `--force`.** Use `dotnet new coreex-ai --dry-run` — it is the only invocation guaranteed never to write anything, regardless of whether the target files already exist. Omitting `--force` (without `--dry-run`) is not equivalent: it blocks and lists conflicts only when existing files would change, but it still creates any missing files for real with no confirmation — so it is not safe to treat as a preview on its own. - **Hand-edits to template-sourced files do not survive a refresh.** Every file under `.github/instructions/`, `.github/skills/`, `.github/prompts/`, `.github/agents/coreex-expert.agent.md`, and `.claude/commands/` is regenerated by this process — see the header on each file. - **Never run this inside the CoreEx framework repository itself.** diff --git a/src/CoreEx.Template/CoreEx.Template.csproj b/src/CoreEx.Template/CoreEx.Template.csproj index faeb209f..496274b5 100644 --- a/src/CoreEx.Template/CoreEx.Template.csproj +++ b/src/CoreEx.Template/CoreEx.Template.csproj @@ -103,6 +103,7 @@ Files sourced for Bootstrap/Ai: - consumer-instructions/.github/copilot-instructions.md → .github/copilot-instructions.md (Bootstrap only) - consumer-instructions/.github/copilot-instructions.md → .github/instructions/coreex.instructions.md (Ai only, generated at pack time with frontmatter prepended) + - .github/AI-WORKFLOWS.md → .github/AI-WORKFLOWS.md (Bootstrap and Ai) - .github/prompts/coreex-scaffold.prompt.md → .github/prompts/coreex-scaffold.prompt.md - .github/skills/coreex-solution-scaffolder/* → .github/skills/coreex-solution-scaffolder/ (Bootstrap and Ai) - .github/skills/coreex-docs-sync/* → .github/skills/coreex-docs-sync/ (Bootstrap and Ai) @@ -128,6 +129,7 @@ <_AiFile Include="$(_RepoRoot)consumer-instructions/.github/copilot-instructions.md" DestSubPath=".github/copilot-instructions.md" /> + <_AiFile Include="$(_RepoRoot).github/AI-WORKFLOWS.md" DestSubPath=".github/AI-WORKFLOWS.md" /> <_AiFile Include="$(_RepoRoot).github/prompts/coreex-scaffold.prompt.md" DestSubPath=".github/prompts/coreex-scaffold.prompt.md" /> <_AiFile Include="$(_RepoRoot).github/prompts/coreex-db-migration.prompt.md" DestSubPath=".github/prompts/coreex-db-migration.prompt.md" /> <_AiFile Include="$(_RepoRoot).github/prompts/coreex-refdata.prompt.md" DestSubPath=".github/prompts/coreex-refdata.prompt.md" /> @@ -248,8 +250,8 @@ - <_AiFile Include="$(_RepoRoot)consumer-instructions/.github/copilot-instructions.md" DestSubPath=".github/copilot-instructions.md" /> - <_AiFile Include="$(_RepoRoot).github/AI-WORKFLOWS.md" DestSubPath=".github/AI-WORKFLOWS.md" /> + <_AiFile Include="$(_RepoRoot).github/coreex-ai-workflows.md" DestSubPath=".github/coreex-ai-workflows.md" /> <_AiFile Include="$(_RepoRoot).github/prompts/coreex-scaffold.prompt.md" DestSubPath=".github/prompts/coreex-scaffold.prompt.md" /> <_AiFile Include="$(_RepoRoot).github/prompts/coreex-db-migration.prompt.md" DestSubPath=".github/prompts/coreex-db-migration.prompt.md" /> <_AiFile Include="$(_RepoRoot).github/prompts/coreex-refdata.prompt.md" DestSubPath=".github/prompts/coreex-refdata.prompt.md" /> @@ -250,8 +250,8 @@ - - + - + - + - + - - + + - - + + @@ -77,14 +77,14 @@ - + - + - + From 622a66ff26f67a5ae48dcc0f39d6738c30d9fbd5 Mon Sep 17 00:00:00 2001 From: Eric Sibly Date: Mon, 6 Jul 2026 11:02:31 -0700 Subject: [PATCH 13/13] Address round 5 Copilot PR review comments (3 threads) - Strengthen serialization round-trip test assertion with WithStrictOrdering() so it actually verifies GetItems()'s documented sorted/stable contract, not just multiset equivalence. - Document (not change) the deliberate coreex-* scope of the Step 6 orphan check in coreex-docs-sync SKILL.md and the Claude command: CoreEx.Template only ever installs coreex-*-prefixed skill folders, so scanning all of .github/skills/*/ would falsely flag a consumer's own unrelated skills (e.g. a personal aspire or acquire-codebase-knowledge skill) as CoreEx orphans. Verified via full rebuild -> pack -> install -> validate cycle (10/10) and a targeted dotnet test run (net8/9/10, all green). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .claude/commands/coreex-docs-sync.md | 2 ++ .github/skills/coreex-docs-sync/SKILL.md | 2 +- .../CoreEx.RefData.Test.Unit/ReferenceDataOrchestratorTests.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.claude/commands/coreex-docs-sync.md b/.claude/commands/coreex-docs-sync.md index d0fdac52..64cefe48 100644 --- a/.claude/commands/coreex-docs-sync.md +++ b/.claude/commands/coreex-docs-sync.md @@ -64,6 +64,8 @@ dotnet new coreex-ai --force Compare the skill folder names under `.github/skills/coreex-*/` against the current known L1 skill set (listed in [`coreex-ai-workflows.md`](/.github/coreex-ai-workflows.md)). Any extra folder is very likely left over from a prior template version and should be flagged for manual review, not assumed safe to delete automatically. +Scoped to the `coreex-` prefix deliberately, not all of `.github/skills/*/`: `CoreEx.Template` only ever installs `coreex-`-prefixed skill folders, and a consumer repo may have other skills installed that this template neither writes nor owns (a personal skill, one from another tool, etc.). Comparing the full folder set would falsely flag those as CoreEx orphans. + ## Step 7 — Report Summarise: diff --git a/.github/skills/coreex-docs-sync/SKILL.md b/.github/skills/coreex-docs-sync/SKILL.md index 471db762..f9137884 100644 --- a/.github/skills/coreex-docs-sync/SKILL.md +++ b/.github/skills/coreex-docs-sync/SKILL.md @@ -36,7 +36,7 @@ Keeps `.github/instructions/`, `.github/skills/`, `.github/prompts/`, `.github/a 3. **Installs the pinned template version explicitly** — `dotnet new install CoreEx.Template::` — never a bare `dotnet new install CoreEx.Template` (which silently resolves to latest and can desync the AI assets from the project's actual CoreEx version). 4. **Dry-runs first.** Runs `dotnet new coreex-ai --dry-run` (and `--app-folder ` if this is a monorepo, per the recorded app folder) and shows the "Create"/"Overwrite" file list it reports — `--dry-run` never writes anything, so this is always safe to run first, review it before proceeding. Do **not** rely on omitting `--force` alone: when none of the target files exist yet (e.g. a repo missing most of the bundle), `dotnet new coreex-ai` without `--force` and without `--dry-run` still writes every file for real — it only blocks and lists conflicts when files it would touch already exist and differ. 5. **Confirms, then applies** — re-runs with `--force`. Note: `--force` overwrites every file the template currently emits; it does **not** delete files the template no longer emits (see [Guardrails](#guardrails)). -6. **Flags possible orphans** — compares the skill folder names under `.github/skills/coreex-*/` against the current known L1 skill set (listed in [`coreex-ai-workflows.md`](/.github/coreex-ai-workflows.md)); any extra folder is very likely left over from a prior version and should be reviewed for manual removal, not assumed safe. +6. **Flags possible orphans** — compares the skill folder names under `.github/skills/coreex-*/` against the current known L1 skill set (listed in [`coreex-ai-workflows.md`](/.github/coreex-ai-workflows.md)); any extra folder is very likely left over from a prior version and should be reviewed for manual removal, not assumed safe. Scoped to the `coreex-` prefix deliberately: `CoreEx.Template` only ever installs `coreex-`-prefixed skill folders, and a consumer repo may have other, unrelated skills installed (its own or from other tooling) that this refresh neither writes nor owns — comparing *all* of `.github/skills/*/` would falsely flag those as CoreEx orphans. 7. **Reports** the refreshed `coreex-version`, the files changed (from the step-4 dry run), and any flagged orphans. ## Guardrails diff --git a/tests/CoreEx.RefData.Test.Unit/ReferenceDataOrchestratorTests.cs b/tests/CoreEx.RefData.Test.Unit/ReferenceDataOrchestratorTests.cs index 0a426709..363db991 100644 --- a/tests/CoreEx.RefData.Test.Unit/ReferenceDataOrchestratorTests.cs +++ b/tests/CoreEx.RefData.Test.Unit/ReferenceDataOrchestratorTests.cs @@ -427,7 +427,7 @@ public void ReferenceDataCollection_Serialization_RoundTrip2() // differently per process (string hash randomization), so the enumerated/serialized order flips between runs. Assert // via GetItems(), which returns a stable, sorted List, rather than BeEquivalentTo on the collection directly - // the latter enumerates via ICollection.CopyTo, which this collection explicitly does not support. - deserialColl!.GetItems().Should().BeEquivalentTo(((DummyRefData2Collection)coll!).GetItems()); + deserialColl!.GetItems().Should().BeEquivalentTo(((DummyRefData2Collection)coll!).GetItems(), o => o.WithStrictOrdering()); deserialColl!.Should().Contain(x => x.ParentSid == "P"); }