# weekly status maintenance via claude code # # two-phase workflow: # 1. workflow_dispatch: archives old STATUS.md sections, generates audio, opens PR # 2. on PR merge: uploads audio to plyr.fm # # required secrets: # ANTHROPIC_API_KEY - claude code # GOOGLE_API_KEY - gemini TTS (for audio generation) # PLYR_BOT_TOKEN - plyr.fm developer token (for audio upload) name: status maintenance on: # TODO: restore schedule after testing # schedule: # - cron: "0 9 * * 1" # every monday 9am UTC workflow_dispatch: inputs: skip_audio: description: "skip audio generation" type: boolean default: false pull_request: types: [closed] branches: [main] jobs: # phase 1: archive + generate audio + open PR maintain: if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' runs-on: ubuntu-latest permissions: contents: write pull-requests: write id-token: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: astral-sh/setup-uv@v4 - uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} claude_args: | --allowedTools "Read,Write,Edit,Bash,Fetch" prompt: | you are maintaining the plyr.fm (pronounced "player FM") project status file. ## critical rules 1. STATUS.md MUST be kept under 500 lines. this is non-negotiable. 2. archive content MUST be moved to .status_history/, not deleted 3. podcast tone MUST be dry, matter-of-fact, slightly sardonic - NOT enthusiastic or complimentary ## task 1: gather temporal context run these commands: ```bash date git log --oneline --since="1 week ago" git log --oneline -30 ls -la .status_history/ 2>/dev/null || echo "no archive directory yet" wc -l STATUS.md ``` determine: - what is today's date? - what shipped in the last week vs earlier? - does .status_history/ exist? (this implies whether or not this is the first episode) - how many lines is STATUS.md currently? ## task 2: archive old sections (MANDATORY if over 250 lines) if STATUS.md > 500 lines: 1. create .status_history/ directory if it doesn't exist 2. identify section boundaries (look for "---" separators and "### " headers with dates) 3. move OLDEST sections to .status_history/YYYY-MM.md (grouped by month) 4. compact the meaning of the original entire STATUS.md into about 500 lines or less 5. generally preserve the document structure (keep "## recent work" header, "## immediate priorities", etc) 6. do NOT summarize archived content - move it verbatim and organize it chronologically so STATUS.md is the living overview, slightly recency biased, but a good general overview of the project. .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. VERIFY: run `wc -l STATUS.md` after archiving. it MUST be under 500 lines. ## task 3: generate audio overview (if skip_audio is false) skip_audio input: ${{ inputs.skip_audio }} if skip_audio is false: ### understand the project first before writing the script, read: - STATUS.md (the current state) - docs/deployment/overview.md if it exists - Fetch https://atproto.com/guides/overview to understand ATProto primitives - Fetch https://atproto.com/guides/lexicon to understand NSIDs and lexicons - fetch and read: https://overreacted.io/open-social/ to understand the vision of open social this context helps you explain things accurately, and accessibly without over-simplifying. ### determine episode type check if .status_history/ directory exists: - if NO .status_history/ exists: this is the INAUGURAL episode - if .status_history/ exists: this is a SUBSEQUENT episode ### write the podcast script write to podcast_script.txt with "Host: ..." and "Cohost: ..." lines. INAUGURAL EPISODE (ignore this if its not the first episode): - introduce what plyr.fm is: decentralized music streaming on ATProto - explain the core value prop: your music data lives in your PDS, portable between apps (open social) - cover the technical foundation that's been built (summarize from STATUS.md) - set expectations: this is an early-stage project, rough edges exist - tone: "here's what is being built and why it could matter" - work chronologically from beginning to end, don't heavily bias towards nascent or recent work SUBSEQUENT EPISODES (ignore this if its the first episode): - focus on what actually shipped since last episode - use git history to determine timing ("last week" vs "earlier this month") - don't re-explain the whole project - listeners already know - tone: "here's what changed since last time" ### tone requirements (CRITICAL) the hosts should sound like two engineers who: - are skeptical, amused and somewhat intruiged by the absurdity of building things - acknowledge problems and limitations honestly - don't over-use superlatives ("amazing", "incredible", "exciting") - explain technical concepts through analogy, not hypey jargon avoid excessive phrasing: - "exciting", "amazing", "incredible", "impressive", "great job" - "the team has done", "they've really", "fantastic work" - any variation of over-congratulating or over-sensationalizing the project pronunciation: "plyr.fm" is pronounced "player FM" (not "plir" or spelled out) target length: 2-3 minutes spoken (~300-400 words) (it should be 4-5 if its the first episode) ### generate audio run: uv run scripts/generate_tts.py podcast_script.txt update.wav then: rm podcast_script.txt ## task 4: open PR if any files changed: 1. git checkout -b status-maintenance-$(date +%Y%m%d) 2. git add .status_history/ STATUS.md update.wav 3. git commit -m "chore: weekly status maintenance" 4. git push -u origin status-maintenance-$(date +%Y%m%d) 5. gh pr create --title "chore: weekly status maintenance" --body "automated status archival and audio overview" add detail as desired to the PR body and title. add a label like "ai-generated" to the PR (create the label if it doesn't exist) if nothing changed, report that no maintenance was needed. env: GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} # phase 2: upload audio after PR merge upload-audio: if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'status-maintenance-') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v4 - name: Upload audio to plyr.fm run: | if [ -f update.wav ]; then uv run --with plyrfm -- plyrfm upload update.wav "plyr.fm update - $(date +'%B %d, %Y')" --album "$(date +%Y)" -t ai else echo "No update.wav found, skipping upload" fi env: PLYR_TOKEN: ${{ secrets.PLYR_BOT_TOKEN }}