Summary
Follow-up to #4257 (fixed in #4273). That issue's Not covered here section flags a deeper design question the memoization fix deliberately left open:
Whether resolve_dc_path doing network I/O implicitly, from deep inside Config::new, is itself the thing to revisit — a config constructor is a surprising place for a fetch, and it is what makes the multiplication so easy to miss.
Context
Config::new (src/config.rs:203) resolves a dc:<name> input by calling diskcache::resolve_dc_path, which — past TTL and unless the entry's refresh_policy is Never — calls get_resource, a network fetch. So constructing a Config from a dc: input can quietly reach out to the network.
#4273 bounded the damage: a (cache_dir, name)-keyed per-run memo means only the first resolution in a run does the fetch/materialization, so every consumer sees the same snapshot. But it deliberately did not move the fetch out of the constructor. The surprising shape remains:
Possible direction
This is essentially Option 1 from #4257: resolve a dc: handle once at command entry, rewrite arg_input to the concrete materialized path so no dc: prefix survives into the rest of the run, and carry the display name (dc:name) separately for titles, derived sidecar names and error messages. viz smart already demonstrates exactly this "resolve once, thread the concrete path, keep the raw handle for display" pattern (src/cmd/viz.rs), so it is proven rather than hypothetical — but it is a wider, user-visible change touching many commands, which is why it was split out of the #4257 fix.
Not urgent
The correctness problem from #4257 is resolved. This is a design/ergonomics cleanup — making the network I/O explicit and keeping constructors side-effect-free — not a bug. Filing so the thread isn't lost.
Summary
Follow-up to #4257 (fixed in #4273). That issue's Not covered here section flags a deeper design question the memoization fix deliberately left open:
Context
Config::new(src/config.rs:203) resolves adc:<name>input by callingdiskcache::resolve_dc_path, which — past TTL and unless the entry'srefresh_policyisNever— callsget_resource, a network fetch. So constructing aConfigfrom adc:input can quietly reach out to the network.#4273 bounded the damage: a
(cache_dir, name)-keyed per-run memo means only the first resolution in a run does the fetch/materialization, so every consumer sees the same snapshot. But it deliberately did not move the fetch out of the constructor. The surprising shape remains:Config::newis documented and used as an infallible, cheap constructor (it returnsConfig, notResult; adc:resolution failure is stashed and surfaced later viaformat_error). A network round-trip hidden inside it is at odds with that contract.dc:handle many times independently, and each resolution can refresh the cache #4257 describes (one command building manyConfigs, each re-resolving) was easy to miss in the first place. The memo hides the cost, not the surprise.Possible direction
This is essentially Option 1 from #4257: resolve a
dc:handle once at command entry, rewritearg_inputto the concrete materialized path so nodc:prefix survives into the rest of the run, and carry the display name (dc:name) separately for titles, derived sidecar names and error messages.viz smartalready demonstrates exactly this "resolve once, thread the concrete path, keep the raw handle for display" pattern (src/cmd/viz.rs), so it is proven rather than hypothetical — but it is a wider, user-visible change touching many commands, which is why it was split out of the #4257 fix.Not urgent
The correctness problem from #4257 is resolved. This is a design/ergonomics cleanup — making the network I/O explicit and keeping constructors side-effect-free — not a bug. Filing so the thread isn't lost.