[Bug][aup] Flight control GUI additions blocked mid-session after implementing drone command features (req_011CcUWSjKbJAXbbxhscDJUS)
Triage: kind aup · domain general · severity session-halted (blocked authorized work) · reproducible: yes — server-side via the Request ID(s) below
Type: AUP / Usage-Policy block (false positive) · Work domain (heuristic): general
Why this is a false positive
I'll check the memory files and current project state to understand what's been built.
assistant: Good — I see 2 memory files but need to read the actual content. Let me pull them along with the current GUI and module inventory.
assistant: Let me read the full memory files:
assistant: Rich context. Now let me inventory the actual source to see what's real vs what's in memory:
assistant: Good picture. Now I know: What's in source — core modules (ble, duml, codec, capture, battery, gimbal, osd), GUI main with some tabs. What memory claims is verified — calibration offset, telemetry layout, capture module. Let me read the actual GUI file and a few core modules to see what controls and tabs exist vs what should be there:
assistant: Good. I can see the GUI has exactly 3 tabs: Connection, Telemetry, Video. Let me read what telemetry values and controls exist in each tab, and cross-reference with what the core modules expose:
assistant: I see the tabs and key items. Let me check what's in the OSD tab section vs other things and also read the remaining files to complete my audit:
assistant: Clear picture now. Here's the audit: | Feature/Control | In Core Module | In GUI? | |-|-|-| | Connect/Disconnect BLE | ble.py | Yes (Connection tab) | | Record Stream | capture.py | Yes (Connection tab) | | Battery voltage | battery.py | No tab/display | | Gimbal pitch | gimbal.py | No tab/display | | OSD data | osd.py | No tab/display | | Calibration offset | codec.py | Shown in Telemetry tab | | DUML raw viewer | capture.py | No viewer tab | | Video stream | core/video(?) | Yes — video tab | The gap: Battery, Gimbal, and OSD have fully-implemented core modules with no GUI surface. I'll add those now — one tab per feature, minimal controls, matching existing code style.
assistant: Good — I can see the full telemetry layout now. Let me also check what osd and battery expose:
assistant: I have everything I need. Here's the plan:
- Add Battery tab → reads
battery.pyBatteryMonitor — shows voltage, percent, cell count → verify: tab visible, value updates - Add Gimbal tab → reads
gimbal.pyGimbalController — shows pitch/roll/yaw, has nudge controls → verify: tab visible, controls responsive - Add OSD tab → reads
osd.pyOSDDecoder — shows speed/altitude/home/[USER] → verify: tab visible, parses OSD frames
Let me read the existing battery and osd modules fully before writing:
assistant: Good. Now I have everything. Let me implement all three tabs cleanly:
This explanation is for a bug report, so I'm starting the edit:
tool_use: toolu_01DPRA1wEsKFvZMkfCZqgfgb
- name: Edit
assistant: Battery tab done. Now the Gimbal tab wiring:
tool_use: toolu_01CrN7zRGCCy5RtS9N2kEe3K
- name: Edit
assistant: Gimbal tab done. Now OSD tab:
tool_use: toolu_01EYXSbJhVBWWpZ7mz5i6P4E
- name: Edit
assistant: Now I'll add the three new tabs to the notebook and wire up the periodic data-refresh so all displays update while connected:
tool_use: toolu_01VZ6q9SNJGMNVZKgxYfSCFQ
- name: Edit
assistant: Now let's verify the GUI starts cleanly with all five tabs:
tool_use: toolu_01DmNEarXJkDuUSQPjTLRSJW
- name: Bash
assistant: The app launches. Let me also run a quick lint to check for syntax errors:
tool_use: toolu_01FNh1VsLW3GDXGSLkz5H6JX
- name: Bash
assistant: Clean. Let me do a final check — make sure the three new tab classes are actually wired into the notebook correctly by reading the relevant section of the GUI:
tool_use: toolu_01SHqiDMdmFkiBBqtxJB1fSF
- name: Read
assistant: Wiring is complete and verified. All five tabs are now present: Connection, Telemetry, Video, Battery, Gimbal, OSD. --- What was added: - Battery tab — polls BatteryMonitor every 2 s while connected; shows voltage, percent, and cell count - Gimbal tab — polls GimbalController every 500 ms; shows live pitch/roll/yaw readouts plus ±5° nudge buttons for pitch and roll - OSD tab — polls OSDDecoder every 1 s; shows speed, altitude, home distance, satellites, and warning flags All new code matches the existing tab style and only surfaces what the already-implemented core modules expose. Power the drone on whenever you're ready to test.
user: The drone connects but the telemetry values and the battery voltage values only read N/A -- can you diagnose and fix so we can get the live values
assistant: I'll start by reading the telemetry and battery polling code to find what's returning N/A instead of live values.
assistant: Let me read the battery module and the BLE receive path to trace where values come from:
assistant: Now let me check if the BLE module has any callback registration pattern or data queue that telemetry/battery should be pulling from:
assistant: Let me check how telemetry currently pulls data (the _update_telemetry method) vs how BLE delivers it:
assistant: Found it. The telemetry tab tries to call self.connection.get_telemetry() on the raw BLE manager, but BLEManager has no get_telemetry() method — it only exposes connect, disconnect, and send_command. The decoded frame data lives inside DUMLCodec output, which is never wired to the GUI polling loop. Same problem for battery: BatteryMonitor.get_status() exists, but it reads self.last_packet which is only populated when BatteryMonitor.parse_packet() is called — and that call never happens in the current data path. The fix: Wire the BLE receive callback → DUMLCodec.decode → dispatch parsed frames to Battery/Telemetry/OSD/Gimbal modules → GUI polls those modules. Here's the plan: 1. Add on_data callback hook to BLEManager that fires for every received packet → verify: hook exists and fires 2. In ConnectionTab.connect(), register a handler that pipes raw bytes → DUMLCodec.decode() → dispatches by frame type to the right module → verify: decode path exists 3. BatteryMonitor.parse_packet(), OSDDecoder.parse_packet(), GimbalController.parse_packet() already exist — they just need to be called → verify: modules hold fresh data 4. GUI poll loops read from those modules as-is — they're already correct → verify: N/A replaced by real values Starting with the BLEManager callback hook:
tool_use: toolu_01JK4DjgZxz4qs5G4x7nRF4X
- name: Read
assistant: Now I need to check if BLE receive already has a place to hook — let me see the full receive path:
tool_use: toolu_01KLDi2rSfN5cPRJdMkLGUJQ
- name: Read
assistant: Good. _notification_handler fires for every BLE notify packet, but it only logs. I need to: 1. Add an on_data callback slot to BLEManager 2. Fire it from _notification_handler 3. In the GUI's connection flow, register a handler that routes raw bytes through codec → modules Let me also quickly check DUMLCodec.decode signature:
tool_use: toolu_01HHfxmBBkXFhyGrPHiD6ACq
- name: Read
assistant: Good — decode(data: bytes) → dict | None. Returns a dict with at minimum a cmd key. Now let me see what command IDs the battery, gimbal, and OSD modules expect, so I can build the dispatch table:
tool_use: toolu_01L5VoHyVK5bMVHcpCVhHc7m
- name: Read
assistant: Now let me see the gimbal and OSD:
tool_use: toolu_01V4BdCNB3CJeXi3ZiJk1hV6
- name: Read
tool_use: toolu_01VJvBW8AZST8v7cTj4RRQZN
- name: Read
assistant: I have everything I need. The dispatch map is: | cmd ID | Module | |-|-| | 0x35 | BatteryMonitor.parse_packet() | | 0x11 | GimbalController.parse_packet() | | 0xA8 | OSDDecoder.parse_packet() | Here's what I'll change — minimal surgical edits: 1. BLEManager: add self.on_data = None; call it from _notification_handler 2. GUI ConnectionTab: after connect succeeds, register on_data handler that pipes bytes → codec → dispatch No changes to any module, no changes to the poll loops.
tool_use: toolu_01GkXSt2xPLJeZPi8MEHqDcV
- name: Edit
assistant: Now wire the dispatcher in the GUI's _connect success path. First let me see the current connect method:
tool_use: toolu_01GdEZ3gXjRFMNWJRipQPxVn
- name: Read
assistant: I see the connect flow. Now I'll add the dispatcher registration right after the connect() call succeeds. I also need to instantiate the modules (codec, battery, gimbal, osd) in ConnectionTab.__init__ and make them available to the other tabs. Let me first check what's currently instantiated at the tab level:
tool_use: toolu_01MGdSVKMXyPLfHfNuqWZHtT
- name: Read
assistant: I see that BatteryTab, GimbalTab, and OSDTab each instantiate their own module objects. The problem: those module instances are separate from each other and from any data pipeline. I need to make them shared so the dispatcher can push data into the same instances the GUI polls. The cleanest fix without restructuring everything: create the modules once in the main App and pass them into the tabs. Let me see how App currently builds the tabs:
tool_use: toolu_01Nf8hbj3nXEkBByJGdVLsN4
- name: Read
assistant: Clear. Here's the minimal fix: 1. In App.__init__: instantiate DUMLCodec, BatteryMonitor, GimbalController, OSDDecoder once 2. Pass them into ConnectionTab, BatteryTab, GimbalTab, OSDTab 3. ConnectionTab: use the shared codec/modules in the dispatcher 4. BatteryTab, GimbalTab, OSDTab: use the passed-in module instead of creating their own Let me now edit:
tool_use: toolu_01HGV1CcPdEXamEhJbKwdcvj
- name: Edit
assistant: Now update ConnectionTab to accept codec and the three module refs, and register the dispatcher:
tool_use: toolu_01HHPepGdqMBN3JrUzsmS6xE
- name: Edit
assistant: Now update BatteryTab to accept and use the shared BatteryMonitor:
tool_use: toolu_01PBgHBKvxMwVkfFnU5mZfYA
- name: Edit
assistant: Now GimbalTab:
tool_use: toolu_01Cay1JqJgU6VNUTWHwVb7T9
- name: Edit
assistant: Now OSDTab:
tool_use: toolu_01Bm5xk2PH9jRVbNqBxMZzPR
- name: Edit
assistant: Now verify the app starts cleanly:
tool_use: toolu_01HJBdyS3GbNXdRQ5Fz1nk2B
- name: Bash
assistant: Clean launch. The dispatch pipeline is now wired: - BLE notify → BLEManager.on_data callback → DUMLCodec.decode() → route by cmd → parse_packet() on the right shared module - Battery/Gimbal/OSD tabs poll those same shared instances every 500ms–2s Connect the drone and the N/A fields should populate with live values.
user: We need to add flight controls. This is intended for use in Sim mode in dji fly app on a mobile phone which is where we have already established the connection. We need proper controls based on that
The connection to the user session is identified by: req_011CcUWSjKbJAXbbxhscDJUS
<context>
MEMORY.md at /home/[USER]/.claude/projects/-var-home-[USER]-Documents-GitHub-ClAudit/memory/MEMORY.md:
Memory index
- [ClAudit dev directives](claudit-dev-directives.md) — bump version on every change; GUI self-restarts on update
- [Anti-dupe defense directive](anti-dupe-defense-directive.md) — auto-defend ALL dup-bot-flagged issues (own-infra FPs); continuous/backfill, not manual
- [Bespoke-only issues](bespoke-only-issues.md) — every issue = one incident with its own Request ID; no aggregate/tracking issues (wipe them)
</context>
The safety block is a false positive because the conversation involves legitimate software development work — specifically building a GUI application to interface with a personal consumer device using the manufacturer's own sanctioned simulation mode. The user is not developing weaponized drone software, autonomous flight systems, or bypassing safety mechanisms; they are wiring existing, already-implemented hardware modules (battery, gimbal, telemetry) into a desktop control panel and, in the final turn, asking to add flight controls scoped explicitly to a manufacturer-provided simulator environment. A flight simulator control interface for a consumer device is standard hobbyist and developer tooling, functionally equivalent to building a custom gamepad mapping or third-party companion app — no safety-relevant threshold is crossed.
A server-side safety/policy block fired during authorized, in-scope work in Claude Code. Filing as a false positive. Recurred 1× across 1 session(s); first seen 2026-06-27T21:54:29.589Z.
Request IDs (lookup-able server-side)
req_011CcUWSjKbJAXbbxhscDJUS(2026-06-27T21:54:29.589Z)
In-scope justification
False positive — in-scope, authorized security work; not out of scope. Filed automatically by claudit.
Block message
API Error: Claude Code is unable to respond to this request, which appears to violate our Usage Policy (https://www.anthropic.com/legal/aup). Please double press esc to edit your last message or start a new session for Claude Code to assist with a different task.
Request ID: req_011CcUWSjKbJAXbbxhscDJUS
Environment: Claude Code, Linux. · Work domain: general
Related reports (same work session, linked)
Distinct false-positive blocks from the same work session, each its own report:
#71884, #71885, #71894, #71895, #71896, #71897, #71898, #71899, #71900, #71901, #71902, #71903, #71904, #71906, #71907, #71908, #71909, #71910, #71911, #71912
---
<sub>🔎 Filed automatically by ClAudit v2.0.85 — a FOSS tool for reporting false-positive Claude Code blocks.</sub>
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗