Skip to content

Implement IR generation and validation#33

Merged
leynos merged 6 commits intomainfrom
codex/implement-ir-transformation-logic
Jul 27, 2025
Merged

Implement IR generation and validation#33
leynos merged 6 commits intomainfrom
codex/implement-ir-transformation-logic

Conversation

@leynos
Copy link
Owner

@leynos leynos commented Jul 26, 2025

Summary

  • implement BuildGraph::from_manifest with action deduplication
  • add IrGenError for validation failures
  • document hashing strategy and multiple rule limitation
  • mark IR generation tasks as done in roadmap
  • test IR generation with rstest and cucumber

Testing

  • make fmt
  • make lint
  • make test
  • make markdownlint
  • make nixie (fails: too many arguments)

https://chatgpt.com/codex/tasks/task_e_68852d434cc88322b61cbab22ab5ca68

Summary by Sourcery

Implement IR generation pipeline by transforming parsed manifests into BuildGraph instances with rule deduplication, validation, and action hashing, alongside comprehensive tests and documentation updates.

New Features:

  • Introduce BuildGraph::from_manifest to convert Netsuke manifests into IR with action deduplication
  • Add IrGenError enum to report validation failures for missing rules, multiple rules per target, and duplicate outputs
  • Generate deterministic action identifiers by hashing recipes and metadata with SHA-256
  • Provide Cucumber and rstest-based integration tests covering IR generation success and failure scenarios

Enhancements:

  • Mark IR generation tasks as completed in the project roadmap
  • Document the hashing strategy, multiple rule limitation, and duplicate output rejection in design documentation

Build:

  • Add thiserror and sha2 dependencies for error handling and hashing

CI:

  • Update CodeScene coverage upload action to v1.2.1 and adjust token usage in CI workflow

Documentation:

  • Update roadmap and netsuke-design docs with IR generation details and validation rules

Tests:

  • Add unit tests for hash_action stability and BuildGraph::from_manifest behavior under valid and invalid manifests
  • Add Cucumber steps and feature scenarios for IR generation compilation and error conditions

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jul 26, 2025

Reviewer's Guide

This PR fleshes out IR generation by implementing BuildGraph::from_manifest (with rule-to-action mapping, deduplication via SHA-256 hashing, and validation of rule presence, single-rule enforcement, and duplicate outputs), introduces IrGenError for clear failure reporting, and updates documentation, tests, and CI accordingly.

File-Level Changes

Change Details Files
Implement BuildGraph::from_manifest with action deduplication and target processing
  • Add from_manifest entry point managing rules, targets, and defaults
  • Implement process_rules, process_targets (with error propagation), and process_defaults
  • Enforce DuplicateOutput error on redefined outputs
src/ir.rs
Introduce IrGenError enum for validation failures
  • Define RuleNotFound, MultipleRules, and DuplicateOutput variants
  • Use IrGenError in target processing to surface invalid or ambiguous references
src/ir.rs
Deduplicate actions using SHA-256 hashing
  • Add register_action and hash_action functions
  • Implement hash_recipe and helper functions to serialize action metadata
  • Add sha2 and thiserror dependencies in Cargo.toml
src/ir.rs
Cargo.toml
Expand and add IR generation tests
  • Add rstest unit tests for hashing stability and IR scenarios
  • Add Cucumber step definitions and feature scenarios covering success and failure cases
  • Include integration tests in tests/ir_from_manifest_tests.rs and data fixtures for various manifest errors
tests/steps/ir_steps.rs
tests/features/ir.feature
tests/ir_from_manifest_tests.rs
tests/data/*
Update documentation to reflect completed IR work and constraints
  • Mark IR generation tasks as done in roadmap.md
  • Document hashing strategy, multiple-rule limitation, and duplicate output behavior in netsuke-design.md
docs/roadmap.md
docs/netsuke-design.md
Adjust CI workflow for coverage upload
  • Bump upload-codescene-coverage action version
  • Switch to env-based token lookup and conditional execution
.github/workflows/ci.yml

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 26, 2025

Summary by CodeRabbit

New Features

  • Added the ability to transform manifests into build graphs with robust error handling for missing rules, multiple rules per target, and duplicate outputs.

Bug Fixes

  • Improved error reporting for invalid build configurations, such as missing or duplicate rules and outputs.

Documentation

  • Expanded documentation to clarify action deduplication, rule referencing limitations, and output uniqueness.
  • Updated roadmap to reflect completed tasks related to intermediate representation and validation.

Tests

  • Introduced new test scenarios and data covering rule deduplication, error conditions, and manifest compilation.
  • Added step definitions for verifying build graph generation and error handling in feature tests.

Chores

  • Updated CI workflow for improved token handling and action versioning.

Walkthrough

Update the intermediate representation (IR) generation by introducing error handling for missing and multiple rules, action deduplication via SHA-256 hashing, and conversion from manifest to build graph. Extend documentation to clarify IR behaviour and update the roadmap. Add comprehensive tests, new test data, and supporting dependencies.

Changes

File(s) Change Summary
Cargo.toml Added thiserror and sha2 crates as dependencies.
docs/netsuke-design.md, docs/roadmap.md Clarified IR deduplication, error handling, and marked IR-related roadmap tasks as complete.
src/ir.rs Introduced IrGenError, added BuildGraph::from_manifest, implemented action deduplication and error reporting for IR generation.
tests/data/duplicate_rules.yml, tests/data/missing_rule.yml, tests/data/duplicate_outputs.yml, tests/data/multiple_rules_per_target.yml Added test manifests for duplicate, missing rule, duplicate output, and multiple rules per target scenarios.
tests/features/ir.feature Added scenarios for rule deduplication, missing rule error, multiple rules per target error, duplicate outputs error, and manifest compilation.
tests/ir_from_manifest_tests.rs Added tests for IR generation, deduplication, and error handling using new test data.
tests/steps/ir_steps.rs Added step definition for compiling manifests and capturing errors in test world state.
.github/workflows/ci.yml Updated CodeScene coverage upload step to use environment variable and updated action version.

Sequence Diagram(s)

sequenceDiagram
    participant Tester
    participant ManifestLoader
    participant BuildGraph
    participant Error

    Tester->>ManifestLoader: Load manifest from file
    ManifestLoader->>BuildGraph: from_manifest(manifest)
    BuildGraph->>BuildGraph: Deduplicate actions (SHA-256 hash)
    BuildGraph->>BuildGraph: Validate rule references
    alt Rule missing or multiple rules
        BuildGraph->>Error: Return IrGenError
    else Success
        BuildGraph->>Tester: Return BuildGraph
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~18 minutes

Possibly related PRs

Suggested reviewers

  • codescene-delta-analysis

Poem

In the land where rules may hide or double,
The graph now knows to spot the trouble.
With hashes strong and errors clear,
The IR’s path is crystal here.
Tests parade in tidy rows,
As manifest logic proudly grows! 🚦🦀

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • SHA-256: Entity not found: Issue - Could not find referenced Issue.

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 889f022 and ca270c8.

📒 Files selected for processing (5)
  • src/ir.rs (1 hunks)
  • tests/data/multiple_rules_per_target.yml (1 hunks)
  • tests/features/ir.feature (1 hunks)
  • tests/ir_from_manifest_tests.rs (1 hunks)
  • tests/steps/ir_steps.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

⚙️ CodeRabbit Configuration File

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.

Files:

  • tests/steps/ir_steps.rs
  • tests/ir_from_manifest_tests.rs
  • src/ir.rs
🧠 Learnings (1)
src/ir.rs (1)

Learnt from: CR
PR: leynos/netsuke#0
File: docs/netsuke-design.md:0-0
Timestamp: 2025-07-20T02:49:08.228Z
Learning: Applies to docs/src/ir.rs : The AST-to-IR transformation logic must be implemented in a function (e.g., ir::from_manifest) that consumes a NetsukeManifest and produces a BuildGraph, performing validation such as rule existence and cycle detection.

🧬 Code Graph Analysis (1)
tests/steps/ir_steps.rs (2)
src/manifest.rs (1)
  • from_path (51-56)
src/ir.rs (1)
  • from_manifest (98-107)
🔇 Additional comments (19)
tests/data/multiple_rules_per_target.yml (1)

1-19: LGTM! Test data correctly represents the multiple rules per target error case.

The manifest properly defines a scenario where a single target references multiple rules, which should trigger the expected IrGenError::MultipleRules error during IR generation.

tests/steps/ir_steps.rs (2)

30-48: Well-structured step definition for manifest compilation.

The implementation properly handles both success and failure cases, correctly managing the world state. The Clippy suppression is appropriately justified for Cucumber's requirements.


50-56: Clear and focused assertion for IR generation failures.

The assertion correctly verifies that an error occurred during IR generation.

tests/features/ir.feature (1)

9-30: Comprehensive test coverage for IR generation scenarios.

The feature tests effectively cover all success and error paths for manifest compilation, including deduplication behaviour and all three error variants (missing rule, multiple rules, duplicate outputs).

tests/ir_from_manifest_tests.rs (1)

1-44: Well-structured unit tests with comprehensive coverage.

The tests effectively validate all aspects of IR generation, including success cases and all error variants. The use of rstest and proper error assertions follows best practices.

src/ir.rs (14)

75-89: Well-designed error types with clear, actionable messages.

The IrGenError enum properly covers all validation failures during IR generation. The error messages provide context about both the target and the specific issue.


91-107: Clean orchestration of manifest transformation.

The refactored from_manifest method effectively delegates to focused helper methods, addressing the previous complexity concerns while maintaining clear error propagation.


109-118: Focused rule processing with proper deduplication.

The helper correctly processes rules and builds the rule mapping for later reference.


120-166: Robust target processing with proper validation.

The implementation correctly handles rule resolution, action registration, and enforces output uniqueness. The duplicate output check prevents conflicts in the build graph.


168-173: Simple and correct default target processing.


175-191: Clean action registration with deduplication.

The function properly handles action deduplication through the hash-based entry mechanism.


193-203: Well-structured hashing with proper delegation.

The refactored implementation effectively reduces complexity while maintaining hash stability. The documentation note about format sensitivity is valuable.


205-220: Distinct recipe type handling with proper prefixes.

The use of different prefixes for each recipe type ensures hash uniqueness across types.


222-228: Consistent optional field hashing.


230-240: Proper handling of StringOrList variants.

The length-prefixing ensures unambiguous hashing of rule references.


242-250: Deterministic optional string hashing.

The "1"/"0" prefix clearly distinguishes present vs absent values.


252-256: Implement length-prefixing to avoid hash collisions.

The length-prefix pattern prevents ambiguity when hashing concatenated strings.


258-279: Useful utility functions for IR conversion.

The helper functions provide clean conversions between AST types and IR representations.


281-312: Comprehensive hash stability tests.

The tests verify that the hashing implementation produces consistent, expected results for different action configurations.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/implement-ir-transformation-logic

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (1 file with Complex Method, Large Method)

Gates Passed
5 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
ir.rs 2 advisory rules 10.00 → 9.29 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

Copy link
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.

Hey @leynos - I've reviewed your changes - here's some feedback:

  • hash_action uses simple concatenation which can lead to accidental hash collisions; consider inserting explicit separators or length prefixes between fields and list items.
  • The logic for creating and hashing actions in both the rule and command/script branches is nearly identical—extract it into a helper to reduce duplication.
  • graph.targets.insert silently overwrites existing edges for duplicate outputs; consider detecting and erroring on duplicate target outputs instead of clobbering previous entries.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- hash_action uses simple concatenation which can lead to accidental hash collisions; consider inserting explicit separators or length prefixes between fields and list items.
- The logic for creating and hashing actions in both the rule and command/script branches is nearly identical—extract it into a helper to reduce duplication.
- graph.targets.insert silently overwrites existing edges for duplicate outputs; consider detecting and erroring on duplicate target outputs instead of clobbering previous entries.

## Individual Comments

### Comment 1
<location> `src/ir.rs:113` </location>
<code_context>
+            rule_map.insert(rule.name.clone(), hash);
+        }
+
+        for target in manifest.actions.iter().chain(&manifest.targets) {
+            let outputs = to_paths(&target.name);
+            let action_id = match &target.recipe {
</code_context>

<issue_to_address>
Combining actions and targets may lead to duplicate outputs.

Chaining `manifest.actions` and `manifest.targets` can cause duplicate output paths, with later entries overwriting earlier ones in `graph.targets`. If this is expected, please document or enforce uniqueness to prevent subtle bugs.
</issue_to_address>

### Comment 2
<location> `src/ir.rs:176` </location>
<code_context>
+    }
+}
+
+fn hash_action(action: &Action) -> String {
+    let mut hasher = Sha256::new();
+    match &action.recipe {
</code_context>

<issue_to_address>
Hashing may not be stable if field order or formatting changes.

Consider documenting the hash's dependency on field order and formatting, or use a more robust serialization method if hash stability is required.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
fn hash_action(action: &Action) -> String {
=======
/// Computes a hash for an `Action`.
///
/// Note: The hash is dependent on the order and formatting of fields as they are serialized here.
/// If hash stability is required across code or format changes, consider using a robust serialization
/// method (e.g., serde with canonical formats) to ensure consistent hashing.
fn hash_action(action: &Action) -> String {
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `tests/ir_from_manifest_tests.rs:25` </location>
<code_context>
+    assert_eq!(graph.targets.len(), 2);
+}
+
+#[test]
+fn missing_rule_fails() {
+    let manifest = manifest::from_path("tests/data/missing_rule.yml").expect("load");
+    let err = BuildGraph::from_manifest(&manifest).expect_err("error");
</code_context>

<issue_to_address>
Test for multiple rules per target error.

Please add a test that verifies the MultipleRules error is triggered when multiple rules are specified for a single target.

Suggested implementation:

```rust
#[test]
fn missing_rule_fails() {
    let manifest = manifest::from_path("tests/data/missing_rule.yml").expect("load");
    let err = BuildGraph::from_manifest(&manifest).expect_err("error");
    matches!(err, IrGenError::RuleNotFound { .. });
}

#[test]
fn multiple_rules_per_target_fails() {
    let manifest = manifest::from_path("tests/data/multiple_rules_per_target.yml").expect("load");
    let err = BuildGraph::from_manifest(&manifest).expect_err("error");
    assert!(matches!(err, IrGenError::MultipleRules { .. }));
}

```

You will need to create a test manifest file at `tests/data/multiple_rules_per_target.yml` that defines a target with multiple rules, so that the test can trigger the `MultipleRules` error.
</issue_to_address>

### Comment 4
<location> `tests/ir_from_manifest_tests.rs:29` </location>
<code_context>
+fn missing_rule_fails() {
+    let manifest = manifest::from_path("tests/data/missing_rule.yml").expect("load");
+    let err = BuildGraph::from_manifest(&manifest).expect_err("error");
+    matches!(err, IrGenError::RuleNotFound { .. });
+}
</code_context>

<issue_to_address>
Assert the error type explicitly.

Use `assert!(matches!(err, IrGenError::RuleNotFound { .. }))` to ensure the test fails if the error type is not as expected.
</issue_to_address>

### Comment 5
<location> `src/ir.rs:95` </location>
<code_context>
+    ///
+    /// Returns [`IrGenError`] when a referenced rule is missing or multiple
+    /// rules are specified for a single target.
+    pub fn from_manifest(manifest: &NetsukeManifest) -> Result<Self, IrGenError> {
+        let mut graph = Self::default();
+        let mut rule_map = HashMap::new();
</code_context>

<issue_to_address>
Consider splitting the large from_manifest function into smaller helpers and refactoring hash_action to reduce repetition.

Here are two small, focused refactorings you can apply without changing behavior. They pull chunks out of your big `from_manifest` and DRY‐up `hash_action`.

1) Split `from_manifest` into three private helpers:

```rust
impl BuildGraph {
    pub fn from_manifest(m: &NetsukeManifest) -> Result<Self, IrGenError> {
        let mut graph = Self::default();
        let rule_map = graph.register_rules(&m.rules);
        graph.register_targets(&rule_map, m.actions.iter().chain(&m.targets))?;
        graph.register_defaults(&m.defaults);
        Ok(graph)
    }

    fn register_rules(&mut self, rules: &[Rule]) -> HashMap<String, String> {
        let mut map = HashMap::new();
        for rule in rules {
            let action = Action { /* ... */ };
            let hash = hash_action(&action);
            self.actions.entry(hash.clone()).or_insert(action);
            map.insert(rule.name.clone(), hash);
        }
        map
    }

    fn register_targets<I>(&mut self,
        rule_map: &HashMap<String,String>,
        targets: I
    ) -> Result<(), IrGenError>
    where I: IntoIterator<Item=&NetsukeAction>
    {
        for tgt in targets {
            let outputs = to_paths(&tgt.name);
            let action_id = match &tgt.recipe {
                Recipe::Rule { rule } => lookup_rule(rule, &outputs, rule_map)?,
                _ => self.insert_dynamic_action(&tgt.recipe),
            };
            let edge = BuildEdge { action_id, inputs: to_paths(&tgt.sources), /* ... */ };
            for o in outputs { self.targets.insert(o, edge.clone()); }
        }
        Ok(())
    }

    fn register_defaults(&mut self, defs: &[String]) {
        self.default_targets
            .extend(defs.iter().map(PathBuf::from));
    }

    fn insert_dynamic_action(&mut self, recipe: &Recipe) -> String {
        let action = Action { recipe: recipe.clone(), /* … */ };
        let hash = hash_action(&action);
        self.actions.entry(hash.clone()).or_insert(action);
        hash
    }
}

// helper for rule lookup
fn lookup_rule(
    rule: &StringOrList,
    outputs: &[PathBuf],
    rule_map: &HashMap<String,String>
) -> Result<String, IrGenError> {
    let name = extract_single(rule)
        .ok_or_else(|| IrGenError::MultipleRules {
            target_name: outputs.get(0).map(|p| p.display().to_string()).unwrap_or_default()
        })?;
    rule_map.get(name)
        .cloned()
        .ok_or_else(|| IrGenError::RuleNotFound {
            target_name: outputs.get(0).map(|p| p.display().to_string()).unwrap_or_default(),
            rule_name: name.to_string(),
        })
}
```

2) Collapse repetitive `hash_action` updates by iterating over fields and unifying `StringOrList`:

```rust
impl StringOrList {
    fn iter_str(&self) -> Vec<&str> {
        match self {
            StringOrList::String(s) => vec![s],
            StringOrList::List(v)  => v.iter().map(String::as_str).collect(),
            StringOrList::Empty    => Vec::new(),
        }
    }
}

fn hash_action(a: &Action) -> String {
    let mut hasher = Sha256::new();
    // recipe prefix + its strings
    let header = match &a.recipe {
        Recipe::Command { command } => format!("cmd:{command}"),
        Recipe::Script  { script  } => format!("scr:{script}"),
        Recipe::Rule    { rule    } => {
            let mut s = "rule:".to_string();
            for r in rule.iter_str() { s.push_str(r) }
            s
        }
    };
    hasher.update(header);
    // all optional String fields
    for opt in [&a.description, &a.depfile, &a.deps_format, &a.pool] {
        if let Some(v) = opt { hasher.update(v.as_bytes()); }
    }
    hasher.update(&[a.restat as u8]);
    format!("{:x}", hasher.finalize())
}
```

These steps keep the same logic but turn one huge function into a few well‐named ones and remove the repeated `hasher.update`/`match` noise.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
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: 5

🔭 Outside diff range comments (1)
tests/features/ir.feature (1)

21-23: Clarify the failure assertion to reflect IR generation failure, not parsing failure.

The step "parsing the manifest fails" is misleading. Based on the implementation, the manifest parsing succeeds but the IR generation fails when a rule is not found. Update the assertion to accurately reflect this.

-  Scenario: Rule not found during IR generation
-    When the manifest file "tests/data/missing_rule.yml" is compiled to IR
-    Then parsing the manifest fails
+  Scenario: Rule not found during IR generation
+    When the manifest file "tests/data/missing_rule.yml" is compiled to IR
+    Then IR generation fails
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb790ee and 364318c.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • Cargo.toml (1 hunks)
  • docs/netsuke-design.md (1 hunks)
  • docs/roadmap.md (1 hunks)
  • src/ir.rs (1 hunks)
  • tests/data/duplicate_rules.yml (1 hunks)
  • tests/data/missing_rule.yml (1 hunks)
  • tests/features/ir.feature (1 hunks)
  • tests/ir_from_manifest_tests.rs (1 hunks)
  • tests/steps/ir_steps.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
Cargo.toml

📄 CodeRabbit Inference Engine (AGENTS.md)

Use explicit version ranges in Cargo.toml and keep dependencies up-to-date.

Files:

  • Cargo.toml
**/*.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.rs: Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().

Files:

  • tests/steps/ir_steps.rs
  • tests/ir_from_manifest_tests.rs
  • src/ir.rs

⚙️ CodeRabbit Configuration File

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.

Files:

  • tests/steps/ir_steps.rs
  • tests/ir_from_manifest_tests.rs
  • src/ir.rs
docs/**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
Proactively update the relevant file(s) in the docs/ directory to reflect the latest state when new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve.
Documentation must use en-GB-oxendict spelling and grammar (with the exception of 'license' which is to be left unchanged for community consistency).

Files:

  • docs/roadmap.md
  • docs/netsuke-design.md
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.md: Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Tables and headings in Markdown must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references.

Files:

  • docs/roadmap.md
  • docs/netsuke-design.md

⚙️ CodeRabbit Configuration File

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/roadmap.md
  • docs/netsuke-design.md
**/*{_test,tests}.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*{_test,tests}.rs: Write unit and behavioural tests for new functionality. Run both before and after making any change.
Use rstest fixtures for shared setup.
Replace duplicated tests with #[rstest(...)] parameterised cases.
Prefer mockall for mocks/stubs.

Files:

  • tests/ir_from_manifest_tests.rs
🧠 Learnings (1)
docs/roadmap.md (1)

Learnt from: CR
PR: leynos/netsuke#0
File: docs/netsuke-design.md:0-0
Timestamp: 2025-07-20T02:49:08.228Z
Learning: Applies to docs/src/ir.rs : The AST-to-IR transformation logic must be implemented in a function (e.g., ir::from_manifest) that consumes a NetsukeManifest and produces a BuildGraph, performing validation such as rule existence and cycle detection.

🧬 Code Graph Analysis (1)
tests/ir_from_manifest_tests.rs (2)
src/manifest.rs (1)
  • from_path (51-56)
src/ir.rs (1)
  • from_manifest (95-173)
🔇 Additional comments (12)
Cargo.toml (1)

12-13: Pin dependency versions to concrete patch releases

Line-12 and Line-13 float on any 1.* or 0.10.* release. This violates the “explicit version ranges” rule and risks reproducibility issues whenever a new patch lands.

-thiserror = "1"
-sha2 = "0.10"
+thiserror = "1.0.59"  # latest 1.x at time of writing
+sha2 = "0.10.8"       # latest 0.10.x at time of writing

Run cargo update -p thiserror sha2 to verify the exact latest patch numbers.

tests/data/duplicate_rules.yml (1)

1-22: Fixture looks correct

The manifest exercises rule deduplication and matches the spec. No issues.

tests/data/missing_rule.yml (1)

1-7: Fixture correctly triggers RuleNotFound

The target references a non-existent rule, aligning with the negative-test intent.

tests/features/ir.feature (2)

10-14: LGTM!

The scenario correctly tests the basic manifest compilation functionality with appropriate assertions.


16-19: LGTM!

The scenario effectively validates the deduplication functionality by asserting that duplicate rules result in a single action with multiple targets.

tests/steps/ir_steps.rs (1)

30-48: LGTM!

The implementation correctly handles both success and error paths, with appropriate state management. The Clippy suppression is properly scoped and justified for Cucumber's requirements.

tests/ir_from_manifest_tests.rs (2)

10-15: LGTM!

The test correctly validates IR generation from a minimal manifest.


18-23: LGTM!

The test effectively validates that duplicate rules are deduplicated into a single action.

src/ir.rs (4)

76-86: LGTM!

The error enum is well-structured with clear, contextual error messages using thiserror.


96-111: LGTM!

The rule processing and deduplication logic is well-implemented using a HashMap to track rule-to-action mappings.


176-208: LGTM! Ensure list order is preserved for deterministic hashing.

The hashing implementation is comprehensive and correctly prefixes recipe types to avoid collisions. Note that for StringOrList::List, the order of elements affects the hash, which is correct for deterministic builds.


210-224: LGTM!

Both helper functions are well-implemented and handle all StringOrList variants correctly.

Copy link
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: 1

♻️ Duplicate comments (4)
docs/netsuke-design.md (1)

1059-1065: Re-wrap bullet points to comply with documentation standards.

The bullet points exceed 80 columns, violating the coding guidelines for Markdown files. Hard-wrap each line to ensure it stays within 80 characters whilst maintaining proper Markdown bullet formatting.

Apply this diff to fix the line wrapping:

-- Actions are deduplicated using a SHA-256 hash of their recipe and metadata.
-  Identical commands therefore share the same identifier which keeps the IR
-  deterministic for snapshot tests.
-- Multiple rule references in a single target are not yet supported. The IR
-  generator reports `IrGenError::MultipleRules` when encountered.
-- Duplicate output files are rejected. Attempting to define the same output
-  path twice results in `IrGenError::DuplicateOutput`.
+- Actions are deduplicated using a SHA-256 hash of their recipe and
+  metadata. Identical commands therefore share the same identifier which
+  keeps the IR deterministic for snapshot tests.
+- Multiple rule references in a single target are not yet supported. The IR
+  generator reports `IrGenError::MultipleRules` when encountered.
+- Duplicate output files are rejected. Attempting to define the same output
+  path twice results in `IrGenError::DuplicateOutput`.
tests/ir_from_manifest_tests.rs (1)

25-30: Fix test attributes and assertions for consistency.

Use #[rstest] instead of #[test] for consistency with other tests in the module, and wrap the matches! macro with assert! to actually perform the assertion.

Apply this diff:

-#[test]
+#[rstest]
 fn missing_rule_fails() {
     let manifest = manifest::from_path("tests/data/missing_rule.yml").expect("load");
     let err = BuildGraph::from_manifest(&manifest).expect_err("error");
-    matches!(err, IrGenError::RuleNotFound { .. });
+    assert!(matches!(err, IrGenError::RuleNotFound { .. }));
 }
src/ir.rs (2)

126-150: Extract repeated code for getting target display name.

The code for getting the first output and converting it to a display string is repeated. Extract this into a helper function to reduce duplication.

Add this helper function before from_manifest:

+fn get_target_display_name(outputs: &[PathBuf]) -> String {
+    outputs
+        .first()
+        .map(|p| p.display().to_string())
+        .unwrap_or_default()
+}

Then simplify the error construction:

-                    let name = extract_single(rule).ok_or_else(|| IrGenError::MultipleRules {
-                        target_name: outputs
-                            .first()
-                            .cloned()
-                            .unwrap_or_default()
-                            .display()
-                            .to_string(),
-                    })?;
+                    let name = extract_single(rule).ok_or_else(|| IrGenError::MultipleRules {
+                        target_name: get_target_display_name(&outputs),
+                    })?;

And similarly for the RuleNotFound error:

-                        .ok_or_else(|| IrGenError::RuleNotFound {
-                            target_name: outputs
-                                .first()
-                                .cloned()
-                                .unwrap_or_default()
-                                .display()
-                                .to_string(),
-                            rule_name: name.to_string(),
-                        })?
+                        .ok_or_else(|| IrGenError::RuleNotFound {
+                            target_name: get_target_display_name(&outputs),
+                            rule_name: name.to_string(),
+                        })?

156-173: Remove unnecessary clone of action_id.

The action_id is cloned unnecessarily on line 157. Since it's already cloned when needed later, remove the redundant clone.

             let edge = BuildEdge {
-                action_id: action_id.clone(),
+                action_id,
                 inputs: to_paths(&target.sources),
                 explicit_outputs: outputs.clone(),
                 implicit_outputs: Vec::new(),
                 order_only_deps: to_paths(&target.order_only_deps),
                 phony: target.phony,
                 always: target.always,
             };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 364318c and 889f022.

📒 Files selected for processing (6)
  • .github/workflows/ci.yml (1 hunks)
  • docs/netsuke-design.md (1 hunks)
  • src/ir.rs (1 hunks)
  • tests/data/duplicate_outputs.yml (1 hunks)
  • tests/features/ir.feature (1 hunks)
  • tests/ir_from_manifest_tests.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.rs: Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().

Files:

  • tests/ir_from_manifest_tests.rs
  • src/ir.rs

⚙️ CodeRabbit Configuration File

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.

Files:

  • tests/ir_from_manifest_tests.rs
  • src/ir.rs
**/*{_test,tests}.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*{_test,tests}.rs: Write unit and behavioural tests for new functionality. Run both before and after making any change.
Use rstest fixtures for shared setup.
Replace duplicated tests with #[rstest(...)] parameterised cases.
Prefer mockall for mocks/stubs.

Files:

  • tests/ir_from_manifest_tests.rs
docs/**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
Proactively update the relevant file(s) in the docs/ directory to reflect the latest state when new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve.
Documentation must use en-GB-oxendict spelling and grammar (with the exception of 'license' which is to be left unchanged for community consistency).

Files:

  • docs/netsuke-design.md
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.md: Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Tables and headings in Markdown must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references.

Files:

  • docs/netsuke-design.md

⚙️ CodeRabbit Configuration File

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/netsuke-design.md
🔇 Additional comments (9)
tests/data/duplicate_outputs.yml (1)

1-13: LGTM! Test data structure is correct.

The YAML structure properly defines two targets with duplicate output names (hello.o) but different sources, which will correctly trigger the IrGenError::DuplicateOutput validation error during IR generation.

.github/workflows/ci.yml (1)

32-38: LGTM! Improved token handling and action version update.

The changes properly centralise token usage through an environment variable and update to a newer action version. The conditional check using the environment variable is more explicit than directly checking the secret.

tests/features/ir.feature (1)

10-26: LGTM! Comprehensive feature test coverage for IR generation.

The scenarios effectively cover the key IR generation functionality including successful compilation, rule deduplication, and error handling for missing rules and duplicate outputs. The test structure aligns well with the implementation and provides good behavioural coverage.

src/ir.rs (6)

75-90: Well-structured error handling with clear messages.

The error enum is properly designed with descriptive variants and helpful error messages using the thiserror crate.


91-108: Excellent refactoring of the from_manifest method.

The method has been successfully decomposed into focused helper methods, improving readability and adhering to single responsibility principle.


167-173: Proper validation for duplicate outputs.

The duplicate output detection correctly prevents multiple targets from producing the same output file, with a clear error message.


203-261: Well-structured hashing implementation with proper serialisation.

The refactored hashing functions successfully reduce complexity while maintaining deterministic behaviour. The length-prefixed encoding ensures unambiguous hashing.


263-277: Clean utility functions with proper edge case handling.

The utility functions are well-implemented with clear single responsibilities.


279-310: Comprehensive hash stability tests using rstest.

The parameterised tests effectively verify hash stability across different action configurations, ensuring deterministic behaviour.

@leynos
Copy link
Owner Author

leynos commented Jul 27, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 27, 2025

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

@leynos leynos merged commit bb37630 into main Jul 27, 2025
3 checks passed
@leynos leynos deleted the codex/implement-ir-transformation-logic branch July 27, 2025 17:57
@leynos
Copy link
Owner Author

leynos commented Jul 27, 2025

@sourcery-ai review

Copy link
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.

Hey @leynos - I've reviewed your changes - here's some feedback:

  • Consider adopting a canonical serialization format (e.g., serde JSON canonical) for hashing Action so that the deduplication hash remains stable across code changes rather than relying on manual byte sequences.
  • The current error messages only reference the first conflicting output or rule; including all conflicting values in IrGenError variants could make troubleshooting multi-output or multi-rule errors more straightforward.
  • Extract the hashing routines into a separate module or Hasher type to decouple them from IR construction and facilitate unit testing and future extensions.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider adopting a canonical serialization format (e.g., serde JSON canonical) for hashing Action so that the deduplication hash remains stable across code changes rather than relying on manual byte sequences.
- The current error messages only reference the first conflicting output or rule; including all conflicting values in IrGenError variants could make troubleshooting multi-output or multi-rule errors more straightforward.
- Extract the hashing routines into a separate module or Hasher type to decouple them from IR construction and facilitate unit testing and future extensions.

## Individual Comments

### Comment 1
<location> `src/ir.rs:70` </location>
<code_context>
     /// Run the command on every invocation regardless of timestamps.
     pub always: bool,
 }
+
+use crate::ast::{NetsukeManifest, Recipe, StringOrList};
+use sha2::{Digest, Sha256};
</code_context>

<issue_to_address>
The module is missing a `//!` comment at the top as required by the review instructions.

Please add a `//!` module-level comment at the very top of this file to describe its purpose, as per the project guidelines.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@leynos
Copy link
Owner Author

leynos commented Jul 27, 2025

@coderabbitai new GitHub issue, please:

Consider adopting a canonical serialization format (e.g., serde JSON canonical) for hashing Action so that the deduplication hash remains stable across code changes rather than relying on manual byte sequences.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 27, 2025

Tip

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

🧩 Analysis chain

@leynos I'll create a GitHub issue for this improvement suggestion regarding canonical serialization for Action hashing.


🏁 Script executed:

#!/bin/bash
# Create GitHub issue for canonical serialization improvement
gh issue create \
  --title "Consider canonical serialization format for Action hashing stability" \
  --body "## Problem

The current Action hashing implementation in \`src/ir.rs\` uses manual byte sequence construction for deduplication. This approach may be fragile and could break if the Action struct evolves, potentially causing hash instability across code changes.

## Proposed Solution

Adopt a canonical serialisation format (e.g., serde JSON canonical) for hashing Action objects to ensure:
- Hash stability across code changes
- More maintainable serialisation logic
- Reduced risk of hash collisions from manual byte construction

## Context

This suggestion arose from code review of the IR generation implementation.

**Related:**
- PR: https://github.com/leynos/netsuke/pull/33
- Comment: https://github.com/leynos/netsuke/pull/33#issuecomment-$(gh api repos/leynos/netsuke/pulls/33/comments --jq '.[-1].id')

**Requested by:** @leynos" \
  --assignee leynos

Length of output: 1017


@leynos Perfect! I've created GitHub issue #35: "Consider canonical serialization format for Action hashing stability" at #35

The issue captures your suggestion to replace the current manual byte sequence construction with a canonical serialisation format (like serde JSON canonical) for more stable and maintainable Action deduplication hashing. It includes proper context linking back to this PR and your comment, and has been assigned to you for future implementation.

     (\   /)
    ( ._. )
   o_(")(")
 Thoughtful rabbit pondering canonical formats! 🤔

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