Add required ModuleResult value access - #3568
Conversation
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b82cb77c49
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Code reviewVerified: built No blocking bugs found. Two non-blocking suggestions:
Nice addition overall — the |
b82cb77 to
9fd80c5
Compare
|
Addressed the P2 record-formatting finding in |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9fd80c5192
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Code review
Verified on HEAD (9fd80c5192): rebuilt ModularPipelines.sln (core, 0 errors/warnings), ran the full ModuleResultContractTests suite (12/12 pass), and dotnet format --verify-no-changes --severity info is clean on the changed file.
Follow-up on prior review rounds
- ✅ Codex's "unsafe record formatting" P2 is fixed and verified. The new
ModuleResult<T>.PrintMembersoverride stops the throwingValueaccessor from being evaluated byToString()/logging onFailure/Skipped/null-Success, whileSuccessstill prints its own safe value exactly once. The four regression tests added for this (Failure_ToString_...,Skipped_ToString_...,NullSuccess_ToString_...,Success_ToString_PrintsValueOnce) all pass. - ❌ Codex's second P2 — constructor parameter rename — is still unaddressed.
ModuleResult<T>.Successused to be a positional record (Success(T? Value)), which generates a public constructor parameter literally namedValue. The rewritten explicit constructor (ModuleResult.cs:353) ispublic Success(T? value)(lowercase). C# named arguments are case-sensitive, so any external caller usingnew ModuleResult<int>.Success(Value: 42)will fail to compile (CS1739) after upgrading. The linked issue (#3490) explicitly scopes this change as "Breaking: no (additive)" — this regresses that guarantee for a real (if narrow) call pattern. Simple fix: keep the constructor parameter namedValueto match the pre-existing public surface, e.g.public Success(T? Value) { this.Value = Value; }.
Design note (non-blocking, previously raised)
Success hides the base's new throwing Value with public new T? Value { get; init; } (nullable, non-throwing). This means .Value has two different meanings depending purely on the static type of the reference — throwing/non-null when typed as ModuleResult<T>, nullable/non-throwing when typed as Success. It's a deliberate trade-off to preserve the existing case Success { Value: var x } pattern-matching idiom, but it's a classic C# footgun (member hiding + static dispatch) — a future reader narrowing/widening a variable's declared type gets silently different behavior from the same object. Not blocking, but worth a doc comment on Success.Value calling out that it intentionally shadows the base accessor, so nobody "cleans it up" later and reintroduces the exception-during-logging bug this PR just fixed.
Overall the core design (throwing Value mirroring Nullable<T>.Value semantics, safe PrintMembers override, docs updates) is solid and well tested — just want the constructor-naming regression closed before merge.
9fd80c5 to
c566860
Compare
|
Addressed named-argument source compatibility in @codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c5668608fe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Code review
Verified on latest commit c5668608fe: rebuilt ModularPipelines.sln (core, 0 errors/warnings) and ran ModuleResultContractTests — 13/13 pass, matching the PR's updated test plan.
Previous feedback — status check
- The two
@codex-flagged follow-ups (record-formatting regression fixed in9fd80c519, named-argument source compatibility fixed inc5668608f) are both correctly resolved. I independently confirmed thePrintMembersoverride inModuleResult<T>(src/ModularPipelines/Models/ModuleResult.cs:474-475) is load-bearing, not cosmetic: without it, the compiler-synthesizedPrintMembersforModuleResult<T>would include the new throwingValueproperty, so calling.ToString()on anyFailure/Skippedresult would itself throw. I verified this by reproducing the pattern in isolation — a throwing property left in the auto-generated member list does blow upToString(). The override plus the four new regression tests (Failure_ToString_DoesNotEvaluateRequiredValue, etc.) close that gap correctly. - The named-argument fix is also verified:
Success(T? Value)'s parameter keeps the capitalized name, so all existing positional call sites (new ModuleResult<T>.Success(value)across the distributed test suite) and the new named-argument test (Success_Constructor_PreservesValueNamedArgument) both compile. - I also independently confirmed the previously-raised equality concern about converting
Successfrom a positional record to an explicit constructor + manually-declarednew T? Valueproperty does not regressEquals/GetHashCode— non-positional auto-properties still participate in record-synthesized equality, confirmed with a small isolated repro.
Still-open, non-blocking suggestion (carried over from the earlier review)
The member-hiding footgun raised previously — Success declaring public new T? Value { get; init; } to shadow the base's new throwing Value — hasn't been addressed, and I'd still recommend fixing it rather than leaving it, for a concrete architectural reason beyond style: new-hiding makes behavior depend on the static type of the reference, not the runtime type, which is unusual for a discriminated-union-style API where callers are trained to trust pattern matching and rely on consistent member semantics. Concretely, ((ModuleResult<T>)success).Value throws-on-null while success.Value (statically typed as Success) does not, for the exact same object — a footgun a future contributor could easily hit by widening/narrowing a variable's declared type during a refactor, with no compiler warning. Since the nullable accessor is only actually needed for the Success { Value: var x } pattern-matching idiom already used in the docs, renaming it (e.g. RawValue) removes the ambiguity entirely and is a strictly safer design than hiding + new — same ergonomics for the pattern-matching case, zero risk of static-dispatch surprises for direct member access. Not a blocker, but worth doing before this shape of API sees wider adoption/follow-up migrations (e.g. the ValueOrDefault! → .Value migration suggested in the earlier review).
No new bugs found. Nice, well-tested addition — the Value/ValueOrDefault split mirrors Nullable<T>'s own .Value/.GetValueOrDefault() convention, which is a good precedent to follow here.
|
Restored the positional record's named |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4265d6aa29
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Code reviewVerified on latest commit This round's changeThis commit restores the manual Status of all prior review roundsEvery previously-raised issue across this PR's iterations is now resolved and independently re-verified:
Still-open, non-blocking design note (carried over, not new)
No new bugs found. This is a solid, well-tested addition — nice work tracking down each source-compatibility edge case across the review rounds. |
|
Synced the required-value example into @codex review |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Code reviewVerified on latest commit This round's changeThis commit syncs the required-value example from Status of all prior review roundsEvery previously-raised issue across this PR's iterations remains resolved:
Still-open, non-blocking design note (carried over, not new)
No new bugs found. This is a well-tested, source-compatible addition — nice work tracking down every edge case across the review rounds. |
0d652e1 to
5eef295
Compare
|
Rebased onto current Validation after rebase:
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
Code review
Verified on latest commit 5eef2953 ("docs(readme): sync required value example"): this commit is a rebase onto current main with an unchanged whole-PR patch-id (476c14c2...), so I independently re-verified rather than assuming it. Checked out the branch in an isolated worktree, built ModularPipelines.sln (core) with plain dotnet build -c Release — 0 errors, 0 warnings — and ran the full ModuleResultContractTests suite via dotnet run --framework net10.0 -- --treenode-filter "/*/*/ModuleResultContractTests/*" — 13/13 pass. Also confirmed README.md and README_Template.md now carry the identical buildResult.Value.OutputPath example (no stale ValueOrDefault! left in either).
(Aside, unrelated to this PR: the scripts/Invoke-AgentDotNet.ps1 wrapper's -SingleNode flag fails with MSB1008: Only one project can be specified in this environment when it appends -m:1 — plain dotnet build/dotnet run work fine. Flagging in case it's an environment quirk worth a look separately; not something to fix in this PR.)
Status of all prior review rounds
Every issue raised across this PR's review history is still resolved on this commit:
- Record
ToString()/logging throwing via theValueaccessor (P2) — fixed by theModuleResult<T>.PrintMembersoverride (ModuleResult.cs:477), which suppresses the base's throwingValuefrom auto-printing whileSuccessstill prints its own safe value once. - Named-argument source compatibility (
new Success(Value: 42)) — fixed; constructor parameter is stillValue(capitalized) via the documentedSA1313suppression. - Positional
Deconstructsource compatibility (success.Deconstruct(Value: out var x)) — fixed and covered by a regression test. - README/README_Template drift — fixed, both files match exactly.
Still-open, non-blocking design note (carried over, not new)
ModuleResult<T>.Success still hides the base's throwing Value with public new T? Value { get; init; } (ModuleResult.cs:362). .Value throws-on-null when statically typed as ModuleResult<T> but doesn't when statically typed as Success, for the same object — a static-dispatch footgun if a future refactor narrows/widens a variable's declared type. This is a deliberate, working trade-off to keep the case Success { Value: var x } pattern-matching idiom intact, and I'm not blocking on it — just noting it's the one item from this review series that hasn't been addressed, and would be worth a RawValue-style rename or a doc-comment callout before the suggested ValueOrDefault! → .Value migration lands elsewhere in the codebase (src/ModularPipelines.Build/Examples).
No new bugs found. This is a well-tested, source-compatible addition with good exception messages for debugging — nice work tracking every edge case down across the review rounds.
Give required dependencies a non-null Value accessor with module-specific failure context while keeping ValueOrDefault for optional data. Refs #3490.
Exclude the throwing required accessor from generic record formatting while preserving the successful value output. Refs #3490
Keep the public Success constructor parameter named Value so existing named-argument call sites continue compiling. Refs #3490
Keep the positional record's public Value parameter names for source compatibility.
5eef295 to
9e24fdd
Compare
Code reviewVerified on latest head Since the diff content is unchanged, the prior review round stands in full:
Still-open, non-blocking design note (carried over, not new): No new bugs found. Nothing to re-verify beyond confirming the rebase is patch-equivalent — approving from a correctness standpoint. |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
All four prior P2 findings were already fixed and are now resolved. Also documented the intentional static-type difference for Success.Value without breaking its existing source-compatible name. ModuleResult contract tests pass (13/13); core Release build has 0 errors. Branch updated from main. @codex review |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Code reviewVerified on latest head This round's changeThe only substantive change since the last review round is a 5-line XML ModularPipelines/src/ModularPipelines/Models/ModuleResult.cs Lines 358 to 368 in a07f310 ModuleResult<T>.Value accessor to preserve the nullable value for the Success { Value: var x } pattern-matching idiom. This closes out the one still-open item carried across every prior round of this review — the member-hiding footgun is now explicitly documented at the point a future reader would encounter it, rather than left implicit. The rest of the diff vs. the previously-reviewed 9e24fdd8e4 is unrelated test-infrastructure churn (HttpTests.cs, DownloaderTests.cs, LocalHttpServer.cs) pulled in by merging main, not authored by this PR.
Status of all prior review roundsEvery issue raised across this PR's review history remains resolved:
No new bugs found. Nothing outstanding from this review series — the design is sound, well-tested, and now well-documented. |
Summary
ModuleResult<T>.Valuefor required, non-null dependency resultsInvalidOperationExceptionmessages for failure, skip, and null successSuccessconstruction, nullable variant access, deconstruction, and JSON round-trippingTest plan
ModuleResultContractTests(8/8)dotnet format --verify-no-changes --severity infonpm run build --prefix docsModularPipelines.sln, 0 errors)Closes #3490