fix(1624): a repo filed under a Comfy Registry id is refused, not swapped for another author's - #1631
fix(1624): a repo filed under a Comfy Registry id is refused, not swapped for another author's#1631artokun wants to merge 4 commits into
Conversation
…pped for someone else's
ComfyUI-Manager v4 does not resolve a from-source install by the URL passed. It
files each channel entry under the pack's Comfy Registry id when the repo is
registered, and under the bare repo name only when it is not:
get_custom_nodes cnr = self.get_cnr_by_repo(v['files'][0])
if cnr: v['id'] = cnr['id']; node_id = v['id']
else: node_id = v['files'][0].split('/')[-1]
So a repository registered under an id that is NOT its own name is unreachable by
that name from any channel, and on a nightly spec the miss falls back to the
registry pack whose id IS the name:
install_by_id if the_node is None and version_spec == 'nightly':
repo_url = self.cnr_map.get(node_id)['repository']
The caller names one author and gets another, reported as a success.
#1619 refuses this only where the bare name is CONTESTED across the six channel
lists. The hazard needs no contest. Replaying 4.2.2's resolution over the six
lists plus the whole Comfy Registry (5117 repositories): 1366 repos are re-keyed,
1268 of them resolve to nothing (Manager answers "not found" and installs nobody's
code — deliberately not recorded), and 98 resolve to a DIFFERENT repository, 84 on
the defaulted channel. 56 of those are invisible to #1619's table.
The generator now runs that simulation once and emits the verdicts —
REKEYED_SUBSTITUTIONS, 98 records — rather than the 5117-row map #1624 proposed
mirroring, so there is no second implementation of Manager's keying to drift.
Read at tag 4.2.2, not `main`: `main` is the 3.41 line and has no CNR fallback.
Refs #1624
Two mutations survived the first pass and now do not:
* dropping `!ambiguity` from the from-source wiring — free on the refusal
path, which returns at the first conflict, but on the WARNING path both
messages ride the same `note` and a caller reads the finding twice with
two different remedies;
* dropping the "resolves to nothing" branch — that is Manager's "not found",
which installs nobody's code, and refusing it would widen this past what
was measured.
Refs #1624
…annel Gate finding against the first commit. The two registry-route returns above do not leave "necessarily from-source" true for the re-keyed table the way they do for the contested one: `rekeyedRegistryVersionAmbiguity` requires a `registryTarget`, and 12 of the 98 re-keyed records have none. For those, an explicit version fell through to the from-source refusal, which told the caller the channel's list was what would be cloned -- on a route where Manager calls `cnr_install(bare, version)`, reads no channel, finds no such id and fails. Refusing was directionally defensible and the remedy it named was correct, which is exactly how a fabricated mechanism survives review. Scoped to the route it describes; Manager's own "not found" (which substitutes nobody's code) stands. The nightly/unknown route test is now one exported predicate, `isFromSourceSpec`, shared by all three call sites -- two copies that drifted would not fail loudly, they would each describe the wrong route to somebody. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…cords The gate finding had two sides and the first commit only pinned one. The warning is what a caller who NAMED the channel gets; it describes the same channel resolution, so on a route where no channel is read it is exactly as fabricated as the refusal -- and worse placed, because it rides a call that DISPATCHES, where nothing later contradicts it. Uses RUFFY-369/ComfyUI-StreamDiffusion on `dev` deliberately: the first URL tried here was also a CONTESTED name, so #1616's registry-version check refused it a step earlier and the test passed while exercising nothing of the warning path. Counts corrected against the table rather than repeated: 26 of the 98 records have no `registryTarget` (not 12). 12 of them are reachable on the defaulted channel -- that is where the 12 came from -- and the other 14 answer only on a channel the caller names explicitly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Status: CI green, gate INDETERMINATE — holding, not mergingA gate finding was open and unfixedThe branch carried a regression test for a codex finding that had never been fixed — it failed against the committed code. Confirmed and closed here. The finding. The two registry-route returns do not leave "necessarily from-source" true for the re-keyed table the way they do for the contested one. Refusing was directionally defensible and the remedy it named was even correct — which is exactly how a fabricated mechanism survives review. The fix. The from-source check is scoped to the route it describes, via one exported Both sides of the split are pinnedThe first pass covered only the refusal. The warning — what a caller who named the channel gets — states the same channel resolution, so it was equally fabricated on that route and worse placed, because it rides a call that dispatches, where nothing later contradicts it. The first URL I picked for that test was also a contested name, so #1616 refused it a step earlier and the test passed while exercising nothing. It now uses Counts measured, not repeatedThe inherited comment said "12 of the 98". Measured against the table: 26 have no Verification
Why this is not mergedVerified directly rather than taken from the gate's own detector — #1624 stays open — it is fixed on this branch, not on |
Closes #1624.
ComfyUI-Manager v4 does not resolve a from-source install by the URL you pass. It files
each channel entry under the pack's Comfy Registry id when the repo is registered, and
under the bare repo name only when it is not — so a repository registered under an id that
is not its own name is unreachable by that name, and something else answers instead.
#1619 refuses this only where the bare name is contested across the six channel lists.
The hazard needs no contest at all, which is what #1624 was split out to say.
The mechanism, read at tag 4.2.2
Read at
4.2.2, not atmain.mainis the 3.41 line — itsinstall_by_idhasno CNR fallback whatsoever, so quoting
mainhere would have described code that does notrun on any v4 host. (The channel URLs in
channels.list.templatedo point atmain,which is why the generator still fetches the lists from there.)
The panel sends
version: "nightly"for every git URL on the v2/v4 dialect(
manager-install.js), so that fallback is reachable from this tool, not hypothetical.What was measured
Replaying that resolution over the six published channel lists plus the whole Comfy
Registry (5117 repositories, fetched 2026-08-15):
The 1268 are deliberately not recorded. Manager returns
Node '<name>@nightly' not found, installs nobody's code, and turning a clean failure into a refusal is a differentbug from this one.
Concrete examples of the 56, none of which needs a channel collision:
Why a precomputed verdict, and not the map #1624 asked for
#1624 proposed mirroring the registry's
repository -> idandid -> repositorymapsinto the snapshot and simulating at call time, and costed that at ~5163 rows churning
continuously. The simulation is the right idea; shipping its input is not:
and that answer never varies per caller;
drift from the generator's.
So the generator runs the simulation once — against registry data it already fetched in
the same pass, for
REKEYED_REPOS— and emits only the repositories whose answer is"someone else": 98 records, not 5117 rows. Same verdicts, one implementation, and the
losing 1268 never enter the snapshot.
What it does
REKEYED_SUBSTITUTIONSis keyed by the caller's lowercasedowner/repoand records theid it is filed under, what each channel's list does answer to the name, and what
cnr_map[name]clones on a miss.actually land and the id that makes theirs unreachable. Unlike fix(1616): refuse the ambiguous from-source install instead of picking a repo #1619's refusal there is
no channel that could aim it — the id is not the name on any channel — so the remedy
offered is
install_custom_node (source:"git"), which clones the URL verbatim.URL passed is what gets cloned) with a warning that does not repeat fix(1616): refuse the ambiguous from-source install instead of picking a repo #1619's "you
chose the channel" framing, because here the channel choice decided nothing.
get_custom_nodesentirely and installs the registry pack whose id is the bare name, which for these
repositories is by definition someone else's. Dropping
versionis not offered asthe remedy here — unlike fix(1616): refuse the ambiguous from-source install instead of picking a repo #1619's version refusal — because the from-source route cannot
reach the repository by name either.
Where both tables know a repository (
wildminder/ComfyUI-Chatterboxis in both), #1619'smessage stays in charge; the two are pinned to agree on the landing.
What it deliberately does not cover
Keyed by the caller's repository, so it fires only for repositories the registry knows.
An unregistered, unlisted fork named
ComfyUI-GGUFhits exactly the same substitution andis not in here — that set is not enumerable from any snapshot. Those keep today's
behaviour and the standing dispatch note. Stated in the module, not implied.
Verification
written for them: dropping the
!ambiguityordering guard (free on the refusal path,which returns at the first conflict — load-bearing on the warning path, where both
messages ride one
note), and dropping the "resolves to nothing" branch. The threewiring deletions each kill a distinct call-site test, so the one-line
ifs are notinvisible to the suite.
tsc --noEmitclean — and verified non-vacuous by planting a type error in the editedfile and watching it fail.
additions (127 lines) —
AMBIGUOUS_BARE_NAMES,REKEYED_REPOSandREGISTRY_TARGETSare byte-identical to what fix(1616): refuse the ambiguous from-source install instead of picking a repo #1619 shipped.
this is a pre-dispatch decision in the orchestrator that either returns a
conflictorthe same args as before.