Fix editability of some data-lake variables - #2776
rafaellehmkuhl merged 6 commits into
Conversation
Automated PR Review (Claude)0. SummaryVerdict: MINOR SUGGESTIONS Minor items to consider: 1.1, 1.2, 6.1. This PR fixes editability of certain data-lake variables (camera zoom/focus speed). It introduces a "value-only edit mode" in 1. Correctness & Implementation Bugs1.1 if (initialValue !== undefined) {
dataLakeVariableTimestamps[variable.id] = performance.now()
}
...
if (variable.persistValue && initialValue !== undefined) {
savePersistentValues()
}When a persisted value exists and 1.2 2. AGENTS.md Adherence — ✅3. Security — ✅4. Performance — ✅5. UI / UX — ✅6. Code Quality & Style6.1 7. Commit Hygiene — ✅8. Tests — ✅9. Documentation — ✅10. Nitpicks / Optional — ✅Generated by Claude. This is advisory; a human reviewer must still approve. |
adef72e to
e1b8787
Compare
Review follow-up — round 1Done
Done differently
Won't change (with reasoning)
|
|
/review |
Automated PR Re-review 2 (Claude)Comparing Previous findings status
Discussion since last review@rafaellehmkuhl posted a review follow-up detailing how each finding was handled:
New findings0. SummaryVerdict: MINOR SUGGESTIONS Minor items to consider: 1.1, 6.1. This PR fixes editability of certain data-lake variables (camera zoom/focus speed). It introduces a 1. Correctness & Implementation Bugs1.1 2. AGENTS.md Adherence — ✅3. Security — ✅4. Performance — ✅5. UI / UX — ✅6. Code Quality & Style6.1 7. Commit Hygiene — ✅8. Tests — ✅9. Documentation — ✅10. Nitpicks / Optional — ✅Generated by Claude. This is advisory; a human reviewer must still approve. |
Review follow-up — round 2All round 1 findings (1.1, 1.2, 6.1) were confirmed addressed in the re-review. Two new minor items this round — declining both, reasoning below. Won't change (with reasoning)
|
e1b8787 to
7549af0
Compare
Review follow-up — round 3Done
Won't change (with reasoning)
|
|
/review |
|
| # | Problem | What it means | Severity | Status |
|---|---|---|---|---|
| 1.2 | Camera zoom/focus buttons vanish from the joystick setup | You can no longer assign camera zoom-in, zoom-out, focus-in or focus-out to a joystick button or axis, because those entries disappear from the mapping list. | major | ❌ |
| 1.3 | Focus speed still forgotten on every restart | The camera focus speed you set goes back to the default every time Cockpit starts, so the PR only fixes half of what it set out to fix. | major | ❌ |
| 1.4 | Type check very likely fails | The project's type checker rejects the new lookup, so the build step that runs on every push would fail and the PR could not be merged as-is. | major | ❌ |
| 1.5 | Variables made from custom widgets can no longer be deleted | A variable you created from a custom widget input is now labelled "Cockpit internal" and its delete button is gone, so it stays in the list forever. | major | ❌ |
| 1.1 | Old hand-named variables lose their buttons | Someone who created a variable before this change and typed their own name for it loses the ability to edit or delete it. | minor | 💬 |
| 3.1 | Helper accepts an input nothing passes it | Extra unused flexibility in a new helper that someone has to read and maintain for no benefit today. | minor | ❌ |
| 6.1 | Defensive cleanup nothing needs | A few lines guard against variable names that the app never produces. | nit | 💬 |
Since round 2 — 0 closed, 2 disputed, 5 new findings, comparing e1b8787 → 7549af0
Range. e1b8787d06e61b0354f3bce50782e7bcab25d6c8 → 7549af0957c787cda6c2a8360dfaab8194240b29.
The incremental diff is not usable this round. It lists roughly 240 files — CI workflows, the review guidelines, the base-station panel, the map composables, yarn.lock — none of which this PR touches. That is the rebase: the base moved to current master and everything master gained came through the comparison. All status judgements below, and every new finding, come from pr.diff (base…head), which is still the six files in pr.json.
The PR's own code did not change. @rafaellehmkuhl's round 3 follow-up says "no code was changed by the resolution — only the base moved". Checked, not taken: the code behind both carried findings is present verbatim in pr.diff (isUserDefinedDataLakeVariable with the info?.userDefined === true || normalizedId.startsWith(...) test, and normalizeDataLakeVariableId), and the six changed files and their line counts match the previous round.
Ledger rebuilt. previous-ledger.json arrived empty, so the ledger was reconstructed from previous-review.md. That review reused the ids 1.1, 1.2 and 6.1 for both its round 1 (closed) and round 2 (new) findings. To keep ids unique going forward, the three closed round 1 entries are carried as r1-1.1, r1-1.2 and r1-6.1; the open ones keep the bare 1.1 and 6.1 the author has been answering. Note that 6.1 was raised under the previous round's section numbering — under the current guidelines it belongs to 7. Code Quality & Style, which is where its body is reprinted below. Ids are never renumbered.
Status changes this round
- 1.1 — 💬 Disputed (was open). @rafaellehmkuhl calls it "real but unfixable cleanly": going forward menu-created variables are stamped, and for old data there is no way to tell a menu variable with a custom id from an input-element variable, so a retroactive stamp would wrongly hand input-element variables delete buttons. Verified against the code that his premise holds — old stored entries carry
persistent: trueand nothing else that separates the two — but an argument is not a code change, so the finding stays open and carries forward until the code moves or a maintainer settles it. - 6.1 — 💬 Disputed (was open). @rafaellehmkuhl says the leading-space/slash normalization is deliberate and was explicitly requested, so ids like
user/custom/xstill resolve. The "explicitly requested" part is a claim about a conversation that is not in this repository and could not be checked; the code claim is accurate as far as it goes, and the finding was only ever a nit. Same rule applies: it stays open. - No finding was closed this round, and no finding became obsolete.
Resolutions and decisions. resolutions.json is [] and decisions.json is [] — no /resolve has been issued on this PR and no dispute has been put to a vote, so nothing was closed by a maintainer and there is no unknown id to report back.
Discussion since round 2. Three comments, all from @rafaellehmkuhl: the round 2 follow-up declining both findings, the round 3 follow-up reporting the rebase and restating those declines, and the bare /review that triggered this run. Note that the round 2 follow-up's reasoning on 1.1 turns on input-element variables not being user-defined; new finding 1.5 is about a different consequence of that same decision (those variables losing their delete button and their "User defined" label), and is not answered by it.
Injection check. Nothing in the PR body, the diff, the commit messages or the comments contained text addressed to the reviewer.
Change map — what was established before judging
Claims. The PR body is only Fix #2774 / Fix #2775; the issue text is not reachable from here, so the claims below are taken from the commit messages in pr.json.
| Claim | Verdict |
|---|---|
"Internal variables with persistValue were always initialized from their default, overwriting values saved in localStorage." |
Verified — src/libs/actions/data-lake.ts:80 assigns initialValue unconditionally, and loadPersistentVariables() (:34-42) only restores values for variables whose info was persisted, which a persistValue-without-persistent variable never is. |
| "Only zoom/focus speed settings should be configurable by the user; increase/decrease variables remain internal-only." | Contradicted as a safe change — allowUserToChangeValue is not only the Data Lake page's edit flag; it is also what the joystick mapping pickers filter on (src/views/ConfigurationJoystickView.vue:847 and :857). See 1.2. |
| "The Data Lake table hid the edit button for internal variables even when they were marked as user-editable." | Verified — src/views/ToolsDataLakeView.vue:117 gated the pencil on isUserDefinedVariable alone. |
| "Editing internal variables should only change the value, not metadata." | Verified — the value-only branch in saveVariable calls setDataLakeVariableData and never updateDataLakeVariableInfo. |
"Internal variables explicitly marked persistent: false were shown as user-defined." |
Verified — src/libs/vehicle/mavlink/vehicle.ts:1513 and src/stores/mainVehicle.ts:872 create telemetry variables with persistent: false, and the old test persistent != null (src/views/ToolsDataLakeView.vue:437) matched them. |
Failure site.
- For the persistence symptom, the misbehaving code is
createDataLakeVariable(src/libs/actions/data-lake.ts:75-96) — in the diff. But the second half of that failure lives insavePersistentValues(src/libs/actions/data-lake.ts:53-65), which is not in the diff and which the fix depends on. See 1.3. - For the editability symptom, the misbehaving code is
isUserDefinedVariable(src/views/ToolsDataLakeView.vue:437) and the pencil'sv-if(:117) — both in the diff. - No file is added by this PR, and it changes nothing under
scripts/,.github/orsrc/electron/.
Entry points.
| Function | Reached from | Frequency |
|---|---|---|
createDataLakeVariable (src/libs/actions/data-lake.ts:75) |
src/main.ts:96 → setupPredefinedLakeAndActionResources → setupMavlinkCameraResources; plus ~20 registration sites (vehicle.ts:1513, omniscientLogger.ts, gnss.ts:365) and two user paths (DataLakeVariableDialog.saveVariable, InputElementConfig.saveOrUpdateParameter) |
one-shot at bootstrap, then per user action |
setupMavlinkCameraResources (src/libs/joystick/protocols/predefined-resources.ts:43) |
src/main.ts:96 |
one-shot |
isUserDefinedDataLakeVariable (src/libs/utils-data-lake.ts) |
ToolsDataLakeView row v-if (:117, :125) and getVariableSource inside the filteredVariables computed; DataLakeVariableDialog.valueOnlyEditMode |
per render of the Data Lake table, i.e. per user action |
isUserEditableVariable (src/views/ToolsDataLakeView.vue:438) |
same row v-if and editVariable |
per render of the Data Lake table |
editVariable (src/views/ToolsDataLakeView.vue:386) |
pencil button click | per user action |
valueOnlyEditMode, isValid, saveVariable (src/components/DataLakeVariableDialog.vue) |
dialog open / Save click | per user action |
Nothing here lands on a hot path: no changed function is reachable from mavlink:onIncomingMessage, dataLake:setVariable or a high-frequency watcher, and no changed function is unreachable either.
Invariants.
persistValueimpliespersistent. Enforced in the dialog (:81disables the checkbox,:194-201clears it) and assumed byloadPersistentVariables, which only restores values for variables whose info was persisted. The two new speed variables (predefined-resources.ts:45-49) deliberately break it, and the PR compensates insidecreateDataLakeVariable. Violating sites: everycreateDataLakeVariablecaller; the PR covers the two it adds.cockpit-persistent-data-lake-valuesholds a value for everypersistValuevariable.savePersistentValuesrebuilds that object from scratch out of the variables registered at that moment (:53-65), so any call made before allpersistValuevariables exist drops the others. The PR adds exactly such a call. → 1.3.allowUserToChangeValuemeans "the user may set this variable". Consumed by the joystick button picker (ConfigurationJoystickView.vue:847), the joystick axis picker (:857) and five custom-widget elements (Dial.vue:197,Slider.vue:121,Switch.vue:102,Checkbox.vue:110,Dropdown.vue:132), not only by the Data Lake table. The PR repurposes it as "the Data Lake table may edit this" and clears it on four variables. → 1.2.- user-defined = created through the Data Lake menu, recognised by a
userDefinedstamp or theuser/custom/id prefix. The only other producer of user-created variables isInputElementConfig.vue:516, which uses theuser/inputs/prefix and is not covered → 1.5; pre-PR menu variables with a hand-typed id are not covered either → 1.1.
1. Correctness & Implementation Bugs — 5 findings
1.2 major — Clearing allowUserToChangeValue removes camera zoom and focus from the joystick mapping pickers. src/libs/joystick/protocols/predefined-resources.ts:44 drops allowUserToChangeValue: true from commonVariableConfig, which is spread into camera-zoom-decrease, camera-zoom-increase, camera-focus-decrease and camera-focus-increase (:46-52). That flag is not private to the Data Lake page. Every data-lake variable becomes a joystick action (src/libs/joystick/protocols/data-lake.ts:37-44), and the joystick configuration screen filters the offered actions on exactly this flag:
src/views/ConfigurationJoystickView.vue:844-848—return dataLakeVariableInfo.allowUserToChangeValue && dataLakeVariableInfo.type !== 'string'for button actions;src/views/ConfigurationJoystickView.vue:853-859— the same test for axis actions.
Those four variables exist for joystick binding: the transforming functions immediately below them compute ({{camera-zoom-increase}} - {{camera-zoom-decrease}}) * {{camera-zoom-speed}} (:64, :83). After this PR they no longer appear in the button or axis mapping lists, so a user cannot assign camera zoom or focus to a joystick at all. Existing mappings keep working — the runtime handler in data-lake.ts:63-72 does not consult the flag — so this fails silently for new setups and for anyone re-mapping a controller.
Fix: restore allowUserToChangeValue: true on the four increase/decrease variables, and gate the Data Lake table's new pencil on something that means what the PR wants it to mean. The table already has isUserEditableVariable; point it at a dedicated property (or at persistValue, which is what actually distinguishes the two speed settings) instead of overloading the flag three other features read.
1.3 major — The saved camera-focus-speed value is erased while camera-zoom-speed is being created, so focus speed still resets on every restart. The new restore block in createDataLakeVariable reads the whole persisted map and then, a few lines down, the pre-existing if (variable.persistValue && valueToSet !== undefined) savePersistentValues() fires. savePersistentValues (src/libs/actions/data-lake.ts:53-65, unchanged by this PR) does not merge — it rebuilds the stored object from dataLakeVariableInfo, which at that instant holds only the variables registered so far.
Walk it through with both speeds saved as, say, zoom 7 and focus 5 (setupMavlinkCameraResources, predefined-resources.ts:45-53, runs one-shot from src/main.ts:96):
camera-zoom-speedis created: its value is correctly restored to 7, thensavePersistentValues()writes{ camera-zoom-speed: 7 }— the storedcamera-focus-speed: 5is gone, because that variable is not registered yet.camera-focus-speedis created three lines later, reads the map that was just overwritten, finds nothing, and falls back to the default3.
User-defined variables survive this because they are persistent: true and so are already in dataLakeVariableInfo before any of this runs (loadPersistentVariables, :24-43); the new speed variables are persistValue without persistent, which is what puts them on the wrong side of the ordering. Net effect: zoom speed persists, focus speed silently reverts to 3 on every boot — half of what commit 1 sets out to fix, and the half that is broken is invisible until the user notices the focus behaving differently from the zoom.
Fix it at the chokepoint rather than at the two call sites: have savePersistentValues read the existing stored object and merge into it instead of rebuilding, and pair that with an explicit delete of the id in deleteDataLakeVariable (which currently relies on the rebuild to drop removed variables). Skipping the save when the value came from storage is a smaller change but only hides the ordering problem — the next persistValue variable registered after another one will hit it again.
1.4 major — The persisted-value lookup will not pass yarn typecheck. In the block added to createDataLakeVariable:
const savedValues = settingsManager.getKeyValue(persistentValuesKey)
if (savedValues && typeof savedValues === 'object' && savedValues[variable.id] !== undefined) {getKeyValue is <T extends SettingValue>(key) => T | undefined (src/libs/settings-management.ts:279) and there is no inference site, so T falls back to its constraint string | number | boolean | object | null | undefined (:37). The truthiness test plus typeof … === 'object' narrows that to bare object, which has no index signature — so savedValues[variable.id] is TS7053 ("expression of type 'string' can't be used to index type 'object'") under the strict: true of tsconfig.app.json:35. yarn typecheck runs in CI at .github/workflows/ci.yml:141, and AGENTS.md requires the final implementation to be clean, so this fails the run rather than reaching a user. Note that the existing reader two functions above avoids it by going through Object.entries (:36) rather than indexing.
Fix — this is also shorter and is the optional-chaining form AGENTS.md asks for ("Use optional chaining (?.) when possible in typescript"), replacing the three-part guard and the cast at once:
const savedValues = settingsManager.getKeyValue<Record<string, string | number | boolean>>(persistentValuesKey)
const savedValue = savedValues?.[variable.id]
if (savedValue !== undefined) valueToSet = savedValue1.5 major — Variables created from custom-widget input elements lose their delete button and are relabelled "Cockpit internal". isUserDefinedVariable (src/views/ToolsDataLakeView.vue:436-438) changes from persistent != null to isUserDefinedDataLakeVariable(id), which matches only the userDefined stamp or the user/custom/ prefix. InputElementConfig.vue:512-523 creates its variables with persistent: true, allowUserToChangeValue: true and the user/inputs/ prefix, and never stamps userDefined. For those variables, after this PR:
- the delete button disappears — its
v-ifisisCompoundVariable(item.id) || isUserDefinedVariable(item.id)(:125), untouched by the diff, anddeleteVariable(:413-426) would refuse them anyway; - the Source column reads "Cockpit internal" instead of "User defined" (
getVariableSource,:321-331); - editing narrows to value-only, since
allowUserToChangeValueis true butisUserDefinedDataLakeVariableis false.
The user created these. The only other way to remove one is the delete action inside the input element's own configuration panel (InputElementConfig.vue:495-509), which is unreachable once the widget holding that element is gone — so the variable becomes permanently undeletable and keeps a row on the Data Lake page labelled as if Cockpit had made it. @rafaellehmkuhl's round 2 follow-up reasons about not stamping these variables, which is a different question; the removal of an affordance they already had is not covered by it and is not mentioned anywhere in the PR.
Fix: decide it explicitly rather than by side effect. Either recognise user/inputs/ as user-created for the delete and Source purposes while keeping full edit for user/custom/ — a second exported prefix beside userDefinedDataLakeVariableIdPrefix, checked in ToolsDataLakeView — or state in the PR that these variables are deliberately no longer deletable from this page and say where the user is meant to delete them instead.
1.1 minor (carried from round 2 — disputed) — A menu-created variable from before this PR whose id was typed by hand stops being recognised as user-defined. isUserDefinedDataLakeVariable (src/libs/utils-data-lake.ts) recognises a variable by info?.userDefined === true or by the user/custom/ id prefix. Variables created through the Data Lake dialog before this PR carry neither if the user turned on manual id editing (DataLakeVariableDialog.vue:173-177, :182-189) and typed an id of their own — the userDefined stamp is new in this PR, and the auto-generated prefix is the only other marker. In the Data Lake table (ToolsDataLakeView.vue:117, :125) such a variable loses both its edit and its delete button, and its Source flips to "Cockpit internal". Variables that kept the generated id are unaffected, which is the normal flow.
Author's position (see the since-last-round block): old stored data cannot distinguish a menu variable with a custom id from an input-element variable, so a retroactive stamp would wrongly grant delete buttons to the latter; the affected variables keep value editing through allowUserToChangeValue. That reasoning checks out against the code, but no code changed, so the finding stays open for a maintainer to settle. If it is accepted as-is, the cheapest mitigation is to stamp userDefined: true on the next full edit of such a variable — which saveVariable already does — and say so in the PR, so the recovery path is at least documented.
2. Persistence & User Data — inventory, no findings of its own
Two persisted keys are touched, both through settingsManager (src/libs/settings-management.ts), which keeps the local copy and syncs the key to the connected vehicle's BlueOS storage — so anything wrong here reaches every topside computer that talks to that vehicle, not just the one that made the change.
| Key | Backend | What happened |
|---|---|---|
cockpit-persistent-data-lake-values |
settings-management.ts (local + vehicle-synced) |
Added entries. camera-zoom-speed and camera-focus-speed become persistValue variables (predefined-resources.ts:45-49), so their values now live in this key. It is also now read during variable creation (data-lake.ts, createDataLakeVariable) rather than only at module load. The rebuild-not-merge behaviour of savePersistentValues is what finding 1.3 is about. |
cockpit-persistent-data-lake-variables |
settings-management.ts (local + vehicle-synced) |
Reshaped, additively. DataLakeVariable gains an optional userDefined flag (src/types/data-lake.ts:38-41), written by saveVariable for variables created or fully edited through the dialog. Entries stored before this PR simply lack it. |
Judgement on each: both keys are cockpit-prefixed and already existed; neither holds a machine-specific value (a zoom speed is a preference, not a device path), so vehicle syncing is the right backend. The userDefined addition is optional and read with a prefix fallback, so no automatic migration was written — which is the right call under the "migrations are a last resort" rule, and worth noting as a point in the PR's favour. The stored shape does not duplicate its own key. The one place where already-configured users are stranded on the old shape is finding 1.1 (disputed) and its user/inputs/ counterpart 1.5, both written up in section 1.
3. AGENTS.md Adherence — 1 finding
3.1 minor — isUserDefinedDataLakeVariable accepts a DataLakeVariable that no caller passes. In src/libs/utils-data-lake.ts the new helper is typed (variableOrId: string | DataLakeVariable) and spends three lines resolving that union:
const id = typeof variableOrId === 'string' ? variableOrId : variableOrId.id
const info = typeof variableOrId === 'string' ? getDataLakeVariableInfo(id) : variableOrIdBoth call sites in this PR pass a string — ToolsDataLakeView.vue:437 and DataLakeVariableDialog.vue (valueOnlyEditMode) — and the object branch, along with the DataLakeVariable type import it forces, has no call site anywhere. AGENTS.md's minimalism ladder and the "no groundwork for future PRs" rule both point the same way: take a string, and let the PR that needs the object form add it next to the usage that justifies it. That drops the union, both ternaries and the import, for a helper that is then three lines long.
7. Code Quality & Style — 1 finding
6.1 nit (carried from round 2 — disputed; the id predates the current section numbering) — normalizeDataLakeVariableId guards against ids the app cannot produce. In src/libs/utils-data-lake.ts, const normalizeDataLakeVariableId = (id: string): string => id.trimStart().replace(/^\/+/, '') strips leading whitespace and slashes before the prefix test. Every id that reaches it comes from 'user/custom/' + machinizeString(name) (DataLakeVariableDialog.vue:186), from 'user/inputs/' + machinizeString(name) (InputElementConfig.vue:516), or from a hard-coded literal — none of which can carry a leading space or slash. A hand-typed id could, but that same path is the one finding 1.1 says is not recognised anyway.
Author's position: the normalization is deliberate and was explicitly requested, so user/custom/x and //user/custom/x still resolve. That request is not visible in this repository and could not be verified; the finding is a nit either way, and it is recorded here only so the ledger stays complete.
Sections with nothing to report (7)
4. Security — ✅ (no new dependency, no network call, no eval/v-html, no encoded blob, no env or secret use; the one added regex /^\/+/ is linear; the PR touches no workflow, build script or Electron main-process file — the CI and workflow changes visible in the incremental diff came from master via the rebase, not from this author)
5. Performance — ✅ (all seven changed functions traced to bootstrap or user-action entry points, none to onIncomingMessage, setVariable or a high-frequency watcher; the per-row work swaps a linear find for a hash lookup in isUserDefinedVariable and adds one back in isUserEditableVariable, so the table render is a wash; no listener, timer or watcher added, so nothing needs teardown)
6. UI / UX — ✅ (the value-only mode reuses the existing dialog: title stays centred, the mx-10 divider above the footer is intact, no divider under the header, globalGlassMenuStyles still on the one surface; no overlay-teleporting control added, so no missing theme="dark"; the pencil is an existing v-btn icon and the edit action is logged past-tense through logUserAction at ToolsDataLakeView.vue:391; no new snackbar, no dialog that can reopen from a loop)
8. Commit Hygiene — ✅ (five commits read from pr.json, each one logical step — restore, flag, button, dialog mode, classification — none reverting another, none bundling unrelated work, all under ~40 lines, no wip/fixup! noise, and the Fix #2774/Fix #2775 references are in the PR body where they belong rather than in any commit message)
9. Tests — ✅ (no test file is touched and none is weakened; src/tests/ contains nothing covering data-lake.ts or the Data Lake view, so nothing was removed to make this pass)
10. Documentation — ✅ (nothing here differs between Lite and Standalone, so no README change is owed; the JSDoc on the new exported helper and on the new userDefined field is present, typed and non-empty, and the renamed editVariable's block was updated)
11. Nitpicks / Optional — ✅ (checked the added template for sentence-case labels and stacked insets; Edit Variable Value matches the Title Case of the Edit Variable/New Variable siblings it sits beside, so it is left alone)
Complexity: complexity-report.json was not present for this head, so the measurement was unavailable — which of CI timing, a failed measurement or no report at all caused that is not something this run can tell. No complexity findings are raised this round.
Generated by Claude. This is advisory; a human reviewer must still approve.
🙋 Decision needed — 1.1Pre-PR menu-created variables with a hand-typed id stop being recognised as user-defined The author's argument: Old stored data cannot distinguish a Data-Lake-menu variable with a custom id from an input-element variable, so stamping them retroactively would wrongly give input-element variables delete buttons; the affected case is narrow and those variables keep value editing through allowUserToChangeValue. How to vote on this disputeReact to this comment and the next
The two reactions already here were left by the bot so that either answer is one click, and neither of them counts. Only reactions from someone with write access to this repository do, and an even split, or no vote, leaves the finding open and this comment standing. Move your reaction to change your mind while the vote is open — once a |
🙋 Decision needed — 6.1normalizeDataLakeVariableId trims leading whitespace and slashes no known id carries The author's argument: The leading-space and leading-slash normalization before the prefix check is deliberate and was explicitly requested, so ids like ' user/custom/x' or '//user/custom/x' still resolve as user-defined. How to vote on this disputeReact to this comment and the next
The two reactions already here were left by the bot so that either answer is one click, and neither of them counts. Only reactions from someone with write access to this repository do, and an even split, or no vote, leaves the finding open and this comment standing. Move your reaction to change your mind while the vote is open — once a |
7549af0 to
875df00
Compare
Review follow-up — round 4Done
Done differently
One consequence worth flagging, since it reverses an answer from round 1: Net effect on the classification change: no variable loses an affordance it had before this PR. |
|
/review |
|
| # | Problem | What it means | Severity | Status |
|---|---|---|---|---|
| 1.6 | Variables you create without "persist between boots" become undeletable | A variable you make yourself, with the "keep between restarts" box unticked, is shown as one of Cockpit's own and loses its rename and delete buttons, so you cannot get rid of a row you just created. | major | ❌ |
| 1.7 | Value editor shows blank for a variable sitting at zero | Opening the new value editor on a variable whose value is 0 (or off) shows an empty box with Save greyed out, so it looks like the variable has no value and you must retype one. | minor | ❌ |
| 6.2 | Pencil offered on live joystick axis readings | The Data Lake page now offers to edit the joystick axis values, but anything you type there is wiped the instant you move the stick. | minor | ❌ |
Since round 3 — 7 closed (5 addressed, 2 no longer applicable), 3 new findings, comparing 7549af0 → 875df00
Range. 7549af0957c787cda6c2a8360dfaab8194240b29 → 875df000c718474107652a32a729a814189967a3.
The incremental diff is the whole PR again. incremental.diff lists the same six files as pr.json, with per-file line counts identical to the PR totals (+32/-10, +22/-7, +3/-2, +11/-0, +39/-0, +12/-7), and the five commits in pr.json all carry new oids with a fresh committedDate — i.e. the branch was rewritten, not appended to. So it tells us nothing about what moved since round 3 that pr.diff does not, and every status judgement below was made against pr.diff (base…head) and the base checkout.
Status changes this round
- 1.2 — ✅ Addressed.
predefined-resources.ts:44keepsallowUserToChangeValue: trueincommonVariableConfig; the newspeedVariableConfigonly addspersistValue: trueand only the two speed variables use it. All six camera variables therefore still pass theConfigurationJoystickView.vue:848/:858filters. - 1.3 — ✅ Addressed, at the chokepoint that was named.
savePersistentValuesnow seeds itself from the stored object (data-lake.ts,getPersistentValues) instead of rebuilding it, anddeleteDataLakeVariableremoves its own id explicitly rather than relying on the rebuild to prune. Walked the bootstrap order again with both speeds stored:camera-zoom-speed's save now writes{zoom, focus}rather than{zoom}, socamera-focus-speedstill finds its value three lines later. - 1.4 — ✅ Addressed. The code is now the typed-generic plus optional-chaining form that was proposed:
getKeyValue<Record<string, string | number | boolean>>(persistentValuesKey) ?? {}, which satisfies theT extends SettingValueconstraint (settings-management.ts:37includesobject) and indexes aRecord, not a bareobject. @rafaellehmkuhl disputes the finding's premise rather than its fix, reporting thatvue-tscexits 0 without checking anything on this repo; that is a claim about a run on his machine which cannot be checked from here, and it does not change the status — the recommended code landed. - 1.5 — ✅ Addressed.
isUserDefinedDataLakeVariableis nowgetDataLakeVariableInfo(id)?.persistent === true, andInputElementConfig.vue:521passespersistent: true, so custom-widget input variables keep their delete button and their "User defined" label. Checked everycreateDataLakeVariablecall site insrc/: only that one andDataLakeVariableDialog.vue:280can passpersistent: true; the internal registrations passfalse(vehicle.ts:1513,mainVehicle.ts:892) or leave it unset. - 3.1 — ✅ Addressed. The helper takes a
string, the union and both ternaries are gone, and so is theDataLakeVariableimport it forced. - 1.1 — ⚪ No longer applicable. The id-prefix classification the finding was about was deleted outright. Classification is now
persistent === true, and anything that survived a boot is incockpit-persistent-data-lake-variablesonly becausesavePersistentVariables(data-lake.ts:47) filtered onpersistentbeing truthy — so a pre-PR menu variable with a hand-typed id keeps both buttons. Its ballot (decision comment) ispendingwith no votes cast; the vote is technically still open but now moot, since the code closed the finding rather than the argument. - 6.1 — ⚪ No longer applicable.
normalizeDataLakeVariableIdno longer exists in the diff. Its ballot (decision comment) is alsopendingwith no votes, and moot for the same reason. - New this round: 1.6, 1.7 (section 1) and 6.2 (section 6), all from a full re-read of
pr.diff.
Resolutions and decisions. resolutions.json is [] — no /resolve has been issued on this PR, so nothing was closed by command and there is no unknown id to report back. decisions.json holds two entries, both gated: true and both pending with empty up/down; a pending verdict decides nothing, and both findings closed on the code regardless.
Discussion since round 3. Two comments, both from @rafaellehmkuhl: a round-4 follow-up walking through each finding, and the bare /review that triggered this run. The follow-up's factual claims were checked rather than taken — the allowUserToChangeValue restoration, the merge-and-explicit-delete rewrite, the persistent === true classifier and the enumeration of persistent: true call sites all hold up against the code. One claim does not: "no variable loses an affordance it had before this PR". The old test was persistent != null (ToolsDataLakeView.vue:437), which matched persistent: false as well as true, and the Data Lake dialog lets a user create or edit a variable with "Persist variable between boots" unticked — that is finding 1.6 below. The follow-up also flags, correctly, that valueOnlyEditMode is once again keyed on persistent, which is where round 1 started.
Injection check. Nothing in the PR body, the diff, the commit messages, the comments or the decision entries contained text addressed to the reviewer. The follow-up comment is written as HTML markup; it is summarised above rather than quoted verbatim.
Change map — what was established before judging
Claims. The PR body is only Fix #2774 / Fix #2775; the issues are not reachable from here, so the claims are read off the commit messages in pr.json, plus the one claim the round-4 follow-up adds.
| Claim | Verdict |
|---|---|
"Internal variables with persistValue were always initialized from their default, overwriting values saved in localStorage." |
Verified — base data-lake.ts:80 assigns initialValue unconditionally, and loadPersistentVariables (:34-42) restores values only for variables whose info was persisted, which a persistValue-without-persistent variable never is. |
| "The speed settings are the two camera variables a user tunes … the increase/decrease variables stay as they were." | Verified — predefined-resources.ts:44-53: commonVariableConfig is unchanged and persistValue: true is added only via speedVariableConfig. |
| "The Data Lake table hid the edit button for internal variables even when they were marked as user-editable." | Verified — base ToolsDataLakeView.vue:117 gated the pencil on isUserDefinedVariable alone. |
| "Editing internal variables should only change the value, not metadata." | Verified — the value-only branch of saveVariable calls setDataLakeVariableData and never updateDataLakeVariableInfo. |
"Internal variables explicitly marked persistent: false were shown as user-defined." |
Verified — vehicle.ts:1513 and mainVehicle.ts:892 create telemetry variables with persistent: false, and the old persistent != null test matched them. |
| (follow-up) "No variable loses an affordance it had before this PR." | Contradicted — persistent != null also matched persistent: false, which is what the Data Lake dialog writes when the user unticks its persist checkbox (DataLakeVariableDialog.vue:70-75, :141, :280). → 1.6 |
Failure site.
- Persistence symptom:
createDataLakeVariable(data-lake.ts:75-96) andsavePersistentValues(:53-65) — both now in the diff, which is what closed 1.3. - Editability symptom:
isUserDefinedVariable(ToolsDataLakeView.vue:436) and the pencil'sv-if(:117) — both in the diff. - One file is added,
src/tests/libs/actions/data-lake.test.ts, read in full: it mockssettings-managementwith a plain object, dynamically imports the module under test in each case, and asserts both the registration-order symptom and the delete path. It shares module state across its two cases (case 2 asserts on values case 1 wrote), but both also pass in isolation because the fixture seeds the same numbers.loadPersistentVariables()runs at import (data-lake.ts:265) and is harmless under the mock. Nothing underscripts/,.github/orsrc/electron/is touched.
Entry points.
| Function | Reached from | Frequency |
|---|---|---|
getPersistentValues (data-lake.ts, new) |
savePersistentValues, createDataLakeVariable, deleteDataLakeVariable |
one-shot at bootstrap, then per user action (and per joystick frame if a persistValue variable is bound to an axis — see section 5) |
savePersistentValues (data-lake.ts) |
createDataLakeVariable, updateDataLakeVariableInfo, setDataLakeVariableData |
as above |
createDataLakeVariable (data-lake.ts:75) |
src/main.ts:96 → setupPredefinedLakeAndActionResources; ~20 registration sites; two user paths (DataLakeVariableDialog.saveVariable, InputElementConfig.saveOrUpdateParameter) |
one-shot at bootstrap, then per user action |
deleteDataLakeVariable (data-lake.ts:140) |
ToolsDataLakeView.deleteVariable, InputElementConfig |
per user action |
setupMavlinkCameraResources (predefined-resources.ts:43) |
src/main.ts:96 |
one-shot |
isUserDefinedDataLakeVariable (utils-data-lake.ts) |
ToolsDataLakeView row v-ifs (:117, :125) and getVariableSource (:321-331); DataLakeVariableDialog.valueOnlyEditMode |
per render of the Data Lake table, i.e. per user action |
isUserEditableVariable (ToolsDataLakeView.vue, new) |
same row v-if and editVariable |
per render of the Data Lake table |
editVariable (ToolsDataLakeView.vue) |
pencil click | per user action |
valueOnlyEditMode, isValid, saveVariable (DataLakeVariableDialog.vue) |
dialog open / Save click | per user action |
No changed function is unreachable, and none sits on onIncomingMessage, addToDataLake or notifyListeners.
Invariants.
persistValueimpliespersistent. Enforced in the dialog (:81disables the checkbox,:194-201clears it) and assumed byloadPersistentVariables. The two speed variables deliberately break it and the PR compensates insidecreateDataLakeVariable. Violating sites: everycreateDataLakeVariablecaller; the PR covers the two it adds.cockpit-persistent-data-lake-valuesholds a value for everypersistValuevariable. Now maintained by merging, withdeleteDataLakeVariableas the only pruner. The one site that can still leave an orphan isupdateDataLakeVariableInfowhen a user turnspersistValueoff — it takes theif (variable?.persistValue)branch not at all, so the old entry is simply left behind. Harmless (loadPersistentVariablesfilters onpersistValue), and written up as prose in section 2 rather than as a finding.- user-defined ⇔
persistent === true. The new classifier. Producers of user-created variables:InputElementConfig.saveOrUpdateParameter(hard-codestrue, covered) andDataLakeVariableDialog.saveVariable(takes it from a user checkbox, so it can befalse— not covered → 1.6). Consumers: the pencil, the delete button and the Source column inToolsDataLakeView, plusvalueOnlyEditMode. No internal registration passespersistent: true, which was checked across allcreateDataLakeVariablecall sites insrc/. allowUserToChangeValuemeans "the user may set this variable". Restored on all six camera variables, and now also the Data Lake pencil's gate. Every holder of the flag therefore gains a pencil, which includes the six joystick axis inputs created atpredefined-resources.ts:158-162→ 6.2.
1. Correctness & Implementation Bugs — 2 findings
1.6 major — A variable created through the Data Lake dialog with "Persist variable between boots" unticked is classified as Cockpit-internal and loses its delete and full-edit buttons. isUserDefinedDataLakeVariable (src/libs/utils-data-lake.ts) is now getDataLakeVariableInfo(id)?.persistent === true, replacing the old persistent != null (ToolsDataLakeView.vue:437). Those two differ on exactly one value: persistent: false, which is what the dialog writes whenever the user unticks its own checkbox:
DataLakeVariableDialog.vue:70-75renders the "Persist variable between boots" checkbox bound tovariable.persistent(defaulttrue,:141);saveVariable(:280) spreads that reactive object straight intocreateDataLakeVariable/updateDataLakeVariableInfo, sopersistentisfalse, not absent.
After this PR such a variable, in ToolsDataLakeView: loses its delete button (v-if="isCompoundVariable(item.id) || isUserDefinedVariable(item.id)", :125, untouched by the diff — and deleteVariable at :420 would refuse it anyway); reads "Cockpit internal" in the Source column (getVariableSource, :321-331); and drops from full edit to the new value-only dialog, so its name, type and description can no longer be changed. Before this PR it had all three.
The permanent case is the edit path, not the create path. updateDataLakeVariableInfo only calls savePersistentVariables() when the new value of persistent is truthy (data-lake.ts:105-107), so turning the checkbox off on an existing user variable never removes it from cockpit-persistent-data-lake-variables. It is reloaded on the next boot with persistent: false and, from this PR on, can never be deleted from this page again.
The premise stated in the new helper's own JSDoc — "Only variables the user creates ask to be persisted between boots" — inverts what the flag means. src/types/data-lake.ts:27-29 documents persistent as "whether the variable existance should be persisted between boots", which is a user choice, not a provenance marker; the diff overloads it as the latter.
Fix: mark provenance explicitly instead of inferring it, without going back to id prefixes. Have the two user-creating call sites stamp userDefined: true (DataLakeVariableDialog.saveVariable already builds a fresh object there, and InputElementConfig.saveOrUpdateParameter:513-523 is one more property), and make the helper info?.userDefined === true || info?.persistent === true. The second half is the legacy fallback for data stored before this PR, and it is exactly correct for it: anything that survived a boot is in storage only because savePersistentVariables filtered on persistent (data-lake.ts:47). That covers old data, new non-persistent user variables, and still excludes the persistent: false telemetry variables the last commit set out to exclude, since nothing in src/ stamps them.
1.7 minor — The value-only editor opens blank, with Save disabled, for any variable whose current value is 0 or false. The dialog fills its field with initialValue.value = currentValue ? currentValue.toString() : '' (DataLakeVariableDialog.vue:219, base numbering — the line is not in the diff), and the new value-only branch of isValid requires initialValue.value !== '' (:261-266 of the diff). So for a variable resting at 0 the user sees an empty "Value (number)" box and a greyed-out Save until they type something.
That falsy check is pre-existing, but this PR is what routes 0-valued variables into this dialog: camera-zoom-increase, camera-zoom-decrease, camera-focus-increase and camera-focus-decrease are created with the value 0 (predefined-resources.ts:46-52) and sit at 0 whenever the bound button is not held, and the same is true of every joystick axis input (see 6.2). In the old full-edit path the blank box was cosmetic — Save stayed enabled because the name and id were filled in, and an empty value simply left the value alone. In the new path it blocks the dialog's only action.
Fix: initialValue.value = currentValue !== undefined ? String(currentValue) : ''. That is one line, it fixes the misleading display in both modes, and it lets isValid's !== '' test mean what it says.
2. Persistence & User Data — inventory, one behaviour change worth recording
One persisted key is touched, through settingsManager (src/libs/settings-management.ts), which keeps a local copy and syncs the key to the connected vehicle's BlueOS storage — so anything wrong here reaches every topside computer that talks to that vehicle. The userDefined field that round 3 reviewed is gone: src/types/data-lake.ts is no longer in the PR at all, so cockpit-persistent-data-lake-variables is now untouched in shape and only affected indirectly (1.6).
| Key | Backend | What happened |
|---|---|---|
cockpit-persistent-data-lake-values |
settings-management.ts (local + vehicle-synced) |
Added entries, and its write strategy changed. camera-zoom-speed and camera-focus-speed become persistValue variables (predefined-resources.ts:45, :49), so their values now live in this key. It is also read during variable creation rather than only at module load, and savePersistentValues now merges into the stored object instead of rebuilding it. |
cockpit-persistent-data-lake-variables |
settings-management.ts (local + vehicle-synced) |
Unchanged by the diff. Listed because 1.6 turns on what it already stores: entries written before this PR carry persistent: true, which is what makes the proposed `userDefined |
Judgement: both keys are cockpit-prefixed and already existed; a camera zoom speed is a vehicle preference rather than a machine-specific value (no device path, no window geometry), so vehicle-syncing is the right backend. No automatic migration is written, and none is needed — the merge is additive and the stored shape does not duplicate its own key. Two consequences of the new write strategy are recorded here rather than raised as findings, because neither is reachable as a defect: pruning now happens only in deleteDataLakeVariable, so an entry whose variable has persistValue turned off is left behind (inert, since loadPersistentVariables filters on persistValue); and getPersistentValues() returns a live reference into the settings manager's cachedSettings (settings-management.ts:328-338 returns the cache itself, and getKeyValue returns the stored value by reference), which both new writers mutate in place before calling the debounced setKeyValue. That happens to be safe today — the debounced write re-wraps the same object — but it is a coupling to an implementation detail of another module that nothing documents.
6. UI / UX — 1 finding
6.2 minor — The new pencil appears on the six joystick axis input variables, where the typed value is overwritten by the next joystick frame. The gate is now isUserDefinedVariable(item.id) || isUserEditableVariable(item.id) (ToolsDataLakeView.vue:117), and isUserEditableVariable is allowUserToChangeValue === true. Besides the camera variables, that flag is carried by every variable created in setupJoystickAxesResources (predefined-resources.ts:158-162): inputs/mavlink/axis-x, -y, -z, -r, -s, -t. Those are live readings — src/libs/joystick/protocols/data-lake.ts:84-88 writes the scaled axis value on every controller update for each mapped axis, and the default profiles map four of them (src/assets/joystick-profiles.ts:51-54). So with a joystick connected, a user who opens the pencil on "Axis X", types a value and saves sees it replaced before the dialog finishes closing, with no feedback explaining why.
This is the part of the widened gate that the round-4 follow-up's reasoning does not cover: it argues, reasonably, that a value-only pencil on camera-zoom-increase is the same capability a custom-widget Slider bound to that variable already has, and that holds for variables nothing else writes continuously. It does not hold for a variable that is written every frame from hardware.
Fix: exclude the continuously-written inputs rather than widening for all of them — gate the pencil on the variable also not being a joystick input (the inputs/mavlink/ ids are already generated from one table, predefined-resources.ts:20-25), or drop allowUserToChangeValue: true from setupJoystickAxesResources' commonVariableConfig if those axes never needed to be user-settable in the first place. Note the second option is not free: that same flag is what lists them in the joystick pickers (ConfigurationJoystickView.vue:848, :858) — the trap that finding 1.2 was about — so check that before taking it.
Sections with nothing to report (8)
3. AGENTS.md Adherence — ✅ (no dependency added, package.json untouched; the new exported helper's JSDoc is typed and non-empty; optional chaining used in both new lookups; the editUserDefinedVariable → editVariable rename is justified by the widened gate rather than incidental; nothing exported without a call site in this PR — the string | DataLakeVariable union and the id-prefix export from round 3 are both gone, which is a net deletion)
4. Security — ✅ (no new dependency, no network call, no eval/v-html, no encoded blob, no env or secret use; no regex left in the diff at all after normalizeDataLakeVariableId was deleted; no workflow, build script, Dockerfile or Electron main-process file touched; the added test mocks a local module and reaches nothing outside the process)
5. Performance — ✅ (all nine changed functions traced to bootstrap or user-action entry points, none to onIncomingMessage, addToDataLake or notifyListeners; getLocalSettings serves getPersistentValues from cachedSettings, so the merge adds an in-memory read rather than a localStorage parse; the one frequency worth naming is setDataLakeVariableData → savePersistentValues, which is O(all data-lake variables) and now also fires for the two speed variables — but only if a user maps a speed to a joystick axis, which is not a default mapping, and that path predates this PR for user-created persistValue variables; no listener, timer or watcher added, so nothing needs teardown)
7. Code Quality & Style — ✅ (complexity-report.json is present for this head: 108 functions measured across the 6 changed files, triggeredCount 0 and not truncated, so nothing the diff writes crosses the complexity-12 or depth-4 thresholds; both reworded comments in data-lake.ts sit on lines the diff changes, so the comment-immutability rule is not engaged; no any, no scoped CSS added, no re-implementation of an existing helper — getPersistentValues is the extraction of a read that was about to appear three times; the added test file needs no JSDoc under jsdoc/require-jsdoc's ArrowFunctionExpression: false)
8. Commit Hygiene — ✅ (five commits read from pr.json, all rewritten this round but still one logical step each — restore, persist the speeds, show the button, value-only dialog, classification — none reverting another, none bundling unrelated work, all small, no wip/fixup! noise, subjects fix:-prefixed in line with the repository's history, and the Fix #2774/Fix #2775 references kept in the PR body rather than in any commit message; the last commit's subject, "stop treating persistent:false variables as user-defined", is an accurate description of what the code now does)
9. Tests — ✅ (no existing test is touched or weakened; the PR adds src/tests/libs/actions/data-lake.test.ts, which reproduces the 1.3 symptom and the delete path. Worth knowing rather than fixing here: CI runs yarn lint, yarn typecheck and yarn build only (.github/workflows/ci.yml:137-142) and never yarn test:ci, so this test will not run on any push — that is the repository's gap, not this PR's)
10. Documentation — ✅ (nothing here differs between Lite and Standalone, so no README change is owed; the one added export carries a complete typed JSDoc — although its summary sentence is the mistaken premise behind finding 1.6 and needs rewording with the fix)
11. Nitpicks / Optional — ✅ (checked the added template for sentence case and stacked insets: "Edit Variable Value" matches the Title Case of the Edit Variable/New Variable siblings it sits beside, and the added name line reuses the existing flex flex-col gap-4 spacing rather than inventing a margin)
Generated by Claude. This is advisory; a human reviewer must still approve.
875df00 to
5b83ffb
Compare
Review follow-up — round 5Done
Won't change (with reasoning)
Notes
|
|
/review |
📝 MINOR SUGGESTIONS (Automated PR Review — round 5)
Both findings from round 4 that were about code are fixed, and fixed at the root. Provenance is now stamped explicitly when a variable is created by the user, instead of being guessed from whether the user asked to keep it between restarts, so a variable you make with the "keep between restarts" box unticked keeps its rename and delete buttons; and the value editor now shows a value of zero as What still needs attention
Since round 4 — 2 closed (both addressed), 1 disputed, 0 new findings, comparing 875df00 → 5b83ffbRange. The incremental diff looks usable this round. Status changes this round
Resolutions and decisions. Discussion since round 4. Two comments, both from @rafaellehmkuhl: a round-5 follow-up and the bare Injection check. Nothing in the PR body, the diff, the commit messages, the comments or the decision entries contained text addressed to the reviewer. The follow-up comment is written as HTML markup; it is summarised above rather than quoted verbatim. Change map — what was established before judgingClaims. The PR body is only
Failure site.
Entry points.
No changed function is unreachable, and none sits on Invariants.
2. Persistence & User Data — inventory, no findingTwo persisted keys are touched, both through
Judgement: both keys are 6. UI / UX — 1 finding (carried from round 4, disputed)6.2 The author's counter-argument, checked and correct on its facts, is that the class is wider than the six generated ids: That is a scope decision for a maintainer rather than something the code settles, which is why this is carried as disputed rather than closed. Either outcome is defensible: accept it and the symptom ships with the PR that introduces the pencil, reject it and the mapping-aware gate lands separately. What the argument does not do is remove the symptom from this diff — before this PR the axis rows had no pencil at all. Sections with nothing to report (9)1. Correctness & Implementation Bugs — ✅ (both round-4 findings fixed at their root and re-verified end to end; walked the headline path once more with the fix in place — a user edits Camera Zoom Speed through the value-only dialog, 3. AGENTS.md Adherence — ✅ (no dependency added, 4. Security — ✅ (no new dependency, no network call, no 5. Performance — ✅ (every changed function traced to a bootstrap or user-action entry point, none to 7. Code Quality & Style — ✅ ( 8. Commit Hygiene — ✅ (five commits read from 9. Tests — ✅ (no existing test touched or weakened; the PR now adds two files — 10. Documentation — ✅ (nothing here differs between Lite and Standalone, so no README change is owed; the new exported helper's JSDoc is complete, correctly typed, and its summary now states what the flag actually means, which was the wording problem behind 1.6) 11. Nitpicks / Optional — ✅ (the added template text matches its surroundings: "Edit Variable Value" keeps the Title Case of the Generated by Claude. This is advisory; a human reviewer must still approve. |
🙋 Decision needed — 6.2Value-only pencil offered on joystick axis inputs whose value is overwritten every frame The author's argument: The author agrees the symptom is real but argues the class is wider than the six joystick axis inputs, since a variable a joystick button is mapped to is also overwritten every frame, so excluding those six ids would fix one instance and leave the identical sibling case broken; the only gate matching the real class would read the live joystick mappings from the controller store in the Data Lake view, which he would rather do in a separate PR. How to vote on this disputeReact to this comment and the next
The two reactions already here were left by the bot so that either answer is one click, and neither of them counts. Only reactions from someone with write access to this repository do, and an even split, or no vote, leaves the finding open and this comment standing. Move your reaction to change your mind while the vote is open — once a |
5b83ffb to
6f08119
Compare
Review follow-up — round 6Done
Done differently
Notes
|
|
/review |
✅ READY TO MERGE (Automated PR Review — round 6)
The PR makes two internal camera settings (zoom speed and focus speed) remember their value between restarts, and lets the Data Lake page edit the value of variables Cockpit itself created but marked as user-changeable, through a stripped-down dialog that changes the value only. It also stops guessing which variables the user created from the persistence checkboxes and records it explicitly instead. This round adds the last missing piece: a variable a joystick control is currently mapped to no longer offers the edit button, because the controller rewrites it many times a second and a typed value would not survive. What still needs attentionNothing is open. Every finding raised across the six rounds is closed. Since round 5 — 1 closed (addressed), 1 dispute settled by vote, 0 new findings, comparing 5b83ffb → 6f08119Range. The incremental diff is usable, but inflated by another rebase. Decisions applied first. Status changes this round
Resolutions. Discussion since round 5. Two comments, both from @rafaellehmkuhl: the round-6 follow-up and the bare Injection check. Nothing in the PR body, the diff, the commit messages, the comments or the decision entry contained text addressed to the reviewer. The follow-up comment is written as HTML markup; it is summarised above rather than quoted verbatim. Change map — what was established before judgingClaims. The PR body is only
Failure site.
Entry points.
No changed function is unreachable, and none sits on Invariants.
2. Persistence & User Data — inventory, no findingTwo persisted keys are touched, both through
Judgement: both keys are One key this round's change reads without writing: Sections with nothing to report (10)1. Correctness & Implementation Bugs — ✅ (the new helper typechecks under the strict config that failed round-2 finding 1.4: 3. AGENTS.md Adherence — ✅ (no dependency added, 4. Security — ✅ (no new dependency, no network call, no 5. Performance — ✅ (the added path is a 6. UI / UX — ✅ (the gate removes an affordance rather than adding a surface, so no dialog anatomy, 7. Code Quality & Style — ✅ ( 8. Commit Hygiene — ✅ (five commits read from 9. Tests — ✅ (no existing test touched or weakened; a third file, 10. Documentation — ✅ (nothing here differs between Lite and Standalone, so no README change is owed; both new exported helpers carry complete, correctly typed JSDoc) 11. Nitpicks / Optional — ✅ (nothing left over: the added template text still matches its surroundings, and the new code introduces no user-facing string at all) Generated by Claude. This is advisory; a human reviewer must still approve. |
Review follow-up — round 7Round 6 closed every finding ( Done
Done differently
|
6f08119 to
2dd00a3
Compare
|
Done — the MANUAL_CONTROL axes ( The gate no longer derives editability from That also removed Rebased into the commits it belongs to, so no new commits on the branch. |
|
/review |
✅ READY TO MERGE (Automated PR Review — round 7)
The PR makes two internal camera settings — zoom speed and focus speed — remember their value between restarts, and gives the Data Lake page a way to change them: a stripped-down dialog that edits the value alone and cannot rewrite Cockpit's own metadata. It also stops guessing which variables the user created from the persistence checkboxes and records that explicitly instead. This round rebuilt the last piece: instead of hiding the edit button for whatever a joystick is currently mapped to, the two speed settings are now marked as the only internal variables meant to be typed in, and everything else internal is uneditable regardless of mapping. What still needs attentionNothing is open. Every finding raised across the seven rounds is closed. Since round 6 — the edit gate was rebuilt on a different mechanism, 0 findings reopened, 0 new, comparing 6f08119 → 2dd00a3Range. The incremental diff is not usable this round. Resolutions and decisions. What actually changed, and what it does to the closed findings. The mechanism that closed 6.2 in round 6 was removed and replaced. Head deletes
Discussion since round 6. Three comments, all from @rafaellehmkuhl: the round-7 follow-up, the description of the rework, and the bare Injection check. Nothing in the PR body, the diff, the commit messages or the comments contained text addressed to the reviewer. The round-7 follow-up is written as HTML markup; it is summarised above rather than quoted verbatim. Change map — what was established before judgingClaims. Read from the PR body (rewritten this round) and the five commit messages in
Failure site.
Entry points.
No changed function is unreachable, and none sits on Invariants.
2. Persistence & User Data — inventory, no findingsTwo persisted keys are touched, both through
Judgement: both keys are Sections with nothing to report (10)1. Correctness & Implementation Bugs — ✅ (traced the value-only path end to end: 3. AGENTS.md Adherence — ✅ (no dependency added, 4. Security — ✅ (no new dependency, no network call, no 5. Performance — ✅ (the round-6 6. UI / UX — ✅ (the gate removes affordances rather than adding a surface: no dialog was added, so no anatomy, 7. Code Quality & Style — ✅ ( 8. Commit Hygiene — ✅ (five commits read from 9. Tests — ✅ (no existing test touched or weakened; 10. Documentation — ✅ (nothing here differs between Lite and Standalone, so no README change is owed; both new exported helpers and both new interface fields carry complete, correctly typed doc blocks, and the 11. Nitpicks / Optional — ✅ (nothing left over: the one user-facing string added this round is the "Edit Variable Value" dialog title, which matches the two titles it sits beside; the value-only layout reuses the existing field styling and introduces no new class beyond the name line) Generated by Claude. This is advisory; a human reviewer must still approve. |
2dd00a3 to
e28dce5
Compare
Review follow-up — round 8Round 7 closed with Why it changed
What replaced them
Deliberately reversing finding 6.2Round 6 removed the value-only pencil from the MANUAL_CONTROL axis inputs and the zoom/focus increase/decrease variables. They get it back here: they carry Correction to the PR bodyThe old description claimed a variable created with persistence unticked "would have lost both actions". That was wrong — |
|
/review |
📝 MINOR SUGGESTIONS (Automated PR Review — round 8)
The pull request gives Cockpit a way to remember which data-lake variables it created itself, instead of guessing from whether a variable happened to set a persistence flag. Variables the app creates are now stamped as its own; anything unstamped — including everything already stored on a user's machine and anything a script creates — counts as the user's, so users keep the edit and delete buttons they had. On the Data Lake page the user can freely edit and delete their own variables, can change the value (but not the name, type or persistence) of the handful the app deliberately exposes for tuning, and can do neither to the rest. Separately it stops joysticks and widget controls from being pointed at computed variables, whose value is recalculated and would immediately discard anything written to it, and it fixes two value-restoring bugs so the camera zoom and focus speeds survive a reboot and the edit dialog shows a stored What still needs attention
Since round 7 — 0 closed, 6 raised, comparing 2dd00a3 → e28dce5Range. Status transitions — none. All 13 entries carried in from round 7 were already closed (11
Resolutions and votes. Discussion since round 7. Two comments, both from Change map — what was established before judgingClaims.
Failure site. The misbehaving code is Entry points.
Nothing changed is unreached; the per-row helpers are three object lookups each, and Invariants. The change rests on one rule: every variable Cockpit itself creates carries 0. Summary — 1 finding0.1 The description states: "Editing one of Cockpit's opens the dialog limited to the value field, so it cannot rewrite the id, type or persistence the app depends on." That holds for plain variables, where This is not a regression — base showed that pencil for every compound and a delete button besides — so it is a documentation problem, not a code one. Either narrow the sentence to non-compound variables, or say that a Cockpit compound stays fully editable by design because its expression is meant to be tuned. Consequence. A reviewer reading the description forms the wrong idea of what the change guarantees, and the next person to touch this code inherits that idea. 1. Correctness & Implementation Bugs — 1 finding1.8
const registerCockpitActions = (): void => {
if (miniWidget.value.options.dataLakeVariable &&
getDataLakeVariableInfo(miniWidget.value.options.dataLakeVariable.id) !== undefined) return
if (miniWidget.value.options.dataLakeVariable) {
createDataLakeVariable(miniWidget.value.options.dataLakeVariable, …)
}
}
onMounted(() => registerCockpitActions())The existence guard means it never clobbers a variable that is already registered, and that covers the common case. It does not cover the case where the widget mounts before the real registrar has run. Those registrars all create-if-absent and never update: The visible result: a Dial or Slider bound to, say, The fix that closes it at the chokepoint rather than at N producers: in Consequence. A vehicle-telemetry value that an existing dashboard widget is already showing can appear on the Data Lake page as if the user had created it, offering a delete button that removes something the app depends on. 2. Persistence & User Data — inventory, no findings
Judgement on each:
6. UI / UX — 3 findings6.3
const commonVariableConfig = {
type: 'number' as DataLakeVariableType,
allowUserToChangeValue: true,
systemOwned: true,
}
The narrow fix is to split the config the way Author's argument (recorded, not adopted): the edit is value-only so it cannot damage metadata, the write being overwritten is simply what a mapped control does, and with no joystick mapped it is a usable way to exercise zoom or an axis — the alternative was a third flag whose only job was excluding these ten rows. Consequence. The Data Lake page still lets you type a value into the six joystick axis readings and the four zoom/focus step values, and a controller overwrites whatever you typed a fraction of a second later. 6.4
Consequence. The Source column no longer says which rows are calculated from an expression, and typing "compound" in the search box no longer finds them. 6.5
Consequence. The pencil next to a computed variable can still open its editor if it is ever reached by any route other than the table, unlike its two neighbours which now refuse. 9. Tests — 1 finding9.1
The two sibling files do not have this problem: Consequence. Running one of the new tests on its own fails, which will confuse whoever next debugs that file. Sections with nothing to report (6)3. AGENTS.md Adherence — ✅ (no new dependency and no 4. Security — ✅ (no new dependency, no network call, no 5. Performance — ✅ (the three new per-row helpers are object lookups, and 7. Code Quality & Style — ✅ ( 8. Commit Hygiene — ✅ (six commits, each one logical change, largest well under a few hundred lines; no 10. Documentation — ✅ (nothing here differs between Lite and Standalone, so no README note is owed; the two new interface fields, all three new Generated by Claude. This is advisory; a human reviewer must still approve. |
🙋 Decision needed — 6.3Value-only pencil returns to the six axis inputs and four zoom/focus step variables The author's argument: The edit is value-only so it cannot damage metadata, the write being overwritten is simply what a mapped control does, and with no joystick mapped it is a usable way to exercise zoom or an axis - the alternative was a third flag whose only job was excluding these ten rows. How to vote on this disputeReact to this comment and the next
The two reactions already here were left by the bot so that either answer is one click, and neither of them counts. Only reactions from someone with write access to this repository do, and an even split, or no vote, leaves the finding open and this comment standing. Move your reaction to change your mind while the vote is open — once a |
e28dce5 to
c990abf
Compare
Review follow-up — round 9Round 8 raised 6. Four are addressed below, one closes by construction and one is voted. Branch force-pushed: Done
Voted
Correction to round 8's follow-upThat note implied the default inversion would also fix the late marking of |
|
/review |
📝 MINOR SUGGESTIONS (Automated PR Review — round 9)
The pull request gives Cockpit a way to remember which data-lake variables it created itself, instead of guessing from whether a variable happened to set a persistence flag. This round the flag's default was turned around: a variable that says nothing about who made it is now taken as Cockpit's, and the three places that create one for the user — the Data Lake dialog, the compound dialog and the custom-widget input config — say so explicitly. Anything read back from a user's existing storage is still treated as theirs, so nobody loses the delete button on a variable they already made. On the Data Lake page the user can freely edit and delete their own variables, can change the value (but not the name, type or persistence) of the handful the app deliberately exposes for tuning, and can do neither to the rest. Separately it stops joysticks and widget controls from being pointed at computed variables, whose value is recalculated and would immediately discard anything written to it, and it fixes two value-restoring bugs so the camera zoom and focus speeds survive a reboot and the edit dialog shows a stored Everything round 8 left open is now closed. What remains are two things this round introduced: one narrow leftover of the ownership question at the same place round 8's finding 1.8 named, and some re-wrapped lines that no longer carry a change. What still needs attention
Since round 8 — 6 closed, 2 raised, comparing e28dce5 → c990abfRange.
Status transitions — 6 closed, 0 still open from round 8.
The 13 entries carried in already closed stay closed. Unlike round 8 that is close to a formality this time — the mechanism they were verified against is the same one, only with its default reversed, and each still resolves the same way. Two are worth naming because the inversion moved the ground under them: 1.5 and 1.6 (input-element and dialog-created variables keeping delete and full edit) no longer hold because unflagged means the user's; they now hold because those creators write Resolutions. Votes. One entry in Discussion since round 8. Two comments, both from
Raised this round. 1.9 ( Change map — what was established before judgingClaims.
Failure site. Unchanged from round 8: Entry points.
Nothing changed is unreached. Invariants. The rule is now inverted from the one rounds 1–8 checked: every variable is Cockpit's unless it says otherwise, and only the user-facing creators say otherwise. Enumerating the exceptions rather than the rule makes the check much shorter —
The three remaining 1. Correctness & Implementation Bugs — 1 finding1.9 Closed finding 1.8 was about a widget snapshot replaying a telemetry variable as the user's. The inversion fixes that and opens the mirror case at the same spot, for the smaller set of variables that storage cannot vouch for.
<select v-model="currentElement.options.dataLakeVariable">
<option v-for="variable in availableDataLakeVariables" :value="variable">and
The variable is theirs, it only exists at all because the widget resurrected it, and the only way left to remove it is to delete the widget element. Note what is not affected, since it bounds this tightly: a variable created with the checkbox left at its default is in The failure direction is now the safe one — nothing the app depends on can be deleted by mistake, which is the half that mattered — so this is worth less than 1.8 was. The chokepoint fix is the same one 1.8 suggested and is unchanged by the inversion: in Consequence. A variable the user created with "Persist variable between boots" switched off, and then pointed a widget element at, comes back after a restart marked as Cockpit's: no delete button, and the editor limited to its value. 3. AGENTS.md Adherence — 1 finding3.2 Round 8's approach added
In all four the content is byte-identical to base — same keys, same values, same order. This will not clean itself up. Prettier is configured at Consequence. Three files carry hunks that only move existing code onto more lines, and one file is in the diff with nothing else in it, which costs every future reader of that blame a detour. 2. Persistence & User Data — inventory, no findings
Judgement on each:
Sections with nothing to report (8)0. Summary — ✅ (the PR body's account now matches the code on every point checked, including the plain/compound split that was finding 0.1, and the follow-up comment volunteers the one correction the body does not cover — the reclaim window on 4. Security — ✅ (no new dependency and no 5. Performance — ✅ (the per-row helpers are object lookups, and 6. UI / UX — ✅ (6.3 settled by vote, 6.4 and 6.5 addressed; the new compound marker is a non-interactive indicator rather than a control, so it owes no label or focus handling, and it sits in a fixed-width slot that is rendered whether or not the icon is, keeping names aligned; both new refusal paths — 7. Code Quality & Style — ✅ ( 8. Commit Hygiene — ✅ (six commits, each one logical change, largest well under a few hundred lines; no 9. Tests — ✅ (9.1 addressed; three focused unit files covering the persisted-value restore and delete behaviour, the pre-flag read defaults on both storage keys, the stamping of stored and new transforming functions with the flags reaching the data lake variable the page actually reads, and the edit/delete matrix including the unmarked-means-Cockpit's default; each of the three files now passes standalone, and the assertions are on observable outcomes — what is in 10. Documentation — ✅ (nothing here differs between Lite and Standalone, so no README note is owed; both added interface fields, all three Generated by Claude. This is advisory; a human reviewer must still approve. |
…ated The page had no way of knowing who created a variable, so it guessed from the persistence flags, and for compound variables it could not guess at all: a transforming function Cockpit sets up looks exactly like one the user wrote. Cockpit creates almost every variable there is, so its ownership is what a variable says by staying quiet, and the three places that create one on the user's behalf say otherwise. Anything read back from storage is taken as the user's instead, since it predates this being recorded: for plain variables that is provable, as only those three places ever write to the persisted-variables key, and for compound ones it is the reading that does not take a variable away from whoever made it, with Cockpit reclaiming its own through the setup routines. Those routines go through a single helper that creates the function or records that it is Cockpit's, leaving the expression the user may have tuned alone, and the compound dialog carries the record through an edit. The camera zoom and focus expressions and the MANUAL_CONTROL axis outputs are marked as the user's to set, joining the speeds, which already were. Nothing reads any of this yet.
c990abf to
cba30c6
Compare
Review follow-up — round 10Round 9 raised 2, both this round. One addressed, one declined. Branch force-pushed: Done
Won't change (with reasoning)
|
|
/review |
📝 MINOR SUGGESTIONS (Automated PR Review — round 10)
The pull request gives Cockpit a way to remember which data-lake variables it created itself, instead of guessing from whether a variable happened to set a persistence flag. A variable that says nothing about who made it is taken as Cockpit's, and the three places that create one for the user — the Data Lake dialog, the compound dialog and the custom-widget input config — say so explicitly; anything read back from a user's existing storage is treated as theirs, so nobody loses the delete button on a variable they already made. On the Data Lake page the user can freely edit and delete their own variables, can change the value (but not the name, type or persistence) of the handful the app deliberately exposes for tuning, and can do neither to the rest. Separately it stops joysticks and widget controls from being pointed at computed variables, whose value is recalculated and would immediately discard anything written to it, and it fixes two value-restoring bugs so the camera zoom and focus speeds survive a reboot and the edit dialog shows a stored This round is small: the formatter-only re-wraps are gone and What still needs attention
Since round 9 — 1 closed, 1 disputed, comparing c990abf → cba30c6Range.
Status transitions — 1 closed, 1 disputed.
The 19 entries carried in already closed stay closed. Nothing in this round's diff touches the mechanism any of them was verified against — the only code change is whitespace being un-done — so they were re-confirmed by checking that the hunks backing them are byte-identical to round 9, rather than re-traced from scratch. Resolutions. Votes. Discussion since round 9. Two comments, both from Raised this round. Nothing. The full sweep over Change map — what was established before judgingClaims.
Failure site. Unchanged: Entry points.
Nothing changed is unreached. Invariants. Every variable is Cockpit's unless it says otherwise, and only the user-facing creators say otherwise.
The three remaining 1. Correctness & Implementation Bugs — 1 finding1.9 Closed finding 1.8 was about a widget snapshot replaying a telemetry variable as the user's. The inversion fixes that and opens the mirror case at the same spot, for the smaller set of variables that storage cannot vouch for.
<option v-for="variable in availableDataLakeVariables" :key="variable.name" :value="variable">and
What is not affected bounds this tightly: a variable created with the checkbox left at its default is in Correction to round 9. The two code remedies this finding originally suggested were both wrong, and the author is right to say so. Carrying the flags from Author's position. Declined on purpose, with the reasoning that for an unflagged snapshot the information genuinely is not there — a replayed telemetry variable and a replayed non-persistent user variable are the same bytes — so any rule has to guess, and this PR takes the guess where nothing the app depends on can be deleted by mistake. The PR body now carries a "Known limitation" paragraph saying so, which is the alternative remedy this finding asked for. That is a coherent answer and the finding is left open only because a body paragraph is not a code change; it is for a maintainer, not for this review, to close it. Consequence. A variable the user created with "Persist variable between boots" switched off, and then pointed a widget element at from a pre-existing profile, comes back after a restart marked as Cockpit's: no delete button, and the editor limited to its value. 2. Persistence & User Data — inventory, no findings
Judgement on each:
Sections with nothing to report (10)0. Summary — ✅ (the PR body's account matches the code on every point checked, including the plain/compound split that was finding 0.1; the new "Known limitation" paragraph added this round is accurate on all three of its factual claims — the whole-object binding at 3. AGENTS.md Adherence — ✅ (3.2 addressed in full this round; the diff is now scoped to what the PR is for, with 4. Security — ✅ (no new dependency and no 5. Performance — ✅ (the per-row helpers are object lookups, and 6. UI / UX — ✅ (6.3 settled by vote, 6.2/6.4/6.5 addressed; the compound marker is a non-interactive indicator rather than a control, so it owes no label or focus handling, and it sits in a fixed 7. Code Quality & Style — ✅ ( 8. Commit Hygiene — ✅ (six commits, each one logical change, largest well under a few hundred lines; no 9. Tests — ✅ (9.1 addressed; three focused unit files covering the persisted-value restore and delete behaviour, the pre-flag read defaults on both storage keys, the stamping of stored and new transforming functions with the flags reaching the data lake variable the page actually reads, and the edit/delete matrix including the unmarked-means-Cockpit's default; each of the three files passes standalone by inspection of its own setup, and the assertions are on observable outcomes — what is in 10. Documentation — ✅ (nothing here differs between Lite and Standalone, so no README note is owed; both added interface fields, all three 11. Nitpicks — ✅ (nothing left; the two nits still standing at round 9 were 3.2's re-wraps, now collapsed, and the rest closed in earlier rounds) Generated by Claude. This is advisory; a human reviewer must still approve. |
🙋 Decision needed — 1.9A user's non-persistent variable replayed from a widget snapshot is read as Cockpit's after the default inversion The author's argument: For an unflagged widget snapshot the information simply is not there, since a replayed telemetry variable and a replayed non-persistent user variable are the same bytes, so any rule has to guess and this PR deliberately takes the guess where nothing the app depends on can be deleted by mistake, states the case as a known limitation in the PR body, and leaves the real fix of storing only the variable id in the element's options to a change that can carry the widget-options migration it needs. How to vote on this disputeReact to this comment and the next
The two reactions already here were left by the bot so that either answer is one click, and neither of them counts. Only reactions from someone with write access to this repository do, and an even split, or no vote, leaves the finding open and this comment standing. Move your reaction to change your mind while the vote is open — once a |
cba30c6 to
13951b0
Compare
Review follow-up — round 11Round 10 left nothing to implement — 3.2 closed in full and 1.9 is the standing dispute, which now carries a maintainer 👍. What follows is presentation work I picked up after reading the round-10 body, not a response to a finding. Branch force-pushed: Done
|
|
/review |
📝 MINOR SUGGESTIONS (Automated PR Review — round 11)
Cockpit now records who created each data-lake variable instead of guessing from whether the variable was marked persistent. The Data Lake page uses that record to decide which rows get an edit button and which get a delete button: the user can delete only their own variables, and can edit Cockpit's only where Cockpit says the value is theirs to set — which is what finally makes the camera zoom and focus speeds adjustable. Those two speeds are also remembered between boots now, and values that were supposed to be remembered are actually restored at startup instead of being overwritten by their defaults. Separately, a variable whose value is computed from an expression can no longer be picked as the target of a joystick axis or a widget control, since anything written to it is recalculated away a moment later. What still needs attention
Since round 10 — 1 settled, comparing cba30c6 → 13951b0Range. Findings that changed status.
No Discussion since round 10. rafaellehmkuhl posted a follow-up describing presentation work not asked for by any finding (comment). Checked against the code rather than taken as read:
The bare Change map — what was established before judgingClaims.
Failure site. Two bugs, both with the misbehaving code in the diff. For #2775 it is Entry points.
No changed function traced to Invariants.
2. Persistence & User Data — inventory, no findings
Judging each:
8. Commit Hygiene — 1 finding8.1 — The final commit's message describes the icon landing in a column it does not land in ·
The rest of the sentence checks out: Fix: on the next rebase, change "beside the name" to "beside the type" in that commit's body. Nothing else in the six commits needs touching — each is one logical change, the ownership recording lands before the gating that reads it, the compound write-target fix rides alone as its own commit, and no message references an issue number. Sections with nothing to report (9)1. Correctness & Implementation Bugs — ✅ (traced the flags from 3. AGENTS.md Adherence — ✅ (all 30 4. Security — ✅ (no new dependency, no network call, no 5. Performance — ✅ ( 6. UI / UX — ✅ (the compound marker is a non-interactive indicator with a tooltip, matching the bare 7. Code Quality & Style — ✅ (the complexity report measured 458 functions across all 20 changed files with nothing triggered and no truncation; the new JSDoc blocks carry typed 9. Tests — ✅ (each of the three files establishes its own state, the transformations test re-claiming 10. Documentation — ✅ (nothing here differs between Lite and Standalone, so the README table is untouched; 11. Nitpicks / Optional — ✅ (nothing beyond the commit wording reported as 8.1) Generated by Claude. This is advisory; a human reviewer must still approve. |
The page inferred ownership from the persistence flags, so internal variables that explicitly set persistent:false, such as the BlueOS ones and Camera Tilt, were offered full edit and delete, and every compound variable was, which included Cockpit's own ArduPilot System ID, camera expressions and POI coordinates. Both actions now follow what was recorded. Only the user's own variables can be deleted, and of Cockpit's, only the ones marked as theirs to set can be edited, which is what makes the camera zoom and focus speeds reachable. Editing one of Cockpit's plain variables opens a dialog limited to the value, so it cannot rewrite the metadata the app relies on; a compound of Cockpit's stays fully editable, as its expression is the thing meant to be tuned. The Source column answers the same question, so it now says who owns the variable rather than mixing that with whether it happens to be a compound one. Which rows are computed moves to an icon beside the type it qualifies, the same one the "Add compound variable" button uses, and searching for "compound" still finds them.
13951b0 to
d2ee804
Compare
Review follow-up — round 12Round 11 left one nit and closed 1.9 on the vote. Branch force-pushed: Done
|
|
/review |
✅ READY TO MERGE (Automated PR Review — round 12)
Cockpit now records who created each data-lake variable instead of guessing from whether the variable was marked persistent. The Data Lake page uses that record to decide which rows get an edit button and which get a delete button: the user can delete only their own variables, and can edit Cockpit's only where Cockpit says the value is theirs to set — which is what finally makes the camera zoom and focus speeds adjustable. Those two speeds are also remembered between boots now, and values that were supposed to be remembered are actually restored at startup instead of being overwritten by their defaults. Separately, a variable whose value is computed from an expression can no longer be picked as the target of a joystick axis or a widget control, since anything written to it is recalculated away a moment later. What still needs attentionNothing. Every finding raised across the twelve rounds is closed. Since round 11 — 1 closed, comparing 13951b0 → d2ee804Range. The incremental diff is not a picture of this round. The branch was force-pushed and the previous head was rewritten in place, so the compare falls back to the commit before it ( That fallback is also what makes the scope of the push checkable without the old head. Because the compare's merge base is Findings that changed status.
Resolutions and decisions. Discussion since round 11. rafaellehmkuhl posted a follow-up stating that the branch was force-pushed, that only the last commit's message changed, and that its tree is byte-identical. The reworded message is verified above; the byte-identical claim cannot be checked directly from here, since the previous head is no longer reachable, but everything that can be checked about it holds — the five earlier commits are provably the same objects, and the final commit's content matches what round 11 read. The bare Injection check. Nothing in the PR body, the diff, the commit messages or the comments contained text addressed to the reviewer. Change map — what was established before judgingClaims.
Failure site. Two bugs, both with the misbehaving code in the diff. For the editability bug it is Entry points.
No changed function traced to Invariants.
2. Persistence & User Data — inventory, no findings
Judging each:
Sections with nothing to report (10)1. Correctness & Implementation Bugs — ✅ (traced the flags from 3. AGENTS.md Adherence — ✅ (all 30 4. Security — ✅ (no new dependency, no network call, no added 5. Performance — ✅ ( 6. UI / UX — ✅ (the compound marker is a non-interactive indicator with a tooltip, matching the bare 7. Code Quality & Style — ✅ ( 8. Commit Hygiene — ✅ (six commits, each one logical change: the ownership recording lands before the gating that reads it, the compound write-target fix rides alone, and 9. Tests — ✅ (each of the three added test files establishes its own state, the transformations test re-claiming 10. Documentation — ✅ (nothing here differs between Lite and Standalone, so the README table is untouched; 11. Nitpicks / Optional — ✅ (nothing left to raise) Generated by Claude. This is advisory; a human reviewer must still approve. |
|
@ArturoManzoli putting this one on #fast-track since it's needed for the 4K Cam launch. |
Why
Two problems on the Data Lake page, both coming from the same root cause: Cockpit had no way of knowing who created a variable, so the page guessed from the persistence flags.
persistent != null, which is true for any variable that sets the flag at all — including the internal ones that explicitly setpersistent: false: every BlueOS variable, Camera Tilt, Autopilot System ID, Network Latency. Those were offered full edit and delete. For compound variables the page could not guess at all, since a transforming function Cockpit sets up looks exactly like one the user wrote, so every compound got both actions — the legacy ArduPilot System ID and the POI coordinates among them.3on every boot.What
Cockpit records who created each variable, through a new optional
systemOwnedflag onDataLakeVariableand onTransformingFunction. Cockpit creates almost every variable there is, so its ownership is what a variable says by staying quiet, and the three places that create one on the user's behalf say otherwise — the Data Lake dialog, the compound dialog and the custom-widget input config.Anything read back from storage is taken as the user's instead, since it predates this being recorded. For plain variables that is provable: only those three places ever write to
cockpit-persistent-data-lake-variables, as Cockpit never marks its ownpersistent. For compound ones it is the reading that cannot take a variable away from whoever made it, with Cockpit reclaiming its own through the setup routines. Without this, upgrading would hand every variable a user had already created over to Cockpit and remove their delete button.Compound variables go through
ensureCockpitTransformingFunction(), which creates the function or records that it is Cockpit's, leaving the expression the user may have tuned untouched. It is idempotent, so the setup routines call it on every boot, and it replaced the find/create dance that was duplicated across four call sites. The compound dialog carries the record through an edit instead of dropping it.Known limitation. A widget input element stores a copy of the whole variable in its own options, and replays it at mount when no variable with that id is registered. A variable the user created with persistence switched off is not remembered anywhere else across a restart, so it comes back from that copy — and if the copy predates this flag it says nothing about who made it, so it is read as Cockpit's: no delete button, and the editor limited to its value. Both readings of an unflagged copy are guesses, and this is the one where nothing the app depends on can be deleted by mistake. Copies written from now on carry the flag, since the selector binds the live registry object.
allowUserToChangeValuesays what the user may set. It was already the flag for "a joystick mapping or a widget element writes here"; it now reads as the user may set this value, by hand from the Data Lake page, and — when the variable is not a compound one — through a control. That is the single question the page asks: a variable is editable when it is the user's own, or when Cockpit marked it as theirs to set. Deletion stays stricter: only their own, never Cockpit's.Marked as the user's to set: the camera zoom and focus speeds, the camera zoom and focus expressions, and the six MANUAL_CONTROL axis outputs — values and expressions that are tuned to fit the vehicle.
Editing one of Cockpit's plain variables opens the dialog limited to the value field, so it cannot rewrite the id, type or persistence the app depends on. A Cockpit compound stays fully editable in the compound dialog, name and expression included, by design — the expression is the thing meant to be tuned, and that matches what
masteralready allowed.No control may write to a compound variable. A compound is computed from its expression, so a write from a joystick or a widget element is undone at the next evaluation. The joystick button picker already excluded them; the axis picker and the custom widget input elements went by
allowUserToChangeValuealone. Both now check as well. A widget element can still be pointed at a compound variable to display its value, it just stays read-only. This lands first, as its own commit, so no intermediate commit offers a compound as something to write to.Persisted values are actually restored.
createDataLakeVariablewas unconditionally writing the default over whatever was in storage, so apersistValuevariable never came back with the user's value. A saved value now wins over the initial one.savePersistentValuesalso merges into the stored object instead of rebuilding it from the variables registered so far — rebuilding dropped the values of variables not registered yet — and deleting a variable removes only its own key.The camera speeds persist, now created with
persistValue: true, so a tuned speed survives a reboot. The increase/decrease variables were left alone, since their value is written by whatever control is mapped to them.Also along the way: the edit dialog filled the value field with a truthiness check, so a variable holding
0orfalseopened with an empty field; and the Source column now answers who owns the variable instead of mixing that with whether it happens to be a compound one. Which rows are computed moved to an icon beside the type, the samemdi-function-variantthe "Add compound variable" button uses, and searching for "compound" still finds them.Tests
Three unit test files: persisted-value restore and delete behaviour, plus a pre-flag stored variable being read back as the user's (
src/tests/libs/actions/data-lake.test.ts); recording ownership on stored and new transforming functions, and it reaching the data lake variable the page reads (src/tests/libs/actions/data-lake-transformations.test.ts); and the edit/delete rules including the unmarked-means-Cockpit's default (src/tests/libs/utils-data-lake.test.ts).One window this does not close. A compound variable Cockpit owns is only recorded as its own when the routine that creates it runs, and two of them run late:
ardupilotSystemIdwhen a MAVLink vehicle is instantiated, and the POI coordinate functions when a map component mounts. On the first boot after upgrading, the stored entries still carry nothing, so until that moment they read as User defined with a delete button. The record is persisted once written, so every later boot is correct from load. Neither is a regression — onmasterboth are editable and deletable outright, with no window — and closing it means moving those two calls to bootstrap, which is a change of its own.Fix #2774
Fix #2775