Skip to content

CI: add retry/cache for Gleam deps; input_otp: support multi-char/paste across slots and remove maxlength#12

Open
bbopen wants to merge 1 commit intomainfrom
codex/locate-major-bug-and-audit-tests-d19p6u
Open

CI: add retry/cache for Gleam deps; input_otp: support multi-char/paste across slots and remove maxlength#12
bbopen wants to merge 1 commit intomainfrom
codex/locate-major-bug-and-audit-tests-d19p6u

Conversation

@bbopen
Copy link
Owner

@bbopen bbopen commented Mar 11, 2026

Motivation

  • Make CI more robust and faster by retrying flaky gleam deps download invocations and caching Gleam artifacts to avoid repeated downloads.
  • Improve the OTP input component to correctly handle multi-character inputs (e.g. paste) that should populate multiple slots.

Description

  • Add scripts/ci/retry.sh to provide a generic retry wrapper and use it to run gleam deps download in both the GitHub Actions workflow and scripts/check.sh via bash scripts/ci/retry.sh 12 10 gleam deps download.
  • Add a GitHub Actions cache step for Gleam artifacts with path entries ~/.cache/gleam and weft_lustre_ui/build/packages and a content-hash key using weft_lustre_ui/gleam.toml and weft_lustre_ui/manifest.toml.
  • Implement replace_slots_from_index and input_otp_apply_slot_input in src/weft_lustre_ui/headless/input_otp.gleam to fan out pasted/multi-character input across subsequent slots and remove the old single-grapheme helper.
  • Remove the `maxlength=

Codex Task

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

📝 Walkthrough

Walkthrough

The PR adds dependency caching and retry logic to CI workflows, introduces a generic retry script for transient failures, and refactors OTP input handling to support multi-character pasted inputs while removing the maxlength constraint.

Changes

Cohort / File(s) Summary
CI Infrastructure
.github/workflows/test.yml, scripts/check.sh, scripts/ci/retry.sh
Adds Gleam dependency caching using actions/cache@v4 with manifest-based keys, and implements a generic retry mechanism (3 arguments: attempts, sleep duration, command) to tolerate transient failures in dependency resolution with configurable backoff.
OTP Input Component
src/weft_lustre_ui/headless/input_otp.gleam
Refactors slot input handling by replacing first_grapheme_or_empty with replace_slots_from_index to support multi-character inputs (e.g., pasted values), introduces public helper input_otp_apply_slot_input for input processing, and removes maxlength attribute from rendered slots.
OTP Tests
test/input_otp_test.gleam
Adds test case validating multi-character pasted input distribution across slots and asserts absence of maxlength attribute in rendered output.

Poem

🐰 A script retries when the network frowns,
While OTP slots now accept crowns
Of pasted digits, no length to bind—
Dependencies cached, peace of mind! ✨

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main changes: CI improvements (retry/cache for Gleam deps) and OTP input enhancements (multi-char/paste support and maxlength removal).
Description check ✅ Passed The description is comprehensive and directly relates to the changeset, detailing the motivation and implementation approach for both the CI robustness improvements and OTP component enhancements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/locate-major-bug-and-audit-tests-d19p6u

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

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
.github/workflows/test.yml (1)

72-79: ⚠️ Potential issue | 🟠 Major

Remove the duplicate gleam deps download before scripts/check.sh.

Line 73 already does the same retry-wrapped download that scripts/check.sh performs at Line 9, so the hex-semver path hits dependency resolution twice. That adds avoidable CI latency and can still fail on the second call after the first one succeeded.

Suggested simplification
-      - if: steps.hex_deps.outputs.deps_ready == 'true'
-        run: bash scripts/ci/retry.sh 12 10 gleam deps download
-        working-directory: weft_lustre_ui
       - if: steps.hex_deps.outputs.deps_ready == 'true'
         run: |
           echo "Dependency mode: hex semver (full checks)"
           bash scripts/check.sh
         working-directory: weft_lustre_ui
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/test.yml around lines 72 - 79, Duplicate dependency
download is invoked: the retry-wrapped "bash scripts/ci/retry.sh 12 10 gleam
deps download" call appears before the hex-semver path which then calls
scripts/check.sh that itself runs the same download; remove the redundant
pre-call so dependency resolution runs only inside scripts/check.sh. Edit the
workflow block guarded by steps.hex_deps.outputs.deps_ready == 'true' and delete
the standalone retry.sh invocation (the step that runs "bash scripts/ci/retry.sh
12 10 gleam deps download") so the subsequent step that echoes "Dependency mode:
hex semver (full checks)" and runs "bash scripts/check.sh" is the sole
dependency-resolution path. Ensure the if condition
(steps.hex_deps.outputs.deps_ready == 'true') remains on the remaining step so
behavior and gating are unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/weft_lustre_ui/headless/input_otp.gleam`:
- Around line 145-147: The code calls the non-existent list.at(values, offset);
replace that with a supported pattern using list.drop and pattern matching: drop
the first offset elements from values (list.drop(values, offset)) and then
case-match on the result ([] -> char; [value | _] -> value). Update the branch
that references list.at to use list.drop(values, offset) and the pattern match
so values, offset and char are returned correctly when present or fall back to
char when empty.

---

Outside diff comments:
In @.github/workflows/test.yml:
- Around line 72-79: Duplicate dependency download is invoked: the retry-wrapped
"bash scripts/ci/retry.sh 12 10 gleam deps download" call appears before the
hex-semver path which then calls scripts/check.sh that itself runs the same
download; remove the redundant pre-call so dependency resolution runs only
inside scripts/check.sh. Edit the workflow block guarded by
steps.hex_deps.outputs.deps_ready == 'true' and delete the standalone retry.sh
invocation (the step that runs "bash scripts/ci/retry.sh 12 10 gleam deps
download") so the subsequent step that echoes "Dependency mode: hex semver (full
checks)" and runs "bash scripts/check.sh" is the sole dependency-resolution
path. Ensure the if condition (steps.hex_deps.outputs.deps_ready == 'true')
remains on the remaining step so behavior and gating are unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9aaefae5-6cbf-4ce7-9e6c-5a099c00f945

📥 Commits

Reviewing files that changed from the base of the PR and between 7494699 and fcd6e87.

📒 Files selected for processing (5)
  • .github/workflows/test.yml
  • scripts/check.sh
  • scripts/ci/retry.sh
  • src/weft_lustre_ui/headless/input_otp.gleam
  • test/input_otp_test.gleam
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: claude-review
🧰 Additional context used
🪛 GitHub Actions: test
src/weft_lustre_ui/headless/input_otp.gleam

[error] 145-145: Gleam compile error: Unknown module value. The expression 'list.at(values, offset)' is invalid because the module 'gleam/list' does not have an 'at' function. Did you mean 'last'?

🔇 Additional comments (4)
scripts/ci/retry.sh (1)

5-38: Nice: this stays generic and safely preserves argv boundaries.

The argument validation is clear, and command=("$@") avoids word-splitting bugs when retrying commands with spaces or flags.

scripts/check.sh (1)

9-9: Good place for the retry wrapper.

Keeping the dependency bootstrap inside scripts/check.sh makes local and CI full-check runs follow the same path.

.github/workflows/test.yml (1)

23-31: The cache key is scoped to both Gleam manifests.

That is the right granularity for dependency cache reuse while still invalidating on dependency graph changes.

test/input_otp_test.gleam (1)

33-43: Good regression coverage for paste fan-out and removed maxlength.

These assertions exercise both user-visible OTP changes introduced by this PR.

Also applies to: 69-70

Comment on lines +145 to +147
case list.at(values, offset) {
Ok(value) -> value
Error(Nil) -> char
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Does the official Gleam standard library documentation for gleam/listprovide a function namedat in Gleam 1.14.x? If not, what supported pattern should be used to read the nth element of a list?

💡 Result:

No. In the official gleam_stdlib docs for gleam/list (the standard library used with modern Gleam releases, including 1.14.x), there is no list.at function; instead the module provides list.drop and list.first. [1][2]

Supported pattern to read the nth element (0-based):

import gleam/list

pub fn nth(list: List(a), n: Int) -> Result(a, Nil) {
  list
  |> list.drop(up_to: n)
  |> list.first
}
  • drop(up_to: n) removes the first n elements. [2]
  • first returns the first element (or Error(Nil) if empty). [1]

🏁 Script executed:

wc -l src/weft_lustre_ui/headless/input_otp.gleam

Repository: bbopen/weft_lustre_ui

Length of output: 112


🏁 Script executed:

sed -n '140,150p' src/weft_lustre_ui/headless/input_otp.gleam

Repository: bbopen/weft_lustre_ui

Length of output: 310


list.at is not a valid gleam/list API.

Line 145 uses list.at(values, offset), which does not exist in the standard library and causes a compile failure. The OTP paste feature cannot build with this code. Switch to a supported list access pattern.

Suggested fix
-        case list.at(values, offset) {
-          Ok(value) -> value
-          Error(Nil) -> char
-        }
+        case values |> list.drop(up_to: offset) |> list.first {
+          Ok(value) -> value
+          Error(Nil) -> char
+        }
📝 Committable suggestion

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

Suggested change
case list.at(values, offset) {
Ok(value) -> value
Error(Nil) -> char
case values |> list.drop(up_to: offset) |> list.first {
Ok(value) -> value
Error(Nil) -> char
}
🧰 Tools
🪛 GitHub Actions: test

[error] 145-145: Gleam compile error: Unknown module value. The expression 'list.at(values, offset)' is invalid because the module 'gleam/list' does not have an 'at' function. Did you mean 'last'?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/weft_lustre_ui/headless/input_otp.gleam` around lines 145 - 147, The code
calls the non-existent list.at(values, offset); replace that with a supported
pattern using list.drop and pattern matching: drop the first offset elements
from values (list.drop(values, offset)) and then case-match on the result ([] ->
char; [value | _] -> value). Update the branch that references list.at to use
list.drop(values, offset) and the pattern match so values, offset and char are
returned correctly when present or fall back to char when empty.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant