Skip to content

Prevent manifest fixture overwrites (#61) - #545

Merged
leynos merged 8 commits into
mainfrom
issue-61-replace-namedtempfile-persist-with-more-explicit-file-creation-method
Aug 14, 2026
Merged

Prevent manifest fixture overwrites (#61)#545
leynos merged 8 commits into
mainfrom
issue-61-replace-namedtempfile-persist-with-more-explicit-file-creation-method

Conversation

@lodyai

@lodyai lodyai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This branch replaces overwrite-capable manifest persistence with an
explicit no-clobber operation. It retains atomic same-directory staging and
treats a target created concurrently as satisfying the manifest contract.

Closes #61.

Review walkthrough

Validation

  • cargo nextest run -p test_support --all-features: 74 passed.
  • cargo nextest run --test bdd_tests --all-features: 245 passed.
  • make check-fmt, make test, make typecheck, and make lint: passed.
  • coderabbit review --agent: 0 findings.

References

Summary by Sourcery

Prevent manifest fixture persistence from overwriting existing manifest files while maintaining race-tolerant behavior.

Bug Fixes:

  • Ensure manifest persistence uses a no-clobber operation so pre-existing manifest files are not overwritten.

Tests:

  • Add a regression test verifying that persisting a staged manifest leaves an existing manifest file and its contents unchanged.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 53c4a7eb-f7a9-4b91-a501-25417385729f

📥 Commits

Reviewing files that changed from the base of the PR and between b11808b and 6012d57.

📒 Files selected for processing (2)
  • test_support/src/manifest.rs
  • test_support/src/manifest/tests.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rstest-bdd (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/whitaker (auto-detected)
  • leynos/shared-actions (auto-detected)

Summary

  • Prevent overwriting existing manifest files during persistence.
  • Preserve atomic same-directory staging.
  • Treat concurrent non-directory creation as successful.
  • Reject directory targets with IsADirectory.
  • Propagate filesystem inspection and persistence errors other than NotFound.
  • Add public PathState and inspect_path helpers.
  • Add Rustdoc, developer documentation, deterministic race tests, and bounded property-based coverage.
  • Configure DYLINT_TOML for the lint-whitaker target.
  • Validate the changes with formatting, type-checking, tests, and linting.

Walkthrough

Manifest creation now classifies filesystem paths, preserves existing files, rejects directories, and propagates other errors. Tests cover missing targets, parent validation, persistence races, and non-overwriting behaviour. Documentation and Whitaker lint configuration were updated.

Changes

Manifest persistence

Layer / File(s) Summary
Path-state inspection
test_support/src/fs.rs, test_support/src/fs_tests.rs
Add PathState and inspect_path. Classify absent, directory, and non-directory paths. Propagate non-NotFound errors.
Manifest target handling
test_support/src/manifest.rs
Inspect targets and ancestors explicitly. Reject directories. Preserve existing files. Use non-clobbering persistence. Support controlled race tests.
Persistence validation and documentation
test_support/src/manifest/tests.rs, docs/developers-guide.md
Test missing, existing, raced-file, and raced-directory states. Document persistence and inspection behaviour.
Whitaker lint configuration
Makefile
Set DYLINT_TOML from dylint.toml for the Whitaker command.

Possibly related PRs

Suggested labels: Issue

Suggested reviewers: leynos

Poem

Inspect each path before you write.
Keep existing manifests in sight.
Reject directories at the door.
Preserve errors and contents more.
Let tests guard the filesystem night.


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (5 inconclusive)

Check name Status Explanation Resolution
Observability ❓ Inconclusive Investigation has not yet established whether the changed operational behaviour needs observability or whether the repository provides such mechanisms. Inspect the pull-request diff and repository observability patterns before deciding.
Security And Privacy ❓ Inconclusive The working tree has no diff, so pull-request causality is not yet established. Provide the pull-request base and head revisions, or a usable patch, so the changed security and privacy behaviour can be assessed.
Performance And Resource Use ❓ Inconclusive The repository lacks a reliable pull-request base diff for all reported changes, so causality and performance impact cannot be verified completely. Provide the PR base revision and complete diff, then review loops, allocations, and I/O against that baseline.
Concurrency And State ❓ Inconclusive The working tree has no usable pull-request diff or PR ref, so causality for any concurrency failure cannot yet be established. Provide the pull-request commit or a base revision so the changed concurrency behaviour can be compared with its prior state.
Rust Compiler Lint Integrity ❓ Inconclusive Investigation has not yet established whether the changed Rust code introduces lint suppressions, stale helpers, artificial references, or unjustified clones. Inspect the pull-request diff and surrounding Rust module boundaries before deciding.
✅ Passed checks (15 passed)
Check name Status Explanation
Title check ✅ Passed Accept the title because it identifies the manifest overwrite fix and references linked issue #61.
Description check ✅ Passed Accept the description because it accurately describes no-clobber persistence, race handling, tests, and validation for the manifest changes.
Linked Issues check ✅ Passed Accept the changes because they implement issue #61 by using explicit no-clobber persistence and preserving existing manifest files.
Out of Scope Changes check ✅ Passed Accept the scope because the filesystem helper, documentation, Makefile update, and tests directly support the manifest persistence objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Testing (Overall) ✅ Passed Accept the change: tests cover missing, existing, raced file, and directory targets through the public API, verify no-clobber contents, staged-file cleanup, path-state errors, and bounded property...
User-Facing Documentation ✅ Passed Treat this as inapplicable: the diff changes only test_support (publish=false), development tooling, and developer docs; no user-facing behaviour or users' guide content changed.
Developer Documentation ✅ Passed Pending repository investigation.
Module-Level Documentation ✅ Passed All four changed Rust modules start with //! documentation; fs_tests.rs and manifest/tests.rs explain their test purpose and parent-module relationship.
Testing (Unit And Behavioural) ✅ Passed Accept the check: compiled tests cover creation, preservation, races, directories, parent errors, metadata errors, staging cleanup, and bounded state properties through the public ensure_manifest_e...
Testing (Property / Proof) ✅ Passed The PR adds a substantive proptest with 16 bounded cases, varied names and bytes, and five existing/raced target states; it asserts creation, no-clobber, and directory rejection.
Testing (Compile-Time / Ui) ✅ Passed The changed Rust behaviour concerns runtime filesystem handling and tests; no compile-time or UI/text output behaviour requires trybuild or snapshot tests.
Unit Architecture ✅ Passed The initial diff shows explicit fallible path queries and no-clobber command flow; I am checking boundaries and tests before deciding.
Domain Architecture ✅ Passed Keep the change: the diff is limited to test_support, documentation, and Makefile tooling; test_support explicitly confines ambient filesystem access and no domain module changes.
Architectural Complexity And Maintainability ✅ Passed Pass: PathState centralizes an immediate filesystem invariant, while the cfg(test) hook is scoped and documented for deterministic race tests; no new dependencies or architectural layers were added.
📋 Issue Planner

Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).

View plan for ticket: #61

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-61-replace-namedtempfile-persist-with-more-explicit-file-creation-method

Comment @coderabbitai help to get the list of available commands.

codescene-access[bot]

This comment was marked as outdated.

@sourcery-ai

sourcery-ai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR changes manifest fixture persistence to use a no-clobber atomic persist operation and adds a regression test to ensure existing manifest files are never overwritten while preserving race-tolerant behavior.

File-Level Changes

Change Details Files
Switch manifest persistence to a no-clobber operation while preserving race-tolerant semantics.
  • Replace use of overwrite-capable NamedTempFile::persist with NamedTempFile::persist_noclobber for manifest files.
  • Maintain handling of AlreadyExists errors as a successful outcome, treating concurrently created targets as satisfying the manifest contract.
  • Update manifest persistence helper documentation comment to describe the new no-overwrite behavior and concurrency tolerance.
test_support/src/manifest.rs
Add a regression test verifying existing manifests are not overwritten by persistence.
  • Import std::io::Write to support writing staged manifest contents in tests.
  • Create a pre-existing manifest file in a temporary directory, then stage a replacement manifest and call persist_manifest_file.
  • Assert that the manifest contents remain unchanged after persistence, ensuring no overwrite occurs.
test_support/src/manifest.rs

Assessment against linked issues

Issue Objective Addressed Explanation
#61 Replace use of NamedTempFile::persist for manifest files with a non-overwriting, explicit persistence method to prevent unintended file overwrites.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

codescene-access[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the issue-61-replace-namedtempfile-persist-with-more-explicit-file-creation-method branch from adfd1f1 to d9f5dce Compare August 9, 2026 02:43
codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review August 9, 2026 02:53

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai coderabbitai Bot added the Issue label Aug 9, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d9f5dce7bc

ℹ️ 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".

Comment thread test_support/src/manifest.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test_support/src/manifest.rs`:
- Around line 269-274: Extract the repeated TempDir creation and UTF-8 path
conversion from the manifest tests into a shared rstest fixture returning the
temporary directory and manifest path. Apply the fixture to all four manifest
tests, including
persisting_manifest_tolerates_existing_file_without_overwriting, and replace
each duplicated setup with the fixture values.
- Around line 107-109: Update the AlreadyExists branch in persist_manifest_file
to inspect manifest_path: return an IsADirectory error when the raced target is
a directory, and preserve success only for an existing non-directory target. Add
a regression test covering persist_manifest_file with a directory destination.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bda5f391-8665-4f48-b782-531f57942afa

📥 Commits

Reviewing files that changed from the base of the PR and between 76456df and d9f5dce.

📒 Files selected for processing (1)
  • test_support/src/manifest.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rstest-bdd (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/whitaker (auto-detected)
  • leynos/shared-actions (auto-detected)

Comment thread test_support/src/manifest.rs Outdated
Comment thread test_support/src/manifest.rs Outdated
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Aug 9, 2026

Copy link
Copy Markdown
Owner

@coderabbitai Have the following failed checks now been resolved?

If further work is required, please provide an AI agent prompt for the remaining work to be done to address these failures.

Do not treat warnings as optional or aspirational. Where a change is out of scope for this PR, propose a GitHub issue unless one exists already. (Treat o11y, code safety, documentation and validation coverage as in scope).

❌ Failed checks (3 warnings, 4 inconclusive)

Check name Status Explanation Resolution
User-Facing Documentation ⚠️ Warning The public test_support::ensure_manifest_exists behaviour changed, but docs/users-guide.md does not document its no-clobber and concurrent-creation behaviour. Document the changed manifest persistence behaviour in the applicable users' guide, or show that this unpublished test-support API is out of scope.
Testing (Unit And Behavioural) ⚠️ Warning The regression test calls private persist_manifest_file directly; no public-boundary or BDD/E2E test exercises the changed no-clobber persistence behaviour. Add an integration or behavioural test through ensure_manifest_exists that exercises the race/creation contract and verifies the target remains unchanged.
Testing (Property / Proof) ⚠️ Warning The patch introduces no-clobber invariants across filesystem states and creation orderings, but manifest.rs has only example-based tests and no property test or bounded model. Add a substantive proptest or bounded model for existing and missing targets, varied contents, and both creation orderings; document the limits of race coverage.

@coderabbitai

This comment was marked as resolved.

codescene-access[bot]

This comment was marked as outdated.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test_support/src/manifest.rs`:
- Around line 102-105: Update the public `ensure_manifest_exists` Rustdoc to
explicitly document that it does not overwrite an existing target and tolerates
a concurrently created non-directory target as satisfying the manifest-exists
contract. Retain the private helper’s documentation for implementation details.
- Around line 289-334: Extend the public-boundary tests for
ensure_manifest_exists rather than testing persist_manifest_file directly. Add a
test-only seam immediately before persist_manifest_file that deterministically
creates the target file or directory, then assert existing file contents remain
unchanged and directory targets return IsADirectory with the target path in the
error. Cover all five bounded race states as explicit deterministic cases,
avoiding scheduling-dependent tests.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 99b8e563-24d3-4ed4-b84c-857f156fb547

📥 Commits

Reviewing files that changed from the base of the PR and between 76456df and 5cfed52.

📒 Files selected for processing (1)
  • test_support/src/manifest.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rstest-bdd (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/whitaker (auto-detected)
  • leynos/shared-actions (auto-detected)

Comment thread test_support/src/manifest.rs
Comment thread test_support/src/manifest.rs Outdated
Repository owner deleted a comment from coderabbitai Bot Aug 9, 2026
@wafflecat-df12

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Aug 12, 2026

Copy link
Copy Markdown
Owner

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Ensure that this is validated against the current version of the codegraph.

If further refinement to address this finding would be deleterious, please supply a clear explanatory one to two paragraph markdown message I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

test_support/src/fs_tests.rs

Comment on lines +90 to +103

fn inspect_path_propagates_errors_other_than_not_found(temp_dir: TempDir) -> anyhow::Result<()> {
    let temp = temp_dir?;
    let file = temp.path().join("regular-file");
    write(&file, b"fixture")?;

    let Err(error) = inspect_path(file.join("child")) else {
        anyhow::bail!("traversing through a regular file should fail");
    };
    anyhow::ensure!(
        error.kind() != io::ErrorKind::NotFound,
        "traversal through a file should not be reported as absence, got {error:?}"
    );
    Ok(())
}

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: inspect_path_propagates_errors_other_than_not_found,try_is_file_propagates_errors_other_than_not_found

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== PR metadata and changed files =='
gh pr view 545 --repo leynos/netsuke \
  --json number,url,headRefName,baseRefName,updatedAt,files,statusCheckRollup \
  --jq '{
    number, url, headRefName, baseRefName, updatedAt,
    files: [.files[] | {path, additions, deletions}],
    checks: [.statusCheckRollup[]? | {name, status, conclusion, detailsUrl}]
  }'

echo '== Relevant files =='
fd -t f -a '^(manifest\.rs|tests\.rs|fs\.rs|lib\.rs|developers-guide\.md|Cargo\.toml|AGENTS\.md)$' \
  test_support docs . 2>/dev/null | sort

echo '== Manifest module outline =='
ast-grep outline test_support/src/manifest.rs --items all --view expanded 2>/dev/null || true

echo '== Manifest implementation and tests =='
cat -n test_support/src/manifest.rs 2>/dev/null || true
printf '\n== Separate manifest tests, if present ==\n'
cat -n test_support/src/manifest/tests.rs 2>/dev/null || true

echo '== Filesystem wrapper API =='
ast-grep outline test_support/src/fs.rs --items all --view expanded 2>/dev/null || true
rg -n -C 5 \
  'pub\s+(?:enum|fn)\s+(?:Target|.*metadata|.*exist|.*dir)|\b(?:metadata|symlink_metadata|exists|is_dir)\s*\(' \
  test_support/src/fs.rs test_support/src/manifest.rs 2>/dev/null || true

echo '== Public API exports and documentation =='
rg -n -C 5 \
  '\bensure_manifest_exists\b|no-clobber|noclobber|concurrent|raced|IsADirectory|manifest fixture' \
  test_support/src/lib.rs test_support/src/manifest.rs docs/developers-guide.md 2>/dev/null || true

echo '== Property-test dependency and manifest test usage =='
rg -n -C 4 \
  '^(proptest|rstest)\s*=|\bproptest!\s*\{|TestCaseError|ensure_manifest_exists(?:\s*\()?' \
  Cargo.toml test_support/Cargo.toml test_support/src/manifest.rs test_support/src/manifest/tests.rs 2>/dev/null || true

echo '== Linked issue =='
gh issue view 61 --repo leynos/netsuke --json number,state,title,url \
  --jq '{number,state,title,url}'

Length of output: 70971


@leynos, treat all six reported checks as resolved on the current PR branch.

Check Status Evidence
Testing (Overall) Resolved test_support/src/manifest/tests.rs uses the exported ensure_manifest_exists with a deterministic scoped pre-persist hook. It tests raced file and raced directory targets.
Unit Architecture Resolved test_support/src/fs.rs adds fallible PathState inspection. test_support/src/manifest.rs uses fs::inspect_path at target, persistence-race, parent, and ancestor decision points. It propagates errors other than NotFound.
Developer Documentation Resolved docs/developers-guide.md now has a test_support::ensure_manifest_exists section. It documents no-clobber persistence, raced files, raced directories, fallible inspection, staging, and the controlled-ordering limit.
Testing (Unit And Behavioural) Resolved The raced-file test asserts Ok(expected_path) and byte-identical competing contents. The raced-directory test asserts io::ErrorKind::IsADirectory. Both call ensure_manifest_exists.
Testing (Property / Proof) Resolved The bounded 16-case proptest! covers missing, existing file, raced file, existing directory, and raced directory states. It generates bounded staged and competing content. It states that it tests controlled creation orderings, not arbitrary scheduler or filesystem behaviour.
Concurrency And State Resolved The test-only hook runs after initial target inspection and before persist_noclobber. Its guard restores prior state on drop. Production builds do not contain the hook.

Do not create a new GitHub issue. Open issue #61 covers the work.

GitHub CI shows a successful build-test check and successful required build checks. The supplied evidence does not show direct exit statuses for make check-fmt, make lint, and make test. Report those command results in the PR description or a follow-up comment before merge if the repository requires explicit validation evidence.


✏️ Learnings added
Learnt from: leynos
URL: https://github.com/leynos/netsuke/pull/545

Timestamp: 2026-08-12T00:33:17.011Z
Learning: For PR reviews in `leynos/netsuke`, treat warnings as required work. Treat observability, code safety, documentation, and validation coverage as in scope. If required work is out of scope and no existing issue covers it, propose a GitHub follow-up issue.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

codescene-access[bot]

This comment was marked as outdated.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test_support/src/fs.rs`:
- Around line 131-164: Update the Rustdoc for inspect_path to explicitly state
that fs::metadata follows symlinks, so dangling symlinks are reported as
PathState::Absent even when the directory entry exists. Keep the implementation
unchanged and document this contract near the behavior description or examples.

In `@test_support/src/manifest.rs`:
- Around line 80-91: Update the Rustdoc for install_before_persist_hook to
explicitly state that the installed action is one-shot and is removed after
run_before_persist_hook executes, so it runs at most once per guard even across
multiple ensure_manifest_exists calls.
- Around line 183-190: Update both PathState::NonDirectory arms in
ensure_parent_directory (test_support/src/manifest.rs:183-190) and
find_existing_ancestor (test_support/src/manifest.rs:234-241) to return
io::ErrorKind::NotADirectory instead of AlreadyExists, preserving their existing
error messages and control flow.

In `@test_support/src/manifest/tests.rs`:
- Around line 120-170: Extend both raced persistence tests,
raced_file_manifest_path_is_returned_unchanged and
raced_directory_manifest_path_is_rejected, to assert that the temporary
workspace directory contains no leftover staged temporary file after
ensure_manifest_exists returns or errors. Use the existing temp_path workspace
root and directory-inspection utilities, while preserving the current content,
error-kind, and returned-path assertions.
- Around line 343-347: Update the TargetState::ExistingDirectory |
TargetState::RacedDirectory branch to validate result is an error with
prop_assert! before extracting it, replacing result.expect_err. Preserve the
existing IsADirectory and expected_path assertions after safely obtaining the
error.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dfc60452-dd35-4e7b-a09a-79a63e96e3a2

📥 Commits

Reviewing files that changed from the base of the PR and between 5cfed52 and 814c485.

📒 Files selected for processing (6)
  • Makefile
  • docs/developers-guide.md
  • test_support/src/fs.rs
  • test_support/src/fs_tests.rs
  • test_support/src/manifest.rs
  • test_support/src/manifest/tests.rs

Comment thread test_support/src/fs.rs
Comment thread test_support/src/manifest.rs
Comment thread test_support/src/manifest.rs
Comment thread test_support/src/manifest/tests.rs
Comment thread test_support/src/manifest/tests.rs
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@wafflecat-df12

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test_support/src/manifest.rs (1)

151-176: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the raced-target classification into a named helper.

persist_manifest_file now nests a three-arm PathState match inside a three-arm persistence match. The function mixes two responsibilities: persistence and post-race target classification. Extract the AlreadyExists handling into a helper so each function keeps one responsibility and the nesting stays shallow.

♻️ Proposed extraction
 fn persist_manifest_file(file: NamedTempFile, manifest_path: &Utf8Path) -> io::Result<()> {
     match file.persist_noclobber(manifest_path.as_std_path()) {
         Ok(_) => Ok(()),
-        Err(e) if e.error.kind() == io::ErrorKind::AlreadyExists => {
-            match inspect_manifest_target(manifest_path)? {
-                fs::PathState::Directory => Err(manifest_path_is_directory_error(manifest_path)),
-                fs::PathState::NonDirectory => Ok(()),
-                fs::PathState::Absent => Err(io::Error::new(
-                    io::ErrorKind::AlreadyExists,
-                    format!(
-                        "Manifest target disappeared after no-clobber persistence reported it existed: {manifest_path}"
-                    ),
-                )),
-            }
-        }
+        Err(e) if e.error.kind() == io::ErrorKind::AlreadyExists => {
+            accept_raced_manifest_target(manifest_path)
+        }
         Err(e) => Err(io::Error::new(
             e.error.kind(),
             format!(
                 "Failed to persist manifest file to {} from {}: {}",
                 manifest_path,
                 e.file.path().display(),
                 e.error
             ),
         )),
     }
 }
+
+/// Classify a target that no-clobber persistence reported as already present.
+fn accept_raced_manifest_target(manifest_path: &Utf8Path) -> io::Result<()> {
+    match inspect_manifest_target(manifest_path)? {
+        fs::PathState::Directory => Err(manifest_path_is_directory_error(manifest_path)),
+        fs::PathState::NonDirectory => Ok(()),
+        fs::PathState::Absent => Err(io::Error::new(
+            io::ErrorKind::AlreadyExists,
+            format!(
+                "Manifest target disappeared after no-clobber persistence reported it existed: {manifest_path}"
+            ),
+        )),
+    }
+}

As per path instructions, "Seek to keep the cognitive complexity of functions no more than 9", "Adhere to single responsibility and CQRS", and "Move conditionals with >2 branches into a predicate function".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test_support/src/manifest.rs` around lines 151 - 176, Extract the
AlreadyExists branch from persist_manifest_file into a named helper that accepts
manifest_path, performs the inspect_manifest_target classification, and returns
the corresponding io::Result. Replace the nested match arm with a call to this
helper, preserving the Directory, NonDirectory, and Absent behaviors and leaving
other persistence errors unchanged.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test_support/src/manifest/tests.rs`:
- Around line 342-396: Extract a shared conversion helper near the property test
that turns an error into TestCaseError::fail(error.to_string()), then replace
the repeated inline map_err closures throughout property_workspace and each
result branch with that helper. Preserve the existing error propagation and
assertion behavior while making each conversion a single helper call.

---

Outside diff comments:
In `@test_support/src/manifest.rs`:
- Around line 151-176: Extract the AlreadyExists branch from
persist_manifest_file into a named helper that accepts manifest_path, performs
the inspect_manifest_target classification, and returns the corresponding
io::Result. Replace the nested match arm with a call to this helper, preserving
the Directory, NonDirectory, and Absent behaviors and leaving other persistence
errors unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1f732dd7-edf4-49b4-a75c-5aad3a037484

📥 Commits

Reviewing files that changed from the base of the PR and between 5cfed52 and b11808b.

📒 Files selected for processing (6)
  • Makefile
  • docs/developers-guide.md
  • test_support/src/fs.rs
  • test_support/src/fs_tests.rs
  • test_support/src/manifest.rs
  • test_support/src/manifest/tests.rs

Comment thread test_support/src/manifest/tests.rs
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@leynos

leynos commented Aug 14, 2026

Copy link
Copy Markdown
Owner

@coderabbitai Have the following now been resolved?

test_support/src/manifest.rs (1)

151-176: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Extract the raced-target classification into a named helper.
persist_manifest_file now nests a three-arm PathState match inside a three-arm persistence match. The function mixes two responsibilities: persistence and post-race target classification. Extract the AlreadyExists handling into a helper so each function keeps one responsibility and the nesting stays shallow.

♻️ Proposed extraction
fn persist_manifest_file(file: NamedTempFile, manifest_path: &Utf8Path) -> io::Result<()> {
     match file.persist_noclobber(manifest_path.as_std_path()) {
         Ok(_) => Ok(()),
-        Err(e) if e.error.kind() == io::ErrorKind::AlreadyExists => {
-            match inspect_manifest_target(manifest_path)? {
-                fs::PathState::Directory => Err(manifest_path_is_directory_error(manifest_path)),
-                fs::PathState::NonDirectory => Ok(()),
-                fs::PathState::Absent => Err(io::Error::new(
-                    io::ErrorKind::AlreadyExists,
-                    format!(
-                        "Manifest target disappeared after no-clobber persistence reported it existed: {manifest_path}"
-                    ),
-                )),
-            }
-        }
+        Err(e) if e.error.kind() == io::ErrorKind::AlreadyExists => {
+            accept_raced_manifest_target(manifest_path)
+        }
         Err(e) => Err(io::Error::new(
             e.error.kind(),
             format!(
                 "Failed to persist manifest file to {} from {}: {}",
                 manifest_path,
                 e.file.path().display(),
                 e.error
             ),
         )),
     }
 }
+
+/// Classify a target that no-clobber persistence reported as already present.
+fn accept_raced_manifest_target(manifest_path: &Utf8Path) -> io::Result<()> {
+    match inspect_manifest_target(manifest_path)? {
+        fs::PathState::Directory => Err(manifest_path_is_directory_error(manifest_path)),
+        fs::PathState::NonDirectory => Ok(()),
+        fs::PathState::Absent => Err(io::Error::new(
+            io::ErrorKind::AlreadyExists,
+            format!(
+                "Manifest target disappeared after no-clobber persistence reported it existed: {manifest_path}"
+            ),
+        )),
+    }
+}

As per path instructions, "Seek to keep the cognitive complexity of functions no more than 9", "Adhere to single responsibility and CQRS", and "Move conditionals with >2 branches into a predicate function".

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @test_support/src/manifest.rs around lines 151 - 176, Extract the
AlreadyExists branch from persist_manifest_file into a named helper that accepts
manifest_path, performs the inspect_manifest_target classification, and returns
the corresponding io::Result. Replace the nested match arm with a call to this
helper, preserving the Directory, NonDirectory, and Absent behaviors and leaving
other persistence errors unchanged.

Source: Path instructions

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Current PR head =='
gh pr view 545 --repo leynos/netsuke --json headRefOid,updatedAt,url \
  --jq '{headRefOid, updatedAt, url}'

echo '== Manifest symbol outline =='
ast-grep outline test_support/src/manifest.rs --items all --view expanded

echo '== Persistence and raced-target classification =='
rg -n -C 20 \
  'fn\s+(persist_manifest_file|accept_raced_manifest_target|inspect_manifest_target|manifest_path_is_directory_error)\b|persist_noclobber' \
  test_support/src/manifest.rs

echo '== Current PR diff for manifest implementation =='
gh pr diff 545 --repo leynos/netsuke --patch -- test_support/src/manifest.rs

Length of output: 6527


@leynos test_support/src/manifest.rspersist_manifest_file, handle_raced_manifest_target

Treat this finding as resolved on the current PR head, 6012d57323b1250a29ca6d80f689648e87d2479b.

persist_manifest_file now delegates its io::ErrorKind::AlreadyExists branch to handle_raced_manifest_target at Lines 167-169.

handle_raced_manifest_target at Lines 80-91 owns the three-way fs::PathState classification:

  • Directory returns io::ErrorKind::IsADirectory.
  • NonDirectory returns Ok(()).
  • Absent returns io::ErrorKind::AlreadyExists with the existing disappearance message.

This preserves the required behaviour. It removes the nested three-arm match from persist_manifest_file. The helper name differs from the proposed accept_raced_manifest_target, but it accurately describes the same responsibility.

Do not make a further change for this finding. The source inspection confirms the extraction. The inspection sandbox did not run formatting, linting, or tests.

You are interacting with an AI system.

leynos and others added 8 commits August 14, 2026 14:18
Persist staged manifest fixtures without replacing an existing target.
Treat a concurrent creation as success and cover that no-clobber path.
Verify targets after no-clobber persistence reports `AlreadyExists` so
existing files remain acceptable while directory targets are rejected.

Share manifest test workspace setup and cover the raced-directory path.
Describe preservation of existing and concurrently created file targets,
directory rejection, atomic staging, and the controlled proof boundary.
Document the no-clobber contract and exercise all controlled target
creation orderings through a test-only persistence seam.

Keep generated staged content, existing files, and directory errors under
bounded property coverage without asserting arbitrary scheduler behaviour.
Inspect manifest paths through a fallible test-support filesystem wrapper
so inaccessible metadata is never mistaken for an absent target.

Exercise the exported manifest helper with a scoped pre-persist hook and
document the controlled no-clobber contract for fixture authors.
Share the file-traversal assertion while keeping the named tests for the
`inspect_path` and `try_is_file` public contracts. Forward Whitaker's root
configuration to its isolated driver so its documented scoped exclusions
remain effective.
Report non-directory parent components as `NotADirectory` and cover both
internal branches. Confirm raced persistence removes staged files, improve
the property assertion, and document the filesystem and hook contracts.
Centralize raced-target classification and property-test error conversion
without changing manifest creation or error-propagation behaviour.
@leynos
leynos force-pushed the issue-61-replace-namedtempfile-persist-with-more-explicit-file-creation-method branch from 6012d57 to aeacfc6 Compare August 14, 2026 12:24
codescene-access[bot]

This comment was marked as outdated.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No quality gates enabled for this code.

@leynos
leynos merged commit 69286cd into main Aug 14, 2026
17 checks passed
@leynos
leynos deleted the issue-61-replace-namedtempfile-persist-with-more-explicit-file-creation-method branch August 14, 2026 12:45
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.

Replace NamedTempFile::persist with more explicit file creation method

3 participants