Sync/autonomy development 20260703#4
Merged
Conversation
Fix a race where the renderer could request system:get-system-info while the Electron BrowserWindow was already being destroyed. The IPC handler previously called isMaximized() on a stale BrowserWindow reference, which raised "Object has been destroyed" and surfaced as an unhandled promise rejection in development logs. This change guards the main-process handler with isDestroyed() before reading window state, and wraps the renderer initialization request in try/catch so startup or teardown races do not produce unhandled rejections.
Guard development-only menu and devtools initialization paths against BrowserWindow teardown races. This avoids Object has been destroyed errors when the menu is rebuilt during window shutdown, adds an explicit catch for asynchronous menu rebuild failures, and downgrades optional React DevTools installation failures to a compact warning instead of a noisy stack trace during development startup.
Signature verification previously ran only in `importFromFile`; the persisted package store was not re-checked afterwards. Add `PackageManagerModule.verifyInstalledSignatures()`, run once at `app.whenReady()` before the first window. It re-verifies every registry-listed package against TRUSTED_PACKAGE_KEYS and removes any whose signature does not validate (directory + registry entry), logging a warning per removal. Out-of-tree registry paths are de-listed without touching disk. No-op when REQUIRE_SIGNATURE is false. Scope: VPP packages only (userData/packages). User-installed libraries (userData/libraries, LibraryManagerModule) are unsigned by design and are not affected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hority Mirrors the web change: the editor now centralizes JWT management in the main process via the shared RuntimeTokenManager, so every runtime call self-heals on expiry — including project upload, which previously did a raw HTTPS POST with no refresh (the reason a long session could keep polling status while uploads 401'd). - main process owns the token + credentials via the shared manager; login sets the session, clearCredentials clears it, and a single onTokenChanged subscription broadcasts runtime:token-refreshed to the renderer. - makeRuntimeApiRequest / makeRuntimeApiPostRequest now run through tokens.withAuth (replacing the bespoke attemptTokenRefresh + per-call broadcast). - New makeRuntimeApiUpload on the bridge does the multipart upload through tokens.withAuth; the upload pipeline (editor-compiler-platform-port) routes to it and the old refresh-less CompilerModule.sendRuntimeUpload is removed. - Shared, byte-identical pieces with web: RuntimeTokenManager (+tests), RuntimePort.getAccessToken, and use-runtime-polling adopting refreshed tokens into the store connection flag. - IPC handlers still accept (but ignore) the legacy jwtToken arg; the param is removed from the signatures in the follow-up cleanup. Validated the runtime auth contract against a live SLM-RP4: TTL 900s, expiry 401. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…PC surface With the main process as the token authority, the renderer no longer holds or passes a token. Removes jwtToken from the runtime + EtherCAT IPC handlers, the renderer bridge signatures, and the editor adapter (which now tracks only a loggedIn flag for isReadyForDebug); makeRuntimeApiRequest/Post drop the param too. The debugger's separate connectionParams.jwtToken is untouched. deviceContext.jwt is kept solely as the 'are we logged in' gate for the upload phase; the compile pipeline no longer threads it into runtime calls. Brings the rewritten editor runtime-adapter to 100% coverage (adds the previously-untested EtherCAT delegators). tsc --build, eslint, prettier clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keeps the editor layer validator aligned with web's classification of the shared RuntimeTokenManager directory. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nfo-window-teardown
…-dev-menu-startup # Conflicts: # src/main/menu.ts
…nfo-window-teardown
…-dev-menu-startup
…ndow-teardown Guard system info requests during window teardown
…-dev-menu-startup
…enu-startup Harden dev menu rebuild and extension startup paths
The .sidebar-scroll rail reserved scrollbar space on its left edge only (scrollbar-gutter: stable + direction: rtl), shifting every icon right of center: +3px in Chromium, +9px in Safari, which reserves the full ~16px native scrollbar width regardless of the ::-webkit-scrollbar customization. Items outside the scroll container (exit button, logo) stayed centered, making the offset visible. Hide the scrollbar from layout entirely (scrollbar-width: none + ::-webkit-scrollbar display: none) so the icon column centers in the full 56px rail; scrolling still works via wheel/trackpad. Also remove the nineties-theme rules that compensated for the old gutter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0118h3QKR2X7kPt2G2LMWjmL
Run the installed-package signature sweep when a project opens instead of once at app startup, and surface each removal as a WARNING in the console panel. verifyInstalledSignatures() is exposed through the PackagePort (editor adapter -> packages:verify-signatures IPC); workspace-screen calls it on project load and logs removed ids. On removal the main process emits packages:boards-updated so the device list refreshes via the existing subscription. Removes the app.whenReady() startup sweep. Platforms without a local package store (web) don't wire a `packages` port, so the shared open-project effect is a no-op there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-installed-signatures-on-startup
…ify-installed-signatures-on-startup fix(package-manager): verify installed VPP signatures on project open
…ed-runtime-token-manager
…lized-runtime-token-manager feat(runtime-auth): centralized token manager (editor half — fixes upload after token expiry)
…sidebar-misalignment fix(ui): center activity-bar icons by hiding the sidebar scrollbar
Fixes three UI/UX issues in the remote-device (Modbus master) and OPC-UA server editors: 1. Editing a Modbus IO Group's size had no effect. `updateIOGroup` only reassigned the group metadata via `Object.assign`, so the underlying `ioPoints` list (and therefore the size shown in the table) never changed. It now regenerates the edited group's I/O points to match the new length / function code, reusing the group's own freed addresses (pool built from every other producer) and preserving each point's alias positionally. The change is localized to the edited group — no project-wide reallocation. 2. Replaced double-click-to-edit on the IO Group table with per-row Edit / Delete icon buttons (matching the OPC-UA Security Profiles pattern) and removed the redundant "-" toolbar button, keeping only "+" to add. 3. Fixed low-contrast table headers on the OPC-UA Security Profiles, Users, and Certificates tables (unreadable in dark mode) by adopting the Modbus IO table's header styling. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ry (Phase 3) Wires the Modbus remote-device producer onto the central IEC address registry, delivering project-wide gap reclamation (bug #4) via the shared core instead of a bespoke allocator. - New `recalculateRemoteDeviceAddresses` project action: derives consumers from live producer state, keeps pin-mapping / VPP / EtherCAT pinned as fixed constraints, re-packs the Modbus producers (capability-scoped so a target switch drops inactive kinds and reclaims their space), writes the compacted addresses back onto the `ioPoints`, and reconciles bound variables via `syncVariableAliases`. - `addIOGroup` / `updateIOGroup` / `deleteIOGroup` / `deleteRemoteDevice` now call it, so removing a group or device slides the survivors down into the freed slots (project-wide, across devices) with no gaps. - EtherCAT stays on its own bit-offset allocator for now (participates as a fixed constraint); it moves onto the registry in a follow-up once its bit packing is verified against the runtime. Updated two tests that had asserted the old gap-preserving / localized behavior to the new project-wide compaction. 266 project-slice tests pass; new code fully covered. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…se 4) `setDeviceBoard` now triggers the central `recalculateRemoteDeviceAddresses` when the target actually changes. Because the recalculation is capability-scoped, switching from a platform WITH pin mapping / VPP to one WITHOUT frees those addresses and the Modbus producers recompact project-wide into the reclaimed space, with bound variables following. No-op when the board is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds `RegistryChannel.memoryKey` (a stable semantic identity like `moduleId:slot:channel` that outlives the channel's presence) and the pure `restoreAliasesFromMemory(registry, memory)` helper. This backs the requirement that removing a producer (e.g. a VPP module) and re-adding the same one on the same slot restores its aliases within a session — the memory is keyed so "same module, different slot" and "different module, same slot" resolve differently. The memory Record lives in the store (session-scoped, never serialized); this is just the pure restore step. 60 tests, 100% coverage. Additive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
VPP now delegates address allocation to the central registry instead of its own per-effect `nextFreeAddress` loop, so VPP + Modbus are packed by one allocator (project-wide, capability-scoped), and adds the session alias-memory that restores a module's aliases on remove→re-add. - Generalized `recalculateRemoteDeviceAddresses` → `recalculateIecAddresses`: reallocates VPP + Modbus (`ALLOCATED_KINDS`), restores aliases from the session memory (`restoreAliasesFromMemory`), and writes addresses+aliases back to VPP `io-mapping` entries (via `setVendorScreenData`) and Modbus `ioPoints`. - Session alias-memory: new `iecAliasMemory` project-slice field (never serialized) + `rememberChannelAlias` action. `migrateToRegistry` now stamps each channel with a stable `memoryKey`; `PoolVppIoInput` carries `moduleId`. - Both VPP layouts: allocator effects build channel structure then call `recalculateIecAddresses`; alias editors record into the session memory. Pins + EtherCAT remain fixed constraints for now (their own commits). 428 store/registry tests pass; tsc, eslint, arch clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EtherCAT channel addresses are now allocated by the central registry alongside VPP + Modbus (independent-prefix model, matching the runtime's separate typed buffers — the esi-parser's cross-width overlap avoidance was over-conservative and its output is now superseded here; the dead allocator is removed in the legacy pass). - `ALLOCATED_KINDS` += `ethercat`; new `applyEthercatAddresses` writes the registry's addresses + aliases back onto each slave's `channelMappings`. - `updateEthercatConfig` triggers `recalculateIecAddresses`, so channel-mapping changes recompact project-wide and bound variables follow. 271 project-slice tests pass; tsc, eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nstraints (Phase 7) - `clearProjects` now resets the session alias-memory so one project's remembered aliases can't leak into the next (the memory is session-scoped and never serialized; only current addresses/aliases persist to disk). - Documents pins as fixed registry constraints (hardware addresses are never reallocated; aliases persist per-board through the same uniqueness gate) and records the implementation status + deliberately-deferred legacy removals in docs/iec-address-registry.md. 272 project-slice tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lobals Derive the composite-key→index map from the debug tree (the one traverseVariable enumeration walk) instead of a parallel inline walk, and fan a polled value out to every composite key that shares an address. Fixes two shared-global bugs surfaced with located VAR_GLOBALs: - forcing a located global from the LD/FBD editor silently no-op'd (the inline index-map walk missed the VAR_EXTERNAL case, so the composite key had no protocol index). - a global referenced by two programs displayed on only one POU (the index→leaf map was 1:1; now 1:many). Also names the single path rule (buildVariableDebugPath); OPC-UA keeps using the shared path primitives + address map. HIL-validated on P1AM-100 + SLM-RP4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…led-globals fix(debugger): resolve + force shared/located globals (Autonomy-Logic#653)
The unit-tests workflow runs 'npm ci --ignore-scripts', which skips the postinstall that fetches strucpp (a GitHub-release package, not an npm dep). The TS sources import strucpp/libs/iec-types.json and its runtime headers, so every suite failed to load with 'Cannot find module strucpp' — the job has been red on every run since it was added. Add a '--strucpp-only' mode to download-binaries (installs just the platform-independent strucpp package, no xml2st binary / native rebuild) and a 'setup:strucpp' script, then run it after install in the workflow so the suites load and the coverage gate actually executes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-tests-strucpp fix(ci): install strucpp in the unit-tests job
These suites predate the XML/xml2st → in-process JSON-transpiler migration and
never ran in CI (the unit-tests job was broken), so they drifted:
- compile/pipeline.test.ts: rename transpileXmlToSt→transpileToSt, assert the
current {projectData} call contract, drop the dead XmlGenerator mock + XML
stage test, add a simulator-no-binary edge test.
- library/build-pipeline.test.ts: drop the XmlGenerator mock and the removed
XML-generation/empty-data paths; assert prepareXmlForLibraryBuild's current
{projectData,knownPous,manifest}|{error} contract.
- library/library-build-orchestrator.test.ts: fake port uses transpileToSt
({projectData}, log); remove plc.xml intermediates/logs; restore coverage.
All three source files back to 100% funcs/lines/statements. 95 tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Distinguish manually-located variables from alias-bound ones by storing the binding itself in `variable.location`: either an alias name OR a literal `%addr`. Aliases resolve to addresses at compile time in the editor (via projectActions.getCompileReadyProjectData); the compiler and runtime never see aliases. A missing alias resolves to an empty location (the variable becomes unlocated). - Remove the separate `alias` field from PLCVariable + the auto-adoption and sync-variable-aliases engine; location is stored verbatim. - Add registry/resolve helpers (buildAliasIndex, resolveLocation, isLiteralLocation) and a pre-compile snapshot transform. - renameAlias cascades onto every bound variable's location; EtherCAT / Modbus / pin / VPP alias edits drive it. - Legacy two-field projects are folded on load (foldLegacyVariableAliases). - Location picker allows a manual %addr even when an alias occupies that address; the cell renders location verbatim (alias name or %addr). - Warn (amber glyph + Radix tooltip) when an alias-bound location no longer resolves (orphaned) OR when a manual %addr collides with an alias in use by another project variable (duplicate-location risk). - Relax variableLocationValidation to accept alias-name locations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-shared-stale-tests test(backend/shared): de-XML-ify stale compile/library-build suites
…ec-address-registry
Replace the removed syncVariableAliases auto-adoption integration test with `alias-location-on-board-load.test.ts` asserting the single-field contract: board-load (setAvailableOptions) never mutates a variable's location, a manual %addr colliding with an alias stays literal, and an alias-bound location resolves to the producer address (or '') only in the compile-ready snapshot. Also note the manual-conflict warning in the CLAUDE.md alias-registry section. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…l-iec-address-registry feat(iec-address): central IEC address registry + single-field variable locations
The debug-compile path (handleDebugger) passed raw project data with unresolved alias-name locations to compileForDebug, so the generated _config.st emitted `AT <alias>` (e.g. `start_pb AT in_0`) which STruC++ rejects with `Expected DirectAddress, found identifier`. The build/upload paths already resolve aliases → addresses via getCompileReadyProjectData; wire the same pre-compile snapshot into the debug-compile call. Regression from the single-field variable-location refactor (Autonomy-Logic#915 / Autonomy-Logic#575): resolution was added to build/upload but the debug path was missed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ompile-resolve-aliases fix(debug): resolve alias-bound locations before debug compilation
… blank) bound vars when an alias is cleared Two variable-location warning bugs: 1. The location cell rendered the warning glyph only in its display branch, so selecting the row (which swaps to the editable combobox) hid the glyph while the conflict/orphan still stood. Render the glyph in both the selected and display branches, in the program and global variables tables. 2. Clearing an alias at its producer cascaded the empty string onto bound variables via renameAlias, silently wiping their location. Now an empty rename is treated as a deletion: bound variables keep the (now-missing) alias name and surface as orphaned (amber warning), matching the behavior of deleting the whole producer/device. Also commit the VPP io-table / module-slots alias inputs on blur (local state) instead of per keystroke, so a single rename reaches the store rather than one per intermediate string while editing. Bump editor to 4.2.8. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n-warning-and-alias-clear fix(alias): persist location warning on select; orphan bound vars on alias clear (4.2.8)
Byte-identical mirror of the shared frontend / middleware changes that land in openplc-web (PR fix/ai-autocomplete-empty-completions), kept in sync by the mirror gate: - monaco editor: `editContext: false` (Safari Tab-accept fix) and `inlineSuggest.experimental.showOnSuggestConflict: 'always'` so AI ghost text renders alongside the STruC++ LSP suggest widget. - ai-port: new `completion_empty` telemetry event name. The empty-completion behavioural fixes themselves live in the web-only AI adapter (context builder + inline completion provider) and the autonomy-edge backend, so no desktop release is required — this only keeps the shared surface identical. Also documents the app-version bump procedure in CLAUDE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…complete-empty-completions fix(ai): mirror shared-surface AI autocomplete fixes from openplc-web
Byte-identical mirror of the openplc-web APP_VERSION bump so the shared surface stays in sync. Desktop's displayed version is tag-driven via CD, so this only aligns the shared constant — no tag, no desktop release. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ersion-4.2.8 chore(version): bump APP_VERSION to 4.2.8 (mirror)
Byte-identical mirror of the shared monaco/index.tsx change in openplc-web (fix/ai-completion-idle-retrigger): after 2s idle with AI on and no ghost visible, re-trigger inline suggest so completions don't "give up" after a late/dropped result. The debounce + token-guard halves of that fix live in the web-only AI adapter, so no desktop behavior beyond this shared editor wiring — no version bump, no tag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…letion-idle-retrigger fix(ai): mirror inline-completion idle re-trigger from openplc-web
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.