You are Claude Code. This brief is the source of truth. Read it once, then execute.
Add labor unions to the Commonweave directory as a new taxonomy branch. Target: federations and national-level unions first (~300-600 orgs), not locals. Cover two ingest paths: Wikidata SPARQL and ITUC affiliates list.
Labor unions are one of the oldest working examples of democratic ownership of a commons (collective labor power). They're principles 3 (Democratic Sovereignty) and 4 (Common Ownership) of the Commonweave framework expressed in the real world. Co-ops, mutual aid societies, and credit unions are already indexed. Unions are the missing sibling.
- Follow the existing pipeline conventions. Don't invent new patterns. Model
ingest_unions.pyoncommonweave/data/ingest_wikidata_bulk.py. - Federations + nationals only in v1. No locals. Locals are a v2 conversation. A good filter: orgs whose
instance of/subclass ofin Wikidata is "trade union federation" (Q3395115), "national trade union center" (Q11038979), or "trade union" (Q178790) and that have "country" set and "headquarters location" set at the national level. ITUC affiliates are all federation-tier by definition. - Mark legibility correctly. ITUC affiliates and Wikidata-sourced national unions =
legibility=formal. Works councils embedded in law (Germany, Austria, Netherlands) =legibility=formal. Informal worker solidarity networks without legal status =legibility=informal(skip for v1 unless already in Wikidata). - No em dashes in any user-visible text.
- Surgical. Don't touch unrelated files. Don't refactor the pipeline.
- Don't run this yet if PIPELINE-PDF-BRIEF.md is still mid-flight. If you see uncommitted changes related to
pipeline.pdf/PIPELINE.mdin the working tree, STOP and announce: the previous task isn't done. This brief runs after the PDF ships and is committed.
commonweave/data/_common.py(shared helpers, DB connection)commonweave/data/ingest_wikidata_bulk.py(model your script on this)commonweave/data/migrate_legibility.py(so you understand the legibility column)commonweave/data/taxonomy.yaml(so you know where to add the new branch — don't dump it in your output, just know the shape)commonweave/PIPELINE.md(should exist from the previous task — confirms the pipeline contract)commonweave/data/dedup_merge.py(so you understand dedup rules — name+country isn't enough, needs location match)
Add a new top-level branch to commonweave/data/taxonomy.yaml:
labor:
description: Worker power and democratic labor organizations
subtypes:
union_federation:
description: National or international federations of unions (AFL-CIO, ITUC, IG Metall)
wikidata_ids: [Q3395115, Q11038979]
national_union:
description: National-level trade unions
wikidata_ids: [Q178790]
worker_cooperative_federation:
description: Federations of worker cooperatives (distinct from individual worker co-ops)
wikidata_ids: [Q4539] # cooperative federation; filter by "worker" subtype in code
works_council_system:
description: Legally-mandated worker representation bodies (German Betriebsrat, Dutch OR, etc.)
wikidata_ids: [Q1141395]
labor_education_org:
description: Worker education, labor colleges, union training institutes
wikidata_ids: [] # manual curation or Wikidata text searchIf the YAML structure in the existing file differs, match the existing pattern. Don't break the file.
Model on ingest_wikidata_bulk.py. Structure:
- Wikidata SPARQL query for each taxonomy subtype above (filter by country-level, skip locals by requiring HQ set + excluding subclasses like "local union chapter" if those exist as Wikidata classes).
- For each org: insert with
legibility=formal, category=labor/<subtype>, source=wikidata_unions, evidence URL = Wikidata item URL. - Dedup against existing rows using the same rules as
dedup_merge.py(name + country + location proximity). Do NOT run dedup yourself — just insert, and let the existing dedup pipeline catch overlaps on the next run. - Log runs to
commonweave/data/ingest-unions-run.log. - Be idempotent: re-running should update existing rows (by Wikidata ID), not create duplicates.
- Print a summary at the end: total queried, total inserted, total updated, total skipped (and why).
Separate script for ITUC (International Trade Union Confederation) affiliates.
- Source: https://www.ituc-csi.org/list-of-affiliated-organisations (public listing). If the listing URL has moved, find the current one. If no clean page exists, fall back to Wikipedia's "List of trade unions" by country pages — document the fallback in the script header.
- Parse the affiliate list: name, country, acronym, website, member count if listed.
- Insert with
legibility=formal, category=labor/union_federationorlabor/national_union, source=ituc_affiliates. - Be polite to the server:
time.sleep(1)between requests, proper User-Agent, respect robots.txt. - Cache HTML responses to
commonweave/data/sources/ituc-cache/so re-runs don't re-hit the server.
Add a ingest_labor.py dispatcher that runs both scripts in order (Wikidata first, then ITUC). Mirrors the ingest_india.py pattern.
- Append a "Labor unions" section to
commonweave/PIPELINE.mdexplaining the new ingest branch. Match the voice and formatting of the rest of the file. - Add a row to
commonweave/DATA.md(if the file documents sources in a list/table) forituc_affiliatesandwikidata_unions. - Update the PDF? No, skip re-rendering the PDF for this pass. Simon can re-render on the next big update. Note the omission in your final commit message so he knows.
Run python commonweave/data/ingest_unions.py --dry-run (add a --dry-run flag that queries Wikidata but doesn't write to the DB). Print the count of orgs that would be inserted, broken down by subtype. This is your verifier — if it returns 0 or some absurd number like 50,000, something's wrong with the SPARQL query.
Target range for dry-run insert count: 200-800 orgs across all subtypes combined. If outside that range, inspect the query and fix before doing a real run.
After the dry-run looks sane, run both scripts for real:
python commonweave/data/ingest_unions.py
python commonweave/data/ingest_ituc.pyCapture the output. If the real run diverges wildly from the dry-run estimate, stop and flag it rather than committing a bad batch.
After ingest, the map + search index are stale. Run:
python commonweave/data/build_map_v2.py
python commonweave/data/build_search_index.py
python commonweave/data/export_directory.pyThis updates the files that the live site uses. Then copy any changed artifacts from commonweave/data/ to simonlpaige.github.io/commonweave/data/ (follow whatever pattern the existing cron commonweave-frontend-update uses — look at its payload in the gateway config, or just mirror the files that differ).
Workspace repo (C:\Users\simon\.openclaw\workspace\commonweave\, branch master):
- Commit message first line:
Add labor unions to the directory (Wikidata + ITUC). - Body: one short paragraph, Feynman voice, no em dashes. What you added, how many orgs it brought in, the legibility decisions you made, the note that the PDF wasn't re-rendered.
Live-site repo (C:\Users\simon\.openclaw\workspace\simonlpaige.github.io\, branch main):
- Commit message first line:
Update directory data: labor unions added. - Body: one short paragraph. What changed on the public-facing side (map points, search index, directory export).
Push both.
-
taxonomy.yamlhas alaborbranch with subtypes -
ingest_unions.pyexists, has--dry-run, runs cleanly -
ingest_ituc.pyexists, caches HTML, runs cleanly -
ingest_labor.pydispatcher exists - Dry-run printed a sane count (200-800 orgs)
- Real run completed, summary captured
-
PIPELINE.mdhas a "Labor unions" section - Map + search index + directory export rebuilt
- Live-site data files synced
- Workspace repo committed + pushed
- Live-site repo committed + pushed
- At end, run:
openclaw system event --text "Done: Labor unions added to Commonweave. N orgs inserted. Ready for Codex red-team." --mode now(replace N with actual count)
- Wikidata SPARQL endpoint rate-limits? Add
time.sleep(2)between queries and set User-Agent. - ITUC site structure has changed? Document the fallback source, implement that, move on.
- Dry-run count is way off? Don't force it. Stop, write what you found to
commonweave/data/ingest-unions-run.log, and announce:openclaw system event --text "ingest_unions dry-run returned X (expected 200-800). Need Simon's input on SPARQL query." --mode now.
- State assumptions. Ask when confused.
- Minimum code. No speculative abstractions.
- Touch only what the task requires.
- The "Done" checklist is your verifier.