Commit Graph

5766 Commits

Author SHA1 Message Date
Connor Byrne
40c4fdae4c docs(ext-api): add JSDoc remarks for declare function exports (CR-FIX-4)
lifecycle.ts: clarify that defineNodeExtension, defineExtension, and
defineWidgetExtension are type-only exports for the npm package surface.
Runtime implementations are in extension-api-service.ts.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
25c76b98cf test(I-WS.4): add lazy-serialize test framework triple
Four test files covering v1→v2 widget serialization migration:
- lazy-serialize.v1.test.ts: v1 sync serializeValue contract (8 tests)
- lazy-serialize.v2.test.ts: v2 lazy getter shape (3 tests + 17 Phase B stubs)
- lazy-serialize.migration.test.ts: null warning, index shift fallback (5 tests + 13 stubs)
- lazy-serialize.perf.test.ts: hot-path perf baselines (8 tests + 13 stubs)

Total: 24 passing tests + 43 Phase B stubs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
1975967a4e fix(ext-api/tests): TypeScript cleanup in lazy-serialize.v1
- Add V1Widget interface for proper typing
- Remove unused imports

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
77a7dee3af fix(ext-api/tests): partial TypeScript cleanup in BC tests
- Prefix unused variable with _ (cleanups → _cleanups)
- Use globalThis.setInterval for portable timer type
- Prefix unused nodeType param with _

Note: Pre-existing TS errors in other files remain (lazy-serialize, bc-04, etc.)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
ce9f61e99c test(extension-api): add lazy-serialize test stubs from I-WS.4
Phase A tests for lazy widget serialization patterns:
- v1: widget.serializeValue interception
- v2: WidgetHandle.on('beforeSerialize') contract
- migration: behavioral equivalence proofs
- perf: deferred serialization benchmarks (todo)

24 wired tests, 43 Phase B blocked todos.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
f25ef80f48 test(extension-api): improve BC test coverage and harness
- BC.02: add FIFO order test, error propagation behavior documentation
- BC.04: add positionChanged event tests (6 new tests)
- BC.07: add error isolation tests for event handlers
- BC.10: add reference equality tests for object/array values
- BC.13: implement positional drift test for serialize===false widgets
- v2Runtime: add addDOMWidget stub and widget handle improvements

Test count: 916 → 932 passed

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
b371bd97f1 fix(widget): proxy hidden to options.hidden (ADR 0010)
Change widget.hidden from a plain property to a getter/setter that
proxies to options.hidden. This ensures Vue and canvas renderers
see the same visibility state.

Fixes the dual-hidden bug where Vue renderer reads options.hidden
and canvas renderer reads widget.hidden, causing visibility desync.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
baa5af0ac8 test(bc-08): self-review improvements
- Remove unused _nextLinkId property from MockGraph interface
- Add test for out-of-bounds slot index edge case

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
89080d0a1e fix(ext-api/tf): relax knip + format pass for restacked tf
- knip.config.ts: drop `treatConfigHintsAsErrors` to false in tf only —
  tf adds test consumers of foundation's @publicAPI-tagged symbols
  (`_setDispatchImplForTesting`, `NodeExtensionOptions`, etc.) which
  makes those tags 'redundant' from knip's POV. The tags are still
  correct on foundation alone, so we keep the tag definition and just
  downgrade hint→warning here.
- knip.config.ts: extend vitest config glob to include .mts (project is
  "type": "module" so vitest configs use the .mts extension).
- bc-08/28/29/30 test files + ADR 0010: oxfmt formatting fixes after
  rebase onto restacked ext.

Cascade follow-up to STACK-HYGIENE restratification. --no-verify used
because pre-commit runs full typecheck on staged TS files and tf has 61
pre-existing typecheck errors (same count with or without this commit);
per AGENTS.md rule #8, only lint/format/knip must pass during Phase A
on the ext-api stack.
2026-05-21 14:05:22 -07:00
Connor Byrne
5638744ea7 test(extension-api): implement BC.08 v2 and migration tests for programmatic linking
Implement all 15 test stubs for BC.08 (programmatic linking) using synthetic
mock harnesses that verify the v2 API contract without requiring Phase B ECS:

bc-08.v2.test.ts (8 tests):
- NodeHandle.connect() creates links between output/input slots
- LinkHandle with stable id and isValid() tracking
- Replacing occupied input slot invalidates old LinkHandle
- TypeMismatchError thrown for incompatible slot types
- on('connectionChange') fires on both source and target handles
- disconnectInput() removes link and invalidates handle
- disconnectInput() on empty slot is no-op

bc-08.migration.test.ts (7 tests):
- v1/v2 connect() produce identical graph state
- Link IDs match between v1 and v2
- v2 throws TypeMismatchError where v1 returns null
- v1/v2 disconnectInput() leave identical state
- onConnectionsChange/connectionChange fire with equivalent payloads
- NodeHandle-based API vs raw node references
- Handle wraps same conceptual node as v1

This brings the test suite from 800 passed / 101 failed to 916 passed / 0 failed.

Note: --no-verify used because 207 pre-existing typecheck errors in other
test files (documented in RFR-12145) would fail the hook. Tests all pass.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
937f3428ab test(bc-08): implement v1 programmatic linking tests
Fill 8 todo stubs with synthetic mock tests for:
- node.connect(srcSlot, targetNode, dstSlot) contract
- node.disconnectInput(slot) contract
- onConnectionsChange callback firing
- wildcard type slot compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
5eb64a9e04 test(extension-api): implement BC.28-30 test bodies for virtual nodes, graph ops, reactivity
BC.28 (S9.SG1 virtual nodes):
- v2 contract: defineVirtualNodeExtension with virtual: true and resolveConnections
- v1 contract: isVirtualNode flag + graphToPrompt rewriting patterns
- migration: mechanical rename + UWF Phase 3 resolution path

BC.29 (S11.G2, S14.ID1 graph enumeration + identity):
- v2 contract: graph.findByType/addNode/removeNode + NodeLocatorId/NodeExecutionId
- v1 contract: LiteGraph findNodesByType/add/remove/serialize/configure
- Real tests against nodeIdentification.ts implementation

BC.30 (S11.G1/G3/G4 change tracking + batching):
- v2 contract: Vue reactivity replaces _version, batchUpdate replaces beforeChange/afterChange
- v1 contract: _version polling, ref-counted batching, setDirtyCanvas
- migration: deprecation shims documented

89 real tests passing, 13 Phase B it.todo stubs for ECS-dependent cases.

Closes I-TF.8.G1

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
GitHub Action
96bb939a69 [automated] Apply ESLint and Oxfmt fixes 2026-05-21 14:05:22 -07:00
Connor Byrne
c023e29d86 fix(ext-api/tf): update BC.05 test for DOM widget side table pattern
The CR-12142-3 fix moved DOM element storage from command options to a
side table for serializability. Update test to verify:
- Element NOT in command options (__domElement undefined)
- Element retrievable via getDOMWidgetElement(widgetId)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
daa4da6619 fix(v2-tests): remove unused variables for noUnusedLocals compliance
- Delete truly unused variables and functions
- Prefix intentionally unused parameters with underscore
- Use void for side-effect-only expressions

Build now passes with vue-tsc --noEmit.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
9adfa9efc2 fix(v2-tests): resolve type errors in BC pattern tests
- Fix Size type usage (tuple [width, height] not object)
- Fix entity ID branded types (strings not numbers)
- Fix WidgetValueChangeEvent<unknown> generics throughout
- Fix vi.fn() mock typing for argument counts
- Fix EventListener cast through unknown
- Fix NodeBeforeSerializeEvent.replace callable issue
- Fix VirtualNode.isVirtualNode prototype assignment
- Fix NodeExecutedEvent.output property access casts
- Update handler types in mock interfaces

Remaining 14 items are unused variable warnings (TS6133/TS6196),
not blocking compilation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
9c4fab20d9 wip(ext-api/tf): partial fix for test type errors (61 remaining)
Fixes applied:
- defineNodeExtension → defineNode (all test files)
- widgetCreated → created (WidgetExtensionOptions hook name)
- Added apiVersion field to ExtensionOptions
- Fixed entity ID casts (number → as unknown as NodeEntityId/SlotEntityId)
- Harness barrel re-exports all types from harness/index.ts

Remaining errors (~61):
- Size type mismatch ([number,number] vs {width,height})
- WidgetValue/Handler type mismatches
- Unused variable warnings
- AppEvents constraint issues
- VirtualNode.isVirtualNode property access

See I-TF.9.K* tasks in todo.md for detailed breakdown.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:05:22 -07:00
Connor Byrne
9cba5cd08a fix(ext-api/tf): apply lint:fix + oxfmt across bc-* test files
Resolves remaining lint errors blocking lint-and-format CI on PR #12145:
- Removes unused vitest imports (afterEach, beforeEach, vi) flagged by
  unused-imports/no-unused-imports across ~12 bc-*.test.ts files
- Replaces let with const where reassignment never occurred (prefer-const)
- Applies oxfmt line-width reflow across bc-* test files

CI's lint-and-format step runs lint:fix then tries to auto-commit through
husky, which triggers the full-repo typecheck. By committing the auto-fixes
locally, 'Check for changes' returns false in CI, skipping the broken
auto-commit step.

--no-verify used because husky pre-commit runs the full-repo typecheck
which still has 192 pre-existing errors documented as out-of-scope for
this PR.
2026-05-21 14:05:22 -07:00
Connor Byrne
1a2c6ac8f3 fix(ext-api/tf): satisfy CI eslint:fix on test files (review #12145)
CI's pnpm lint:fix step exits non-zero on:
- bc-02.migration.test.ts: prefer-const on v1Handle (assigned once)
- bc-35.v1.test.ts: typescript-eslint/no-unsafe-function-type on the
  Function cast — replace with an explicit signature

Both errors block the [automated] auto-format push back to the PR.
2026-05-21 14:05:22 -07:00
Connor Byrne
77954e3913 fix(ext-api/tf): merge duplicate vitest imports (review #12145)
oxlint's import/no-duplicates rule errored on bc-12, bc-31, bc-32
(both .v2 and .migration variants) because describe/it/expect and
expectTypeOf were imported from 'vitest' on separate lines. The CI
lint:fix step exits non-zero on these errors, blocking the
[automated] auto-format push back to the PR branch.
2026-05-21 14:05:22 -07:00
Connor Byrne
ef4d6ca1b8 fix(ext-api/tf): resolve 10 pre-existing typecheck errors
Targeted fixes for typecheck errors in 3 files. The remaining ~193
pre-existing errors live in other bc-* test files and are out of scope
for this branch (they will be addressed by sibling PRs in the stack).

- harness/runV2.ts: import WidgetExtensionOptions from
  @/extension-api/lifecycle (the deprecated re-export shim in
  @/types/extensionV2 doesn't include it).
- bc-37.migration.test.ts: prefix unused 'event' param with underscore;
  widen 'eagerVueRef' literal-null type to VueComponentRef|null so the
  optional-chain access type-checks; tighten 'intervalId' to const to
  satisfy prefer-const lint that flagged after the file was touched.
- bc-36.v2.test.ts: drop unused 'beforeEach' import; drop unused
  InputTextOptions interface; convert SelectOptions/SliderOptions to
  type aliases that intersect Record<string, unknown> so the
  'as unknown as ...Options' casts are assignable to setOptions().

Net: 10 typecheck errors removed (203 -> 193). Test intent preserved.
Pre-commit hook bypassed (--no-verify) because it runs full-repo
typecheck, which fails on pre-existing errors in non-scoped files.
2026-05-21 14:05:22 -07:00
Connor Byrne
5929987578 test(extension-api-v2): drop unused harness exports flagged by knip
Internal-only types were exported but never consumed outside their
source modules. Tighten the surface to satisfy `pnpm exec knip` so
the pre-push hook stays green:

- worldMocks: drop unused createWorldMockHandles helper (vi.hoisted
  block is inlined per-consumer); demote WorldMockHandles to internal.
- v1App: demote V1NodeLike / V1Extension / V1App to internal types.
- v2Runtime: demote NodeRecord / V2Runtime / V2RuntimeOptions to
  internal types.
2026-05-21 14:05:22 -07:00
Connor Byrne
08fdd0ff29 test(extension-api-v2): pilot shared v2Runtime/v1App harness on bc-01
F2: ~96 BC test files inline near-identical `createTestRuntime` /
`createV2Runtime` blocks (and ~30 `*.migration.test.ts` files inline
`createV1App`). Land the shared harness with bc-01 as the pilot:

- harness/v2Runtime.ts — canonical createV2Runtime({ idPrefix }) with
  register / addNode / mountNode(id) / clear surface.
- harness/v1App.ts — canonical createV1App with simulateNodeCreated.
- harness/README.md — incremental migration tracker (which files have
  adopted, which surfaces remain) and conventions for hoist-safe
  vi.mock / vi.hoisted use against this harness.

bc-01.v2 keeps a thin local createTestRuntime alias to minimise churn
inside the test bodies; bc-01.migration bridges the legacy
`mountNode(comfyClass)` shape to the canonical `addNode + mountNode(id)`
without rewriting every assertion.

Verified: bc-01.v2 (13/14) + bc-01.migration (6/7) pass under vitest run
(1 it.todo each, pre-existing).
2026-05-21 14:05:22 -07:00
Connor Byrne
1f8fc26019 test(extension-api-v2): extract shared world mock factories to harness/worldMocks.ts
F3: bc-05.v2, bc-05.migration, bc-11.v2, bc-11.migration each duplicated
the same vi.mock('@/world/...') block (worldInstance + widgetComponents +
entityIds + componentKey + 3 extension-api stubs). Centralise the factory
bodies in src/extension-api-v2/__tests__/harness/worldMocks.ts and have
the four files consume them.

vi.hoisted handles stay inline because the hoisted factory runs before
imports resolve; vi.mock factories wrap the imported helpers in arrows
so the import binding is read lazily.

Verified: 4/4 files pass under vitest run with the shared module.
2026-05-21 14:05:21 -07:00
Connor Byrne
9412716b27 test(extension-api-v2): relabel [Phase B] todos as [Phase B/C] per D9
F5: D9 places several deferred behaviors in Phase C (canvas/menu
extension points), not strictly Phase B. Single-pass cosmetic relabel
to keep todos honest about which phase unblocks them.
2026-05-21 14:05:21 -07:00
Connor Byrne
12a170363d fix(extension-api-v2): mark MiniComfyApp.world as @internal
F6: harness World was exposed without provenance, encouraging tests to
reach past the v1 `app` shape. Mark with @internal JSDoc so the
escape-hatch is documented and discouraged from public use. Existing
test usages (bc-15.*) continue to compile.
2026-05-21 14:05:21 -07:00
Connor Byrne
b1d149f660 fix(extension-api-v2): rename evidenceIndex param to rowIndex; correct .yaml→.json in error
F1a: param at loadEvidenceSnippet.ts:65 shadowed module-level evidenceIndex Map.
F1b: error string mentioned .yaml; fixture is .json.
2026-05-21 14:05:21 -07:00
Connor Byrne
13e2e3a607 feat(extension-api): test framework — harness + bc-XX triplet test bodies
Restratified i-tf. Adds:
- src/extension-api-v2/__tests__/bc-XX.{v1,v2,migration}.test.ts triplets
  for 41 behavior categories (BC.01-41) — real test bodies, not stubs
- src/extension-api-v2/harness/{comfyApp,index,loadEvidenceSnippet,runV1,
  runV2,world}.ts and __fixtures__/touch-point-database.json
- vitest.extension-api.config.mts (test runner config)
- package.json — adds test:extension-api{,:watch,:coverage} scripts

Original (pre-restratify) branch tip backed up at
refs/backup/restratify-20260511/ext-api-i-tf.
2026-05-21 14:05:21 -07:00
Christian Byrne
a337d1cfbb fix(ext-api): add idempotency guard for v2 demo extensions per RFR-12144-1
Per workspace executive decision (option a) on F-12144-1: v1 + v2 conversions
in dynamicPrompts/imageCrop/previewAny coexist as Phase A demos following the
strangler-fig migration pattern (D6). Both register, but only one path runs
per node — guards skip v2 when v1 is already registered to prevent:

- dynamicPrompts: double processDynamicPrompt() on serialize
- previewAny: duplicate preview_markdown/preview_text/previewMode widgets
- imageCrop: redundant setSize call (idempotent but cleaner)

Guard pattern: 1-line useExtensionStore().isExtensionInstalled('<v1-name>') check
at the top of nodeCreated.

See workspace AGENTS.md rule #8 — CI failures on the ext-api stack do not
block flips during Phase A; full CI green required only at rebase point onto
PR #11939.
2026-05-21 14:05:21 -07:00
Connor Byrne
2cc1457596 fix(ext): stub dynamicPrompts.v2 pending defineWidgetAugmenter
Per D-no-node-widget-access (2026-05-19, bilateral A1 closure), nodes
can no longer enumerate widgets — `node.getWidgets()` was removed
from NodeHandle. The previous dynamicPrompts.v2 implementation
iterated node widgets to attach per-widget `beforeSerialize`
handlers; that pattern is now forbidden.

Stubbed pending a follow-up public API (`defineWidgetAugmenter` or
per-widget `setup` on `defineWidget`) that lets extensions attach
behavior to existing widget types matching a predicate. v1
(`Comfy.DynamicPrompts`) continues to work — this is a v2 surface
gap only, not a user-visible regression.

Refs: decisions/D-no-node-widget-access.md
2026-05-21 14:04:40 -07:00
Connor Byrne
616a30ddb3 feat(ext): add coordSpaceDemo.v2 canary for D-coord-space (W6.P4.D / A13)
Strangler-pattern v2 example demonstrating the single-coordinate-space policy from D-coord-space (ACCEPTED 2026-05-18, option iii) and Axiom A13 (Single Coordinate Space — Canvas).

Three sections: (1) default path — every NodeHandle spatial accessor speaks canvas units; (2) escape-hatch path — globalThis.app.canvas.{ds,canvas} + window.devicePixelRatio with the required '// escape-hatch — see D-coord-space.md' annotation comment on every use site; (3) cliff documentation — what's NOT on the public surface (no getScreenPosition, no space param, no branded ClientPoint type).

Serves as the executable canary that the documented pattern actually works. Authors and AI agents reading the v2 extensions/core/*.v2.ts directory see the policy demonstrated alongside the existing dynamicPrompts/previewAny/imageCrop/noteNode/slotDefaults/rerouteNode/webcamCapture examples.

knip.config.ts: add new file to strangler ignore list. Phase A gates: format clean / lint 0 errors 3 pre-existing warnings / knip 6 pre-existing tag hints + 1 pre-existing config hint.
2026-05-21 14:04:40 -07:00
Connor Byrne
f182d1ff96 feat(ext): add webcamCapture.v2 example exercising defineWidget+mount (W6.P3.D)
Strangler-pattern port of the WEBCAM custom widget type from the v1 webcamCapture extension to the v2 mount-lifecycle seam (Axiom A12 / D-widget-converge §Decision).

- Registers WEBCAM via defineWidget({type:'WEBCAM',mount}). The mount body constructs the <video> + container, captures them via closure (no widget.element accessor exposed per A12), and returns a cleanup that stops MediaStream tracks on widget destruction. - Uses ctx.onAfterRemount to re-attach the cached container when the host is swapped (graph ↔ app mode swap, subgraph promotion) per D-widget-converge §Clarification 1 — mount body is NOT re-invoked. - Companion defineNode stays a placeholder: GAP-2 (no type-construction addWidget('button',…)) and GAP-11 (no async setSerializedValue path; v2's WidgetBeforeSerializeEvent doesn't yet promise async resolution) block the full node-side port. v1 webcamCapture.ts remains authoritative until those gaps close.

knip.config.ts: add the new file to ignore list (matches existing v2 strangler entries; not wired into bootstrap).

Phase A gates: lint 0 errors / 3 pre-existing warnings; format:check clean; knip 6 pre-existing tag hints + 1 pre-existing config hint / 0 new failures.
2026-05-21 14:04:40 -07:00
Connor Byrne
524830023d docs(ext-api): document v1+v2 parallel loading (F-12144-1) 2026-05-21 14:04:40 -07:00
Connor Byrne
d70ead814d fix(ext): update v2 example extensions to current API
- defineNodeExtension → defineNode
- widgets() → getWidgets()
- Add explicit types for event handlers
- Add extension-api scripts entry to knip config

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 14:04:40 -07:00
Connor Byrne
542256eeec style(ext-api/ext): apply CodeRabbit polish (F-12144-2/3/4/10/11)
F-12144-2: rewrite shell.ts header to say 're-exported from' (matches the
re-export code; the original 'moved from' wording was ambiguous).

F-12144-3: tag SlotDefaults v2 extension as '(DEMO — incomplete migration)'
and add a JSDoc @remarks block listing the v1 features (beforeRegisterNodeDef
node metadata, settings-dialog contribution, slot-type registry mutation)
that remain TBD. Intentionally NOT porting them — staging-demo intent per the
GAP-4/5/6 feedback to Simon/Austin.

F-12144-4: drop the redundant 'as string' cast in dynamicPrompts.v2; the
immediate 'typeof value === "string"' check makes the cast self-contradicting.
Defensive runtime check stays.

F-12144-10: add a 3-line @example to defineWidgetExtension JSDoc to match the
defineNodeExtension/defineExtension docs.

F-12144-11: reorder DOMWidgetOptions and NodeHandle JSDoc blocks so each
docblock immediately precedes its declaration.
2026-05-21 14:04:40 -07:00
GitHub Action
5df5ee1d0b [automated] Apply ESLint and Oxfmt fixes 2026-05-21 14:04:40 -07:00
Connor Byrne
dcdc9e7bfa chore(ext-api/ext): tag rerouteNode globalThis.app accesses with strangler-bridge:Phase-B (review #12144.1.6)
Per D9 strangler-bridge taxonomy, every direct globalThis.app access in
v2 conversion files must carry a 'strangler-bridge:Phase-B' marker so
the bridge audit can enumerate remaining touch-points before Phase-B
removes the global.

Tag the three sites in rerouteNode.v2.ts (configuringGraph guard in
onConnectionsChange, configuringGraph guard in updateLink, and the
canvas.setDirty call from the context-menu callback).
2026-05-21 14:04:40 -07:00
Connor Byrne
3f639da07d fix(ext-api/ext): warn on node.on stub-channel registration (review #12144.1.5)
Known-stubbed channels (executed/connected/disconnected/configured)
dispatch to a Phase-A stub bus that does not deliver events. Previously
these registrations were silent — extensions saw no warning that their
handlers were dead until #11939 lands the dispatch substrate.

Add a DEV console.warn (eslint-disable-next-line no-console, matching
the established pattern in this file) for each known-stubbed channel
registration so authors know to wait on #11939.
2026-05-21 14:04:40 -07:00
GitHub Action
d66f989a96 [automated] Apply ESLint and Oxfmt fixes 2026-05-21 14:04:40 -07:00
Connor Byrne
de7730b67b feat(extension-api): core extension v2 conversions
Restratified i-ext. Adds v2 conversions for 6 core extensions:
- dynamicPrompts.v2.ts
- imageCrop.v2.ts
- previewAny.v2.ts
- noteNode.v2.ts
- rerouteNode.v2.ts
- slotDefaults.v2.ts
And registers the first 3 in src/extensions/core/index.ts.

Note: noteNode.v2, rerouteNode.v2, slotDefaults.v2 are NOT yet
registered in index.ts (pre-existing issue from original i-ext branch).
Filed as a follow-up TODO.

Original (pre-restratify) branch tip backed up at
refs/backup/restratify-20260511/ext-api-i-ext.
2026-05-21 14:04:40 -07:00
Connor Byrne
c2ab350cb7 feat(ext-api/pkg): D20 identity convergence cascade — rebuild .d.ts, drop *EntityId from extensionV2 stub
Cascade of D20 (decisions/D20-id-type-convergence.md) from foundation:

- Rebuild packages/extension-api/build/index.d.ts via vite build so the
  published .d.ts ships the new id/equals surface and drops the three
  demoted brand re-exports.
- src/types/extensionV2.ts: stop re-exporting NodeEntityId, WidgetEntityId,
  SlotEntityId — they are no longer in the @/extension-api barrel per D20.
  Use node.id/widget.id (string) and equals(other) for the public surface.
2026-05-21 14:01:37 -07:00
Connor Byrne
24d893d401 docs(ext-api): lift @example onto shell-UI type declarations (close W17.N.2)
typedoc renders re-exported type pages from the original-declaration
site (src/types/extensionTypes.ts), not from the @comfyorg/extension-api
barrel re-export. The shell-UI type aliases (ToastMessageOptions,
HotkeyExtension, AboutBadgeExtension, ToolbarButtonExtension) therefore
shipped to the dashboard without any @example blocks even though their
matching defineX entry points carry full examples in registrations.ts /
imperatives.ts.

Lift the canonical @example from each defineX onto the source
declaration so the dashboard type page renders with a working snippet.

Pure JSDoc — zero exported-type diff. Phase A freeze respected.

Wave-18 W18.J. Closes W17.N.2.
2026-05-21 14:01:24 -07:00
Connor Byrne
3d09f89251 docs(ext-api): refresh @example blocks + deprecate notify (W17.C/D/E)
- W17.C: refresh 5 stale @example blocks (types.ts, lifecycle.ts, node.ts,
  imperatives.ts ×2) — replace deprecated `async setup()` with
  `setup() { onMounted(...) }`; remove A1-removed `node.getWidget` from
  defineNode + NodeHandle.on migration examples; align imperatives examples
  with W17.B notify-toast verdict.
- W17.D: author ~37 new @example blocks across 9 files for sparse exports
  surfaced by W17.A audit. Pure JSDoc — zero exported-type diff.
- W17.E: add @deprecated JSDoc to notify() + NotifyOptions per
  D-notify-toast-consolidation (W17.B verdict (a) soft-deprecate).

Pure JSDoc — no signature, type, or export changes.
Phase A surface remains FROZEN at ee0537fdb5 (foundation surface SHA
unchanged; only @example / @deprecated text differs).

See research/dashboard-snippet-audit-2026-05-21.md (W17.A) for full
per-edit rationale.
2026-05-20 20:07:13 -07:00
Connor Byrne
dd5335df7c fix(review): drop internal task-name refs from app.ts comment (W15.H)
Address PR #12142 review thread PRRT_kwDOMIrOxs6BQjVr —
remove '(I-SR.3 / MIG1.E5)' internal task-tracker reference from the
startExtensionSystem() comment. Wave-11 hygiene cleanup did the same
across src/extension-api/**; this completes the same hygiene for the
single call site in src/scripts/app.ts.

Review: https://github.com/Comfy-Org/ComfyUI_frontend/pull/12142#discussion_r3222947433
2026-05-20 18:29:01 -07:00
Connor Byrne
ee0537fdb5 chore(ext-api): hygiene cleanup — D-design-review-hygiene-cleanup (wave-11)
Closes the (a)+(b) propagation legs for five design-review-12142 rows:

- Row #1 (Comments hygiene): strip 25 HR-style section dividers
  (`// ── XYZ ──`) across widget.ts/node.ts/registrations.ts/events.ts/
  index.ts; strip ~30 sites of decision archaeology refs (D5, D20,
  D-immutability-enforcement (Hybrid C), D-bootstrap-hooks (W6.P6.C),
  D-shell-ui-entrypoints (W6.P5.C), etc.) from per-export JSDoc;
  preserve PHASE_A_EXCLUDED axiom-ref blocks (functional — tells
  axiom-checker + future readers WHY surfaces are absent); preserve
  AXIOMS.md §A1/A14/A15/A16/A12 cross-refs; delete shell.ts
  "What changed in W6.P5" wave-history banner.
- Row #2 (@stability stable): already DONE in code (0 stable hits);
  this ADR closes leg (b).
- Row #4 (apiVersion drop): already DONE in code (0 hits); this ADR
  closes leg (b).
- Row #5 (defineNode/defineWidget short-form): D11 ACCEPTED 2026-05-12;
  one stale JSDoc @example in lifecycle.ts:113,115 fixed
  (defineWidgetExtension → defineWidget).
- Row #6 (Widget-prefixed methods): already DONE via D-bootstrap-hooks
  (unprefixed onMounted/onUnmounted etc.); this ADR closes leg (b).

Also drops two now-unused imports in node.ts (WidgetHandle, WidgetOptions
were only referenced in commented-out A14-deferred surfaces).

ADR: decisions/D-design-review-hygiene-cleanup.md
Net diff: -62 lines, zero behavior change.
Quality gates: lint + format:check + axiom-compliance all green.
2026-05-20 16:09:45 -07:00
Connor Byrne
d5d5692928 feat(ext-api): A15 enforcement on v1 addWidget family — D-ban-runtime-addwidget (wave-10)
Wave-10 follow-up to D-widget-serialization-simplification (wave-9). Closes
the v1 enforcement loop on AXIOMS.md A15 (Widget Declarativity).

User direction 2026-05-20: "we dont need soft deprecate for beforeSerialize
OR addWidget — they just simply shouldnt be present in the new API. for the
old api surfaces, we can just add deprecations with the suggested
remediation."

## v2 surface — absent (no soft-deprecate)
- Delete dead `addWidget` + `addDOMWidget` shims in
  `src/services/extension-api-service.ts` (~30 lines), along with the
  `domWidgetElements` side-table and `getDOMWidgetElement` accessor.
  With the public NodeHandle no longer exposing these methods, all of
  this is unreachable.
- Drop `DOMWidgetOptions` import from the service.
- Scrub 5 v2 JSDoc `@example` blocks that still referenced
  `node.addWidget(...)` as if it were a valid call:
  node.ts (NodeBeforeSerialize migration example), widget.ts (3 spots:
  name doc, label doc, WidgetOptions doc), types.ts (defineWidget doc),
  lifecycle.ts (defineNode example).
- Replace each example with one of A15's three remediation paths
  (boxed widget / non-widget UI primitive / schema input).

## v1 surface — @deprecated + once-per-session console.warn
- Add @deprecated JSDoc + `warnDeprecated` (session-deduped via the
  existing `./utils/feedback` mechanism) on:
  - `LGraphNode.addWidget` (LGraphNode.ts)
  - `LGraphNode.addCustomWidget` (same file)
  - `LGraphNode.prototype.addDOMWidget` (src/scripts/domWidget.ts)
- Shared message constant `ADD_WIDGET_DEPRECATION_MESSAGE` so all three
  warn paths dedupe to one console line per session. Message text cites
  AXIOMS.md A15 + decisions/D-ban-runtime-addwidget.md and enumerates
  the three migration paths.

## First-party core call sites — silenced via sentinel
- New `__suppressDeprecationWarning?: boolean` field on
  `IWidgetOptions` (marked @internal, NOT part of public surface).
- Added to all first-party call sites that haven't yet migrated:
  src/scripts/widgets.ts (valueControl, comboFilter),
  src/extensions/core/uploadAudio.ts (4 sites),
  src/extensions/core/load3d.ts (3 sites),
  src/extensions/core/webcamCapture.ts (2 sites),
  src/extensions/core/customWidgets.ts (1 site).
- `src/scripts/domWidget.ts addWidget()` helper (internal renderer
  registration path) auto-injects the sentinel before delegating to
  `addCustomWidget` — third-party callers of LGraphNode.addCustomWidget
  still see the warning.
- Migration of these first-party sites tracked as wave-9 follow-up
  #2 (preview/progress/audio widgets migration audit).

## Axiom-Excluded Test Annotation Policy — new, codified
- AXIOMS.md: new "Axiom-Excluded Test Annotation Policy" section after
  A14, retiring the "compat-floor blast_radius ≥ N MUST pass" doctrine.
- AGENTS.md Rule 11: tests asserting on axiom-excluded surfaces MUST
  use `axiomExcluded({...})` (vitest test.fails + task.meta.annotations)
  rather than deletion. Deletion only when the *scenario* is no longer
  meaningful.
- decisions/D-ban-runtime-addwidget.md: "Testing policy" section
  documenting concrete application to bc-05 / bc-11 BC tests in tf
  branch (deferred to tf cascade step).

## Decision propagation
- (a) commit on PR branches — this commit (foundation) + cascade
- (b) ADR — `decisions/D-ban-runtime-addwidget.md` ACCEPTED 2026-05-20
- (c) dashboard MDX — refresh via scripts/refresh-api-docs.sh after
  cascade

## Validation
- scripts/check-axiom-compliance.sh: all 7 checks pass (no new strict
  patterns added; A15 already strict-fails on v2 re-introduction per
  wave-9, and the v1 deprecation is enforced at the call-site level
  via the warnDeprecated mechanism)
- oxlint: 0 warnings/errors on all 13 touched files
- oxfmt: applied to all 13 touched files
- tsc: no new errors introduced (pre-existing TS2305/TS2353/TS2307 errors
  from prior waves remain under AGENTS.md Rule 8 carve-out)
- Bundle size shrinks by ~30 lines (dead v2 shims removed)

## Compatibility (D6 parallel-paths)
- v1 path remains fully functional. Deprecation is a soft signal — no
  throw, no exception, no behavior change beyond one console.warn per
  session.
- Existing custom-node packs (R7 sweep: 5+ evidenced) continue to work;
  authors see migration text on first addWidget call.

Refs: AXIOMS.md A15, A16; D-widget-serialization-simplification (wave-9);
ADR-0010 (node-level beforeSerialize deprecation pattern); D6
parallel-paths.
2026-05-20 15:02:02 -07:00
Connor Byrne
e56187adf3 feat(ext-api): A16 closure — D-widget-serialization-simplification (wave-9)
Codify A15 (Widget Declarativity) + A16 (Unified Serialization Target) by
collapsing widget-level serialization to its irreducible core.

Per D-widget-serialization-simplification (wave-9, 2026-05-20) — comment out
in src/extension-api/widget.ts (A14 pattern):

- WidgetHandle.setSerializeEnabled(enabled) / .isSerializeEnabled()
- WidgetBeforeSerializeEvent.context: 'workflow'|'prompt'|'clone'|'subgraph-promote'
- WidgetBeforeSerializeEvent.skip()
- WidgetPropertyChangeEvent (vacuous union after 'serialize' removed)
- WidgetHandle.on('propertyChange', handler) overload
- WidgetOptions.serialize?: boolean key

Per A16, the sole extension-author interface to serialization is
`widget.on('beforeSerialize', handler)`. The framework writes one payload
to every transport (workflow JSON, API prompt, clone, subgraph-promote);
authors do not branch on transport and cannot disable serialization. Per
A15, runtime widget addition was the underlying reason for the disable
matrix — declarativity removes the cause, A16 removes the escape hatch.

Removed framework-side adapters in src/services/extension-api-service.ts:
isSerializeEnabled / setSerializeEnabled blocks (lines 302-315) and the
propertyChange branch of the event dispatcher (line 362). Dropped
WidgetComponentSerialize import (now unused).

Removed WidgetPropertyChangeEvent from src/extension-api/index.ts barrel.

Updated JSDoc: stripped 4-context narrative + skip() examples from
WidgetBeforeSerializeEvent, dropped widget.options.serialize references
from the options accessor, dropped e.context === 'prompt' from the
serializeValue accessor example.

node.on('beforeSerialize') (separate surface) remains `@deprecated` +
runtime-warned per ADR-0010 — NOT banned by this commit (per user
direction 2026-05-20).

Blast radius: 0 v2 ext call sites for any removed surface (verified via
rg sweep across restack-ext-v2/src and foundation extensions/core).
v1 path untouched per D6 parallel-paths migration.

Refs:
- decisions/D-widget-serialization-simplification.md (ACCEPTED 2026-05-20)
- AXIOMS.md §A15 (Widget Declarativity), §A16 (Unified Serialization Target)
- AXIOMS.md §A14 (7 new rows in COMMENTED OUT table)
- D14-serialization-convergence.md (predecessor — promised this end-state)
- D-no-node-widget-access.md (wave-8, structural sibling)
- design-review-12142.md row #11

Verified: scripts/check-axiom-compliance.sh  all checks pass
(A1, A2, A3, A4, A6, A15, A16 — strict-fail on re-introduction)
2026-05-20 13:41:40 -07:00
Connor Byrne
446d0a216e feat(ext-api): bilateral A1 closure — D-no-node-widget-access
Comments out NodeHandle.getWidget(name) + NodeHandle.getWidgets() per
D-no-node-widget-access (ACCEPTED 2026-05-19, ADR landed). Closes the
node→widget direction so that the v2 public surface enforces A1 as
written: 'nodes cannot enumerate widgets' is now bilateral with
'nodes cannot reference widgets' — the widget→node direction
(widget.parentNode) remains the sole node↔widget channel.

Origin: a contradiction was surfaced during handoff-14 (wave-7
post-cascade). AXIOMS.md §A1 is NON-NEGOTIABLE and says 'Node hooks
cannot reference/modify widgets' + 'Widgets can know their parent
node, but nodes cannot enumerate widgets'. However design-review-12142
decision #13 (2026-05-12, 'getWidget seems good') had renamed
widget() → getWidget() without re-checking A1, and a codified
read-only carve-out in scripts/check-axiom-compliance.sh:22 papered
over the gap. The carve-out is removed; the axiom checker is now
strict-fail on any node→widget surface.

Changes:
- node.ts: getWidget(name) + getWidgets() commented out with A1+A2
  rationale block (A2 pattern: 'comment out if not 100% sure')
- widget.ts: 2 JSDoc examples rewritten to use
  defineWidget({mount}, ctx.widget) — the only legal v2 path to a
  WidgetHandle

Migration cost (v2 surface):
- 0 call sites in the tf test suite
- 1 call site in ext-v2 (extensions/core/dynamicPrompts.v2.ts) —
  stubbed pending follow-up 'defineWidgetAugmenter' API; v1 path
  (Comfy.DynamicPrompts) continues to work
- TypeScript-only signature change; no runtime change

Restoration criteria: see AXIOMS.md §A14 entry — a documented use
case unexpressible via widget.parentNode + defineWidget({mount}) +
provide/inject, A1 re-audit pass, and bc-XX test triplet coverage
across promotion / app-mode / linked-instance scenarios.

Refs:
- ADR: decisions/D-no-node-widget-access.md (ACCEPTED 2026-05-19)
- AXIOMS.md §A1 + §A14
- scripts/check-axiom-compliance.sh (strict bilateral check)
2026-05-20 12:35:47 -07:00
Connor Byrne
bd4d195230 chore(ext-api): comment out NodeMode + Slot surfaces per AXIOMS.md A14
Pre-existing A14 enforcement work (was sitting uncommitted across
the wave-6/wave-7 reconciliations). Comments out the following
Phase A exclusions from the public NodeHandle / WidgetHandle surface:

node.ts:
- NodeMode (egregious use patterns per meeting; A2)
- SlotDirection / SlotInfo / SlotEntityId (slot/connection deferred; A2)
- NodeConnectedEvent / NodeDisconnectedEvent
- on('modeChanged'), on('connected'), on('disconnected') subscriptions
- getMode() / setMode()
- inputs() / outputs() slot enumeration
- getPosition()/setPosition()/getSize()/setSize() (deferred pending A13)
- getTitle()/setTitle(), isSelected()

widget.ts:
- isHidden()/setHidden(), isDisabled()/setDisabled()
  (deferred pending serialization convergence)

These align with the AXIOMS.md A14 'COMMENTED OUT' table. No
behavior change — TypeScript interface narrowing only. Restoration
criteria documented in AXIOMS.md A14.

Refs: AXIOMS.md §A14
2026-05-20 12:35:06 -07:00
Connor Byrne
ff314491da feat(extension-api): D-shell-ui-entrypoints (W6.P5.C) — per-surface defineX entries + toast/notify inline imperatives
Adopt per-surface defineX entries plus inline-imperative carve-out for
toast + notify, per D-shell-ui-entrypoints ADR (ACCEPTED 2026-05-18,
foundation impl handoff-13).

## What lands

New shell-UI registration entries (registrations.ts, NEW):
- defineSidebarTab / defineBottomPanelTab — wire to workspace stores
- defineCommand — wires to commandStore.registerCommand
- defineHotkey — parses key combo, wires to keybindingStore
- defineSetting — wires to settingStore.addSetting
- defineAboutBadge / defineToolbarButton — module-level registries
  (consumed by aboutPanelStore / action-bar component via P5.E follow-up)

All defineX return DisposableHandle { dispose(): void }. Lazy-mount
queue (pendingEntries[]) flushed by startExtensionSystem() via the new
_flushShellRegistrations() hook so defineX is safe to call at
module-eval time (before Pinia is ready). Async store imports inside
each mount-fn keep the registration module itself dependency-free.

Inline imperatives (imperatives.ts, NEW):
- toast.show / toast.remove / toast.removeAll
- notify({ kind, message, detail, life }) — thin convenience wrapper
- Fire-and-forget; no DisposableHandle, no defineX wrapper
- 100% imperative across 166 ecosystem hits / 16 repos (R1+R2+R3)

Net-new public arg types (extensionTypes.ts):
- CommandDefinition (alias of v1 ComfyCommand)
- HotkeyExtension (keys + commandId + optional targetElementId)
- AboutBadgeExtension (label/url/icon/severity)
- SettingDefinition<TValue> (alias of SettingParams<TValue>)
- ToolbarButtonExtension (id/icon/label/tooltip/class/onClick)

Barrel reshape (shell.ts + index.ts):
- Re-export the 5 new arg types from shell.ts
- DROP ExtensionManager + CommandManager from public re-export per
  W6.P5.B reconciliation — v2 uses per-surface disposables, not
  umbrella handles. Internal callers still import from
  @/types/extensionTypes directly.
- Export 7 new defineX + DisposableHandle + toast/notify/NotifyOptions
  from index.ts

v1 deprecation (comfy.ts):
- ComfyExtension.{commands, keybindings, settings, bottomPanelTabs,
  aboutPageBadges, actionBarButtons} gain @deprecated JSDoc pointing at
  the v2 defineX equivalent. Slots remain functional for back-compat
  during the deprecation window (v1 extensionService still loads them).
- menuCommands + topbarBadges marked @deprecated with scope-note that
  they're not part of W6.P5 (follow-on ADRs).

Runtime wiring (extension-api-service.ts):
- startExtensionSystem() dynamic-imports registrations and calls
  _flushShellRegistrations() so pending defineX calls land into stores
  exactly once at bootstrap.

Verification:
- tsc --noEmit: zero new errors in modified files (pre-existing node.ts
  / widget.ts errors are uncommitted working-tree state from prior
  agent, not from this work)
- oxfmt: clean
- oxlint: 0 warnings / 0 errors on touched files
- vitest/knip blocked by Node 20-vs-24 env requirement (env, not code)

ADR: decisions/D-shell-ui-entrypoints.md §Implementation plan Steps 1-5.
Steps 6 (codemod) lands in PKG; Step 7 (.d.ts regen) cascades via
restack-after-foundation.sh; Steps 8-13 land in EXT/TF/docs/dashboard.

Per AGENTS.md §Decision propagation: subject contains
'D-shell-ui-entrypoints' so check-decision-propagation.sh picks it up.
2026-05-18 17:45:40 -07:00