fix: derive activity from observed print state, not equipment_status - #1
Open
cyrilcaoyang wants to merge 1 commit into
Open
fix: derive activity from observed print state, not equipment_status#1cyrilcaoyang wants to merge 1 commit into
cyrilcaoyang wants to merge 1 commit into
Conversation
STATUS_SPEC §2.3 requires `activity` to come from observed hardware state and forbids computing it from `equipment_status` — the v1.2 migration did exactly that (`"idle" if state == "ready" else "unknown"`), so both live printers reported `busy` with `activity: "unknown"` while printing, which also breaks the §2.3 invariant `busy ⇒ running`. Map the printer's own gcode state on its own axis instead. PAUSE counts as running: the job is in flight and the printer cannot take another one, and that keeps `busy ⇒ running` true (the sub-state stays legible in `components["print_job"]` and `message`). FAILED is idle — the job stopped, and §2.3 permits any activity under `error`. MQTT down, telemetry not yet arrived, stale telemetry, and a monitor exception all yield `unknown` rather than a stale idle/running. `activity_since` was `now` on every status build, so it reset on every poll and could never yield a duration. The transition is now detected in the background poll (routes stay pure cache reads, per AGENTS.md), and it is null whenever the transition itself was never observed — a restart mid-print, or telemetry that went stale and recovered. The span began before this service could see it, so stamping first-observation time would report a far-too-short duration for exactly the in-progress operation the field exists to measure. `activity` is evaluated at request time so it cannot contradict `equipment_status` within one response (staleness is the only input that moves between polls); `activity_since` is emitted only when the request-time answer matches the tracked one. README documents the primary operation (running a print job) and the state mapping, closing that §9 v1.2 checklist item. Also fixes the ruff I001 the migration commit left in models.py. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Why
The v1.2 migration (
e2ca7ff) computedactivityfromequipment_status:STATUS_SPEC §2.3 forbids exactly that ("MUST derive
activityfrom observed hardware state, never fromequipment_status"). Live consequence, observed on both printers after deploying that commit:equipment_status: busywithactivity: "unknown"while printing — which also violates the §2.3 invariantbusy ⇒ running. Andactivity_sincewas the status-build instant, so it reset every poll and could never express a duration.What changed
activitynow maps the printer's own observed gcode state, independently of the health mapping:equipment_statusactivityIDLE,FINISHreadyidlePREPARE,RUNNING,PAUSEbusyrunningFAILEDerroridleunknownunknownPAUSE→running— the job is in flight and the printer cannot take another; keepsbusy ⇒ runningtrue. Sub-state stays legible incomponents["print_job"].stateandmessage.FAILED→idle— the job stopped; §2.3 permits any activity undererror.unknown, never a staleidle/running.activity_sinceis detected in the background poll (routes stay pure cache reads perAGENTS.md) and isnullwhen the transition was never observed — restart mid-print, or telemetry that went stale and recovered. Stamping first-observation time would report a far-too-short duration for the very in-progress operation the field exists to measure.activityis evaluated at request time so it cannot contradictequipment_statuswithin one response;activity_sinceis emitted only when the request-time answer matches the tracked one.README now defines the primary operation (running a print job) and documents the mapping, closing that §9 v1.2 checklist item. Also fixes the ruff
I001left inmodels.py.Verification
uv run pytest -q→ 17 passed (was 11); newtests/test_monitor.pycovers cold-start-null, observed transition stamped and stable across later polls, stale-clears-mid-job, and monitor-failure.uv run ruff check .→ clean.busy+activity: running,activity_since: null(both jobs predate the restart), and the dashboard aggregator polls all three ids withfetch_error: null.🤖 Generated with Claude Code