Commit Graph
5 Commits
Author SHA1 Message Date
DumiFlex 94ccd7f192 test(wp_api): lock comma rejection on variable names server-side
The engine handlers already enforce _IDENT_RE on output_var / var_binding
/ fixed values[].name via validate_payload, wired into every
create/update/import through _validate_payload_for_type (returns 400 on
ValueError). Existing tests cover leading-digit / dunder / too-long /
empty cases; add the explicit comma scenario the user reported so the
exact "no settle-delimiter in a var name" contract is regression-proof
for all three module types. No production change — server gate already
existed; this characterizes + locks it.
2026-06-09 15:39:16 +03:00
DumiFlex 4a69c5e212 fix: cross-node constraint drag + scanner slot allocator + library fallback + save hydrate
- Cross-node drag MOVE-becomes-COPY race: ContextWidget.onDrop sync-claims
  consumedBy before any await so the source widget's dragend reads the
  set value. dragend filters by _uid (idempotent) instead of stale
  srcIdx — fixes the secondary issue where the backup pendingHandoffs
  queue + dragend double-removed an adjacent target wildcard.
- pendingHandoffs queue in drag-store: belt-and-suspenders source-side
  cleanup that runs even when dragend skips (constraint rows inside the
  bundle frame were the trigger).
- Conflict scanner constraint_orphan_target slot allocator: per-target
  sorted local positions + remaining downstream count. Each constraint
  claims smallest local slot > own index, else a downstream slot. Fixes
  duplicated constraints with target downstream cross-node + correctly
  flags when only one local target sits BEFORE the constraint.
- collectDownstreamWildcardUuids: per-instance occurrences (not Set-
  deduped) so duplicated downstream targets each count as a separate
  claimable slot.
- RichTextPreview + ConstraintInstanceModal: cross-Context refs fall
  back to preview-resolver cache (sub_categories, option values,
  options_by_id, hasNullOption) so the canvas modal renders the actual
  wildcard name, sub-cat list, and chip suffix instead of "source"/
  "target" placeholders + stale matrix keys.
- Preview-resolver schema extended with subCategories / optionValues /
  optionsById / hasNullOption populated from embed-bundle response.
- Constraint save hydrate: wp_api modules + bundles endpoints fill
  exception source_value / target_value from source_id / target_id by
  looking up the wildcard's option list before validation. Heals
  library rows that stored only ids.
- TestRunner constraint table uses RichTextPreview for column headers
  + row values so nested @{uuid} refs render as chips.
- Inverted null filter UI: SubcategoryFilterPicker "Exclude null"
  checkbox + RefChip "!null" suffix render.
- Bundle size budget bumped 330 KB to 340 KB.
- Drop stale CHECKPOINT-*.md docs.
2026-05-25 00:17:20 +03:00
DumiFlex 0a8f376f4f fix(api,engine): qa hardening sweep — validators, concurrency, orphans
Lands the 15-bug catalogue from the 2026-05-19 creative-QA pass.

API layer (wp_api/):
- New _validators.py centralises meta + identifier + body-size checks
  so modules and bundles share the same shape rules
- SPA catch-all now returns JSON 404 for unknown /wp/api/* paths
  instead of the SPA shell (debugging hostile previously)
- create/update/import_from_workflow validate meta (name/desc/tags/
  category_id) before hitting the repo; tag list now rejects non-str
  entries (was silently stored, breaking the SPA type contract)
- IntegrityError on dangling category_id surfaces as 400, not 500
- Optional If-Match header on PUT enables opt-in optimistic
  concurrency — stale version → 409 instead of silent overwrite
- Bundle name collisions auto-suffixed with (copy)/(copy N) mirroring
  the modules-side forkModule rule
- Bundle children deduped by id at create + update time
- Deleting a module flags meta.orphaned=true on every bundle child
  whose snapshot referenced it so the SPA can render a stale badge

Engine layer (engine/modules/*_handler.py):
- wildcard: reject empty + __dunder var_binding, cap at 64 chars,
  reject duplicate option ids
- combine: reject empty template, cap template at 8000 chars, cap
  output_var at 64, reject __dunder output_var
- fixed_values: cap value name length, reject __dunder names, reject
  duplicate names across rows (was silent last-write-wins)
- derivation: reject duplicate rule ids (was nondeterministic for
  disabled_rule_ids / rule_order_override lookups)

Tests:
- New test_qa_hardening.py exercises every endpoint-level fix
- Extended test_payload_validation.py parametrised rejection matrix
- New test_spa_fallback.py case for /wp/api/* JSON 404

655 pytest pass, 1665 vitest pass, ruff clean.
2026-05-19 20:12:41 +03:00
DumiFlex 909af12b0e feat(api,spa): phase d + push-to-library follow-ups
Phase D — alias removal:

- Dropped PUT /wp/api/modules/{id}/payload + library_save.py module.
  PushToLibraryModal now calls the canonical PUT /wp/api/modules/{id}
  with flattened meta fields (name, description, tags) instead of the
  nested meta sub-object the alias accepted. Lifetime ended at one
  workflow review cycle; no external consumers exist.
- Updated test_save_to_library_unified.py and test_payload_validation
  .py to point at the canonical route. Deleted test_library_save_
  endpoint.py.

Bug 3b — modal silent on description/tags:

- Workflow rows only carry meta.name + meta.library_name at insert
  time, so opening PushToLibraryModal on an existing library-tracked
  row left description/tags blank even when the library row had them.
- Added seedFromLibrary() that fetches /wp/api/modules/<id> on modal
  open and fills in any blanks. Respects in-modal edits: only writes
  to fields that are still empty when the response lands.

Bug 3c — wildcard binding override didn't surface as outer mod state:

- Combine already checked inst.variable_binding in isModified();
  wildcard didn't. Brought the wildcard branch in line so renaming
  the binding on a wildcard surfaces the same orange dot a combine
  rename does. INSTANCE_FIELDS_PER_KIND already listed binding on
  both kinds — this row brings the outer dot to match the modal's
  mod gauge.

Bug 3a — reset-icon semantics after Update existing:

- The right-click push-to-library flow updated the library entry but
  never refreshed the workflow row's meta.library_name; the per-field
  reset-to-library button then offered to revert to the pre-save
  name. Both right-click onPushSaved and the in-modal
  onPushToLibrarySaved now stamp library_name alongside name +
  payload_hash on Update mode so reset reflects the new library
  state.

Tests: 1665 SPA + 59 Python passing. Ruff + lint clean.
Bundle size: 306499 / 307200 (701 bytes headroom).
Live QA pending: needs ComfyUI restart to pick up the route removal +
Python validator paths.
2026-05-19 18:54:13 +03:00
DumiFlex d661c6d87b fix(api): plug payload validation hole + draft save-to-library rethink
Bug 6 fix: wildcard + fixed_values handlers had no validate_payload
method, so the base no-op was silently accepting any shape on every
write boundary. Verified live: PUT /payload with {options: 'not-an-array'}
returned 200 and persisted the garbage; POST accepted {options: 42};
cross-type pollution (combine-shaped payload PUT to a wildcard) also
succeeded with 200.

Changes:
- engine/modules/wildcard_handler.py: validate_payload checks options
  list shape + per-option value/weight types + non-negative weights +
  optional sub_category strings + var_binding identifier + sub_categories
  list-of-strings.
- engine/modules/fixed_values_handler.py: validate_payload checks values
  list shape + per-value name/value types + identifier rule on non-empty
  names.
- wp_api/modules.py: validates payload on POST /modules, PUT /modules/{id}
  (when payload field is present), and POST /modules/import-from-workflow.
  Shared _validate_payload_for_type helper.
- wp_api/bundles.py: validates every child snapshot's payload on POST
  /bundles and PUT /bundles/{id} when children[] is in the body — closes
  the side door that let bundle writes bypass per-module validation.
- tests/wp_api/test_payload_validation.py: 26 cases covering happy paths
  + malformed payloads per kind, cross-type pollution, import-from-
  workflow, and bundle-children side door.
- tests/wp_api/test_bundles_crud.py: fixture combine child gained the
  required output_var field (it was implicitly invalid before).

Plus docs/superpowers/specs/2026-05-19-save-to-library-rethink-design.md:
the broader four-phase plan that addresses Bugs 1-5 (meta drop, bundle
propagation, fork discoverability, modal button visibility, inline
modules can't save). Phase A (this commit) plugs the validation hole.
Phases B-D will land separately after design review.

Live QA reproduction order remains in the design doc.
2026-05-19 17:34:37 +03:00