Add supported module testing harness - #3579
Conversation
There was a problem hiding this comment.
Code review
Reviewed the new ModularPipelines.Testing harness (ModuleTester, RecordingCommandInterceptor, InMemoryFileSystemProvider, NoOpConsoleServices) and the supporting core changes (ICommandInterceptor, File.Exists/Folder.Exists routing through IFileSystemProvider, CommandResult.Ok). No CLAUDE.md violations found. Found several issues, ranked by severity — all verified against the code as it currently exists on this branch.
1. Command interception silently defeats ThrowOnNonZeroExitCode (bug)
ModularPipelines/src/ModularPipelines/Context/Command.cs
Lines 92 to 102 in 8a5cc93
When an ICommandInterceptor returns a result, the method returns immediately at line 99 — before Of(...) (the only caller of CreateCommandFailure, which throws CommandException when ExitCode != 0 && ThrowOnNonZeroExitCode, default true). So a module (or a test) that intercepts a command and returns a non-zero-exit-code CommandResult gets that result back silently instead of the CommandException a real failing command would throw. Any module whose error handling relies on the throw (which is the framework default) behaves differently under a stubbed command than it would in production — the exact opposite of what a test double should guarantee.
Suggested fix: route the intercepted result through the same failure-evaluation step (CreateCommandFailure, or a shared helper) instead of returning early. This is worth fixing at the abstraction level, not just for tests — ICommandInterceptor is a public seam usable outside tests too (per docs/docs/how-to/testing.md), so any production use of interception has the same silent-failure hole.
2. InMemoryFileSystemProvider.IsDescendant never matches anything under a root path (bug)
ModularPipelines/src/ModularPipelines.Testing/InMemoryFileSystemProvider.cs
Lines 335 to 383 in 8a5cc93
Normalize keeps the trailing separator only for root paths (/, C:\), so IsDescendant("/tmp/x.txt", "/") checks candidate[parent.Length], which lands on 't' (the first char of the child name) rather than a separator, and returns false. Since the constructor pre-registers the root as a directory, this is reachable: EnumerateFiles("/", "*", AllDirectories) silently returns empty even with files present, and DeleteDirectory("/", recursive: true) reports success while deleting none of the root's contents (only the root's own dictionary entry is removed). No existing test exercises operations against the root — the test suite only touches paths under GetTempPath().
Suggested fix: special-case parents that already end in a separator in IsDescendant (treat the boundary as already consumed), e.g. if (parent[^1] is '/' or '\\') return true; before the boundary-character check.
3. Filesystem virtualization is incomplete — Exists is virtualized, most everything else isn't (design gap)
ModularPipelines/src/ModularPipelines/FileSystem/Folder.cs
Lines 512 to 524 in 8a5cc93
This PR routes File.Exists/Folder.Exists through _provider, but Folder.GetFiles/GetFolders/ListFiles/ListFolders/Clean and File.Length/Attributes/CreationTime/LastWriteTimeUtc/Extension/IsReadOnly/Hidden/Name all still go straight to DirectoryInfo/FileInfo/System.IO.Directory, bypassing IFileSystemProvider entirely. Under InMemoryFileSystemProvider, a module can observe folder.Exists == true (in-memory) and then have folder.GetFiles() throw DirectoryNotFoundException or silently read unrelated files from the real disk — an inconsistent, half-virtualized filesystem that's worse than not virtualizing at all, because it looks trustworthy.
Suggested approach: rather than patching individual members opportunistically (which is how this partial state arose), treat IFileSystemProvider as the single seam for all of File/Folder's disk interaction — extend the provider interface to cover enumeration and metadata, and route every member through it. If full coverage isn't feasible in this PR, the safer interim behavior is for InMemoryFileSystemProvider-backed File/Folder instances to throw NotSupportedException on the un-virtualized members, so gaps fail loudly during test authoring instead of silently falling through to the real filesystem.
4. AppendAllTextAsync/CopyFile/MoveFile aren't actually thread-safe, despite the class doc comment (bug vs. documented contract)
The class is documented as "Thread-safe" on the strength of using ConcurrentDictionary, but AppendAllTextAsync does an unguarded read-modify-write (FileExists → GetFile → SetFile), and CopyFile/MoveFile do check-then-act (ContainsKey then SetFile/TryRemove). ConcurrentDictionary only makes each individual operation atomic — it does not make a sequence of operations atomic. Two concurrent appends to the same path can lose one; overwrite: false on CopyFile can still be raced into an overwrite.
Suggested fix: use ConcurrentDictionary.AddOrUpdate (with a CAS-retry factory) for the append path, and guard copy/move with a per-path lock or an atomic TryAdd-first pattern. If true thread-safety isn't actually needed for this harness's use case (single module, sequential execution), the simpler and more honest fix is to correct the doc comment rather than leave a contract the code doesn't meet.
5. Unseeded [DependsOn<T>] dependency hangs for up to 30 minutes instead of failing fast (API design)
ModularPipelines/src/ModularPipelines.Testing/ModuleTester.cs
Lines 148 to 191 in 8a5cc93
ModuleTester auto-registers a module's dependencies in DI but only resolves their results if seeded via WithDependencyResult (which internally forces the result via TrySetDistributedResult). It executes the module directly through IModuleExecutionPipeline, bypassing the scheduler (IDependencyWaiter) entirely. If a test forgets to seed a dependency and the module awaits it (context.GetModule<Dep>()), nothing ever completes that dependency's TaskCompletionSource — the await blocks until PipelineOptions.DefaultModuleTimeout (30 minutes) elapses and throws ModuleTimeoutException, instead of an immediate, clear "dependency not seeded" error. For a unit-testing library, a fast, precise failure on a missing test setup is the whole point — a half-hour hang is close to worst-case UX for a typo'd or forgotten WithDependencyResult call.
Suggested fix: validate at ExecuteAsync/build time that every [DependsOn<T>] dependency of the module under test has either been seeded or is otherwise resolvable, and throw immediately with a clear message naming the missing dependency if not.
Related, lower-severity architectural note: ModuleTester reimplements the single-module execution path by hand (ExecutionContextFactory → ModuleExecutionDelegateFactory.GetExecutor → IModuleExecutionPipeline) rather than reusing ModuleRunner, so ModuleRunner-owned behavior — attribute-based lifecycle handlers (IModuleStartHandler/IModuleEndHandler), global IModuleEventReceivers, dependency-skip propagation, mediator notifications — silently doesn't run under test even though it runs in production. Today's docs/docs/how-to/testing.md claims are narrow enough to still be technically accurate (it only promises skip conditions/timeouts/retries/direct hooks, which do run), but the gap is real and will only grow as more is added to ModuleRunner over time. A narrower shared seam — e.g. an internal single-module execution entry point used by both ModuleRunner and ModuleTester — would prevent this from silently drifting further as the engine evolves, versus maintaining two parallel orchestration implementations.
Nice addition overall — the builder API (ModuleTester.For<T>(), WithDependencyResult, InterceptCommands) is a clean, discoverable shape for module authors, and the File.Exists/Folder.Exists fix (routing through _provider instead of hitting FileInfo/DirectoryInfo directly) is a genuine pre-existing bug fix. The issues above are mostly about the harness's guarantees not fully matching what it implies (thread-safety, full filesystem virtualization, production-parity execution, fail-fast on missing setup) — worth tightening before this becomes the documented, supported way to test modules.
|
Addressed all five review findings in beb5acf and rebased onto current main.\n\n- intercepted nonzero command results now honor ThrowOnNonZeroExitCode and throw CommandException after logging\n- root paths now match descendants correctly\n- File/Folder enumeration, globbing, and Clean route through IFileSystemProvider; unsupported physical metadata/copy semantics fail loudly instead of touching disk\n- in-memory compound mutations are synchronized, including append/copy/move\n- required dependency results are validated before execution with a named, immediate setup error\n- docs describe dependency, command, and metadata contracts\n\nValidation: ModularPipelines.Testing.UnitTests 19/19; FolderTests 25/25; FileTests 29/29; changed-file analyzer gate passes at warning severity; whitespace gate passes. @codex review |
8a5cc93 to
beb5acf
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: beb5acff91
ℹ️ 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 reviewRe-reviewed after the
All five are re-verified against the current code, not just the commit message, and each has direct test coverage. No CLAUDE.md violations found. Two design points remain worth tracking, neither blocking: A. Physical-metadata gating is identity-based, not capability-based. ModularPipelines/src/ModularPipelines/FileSystem/File.cs Lines 464 to 473 in beb5acf SystemFileSystemProvider is sealed with a private constructor, so the real filesystem can only ever be reached through that one singleton. But it means any future provider that legitimately backs onto real disk — a logging/auditing decorator around SystemFileSystemProvider, a chroot'd or read-only wrapper, etc. — would be misclassified as "unsupported" purely because it isn't that exact instance, even though FileInfo/DirectoryInfo would work fine against it. A capability contract (e.g. an optional interface the provider implements, or extending IFileSystemProvider itself to expose these members as the original review suggested) would let correctness be a property of what a provider can do rather than which concrete singleton it happens to be, so this doesn't need revisiting every time a new provider wraps or extends the physical one.
B. ModularPipelines/src/ModularPipelines.Testing/ModuleTester.cs Lines 120 to 167 in beb5acf ModuleRunner, so ModuleRunner-owned behavior (IModuleStartHandler/IModuleEndHandler, global IModuleEventReceivers, dependency-skip propagation) still doesn't run under test. The docs are scoped narrowly enough to stay accurate today, but as more responsibility accretes onto ModuleRunner over time, this is the kind of gap that grows silently rather than failing loudly. Worth eventually factoring out a shared single-module execution seam used by both ModuleRunner and ModuleTester, so the two orchestration paths can't drift apart.
Nice follow-through — all five issues were fixed at the root cause (not papered over) and each got direct regression coverage rather than just a manual claim in the PR description. |
All five requested changes were fixed in beb5acf; Claude re-reviewed the current implementation and explicitly confirmed every finding resolved in issue comment 5137754897.
Code reviewRe-checked after the latest commit ( That commit swaps All 5 previously-flagged issues (command interception bypassing No new issues found. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78d8a23954
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
Code review
Re-reviewed at the current head (051f626e, "refactor(testing): simplify file opening"). The five issues from my first pass and the two from the second pass remain fixed and test-covered — nothing in 051f626e (a pure Open-method extraction: ValidateOpenArguments/GetInitialContents/ShouldInitializeFile/CreateStream) changes that behavior.
Since my last review, chatgpt-codex-connector posted six new P2 findings against this same head. I independently verified each against the current code; all six are real and none has been addressed yet:
1. GetFiles/GetFolders regression — full recursive walk before exclusion, SafeWalk semantics lost (src/ModularPipelines/FileSystem/Folder.cs:493-524)
On main, these methods use SafeWalk.EnumerateFolders/EnumerateFiles, which prune excluded directories before recursing into them and tolerate inaccessible directories. This PR replaces that with _provider.EnumerateDirectories(Path, "*", SearchOption.AllDirectories).Where(path => !IsExcludedByDirectoryFilter(...)). For SystemFileSystemProvider, that's Directory.EnumerateFiles(path, "*", AllDirectories) — it walks the entire tree first, then the predicate filters results after the fact. This is the one finding here that isn't testing-library-specific: it's a behavior change to the core File/Folder API used in production. Any pipeline excluding bin/obj/.git/node_modules/a permission-restricted directory specifically to avoid descending into it can now throw UnauthorizedAccessException (no more IgnoreInaccessible) or just pay for a much more expensive walk than before, exactly where the exclusion filter existed to prevent that. Worth fixing before merge, since it's a silent regression for any existing pipeline module, not just an edge case in the new harness.
2-5. InMemoryFileSystemProvider fidelity gaps (all reachable, all diverge from SystemFileSystemProvider):
SetFile(line 376) doesn't check_directories, andCreateDirectorydoesn't check_files— a file and a directory can coexist at the same path, which a real filesystem rejects.SetFileauto-creates the entire parent directory chain (CreateDirectory(Path.GetDirectoryName(normalized) ...), line 381) instead of requiring the parent to already exist. RealFile.WriteAllText/CreatethrowDirectoryNotFoundExceptionwhen the parent is missing; a module that forgot to create its output directory will pass under the harness and fail in production.CommittingMemoryStream(line 440) only guards writes viaEnsureWritable(); it never restricts reads.Open(path, mode, FileAccess.Write)returns a stream whoseCanReadis stilltrue(inherited fromMemoryStream) and whoseRead/ReadBytestill succeed, where a realFileStreamopened write-only throws on read.JoinLines(line 365) always appendsEnvironment.NewLine, soWriteAllLinesAsync/AppendAllLinesAsyncwith an empty sequence write a single newline character.System.IO.File.WriteAllLineswith an empty sequence produces a genuinely empty (0-byte) file.
6. RecordingCommandInterceptor records the pre-metadata result, not what the module receives (src/ModularPipelines.Testing/RecordingCommandInterceptor.cs:25, src/ModularPipelines/Context/Command.cs:92-103)
InterceptAsync enqueues the handler's raw CommandResult into _commands before Command.ExecuteCommandLineToolAsync calls ApplyCommandMetadata(intercepted, command). ApplyCommandMetadata overwrites CommandInput/WorkingDirectory/EnvironmentVariables on the result actually returned to the module — so run.Commands[i].Result in a test permanently reflects the interceptor's raw stub (e.g. CommandResult.Ok()'s defaults) rather than the enriched result the module code actually saw. A test asserting on the recorded working directory or env vars will get the wrong values.
Architectural observation
Items 2-5 are the third consecutive round where InMemoryFileSystemProvider ships a divergence from SystemFileSystemProvider that a reviewer had to spot by manual inspection (root-path matching, non-atomic ops, and now directory/file collisions, parent-existence, read/write access, and empty-line joining). Each round fixes the specific instances flagged, but the underlying cause — no mechanism that forces the two providers to agree — stays in place, so the next behavioral gap surfaces the same way. A parameterized/shared contract test suite ([Theory] over both provider instances, run against a real temp directory for the system provider and a fresh InMemoryFileSystemProvider for the other, asserting identical results and identical exception types for the same sequence of operations) would catch this class of bug systematically instead of one divergence at a time, and is worth adding before InMemoryFileSystemProvider grows further. The two design points I raised in the previous round (identity-based physical-metadata gating, ModuleTester reimplementing single-module execution instead of reusing ModuleRunner) are unchanged and still non-blocking.
No CLAUDE.md violations found. The extraction in 051f626e itself is clean and behavior-preserving.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 051f626e74
ℹ️ 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".
051f626 to
478346f
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 478346f025
ℹ️ 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 reviewRe-checked after the two commits since my last review comment (
What changed:
Validation: built No new issues found, and no CLAUDE.md violations. The two design notes from my previous review (capability- vs identity-based physical-metadata gating, and |
Provide supported isolated module execution with dependency seeding, command interception/recording, in-memory files, and safe output defaults. Refs #3538
478346f to
4d39375
Compare
|
Rebased onto current main. Relevant tests pass 66/66; Testing package and core Release builds pass. @codex review |
Code reviewChecked the current head ( No files owned by this PR ( No new issues found — nothing to re-review since the rebase carried no functional changes for this PR. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4d3937524d
ℹ️ 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.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c11a465b79
ℹ️ 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
Reviewed the commit pushed since my last review comment (against c11a465b79): ae08d4660c ("fix(testing): close filesystem parity gaps"), which appears to be the current head. Also spot-checked the peripheral files that hadn't been called out explicitly in the 25+ prior review rounds on this PR (FindProjectsModule.cs, README.md, ModularPipelines.csproj) — these are routine, mechanical registrations (new project added to the build's project list, README table row, InternalsVisibleTo) with no issues.
ae08d4660c changes, both correct and well-tested:
-
InMemoryFileSystemProvider.DeleteFilenow checks the directory-form path. Previously it looked up_directories.ContainsKey(normalized)using the file-normalized path, which retains a trailing separator when the caller passes one (e.g.DeleteFile("dir" + Path.DirectorySeparatorChar)), but_directorieskeys are stored without a trailing separator. So a directory-form delete request silently fell through to_files.TryRemove(a no-op, since no such file key exists) instead of throwingUnauthorizedAccessExceptionlike realFile.Deletedoes against a directory. Normalizing to directory form before the lookup fixes it, and it's the same class of trailing-separator bug fixed forValidateFileDestinationa few commits ago, just in the delete path this time. Covered by the newDeleteFileRejectsDirectoryPathsWithTrailingSeparatorstest. -
Zip.ZipFolder's directory-vs-file output heuristic no longer touches the physical filesystem. This is the real fix, and it closes a gap flagged bychatgpt-codex-connectorseveral rounds ago ("Determine ZIP output type through the configured provider") that a prior commit only partially addressed. The old code was:var outputIsDirectory = _fileSystemProvider.DirectoryExists(outputPath) || (!_fileSystemProvider.FileExists(outputPath) && outputPath.GetPathType() == PathType.Directory);
GetPathType()(src/ModularPipelines/Helpers/PathHelpers.cs) starts withSystem.IO.File.Exists/Directory.Exists— real disk checks — before falling back to the separator/extension heuristic. Since the provider-based checks (DirectoryExists/FileExists) already short-circuit for genuine matches,GetPathType()'s own physical checks were pure redundancy forSystemFileSystemProvider, but forInMemoryFileSystemProviderthey let real, coincidental disk state leak into a decision that's supposed to be governed entirely by the virtual provider — breaking the isolation guarantee that's the whole point of this harness. The new privateIsDirectoryPathhelper reimplements only the separator/extension heuristic, with no disk I/O. I confirmedGetPathType()has no other production call sites (only its own dedicatedPathHelpersTests), so this was the last remaining leak of that kind.The accompanying test change is a good regression test, not just a rename: it creates a real directory on disk at the same path the in-memory provider would resolve the zip output to (
host-collision.zip), then asserts the module's in-memory zip output still resolves to that exact file path rather than being treated as a directory and nested under it. This would have failed under the old code (physicalDirectory.Existswould have returned true, real disk collision leaking into virtual behavior) and passes under the new one.Directory.Delete(root, recursive: true)in afinallycleans up the physical artifact regardless of outcome.
No new issues found, and no CLAUDE.md violations (review was diff/API-level; no builds were needed to verify correctness here since the logic changes are narrow and the reasoning traces cleanly through the code).
This is the 25+ round mark on this PR, and it's been an unusually thorough back-and-forth — codex and I independently caught overlapping and complementary issues across InMemoryFileSystemProvider fidelity, Command interception semantics, Zip virtualization, and ModuleTester fail-fast behavior, and each was fixed at the root cause with direct regression coverage rather than papered over. The two longer-running architectural notes from earlier rounds remain open and still non-blocking for merge, worth tracking as follow-ups rather than blockers:
- Physical-metadata gating in
File/Folderis identity-based (ReferenceEqualsagainst theSystemFileSystemProvidersingleton) rather than capability-based — a future provider that legitimately wraps real disk (e.g. a logging/audit decorator) would be misclassified as unsupported. ModuleTesterstill hand-assembles single-module execution (ExecutionContextFactory→ModuleExecutionDelegateFactory→IModuleExecutionPipeline) instead of routing throughModuleRunner, soModuleRunner-owned behavior (lifecycle handlers, global event receivers, dependency-skip propagation) doesn't run under test. Worth eventually factoring a shared single-module execution seam so the two orchestration paths can't silently drift apart asModuleRunnergrows.InMemoryFileSystemProviderhas grown large (~870+ lines) through many incrementalSystem.IO-parity fixes. Each fix so far has been correct and well-tested, but the set of edge cases it needs to match is open-ended (symlinks,FileAttributes, case-preserving-but-insensitive filesystems, etc.). A parameterized contract-test suite run against both providers (asserting identical results and exception types for the same operation sequence) would catch future divergences systematically rather than one at a time, and might be worth adding now that the provider surface is mostly stable.
Mergeable from a correctness standpoint.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae08d4660c
ℹ️ 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 reviewReviewed the one commit not yet covered by prior review rounds (against Change: Traced the call sites:
The new No new issues found in this commit. Status of previously-flagged architectural notes (unchanged by this commit, still open, still non-blocking per prior rounds — repeating briefly for visibility since this is a good stopping point after 25+ review rounds):
None of these are blockers — they're the kind of investment worth making once the provider surface stabilizes rather than mid-flight. Mergeable from a correctness standpoint. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c30c7163dc
ℹ️ 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".
Stream line enumerations into the in-memory file so cancellation and enumeration failures match System.IO partial-write semantics.
Code reviewReviewed the single commit pushed since my last review comment (against Change: I traced the commit path closely since this is exactly the kind of "silent partial write" behavior that's easy to get wrong: the Validation:
No new correctness bugs in this commit, and no CLAUDE.md violations (validation was scoped to the single affected test project, per repo guidance — no full-solution build was needed). Previously flagged, still open, still non-blocking (repeating briefly since this is a natural checkpoint after many rounds):
Mergeable from a correctness standpoint. The flaky timeout test is the one item from this round I'd actually act on before merge, since it's now reproduced twice. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 27812717ec
ℹ️ 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".
Sequence intercepted commands before asynchronous handlers so concurrent completion cannot reorder recorded assertions.
…rness # Conflicts: # docs/docs/how-to/testing.md
Code reviewReviewed the two commits pushed since my last review comment (against
No new issues found. No CLAUDE.md violations. This PR continues to look mergeable from a correctness standpoint — all previously-flagged issues across the review history remain fixed and test-covered, and the two carried-forward architectural notes (non-blocking) still stand: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d367bbb905
ℹ️ 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".
Align the nested-module code-fix fixture with formatting preserved by the current main implementation.
Code reviewReviewed the single commit pushed since my last review comment (against Change: Updates the No new issues, and no CLAUDE.md violations. This closes out the review for the PR as a whole. Across 25+ rounds, every issue raised (by this review and independently by |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cb48f4e45f
ℹ️ 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".
State the pipeline failure option and successful status used by the testing harness so the guide remains aligned with the compiled API snippets.
Code reviewRe-reviewed at the current head ( New change: adds a 2-sentence note above the "Assert skipped and failed runs" example in Overall assessment: across the ~20 prior review rounds (mine and Codex's), every flagged correctness issue — command interception bypassing One architectural point worth considering (not a blocker for this PR): the amount of iteration needed to get No blocking issues. The harness design (isolated DI-built pipeline per module, |
Sanitize intercepted command failures and reject platform-invalid in-memory path segments so tests match runtime behavior.
|
Pushed |
Code reviewRe-reviewed at the current head ( 1. Intercepted command failures now obfuscate secrets before throwing ( 2. In-memory provider now rejects platform-invalid file-name characters ( Validation: traced both changes end-to-end against their call sites and the pre-existing obfuscation/normalization patterns elsewhere in the same files; the author's stated 72/72 testing-harness test pass and clean Release build are consistent with what I'd expect from these diffs. No CLAUDE.md violations. This closes out the review for the PR as a whole. Across 25+ rounds (mine and |
Summary
ModularPipelines.Testingwith isolatedModuleTestertyped/untyped APIs and dependency seedingValidation
src/ModularPipelines.Testing/ModularPipelines.Testing.slnModularPipelines.Testing.UnitTestspassed with coverageCloses #3538