Repository navigation
fix(drivers): drivers follow the Core release - #1422
Conversation
On the home box a managed easee_cloud 1.3.2 from the beta channel kept running over the bundled 1.3.3 in v0.136.4-beta.1. The resolver picks a driver by which file exists, local then managed then bundled, never by version, and Settings called the old copy up to date. Drivers now ship with the release. At the first start of a new release, Core retires each managed install whose bundled copy at the same path is at least as new, so the release's driver runs. Within one release an override stays, including an older version chosen on purpose. The release that last reconciled is kept in the config key-value store; a box without it reconciles on its first start. The comparison goes by path, the key the resolver uses, so it is safe for drivers whose source and channel ids or versions are spelled differently: a lower bundled version keeps the override. The driver catalog now names the configured drivers that run each file, and `ftw status` lists each driver's version and names any override. AGENTS.md, the architecture, self-update and device repository docs and ADR 0007 describe the model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9cf17ab313
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ) | ||
| // Drivers ship with the release. A managed install that this release's | ||
| // bundled copy has caught up with ends here, before any path resolves. | ||
| driverRepository.RetireSupersededByBundled(resolveDriverDir()) |
There was a problem hiding this comment.
Retire overrides only after trial readiness
During a native update trial, this runs long before trial.complete marks the candidate ready. If the candidate later fails readiness and the launcher falls back, the shared managed-driver symlink and state have already been deactivated, so the previous Core returns with its older bundled driver rather than the previously working override. Defer this destructive reconciliation until the trial is committed, or restore the activation during fallback.
AGENTS.md reference: AGENTS.md:L172-L176
Useful? React with 👍 / 👎.
| s.deps.CfgMu.RLock() | ||
| for _, d := range s.deps.Cfg.Drivers { | ||
| if !d.Disabled && d.Lua != "" { | ||
| usedBy[filepath.Base(d.Lua)] = append(usedBy[filepath.Base(d.Lua)], d.Name) |
There was a problem hiding this comment.
Match running drivers by resolved path
For a supported configuration that points lua at an absolute custom file outside the user, managed, and bundled catalog directories, matching only the basename either attaches used_by to an unrelated bundled entry with the same filename or omits the driver entirely. Consequently ftw status can report the wrong version/source or fail to identify a local override; classify the resolved path against the source directories and parse unmatched configured files directly.
AGENTS.md reference: AGENTS.md:L17-L19
Useful? React with 👍 / 👎.
| if err := m.store.SaveConfig(releaseMarkerKey, m.hostVersion); err != nil { | ||
| slog.Warn("driver repository: record reconciled release", "err", err) |
There was a problem hiding this comment.
Retry retirement failures before recording the release
The release marker is persisted even when one or more calls to Deactivate failed. On every later boot of the same release, the marker causes an immediate return, so a filesystem failure leaves the managed driver shadowing the bundled copy indefinitely; after a transient database failure, reconcileActive can even recreate the removed symlink on restart and the marker prevents removing it again. Record the marker only when every eligible retirement succeeds.
Useful? React with 👍 / 👎.
| permissions. A driver the catalog marks `control: true` keeps its control path | ||
| and runs under the same terms as the copy bundled with Core, which is the same | ||
| source. Every other entry gets only the read permissions it needs: |
There was a problem hiding this comment.
Describe control drivers' allowed write phases accurately
Including control: true entries in this section makes the following blanket statement that FTW denies writes during command and default mode false. The runtime deliberately permits granted modbus.write operations during those two leased phases while denying writes during init, poll, and cleanup, so the trust documentation now contradicts the enforced control-v2 policy and should distinguish control packages from read-only entries.
Useful? React with 👍 / 👎.
…gs (#1426) Follow-up to #1422 after the owner set the rule: an owner picks which signed driver version runs, and nothing changes without that choice. - A version installed although the release bundles a newer one is a choice and stays across Core updates until the owner changes it. A newer version installed early still ends when a release catches up. The choice is kept per logical path in the config key-value store and forgotten when the owner goes back to the release's copy. - The bundled sources spell ids their own way (easee-cloud for the channel's easee_cloud). "Use bundled" answered "no bundled file declares driver easee_cloud" on the home box, and a first install did not reach the running instance until Core restarted. Both now accept the file the signed manifest names when its id passes the publisher's identity rule (device-drivers _identifies_same_driver); an equal id still counts anywhere, and a different id at that path does not. - The driver catalog reports release_version beside an override, and `ftw status` prints it. - The built-in beta channel honours device_repository.enabled: false. - Update Center counted any beta whose version differed as an update, an older one included; it now compares SemVer. - The note in drivers/BUNDLED_SOURCE.json no longer claims every channel artifact is write-inert. Claude-Session: https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Independent review of #1422 and #1426: - RetireSupersededByBundled deactivated the managed selection when a release bundled a version at least as new. A failed update trial that fell back, or `ftw rollback`, then ran the older release's older copy and lost the version the owner had installed early. - A choice counted only when it was older than the bundled copy at that moment, so going back from an early 1.3.3 to 1.3.2 under a release with 1.3.0 was not kept, and the next release ran the rejected 1.3.3. The selection is no longer changed at start. driver-repository/active records what the owner selected; driver-repository/effective, which paths now resolve through (config, catalog, API, driver inventory), is derived from it and the release's drivers at every start and after every change. A selection runs while it is at least as new as the release's copy, or when the owner went back to it from a newer version that was running. A rollback of an activation restores the choice it replaced, so recovering from a failed install leaves no mark. The release marker is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7
…runs Codex review of #1422 found two defects: - RetireSupersededByBundled recorded the release as reconciled even when a Deactivate failed, so a retirement blocked once (for example by an unwritable active directory) was never tried again on that release. The release marker is now saved only when every retirement succeeded. - The catalog's used_by matched configured drivers by file name. An operator's own file elsewhere with the same name was credited to the catalog entry, and `ftw status` reported a version that was not running. It now compares the configured resolved path with the entry's file in its source directory. Both new tests fail on the previous code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7
Independent review of #1422 and #1426: - RetireSupersededByBundled deactivated the managed selection when a release bundled a version at least as new. A failed update trial that fell back, or `ftw rollback`, then ran the older release's older copy and lost the version the owner had installed early. - A choice counted only when it was older than the bundled copy at that moment, so going back from an early 1.3.3 to 1.3.2 under a release with 1.3.0 was not kept, and the next release ran the rejected 1.3.3. The selection is no longer changed at start. driver-repository/active records what the owner selected; driver-repository/effective, which paths now resolve through (config, catalog, API, driver inventory), is derived from it and the release's drivers at every start and after every change. A selection runs while it is at least as new as the release's copy, or when the owner went back to it from a newer version that was running. A rollback of an activation restores the choice it replaced, so recovering from a failed install leaves no mark. The release marker is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7
* fix(drivers): retry failed retirements and credit only the file that runs Codex review of #1422 found two defects: - RetireSupersededByBundled recorded the release as reconciled even when a Deactivate failed, so a retirement blocked once (for example by an unwritable active directory) was never tried again on that release. The release marker is now saved only when every retirement succeeded. - The catalog's used_by matched configured drivers by file name. An operator's own file elsewhere with the same name was credited to the catalog entry, and `ftw status` reported a version that was not running. It now compares the configured resolved path with the entry's file in its source directory. Both new tests fail on the previous code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7 * fix(drivers): never lose a driver selection to a Core update Independent review of #1422 and #1426: - RetireSupersededByBundled deactivated the managed selection when a release bundled a version at least as new. A failed update trial that fell back, or `ftw rollback`, then ran the older release's older copy and lost the version the owner had installed early. - A choice counted only when it was older than the bundled copy at that moment, so going back from an early 1.3.3 to 1.3.2 under a release with 1.3.0 was not kept, and the next release ran the rejected 1.3.3. The selection is no longer changed at start. driver-repository/active records what the owner selected; driver-repository/effective, which paths now resolve through (config, catalog, API, driver inventory), is derived from it and the release's drivers at every start and after every change. A selection runs while it is at least as new as the release's copy, or when the owner went back to it from a newer version that was running. A rollback of an activation restores the choice it replaced, so recovering from a failed install leaves no mark. The release marker is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…1434) When a release's newer copy overtakes a kept driver selection, the selection stays but does not run. Three places still assumed it ran: - A failed trial restarted the kept selection's file in the effective directory, which no longer exists, so the device stayed stopped. The restart now falls back to the release's copy when no managed file runs at the path. - A failed switch recovered with ActivateInstalled, which pinned the kept older version and ran it over the release's copy. It now uses Rollback, which restores the selection and the choice together. - The Versions list marked the kept selection as selected and hid its "Use this". /versions now names it as superseded_version, and the list offers it like any version on disk. Found by the Codex review of #1422..#1433. Claude-Session: https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Why
On the home box, a managed
easee_cloud1.3.2 from the beta channel kept running over the bundled 1.3.3 in v0.136.4-beta.1. The resolver picks a driver by which file exists (local, then managed, then bundled), never by version, and Settings called the old copy up to date. The owner then decided the model: drivers reach users with the Core release. There is one version, andftw updateandftw rollbackcover the drivers. A driver fix is a pin bump in the next beta. The signed channel serves installs that cannot take a new Core (1.x–3.x) and expert overrides. An override ends when a release catches up.What changes
driverrepo.RetireSupersededByBundled, called before driver paths resolve). At the first start of a new release, Core deactivates each managed install whose bundled copy at the same path is at least as new.configkey-value store (driver_repository/bundled_release). No schema change. A box without that key reconciles on its first start.easee-cloud/easee_cloud, sdm630 1.1.1/2.0.1), a lower bundled version keeps the override, which is the safe direction.ftw statusshows drivers. The driver catalog gainsused_by, the enabled configured drivers that run each file.ftw statusprintsDrivers: easee 1.3.3, sungrow 1.5.8and anOverride:line for a channel install or a local file. A Core withoutused_byleaves the section out.drivers-stablefollowing stable Core is marked proposed. The device repository doc no longer claims the channel is read-only (control: trueentries keep their control path), and it drops the outdated YAML repoint for beta in favour of the built-in beta channel.AGENTS.md and architecture.md are also touched by open #1364 and #1292, in other hunks (#1364 around line 271; #1292 around lines 91 and 283 and in architecture.md). This PR changes only the Releases paragraph and the versioning paragraph.
Evidence
go/internal/driverrepo/bundled_supersedes_test.gocovers:TestDriverCatalogNamesTheDriversThatRunEachFilechecksused_bybefore and after an install and for a disabled driver.make test: 68 packages ok.go vet ./...is clean, andftw,ftw-cliandftw-launcherbuild.Follow-up steps and bugs found along the way are in a separate issue.
🤖 Generated with Claude Code
https://claude.ai/code/session_01MuerPFZFG88kgu8sWVHeq7