Skip to content

feat: support prebuilt VPP packages (runtime-v4 upload + arduino-cli mixed compile/link) - #886

Merged
JoaoGSP merged 9 commits into
developmentfrom
feat/vpp-arduino-prebuilt
Jun 19, 2026
Merged

feat: support prebuilt VPP packages (runtime-v4 upload + arduino-cli mixed compile/link)#886
JoaoGSP merged 9 commits into
developmentfrom
feat/vpp-arduino-prebuilt

Conversation

@marconetsf

@marconetsf marconetsf commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Editor-side support for provisioning: "prebuilt" VPP packages (vendor IP protection). Consumes the packages produced by the openplc-packages PR:

  • runtime-v4 — packages the prebuilt-object plugin for upload to the runtime.
  • arduino-cli — compiles the open hal.source integration layer locally and links the precompiled vendor library; pins the ABI-locked core.

What's in here

  • Types + BoardInfoResolver: surface target.coreVersion and hal.precompiledLibrary (resolved to an absolute dir) on the board build info.
  • Compile pipeline (handleCompileArduinoProgram): pass the precompiled lib as a 2nd --library so arduino-cli resolves the vendor boundary header and auto-links src/<build.mcu>/lib*.a (precompiled=full). The hal.source layer still compiles locally alongside the sketch (sees defines.h/vpp_config.h).
  • Core pin: install the exact target.coreVersion (core install <id>@<version>, replacing a divergent installed version, failing if it does not exist) — aligned with the producer-side build:objects. Unpinned source-mode boards still accept any installed version.
  • Tests: 2nd --library emission, resolver mapping of coreVersion/precompiledLibraryDir, and the exact-version core pin.

Verification

  • tsc --noEmit clean on touched files; new/updated jest suites pass.
  • End-to-end: installing the P1AM-200 .vpp and compiling a POU pins FACTS:samd@1.7.13, compiles the source layer, links the vendor lib, and produces a .hex.

Relates to DOPE-313.

Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for mixed compilation with prebuilt Arduino-hal by linking an additional precompiled library during Arduino CLI compile.
    • Introduced optional exact Arduino core version pinning to ensure ABI-compatible core installation/verification.
    • Enhanced vendor plugin packaging to handle both source and prebuilt provisioning modes, with clearer copy-source logging.
  • Bug Fixes

    • Updated core installation behavior to only skip installs when no version pin is provided and the core is already present.
  • Tests

    • Added Jest coverage for core installation, vendor plugin packaging, board selection resolution, and generated Arduino CLI arguments.

marconetsf and others added 7 commits June 17, 2026 11:25
Supports runtime-v4 native plugins shipped as precompiled objects
(hal.provisioning === "prebuilt", Option C of the packages repo). In prebuilt
mode hal.pluginEntry is the plugin DIRECTORY (holding the .o objects + link-only
Makefile) rather than an entry source file, so handleVendorPluginPackaging
resolves the dir directly instead of via dirname(). The existing collectAndCopy
+ checksum then copy the .o + Makefile into vpp_plugin/ unchanged (config_template
is still excluded), and the runtime links them as it already does for sources.

- types.ts: add optional hal.provisioning and hal.minRuntimeVersion (the zod
  transport schema stays untouched — it is passthrough by design).
- compiler-module.ts: branch the plugin-dir resolution on provisioning; mode-aware
  success log.

No change for existing source packages (provisioning absent => source).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… branch

The prebuilt-object plugin support didn't require any runtime change, so
the minRuntimeVersion ABI floor was never consumed — drop the field from
the shared PackageManifest.hal type (provisioning stays). Add a direct
test for handleVendorPluginPackaging's prebuilt-vs-source provisioning
branch (pluginEntry as directory vs file), exclusion of editor-only
files, and the deterministic checksum.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…led lib)

Wire the editor compile path for prebuilt arduino-hal VPPs, which ship an
open hal.source integration layer (compiled locally, owns the OpenPLC
contract and defines.h/vpp_config.h coupling) plus a precompiled vendor
library linked via --library.

- types: PackageManifest gains target.coreVersion and hal.precompiledLibrary
- board-info-resolver: BoardBuildInfo carries coreVersion/precompiledLibraryDir;
  #fromVppDevice maps them from the manifest
- resolve-board-selection: forward both fields onto boardEntry
- pipeline: pass coreVersion to installArduinoCore and precompiledLibraryDir
  as prebuiltLibraryPath to the arduino-cli arg builder
- build-arduino-cli-args: emit a 2nd --library when prebuiltLibraryPath is set
- compiler-platform-port + adapter: thread coreVersion through
- compiler-module: handleCoreInstallation installs core@version (pins and
  verifies the ABI-locked core; fails on mismatch)

The source layer still compiles through the existing arduino-cli path, so it
sees defines.h/vpp_config.h; no runtime pin-config shim and no generate-defines
marker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
handleCompileArduinoProgram (the editor's real arduino-cli compile path,
distinct from the shared pipeline.ts) composed its args without the vendor
precompiled library, so a prebuilt arduino-hal VPP failed with
"p1am_vendor.h: No such file or directory" — arduino-cli never saw the lib.

The open hal.source layer is renamed to arduino.cpp and compiled here
alongside the sketch (NOT in the precompile pass), and it includes the
vendor boundary header. Pass info.precompiledLibraryDir as a 2nd --library
so arduino-cli puts the lib's src/ on the include path (resolves the header)
and auto-links the src/<build.mcu>/lib*.a archive (the lib ships
precompiled=full). Preserves the OpenPLCUserLib ldflags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The prebuilt arduino library is ABI-locked to target.coreVersion, so the
strict policy installed and verified exactly that version. Relax it for now:
if the same core (by id) is already installed, accept any version instead of
forcing the pinned one. The pinned version is still used to choose what to
install when the core is absent.

Left a TODO to re-introduce exact-version verification once the team settles
the ABI-pin policy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- build-arduino-cli-args: a 2nd --library is emitted for the vendor
  precompiled lib (right after the main one, before --export-binaries),
  and a single --library when prebuiltLibraryPath is absent.
- resolve-board-selection: a prebuilt arduino-hal VPP board surfaces
  coreVersion and the package-resolved precompiledLibraryDir on boardEntry.
- handle-core-installation: the relaxed core pin skips install (no spawn)
  when the same core is already present, even with a divergent pinned
  version, and logs accordingly.

The handleCoreInstallation test lives in __tests__/ rather than the
existing compiler-module.spec.ts because jest only collects .test.ts under
__tests__/ here — the .spec.ts files are not picked up by the current
testMatch config.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Revert the relaxed "same core present" policy. A prebuilt arduino library is
ABI-locked to target.coreVersion, so the editor must install exactly that
version. With a pinned version, always run `core install <id>@<version>`
(arduino-cli installs that exact version and fails if it does not exist),
which both pins and verifies it; a divergent already-installed version is
replaced. Without a pinned version (source-mode boards), any installed
version is still accepted.

Update the tests to assert the exact-version install (core present with a
divergent version, and core absent), the non-zero-exit rejection, and that
the skip path only triggers for an unpinned, already-installed core.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a95b6ae0-f444-43b6-8729-ad281b184c90

📥 Commits

Reviewing files that changed from the base of the PR and between ee03886 and d037135.

📒 Files selected for processing (1)
  • src/backend/editor/compiler/__tests__/handle-core-installation.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/backend/editor/compiler/tests/handle-core-installation.test.ts

Walkthrough

Adds end-to-end support for prebuilt arduino-hal boards with ABI-locked Arduino core versions. Extends PackageManifest, BoardBuildInfo, and pipeline types with precompiledLibraryDir, coreVersion, and provisioning fields; threads them through board resolution, compile arg construction, core installation, and VPP plugin packaging, with comprehensive new tests for each changed component.

Changes

Prebuilt arduino-hal board support

Layer / File(s) Summary
Type contracts for prebuilt HAL manifest fields
src/middleware/shared/ports/types.ts, src/backend/shared/hardware/board-info-resolver.ts, src/backend/shared/compile/pipeline.ts, src/middleware/shared/ports/compiler-platform-port.ts, src/backend/shared/firmware/build-arduino-cli-args.ts
PackageManifest gains hal.provisioning, hal.precompiledLibrary, and target.coreVersion. BoardBuildInfo, BoardHalsBuildEntry, InstallArduinoCoreArgs, and BuildArduinoCliCompileArgsOptions receive corresponding optional precompiledLibraryDir, coreVersion, and prebuiltLibraryPath fields.
VPP board info resolution and boardEntry mapping
src/backend/shared/hardware/board-info-resolver.ts, src/backend/shared/compile/steps/resolve-board-selection.ts, src/backend/shared/compile/__tests__/resolve-board-selection.test.ts
#fromVppDevice populates coreVersion and precompiledLibraryDir from the manifest via resolvePackageRelativePath. resolveBoardSelection forwards both into boardEntry. A new test verifies the full prebuilt VPP arduino-hal mapping.
arduino-cli compile args: conditional prebuilt --library
src/backend/shared/firmware/build-arduino-cli-args.ts, src/backend/shared/firmware/__tests__/build-arduino-cli-args.test.ts
buildArduinoCliCompileArgs unconditionally pushes --library libraryPath and conditionally appends --library prebuiltLibraryPath before --export-binaries. Two tests verify the single vs double --library argument behavior.
Pinned core installation and pipeline wiring
src/backend/editor/compiler/compiler-module.ts, src/backend/editor/compiler/editor-compiler-platform-port.ts, src/backend/shared/compile/pipeline.ts, src/backend/editor/compiler/__tests__/handle-core-installation.test.ts
handleCoreInstallation gains optional coreVersion; when set the already-installed skip is bypassed and boardCore@coreVersion is used. The port and pipeline thread coreVersion and precompiledLibraryDir to the handler and CLI arg builder. Five tests cover all handleCoreInstallation control-flow branches: null core, pinned install with/without existing version, rejection on failed exit, and skip-install with log message.
VPP plugin packaging prebuilt provisioning branch
src/backend/editor/compiler/compiler-module.ts, src/backend/editor/compiler/__tests__/handle-vendor-plugin-packaging.test.ts
handleVendorPluginPackaging Step 2 computes pluginSourceDir as the pluginEntry directory itself when provisioning === 'prebuilt', versus path.dirname(pluginEntry) for source mode. The completion log distinguishes the two modes. Tests cover both branches, verify config_template.json exclusion, checksum emission, and byte-for-byte .o file preservation.

Sequence Diagram(s)

sequenceDiagram
  participant Pipeline
  participant EditorPort as editor-compiler-platform-port
  participant HandleCore as handleCoreInstallation
  participant HandleCompile as handleCompileArduinoProgram
  participant ArduinoCLI as arduino-cli

  rect rgba(70, 130, 180, 0.5)
    note over Pipeline,HandleCore: Core install step
    Pipeline->>EditorPort: installArduinoCore({coreId, coreVersion?})
    EditorPort->>HandleCore: handleCoreInstallation(boardCore, logCb, coreVersion?)
    alt coreVersion provided
      HandleCore->>ArduinoCLI: core install boardCore@coreVersion
    else already installed, no pin
      HandleCore-->>EditorPort: skip — log "already installed"
    end
  end

  rect rgba(60, 160, 80, 0.5)
    note over Pipeline,ArduinoCLI: Compile step
    Pipeline->>HandleCompile: buildArduinoCliCompileArgs({libraryPath, prebuiltLibraryPath?})
    HandleCompile->>ArduinoCLI: compile --library libraryPath
    opt prebuiltLibraryPath present
      HandleCompile->>ArduinoCLI: --library prebuiltLibraryPath
    end
    HandleCompile->>ArduinoCLI: --export-binaries -b platform sketchPath
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • Autonomy-Logic/openplc-editor#794: Extends the shared compile pipeline introduced in that PR by adding prebuilt arduino-hal support through manifest fields, board resolution, and conditional core installation/library linking.

Suggested labels

feature

Suggested reviewers

  • thiagoralves
  • vmleroy

🐇 Hop hop, the bunny compiles with care,
Prebuilt libs linked without a spare!
--library twice with coreVersion pinned,
VPP provisioning modes intertwined,
From manifest shape to arduino-cli's call,
The prebuilt flow handles it all! 🎉✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% 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
Title check ✅ Passed The title accurately summarizes the main feature: adding support for prebuilt VPP packages with runtime-v4 upload and arduino-cli mixed compile/link capabilities.
Description check ✅ Passed The description provides a comprehensive summary covering objectives, implementation details, verification steps, and issue references, but lacks completion of the required DOD checklist.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/vpp-arduino-prebuilt

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

…ll verify

The coreVersion comments claimed the editor "verifies the installed version
matches", but there is no separate post-install verification step. Reword both
the InstallArduinoCoreArgs.coreVersion doc and the handleCoreInstallation
comment to describe what the code actually does: run `core install <id>@<version>`,
which installs exactly that version and fails if it is unavailable. No behavior
change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@marconetsf
marconetsf marked this pull request as ready for review June 18, 2026 11:56
Fixes the CI Format Check (npx prettier --check). Formatting only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (2)
src/backend/editor/compiler/compiler-module.ts (1)

1065-1084: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle spawn errors in core installation path to avoid hangs/unhandled process errors.

The Promise only resolves/rejects on close; if process launch fails, there is no error listener, so this path can fail noisily or never settle.

Suggested fix
     return new Promise<MethodsResult<string | Buffer>>((resolve, reject) => {
       const executeCommand = spawn(binaryPath, ['core', 'install', coreRef, ...this.arduinoCliBaseParameters])
+      executeCommand.on('error', (err: Error) => {
+        reject(new Error(`Failed to start Arduino CLI core install: ${err.message}`))
+      })

       let stderrData = ''
🤖 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 `@src/backend/editor/compiler/compiler-module.ts` around lines 1065 - 1084, The
spawn process for core installation does not have an error listener, causing the
Promise to never settle if the process fails to launch. Add an error event
listener to the executeCommand object returned by spawn to properly handle
process launch failures. The listener should call reject with the error that is
emitted, ensuring the Promise resolves or rejects in all cases and preventing
unhandled errors from occurring.
src/backend/editor/compiler/__tests__/handle-core-installation.test.ts (1)

1-127: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Prettier check is failing for this test file.

CI reports formatting issues here; please run Prettier on this file so the format job passes.

As per coding guidelines, **/*.{ts,tsx,js,jsx} must follow Prettier formatting (120 char width, no semicolons, single quotes, trailing commas).

🤖 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 `@src/backend/editor/compiler/__tests__/handle-core-installation.test.ts`
around lines 1 - 127, The test file handle-core-installation.test.ts has
Prettier formatting violations that need to be corrected. Run Prettier on this
file to automatically fix all formatting issues according to the project's style
guidelines (120 character line width, no semicolons, single quotes, and trailing
commas). This will ensure the file passes the CI formatting checks.

Sources: Coding guidelines, Pipeline failures

🧹 Nitpick comments (2)
src/backend/editor/compiler/__tests__/handle-vendor-plugin-packaging.test.ts (1)

33-41: ⚡ Quick win

Use @root/* imports instead of relative paths in this test.

Please replace ../../package-manager and ../compiler-module with @root/* aliases to match repository import conventions.

As per coding guidelines, "**/*.{ts,tsx,js,jsx}: ... Use path alias @root/* to reference ./src/*".

🤖 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 `@src/backend/editor/compiler/__tests__/handle-vendor-plugin-packaging.test.ts`
around lines 33 - 41, Replace the relative path strings used in the jest.mock()
call and the import statement with `@root/`* path aliases to match repository
conventions. Specifically, update the string argument in jest.mock() from the
relative path to the corresponding `@root/`* alias pointing to the package-manager
module, and change the import statement for CompilerModule from the relative
path to use the `@root/`* alias pointing to the compiler-module file. Ensure both
import paths follow the repository's path alias pattern where `@root/`* references
./src/*.

Source: Coding guidelines

src/middleware/shared/ports/types.ts (1)

759-769: ⚡ Quick win

Narrow hal.provisioning to a literal union.

Using provisioning?: string weakens the new contract and allows silent typos ('prebuit') to bypass the prebuilt path. Please type it as 'source' | 'prebuilt' (and keep precompiledLibrary documented as prebuilt-only).

♻️ Proposed fix
+type HalProvisioningMode = 'source' | 'prebuilt'
+
 export interface PackageManifest {
   ...
     hal: {
       type: string
       pluginType?: string
       /**
        * Native runtime-v4 plugin provisioning. "source" (default when absent):
        * pluginEntry is the entry source file and its directory is compiled on
        * the runtime. "prebuilt": pluginEntry is the directory holding the
        * precompiled .o objects plus a link-only Makefile; the runtime only links.
        */
-      provisioning?: string
+      provisioning?: HalProvisioningMode
       pluginEntry?: string
       ...
       precompiledLibrary?: string
🤖 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 `@src/middleware/shared/ports/types.ts` around lines 759 - 769, The
provisioning property in the type definition is currently typed as a generic
string, which allows typos and invalid values to pass undetected. Change the
type of the provisioning property from string to a literal union type that only
accepts the valid values 'source' or 'prebuilt'. This will ensure type safety
and prevent silent errors from typos in the provisioning value. Keep the
existing documentation comment for precompiledLibrary that explains it is
prebuilt-only.
🤖 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
`@src/backend/editor/compiler/__tests__/handle-vendor-plugin-packaging.test.ts`:
- Around line 156-157: The readFileSync call in the test is reading the binary
object file with 'utf-8' encoding, which can mask binary differences. Remove the
'utf-8' encoding parameter from the readFileSync function call for the
rpi_plugin.o file so that it returns a Buffer instead of a UTF-8 string. Then
update the expect assertion to compare the returned Buffer directly against a
Buffer value (rather than the string 'OBJECT-BYTES') to properly verify
byte-for-byte preservation of the binary file.

In `@src/backend/shared/hardware/board-info-resolver.ts`:
- Around line 288-296: The code currently allows hal.precompiledLibrary to be
set independently of target.coreVersion, but prebuilt libraries require the core
version to be pinned for ABI compatibility. Add validation in the manifest
schema or validation logic to enforce that whenever
device.hal.precompiledLibrary is declared, device.target.coreVersion must also
be present. This validation should run before the resolution logic that
populates the info object to catch the constraint violation early.

---

Outside diff comments:
In `@src/backend/editor/compiler/__tests__/handle-core-installation.test.ts`:
- Around line 1-127: The test file handle-core-installation.test.ts has Prettier
formatting violations that need to be corrected. Run Prettier on this file to
automatically fix all formatting issues according to the project's style
guidelines (120 character line width, no semicolons, single quotes, and trailing
commas). This will ensure the file passes the CI formatting checks.

In `@src/backend/editor/compiler/compiler-module.ts`:
- Around line 1065-1084: The spawn process for core installation does not have
an error listener, causing the Promise to never settle if the process fails to
launch. Add an error event listener to the executeCommand object returned by
spawn to properly handle process launch failures. The listener should call
reject with the error that is emitted, ensuring the Promise resolves or rejects
in all cases and preventing unhandled errors from occurring.

---

Nitpick comments:
In
`@src/backend/editor/compiler/__tests__/handle-vendor-plugin-packaging.test.ts`:
- Around line 33-41: Replace the relative path strings used in the jest.mock()
call and the import statement with `@root/`* path aliases to match repository
conventions. Specifically, update the string argument in jest.mock() from the
relative path to the corresponding `@root/`* alias pointing to the package-manager
module, and change the import statement for CompilerModule from the relative
path to use the `@root/`* alias pointing to the compiler-module file. Ensure both
import paths follow the repository's path alias pattern where `@root/`* references
./src/*.

In `@src/middleware/shared/ports/types.ts`:
- Around line 759-769: The provisioning property in the type definition is
currently typed as a generic string, which allows typos and invalid values to
pass undetected. Change the type of the provisioning property from string to a
literal union type that only accepts the valid values 'source' or 'prebuilt'.
This will ensure type safety and prevent silent errors from typos in the
provisioning value. Keep the existing documentation comment for
precompiledLibrary that explains it is prebuilt-only.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 14c20695-d7f6-473a-a2a0-9e318a6c2a01

📥 Commits

Reviewing files that changed from the base of the PR and between 852f2a1 and ee03886.

📒 Files selected for processing (12)
  • src/backend/editor/compiler/__tests__/handle-core-installation.test.ts
  • src/backend/editor/compiler/__tests__/handle-vendor-plugin-packaging.test.ts
  • src/backend/editor/compiler/compiler-module.ts
  • src/backend/editor/compiler/editor-compiler-platform-port.ts
  • src/backend/shared/compile/__tests__/resolve-board-selection.test.ts
  • src/backend/shared/compile/pipeline.ts
  • src/backend/shared/compile/steps/resolve-board-selection.ts
  • src/backend/shared/firmware/__tests__/build-arduino-cli-args.test.ts
  • src/backend/shared/firmware/build-arduino-cli-args.ts
  • src/backend/shared/hardware/board-info-resolver.ts
  • src/middleware/shared/ports/compiler-platform-port.ts
  • src/middleware/shared/ports/types.ts

Comment on lines +156 to +157
const copied = readFileSync(join(targetDir, 'vpp_plugin', 'rpi_plugin.o'), 'utf-8')
expect(copied).toBe('OBJECT-BYTES')

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

The “byte-for-byte” assertion should compare binary buffers, not UTF-8 text.

Reading with 'utf-8' can mask binary differences for real .o payloads. Compare Buffer values directly to make this test truly byte-preserving.

Proposed fix
-    const copied = readFileSync(join(targetDir, 'vpp_plugin', 'rpi_plugin.o'), 'utf-8')
-    expect(copied).toBe('OBJECT-BYTES')
+    const copied = readFileSync(join(targetDir, 'vpp_plugin', 'rpi_plugin.o'))
+    expect(copied.equals(Buffer.from('OBJECT-BYTES'))).toBe(true)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const copied = readFileSync(join(targetDir, 'vpp_plugin', 'rpi_plugin.o'), 'utf-8')
expect(copied).toBe('OBJECT-BYTES')
const copied = readFileSync(join(targetDir, 'vpp_plugin', 'rpi_plugin.o'))
expect(copied.equals(Buffer.from('OBJECT-BYTES'))).toBe(true)
🤖 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 `@src/backend/editor/compiler/__tests__/handle-vendor-plugin-packaging.test.ts`
around lines 156 - 157, The readFileSync call in the test is reading the binary
object file with 'utf-8' encoding, which can mask binary differences. Remove the
'utf-8' encoding parameter from the readFileSync function call for the
rpi_plugin.o file so that it returns a Buffer instead of a UTF-8 string. Then
update the expect assertion to compare the returned Buffer directly against a
Buffer value (rather than the string 'OBJECT-BYTES') to properly verify
byte-for-byte preservation of the binary file.

Comment on lines +288 to +296
if (device.target.coreVersion) info.coreVersion = device.target.coreVersion

const resolveRel = this.config.resolvePackageRelativePath
if (device.hal.source) info.halSourceFile = resolveRel(pkg.path, device.hal.source)
if (device.hal.pluginEntry) info.pluginEntry = resolveRel(pkg.path, device.hal.pluginEntry)
if (device.hal.configTemplate) info.configTemplate = resolveRel(pkg.path, device.hal.configTemplate)
if (device.hal.requirements) info.requirements = resolveRel(pkg.path, device.hal.requirements)
if (device.hal.libraries) info.localLibrariesDir = resolveRel(pkg.path, device.hal.libraries)
if (device.hal.precompiledLibrary) info.precompiledLibraryDir = resolveRel(pkg.path, device.hal.precompiledLibrary)

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Locate manifest schema/rules that define prebuilt HAL requirements
fd -i 'manifest.schema.json|schema.*manifest.*\.json|package-manifest.*\.json'

# 2) Inspect whether prebuilt implies target.coreVersion and hal.precompiledLibrary
rg -n -C3 '"provisioning"|coreVersion|precompiledLibrary|if|then|required|allOf|oneOf' $(fd -i 'manifest.schema.json|schema.*manifest.*\.json|package-manifest.*\.json')

# 3) Confirm runtime mapping + downstream pinning conditions
rg -n -C4 'coreVersion|precompiledLibraryDir|precompiledLibrary|handleCoreInstallation|core install' \
  src/backend/shared/hardware/board-info-resolver.ts \
  src/backend/shared/compile/steps/resolve-board-selection.ts \
  src/backend/editor/compiler/compiler-module.ts

Repository: Autonomy-Logic/openplc-editor

Length of output: 50388


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find manifest schema files in backend directories
fd -i 'schema|manifest' src/backend | grep -i 'json\|ts' | head -20

# 2) Look for device/hal type definitions
fd 'device\|board' src/backend | grep -E '\.(ts|json)$' | head -20

# 3) Check for validation or constraint logic
rg -l 'precompiledLibrary|coreVersion' src/backend --type ts | head -10

Repository: Autonomy-Logic/openplc-editor

Length of output: 307


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the board-info-resolver file to understand the device structure
head -100 src/backend/shared/hardware/board-info-resolver.ts

# Find device/hal type definitions
rg -n 'interface.*device|type.*device' src/backend --type ts -A 5 | head -40

# Search for precompiledLibrary and coreVersion usage
rg -n 'precompiledLibrary|coreVersion' src/backend/shared/hardware/board-info-resolver.ts -B 2 -A 2

Repository: Autonomy-Logic/openplc-editor

Length of output: 9074


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the type definition for `device` parameter in `#fromVppDevice`
rg -n 'fromVppDevice|device.*:.*Device' src/backend/shared/hardware/board-info-resolver.ts -B 2 -A 2

# Look at PackageManifest type definition
rg -n 'export.*type PackageManifest|interface PackageManifest' src/middleware/shared/ports/types.ts -A 30

# Check downstream code that consumes coreVersion
rg -n 'coreVersion|precompiledLibraryDir' src/backend --type ts -l

Repository: Autonomy-Logic/openplc-editor

Length of output: 1776


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get the full PackageManifest device structure
rg -n 'devices: Array' src/middleware/shared/ports/types.ts -A 100 | head -150

# Look for hal field in the device definition
rg -n 'hal:\s*\{' src/middleware/shared/ports/types.ts -A 20

# Check if there's a manifest.schema.json in resources
find . -name 'manifest.schema.json' -o -name '*manifest*schema*.json' 2>/dev/null | head -10

Repository: Autonomy-Logic/openplc-editor

Length of output: 4896


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check how precompiledLibraryDir and coreVersion are used downstream in resolve-board-selection
cat src/backend/shared/compile/steps/resolve-board-selection.ts

# Also check compiler-module for core installation
rg -n 'coreVersion|handleCoreInstallation|core.*install' src/backend/editor/compiler/compiler-module.ts -B 3 -A 3

Repository: Autonomy-Logic/openplc-editor

Length of output: 8652


Prebuilt Arduino HAL packages must enforce coreVersion alongside precompiledLibrary.

The manifest schema allows hal.precompiledLibrary and target.coreVersion to be declared independently. If a package declares hal.precompiledLibrary without target.coreVersion, the downstream core installation skips pinning (see handleCoreInstallation line 1049: core install only pins when coreVersion is present). Since prebuilt libraries are ABI-locked to their build core version (per code comments), this creates a contract violation: the wrong core version can be installed, breaking binary compatibility.

Add manifest schema validation to require target.coreVersion whenever hal.precompiledLibrary is declared.

🤖 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 `@src/backend/shared/hardware/board-info-resolver.ts` around lines 288 - 296,
The code currently allows hal.precompiledLibrary to be set independently of
target.coreVersion, but prebuilt libraries require the core version to be pinned
for ABI compatibility. Add validation in the manifest schema or validation logic
to enforce that whenever device.hal.precompiledLibrary is declared,
device.target.coreVersion must also be present. This validation should run
before the resolution logic that populates the info object to catch the
constraint violation early.

@marconetsf
marconetsf requested a review from JoaoGSP June 18, 2026 16:04

@JoaoGSP JoaoGSP left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@JoaoGSP
JoaoGSP merged commit fdca711 into development Jun 19, 2026
24 of 25 checks passed
@JoaoGSP
JoaoGSP deleted the feat/vpp-arduino-prebuilt branch June 19, 2026 12:02
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.

2 participants