Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/packages/compono-tunit.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ composer's own `Create<T>()`).

## 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.
Expand Down
2 changes: 1 addition & 1 deletion docs/packages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ need the first two.
| [`Compono.XunitV3`](compono-xunitv3.md) | `[Compose]`/`[Compose<TProfile>]`/`[Compose<TProfile, TConfig>]` 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<T>` 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<TProfile>]`/`[Compose<TProfile, TConfig>]` 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 —
Expand Down
65 changes: 52 additions & 13 deletions docs/plans/0040-compono-tunit-package-design.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# [PLAN-0040] Compono.TUnit Package Design

**Status:** In Progress
**Status:** Done
Comment thread
ncipollina marked this conversation as resolved.

**Implements:** [ADR-0040](../adr/0040-compono-tunit-package-design.md)
(`Compono.TUnit` package: method-parameter composition only, no new
Expand Down Expand Up @@ -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<TProfile>]` 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<TProfile>]`
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

Expand Down Expand Up @@ -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.**
2 changes: 1 addition & 1 deletion docs/plans/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
57 changes: 21 additions & 36 deletions docs/roadmap/future-packages.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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)

Expand Down
15 changes: 14 additions & 1 deletion skills/compono-evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,19 @@
"Correctly distinguishes 'not shipped' from 'impossible' \u2014 core Compono usable directly in any test framework via Composer.Create<T>()",
"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<TProfile>] 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<TProfile>] (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"
]
}
]
}
}
17 changes: 9 additions & 8 deletions skills/compono/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ some packages and not others.
|---|---|---|---|
| `<PackageReference Include="Compono"` | any `.csproj` in the project | Definitive | Core Compono in use |
| `<PackageReference Include="Compono.XunitV3"` | `.csproj` | Definitive | `[Compose]`/`[Compose<TProfile>]`/`[Compose<TProfile, TConfig>]`/`[Shared]` available — load `references/xunit-v3.md` |
| `<PackageReference Include="Compono.TUnit"` | `.csproj` | Definitive | `[Compose]`/`[Shared]` available (method-parameter composition only so far - no `[Compose<TProfile>]` yet) — load `references/tunit.md` |
| `<PackageReference Include="Compono.TUnit"` | `.csproj` | Definitive | `[Compose]`/`[Compose<TProfile>]`/`[Compose<TProfile, TConfig>]`/`[Shared]` available — load `references/tunit.md` |
Comment thread
ncipollina marked this conversation as resolved.
| `<PackageReference Include="Compono.NSubstitute"` | `.csproj` | Definitive | `UseNSubstitute()` available — load `references/nsubstitute.md` |
| `<PackageReference Include="Compono.Bogus"` | `.csproj` | Definitive | `UseBogus()`/`UseBogus<T>()` available — load `references/bogus.md` |
| `Composer.Create(`, `.Create<`, `.CreateMany<`, `CompositionBuilder` | `*.cs` | High | Core Compono API in active use |
Expand Down Expand Up @@ -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<TProfile, TConfig>]`, if that package is referenced — see
`references/xunit-v3.md`. Prefer an enum/`typeof(...)` over a bare
top-level test parameter) → `[Compose<TProfile, TConfig>]` (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
Expand Down Expand Up @@ -185,9 +186,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<TProfile>]`/`[Compose<TProfile, TConfig>]`
see `references/tunit.md`) — there is no `Compono.NUnit`,
(`Compono.TUnit` ships the full attribute family —
`[Compose]`/`[Compose<TProfile>]`/`[Compose<TProfile, TConfig>]`/`[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
Expand Down Expand Up @@ -248,7 +249,7 @@ Load only what the Detection table says is relevant to the current task.
| `references/registrations-profiles-and-scopes.md` | Using `Register<T>()`, `.For<T>().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<TProfile>]`/`[Compose<TProfile, TConfig>]`/`[Shared]` theory work |
| `references/tunit.md` | `Compono.TUnit` is referenced — `[Compose]`/`[Shared]` test-method work |
| `references/tunit.md` | `Compono.TUnit` is referenced — `[Compose]`/`[Compose<TProfile>]`/`[Compose<TProfile, TConfig>]`/`[Shared]` test-method work |
| `references/nsubstitute.md` | `Compono.NSubstitute` is referenced — `UseNSubstitute()` work |
| `references/bogus.md` | `Compono.Bogus` is referenced — `UseBogus()`/`UseBogus<T>()` work |
| `references/patterns-and-antipatterns.md` | Reviewing existing Compono usage for correctness, migrating from AutoFixture, or unsure whether an approach is idiomatic |
6 changes: 3 additions & 3 deletions skills/compono/references/tunit.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<TProfile>]`, and
`[Compose<TProfile, TConfig>]`, method-parameter-only — see ADR-0040 for
the full design.
The full attribute family has shipped: `[Compose]`, `[Compose<TProfile>]`,
and `[Compose<TProfile, TConfig>]`, method-parameter-only — see ADR-0040
for the full design.

## `[Compose]`

Expand Down