Skip to content

Preserve script shell variables (#737) - #753

Open
leynos wants to merge 5 commits into
mainfrom
issue-737-escape-in-and-out-as-shell-variables-in-scripts
Open

leynos wants to merge 5 commits into
mainfrom
issue-737-escape-in-and-out-as-shell-variables-in-scripts

Conversation

@leynos

@leynos leynos commented Sep 20, 2026

Copy link
Copy Markdown
Owner

Summary

script: recipes now preserve $in and $out as shell variables. The Ninja
backend writes them as $$in and $$out, preventing Ninja from expanding its
input and output markers. {{ ins }} and {{ outs }} retain their Netsuke
marker behaviour.

Closes #737.

Review walkthrough

  • Script token matching
    now uses the same internal-token matcher as commands, so literal shell
    variables fall through to backend escaping.
  • Ninja regression coverage
    proves scripts emit doubled $in and $out, while quoted marker tests retain
    path-substitution coverage.
  • ADR-033
    and the user guide
    distinguish Netsuke markers, internal tokens, and shell variables.

Validation

  • make check-fmt
  • make lint
  • make doc-coverage (98.80%)
  • make test (3,227 passed, 5 skipped; doctests passed)
  • make markdownlint
  • make nixie
  • coderabbit review --agent (zero findings)

References

Summary by Sourcery

Preserve $in and $out as shell variables in scripts while retaining explicit Netsuke markers for input and output path substitution.

Bug Fixes:

  • Preserve $in and $out in script: recipes as shell variables so Ninja no longer expands them as its own input and output markers.

Enhancements:

  • Unify script interpolation with command interpolation so only {{ ins }} and {{ outs }} are treated as Netsuke path markers while shell variables remain unchanged.
  • Clarify the distinction between Netsuke markers, internal tokens, and shell variables across the design records and user documentation.

Documentation:

  • Add ADR-033 and update design, user, roadmap, execution-plan, and RFC documentation to define the revised script variable semantics.

Tests:

  • Add unit, property, and Ninja regression coverage for preserving script shell variables and retaining quoted {{ ins }} and {{ outs }} path substitution.

leynos added 4 commits September 20, 2026 16:46
Treat `$in` and `$out` in script recipes as shell variables so the
Ninja backend doubles their dollars instead of expanding Ninja markers.

Keep Netsuke markers and internal tokens distinct in code and tests.
Define markers, internal tokens, and shell variables consistently and
record the reversal of script `$in` and `$out` lowering in ADR-033.
Keep the ADR status within the documented value set while retaining
the ADR-033 supersession reference.
Keep ADR-014 accepted because ADR-033 reverses only its script $in and $out decision. The backend-escaping, path, metadata, and marker decisions remain active.
@coderabbitai

coderabbitai Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: 07709596-6f8c-4647-bdbb-79c340558252

📥 Commits

Reviewing files that changed from the base of the PR and between c7cf148 and 24dc15b.

📒 Files selected for processing (13)
  • docs/adr-014-backend-text-escaping-seam.md
  • docs/adr-033-preserve-script-in-out-as-shell-variables.md
  • docs/contents.md
  • docs/execplans/3-14-7-escape-backend-dollar-syntax-after-netsuke-placeholder-lowering.md
  • docs/netsuke-design.md
  • docs/rfcs/0001-structured-command-blocks.md
  • docs/roadmap.md
  • docs/users-guide.md
  • src/ir/cmd_interpolate/mod.rs
  • src/ir/cmd_interpolate_property_tests.rs
  • src/ir/cmd_interpolate_tests.rs
  • tests/ninja_dollar_escaping_tests.rs
  • tests/ninja_dollar_escaping_tests/scripts.rs
🔗 Linked repositories identified

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

  • leynos/monotony (auto-detected)
  • leynos/whitaker (auto-detected)
  • leynos/rstest-bdd (auto-detected)
  • leynos/mdtablefix (auto-detected)
  • leynos/typos-config-builder (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/lading (auto-detected)
  • leynos/shared-actions (auto-detected)
  • leynos/nixie (auto-detected)
  • leynos/ansible (auto-detected)

Included review availability: 0 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.


Summary

Preserve $in and $out as shell variables in script: recipes. Escape them at the Ninja boundary as $$in and $$out.

Keep {{ ins }} and {{ outs }} as Netsuke markers. Keep INS_TOKEN and OUTS_TOKEN as internal interpolation tokens.

Update interpolation logic, unit tests, property tests, and Ninja regression tests. Clarify the behaviour in ADR-033, ADR-014, the design documentation, user guide, and structured-command RFC.

Relate the implementation to issue #737 and the existing backend escaping design.

Validation

Formatting, lint, documentation, tests, Markdown checks, and Nixie checks passed.

Walkthrough

The change separates Netsuke markers from shell variables. Script interpolation now lowers only internal input and output tokens. Ninja generation escapes residual dollars, and documentation and tests record the updated contract.

Changes

Script shell-variable preservation

Layer / File(s) Summary
Interpolation contract and documentation
docs/adr-014-backend-text-escaping-seam.md, docs/adr-033-preserve-script-in-out-as-shell-variables.md, docs/contents.md, docs/execplans/..., docs/netsuke-design.md, docs/rfcs/..., docs/roadmap.md, docs/users-guide.md
Document {{ ins }} and {{ outs }} as Netsuke markers. Document $in, $out, $ins, and $outs as shell variables. Record ADR-033’s limited supersession of ADR-014.
Script interpolation behaviour
src/ir/cmd_interpolate/mod.rs
Remove script-specific matching for $in and $out. Substitute only internal input and output tokens while preserving shell variables.
Ninja escaping and regression coverage
src/ir/cmd_interpolate_property_tests.rs, src/ir/cmd_interpolate_tests.rs, tests/ninja_dollar_escaping_tests.rs, tests/ninja_dollar_escaping_tests/scripts.rs
Verify literal shell variables during script interpolation. Verify Ninja dollar escaping and marker handling for command and script recipes.

Sequence Diagram(s)

sequenceDiagram
  participant ScriptRecipe
  participant ScriptInterpolator
  participant NinjaBackend
  participant NinjaBuild
  ScriptRecipe->>ScriptInterpolator: Provide script with markers and shell variables
  ScriptInterpolator->>ScriptInterpolator: Lower {{ ins }} and {{ outs }}
  ScriptInterpolator->>NinjaBackend: Preserve $in and $out
  NinjaBackend->>NinjaBuild: Escape residual dollars
Loading

Suggested labels: Issue, Roadmap

Priority: ⬇️ Low

Change: Bug fix


Caution

Pre-merge checks failed

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

  • Ignore

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Testing (Overall) ❌ Error The new tests do not rigorously guard the end-to-end script: behaviour. The direct unit and property tests only assert that interpolate_script_with_bindings returns the input string; their generat… Add a POSIX Ninja integration test for a Recipe::Script that uses both $in and $out, run the generated build, and assert the produced output contains values supplied through the child shell environment rather than the declared input a…
User-Facing Documentation ⚠️ Warning docs/users-guide.md clearly documents the new behaviour: it defines $in, $out, $ins, and $outs as shell variables, describes Ninja dollar escaping, identifies {{ ins }} and {{ outs }} as… Update the applicable n+1 migration guide, currently docs/v0-1-0-migration-guide.md, to state that script $in and $out are now preserved as shell variables and are emitted as doubled dollars for Ninja. State that manifests using $in
✅ Passed checks (13 passed)
Check name Status Explanation
Title check ✅ Passed Accept the title. It clearly identifies the main change and references issue #737, as required for an issue-fixing change.
Description check ✅ Passed Accept the description. It directly explains the script shell-variable change, marker behaviour, tests, documentation, and issue reference.
Linked Issues check ✅ Passed Accept the implementation for issue #737. Preserve $in and $out during script interpolation, and escape them as $$in and $$out at the Ninja boundary. Keep {{ ins }} and {{ outs }} as the N…
Out of Scope Changes check ✅ Passed Accept the change scope. The source changes implement script interpolation and Ninja escaping for issue #737. The tests verify the requested interpolation and generated-output behaviour. The documenta…
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 5 files. (8 skipped: 8…
Developer Documentation ✅ Passed Pass. The pull request records the changed interpolation boundary in ADR-033, updates the relevant design and user documentation, and logs the partial supersession in ADR-014's revision history. The d…
Module-Level Documentation ✅ Passed Pass. Retain the module documentation. All changed Rust modules start with //! documentation that states their purpose and role. src/ir/cmd_interpolate/mod.rs describes interpolation, token handli…
Testing (Unit And Behavioural) ✅ Passed Pass the testing check. Additions cover local and boundary behaviour. Unit and property tests verify that script interpolation preserves $in, $out, $ins, and $outs. Existing interpolation test…
Testing (Property / Proof) ✅ Passed Mark this check PASS. The change introduces the invariant that script interpolation preserves shell variables while varying input and output bindings. The PR adds a Rust proptest case, `script_dolla…
Testing (Compile-Time / Ui) ✅ Passed Pass this check. The PR changes runtime Rust interpolation and Ninja rendering, not compile-time type checking, macros, or compile-fail diagnostics. No trybuild test is therefore required. The added u…
Unit Architecture ✅ Passed Pass the check. The PR changes only pure interpolation logic and tests. Production functions take explicit template and CommandBindings inputs, return Result where interpolation can fail, and perf…
Domain Architecture ✅ Passed Pass the domain architecture check. The pull request changes only IR command interpolation policy, tests, and documentation. The changed Rust code retains {{ ins }} and {{ outs }} token lowering a…
Observability ✅ Passed Pass observability. The PR changes manifest-to-Ninja interpolation and does not add a new service, process, storage, queue, or asynchronous boundary. Existing runner telemetry already covers this path…
Full details: Testing (Overall)

Explanation

The new tests do not rigorously guard the end-to-end script: behaviour. The direct unit and property tests only assert that interpolate_script_with_bindings returns the input string; their generated bindings do not affect the assertion. The integration test script_dollar_in_out_are_doubled_as_shell_variables only checks that the generated Ninja text contains \$$in and \$$out. The parameterized backend test also uses substring checks. Neither test runs Ninja for this new $in/$out case, so neither verifies Ninja parsing, the printf %b wrapper, or expansion by the child shell. The pull request removes the previous integration test that executed a script through Ninja, and the remaining execution tests cover {{ ins }} and {{ outs }} instead. A plausible incorrect wrapper or escaping change could therefore pass the new assertions while failing to preserve shell-variable behaviour at execution time.

Resolution

Add a POSIX Ninja integration test for a Recipe::Script that uses both $in and $out, run the generated build, and assert the produced output contains values supplied through the child shell environment rather than the declared input and output paths. Keep the existing generated-text assertion as a lower-level serialization check. Extend the test helper to set the required environment variables, or invoke Ninja with an explicit environment, so the test fails when interpolation lowers $in/$out, when Ninja expands them as its own markers, or when the nested script wrapper loses the variables.

Full details: User-Facing Documentation

Explanation

docs/users-guide.md clearly documents the new behaviour: it defines $in, $out, $ins, and $outs as shell variables, describes Ninja dollar escaping, identifies {{ ins }} and {{ outs }} as the path markers, and gives the former-placeholder migration. However, the pull request changes script interpolation by removing $in/$out lowering, which changes the behaviour of existing manifests. The required release migration document is not updated: docs/v0-1-0-migration-guide.md still lists only $ins and $outs as shell variables and does not signpost the changed $in/$out script semantics. No migration-guide file is in the pull-request diff.

Resolution

Update the applicable n+1 migration guide, currently docs/v0-1-0-migration-guide.md, to state that script $in and $out are now preserved as shell variables and are emitted as doubled dollars for Ninja. State that manifests using $in or $out as former Netsuke path placeholders must replace them with {{ ins }} or {{ outs }}. Link to the detailed safety-boundary section in docs/users-guide.md.


Markers take their token path
Shell dollars keep their place
Ninja doubles every sign
Tests guard the boundary line
Clear seams guide each recipe

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

@sourcery-ai

sourcery-ai Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Scripts now preserve $in and $out as ordinary shell variables: interpolation recognizes only internal tokens generated from {{ ins }} and {{ outs }}, while the Ninja backend doubles all residual dollars. Regression tests and documentation establish the updated marker and escaping contract.

Sequence diagram for preserving script shell variables

sequenceDiagram
    participant Manifest
    participant Interpolation
    participant NinjaBackend
    participant Shell

    Manifest->>Interpolation: Render {{ ins }} and {{ outs }} as internal tokens
    Interpolation->>Interpolation: find_substitution()
    Interpolation-->>NinjaBackend: Preserve $in and $out in ShellText
    NinjaBackend->>NinjaBackend: Escape residual dollars as $$
    NinjaBackend-->>Shell: Execute script with $in and $out unchanged
Loading

Flow diagram for script markers and shell variables

flowchart LR
    A[Script text] --> B{Token type}
    B -->|"{{ ins }} or {{ outs }}"| C[Internal token]
    C --> D[Substitute input or output paths]
    B -->|$in or $out| E[Shell variable unchanged]
    D --> F[ShellText to NinjaValue]
    E --> F
    F --> G[Ninja emits doubled dollars]
Loading

File-Level Changes

Change Details Files
Preserve $in and $out as shell variables in scripts while limiting Netsuke path substitution to explicit markers.
  • Reuse the internal-token matcher for script interpolation.
  • Remove script-only $in/$out placeholder matching.
  • Continue contextual lowering and quoting for {{ ins }} and {{ outs }}.
src/ir/cmd_interpolate/mod.rs
src/ir/cmd_interpolate_property_tests.rs
src/ir/cmd_interpolate_tests.rs
Verify residual shell dollars are escaped at the Ninja backend boundary for both recipe forms.
  • Add script coverage for doubled $in and $out.
  • Extend backend escaping cases across command and script recipes.
  • Retain execution coverage for quoted explicit markers and shell punctuation.
tests/ninja_dollar_escaping_tests.rs
tests/ninja_dollar_escaping_tests/scripts.rs
Document the separation between Netsuke markers, internal tokens, and shell variables.
  • Add ADR-033 and supersede the prior script-only lowering decision.
  • Update design, user, roadmap, execution-plan, and ADR index documentation.
docs/adr-014-backend-text-escaping-seam.md
docs/adr-033-preserve-script-in-out-as-shell-variables.md
docs/contents.md
docs/execplans/3-14-7-escape-backend-dollar-syntax-after-netsuke-placeholder-lowering.md
docs/netsuke-design.md
docs/roadmap.md
docs/users-guide.md

Assessment against linked issues

Issue Objective Addressed Explanation
#737 Ensure $in and $out in script: recipes remain shell variables and are escaped in generated Ninja output rather than being interpreted as Ninja input/output markers.
#737 Preserve {{ ins }} and {{ outs }} as the Netsuke-owned markers for input and output path substitution while keeping internal tokens separate from shell variables.
#737 Update regression tests and documentation to consistently distinguish markers, internal tokens, and shell variables.

Possibly linked issues


Tips and commands

Interacting with Sourcery

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

Customizing Your Experience

Access your dashboard to:

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

Getting Help

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review September 20, 2026 19:36

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @leynos, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 3 days and 16 hours by commenting @sourcery-ai review. Upgrade to get a review now.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cbbef5bfd7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +47 to +48
`$in` and `$out` are shell variables in every recipe. They are not Netsuke
markers or internal tokens, so script interpolation leaves them unchanged. The

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update the structured-command RFC for the new variable contract

This new contract conflicts with the active proposed specification in docs/rfcs/0001-structured-command-blocks.md: lines 101–105 still state that standalone $in and $out undergo late substitution, while lines 1261–1265 promise that their existing behaviour remains for legacy and shell-mode commands. Since roadmap tasks use that RFC as their implementation specification, leaving it unchanged can reintroduce the superseded script semantics or produce incorrect compatibility guarantees; amend those sections or add an explicit ADR-033 supersession note.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-20T19:40:32.557146Z cbbef5b Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Define only {{ ins }} and {{ outs }} as Netsuke markers, retain dollar-prefixed names as shell variables, and record ADR-033 supersession of script-specific lowering.
@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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

@coderabbitai coderabbitai Bot added Issue A pull request originating from an issue Roadmap A pull request originating from a roadmap item labels Sep 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pre-merge checks failed. Please resolve the failing checks before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Issue A pull request originating from an issue Roadmap A pull request originating from a roadmap item

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Escape $in and $out as shell variables in scripts

2 participants