Skip to content

fix(mates): solve mated components that do not reach the first part - #1385

Merged
w1ne merged 3 commits into
developfrom
fix/mate-forest-components-20261113
Nov 13, 2026
Merged

w1ne merged 3 commits into
developfrom
fix/mate-forest-components-20261113

Conversation

@w1ne

@w1ne w1ne commented Nov 13, 2026

Copy link
Copy Markdown
Owner

Summary

If the first declared part has no mates, the mate solver ignored every mate in every other component. Each child stayed at its authored pose and no diagnostic reported it. This affected the lowered geometry (mateFk) and solveMates, which swept collision and the reviews use. This PR walks every mated component from its first declared part. That part stays where its at puts it, the same as the grounded part.

Reproduction (develop 80f7542be)

The kernelcad-kinematic skill cookbook 05-clamshell-hinge-swept.kcad.ts declares an unmated table before the base–lid hinge. Agents get this file through lookup_cookbook/execute_cookbook.

Case Before After
evaluate cookbook 05 (solvedModel({ hinge: 90 })) ok:false, assembly.interference.overlap: base/lid 180000 mm³ near (0, 50, 3), which is lid at its authored pose ok:true, no diagnostics
Same model with table declared last (control) ok:true ok:true
checkSweptCollision hinge -15..135 step 5, table first 31/31 poses colliding (base|lid at every angle) 3/31 (-15, -10, -5), the same as the control

Changes

  • src/modeling/mates/solver.ts
    • walkResolvedMateTree (mateFk) now walks each mated component from its first declared part. Parts with no mates stay at identity, as before.
    • solveMates now solves disconnected components by default. solveDisconnectedComponents: false keeps the old walk, which covers only the grounded component.
  • examples/assemblies/assembly-sequence-gearbox.kcad.ts: this example depended on its shaft/gear mates being ignored ("it does not solve the mates in this example"), and its seat values were not at the gear centres. The seats are now at the modelled gear centres, so the mates keep every part where it is drawn. The planner result is unchanged (checkAssemblyPath gearbox test: 19 steps).
  • Tests:
    • New: tests/unit/kinematic/checkSweptCollisionForest.test.ts. The clamshell with table first must give the same colliding angles as with table last.
    • New case in tests/integration/lowering/solvedAssemblyMateFk.test.ts. The mated pair after an unmated first part lowers to Z max 7.5, not 5.
    • solver.test.ts: forest is the default, and the opt-out keeps the old result.
    • placementIgnoredByMateFk.test.ts: an unreached component now gets the note for its mated child (b) only, not for its root (a).

Both new tests fail on unfixed solver.ts: Z max is 5 instead of 7.5, and the sweep reports 11 colliding angles instead of [-15].

Results (local)

  • tests/integration: 271 files passed, 17 skipped; 1770 tests passed, 0 failed.
  • tests/unit + src: 7960 tests passed. 7 failures in connectorManifestExport.test.ts and export.atomic.test.ts (--connector-manifest ancestry is u…) also fail on unfixed code. Their error message refers to the worktree path on this host, not to the solver.
  • npm run typecheck: clean. ESLint on changed files: clean. npm run test:ratchet: 13/13 passed.

Limits

  • Behaviour change: a mate in a component that does not reach the first part now moves its child, and an at on that child is now ignored (reported as assembly.placement-ignored-by-mate-fk). This is the same rule that already applies in the grounded component.
  • Not in this PR: skill cookbook 08-sweep-tolerance-clearance.kcad.ts fails its own assertion. The bisected interference boundary is -0.01007 mm and the assertion limit is 0.01 mm. That is a separate issue.
  • No demo MP4: this is a solver correctness fix, not a new feature or tool.

🤖 Generated with Claude Code

The mate solver grounds the first declared part and walked only that
part's component. Mates in any other component were silently dropped:
the child stayed at its authored pose. Both the lowered geometry
(mateFk) and solveMates (swept collision, reviews) were affected.

The kernelcad-kinematic cookbook 05-clamshell-hinge-swept declares an
unmated `table` first. Its `lid` never swung: evaluate reported a
180000 mm3 base/lid overlap at hinge 90, and checkSweptCollision
reported 31/31 poses colliding. Declaring `table` last gave 3/31.

Now every mated component is walked from its first declared part,
which stays where its `at` puts it, like the grounded part.
solveMates solves disconnected components by default;
`solveDisconnectedComponents: false` keeps the old grounded-only walk.

The assembly-sequence-gearbox example relied on its shaft/gear mates
being ignored; its seats did not sit at the gear centres. Put the
seats at the modelled gear centres so the mates keep every part where
it is drawn. The placement-ignored-by-mate-fk test now expects the
note for the mated child of an unreached component.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: w1ne <41898282+w1ne@users.noreply.github.com>
Auto-committed by run-codex-lane.sh so a dying loop can never lose work
again. Squash or amend before review.

Signed-off-by: w1ne <41898282+w1ne@users.noreply.github.com>
This reverts commit 7aa52c6.

Signed-off-by: w1ne <41898282+w1ne@users.noreply.github.com>
@w1ne
w1ne merged commit ff85bac into develop Nov 13, 2026
17 checks passed
@w1ne
w1ne deleted the fix/mate-forest-components-20261113 branch November 13, 2026 21:37
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