1# weekly status maintenance via claude code 2# 3# two-phase workflow: 4# 1. workflow_dispatch: archives old STATUS.md sections, generates audio, opens PR 5# 2. on PR merge: uploads audio to plyr.fm 6# 7# required secrets: 8# ANTHROPIC_API_KEY - claude code 9# GOOGLE_API_KEY - gemini TTS (for audio generation) 10# PLYR_BOT_TOKEN - plyr.fm developer token (for audio upload) 11 12name: status maintenance 13 14on: 15 # TODO: restore schedule after testing 16 # schedule: 17 # - cron: "0 9 * * 1" # every monday 9am UTC 18 workflow_dispatch: 19 inputs: 20 skip_audio: 21 description: "skip audio generation" 22 type: boolean 23 default: false 24 pull_request: 25 types: [closed] 26 branches: [main] 27 28jobs: 29 # phase 1: archive + generate audio + open PR 30 maintain: 31 if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' 32 runs-on: ubuntu-latest 33 permissions: 34 contents: write 35 pull-requests: write 36 id-token: write 37 38 steps: 39 - uses: actions/checkout@v4 40 with: 41 fetch-depth: 0 42 43 - uses: astral-sh/setup-uv@v4 44 45 - uses: anthropics/claude-code-action@v1 46 with: 47 anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} 48 claude_args: | 49 --allowedTools "Read,Write,Edit,Bash,Fetch" 50 prompt: | 51 you are maintaining the plyr.fm (pronounced "player FM") project status file. 52 53 ## critical rules 54 55 1. STATUS.md MUST be kept under 500 lines. this is non-negotiable. 56 2. archive content MUST be moved to .status_history/, not deleted 57 3. podcast tone MUST be dry, matter-of-fact, slightly sardonic - NOT enthusiastic or complimentary 58 59 ## task 1: gather temporal context 60 61 run these commands: 62 ```bash 63 date 64 git log --oneline --since="1 week ago" 65 git log --oneline -30 66 ls -la .status_history/ 2>/dev/null || echo "no archive directory yet" 67 wc -l STATUS.md 68 ``` 69 70 determine: 71 - what is today's date? 72 - what shipped in the last week vs earlier? 73 - does .status_history/ exist? (this implies whether or not this is the first episode) 74 - how many lines is STATUS.md currently? 75 76 ## task 2: archive old sections (MANDATORY if over 250 lines) 77 78 if STATUS.md > 500 lines: 79 1. create .status_history/ directory if it doesn't exist 80 2. identify section boundaries (look for "---" separators and "### " headers with dates) 81 3. move OLDEST sections to .status_history/YYYY-MM.md (grouped by month) 82 4. compact the meaning of the original entire STATUS.md into about 500 lines or less 83 5. generally preserve the document structure (keep "## recent work" header, "## immediate priorities", etc) 84 6. do NOT summarize archived content - move it verbatim and organize it chronologically 85 86 so STATUS.md is the living overview, slightly recency biased, but a good general overview of the project. 87 88 .status_history/ is the archive of temporally specific sections of STATUS.md that are worth preserving for historical context, but not significant enought to be stated literally in STATUS.md in perpetuity. 89 90 VERIFY: run `wc -l STATUS.md` after archiving. it MUST be under 500 lines. 91 92 ## task 3: generate audio overview (if skip_audio is false) 93 94 skip_audio input: ${{ inputs.skip_audio }} 95 96 if skip_audio is false: 97 98 ### understand the project first 99 100 before writing the script, read: 101 - STATUS.md (the current state) 102 - docs/deployment/overview.md if it exists 103 - Fetch https://atproto.com/guides/overview to understand ATProto primitives 104 - Fetch https://atproto.com/guides/lexicon to understand NSIDs and lexicons 105 - fetch and read: https://overreacted.io/open-social/ to understand the vision of open social 106 107 this context helps you explain things accurately, and accessibly without over-simplifying. 108 109 ### determine episode type 110 111 check if .status_history/ directory exists: 112 - if NO .status_history/ exists: this is the INAUGURAL episode 113 - if .status_history/ exists: this is a SUBSEQUENT episode 114 115 ### write the podcast script 116 117 write to podcast_script.txt with "Host: ..." and "Cohost: ..." lines. 118 119 INAUGURAL EPISODE (ignore this if its not the first episode): 120 - introduce what plyr.fm is: decentralized music streaming on ATProto 121 - explain the core value prop: your music data lives in your PDS, portable between apps (open social) 122 - cover the technical foundation that's been built (summarize from STATUS.md) 123 - set expectations: this is an early-stage project, rough edges exist 124 - tone: "here's what is being built and why it could matter" 125 - work chronologically from beginning to end, don't heavily bias towards nascent or recent work 126 127 SUBSEQUENT EPISODES (ignore this if its the first episode): 128 - focus on what actually shipped since last episode 129 - use git history to determine timing ("last week" vs "earlier this month") 130 - don't re-explain the whole project - listeners already know 131 - tone: "here's what changed since last time" 132 133 ### tone requirements (CRITICAL) 134 135 the hosts should sound like two engineers who: 136 - are skeptical, amused and somewhat intruiged by the absurdity of building things 137 - acknowledge problems and limitations honestly 138 - don't over-use superlatives ("amazing", "incredible", "exciting") 139 - explain technical concepts through analogy, not hypey jargon 140 141 avoid excessive phrasing: 142 - "exciting", "amazing", "incredible", "impressive", "great job" 143 - "the team has done", "they've really", "fantastic work" 144 - any variation of over-congratulating or over-sensationalizing the project 145 146 pronunciation: "plyr.fm" is pronounced "player FM" (not "plir" or spelled out) 147 148 target length: 2-3 minutes spoken (~300-400 words) (it should be 4-5 if its the first episode) 149 150 ### generate audio 151 152 run: uv run scripts/generate_tts.py podcast_script.txt update.wav 153 then: rm podcast_script.txt 154 155 ## task 4: open PR 156 157 if any files changed: 158 1. git checkout -b status-maintenance-$(date +%Y%m%d) 159 2. git add .status_history/ STATUS.md update.wav 160 3. git commit -m "chore: weekly status maintenance" 161 4. git push -u origin status-maintenance-$(date +%Y%m%d) 162 5. gh pr create --title "chore: weekly status maintenance" --body "automated status archival and audio overview" 163 164 add detail as desired to the PR body and title. 165 add a label like "ai-generated" to the PR (create the label if it doesn't exist) 166 if nothing changed, report that no maintenance was needed. 167 168 env: 169 GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} 170 171 # phase 2: upload audio after PR merge 172 upload-audio: 173 if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'status-maintenance-') 174 runs-on: ubuntu-latest 175 176 steps: 177 - uses: actions/checkout@v4 178 179 - uses: astral-sh/setup-uv@v4 180 181 - name: Upload audio to plyr.fm 182 run: | 183 if [ -f update.wav ]; then 184 uv run --with plyrfm -- plyrfm upload update.wav "plyr.fm update - $(date +'%B %d, %Y')" --album "$(date +%Y)" -t ai 185 else 186 echo "No update.wav found, skipping upload" 187 fi 188 env: 189 PLYR_TOKEN: ${{ secrets.PLYR_BOT_TOKEN }}