# tide-table · *Table des Vagues*

The catalog — one source of truth for every ParVagues asset and where it lives.

## Sources (reconciled, not duplicated)

1. **`Prod/` archive** (freebox canonical) — masters, mixdowns, Audacity projects.
2. **Site setlists** — `~/Work/Web/www/content/lives/**/tracks.json` already
   names tracks per gig, with BPM, style, sample packs, and Tidal ingredients.
3. **External** — SoundCloud, Bandcamp, YouTube, archive.org, Spotify/DSPs, IG.
4. **Screen-rec / phone recordings** — provenance-tagged candidates.
5. **`backlog.md`** (repo root) — hand-kept **monthly creation log**: the chronological
   spine. Dates tracks/études, reveals creation cadence, carries collab attributions
   (`[Louis]`, `[Raph]`…) and status sections (WIP / ÉBAUCHES / Somewhat complete / Prod)
   + gig tracklists. Use it to date the undated SC/.tidal items and to seed the calendar.

## Source-fetch methods (learned the hard way)

- **Bandcamp** — WebFetch works: tracklist + **durations** + release date + license + tags.
- **Site** (Next.js SSR) — WebFetch works: gig, setlist, BPM/style, collabs. Note a gig may
  have BOTH a **live recap** (`/parvagues/live/{slug}`) and an **owned-event landing**
  (`/{slug}`) — capture both; the landing carries lineup/poster (→ flotille + media).
- **SoundCloud** — WebFetch BLOCKED (JS app, "browser not compatible"). Use **oEmbed**
  (`https://soundcloud.com/oembed?format=json&url=…`) or the API instead.
- **Canonicalization** — reconcile FR⇄EN + stage-vs-release titles via `aka`
  (e.g. *Blue Gold* → **L'Or Bleu**, *Sept1* → **Premier Septembre**). Track
  `performed_not_released` so the gap is visible.

## Entry schema (per canonical track)

```yaml
- id: blue-gold              # stable slug
  title: "Blue Gold"
  aka: ["L'Or Bleu"]         # variants / French⇄EN
  type: track                # track | live-set | demo | sketch | remix | collab
  key: null
  bpm: null
  genre: [breaks, ambient]
  status: performed          # written|demo|performed|recorded|mastered|released
  tidal: []                  # étude/ébauche source .tidal path(s) — the written genesis
  collab: []                 # flotille refs (see escales/flotille.yaml)
  sources:                   # everywhere it exists
    - { platform: prod,       path: "Prod/...", format: flac, quality: master }
    - { platform: soundcloud, url: "https://soundcloud.com/parvagues/..." }
    - { platform: bandcamp,   url: "..." }
    - { platform: spotify,    url: "..." }      # presence = DSP-live ✓
    - { platform: site,       gig: "cosmicfest", role: setlist }
  master: { path: null, lufs: null }
  artwork: null
  first_performed: null
  release_date: null
  notes: ""
```

## Gigs vs tracks — DO NOT conflate

A **gig / live-set** (CosmicFest, Opal, 38C3, CCC LIVE…) is one entry of
`type: live-set`. Its `tracks.json` *enumerates* the **tracks** (`type: track`)
performed in it. A gig name is **never** a track. The Prod `cosmicfest/` dir is the
*recording of that gig* → a source on the live-set entry, which `setlist:`-links to the
individual track entries it contains.

```yaml
- id: cosmicfest-2025
  title: "Live @ CosmicFest v0"
  type: live-set            # NOT a track
  sources: [{ platform: site, gig: cosmicfest }, { platform: prod, path: "Prod/cosmicfest/..." }]
  setlist: [quand-on-decolle, fabuleux, technorage, blue-gold, ...]  # → track entries
```

## Lifecycle: from étude to release

A track has a life **before** it's a track. The earliest state is **written** — a
`.tidal` *ébauche/étude*. Most études never progress, and that's fine: they're material
(seeds, studies, fragments), catalogued and searchable — not failures.

```
written (étude/ébauche) → demo → performed → recorded → mastered → released
```

The funnel as it stands (2026-06):

| stage | count |
|---|---|
| **written** (.tidal études) | ~818 — 688 `live/`, 86 `study/`, 12 `copycat/` … |
| performed | dozens (site setlists) |
| recorded (SoundCloud) | 68 |
| released (DSP) | 25 |

The site `tracks.json` `file` field already points each performed track to its `.tidal`
source → the scanner wires `released ← performed ← written` automatically. The full étude
corpus gets a lightweight index (`etudes.index`), so it's a seed bank, not dead weight.

## Sampling & rights tiers (releasability)

ParVagues' method is demucs-sampling, so DSP-releasability varies per track. Two fields:

- `sample_tier`: `original` | `light` | `heavy` | `remix` | `voice` (+ `set` for live sets)
- `rights`: `clear` | `review` | `needs-license` | `no-go`

Mapping → DSP decision:

| sample_tier | rights | DSP decision |
|---|---|---|
| original | clear | ✅ release |
| light | review | ✅ low-risk (or clear sample) |
| heavy | needs-license | 🟡 clear or swap sample before DSP |
| remix / cover | no-go | 🔴 SoundCloud / live-only unless licensed |
| voice / satire | no-go | 🔴 publicity/defamation risk (celebrity/political voice) |

Triage lives in `triage.csv` (auto-seeded; PLN classifies the judgment calls), then
gets ingested into per-track `sample_tier`/`rights` and a derived `dsp_ok`.

## Gap analysis (the payoff)

Once unified, query for **state mismatches** — e.g. tracks on Bandcamp/SoundCloud
but *absent from Spotify* (the "friend asks for new music on Spotify" problem).
Drives the `manifeste` release ordering.

## Build

`scan.py` (TODO) walks `Prod/` + ingests site `tracks.json` → emits `catalog.yaml`.
Katana-first: validate the scanner against a few known tracks before trusting it.
