Hi @Alex
We're finishing the implementation of tool_uca_maps and preparing everything for the PR.
Before we open it, six questions are still pending: in each one the tool's code depends on your
answer, so we'd rather ask than guess. They are independent of the tile proxy / data-shape thread
from 7 October.
All paths are relative to the repository root and checked against master as of 21 October.
The tool lives in tools/tool_uca_maps/.
- A Beerware-licensed browser library
-----------------------------------------------------------------------------------------------
Today. The tool vendors two browser libraries under tools/tool_uca_maps/js/lib/:
- dom-to-image-more 3.10.2 (MIT), used by js/map_image_download.js to export the map as an
image;
- Leaflet.ImageOverlay.Rotated 0.1.4 (https://github.com/IvanSanchez/Leaflet.ImageOverlay.Rotated),
used by js/image_upload.js (loaded at :90, called at :631). It places a georeferenced image
by three corner points instead of a bounding box: a GeoTIFF reprojected to WGS84 is a
slightly rotated, skewed quadrilateral, and L.imageOverlay(url, bounds) would stretch it.
Plan. Following engineering/VENDORING.md §1 and the existing rows in
src/core/client_libs/registry.ts (qrcode, geoman), both move to exact npm pins (no ^) plus a
registry row each. That also clears dependency_integrity_tripwire and
client_idempotency_tripwire, and about 312 of the tool's lint:browser warnings.
The problem. Leaflet.ImageOverlay.Rotated is Beerware: "as long as you retain this notice you
can do whatever you want with this stuff" - as permissive as MIT, with no other condition. But
Beerware is not in LICENCE_SPDX_IDS (scripts/vendor_verify.ts:169: MIT, ISC, BSD-2/3-Clause,
Apache-2.0, GPL/LGPL, MPL-2.0), and adding it is a core decision.
Question. Is Beerware acceptable for a pinned dependency served to browsers (i.e. would you add
it to LICENCE_SPDX_IDS)? If not, we'll drop the library and reimplement its 60 lines of logic
(the affine CSS transform from three corners) inside the tool, with its own tests.
- Which outbound door for five third-party API lookups
-----------------------------------------------------------------------------------------------
Today. Five server modules of the tool call fixed public APIs with fetchGuardedText:
The problem. Since 1 October, engineering/OUTBOUND_SPEC.md §2 (table row at :52) says
fetchGuardedText is "engine code only - never a tool", and test/unit/ssrf_one_guard_tripwire.test.ts
is red on exactly these five files. The door left to a tool, harvestFetch, cannot carry them:
- it honours robots.txt, and Nominatim's robots.txt disallows /search for *, so the place
search would stop working;
- it paces >= 3 s per origin (src/core/harvest/pacing.ts:36, MIN_INTERVAL_MS), which turns a
Catastro click into 4-7 s and DARE's autocomplete into 6-7 s.
And by the spec's own rule ("the request's KIND chooses the door") these are API calls, not
harvesting. What a user points the tool at (WMS GetCapabilities in server/wms.ts, linked images
in server/external_image.ts, the gazetteer dumps in gazetteers/provision.ts) already goes through
harvestFetch, and stays there. We won't copy the guard into the tool (CARRY-14).
Questions.
a. Where should these calls live: a per-service engine module like
src/core/tools/translation.ts, something under src/external/, or another place?
b. Fixed endpoints as in v6, or operator-configured?
c. Pacing: is the per-origin concurrency cap you designed for the tile proxy the right one
here? Nominatim's usage policy asks for <= 1 request/s and an identifying User-Agent.
As with the proxy, we'd leave the engine side to you and adapt the tool to whatever door you
provide.
- Who shows a tool's refusal to the user
-----------------------------------------------------------------------------------------------
Today. When the tool refuses something (a .zip without a .shp, a file GDAL cannot convert, a
target it cannot process), its server throws a public DedaloError - request.invalid_options,
tool.unsupported_target, media.file_not_found or media.upload_rejected - with the concrete
reason as publicMessage. 18 of the tool's 46 throws carry one. Then, on the client:
- client/dedalo/core/common/js/data_manager.js:798 publishes every api_error, so the page
shows its notice (top right);
- that notice uses error_text() in client/dedalo/core/common/js/render_api_error.js:75, which
shows the code's label from master.json - the generic one ("Invalid request options"),
since tool codes are generic by design (src/core/errors/registry.ts:2470);
- so the tool also writes its own line in the panel the user is working in
(tools/tool_uca_maps/js/tool_error_text.js, REASON_CODES): a translated lead ("The file
could not be uploaded") plus the concrete reason, which is English because publicMessage has
no translation path.
The user sees two notices for one refusal, and only the tool's says what happened.
What we can't do in the tool. data_manager has notices:'caller' (:524, :825) for a caller that
renders its own notices, but nothing equivalent for a failure; registering the codes as silent
would mute them page-wide. And a concrete, translated message would need either new codes with
master.json labels or a label coming from the tool's register.json - both are core decisions.
Question. Do you want core to be the one that shows a tool's refusal, with a translated text
that says what happened (and the tool to drop its own line)? Or which approach do you prefer?
Whatever you choose, we'll map the tool's 18 reasons to it.
- A retryable failure on a read ends as "outcome unknown"
-----------------------------------------------------------------------------------------------
What the user sees. They click a parcel while Catastro is in maintenance and, instead of
"Catastro is not responding", they read idempotency.outcome_unknown: "The first attempt failed
after it may already have been applied" - on a lookup that writes nothing.
Why it happens.
- Some codes are retryable in src/core/errors/registry.ts: security.outbound_failed (thrown
by the guard when the remote server fails), rate.limited, external.http_status. On those,
data_manager retries automatically with the same idempotency_key (default 5 retries).
- Every tool action goes through tool_request, classed
mutating in
src/core/api/dispatch.ts:390 because it is a polymorphic door - the right fail-safe for
writes.
- When a mutating action throws, runIdempotentLeader (src/core/api/dispatch.ts:933, catch at
:968-982) keeps the key marked ambiguous, so every retry gets idempotency.outcome_unknown
instead of the real error.
A tool cannot opt out: tool_request in client/dedalo/core/tools_common/js/tool_common.js
(request at :662) forwards no retries option to data_manager.request.
Effect on the tool's code. To avoid it, the tool answers a non-2xx from a provider with
tool.action_failed (not retryable) instead of the code the registry intends,
external.http_status (server/wms.ts:73, server/roman_empire.ts:357, server/external_image.ts:123).
But security.outbound_failed is thrown by the guard itself, so with Catastro, IGN or Nominatim
down the user does get the misleading message, in normal use.
Question. Should a thrown retryable failure be stored as a replayable answer (so the retry gets
the real error), or should a tool action be able to declare itself read-only - or pass
retries through tool_request? With either, we'd switch those three files to
external.http_status / external.not_found.
- The tool's colour tokens don't reach its panels
-----------------------------------------------------------------------------------------------
Today. scripts/tool_colors.ts:270 emits each tool's four tokens (--tool_edge, --tool_fill,
--tool_fill_hover, --tool_on_fill) under .tool_header.<tool>, .wrapper_tool.<tool> only
(generated into client/dedalo/core/tools_common/css/tool_colors.less, :440-447 for this tool),
and since 83394ab5ae the checkbox/radio accent keys off the same selector pair.
The problem. tool_uca_maps works on component_geolocation's map: its modal is only a launcher
that closes itself once the tool's controls are on the map, and every panel hangs off the map container, outside
any .wrapper_tool. There the tokens don't exist, so:
- test/unit/tool_color_contrast_tripwire.test.ts is red;
- the panels can't use the shared .tool_action_button() mixin
(client/dedalo/core/tools_common/css/tool_mixins.less:16) - it has no colours there - so
the tool keeps its own mixins with the same pixels (only the hover differs).
Giving each panel the .wrapper_tool class drags in page layout from
client/dedalo/core/tools_common/css/tool_common.less:33-41 (contain:content, display:flex,
flex-wrap, min-height:inherit), and copying the token values into the tool's stylesheet would
fork what the generator owns.
Question. Would you add a box-free scope to the generator, e.g. [data-tool="<tool>"], used by
both the token rule and the checkbox accent? We'd put that attribute on each panel and switch
the buttons to .tool_action_button().
- Which languages a tool should ship
-----------------------------------------------------------------------------------------------
Today. tools/tool_uca_maps/register.json carries its labels (about 434) in lg-eng and lg-spa
only. Most tools ship the same ten (lg-cat, lg-deu, lg-ell, lg-eng, lg-eus, lg-fra, lg-ita,
lg-nep, lg-por, lg-spa), some add lg-ara and lg-vlca; a missing lang falls back down
resolve/lang_fallback.ts. We can review lg-spa, lg-cat and lg-vlca ourselves; any other lang
would be machine-translated.
Question. Is there a minimum set of languages a tool must ship for the PR? And for the langs no
one on our side can review, do you prefer machine translations or leaving them to the fallback?
Best,
Sergio