Bootstrap repository scaffolding and microservices documentation - #1
Conversation
Initial PR for the new xavierjohn/Trellis.Microservices repository. Establishes file conventions, central package management, and the four LLM-discoverability docs (cookbook + 3 package references) plus the .github/copilot-instructions.md agent guardrails. No .cs yet — source for the three packages lands in follow-up PRs B and C. Layout mirrors xavierjohn/Trellis conventions: TreatWarningsAsErrors, central package management, BOM-enforced .editorconfig, Nerdbank.GitVersioning, TrellisApiRefName auto-pack mechanism, doc lint script. Doc-lint validation: api-reference lint script passes against all 4 new reference docs after fixing 11 bare-cross-doc-link warnings (added #use-this-file-when anchors) and 4 broken-anchor warnings (added allow-broken-anchor markers for anchors that resolve once Recipe 1/2 bodies are inlined in PR C). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Bootstraps the new Trellis.Microservices repository with baseline build/scaffolding assets and the initial set of LLM-discoverability / API-reference docs that define the repo’s intended microservices/JWT trust-boundary surface before any C# source lands.
Changes:
- Adds repo-wide .NET build configuration (Directory.Build.* + central package versions) and versioning config (Nerdbank.GitVersioning).
- Introduces DocFX-style API reference markdown + a PowerShell lint gate for cross-doc link/anchor hygiene.
- Adds contribution/agent guidance (
.github/copilot-instructions.md) and standard repo scaffolding (editorconfig/gitattributes/gitignore, solution items, dependabot, etc.).
Reviewed changes
Copilot reviewed 21 out of 23 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
version.json |
Initializes Nerdbank.GitVersioning configuration for prerelease versioning. |
Trellis.Microservices.slnx |
Adds a solution-items-only .slnx referencing scaffolding + docs. |
README.md |
Replaces stub readme with repo purpose, package inventory, and doc entry points. |
nuget.config |
Sets NuGet source configuration (nuget.org only). |
global.json |
Pins SDK and test runner settings for the repo. |
docs/lint-api-reference.ps1 |
Adds API-reference markdown lint script (anchors/cross-doc link rules). |
docs/lint-api-reference.md |
Documents the lint rules and intended usage. |
docs/docfx_project/api_reference/trellis-api-yarp.md |
Adds/migrates gateway-side YARP/internal-JWT contract reference. |
docs/docfx_project/api_reference/trellis-api-microservices-cookbook.md |
Adds the cookbook entry point + recipe placeholders and routing tables. |
docs/docfx_project/api_reference/trellis-api-microservices-abstractions.md |
Adds the abstractions/claim-constants contract reference. |
docs/docfx_project/api_reference/trellis-api-internal-jwt.md |
Adds consumer-side internal-JWT actor provider/options reference + migration note. |
Directory.Packages.props |
Introduces central package version pinning for runtime + test dependencies. |
Directory.Build.targets |
Adds packing conventions for API refs + optional lint target hook. |
Directory.Build.props |
Establishes repo-wide build properties, packaging metadata, analyzers, etc. |
CHANGELOG.md |
Adds initial changelog describing repo bootstrap contents. |
build/Trellis.ApiReference.targets |
Adds MSBuild target to sync packaged API refs into .github/ for Copilot. |
build/test.props |
Adds shared test project props (MTP runner, package refs, analyzers off). |
.gitignore |
Replaces stub/updates ignore rules and adds Trellis-specific generated paths. |
.github/dependabot.yml |
Enables weekly Dependabot updates for Actions + NuGet. |
.github/copilot-instructions.md |
Adds agent/contribution guidance + “P4 invariants — never regress” checklist. |
.gitattributes |
Configures text/binary handling and encoding declarations. |
.editorconfig |
Adds repo-wide formatting/style rules and UTF-8 BOM enforcement. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| | `using Trellis.Asp.Authorization;` (for `TrellisInternalJwt*`) | `using Trellis.Microservices.AspNetCore;` | | ||
| | `Trellis.Asp` NuGet package | `Trellis.Microservices.AspNetCore` NuGet package (add reference) + still keep `Trellis.Asp` (for everything else) | | ||
| | `services.AddTrellisInternalJwtActorProvider(...)` | unchanged (extension method is `IServiceCollection`-scoped, namespace move only) | | ||
| | `builder.AddTrellis(b => b.UseTrellisInternalJwtActor(...))` | `services.AddTrellis(b => b.UseTrellisInternalJwtActor(...))` — `AddTrellis` is an `IServiceCollection` extension. The composition-root slot (`TrellisServiceBuilder.UseTrellisInternalJwtActor`) remains on `Trellis.ServiceDefaults.TrellisServiceBuilder` in the upstream `xavierjohn/Trellis` repo; this package supplies the underlying `TrellisInternalJwtActorProvider`. Slot signature unchanged. | |
There was a problem hiding this comment.
Fixed in b822902. The Old column now correctly shows the actual pre-carveout form (services.AddTrellis(b => b.UseTrellisInternalJwtActor(...))) and notes the call site is unchanged — the only migration step is adding the Trellis.Microservices.AspNetCore NuGet reference so the upstream TrellisServiceBuilder.UseTrellisInternalJwtActor slot can resolve the moved provider.
| pwsh docs/lint-api-reference.ps1 | ||
| ``` | ||
|
|
||
| The solution build runs the same script through `docs\Trellis.DocsLint.csproj`, so failures are emitted as MSBuild errors. |
There was a problem hiding this comment.
Fixed in b822902. Now describes the actual integration: the LintApiReference target in Directory.Build.targets, opt-in per project via <TrellisEnableApiReferenceLint>true</TrellisEnableApiReferenceLint>. The Trellis.DocsLint.csproj reference was stale from when this doc was copied verbatim from main.
…ild integration reference Round-1 Copilot reviewer findings: 1. trellis-api-internal-jwt.md migration table: the `Old` column listed `builder.AddTrellis(...)` which was never a valid Trellis registration surface. AddTrellis is and always was an IServiceCollection extension, so the call site is identical pre- and post-carveout. Rewrite the row to reflect actual migration shape: call site unchanged; the only change is adding the Trellis.Microservices.AspNetCore NuGet reference, which lets the upstream TrellisServiceBuilder.UseTrellisInternalJwtActor slot resolve the provider that moved to this package. 2. lint-api-reference.md: line 11 referenced `docs\Trellis.DocsLint.csproj` which does not exist in this repo (that csproj lives only in xavierjohn/Trellis). The actual MSBuild integration is the LintApiReference target in Directory.Build.targets, opt-in per project via <TrellisEnableApiReferenceLint>true. Rewrite the line to describe the actual mechanism. Doc lint passes (4 markdown files scanned, exit 0). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…CHANGELOG
Round-1 Copilot reviewer findings (all 11 applied):
1+5. ReleaseGateScenarios.cs:16 - class XmlDoc listed a "KeyRotation"
gateway-to-downstream scenario that doesn't exist. Updated the
docstring to list the actual 8 scenarios by category.
2. HarnessFixtures.cs:94 - fixture claimed to use "strict cookbook
Recipe 1 profile" but missed RequireExpirationTime=true. Added.
3. HarnessFixtures.cs:62 - remarks said "mediator pipeline 401s" but
this harness has no mediator. The /probe endpoint handler itself
returns 401 on Maybe.None. Comment updated.
4. E2EHarness.Tests.csproj:37 - removed unused
Microsoft.Extensions.TimeProvider.Testing PackageReference
(nothing in the project uses FakeTimeProvider).
6+7+8. POST -> GET across ReleaseGateScenarios.cs:27, HarnessFixtures.cs:30,
README.md:27. The harness actually uses GET /probe with
Authorization: Bearer header (per HarnessFixtures.ProbeAsync); the
"POST" wording in the docs was inaccurate.
9. README.md:69 - P4-invariant mapping row for MapInboundClaims=false /
TryAllIssuerSigningKeys=false was still over-claiming. Narrowed
further: the row now says the fixture configures both consistent
with Recipe 1 but does NOT prove either's protective effect (the
actor provider has explicit sub short-long fallback that lets the
contract work even with MapInboundClaims=true; the wrong-kid
fallback path is not exercised with the single-key fixture).
10. GatewayHarness.cs:89 - the TestServerForwarderHttpClientFactory
captures an HttpMessageHandler but never disposes it (handler leak
across larger/looped runs). Made the factory IDisposable so DI
disposes the handler when the host disposes.
11. CHANGELOG.md:11 - section heading "0.1-alpha.d - Cookbook Recipes
1+2 inlined" had bullets describing the Abstractions package. Root
cause: a prior edit during PR #7 ate the 0.1-alpha.b heading and
mis-attributed 0.1-alpha.b's bullets to 0.1-alpha.c, then PR #8
compounded the damage by renaming 0.1-alpha.c -> 0.1-alpha.d.
Reconstructed the full CHANGELOG from git history. Structure now:
## Unreleased - E2E harness (this PR)
## 0.1-alpha.d - Recipes inlining + slot-ref cleanup (PR #7)
## 0.1-alpha.c - Yarp move + AspNetCore carve-out (PR #3)
## 0.1-alpha.b - Abstractions package (PR #2)
## 0.1-alpha.a - Initial bootstrap (PR #1)
The 0.1-alpha.d body was recovered from origin/main's Unreleased
section (post-PR-#7-merge); the 0.1-alpha.c body was recovered from
PR #3's merge-commit CHANGELOG state via git show.
Build + test + lint: 0 warnings, 0 errors; 231/231 pass; doc-lint clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…m the RBAC rework
The Aspire+RBAC sample had 4 places where the prose/snippets hadn't been
updated when the RBAC layer changed the Orders endpoint shapes. Found by a
local code-review pass (gpt-5.5).
1. ARCHITECTURE.md §3 happy-path sequenceDiagram showed the response as
`{ "service":"orders", "actor":{...} }` — that was the SHAPE before RBAC.
The current `GET /api/orders` endpoint returns an array of OrderResponse
DTOs (`orders.Select(OrderResponse.From).ToArray()` in Orders/Program.cs).
Updated the diagram's last step to show the actual array shape.
2. README.md "Drive it with curl" section had the same stale "service+actor"
response example for /api/orders. Replaced with:
- Orders → array of seeded OrderResponse DTOs (the actual response)
- Billing → unchanged Actor-echo shape (Billing IS still actor-echo; only
Orders gained the domain layer)
Also updated the `Invoke-RestMethod` comments to make the contrast
explicit (Orders has domain; Billing stays trust-boundary-only).
3. README.md "Load-once invariant" bullet over-stated the claim:
"Orders handlers inject IAuthorizedResource<,> instead of IOrderRepository"
— but ListOrdersHandler CORRECTLY injects IOrderRepository (list is a
static-permission-only path; not a single-resource auth path). The
broad claim would mislead a careful reader auditing whether the
load-once invariant holds for list too. Narrowed to: "the single-resource
Orders handlers (GetOrderHandler / UpdateOrderHandler) inject
IAuthorizedResource<TCommand, Order>", with an explicit note that
ListOrdersHandler uses IOrderRepository.ListAllAsync intentionally.
4. Sample.http "Load-once proof" comment had the same over-broad claim.
Same narrowing applied.
5. Sample.http request #1 description said the response is "service:orders
+ round-tripped Actor" — same stale shape. Updated to "an array of
seeded OrderResponse DTOs (order-1 + order-2)".
6. CHANGELOG entry said "bumped all 7 upstream Trellis.* pins from
alpha.342 to alpha.360". Only 5 pins existed at alpha.342 — bumped those
5; Trellis.Mediator + Trellis.Primitives are NEW pins added at alpha.360
(the sample didn't consume them before RBAC). Rewrote the line to be
accurate about which pins are bumped vs newly-added.
7. OrderResourceLoader.cs:7-10 source comment had the bridge direction
REVERSED — it said "Bridges from Result<Order> (auth) to Maybe<Order>
(repo)" but the code does the opposite (repo returns Maybe<Order>; the
loader translates to Result<Order> via Error.NotFound). Swapped the
from/to in the comment to match the code.
No runtime behavior change. Pure documentation/comment alignment.
Verified
--------
- dotnet build Trellis.Microservices.slnx -c Release → 0 warnings, 0 errors
- All 11 Mermaid diagrams in ARCHITECTURE.md still parse via mermaid-cli
- Diff is doc-only: CHANGELOG + ARCHITECTURE + README + Sample.http + 1
source comment (OrderResourceLoader.cs). No .cs runtime code touched.
Diff: 5 files changed, 31 insertions(+), 17 deletions(-)
What
PR A of the carve-out — repository scaffolding plus the four LLM-discoverability docs that establish this repo's shape before any
.cslands.Zero
.csin this PR. Source for the three packages lands in follow-ups:Trellis.Microservices.Abstractions(promotesTrellisInternalJwtClaimNamesto public).Trellis.Yarpmove from main +Trellis.Microservices.AspNetCorecarve-out (namespace rename).File inventory (24 files)
Standard scaffolding (verbatim from
xavierjohn/Trelliswhere possible).editorconfig,.gitattributes,.gitignore(replaces stub),nuget.config,global.json,icon.pngbuild/test.props,build/Trellis.ApiReference.targets.github/dependabot.ymlLICENSEAdapted (not verbatim)
Directory.Build.propsRepositoryUrl+PackageProjectUrlpoint toxavierjohn/Trellis.Microservices;Copyrightyear 2026Directory.Build.targetsTrellisShipsCookbooknow packstrellis-api-microservices-cookbook.md(not main'strellis-api-cookbook.md)Directory.Packages.propsTrellis.Asp/Trellis.Authorization/Trellis.Core/Trellis.ServiceDefaultsdeps as NuGet refsversion.json0.1-alpha.{height}— placeholder; tracked as outstanding decision insession-state/files/carveout-plan.mdTrellis.Microservices.slnxREADME.md,CHANGELOG.mdDocumentation
.github/copilot-instructions.mddocs/docfx_project/api_reference/trellis-api-microservices-cookbook.mddocs/docfx_project/api_reference/trellis-api-yarp.mdtrellis-api-asp.md→trellis-api-internal-jwt.md)docs/docfx_project/api_reference/trellis-api-internal-jwt.mdtrellis-api-asp.mdTrellisInternalJwt*sections (namespace renamedTrellis.Asp.Authorization→Trellis.Microservices.AspNetCore)docs/docfx_project/api_reference/trellis-api-microservices-abstractions.mddocs/lint-api-reference.ps1,docs/lint-api-reference.mdVerification
pwsh docs/lint-api-reference.ps1— passes (4 markdown files scanned, exit 0)..md/.xml/.props/.targets/.slnxfiles have UTF-8 BOM per.editorconfig. JSON files (global.json,version.json,nuget.config) match upstream convention.dotnet build/dotnet test— N/A (no.csprojyet). First signal lands in PR B.audit-stale-docs.ps1— not copied (it scans.csfor deprecated vocabulary; nothing to audit yet). Add in PR B with the first source file.Lint regressions fixed during validation
The api-reference lint script (copied from main) caught 15 issues across the 4 drafted docs:
TRLDOC001— bare cross-doc trellis-api link missing anchor#use-this-file-whenanchorsTRLDOC003— anchor doesn't resolve in (self)<!-- trellis-doc-lint: allow-broken-anchor -->markers; anchors resolve once Recipe 1+2 bodies are inlined in PR COutstanding decisions (deferred to PR B or beyond)
0.1-alpha.{height}placeholder. Final choice (0.1.0-preview.0vs1.0.0-preview.0) needs to land before PR B publishes the first preview NuGet.Trellis.Yarpversion reset behavior on move — continue main's Nerdbank.GitVersioning numbering vs reset.TrellisServiceBuilder.UseTrellisInternalJwtActorslot location — keep on upstreamTrellis.ServiceDefaults(current) vs move to this repo.Reviewer focus
#use-this-file-whenanchor, or be a full URL to upstreamxavierjohn/Trellis.P4 invariants — never regresstable in.github/copilot-instructions.md— verify the 14 invariants match shipped behavior inxavierjohn/TrellisPR #582/#583/#584.trellis-api-internal-jwt.md(bottom) — verifyservices.AddTrellis(b => ...)syntax matches the actualTrellis.ServiceDefaultsextension method (TrellisServiceCollectionExtensions.cs).Trellis.Microservices.slnx— no projects yet; this PR is solution-items only. PR B adds the first project.🤖 Generated with GitHub Copilot CLI