spec+plan: the arm measures its own joint travel — rolling the encoder seam out of the way (#43)#44
spec+plan: the arm measures its own joint travel — rolling the encoder seam out of the way (#43)#44OriNachum wants to merge 2 commits into
Conversation
Issue #43 found that the software bound lands EXACTLY on the encoder seam for every factory-offset joint: reported 4095 under Ofs=85 is raw 84, one tick below the seam at raw 85. Three joints reached that bound with no contact — still free. The arm has been reporting the seam as its boundary, so no joint's real travel could ever be seen past it. The spec's mechanism is a ROLLING FRAME: write a temporary offset that maps the joint's current raw position to reported 2048, putting the seam half a turn away, then creep outward and RE-CENTRE whenever the creep nears the bound. The seam is rolled ahead of the joint and never obstructs it. Displacement accumulates in RAW ticks, which are frame-independent, so the measurement survives the frame moving underneath it. Two user rulings shape the build: - NAME THE FAKE WALL HONESTLY. A gravity-loaded joint can stall against the torque cap with nothing in front of it. Four verdicts (WALL / TORQUE-LIMITED / EDGE / TIMEOUT), carried per END. A torque-limited end is a lower bound, never a wall. This failure is SAFE — it makes a range too narrow, never too wide. - PERSIST RAW TICKS EVERYWHERE. A re-zero invalidates history only because we persist REPORTED ticks, which are a view through the current offset. Raw ticks are invariant under a re-zero. This eliminates the failure mode rather than detecting it, so no calibration-identity machinery is needed. 15 tasks, 10 waves. t12 is a STOP-GATE: if the verb cannot re-derive the two answers we already know (elbow_flex BOUNDED, wrist_roll CONTINUOUS) from measurement alone, it is guessing, and everything downstream halts. Refs #43 #35 #34 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vrVNSJ9kzhCQqwpA2e8u6
PR Summary by QodoAdd spec+plan for "arm limits" rolling encoder seam out of the way (issue #43)
AI Description
Diagram
High-Level Assessment
Files changed (9)
|
Code Review by Qodo
Context used✅ Tickets:
🎫 elbow_flex and wrist_roll encoders wrap — a [min, max] range cannot describe them, and gentle_move cannot converge across the seam 🎫 Every factory-offset joint is clamped exactly ON its encoder seam — #35 may affect all six✅ Compliance rules (platform):
8 rules✅ Skills:
cicd 1. Stale uv.lock version
|
| [project] | ||
| name = "arm101-cli" | ||
| version = "0.22.1" | ||
| version = "0.22.2" |
There was a problem hiding this comment.
2. Stale uv.lock version 🐞 Bug ☼ Reliability
pyproject.toml bumps the project version to 0.22.2 but uv.lock still lists the editable root package as 0.22.1, leaving the committed lockfile inconsistent with the project metadata. This can force CI/dev installs to modify the working tree during uv sync (or fail in locked/frozen modes), reducing reproducibility.
Agent Prompt
### Issue description
The PR bumps `pyproject.toml` to version `0.22.2`, but the committed `uv.lock` still records the root editable package (`arm101-cli`) as `0.22.1`. This makes the lockfile stale relative to the project metadata.
### Issue Context
CI runs `uv sync`, which relies on `uv.lock` for reproducible environments. When `uv.lock` is stale, `uv sync` may rewrite the lockfile (dirty working tree) or fail in workflows that use locked/frozen modes.
### Fix Focus Areas
- pyproject.toml[1-4]
- uv.lock[5-8]
### Expected fix
1. Regenerate the lockfile (e.g. `uv lock`, or the repo’s standard lock update command).
2. Verify `uv.lock` now records `arm101-cli` as `0.22.2`.
3. Commit the updated `uv.lock`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
…by Ofs=85 The claim as written said the software bound lands on the seam "for every factory-offset joint", implying it is an artifact of the factory Ofs=85. It is not. reported = (raw - Ofs) mod 4096, so reported 0 IS the seam and reported 4095 is the tick immediately below it — for EVERY offset, including 0. A re-zero can never move the bound off the seam; it can only move the seam to a raw angle the joint cannot physically reach. So the actual bug is not that the bound coincides with the seam (it always does), but WHERE the seam sits: at Ofs=85 it lands at raw 85, INSIDE these joints' travel, so they hit the bound while still physically free. Both ends of the commandable range are therefore pinned to the cut, and they are physically adjacent on the shaft — worse than one end resting on it. Found by task t4 of the build while pinning the seam arithmetic. Left uncorrected, a later task would have reasoned from "re-zero fixes the bound", which is false. Refs #43 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vrVNSJ9kzhCQqwpA2e8u6
|



Carries the converged devague spec and its buildable plan for issue #43. No runtime code yet — the build follows via
/assign-to-workforce.The finding this answers
arm explore's bounds have been artifacts of our own clamp. The software bound lands exactly ON the encoder seam for every factory-offset joint: five of six still carryOfs = 85, the commandable bound is reported tick 4095, and 4095 underOfs=85is raw 84 — one tick below the seam at raw 85.Three joints reached that bound with no contact — still free, 2 / 3 / 11 raw ticks short.
shoulder_liftthen sagged through the seam under gravity with torque off, and now reads 2. Nothing commanded it there. So #35 is potentially a six-joint problem, not two, and every per-joint range recorded before now is suspect at its high end.The mechanism: a rolling frame
The chicken-and-egg is that you cannot measure a joint's unreachable arc until you can see past the seam, and you cannot evict the seam until you know the arc.
Write a temporary offset mapping the joint's current raw position to reported 2048 — the seam is then half a turn away, the farthest it can possibly be, with ~2048 clear ticks each side. Creep outward, and re-centre whenever the creep nears the bound. The seam is rolled ahead of the joint and never obstructs it, so travel is unbounded by the frame.
Displacement accumulates in raw ticks, which are frame-independent — so the measurement survives the frame moving underneath it. Termination is by accumulated raw displacement: a full 4096 ticks with no wall means the joint is continuous and no offset can ever help it. That is how the verb derives
wrist_roll's known verdict rather than being told it.Two rulings that shape the build
Name the fake wall honestly.
gentle_movecalls contact when load is high and the joint stops advancing — but a gravity-loaded joint lifting the arm's own weight against the 500 torque cap can stall with nothing in front of it. Four verdicts (WALL/TORQUE-LIMITED/EDGE/TIMEOUT), carried per end. A torque-limited end is a lower bound, never a wall.This failure is safe, which is why naming it is enough: a torque-limited stall makes a range too narrow, never too wide — the arm under-claims its reach. So
arm_specstores "the widest envelope observed", not "the mechanical limit", and says which ends it can vouch for.It also scopes the max-over-poses rule honestly. That rule is sound for an obstacle — obstacles shrink ranges, and taking the max over poses undoes that. It is false for the arm's own weakness, which shrinks the range in every pose, so no number of poses ever escapes it.
Persist raw ticks everywhere. A re-zero invalidates history only because we persist reported ticks, which are a view through the current offset. Raw ticks are invariant under a re-zero — the offset changes, the encoder does not.
REZERO_ARCSalready made this move in PR #41; this finishes it. It eliminates the failure mode rather than detecting it, so the calibration-identity machinery the #36 spec proposed is not needed to protect data that cannot go stale.The plan — 15 tasks, 10 waves
Wave 0 is four file-disjoint pure tasks (raw-tick boundary, the four-verdict record, the crash-safe calibration journal, guard tests). Then the probe engine strictly serial (rolling frame → creep → classifier → measured arcs), then the verb, then four human-gated hardware waves.
t12 is a stop-gate. If the verb cannot re-derive the two answers we already know —
elbow_flexBOUNDED,wrist_rollCONTINUOUS — from measurement alone, it is not measuring, it is guessing, and no verdict it gives on the other four joints may be believed. Everything downstream halts and returns to the user.Claims that can prove themselves wrong
The frame forced every accusation to carry a falsification condition:
_REZERO_UNNECESSARYcurrently asserts four joints don't wrap. If all four come back with the seam outside their travel, the message was right, the claim is retracted, and no code changes.Artifacts
docs/specs/2026-07-12-arm101-finds-its-own-joint-limits-a-new-gated-verb.mddocs/plans/2026-07-12-arm101-finds-its-own-joint-limits-a-new-gated-verb.md.devague/frame + plan stateRefs #43 #35 #34