-
Notifications
You must be signed in to change notification settings - Fork 1
STOP reliability + Freeze Live Graph after Set (PR1+PR2) #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
… STOP: retries + polling pause + cleanup; UI debounce + timing logs\n- Graph: freeze on STOP/auto-complete; Resume Live Graph control; state guards\n- Verified with Chrome DevTools MCP against working hardware (no console errors)\n\nDocs: plan consolidated; follow-up appendix retained
|
@codex - review this PR keeping in mind repo guidance @AGENTS.MD |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } catch (error) { | ||
| console.error("Stop workout error:", error); | ||
| this.addLogEntry(`Failed to stop workout: ${error.message}`, "error"); | ||
| alert(`Failed to stop workout: ${error.message}`); | ||
| } finally { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UI remains ‘Connected’ when STOP fallback disconnects device
When STOP exhausts its retries, sendStopCommand calls disconnect() as a safety fallback, but the catch block in stopWorkout() only logs the error and re‑enables the button. Because no call to updateConnectionStatus(false) is made (and no disconnect event propagates to the app), the UI continues to show a connected state even though the GATT session was forcibly closed. A subsequent Start will fail with opaque GATT errors. The catch block should synchronize the UI with the disconnect or surface a reconnect affordance.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@claude Review this PR and the findings by codex.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude finished @Allmight97's task —— View job
PR Review Complete
I've analyzed this PR and Codex's findings. Here's my comprehensive review:
Summary
This PR delivers meaningful improvements to STOP reliability and graph UX. The implementation is generally solid and follows repo conventions, but Codex's P1 finding is valid and should be addressed before merge.
Critical Issue: UI State Inconsistency (P1)
Codex is correct. When STOP retries are exhausted, device.sendStopCommand() calls disconnect() as a safety fallback (device.js:271), but the UI is never updated to reflect this disconnection.
The problem:
device.disconnect()is called in the catch block (device.js:271)- This closes the GATT session via
device.gatt.disconnect()(device.js:544) - However, the
gattserverdisconnectedevent listener (device.js:77) only callsdevice.handleDisconnect(), which updates internal device state - No call to
app.updateConnectionStatus(false)occurs, so the UI continues showing "Connected" - The catch block in
app.stopWorkout()(app.js:599-602) only logs the error and re-enables the STOP button
Impact:
- User sees "Connected" but cannot start new workouts (GATT errors)
- No affordance to reconnect
- Violates Fail Fast principle (AGENTS.md:10) - the error is logged but the UI masks the true system state
Fix required:
Add UI synchronization in app.stopWorkout() catch block (app.js:599-602):
} catch (error) {
console.error("Stop workout error:", error);
this.addLogEntry(`Failed to stop workout: ${error.message}`, "error");
// Sync UI if disconnect fallback was triggered
if (!this.device.isConnected) {
this.updateConnectionStatus(false);
this.addLogEntry("Device disconnected as safety fallback - please reconnect", "error");
}
alert(`Failed to stop workout: ${error.message}`);
}Code Quality Assessment
Core Principles Rating (per AGENTS.md:8-12):
| Principle | Rating | Notes |
|---|---|---|
| Orthogonality | 4/5 | STOP logic cleanly separated; graph freeze is independent |
| Separation of Concerns | 4/5 | Device layer handles BLE; app layer handles UI state |
| High Cohesion | 4/5 | Related functions grouped logically |
| Loose Coupling | 3/5 | UI-device coupling via isConnected check could be cleaner |
| DRY | 4/5 | Good reuse; minor: freeze messages could be constants |
| KISS | 4/5 | Solutions are appropriately simple |
| YAGNI | 5/5 | No over-engineering; minimal scope |
| Fail Fast | 2/5 | Degraded by P1 issue - silent failure on disconnect |
Strengths
-
STOP Reliability (device.js:224-282)
- ✅ Idempotent via
_isStoppingflag (device.js:229-232) - ✅ Pauses polling to prevent queue contention (device.js:237-238)
- ✅ Retry logic with backoff for transient BLE errors (device.js:243-267)
- ✅ Disconnect fallback on exhaustion (device.js:270-271)
- ✅ Proper finally block ensures flag reset (device.js:279-281)
- ✅ Good logging for diagnostics (device.js:244, 257-260, 269)
- ✅ Idempotent via
-
Graph Freeze (app.js:85-120)
- ✅ Guards prevent double-freeze (app.js:86-88)
- ✅ Deep copy of history preserves snapshot (app.js:95)
- ✅ Silent resume option for automated flows (app.js:105, 666, 734)
- ✅ State guards in
updateLiveStatsprevent polluting frozen data (app.js:345-347)
-
UI/UX
- ✅ Resume button hidden by default, shown when frozen (app.js:72-83, 96)
- ✅ Informative freeze messages (app.js:98-100, 461-463, 596-598)
- ✅ Numeric stats remain live even when graph is frozen (good design choice)
-
Code Hygiene
- ✅ Minimal diff (194 additions, 12 deletions)
- ✅ Follows established patterns and naming conventions
- ✅ All functions within complexity limits (AGENTS.md:118)
- ✅ No external dependencies added
Issues & Recommendations
1. P1: UI State Sync (Critical - see above)
Must fix before merge
2. STOP Button Re-enable Timing (app.js:607-609)
The button is re-enabled in the finally block even if disconnect fallback occurred. If the device is disconnected, the STOP button shouldn't be enabled.
Recommendation:
finally {
const duration = Date.now() - startedAt;
this.addLogEntry(`STOP flow completed in ${duration}ms`, "info");
if (stopBtn && this.device.isConnected) {
stopBtn.disabled = false;
}
this._isStopping = false;
}3. Resume Button Accessibility (index.html:756-763)
The Resume Live Graph button uses class="secondary hidden" but sets disabled: false in both show/hide states (app.js:78, 81). This is inconsistent.
Recommendation:
Use disabled state consistently or remove the disabled attribute entirely (rely on hidden class).
4. Race Condition: Auto-Complete + Manual STOP
If the user clicks STOP at the exact moment target reps are reached (app.js:528-534), both completeWorkout() and stopWorkout() will be called. While _isStopping guards the device call, both flows call completeWorkout() which will double-add to history.
Impact: Low probability but data integrity issue
Recommendation:
Add a _isCompleting guard similar to _isStopping, or check if currentWorkout is null before adding to history in completeWorkout().
5. Disconnect Event Propagation
The gattserverdisconnected event listener (device.js:77) never notifies the app layer. This is why the P1 issue exists.
Recommendation:
Add a callback mechanism:
// In device.js constructor
this.onDisconnect = null;
// In gattserverdisconnected listener (device.js:77-80)
this.device.addEventListener("gattserverdisconnected", () => {
this.log("Device disconnected", "error");
this.handleDisconnect();
if (this.onDisconnect) {
this.onDisconnect();
}
});
// In app.js connect() after device.connect()
this.device.onDisconnect = () => {
this.updateConnectionStatus(false);
this.addLogEntry("Device disconnected unexpectedly", "error");
};This follows the existing pattern with onLog (device.js:34, app.js:27-29).
Testing & Verification Notes
✅ Verified with Chrome DevTools MCP against working hardware (per PR description)
✅ No console errors reported
✅ STOP flow timing logged and completed cleanly
However: The P1 scenario (STOP retry exhaustion → disconnect fallback) wasn't tested, as it requires simulating 3 consecutive BLE failures. This edge case needs hardware validation after the fix.
Safety Considerations
Per AGENTS.md:86, "The STOP control must always succeed" - this PR improves that guarantee significantly:
- Retries handle transient failures
- Disconnect fallback ensures physical safety if BLE is unresponsive
- But the UI issue undermines user confidence and creates confusion
The implementation is fundamentally sound; the P1 issue is an integration gap, not a logic flaw.
Verdict
Request Changes - Address P1 issue before merge. Issue #2 (button re-enable) is strongly recommended. Issues #3-5 are nice-to-have improvements.
The core implementation is excellent work that materially improves STOP reliability and addresses the reported UX concern with the graph. Once the UI synchronization issue is fixed, this will be a solid addition to the codebase.
Alignment with AGENTS.md:
- ✅ Minimal diff (AGENTS.md:103)
- ✅ Follows established patterns (AGENTS.md:104)
- ✅ Hardware tested (AGENTS.md:164)
- ✅ Safety-focused (AGENTS.md:86, 176)
⚠️ Fail Fast needs fixing (AGENTS.md:10)
- P1 (Critical): Add onDisconnect callback to sync UI when STOP fallback disconnects - P2: Only re-enable STOP button if device still connected - P3: Remove redundant disabled attribute from Resume button - P4: Add _isCompleting guard to prevent double-add race condition - P5: Implement disconnect event propagation to app layer All review feedback from chatgpt-codex-connector and claude bots addressed.
- Implemented onDisconnect callback to ensure UI updates when STOP fallback disconnects. - Adjusted STOP button re-enabling logic to only activate if the device remains connected. - Removed unnecessary disabled attribute from Resume button for cleaner UI. - Added _isCompleting guard to prevent race conditions during double-add scenarios. - Propagated disconnect events to the app layer for better state management. These changes improve user experience and reliability based on recent feedback.
Code Review: PR #1 - STOP Reliability + Freeze Live GraphOverall Assessment: Strong implementation that delivers on both PR1 and PR2 objectives with solid engineering practices. The changes are well-scoped, maintainable, and align with project conventions. StrengthsArchitecture & Design (4/5)
Code Quality (4/5)
Documentation & Logging (5/5)
Issues & Recommendations🔴 Critical1. Race condition in
// In completeWorkout():
try {
// ... existing code ...
this.freezeGraphSnapshot(
"Workout completed – graph frozen. Click Resume Live Graph to review the last set."
);
} finally {
this._isCompleting = false;
}
// In stopWorkout():
// Remove the freezeGraphSnapshot call at line 611-613🟡 High Priority2. Missing listener cleanup on disconnect (device.js:536-545)
handleDisconnect() {
this.isConnected = false;
this.stopPropertyPolling();
this.stopMonitorPolling();
this.propertyListeners = [];
this.repListeners = [];
this.monitorListeners = [];
// ... rest of existing code
}3. STOP button re-enable timing issue (app.js:631-633)
if (stopBtn) {
stopBtn.disabled = !this.device.isConnected;
}🟠 Medium Priority4. Potential memory leak in frozen history (app.js:102)
5. Inconsistent error message regex (device.js:263)
6. Missing ARIA for Resume Graph button (index.html:756-763)
Performance Considerations✅ Excellent
|
|
@codex - changes made, re-audit PR. |
PR Review: STOP reliability + Freeze Live Graph after Set (PR1+PR2)SummaryThis PR addresses two critical user-reported issues: intermittent STOP failures and graph data disappearing after set completion. The implementation adds retry logic with BLE cleanup and a graph freeze/resume feature. Code Quality & Best Practices✅ Strengths
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (stopBtn && this.device.isConnected) { | ||
| stopBtn.disabled = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-enable STOP button after safety disconnect
The STOP handler disables the button and only re-enables it when this.device.isConnected is still true. When sendStopCommand() fails and performs its disconnect fallback, the DOM element remains disabled and there is no subsequent code that flips it back on when the user reconnects. After recovering from such an error the user cannot issue another STOP without refreshing the page, which undermines the primary safety control. Consider clearing the disabled state on reconnect or in the failure path regardless of isConnected.
Useful? React with 👍 / 👎.
Intent
Deliver the first two items from the consolidated change plan:
Tagging @codex as the reviewing agent.
What / Why / Impact
STOP reliability (PR1)
Freeze chart after set (PR2)
Changes
Verification
Console and page snapshots captured during monitoring showed clean flows and expected logs.
Risk / Rollback
Notes