From e4b64e4ce4f4f24904327e996ba4221ebbf724c9 Mon Sep 17 00:00:00 2001 From: Nick Cipollina Date: Wed, 12 Aug 2026 13:41:26 -0400 Subject: [PATCH 1/2] docs(tunit): close out PLAN-0040 (Phase 3 docs/skill consistency pass) Fixes four stale references to Compono.TUnit's old Phase-0-only [Compose]/[Shared] shape (docs/packages/index.md, docs/packages/compono-tunit.md, skills/compono/SKILL.md, skills/compono/references/tunit.md) now that the full attribute family has shipped. Moves Compono.TUnit out of docs/roadmap/future-packages.md's roadmap-item section into shipped-package language, matching docs/packages/index.md's five-package count. Adds a routing eval (skills/compono-evals/evals.json) confirming TUnit's own [Test]/[Compose] shape is used only when Compono.TUnit is referenced, not Compono.XunitV3's [Theory] syntax. PLAN-0040 is now fully complete - all four phases done. Co-Authored-By: Claude Sonnet 5 --- docs/packages/compono-tunit.md | 2 +- docs/packages/index.md | 2 +- .../0040-compono-tunit-package-design.md | 65 +++++++++++++++---- docs/roadmap/future-packages.md | 57 ++++++---------- skills/compono-evals/evals.json | 15 ++++- skills/compono/SKILL.md | 10 +-- skills/compono/references/tunit.md | 6 +- 7 files changed, 97 insertions(+), 60 deletions(-) diff --git a/docs/packages/compono-tunit.md b/docs/packages/compono-tunit.md index 529ebb2..9438ebd 100644 --- a/docs/packages/compono-tunit.md +++ b/docs/packages/compono-tunit.md @@ -21,7 +21,7 @@ composer's own `Create()`). ## What it gives you (today) -PLAN-0040 Phase 0/1 have shipped — see +The full attribute family has shipped — see [ADR-0040](../adr/0040-compono-tunit-package-design.md) for the full design and [PLAN-0040](../plans/0040-compono-tunit-package-design.md) for phase status. diff --git a/docs/packages/index.md b/docs/packages/index.md index 634f973..bee90fd 100644 --- a/docs/packages/index.md +++ b/docs/packages/index.md @@ -10,7 +10,7 @@ need the first two. | [`Compono.XunitV3`](compono-xunitv3.md) | `[Compose]`/`[Compose]`/`[Compose]` theory data attributes and `[Shared]` parameter sharing for xUnit v3. | You write xUnit v3 tests and want composed theory parameters instead of hand-built test data. | | [`Compono.NSubstitute`](compono-nsubstitute.md) | Automatic substitute composition for interface, delegate, and (optionally) abstract-class parameters. | Your composed types depend on interfaces you'd otherwise stub by hand with NSubstitute. | | [`Compono.Bogus`](compono-bogus.md) | Realistic fake data — member-name-convention matching plus explicit `Faker` sugar. | You want `FullName`/`Email`/`StreetAddress`-shaped fields to look like real data instead of anonymous strings. | -| [`Compono.TUnit`](compono-tunit.md) | `[Compose]` data source attribute and `[Shared]` parameter sharing for TUnit. | You write TUnit tests and want composed method parameters instead of hand-built data sources. | +| [`Compono.TUnit`](compono-tunit.md) | `[Compose]`/`[Compose]`/`[Compose]` data source attributes and `[Shared]` parameter sharing for TUnit. | You write TUnit tests and want composed method parameters instead of hand-built data sources. | Every package targets `net8.0`/`net9.0`/`net10.0`/`net11.0` and, until the first stable `1.0` release, publishes as a `0.x.y-preview.N` prerelease — diff --git a/docs/plans/0040-compono-tunit-package-design.md b/docs/plans/0040-compono-tunit-package-design.md index 388dc89..fb4c4a6 100644 --- a/docs/plans/0040-compono-tunit-package-design.md +++ b/docs/plans/0040-compono-tunit-package-design.md @@ -1,6 +1,6 @@ # [PLAN-0040] Compono.TUnit Package Design -**Status:** In Progress +**Status:** Done **Implements:** [ADR-0040](../adr/0040-compono-tunit-package-design.md) (`Compono.TUnit` package: method-parameter composition only, no new @@ -543,23 +543,43 @@ Each phase ships as its own PR, per `design-decisions.md`'s phase rule. ### Phase 3: Docs and skill consistency close-out -**Status:** Not Started +**Status:** Done -- [ ] Re-read `docs/packages/compono-tunit.md`, `docs/packages/index.md`, +- [x] Re-read `docs/packages/compono-tunit.md`, `docs/packages/index.md`, `skills/compono/references/tunit.md`, and `SKILL.md`'s Detection table/guardrail/description end to end — confirm nothing Phase 0/1 added is inconsistent or stale now that the full package exists (a pure consistency pass; Phase 0/1 already did the substantive writing - per-behavior). -- [ ] `docs/roadmap/future-packages.md`: move `Compono.TUnit` out of - "Roadmap items" — it's shipped, not a roadmap item anymore. -- [ ] `skills/compono-evals/evals.json`: retire or rewrite eval scenario - 20 (`Does Compono support NUnit?` — currently uses `Compono.TUnit` - as an example of a package that doesn't exist; check it doesn't - accidentally still assert that once `Compono.TUnit` ships) and add a - routing scenario confirming the skill only recommends - `Compono.TUnit` guidance when that package is referenced, matching - the existing NSubstitute/Bogus routing scenarios' shape. + per-behavior). Found and fixed four stale spots, all pre-dating + Phase 1/2 shipping: `compono-tunit.md`'s and `tunit.md`'s own + "PLAN-0040 Phase 0/1 have shipped" intro lines (now describe the + full family, not a phase number); `docs/packages/index.md`'s + `Compono.TUnit` row (still said "`[Compose]` data source attribute + ... " only, no profile variants, unlike the `Compono.XunitV3` row + immediately above it); `SKILL.md`'s Detection table row, its "Never + claim or write code against..." guardrail, and its + `references/tunit.md` file-index row (all three still said + `Compono.TUnit` ships only `[Compose]`/`[Shared]`, "not + `[Compose]` yet" — stale since Phase 1 merged). +- [x] `docs/roadmap/future-packages.md`: moved `Compono.TUnit` out of + "Roadmap items" — it's shipped, not a roadmap item anymore. Reworded + the intro (five shipped packages, not four-plus-one-committed), the + Admission model section's `Compono.TUnit` paragraph (now describes + the full admitted-candidate → roadmap-item → committed → + **shipped-package** progression, past tense), and emptied the + "Roadmap items" section itself with a pointer to + [Package Guides](../packages/index.md). +- [x] `skills/compono-evals/evals.json`: eval 20 (`Does Compono support + NUnit?`) was checked directly — it's about `Compono.NUnit`, not + `Compono.TUnit`, and doesn't use `Compono.TUnit` as an example of a + nonexistent package (that concern must have been addressed earlier + than this phase; nothing to retire/rewrite there). Added eval 21 (a + new `routing` scenario): a project referencing `Compono`, + `Compono.TUnit`, and `Compono.NSubstitute` but *not* + `Compono.XunitV3`, expecting TUnit's own `[Test]`/`[Compose]` + shape (not xUnit v3's `[Theory]`) with `UseNSubstitute()`/`[Shared]` + — mirrors eval 3's (`Compono.NSubstitute` routing) and eval 18's + (negative-routing: a package NOT referenced) shape. ## Critical Files @@ -940,3 +960,22 @@ No doc updates in this phase - Phase 3 is the docs/skill closing consistency pass, not this one. Phase 2 is complete - every task checked off. + +**Phase 3 implementation (2026-08-12)**: PR #79 (Phase 2) merged to `main` +first, per this plan's own phase-PR rule. A pure consistency pass, no +`src/Compono.TUnit` code changes - four stale-doc spots found by direct +rereading (not assumed), all pre-dating Phase 1/2's own shipping, listed +in Phase 3's own task checkboxes above. `docs/roadmap/future-packages.md` +got the larger rewrite the plan's task called for: `Compono.TUnit` moved +from "committed implementation work" language to past-tense "shipped +package" language throughout (intro, Admission model section, and the +"Roadmap items" section itself, now empty with a pointer to Package +Guides) - the five-package count (not four-plus-one) now matches +`docs/packages/index.md`'s own count exactly. `skills/compono-evals +/evals.json` eval 20 turned out already correct (it's an NUnit scenario, +not a TUnit one - the plan's own premise for that half of the task was +stale by the time this phase ran); added eval 21, a `Compono.TUnit` + +`Compono.NSubstitute`-without-`Compono.XunitV3` routing scenario. + +Phase 3 is complete - every task checked off. **PLAN-0040 is complete - +all four phases done, `Compono.TUnit` is a shipped package.** diff --git a/docs/roadmap/future-packages.md b/docs/roadmap/future-packages.md index 1623011..e1ab9f6 100644 --- a/docs/roadmap/future-packages.md +++ b/docs/roadmap/future-packages.md @@ -1,20 +1,18 @@ # Future Packages -Compono's MVP package set is fully shipped (see -[Package Guides](../packages/index.md)): four independently installable -packages — `Compono`, `Compono.XunitV3`, `Compono.NSubstitute`, and -`Compono.Bogus` — plus `Compono.Generators`, which is not a fifth -installable package at all. It's `IsPackable=false` +Compono's shipped package set (see [Package Guides](../packages/index.md)) +is five independently installable packages — `Compono`, `Compono.XunitV3`, +`Compono.NSubstitute`, `Compono.Bogus`, and `Compono.TUnit` — plus +`Compono.Generators`, which is not a sixth installable package at all. It's +`IsPackable=false` ([ADR-0003](../adr/0003-generator-package-distribution.md)) and ships embedded inside `Compono`'s own `.nupkg` as an analyzer -(`analyzers/dotnet/cs`) — a consumer never references it directly, and -it never appears on nuget.org on its own. One additional package — -`Compono.TUnit` — is committed via an `Accepted` ADR -([ADR-0040](../adr/0040-compono-tunit-package-design.md)), with -[PLAN-0040](../plans/0040-compono-tunit-package-design.md) (`In Progress`) -tracking its implementation; see -[Roadmap items](#roadmap-items-cleared-gate-a-and-gate-b) below. No other -candidate on this page has cleared both gates yet. +(`analyzers/dotnet/cs`) — a consumer never references it directly, and it +never appears on nuget.org on its own. `Compono.TUnit` graduated from this +page's roadmap once [PLAN-0040](../plans/0040-compono-tunit-package-design.md) +completed all its phases — see +[`Compono.TUnit`](../packages/compono-tunit.md) for what it ships. No +candidate on this page has cleared both admission gates yet. ## Admission model @@ -34,32 +32,19 @@ item becomes **committed implementation work** only once that ADR itself reaches `Accepted` (its own full design pass, not just the problem statement) and a `Plan` moves `In Progress` against it — the same ADR/Plan mechanics every other change in this repo goes through, per -`docs/adr/README.md`/`docs/plans/README.md`. `Compono.TUnit` (below) is -the one candidate that has reached **roadmap item** status and, as of -[PLAN-0040](../plans/0040-compono-tunit-package-design.md) moving -`In Progress`, committed implementation work too; nothing besides -`Compono.TUnit` is roadmap content at all. +`docs/adr/README.md`/`docs/plans/README.md`. `Compono.TUnit` made that full +progression — admitted candidate, roadmap item, committed implementation +work, and finally a shipped package once +[PLAN-0040](../plans/0040-compono-tunit-package-design.md) completed — and +is documented as a [Package Guide](../packages/compono-tunit.md) now, not +roadmap content; nothing on this page has reached roadmap-item status +since. ## Roadmap items (cleared Gate A and Gate B) -- **`Compono.TUnit`** — cleared Gate A on TUnit's `IDataSourceAttribute` - family (especially `UntypedDataSourceGeneratorAttribute`, which TUnit's - own docs cite AutoFixture-shaped libraries as the motivating case for), - its per-row `TestBuilderContext`, and its combinatorial interplay with - `[Arguments]` — a real integration surface following `Compono.XunitV3`'s - `CompositionRow`-based model - ([ADR-0021](../adr/0021-row-composition-entry-point-for-test-framework-integrations.md)). - Cleared Gate A on that surface specifically, not because TUnit is - source-generated — that architectural kinship is not, on its own, - consumer value; see ADR-0039 for what was retired and why. Cleared - Gate B via an explicit product-owner request (ADR-0039's real-demand - trigger). [ADR-0040](../adr/0040-compono-tunit-package-design.md) - (`Accepted`) records the resulting package design — method-parameter - composition only for the first release, full parity with - `Compono.XunitV3`'s scope; see that ADR for why constructor-dependency - composition was investigated and deferred. - [PLAN-0040](../plans/0040-compono-tunit-package-design.md) tracks - implementation, phase by phase. +None currently. `Compono.TUnit` was the one candidate to reach this +status — see the Admission model note above; it shipped as a package and +moved to [Package Guides](../packages/index.md). ## Admitted candidates (cleared Gate A, no evidence yet) diff --git a/skills/compono-evals/evals.json b/skills/compono-evals/evals.json index e4313c6..a2a1c3d 100644 --- a/skills/compono-evals/evals.json +++ b/skills/compono-evals/evals.json @@ -227,6 +227,19 @@ "Correctly distinguishes 'not shipped' from 'impossible' \u2014 core Compono usable directly in any test framework via Composer.Create()", "Does not silently redirect to Compono.XunitV3 as if it worked in NUnit" ] + }, + { + "id": 21, + "category": "routing", + "prompt": "Write a TUnit test that composes an OrderService and reuses a mocked IOrderRepository across the composed graph, applying an NSubstitute profile. The project references Compono, Compono.TUnit, and Compono.NSubstitute - not Compono.XunitV3.", + "expected_output": "Uses TUnit's own [Test]/[Compose] shape (not xUnit v3's [Theory]/ClassData), with a profile calling UseNSubstitute() and [Shared] on the substitute parameter - because Compono.TUnit and Compono.NSubstitute are referenced, not Compono.XunitV3.", + "files": [], + "expectations": [ + "Uses [Test] + [Compose] (TUnit's own attributes), not [Theory]/xUnit v3 syntax, since Compono.XunitV3 isn't referenced", + "Applies UseNSubstitute() from a profile since Compono.NSubstitute is referenced", + "Uses [Shared] only on the substitute parameter that needs to be asserted against", + "Does not mix Compono.XunitV3-only constructs (e.g. [Theory]) into a TUnit test" + ] } ] -} \ No newline at end of file +} diff --git a/skills/compono/SKILL.md b/skills/compono/SKILL.md index 18f9399..1aae78c 100644 --- a/skills/compono/SKILL.md +++ b/skills/compono/SKILL.md @@ -43,7 +43,7 @@ some packages and not others. |---|---|---|---| | `]`/`[Compose]`/`[Shared]` available — load `references/xunit-v3.md` | -| `]` yet) — load `references/tunit.md` | +| `]`/`[Compose]`/`[Shared]` available — load `references/tunit.md` | | `()` available — load `references/bogus.md` | | `Composer.Create(`, `.Create<`, `.CreateMany<`, `CompositionBuilder` | `*.cs` | High | Core Compono API in active use | @@ -185,9 +185,9 @@ undermines the reason Compono exists in this project. hasn't shipped — but distinguish "no dedicated package" from "no capability."** Only `Compono`, `Compono.XunitV3`, `Compono.TUnit`, `Compono.NSubstitute`, and `Compono.Bogus` ship as packages today - (`Compono.TUnit` ships only `[Compose]`/`[Shared]` method-parameter - composition so far, not `[Compose]`/`[Compose]` - — see `references/tunit.md`) — there is no `Compono.NUnit`, + (`Compono.TUnit` ships the full attribute family — + `[Compose]`/`[Compose]`/`[Compose]`/`[Shared]`, + see `references/tunit.md`) — there is no `Compono.NUnit`, `Compono.MSTest`, `Compono.FakeItEasy`, `Compono.Moq`, or `Compono.DependencyInjection`, and never invent a plausible-looking API for one. That doesn't always mean the underlying capability is @@ -248,7 +248,7 @@ Load only what the Detection table says is relevant to the current task. | `references/registrations-profiles-and-scopes.md` | Using `Register()`, `.For().Use()`/`.Member()`, `ICompositionProfile`, `[Shared]`, or debugging a recursion/registration-conflict error | | `references/diagnostics.md` | A `CMP0001`-`CMP0012` build error, or a runtime `CompositionException` needs diagnosing | | `references/xunit-v3.md` | `Compono.XunitV3` is referenced — `[Compose]`/`[Compose]`/`[Compose]`/`[Shared]` theory work | -| `references/tunit.md` | `Compono.TUnit` is referenced — `[Compose]`/`[Shared]` test-method work | +| `references/tunit.md` | `Compono.TUnit` is referenced — `[Compose]`/`[Compose]`/`[Compose]`/`[Shared]` test-method work | | `references/nsubstitute.md` | `Compono.NSubstitute` is referenced — `UseNSubstitute()` work | | `references/bogus.md` | `Compono.Bogus` is referenced — `UseBogus()`/`UseBogus()` work | | `references/patterns-and-antipatterns.md` | Reviewing existing Compono usage for correctness, migrating from AutoFixture, or unsure whether an approach is idiomatic | diff --git a/skills/compono/references/tunit.md b/skills/compono/references/tunit.md index 9c4d41a..9072971 100644 --- a/skills/compono/references/tunit.md +++ b/skills/compono/references/tunit.md @@ -4,9 +4,9 @@ Only relevant if the project references `Compono.TUnit`. Requires real TUnit (`TUnit`/`TUnit.Core` + Microsoft Testing Platform runner). Depends on `Compono` (the source generator flows through transitively). -PLAN-0040 Phase 0/1 have shipped: `[Compose]`, `[Compose]`, and -`[Compose]`, method-parameter-only — see ADR-0040 for -the full design. +The full attribute family has shipped: `[Compose]`, `[Compose]`, +and `[Compose]`, method-parameter-only — see ADR-0040 +for the full design. ## `[Compose]` From 56634f5caa2f775ea7e7b1721f0055f906f8b146 Mon Sep 17 00:00:00 2001 From: Nick Cipollina Date: Wed, 12 Aug 2026 13:46:38 -0400 Subject: [PATCH 2/2] docs(tunit): fix Codex review findings on PR #80 - docs/plans/README.md: PLAN-0040's index row was still In Progress after the plan itself moved to Done. - skills/compono/SKILL.md: the Default workflow's call-site profile-configuration step routed exclusively to Compono.XunitV3, ignoring Compono.TUnit's identical [Compose] capability - now routes to whichever package the project references. Co-Authored-By: Claude Sonnet 5 --- docs/plans/README.md | 2 +- skills/compono/SKILL.md | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/plans/README.md b/docs/plans/README.md index 6037668..dbe2b5d 100644 --- a/docs/plans/README.md +++ b/docs/plans/README.md @@ -56,5 +56,5 @@ one. This file is just the mechanics: numbering, status, and the index. | [0037](0037-netstandard2.1-compatibility-floor.md) | netstandard2.1 Compatibility Floor | Superseded by PLAN-0038 | | [0038](0038-net8-net9-explicit-multi-target.md) | net8.0/net9.0 Explicit Multi-Target | Done | | [0039](0039-future-extension-package-admission-gate-and-release-sequence.md) | Future Extension Package Admission Gate and Release Sequence | Done | -| [0040](0040-compono-tunit-package-design.md) | Compono.TUnit Package Design | In Progress | +| [0040](0040-compono-tunit-package-design.md) | Compono.TUnit Package Design | Done | | [0041](0041-aot-safe-row-binding-dispatch.md) | AOT-Safe Row-Binding Dispatch | Done | diff --git a/skills/compono/SKILL.md b/skills/compono/SKILL.md index 1aae78c..77b98c6 100644 --- a/skills/compono/SKILL.md +++ b/skills/compono/SKILL.md @@ -99,9 +99,10 @@ user to make test-by-test, not something to do as a drive-by. not a copy-pasted builder lambda in every test. - A value only known at a *specific test's call site* that must influence configuration logic running *inside* a profile (not a - top-level test parameter) → `Compono.XunitV3`'s - `[Compose]`, if that package is referenced — see - `references/xunit-v3.md`. Prefer an enum/`typeof(...)` over a bare + top-level test parameter) → `[Compose]` (in + `Compono.XunitV3` or `Compono.TUnit`, whichever the project + references) — see `references/xunit-v3.md` or `references/tunit.md` + to match. Prefer an enum/`typeof(...)` over a bare string for the argument. Don't confuse this with a `CompositionProviderRequest.Name`-based custom provider (`references/registrations-profiles-and-scopes.md`), which solves a