Skip to content

Tell the reader what to do when their bootloader has no && - #118

Merged
openipc-ai merged 2 commits into
masterfrom
compound-command-fallback
Aug 25, 2026
Merged

Tell the reader what to do when their bootloader has no &&#118
openipc-ai merged 2 commits into
masterfrom
compound-command-fallback

Conversation

@openipc-ai

Copy link
Copy Markdown
Collaborator

From OpenIPC/firmware#2299, found while triaging website bugs filed against
the firmware repo.

The problem

guarded_flash chains the transfer to the erase and the write with &&, so a
failed transfer cannot reach the erase. That is the #55 fix and it should stay.

But && is a hush feature, and the bootloader somebody is running the first
time they follow this page is the stock vendor one. The reporter in #2299:

the boot loader does NOT work with compound commands, I don't think it
recognizes the && and just gives me the help entry for the first command. I
had to execute each one separately

The bootloader parses the whole line as one command with too many arguments,
prints usage, and does nothing. Safe — nothing transferred, nothing erased —
but silent about why, and the line is not one a reader can take apart unaided.
Splitting it wrongly is how you get an erase without a transfer, which is
precisely what && was added to prevent.

The change

list_of_commands appends a note to any block whose text chains, and to no
other:

tftpboot 0x82000000 openipc-hi3518ev200-nor-lite-16mb.bin && sf erase 0x0 0x1000000 && sf write …
# if there is no tftpboot but tftp then run this instead
tftp 0x82000000 openipc-hi3518ev200-nor-lite-16mb.bin && sf erase 0x0 0x1000000 && sf write …
reset

  If your bootloader answers with a usage message instead of running the line,
  it does not understand &&. Enter the parts one at a time, and run the erase
  and the write only after the transfer has reported success — checking that is
  what && does for you.

Keyed off the rendered text rather than the calling helper, so a block that
stops chaining loses the note on its own. run setnor8m, run uknand; run urnand and the mw.b lines are untouched.

Ten locales; i18n-tasks missing and unused do not mention the new key. Two
translations needed an em dash where a colon would naturally have gone — a plain
YAML scalar cannot contain ": ", and it fails at load rather than at review.

Verification

bin/rails test — 203 runs, 765 assertions, 0 failures (200 before).

Three tests at the helper level, where the include/exclude rule actually lives,
and two through the rendered page. One asserts the Russian string: English is
the fallback for a missing translation, so an English assertion cannot tell a
translated string from a hardcoded one.

Removing the note fails four of the five; the "does not chain" test correctly
keeps passing. One new rubocop offence appeared during development (a stray
blank line) and is fixed; the rest is the two class/module length counters
rising.

Note on scope

This does not weaken the guard — the && is still there for every bootloader
that understands it. It only stops the failure being mute for the ones that do
not.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YFg9PRy2T6cr983S8gran5

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Explain safe flashing when bootloaders lack compound commands

🐞 Bug fix 📝 Documentation 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Explain safe manual flashing when stock bootloaders reject && command chains.
• Show the caveat only beside command blocks that actually use compound commands.
• Localize the guidance across ten languages and cover helper and page rendering.
Diagram

graph TD
  A["Installation commands"] --> B["Command renderer"] --> C{"Contains &&?"}
  C -- Yes --> D["Localized caveat"] --> E["Installation page"]
  C -- No --> E
Loading
High-Level Assessment

The rendered-text check is the best fit because the warning follows the actual presence of && and disappears automatically if a command block stops chaining. An explicit caller flag was considered but would duplicate knowledge across call sites and could drift from the rendered commands.

Files changed (13) +88 / -1

Bug fix (1) +18 / -1
installation_helper.rbAppend fallback guidance to compound-command blocks +18/-1

Append fallback guidance to compound-command blocks

• Updates 'list_of_commands' to detect '&&' in its command lines and append a localized, HTML-safe caveat. Blocks without compound commands retain their existing output.

app/helpers/installation_helper.rb

Tests (2) +60 / -0
socs_controller_test.rbVerify caveat rendering and localization end to end +29/-0

Verify caveat rendering and localization end to end

• Adds page-level assertions that chained flashing instructions include the caveat and that Russian requests render Russian guidance rather than an English fallback.

test/controllers/socs_controller_test.rb

installation_helper_test.rbCover conditional and HTML-safe caveat rendering +31/-0

Cover conditional and HTML-safe caveat rendering

• Adds focused helper tests for chained blocks, unchanged non-chained blocks, and unescaped '<code>' markup in the translated caveat.

test/helpers/installation_helper_test.rb

Documentation (10) +10 / -0
de.ymlAdd German compound-command fallback guidance +1/-0

Add German compound-command fallback guidance

• Adds the German translation explaining how to safely execute flashing steps when the bootloader rejects '&&'.

config/locales/de.yml

en.ymlAdd English compound-command fallback guidance +1/-0

Add English compound-command fallback guidance

• Defines the source guidance describing the usage-message symptom and requiring transfer success before manual erase and write steps.

config/locales/en.yml

es.ymlAdd Spanish compound-command fallback guidance +1/-0

Add Spanish compound-command fallback guidance

• Adds the Spanish translation explaining how to safely execute flashing steps when the bootloader rejects '&&'.

config/locales/es.yml

fa.ymlAdd Persian compound-command fallback guidance +1/-0

Add Persian compound-command fallback guidance

• Adds the Persian translation explaining how to safely execute flashing steps when the bootloader rejects '&&'.

config/locales/fa.yml

fr.ymlAdd French compound-command fallback guidance +1/-0

Add French compound-command fallback guidance

• Adds the French translation explaining how to safely execute flashing steps when the bootloader rejects '&&'.

config/locales/fr.yml

it.ymlAdd Italian compound-command fallback guidance +1/-0

Add Italian compound-command fallback guidance

• Adds the Italian translation explaining how to safely execute flashing steps when the bootloader rejects '&&'.

config/locales/it.yml

pl.ymlAdd Polish compound-command fallback guidance +1/-0

Add Polish compound-command fallback guidance

• Adds the Polish translation explaining how to safely execute flashing steps when the bootloader rejects '&&'.

config/locales/pl.yml

pt.ymlAdd Portuguese compound-command fallback guidance +1/-0

Add Portuguese compound-command fallback guidance

• Adds the Portuguese translation explaining how to safely execute flashing steps when the bootloader rejects '&&'.

config/locales/pt.yml

ru.ymlAdd Russian compound-command fallback guidance +1/-0

Add Russian compound-command fallback guidance

• Adds the Russian translation explaining how to safely execute flashing steps when the bootloader rejects '&&'.

config/locales/ru.yml

zh.ymlAdd Chinese compound-command fallback guidance +1/-0

Add Chinese compound-command fallback guidance

• Adds the Chinese translation explaining how to safely execute flashing steps when the bootloader rejects '&&'.

config/locales/zh.yml

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Fallback skips erase check 🐞 Bug ≡ Correctness
Description
The fallback tells users to run both erase and write after checking only the transfer, but every
generated chain also uses the second && to prevent the write when erase fails. Following this
instruction can therefore write into unsuccessfully erased flash, corrupting the image or bootloader
where the original command would have stopped.
Code

config/locales/en.yml[76]

+      compound_caveat_html: If your bootloader answers with a usage message instead of running the line, it does not understand <code>&amp;&amp;</code>. Enter the parts one at a time, and run the erase and the write only after the transfer has reported success — checking that is what <code>&amp;&amp;</code> does for you.
Evidence
guarded_flash generates transfer && erase && write, so the chain enforces two independent
success gates. Every current command that triggers the new caveat is generated through this helper,
while all ten new translations condition erase and write only on transfer success.

app/helpers/installation_helper.rb[51-55]
app/helpers/installation_helper.rb[104-111]
app/helpers/installation_helper.rb[127-134]
app/helpers/installation_helper.rb[151-157]
app/helpers/installation_helper.rb[201-205]
config/locales/en.yml[76-76]
config/locales/ru.yml[76-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The manual fallback preserves the transfer check but omits the erase-success check represented by the second `&&`. Tell readers to proceed to each next part only after the preceding part reports success.

## Issue Context
All matching `guarded_flash` lines have the form `transfer && erase && write`; writing after a failed erase can corrupt flash. Keep all locale translations semantically aligned and update assertions as needed.

## Fix Focus Areas
- config/locales/en.yml[76-76]
- config/locales/de.yml[76-76]
- config/locales/es.yml[76-76]
- config/locales/fa.yml[76-76]
- config/locales/fr.yml[76-76]
- config/locales/it.yml[76-76]
- config/locales/pl.yml[76-76]
- config/locales/pt.yml[76-76]
- config/locales/ru.yml[76-76]
- config/locales/zh.yml[76-76]
- test/helpers/installation_helper_test.rb[11-29]
- test/controllers/socs_controller_test.rb[562-580]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources

Grey Divider

Tip of the day
💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread config/locales/en.yml Outdated
openipc-ai and others added 2 commits August 25, 2026 12:20
guarded_flash joins the transfer to the erase and the write with `&&` so
a failed transfer cannot reach the erase. That fix is why #55
exists and it should stay. But `&&` is a hush feature, and the bootloader
somebody is running the first time they follow this page is the stock
vendor one -- which does not always have it.

The reporter in OpenIPC/firmware#2299 hit exactly that: "the boot loader
does NOT work with compound commands, I don't think it recognizes the &&
and just gives me the help entry for the first command. I had to execute
each one separately."

That failure is safe. The bootloader parses the line as one command with
too many arguments, prints usage, and transfers nothing, erases nothing.
It is also completely silent about why, and the line is not one a reader
can take apart unaided -- splitting it wrongly is how you get an erase
without a transfer, which is the thing `&&` was added to prevent.

So list_of_commands now appends a note to any block that chains, and to
no other, naming the symptom and saying to check the transfer reported
success before running the erase. Keyed off the rendered text rather than
the caller, so a block that stops chaining loses the note by itself.

Ten locales. `i18n-tasks missing` and `unused` do not mention the new key.
Two of the translations needed an em dash where a colon would have been,
because a plain YAML scalar cannot contain ": ".

Three tests at the helper level, where the include/exclude rule lives,
and two through the rendered page, one of them in Russian -- the English
string is the fallback, so an English assertion cannot tell a translated
heading from a hardcoded one. Removing the note fails four of the five.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFg9PRy2T6cr983S8gran5
Qodo's review, and it is right. guarded_flash is
`transfer && erase && write` -- two gates, not one. The note rebuilt only
the first: it said to run the erase and the write once the transfer had
reported success, which leaves a reader whose erase failed writing into
flash that was never cleared. On NOR that is a write over unerased
bytes; where the block starts at 0x0 it is a write over the bootloader.

The rule is sequential, so say that: run each part only after the part
before it has reported success. That covers both gates and is shorter.

Rewritten in all ten locales. The new helper test pins the sequential
form and rejects the transfer-only one, so this cannot come back as a
rewording; putting the old sentence back in en.yml fails it.

208 runs, 786 assertions. 31 rubocop offences across the touched files,
identical to master.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFg9PRy2T6cr983S8gran5
@openipc-ai
openipc-ai force-pushed the compound-command-fallback branch from 7b91f08 to a3f3a17 Compare August 25, 2026 12:22
@openipc-ai
openipc-ai merged commit b08954c into master Aug 25, 2026
2 checks passed
@openipc-ai
openipc-ai deleted the compound-command-fallback branch August 25, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant