Skip to content

feat(products): pick tags from a curated corpus in the product form - #647

Merged
alukach merged 16 commits into
mainfrom
feat/product-tags
Oct 26, 2026
Merged

alukach merged 16 commits into
mainfrom
feat/product-tags

Conversation

@alukach

@alukach alukach commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Product owners can now set a product's tags from the product form, in both create and edit mode, choosing from a curated corpus kept in a new DynamoDB table.

What changes

  • New table sc-{stage}-tags (deploy/lib/database-construct.ts), partition key tag_id, which is the tag string itself (the same string stored in product.metadata.tags). Each row is just { tag_id }. The Vercel role gets grantReadData only, because the app never writes the corpus. Curation happens outside the app.
  • TagsTable.listAll() (src/lib/clients/database/tags.ts) scans the table with pagination and returns the tags sorted.
  • TagPicker (src/components/features/products/TagPicker.tsx) shows the chosen tags as removable chips beside an "Edit tags" button. The button opens a Radix Themes Popover that lists every corpus tag as a checkbox, with a box to filter them, so people can browse the whole list without guessing what to type. Each chosen tag is submitted as its own tags form value from a hidden input next to its chip. It is wired into ProductCreationForm as a custom field under Description, and both pages pass the corpus in as tagOptions.
  • Server enforcement (src/lib/operations/products.ts): createProduct and updateProduct in the operations layer take an optional tags array, dedupe it, and reject any tag outside the corpus with a tags field error. A tag the product already carries is accepted even after it leaves the corpus. This follows the same legacy-drift rule as visibility: editing the title shouldn't fail on a tag nobody chose today. Because the API routes share these operations, POST and PATCH on products accept tags too, documented in the generated reference. The form actions in src/lib/actions/products.ts always pass formData.getAll("tags"); Object.fromEntries(formData) alone would keep only the last tag.
  • Tag cleanup and seeding: scripts/clean-tags.ts <stage> reads every product's free-text tags, cleans them, and builds the corpus from the result (see Tag cleanup below). It previews by default; APPLY=1 rewrites each product's metadata.tags to its cleaned tags and puts the corpus into sc-{stage}-tags. init-local creates the table and seeds it from the fixture products' tags.

Rollout

  1. Deploy the CDK stack, which creates the table and the grant.
  2. Run AWS_REGION=us-west-2 npx tsx scripts/clean-tags.ts prod and review the preview: the corpus, the dropped tags, and each product whose tags change. Production is in us-west-2; the script defaults to us-east-1 like the others in scripts/. Adjust ALIASES and KEEP in the script until the result is right.
  3. Run it again with APPLY=1. This rewrites product tags, so take an on-demand backup of sc-prod-products first. Until then the corpus is empty, and the popover shows "No tags to choose from yet."

Tag cleanup

Production's products carry 416 distinct free-text tags. Many are the same tag in another case or spelling (PMTiles and pmtiles; ftw and fieldsoftheworld), several tags typed as one string (#overturemaps #roads #riyadh …), places below country level (14 Spanish regions, German state codes from the fiboa datasets), or noise (test, foo, --, an empty tag, and geospatial and open data, which are true of everything on Source).

scripts/clean-tags.ts lowercases each tag, strips #, splits combined tags, maps synonyms, typos and places to one canonical tag through ALIASES, and drops noise. A canonical tag joins the corpus when two or more products carry it, or when it is in KEEP (countries, plus elevation, machine learning, time series, forests and hydrology). Each product keeps only its cleaned tags that are in the corpus.

Run against the production counts (TABLE=1 npx tsx scripts/clean-tags.ts --counts counts.txt): 416 raw tags → 196 cleaned → 136 in corpus. Counts are products per tag. They are built from per-tag counts, so a product that had two spellings of one tag counts twice and some totals run slightly high; the <stage> run counts per product exactly.

The 136 tags in the corpus, by number of products
Tag Products
field boundaries 113
geoparquet 101
pmtiles 85
agriculture 75
vector 72
fiboa 59
fields of the world 58
spain 30
fieldscapes 27
sentinel-2 25
segmentation 20
weather 18
climate 17
germany 17
zarr 17
building footprints 14
energy 13
hydrology 13
sigpac 13
crop type 12
crops 12
land cover 12
raster 12
united states 12
cog 11
satellite imagery 11
overture maps 10
saudi arabia 10
biodiversity 9
forecasting 9
parquet 9
sentinel-1 9
admin boundaries 8
places 8
precipitation 8
conservation 7
elevation 7
forests 7
india 7
labels 7
machine learning 7
carbon 6
cloud native 6
icechunk 6
japan 6
land use 6
solar 6
stac 6
transportation 6
vegetation 6
embeddings 5
global 5
landsat 5
ocean 5
openstreetmap 5
planetscope 5
sar 5
ai 4
carbon dioxide removal 4
flatgeobuf 4
foundation models 4
gapfilled 4
lidar 4
modis 4
natural gas 4
nhd 4
remote sensing 4
tanzania 4
wildfire 4
youthmappers 4
aerosols 3
atmosphere 3
belgium 3
canada 3
census 3
cer 3
clay 3
cloud 3
ecology 3
fisheries 3
image classification 3
imagery 3
netherlands 3
radar 3
reanalysis 3
regression 3
rwanda 3
africa 2
austria 2
bathymetry 2
brazil 2
computer vision 2
duckdb 2
eurocrops 2
exports 2
flood detection 2
geopackage 2
geostationary 2
google 2
h3 2
hls 2
imports 2
invekos 2
latvia 2
marine debris 2
microsoft 2
monitoring 2
naip 2
neb 2
noaa 2
object detection 2
observations 2
slovenia 2
switzerland 2
tiles 2
time series 2
united kingdom 2
usda 2
wetlands 2
wildlife 2
antarctica 1
cambodia 1
croatia 1
czechia 1
denmark 1
estonia 1
europe 1
finland 1
france 1
ireland 1
luxembourg 1
new zealand 1
portugal 1
slovakia 1
sweden 1
vietnam 1
Dropped because only one product carries them (60)
Tag Products
american bollworms 1
attribution benchmark 1
bioregionalisation 1
brp 1
catalog 1
copernicus 1
corridors 1
csv 1
digitaltwin 1
disaster risk 1
dwr 1
ee-mrio 1
egms 1
eia 1
environment 1
environmental data 1
epa 1
equity 1
esa 1
exiobase 1
ferc 1
finance 1
foodsecurity 1
foodsystems 1
fude 1
gdal 1
geocoding 1
geos 1
geozarr 1
goes 1
government 1
hex 1
historical 1
human footprint 1
iceberg 1
imputation 1
jpss 1
justice 1
landscape elements 1
meiji era 1
metop 1
nasa 1
neon 1
ngff 1
nsta 1
pink bollworms 1
plodiny 1
png 1
policy 1
population 1
procurement 1
property data 1
protected areas 1
realtime 1
sahara 1
snow 1
supplychain 1
synthetic data 1
terrestrial 1
usgs 1

Keep any of these by adding it to KEEP, or merge it into a corpus tag in ALIASES.

Every tag that changes (295 rows; unchanged tags appear only in the corpus list)
Count Before After Notes
30 ftw fields of the world Merged
28 fieldsoftheworld fields of the world Merged
10 geospatial — Dropped: noise
5 boundaries admin boundaries Merged
4 satellite satellite imagery Merged
4 overture overture maps Merged
4 landsat 8 landsat Merged
4 foundational model foundation models Merged
3 nhd nhd, hydrology Split into several tags
3 ml machine learning Merged
3 imagery satellite imagery Merged
3 (empty) — Dropped: noise
2 usa united states Merged
2 uk united kingdom Merged
2 sentinel2 sentinel-2 Merged
2 opendata — Dropped: noise
2 karnataka india Merged
2 forecast forecasting Merged
2 fieldboundaries field boundaries Merged
2 field boundary field boundaries Merged
2 field — Dropped: noise
2 earth — Dropped: noise
2 dem elevation Merged
2 crop crops Merged
2 cloud optimised geotiff cog Merged
2 climate change climate Merged
2 cdr carbon dioxide removal Merged
2 california united states Merged
2 buildings building footprints Merged
2 building footprint building footprints Merged
2 admin admin boundaries Merged
2 Sentinel2 sentinel-2 Merged
2 PMTiles pmtiles Normalized (case, #)
2 Japan japan Normalized (case, #)
2 COG cog Normalized (case, #)
2 AI ai Normalized (case, #)
2 #geoparquet #aois #places #dataset #riyadh #geospatial #overturemaps #saudiarabia geoparquet, places, saudi arabia, overture maps Split into several tags
1 youthmasppers youthmappers Merged
1 years — Dropped: noise
1 xai machine learning Merged
1 wheat crops Merged
1 wbd hydrology Merged
1 waterways hydrology Merged
1 watershed boundaries hydrology Merged
1 water hydrology Merged
1 wadhwaniai — Dropped: noise
1 vegetation cover vegetation Merged
1 valencia spain Merged
1 usgs — Dropped: on one product only
1 us united states Merged
1 tropical storm weather Merged
1 trees forests Merged
1 topography elevation Merged
1 timeseries time series Merged
1 tiff raster Merged
1 ticks biodiversity Merged
1 th germany Merged
1 testrepo12 — Dropped: noise
1 test unidata netcdf — Dropped: noise
1 test — Dropped: noise
1 terrestrial — Dropped: on one product only
1 temperature weather Merged
1 taxi zones transportation Merged
1 tamsat precipitation Merged
1 synthetic data — Dropped: on one product only
1 swiss switzerland Merged
1 supplychain — Dropped: on one product only
1 street level imagery imagery Merged
1 static — Dropped: noise
1 state — Dropped: noise
1 stac-geoparquet stac, geoparquet Split into several tags
1 sst ocean Merged
1 species distribution model biodiversity Merged
1 southern ocean ocean Merged
1 south fork eel river united states Merged
1 source — Dropped: noise
1 solar maps solar Merged
1 snow — Dropped: on one product only
1 slovenia fiboa field boundaries krms gerk geoparquet vector pmtiles slovenia, fiboa, field boundaries, geoparquet, vector, pmtiles Split into several tags
1 sl germany Merged
1 sixpac sigpac Merged
1 sh germany Merged
1 sfer — Dropped: noise
1 self-supervised machine learning Merged
1 sax — Dropped: noise
1 saudiarabia saudi arabia Merged
1 sahara — Dropped: on one product only
1 riyadh saudi arabia Merged
1 remotesensing remote sensing Merged
1 reanalysis data reanalysis Merged
1 realtime — Dropped: on one product only
1 rainfall radar precipitation, radar Split into several tags
1 pv solar Merged
1 public-data — Dropped: noise
1 public — Dropped: noise
1 protected areas — Dropped: on one product only
1 property data — Dropped: on one product only
1 procurement — Dropped: on one product only
1 population — Dropped: on one product only
1 polygon — Dropped: noise
1 political boundaries admin boundaries Merged
1 policy — Dropped: on one product only
1 pois places Merged
1 poi places Merged
1 png — Dropped: on one product only
1 pmtiles gtfs pmtiles, transportation Split into several tags
1 plodiny — Dropped: on one product only
1 planet fusion planetscope Merged
1 pink bollworms — Dropped: on one product only
1 photovoltaic solar Merged
1 perspective images imagery Merged
1 pacificspatial — Dropped: noise
1 overturemaps overture maps Merged
1 osm openstreetmap Merged
1 open data — Dropped: noise
1 ome — Dropped: noise
1 oil production energy Merged
1 observation observations Merged
1 nyc united states Merged
1 nwp forecasting, weather Split into several tags
1 nsta — Dropped: on one product only
1 nrw germany Merged
1 ngff — Dropped: on one product only
1 netherlands fiboa field boundaries aan geoparquet vector pmtiles netherlands, fiboa, field boundaries, geoparquet, vector, pmtiles Split into several tags
1 net zero carbon Merged
1 neon — Dropped: on one product only
1 nds germany Merged
1 navarra spain Merged
1 natural gas production natural gas Merged
1 national hydrology dataset nhd, hydrology Split into several tags
1 nasa — Dropped: on one product only
1 mv germany Merged
1 multitemporal time series Merged
1 mrms precipitation Merged
1 misc — Dropped: noise
1 metop — Dropped: on one product only
1 metadata — Dropped: noise
1 maize crops Merged
1 madrid spain Merged
1 luxembourg fiboa field flik boundaries geoparquet vector pmtiles luxembourg, fiboa, field boundaries, geoparquet, vector, pmtiles Split into several tags
1 lulc land cover, land use Split into several tags
1 lowcoder — Dropped: noise
1 live fuel moisture wildfire Merged
1 lidar 3dtiles mago lidar Merged
1 landscape elements — Dropped: on one product only
1 landcover land cover Merged
1 land-use and land-cover change land cover, land use Split into several tags
1 land — Dropped: noise
1 lacuna — Dropped: noise
1 ksrsac india Merged
1 karnatak india Merged
1 justice — Dropped: on one product only
1 jpss — Dropped: on one product only
1 ireland fiboa field boundarie geoparquet vector pmtiles ireland, fiboa, field boundaries, geoparquet, vector, pmtiles Split into several tags
1 insolation solar Merged
1 imputation — Dropped: on one product only
1 imerg precipitation Merged
1 image — Dropped: noise
1 iceberg — Dropped: on one product only
1 hydrography hydrology Merged
1 human footprint — Dropped: on one product only
1 historical — Dropped: on one product only
1 hex — Dropped: on one product only
1 height elevation Merged
1 hcat — Dropped: noise
1 havasu united states Merged
1 hamburg germany Merged
1 groundwater quality hydrology Merged
1 groundtruthed labels Merged
1 ground-truthed labels Merged
1 ground-truth labels Merged
1 gpm precipitation Merged
1 government — Dropped: on one product only
1 goes — Dropped: on one product only
1 geozarr — Dropped: on one product only
1 geos — Dropped: on one product only
1 geocoding — Dropped: on one product only
1 geo spatial — Dropped: noise
1 gdal — Dropped: on one product only
1 galicia spain Merged
1 fude — Dropped: on one product only
1 forest carbon forests, carbon Split into several tags
1 forest forests Merged
1 forecasts forecasting Merged
1 foodsystems — Dropped: on one product only
1 foodsecurity — Dropped: on one product only
1 foo — Dropped: noise
1 flowlines hydrology Merged
1 flanders belgium Merged
1 fishbase fisheries Merged
1 fish fisheries Merged
1 fire wildfire Merged
1 finance — Dropped: on one product only
1 ferc — Dropped: on one product only
1 farmland agriculture Merged
1 extremadura spain Merged
1 euhvd — Dropped: noise
1 esa — Dropped: on one product only
1 era5 reanalysis, climate Split into several tags
1 equity — Dropped: on one product only
1 epa — Dropped: on one product only
1 environmental data — Dropped: on one product only
1 environment — Dropped: on one product only
1 energy use by fuel energy Merged
1 energy use energy Merged
1 energy transition energy Merged
1 energy production energy Merged
1 energy efficiency energy Merged
1 energy demand by sector energy Merged
1 energy demand energy Merged
1 emissions carbon Merged
1 embedding embeddings Merged
1 electricity generation energy Merged
1 electricity energy Merged
1 eia — Dropped: on one product only
1 eatch monitoring monitoring Merged
1 dwr — Dropped: on one product only
1 dust aerosols Merged
1 drone imagery Merged
1 disaster risk — Dropped: on one product only
1 digitaltwin — Dropped: on one product only
1 deforestation forests Merged
1 deep learning machine learning Merged
1 dataset — Dropped: noise
1 czech czechia Merged
1 csv — Dropped: on one product only
1 croptype crop type Merged
1 county — Dropped: noise
1 countries — Dropped: noise
1 cotton crops Merged
1 corridors — Dropped: on one product only
1 contour elevation Merged
1 coal energy Merged
1 cloud-native cloud native Merged
1 cdl crop type, united states Split into several tags
1 catalonia spain Merged
1 catalog — Dropped: on one product only
1 castillalamancha spain Merged
1 castile and leon spain Merged
1 carbon emissions carbon Merged
1 cantabria spain Merged
1 canary islands spain Merged
1 businesses places Merged
1 building building footprints Merged
1 brp — Dropped: on one product only
1 bremen germany Merged
1 boundary vector agriculture field boundaries, vector, agriculture Split into several tags
1 blore india Merged
1 bioregionalisation — Dropped: on one product only
1 biomass forests Merged
1 belgium wallonia fiboa field boundaries geoparquet vector pmtiles belgium, fiboa, field boundaries, geoparquet, vector, pmtiles Split into several tags
1 beetles biodiversity Merged
1 bb germany Merged
1 basque spain Merged
1 balearic islands spain Merged
1 attribution benchmark — Dropped: on one product only
1 arc — Dropped: noise
1 aragon spain Merged
1 aquatics ocean Merged
1 aois — Dropped: noise
1 andalusia spain Merged
1 andalucía spain Merged
1 analysis — Dropped: noise
1 amx — Dropped: noise
1 american bollworms — Dropped: on one product only
1 aerosol aerosols Merged
1 Trees forests Merged
1 Tottori japan Merged
1 Topographic Map elevation Merged
1 Sentinel2 mosaics cloud-free global cog raster sentinel-2, global, cog, raster Split into several tags
1 PMTiles FlatGeobuf pmtiles, flatgeobuf Split into several tags
1 Meiji era — Dropped: on one product only
1 Forest forests Merged
1 EXIOBASE — Dropped: on one product only
1 EU europe Merged
1 EGMS — Dropped: on one product only
1 EE-MRIO — Dropped: on one product only
1 DEM elevation Merged
1 Copernicus — Dropped: on one product only
1 Cloud-Native cloud native Merged
1 3d tiles lidar Merged
1 -- — Dropped: noise
1 #waterways hydrology Merged
1 #vector vector Normalized (case, #)
1 #tif raster Merged
1 #stac stac Normalized (case, #)
1 #sentinel #satellite #imagery #cog #aws #remotesensing #saudiarabia #tabaqat sentinel-2, satellite imagery, cog, remote sensing, saudi arabia Split into several tags
1 #riyadh #satellite #rgb #cog #2024 saudi arabia, satellite imagery, cog Split into several tags
1 #raster raster Normalized (case, #)
1 #pmtiles #cng #saudiarabia #sentinel #tabaqat #aws pmtiles, cloud native, saudi arabia, sentinel-2 Split into several tags
1 #parquet parquet Normalized (case, #)
1 #overturemaps #roads #riyadh #osm #pmtiles #cng #tabaqat overture maps, transportation, saudi arabia, openstreetmap, pmtiles, cloud native Split into several tags
1 #overturemaps #roads #pmtiles #cng #osm #tabaqat #saudiarabia overture maps, transportation, pmtiles, cloud native, openstreetmap, saudi arabia Split into several tags
1 #geospatial #ksa #riyadh #geoparquet #places #pois saudi arabia, geoparquet, places Split into several tags
1 #cbam #climate reanalysis reanalysis, climate Split into several tags

Decisions worth a second look

  • Which tags survive is a judgement call. The ALIASES and KEEP lists in scripts/clean-tags.ts encode it, and the tables above show the result. Calls most worth checking: boundaries and admin become admin boundaries; satellite and imagery merge into satellite imagery; agencies on one product (nasa, usgs, esa, epa) are dropped while those on two or more (noaa, usda, cer, neb) stay.
  • No admin UI for the corpus. Tags are added in the AWS console or CLI for now. An admin page is the natural follow-up if curation becomes frequent.
  • Leaving tags out of an update keeps them; an empty list clears them. That suits API callers, who shouldn't wipe a product's tags by editing its title. The form always sends the list, because removing every chip submits no tags field at all, and that has to clear them.
  • No cap on tag count. The docs used to promise "up to 20". Nothing enforced that before and nothing does now, so I dropped the claim from the docs instead of inventing a limit.
  • Popover picker instead of a datalist or a combobox library. The first version used a native <datalist>, but its dropdown ignored the theme and hid the list until you typed. Of the alternatives (every tag as a toggle chip, or a popover like GitHub's label picker), the popover keeps the field compact and uses only components Radix Themes already ships. The list renders every tag without virtualisation, which is fine to a few hundred tags; past that it should be virtualised (marked with a ponytail: comment).

Stories

On this branch's Storybook deploy:

The Filtering story, with the popover open and filtered to tags containing "c":

TagPicker with the popover open

Docs

  • docs/using-source/create-a-data-product.md described tags as "Comma-separated, up to 20 tags", which is now stale. Updated in docs.source.coop#39.
  • docs/about-source/core-concepts.md ("Tags and Discoverability") still holds: it says what tags are for, not how they are entered.
  • public/llms.txt doesn't mention tags, and this adds no public endpoint. Checked the data-proxy ADRs: tags have no bearing on authorization, credentials, or federation.

Testing

  • npm run test for src/lib/actions/products.test.ts and src/components/features/products/TagPicker.test.tsx passes locally (30 tests), and so does npm run type-check. npm run lint reports nothing in the changed files.
  • The action tests cover create saving corpus tags, rejecting unknown ones, and not reading the tag table when no tags are sent, and update saving corpus tags, rejecting unknown ones, and keeping a retired tag the product already had.
  • TagPicker.test.tsx covers removing a chip, and the popover filtering case-insensitively and toggling a tag on and off.

🤖 Generated with Claude Code

The product form, in create and edit mode, gets a Tags field: a text input that suggests tags from a new sc-{stage}-tags table (keyed by tag_id, the tag itself) and shows chosen tags as removable chips. createProduct and updateProduct save the submitted tags to metadata.tags and reject any outside the corpus, except tags the product already carries, so a tag retired from the corpus never blocks an edit to some other field.

The app only reads the corpus, so the Vercel role gets read access to the table and nothing more. scripts/seed-tags.ts seeds it from the tags products already have, and init-local creates and seeds it locally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
source-coop-ui Ready Ready Preview Oct 26, 2026 7:23pm UTC
source-cooperative Ready Ready Preview Oct 26, 2026 7:23pm UTC

Request Review

@claude

claude Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @alukach's task in 22s —— View job


✅ No blocking issues — safe to merge.

I read the operations, action, TagsTable, TagPicker, form wiring, CDK and clean-tags.ts changes in the PR. I did not run the tests or type-check.

  • The server enforces the corpus. createProduct and updateProduct dedupe the tags and reject any tag outside the corpus. A tag the product already has is allowed through (src/lib/operations/products.ts:98-109).
  • Leaving tags out of an update keeps the existing tags. The form actions always send getAll("tags"), so removing every chip clears them (src/lib/actions/products.ts).
  • The Vercel role gets grantReadData only (deploy/lib/api-stack.ts:63), which matches the stated intent that the app never writes the corpus.
  • The popover blocks Enter in the filter box, so it can't submit the form behind it.

Non-blocking notes:

  • TagsTable.listAll goes through cachedSend, so a corpus edit made in the console may not show up until the memoised read expires. If that window is long, curators will see new tags appear late. Check the TTL in base.ts, or note it in the rollout steps.
  • checkTags scans the whole table on every create or update that sends tags. That is fine at ~136 rows and cached.
  • clean-tags.ts has an APPLY mode that rewrites products in place. The PR description already says to take a backup of sc-prod-products first. Several reviewer suggestions on the aliases look already applied in the script, such as the ome removal and the extra KEEP entries. The tables in the description may be out of date, so re-run TABLE=1 and refresh them before merge. Still open from the review thread: whether field boundaries should fold into boundaries (alukach, 2026-10-26).

Simplify (ponytail)

  • src/components/features/products/TagPicker.tsx: the filter reset in onOpenChange could go if the filter is allowed to persist across opens. This is cosmetic.
  • scripts/clean-tags.ts: the --counts and TABLE preview paths are only needed for the one-time cleanup. Delete the script once prod has been cleaned.

Docs

  • The description names docs.source.coop#39 for the stale "comma-separated, up to 20" line. It says core-concepts.md still holds. It also says llms.txt has no tags entry and no public endpoint was added. It also says the data-proxy ADRs were checked and tags have no bearing on authorization, credentials or federation. That covers the CLAUDE.md requirement.
  • One gap: POST and PATCH /api/v1/products now accept tags. That is a new thing an agent could use, so consider a line in public/llms.txt if tags are meant to be scriptable. The current description says nothing is needed.

💰 Estimated review cost: $0.63 · 0m21s · 8 turns

The browser's datalist dropdown wasn't styled by the theme and hid the corpus until you typed. Chosen tags now show as removable chips beside an "Edit tags" button, which opens a popover listing every corpus tag as a checkbox, with a box to filter them. Submission and server-side validation are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alukach added a commit to source-cooperative/docs.source.coop that referenced this pull request Oct 8, 2026
The tag field now opens a list to tick tags from rather than suggesting them as you type (source-cooperative/source.coop#647).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
parseTags now returns either the tags or the form error naming those outside the corpus, so the one-use unknownTagsState wrapper goes away. The popover's tag list gets a note on its ceiling: it renders every tag, which is fine to a few hundred and wants virtualising past that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/clean-tags.ts replaces seed-tags.ts. Instead of copying every free-text tag into the corpus, it lowercases them, strips `#`, splits tags that hold several, maps synonyms, typos and sub-national places to one canonical tag, and drops noise. A canonical tag joins the corpus when two or more products carry it, or when it is on a keep-list (countries and a few broad topics). Each product then keeps only its cleaned tags that made the corpus.

It previews by default and writes only with APPLY=1. Given the `uniq -c` counts of production's tags (--counts), it previews from those instead of DynamoDB, and TABLE=1 adds a before/after table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Saving a product without tags no longer reads the tags table, so a create or edit that sets no tags doesn't depend on that table existing. The picker's toggle also updates from the latest tags rather than the ones it rendered with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alukach

alukach commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

@tylere please review the tag corpus table in this PR description and the tags that we will be dropping, I'd be curious to hear your thoughts

@tylere

tylere commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

While I am fully on board with (e.g. tag cleanup), I think there are aspects that would benefit from future discussion.
Some examples:

  • What is the mechanism by which users can suggest new tags?
  • Would it be beneficial to group/order tags in some way to guide users in selecting appropriate tags?
    • Examples
      • feature type (e.g. field-boundaries, land-parcels, admin-units)
      • methodology (e.g. remote-sensing, machine-learning, foundation-model)
      • format (e.g. cog, zarr, pmtiles)
      • domain (e.g. climate, weather, oceanography, agriculture, conservation, medical)
      • temporal frequency (e.g. hourly, daily, yearly)
  • Should we give some indication of a tags popularity?

@alukach
alukach added this pull request to stack #660 October 13, 2026 17:55
# Conflicts:
#	src/lib/actions/products.test.ts
#	src/lib/actions/products.ts
Co-authored-by: Tyler Erickson <92862+tylere@users.noreply.github.com>

@tylere tylere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of minor suggestions on aliasing, but approving overall.

Comment thread scripts/clean-tags.ts Outdated
Comment thread scripts/clean-tags.ts
Comment thread scripts/clean-tags.ts
Comment thread scripts/clean-tags.ts
Comment thread scripts/clean-tags.ts Outdated
Comment thread scripts/clean-tags.ts
Comment thread scripts/clean-tags.ts Outdated
Comment thread scripts/clean-tags.ts
Comment thread scripts/clean-tags.ts Outdated
Comment thread scripts/clean-tags.ts Outdated
Co-authored-by: Tyler Erickson <92862+tylere@users.noreply.github.com>
Co-authored-by: Tyler Erickson <92862+tylere@users.noreply.github.com>
alukach and others added 2 commits October 19, 2026 19:14
Co-authored-by: Tyler Erickson <92862+tylere@users.noreply.github.com>
Co-authored-by: Tyler Erickson <92862+tylere@users.noreply.github.com>
alukach and others added 2 commits October 19, 2026 19:15
Co-authored-by: Tyler Erickson <92862+tylere@users.noreply.github.com>
Co-authored-by: Tyler Erickson <92862+tylere@users.noreply.github.com>
Co-authored-by: Tyler Erickson <92862+tylere@users.noreply.github.com>

This branch was successfully deployed

3 active (1 outdated) and 1 inactive (outdated) deployments
Preview – source-cooperative — b0e8fabc Deployed Oct 26, 2026 by vercel[bot]
Preview – source-coop-ui — b0e8fabc Deployed Oct 26, 2026 by vercel[bot]
Staging-Infra — 8faa705d Deployed Oct 19, 2026 by alukach via deploy-cdk #306
Staging — 8faa705d Deployed Oct 19, 2026 by alukach via deploy-vercel #306
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants