Commit Graph

8530 Commits

Author SHA1 Message Date
Nathaniel Parson Koroso
2c7ee46beb detection-proof: break pack runtime (WAS Constant Number raises) - CI-side, row 9 2026-07-08 19:04:46 -07:00
Nathaniel Parson Koroso
cc0cbb3e2e detection-proof: break pack console (Custom-Scripts showText.js) - CI-side, row 8 2026-07-08 19:01:21 -07:00
Nathaniel Parson Koroso
b5a8d1a147 detection-proof: break graph-load hook - extension throws on load 2026-07-08 18:52:27 -07:00
Nathaniel Parson Koroso
91a979ecba detection-proof: break execution - drops numeric widget inputs from the prompt 2026-07-08 18:52:27 -07:00
Nathaniel Parson Koroso
aae02ff355 detection-proof: break linking (drag-drop) - slot hit-test never resolves 2026-07-08 18:52:27 -07:00
Nathaniel Parson Koroso
c711ebce29 detection-proof: break linking (by type) - rejects all IMAGE connections 2026-07-08 18:52:27 -07:00
Nathaniel Parson Koroso
1470d96f2a detection-proof: break persistence - off-by-one drops last saved widget value on reload 2026-07-08 18:52:27 -07:00
Nathaniel Parson Koroso
55b06a319a detection-proof: break mount (v2 Vue renderer) - drops the int widget mapping 2026-07-08 18:52:27 -07:00
Nathaniel Parson Koroso
1152ac59a2 detection-proof: break mount (v1 litegraph renderer) - drops last declared input 2026-07-08 18:52:27 -07:00
Nathaniel Parson Koroso
baa2e046b7 docs(custom-nodes): Detection Proof accuracy pass
Pack-mode breaks are delivered via manifest pin swaps (CI clones packs at
pins, so in-repo pack-file edits cannot reach it); corpus-derived red
messages promise the tier and failure class, not byte-identical offender
text; remaining citations and mechanism descriptions tightened to what the
code and captured runs actually show.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 18:19:11 -07:00
Nathaniel Parson Koroso
0cc21040c4 docs(custom-nodes): correct Detection Proof citations after a hallucination audit
Every quoted red message re-verified against its original run log (10/10
match). Seven citation corrections from the audit: the iTools missing-
button tickets are Nodes 2.0 regressions and move to the v2 mount row
(the v1 row is now honestly class-only); the SAM3 hidden-values tickets
are removed (extras-exposed class, which the mount tier tolerates by
design, so citing them overclaimed coverage); the persistence row now
cites the verified defaultInput migration regression (widgets reverting
to socket-only on reload) that open PR #12279 fixes, instead of a live
widget-interaction ticket; the links-type and serialization rows drop
borrowed tickets and state their class plainly; the drag row's tickets
are labeled nearest-symptom family; the two expansion bullets now cite
the committed pure-spec catches instead of an unproven live-sweep catch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 17:46:21 -07:00
Nathaniel Parson Koroso
d65d853227 test(custom-nodes): per-test backend isolation via an afterEach drain to idle
Every test gets a fresh page but all tests share one backend, locally and
on CI alike (the CI job is deliberately unsharded). A test ending while
its prompt still executed left that work running, and the next test's
fresh page connected mid-execution and inherited its async errors or its
busy queue. Drain the backend to idle in an afterEach in all four
backend-running specs, while the finishing test's own page is still open
so late events land there: no test can affect the next.

The drain helper moves to the shared fixture util (drainBackendToIdle,
byte-identical body); the auto-run tier's queue guard and runBatch
post-timeout drain rewire to it with their explicit budgets. The hooks
use a 10s budget: a no-op when already idle, and a backend still busy
past it is wedged, which the auto-run tier's 150s guard surfaces with
the restart diagnostic.

DETECTION_PROOF.md's caveat is rewritten to match, and a false claim
that CI shards one backend per pack is corrected in every location
(code comments and doc): the CI job runs the whole suite against one
fresh backend on an unloaded runner, which is why executions stay
inside their budgets there.

Empirical: a full-suite run with the hook eliminated the cross-test
bleed class entirely (zero mount/save-reload/core-smoke console or
overlay failures, previously 3-5 per run).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 17:14:14 -07:00
Nathaniel Parson Koroso
08106db082 test(custom-nodes): make the local full run idempotent (drain, foreign-noise filter, slow-node budgets)
The suite runs all 7 packs' execution tiers against one shared backend
locally (CI shards one backend per pack). Serial execution created three
distinct cross-test contaminations that failed a different set of packs
each run; each is now fixed at its mechanism:

- Foreign execution noise: mount/persistence/wiring/T0/core-smoke tiers
  queue no prompts, yet caught a prior tier's async execution error
  (PromptExecutionError, a 400 on /api/prompt). isForeignExecutionNoise
  filters execution-domain console lines from the non-executing tiers
  only; the executing tiers still assert them. Same "not this test's
  evidence" principle as event attribution (ARCHITECTURE section 9).
- Queue contention: the auto-run queue-busy guard hard-failed when a prior
  pack's slow CPU execution was still draining. drainUntilIdle waits it
  out (interrupt + clear + poll, throw-on-error so a failed read counts as
  busy); only a genuinely wedged backend fails. runBatch's post-timeout
  drain grows from 5s to 90s for the same reason.
- Slow-under-load misread as a regression: the single-node disambiguation
  re-run gets 60s instead of the batch's 20s. A real hang still exceeds it.

Also excludes the CLIPSeg model loaders (essentials, WAS) - model-download
nodes, same non-interruptible class as the listed BLIP/SAM/MiDaS loaders.
Reviewed by four-hat CORE (ship it); the new predicate is unit-pinned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 16:22:11 -07:00
Nathaniel Parson Koroso
10d7769ab5 docs(custom-nodes): neutral phrasing in Detection Proof (drop first-person reference)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 14:32:41 -07:00
Nathaniel Parson Koroso
349d82b63e docs(custom-nodes): add Detection Proof plan (falsify every guard, correlate to real regressions)
The correlation matrix + throwaway-PR plan that proves the suite catches
every failure mode ARCHITECTURE.md claims: one deliberate break per
surface, each citing the real historical regression it recreates (Linear
Custom Node Bugs issues + FE PR #12279) and the exact CI red it produces.
Every "exact red" is captured from a real falsification run, not a
prediction. Renames the earlier "kill-test" work to the falsification
pass. States the honest local-full-run idempotency caveat (CI shards
per-pack; a single-backend serial run is not the oracle).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 14:32:00 -07:00
Nathaniel Parson Koroso
6be7c18bef test(custom-nodes): sharpen three detection surfaces the break-suite exercise found
- Wiring drop resolution: the curated drag test only targeted first-slot
  inputs, so a slot hit-test regression that falls back to the first
  compatible input went undetected. Add a second-slot anchor
  (EmptyImage.IMAGE -> ImageBatch.image2) that only links if the drop
  resolves the exact slot; proven by breaking getNodeInputOnPos.
- Curated-run failure naming: a backend validation rejection answers
  /prompt with node_errors but app.queuePrompt swallows it, so a
  VALIDATION_FAIL reported {}. Capture and flatten the node_errors
  (summarizePromptError, typed off apiSchema PromptResponse) into the
  result's clientError and surface it in the T1 message, so a red names
  the node and input. Exported with a pure unit test since the happy path
  never runs it.
- Console-error window: document (README + ARCHITECTURE section 10) that
  the ledger collects per-tier, so boot-time pack console noise before the
  first tier action is out of scope by design, backstopped by the startup
  zero-visible-errors check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 14:27:07 -07:00
Nathaniel Parson Koroso
58e0fe7511 docs(custom-nodes): section 6 shows the census feeding three parsers; onboarding mirrors the scoped invariant
Round-3 review follow-ups: the definition-pipeline diagram no longer
implies a centralized normalizer (live census -> wiring slot normalizer /
execution classifier / mount declared-shape parser, matching section 4),
and ADDING_CUSTOM_NODES scopes the zero-visible-errors claim to the tiers
that assert it, same wording as the README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 12:22:05 -07:00
Nathaniel Parson Koroso
72ad08db63 test(custom-nodes): shared console ledger, drift guards, and a doc truth pass from round-2 review
- Extract the pack console-error allowlist into a shared fixture
  (consoleErrorLedger.ts); the curated T1 run now collects console and
  page errors across load+run and asserts them through the ledger. The
  filter is pinned by a discriminating pure spec (pattern match,
  cross-pack ownership, unknown-pack fail-open).
- T1 asserts every expectedNodes type is actually present in the curated
  workflow before running it, killing the vacuous-green path where a
  drifted fixture shrank the executed-set check to an empty id list.
- typePairing records unrecognizable slot specs (unknownSlots on the
  node, unknownShapes on the plan) instead of silently dropping them;
  connectivity logs the list; pure tests pin the input/output drop paths
  and the socketless boundary.
- Add test:custom-nodes:ci, the gate-equivalent run against the
  backend-served built frontend; README re-scopes test:custom-nodes as
  the dev-server loop that is NOT the gate, and scopes the
  zero-visible-errors invariant to the tiers that hold it.
- ARCHITECTURE truth pass: event attribution leads with the positive
  prompt-id capture; section 10 grades ledger guards in three strengths;
  the decentralized parser story (declaredShape, classifyInput,
  normalizer) is stated consistently in section 4, gotcha G5, and the
  legend; ADDING_CUSTOM_NODES points the console ledger at its new home.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 11:43:42 -07:00
Nathaniel Parson Koroso
24659c2caf test(custom-nodes): implement 8 external-review hardenings with discriminating self-checks
- positive prompt-id attribution: capture the /prompt response id as the
  primary event filter (seen-set + graph membership stay as depth); a new
  attribution self-check injects a foreign-prompt terminal error mid-run
  and proves it cannot fail the run
- console collection now includes pageerror (uncaught exceptions and
  rejections), with a collector self-check as positive control; surfaces a
  real Custom-Scripts betterCombos typeof-null bug, ledgered with mechanism
- connectivity allowlists are two-way stale-guarded: every entry must be
  observed failing in its recorded way, all stale keys reported per run
- manifest pins are required full 40-hex SHAs (CUSTOM_NODES_ALLOW_UNPINNED=1
  admits only empty pins, reserved for the planned pack-HEAD canary); pack
  must be a plain path segment; contract pinned by pure specs
- CI installs each pack under custom_nodes/<pack> with charset and pin
  gates before cloning (attribution keys on the install dirname)
- allNodes renderer loops honor rendererPassesFor (vueNodesCompatible)
- curated T1 asserts every display sink emitted a ui payload; console
  sinks documented as excluded (no ui payload by design)
- the always()-wrapper suggestion was rejected on sibling evidence:
  ci-tests-unit.yaml gates its required check with a changes job and
  job-level if, and no repo workflow uses a wrapper

Reviewed via ninja pipeline: 4-hat CORE panel (2 passes), senior QA gate
(2 rounds, discrimination proven by falsification), gated review (Primary,
Double Checker, Ultimate Skeptic - 15-entry evidence ledger, all PROVEN).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 22:34:08 -07:00
Nathaniel Parson Koroso
e46487f3ed docs(custom-nodes): regression-suite title, tier gating truth, verified stack claims
Implements the cold insider review's full finding set plus pipeline
verification fixes:
- title gains 'regression', matching every sibling doc
- wiring tier renderer coverage told truthfully (breadth sweep one,
  curated drags both); decision 7 enumerates all renderer surfaces
- section 5 gains the manifest tiers vocabulary bridge (run and
  connectivity gate; load and io currently gate nothing)
- renderers named once (LiteGraph / Vue Nodes 2.0); opener deduplicated
  against section 1; implementation map matches the real manifest schema
- section 13 names Playwright, bundled Chromium, GitHub Actions, with a
  caveated runtime ballpark; gotcha receipts carry only verifiable claims
- sections 10 and 13 diagrams conform to the doc's diagram grammar
- ADDING_CUSTOM_NODES tiers gloss and manifest.ts workflow comment
  aligned with the same gating reality

Reviewed via ninja pipeline: 4-hat CORE panel (2 passes), senior QA gate
(2 rounds), gated review (Primary, Double Checker, Ultimate Skeptic).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:40:13 -07:00
Nathaniel Parson Koroso
3bc40bb148 docs(custom-nodes): legend reads in ascending section order per depth; name the map's ordering principle
Dagre's crossing minimizer ignores edge declaration order, so the fix is
node declaration order. Also states explicitly that the map is ordered by
zoom, not page order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 19:30:14 -07:00
Nathaniel Parson Koroso
cbceaf5dd9 docs(custom-nodes): context + execution + persistence diagrams restructured per review
- context diagram flows one way: driver -> frontend -> verdict synthesis -> team
- execution flow: classification fans out to its three verdicts; runnable paths
  converge on batching, blocked routes straight to reconciliation
- persistence check: sequence diagram replaced with a linear pipeline (one
  actor issuing commands is a procedure, not a message exchange)
- building blocks: tiers fan 2x2 inside the horizontal pipeline
- tripwire step + small recovers? diamond instead of one giant diamond

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:31:15 -07:00
Nathaniel Parson Koroso
497dd6ede5 docs(custom-nodes): building-blocks view horizontal, raise label wrap width
The building-blocks pipeline rendered as a tall narrow strip for two
mermaid reasons: labels auto-wrap at the ~200px default regardless of
line length, and a subgraph's declared direction is ignored once it has
external edges, so the tier row silently stacked vertically. The view
is now a left-to-right pipeline with the tier group in the middle, and
the wrap-width directive makes boxes wide instead of tall here and in
the definition-pipeline and execution-flow views.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:15:08 -07:00
Nathaniel Parson Koroso
cec09da789 docs(custom-nodes): close the observation loop in the context view
The frontend box was a dead end: the suite drove it but nothing flowed
back, so the verdicts arrow to the team looked sourceless. Added the
return edge (observations back: what mounted, what persisted, what
executed, every error) and reworded the team edge so verdicts are
visibly the synthesis of those observations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:13:16 -07:00
Nathaniel Parson Koroso
9eb035e8a8 docs(custom-nodes): drop the redundant D-diagram labels; de-jargon the batching box
Headings were double-numbered ("2. D1 - system context") with an
internal diagram-numbering scheme that means nothing to a reader.
Sections are already numbered: headings now just name the view, and
every cross-reference points at a section. Also replaced "queue cost
is amortized" with plain English: one submission carries many nodes
instead of paying the round-trip per node.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:08:53 -07:00
Nathaniel Parson Koroso
3cef258bac docs(custom-nodes): wide diagram boxes - strip forced line breaks from D1-D4
Reader feedback on the rendered views: manual line breaks inside boxes
force Mermaid to render narrow, tall boxes with heavy wrapping, so the
diagrams cost too much scrolling. Mermaid sizes a box to its longest
line, so the fix is one or two long lines per box with elaboration in
the prose below the diagram. Applied to D1 (context), D2 (pipeline),
D3 (definition pipeline), and D4 (execution flow); D7 and D8 stay as
approved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:06:01 -07:00
Nathaniel Parson Koroso
34faaa1a1d docs(custom-nodes): redraw D2/D3 for single-flow readability
Reader feedback on the rendered views: D2's service-to-tier arrows
crossed the whole diagram with ambiguous fan-ins (three unattributed
"pass/fail + exceptions" curves), and D3's corpus box mixed the
two-dialects annotation into a flow node right where three arrows fan
out, reading as if the dialects explained the fan-out.

Fixes, using the rules that make the CI view work: one direction of
flow per diagram, no many-to-many edges (the service-to-tier matrix is
now a table, which is what a matrix is), and annotations live in prose
rather than inside flow boxes. D2 is now a straight
manifest -> orchestrator -> tiers -> evidence -> verdict pipeline with
a three-row shared-services table; D3 moves the dialect fact into the
normalize step, labels the fan-out "derives", and adds one sentence
mapping each derived plan to its consuming tier.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:03:02 -07:00
Nathaniel Parson Koroso
95ced4c6ef docs(custom-nodes): ARCHITECTURE.md - C4-style views, design decisions, gotchas reference
Architecture documentation for the custom-node regression suite,
written as design views rather than an implementation dump:

- eight responsibility-level views: system context, building blocks,
  the node-definition pipeline, the execution flow, the persistence
  check, event attribution, the evidence model, and the CI deployment
  view; every diagram box names a responsibility or concept, arrows
  carry meaning, and decision points read in plain English
- a one-minute What/Why/How opening with the three explicit non-goals
  (output semantics, frontend-virtual nodes, hour-scale soak) and a
  clearly labeled scale snapshot so instance numbers never read as
  properties of the design
- a 12-row design-decisions table with honest trade-offs (why a real
  browser at all, why the backend serves the built frontend, one
  worker, disabled execution cache, pinned pack versions, one-row
  extensibility, per-tier renderer policy, mechanism-carrying
  exceptions, the two-way baseline, batch+bisect, and the scope line),
  plus the curated-workflow fixture named as the deliberate extension
  seam
- a 14-item gotchas reference, each entry in symptom / root cause /
  defense / which-team-concern-it-answers form, with named nodes kept
  only as worked examples of their class
- one implementation map section where architecture names meet code
  symbols, covering every building block including the orchestrator
  and the evidence ledgers
- the workflow's rotted sharding comment fixed (suite duration and
  the real shard trigger)

Grounded on the C4 model's published guidance, reviewed by an
independent architect pass (two view-coherence gaps found and fixed)
after three earlier Opus review passes on content accuracy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:33:08 -07:00
Nathaniel Parson Koroso
f9a94d0296 docs(custom-nodes): ARCHITECTURE.md - system design, data flow, and the incident behind every invariant
The suite had run/onboarding docs but nothing describing the SYSTEM:
what the pieces are, how node definitions flow through the planners and
classifiers, how the execution harness attributes outcomes, and why each
non-obvious rule exists. This adds the missing third doc with four
Mermaid box-line diagrams (system overview, def data flow, run pipeline,
CI pipeline), the tier-by-renderer coverage matrix, the full ledger
table with the two-way baseline semantics, and the hard-won invariants
each tied to the incident that forced it (widgetValueStore id bleed,
event cross-attribution, pack JS queue-hook crashes, Vue effect timing,
queue-jam tripwire). Scope contract is stated up front: compatibility
and regression gate, not a behavior certifier.

Every path, symbol, and number cross-checked against the tree before
commit. README and ADDING_CUSTOM_NODES now cross-reference it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:38:37 -07:00
Nathaniel Parson Koroso
e7681be896 test(custom-nodes): shape-census audit - classify V2-form combos, forceInput beats every form, census-derived fixtures
Systematic audit for siblings of the two combo bug classes (ungrounded
contract, shape blindness), driven by a live shape census of the exact
getNodeDefs object the suite consumes:

- classifyInput now handles the V2 schema form (string 'COMBO' with
  options in the opts object; 495 such inputs exist in the transformed
  defs): options present = widget, empty or remote/lazy = NEEDS_MODELS.
  Real effect measured: 8 KJNodes nodes were silently misclassified
  NEEDS_WIRES and never executed - 5 now run clean, 3 correctly land in
  NEEDS_MODELS (remote combos).
- forceInput now beats every input form, list-form combos included (a
  census-found form the old branch order classified as widget; today's
  4 instances are optional or non-manifest, so this is protection, not
  a behavior change).
- pure-spec fixtures for both parsers now include every census form,
  copied from real census examples (V2 options, V2 empty, V2 remote,
  forceInput-on-combo, cross-form vocabulary pairing) so fixtures can
  no longer self-confirm the parser's assumptions.
- ADDING_CUSTOM_NODES.md gains the evidence rules: independent-oracle
  grounding for semantic claims, shape-census-driven parsing with
  exclude-with-record on unknown shapes, and verify-against-the-source-
  the-code-consumes.

defaultInput checked against frontend source: deprecated and ignored
(nodeDefStore warning only) - deliberately not handled.

Local verification: full customNodes suite 72/72 under CI parity; lint,
format:check, knip, and both typechecks clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:30:51 -07:00
Nathaniel Parson Koroso
70233bbd04 test(custom-nodes): combo vocabulary is a set, not a sequence; never pair combos with unknown options
A wired combo input bypasses its own widget, so menu order and the
options[0] default are not part of the wire contract - membership is
(backend validation checks value-in-options). Vocabulary fingerprints
are now order-insensitive (sorted, element-wise canonicalized). In the
current corpus this changes zero pairs (measured: no same-set,
different-order combos exist across the 7 packs); the rule is now
correct for packs where they do.

Auditing that change surfaced a real hole: the frontend's transformed
defs present some combos as the literal string COMBO with options in
the opts object. The old fingerprint hashed all of those identically,
silently cross-pairing dropdowns with no vocabulary evidence - exactly
the checkpoint-into-scheduler class the combo rule exists to exclude.
Normalization now pulls V2-form options, and a combo with no known
option list is excluded from pairing instead of blind-matched. Plan
moves 5,058 -> 5,030 pairs; the 28 removed were vocabulary-blind.

Also from CI: MiDaS Mask Image excluded (torch.hub download inside
execute hung the Linux runner; runs clean only where the hub cache is
warm) and ImageTransformKJ ledgered (pack JS initializes its
fill-options JSON widget on configure).

Local verification: full customNodes suite 68/68 twice, lint, format,
knip, and both typechecks clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:01:32 -07:00
Nathaniel Parson Koroso
a22acc4f48 test(custom-nodes): save/reload under both renderers; classify client-side queue throws; scope decisions written in-code
Coverage-gap audit after the mount-fidelity miss (assertions silently
narrower than their claim). Fixes:

- save/reload now runs under BOTH renderers with staged evaluates and
  frame yields, so Vue component mount/configure effects actually flush
  before each serialize - the one renderer-dependent value path a
  LiteGraph-only pass could not see. Console errors are now collected
  during the tier too (configure-time pack JS noise was uncovered).
- queuePrompt is wrapped in-page: pack JS that THROWS mid-graphToPrompt
  (VHS applyToGraph crashed CI's whole VHS tier) now classifies as
  VALIDATION_FAIL carrying the exception text, so the offender
  self-identifies instead of aborting the tier. VHS_SelectLatest
  excluded with that mechanism: its applyToGraph assumes downstream
  inputs have widgets and hard-crashes when its output feeds a pure
  socket while the input dir has matching files (upstream-report
  candidate).
- pack-owned-value nodes (ROUNDTRIP_VALUE_ALLOWLIST) no longer receive
  set-and-stick probe writes - writing `_cn` markers into editor JSON
  widgets just made pack JS choke on our own probes.
- deliberate scopes are now stated where the assertion lives: auto-run
  runs single-renderer because execution is a backend contract and
  values flow through the same store in both renderers; it deliberately
  skips the zero-visible-errors check because it provokes expected
  failures; the connectivity breadth sweep is renderer-independent with
  the curated drag test covering both renderers; combo vocabulary
  matching is deliberately order-sensitive (option order defines the
  default).

Local verification: full customNodes suite 67/67 under CI parity, plus
lint, format:check, knip, and both typechecks clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 13:31:43 -07:00
Nathaniel Parson Koroso
e6ed6120a1 test(custom-nodes): assert def-vs-instance mount fidelity under both renderers; reconcile Linux CI flips
Mount fidelity now has a renderer-independent bar: under BOTH the
LiteGraph and Vue passes, every created instance must materialize
everything its def declares - each non-socketless input exists as a
widget or a socket (autogrow templates count via their dot-qualified
expansion slots, e.g. variables.a/variables.b), and every declared
output exists. The Vue pass keeps its extra layer: the DOM must render
at least the instance's widget and slot counts. Verified against all
823 nodes under both renderers; the only def-shape special case found
was the core autogrow container semantics.

Also reconciles the first Linux CI run of the chain-builder tier:
- environment flips move to AUTO_RUN_EXCLUDE with mechanisms and leave
  the baseline: Image Analyze, Text Parse A1111 Embeddings (fail macOS,
  clean Linux), Image Crop Face (clean macOS, AttributeError Linux),
  ImageReceiver (av decode error macOS, clean Linux)
- run-to-run flip-floppers excluded: ImpactRemoteInt,
  ImpactSchedulerAdapter, ImpactQueueTriggerCountdown (queue-hook JS
  transient refusals), LoadText|pysssss (state-dependent file combo)

Local verification: allNodes 21/21 twice consecutively, plus lint,
format:check, knip, and both typechecks clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 13:00:17 -07:00
Nathaniel Parson Koroso
5ce414653e test(custom-nodes): chain-run NEEDS_WIRES nodes, assert outputs, mount fidelity, widget round-trip, combo pairing
Executes ~340 more nodes and hardens every tier's assertions:

- CHAINABLE verdict: required sockets with a model-free producer
  (EmptyImage, EmptyLatentImage, SolidMask, Primitive*, EmptyAudio) are
  synthesized and wired automatically; NEEDS_WIRES now means only truly
  unproducible types (MODEL, SEGS, CONDITIONING...)
- auto-run asserts data flow: every PreviewAny sink must emit a ui
  payload (NO_OUTPUT class); OUTPUT_NODE targets stay event-covered
- Vue mount asserts DOM widget/slot counts (missing fails, extras and
  in-row control_after_generate tolerated)
- save/reload is now two passes: pristine (reload must never shrink a
  node or change a value - the "widgets disappear" bug class) and
  set-and-stick (every plain widget holds a programmatic non-default
  write and it survives reload where topology is stable)
- connectivity pairs COMBO slots on exact option-vocabulary match
  (+~120 pairs); mismatched vocabularies stay excluded by design
- harness invariants: node ids never reused within a page (the
  widgetValueStore keys state by node id and survives graph.clear(), so
  a reused id inherits stale widget values - core bug, reported
  separately), and run events are filtered by prompt id + graph node id
  membership so late websocket events or flap-retry double-queues can
  never pin one node's failure on the next
- new mechanism ledgers: WIDGET_SET_ALLOWLIST, ROUNDTRIP_VALUE_ALLOWLIST,
  MOUNT_WIDGET_ALLOWLIST, all stale-guarded; AUTO_RUN_EXCLUDE gains the
  observed offenders (rembg pip-install-at-execute, empty-find infinite
  loop, from_pretrained downloads, minutes-long per-pixel loops)
- manifest baselines reconciled against three observation runs; stale
  entries removed, real failures (missing optional deps, degenerate
  synthesized inputs, CUDA-only recorders) baselined

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 12:32:16 -07:00
Nathaniel Parson Koroso
c5de8d421d test: fix ImageGrabPIL pack attribution; exclude WAS ffmpeg path node
ImageGrabPIL is a KJNodes node and was ledgered under WAS, so its
exclusion never applied - moved to the right pack, and the auto-run
test now asserts every exclusion key is actually registered by its
pack so a wrong-pack entry fails loudly instead of silently doing
nothing. WAS Create Video from Path joins the exclusions with CI
evidence (ffmpeg discovery differs per host).
2026-07-07 10:43:57 -07:00
Nathaniel Parson Koroso
9bb0587ec5 Merge remote-tracking branch 'origin/main' into nathaniel/custom-node-e2e-suite 2026-07-07 10:24:36 -07:00
Nathaniel Parson Koroso
de23856742 test: exclude runtime model-downloaders and content-variable nodes from auto-run
The rebuilt environment surfaced the rest of the download class: WAS
BLIP/SAM/MiDaS model loaders all hang the queue in non-interruptible
weight downloads (and would mass-download on a networked runner), the
random.org node needs internet by definition, KJ LoadAndResizeImage and
WAS Create Grid Image follow input-dir contents, and Impact media
widgets preview values via root-relative URLs (console allowlist
widened). Every entry carries its mechanism; all keep mount,
save/reload, and connectivity coverage. 65/65 both environments.
2026-07-07 10:15:11 -07:00
Nathaniel Parson Koroso
650abec3ab test: fix browser typecheck errors; recalibrate exclusions for CI environment
typecheck:browser (which the lint job runs, unlike root typecheck)
caught an unused classifyInput param, a branded-NodeId lookup, and
api.interrupt's required argument. The first serialized CI run then
exposed environment-variable nodes: clean on one host, failing on the
other (screen capture with no X display, PIL screen grabs headless,
torch-stack RuntimeErrors that are macOS-only, state-dependent WAS
history). Those move from the cannotRunAlone baseline to
AUTO_RUN_EXCLUDE with per-node mechanisms so both environments stay
deterministic, and Impact's hardcoded example.png preview 404 joins
the scoped console allowlist.
2026-07-07 09:59:24 -07:00
Wei Hai
747f76db76 ci: auto-merge approved, green backport PRs (#13390)
## Problem
`pr-backport.yaml` opens each backport PR (labelled `backport`) and
calls `gh pr merge --auto --squash`. GitHub's `--auto` only takes effect
when the repository's **"Allow auto-merge"** setting is enabled — it's
currently off, so that call is a silent no-op (swallowed by its `|| echo
"::warning::…"`). The result: every backport PR sits unmerged until
someone manually clicks merge, even when it's already approved with
green checks.

## What this does
Adds `.github/workflows/backport-auto-merge.yaml`, which completes the
merge directly — a plain `gh pr merge --squash` (which does **not**
depend on the "Allow auto-merge" setting) — once GitHub reports the PR
ready to merge.

Ready = `reviewDecision == APPROVED` **and** `mergeStateStatus` is
`CLEAN` or `UNSTABLE`. `UNSTABLE` means the required checks passed but a
*non-required* check is still pending/failing — GitHub still permits
that merge, and gating on `CLEAN` alone would leave backports stuck
behind slow/flaky non-required checks (Socket, codecov, perf, storybook,
etc.).

**Branch protection stays the real gate.** The `core/**` / `cloud/**`
ruleset unconditionally requires an approval + the required checks and
can't be bypassed, and GitHub's merge API re-enforces it at merge time —
so this workflow can only ever finish a merge that already satisfies
those rules. The eligibility check just avoids pointless attempts.

## Design notes
- **Merges with `PR_GH_TOKEN`, not the default token**, on purpose: a
merge by the default `GITHUB_TOKEN` does not emit the `pull_request:
closed` event, which would silently starve `cloud-backport-tag.yaml` (it
creates the `cloud/vX.Y.Z` tag on that event).
- **Triggers:** review submission + check-suite completion (low
latency), plus a 30-min sweep as a backstop for cases the events miss.
- **Never checks out PR code** (no untrusted-code path); only reads PR
metadata via the API. `permissions` on the default token are read-only.
- **Idempotent, bounded merge loop:** treats an already-merged PR (e.g.
a concurrent run or a human) as success, so it won't post a false
failure comment.
- Leaves the existing conflict path in `pr-backport.yaml` untouched
(conflicts never create a PR, so there's nothing here to act on).

## Validation
YAML parses; `actionlint` (with shellcheck) and `zizmor` both clean (0
findings).

## Before relying on it
- Confirm the org allows this workflow to run/merge (Actions policy) —
the merge uses a PAT so it shouldn't depend on the "Actions can approve
PRs" toggle, but worth verifying.
- First real backport: confirm it merges on ready and that
`cloud-backport-tag.yaml` then fires and creates the tag.
2026-07-07 16:50:23 +00:00
Mobeen Abdullah
386460afef fix(website): center button labels by tuning ppformula-text-center (#13445)
## Summary

Vertically center button/badge/nav labels by tuning the shared
`ppformula-text-center` utility from `top: 0.19em` to `top: 0.1em`.

## The alignment issue

PP Formula (our brand font) has asymmetric vertical metrics: its caps
sit high in the line box, so a naively centered label looks too high.
`ppformula-text-center` compensates by nudging the label down with
`position: relative; top: <em>` (a purely visual shift, it does not
change the element's box, so button/badge sizes are unaffected).

The value was `0.19em`, which **over-corrected**: the glyph ink ended up
~1.4px **below** center on every button, so labels read slightly low.
Measuring the actual glyph ink (canvas `measureText`
`actualBoundingBox*`) showed ~**0.09-0.10em** centers uppercase labels;
`0.1em` lands the ink within ~0.1px of center.

## Why it's safe (verified)

This utility is used site-wide (Button, Badge, ButtonPill, ButtonMask,
BrandButton, nav triggers, section labels). Because it's a
`position:relative` nudge, there is **no layout/box-size change**
anywhere. I measured glyph-ink centering across **12 pages** (home,
cloud, cloud/pricing, download, careers, customers, demos, enterprise,
api, mcp, gallery, learning):

- Buttons/badges/pills went from ~1.37px low to **~0.11px** (centered).
- **Nothing regressed** (no element pushed too high).
- The handful of numeric "outliers" were `text-transform: uppercase`
measurement artifacts (source text with descenders that don't render);
confirmed visually as centered.

## Changes

- **What**: `apps/website/src/styles/global.css` —
`ppformula-text-center` `top: 0.19em` → `0.1em` (one line).

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-07-07 20:37:32 +05:00
Mobeen Abdullah
5cf647d183 feat(website): add GPT Image 2 to cloud page model list (#13431)
## Summary

Add GPT Image 2 to the `/cloud` "AI models" section, and apply the
design review polish from Bert and June on the same section and the
neighbouring cloud-page cards.

## Changes

- **GPT Image 2 card**: 6th card in `AIModelsSection` (workflow video on
`media.comfy.org`, OpenAI badge reused from `packages/design-system`),
new `cloud.aiModels.card.gptImage2` i18n key (en + zh-CN), and GPT Image
2 added to the `cloud.reason.2.description` partner-model list.
- **AI models layout**: the six cards are now equal 1:1 squares in one
shared grey container (was a per-card treatment, then simplified to a
single container per design), with a corner arrow affordance on each.
- **Audience cards** (`AudienceSection`): the creators / teams cards now
link to `cloud.comfy.org` with the same corner arrow (highlights on card
hover).
- **Reusable `CardArrow`**: extracted the corner arrow into a shared,
decorative (`aria-hidden`) component used by both sections;
`hover="group"` (card hover) for audience, self-hover for the model
cards so it doesn't double up with the provider badge.
- **`ProductCard` CTA**: swapped the hand-rolled pill `<span>` for the
shared `Button` (`as="span"`) so the label is vertically centered (fixes
Bert's off-centre text) without nesting an anchor inside the card link.

## Split out of this PR

- **Button label centering** (the global `ppformula-text-center` tweak)
→ separate PR #13445, so the site-wide change is reviewed in isolation.
- **Pricing banner frame fix** reverted here; it belongs in Michael's
upcoming pricing PR (team tier, edu billing, FAQ). `PricingSection.vue`
shows only an automatic Tailwind class-order reformat from the
pre-commit hook, no behaviour change.

## Review focus

- The single-container AI models layout and the `CardArrow` hover
behaviour (group vs self).
- `Button as="span"` inside the `ProductCard` link (avoids nested
`<a>`).

Linear: FE-423

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-07-07 20:19:54 +05:00
Mobeen Abdullah
fe1fc8baa6 fix(website): standardize favicon to square brand icon (#13467)
## Summary

Standardize the marketing-site favicons to the square, full-bleed brand
mark (ink background, yellow C) so each platform applies its own corner
mask instead of double-rounding a pre-rounded asset.

## Changes

- **What**: Replace three files in `apps/website/public/`:
- `favicon.svg` — was a 57 KB RealFaviconGenerator wrapper around an
embedded PNG; now a 1.2 KB vector of the square mark.
- `favicon-96x96.png` and `apple-touch-icon.png` — regenerated square.
The apple-touch icon previously had transparent rounded corners, which
iOS composites onto a white tile; it is now full-bleed.
- `favicon.ico` and the `web-app-manifest-*.png` files were already the
square mark, so they are left unchanged.

## Review Focus

- Favicons cache aggressively (browser + CDN, and these paths are marked
`immutable` in `vercel.json`), so verify the preview with a hard refresh
or a fresh profile.
- Part of the org-wide favicon standardization (FE-705). Companion PRs
update the workflows hub, docs, and registry favicons to the same mark.
Design direction (square, not rounded) confirmed by Bert.

## Screenshots

The before/after for each binary is visible inline in the Files changed
tab. Square ink + yellow C, no transparency, sharp corners.
2026-07-07 20:18:12 +05:00
Benjamin Lu
3e4dd59e5f feat(navigation): add opt-in strip-on-capture to preserved-query tracker (#13465)
## What

- `installPreservedQueryTracker` definitions accept an opt-in
`stripAfterCapture` flag: the marked keys are captured into the
sessionStorage stash and removed from the URL before the navigation
completes (single guard redirect at the decoded query-object level;
push/replace semantics are inherited from the original navigation, and
vue-router force-replaces the initial one).
- `preservedQueryManager` now captures the first non-empty string
element of repeated (array-valued) params instead of silently dropping
them.
- New real-router test suite for the tracker (createRouter +
createMemoryHistory, no router mocks), including a history-depth test
pinning the push/replace inheritance; manager tests extended for the
array/junk-value cases.

## Why

One-time secrets in query params (first consumer: desktop login codes,
GTM-93) must not linger in the visible URL, browser history,
`previousFullPath` redirects, or telemetry. Stripping after navigation —
what each loader does ad hoc today — leaves a window and forces
per-feature URL scrubbing; #13418 originally needed a hand-rolled
encoding-aware string parser in three places. Stripping at capture time,
at the decoded query-object level, makes the stash the only carrier and
lets vue-router round-trip the surviving params' encoding itself.

Capability only — no existing namespace opts in; behavior is unchanged
for all current definitions. `stripAfterCapture`'s contract is
documented on the option: strip-marked keys must never be read from
`route.query` by later guards or views; the stash is the only
post-capture source.

## Landing order

Independent of everything else; #13418 stacks on this branch.
2026-07-07 07:08:11 +00:00
Benjamin Lu
e25e0f2e16 refactor: simplify slot link drop finalization (#13471)
## Summary

Small cleanup in `useSlotLinkInteraction.ts`, no behavior change:

- Removed a duplicated `raf.flush()` in `finishInteraction` (it was
called twice back-to-back).
- Collapsed four single-line `attempt*` alias closures in
`connectByPriority` into a direct short-circuit chain, preserving the
same evaluation order:

  ```ts
  return (
    tryConnectToCandidate(snappedCandidate) ||
    tryConnectToCandidate(domSlotCandidate) ||
    tryConnectToCandidate(nodeSurfaceSlotCandidate) ||
    tryConnectViaRerouteAtPointer()
  )
  ```

The closures added no behavior beyond renaming the calls (AGENTS.md rule
26), and `||` gives the same first-truthy-wins semantics as the previous
`if (attempt()) return true` ladder.

Verification not rerun after rebasing onto `Comfy-Org/main`; original
branch reported `pnpm typecheck`, `pnpm lint`, `pnpm format:check`,
`pnpm knip`, and the existing `useSlotLinkInteraction` unit tests
passing.

Link to Devin session:
https://app.devin.ai/sessions/1351ff5174494106a7a688777554f387
Requested by: @benceruleanlu

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-07 07:05:36 +00:00
Benjamin Lu
2ee91c30ee fix: report CLA check in merge queue (#13477)
Report the CLA Assistant required check for merge queue commits so
queued PRs do not wait indefinitely on a check that ran on the PR head.
2026-07-07 07:57:02 +00:00
Alexis Rolland
854770d305 ci: Update CLA workflow to build author-only allowlist (#13378)
## Summary

Update CLA workflow to build a dynamic `allowlist` that includes
everyone except the author of the PR. This relaxes the CLA signature
requirement so that it is limited to the PR author only. By signing, the
author confirms he gots approval from other contributors.

## Changes

- **What**: `cla.yml`

## Screenshots (if applicable)

<img width="1831" height="756"
alt="{B6F6C23D-EC2E-4BB3-A288-99B6087F4CAC}"
src="https://github.com/user-attachments/assets/62c04465-d1a3-4ddb-bfe7-950a29a802c4"
/>
2026-07-06 19:29:59 -07:00
Nathaniel Parson Koroso
b99100d0b4 test: un-export AutoRunClass (knip: consumers use AutoRunVerdict) 2026-07-06 17:12:24 -07:00
Nathaniel Parson Koroso
6d5bcb9e04 test: suppress deferred-await false positives in turnstile timer tests
vitest/valid-expect flags assertions stored before advancing fake
timers and awaited after - awaiting at creation would deadlock the
timer advance. The file is byte-identical to main; the warnings appear
because the type-aware lint toolchain moved. Suppressed per line with
the reason; also drops a scratch await added while chasing this.
2026-07-06 17:05:39 -07:00
Nathaniel Parson Koroso
0723702791 test: serialize CI workers, prune comment noise, clearer naming
The auto-run tier needs exclusive backend-queue access, so the CI job
now runs with workers=1 - parallel workers were interrupting each
other's executions and cross-attributing errors. Await the async
toHaveLength assertion the type-aware lint flagged, drop the
calibration measurement harness (one-shot scaffolding; the measured
constant keeps a provenance note), rename NO_SINK to
NO_OBSERVABLE_OUTPUT, and cut comments down to load-bearing WHYs.
2026-07-06 17:03:21 -07:00
Nathaniel Parson Koroso
9825047176 ci: pin every manifest pack to its verified commit
An unpinned pack means any upstream push can red the gating check for
every PR in the repo. Each row now pins the exact SHA the suite was
verified against locally (all tiers green, both environments); bumps
are deliberate, re-verified changes. Also records why the job is not
sharded yet: per-shard setup (~4.5 min of pack installs and backend
boot) dominates the ~5.5 min suite, so a prebuilt image comes first.
2026-07-06 16:33:53 -07:00