diff --git a/.agents/skills/gitp-asset-gathering/SKILL.md b/.agents/skills/gitp-asset-gathering/SKILL.md index c5c1c6bd6..8ef405c0a 100644 --- a/.agents/skills/gitp-asset-gathering/SKILL.md +++ b/.agents/skills/gitp-asset-gathering/SKILL.md @@ -2,71 +2,56 @@ name: gitp-asset-gathering description: >- Prepare a GitP episode from an exported WAV and a Making/Music/Log entry: - create the garden episode, capture MicroFreak presets, enrich notes from MIDI - and manuals, upload MP3 and presets to B2, and prepare a Gitpa publication PR. - The human listens to one episode recording and merges the PR to publish. + create the garden session episode, capture MicroFreak presets, enrich notes + from MIDI and manuals, upload media, and prepare a Gitpa publication PR. The + human listens to one recording and merges the PR to publish. --- # Prepare a GitP episode Read `pages/GitP___How To___Draft an Episode from Session Assets.md` for the -human workflow and `pages/GitP___How To___Prepare Podcast Metadata.md` for the -metadata commands. Start from the supplied Making/Music/Log entry and follow its -project and WAV pointers. Search existing notes before asking for a missing path. +human workflow and `pages/GitP___How To___Prepare Podcast Metadata.md` for +publication preparation. Start with the supplied music-making log and follow its +project and exported WAV links. Search existing notes before asking for a path. -## Create the episode and prepare audio +## Prepare the session and recording -Read the Podcast/Episode entity definition and search for an existing episode. -Create or update the GitP episode under the show's namespace, linking the source -log entry and its pointers. Preserve human notes and publication identity. Do not -invent an air date for an unpublished episode. Record graph edits in today's journal. -Keep any technical file inventory on the session assets page and link it from the -episode; the episode page holds the description and listening/download links. +Read the Podcast/Episode and Asset entity definitions. Create or update the +session episode under `GitP/A/Session/YY/MM/DD-Day`, linking its source log. +The episode has one H1 title, a first-child description, and an embedded MP3 +asset page. Keep the recording URL on that asset page. Record graph edits in +today's journal. -Use the exported WAV as the one episode recording. Convert it to MP3 without -changing the source WAV. Do not select among stems, assemble commentary, or ask -the human for time ranges, trims, or editorial assessments. Upload the MP3 to B2 -under a stable episode path using the garden's configured credentials; never print -secrets. Verify uploaded bytes and media metadata against the local MP3. +Convert the exported WAV to MP3 without changing the source WAV. Upload it to +B2 using the garden's configured credentials and page-derived asset filename. +Verify uploaded bytes and media metadata against the local MP3. -## Capture saved presets +## Capture presets and describe the session -Read the MicroFreak preset entity rules and existing device workflow documentation -before device writes. Identify the saved slots used in this episode from the log, -MIDI, and any existing captures. A current slot's contents alone cannot establish -what was used in an older session. If the association cannot be established, ask -only for the missing slot identification and continue independent episode work. +Read the MicroFreak preset entity rules and device workflow. Identify saved slots +from the source log, captured MIDI, and existing captures. Ask for missing slot +identification when the available evidence cannot establish which presets were +used; continue the recording and page preparation. -Back up each identified preset before renaming. Use the documented episode naming -convention and device name limits. Protect any unsaved active sound, verify that -the slot still holds the captured sound, rename it, download it, and verify that -only intended metadata changed. Upload the exports to B2 and add labeled download -links to the episode page. Never rename unrelated presets or claim that an -unimplemented device command has run. +Back up each identified preset before renaming. Protect any unsaved active sound, +verify the slot still matches the backup, apply the episode naming convention, +download it, and verify that only intended metadata changed. Upload the exports +and add labeled download links to the session page. -## Describe the session +Correlate captured MIDI CC numbers, values, and times with the MicroFreak manual. +Use preset metadata for oscillator type, category, and supported settings, and +read the Launchpad manual where relevant. Link useful explanations to their +sources. Distinguish recorded events from saved preset settings. -Read the captured MIDI data, source log, and relevant MicroFreak and Launchpad -manual sections. Correlate MIDI CC numbers, values, and times with the documented -controls. Read preset metadata for oscillator type, category, and other supported -settings. Link each useful explanation to its source. Distinguish session events -from saved preset settings; omit unsupported historical claims. Audio filenames, -track names, and a preset's current name do not establish recording content. -Commentary transcription is not required for this workflow. +## Prepare publication -## Prepare the Gitpa publication PR +Follow the metadata how-to to sync the session and embedded assets into Gitpa as +exact-name Logseq proxies. Keep episode text in the source garden and publication +identity on the Gitpa proxy. Prepare a worktree PR with the public page and feed +changes, run the relevant sync and RSS checks, commit, and push. -Use `mise run gitpa:episode:draft -- ` for the available metadata checks -and JSON export, then the Gitpa importer. Read the task documentation first. If -an edited session asset page already exists, use fresh output paths and transfer -checked facts without replacing human edits. Review proposed copy yourself. +The PR includes the episode title, a direct MP3 link, and the reason to listen: +decide whether to publish this recording. Leave the publication PR for the human +to merge after one listen and a go decision. CI publishes after the merge. -Prepare the Gitpa episode page, media links, publication metadata, and feed changes -in a worktree. Run the relevant importer and feed checks, commit, push, and open a -PR. Include the episode title, a direct MP3 link, and the reason to listen: decide -whether to publish this recording. The human's remaining work is one listen and a -go/no-go decision. Leave the publication PR for the human to merge; CI publishes -after that merge. Do not substitute an AI listening assessment for this decision. - -Some parts of this workflow still need automation, particularly device capture, -rename, and MIDI enrichment. Report a concrete missing capability if it blocks -completion; do not turn it into an extra human review checklist. +Automatic preset capture, renaming, and MIDI enrichment still need implementation. +Report the concrete missing capability if it prevents preparing the episode. diff --git a/.claude/skills/gitp-asset-gathering/SKILL.md b/.claude/skills/gitp-asset-gathering/SKILL.md index c5c1c6bd6..8ef405c0a 100644 --- a/.claude/skills/gitp-asset-gathering/SKILL.md +++ b/.claude/skills/gitp-asset-gathering/SKILL.md @@ -2,71 +2,56 @@ name: gitp-asset-gathering description: >- Prepare a GitP episode from an exported WAV and a Making/Music/Log entry: - create the garden episode, capture MicroFreak presets, enrich notes from MIDI - and manuals, upload MP3 and presets to B2, and prepare a Gitpa publication PR. - The human listens to one episode recording and merges the PR to publish. + create the garden session episode, capture MicroFreak presets, enrich notes + from MIDI and manuals, upload media, and prepare a Gitpa publication PR. The + human listens to one recording and merges the PR to publish. --- # Prepare a GitP episode Read `pages/GitP___How To___Draft an Episode from Session Assets.md` for the -human workflow and `pages/GitP___How To___Prepare Podcast Metadata.md` for the -metadata commands. Start from the supplied Making/Music/Log entry and follow its -project and WAV pointers. Search existing notes before asking for a missing path. +human workflow and `pages/GitP___How To___Prepare Podcast Metadata.md` for +publication preparation. Start with the supplied music-making log and follow its +project and exported WAV links. Search existing notes before asking for a path. -## Create the episode and prepare audio +## Prepare the session and recording -Read the Podcast/Episode entity definition and search for an existing episode. -Create or update the GitP episode under the show's namespace, linking the source -log entry and its pointers. Preserve human notes and publication identity. Do not -invent an air date for an unpublished episode. Record graph edits in today's journal. -Keep any technical file inventory on the session assets page and link it from the -episode; the episode page holds the description and listening/download links. +Read the Podcast/Episode and Asset entity definitions. Create or update the +session episode under `GitP/A/Session/YY/MM/DD-Day`, linking its source log. +The episode has one H1 title, a first-child description, and an embedded MP3 +asset page. Keep the recording URL on that asset page. Record graph edits in +today's journal. -Use the exported WAV as the one episode recording. Convert it to MP3 without -changing the source WAV. Do not select among stems, assemble commentary, or ask -the human for time ranges, trims, or editorial assessments. Upload the MP3 to B2 -under a stable episode path using the garden's configured credentials; never print -secrets. Verify uploaded bytes and media metadata against the local MP3. +Convert the exported WAV to MP3 without changing the source WAV. Upload it to +B2 using the garden's configured credentials and page-derived asset filename. +Verify uploaded bytes and media metadata against the local MP3. -## Capture saved presets +## Capture presets and describe the session -Read the MicroFreak preset entity rules and existing device workflow documentation -before device writes. Identify the saved slots used in this episode from the log, -MIDI, and any existing captures. A current slot's contents alone cannot establish -what was used in an older session. If the association cannot be established, ask -only for the missing slot identification and continue independent episode work. +Read the MicroFreak preset entity rules and device workflow. Identify saved slots +from the source log, captured MIDI, and existing captures. Ask for missing slot +identification when the available evidence cannot establish which presets were +used; continue the recording and page preparation. -Back up each identified preset before renaming. Use the documented episode naming -convention and device name limits. Protect any unsaved active sound, verify that -the slot still holds the captured sound, rename it, download it, and verify that -only intended metadata changed. Upload the exports to B2 and add labeled download -links to the episode page. Never rename unrelated presets or claim that an -unimplemented device command has run. +Back up each identified preset before renaming. Protect any unsaved active sound, +verify the slot still matches the backup, apply the episode naming convention, +download it, and verify that only intended metadata changed. Upload the exports +and add labeled download links to the session page. -## Describe the session +Correlate captured MIDI CC numbers, values, and times with the MicroFreak manual. +Use preset metadata for oscillator type, category, and supported settings, and +read the Launchpad manual where relevant. Link useful explanations to their +sources. Distinguish recorded events from saved preset settings. -Read the captured MIDI data, source log, and relevant MicroFreak and Launchpad -manual sections. Correlate MIDI CC numbers, values, and times with the documented -controls. Read preset metadata for oscillator type, category, and other supported -settings. Link each useful explanation to its source. Distinguish session events -from saved preset settings; omit unsupported historical claims. Audio filenames, -track names, and a preset's current name do not establish recording content. -Commentary transcription is not required for this workflow. +## Prepare publication -## Prepare the Gitpa publication PR +Follow the metadata how-to to sync the session and embedded assets into Gitpa as +exact-name Logseq proxies. Keep episode text in the source garden and publication +identity on the Gitpa proxy. Prepare a worktree PR with the public page and feed +changes, run the relevant sync and RSS checks, commit, and push. -Use `mise run gitpa:episode:draft -- ` for the available metadata checks -and JSON export, then the Gitpa importer. Read the task documentation first. If -an edited session asset page already exists, use fresh output paths and transfer -checked facts without replacing human edits. Review proposed copy yourself. +The PR includes the episode title, a direct MP3 link, and the reason to listen: +decide whether to publish this recording. Leave the publication PR for the human +to merge after one listen and a go decision. CI publishes after the merge. -Prepare the Gitpa episode page, media links, publication metadata, and feed changes -in a worktree. Run the relevant importer and feed checks, commit, push, and open a -PR. Include the episode title, a direct MP3 link, and the reason to listen: decide -whether to publish this recording. The human's remaining work is one listen and a -go/no-go decision. Leave the publication PR for the human to merge; CI publishes -after that merge. Do not substitute an AI listening assessment for this decision. - -Some parts of this workflow still need automation, particularly device capture, -rename, and MIDI enrichment. Report a concrete missing capability if it blocks -completion; do not turn it into an extra human review checklist. +Automatic preset capture, renaming, and MIDI enrichment still need implementation. +Report the concrete missing capability if it prevents preparing the episode. diff --git a/.github/skills/gitp-asset-gathering/SKILL.md b/.github/skills/gitp-asset-gathering/SKILL.md index c5c1c6bd6..8ef405c0a 100644 --- a/.github/skills/gitp-asset-gathering/SKILL.md +++ b/.github/skills/gitp-asset-gathering/SKILL.md @@ -2,71 +2,56 @@ name: gitp-asset-gathering description: >- Prepare a GitP episode from an exported WAV and a Making/Music/Log entry: - create the garden episode, capture MicroFreak presets, enrich notes from MIDI - and manuals, upload MP3 and presets to B2, and prepare a Gitpa publication PR. - The human listens to one episode recording and merges the PR to publish. + create the garden session episode, capture MicroFreak presets, enrich notes + from MIDI and manuals, upload media, and prepare a Gitpa publication PR. The + human listens to one recording and merges the PR to publish. --- # Prepare a GitP episode Read `pages/GitP___How To___Draft an Episode from Session Assets.md` for the -human workflow and `pages/GitP___How To___Prepare Podcast Metadata.md` for the -metadata commands. Start from the supplied Making/Music/Log entry and follow its -project and WAV pointers. Search existing notes before asking for a missing path. +human workflow and `pages/GitP___How To___Prepare Podcast Metadata.md` for +publication preparation. Start with the supplied music-making log and follow its +project and exported WAV links. Search existing notes before asking for a path. -## Create the episode and prepare audio +## Prepare the session and recording -Read the Podcast/Episode entity definition and search for an existing episode. -Create or update the GitP episode under the show's namespace, linking the source -log entry and its pointers. Preserve human notes and publication identity. Do not -invent an air date for an unpublished episode. Record graph edits in today's journal. -Keep any technical file inventory on the session assets page and link it from the -episode; the episode page holds the description and listening/download links. +Read the Podcast/Episode and Asset entity definitions. Create or update the +session episode under `GitP/A/Session/YY/MM/DD-Day`, linking its source log. +The episode has one H1 title, a first-child description, and an embedded MP3 +asset page. Keep the recording URL on that asset page. Record graph edits in +today's journal. -Use the exported WAV as the one episode recording. Convert it to MP3 without -changing the source WAV. Do not select among stems, assemble commentary, or ask -the human for time ranges, trims, or editorial assessments. Upload the MP3 to B2 -under a stable episode path using the garden's configured credentials; never print -secrets. Verify uploaded bytes and media metadata against the local MP3. +Convert the exported WAV to MP3 without changing the source WAV. Upload it to +B2 using the garden's configured credentials and page-derived asset filename. +Verify uploaded bytes and media metadata against the local MP3. -## Capture saved presets +## Capture presets and describe the session -Read the MicroFreak preset entity rules and existing device workflow documentation -before device writes. Identify the saved slots used in this episode from the log, -MIDI, and any existing captures. A current slot's contents alone cannot establish -what was used in an older session. If the association cannot be established, ask -only for the missing slot identification and continue independent episode work. +Read the MicroFreak preset entity rules and device workflow. Identify saved slots +from the source log, captured MIDI, and existing captures. Ask for missing slot +identification when the available evidence cannot establish which presets were +used; continue the recording and page preparation. -Back up each identified preset before renaming. Use the documented episode naming -convention and device name limits. Protect any unsaved active sound, verify that -the slot still holds the captured sound, rename it, download it, and verify that -only intended metadata changed. Upload the exports to B2 and add labeled download -links to the episode page. Never rename unrelated presets or claim that an -unimplemented device command has run. +Back up each identified preset before renaming. Protect any unsaved active sound, +verify the slot still matches the backup, apply the episode naming convention, +download it, and verify that only intended metadata changed. Upload the exports +and add labeled download links to the session page. -## Describe the session +Correlate captured MIDI CC numbers, values, and times with the MicroFreak manual. +Use preset metadata for oscillator type, category, and supported settings, and +read the Launchpad manual where relevant. Link useful explanations to their +sources. Distinguish recorded events from saved preset settings. -Read the captured MIDI data, source log, and relevant MicroFreak and Launchpad -manual sections. Correlate MIDI CC numbers, values, and times with the documented -controls. Read preset metadata for oscillator type, category, and other supported -settings. Link each useful explanation to its source. Distinguish session events -from saved preset settings; omit unsupported historical claims. Audio filenames, -track names, and a preset's current name do not establish recording content. -Commentary transcription is not required for this workflow. +## Prepare publication -## Prepare the Gitpa publication PR +Follow the metadata how-to to sync the session and embedded assets into Gitpa as +exact-name Logseq proxies. Keep episode text in the source garden and publication +identity on the Gitpa proxy. Prepare a worktree PR with the public page and feed +changes, run the relevant sync and RSS checks, commit, and push. -Use `mise run gitpa:episode:draft -- ` for the available metadata checks -and JSON export, then the Gitpa importer. Read the task documentation first. If -an edited session asset page already exists, use fresh output paths and transfer -checked facts without replacing human edits. Review proposed copy yourself. +The PR includes the episode title, a direct MP3 link, and the reason to listen: +decide whether to publish this recording. Leave the publication PR for the human +to merge after one listen and a go decision. CI publishes after the merge. -Prepare the Gitpa episode page, media links, publication metadata, and feed changes -in a worktree. Run the relevant importer and feed checks, commit, push, and open a -PR. Include the episode title, a direct MP3 link, and the reason to listen: decide -whether to publish this recording. The human's remaining work is one listen and a -go/no-go decision. Leave the publication PR for the human to merge; CI publishes -after that merge. Do not substitute an AI listening assessment for this decision. - -Some parts of this workflow still need automation, particularly device capture, -rename, and MIDI enrichment. Report a concrete missing capability if it blocks -completion; do not turn it into an extra human review checklist. +Automatic preset capture, renaming, and MIDI enrichment still need implementation. +Report the concrete missing capability if it prevents preparing the episode. diff --git a/.gitignore b/.gitignore index d9675b470..1225f2b0f 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,5 @@ worktrees/ /assets/.remote/** !/assets/.remote/**/ !/assets/.remote/**/*.dvc + +.worktree/ diff --git a/.rulesync/skills/gitp-asset-gathering/SKILL.md b/.rulesync/skills/gitp-asset-gathering/SKILL.md index 116a02478..4d84ab150 100644 --- a/.rulesync/skills/gitp-asset-gathering/SKILL.md +++ b/.rulesync/skills/gitp-asset-gathering/SKILL.md @@ -2,73 +2,58 @@ name: gitp-asset-gathering description: >- Prepare a GitP episode from an exported WAV and a Making/Music/Log entry: - create the garden episode, capture MicroFreak presets, enrich notes from MIDI - and manuals, upload MP3 and presets to B2, and prepare a Gitpa publication PR. - The human listens to one episode recording and merges the PR to publish. + create the garden session episode, capture MicroFreak presets, enrich notes + from MIDI and manuals, upload media, and prepare a Gitpa publication PR. + The human listens to one recording and merges the PR to publish. targets: ["*"] --- # Prepare a GitP episode Read `pages/GitP___How To___Draft an Episode from Session Assets.md` for the -human workflow and `pages/GitP___How To___Prepare Podcast Metadata.md` for the -metadata commands. Start from the supplied Making/Music/Log entry and follow its -project and WAV pointers. Search existing notes before asking for a missing path. +human workflow and `pages/GitP___How To___Prepare Podcast Metadata.md` for +publication preparation. Start with the supplied music-making log and follow its +project and exported WAV links. Search existing notes before asking for a path. -## Create the episode and prepare audio +## Prepare the session and recording -Read the Podcast/Episode entity definition and search for an existing episode. -Create or update the GitP episode under the show's namespace, linking the source -log entry and its pointers. Preserve human notes and publication identity. Do not -invent an air date for an unpublished episode. Record graph edits in today's journal. -Keep any technical file inventory on the session assets page and link it from the -episode; the episode page holds the description and listening/download links. +Read the Podcast/Episode and Asset entity definitions. Create or update the +session episode under `GitP/A/Session/YY/MM/DD-Day`, linking its source log. +The episode has one H1 title, a first-child description, and an embedded MP3 +asset page. Keep the recording URL on that asset page. Record graph edits in +today's journal. -Use the exported WAV as the one episode recording. Convert it to MP3 without -changing the source WAV. Do not select among stems, assemble commentary, or ask -the human for time ranges, trims, or editorial assessments. Upload the MP3 to B2 -under a stable episode path using the garden's configured credentials; never print -secrets. Verify uploaded bytes and media metadata against the local MP3. +Convert the exported WAV to MP3 without changing the source WAV. Upload it to +B2 using the garden's configured credentials and page-derived asset filename. +Verify uploaded bytes and media metadata against the local MP3. -## Capture saved presets +## Capture presets and describe the session -Read the MicroFreak preset entity rules and existing device workflow documentation -before device writes. Identify the saved slots used in this episode from the log, -MIDI, and any existing captures. A current slot's contents alone cannot establish -what was used in an older session. If the association cannot be established, ask -only for the missing slot identification and continue independent episode work. +Read the MicroFreak preset entity rules and device workflow. Identify saved slots +from the source log, captured MIDI, and existing captures. Ask for missing slot +identification when the available evidence cannot establish which presets were +used; continue the recording and page preparation. -Back up each identified preset before renaming. Use the documented episode naming -convention and device name limits. Protect any unsaved active sound, verify that -the slot still holds the captured sound, rename it, download it, and verify that -only intended metadata changed. Upload the exports to B2 and add labeled download -links to the episode page. Never rename unrelated presets or claim that an -unimplemented device command has run. +Back up each identified preset before renaming. Protect any unsaved active sound, +verify the slot still matches the backup, apply the episode naming convention, +download it, and verify that only intended metadata changed. Upload the exports +and add labeled download links to the session page. -## Describe the session +Correlate captured MIDI CC numbers, values, and times with the MicroFreak manual. +Use preset metadata for oscillator type, category, and supported settings, and +read the Launchpad manual where relevant. Link useful explanations to their +sources. Distinguish recorded events from saved preset settings. -Read the captured MIDI data, source log, and relevant MicroFreak and Launchpad -manual sections. Correlate MIDI CC numbers, values, and times with the documented -controls. Read preset metadata for oscillator type, category, and other supported -settings. Link each useful explanation to its source. Distinguish session events -from saved preset settings; omit unsupported historical claims. Audio filenames, -track names, and a preset's current name do not establish recording content. -Commentary transcription is not required for this workflow. +## Prepare publication -## Prepare the Gitpa publication PR +Follow the metadata how-to to sync the session and embedded assets into Gitpa as +exact-name Logseq proxies. Keep episode text in the source garden and publication +identity on the Gitpa proxy. Prepare a worktree PR with the public page and feed +changes, run the relevant sync and RSS checks, commit, and push. -Use `mise run gitpa:episode:draft -- ` for the available metadata checks -and JSON export, then the Gitpa importer. Read the task documentation first. If -an edited session asset page already exists, use fresh output paths and transfer -checked facts without replacing human edits. Review proposed copy yourself. +The PR includes the episode title, a direct MP3 link, and the reason to listen: +decide whether to publish this recording. Leave the publication PR for the human +to merge after one listen and a go decision. CI publishes after the merge. -Prepare the Gitpa episode page, media links, publication metadata, and feed changes -in a worktree. Run the relevant importer and feed checks, commit, push, and open a -PR. Include the episode title, a direct MP3 link, and the reason to listen: decide -whether to publish this recording. The human's remaining work is one listen and a -go/no-go decision. Leave the publication PR for the human to merge; CI publishes -after that merge. Do not substitute an AI listening assessment for this decision. - -Some parts of this workflow still need automation, particularly device capture, -rename, and MIDI enrichment. Report a concrete missing capability if it blocks -completion; do not turn it into an extra human review checklist. +Automatic preset capture, renaming, and MIDI enrichment still need implementation. +Report the concrete missing capability if it prevents preparing the episode. diff --git a/assets/GitP/Session/2026/09/24/handoff.json b/assets/GitP/Session/2026/09/24/handoff.json deleted file mode 100644 index 69e310925..000000000 --- a/assets/GitP/Session/2026/09/24/handoff.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "recorded_on": "2026-09-24", - "episode_title": "GitP.26.09.24", - "description": "A MicroFreak and Launchpad session.", - "audio_url": "https://f005.backblazeb2.com/file/logseq-encode-garden/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full.mp3", - "audio_length": 18188163, - "audio_type": "audio/mpeg" -} diff --git a/journals/2026_10_04.md b/journals/2026_10_04.md index 47a2429d4..0ea301a49 100644 --- a/journals/2026_10_04.md +++ b/journals/2026_10_04.md @@ -7,8 +7,27 @@ - [[Logseq/Entity/Asset/B2]] - [[Logseq/Entity/Asset/Frontmatter/duration-seconds]] - [[Logseq/Entity/Mise/Task]] + - [[Logseq/Entity/Podcast/Episode/Frontmatter/podcast-guid]] + - [[Logseq/Entity/Podcast/Episode/Frontmatter/podcast-published-at]] - making music - - [[GitP/A/Session/26/09/24-Thu/Asset/Synth/Full/mp3]] + - [[GitP/A/Session/24/11/19-Tue]] + - [[GitP/A/Session/24/11/19-Tue/Asset/Artwork/Overview/gif]] + - [[GitP/A/Session/24/11/19-Tue/Asset/Preset/A/mfpz]] + - [[GitP/A/Session/24/11/19-Tue/Asset/Preset/B/mfpz]] + - [[GitP/A/Session/24/11/19-Tue/Asset/Preset/C/mfpz]] + - [[GitP/A/Session/24/11/19-Tue/Asset/Synth/Full/mp3]] + - [[GitP/A/Session/24/12/04-Wed]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Artwork/Overview/gif]] + - [[GitP/A/Session/24/12/04-Wed/Asset/MIDI/Microfreak/mid]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Preset/A/mfpz]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Preset/B/mfpz]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Preset/C/mfpz]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Preset/D/mfpz]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Synth/Full/mp3]] + - [[GitP/A/Session/26/09/24-Thu]] + - [[GitP/A/Session/26/09/24-Thu/Asset/Synth/Full/mp3]] + - [[GitP/A/Session/26/09/25-Fri]] + - [[GitP/A/Session/26/09/25-Fri/Asset/Synth/Full/mp3]] - [[GitP/How To/Prepare Podcast Metadata]] - [[GitP/How To/Draft an Episode from Session Assets]] - [[GitP/A/Session/26/09/24-Thu/Session Assets]] @@ -23,13 +42,11 @@ - [[GitP/House/Back]] - mise tasks - [[Asset/mise/Task/add]] + - [[Asset/mise/Task/convert]] - [[Asset/mise/Task/fetch]] - [[Asset/mise/Task/push]] - [[Asset/mise/Task/status]] - - [[Asset/mise/Task/convert]] - - [[GitP/mise/Task/episode/draft]] - - [[GitP/mise/Task/episode/test]] - - [[GitP/mise/Task/episode/transcribe]] + - [[GitP/mise/Task/episode/sync]] - [[GitP/mise/Task/media/upload]] - reflection - [[Week/Review/26/10/04 Sun]] @@ -42,6 +59,10 @@ - [[Backblaze/B2]] - entities - [[Logseq/Entity/Frontmatter/Definition]] + - [[Logseq/Entity/Podcast/Episode]] + - making music + - [[Music/Composition/Log/26/09/25 Fri]] + - [[GitP/A/Log/26/09/26 Sat - RSS Project]] - mise tasks - [[mise/Task]] - [[My/Pref/Dev/mise/Tasks]] diff --git a/journals/2026_10_05.md b/journals/2026_10_05.md index 5d4b0f82e..167803a66 100644 --- a/journals/2026_10_05.md +++ b/journals/2026_10_05.md @@ -30,4 +30,20 @@ - entity work - [[Logseq/Entity/Definition]] - [[Logseq/Entity/Mise/Task]] - - [[Logseq/Entity/Proxy/Page]] \ No newline at end of file + - [[Logseq/Entity/Proxy/Page]] + - gitpa + - [[GitP/How To/Draft an Episode from Session Assets]] + - [[GitP/A/Session/26/09/24-Thu/Asset/Synth/Full/mp3]] + - [[GitP/A/Session/24/11/19-Tue/Asset/Artwork/Overview/gif]] + - [[GitP/A/Session/24/11/19-Tue/Asset/Preset/A/mfpz]] + - [[GitP/A/Session/24/11/19-Tue/Asset/Preset/B/mfpz]] + - [[GitP/A/Session/24/11/19-Tue/Asset/Preset/C/mfpz]] + - [[GitP/A/Session/24/11/19-Tue/Asset/Synth/Full/mp3]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Artwork/Overview/gif]] + - [[GitP/A/Session/24/12/04-Wed/Asset/MIDI/Microfreak/mid]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Preset/A/mfpz]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Preset/B/mfpz]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Preset/C/mfpz]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Preset/D/mfpz]] + - [[GitP/A/Session/24/12/04-Wed/Asset/Synth/Full/mp3]] + - [[GitP/A/Session/26/09/25-Fri/Asset/Synth/Full/mp3]] diff --git a/mise-tasks/gitpa/episode/draft b/mise-tasks/gitpa/episode/draft deleted file mode 100755 index 536f7e5f2..000000000 --- a/mise-tasks/gitpa/episode/draft +++ /dev/null @@ -1,366 +0,0 @@ -#!/usr/bin/env python3 -#MISE description="Prepare podcast metadata JSON and a GitP session assets page" -#MISE tools={python="3.13.16", "conda:ffmpeg"="9.0.2"} -#USAGE arg "" help="Ableton project directory containing a prepared MP3" -#USAGE flag "--garden " help="Garden checkout (defaults to this task's repository)" -#USAGE flag "--note " help="Source garden page when session discovery is ambiguous" -#USAGE flag "--mp3 " help="Prepared MP3 outside the project directory" -#USAGE flag "--output-dir " help="Fresh JSON handoff directory" -#USAGE flag "--assets-page " help="Fresh session assets page under garden pages/" -#USAGE flag "--audio-url " help="Existing permanent public MP3 URL to verify" -#USAGE flag "--transcript " help="Existing Whisper JSON transcript to assess" -#USAGE flag "--transcribe" help="Run local MLX Whisper on matching commentary (macOS Apple Silicon)" -#USAGE flag "--model " help="Local model directory or MLX Whisper model" default="mlx-community/whisper-large-v3-turbo" -"""Prepare garden session assets and a public episode handoff from an Ableton session.""" - -from __future__ import annotations - -import argparse -from collections import Counter -from datetime import date -import gzip -import json -import hashlib -import os -import platform -import sys -from pathlib import Path -import re -import subprocess -import tempfile -from urllib.parse import quote, urlparse -from urllib.request import Request, urlopen -import xml.etree.ElementTree as ET - - -ROOT = Path(os.environ.get("MISE_PROJECT_ROOT", Path(__file__).resolve().parents[3])).resolve() -DATE_IN_NAME = re.compile(r"(? date: - match = DATE_IN_NAME.search(project.name) - if not match: - raise ValueError(f"Cannot find YY.MM.DD in project name: {project.name}") - year, month, day = map(int, match.groups()) - return date(2000 + year, month, day) - - -def find_note(garden: Path, day: date, explicit: Path | None) -> Path: - if explicit: - return explicit.resolve() - marker = f"{day:%y}___{day:%m}___{day:%d}" - matches = [ - path for path in sorted(garden.glob(f"pages/*{marker}*.md")) - if not any(label in path.stem for label in ("___Session Assets", "___Production Evidence")) - and not path.read_text(encoding="utf-8").startswith(("- # Session Assets\n", "- # Production Evidence\n")) - and re.search(r"\bGitP\b|\bGitpa\b", path.read_text(encoding="utf-8"), re.IGNORECASE) - ] - if len(matches) != 1: - raise ValueError(f"Expected one GitP garden note for {day}; found {len(matches)}. Pass --note.") - return matches[0] - - -def readable_links(text: str) -> str: - return re.sub( - r"\[\[([^\]]+)\]\]", - lambda match: re.sub(r"^\d+\s+", "", match.group(1).split("/")[-1]), - text, - ) - - -def note_facts(note: Path) -> tuple[str, list[str], list[str]]: - text = note.read_text(encoding="utf-8") - heading = re.search(r"^- # .*? with ([^\n]+)", text, re.MULTILINE | re.IGNORECASE) - devices = heading.group(1).strip() if heading else "" - observations = [] - for line in text.splitlines(): - match = re.match(r"^\s*-\s+(.+)$", line) - if not match: - continue - content = readable_links(match.group(1)).strip() - if content and not content.startswith("#") and not re.match(r"(?:Podcast title|Description|Audio):", content, re.IGNORECASE): - observations.append(content) - manual_refs = list(dict.fromkeys(re.findall(r"\[\[(Microfreak/UG/[^\]]+)\]\]", text, re.IGNORECASE))) - return devices, observations, manual_refs - - -def explicit_description(note: Path) -> str: - match = re.search(r"^\s*-\s*Description:\s*(.+)$", note.read_text(encoding="utf-8"), re.MULTILINE) - return match.group(1).strip() if match else "" - - -def description_from_note(devices: str) -> str: - devices = re.sub(r"\bMicrofreak\b", "MicroFreak", devices, flags=re.IGNORECASE) - if devices: - return f"A {devices} session." - return "" - - -def ableton_tracks(project: Path) -> tuple[list[str], bool]: - sets = sorted(project.glob("*.als")) - if len(sets) != 1: - raise ValueError(f"Expected one Ableton set in {project}; found {len(sets)}") - with gzip.open(sets[0], "rb") as source: - root = ET.parse(source).getroot() - tracks: list[str] = [] - for track in root.findall(".//Tracks/*"): - if track.tag not in {"AudioTrack", "MidiTrack"}: - continue - name = track.find("./Name/EffectiveName") - if name is not None and name.get("Value"): - label = f"{name.get('Value')} ({'MIDI' if track.tag == 'MidiTrack' else 'audio'})" - if label not in tracks: - tracks.append(label) - program_changes = [ - node.get("Value") for node in root.findall(".//Tracks/MidiTrack//ProgramChange") - if node.get("Value") not in {None, "-1"} - ] - return tracks, bool(program_changes) - - -def duration_seconds(audio: Path) -> float: - result = subprocess.run( - ["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", str(audio)], - check=True, capture_output=True, text=True, - ) - return float(result.stdout.strip()) - - -def commentary_stem(project: Path, export_duration: float) -> tuple[Path, float] | None: - candidates = sorted((project / "Samples" / "Recorded").glob("commentary *.aif")) - if not candidates: - return None - ranked = sorted(((path, duration_seconds(path)) for path in candidates), key=lambda item: item[1], reverse=True) - path, duration = ranked[0] - return (path, duration) if abs(duration - export_duration) <= 10 else None - - -def transcript_quality(path: Path) -> tuple[bool, str]: - data = json.loads(path.read_text(encoding="utf-8")) - segments = [" ".join(segment.get("text", "").lower().split()) for segment in data.get("segments", [])] - segments = [segment for segment in segments if segment] - if len(segments) < 3: - return False, "Too little recognized speech to support a description." - common = Counter(segments) - if len(segments) >= 10 and (common.most_common(1)[0][1] / len(segments) >= 0.25 or len(common) / len(segments) < 0.5): - return False, "Repeated phrases suggest speech-recognition hallucination." - return True, "Transcript needs a listening check before its claims are used." - - -def transcribe_stem(stem: Path, model: str) -> tuple[bool, str]: - """Run speech recognition locally and discard its raw output after assessment.""" - if platform.system() != "Darwin" or platform.machine() != "arm64": - raise ValueError("--transcribe requires macOS Apple Silicon; use --transcript to assess existing recognition on other hosts") - with tempfile.TemporaryDirectory(prefix="gitpa-transcript-") as temporary: - temporary_path = Path(temporary) - normalized = temporary_path / "commentary.wav" - subprocess.run( - ["ffmpeg", "-hide_banner", "-nostdin", "-loglevel", "error", "-i", str(stem), - "-af", "highpass=f=100,lowpass=f=5000,loudnorm=I=-18:TP=-1.5:LRA=11", - "-ac", "1", "-ar", "16000", "-c:a", "pcm_s16le", str(normalized)], - check=True, - ) - subprocess.run( - ["mise", "run", "gitpa:episode:transcribe", "--", str(normalized), - "--model", model, "--output-dir", str(temporary_path)], - check=True, cwd=ROOT, - ) - return transcript_quality(temporary_path / "commentary.json") - - -def verify_public_audio(url: str, mp3: Path) -> None: - """Check enclosure metadata and range playback against the prepared file.""" - parsed = urlparse(url) - if (parsed.scheme != "https" or not parsed.netloc or parsed.username or parsed.password - or parsed.query or parsed.fragment): - raise ValueError("Audio URL must be permanent public HTTPS without credentials or query parameters") - expected_size = mp3.stat().st_size - if expected_size < 1: - raise ValueError("Prepared MP3 is empty") - with urlopen(Request(url, method="HEAD"), timeout=10) as response: - if response.status != 200: - raise ValueError("Public audio HEAD did not return HTTP 200") - if response.headers.get("Content-Type", "").split(";", 1)[0] != "audio/mpeg": - raise ValueError("Public audio has unexpected content type") - if int(response.headers.get("Content-Length", "0")) != expected_size: - raise ValueError("Public audio length differs from prepared MP3") - remote_sha1 = response.headers.get("x-bz-content-sha1") - if remote_sha1 and remote_sha1 != "none" and remote_sha1 != file_checksum(mp3, "sha1"): - raise ValueError("Public Backblaze object checksum differs from prepared MP3") - end = min(expected_size, 1024) - 1 - with urlopen(Request(url, headers={"Range": f"bytes=0-{end}"}), timeout=10) as response: - if response.status != 206 or response.headers.get("Content-Range") != f"bytes 0-{end}/{expected_size}": - raise ValueError("Public audio does not support the expected byte range") - with mp3.open("rb") as local: - if response.read(end + 2) != local.read(end + 1): - raise ValueError("Public audio opening range differs from prepared MP3") - - -def file_checksum(path: Path, algorithm: str = "sha256") -> str: - digest = hashlib.new(algorithm) - with path.open("rb") as source: - for chunk in iter(lambda: source.read(1024 * 1024), b""): - digest.update(chunk) - return digest.hexdigest() - - -def session_assets(project: Path) -> list[tuple[str, str]]: - """List available file types and project-relative paths for inspection.""" - categories = {".als": "Ableton set", ".wav": "WAV audio", ".aif": "AIFF audio", - ".aiff": "AIFF audio", ".mp3": "MP3 audio", ".mid": "MIDI file", - ".midi": "MIDI file", ".mfpz": "MicroFreak patch"} - return [(categories[path.suffix.lower()], path.relative_to(project).as_posix()) - for path in sorted(project.rglob("*")) - if path.is_file() and path.suffix.lower() in categories] - - -def logseq_page(lines: list[str]) -> str: - """Nest session sections beneath the page heading in Logseq Markdown.""" - rendered = [] - section = False - for line in lines: - if not line.strip(): - continue - if line.startswith("# "): - rendered.append("- " + line) - elif line.startswith("## "): - rendered.append("\t- " + line) - section = True - else: - text = line.removeprefix("- ") - rendered.append(("\t\t" if section else "\t") + "- " + text) - return "\n".join(rendered) + "\n" - - -def draft(project: Path, garden: Path, note: Path, transcript: Path | None = None, - transcribe: bool = False, model: str = "mlx-community/whisper-large-v3-turbo", - *, mp3: Path | None = None, output_dir: Path | None = None, - audio_url: str | None = None, assets_page: Path | None = None) -> tuple[Path, Path]: - day = project_date(project) - if note.resolve().parent != (garden / "pages").resolve(): - raise ValueError("Source note must be a page in this garden") - note_page = note.stem.replace("___", "/") - output_dir = output_dir or garden / "assets" / "GitP" / "Session" / f"{day:%Y/%m/%d}" - output = assets_page or garden / "pages" / f"GitP___Session___{day:%y}___{day:%m}___{day:%d %a}___Session Assets.md" - if output.resolve().parent != (garden / "pages").resolve() or output.suffix != ".md": - raise ValueError("Session assets page must be a Markdown file directly under the garden pages directory") - handoff = output_dir / "handoff.json" - for path in (output, handoff): - if path.exists(): - raise ValueError(f"Output already exists; choose fresh --assets-page and --output-dir paths to preserve edits: {path}") - mp3 = mp3 or project / f"GitP.{day:%y.%m.%d}.mp3" - if not mp3.is_file(): - raise FileNotFoundError(f"Prepare the MP3 before drafting: {mp3}") - duration = duration_seconds(mp3) - devices, observations, manual_refs = note_facts(note) - description = explicit_description(note) or description_from_note(devices) - if not description: - raise ValueError(f"No usable description facts in {note}; add a description to the session note") - tracks, has_program_changes = ableton_tracks(project) - stem = commentary_stem(project, duration) - record = {"recorded_on": day.isoformat(), "episode_title": f"GitP.{day:%y.%m.%d}", - "description": description} - if audio_url: - verify_public_audio(audio_url, mp3) - record.update(audio_url=audio_url, audio_length=mp3.stat().st_size, audio_type="audio/mpeg") - journal = garden / "journals" / f"{day:%Y_%m_%d}.md" - journal_url = "https://github.com/codekiln/logseq-encode-garden/blob/main/journals/" + quote(journal.name) - lines = [ - f"# Session Assets", "", f"Episode: GitP.{day:%y.%m.%d}", - f"Proposed description: {description}", "", - "## Recordings and notes", - f"- Source note: [[{note_page}]].", - f"- Prepared MP3: `{mp3.name}`, {duration / 60:.1f} minutes.", - f"- MP3 SHA-256: `{file_checksum(mp3)}`.", - f"- Ableton tracks: {', '.join(tracks) if tracks else 'none named'}.", - ] - for observation in observations: - lines.append(f"- Session note: {observation}") - if audio_url: - lines.append(f"- Release audio: [{mp3.name}]({audio_url}); length and opening range match the prepared MP3.") - if journal.is_file(): - lines.append(f"- [Recording-day journal]({journal_url}) links the session note and nearby work.") - for reference in manual_refs: - manual = garden / "pages" / (reference.replace("/", "___") + ".md") - if manual.is_file(): - manual_url = "https://github.com/codekiln/logseq-encode-garden/blob/main/pages/" + quote(manual.name) - lines.append(f"- [MicroFreak manual: {readable_links('[[' + reference + ']]')}]({manual_url}) is linked from the session note.") - if stem: - lines.append(f"- Commentary file (filename and duration): `{stem[0].name}`, {stem[1] / 60:.1f} minutes.") - if transcript or transcribe: - if transcribe and stem: - valid, reason = transcribe_stem(stem[0], model) - elif transcript: - valid, reason = transcript_quality(transcript) - else: - valid, reason = False, "No commentary stem matches the exported episode." - lines.append(f"- Speech recognition: {'review required' if valid else 'rejected'} — {reason}") - lines += ["", "## Available files"] - for category, relative_path in session_assets(project): - display_path = relative_path.replace("`", "\\`").replace("\n", " ").replace("\r", " ") - lines.append(f"- {category}: `{display_path}`.") - lines += ["", "## Review notes", - "- Ableton track names alone do not establish preset changes.", - "- Ableton MIDI clips " + ("contain program-change values; inspect the active clips before naming presets." if has_program_changes else "have no program-change values that identify presets."), - "- The proposed description comes from the source note; the AI checks it before preparing the publication PR.", "", - ] - if handoff.resolve().is_relative_to(garden.resolve()): - link = "../" + quote(handoff.resolve().relative_to(garden.resolve()).as_posix()) - lines.insert(3, f"[Episode handoff]({link})") - output_dir.mkdir(parents=True, exist_ok=True) - output.parent.mkdir(parents=True, exist_ok=True) - # Exclusive creation protects edits even if another process creates an output - # after the preflight. Remove only this invocation's handoff if note creation fails. - with handoff.open("x", encoding="utf-8") as target: - target.write(json.dumps(record, indent=2, ensure_ascii=False) + "\n") - try: - with output.open("x", encoding="utf-8") as target: - target.write(logseq_page(lines)) - except Exception: - handoff.unlink() - raise - print(f"Session assets: {output}") - print(f"Public handoff: {handoff}") - return output, handoff - - -def task_arguments() -> list[str]: - """Mise's usage parser supplies named arguments through the environment.""" - if "usage_project" not in os.environ: - return sys.argv[1:] - args = [os.environ["usage_project"]] - for name in ("garden", "note", "mp3", "output_dir", "assets_page", "audio_url", "transcript", "model"): - value = os.environ.get("usage_" + name) - if value: - args.extend(["--" + name.replace("_", "-"), value]) - if os.environ.get("usage_transcribe") == "true": - args.append("--transcribe") - return args - - -def main() -> None: - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("project", type=Path, help="Ableton project directory") - parser.add_argument("--garden", type=Path, default=ROOT, help="Garden checkout; defaults to this task's repository") - parser.add_argument("--note", type=Path, help="Garden session page when date lookup is ambiguous") - parser.add_argument("--mp3", type=Path, help="Prepared MP3 when stored outside the project directory") - parser.add_argument("--output-dir", type=Path, help="Fresh JSON handoff directory; defaults to assets/GitP/Session/YYYY/MM/DD") - parser.add_argument("--assets-page", type=Path, help="Fresh session assets page directly under garden pages/") - parser.add_argument("--audio-url", help="Existing public MP3 URL to verify and attach to the handoff") - speech = parser.add_mutually_exclusive_group() - speech.add_argument("--transcript", type=Path, help="Whisper-style JSON transcript to assess") - speech.add_argument("--transcribe", action="store_true", help="Transcribe the matching commentary stem locally with MLX Whisper") - parser.add_argument("--model", default="mlx-community/whisper-large-v3-turbo", help="Local model directory or MLX Whisper model name") - args = parser.parse_args(task_arguments()) - try: - project = args.project.resolve() - garden = args.garden.resolve() - note = find_note(garden, project_date(project), args.note) - draft(project, garden, note, args.transcript, args.transcribe, args.model, - mp3=args.mp3, output_dir=args.output_dir, audio_url=args.audio_url, assets_page=args.assets_page) - except (ValueError, OSError, subprocess.CalledProcessError) as error: - parser.exit(1, f"error: {error}\n") - - -if __name__ == "__main__": - main() diff --git a/mise-tasks/gitpa/episode/test b/mise-tasks/gitpa/episode/test deleted file mode 100755 index f166c7af1..000000000 --- a/mise-tasks/gitpa/episode/test +++ /dev/null @@ -1,318 +0,0 @@ -#!/usr/bin/env python3 -#MISE description="Test GitP release handoff preparation and session asset pages" -#MISE tools={python="3.13.16"} -#USAGE flag "-v --verbose" help="Show individual test names" -import json -import hashlib -import os -from datetime import date -import gzip -from pathlib import Path -import tempfile -import unittest -from unittest.mock import patch - -from importlib.machinery import SourceFileLoader -from importlib.util import module_from_spec, spec_from_loader -import sys - -TASK = Path(__file__).resolve().with_name("draft") -LOADER = SourceFileLoader("gitp_episode_draft", str(TASK)) -SPEC = spec_from_loader(LOADER.name, LOADER) -MODULE = module_from_spec(SPEC) -sys.modules[LOADER.name] = MODULE -LOADER.exec_module(MODULE) - -from gitp_episode_draft import ( - description_from_note, - explicit_description, - find_note, - note_facts, - transcript_quality, - draft, - project_date, - ableton_tracks, - verify_public_audio, - task_arguments, - transcribe_stem, - session_assets, -) - - -class DraftSessionNoteTests(unittest.TestCase): - def test_logseq_manual_link_retains_source_observations(self): - with tempfile.TemporaryDirectory() as temporary: - note = Path(temporary) / "session.md" - note.write_text( - "- # [[GitP/Session/26/09/24 Thu]] with Microfreak and Launchpad\n" - "\t- On the [[Microfreak]], started with [[Microfreak/UG/06 Dig Osc/03 Types/08 Two Op.FM]] on initialized patch.\n", - encoding="utf-8", - ) - devices, observations, references = note_facts(note) - self.assertEqual(description_from_note(devices), "A MicroFreak and Launchpad session.") - self.assertEqual(observations, ["On the Microfreak, started with Two Op.FM on initialized patch."]) - self.assertEqual(references, ["Microfreak/UG/06 Dig Osc/03 Types/08 Two Op.FM"]) - - def test_repeated_asr_output_is_rejected(self): - with tempfile.TemporaryDirectory() as temporary: - transcript = Path(temporary) / "transcript.json" - transcript.write_text( - json.dumps({"segments": [{"text": "Thank you."}] * 25 + [{"text": "This is weird."}]}), - encoding="utf-8", - ) - self.assertFalse(transcript_quality(transcript)[0]) - - def test_note_discovery_uses_contents_when_filename_lacks_gitpa(self): - with tempfile.TemporaryDirectory() as temporary: - pages = Path(temporary) / "pages" - pages.mkdir() - note = pages / "Music___Composition___Log___26___09___25 Fri.md" - note.write_text("- Podcast title: GitP.26.09.25\n\t- Description: A synth session.\n", encoding="utf-8") - self.assertEqual(find_note(Path(temporary), date(2026, 9, 25), None), note) - self.assertEqual(explicit_description(note), "A synth session.") - - def test_source_discovery_excludes_prior_generated_page_titles(self): - with tempfile.TemporaryDirectory() as temporary: - project, garden, note = self.fixture(Path(temporary)) - legacy = garden / "pages" / "GitP___Session___26___09___24 Thu___Production Evidence.md" - legacy.write_text("- # Production Evidence\n\t- Episode: GitP.26.09.24\n") - current = garden / "pages" / "GitP___Session___26___09___24 Thu___Session Assets.md" - current.write_text("- # Session Assets\n\t- Episode: GitP.26.09.24\n") - self.assertEqual(find_note(garden, date(2026, 9, 24), None), note) - - def test_mise_usage_environment_reaches_task_arguments(self): - values = {"usage_project": "/recordings/Project", "usage_assets_page": "pages/Assets.md", - "usage_output_dir": "assets/comparison", "usage_audio_url": "https://example.com/audio.mp3", - "usage_transcribe": "true", "usage_model": "mlx-community/whisper-large-v3-turbo"} - with patch.dict(os.environ, values, clear=True): - self.assertEqual(task_arguments(), ["/recordings/Project", "--output-dir", "assets/comparison", - "--assets-page", "pages/Assets.md", "--audio-url", "https://example.com/audio.mp3", - "--model", "mlx-community/whisper-large-v3-turbo", "--transcribe"]) - - def test_optional_transcription_platform_guard(self): - with patch("gitp_episode_draft.platform.system", return_value="Linux"), patch("gitp_episode_draft.subprocess.run") as command: - with self.assertRaisesRegex(ValueError, "macOS Apple Silicon"): - transcribe_stem(Path("commentary.aif"), "model") - command.assert_not_called() - - def test_optional_transcription_invokes_its_mise_dependency_task(self): - with patch("gitp_episode_draft.platform.system", return_value="Darwin"), patch("gitp_episode_draft.platform.machine", return_value="arm64"), patch("gitp_episode_draft.subprocess.run") as command, patch("gitp_episode_draft.transcript_quality", return_value=(False, "Repeated phrases")): - self.assertEqual(transcribe_stem(Path("commentary.aif"), "model"), (False, "Repeated phrases")) - arguments = command.call_args_list[1].args[0] - self.assertEqual(arguments[:4], ["mise", "run", "gitpa:episode:transcribe", "--"]) - self.assertNotIn("--with", arguments) - self.assertEqual(command.call_args_list[1].kwargs["cwd"], MODULE.ROOT) - self.assertNotIn("--offline", arguments) - - def test_optional_task_declares_whisper_and_preserves_existing_output(self): - task = TASK.with_name("transcribe") - self.assertIn('"pypi:mlx-whisper"="0.4.3"', task.read_text()) - self.assertNotIn('pypi:mlx-whisper', TASK.read_text()) - loader = SourceFileLoader("gitp_episode_transcribe", str(task)) - spec = spec_from_loader(loader.name, loader) - module = module_from_spec(spec) - loader.exec_module(module) - with tempfile.TemporaryDirectory() as temporary: - root = Path(temporary) - audio = root / "audio.wav" - audio.write_bytes(b"selected recording") - output = root / "commentary.json" - output.write_text("Reviewed recognition") - with patch.object(module.platform, "system", return_value="Darwin"), patch.object(module.platform, "machine", return_value="arm64"), patch.object(module.subprocess, "run") as run: - with self.assertRaisesRegex(ValueError, "already exists"): - module.transcribe(audio, root, "model") - run.assert_not_called() - self.assertEqual(output.read_text(), "Reviewed recognition") - - def test_optional_task_runs_declared_whisper_executable_and_json_flags(self): - loader = SourceFileLoader("gitp_episode_transcribe", str(TASK.with_name("transcribe"))) - module = module_from_spec(spec_from_loader(loader.name, loader)) - loader.exec_module(module) - with tempfile.TemporaryDirectory() as temporary: - root = Path(temporary) - audio = root / "audio.wav" - audio.write_bytes(b"selected recording") - def recognize(arguments, **kwargs): - (root / "commentary.json").write_text('{"segments": []}') - with patch.object(module.platform, "system", return_value="Darwin"), patch.object(module.platform, "machine", return_value="arm64"), patch.object(module.subprocess, "run", side_effect=recognize) as run: - result = module.transcribe(audio, root, "model") - arguments = run.call_args.args[0] - self.assertEqual(arguments[:2], ["mlx_whisper", str(audio)]) - self.assertEqual(arguments[arguments.index("--output-name") + 1], "commentary") - self.assertEqual(arguments[arguments.index("--output-format") + 1], "json") - self.assertEqual(result, root / "commentary.json") - self.assertEqual(audio.read_bytes(), b"selected recording") - - def fixture(self, root): - project = root / "GitP26.09.24 Project" - project.mkdir() - (project / "GitP.26.09.24.mp3").write_bytes(b"prepared audio") - with gzip.open(project / "Session.als", "wb") as target: - target.write(b'') - garden = root / "garden" - (garden / "pages").mkdir(parents=True) - note = garden / "pages" / "Music___Composition___Log___26___09___24 Thu.md" - note.write_text("- Podcast title: GitP.26.09.24\n\t- Description: A synth session.\n") - return project, garden, note - - def test_asset_inventory_lists_relative_candidates_without_changing_sources(self): - with tempfile.TemporaryDirectory() as temporary: - project, garden, note = self.fixture(Path(temporary)) - candidates = {"Samples/Recorded/mystery.wav": b"wav", "Samples/Recorded/part.aif": b"aif", - "Samples/Recorded/other.aiff": b"aiff", "patches/program.mfpz": b"patch", - "clips/sequence.mid": b"midi", "clips/other.midi": b"midi", "ignore.asd": b"analysis"} - for relative, content in candidates.items(): - target = project / relative - target.parent.mkdir(parents=True, exist_ok=True) - target.write_bytes(content) - before = {path.relative_to(project).as_posix(): path.read_bytes() for path in project.rglob("*") if path.is_file()} - inventory = dict((name, category) for category, name in session_assets(project)) - self.assertEqual(inventory["Samples/Recorded/mystery.wav"], "WAV audio") - self.assertEqual(inventory["Samples/Recorded/part.aif"], "AIFF audio") - self.assertEqual(inventory["Samples/Recorded/other.aiff"], "AIFF audio") - self.assertEqual(inventory["patches/program.mfpz"], "MicroFreak patch") - self.assertEqual(inventory["clips/sequence.mid"], "MIDI file") - self.assertEqual(inventory["clips/other.midi"], "MIDI file") - self.assertEqual(inventory["Session.als"], "Ableton set") - self.assertNotIn("ignore.asd", inventory) - with patch("gitp_episode_draft.duration_seconds", return_value=180): - output, handoff = draft(project, garden, note) - text = output.read_text() - self.assertTrue(text.startswith("- # Session Assets\n")) - self.assertIn("\t- ## Available files\n", text) - self.assertIn("\t\t- WAV audio: `Samples/Recorded/mystery.wav`.", text) - self.assertNotIn(str(project), text) - self.assertEqual(before, {path.relative_to(project).as_posix(): path.read_bytes() for path in project.rglob("*") if path.is_file()}) - - def test_ableton_track_names_and_program_changes(self): - with tempfile.TemporaryDirectory() as temporary: - project, _, _ = self.fixture(Path(temporary)) - self.assertEqual(project_date(project), date(2026, 9, 24)) - self.assertEqual(ableton_tracks(project), (["Commentary (audio)", "MicroFreak (MIDI)"], False)) - - def test_handoff_is_production_metadata_without_publication_identity(self): - with tempfile.TemporaryDirectory() as temporary: - project, garden, note = self.fixture(Path(temporary)) - original = (project / "GitP.26.09.24.mp3").read_bytes() - with patch("gitp_episode_draft.duration_seconds", return_value=180): - output, handoff = draft(project, garden, note) - record = json.loads(handoff.read_text()) - self.assertEqual(record, {"recorded_on": "2026-09-24", "episode_title": "GitP.26.09.24", "description": "A synth session."}) - self.assertIn("MP3 SHA-256", output.read_text()) - self.assertEqual(output.parent, garden / "pages") - self.assertFalse(list((garden / "assets").rglob("*.md"))) - self.assertTrue(all(line.lstrip("\t").startswith("- ") for line in output.read_text().splitlines())) - self.assertIn("\t- ## Recordings and notes\n\t\t- Source note: [[Music/Composition/Log/26/09/24 Thu]]", output.read_text()) - self.assertEqual(find_note(garden, date(2026, 9, 24), None), note) - self.assertEqual((project / "GitP.26.09.24.mp3").read_bytes(), original) - self.assertFalse((garden / "pages" / "Ceremony___2026___09___24.md").exists()) - - def test_verified_media_attaches_only_public_enclosure_fields(self): - with tempfile.TemporaryDirectory() as temporary: - project, garden, note = self.fixture(Path(temporary)) - with patch("gitp_episode_draft.duration_seconds", return_value=180), patch("gitp_episode_draft.verify_public_audio") as verify: - output, handoff = draft(project, garden, note, audio_url="https://example.com/audio.mp3") - record = json.loads(handoff.read_text()) - self.assertEqual(record["audio_url"], "https://example.com/audio.mp3") - self.assertEqual(record["audio_length"], 14) - self.assertEqual(record["audio_type"], "audio/mpeg") - verify.assert_called_once() - - def test_existing_assets_and_handoff_are_preserved_before_inspection(self): - for existing in ("Session Assets.md", "handoff.json"): - with self.subTest(existing=existing), tempfile.TemporaryDirectory() as temporary: - project, garden, note = self.fixture(Path(temporary)) - output = garden / "assets" / "GitP" / "Session" / "2026" / "09" / "24" - output.mkdir(parents=True) - destination = garden / "pages" / "GitP___Session___26___09___24 Thu___Session Assets.md" if existing == "Session Assets.md" else output / existing - original = "tags:: [[Protected]]\n- Human edits\n" - destination.write_text(original) - with patch("gitp_episode_draft.duration_seconds") as inspect: - with self.assertRaisesRegex(ValueError, "Output already exists"): - draft(project, garden, note) - inspect.assert_not_called() - self.assertEqual(destination.read_text(), original) - self.assertEqual(len(list(output.iterdir())), 0 if existing == "Session Assets.md" else 1) - - def test_comparison_outputs_require_fresh_page_and_handoff_paths(self): - with tempfile.TemporaryDirectory() as temporary: - project, garden, note = self.fixture(Path(temporary)) - with patch("gitp_episode_draft.duration_seconds", return_value=180): - original_page, original_handoff = draft(project, garden, note) - original_page.write_text("tags:: [[Protected]]\n- Human edits\n") - comparison = garden / "pages" / "GitP___Session___26___09___24 Thu___Session Assets Comparison.md" - page, handoff = draft(project, garden, note, assets_page=comparison, output_dir=garden / "assets" / "comparison") - self.assertEqual(page, comparison) - self.assertEqual(handoff, garden / "assets" / "comparison" / "handoff.json") - self.assertEqual(original_page.read_text(), "tags:: [[Protected]]\n- Human edits\n") - self.assertTrue(original_handoff.is_file()) - self.assertEqual(find_note(garden, date(2026, 9, 24), None), note) - - def test_assets_cannot_be_written_under_assets(self): - with tempfile.TemporaryDirectory() as temporary: - project, garden, note = self.fixture(Path(temporary)) - with self.assertRaisesRegex(ValueError, "garden pages directory"): - draft(project, garden, note, assets_page=garden / "assets" / "note.md") - self.assertFalse((garden / "assets").exists()) - - def test_invalid_upload_leaves_no_partial_outputs(self): - with tempfile.TemporaryDirectory() as temporary: - project, garden, note = self.fixture(Path(temporary)) - with patch("gitp_episode_draft.duration_seconds", return_value=180), patch("gitp_episode_draft.verify_public_audio", side_effect=ValueError("length differs")): - with self.assertRaisesRegex(ValueError, "length differs"): - draft(project, garden, note, audio_url="https://example.com/audio.mp3") - self.assertFalse((garden / "assets").exists()) - - def test_transcript_findings_stay_in_assets_and_not_public_copy(self): - with tempfile.TemporaryDirectory() as temporary: - project, garden, note = self.fixture(Path(temporary)) - transcript = Path(temporary) / "speech.json" - transcript.write_text(json.dumps({"segments": [{"text": "Thank you."}] * 25})) - with patch("gitp_episode_draft.duration_seconds", return_value=180): - output, handoff = draft(project, garden, note, transcript) - self.assertIn("Speech recognition: rejected", output.read_text()) - self.assertEqual(json.loads(handoff.read_text())["description"], "A synth session.") - - def test_permanent_url_validation_happens_before_network(self): - with tempfile.TemporaryDirectory() as temporary: - mp3 = Path(temporary) / "audio.mp3" - mp3.write_bytes(b"abc") - for url in ("http://example.com/audio.mp3", "https://example.com/audio.mp3?token=x", ("https://" + "fixture-user" + ":" + "fixture-pass" + "@example.com/audio.mp3"), "https://example.com/audio.mp3#fragment"): - with self.subTest(url=url), patch("gitp_episode_draft.urlopen") as network: - with self.assertRaisesRegex(ValueError, "permanent public HTTPS"): - verify_public_audio(url, mp3) - network.assert_not_called() - - def test_public_length_range_and_audio_content_checks(self): - from unittest.mock import MagicMock - with tempfile.TemporaryDirectory() as temporary: - mp3 = Path(temporary) / "audio.mp3" - mp3.write_bytes(b"abc") - def response(status, headers, body=b""): - result = MagicMock() - result.__enter__.return_value = result - result.status = status - result.headers = headers - result.read.return_value = body - return result - head = response(200, {"Content-Type": "audio/mpeg", "Content-Length": "3"}) - ranged = response(206, {"Content-Range": "bytes 0-2/3"}, b"abc") - with patch("gitp_episode_draft.urlopen", side_effect=[head, ranged]): - verify_public_audio("https://example.com/audio.mp3", mp3) - for bad_range in (response(200, {}, b"abc"), response(206, {"Content-Range": "bytes 0-2/3"}, b"xyz")): - with patch("gitp_episode_draft.urlopen", side_effect=[head, bad_range]): - with self.assertRaises(ValueError): - verify_public_audio("https://example.com/audio.mp3", mp3) - head.headers["x-bz-content-sha1"] = hashlib.sha1(b"wrong content").hexdigest() - with patch("gitp_episode_draft.urlopen", return_value=head): - with self.assertRaisesRegex(ValueError, "checksum differs"): - verify_public_audio("https://example.com/audio.mp3", mp3) - del head.headers["x-bz-content-sha1"] - head.headers["Content-Length"] = "4" - with patch("gitp_episode_draft.urlopen", return_value=head): - with self.assertRaisesRegex(ValueError, "length differs"): - verify_public_audio("https://example.com/audio.mp3", mp3) - - -if __name__ == "__main__": - unittest.main(argv=[sys.argv[0]], verbosity=2 if os.environ.get("usage_verbose") == "true" or "--verbose" in sys.argv else 1) diff --git a/mise-tasks/gitpa/episode/transcribe b/mise-tasks/gitpa/episode/transcribe deleted file mode 100755 index b18243553..000000000 --- a/mise-tasks/gitpa/episode/transcribe +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 -#MISE description="Transcribe a selected recording with MLX Whisper on Apple Silicon macOS" -#MISE tools={python="3.13.16", uv="0.12.22", "pypi:mlx-whisper"="0.4.3"} -#USAGE arg "" help="Selected audio recording to transcribe" -#USAGE flag "--output-dir " required=#true help="Directory for commentary.json" -#USAGE flag "--model " default="mlx-community/whisper-large-v3-turbo" help="Local model directory or MLX Whisper model" - -"""Transcribe selected audio; recognition output needs a listening review.""" - -import argparse -import os -from pathlib import Path -import platform -import subprocess -import sys - - -def transcribe(recording: Path, output_dir: Path, model: str) -> Path: - if platform.system() != "Darwin" or platform.machine() != "arm64": - raise ValueError("MLX Whisper requires macOS Apple Silicon") - if not recording.is_file(): - raise ValueError(f"Recording not found: {recording}") - output = output_dir / "commentary.json" - if output.exists(): - raise ValueError(f"Transcript already exists: {output}") - output_dir.mkdir(parents=True, exist_ok=True) - subprocess.run( - ["mlx_whisper", str(recording), "--model", model, "--language", "en", - "--condition-on-previous-text", "False", "--verbose", "False", - "--output-format", "json", "--output-name", "commentary", "--output-dir", str(output_dir)], - check=True, - ) - if not output.is_file(): - raise ValueError("Whisper did not produce commentary.json") - return output - - -def main() -> None: - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("recording", type=Path) - parser.add_argument("--output-dir", type=Path, required=True) - parser.add_argument("--model", default="mlx-community/whisper-large-v3-turbo") - arguments = sys.argv[1:] - if "usage_recording" in os.environ: - arguments = [os.environ["usage_recording"], "--output-dir", os.environ["usage_output_dir"], - "--model", os.environ.get("usage_model", "mlx-community/whisper-large-v3-turbo")] - args = parser.parse_args(arguments) - try: - output = transcribe(args.recording, args.output_dir, args.model) - except (OSError, ValueError, subprocess.CalledProcessError) as error: - parser.exit(1, f"error: {error}\n") - print(f"Transcript: {output}") - - -if __name__ == "__main__": - main() diff --git a/pages/GitP___A___Log___26___09___26 Sat - RSS Project.md b/pages/GitP___A___Log___26___09___26 Sat - RSS Project.md index 06fa65b3a..fd5b2eed0 100644 --- a/pages/GitP___A___Log___26___09___26 Sat - RSS Project.md +++ b/pages/GitP___A___Log___26___09___26 Sat - RSS Project.md @@ -9,14 +9,14 @@ - 14:02 - [[GitP/A/Log/26/09/26 Sat - RSS Project/Q/What Backblaze Options would work with Logseq?]] - ## Plan - - TODO Publish `GitP.26.09.25` as the first RSS episode. + - DONE Publish [[GitP/A/Session/26/09/25-Fri]] as the first RSS episode. - TODO Give the uploaded MP3 a stable Cloudflare media URL and verify HEAD and byte-range requests. - - TODO Create an editable public episode page and `episode.yml` from [[Music/Composition/Log/26/09/25 Fri]]; include a stable GUID, recording and publication dates, description, media URL, enclosure type and length, and page link. - - TODO Generate RSS from episode records, publish it on GitHub Pages, and validate its self-link, artwork, and enclosure. + - DONE Create the public episode from [[Music/Composition/Log/26/09/25 Fri]], with its recording and RSS release identity. + - DONE Generate RSS, publish it on GitHub Pages, and validate its self-link, artwork, and enclosure. - TODO Subscribe to the feed by URL and check playback in a podcast app. - TODO Add staged-file secret and identity scanning and a CI scan to Gitpa before publishing the feed. - TODO [Rename the shared Backblaze rclone remote · dotfiles issue #131](https://github.com/codekiln/dotfiles/issues/131), then update the garden upload task's default remote. - - TODO Define a remote-file entity for garden media pages: derive the initial MP3 filename from the page name, link the upload instructions, record public or private visibility, and retain the published object key and URL when a page is renamed. + - DONE Define [[Logseq/Entity/Asset]] and [[Logseq/Entity/Asset/B2]] for garden media and page-derived upload filenames. - TODO Automate bringing selected garden notes into public episode pages after the first manual publication works. - DONE [Set up the shared Backblaze rclone remote and 1Password publishing access · dotfiles issue #125](https://github.com/codekiln/dotfiles/issues/125) id:: 6ab7f21f-4627-4e47-a60c-7a9e76e8acbf diff --git a/pages/GitP___A___Log___26___10___04 Sun___Front v Back Split___Plan.md b/pages/GitP___A___Log___26___10___04 Sun___Front v Back Split___Plan.md index e307b88e7..f199a28c1 100644 --- a/pages/GitP___A___Log___26___10___04 Sun___Front v Back Split___Plan.md +++ b/pages/GitP___A___Log___26___10___04 Sun___Front v Back Split___Plan.md @@ -1,34 +1,11 @@ -# Front and Back of House - - [[GitP/House/Back]] owns session evidence, recording preparation, episode metadata assembly, and media uploads in [[Person/codekiln/GitHub/logseq-encode-garden]]. Raw sessions remain in `~/Documents/ableton/GitP`, with their future storage covered by [[GitP/A/Log/26/10/04 Sun/Dvc for Knowledge Gardens/Plan]]. - - [[GitP/House/Front]] owns the public episode selection, final copy, artwork, web pages, RSS, and site deployment in [[Person/codekiln/GitHub/gitpa]]. - - The handoff is an explicit episode record with permanent public media URLs. Front of House reviews the record, adds its publication decision, and builds the public page and feed. - - ## Inventory - - Garden session notes: [[Making/Music/Log/26/09/24 Thu - GitP Reboot with Novation Launchpad]] and [[Music/Composition/Log/26/09/25 Fri]] contain recording evidence and descriptions. MicroFreak and Launchpad manuals, screenshots, patch inspections, and musical experiments belong with the production notes. - - Garden media tasks: [prepare](https://github.com/codekiln/logseq-encode-garden/blob/main/mise-tasks/gitpa/media/prepare) converts an exported WAV to MP3; [upload](https://github.com/codekiln/logseq-encode-garden/blob/main/mise-tasks/gitpa/media/upload) writes an immutable object under `logseq-encode-garden/gitpa/episodes/`. These already implement Back of House responsibilities. - - Ableton recording tree: project sets, backups, recorded WAV and AIF audio, analysis files, MicroFreak patches, and exported MP3s are production sources. The `exports/` directory holds the September 25 prepared MP3 and preview. Rough session material belongs in the private garden's future bucket; selected public releases use the public garden's bucket. - - Gitpa `gitp-garden/`: public ceremony pages, episode records, logo, animated artwork, older MP3 releases, MIDI, and downloadable MicroFreak patches. Public pages and branding stay in Gitpa. Release audio, MIDI, and patches gain garden-owned remote objects, with Gitpa retaining public links. - - Gitpa [build_rss.py](https://github.com/codekiln/gitpa/blob/main/scripts/build_rss.py), `rss.xml`, Logseq styles/configuration, and the Pages workflow serve the audience and stay in Gitpa. - - Gitpa [gitp-acolyte](https://github.com/codekiln/gitpa/tree/main/gitp-acolyte) mixes source-file selection, AI metadata extraction, episode record creation, and page rendering. Source inspection and metadata extraction move to the garden; page rendering becomes a consumer of the handoff. - - [Import garden metadata and draft September 24 · Gitpa PR #9](https://github.com/codekiln/gitpa/pull/9) is merged. It supplies the importer and unpublished public draft; session assets and optional transcription are maintained in the garden. - - ## Handoff - - Back of House supplies recording date, proposed title and description, source-note link, selected asset names, permanent object keys and public URLs, media type, enclosure length, checksum, and any useful listening or transcription caveats. - - Front of House chooses the public copy and artwork, assigns the permanent GUID and publication timestamp, selects the public page name, and sets `published: true` after review. - - Store production provenance beside the garden record. Transfer only the selected public fields and assets into Gitpa. Page proxies should select named pages and fields; copying a whole production namespace requires a separate editorial decision. - - Preserve each already published GUID, publication timestamp, page URL, and enclosure URL through the migration. Front of House keeps its editable publication record; garden release metadata owns the uploaded object's identity and verification. - - [[GitP/A/Log/26/10/04 Sun/Front v Back Split/Plan/Sep25 Handoff]] provides a working example using the current September 25 publication fields and verified public media headers. - - Next actions are in [[GitP/A/Log/26/10/04 Sun/Podcast/Plan]], starting with the September 24 draft and the Mac credential-cache trial. - - ## Migration - [[Ghost Gardener]] please - - DONE Split the September 24 draft in PR #9: place the extraction script, source-note output, and optional transcription in the garden; leave the curated page and draft publication record in Gitpa. Keep the episode unpublished until the prepared MP3 and description have been reviewed. - - [Gather session assets and prepare podcast drafts · Garden PR #176](https://github.com/codekiln/logseq-encode-garden/pull/176) is merged. It adds the gathering skill, garden preparation tasks, September 24 session assets, and JSON export. - - [Import garden handoffs and draft September 24 · Gitpa PR #9](https://github.com/codekiln/gitpa/pull/9) keeps the public draft and imports selected fields while preserving editorial copy and episode identity. - - DONE Add a garden task that exports selected public episode fields to a handoff record. Add a Gitpa import task that validates the record and renders a draft page without changing an existing GUID or publication date. - - The September 24 handoff passed public-media verification and imported without changing the existing edited record or page. The paired PRs include producer, importer, and RSS tests. - - DOING Use September 24 as the dry run: gather session assets in the garden, prepare the release, verify its URL and range playback, import into Gitpa, review the description, and run `rss:check` and `rss:test` before publication. - - TODO Separate `gitp-acolyte` production functions from presentation functions, carrying over the applicable tests. Replace file relocation in [pub_rec_files.py](https://github.com/codekiln/gitpa/blob/main/gitp-acolyte/gitp_acolyte/ceremonial/spells/recording/files/pub_rec_files.py) with source-preserving release preparation: its current `Path.replace` calls move the original recordings. - - TODO Inventory older ceremony downloads with checksums and their existing published URLs. Copy selected MP3, MIDI, and patch releases to permanent remote objects, verify the copies, and then update public page links. Preserve working historical links during the transition. - - TODO Connect [[GitP/A/Log/26/10/04 Sun/Fnox/Plan]] and [[GitP/A/Log/26/10/04 Sun/Dvc for Knowledge Gardens/Plan]] to the garden production tasks. Gitpa site and RSS builds consume public URLs without bucket-write credentials or access to local recordings. - - ## Acceptance - - A Gitpa checkout can build the public site and RSS using its curated records and public asset URLs. - - A garden checkout can create and verify a release from available production assets, recording source provenance and checksums. - - Re-importing a handoff preserves Front of House editorial changes and permanent episode identity, with conflicts reported for review. - - The September 25 episode remains playable throughout the change, and the September 24 draft remains editable before publication. \ No newline at end of file +- # Front and Back of House + - [[GitP/House/Back]] prepares recordings, uploads media, and maintains session episodes in [[Person/codekiln/GitHub/logseq-encode-garden]]. Raw recordings stay under `~/Documents/ableton/GitP`. + - [[GitP/House/Front]] serves those episodes through [[Person/codekiln/GitHub/gitpa]]'s website and RSS, with its branding and deployment. + - ## One source for episode content + - Garden session pages hold the title, description, source notes, and asset-page embeds. Gitpa mirrors the session and embedded assets as [[Logseq/Entity/Proxy/Page]] instances with identical names. + - Edit episode content in the garden, then run [[GitP/mise/Task/episode/sync]] in Gitpa. Resync replaces the body and preserves Gitpa's publication properties. + - Gitpa's `public::` controls visibility. RSS release identity and publication time belong to its proxy. [[GitP/How To/Prepare Podcast Metadata]] describes publication preparation. + - ## Remaining work + - TODO Connect [media upload](https://github.com/codekiln/logseq-encode-garden/blob/main/mise-tasks/gitpa/media/upload) to the [Fnox assets profile](https://github.com/codekiln/logseq-encode-garden/blob/main/fnox.toml) for unattended uploads on the Mac. + - TODO Implement the asset commands in [[GitP/A/Log/26/10/04 Sun/Dvc for Knowledge Gardens/Plan]] before collecting private Ableton sessions. + - DONE Move the historical release recordings, artwork, presets, and MIDI to garden-owned B2 assets, linked from [[GitP/A/Session/24/11/19-Tue]] and [[GitP/A/Session/24/12/04-Wed]]. diff --git a/pages/GitP___A___Log___26___10___04 Sun___Front v Back Split___Plan___Sep25 Handoff.md b/pages/GitP___A___Log___26___10___04 Sun___Front v Back Split___Plan___Sep25 Handoff.md index 917729b8a..bab715003 100644 --- a/pages/GitP___A___Log___26___10___04 Sun___Front v Back Split___Plan___Sep25 Handoff.md +++ b/pages/GitP___A___Log___26___10___04 Sun___Front v Back Split___Plan___Sep25 Handoff.md @@ -1,24 +1,4 @@ -- # September 25 release handoff - - Production note: [[Music/Composition/Log/26/09/25 Fri]]. - - Public episode: [GitP.26.09.25](https://codekiln.github.io/gitpa/#/page/Ceremony%2F2026%2F09%2F25). - - Object key: `gitpa/episodes/2026-09-25/GitP.26.09.25.mp3` in the `logseq-encode-garden` bucket. - - Backblaze object SHA-1: `bb21670ca393a0d0afa5a5ab22eef27f457aa6a5`, reported by the public object's response header. Local source comparison and a SHA-256 release manifest remain to be added. - - On [[2026-10-04 Sun]], the public media URL returned HTTP 200, `audio/mpeg`, and `Content-Length: 23569806`; a request for the opening byte returned HTTP 206 with the matching total length. - - ## Publication record - - These fields reproduce the [published Gitpa episode record](https://github.com/codekiln/gitpa/blob/main/gitp-garden/assets/Ceremony/2026/09/25/episode.yml). The YAML can be saved as `episode.yml` and consumed by Gitpa's existing RSS builder alongside the public ceremony page. Its GUID and publication time belong to the existing release. - - ~~~yaml - published: true - episode_title: GitP.26.09.25 - description: MicroFreak SAWX and WaveUser preset 1910, with a Novation Launchpad sequence. - recorded_on: '2026-09-25' - published_at: '2026-10-03T12:00:00-04:00' - guid: gitpa20260925 - page: Ceremony/2026/09/25 - page_url: https://codekiln.github.io/gitpa/#/page/Ceremony%2F2026%2F09%2F25 - audio_url: https://f005.backblazeb2.com/file/logseq-encode-garden/gitpa/episodes/2026-09-25/GitP.26.09.25.mp3 - audio_length: 23569806 - audio_type: audio/mpeg - ~~~ - - ## Ownership - - Garden release preparation owns `recorded_on`, `audio_url`, `audio_length`, `audio_type`, the object key, and checksum evidence. The session note supplies a proposed title and description. - - Gitpa episode review owns `published`, `published_at`, `guid`, `page`, `page_url`, and the final title and description. +- # September 25 episode + - [[GitP/A/Session/26/09/25-Fri]] holds the episode text and recording link, drawn from [[Music/Composition/Log/26/09/25 Fri]]. + - [[GitP/How To/Prepare Podcast Metadata]] explains how Gitpa mirrors the session and builds RSS. + - Gitpa retains the published episode's GUID and publication date on its proxy. diff --git a/pages/GitP___A___Log___26___10___04 Sun___Podcast___Plan.md b/pages/GitP___A___Log___26___10___04 Sun___Podcast___Plan.md index 0b47b14da..f2737920b 100644 --- a/pages/GitP___A___Log___26___10___04 Sun___Podcast___Plan.md +++ b/pages/GitP___A___Log___26___10___04 Sun___Podcast___Plan.md @@ -1,23 +1,17 @@ - # Podcast action plan - - Finish the September 24 episode, make asset commands work while the Mac is unattended, then try DVC on a disposable recording. For later sessions, start with the exported WAV and music-making log. The AI prepares the episode, presets, session details, media links, and Gitpa publication PR. The human listens to one MP3 and merges the PR to publish. - - ## Finish September 24 - - DOING Complete [September 24 session assets](https://github.com/codekiln/logseq-encode-garden/blob/main/pages/GitP___A___Session___26___09___24-Thu___Session%20Assets.md) and the [September 24 episode draft](https://github.com/codekiln/gitpa/blob/main/gitp-garden/pages/Ceremony___2026___09___24.md). - - DONE Check the public MP3 against the prepared local file. Its media type, size, opening audio range, and Backblaze checksum match. - - DONE Import the garden's JSON metadata into Gitpa. The existing description and draft page remain intact. The feed check and importer/feed tests pass. - - TODO Listen to [GitP.26.09.24 — prepared episode audio, about 13 minutes](https://f005.backblazeb2.com/file/logseq-encode-garden/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full.mp3) and give this recording a go or a no-go for publication. - - TODO AI: Check the description on the [September 24 episode draft](https://github.com/codekiln/gitpa/blob/main/gitp-garden/pages/Ceremony___2026___09___24.md) with [September 24 recording note: MicroFreak and Launchpad](https://github.com/codekiln/logseq-encode-garden/blob/main/pages/Making___Music___Log___26___09___24%20Thu%20-%20GitP%20Reboot%20with%20Novation%20Launchpad.md) and keep the copy grounded in the source note. Keep unsupported preset claims out of the public description. - - TODO AI: Prepare a Gitpa publication PR for the [September 24 episode record](https://github.com/codekiln/gitpa/blob/main/gitp-garden/assets/Ceremony/2026/09/24/episode.yml) and [September 24 draft page](https://github.com/codekiln/gitpa/blob/main/gitp-garden/pages/Ceremony___2026___09___24.md). Assign the GUID and publication time, add the public page to the [GitP episode listing](https://github.com/codekiln/gitpa/tree/main/gitp-garden/pages), run `mise run rss:build` in Gitpa, and leave the PR for the human to merge after listening. After the merge, check [the deployed podcast](https://codekiln.github.io/gitpa/) and [RSS feed](https://codekiln.github.io/gitpa/rss.xml). - - ## Make unattended asset work practical - - DONE Configure B2 access using [Fnox plan: unattended B2 access on the Mac](https://github.com/codekiln/logseq-encode-garden/blob/main/pages/GitP___A___Log___26___10___04%20Sun___Fnox___Plan.md) on the Mac running Codex and Claude. Keep B2 keys in 1Password and create a gitignored cache encrypted with a file-based age key. - - The supplied vault references are configured, the encrypted cache resolves without the 1Password CLI or daemon, and a disposable B2 upload/list/download/replace/delete trial passed. The key is restricted to `logseq-encode-garden` but covers the whole bucket. - - TODO Connect [the garden media upload task](https://github.com/codekiln/logseq-encode-garden/blob/main/mise-tasks/gitpa/media/upload) to [the tested fnox assets profile](https://github.com/codekiln/logseq-encode-garden/blob/main/fnox.toml), so agents can upload prepared media using cached credentials. Use the same credential wrapper for later inventory, fetch, and deletion tasks. - - Repeat the same command interface in each garden, with that garden's bucket settings and B2 key. Give rough recordings a private bucket. - - ## Try DVC before moving recordings - - DONE Run the restore trial in [DVC plan: track and restore garden recordings](https://github.com/codekiln/logseq-encode-garden/blob/main/pages/GitP___A___Log___26___10___04%20Sun___Dvc%20for%20Knowledge%20Gardens___Plan.md) using the approved September 24 MP3 and the garden B2 key. - - The B2 trial restored the MP3 into a fresh checkout with a matching checksum. A selected pull restored only the requested file; a full pull restored the other test asset. - - Working audio and its adjacent `.dvc` file use the same path under `assets/.remote/`. Git stores the `.dvc` file; DVC restores the audio beside it. The [DVC plan’s concrete September 24 layout](https://github.com/codekiln/logseq-encode-garden/blob/main/pages/GitP___A___Log___26___10___04%20Sun___Dvc%20for%20Knowledge%20Gardens___Plan.md#a-recording-and-its-metadata) shows the paths and pull command. - - TODO Implement the asset tasks and WAV conversion described in [DVC plan: put the layout to work](https://github.com/codekiln/logseq-encode-garden/blob/main/pages/GitP___A___Log___26___10___04%20Sun___Dvc%20for%20Knowledge%20Gardens___Plan.md#put-the-layout-to-work), then restore a collected Ableton project in the private garden before migrating more sessions. - - ## Repeat for later sessions - - TODO Follow [Gather session assets and draft an episode](https://github.com/codekiln/logseq-encode-garden/blob/main/pages/GitP___How%20To___Draft%20an%20Episode%20from%20Session%20Assets.md) for raw recordings, including sessions with no prepared MP3. Create the garden episode entity, capture the saved MicroFreak presets, upload the MP3 and presets, and enrich the episode from MIDI and manuals. Keep technical inventories on the session asset page. - - TODO Follow [Prepare podcast metadata for Gitpa](https://github.com/codekiln/logseq-encode-garden/blob/main/pages/GitP___How%20To___Prepare%20Podcast%20Metadata.md) after converting the exported WAV to MP3. Import the episode fields into Gitpa and prepare the publication PR. The human merges it after giving the one recording a go. - - TODO Replace [gitp-acolyte’s recording-moving functions](https://github.com/codekiln/gitpa/blob/main/gitp-acolyte/gitp_acolyte/ceremonial/spells/recording/files/pub_rec_files.py) as the [garden media tasks](https://github.com/codekiln/logseq-encode-garden/tree/main/mise-tasks/gitpa/media) cover their uses. Inventory [the historical ceremony downloads](https://github.com/codekiln/gitpa/tree/main/gitp-garden/assets/Ceremony) and verify their replacements before updating working public links. + - Maintain each episode as a garden session with linked asset pages, mirror it into Gitpa, and publish through a Gitpa PR. [[GitP/How To/Draft an Episode from Session Assets]] describes the recording and listening steps; [[GitP/How To/Prepare Podcast Metadata]] describes preparation and sync. + - ## September 24 publication + - DONE Prepare [GitP.26.09.24 publication PR #12](https://github.com/codekiln/gitpa/pull/12), including the MP3 and RSS release identity. + - TODO Listen to [GitP.26.09.24 — episode MP3, about 13 minutes](https://f005.backblazeb2.com/file/logseq-encode-garden/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full.mp3) and merge [publication PR #12](https://github.com/codekiln/gitpa/pull/12) for a go decision. + - ## One source for episode information + - DONE Prepare [garden source PR #178](https://github.com/codekiln/logseq-encode-garden/pull/178) and [Gitpa publication PR #12](https://github.com/codekiln/gitpa/pull/12): four session pages, matching asset proxies, and RSS from the pages. + - TODO Merge the garden source PR first, then the Gitpa publication PR after the September 24 listening decision. + - ## Unattended assets on the Mac + - DONE Configure the encrypted local B2 credential cache in [[GitP/A/Log/26/10/04 Sun/Fnox/Plan]]. + - DONE Prepare [cached media upload PR #180](https://github.com/codekiln/logseq-encode-garden/pull/180), using page-derived filenames and verified Fnox cache access without Touch ID. + - ## Collect source recordings + - DONE Verify DVC restore using the September 24 MP3 in [[GitP/A/Log/26/10/04 Sun/Dvc for Knowledge Gardens/Plan]]. + - DONE Prepare [DVC asset tasks PR #179](https://github.com/codekiln/logseq-encode-garden/pull/179), including add, fetch, push, status, and tracked WAV-to-MP3 conversion. + - DOING Collect and restore the September 24 Ableton project in the private garden, using [private storage PR #1](https://github.com/codekiln/logseq-garden/pull/1). + - ## Later episode preparation + - TODO Automate MicroFreak capture and renaming, then MIDI enrichment, following [[GitP/How To/Draft an Episode from Session Assets]]. diff --git a/pages/GitP___A___Log___26___10___05 Mon - simplify gitpa garden proxies___Plan.md b/pages/GitP___A___Log___26___10___05 Mon - simplify gitpa garden proxies___Plan.md index a5e0dcc41..4c6625ae7 100644 --- a/pages/GitP___A___Log___26___10___05 Mon - simplify gitpa garden proxies___Plan.md +++ b/pages/GitP___A___Log___26___10___05 Mon - simplify gitpa garden proxies___Plan.md @@ -5,6 +5,7 @@ - Gitpa lacks the proxied Asset/B2, Podcast/Episode, and Proxy/Page definitions. Its sync command follows embeds and GitP asset links, but does not carry entity definitions or task implementations. The current Proxy/Page definition describes manual syncing without a reusable script. - [Mise Task](https://github.com/codekiln/logseq-encode-garden/blob/main/pages/Logseq___Entity___Mise___Task.md) already exists as `Logseq/Entity/Mise/Task`. Extend this entity to cover portable implementations and their dependencies. - The review also reports [clutter, failed audio/GIF rendering, and unavailable source pages](https://github.com/codekiln/gitpa/pull/12#discussion_r4182600404), plus a [broken homepage query](https://github.com/codekiln/gitpa/pull/12#discussion_r4182628597). The query includes `[(get ?props :public) true]`, which treats `true` as a result binding. Asset links currently name the public `logseq-encode-garden` bucket. Check anonymous asset access to determine whether hosting or rendering causes the media failures. + - Anonymous checks on October 5 returned `200` for every episode asset, `audio/mpeg` for recordings, and `image/gif` for artwork. MP3 range requests returned `206` with `Content-Range`. The session assets use the public S3 endpoint `https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/`, retaining their page-derived filenames. Artwork loads in the browser; embedded audio playback still needs verification. - ## Reusable proxy task - Follow [[Logseq/Entity/Log/26/10/05 Mon - Mise Tasks Accompany Logseq Proxies/Plan]] for the shared entity-aware sync task. Start from Gitpa's existing sync implementation, preserving its collision checks, destination properties, protected tags, exact names, asset handling, and validation before writes. - Provide `mise run logseq:entity:proxy:page:sync` as a file task. Document it on the Proxy/Page definition and on a task reference page, with links in both directions between documentation and implementation. Declare the task's entry point, helper files, tool requirements, and required supporting pages. A destination receives a runnable implementation with its dependencies and source provenance; refreshing updates the copied implementation as well as its documentation. Detect conflicting local tasks before replacing files. diff --git a/pages/GitP___A___Session___24___11___19-Tue.md b/pages/GitP___A___Session___24___11___19-Tue.md new file mode 100644 index 000000000..0a382e3ce --- /dev/null +++ b/pages/GitP___A___Session___24___11___19-Tue.md @@ -0,0 +1,12 @@ +logseq-entity:: [[Logseq/Entity/Podcast/Episode]] +created-by:: [[Person/codekiln]] +date-created:: [[2024-11-19 Tue]] +- # GitP.24.11.19 + - A MicroFreak session recorded on November 19, 2024. + - {{embed [[GitP/A/Session/24/11/19-Tue/Asset/Synth/Full/mp3]]}} + - {{embed [[GitP/A/Session/24/11/19-Tue/Asset/Artwork/Overview/gif]]}} + - ## Files + - MicroFreak presets + - {{embed [[GitP/A/Session/24/11/19-Tue/Asset/Preset/A/mfpz]]}} + - {{embed [[GitP/A/Session/24/11/19-Tue/Asset/Preset/B/mfpz]]}} + - {{embed [[GitP/A/Session/24/11/19-Tue/Asset/Preset/C/mfpz]]}} diff --git a/pages/GitP___A___Session___24___11___19-Tue___Asset___Artwork___Overview___gif.md b/pages/GitP___A___Session___24___11___19-Tue___Asset___Artwork___Overview___gif.md new file mode 100644 index 000000000..7d6664172 --- /dev/null +++ b/pages/GitP___A___Session___24___11___19-Tue___Asset___Artwork___Overview___gif.md @@ -0,0 +1,3 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +- # GitP.24.11.19 artwork + - ![GitP.24.11.19 artwork](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___11___19-Tue___Asset___Artwork___Overview.gif) diff --git a/pages/GitP___A___Session___24___11___19-Tue___Asset___Preset___A___mfpz.md b/pages/GitP___A___Session___24___11___19-Tue___Asset___Preset___A___mfpz.md new file mode 100644 index 000000000..ac29b1694 --- /dev/null +++ b/pages/GitP___A___Session___24___11___19-Tue___Asset___Preset___A___mfpz.md @@ -0,0 +1,3 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +- # GitP.24.11.19 preset A + - [Download MicroFreak preset A](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___11___19-Tue___Asset___Preset___A.mfpz) diff --git a/pages/GitP___A___Session___24___11___19-Tue___Asset___Preset___B___mfpz.md b/pages/GitP___A___Session___24___11___19-Tue___Asset___Preset___B___mfpz.md new file mode 100644 index 000000000..7d4f0c70f --- /dev/null +++ b/pages/GitP___A___Session___24___11___19-Tue___Asset___Preset___B___mfpz.md @@ -0,0 +1,3 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +- # GitP.24.11.19 preset B + - [Download MicroFreak preset B](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___11___19-Tue___Asset___Preset___B.mfpz) diff --git a/pages/GitP___A___Session___24___11___19-Tue___Asset___Preset___C___mfpz.md b/pages/GitP___A___Session___24___11___19-Tue___Asset___Preset___C___mfpz.md new file mode 100644 index 000000000..17adabe2e --- /dev/null +++ b/pages/GitP___A___Session___24___11___19-Tue___Asset___Preset___C___mfpz.md @@ -0,0 +1,3 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +- # GitP.24.11.19 preset C + - [Download MicroFreak preset C](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___11___19-Tue___Asset___Preset___C.mfpz) diff --git a/pages/GitP___A___Session___24___11___19-Tue___Asset___Synth___Full___mp3.md b/pages/GitP___A___Session___24___11___19-Tue___Asset___Synth___Full___mp3.md new file mode 100644 index 000000000..2745603bc --- /dev/null +++ b/pages/GitP___A___Session___24___11___19-Tue___Asset___Synth___Full___mp3.md @@ -0,0 +1,4 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +duration-seconds:: 422.752313 +- # GitP.24.11.19 MP3 + - ![GitP.24.11.19 — episode recording](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___11___19-Tue___Asset___Synth___Full.mp3) diff --git a/pages/GitP___A___Session___24___12___04-Wed.md b/pages/GitP___A___Session___24___12___04-Wed.md new file mode 100644 index 000000000..11fb61485 --- /dev/null +++ b/pages/GitP___A___Session___24___12___04-Wed.md @@ -0,0 +1,15 @@ +logseq-entity:: [[Logseq/Entity/Podcast/Episode]] +created-by:: [[Person/codekiln]] +date-created:: [[2024-12-04 Wed]] +- # GitP.24.12.04 + - A MicroFreak session recorded on December 4, 2024. + - {{embed [[GitP/A/Session/24/12/04-Wed/Asset/Synth/Full/mp3]]}} + - {{embed [[GitP/A/Session/24/12/04-Wed/Asset/Artwork/Overview/gif]]}} + - ## Files + - MicroFreak presets + - {{embed [[GitP/A/Session/24/12/04-Wed/Asset/Preset/A/mfpz]]}} + - {{embed [[GitP/A/Session/24/12/04-Wed/Asset/Preset/B/mfpz]]}} + - {{embed [[GitP/A/Session/24/12/04-Wed/Asset/Preset/C/mfpz]]}} + - {{embed [[GitP/A/Session/24/12/04-Wed/Asset/Preset/D/mfpz]]}} + - MicroFreak MIDI + - {{embed [[GitP/A/Session/24/12/04-Wed/Asset/MIDI/Microfreak/mid]]}} diff --git a/pages/GitP___A___Session___24___12___04-Wed___Asset___Artwork___Overview___gif.md b/pages/GitP___A___Session___24___12___04-Wed___Asset___Artwork___Overview___gif.md new file mode 100644 index 000000000..dc5ce2bdf --- /dev/null +++ b/pages/GitP___A___Session___24___12___04-Wed___Asset___Artwork___Overview___gif.md @@ -0,0 +1,3 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +- # GitP.24.12.04 artwork + - ![GitP.24.12.04 artwork](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___12___04-Wed___Asset___Artwork___Overview.gif) diff --git a/pages/GitP___A___Session___24___12___04-Wed___Asset___MIDI___Microfreak___mid.md b/pages/GitP___A___Session___24___12___04-Wed___Asset___MIDI___Microfreak___mid.md new file mode 100644 index 000000000..4f2276567 --- /dev/null +++ b/pages/GitP___A___Session___24___12___04-Wed___Asset___MIDI___Microfreak___mid.md @@ -0,0 +1,3 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +- # GitP.24.12.04 MicroFreak MIDI + - [Download MicroFreak MIDI](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___12___04-Wed___Asset___MIDI___Microfreak.mid) diff --git a/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___A___mfpz.md b/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___A___mfpz.md new file mode 100644 index 000000000..942804e1a --- /dev/null +++ b/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___A___mfpz.md @@ -0,0 +1,3 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +- # GitP.24.12.04 preset A + - [Download MicroFreak preset A](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___12___04-Wed___Asset___Preset___A.mfpz) diff --git a/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___B___mfpz.md b/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___B___mfpz.md new file mode 100644 index 000000000..4ce40cb63 --- /dev/null +++ b/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___B___mfpz.md @@ -0,0 +1,3 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +- # GitP.24.12.04 preset B + - [Download MicroFreak preset B](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___12___04-Wed___Asset___Preset___B.mfpz) diff --git a/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___C___mfpz.md b/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___C___mfpz.md new file mode 100644 index 000000000..1ebc1aac0 --- /dev/null +++ b/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___C___mfpz.md @@ -0,0 +1,3 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +- # GitP.24.12.04 preset C + - [Download MicroFreak preset C](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___12___04-Wed___Asset___Preset___C.mfpz) diff --git a/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___D___mfpz.md b/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___D___mfpz.md new file mode 100644 index 000000000..2690d28af --- /dev/null +++ b/pages/GitP___A___Session___24___12___04-Wed___Asset___Preset___D___mfpz.md @@ -0,0 +1,3 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +- # GitP.24.12.04 preset D + - [Download MicroFreak preset D](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___12___04-Wed___Asset___Preset___D.mfpz) diff --git a/pages/GitP___A___Session___24___12___04-Wed___Asset___Synth___Full___mp3.md b/pages/GitP___A___Session___24___12___04-Wed___Asset___Synth___Full___mp3.md new file mode 100644 index 000000000..b15d554a6 --- /dev/null +++ b/pages/GitP___A___Session___24___12___04-Wed___Asset___Synth___Full___mp3.md @@ -0,0 +1,4 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +duration-seconds:: 2872.065669 +- # GitP.24.12.04 MP3 + - ![GitP.24.12.04 — episode recording](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___24___12___04-Wed___Asset___Synth___Full.mp3) diff --git a/pages/GitP___A___Session___26___09___24-Thu.md b/pages/GitP___A___Session___26___09___24-Thu.md new file mode 100644 index 000000000..b9069e847 --- /dev/null +++ b/pages/GitP___A___Session___26___09___24-Thu.md @@ -0,0 +1,10 @@ +logseq-entity:: [[Logseq/Entity/Podcast/Episode]] +created-by:: [[Person/codekiln]] +date-created:: [[2026-09-24 Thu]] +- # GitP.26.09.24 + - A MicroFreak and Launchpad session beginning with two-operator FM on an initialized patch. + - {{embed [[GitP/A/Session/26/09/24-Thu/Asset/Synth/Full/mp3]]}} + - ## Notes + - [[Making/Music/Log/26/09/24 Thu - GitP Reboot with Novation Launchpad]] + - Ableton tracks: commentary, microfreak, Mixed Output all, and Microfreak Midi all. + - Ableton MIDI clips contain no program-change values identifying presets. diff --git a/pages/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full___mp3.md b/pages/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full___mp3.md index c5757b1e9..75ad1b193 100644 --- a/pages/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full___mp3.md +++ b/pages/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full___mp3.md @@ -1,4 +1,4 @@ logseq-entity:: [[Logseq/Entity/Asset/B2]] duration-seconds:: 757.761066 -- # September 24 Episode MP3 - - ![GitP.26.09.24 — episode recording](https://f005.backblazeb2.com/file/logseq-encode-garden/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full.mp3) +- # GitP.26.09.24 MP3 + - ![GitP.26.09.24 — episode recording](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full.mp3) diff --git a/pages/GitP___A___Session___26___09___24-Thu___Session Assets.md b/pages/GitP___A___Session___26___09___24-Thu___Session Assets.md index a6947270e..17f2e4be5 100644 --- a/pages/GitP___A___Session___26___09___24-Thu___Session Assets.md +++ b/pages/GitP___A___Session___26___09___24-Thu___Session Assets.md @@ -1,68 +1,3 @@ -# Session Assets - - TODO this page was created without human oversight and does NOT represent human preferences about a useful page structure for GitP. Either remove or adapt. --codekiln - - Episode: GitP.26.09.24 - - [Podcast metadata JSON](../assets/GitP/Session/2026/09/24/handoff.json) - - Session: [[GitP/A/Session/26/09/24-Thu]] - - [[GitP/A/Session/26/09/24-Thu/Asset/Synth/Full/mp3]] — listen to the episode recording. - - Proposed description: A MicroFreak and Launchpad session. - - ## Recordings and notes - - Source note: [[Making/Music/Log/26/09/24 Thu - GitP Reboot with Novation Launchpad]]. - - Prepared MP3: `GitP.26.09.24.mp3`, 12.6 minutes. - - Ableton tracks: commentary (audio), microfreak (audio), Mixed Output all (audio), Microfreak Midi all (MIDI). - - Session note: On the Microfreak, started with Two Op.FM on initialized patch. - - Session note: I was using the Session Mode the whole time in Session, with three audio tracks. - - Release audio: [GitP.26.09.24.mp3](https://f005.backblazeb2.com/file/logseq-encode-garden/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full.mp3); length and opening range match the prepared MP3. - - [Recording-day journal](https://github.com/codekiln/logseq-encode-garden/blob/main/journals/2026_09_24.md) links the session note and nearby work. - - [MicroFreak manual: Two Op.FM](https://github.com/codekiln/logseq-encode-garden/blob/main/pages/Microfreak___UG___06%20Dig%20Osc___03%20Types___08%20Two%20Op.FM.md) is linked from the session note. - - Commentary file (filename and duration match): `commentary 0010 [2026-09-24 190403].aif`, 12.6 minutes. - - [Earlier commentary transcription assessment · Gitpa PR #9](https://github.com/codekiln/gitpa/pull/9): repeated phrases suggested speech-recognition hallucination; recognized speech was excluded from episode copy. - - ## Available files - - Ableton set: `Backup/GitP26.09.24 Project [2026-09-24 190244].als`. - - Ableton set: `Backup/GitP26.09.24 Project [2026-09-24 191657].als`. - - Ableton set: `Backup/GitP26.09.24 Project [2026-09-25 181235].als`. - - MP3 audio: `GitP.26.09.24.mp3`. - - Ableton set: `GitP26.09.24 Project.als`. - - WAV audio: `GitP26.09.24 Project.wav`. - - AIFF audio: `Samples/Recorded/commentary 0004 [2026-09-24 190213].aif`. - - AIFF audio: `Samples/Recorded/commentary 0004 [2026-09-24 190313].aif`. - - AIFF audio: `Samples/Recorded/commentary 0005 [2026-09-24 190319].aif`. - - AIFF audio: `Samples/Recorded/commentary 0006 [2026-09-24 190322].aif`. - - AIFF audio: `Samples/Recorded/commentary 0007 [2026-09-24 190331].aif`. - - AIFF audio: `Samples/Recorded/commentary 0008 [2026-09-24 190332].aif`. - - AIFF audio: `Samples/Recorded/commentary 0009 [2026-09-24 190357].aif`. - - AIFF audio: `Samples/Recorded/commentary 0010 [2026-09-24 190403].aif`. - - AIFF audio: `Samples/Recorded/intermediate out 0001 [2026-09-24 190234].aif`. - - AIFF audio: `Samples/Recorded/intermediate out 0001 [2026-09-24 190313].aif`. - - AIFF audio: `Samples/Recorded/intermediate out 0002 [2026-09-24 190319].aif`. - - AIFF audio: `Samples/Recorded/intermediate out 0003 [2026-09-24 190322].aif`. - - AIFF audio: `Samples/Recorded/intermediate out 0004 [2026-09-24 190331].aif`. - - AIFF audio: `Samples/Recorded/intermediate out 0005 [2026-09-24 190332].aif`. - - AIFF audio: `Samples/Recorded/intermediate out 0006 [2026-09-24 190357].aif`. - - AIFF audio: `Samples/Recorded/intermediate out 0007 [2026-09-24 190403].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0001 [2026-09-24 184029].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0001 [2026-09-24 190559].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0001 [2026-09-24 190901].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0002 [2026-09-24 185643].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0002 [2026-09-24 190313].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0002 [2026-09-24 191156].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0003 [2026-09-24 190319].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0004 [2026-09-24 190322].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0005 [2026-09-24 190331].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0006 [2026-09-24 190332].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0007 [2026-09-24 190357].aif`. - - AIFF audio: `Samples/Recorded/microfreak 0009 [2026-09-24 191304].aif`. - - ## Review notes - - Patch exports: 0; MIDI exports: 0. Ableton track names alone do not establish preset changes. - - Ableton MIDI clips have no program-change values that identify presets. - - The proposed description is supported by the source note. - - ## Episode draft - - Proposed title: GitP.26.09.24. - - Proposed description: A MicroFreak and Launchpad session beginning with two-operator FM on an initialized patch. - - The source note supports this description. Commentary transcription remains excluded because its repeated phrases were unreliable. - - ## Episode approval - - The prepared `GitP.26.09.24.mp3` is the episode recording. Technical file and public-URL checks pass; publication approval remains pending. - - TODO Listen to [GitP.26.09.24 — prepared MP3](https://f005.backblazeb2.com/file/logseq-encode-garden/GitP___A___Session___26___09___24-Thu___Asset___Synth___Full.mp3) and give this recording a go or a no-go for publication. - - ## Publication - - On [[2026-10-04 Sun]], the public MP3 passed media type, length, opening-range, and Backblaze checksum checks against the prepared local file. - - Re-importing the garden JSON preserved the existing [Gitpa episode draft](https://github.com/codekiln/gitpa/blob/main/gitp-garden/pages/Ceremony___2026___09___24.md) and its description. The feed check and importer/feed tests pass. - - The episode remains unpublished. Publication requires a Gitpa PR for the human to merge after listening to the episode MP3. \ No newline at end of file +- # Session Assets + - DONE this page was created without human oversight and does NOT represent human preferences about a useful page structure for GitP. Either remove or adapt. --codekiln + - [[GitP/A/Session/26/09/24-Thu]] contains the episode description, recording, and session notes. diff --git a/pages/GitP___A___Session___26___09___25-Fri.md b/pages/GitP___A___Session___26___09___25-Fri.md new file mode 100644 index 000000000..e67210847 --- /dev/null +++ b/pages/GitP___A___Session___26___09___25-Fri.md @@ -0,0 +1,8 @@ +logseq-entity:: [[Logseq/Entity/Podcast/Episode]] +created-by:: [[Person/codekiln]] +date-created:: [[2026-09-25 Fri]] +- # GitP.26.09.25 + - MicroFreak SAWX and WaveUser preset 1910, with a Novation Launchpad sequence. + - {{embed [[GitP/A/Session/26/09/25-Fri/Asset/Synth/Full/mp3]]}} + - ## Notes + - [[Music/Composition/Log/26/09/25 Fri]] diff --git a/pages/GitP___A___Session___26___09___25-Fri___Asset___Synth___Full___mp3.md b/pages/GitP___A___Session___26___09___25-Fri___Asset___Synth___Full___mp3.md new file mode 100644 index 000000000..01229f9cc --- /dev/null +++ b/pages/GitP___A___Session___26___09___25-Fri___Asset___Synth___Full___mp3.md @@ -0,0 +1,4 @@ +logseq-entity:: [[Logseq/Entity/Asset/B2]] +duration-seconds:: 982.009977 +- # GitP.26.09.25 MP3 + - ![GitP.26.09.25 — episode recording](https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/GitP___A___Session___26___09___25-Fri___Asset___Synth___Full.mp3) diff --git a/pages/GitP___How To___Draft an Episode from Session Assets.md b/pages/GitP___How To___Draft an Episode from Session Assets.md index 9e5595b15..76fdb7338 100644 --- a/pages/GitP___How To___Draft an Episode from Session Assets.md +++ b/pages/GitP___How To___Draft an Episode from Session Assets.md @@ -8,7 +8,7 @@ see-also:: [[GitP/House/Back]], [[GitP/How To/Prepare Podcast Metadata]] - Ask the AI assistant to prepare the episode from that log entry. The exported WAV is the episode recording. - ## What the AI prepares - ### Episode page and audio - - The AI creates a GitP episode page using [[Logseq/Entity/Podcast/Episode]], linking the music-making log and its project, recording, and other source pointers. + - The AI creates the session episode page using [[Logseq/Entity/Podcast/Episode]], linking the music-making log and its project, recording, and other source pointers. - The AI converts the exported WAV to an MP3, uploads it to Backblaze B2 at a stable, readable URL, and verifies that the uploaded file matches the prepared MP3. - ### MicroFreak presets - The AI identifies the saved MicroFreak presets used in the episode, connects to the instrument, backs them up, renames them for the episode, and downloads the renamed presets. @@ -18,12 +18,18 @@ see-also:: [[GitP/House/Back]], [[GitP/How To/Prepare Podcast Metadata]] - It correlates recorded MIDI CC messages with the MicroFreak manual and reads the downloaded presets for oscillator type, preset category, and other supported details. The episode page links the sources for those details. - The AI distinguishes recorded changes from the settings in a saved preset. It leaves unknown details out of the episode description. - ### Publication PR - - The AI fills in the episode's MP3 and preset links in this garden, then prepares a Gitpa PR containing the episode page and podcast metadata needed for publication. + - The AI fills in the episode's MP3 and preset links in this garden, then mirrors those pages into Gitpa and prepares a publication PR. - The PR includes a direct link to the one episode MP3, its episode title, and a short description so the recording is easy to find and approve. + - ## Check public media + - GitP uses the public S3 download endpoint `https://s3.us-east-005.backblazeb2.com/logseq-encode-garden/`. [Backblaze supports bucket names in the URL path](https://www.backblaze.com/docs/en/cloud-storage-call-the-s3-compatible-api). + - Open the asset page’s URL without signing in. An MP3 should return `audio/mpeg`; artwork should return its image format, such as `image/gif`. + - Request a small MP3 range with `curl --range 0-1023 --dump-header - --output /dev/null `. A seekable recording returns `206` with a `Content-Range` header. + - In Gitpa’s preview, play and seek the recording, load the artwork, and follow preset and MIDI download links. + - If the download host fails DNS resolution, compare its DNS answer through another resolver before changing the asset URL. A DNS failure alone says nothing about bucket access. - ## Listen and publish - Listen to the episode MP3 once and give it a go or a no-go. - For a go, merge the Gitpa PR. Gitpa's CI publishes the episode after the merge. - For a no-go, tell the AI to hold the episode. Any changes to the recording follow that decision. - ## AI execution references - The `gitp-asset-gathering` skill describes the assistant's responsibilities. [[GitP/How To/Prepare Podcast Metadata]] covers the existing metadata commands. - - The metadata export and Gitpa importer are available. Automatic preset capture, renaming, and MIDI enrichment still need implementation; they are part of the intended workflow above. + - Session-page sync and RSS generation are available. Automatic preset capture, renaming, and MIDI enrichment still need implementation; they are part of the intended workflow above. diff --git a/pages/GitP___How To___Prepare Podcast Metadata.md b/pages/GitP___How To___Prepare Podcast Metadata.md index 84aff4801..41cbefe63 100644 --- a/pages/GitP___How To___Prepare Podcast Metadata.md +++ b/pages/GitP___How To___Prepare Podcast Metadata.md @@ -1,44 +1,15 @@ logseq-entity:: [[Logseq/Entity/Diataxis/How To]] -see-also:: [[GitP/mise/Task/episode/draft]] - +see-also:: [[GitP/mise/Task/episode/sync]], [[GitP/How To/Draft an Episode from Session Assets]] - # Prepare Podcast Metadata - - This is a technical reference for the AI assistant preparing a GitP episode. The recording and publication steps for the human are in [[GitP/How To/Draft an Episode from Session Assets]]. - - ## Overview - - After converting the exported episode WAV to MP3, collect the garden note, Ableton track names, MP3 checksum, on a session assets page. Export selected episode fields as JSON for the podcast website to import. - - The JSON supplies a proposed title and description. A public MP3 URL adds verified enclosure metadata. Gitpa keeps the final copy, episode page, GUID, publication time, and publication decision. - - Start with [[GitP/How To/Draft an Episode from Session Assets]] when only raw recordings and notes are available. - - ## When the export helps - - For the September 24 recording, the session note supplies the recording date and description; the prepared MP3 supplies the file size. The JSON lets Gitpa import those fields without retyping them. - - If a recording has no release MP3 yet, convert the exported WAV first. A JSON export is not needed to keep notes or draft copy in the garden. - - A permanent uploaded MP3 URL can be checked against the local file before adding its URL, byte length, and media type to the podcast feed. - - ## Prerequisites - - An Ableton project directory whose name contains the recording date as `YY.MM.DD`, a prepared MP3, and a session note in this garden's `pages/` directory. - - [[mise]] installs the Python and FFmpeg versions declared by the preparation task. Optional transcription declares its own MLX Whisper and uv dependencies when it runs. - - The project contains one `.als` set. The default MP3 name is `GitP.YY.MM.DD.mp3` in the project directory; `--mp3` selects another file. - - ## Steps - - ### 1. Prepare the session asset page and JSON - - Run [[GitP/mise/Task/episode/draft]] on the session directory: - - ~~~sh - mise run gitpa:episode:draft -- '/path/to/GitP26.09.24 Project' - ~~~ - - The task creates `GitP/A/Session/26/09/24-Thu/Session Assets` as an LFM page and `assets/GitP/Session/2026/09/24/handoff.json`. - - If several source notes match the date, pass `--note pages/.md`. An explicit `Description:` bullet supplies the proposed description; otherwise the device heading supplies a short proposal. - - The JSON contains `recorded_on`, `episode_title`, and `description`. Track names, observations, and source links stay on the session asset page for review. - - ### 2. Attach a public MP3 if available - - Add `--audio-url https://example.com/episode.mp3` to the preparation command. Use a permanent HTTPS URL. - - The task checks media type, length, and the opening audio range against the prepared MP3, plus the Backblaze SHA-1 when supplied. Successful verification adds `audio_url`, `audio_length`, and `audio_type` to the JSON. The session asset page records the local SHA-256. - - If speech recognition is useful, pass `--transcript /path/to/whisper.json` to assess existing output. On Apple Silicon macOS, `--transcribe` invokes [[GitP/mise/Task/episode/transcribe]] to run local MLX Whisper on a matching commentary stem; `--model` selects the model. Check recognized speech against the recording before using it in public copy. - - ### 3. Review and import the JSON - - Check the session assets against the source log and prepared MP3 metadata. [[GitP/A/Session/26/09/24-Thu/Session Assets]] and [its JSON export](../assets/GitP/Session/2026/09/24/handoff.json) provide an example. - - From the Gitpa checkout, import the JSON: - - ~~~sh - mise run episode:import -- /path/to/garden/assets/GitP/Session/2026/09/24/handoff.json - ~~~ - - The importer creates an unpublished draft, preserves existing episode copy and publication identity, and rejects conflicting media metadata. Prepare and check the publication changes in a Gitpa PR. The human listens to the linked episode MP3 and merges that PR to publish. - - ## Troubleshooting - - An existing session asset page or JSON export stops preparation before inspection. For a new comparison, choose fresh destinations for both outputs: - - ~~~sh - mise run gitpa:episode:draft -- '/path/to/GitP26.09.24 Project' --assets-page 'pages/GitP___Session___26___09___24 Thu___Session Assets Comparison.md' --output-dir assets/GitP/Session/2026/09/24/comparison - ~~~ - - The session asset page must be directly under the garden's `pages/` directory. `--output-dir` selects the JSON destination. Source recordings and existing page content remain intact. - - If public-media verification fails, check the uploaded object's length, content type, range support, and identity against the local MP3 before preparing another JSON export. \ No newline at end of file + - Prepare the episode in this garden, then mirror it into [[Person/codekiln/GitHub/gitpa]] for publication. The session page supplies the episode text; its linked asset page supplies the recording. + - ## Prepare the session + - Create or update the recording's [[Logseq/Entity/Podcast/Episode]] page under `GitP/A/Session/YY/MM/DD-Day`. Link the music-making log and add the supported session details, preset downloads, and recording asset. + - Give the page one H1 episode title and a short description as its first child. Embed the MP3 asset page beneath it. [[GitP/A/Session/26/09/24-Thu]] is an example. + - Follow [[Logseq/Entity/Asset]] for the recording's asset page. The audio link belongs there; episode pages embed that page. + - ## Mirror into Gitpa + - Run [[GitP/mise/Task/episode/sync]] from the Gitpa checkout with this garden's checkout path and the session page name. It copies the session body and embedded asset pages as [[Logseq/Entity/Proxy/Page]] instances. + - Edit episode text in this garden and sync again. Gitpa keeps the publication properties on its proxy; source text replaces the proxy body. + - ## Prepare the publication PR + - On the Gitpa proxy, set `public:: true`, `podcast-guid::`, and `podcast-published-at::`. The latter two identify the RSS release and retain their values after publication. `public::` is the publication switch. + - Run `mise run rss:build`, `mise run rss:check`, and `mise run rss:test` in Gitpa. RSS reads the proxy title, description, and recording link directly; enclosure size comes from the public media response. + - Open a Gitpa PR with the episode title, a direct MP3 link, and the reason to listen: decide whether to publish this recording. The human merges the PR after one listen and a go decision. diff --git a/pages/GitP___mise___Task___episode___draft.md b/pages/GitP___mise___Task___episode___draft.md deleted file mode 100644 index 04a0c6ac4..000000000 --- a/pages/GitP___mise___Task___episode___draft.md +++ /dev/null @@ -1,36 +0,0 @@ -logseq-entity:: [[Logseq/Entity/Mise/Task]] -task-owner:: [[Person/codekiln/GitHub/logseq-encode-garden]] -task-config-root:: . -task-name:: gitpa:episode:draft -source-link:: https://github.com/codekiln/logseq-encode-garden/blob/main/mise-tasks/gitpa/episode/draft -see-also:: [[GitP/How To/Prepare Podcast Metadata]], [[GitP/House/Back]] -- # Prepare Session Assets and Podcast Metadata - - After audio selection and MP3 preparation, collects the source note, Ableton track names, MP3 checksum, and optional transcription assessment for an episode review. Exports proposed episode copy and optional verified enclosure metadata for the Gitpa importer. - - Asset gathering and audio selection begin with [[GitP/How To/Draft an Episode from Session Assets]]. This task requires a prepared MP3 and does not choose recordings for inclusion. - - ## Invocation - - Runs from the `logseq-encode-garden` checkout: - - ~~~sh - mise run gitpa:episode:draft -- '/path/to/GitP26.09.24 Project' - ~~~ - - `--note` selects a source page explicitly. `--mp3` selects a prepared MP3 outside the project directory. `--audio-url` verifies a permanent public MP3 URL against the local file. - - ## Inputs and outputs - - Inputs: a project name containing `YY.MM.DD`, one Ableton `.als` set, a prepared MP3, and a session note in the garden's `pages/` directory. - - Output page: `GitP/Session/YY/MM/DD Day/Session Assets`, containing available files, source links, track names, checksum, observations, and recognition-quality findings when requested. - - Output asset: `assets/GitP/Session/YYYY/MM/DD/handoff.json`, containing `recorded_on`, `episode_title`, and `description`. A successful public-media check adds `audio_url`, `audio_length`, and `audio_type` together. - - The proposed description comes from a `Description:` source-note bullet or the source note's device heading. Listening review determines the final public copy. - - ## Side effects - - Creates a new session asset page and JSON export. Existing outputs stop preparation before inspection. `--assets-page` and `--output-dir` select fresh comparison destinations. - - Reads source recordings without moving them. `--audio-url` makes HTTP requests to verify media type, length, opening audio range, and Backblaze checksum when available. - - `--transcribe` invokes [[GitP/mise/Task/episode/transcribe]] for local speech recognition and may download Python dependencies and model files. It creates temporary audio for recognition; findings appear on the session asset page. - - ## Dependencies and access - - Mise supplies the task's declared Python and FFmpeg/ffprobe versions. The optional transcription task declares MLX Whisper and uv for Apple Silicon macOS; `--transcript` assesses an existing Whisper JSON file. - - Ordinary preparation needs local read access to the session files. Public-media verification uses the supplied HTTPS URL. This task prepares metadata; uploading media and publishing the episode are separate jobs. - - ## Failure and recovery - - Ambiguous source-note discovery: select the page with `--note`. - - Existing output: retain the edited page and JSON export, then select fresh output paths for a comparison. - - Media mismatch: compare the uploaded object with the prepared MP3 before producing another JSON export. - - ## Source and help - - [Preparation file task](https://github.com/codekiln/logseq-encode-garden/blob/main/mise-tasks/gitpa/episode/draft) - - ~~~sh - mise run gitpa:episode:draft --help - ~~~ diff --git a/pages/GitP___mise___Task___episode___sync.md b/pages/GitP___mise___Task___episode___sync.md new file mode 100644 index 000000000..6b2c71010 --- /dev/null +++ b/pages/GitP___mise___Task___episode___sync.md @@ -0,0 +1,27 @@ +logseq-entity:: [[Logseq/Entity/Mise/Task]] +task-owner:: [[Person/codekiln/GitHub/gitpa]] +task-config-root:: . +task-name:: episode:sync +source-link:: https://github.com/codekiln/gitpa/blob/main/mise-tasks/episode/sync +see-also:: [[GitP/How To/Prepare Podcast Metadata]] +- # Sync an Episode into Gitpa + - Mirrors a garden session and its asset pages into Gitpa as [[Logseq/Entity/Proxy/Page]] instances. Run it after preparing or editing an episode in this garden. + - ## Invocation + - From the Gitpa checkout: + - ~~~sh + mise run episode:sync -- --source /path/to/logseq-encode-garden 'GitP/A/Session/26/09/24-Thu' + ~~~ + - Use `--source` to select a review worktree when preparing a paired garden/Gitpa PR. + - ## Inputs and outputs + - The source session lives under `pages/`. Its embedded recording and linked asset pages are copied with identical filenames into Gitpa's `gitp-garden/pages/`. + - Each copy carries the source Logseq URL, code-forge URL, and sync date. Relative source assets are copied to matching destination paths. + - ## Side effects + - A new proxy starts with the source properties and body. Resync replaces the body and proxy properties while preserving Gitpa's other properties, including tags and publication identity. + - A destination page with the same name that is not a proxy stops the sync. Review that collision before changing either page. + - Public remote asset links remain links; this task uploads no media and publishes no website. + - ## Dependencies and recovery + - Mise supplies Python. Both repository checkouts must be available locally; source preparation and uploads happen before sync. + - Check reported missing source assets and repair their links or files. Run the sync again after updating the garden episode. + - ## Source and help + - [Episode sync task](https://github.com/codekiln/gitpa/blob/main/mise-tasks/episode/sync) + - `mise run episode:sync --help` diff --git a/pages/GitP___mise___Task___episode___test.md b/pages/GitP___mise___Task___episode___test.md deleted file mode 100644 index 878fdd5e4..000000000 --- a/pages/GitP___mise___Task___episode___test.md +++ /dev/null @@ -1,25 +0,0 @@ -logseq-entity:: [[Logseq/Entity/Mise/Task]] -task-owner:: [[Person/codekiln/GitHub/logseq-encode-garden]] -task-config-root:: . -task-name:: gitpa:episode:test -source-link:: https://github.com/codekiln/logseq-encode-garden/blob/main/mise-tasks/gitpa/episode/test -see-also:: [[GitP/mise/Task/episode/draft]] -- # Test Episode Preparation - - Checks episode metadata extraction, media verification, asset-page layout, argument handling, and preservation of existing outputs before changing the preparation task. - - ## Invocation - - Runs from the `logseq-encode-garden` checkout: - - ~~~sh - mise run gitpa:episode:test --verbose - ~~~ - - ## Inputs, outputs, and side effects - - Loads the preparation implementation and creates synthetic session fixtures in temporary directories. Network verification is mocked. - - Prints unittest results and exits unsuccessfully if a test fails. It creates no production page or handoff, uploads no media, and reads no real recording. - - ## Dependencies and access - - Mise supplies the declared Python version. Fixtures and mocks use Python's standard library. - - ## Failure and recovery - - A failing assertion identifies the behavior that changed. Compare that behavior with [[GitP/mise/Task/episode/draft]] before updating the implementation or its test expectations. - - ## Source and help - - [Preparation test file task](https://github.com/codekiln/logseq-encode-garden/blob/main/mise-tasks/gitpa/episode/test) - - ~~~sh - mise run gitpa:episode:test --help - ~~~ diff --git a/pages/GitP___mise___Task___episode___transcribe.md b/pages/GitP___mise___Task___episode___transcribe.md deleted file mode 100644 index f2fdaf004..000000000 --- a/pages/GitP___mise___Task___episode___transcribe.md +++ /dev/null @@ -1,25 +0,0 @@ -logseq-entity:: [[Logseq/Entity/Mise/Task]] -task-owner:: [[Person/codekiln/GitHub/logseq-encode-garden]] -task-config-root:: . -task-name:: gitpa:episode:transcribe -source-link:: https://github.com/codekiln/logseq-encode-garden/blob/main/mise-tasks/gitpa/episode/transcribe -see-also:: [[GitP/mise/Task/episode/draft]], [[GitP/How To/Draft an Episode from Session Assets]] -- # Transcribe a Session Recording - - Produces a candidate transcript for checking against the recording. Recognition output needs listening review before it supplies public episode copy. - - ## Invocation - - From the garden checkout on Apple Silicon macOS: - - ~~~sh - mise run gitpa:episode:transcribe -- /path/to/commentary.wav --output-dir /path/to/transcript - ~~~ - - `--model` selects a local model directory or MLX Whisper model. - - ## Inputs and outputs - - Input: a recording supported by MLX Whisper. Output: `commentary.json` in the selected output directory. - - [[GitP/mise/Task/episode/draft]] invokes this task through its optional `--transcribe` flag and assesses the temporary result for recognition errors. - - ## Dependencies and side effects - - Mise declares MLX Whisper as a pinned `pypi:mlx-whisper` tool. Mise supplies its isolated Python environment through uv when this task is invoked; ordinary metadata preparation and tests do not install Whisper. - - Recognition may download model files and writes transcript output. Source audio stays in place. - - ## Failure and help - - Requires Apple Silicon macOS. Missing recordings, unavailable models, and recognition failures stop transcription; metadata preparation can proceed without `--transcribe`. - - ~~~sh - mise run gitpa:episode:transcribe --help - ~~~ diff --git a/pages/Logseq___Entity___Log___26___10___05 Mon - Mise Tasks Accompany Logseq Proxies___Plan.md b/pages/Logseq___Entity___Log___26___10___05 Mon - Mise Tasks Accompany Logseq Proxies___Plan.md index 3e2c4ab3f..1a17592c2 100644 --- a/pages/Logseq___Entity___Log___26___10___05 Mon - Mise Tasks Accompany Logseq Proxies___Plan.md +++ b/pages/Logseq___Entity___Log___26___10___05 Mon - Mise Tasks Accompany Logseq Proxies___Plan.md @@ -2,7 +2,7 @@ - Proxying an entity instance brings along the entity definitions and executable tasks needed to use it in the destination garden. Running sync again updates those pages and task files from the same source. - ## Existing definitions - [[Logseq/Entity/Proxy/Page]] defines source discovery, page copying, frontmatter preservation, collision handling, and asset copying. [[Logseq/Entity/Proxy/Page/mise/Task/sync]] supplies the executable sync task. - - [[Logseq/Entity/Mise/Task]] defines task identity, ownership, invocation, dependencies, and source links. [[GitP/mise/Task/episode/draft]] is an existing instance. + - [[Logseq/Entity/Mise/Task]] defines task identity, ownership, invocation, dependencies, and source links. [[GitP/mise/Task/episode/sync]] is an existing instance. - ## Companion-task model - Extend [[Logseq/Entity/Mise/Task]] with the file-task requirements needed for proxying. Task instances use the established singular `logseq-entity::` property. - An entity definition declares its companion task pages through the `entity-tasks::` property. diff --git a/pages/Logseq___Entity___Mise___Task.md b/pages/Logseq___Entity___Mise___Task.md index b53f70377..1785e0546 100644 --- a/pages/Logseq___Entity___Mise___Task.md +++ b/pages/Logseq___Entity___Mise___Task.md @@ -8,7 +8,7 @@ see-also:: [[mise/Task]], [[Logseq/Entity/CLI/Command]], [[Logseq/Entity/Diataxi - Built-in mise subcommands such as `mise run` are [[Logseq/Entity/CLI/Command]] instances. Jobs invoked through them are Mise Task instances. - ## Naming and links - Task reference pages belong to the topic served by the job: `Topic/mise/Task/`. A namespace prefix already expressed by the topic can be omitted from the page name while remaining in `task-name::`. - - For example, `gitpa:episode:draft` lives at [[GitP/mise/Task/episode/draft]], and `gitpa:episode:test` at [[GitP/mise/Task/episode/test]]. Repository ownership remains explicit in frontmatter. + - For example, `episode:sync` lives at [[GitP/mise/Task/episode/sync]]. Repository ownership remains explicit in frontmatter. - If the topic contains distinct implementations of the same task name, add a repository or config-scope segment before the task path. Displayed invocation text retains the exact runnable name. - ## Frontmatter - `logseq-entity::` marks the page as a Mise Task instance. diff --git a/pages/Logseq___Entity___Podcast___Episode.md b/pages/Logseq___Entity___Podcast___Episode.md index cf7a29975..7925007ec 100644 --- a/pages/Logseq___Entity___Podcast___Episode.md +++ b/pages/Logseq___Entity___Podcast___Episode.md @@ -1,7 +1,7 @@ logseq-entity:: [[Logseq/Entity/Definition]] - # Podcast Episode - - In this garden, **Podcast Episode** pages model one episode of a [[Logseq/Entity/Podcast]], captured as first-class listening notes. + - In this garden, **Podcast Episode** pages model one episode of a [[Logseq/Entity/Podcast]], captured as listening notes or a recording prepared for publication. - ## What counts as a Podcast Episode - One specific episode of a show, usually with a title, a host, an air date, and often a URL of its own. - Not Podcast Episodes: the show itself, which is a [[Logseq/Entity/Podcast]]; the host's person hub; a guest's person hub; a [[Logseq/Entity/Quote]] pulled from an episode; a [[Logseq/Entity/Concept]] page that synthesizes ideas an episode discussed. @@ -12,8 +12,9 @@ logseq-entity:: [[Logseq/Entity/Definition]] - Episode pages live under the show's `Pod` namespace, dated: `Person//Pod// `, or `/Pod// ` for a show filed under its own name. [[Logseq/Entity/Podcast]] says where the show page goes. - On disk that is `pages/Person______Pod______ .md`. - `` is the two-digit year and `` the two-digit month of the episode's air date. + - A producer-owned episode recorded in a music session may use that session's dated path: `/A/Session///`. The session page holds the episode description, recording, and notes. For example, [[GitP/A/Session/26/09/24-Thu]]. - An episode that is also part of a named sequence is both a Podcast Episode and a [[Logseq/Entity/Series]] instance. Its path then adds `Series/` after `Pod`, as [[Logseq/Entity/Series]] describes. - - Pages filed before this definition existed use either `Pod` or `Podcast` as the namespace segment, and some put a `/` after the month instead of a space. New episode pages use `Pod` and the space. Leave existing pages as filed unless the author asks for a migration. + - Pages filed before this definition existed use either `Pod` or `Podcast` as the namespace segment, and some put a `/` after the month instead of a space. New listening-note pages use `Pod` and the space. Leave existing pages as filed unless the author asks for a migration. - ## Finding and deduplicating - Search in order: the episode URL; the YouTube video ID when there is a recording; the exact episode title; distinctive title words under `Person/*/Pod*` and `*/Pod*`; then host plus a distinctive phrase. Classify as existing, similar, new, or blocked. - One page per episode. @@ -23,12 +24,14 @@ logseq-entity:: [[Logseq/Entity/Definition]] - ## Frontmatter - Mark instances with **`logseq-entity:: [[Logseq/Entity/Podcast/Episode]]`** so this definition page collects backlinks to every episode. - Set **`created-by::`** to the host. - - Set **`date-created::`** to the air date when known. + - Set **`date-created::`** to the air date for listening notes, or the recording date for a producer-owned session. + - The publishing garden records feed identity with [[Logseq/Entity/Podcast/Episode/Frontmatter/podcast-guid]] and publication time with [[Logseq/Entity/Podcast/Episode/Frontmatter/podcast-published-at]]. - Optional: **`logseq-created-time-year::`** linking to the matching [[Logseq/Entity/Time/Year]] instance; see that page for how it differs from `date-created::`. - **Never add, remove, or edit a `tags::` line on an existing page.** Older episode pages carry assorted `tags::` values; those are the author's and stay as they are. - ## Page shape - Logseq Flavored Markdown. - The first body block is the H1 heading with the episode title linked to the episode when a URL is known: `- # [Episode Title](https://example.com/episode)`. Without a URL, use a plain `- # Episode Title`. + - A producer-owned session starts with a short episode description beneath the H1, followed by an embed of its recording's [[Logseq/Entity/Asset]] page. Notes and downloadable presets follow when available. - Recommended body sections are **Notes**, **Highlights**, and **Transcript**, kept only when there is something to put in them. - Prefer synthesis over pasting a full transcript unless the author asks for the full text. - ## Combined with YouTube diff --git a/pages/Logseq___Entity___Podcast___Episode___Frontmatter___podcast-guid.md b/pages/Logseq___Entity___Podcast___Episode___Frontmatter___podcast-guid.md new file mode 100644 index 000000000..79a0d28fb --- /dev/null +++ b/pages/Logseq___Entity___Podcast___Episode___Frontmatter___podcast-guid.md @@ -0,0 +1,7 @@ +logseq-entity:: [[Logseq/Entity/Frontmatter/Definition]] +- # `podcast-guid::` — episode identity in a podcast feed + - A stable, unique identifier retained across changes to an episode title, recording URL, or page name. + - ## Values + - `podcast-guid:: gitpa20260925`. + - ## Ownership + - The publishing garden stores this property on its [[Logseq/Entity/Podcast/Episode]] page. A [[Logseq/Entity/Proxy/Page]] preserves the property when its source body is synced. diff --git a/pages/Logseq___Entity___Podcast___Episode___Frontmatter___podcast-published-at.md b/pages/Logseq___Entity___Podcast___Episode___Frontmatter___podcast-published-at.md new file mode 100644 index 000000000..297c9c83b --- /dev/null +++ b/pages/Logseq___Entity___Podcast___Episode___Frontmatter___podcast-published-at.md @@ -0,0 +1,7 @@ +logseq-entity:: [[Logseq/Entity/Frontmatter/Definition]] +- # `podcast-published-at::` — episode publication time + - The date and time an episode entered its podcast feed, with an explicit UTC offset. + - ## Values + - `podcast-published-at:: 2026-10-03T12:00:00-04:00`. + - ## Ownership + - The publishing garden stores this property on its [[Logseq/Entity/Podcast/Episode]] page. A [[Logseq/Entity/Proxy/Page]] preserves the property when its source body is synced. diff --git a/pages/Music___Composition___Log___26___09___25 Fri.md b/pages/Music___Composition___Log___26___09___25 Fri.md index 7957c8a82..4ddadbc56 100644 --- a/pages/Music___Composition___Log___26___09___25 Fri.md +++ b/pages/Music___Composition___Log___26___09___25 Fri.md @@ -1,9 +1,7 @@ next:: [[Music/Composition/Log/26/09/26 Sat]] - # [[2026-09-25 Fri]] - - Podcast title: GitP.26.09.25 - - Description: MicroFreak SAWX and WaveUser preset 1910, with a Novation Launchpad sequence. - - Audio: [GitP.26.09.25.mp3](https://f005.backblazeb2.com/file/logseq-encode-garden/gitpa/episodes/2026-09-25/GitP.26.09.25.mp3) + - Episode: [[GitP/A/Session/26/09/25-Fri]]. - at first, using [[Microfreak/UG/06 Dig Osc/03 Types/15 SAWX]] - preset 397 - Imit - saved 1910 using - [[Microfreak/UG/06 Dig Osc/03 Types/17 WaveUser]]