Skip to content

Lower target and action deps (3.14.3)#315

Merged
leynos merged 29 commits into
mainfrom
3-14-3-lower-target-and-action-deps
May 31, 2026
Merged

Lower target and action deps (3.14.3)#315
leynos merged 29 commits into
mainfrom
3-14-3-lower-target-and-action-deps

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented May 23, 2026

Summary

This branch implements roadmap task (3.14.3) by lowering target and action deps into a dedicated implicit-dependency class in the IR and generated Ninja output. It keeps sources as the explicit recipe-input class used by $in and {{ ins }}, while deps now affect dependency ordering, rebuild decisions, and cycle detection without appearing in recipe arguments.

Roadmap task: (3.14.3)
Execplan: docs/execplans/3-14-3-lower-target-and-action-deps.md

Review walkthrough

Validation

  • cargo test --workspace: passed after the IR field addition.
  • cargo test --test ir_from_manifest_tests: passed with 15 tests after manifest lowering.
  • cargo test -p netsuke ir::cycle: passed after implicit-dependency cycle traversal.
  • cargo test --test ninja_gen_tests: passed after Ninja emission updates.
  • cargo test --test bdd_tests implicit: passed after behavioural coverage was added.
  • make check-fmt: passed in the final gate.
  • make lint: passed in the final gate.
  • make test: passed in the final gate.
  • make markdownlint: passed in the final gate.
  • make nixie: passed in the final gate.
  • coderabbit review --agent: completed with zero findings.

Notes

make fmt still triggers a pre-existing repository-wide Markdown formatting backlog and rewrites unrelated documentation before failing. This branch restored that unrelated churn and used the non-mutating gates above as the applicable completion checks.

No new external crate dependencies were added. The planned proptest coverage was replaced with deterministic bounded cycle coverage because proptest is not currently in the dependency graph and the execplan requires explicit approval before adding dependencies.

Summary by Sourcery

Lower manifest target and action deps into a dedicated implicit-dependency class in the IR and generated Ninja output, aligning cycle detection, recipe interpolation, tests, and documentation with the intended dependency semantics.

New Features:

  • Introduce implicit_deps on IR build edges to represent manifest deps separately from explicit sources inputs.
  • Support manifest deps for both targets and actions as implicit dependencies that appear in generated Ninja files using Ninja's | syntax.

Enhancements:

  • Update cycle detection to traverse both explicit inputs and implicit dependencies while keeping order-only dependencies excluded.
  • Preserve {{ ins }} and {{ outs }} placeholders through manifest rendering so recipe interpolation can expand only explicit sources and outputs.
  • Clarify and expand user, developer, design, and formal-verification documentation around dependency classes and cycle participation, and mark roadmap item 3.14.3 as complete.

Tests:

  • Add IR unit tests to ensure manifest deps populate implicit dependencies and do not affect explicit recipe inputs.
  • Extend cycle-detection tests to cover implicit-only, mixed, missing-implicit, and bounded small cycles.
  • Add Ninja generation tests and BDD scenarios validating implicit dependency lowering, Ninja edge formatting, and recipe interpolation behaviour.

Draft an execution plan for lowering target and action `deps` into a new
implicit-dependency class on the IR `BuildEdge` and emitting them as
Ninja `|` implicit dependencies between explicit inputs and `||`
order-only deps.

The plan records the cycle-participation decision (explicit inputs and
implicit deps participate; order-only deps do not), defers rule-level
`Rule.deps` rejection to roadmap item 3.14.6, and keeps action hashing
unchanged because `implicit_deps` lives on `BuildEdge` rather than
`Action`. Validation runs through `make check-fmt`, `make lint`,
`make test`, `make markdownlint`, `make nixie`, and `coderabbit review
--agent`.

The plan is DRAFT and must be approved before implementation begins.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 23, 2026

Review Change Stack

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: d7523cc5-0ef1-47b8-b141-f9308c58b3b5

📥 Commits

Reviewing files that changed from the base of the PR and between 2127534 and b37869f.

📒 Files selected for processing (4)
  • src/ir/cycle.rs
  • src/ir/from_manifest.rs
  • test_support/src/ninja_gen.rs
  • tests/ninja_gen_unit_tests.rs

Overview

This PR implements roadmap item 3.14.3: Lower target and action deps into implicit IR and Ninja edges, lowering manifest dependency fields into dedicated IR classes. The implementation introduces an implicit_deps field on BuildEdge that participates in cycle detection and rebuild ordering but is excluded from recipe arguments and interpolation. This distinguishes between explicit dependencies (sources passed to $in/{{ ins }}) and implicit dependencies (Ninja | syntax).

Design & Documentation

The PR includes a new execplan document (docs/execplans/3-14-3-lower-target-and-action-deps.md) capturing the observable gap that manifest Target.deps was rendered but discarded during IR generation, preventing impact on rebuild behaviour and cycle detection. The execplan specifies staging, validation, and API surface: adding BuildEdge.implicit_deps: Vec<Utf8PathBuf>, no new crate dependencies or CLI/config/schema changes.

Related design documents were updated:

  • netsuke-design.md: Adjusted BuildEdge struct documentation to clarify implicit_deps maps to Ninja | syntax
  • formal-verification-methods.md: Updated cycle-participation contract to explicitly include explicit inputs and implicit dependencies only, excluding order-only dependencies
  • users-guide.md: Clarified dependency semantics—sources as explicit recipe inputs passed to $in/{{ ins }} triggering rebuilds, deps as implicit dependencies triggering rebuilds but excluded from recipe arguments, order_only_deps affecting only build ordering
  • developers-guide.md: Added section describing IR dependency class lowering and cycle detection behaviour
  • roadmap.md: Marked 3.14.3 and all subtasks as complete
  • README.md: Updated to describe deps as implicit dependencies mapping to Ninja | operator

IR & Cycle Detection

The IR graph (src/ir/graph.rs) extends BuildEdge with the new implicit_deps field. Cycle detection (src/ir/cycle.rs) now traverses both inputs and implicit_deps while explicitly excluding order_only_deps. Missing-dependency logging was relocated from the cycle detector to its caller (src/ir/from_manifest.rs) to maintain side-effect-free analysis. The manifest-to-IR lowering (src/ir/from_manifest.rs) now converts manifest deps into implicit_deps and reports counts of unresolved dependencies via structured tracing events.

Recipe Rendering & Command Interpolation

Manifest rendering (src/manifest/render.rs) ensures recipe command templates always include ins and outs context by injecting placeholder tokens (__NETSUKE_INS_PLACEHOLDER__ / __NETSUKE_OUTS_PLACEHOLDER__) when absent, enabling later IR-level substitution. Command interpolation (src/ir/cmd_interpolate.rs) was refactored to handle both $in/$out and literal placeholder tokens while preserving backtick-delimited sections, with precomputed joined input/output strings.

Ninja Emission

Ninja generation (src/ninja_gen.rs) conditionally renders | <paths> segment for non-empty implicit_deps after explicit inputs and before order-only dependencies, implementing Ninja's implicit dependency syntax.

Testing

Comprehensive test coverage includes:

  • Unit tests for cycle detection with mixed explicit/implicit cycles and bounded cycle positions (src/ir/cycle.rs)
  • Manifest-to-IR tests asserting deps translation (tests/ir_from_manifest_tests.rs)
  • BDD steps for asserting target edge inputs and implicit_deps paths (tests/bdd/steps/ir.rs)
  • End-to-end Ninja snapshot test (tests/ninja_snapshot_tests.rs) validating | syntax presence
  • New Ninja unit test module (tests/ninja_gen_unit_tests.rs) with parameterised cases for dependency combinations
  • Test data fixture (tests/data/implicit_deps.yml) and BDD scenarios (tests/features/ninja.feature)
  • Test infrastructure (test_support/src/ninja_gen.rs) exposing shared NinjaIntegrationCase and assertion helpers for Ninja integration tests

All tests pass; no new external crates added.

Walkthrough

Lower manifest deps into IR BuildEdge.implicit_deps, include implicit deps in cycle detection traversal (excluding order-only deps), emit implicit deps in Ninja output using |, guarantee ins/outs placeholders during rendering for later interpolation, and extend tests and documentation accordingly.

Changes

Implicit dependencies implementation

Layer / File(s) Summary
IR data structure for implicit dependencies
src/ir/graph.rs, docs/netsuke-design.md
BuildEdge gains implicit_deps: Vec<Utf8PathBuf>; docs clarify mapping to Ninja `
Manifest to IR conversion: deps→implicit_deps
src/ir/from_manifest.rs
BuildGraph::process_targets extracts target.deps and converts them into implicit_deps; BuildEdge initialiser sets implicit_deps and logs computed counts.
Cycle detection including implicit dependencies
src/ir/cycle.rs, tests/ir_tests.rs
Extend cycle analysis to traverse edge.inputs chained with edge.implicit_deps while excluding order_only_deps; move logging to caller; add helpers and tests for implicit-only, mixed, bounded and missing-dep cases.
Template rendering and variable guarantees
src/manifest/render.rs
Introduce render_recipe_str_with to guarantee ins/outs keys by inserting placeholder tokens when absent; use helper for rule and target command rendering.
Command interpolation for placeholders and tokens
src/ir/cmd_interpolate.rs
Refactor find_substitution to resolve $in/$out (dollar-guarded with word boundaries) and __NETSUKE_INS_PLACEHOLDER__/__NETSUKE_OUTS_PLACEHOLDER__ tokens; precompute joined ins/outs; preserve braced templates inside backticks; add tests.
Ninja generation for implicit dependencies
src/ninja_gen.rs
DisplayEdge emits conditional `
Test support modules and fixtures
test_support/src/lib.rs, test_support/src/ninja_gen.rs
Expose test_support::ninja_gen; add AssertionType, NinjaIntegrationCase, and ninja_integration_setup() to support Ninja integration tests.
BDD feature scenarios and step support
tests/data/implicit_deps.yml, tests/features/ninja.feature, tests/bdd/steps/ir.rs
Add fixture implicit_deps.yml; add ninja.feature scenario validating implicit deps in generated Ninja; extend BDD step helpers to assert inputs and implicit_deps path lists.
Manifest-to-IR rstest cases
tests/ir_from_manifest_tests.rs
Add parametrised cases verifying target.deps and action.deps populate edge.implicit_deps (never edge.inputs); validate command interpolation uses only sources for $in.
Ninja generation unit tests and snapshots
tests/ninja_gen_unit_tests.rs, tests/ninja_snapshot_tests.rs
Add ninja_gen_unit_tests.rs with parameterised scenarios and implicit_deps_manifest_ninja snapshot test asserting Ninja `
Integration test refactor
tests/ninja_gen_integration_tests.rs
Refactor integration tests to use shared test_support::ninja_gen helpers; update BuildEdge initialisations to include implicit_deps: Vec::new().
Documentation, ExecPlan and roadmap
docs/developers-guide.md, docs/formal-verification-methods-in-netsuke.md, docs/users-guide.md, docs/execplans/3-14-3-lower-target-and-action-deps.md, docs/roadmap.md, README.md, tests/bdd_tests.rs
Publish ExecPlan 3.14.3; update developer/user/formal docs and README to clarify sources/deps/order_only_deps semantics and cycle-detection behaviour; mark roadmap task complete.

Possibly related PRs

  • leynos/netsuke#38: Related — both PRs touch src/ninja_gen.rs and the Ninja edge emission logic; #38 originally structured Ninja generation and this PR extends output to include implicit deps.

Poem

Implicit edges now found their way,
Through IR fields they'll always stay,
Cycle detectors walk their line,
Ninja pipes show them in time,
Placeholders wait to be replaced.

🚥 Pre-merge checks | ✅ 18 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
User-Facing Documentation ⚠️ Warning User-facing documentation (users-guide.md, README.md) clearly describes the new deps semantics, but the PR lacks a migration guide for the breaking change as required for pre-1.0.0 projects. Create a v0.2.0 migration guide documenting the deps field semantic change: from being silently ignored to being honoured as implicit dependencies in cycle detection and rebuild triggering.
Testing (Property / Proof) ⚠️ Warning Introduces algorithmic invariants (cycle traversal, Ninja ordering, interpolation) across variable input ranges but lacks property tests or formal verification. Add proptest-based generative tests for cycle detection (graph topology), Ninja ordering (edge configurations), and command interpolation (placeholder positions).
✅ Passed checks (18 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarises the main change: lowering manifest target and action deps into an implicit-dependency class, and includes the required roadmap item reference (3.14.3).
Description check ✅ Passed The PR description is directly related to the changeset, detailing the implementation of roadmap task 3.14.3, providing comprehensive context about IR lowering, cycle detection, Ninja generation, tests, and documentation updates.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Testing (Overall) ✅ Passed Tests assert exact Ninja syntax with | separator, cycle detection includes implicit_deps, IR tests verify deps→implicit_deps not inputs, snapshot test verifies | syntax presence.
Developer Documentation ✅ Passed Developer's guide documents IR dependency classes; roadmap 3.14.3 complete; execplan COMPLETE; design docs updated; modules documented; acronyms expanded; guides document dependency semantics.
Module-Level Documentation ✅ Passed All 217 Rust modules carry module-level documentation (//!) explaining purpose, utility, and relationships to other components.
Testing (Unit And Behavioural) ✅ Passed Tests verify meaningful local behaviour (cycle detection, interpolation, IR lowering), edge cases, error paths, and full workflow behavioural tests with explicit assertions on Ninja output syntax.
Testing (Compile-Time / Ui) ✅ Passed Snapshot test explicitly asserts Ninja | syntax with committed snapshot. Multiline script test includes detailed output assertions. Six command-interpolation tests cover key behaviours.
Unit Architecture ✅ Passed Cycle analysis remains side-effect-free; logging moved to command boundary; injectable dependencies; no global state; errors explicitly propagated.
Domain Architecture ✅ Passed IR domain model properly isolated from adapters; implicit_deps field expresses domain concept; cycle detection has no infrastructure dependencies; Ninja rendering cleanly separated.
Observability ✅ Passed Logging at decision points (IR lowering, cycle analysis) with contextual fields; analyser side-effect-free; no secrets; path cardinality bounded.
Security And Privacy ✅ Passed No secrets, injection risks, or sensitive data exposure detected. Paths properly quoted via shell_quote, error messages truncated, proper input validation.
Performance And Resource Use ✅ Passed Cycle detection remains O(V+E). New allocations bounded by input size: implicit_deps Vec, render-time Vars clones, Ninja join() operations match existing patterns.
Concurrency And State ✅ Passed PR introduces no concurrency patterns. BuildEdge.implicit_deps is immutable; BuildGraph owned; CycleDetector borrows immutably. State mutations occur only during construction; graphs immutable after.
Architectural Complexity And Maintainability ✅ Passed Implicit_deps field provides necessary semantic separation. Test support module consolidates existing code with documented reuse pattern. No circular dependencies, speculation, or duplication.
Rust Compiler Lint Integrity ✅ Passed PR introduces no new lint suppressions, preserves all helpers with real usage, maintains low clone density (0–3%), and reflects actual module boundaries.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 3-14-3-lower-target-and-action-deps

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

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

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 2500000 diff characters.

Please try again later or upgrade to continue using Sourcery

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos changed the title Plan: lower target and action deps (3.14.3) Lower target and action deps (3.14.3) May 24, 2026
leynos added 8 commits May 24, 2026 14:54
Introduce the `BuildEdge.implicit_deps` field and initialise it as
empty at every existing construction site.

Keep behaviour unchanged for this step so the later manifest lowering,
cycle detection, and Ninja rendering changes can be reviewed separately.
Populate `BuildEdge.implicit_deps` from manifest `deps` while keeping
`sources` as the only explicit recipe input class.

Preserve documented `{{ ins }}` and `{{ outs }}` recipe placeholders
through manifest rendering so IR interpolation can substitute them from
explicit inputs and outputs before action hashing.
Traverse `BuildEdge.implicit_deps` alongside explicit inputs during IR
cycle analysis while continuing to ignore order-only dependencies.

Add regression coverage for implicit-only cycles, mixed cycles, missing
implicit dependencies, and bounded small cycles without adding a new test
dependency.
Emit `BuildEdge.implicit_deps` with Ninja's single-pipe separator between
explicit inputs and order-only dependencies.

Cover explicit, implicit-only, order-only, and phony action edge shapes
so the separator ordering remains stable.
Add an end-to-end manifest fixture that lowers target and action `deps`
into IR implicit deps and renders them as Ninja single-pipe dependencies.

Assert that recipe interpolation still receives only explicit `sources`,
including the documented `{{ ins }}` placeholder path.
Record that manifest `sources` become explicit recipe inputs, `deps`
become implicit dependencies, and order-only dependencies remain outside
cycle detection.

Align the user, developer, design, and formal-verification notes with the
implemented IR and Ninja dependency classes.
Record the final deterministic validation state before CodeRabbit review
and note the existing Markdown formatting exception that prevents using
`make fmt` as an all-or-nothing gate for this branch.
Mark the implicit dependency lowering roadmap item complete after the
implementation, deterministic gates, and CodeRabbit review all passed.
Update the ExecPlan with the final review and validation state.
codescene-delta-analysis[bot]

This comment was marked as outdated.

Mark the ExecPlan complete after pushing the branch and refreshing the
draft pull request with the final validation and reviewer context.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 24, 2026

@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.

Code Duplication

src/ir/cycle.rs:

What lead to degradation?

The module contains 2 functions with similar structure: tests.cycle_detector_records_missing_dependencies,tests.cycle_detector_records_missing_implicit_dependencies

Why does this problem occur?

Duplicated code often leads to code that's harder to change since the same logical change has to be done in multiple functions. More duplication gives lower code health.

How to fix it?

A certain degree of duplicated code might be acceptable. The problems start when it is the same behavior that is duplicated across the functions in the module, ie. a violation of the Don't Repeat Yourself (DRY) principle. DRY violations lead to code that is changed together in predictable patterns, which is both expensive and risky. DRY violations can be identified using CodeScene's X-Ray analysis to detect clusters of change coupled functions with high code similarity. Read More
Once you have identified the similarities across functions, look to extract and encapsulate the concept that varies into its own function(s). These shared abstractions can then be re-used, which minimizes the amount of duplication and simplifies change.

@coderabbitai

This comment was marked as resolved.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 25, 2026

@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.

Code Duplication

src/ir/cycle.rs:

What lead to degradation?

The module contains 2 functions with similar structure: tests.cycle_detector_records_missing_dependencies,tests.cycle_detector_records_missing_implicit_dependencies

Why does this problem occur?

Duplicated code often leads to code that's harder to change since the same logical change has to be done in multiple functions. More duplication gives lower code health.

How to fix it?

A certain degree of duplicated code might be acceptable. The problems start when it is the same behavior that is duplicated across the functions in the module, ie. a violation of the Don't Repeat Yourself (DRY) principle. DRY violations lead to code that is changed together in predictable patterns, which is both expensive and risky. DRY violations can be identified using CodeScene's X-Ray analysis to detect clusters of change coupled functions with high code similarity. Read More
Once you have identified the similarities across functions, look to extract and encapsulate the concept that varies into its own function(s). These shared abstractions can then be re-used, which minimizes the amount of duplication and simplifies change.

@coderabbitai

This comment was marked as resolved.

Remove duplicated `CycleDetector` setup from the missing dependency tests
by sharing the assertion skeleton inside the cycle test module.
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 26, 2026

@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.

Code Duplication

src/ir/cycle.rs:

What lead to degradation?

The module contains 2 functions with similar structure: tests.cycle_detector_records_missing_dependencies,tests.cycle_detector_records_missing_implicit_dependencies

Why does this problem occur?

Duplicated code often leads to code that's harder to change since the same logical change has to be done in multiple functions. More duplication gives lower code health.

How to fix it?

A certain degree of duplicated code might be acceptable. The problems start when it is the same behavior that is duplicated across the functions in the module, ie. a violation of the Don't Repeat Yourself (DRY) principle. DRY violations lead to code that is changed together in predictable patterns, which is both expensive and risky. DRY violations can be identified using CodeScene's X-Ray analysis to detect clusters of change coupled functions with high code similarity. Read More
Once you have identified the similarities across functions, look to extract and encapsulate the concept that varies into its own function(s). These shared abstractions can then be re-used, which minimizes the amount of duplication and simplifies change.

@coderabbitai

This comment was marked as resolved.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 26, 2026

@coderabbitai Please investigate the cause of the following issue using codegraph exploration and research, identify a fix and provide an AI coding agent prompt for the fix:

error: this argument is passed by value, but not consumed in the function body
Error:    --> src/ir/cycle.rs:179:18
    |
179 |         targets: HashMap<Utf8PathBuf, BuildEdge>,
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
    = note: requested on the command line with `-D clippy::needless-pass-by-value`
help: consider taking a reference instead
    |
179 |         targets: &HashMap<Utf8PathBuf, BuildEdge>,
    |                  +

error: could not compile `netsuke` (lib test) due to 1 previous error
warning: build failed, waiting for other jobs to finish...
make: *** [Makefile:32: lint] Error 101
Error: Process completed with exit code 2.

@coderabbitai

This comment was marked as resolved.

Pass the test target map by reference so Clippy does not flag the helper
for taking ownership of data it only inspects.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos marked this pull request as ready for review May 27, 2026 19:33
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented May 27, 2026

Reviewer's Guide

Implements roadmap task 3.14.3 by introducing an explicit implicit-dependency class (implicit_deps) on IR build edges, wiring manifest deps into it, making cycle detection traverse it, emitting it as Ninja implicit dependencies, and updating tests and docs to reflect the new dependency and recipe-interpolation contracts.

File-Level Changes

Change Details Files
Extend the IR build graph to represent implicit dependencies separately from explicit inputs.
  • Add implicit_deps: Vec<Utf8PathBuf> to BuildEdge in the IR graph, documented as implicit dependencies that do not enter recipes.
  • Update all BuildEdge construction sites in tests and doctests to initialise implicit_deps (usually Vec::new()).
  • Clarify the BuildEdge struct documentation in docs/netsuke-design.md to describe inputs as explicit recipe inputs and implicit_deps as Ninja `
deps kept separate from$in/{{ ins }}`.
Lower manifest-level deps into the new IR implicit-dependency field without affecting explicit recipe inputs or interpolation.
  • In manifest-to-IR lowering, convert Target.deps to paths and populate BuildEdge.implicit_deps alongside existing inputs and order_only_deps.
  • Add unit tests to assert that deps from both targets and actions populate implicit_deps, that inputs reflect only sources, and that phony flags are preserved.
  • Ensure manifest rendering preserves {{ ins }} and {{ outs }} placeholders by wrapping recipe rendering in a helper that injects default placeholder values, so later IR interpolation can substitute them correctly from explicit inputs/outputs.
  • Add tests to verify that recipe commands interpolate $in and {{ ins }} using only sources, and that deps never appear in the command text while still populating implicit_deps.
  • Add BDD steps and a manifest fixture to assert graph-level inputs vs implicit_deps for targets and actions and to verify Ninja output and recipe interpolation behaviour end-to-end.
src/ir/from_manifest.rs
tests/ir_from_manifest_tests.rs
src/manifest/render.rs
tests/bdd/steps/ir.rs
tests/features/ninja.feature
tests/data/implicit_deps.yml
Update cycle detection so that implicit dependencies participate in cycle discovery and missing-dependency reporting.
  • Change the cycle detector to traverse both inputs and implicit_deps when visiting dependencies of a node.
  • Refactor and extend cycle tests to construct edges with both classes, including helpers to build edges, insert cycle edges, and assert missing dependencies.
  • Add tests for cycles that use only implicit deps, mixed explicit/implicit cycles, and bounded cycles of varying lengths where one edge uses an implicit dep, ensuring they are all detected.
  • Add tests ensuring missing dependencies are recorded for both explicit and implicit deps without affecting other behaviour.
src/ir/cycle.rs
Emit Ninja build lines that include implicit dependencies in the correct position and document the dependency-class and cycle contracts.
  • Update Ninja edge formatting to render `
<implicit_deps>after explicit inputs but before

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

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 31, 2026

@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.

❌ Failed checks (1 error, 2 warnings)

Check name Status Explanation Resolution
Module-Level Documentation ❌ Error Two test modules lack docstring descriptions of relationships: tests/ir_tests.rs and tests/ninja_gen_tests.rs do not document what modules they test. Add cross-module relationships to test module docstrings: reference netsuke::ir module for ir_tests and crate::ir::BuildGraph conversion for ninja_gen_tests.
Testing (Property / Proof) ⚠️ Warning PR introduces cycle-detection and Ninja syntax invariants over arbitrary graph orderings without property-based testing; recommend proptest for generative coverage. Add proptest property tests for: cycle detection across arbitrary graph sizes/orderings, Ninja separator ordering invariant, command interpolation token matching correctness.
Observability ⚠️ Warning Missing dependencies are logged individually during traversal but missing summary event logging when no cycle is detected, leaving unresolved implicit deps invisible in normal builds. Add structured logging in detect_cycles to emit all collected missing_dependencies at info level, ensuring visibility of unresolved dependencies even without cycle errors.

@coderabbitai

This comment was marked as resolved.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 31, 2026

@coderabbitai please create a new GitHub issue for the following:

Testing (Property / Proof) ⚠️ Out of scope for this PR

Adding proptest requires adding the proptest crate as a dev-dependency, designing property-based test strategies for BuildGraph construction, and writing generative tests for cycle detection, Ninja separator ordering, and command interpolation. This is non-trivial and independent of the current roadmap item. Raise it as a follow-up GitHub issue.


Note that proptest is an approved dependency for df12 Productions Rust codebases and property tests should be considered de rigueur.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 31, 2026

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh issue create \
  --repo leynos/netsuke \
  --title "Add proptest property-based tests for cycle detection, Ninja separator ordering, and command interpolation" \
  --assignee leynos \
  --body "## Summary

Property-based tests using \`proptest\` are de rigueur for df12 Productions Rust codebases. This issue tracks adding generative coverage for three invariants introduced or extended by PR `#315`.

## Background

PR `#315` (lower manifest target and action \`deps\` into an explicit implicit-dependency class in the IR and generated Ninja output) introduced new failure modes in cycle detection (traversing \`implicit_deps\` as well as \`inputs\`), a new Ninja separator ordering contract (\`inputs\`\`| implicit_deps\`\`|| order_only_deps\`), and extended command interpolation with new placeholder tokens. These invariants are currently covered only by hand-written example-based tests; generative coverage is absent.

## Work required

1. **Add \`proptest\` dev-dependency** to \`Cargo.toml\`.

2. **Cycle detection** — write property tests asserting:
   - A graph with no back-edges never produces a cycle regardless of node count or edge ordering.
   - Any graph with a back-edge always produces a cycle.
   - Missing-dependency records are a subset of edges whose targets are absent from the target map.
   - Results are stable across arbitrary \`HashMap\` insertion orderings.

3. **Ninja separator ordering** — write property tests asserting:
   - For any \`BuildEdge\` with non-empty \`inputs\`, \`implicit_deps\`, and \`order_only_deps\`, the emitted line always places \`|\` after explicit inputs and before \`||\`.
   - The separator is absent when \`implicit_deps\` is empty.

4. **Command interpolation** — write property tests asserting:
   - \`\$in\`/\`\$out\` tokens inside backtick-delimited regions are never substituted.
   - \`__NETSUKE_INS_PLACEHOLDER__\`/\`__NETSUKE_OUTS_PLACEHOLDER__\` outside backticks are always replaced.
   - Tokens inside backticks are always preserved verbatim.

## References

- PR `#315`: https://github.com/leynos/netsuke/pull/315
- Raised by `@leynos`
"

Length of output: 2212


🏁 Script executed:

#!/bin/bash
gh issue list --repo leynos/netsuke --limit 5 --json number,title,url | jq -r '.[] | "\(.number) \(.url) \(.title)"'

Length of output: 784



@leynos Issue #322 has been opened: Add proptest property-based tests for cycle detection, Ninja separator ordering, and command interpolation.

It covers:

  • Adding proptest as a dev-dependency.
  • Cycle detection — generative graph construction asserting acyclic/cyclic invariants and missing-dependency subset properties across arbitrary HashMap orderings.
  • Ninja separator ordering — asserting | placement relative to explicit inputs and || for arbitrary BuildEdge combinations.
  • Command interpolation — asserting backtick-region preservation and placeholder substitution correctness across arbitrary input strings.

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

Timestamp: 2026-05-31T02:30:40.322Z
Learning: In the leynos/netsuke repository, `proptest` is an approved dev-dependency for df12 Productions Rust codebases, and property-based tests are considered de rigueur. When flagging missing property-based test coverage, always raise a GitHub issue rather than treating it as optional or out of scope.

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.

Expand the ninja generation test module docs to name the production module and IR type under test.

Emit structured info events for unresolved dependencies during cycle detection so missing external-file assumptions are visible on the successful path.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 31, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 31, 2026

✅ Actions performed

Review triggered.

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
tests/ninja_gen_tests.rs (1)

1-547: ⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Decompose test module to satisfy file-length limit.

This file has 547 lines, violating the coding guideline that files must not exceed 400 lines. As per coding guidelines, Python functions should use snake_case.

Split the module by test category:

  1. Move unit generation tests (generate_ninja_scenarios, generate_empty_graph, generate_multiline_script_snapshot) into tests/ninja_gen_unit_tests.rs.
  2. Keep integration tests (ninja_integration_tests) and error-path tests in this file, renaming to tests/ninja_gen_integration_tests.rs.
  3. Extract shared fixtures (ninja_integration_setup, NinjaIntegrationCase, AssertionType) into a test_support::ninja_gen module if not already present.

As per coding guidelines, files must not exceed 400 lines; large modules must be decomposed.

🤖 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 `@tests/ninja_gen_tests.rs` around lines 1 - 547, This test module exceeds the
400-line limit; split it by moving unit-focused tests (generate_ninja_scenarios,
generate_empty_graph, generate_multiline_script_snapshot) into a new unit test
file and keep integration/error tests (ninja_integration_tests,
errors_when_action_missing, generate_format_error) in a separate integration
test file; extract and reuse the shared setup and types
(ninja_integration_setup, NinjaIntegrationCase, AssertionType) into a shared
test_support::ninja_gen module so both new files can import them, and ensure
each resulting Rust test file remains under 400 lines.
🤖 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.

Outside diff comments:
In `@tests/ninja_gen_tests.rs`:
- Around line 1-547: This test module exceeds the 400-line limit; split it by
moving unit-focused tests (generate_ninja_scenarios, generate_empty_graph,
generate_multiline_script_snapshot) into a new unit test file and keep
integration/error tests (ninja_integration_tests, errors_when_action_missing,
generate_format_error) in a separate integration test file; extract and reuse
the shared setup and types (ninja_integration_setup, NinjaIntegrationCase,
AssertionType) into a shared test_support::ninja_gen module so both new files
can import them, and ensure each resulting Rust test file remains under 400
lines.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 70d335c7-a1de-4277-90cd-4fe480b80cf4

📥 Commits

Reviewing files that changed from the base of the PR and between 54bdd8a and 84eca2b.

📒 Files selected for processing (2)
  • src/ir/from_manifest.rs
  • tests/ninja_gen_tests.rs

Move pure Ninja rendering coverage into a dedicated unit test
module and leave integration/error-path coverage in a smaller
integration module.

Extract the shared Ninja integration case types and setup helper into
`test_support::ninja_gen` so the split modules stay below the file-size
limit while preserving the existing scenarios and snapshot coverage.
codescene-delta-analysis[bot]

This comment was marked as outdated.

Replace the duplicated missing-dependency test bodies with one
`rstest` table that feeds shared data into `assert_missing_deps`.
Keep CycleDetector setup and missing-dependency assertions in the helper.
codescene-delta-analysis[bot]

This comment was marked as outdated.

Clarify that generated Ninja files are written by runner and manifest
subcommands for downstream execution by the Ninja build system.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 31, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 31, 2026

✅ Actions performed

Review triggered.

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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/ninja_gen.rs`:
- Around line 1-2: Update the module doc comment at the top of
test_support/src/ninja_gen.rs to expand purpose, utility and relationships:
describe the module as "Shared fixtures and case types for Ninja generation
tests", list and briefly explain the exported items (`AssertionType`,
`NinjaIntegrationCase`, `ninja_integration_setup`), state how they integrate
with rstest (e.g., use with #[case::...] parameterisation) and with
test_support::ninja::ninja_integration_workspace for conditional Ninja
availability, and add a short usage pattern or example showing typical test flow
(construct a `NinjaIntegrationCase`, use `ninja_integration_setup` fixture, run
ninja and assert via `AssertionType`); ensure the docstring replaces the
existing one-line comment and covers purpose, utility, relationships and a
typical usage snippet.

In `@tests/ninja_gen_unit_tests.rs`:
- Around line 209-212: Replace assertions that use ensure! comparing generated
manifests with expected values (e.g., ensure!(ninja == expected, "generated
ninja manifest did not match expectation")) with assert_eq! so failures show
diffs; specifically change the ensure! that compares the variables ninja and
expected to assert_eq!(ninja, expected) (you may keep the optional message as a
third argument if desired) and apply the same replacement to the other ensure!
usages in this test file that validate generated output (the occurrences noted
around the other assertions).
🪄 Autofix (Beta)

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: fa9f242a-cfe2-44e5-ad88-095e059496c6

📥 Commits

Reviewing files that changed from the base of the PR and between 84eca2b and 2127534.

⛔ Files ignored due to path filters (1)
  • tests/snapshots/ninja/ninja_gen_unit_tests__multiline_script_ninja.snap is excluded by !**/*.snap
📒 Files selected for processing (6)
  • src/ir/cycle.rs
  • src/ninja_gen.rs
  • test_support/src/lib.rs
  • test_support/src/ninja_gen.rs
  • tests/ninja_gen_integration_tests.rs
  • tests/ninja_gen_unit_tests.rs

Comment thread test_support/src/ninja_gen.rs
Comment thread tests/ninja_gen_unit_tests.rs Outdated
Document the exported Ninja generation test helpers, their relationship to
`rstest` parameterisation, and conditional Ninja workspace setup.

Use assertions for generated Ninja output checks so failed comparisons show
clearer diagnostics while keeping fallible generation errors explicit.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 31, 2026

@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.

❌ Failed checks (2 errors, 3 warnings)

Check name Status Explanation Resolution
Module-Level Documentation ❌ Error test_support/src/ninja_gen.rs module doc lacks utility/function explanation, exported-type enumeration, and relationship description to rstest and test_support::ninja. Expand test_support/src/ninja_gen.rs module doc to enumerate exported types (AssertionType, NinjaIntegrationCase, ninja_integration_setup), describe utility/function, and document integration with rstest and test_support::ninja.
Unit Architecture ❌ Error Logging side-effects hidden in cycle::analyse query path; missing-dependencies logged twice (in cycle.rs and from_manifest.rs); module doc incomplete per review guidelines. Move missing-dependency logging out of cycle.rs into from_manifest.rs caller. Document cycle::analyse side-effects explicitly. Expand test_support/src/ninja_gen.rs module doc per code standards to enumerate types and usage.
Developer Documentation ⚠️ Warning test_support/src/ninja_gen.rs module doc is inadequate per review feedback requesting expanded documentation enumerating exports and usage patterns. Expand test_support/src/ninja_gen.rs module doc to enumerate AssertionType, NinjaIntegrationCase, ninja_integration_setup, describe rstest/test_support::ninja integration, and add usage context per review recommendations.
Testing (Property / Proof) ⚠️ Warning PR introduces invariants (cycle detection, Ninja ordering, backtick preservation, field mapping) but uses only fixed test cases; no proptest coverage despite acknowledging deferral to follow-up. Add proptest property tests for cycle detection, Ninja ordering, backtick preservation, and field mapping invariants per acknowledged follow-up GitHub issue.
Observability ⚠️ Warning Per-missing-dependency info logging added but review-requested summary logging in detect_cycles omitted; no metrics for implicit_deps processing. Add summary count logging at end of detect_cycles() to surface unresolved implicit deps without cycles. Add success-path logging for processed implicit_deps count in process_targets().

@coderabbitai

This comment was marked as resolved.

Keep cycle analysis side-effect-free by removing logging from the
missing-dependency recorder and documenting that callers own observation.

Add caller-side summary and implicit dependency count events so IR lowering
remains observable without double-logging missing dependencies.
@leynos leynos merged commit 45a7d95 into main May 31, 2026
17 checks passed
@leynos leynos deleted the 3-14-3-lower-target-and-action-deps branch May 31, 2026 20:27
@lodyai lodyai Bot mentioned this pull request May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant