Skip to content

Spec: an action sends exactly what was asked - #161

Merged
nofuturekid merged 3 commits into
rebuildfrom
docs/no-own-logic
Nov 13, 2026
Merged

nofuturekid merged 3 commits into
rebuildfrom
docs/no-own-logic

Conversation

@nofuturekid

@nofuturekid nofuturekid commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

The principle

Spec section 2 now states it once: a request sends exactly what it names: the frames of that setting or action, nothing before them and nothing after them. The daemon never switches an automatic control or a tracking mode off on its own. What the camera then does is camera behaviour: the state shows it, and until it is recorded it is a camera item. The CLI and the widget never chain a second request to the one the user made. Every other place in the spec refers back to section 2.

The owner asked to keep the daemon free of logic of its own. The tracking switch carried over from 0.13 was such logic and contradicted section 2, so it is gone. Section 15.1 (new, "Changes after approval") records it as row 88.

Removed

  • Spec 4.4: gimbal.move and gimbal.turn no longer switch tracking.mode to static before they move. They send only the move, leave the tracking mode alone, and are not refused while the tracking is on. A client that wants the tracking off sends set tracking.mode=static itself, as its own request.
  • Spec 8.8, touched groups: the moves no longer touch tracking and framing. They touch the tracking group only if the camera turns out to change the mode itself.
  • Field ledger: the rows gimbal.move and gimbal.turn no longer list the short command 16 02 00 00 before the move frame.
  • W0 plan: in fake-a, gimbal.move and gimbal.turn no longer set tracking.mode=static, and their touched is empty. The test "4.4: a move switches tracking to static" is replaced by a_move_while_tracking_sends_only_the_move, which pins the opposite: with tracking.mode=normal, each move reaches the fake as exactly one operation, the mode stays normal, and nothing is touched. The assertion messages name spec 2.

Added so that the rule can be checked

  • CAM-34 (stage A, CS-1): what the camera does with a manual move while it tracks: it moves and tracks on, it switches the mode, or it does nothing.
  • Spec 8.3: the send-table test also checks that each setting and action sends exactly the frames of its ledger row.
  • Track B: a new named test: a set causes one apply per leaf, a do causes one act, and nothing else reaches the fake.
  • W0 acceptance: a new row for the fake test above.
  • Elsewhere: a migration note for gimbal --pan/--tilt and gimbal rate (12.2), the CLI's "one request per command" (6.2), the widget chains nothing (12.3, 10.3 D), notes in the three gimbal parity rows, and one sentence in rule 6 of docs/protocol-2.md in the W0 plan.

Presets saved without a name

preset.save without name sends the preset and no name: no rename frame follows, and obsbot4linux no longer invents Preset <slot + 1> (0.13 did). What the camera reports as the name of an unnamed preset is a new camera item, CAM-35 (stage A, CS-1). A client may show its own placeholder for an empty name, as pure display, and must not write it to the camera (CLI 6.2, widget 12.3). Migration note in 12.2; row 90 of 15.1; the field-ledger row of preset.save sends the rename frame only when a name is given. In the W0 plan both fakes store an empty name for an unnamed preset (their own behaviour, labelled so), and the new test a_preset_saved_without_a_name_gets_none pins: one act and no rename, the stored name empty; with a name, the name is stored.

Stops the daemon sent on its own

Also removed (row 89): the stop when the camera falls asleep while a turn runs, and the best-effort stop of a keep:true turn on daemon shutdown. The client that started a turn can still end it, and gimbal.stop works from any connection and after a restart. 0.13 sent neither.

What stays, and why

  • The end the request itself set: a gimbal.turn without keep:true lasts as long as its connection, so its stop is sent when that connection closes, a daemon shutdown included (5.6, 5.9).
  • The yaw sign of gimbal.turn under mirror xor invert: it is the encoding in the one turn frame that keeps "pan positive = the camera turns to its left" true for both gimbal.move and gimbal.turn (4.2, 4.4). It is not a second command.
  • Frames the camera's protocol defines for the one operation asked for:
    • the two bounds of a zone or hand area;
    • the four frames of hand.area_reset;
    • the shared auto-sleep word;
    • the initial-state block that power_on.start.mode and the start-pose actions write back;
    • the two frames of preset.delete and of view.save;
    • the keep value of a missing axis.
  • The rewind write of the preset list read stays as the one named exception until CAM-19 decides (2, 8.8).

Verification

  • The W0 plan's inlined code was extracted into a scratch copy of the rebuild workspace (with the files the plan adds, and without stage G's legacy/ modules, which the plan's code does not use). This round was rebuilt: cargo fmt --check, cargo clippy --workspace --all-targets --locked -- -D warnings and cargo test --workspace --locked pass, 185 tests (184 before the new one).
  • scripts/regenerate.sh produced the description files (1875, 1875 and 289 lines) and the schema; the stale tests pass and nothing the plan says about them changed.
  • Putting the default name back into fake-a makes the new test fail (left: "Preset 1", right: ""); putting the tracking switch back makes a_move_while_tracking_sends_only_the_move fail.
  • Prettier 3 touched only the changed lines.

Documentation only. The groundwork plan and the README are not touched.

🤖 Generated with Claude Code

nofuturekid and others added 3 commits November 13, 2026 13:11
Section 2 now states the rule once: a request sends the frames of the
setting or action it names, nothing before them and nothing after
them. Two kinds of frames belong to a request: the frames the camera's
protocol defines for that one operation, and the stop of a continuing
action nobody can end any more. Clients chain no second request.

The switch to the static tracking mode that gimbal.move and gimbal.turn
carried over from 0.13 is gone. The moves leave tracking.mode alone,
touch no group, and are not refused while the tracking is on; what the
camera does with a manual move while it tracks is the new camera item
CAM-34. Migration notes, the CLI and the widget say that a client that
wants the tracking off sends tracking.mode=static itself. Section 15.1
records the change after approval (row 88).

The field ledger drops the static short command from both move rows.
The W0 plan follows: fake-a only moves, touches nothing for a move, and
the test that pinned the switch is replaced by one that pins one
operation and an unchanged tracking mode. The plan's code was extracted
and rebuilt (fmt, clippy -D warnings, every test); the regenerated
description files and schema did not change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The daemon no longer stops a turn when the camera falls asleep or a
keep:true turn at shutdown; the only stop it sends is the end the
request set (a turn without keep:true lasts as long as its
connection). Section 2 names that end, the unit-keeping yaw encoding
of gimbal.turn and the preset rewind exception; 3.2 and track A drop
the carried tracking switch; W3 drops the README lines about it;
tracks B, C and D get checks for the one-request rule; CAM-7 no
longer fixes its outcome; 15.1 row 88 lists every touched section and
row 89 records the stops. W0 plan: protocol-2 rule 6 no longer reads
as a camera promise; spec coverage names section 2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
preset.save without a name sends no rename frame and the daemon invents no
"Preset <slot + 1>" (0.13 did). What the camera reports as the name of an
unnamed preset is a camera item, CAM-35. A client may show its own
placeholder for an empty name as display only and never writes it to the
camera. Spec 2, 4.3, 4.4, 6.2, 9.1, 10.3, 12.2, 12.3, 14 and 15.1 (row 90),
the field ledger row of preset.save, and the W0 plan: both fakes store an
empty name for an unnamed preset (their own behaviour), and a new test pins
one act without a rename and the stored name. The plan's code was rebuilt:
fmt, clippy -D warnings and 185 tests pass; the new test fails when the
default name is put back; the generated files did not change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@nofuturekid
nofuturekid merged commit 6a34edc into rebuild Nov 13, 2026
6 checks passed
@nofuturekid
nofuturekid deleted the docs/no-own-logic branch November 13, 2026 12:12
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