Skip to content

Add reproducible Foundry baseline#4

Merged
punk6529 merged 5 commits into
mainfrom
codex/gate-a-reproducible-baseline
Jun 9, 2026
Merged

Add reproducible Foundry baseline#4
punk6529 merged 5 commits into
mainfrom
codex/gate-a-reproducible-baseline

Conversation

@punk6529

@punk6529 punk6529 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This Gate A PR makes a fresh checkout compile the actual Solidity source tree and gives contributors a reproducible smoke path.

  • Adds pinned Foundry configuration for smart-contracts/ with Solidity 0.8.19.
  • Adds make check, Windows/Linux check scripts, and bootstrap scripts for local setup.
  • Adds GitHub Actions smoke CI for forge build and forge test -vvv, including uploaded logs.
  • Adds basic status/tooling/docs skeletons so contributors can see the current maturity level from the README.
  • Fixes the compile surface in RandomizerRNG.sol and RandomizerVRF.sol by importing the existing Stream interfaces instead of missing NextGen interface files.

Important Limitations

  • forge test -vvv currently executes but reports no tests found. This PR intentionally documents that gap; it does not claim protocol correctness.
  • Formatting and Slither are documented as non-gating diagnostics until their roadmap baseline PRs land.
  • Existing Foundry warnings are not fixed in this PR; they remain future roadmap work.

Validation

  • forge build passed.
  • forge test -vvv passed command execution and reported no tests found.
  • make check passed.
  • powershell -ExecutionPolicy Bypass -File scripts\check.ps1 passed.
  • git check-ignore -v out/ cache/ broadcast/ .venv-tools/ .env.local confirmed generated/local artifacts are ignored.

Roadmap

This advances Gate A: Reproducible baseline from ops/ROADMAP.md.

Summary by CodeRabbit

  • Documentation

    • Rewritten README and added ADRs, known blockers, status, tooling, ops notes, bootstrap/script docs, and test guidance.
  • Chores

    • Added CI workflow, Makefile, foundry config, bootstrap and check scripts (Linux/Windows), repo build/test helpers, toolchain pins, and editor/git attributes; expanded .gitignore.
  • Refactor

    • Updated smart contracts to use revised interface types.

punk6529 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@claude please review this Gate A reproducible-baseline PR for contributor setup, CI/tooling portability, and whether the compile-surface randomizer import fixes are appropriate for making forge build compile the actual source tree. Please pay special attention to Windows/Linux setup paths and the fact that forge test -vvv currently executes but has no meaningful tests yet.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c71761fd-d082-4468-a8c6-4fb6f5bceedc

📥 Commits

Reviewing files that changed from the base of the PR and between 8944025 and a4faac0.

📒 Files selected for processing (1)
  • ops/AUTONOMOUS_RUN.md
✅ Files skipped from review due to trivial changes (1)
  • ops/AUTONOMOUS_RUN.md

📝 Walkthrough

Walkthrough

Adds repository formatting and EOL rules, Foundry config and Make targets, cross-platform bootstrap and check scripts, CI workflow, smart-contract interface retyping to IStream*, and documentation/ops updates to establish a reproducible Gate A baseline.

Changes

Gate A Reproducible Baseline

Layer / File(s) Summary
Repository configuration standards
.editorconfig, .gitattributes, .gitignore
Editor formatting rules, Git EOL normalization (LF for sources/scripts, CRLF for PowerShell), and ignore patterns for build outputs, virtualenv, and IDE metadata.
Foundry build & Make targets
foundry.toml, Makefile, requirements-tools.txt
Foundry workspace and formatter configured, solc pinned to 0.8.19, EVM paris, optimizer enabled; Make targets for check, build, test, fmt-check, slither, and clean with OS-aware cleanup; Python tooling pinned (slither-analyzer==0.11.5, solc-select==1.2.0).
Cross-platform bootstrap and verification scripts
scripts/bootstrap-ec2.sh, scripts/bootstrap-windows.ps1, scripts/check.sh, scripts/check.ps1
Linux/EC2 and Windows bootstraps install and verify Foundry release (checksum-verified), create .venv-tools, install Python tools, select solc 0.8.19; check scripts run forge build and forge test -vvv.
GitHub Actions CI automation
.github/workflows/ci.yml
CI triggers on pull requests and pushes to main, runs Foundry build/test, tees logs into ci-logs/, and uploads them as foundry-smoke-logs with 14-day retention.
Smart contract interface wiring updates
smart-contracts/RandomizerRNG.sol, smart-contracts/RandomizerVRF.sol
Randomizer contracts retype stored references from INextGenCore/INextGenAdmins to IStreamCore/IStreamAdmins, update constructor casts and admin/core update functions accordingly.
Project documentation and directory guidance
README.md, docs/adr/README.md, docs/known-blockers.md, docs/status.md, docs/tooling.md, script/README.md, test/README.md, ops/AUTONOMOUS_RUN.md
README rewritten with project overview and Foundry quickstart; ADR guidance, known blockers, tooling docs, and status explain the pre-audit baseline; AUTONOMOUS_RUN updated to record Gate A work and decision log.

Sequence Diagram(s)

sequenceDiagram
  participant Developer
  participant BootstrapScript
  participant FoundryRelease
  participant PythonTooling
  participant CheckScript
  Developer->>BootstrapScript: run scripts/bootstrap-ec2.sh or scripts/bootstrap-windows.ps1
  BootstrapScript->>FoundryRelease: download archive + .sha256 and verify checksum
  BootstrapScript->>FoundryRelease: extract to install dir (~/.foundry/bin or $InstallDir)
  BootstrapScript->>PythonTooling: create .venv-tools, install requirements-tools.txt, select solc 0.8.19
  Developer->>CheckScript: run scripts/check.sh or scripts/check.ps1
  CheckScript->>FoundryRelease: run forge build then forge test -vvv
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • 6529-Collections/6529Stream#3: Merged PR that updates ops/AUTONOMOUS_RUN.md and provides the autonomous run control plane referenced by this Gate A work.

Poem

A rabbit hops through scripts and configs so spry,
Verifies checksums, sets solc to the sky.
From PowerShell paths to Linux boot delight,
Foundry builds run, CI captures the night. 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add reproducible Foundry baseline' clearly and accurately summarizes the main objective of the PR, which is to establish a reproducible Foundry build/test baseline with tooling, configuration, and cross-platform setup scripts.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/gate-a-reproducible-baseline

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

punk6529 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@claude second explicit review request for PR #4. Please review the Gate A reproducible-baseline changes, especially setup reproducibility across Windows/Linux, the new CI smoke workflow, and the minimal randomizer interface import fixes required for forge build to compile the real source tree.

Comment thread scripts/bootstrap-windows.ps1 Outdated
Comment thread scripts/check.sh
Comment thread Makefile Outdated
Comment thread scripts/bootstrap-ec2.sh
Comment thread Makefile
Comment thread .gitignore

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
scripts/check.sh (1)

7-10: ⚡ Quick win

Add an explicit forge preflight check for clearer Linux setup failures.

Right now, missing forge fails with a generic command-not-found path. Mirror the Windows script’s explicit check so contributors get actionable setup guidance.

Proposed patch
 export PATH="$HOME/.foundry/bin:$PATH"
 
+if ! command -v forge >/dev/null 2>&1; then
+  echo "forge was not found. Run scripts/bootstrap-ec2.sh, then retry this command." >&2
+  exit 1
+fi
+
 forge build
 forge test -vvv
🤖 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 `@scripts/check.sh` around lines 7 - 10, Add an explicit preflight check for
the `forge` binary before running `forge build`/`forge test`: use `command -v
forge >/dev/null 2>&1` (or `which forge`) to detect absence, print a clear
actionable error message instructing how to install Foundry (matching the
Windows script guidance), and exit with a non-zero status if missing; place this
check immediately before the existing `forge build` invocation so `forge build`
and `forge test -vvv` only run when `forge` is available.
🤖 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 @.github/workflows/ci.yml:
- Around line 15-16: Update the Checkout step that uses actions/checkout@v4 to
explicitly set persist-credentials: false so the runner does not persist the
GITHUB_TOKEN in the workspace; locate the step referencing uses:
actions/checkout@v4 and add the persist-credentials: false input to that step to
reduce token exposure for later steps.
- Line 16: Replace mutable action tags with immutable 40-character commit SHAs
for every `uses:` entry (e.g., change `actions/checkout@v4` to
`actions/checkout@<commit-sha>`) so CI is not vulnerable to upstream tag
retargeting; update all occurrences (including the other `uses:` entries
flagged) and ensure each `uses:` value is a full 40-char SHA rather than `@vX`
or other mutable tags.

In `@ops/AUTONOMOUS_RUN.md`:
- Line 62: Update the markdown header "### PR 2: Reproducible baseline tooling"
to match the correct GitHub PR number by changing it to "### PR `#4`: Reproducible
baseline tooling" (or alternatively "### Queue Item 2: Reproducible baseline
tooling" if you intend to reference queue order); search for the exact header
string "PR 2: Reproducible baseline tooling" and replace it so the worklog
header matches the identifier used later on (line referencing Pull Request `#4`).

In `@scripts/bootstrap-ec2.sh`:
- Line 29: The script currently calls foundryup with the -v flag which only
prints the tool version instead of installing/switching to the requested
release; update the invocation in scripts/bootstrap-ec2.sh to call the install
subcommand with the pinned version (e.g., use foundryup install
"$FOUNDRY_VERSION" or the foundryup command form that explicitly
installs/switches to the provided version) so the environment actually uses the
FOUNDRY_VERSION variable, and then validate by running forge --version to ensure
the pinned release is active.

In `@scripts/bootstrap-windows.ps1`:
- Line 57: The script currently calls "python -m venv .venv-tools" directly
which fails if python isn't on PATH; change the venv creation to detect and
prefer the "python" executable but fall back to the Windows Python launcher "py"
if "python" isn't found, and fail with a clear message if neither is available.
Implement detection using PowerShell command existence checks (e.g.,
Get-Command) and then invoke either "python -m venv .venv-tools" or "py -m venv
.venv-tools" accordingly, ensuring the chosen launcher is stored/used
consistently for subsequent Python operations in the script. Ensure error
handling logs which launcher was attempted when reporting failure.

---

Nitpick comments:
In `@scripts/check.sh`:
- Around line 7-10: Add an explicit preflight check for the `forge` binary
before running `forge build`/`forge test`: use `command -v forge >/dev/null
2>&1` (or `which forge`) to detect absence, print a clear actionable error
message instructing how to install Foundry (matching the Windows script
guidance), and exit with a non-zero status if missing; place this check
immediately before the existing `forge build` invocation so `forge build` and
`forge test -vvv` only run when `forge` is available.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b68491db-f711-4f2f-9c31-e9d552725ec7

📥 Commits

Reviewing files that changed from the base of the PR and between 58cc1d7 and 740a02a.

📒 Files selected for processing (21)
  • .editorconfig
  • .gitattributes
  • .github/workflows/ci.yml
  • .gitignore
  • Makefile
  • README.md
  • docs/adr/README.md
  • docs/known-blockers.md
  • docs/status.md
  • docs/tooling.md
  • foundry.toml
  • ops/AUTONOMOUS_RUN.md
  • requirements-tools.txt
  • script/README.md
  • scripts/bootstrap-ec2.sh
  • scripts/bootstrap-windows.ps1
  • scripts/check.ps1
  • scripts/check.sh
  • smart-contracts/RandomizerRNG.sol
  • smart-contracts/RandomizerVRF.sol
  • test/README.md

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread ops/AUTONOMOUS_RUN.md Outdated
Comment thread scripts/bootstrap-ec2.sh Outdated
Comment thread scripts/bootstrap-windows.ps1 Outdated

punk6529 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@claude pushed 47a23d1 addressing your PR #4 review:

  • scripts/bootstrap-windows.ps1 now uses -UseBasicParsing, preflights real Python / py -3, and checks native command exit codes.
  • scripts/bootstrap-ec2.sh now downloads the pinned Foundry release tarball plus .sha256, verifies it, and extracts directly instead of curl | bash.
  • Makefile now handles native Windows vs Git Bash/MSYS PATH separators and prepends the local venv tool directory for make slither.
  • scripts/check.sh now mirrors the PowerShell missing-Forge guard.
  • .gitignore now permits .env.example, .env.sample, and .env.template while still ignoring local env files.

Validation rerun locally: make check, scripts\check.ps1, bash -n for both bash scripts, PowerShell parser check for bootstrap-windows.ps1, make -n slither, and env-ignore exit-code checks. Please re-review the updated head.

punk6529 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@claude pushed 8944025 addressing the CodeRabbit CI hardening pass:

  • pinned every GitHub Actions uses: reference to a 40-character commit SHA
  • set actions/checkout persist-credentials: false
  • clarified the durable worklog heading/status for PR Add reproducible Foundry baseline #4
  • re-ran local gates: action pin check, git diff --check, make check, and powershell -ExecutionPolicy Bypass -File scripts\check.ps1

Please re-review this PR.

@punk6529 punk6529 merged commit 0d94384 into main Jun 9, 2026
2 checks passed
Comment on lines +75 to +88
if (-not $SkipPathUpdate) {
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
$pathParts = @()
if ($userPath) {
$pathParts = $userPath -split ";" | Where-Object { $_ -ne "" }
}

$normalizedInstallDir = $InstallDir.TrimEnd("\")
$alreadyPresent = $pathParts | Where-Object { $_.TrimEnd("\") -ieq $normalizedInstallDir }
if (-not $alreadyPresent) {
[Environment]::SetEnvironmentVariable("Path", (($pathParts + $InstallDir) -join ";"), "User")
Write-Host "Added $InstallDir to the user PATH. Open a new terminal to use it automatically."
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 🔴 scripts/bootstrap-windows.ps1 lines 75-88 silently corrupt the persistent User PATH when it has exactly one non-empty entry. PowerShell pipeline assignment auto-unrolls a single-element result on line 79, so $pathParts becomes a scalar [string] (the $pathParts = @() on line 77 is overwritten). Line 85's ($pathParts + $InstallDir) is then string concatenation (no separator), and SetEnvironmentVariable durably writes a mashed entry like …\WindowsAppsC:\Users\foo\.foundry\bin to HKCU PATH — the script prints "Bootstrap complete" with no error, but the next shell has both WindowsApps and .foundry\bin unresolvable. The single-entry case is the default state on fresh Windows 10/11 (only %USERPROFILE%\AppData\Local\Microsoft\WindowsApps in User PATH) — exactly the persona this onboarding script targets. Fix: wrap with @(...) to force array semantics — $pathParts = @($userPath -split ";" | Where-Object { $_ -ne "" }).

Extended reasoning...

What the bug is

scripts/bootstrap-windows.ps1 lines 75-88 update the persistent User PATH via this idiom:

$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
$pathParts = @()
if ($userPath) {
    $pathParts = $userPath -split ";" | Where-Object { $_ -ne "" }
}
$normalizedInstallDir = $InstallDir.TrimEnd("\")
$alreadyPresent = $pathParts | Where-Object { $_.TrimEnd("\") -ieq $normalizedInstallDir }
if (-not $alreadyPresent) {
    [Environment]::SetEnvironmentVariable("Path", (($pathParts + $InstallDir) -join ";"), "User")
    Write-Host "Added $InstallDir to the user PATH. Open a new terminal to use it automatically."
}

The line 77 $pathParts = @() initialization is overwritten on line 79 by the pipeline result. In PowerShell, pipeline output assigned to a variable is auto-unrolled when it emits exactly one item — $pathParts becomes a scalar [string], not a single-element array. That's the canonical PowerShell footgun this bug is about.

Step-by-step proof (default Windows 10/11, fresh contributor)

A default Windows 10/11 User PATH typically contains exactly one entry: %USERPROFILE%\AppData\Local\Microsoft\WindowsApps, added automatically by Windows for the App Execution Alias system. So:

  1. Line 76: $userPath = "C:\Users\foo\AppData\Local\Microsoft\WindowsApps" (single non-empty entry).
  2. Line 77: $pathParts = @() (empty array — immediately overwritten by line 79).
  3. Line 79: $userPath -split ";" returns @("C:\…\WindowsApps") (1-element array); | Where-Object { $_ -ne "" } emits the single non-empty string; pipeline assignment unrolls it to a scalar [string]. $pathParts is now a [string], not an array.
  4. Line 83: $pathParts | Where-Object { … } — piping a scalar through Where-Object works (PowerShell pipes scalars), but it doesn't match $normalizedInstallDir, so $alreadyPresent is $null.
  5. Line 85: ($pathParts + $InstallDir) — when the left operand is a [string], + is string concatenation, not array append. Result: "C:\Users\foo\AppData\Local\Microsoft\WindowsAppsC:\Users\foo\.foundry\bin" (no separator).
  6. -join ";" on a single scalar string is a no-op.
  7. SetEnvironmentVariable("Path", $mashed, "User") durably writes the corrupted single-entry value to HKCU PATH.
  8. Script prints "Added $InstallDir to the user PATH..." and exits with Bootstrap complete.

Why existing code doesn't prevent it

The $pathParts = @() on line 77 looks defensive but is overwritten on line 79 by the unrolled pipeline result. There is no @(...) wrap around the pipeline to force array semantics. The empty-PATH case is safe (line 77's @() is kept), and the 2+-entry case is safe (the pipeline emits multiple items, so the assignment captures an Object[]). Only the exactly-1-entry case triggers — but that's the default state on the fresh-Windows persona this script explicitly targets.

The PR's Windows validation only ran scripts\check.ps1 (per ops/AUTONOMOUS_RUN.md), which does not exercise the bootstrap PATH-rewrite path. The Claude and CodeRabbit reviews flagged -UseBasicParsing, the Python launcher fallback, and CI-action pinning, but did not catch this distinct array-unrolling bug. The recent commit 47a23d1 added -UseBasicParsing and Python preflight, but lines 75-88 are unchanged.

Impact

Silent, durable corruption of HKCU PATH — the worst kind of side effect for a contributor-onboarding script. After bootstrap "succeeds":

  • WindowsApps becomes unresolvable on the next shell (python, winget, etc. break via the App Execution Alias system).
  • .foundry\bin\forge.exe is also not on PATH (glued to WindowsApps without a separator).
  • No error signal — the script prints Bootstrap complete.
  • Recovery requires opening the Environment Variables UI and pasting back a properly-separated PATH.

Fix

The canonical one-character idiom — wrap the pipeline in @(...) to force array semantics regardless of element count:

$pathParts = @($userPath -split ";" | Where-Object { $_ -ne "" })

With @(...), the result is always an array (0, 1, or N items), so:

  • $pathParts + $InstallDir is array+scalar = array append.
  • -join ";" produces a correctly semicolon-separated PATH.

This also makes the $pathParts = @() initializer on line 77 redundant (the @(...) wrap covers the empty-string case too), though leaving it in place is harmless.

Comment thread requirements-tools.txt
Comment on lines +1 to +2
slither-analyzer==0.11.5
solc-select==1.2.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 The pinned slither-analyzer==0.11.5 in requirements-tools.txt requires Python >=3.10 per PyPI metadata, but README.md:40, docs/tooling.md:52, and the bootstrap-windows.ps1:57 error message all say "Python 3.8+". Neither bootstrap-ec2.sh (which installs the distro default python3 — 3.7 on AL2, 3.8 on Ubuntu 20.04, 3.9 on AL2023/Debian 11/RHEL 9) nor bootstrap-windows.ps1 (whose Resolve-Python only filters out WindowsApps stubs) validates the interpreter version before pip install. A fresh contributor on any of those common images runs the bootstrap, hits a cryptic Package slither-analyzer requires a different Python: 3.x not in >=3.10, and set -euo pipefail aborts the whole bootstrap — directly contradicting the docs they just followed. Fix: either pin a 3.8-compatible slither line, or raise the documented floor to "Python 3.10+" uniformly in README.md / docs/tooling.md / the PS1 error message AND add an explicit sys.version_info >= (3, 10) preflight in both bootstrap scripts before pip install.

Extended reasoning...

What the bug is

requirements-tools.txt pins slither-analyzer==0.11.5, whose PyPI metadata explicitly declares Requires-Python: ">=3.10" (and classifiers list only Python 3.10–3.14). Both bootstrap scripts add this requirements file to a freshly-created .venv-tools without first checking the interpreter version, while the documentation and the Windows error message advertise Python 3.8+ as sufficient.

Specifically:

  • requirements-tools.txt:1slither-analyzer==0.11.5 (Requires-Python: >=3.10)
  • README.md:40 — "On Windows, install Python 3.8+ or the py launcher"
  • docs/tooling.md:52 — "Windows bootstrap requires Python 3.8+ or the py launcher"
  • scripts/bootstrap-windows.ps1:57throw "Python 3.8+ is required. Install Python from python.org or install the py launcher, then re-run this script."
  • scripts/bootstrap-ec2.sh:9–17install_packages() just runs apt-get/dnf/yum install -y ... python3 ... with no version pin
  • scripts/bootstrap-ec2.sh:79python3 -m venv .venv-tools then pip install -r requirements-tools.txt, no preflight
  • scripts/bootstrap-windows.ps1:36–58Resolve-Python only filters out WindowsApps reparse stubs; the py -3 fallback accepts any Python 3.x including 3.7/3.8/3.9

Why the existing code does not prevent it

The Linux path trusts whatever python3 the distro packages. The Windows path trusts whatever python or py -3 resolves to that is not a WindowsApps stub. set -euo pipefail aborts on the pip failure but only after producing a generic pip error — the script never says "your Python is too old"; the docs the user just followed said 3.8 was fine. solc-select==1.2.0 (the other pinned tool) declares Requires-Python: ">=3.8", so it does not force the floor up — only slither does.

Step-by-step proof

  1. Provision a fresh Amazon Linux 2 EC2 instance (python3 → 3.7) — exactly the persona bootstrap-ec2.sh is named for.
  2. Clone the repo and run bash scripts/bootstrap-ec2.sh.
  3. install_packages() installs python3 (3.7); install_foundry() succeeds.
  4. install_python_tools() runs python3 -m venv .venv-tools — succeeds with the 3.7 interpreter.
  5. pip install -r requirements-tools.txt fails: ERROR: Package slither-analyzer requires a different Python: 3.7.x not in >=3.10.
  6. set -euo pipefail exits the bootstrap non-zero, leaving the contributor with Foundry installed but no slither/solc-select, and an error that does not reference the documented "Python 3.8+" claim.

Same outcome on Ubuntu 20.04 (3.8), Amazon Linux 2023 / Debian 11 / RHEL 9 (3.9). On Windows, a contributor who followed README.md:40 and installed Python 3.8 or 3.9 from python.org hits the same pip failure — Resolve-Python returns that interpreter happily.

Impact

This is the Gate A contributor-onboarding promise of this PR, and the bootstrap network/venv path was not validated end-to-end on a fresh image (the PR description shows validation was forge build / forge test / make check on an already-configured machine). The most common EC2 AMI families (AL2, AL2023) and Ubuntu 20.04 / Debian 11 / RHEL 9 all fail. The user-visible symptom is a cryptic pip error against documentation that promised 3.8+ would suffice.

How to fix

Pick one (the verifiers consistently propose the same two options):

  1. Pin a Python-3.8-compatible slither line (e.g. an older 0.10.x release that supports Python 3.8+). Keeps the docs accurate.
  2. Raise the documented floor to "Python 3.10+" uniformly in README.md:40, docs/tooling.md:52, and the bootstrap-windows.ps1:57 error message, AND add a preflight in both bootstrap scripts before the pip install, for example:
    • Linux: python3 -c "import sys; sys.exit(0 if sys.version_info >= (3, 10) else 1)" || { echo "Python 3.10+ required (found $(python3 -V))"; exit 1; }
    • Windows: a matching $ver = & $python.FilePath -c "import sys; print(f{sys.version_info.major}.{sys.version_info.minor})" check that throws a clear actionable error.

Either way, align the docs with the actual floor that requirements-tools.txt imposes, so the bootstrap can fail fast and informatively instead of dying inside pip.

Comment thread .editorconfig
Comment on lines +3 to +9
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The new .editorconfig sets indent_size = 4 under [*] with no overrides for YAML or shell, but the YAML and shell files added in this same PR — .github/workflows/ci.yml, scripts/bootstrap-ec2.sh, scripts/check.sh — all use 2-space indentation. Editors that honor .editorconfig (VS Code with the EditorConfig extension, JetBrains, vim-editorconfig) will auto-indent newly typed lines at 4 spaces in these files, producing mixed-indent diffs and — for YAML, where indentation is structurally significant — potentially breaking the document. One-line fix: add [*.{yml,yaml}] and [*.sh] blocks with indent_size = 2 next to the existing [Makefile] / [*.md] / [*.ps1] overrides.

Extended reasoning...

What the bug is. .editorconfig lines 3-9 declare [*] with indent_style = space and indent_size = 4. The only overrides are [Makefile] (tab), [*.md] (whitespace), and [*.ps1] (CRLF) — none of which set indent_size for YAML or shell. Yet the files this PR introduces under those globs use 2-space indentation: .github/workflows/ci.yml is standard 2-space YAML, and scripts/bootstrap-ec2.sh / scripts/check.sh use 2-space throughout.

Why existing config doesn't prevent it. There is no glob entry for *.yml, *.yaml, or *.sh, so they all fall through to the [*] default of 4. The .editorconfig and the committed files therefore disagree about indent width for the very files the PR adds.

Step-by-step proof. A contributor opens .github/workflows/ci.yml in VS Code with the EditorConfig extension (or JetBrains IDE / vim-editorconfig). They hit Enter inside the existing jobs.foundry.steps block, currently indented at 6 spaces (3 levels × 2 spaces). The editor reads .editorconfig, sees indent_size = 4 applies to this file, and inserts 4 spaces for the next level instead of 2. The new line is now at 10 spaces instead of 8, which is invalid YAML structurally — YAML treats different indentation widths within the same block as a parse error or as a different scope. For scripts/check.sh and scripts/bootstrap-ec2.sh the consequence is less severe (bash doesn't care about indent width) but the file accumulates mixed 2/4-space blocks that produce noisy diffs.

Impact. Low — .editorconfig is editor-side guidance, not a CI gate, and the committed files themselves are well-formed today. The breakage is contributor-side and only manifests when a contributor edits one of these files in an EditorConfig-aware editor without manually overriding indent. However, the asymmetry is real: the PR explicitly establishes a reproducible-baseline formatting layer and goes to the trouble of overriding [Makefile], [*.md], and [*.ps1] — leaving YAML and shell out is an obvious gap in the same set of files.

How to fix. One line per filetype:

[*.{yml,yaml}]
indent_size = 2

[*.sh]
indent_size = 2

Alternatively, invert the default to indent_size = 2 and add [*.sol] indent_size = 4 for Solidity (which is 4-space in this repo). Either approach restores agreement between .editorconfig and the source-of-truth indentation used in the files the PR ships.

Comment thread scripts/check.ps1
Comment on lines +19 to +20
forge build
forge test -vvv

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 scripts/check.ps1 runs forge build and forge test -vvv sequentially (lines 19-20) without checking $LASTEXITCODE between them. In PowerShell 5.1 (the declared target via #Requires -Version 5.1), $ErrorActionPreference = 'Stop' only governs cmdlet errors — native command non-zero exits set $LASTEXITCODE but do not terminate the script. So if forge build fails, forge test -vvv still runs, and the final exit code reflects only forge test. With the documented "no tests found" state where forge test exits 0, a forge build failure can be silently masked. This is asymmetric with scripts/check.sh's set -euo pipefail and with the sibling scripts/bootstrap-windows.ps1:23-34 Invoke-Native helper added in the same PR. Fix: reuse the Invoke-Native helper or add if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } after each forge invocation.

Extended reasoning...

What the bug is

scripts/check.ps1 lines 19-20 invoke two native commands in sequence:

forge build
forge test -vvv

The script declares #Requires -Version 5.1 on line 1, so Windows PowerShell 5.1 is the supported target. In PS 5.1, $ErrorActionPreference = 'Stop' (line 5) governs cmdlet errors only. Native executables like forge.exe set $LASTEXITCODE on failure but do not raise a terminating error — they continue execution. The opt-in fix ($PSNativeCommandUseErrorActionPreference) only landed in PowerShell 7.3+, so PS 5.1 users get no automatic propagation.

Result: a failed forge build does not stop the script. forge test -vvv runs anyway, and the script's final exit code becomes whatever forge test returned.

Why the existing code doesn't prevent it

$ErrorActionPreference = 'Stop' is the only error-handling layer in check.ps1. There is no Invoke-Native wrapper, no manual $LASTEXITCODE check, no -ErrorAction plumbing. Crucially, this same PR's sibling script scripts/bootstrap-windows.ps1:23-34 already implements exactly the right pattern:

function Invoke-Native {
    param([Parameter(Mandatory = $true)][string]$FilePath, [string[]]$Arguments = @())
    & $FilePath @Arguments
    if ($LASTEXITCODE -ne 0) {
        throw "$FilePath failed with exit code $LASTEXITCODE."
    }
}

That helper was added in commit 47a23d1 in direct response to Claude's earlier review flagging this exact PS 5.1 footgun. The same fix was not propagated to check.ps1, even though check.ps1 is the documented Windows smoke entrypoint referenced from README.md and docs/tooling.md.

Asymmetry with check.sh

The Linux peer scripts/check.sh:2 has set -euo pipefail, which aborts on the first non-zero exit. Two scripts that are presented as platform-equivalent entrypoints actually behave differently in the failure case.

Step-by-step proof

  1. Windows contributor runs powershell -ExecutionPolicy Bypass -File scripts\check.ps1 against a checkout where a Solidity file has a non-compilation forge build issue (e.g. a cache write permission failure, a flag-specific build issue, or a future script/ compile error confined to deployment scripts not included in the test compile path).
  2. Line 19 forge build exits non-zero. $LASTEXITCODE is set, but $ErrorActionPreference = 'Stop' does not raise a terminating error for native exit codes in PS 5.1.
  3. Execution continues to line 20. forge test -vvv runs.
  4. The PR description explicitly notes "forge test -vvv currently executes but reports no tests found" — the no-tests path exits 0.
  5. The script exits with code 0. CI / the contributor's terminal reports success despite the build failure.

Compare with check.sh: set -euo pipefail would abort at step 2 with the build's non-zero exit. Compare with bootstrap-windows.ps1: every native call is wrapped in Invoke-Native and throws on non-zero $LASTEXITCODE.

Impact

Nit severity. The practical masking window is narrow today because forge test re-compiles sources and typically catches the same compile errors forge build would. The realistic masking case is a forge build failure that doesn't recur in forge test's code path — for example a compile error confined to script/ (Foundry's script directory is built by forge build but not by forge test), a cache/permission issue specific to a build flag, or any non-compilation forge build failure while forge test exits 0 with no tests found.

Even where the masking is unlikely to fire today, the asymmetry with the same PR's own Invoke-Native helper and with check.sh's set -euo pipefail is the substantive concern — the PR explicitly addressed this PS 5.1 footgun in one place and missed the documented Windows smoke entrypoint.

How to fix

One-line fix per invocation:

forge build
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
forge test -vvv
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

Or, cleaner, reuse the existing helper from bootstrap-windows.ps1 (lift it into a shared module or duplicate it):

Invoke-Native -FilePath 'forge' -Arguments @('build')
Invoke-Native -FilePath 'forge' -Arguments @('test', '-vvv')

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant