Skip to content

Do not capture what a specification declares - #2546

Merged
woksin merged 2 commits into
mainfrom
fix/do-not-capture-artifacts-declared-in-specifications
Aug 13, 2026
Merged

Do not capture what a specification declares#2546
woksin merged 2 commits into
mainfrom
fix/do-not-capture-artifacts-declared-in-specifications

Conversation

@woksin

@woksin woksin commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Fixed

  • A type a specification declares is no longer captured as an artifact the application ships. A fixture declared to assert something about a contract is Debug-only and stripped from any deployed build, and where it stands in for a real artifact — a second projection over a read model that already has one — the captured document said the read model was built twice, which is not legal Screenplay, so the document did not compile and nothing downstream could read it.

The artifact catalogue walked every nested type, so a fixture a
specification declares to assert something about a contract was
captured as though the application shipped it. It does not: the fixture
is Debug-only and stripped from anything deployed.

Where the fixture stands in for a real artifact the effect is worse
than an extra entry. A specification that declares a second projection
over a read model that already has one makes the captured document say
the read model is built twice, which is not legal Screenplay - so the
document does not compile and nothing downstream can read it.

Recognised by the base class rather than by the members a
specification usually holds: one that only inspects a contract has no
Because, and those are exactly the ones that declare a fixture.
@woksin woksin added the patch label Aug 13, 2026
@woksin

woksin commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Reviewer context.

Found by capturing a real application. cratis screenplay generate over Ada — 3,081 files, 241 slices — produced a 14,119-line document with 2 errors, both PLAY0191 Read model 'UserAccessPolicy' is built more than once. The generator already reported them as its own fault via SP0034; this is the cause.

Both came from projections declared inside specification classes:

fixture declared in
SpecificationProjection for_UserAccessPolicy/when_the_projection_contract_is_inspected/…
ProjectionWithRuntimeConditionalHelper when_provisioning_a_new_internal_human/and_the_reactor_contract_is_inspected.cs

Each exists so the specification can assert something about the real projection's contract, and each was emitted beside it — so the read model had three builders.

Why the base class and not the members. SpecificationReader.IsWrittenAsOne requires a Because method. Neither of those specifications has one — a specification that only inspects a contract never needs one — so a members-based rule would have missed precisely the specifications that declare fixtures. Ada's own specification already solves this internally by walking the declaring-type chain for Specification; this uses the same rule.

Verified end to end, not just in specs. Packed this branch locally, pointed the CLI at it and regenerated Ada:

before after
errors 2 0
diagnostics 3,600 3,589
document compiles no yes
Studio import parses no yes
canvas → document compiles yes

That last row is the point: the two errors blocked Studio's import outright, because it will not parse a document with errors. With them gone, Ada imports — 17 modules, 241 slices — and round-trips back to a document that compiles.

The spec was vacuous first, and I nearly shipped it that way. My initial fixture put the specification in a namespace that produced no slice, so nothing was captured with or without the change and all three facts passed either way. Reverting the source and re-running is what caught it. In its current shape all three fail with the fix reverted.

Not verified: no other application was captured, so this rests on Ada plus the spec. And the fix stops recursion into specifications entirely — a type nested in one is never an artifact — which is the intent, but it is broader than projections.

Source strings as constants rather than inline arguments, the note as
a documentation comment, and no unnecessary using. Debug does not treat
these as errors and Release does, which is what CI builds.
@woksin
woksin merged commit 588d135 into main Aug 13, 2026
56 checks passed
@woksin
woksin deleted the fix/do-not-capture-artifacts-declared-in-specifications branch August 13, 2026 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant