Mega commit - #22
Open
swissskimmilk wants to merge 197 commits into
Open
Conversation
… CSVs (fixes Fuel Main flashing open on debug toggle in IDLE)
…im generator) + purple Simulated Data / Session Stopped status badge
…ped WindProfile + /api/drift)
…essure_limits], operator-gated live config editing with download/upload, and dead-config scrub
…try fixes, flight-dynamics graph popups
…esign dialog, trim redundant controls)
…andalone installer and bootstrap step
…e-series overlay + angle-from-vertical, square ground track, larger legible charts
…ments, identifiers, deploy configs, openrocket subdomain + auth slug, landing card)
…der backend/recovery, tools/site-climatology/reference, unified health, tests (5 frontend-coupled quarantined for Phase 3)
…tabs, unified OrkConfig design + single header design bar, recovery Inter/engineering-palette index.css, UnitsProvider, vitest
…sign (Flight Dynamics apogee + CAD structure/spent-motor mass) or typed in; lateral velocity deferred
…/Caddy subdomain, fold userdata volume + OPENROCKET_S3 into star-openrocket-api, drop landing card, delete source
…g cad/recovery slices, never throw); remove dead recovery storage.py (unused RECOVERY_S3); add legacy-blob persist test
…e (fixed-height status line) so editing a value no longer jumps the page
…nt no longer forces a triple-click)
… verbose-gated), unify verbose flag, demote per-packet/hex/debug noise to verbose, throttle OTA progress to 25%
…espace Diablo:: -> daq::)
…d by config mode byte (0-3), 1Hz buffered flush + ethernet-up announce
…only (/api/onshape, /api/build); any Berkeley login can use recovery/flight/cached models; friendly 403 in the model picker; landing copy
…strip eq/section refs from UI text
…grate the horizontal ground track (loads use resultant deployment airspeed, drift reads the track, shear lag modeled), lateral velocity at apogee on Vehicle; crosscheck drives ours+mastersheet from config wind; calm = byte-identical
…+crosscheck see the same wind), lateral velocity at apogee input + from-design toggle (RocketPy apogee velocity), design source wiring
…backend cache→GUI viewer + per-board log-mode control (0-3) + integration coverage; config editor read-only/cleanup
… GSE Abort and Emergency Abort sent SystemState.ENGINE_ABORT / GSE_ABORT / EMERGENCY_ABORT - literals 17, 18, 19 - and the engine-abort vent step sent VENT, 13. A config on any other numbering has none of those ids, so the command resolved to nothing and the button did nothing, with no error and no visible difference from a press that worked. Worse, the vent step's 13 fell through the backend's legacy naming to "Vent", which this config does define, so the sequence half-executed: it vented, then the abort itself silently went nowhere. Resolve every abort target by name from [[states]], and log an error when the config declares no such state rather than sending an id nobody claims Also removed rather than repaired: STATIC_TRANSITIONS, a 77-entry graph transcribed from PressureStateMachine.cpp and kept as a "permanent fallback so arrows are always visible" - on a renumbered rig its pairs name other states, so it showed a different rig's transitions while looking perfectly populated, and no graph beats a wrong graph. EXCLUDED_STATES and ALWAYS_REACHABLE go with it, the first being what hid a state declared at id 0. Fire is now isFireState() from config, so Extend Fire enables during the burn and the PWM page stops refusing in it; state colours key on isFire/isAbort; currentState starts null instead of asserting Idle before the rig has reported anything The suite leaned on the deleted fallback - it subscribed for transitions and never delivered any, so the graph it exercised was the enum's rather than one a rig runs. It now delivers a graph the way the backend does
…and then resolved every command against another: init() called StateMachine::loadStatesFromConfig() below both CSV loads, so state_transitions.csv was parsed while configStates() was still empty and fromName() was answering from the compiled enum - the table held Armed(2) -> Press Standby(20), while the command that arrived later resolved Press Standby to the config's 3 and was refused. Only ids identical in both numberings survived, so Idle <-> Armed worked and every other transition was rejected with "is not allowed" against a CSV that plainly allowed it. The comment above the call already said it had to happen before anything resolved a state name; it was just in the wrong place. reloadConfig() never adopted [[states]] at all, so a live reload after a rename re-parsed both CSVs against the previous list Also republish state when a transition is refused. Nothing was published on the refusal path, so a client that had moved its own display in anticipation was never corrected and went on showing a state the rig had refused to enter
…t back, so a refused transition was indistinguishable from one that worked - the rig sat in Armed while the diagram showed Press Standby. The backend command path dropped its optimistic update for exactly this reason ("real state arrives via _SEQUENCER_STATE"); this was the copy left behind in the component. The sequencer is authoritative, so wait for it
A 1 Hz startStateSnapshotPublisher that would have corrected this drift existed until aab49be, but it was only ever declared and defined - no call site - so the resync it promised never ran. Correcting on the refusal itself (see the sequencer change) is both immediate and cheaper than a poll
The test asserted the optimistic update as the contract; it now asserts the opposite, which is what makes a refusal visible
…uator roles re-sort by board then channel the way the editor now writes them on Save. Press Standby is the hub every operational state routes through, so with no edge back out of it the machine could reach it and then only move deeper
…abort/boot no longer fall through to compiled Fire/17-18-19/IDLE ids (disable + loud log instead), fromName returns UNKNOWN for an undeclared name, and the controller process now loads config states so its PWM fire-gate id matches what the sequencer publishes
…ator's per-sensor model (ratiometric physics inverse) instead of a factory PT cubic that the physics-or-nothing deploy never ships — the old path resolved to null for every abort sensor and silently broadcast zero thresholds, disabling the hardware overpressure net; unresolvable/non-physics sensors now log loudly and are omitted rather than dropped in silence, and abort_pts resolves across all PT boards
… for (wire the already-defined hasState guard into ENG/GSE/E-ABORT in TopBar and MobileDashboard) so a config-renamed or -removed abort state greys the button out instead of leaving it enabled but silently resolving to nothing
…t-procedure vent-to-safe ADC through its real model (cubic/robust/physics) by bisecting the same forward conversion the stream uses, so the board's gate matches the pressure the operator sees; it re-emits on every capture/clear/reload so a mid-session calibration goes live within a broadcast cycle; sparse cal (<3 points) falls back to physics so a loose fit can't emit a wrong gate; config_broadcast prefers this file (PSI-matched) over its inline physics fallback
… service regenerates from live cal, like cubic_calibration.json
…ll three CSVs (on blur), so a rename no longer leaves the old column and silently drops that state's actuators/transitions on load; and Save is refused when [[states]] and the CSV columns diverge or ids/names collide/empty — the sequencer resolves by name, so a mismatch was dropping states quietly
…tes]] doesn't declare instead of resolving it through the compiled SystemState enum — on a renumbered rig the enum names a different state, so a stale/foreign id could be silently rewritten into a real wrong transition; the enum fallback now stands only when the config declares no states at all
…t_pts entry too — abort_pts is keyed by role name and feeds the boards' abort thresholds, so an un-synced rename/remove silently orphaned that sensor's overpressure gate
…cker-image CI gate onto star-openrocket, and take main's firewall/auto-deploy deploy docs alongside STAR_IMAGE_TAG pinning
swissskimmilk
force-pushed
the
star-openrocket-merge
branch
from
September 7, 2026 09:38
ad9d4c1 to
ab3ce2d
Compare
…s recovery gating update follows the fold into star-openrocket/frontend/src/recovery
…signs bar was unified into the shared header on this branch, so it is outside this test's components glob and lib/gating.test.ts audits it there; main's copy still listed it and the staleness check failed on the merge
…m the stored blob — tsc forces a field to appear in the reviver but 'x: base.x' compiles fine and silently defaults on every load, and the round-trip test only pokes the two fields someone named, so a new field is default-in/default-out and passes
…es including both checkout-gating audits have never run in CI, so they were green by default; also move the summary comment back onto the summary job after the docker-image merge
…ries someone listed — the CAD half globbed components/{viewer,environment} and so skipped ui.tsx, UnitsPanel.tsx, ConfigVersions.tsx and App.tsx by accident rather than by decision, and the recovery half skipped RecoveryTab.tsx the same way; all five are now named in NOT_EDITING with a reason, which also makes re-narrowing the glob fail the exemption-staleness test
… the Vite migration, smol-toml and the config-driven [[states]] rework, so six of its sixteen items were already fixed; those are recorded with how rather than dropped, and the new audit adds the sequencer concurrency races, the heartbeat's hardcoded state-enum mapping, read_packet's stream desync, the unbounded WS broadcast and the static-analysis job that cannot fail Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QtfWvMo4RZWe9Fmidm3rCD
… broadcast queueing behind housekeeping — a thread per TCP client plus FireManager's expiry thread ran transitionTo concurrently and double-joined the actuator republish thread (confirmed under TSan: pre-fix dies in 0.2s at ~50 commands, a plain -O2 build survives 1016 because glibc tolerates it, which is worse); a mutex deadlocks here since fire_manager_.stop() joins the very thread waiting on it, so commands now go through a queue the timer posts to without waiting, ABORT goes out first instead of behind an unbounded controller connect (measured 133,348ms), and reload re-resolves [fire] and stops the republish loop before rewriting the tables it reads Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QtfWvMo4RZWe9Fmidm3rCD
…ntegration fire gate passing vacuously — both new tests were validated by building them against the pre-fix sources (concurrency dies at the same stopContinuousLoop double-join, abort ordering measures 453ms vs 0ms), the TSan job gates on exit code rather than grepping for "WARNING" because the failure is a sanitizer CHECK abort and needs setarch -R or it never starts, test_robust_ddp is finally registered with CTest after being built-but-never-run, and the harness now rewrites [controller_service].port so FIRE_START/FIRE_STOP actually reach the controller instead of being sent to 9999 while it listens on 9997 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QtfWvMo4RZWe9Fmidm3rCD
… ten items move to a dated Resolved section recording how each was fixed and what the measurements were, the ABORT and sanitizer entries narrow to the halves still open (broadcast routing; -Wall and ASan/UBSan, since only TSan landed), test_robust_ddp drops out now that it is registered, and the four line anchors my own edits shifted are re-verified Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QtfWvMo4RZWe9Fmidm3rCD
…reconnect — config now reaches a running rig at exactly one point, the deploy at session start, and the pipeline services hold what they booted with; the reload verb could never reach a live sequencer anyway (its only caller fired when the CSV was deployed, which only happens with no session active) so it was untested machinery in the safety-critical path, and deleting it also removes the reload half of 0c0dfd6; tryConnectElodin() now reuses an actuator-board snapshot from init() instead of re-parsing config.toml on every reconnect, which had the retry thread rebuilding the actuator tables from whatever was on disk when a db blip happened mid-run Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GBjJb8yqSFaBer57cAKbzT
…eploy fails, and stop board log mode reverting itself — the always-on backend cannot read config once at boot the way the session-gated services do, so readDeployedConfig() caches and deployActiveProfile() invalidates, which removes a file read + TOML parse from every /api/gui-config request, every operator command and all 24 resubscribe retries; a failed deploy at session start now aborts the start instead of warning and running the rig on stale config behind a green "session active" light; and patchBoardField() writes the active profile as well as config.toml, since board log mode is a deliberate live exception but was being silently reverted by the very next deploy Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GBjJb8yqSFaBer57cAKbzT
…, and warn that sim ignores it — the API already returned a deployed flag and a message ("Saved as draft (applies at next session start)") and the banner threw both away for a fixed "saved successfully", so an operator saving mid-session was told the change was in effect when it was not; the banner now uses the server's own wording in amber, a new one lists the profile files that differ from what is deployed via GET /api/config/profiles, and the session page notes that a simulated run uses the committed sim config rather than the profile, which otherwise reads as the draft mechanism being broken
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBjJb8yqSFaBer57cAKbzT
…, deploy, session-start and name the two exceptions — the old guide described a flight/ground split config that has not matched the profile system for months, so it documented a shape of the system that no longer exists; the rewrite says which of the three files called "config" is which, that session start is the one apply point, and that board-config broadcast and calibration are deliberate live exceptions (including that [abort_pts] and boards.*.enabled reach hardware live), plus the two traps worth knowing: the freeze is inert wherever SESSION_SERVICE_MODE is off, and sim never applies drafts; SENSOR_ASSIGNMENT_SYSTEM.md gets an aspirational banner because its MAC-hash assignment flow is not what ships Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GBjJb8yqSFaBer57cAKbzT
…oped wrong: no socket in the DAQ path bound an egress interface, which only stopped mattering because the DAQ owned its machine and started mattering again on the shared apps box (board LAN + site LAN + docker bridge); one resolver picks the interface holding the board subnet by address the way bootstrap_daq.sh already did, refusing to start on an explicit bind_ip that is not on the host or on two candidate NICs, and binding a source address rather than SO_BINDTODEVICE because the units run unprivileged with no CAP_NET_RAW; the abort now takes its destination and port from [server_heartbeat] instead of 255.255.255.255:5005, which needed no config change since every profile already sets it; and the firmware turns out not to act on ABORT at all (ENABLE_ALL_STATE_TRANSITIONS is false everywhere, which also makes the [abort_pts] trips unreachable) while CLEAR_ABORT is consumed and never sent, so the entry is downgraded and that is filed instead Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GBjJb8yqSFaBer57cAKbzT
…in the same calibration page
…tate was 0 forever — it consumes [0x50,0x00] and called subscribe_stream(), whose doc comment promised "all stream data" but which actually sent the calibration service's list (480 raw sensor ids from a hardcoded boards 1-8 x channels 1-10 guess, plus the calibration command table); measured with the real binaries against a real elodin-db, four transitions produced 101 heartbeats all carrying 0, at 0% CPU with the Elodin thread parked in recv() and nothing logged, and it now reports {0,1}; subscribe_stream() is deleted and every consumer names what it reads via raw_sensor_tables()/calibrated_sensor_tables() built from the boards actually in config, the way ControllerService already did; also fixes the read path this was originally filed against, which was real but never firing — an oversized packet abandoned its payload in the socket and desynced the stream permanently, the bound was off by four so packet_len == max_len overran the caller's buffer (EFAULT against a guard page), read_exact returned TIMEOUT after a partial read for the same desync, and only the write path ever cleared connected_ so a dead socket read as live; the 256-byte buffer this file blamed was never overflowed, nothing emits over 179 bytes, and that claim is withdrawn
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBjJb8yqSFaBer57cAKbzT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Another mega commit. Change includes
STAR ORK
DAQ Server
DAQ Run viewer
Firmware