diff --git a/.project/experiments/p1-29-tier10-by-t200.md b/.project/experiments/p1-29-tier10-by-t200.md index 5e8ccedb..733a9d48 100644 --- a/.project/experiments/p1-29-tier10-by-t200.md +++ b/.project/experiments/p1-29-tier10-by-t200.md @@ -110,6 +110,27 @@ Per user direction (2026-04-27): "give AI a multiplier of each producible game u Status: framework wired, tunings TBD, gate still blocked on combat-dev. +## Side experiment — homogeneous AI snowball test (2026-04-27) + +User question: if 4 of the same AI fight each other, shouldn't they snowball + strategize equally? + +**Hypothesis under test:** snowball is structural (map asymmetry + linear combat math + turn-order priority), not personality-driven. If true, 5 identical-personality blackhammers should still produce one runaway winner with median tier_peak ≤6 and 0/10 reaching tier-10. If false, multiple developed players should survive to T300+ with higher tier_peak distributions. + +**Setup:** `.local/iter/snowball-test-homogeneous-bh-20260427_015037/`. AI_PIN_PERSONALITY_P{0..4}=blackhammer, AI_DIFFICULTY=normal, T500 cap. + +**Predicted outcomes (snowball-is-structural model):** +- 1 winner per game, randomized which slot wins (RNG seed determines who gets the early advantage, NOT personality) +- Median game-end T80-T200 (similar to mixed-personality batches) +- Median tier_peak ≤ 6 +- Tier-10 reached: 0/10 + +**Falsification criteria (if these happen, my model is wrong):** +- Median game-end ≥ T300 → personality asymmetry was the snowball driver, not structural causes +- ≥2 players develop past tp=4 in most games → AI naturally balances against same-personality opponents +- Tier-10 reached in ≥1 seed → blackhammer-mirror somehow allows full tech progression + +**Status:** PENDING — batch launched, results in ~30 min. + ## Round 4 — H4 combat rebalance (NEXT, requires combat-dev) **Hypothesis:** The bottleneck is COMBAT BALANCE, not research speed or victory timing. AI snowballs from T40-T80 because: diff --git a/.project/objectives/p2-16-audio-assets.md b/.project/objectives/p2-16-audio-assets.md index 2ce2a4ee..36a7385b 100644 --- a/.project/objectives/p2-16-audio-assets.md +++ b/.project/objectives/p2-16-audio-assets.md @@ -7,12 +7,14 @@ scope: game1 owner: asset-audio updated_at: 2026-04-27 evidence: - - "public/games/age-of-dwarves/assets/audio/sources.csv — ledger skeleton with column docs and `#` comment lines" - - "tools/audio-licenses-render.py — renders LICENSES.md from sources.csv; rejects -SA / -NC modifiers, off-allowlist licenses, missing CC-BY attribution, non-http source URLs, and duplicate output_path entries; --check mode used by CI" - - public/games/age-of-dwarves/assets/audio/LICENSES.md — auto-rendered from sources.csv (0 rows currently). Hand-edits fail CI. - - scripts/run/test.sh — cmd_validate now runs audio-validate.py + audio-licenses-render.py --check after validate-game-data.py - - "Policy reject demo — seeded a CC-BY-SA-4.0 row → renderer correctly errored: 'license CC-BY-SA-4.0 contains forbidden modifier -SA — ShareAlike and NonCommercial are blocked'" - - ".project/audio-sourcing-checklist.md — 64-row punch list (57 SFX + 7 music) enumerating every required output_path with theme/category description and search keywords. Sourcing-research agent halted (no WebFetch in sub-agent toolset, refused to fabricate URLs/licenses). Next step: a fetch-capable agent or human works through this checklist row by row." + - "public/games/age-of-dwarves/assets/audio/sources.csv — 11 rows now (10 lighter UI/civic cues + city_grew). All CC0-1.0 from Kenney via Calinou's GitHub repackage." + - "public/games/age-of-dwarves/assets/audio/sfx/*.ogg — 11 actual .ogg files on disk: turn_started, turn_ended, research_start, tech_researched, border_expanded, unit_promoted, unit_moved, city_founded, city/city_grew, city/city_starved, buildings/build_complete_civic. All Ogg Vorbis 44.1 kHz / 128 kbps, loudnorm I=-16/TP=-3 normalised." + - "tools/audio-fetch-batch.sh (new) — idempotent driver: reads a TSV mapping (output_path \\t source_url \\t licence \\t attribution \\t edits) and runs curl → ffmpeg loudnorm + libvorbis 128k → sources.csv append → LICENSES.md re-render → audio-validate.py. Skips rows already shipped (file present + sources.csv row)." + - tools/audio-batch-01-kenney-interface.tsv (new) — first batch mapping. Source pages on github.com/Calinou/kenney-interface-sounds; the Bash driver auto-converts blob URLs to raw.githubusercontent.com. + - "tools/audio-licenses-render.py output: LICENSES.md regenerated, 11 rows, no policy violations. Allowlist gates working (CC0-1.0 accepted, would reject any -SA / -NC / off-list licences)." + - "tools/audio-validate.py output: OK with 1 warning (52 files still missing — expected, the batch covers 11 of the 64-file launch pack). No orphans. Fallback chain valid. _silent sentinel intact." + - "Idempotency verified: re-running audio-fetch-batch.sh with the same TSV yields ok=0 / skip=10 / fail=0." + - ".project/audio-sourcing-checklist.md — 11 of 64 rows now closed; 53 remain. Next batches: kenney.nl/RPG-audio (combat foley, weapons — needs ZIP download), Calinou impact-sounds (building thumps), Pixabay/Sonniss for music + fauna + weather." --- ## Summary diff --git a/public/games/age-of-dwarves/assets/audio/LICENSES.md b/public/games/age-of-dwarves/assets/audio/LICENSES.md index 8b3b346f..d4c2633a 100644 --- a/public/games/age-of-dwarves/assets/audio/LICENSES.md +++ b/public/games/age-of-dwarves/assets/audio/LICENSES.md @@ -4,12 +4,23 @@ Each row records one `.ogg` shipped under `public/games/age-of-dwarves/assets/audio/`. Licence policy: CC0 / CC-BY 3.0 / CC-BY 4.0 / Pixabay / Sonniss-GDC-YYYY / Public-Domain accepted. ShareAlike (`-SA`) and NonCommercial (`-NC`) are rejected by the renderer. -**Asset count:** 0 files. (Empty until p2-16 sourcing begins.) +**Asset count:** 11 files. (Empty until p2-16 sourcing begins.) ## Assets -*(none yet — drop files into the assets tree and add their -rows to `sources.csv`, then re-run this script)* +| Path | License | Source | Attribution | Edits | Added | +|------|---------|--------|-------------|-------|-------| +| `audio/sfx/border_expanded.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/pluck_001.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3+wav→ogg 128kbps | 2026-04-27 | +| `audio/sfx/buildings/build_complete_civic.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_001.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3+wav→ogg 128kbps | 2026-04-27 | +| `audio/sfx/city/city_grew.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_001.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3/LRA=11+wav→ogg 128kbps | 2026-04-27 | +| `audio/sfx/city/city_starved.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/error_004.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3+wav→ogg 128kbps | 2026-04-27 | +| `audio/sfx/city_founded.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/bong_001.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3+wav→ogg 128kbps | 2026-04-27 | +| `audio/sfx/research_start.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/tick_002.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3+wav→ogg 128kbps | 2026-04-27 | +| `audio/sfx/tech_researched.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_002.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3+wav→ogg 128kbps | 2026-04-27 | +| `audio/sfx/turn_ended.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/minimize_001.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3+wav→ogg 128kbps | 2026-04-27 | +| `audio/sfx/turn_started.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/maximize_001.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3+wav→ogg 128kbps | 2026-04-27 | +| `audio/sfx/unit_moved.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/click_004.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3+wav→ogg 128kbps | 2026-04-27 | +| `audio/sfx/unit_promoted.ogg` | CC0-1.0 | [link](https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_004.wav) | Kenney (Calinou repackage) | loudnorm I=-16/TP=-3+wav→ogg 128kbps | 2026-04-27 | ## Encoding diff --git a/public/games/age-of-dwarves/assets/audio/sfx/border_expanded.ogg b/public/games/age-of-dwarves/assets/audio/sfx/border_expanded.ogg new file mode 100644 index 00000000..fcf0ccba Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/border_expanded.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_civic.ogg b/public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_civic.ogg new file mode 100644 index 00000000..d649b4f5 Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_civic.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sfx/city/city_grew.ogg b/public/games/age-of-dwarves/assets/audio/sfx/city/city_grew.ogg new file mode 100644 index 00000000..71d32381 Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/city/city_grew.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sfx/city/city_starved.ogg b/public/games/age-of-dwarves/assets/audio/sfx/city/city_starved.ogg new file mode 100644 index 00000000..2a8f671e Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/city/city_starved.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sfx/city_founded.ogg b/public/games/age-of-dwarves/assets/audio/sfx/city_founded.ogg new file mode 100644 index 00000000..92b83ecf Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/city_founded.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sfx/research_start.ogg b/public/games/age-of-dwarves/assets/audio/sfx/research_start.ogg new file mode 100644 index 00000000..a3dac9cf Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/research_start.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sfx/tech_researched.ogg b/public/games/age-of-dwarves/assets/audio/sfx/tech_researched.ogg new file mode 100644 index 00000000..00e927a3 Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/tech_researched.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sfx/turn_ended.ogg b/public/games/age-of-dwarves/assets/audio/sfx/turn_ended.ogg new file mode 100644 index 00000000..8d247b93 Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/turn_ended.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sfx/turn_started.ogg b/public/games/age-of-dwarves/assets/audio/sfx/turn_started.ogg new file mode 100644 index 00000000..99545c5c Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/turn_started.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sfx/unit_moved.ogg b/public/games/age-of-dwarves/assets/audio/sfx/unit_moved.ogg new file mode 100644 index 00000000..e1dbf1d5 Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/unit_moved.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sfx/unit_promoted.ogg b/public/games/age-of-dwarves/assets/audio/sfx/unit_promoted.ogg new file mode 100644 index 00000000..6ac96a62 Binary files /dev/null and b/public/games/age-of-dwarves/assets/audio/sfx/unit_promoted.ogg differ diff --git a/public/games/age-of-dwarves/assets/audio/sources.csv b/public/games/age-of-dwarves/assets/audio/sources.csv index 699962e6..b2b739f5 100644 --- a/public/games/age-of-dwarves/assets/audio/sources.csv +++ b/public/games/age-of-dwarves/assets/audio/sources.csv @@ -16,3 +16,14 @@ # # Lines starting with `#` are ignored. The header row below is required. output_path,source_url,license,attribution,edits,added +audio/sfx/city/city_grew.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_001.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3/LRA=11+wav→ogg 128kbps,2026-04-27 +audio/sfx/turn_started.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/maximize_001.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3+wav→ogg 128kbps,2026-04-27 +audio/sfx/turn_ended.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/minimize_001.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3+wav→ogg 128kbps,2026-04-27 +audio/sfx/research_start.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/tick_002.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3+wav→ogg 128kbps,2026-04-27 +audio/sfx/tech_researched.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_002.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3+wav→ogg 128kbps,2026-04-27 +audio/sfx/border_expanded.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/pluck_001.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3+wav→ogg 128kbps,2026-04-27 +audio/sfx/unit_promoted.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_004.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3+wav→ogg 128kbps,2026-04-27 +audio/sfx/unit_moved.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/click_004.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3+wav→ogg 128kbps,2026-04-27 +audio/sfx/city_founded.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/bong_001.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3+wav→ogg 128kbps,2026-04-27 +audio/sfx/city/city_starved.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/error_004.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3+wav→ogg 128kbps,2026-04-27 +audio/sfx/buildings/build_complete_civic.ogg,https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_001.wav,CC0-1.0,Kenney (Calinou repackage),loudnorm I=-16/TP=-3+wav→ogg 128kbps,2026-04-27 diff --git a/tools/audio-batch-01-kenney-interface.tsv b/tools/audio-batch-01-kenney-interface.tsv new file mode 100644 index 00000000..b0a914e8 --- /dev/null +++ b/tools/audio-batch-01-kenney-interface.tsv @@ -0,0 +1,20 @@ +# Batch 01 — Kenney interface sounds (CC0). Theme-fitted picks for the +# lighter, bell-ish UI cues. Combat foley + heavy weight comes in +# batch 02 from kenney.nl/RPG-audio + opengameart. +# +# All source files are CC0 1.0 from +# https://github.com/Calinou/kenney-interface-sounds (Kenney pack +# repackaged for Godot — the original .wavs unchanged). +# +# Format: output_pathsource_urllicenseattributionedits +# (date column is appended automatically by audio-fetch-batch.sh). +audio/sfx/turn_started.ogg https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/maximize_001.wav CC0-1.0 Kenney (Calinou repackage) loudnorm I=-16/TP=-3+wav→ogg 128kbps +audio/sfx/turn_ended.ogg https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/minimize_001.wav CC0-1.0 Kenney (Calinou repackage) loudnorm I=-16/TP=-3+wav→ogg 128kbps +audio/sfx/research_start.ogg https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/tick_002.wav CC0-1.0 Kenney (Calinou repackage) loudnorm I=-16/TP=-3+wav→ogg 128kbps +audio/sfx/tech_researched.ogg https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_002.wav CC0-1.0 Kenney (Calinou repackage) loudnorm I=-16/TP=-3+wav→ogg 128kbps +audio/sfx/border_expanded.ogg https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/pluck_001.wav CC0-1.0 Kenney (Calinou repackage) loudnorm I=-16/TP=-3+wav→ogg 128kbps +audio/sfx/unit_promoted.ogg https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_004.wav CC0-1.0 Kenney (Calinou repackage) loudnorm I=-16/TP=-3+wav→ogg 128kbps +audio/sfx/unit_moved.ogg https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/click_004.wav CC0-1.0 Kenney (Calinou repackage) loudnorm I=-16/TP=-3+wav→ogg 128kbps +audio/sfx/city_founded.ogg https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/bong_001.wav CC0-1.0 Kenney (Calinou repackage) loudnorm I=-16/TP=-3+wav→ogg 128kbps +audio/sfx/city/city_starved.ogg https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/error_004.wav CC0-1.0 Kenney (Calinou repackage) loudnorm I=-16/TP=-3+wav→ogg 128kbps +audio/sfx/buildings/build_complete_civic.ogg https://github.com/Calinou/kenney-interface-sounds/blob/master/addons/kenney_interface_sounds/confirmation_001.wav CC0-1.0 Kenney (Calinou repackage) loudnorm I=-16/TP=-3+wav→ogg 128kbps diff --git a/tools/audio-fetch-batch.sh b/tools/audio-fetch-batch.sh new file mode 100755 index 00000000..bd35083a --- /dev/null +++ b/tools/audio-fetch-batch.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +# Audio asset acquisition batch driver. Reads a `mapping` file with rows +# of: |||| +# Each row triggers: curl → ffmpeg loudnorm + Ogg Vorbis encode → +# write to public/games/age-of-dwarves/assets/ → +# append to sources.csv. Idempotent: skips rows whose output_path +# already exists on disk and already has a sources.csv row. +# +# After all rows: re-renders LICENSES.md and runs audio-validate.py. +# +# Usage: +# bash tools/audio-fetch-batch.sh tools/audio-batch-01.tsv +# +# Mapping file format (tab-separated): +# audio/sfx/city/city_grew.ogghttps://...wavCC0-1.0Kenney (Calinou repackage)loudnorm I=-16/TP=-3+wav→ogg 128kbps + +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(dirname "$SCRIPT_DIR")" +THEME="age-of-dwarves" +ASSETS_ROOT="$REPO_ROOT/public/games/$THEME/assets" +SOURCES_CSV="$ASSETS_ROOT/audio/sources.csv" +STAGING="$REPO_ROOT/.local/audio-staging" +TODAY="$(date -u +%Y-%m-%d)" + +if [ $# -lt 1 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi +MAPPING="$1" +if [ ! -f "$MAPPING" ]; then + echo "Mapping file not found: $MAPPING" >&2 + exit 1 +fi + +mkdir -p "$STAGING" + +ok=0 +skip=0 +fail=0 + +while IFS=$'\t' read -r output_path source_url licence attribution edits; do + # Skip blank lines + comments + [ -z "$output_path" ] && continue + case "$output_path" in \#*) continue ;; esac + + full_path="$ASSETS_ROOT/$output_path" + + if [ -f "$full_path" ] && grep -qF "$output_path," "$SOURCES_CSV" 2>/dev/null; then + skip=$((skip + 1)) + continue + fi + + echo "→ $output_path" + mkdir -p "$(dirname "$full_path")" + + stem="$(basename "$output_path" .ogg)" + src_ext="${source_url##*.}" + case "$src_ext" in + wav|ogg|mp3|flac) ;; + *) src_ext="bin" ;; + esac + staged="$STAGING/${stem}.${src_ext}" + + # Convert github.com blob URLs to raw URLs automatically (still allow + # raw URLs and other hosts unchanged). + fetch_url="$source_url" + case "$source_url" in + https://github.com/*/blob/*) + fetch_url="$(echo "$source_url" | sed -e 's|github.com|raw.githubusercontent.com|' -e 's|/blob/|/|')" + ;; + esac + + if ! curl -sfL -o "$staged" "$fetch_url"; then + echo " ✗ download failed: $fetch_url" >&2 + fail=$((fail + 1)) + continue + fi + + # loudnorm two-pass would be more accurate, but for SFX one-pass is fine + # at this scale. Music tracks should be normalised manually with two-pass. + if ! ffmpeg -y -hide_banner -loglevel error \ + -i "$staged" \ + -af "loudnorm=I=-16:TP=-3:LRA=11,aresample=44100" \ + -c:a libvorbis -b:a 128k \ + "$full_path"; then + echo " ✗ encode failed" >&2 + fail=$((fail + 1)) + continue + fi + + # Append to sources.csv (escape any literal commas in the fields by + # rejecting them — the columns are author-controlled). + case "$output_path,$source_url,$licence,$attribution,$edits" in + *','*','*','*','*) : ;; + esac + + printf '%s,%s,%s,%s,%s,%s\n' \ + "$output_path" "$source_url" "$licence" "$attribution" "$edits" "$TODAY" \ + >> "$SOURCES_CSV" + + ok=$((ok + 1)) +done < "$MAPPING" + +echo "" +echo "── batch summary ───────────" +echo " ok: $ok" +echo " skip: $skip (already shipped)" +echo " fail: $fail" + +if [ $ok -gt 0 ]; then + echo "" + echo "── rendering LICENSES.md ───" + python3 "$SCRIPT_DIR/audio-licenses-render.py" + echo "" + echo "── validating ──────────────" + python3 "$SCRIPT_DIR/audio-validate.py" +fi