fix(studio): Validity panel reports the loaded model, not a vacuous green - #689
Merged
Conversation
…reen
The Validity tab rendered 'solved - 0 parts - 0 joints - 0 diagnostics'
for a saved project whose model has 2 parts and 1 revolute joint, while
the Scene tab listed both parts on the same page. Two defects stacked.
1. reviewToValidity() hardcoded partCount: 0 and jointCount: 0. Its own
header called this a conservative Phase 1.1 adapter pending a deeper
server payload; the deeper payload had in fact already landed
(reviewPipeline returns a validator block with real counts on both
its ok and not-ok branches) but the adapter never read it, so EVERY
model reported zero regardless of how it was declared.
2. When a mesh response carries no review block - which is every
session-backed load, plus the dev endpoint's live=1 short-circuit -
GeometryContext substitutes { ok: true, diagnostics: [] } and
deriveStatus turned that ok into 'solved'. A green verdict computed
over nothing.
Counts now come from the server's validator block when present and from
the loaded model's own featureRecords otherwise, so the panel reports
what the model declares. Provenance is derived (not flagged) from the
payload: a real reviewCadTool result always carries validator, fitness
and a mechanism verdict, a review that found something carries
diagnostics; a payload with none of those is marked unvalidated and the
chip reads 'not run' with an explanatory notice. Only the PASSING
verdict is suppressed - a real failure still reports as itself.
Joints tab: extractJointSnapshots() only read metadata.mates, so a model
built with joint primitives (asm.revolute) produced an empty list, which
emptied the tab and greyed it out via getVisibleTabs. Joint primitives
capture as their own assemblyJoint records with their pose on the
solvedAssembly record's pose map; both vocabularies are now read.
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.
The defect (KC-06)
Open a saved project with 2 parts and 1 revolute joint on app.kernelcad.com. The Validity tab renders:
…while the Scene tab lists both parts and the status bar reads "2 bodies". A green verdict computed over an empty set — worse than no panel, because it tells the user their mechanism validated when nothing was checked.
Root cause — two independent defects, both in
src/studio/**My initial hypothesis (the joint-primitive vs mate split) was wrong for the counts. It was right for the Joints tab.
1. The counts were literally hardcoded.
src/studio/adapters/reviewToValidity.ts:61-62on develop:Every model reported 0/0 — a
.mate()-based model too. The file described itself as a "conservative Phase 1.1 adapter"; the deeper payload it was waiting for had already landed.src/agent/review/reviewPipeline.ts:262-267returnsvalidator: { status, partCount, jointCount }on both branches, but Studio'sScriptReviewSummarynever declared the field, so the adapter never read it.2. The green was fabricated.
GeometryContext.tsx:988(hosted path) andvite.config.ts:630:deriveStatusreadsif (review.ok) return 'solved'. So a placeholder no validator ever touched rendered as a pass.3. Joints tab — this one is the joint/mate split.
extractJointSnapshotsread onlymetadata.mates, and__buildMateMetadata()returnsundefinedwhenmates.length === 0. Joint primitives capture as their ownassemblyJointrecords, with poses on thesolvedAssemblyrecord — reachable all along.The change
Counts prefer the server's
validatorblock and fall back to counting the loaded model's feature records.reviewWasValidated()derives provenance instead of trusting a flag a server could forget to set. When a review did not actually run, the chip readsnot run(grey) with an explanatory notice — and only the passing verdict is suppressed; a realerror/warningstill reports as itself, covered by its own test. Joint primitives now produce joint snapshots, so the Joints tab populates.Deliberately scoped to
src/studio/**. Adding a'not run'member toValidatorStatuswould have rippled through exhaustive switches across modeling and collided with concurrent work, soStudioValiditycarries it Studio-side.Verification
tsc --noEmitclean; eslint clean on all 9 changed files.npx vitest run src/studio→ 99 files, 654 tests, all passing (re-run independently in a checkout with generated assets).0, 0expected +0 to be 2,expected +0 to be 5reviewWasValidated→return trueexpected 'solved' to be 'not run'expected [] to have length 1I independently re-ran the second one — the one that guards the actual reported symptom — and reproduced
expected 'solved' to be 'not run'exactly.tests/failures in a fresh worktree are all environmental (missing gitignoredassets/parts/, unbuiltdist/cli,kernelcadnot on PATH, the knownlightningcss.darwin-arm64.node); none importsrc/studio.Two things for reviewer judgement
ValidityTab.test.tsx'smakeValidityhelper now setsvalidated: true, since a hand-built validator result stands for a validation that ran. Without it, every existing green-chip assertion would readnot run.reviewblock, and thelive=1short-circuit is used for session-backed initial load. So on hosted load the panel will honestly read "not run" until the user presses Validate. If a saved project is meant to arrive already validated, that fix belongs on the server/review side — this PR stops the lie rather than papering over it.