Skip to content

Add ConstraintLens architectural spec#1

Merged
Botrops1 merged 22 commits into
mainfrom
claude/fusion-constraintlens-spec-94gPu
May 22, 2026
Merged

Add ConstraintLens architectural spec#1
Botrops1 merged 22 commits into
mainfrom
claude/fusion-constraintlens-spec-94gPu

Conversation

@Botrops1
Copy link
Copy Markdown
Owner

Comprehensive SPEC.md covering MVP scope, folder structure, module
responsibilities, exhaustive constraint type dispatch table, event
handler GC pattern, palette message contract, fixture sketch, known
API landmines, and open questions to validate in Fusion.

claude and others added 22 commits May 21, 2026 10:41
Comprehensive SPEC.md covering MVP scope, folder structure, module
responsibilities, exhaustive constraint type dispatch table, event
handler GC pattern, palette message contract, fixture sketch, known
API landmines, and open questions to validate in Fusion.
Replace the three deferred forks at the end of SPEC.md with confirmed
decisions and the concrete implementation rules each one implies.
fixture_sketch.py builds the deterministic test sketch from SPEC.md
section 8 (rectangle + circle + 4 explicit constraints + 2 dimensions).

spike_probe.py answers the five open questions from SPEC.md section 10
in one shot: enumerates the current workspace's panel ids, probes
Sketch.ShowUnderconstrained inside vs outside sketch edit, introspects
Palette event surface, dumps the full GeometricConstraint inventory
with accessor types per row, and captures an entityToken for the
save-reload stability test.

Output is written to the OS temp dir and previewed in a message box
so the user can paste it back verbatim.
Implements every module specified in SPEC.md sections 3-7:

- ConstraintLens.py / .manifest — Fusion add-in entry point with
  sys.path bootstrap.
- lib/lifecycle.py — command button, palette creation, all event
  wiring, JS->PY action handlers (selectEntities, selectConstraint,
  deleteConstraint, requestRefresh, paletteReady).
- lib/events.py — GC-safe handler registry (landmine M-7) plus
  DocumentActivated / CommandTerminated / Palette incoming + closed
  subscriptions.
- lib/dispatch.py — full 21-row constraint descriptor table with
  per-subtype label/chip builders; landmines M-1 (MidPoint .point
  guarded), M-2 (pattern stubs marked read-only) folded in. Includes
  a parallel dimension-kind table.
- lib/scanner.py — sketch enumeration: geometric constraints,
  dimensions, and reconstructed implicit coincident joins (M-3).
- lib/labels.py — per-sketch (token -> "Line 3") map indexed across
  lines/points/circles/arcs/ellipses/splines.
- lib/selection.py / actions.py / tokens.py / messaging.py — viewport
  selection, delete via entityToken resolve, JSON wire format with
  isVisible gating (M-8).
- palette/index.html + app.js + styles.css — vanilla-JS, no-build
  single-page UI matching SPEC section 7's message contract. Dark
  theme matches Fusion.
- README.md — install + verify workflow pointing at the spike probe.

Open items pending the spike-probe output: SPEC open-question 1
(_PANEL_ID is currently SolidScriptsAddinsPanel as the safe default;
will move to a sketch-specific panel once the probe confirms the id)
and 5 (VerticalConstraint enumeration verification).

Notes:
- palette/ lives inside ConstraintLens/ (the SPEC's "shipped layout"),
  consolidating the dev/ship split for MVP — no copy step needed.
- Resource icons are not committed; Fusion falls back to a default
  glyph for the toolbar button until artwork is added.
Hardening (caught by ruff + self-review):
- dispatch.py: rename ambiguous 'l' variable; import Callable from
  collections.abc (Python 3.14 preferred form).
- lifecycle.py: drop unused 'ui' assignment in stop(); wrap risky
  property writes (palette.dockingState, button.isPromoted) in
  try/except since some Fusion builds make these conditional.
- events.py: add a public pin(event, handler) helper so other
  modules no longer need to poke events._handlers / _subscriptions
  directly; lifecycle's CommandCreated registration uses it.

New artifacts:
- tests/fixture_midpoint.py builds the canonical M-1 trigger
  (one sketch point constrained as midpoint of two lines). Lets
  the user verify the .point defensive guard at PC time.
- PC_VALIDATION.md is the step-by-step checklist for the first
  PC session: install, run the spike probe, smoke-test the panel
  against both fixtures, exercise the landmines, and report back.
- LICENSE settles the README "TBD" with MIT.

All Python passes ruff (E/F/W/B/UP) and `python3 -m py_compile`.
- dispatch.py: OffsetConstraint.parentCurves/.childCurves return
  SketchCurveVector (no .count attr); switch to len() with try/except.
  This eliminates the ACCESSOR error badge shown in PC test 1.

- lifecycle.py: _entities_for_row() iterated collections with .count +
  .item(i); replaced with direct iteration so SketchCurveVector is also
  handled without error.

- palette/app.js: paletteReady was sent on DOMContentLoaded, but
  adsk.fusionSendData is injected by Fusion asynchronously after the
  page loads. Added _sendWhenReady() which polls every 100 ms (up to
  5 s) until the bridge is available, fixing the "Loading..." stuck
  state that required a manual Refresh click.

- tests/: Fusion requires each script to live in a same-named subfolder
  (e.g. fixture_sketch/fixture_sketch.py). Moved all three test scripts
  into their own folders; updated README and PC_VALIDATION paths.

https://claude.ai/code/session_01GBGpa4pZXXLbrWDRJmqT5F
findEntityByToken returned a non-empty BaseVector after save-close-reopen.
Token-based row keys in messaging.py are correct; no fallback needed.

https://claude.ai/code/session_01GBGpa4pZXXLbrWDRJmqT5F
…t 2)

Q1: SolidScriptsAddinsPanel confirmed. SketchConstraintsPanel noted as
    better v1 home but not an MVP blocker.
Q2: ShowUnderconstrained confirmed requires sketch edit context; returns
    plain string 'Under constrained points: N, under constrained curves: N'.
Q3: Palette event surface is [closed, incomingFromHTML, navigatingURL] —
    no shown/opened event; push-on-restore via event hook is not possible.
Q5: adsk::fusion::VerticalConstraint enumerated at row [1] — PASS.

All five open questions in section 10 are now resolved. SPEC is complete.

https://claude.ai/code/session_01GBGpa4pZXXLbrWDRJmqT5F
…, lock UX

Bug 1 — Tangent spline+line: line not highlighted on row click.
  JS now embeds entity tokens in data-entity-tokens on each row and sends
  them with the selectEntities message. Python resolves each entity directly
  by token (findEntityByToken → concrete typed object) rather than re-scanning
  accessor names. Token-based resolution is reliable for all curve types
  including SketchFittedSpline / SketchControlPointSpline where the accessor
  path could return a base-class proxy that activeSelections.add() rejects.

Bug 2 — SketchOffsetCurvesDimension row shows no highlight.
  dispatch.py describe_dimension() now special-cases this type: iterates
  .curves (SketchCurveVector) and builds entity chips with tokens, so both
  the label and the selection payload carry the actual offset curves.
  lifecycle.py _entities_for_row adds 'curves' to the collection loop so
  the fallback path also covers this dimension type.

Bug 3 — Implicit join × button shows confusing 'blocked' cursor with no
  explanation. Replaced the disabled × button on pseudo/implicit rows with
  a ⊘ lock indicator (non-interactive span) whose title tooltip reads
  'Endpoint joins are shared sketch points and cannot be individually
  deleted'. Added .row-lock CSS class; aligned row-actions items with
  align-items: center.

https://claude.ai/code/session_01GBGpa4pZXXLbrWDRJmqT5F
Captures current version (0.1.3), what's verified vs pending PC test,
architecture quick-reference, resolved open questions, and v1 backlog.
Update this file at the end of every session.

https://claude.ai/code/session_01GBGpa4pZXXLbrWDRJmqT5F
Fires only when there are commits to push that don't include CLAUDE.md.
Injects additionalContext reminding Claude to update Current Status
(Working on / Next step / Blocked by) and commit before pushing.

CLAUDE.md at project root is auto-loaded by Claude Code at session
start as project memory — no extra config needed for that.

https://claude.ai/code/session_01GBGpa4pZXXLbrWDRJmqT5F
OffsetConstraint row previously rendered with no entity chips
(label-only "Offset 1->1 curves @ ?"). Now iterates parentCurves and
childCurves via a shared _iter_curves_into_chips helper that tolerates
both SketchCurveVector (iteration + len) and ObjectCollection (count +
item) shapes.

SketchOffsetCurvesDimension selection didn't work because the dimension
doesn't reliably expose its source curves under any standard attribute
name. describe_dimension now tries .curves / .parentCurves /
.childCurves directly, then falls back to scanning the sketch's
geometricConstraints for an OffsetConstraint whose distance.parameter
shares the dimension's parameter entityToken — that OffsetConstraint's
parentCurves + childCurves become the dimension's chips.

CLAUDE.md updated: bumped to test session 4 pending; backlog now lists
(7) sketch-to-palette reverse lookup and (8) marking invisible entities
that users can't click on the canvas.

https://claude.ai/code/session_01GBGpa4pZXXLbrWDRJmqT5F
Test 4 showed the dimension still resolved 0 curves. Root cause:
OffsetConstraint.distance accessor returns None in the January 2026
build (visible as "@ ?" in the offset constraint row label since
0.1.3), so my parameter-token match in 0.1.4 had nothing to compare
against.

_find_offset_constraint_for_dim now stacks four matching strategies
and uses whichever lands first:
  1. parameter entityToken (clean path when .distance works)
  2. parameter name
  3. positional pairing — nth SketchOffsetCurvesDimension corresponds
     to nth OffsetConstraint in the sketch
  4. single-OffsetConstraint sketch — there's only one, use it

Once the constraint is matched, parentCurves + childCurves become the
dimension's entity chips and selection works through the existing
token-based path.

Backlog item #9 added: normalize the OffsetConstraint label so the
"@ ?" cosmetic glitch goes away (pull the distance from the matched
dimension's parameter instead of the broken .distance accessor).

https://claude.ai/code/session_01GBGpa4pZXXLbrWDRJmqT5F
All five PC test sessions complete. SketchOffsetCurvesDimension row
now shows curve chips and highlights on click (0.1.5 confirmed).
README updated from dev scaffold to user-facing install guide.
CLAUDE.md reflects release-ready status.

https://claude.ai/code/session_01GBGpa4pZXXLbrWDRJmqT5F
…fication

#10: Angular/Diameter and other dim types show class-name fallback labels
("SketchLine") instead of friendly names ("Line 2") because describe_dimension
assumes entityOne/entityTwo but some types use type-specific accessors.
Selection still works via _entities_for_row broad scan.
Fix: per-type accessor map for dimensions, mirroring geometric constraint dispatch.

#11: Fully-constrained green banner is implemented correctly (app.js L128–133)
but has never been exercised in a PC test — fixture sketch is under-constrained.
Needs manual verification with a fully-constrained sketch.

https://claude.ai/code/session_01GBGpa4pZXXLbrWDRJmqT5F
… #9 #10)

- #1: Move toolbar button from SolidScriptsAddinsPanel to SketchConstraintsPanel
- #2: Add 'Show u/c' button calling executeTextCommand('Sketch.ShowUnderconstrained'); result surfaced as toast
- #3: Add filter bar below toolbar; client-side filtering by label/kind; section headers show (N of M) counts
- #9: Normalize OffsetConstraint label to 'Offset (N->M curves, expr)' via matched SketchOffsetCurvesDimension
- #10: Add _DIM_ACCESSORS map in dispatch.py for per-type dimension entity accessors (Angular, Diameter, Radial, etc.) so chips show friendly names
- Add .vscode/ to .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- #5: Checkboxes on all deletable rows; "Delete N" + "Clear" buttons appear in toolbar when selection > 0; confirm dialog notes Ctrl+Z undo; single per-row delete button removed; Python bulk handler loops deletions and returns summary toast
- #8: chip_for() checks entity.isVisible; invisible chips rendered dimmed with dashed border and "hidden" badge; clicking row still reveals hidden entity (Fusion native behaviour)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Botrops1 pushed a commit that referenced this pull request May 22, 2026
- Bump version to 1.0.0 in manifest
- README: complete rewrite covering all v1 interactions for new users
- SPEC.md: update deferred list (4 items shipped); fix PolygonConstraint
  .lines type (SketchLineVector, no .count) and centerSketchPoint None
  note; fix Circular/Rectangular pattern rows (parameters ARE writable);
  add missing JS→PY and PY→JS message actions
- RELEASE_NOTES.md: new file with full changelog v0.1.0 → v1.0.0
- Remove generate_icons.py (one-time dev utility, not part of release)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Botrops1 pushed a commit that referenced this pull request May 22, 2026
Full v1 Polish Backlog (#1–#21) complete and released.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Botrops1 Botrops1 merged commit b837baa into main May 22, 2026
1 check passed
@Botrops1 Botrops1 deleted the claude/fusion-constraintlens-spec-94gPu branch May 22, 2026 20:22
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.

2 participants