Add ConstraintLens architectural spec#1
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.