Skip to content

Kit form: copies stepper, first-layer temp + brim, camera/gate hardening, upgrade-durable form callback - #26

Merged
bbolinger merged 7 commits into
mainfrom
feature/copies-stepper
Jul 20, 2026
Merged

bbolinger merged 7 commits into
mainfrom
feature/copies-stepper

Conversation

@bbolinger

Copy link
Copy Markdown
Owner

Summary

The bundle held for the next release. Everything here is tested and already confirmed with a live kit run on the operator box.

Form

  • Copies: replace the capped 1-9 option grid with a +/- stepper that dials 1..50 inline on the setup screen.
  • First-layer nozzle temperature: a third Temperature stepper (Nozzle / First-layer nozzle / Bed), same per-material range as the main nozzle; overrides only the initial layer when touched, for adhesion tuning.
  • Brim: expand off/auto to the U1 Orca fork's full brim_type set: off, outer, auto, mouse ears.

Reliability and safety plumbing

  • Bed capture retry: the U1 MJPEG endpoint intermittently returns a short read or a JPEG missing its EOI marker; re-GET up to 3 times and accept only a complete frame. Persistent failure still raises, so the start gate stays fail-closed.
  • Re-slice invalidates a stale prompt: when a re-slice reused the same request, a bed-clear prompt armed for the earlier plan could survive a failed capture and be redeemed against the new plan. Clear the pending prompt at plan-persist so a fresh plan never carries an old one. Clearing only tightens the gate.
  • Upgrade-durable form callback: the per-turn form callback is now published from the plugin's pre_gateway_dispatch hook, so it survives a Hermes package upgrade that replaces gateway/run.py. The run.py patch stays as a fallback for older builds. This is form-render plumbing only; the model-free print-start boundary is untouched.

Housekeeping

  • Remove dead code: orphaned slice helpers and the unwired form-gateway text-fallback scaffolding.
  • Docs: drop a stale slice_summary.txt reference and point the profile-verification tip at where the setting id actually lives.

Test

Full suite green (1069 passed, 8 skipped). Deployed to the operator box and confirmed with a live kit driven through the form, the two-stage gate, and the cancel button.

…fallback

Drops code with zero references, left behind after single-STL was folded into
the kit workflow and the model-free form landed:

- u1_slice_workflow: _cmd_prefix, triage_stl, choose_default,
  _trim_option_payload, write_slice_summary, DEFAULT_OUT_BASE
- form_gateway: get_pending_for_session, mark_awaiting_text, has_pending,
  set_notify, the _FormEntry.signature method, and the awaiting_text field.
  These were scaffolding for a text-fallback intercept (_handle_message) that
  was never built; typed answers route through the model instead.
- u1_kit_tool: check_u1_kit_requirements (register_tool has no check slot)
- u1_config: _ConfigPathProxy.exists_path
- u1_toolmap: unused CHANNEL_EXTRUDER reverse map

No behavior change. Full suite green (1040 passed, 15 skipped).
write_slice_summary (the only producer of slice_summary.txt) had no callers and
was removed in the dead-code pass, so that file is not generated. Point the
profile-verification tip at where print_settings_id actually lives: the slice
result metadata and the gcode's own metadata.
The second setup question (copies of a single-part job) was a fixed grid that
stopped at 9. Reuse the numeric stepper the advanced settings already use: a
"Copies" header plus a -5/-/+/+5 row, dialing 1..50 inline on the setup screen.

- register a "quantity" stepper (steps 5/1, range 1..50) and mark the field
- render steppers on grouped screens too, with a plain header (base = the field
  default, no "keep profile" language) for a top-level count vs a profile override
- review echo and answer_json read the dialed value from form["steps"]
- parse validates the count against the range instead of a fixed option list;
  text ("x12", "qty 12") and JSON both accept 1..50, reject out of range
- drop the now-unused _QUANTITY_IDS

Full suite green (1045 passed, 13 skipped).
The U1 MJPEG endpoint intermittently closes the connection a few hundred bytes
short of the declared length (http.client.IncompleteRead) or returns a JPEG
missing its EOI marker. fetch_monitor took the first frame unconditionally, so a
single short read aborted _capture_bed_and_issue_token and the start gate refused
a real print (live 2026-07-18: a 487-byte-short frame during a kit drill).

Re-GET up to 3 times, accepting only a complete JPEG (SOI + EOI, over 1KB);
persistent failure still raises so the caller stays fail-closed. Adds
test_u1_camera_retry.py.

Full suite green (1050 passed, 13 skipped).
When a re-slice reused the same request, a bed-clear prompt armed for the earlier
plan survived if the new slice's bed capture failed (the arm/overwrite only
happened on capture success). The operator's YES then redeemed that stale prompt
against the new plan and the Stage-2 gate refused (revision + gcode_hash mismatch,
live 2026-07-18).

Clear pending_bed_clear_start at plan-persist, before the capture, so a fresh plan
never carries an old prompt regardless of capture outcome. Clearing only tightens
the gate (a missing pending refuses), so it stays fail-closed. On capture success
_action_start re-arms a fresh prompt as before. Adds a regression test.

Full suite green (1051 passed, 13 skipped).
Two operator-requested form additions:

- First-layer nozzle temperature: a third Temperature stepper (Nozzle /
  First-layer nozzle / Bed), same per-material range as the main nozzle. The
  main nozzle still sets both layers; the first-layer control overrides only the
  initial layer (nozzle_temperature_initial_layer) when touched, for adhesion
  tuning without changing the rest of the print.
- Brim: expanded from off/auto to the U1 Orca fork's full brim_type set:
  off (no_brim), outer (outer_only), auto (auto_brim), mouse ears (brim_ears).

Full suite green (1058 passed, 13 skipped).
A Hermes package upgrade replaces gateway/run.py and wipes the anchor patch
that published the per-turn form callback, so every deployment silently loses
the form flow on a Hermes upgrade (the cryptic "callback not registered"
error operators hit after pip install -U).

Publish the callback from the plugin's own pre_gateway_dispatch hook instead.
That hook already holds the live adapter, the inbound chat_id, and the gateway
loop, so it can build the same callback and register it on every inbound
message. The plugin lives on the persistent volume, so this survives upgrades
and self-heals on the next message.

The run.py patch stays as a belt-and-suspenders fallback for older Hermes
builds whose dispatch context is thinner; last-writer-wins per turn and both
build an equivalent callback. install.py no longer aborts when it cannot patch
run.py.

This is form-render plumbing only; the model-free print-start boundary is
untouched.
@bbolinger
bbolinger merged commit c34f0aa into main Jul 20, 2026
3 checks passed
@bbolinger
bbolinger deleted the feature/copies-stepper branch July 21, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant