fluncle 0.167.0 → 0.168.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/fluncle.mjs +87 -2
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -561,7 +561,7 @@ function parseVersion(version) {
561
561
  var currentVersion;
562
562
  var init_version = __esm(() => {
563
563
  init_output();
564
- currentVersion = "0.167.0".trim() ? "0.167.0".trim() : "0.1.0";
564
+ currentVersion = "0.168.0".trim() ? "0.168.0".trim() : "0.1.0";
565
565
  });
566
566
 
567
567
  // src/update-notifier.ts
@@ -2229,7 +2229,7 @@ function parseVersion2(version) {
2229
2229
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2230
2230
  var init_version2 = __esm(() => {
2231
2231
  init_output();
2232
- currentVersion2 = "0.167.0".trim() ? "0.167.0".trim() : "0.1.0";
2232
+ currentVersion2 = "0.168.0".trim() ? "0.168.0".trim() : "0.1.0";
2233
2233
  });
2234
2234
 
2235
2235
  // ../../packages/registry/src/index.ts
@@ -3119,6 +3119,19 @@ var init_src = __esm(() => {
3119
3119
  title: "Recording MBIDs",
3120
3120
  weights: { status: "hidden" }
3121
3121
  },
3122
+ {
3123
+ command: "fluncle admin backfills label-lineage",
3124
+ exposedContent: [
3125
+ "resolve each label's founding date + place + parent imprint from MusicBrainz → the labels row"
3126
+ ],
3127
+ kind: "cron",
3128
+ name: "cron.label-lineage",
3129
+ operatorNotes: "every 60m, run by a rave-02 HOST systemd timer (docs/agents/hermes/label-lineage-timer/). The label entity's LINEAGE half (RFC label-lineage-remixer, U1): gives each label its founding facts + its place in the imprint hierarchy from MusicBrainz — `life-span.begin` → `founding_date`, `area.name` → `founded_location`, and the `backward` `label ownership` / `imprint` label-rels → `parent_label_id` (matched to an EXISTING label by MBID; NEVER minted — an unmatched parent is only counted). A dedicated sweep, not a rider on the label-image sweep, because that one is terminal per label and a logo-resolved label would never get its lineage: this carries its OWN `lineage_state` machine so it reaches every label once. METADATA ONLY — it certifies nothing, mints nothing, publishes nothing (agent tier, the `backfill_label_images` precedent). Worker-paced (the box holds no MusicBrainz budget): one bounded batch per tick, 1 req/s, circuit-broken on a throttle, reusing the shared MB client + exact-fold identity search. The `labels` row carries the durable reliability state (lineage_state/lineage_attempted_at/lineage_failures), so a resolved/none label is terminal. Emitted as the `/label/<slug>` Organization's `foundingDate` / `location` / `parentOrganization` / `subOrganization`. Zero LLM tokens. Source: docs/agents/hermes/scripts/label-lineage-sweep.*. See docs/label-entity.md.",
3130
+ probeConfig: { cadenceMs: 60 * MINUTE_MS, cronName: "fluncle-label-lineage", kind: "cron" },
3131
+ statusDescription: "resolves each label's founding and imprint",
3132
+ title: "Label lineage",
3133
+ weights: { status: "hidden" }
3134
+ },
3122
3135
  {
3123
3136
  command: "fluncle admin backfills cover-masters",
3124
3137
  exposedContent: [
@@ -6250,6 +6263,7 @@ __export(exports_admin_labels, {
6250
6263
  labelsBioQueueCommand: () => labelsBioQueueCommand,
6251
6264
  draftLabelBioCommand: () => draftLabelBioCommand,
6252
6265
  describeLabelCommand: () => describeLabelCommand,
6266
+ backfillLabelLineageCommand: () => backfillLabelLineageCommand,
6253
6267
  backfillLabelImagesCommand: () => backfillLabelImagesCommand
6254
6268
  });
6255
6269
  async function describeLabelCommand(slug, options) {
@@ -6269,6 +6283,13 @@ async function backfillLabelImagesCommand(limit, dryRun, cursor) {
6269
6283
  }
6270
6284
  return adminApiPost(`/api/admin/backfill/label-images?${params.toString()}`);
6271
6285
  }
6286
+ async function backfillLabelLineageCommand(limit, dryRun, cursor) {
6287
+ const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
6288
+ if (cursor) {
6289
+ params.set("cursor", cursor);
6290
+ }
6291
+ return adminApiPost(`/api/admin/backfill/label-lineage?${params.toString()}`);
6292
+ }
6272
6293
  var init_admin_labels = __esm(() => {
6273
6294
  init_api();
6274
6295
  init_admin_artists2();
@@ -9510,6 +9531,10 @@ function addAdminCommands(program2) {
9510
9531
  const { backfillLabelImagesCommand: backfillLabelImagesCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
9511
9532
  await runBackfillLabelImages(options, backfillLabelImagesCommand2);
9512
9533
  });
9534
+ backfill.command("label-lineage").description("Resolve label lineage (founding date, place, parent imprint) from MusicBrainz").option("--dry-run", "Report the eligible worklist without any vendor call or write", false).option("--limit <limit>", "Max labels to process", "50").option("--json", "Print JSON", false).action(async (options) => {
9535
+ const { backfillLabelLineageCommand: backfillLabelLineageCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
9536
+ await runBackfillLabelLineage(options, backfillLabelLineageCommand2);
9537
+ });
9513
9538
  backfill.command("recording-mbids").description("Fill MusicBrainz recording MBIDs (crawler PK strip + ISRC resolve) over tracks").option("--dry-run", "Report the eligible worklist without any vendor call or write", false).option("--limit <limit>", "Max ISRC lookups to process", "50").option("--json", "Print JSON", false).action(async (options) => {
9514
9539
  const { backfillRecordingMbidsCommand: backfillRecordingMbidsCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
9515
9540
  await runBackfillRecordingMbids(options, backfillRecordingMbidsCommand2);
@@ -10308,6 +10333,66 @@ async function runBackfillCoverMasters(options, backfillCoverMastersCommand2) {
10308
10333
  process.exitCode = 1;
10309
10334
  }
10310
10335
  }
10336
+ async function runBackfillLabelLineage(options, backfillLabelLineageCommand2) {
10337
+ const limit = parseListLimit(options.limit);
10338
+ const resolved = [];
10339
+ const none = [];
10340
+ const failed = [];
10341
+ let cursor;
10342
+ let dryRun = options.dryRun;
10343
+ let throttled = false;
10344
+ let unmatchedParents = 0;
10345
+ while (resolved.length + none.length + failed.length < limit) {
10346
+ const remaining = limit - (resolved.length + none.length + failed.length);
10347
+ const result = await backfillLabelLineageCommand2(remaining, options.dryRun, cursor);
10348
+ dryRun = result.dryRun;
10349
+ resolved.push(...result.resolved);
10350
+ none.push(...result.none);
10351
+ failed.push(...result.failed);
10352
+ unmatchedParents += result.unmatchedParents;
10353
+ if (!options.json) {
10354
+ const verb2 = result.dryRun ? "would walk" : "walked";
10355
+ console.log(` \u2026${verb2} ${result.resolvedCount}; ${result.noneCount} with no MusicBrainz identity; ${result.failedCount} failed; ${result.unmatchedParents} unmatched parent(s)`);
10356
+ }
10357
+ if (result.rateLimited) {
10358
+ throttled = true;
10359
+ break;
10360
+ }
10361
+ if (result.nextCursor === null) {
10362
+ break;
10363
+ }
10364
+ cursor = result.nextCursor;
10365
+ }
10366
+ if (options.json) {
10367
+ printJson({
10368
+ dryRun,
10369
+ failed,
10370
+ failedCount: failed.length,
10371
+ none,
10372
+ noneCount: none.length,
10373
+ ok: true,
10374
+ rateLimited: throttled,
10375
+ resolved,
10376
+ resolvedCount: resolved.length,
10377
+ unmatchedParents
10378
+ });
10379
+ if (failed.length > 0) {
10380
+ process.exitCode = 1;
10381
+ }
10382
+ return;
10383
+ }
10384
+ const verb = dryRun ? "Would walk" : "Walked";
10385
+ console.log(`${verb} ${resolved.length} label lineage(s); ${none.length} with no MusicBrainz identity; ${failed.length} failed; ${unmatchedParents} unmatched parent(s).`);
10386
+ for (const slug of resolved) {
10387
+ console.log(` ${slug}`);
10388
+ }
10389
+ for (const item of failed) {
10390
+ console.log(` ${item.slug}: ${item.error}`);
10391
+ }
10392
+ if (failed.length > 0) {
10393
+ process.exitCode = 1;
10394
+ }
10395
+ }
10311
10396
  async function runBackfillLabelImages(options, backfillLabelImagesCommand2) {
10312
10397
  const limit = parseListLimit(options.limit);
10313
10398
  const resolved = [];
package/package.json CHANGED
@@ -31,5 +31,5 @@
31
31
  "url": "git+https://github.com/mauricekleine/fluncle.git"
32
32
  },
33
33
  "type": "module",
34
- "version": "0.167.0"
34
+ "version": "0.168.0"
35
35
  }