Conversation
|
I hit an error while handling your request (Model unavailable on AI Gateway free tier: Free tier users do not have access to this model. Upgrade to paid credits at https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dtop-up for unrestricted…). Please try again, rephrase, or reach out if it keeps failing. Error id: 0209bfbf-0dd7-48a7-91b5-f54e87593875 |
2fb7193 to
11f4879
Compare
43a639c to
ca205c9
Compare
11f4879 to
8879032
Compare
ca205c9 to
adad86c
Compare
8879032 to
fd9aaa2
Compare
adad86c to
62b192d
Compare
fd9aaa2 to
97673ca
Compare
3a55885 to
1d2e1d3
Compare
…ons (AK-04a) v2 recipes gain parts[].parent (the part moves with its parent, repeating once or exactly as often, bound by index), parts[].frame (the part's shapes, light and joint are authored in it) and joints[]: fixed, revolute, continuous or prismatic, with any axis, origin, open, rest, range, speed and #930 timing. A joint is keyed by its child part and evaluates to the same EvaluatedMotion records as flat part.motion: a root joint on a principal axis compiles to exactly the flat motion it replaces, and group ids stay `${part}` / `${part}~i`. Nested groups carry `parent`, off-axis joints carry `direction`, and rest values are baked into the shapes. part.motion stays a valid input, exclusive with joints on the same part and outside part trees. Motion envelopes for joints propagate per-joint interval bounds up the tree (swept hinge arcs of box corners, slide extents) instead of sampling products of joint values. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…side its cut (AK-04a) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…is is negative (AK-04a) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…anges, empty joints gate (AK-04a, failing) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… by joint limits, empty joints array is v2 (AK-04a) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
97673ca to
0dddbbe
Compare
cd70a09 to
4e6255c
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4e6255c. Configure here.
| } | ||
| for (const shape of shapes) { | ||
| if (!shape.motionGroup) continue | ||
| if (!shape.motionGroup || jointGroups.has(shape.motionGroup)) continue |
There was a problem hiding this comment.
Joint travel omitted from reach
Medium Severity
Joint envelopes are checked against the floor, wall and ceiling cut, but they are never merged into reach. Joint groups are then skipped in the sampling loop that grows reach from flat motion. Recessed placement still reads evaluation.reach to keep moving parts inside the storey for the whole travel, so a jointed hanging part can swing through the room floor or past the level height without being rejected.
Reviewed by Cursor Bugbot for commit 4e6255c. Configure here.
| throw new Error( | ||
| `Part ${part.id} must repeat once or exactly as often as its parent ${host.id}`, | ||
| ) | ||
| parent = place(host, single ? 0 : i) |
There was a problem hiding this comment.
Zero-count parents still place children
Medium Severity
A single child always attaches to parent index 0, and place never checks that this index is below the parent’s count. When the parent count evaluates to 0 (a valid way to omit a part), the child is still built against a fabricated parent instance. The stated rule that a skipped parent repeat skips its children does not hold, so optional parents can leave floating children and motions whose parent group was never emitted.
Reviewed by Cursor Bugbot for commit 4e6255c. Configure here.


AK-04a: part trees and joints evaluate to nested motions
Stacked on #952 (AK-03c) → … → #945. Review the top commit only. This is part 1 of AK-04. Ship it with AK-04b (render, bake, controls), which is the next PR and draws what this one evaluates.
What (v2 only).
parts[].parent: the part moves with its parent part. The child must repeat exactly as often as its parent: both once, or equal counts bound by index. Chains are acyclic and at most 8 deep. A skipped parent repeat skips its children.parts[].frame { position, rotation? }: the part's shapes, light and joint are authored in this frame, which is relative to the parent.joints[]:{ child, kind: fixed | revolute | continuous | prismatic, origin, axis (any vector), open?, rest?, range?, speed?, delay?, duration?, easing? }, at most one per part.hinge,slideandspin. Values are radians, metres, or radians per second forspeed, with feat(procedural-items): curved shapes, choreographed mechanisms, part lights; shared item controls and light pool #930's limits and timing rules.restis baked into the evaluated shapes, and the motion amount isopen − rest.EvaluatedMotionrecords as flatpart.motion. Group ids stay${part}/${part}~iand kinds stay hinge/slide/spin, so the controller, clip names and baked extras keep their contract. Three optional fields are added:parent: the group this one rides in;direction: only for off-principal axes;axisstays the nearest letter;range: relative to rest.part.motionstays a valid, permanent input. On one part it excludes a joint, and it stays outside part trees. Nothing is migrated.spatial.tsgains poses, Euler↔matrix (three's XYZ convention, round-trip tested) and swept-arc bounds.Why. Audit limit #11 and AK-04. Articraft has nested movable chains in 51.9 % of 9,996 records. Doors with knobs, drawers with pulls and off-axis hinges could not be expressed.
Failing case first. Before the fix,
core/src/procedural-items/joints.test.tsfailed 6 of 6 tests (scratch/AK-CORE/04a-failing-before.txt).Proof.
joints.test.tscovers:direction;rest: an ajar door carrying its knob;knob~1ridesdoor~1;bun test src(core): 3023 pass, 0 fail. nodes procedural tests: 53 pass.turbo check-typesfor core, nodes and editor: pass.bun run check: clean.House effect: no change. There are no
procedural-itemnodes on /next.Follow-up commit: a jointed part of a recessed design may move inside its ceiling cut. The joint reach box is tested against the cut, matching #946's rule for flat motion.
Bugbot fix (later commit): a joint's
rangeflips with the stored amount when its axis is negative. Tests: the stored range, and a flipped range that swings a pull through the floor, which is refused.EvaluatedMotionfields.ProceduralMotionControlleris unchanged and exportedFor Wassim to review:
originandaxisare in the part's own frame (afterframe, before the joint), so they share coordinates with its shapes.restis baked into the shapes, so bounds, surfaces and exports show the authored "left open" pose.Local preview
Shared setup for the whole stack:
previews/AK.md(preview.sh start AK-PV previews/AK/ak-after.jsonserves on :3301).Setup is in
previews/AK.md.What you will notice
The blue cabinet, back-left, has two nested mechanisms:
Before, the knob and pull were fixed in place, so they were left behind in the air when the door and drawer moved.
Where to look
Pose
4-cabinet, "Cabinet".Try this
<node>:door|knob|drawer|pull: open.Before / after
previews/AK/before-4-cabinet.png/after-4-cabinet.pngshow the cabinet at rest; the change is in motion.Checked by me: the static renders at the listed poses (captured from :3100 and :3301). I did not click through the interactive steps in a browser; unit tests cover them.
Known limits
🤖 Generated with Claude Code
Note
Medium Risk
Touches core recipe parsing and evaluation (poses, motion groups, mounting envelopes) with new validation paths, but changes are v2-only and flat v1 evaluation is intended to stay byte-identical.
Overview
Adds recipe v2 part trees and joints so child parts (knobs, pulls, etc.) can ride on moving parents and get their own hinge/slide/spin motion, without changing the existing flat
part.motionpath.Schema:
parts[].parentand optionalframe, plusjoints[](one per child: revolute/prismatic/continuous/fixed with origin, axis, open/rest/range). Joints compile to the sameEvaluatedMotionrecords as today, with optionalparent,direction(off-axis), andrange.restis baked into evaluated shape poses; motion amount isopen − rest.Evaluation: New
joints.tsvalidates trees,placePartsbuilds nested motion groups and design-space poses, andjointReachchecks floor/wall/ceiling envelopes via per-joint interval sweeps (flat motions still use the old corner sampling).spatial.tsgains pose composition andsweptHingeBoundsfor those checks. Moving-part limits and support/surface rules now treat any part in a joint chain as moving viapartMoves.Tests in
joints.test.tscover nested chains, principal-axis equivalence to flat motion, repeats, validation, and envelope edge cases.Reviewed by Cursor Bugbot for commit 4e6255c. Bugbot is set up for automated code reviews on this repo. Configure here.
Codex review round (rev-956-a)
Failing test first:
test(... AK-04a, failing), then the fix. Per the coordinator, this PR merges together with #957.rangeendpoints must lie within the joint limits from rest. A negative axis flips the range.joints: []requiresversion: 2.part.motion,extras.proceduralMotion,<node>:<part>clip names and motion group ids are unchanged for v1 recipes.