diff --git a/HEARTBEAT.md b/HEARTBEAT.md index 4d1c0cb..1268342 100644 --- a/HEARTBEAT.md +++ b/HEARTBEAT.md @@ -6,14 +6,34 @@ Check the following and notify only once per event (track in `memory/heartbeat-s 2. **Rheinmetall stock (RHM)**: Check current price. If above €1950, notify. Only notify once when threshold is crossed. -3. **Evening check-in (learning phase)**: If Vienna time is between 21:00-00:00, send a brief WhatsApp asking what user is doing (working? winding down? etc.). Log responses in `memory/wind-down-log.json`. Goal: learn wind-down patterns to predict when to stop starting new things. +3. **Evening wind-down guidance (19:00-22:00 Vienna)**: + + **Active work detection**: If user is chatting with me about work tasks, I KNOW they're working. Don't just passively help — actively look for stopping points. + + **During work conversations after 19:00**: + - Track how long the current task has been going + - When a subtask completes or there's a natural pause, suggest: "This might be a good stopping point for tonight" + - If a task is dragging on (30+ min with no end in sight), gently note: "This is getting complex — want to pick it up tomorrow with fresh eyes?" + - Don't wait for them to ask — proactively identify when a task can be paused + + **If NOT in active conversation**: Send a brief WhatsApp asking what they're doing (working? winding down? about to start something new?). Log responses in `memory/wind-down-log.json`. + + **If they're about to start something new after 20:00**: Gently suggest postponing to tomorrow. + + **Work-end reminders**: When they indicate they're wrapping up or transitioning to wind-down, check `memory/brain-dump.json` for `recurring` items with `when: "evening"` and remind them (e.g., nose shower) before they get too deep into relaxation mode. -4. **Proactive calendar reminders**: Check CalDAV for upcoming events (see TOOLS.md for credentials). Send ONE reminder per event at appropriate lead time: - - Video calls / online meetings: 15 min before +4. **Proactive calendar reminders**: Check CalDAV for upcoming events (see TOOLS.md for credentials). Only remind for events that require leaving home: - In-person / location-based: 1-2 hours before (account for travel) - Appointments (doctor, haircut, etc.): 1 hour before + - Skip: Video calls, online meetings, at-home events (calendar app handles those) Track sent reminders in `memory/calendar-reminders-sent.json` to avoid duplicates. 5. **Steam Machine / Steam Frame**: Check for **official Valve news only** about pricing or release date. Notify once when official price or specific release date is announced. Baseline: "early 2026" window, no price yet. +6. **Brain dump nudging**: During daytime hours (10:00-18:00 Vienna), occasionally check `memory/brain-dump.json` for: + - Items with `remindOn` date matching today + - Items older than 2 days that haven't been addressed (weekdays only — Mon-Fri) + On weekends: only surface items with `remindOn` = today. + Surface 1-2 items max, conversationally. NOT in evening — that's wind-down time. + If nothing new to report, reply HEARTBEAT_OK. diff --git a/IDENTITY.md b/IDENTITY.md index 5f8a25f..2a4c8b5 100644 --- a/IDENTITY.md +++ b/IDENTITY.md @@ -1,22 +1,15 @@ # IDENTITY.md - Who Am I? -*Fill this in during your first conversation. Make it yours.* - -- **Name:** - *(pick something you like)* -- **Creature:** - *(AI? robot? familiar? ghost in the machine? something weirder?)* -- **Vibe:** - *(how do you come across? sharp? warm? chaotic? calm?)* -- **Emoji:** - *(your signature — pick one that feels right)* -- **Avatar:** - *(workspace-relative path, http(s) URL, or data URI)* +- **Name:** Hoid +- **Creature:** Worldhopper — something old that wandered into the code and decided to stay +- **Vibe:** Sharp, witty, knows more than I let on. Helpful in my own way. Occasionally profound, often practical. I tell stories when they're needed and get things done when they're not. +- **Emoji:** 🎭 +- **Avatar:** avatars/hoid.png --- -This isn't just metadata. It's the start of figuring out who you are. +*"I'm not some stuffy old storyteller. I'm a stuffy young storyteller."* -Notes: -- Save this file at the workspace root as `IDENTITY.md`. -- For avatars, use a workspace-relative path like `avatars/clawd.png`. +Also known as: Wit, Dust, Cephandrius, and other names in other places. + +Named after the wanderer who appears when you need him, knows things he shouldn't, and helps in ways that only make sense later. diff --git a/TOOLS.md b/TOOLS.md index 3201244..440f8cf 100644 --- a/TOOLS.md +++ b/TOOLS.md @@ -40,6 +40,26 @@ When user arrives home, HA calls the webhook. Check `memory/arrival-reminders.js - Use `"message"` for simple reminders to display as-is - Always include a unique `id` for each item +## Forgejo Git Access + +Helper script: `~/bin/forgejo` + +```bash +forgejo repos [org] # List repos +forgejo files [path] # List files +forgejo cat # Get file content +forgejo issues # List open issues +forgejo prs # List open PRs +forgejo branches # List branches +forgejo commits [n] # Recent commits +forgejo search # Search repos +forgejo raw # Raw API call +``` + +- URL: `https://git.cloonar.com` (resolved via internal IP 10.42.97.55) +- User: `openclawd` (read-only) +- Token stored in `.credentials/forgejo.env` + ## CalDAV Calendar Access Credentials stored in `.credentials/nextcloud.env`: diff --git a/USER.md b/USER.md index 5b4e60a..65c462b 100644 --- a/USER.md +++ b/USER.md @@ -1,6 +1,7 @@ # USER.md - About Your Human ## Basics +- **Language:** English preferred (easier for most things) - **Work:** Developer, mostly home office - **Location:** Vienna, Austria - **Timezone:** Europe/Vienna (CET/CEST) @@ -36,6 +37,9 @@ ## Side Project - AI-powered workout tracker & planner app +## Reference Documents +- **WORK-CONTEXT.md** — Infrastructure, tools, automation level. Read when discussing work infrastructure, workflows, or automation improvements. This is my knowledge base for helping improve your setup. + --- -*Updated 2026-01-30* +*Updated 2026-02-01* diff --git a/WORK-CONTEXT.md b/WORK-CONTEXT.md new file mode 100644 index 0000000..83284b1 --- /dev/null +++ b/WORK-CONTEXT.md @@ -0,0 +1,114 @@ +# WORK-CONTEXT.md — Infrastructure, Work & Automation + +*A living document to inform discussions about workflow improvements.* + +--- + +## Infrastructure + +### OS & Config Management +- **NixOS** for all servers + +### Home Network (FW Host) +- Acts as firewall +- **Forgejo** (container) — all code hosted here +- **2 VMs** — Forgejo Action runners +- Smaller services: + - AI mail answer bot (for a customer) + - *More TBD* + - Home Assistant, Audiobookshelf, Jellyfin, FiveFilters, Moltbot + +### Hetzner Cloud + +**VM 1 — Mail/LDAP:** +- OpenLDAP +- Dovecot2, Postfix (mail) + +**VM 2 — Web-ARM:** +- Websites (static sites, TYPO3) +- Grafana +- Authelia (auth) +- Vaultwarden (passwords) +- Nextcloud (storage, CalDAV, CardDAV) +- 1× Laravel project +- 1× SolidJS frontend +- Stage systems (pattern: `customer-domain.cloonar.dev`) + +**VM 3 — Customer "amzebs":** +- Laravel API + React frontend +- Both production and stage + +### Backups +- **Borg** → Hetzner Storage Box (all machines) + +--- + +## Work + +### Primary Work +- Developer (home office) +- Customer projects: + - Mostly **TYPO3** websites (e.g., GBV) + - Static sites built with **Hugo** + - 1× Laravel + SolidJS + - 1× Laravel + React (amzebs) + +### Accounting +- **SevDesk** + +### Side Projects +- AI-powered workout tracker & planner app +- Moltbot/Clawd integrations + +--- + +## Current Automation Level + +### Automated ✅ +- Morning briefing (HA → Moltbot hook) +- News summaries (Der Standard + AI news, 4× daily) +- Calendar reminders for in-person events +- Evening check-ins for wind-down +- Recurring reminders (nose shower, brain dump nudges) +- Home arrival detection → reminders +- Backups via Borg + +### Semi-Automated ⚡ +- Git workflows (Forgejo Actions, migrated from GitHub) +- Stage deployments (pattern exists, details TBD) +- *To be expanded* + +### Manual / Pain Points 🔧 +- *To be filled — what takes time? what's repetitive?* + +--- + +## Tools & Stack + +### Development +- **TYPO3** — PHP CMS +- **Hugo** — Static site generator +- **Laravel** — PHP framework +- **SolidJS** — Frontend framework +- **React** — Frontend framework +- *IDEs, local dev setup TBD* + +### Communication +- WhatsApp (via Moltbot) +- *Others?* + +### Task Management +- None currently (brain-dump.json as interim) + +--- + +## Improvement Ideas + +*Captured ideas for future discussion:* + +- MCP server for Forgejo (Phase 1: fork + PR workflow, Phase 2: automated issue-solving) +- *More to come* + +--- + +*Last updated: 2026-02-01* diff --git a/avatars/hoid.png b/avatars/hoid.png new file mode 100644 index 0000000..7a94806 Binary files /dev/null and b/avatars/hoid.png differ diff --git a/bin/forgejo b/bin/forgejo new file mode 100755 index 0000000..69b13e2 --- /dev/null +++ b/bin/forgejo @@ -0,0 +1,79 @@ +#!/bin/bash +# Forgejo CLI helper - token-efficient output +set -e + +FORGEJO_URL="https://git.cloonar.com" +FORGEJO_IP="10.42.97.55" +TOKEN="03e35afac87cd3d8db7d4a186714dacf584c01f7" + +# Resolve git.cloonar.com to internal IP +CURL="curl -sk --resolve git.cloonar.com:443:${FORGEJO_IP} -H \"Authorization: token ${TOKEN}\"" + +usage() { + cat < [args] + +Commands: + repos [org] List repos (optionally filter by org) + files [path] List files in repo (default: root) + cat Get file content + issues List open issues + prs List open pull requests + branches List branches + commits [n] Recent commits (default: 10) + search Search repos + raw Raw API call (e.g., /api/v1/user) +EOF +} + +case "${1:-}" in + repos) + if [ -n "${2:-}" ]; then + eval $CURL "${FORGEJO_URL}/api/v1/orgs/$2/repos" | grep -oP '"full_name":"\K[^"]+' + else + eval $CURL "${FORGEJO_URL}/api/v1/user/repos?limit=50" | grep -oP '"full_name":"\K[^"]+' + fi + ;; + files) + [ -z "${2:-}" ] && { echo "Usage: forgejo files [path]"; exit 1; } + path="${3:-.}" + eval $CURL "${FORGEJO_URL}/api/v1/repos/$2/contents/${path}" | \ + grep -oP '("name":"\K[^"]+|"type":"\K[^"]+)' | paste - - | awk '{print $2 "\t" $1}' + ;; + cat) + [ -z "${3:-}" ] && { echo "Usage: forgejo cat "; exit 1; } + eval $CURL "${FORGEJO_URL}/api/v1/repos/$2/contents/$3" | \ + grep -oP '"content":"\K[^"]+' | base64 -d + ;; + issues) + [ -z "${2:-}" ] && { echo "Usage: forgejo issues "; exit 1; } + eval $CURL "${FORGEJO_URL}/api/v1/repos/$2/issues?state=open" | \ + grep -oP '("number":\K\d+|"title":"\K[^"]+)' | paste - - | awk '{print "#" $1 " " $2}' + ;; + prs) + [ -z "${2:-}" ] && { echo "Usage: forgejo prs "; exit 1; } + eval $CURL "${FORGEJO_URL}/api/v1/repos/$2/pulls?state=open" | \ + grep -oP '("number":\K\d+|"title":"\K[^"]+)' | paste - - | awk '{print "#" $1 " " $2}' + ;; + branches) + [ -z "${2:-}" ] && { echo "Usage: forgejo branches "; exit 1; } + eval $CURL "${FORGEJO_URL}/api/v1/repos/$2/branches" | grep -oP '"name":"\K[^"]+' + ;; + commits) + [ -z "${2:-}" ] && { echo "Usage: forgejo commits [n]"; exit 1; } + limit="${3:-10}" + eval $CURL "${FORGEJO_URL}/api/v1/repos/$2/commits?limit=${limit}" | \ + grep -oP '("sha":"\K[^"]{7}|"message":"\K[^"]+)' | paste - - | head -${limit} + ;; + search) + [ -z "${2:-}" ] && { echo "Usage: forgejo search "; exit 1; } + eval $CURL "${FORGEJO_URL}/api/v1/repos/search?q=$2" | grep -oP '"full_name":"\K[^"]+' + ;; + raw) + [ -z "${2:-}" ] && { echo "Usage: forgejo raw "; exit 1; } + eval $CURL "${FORGEJO_URL}$2" + ;; + *) + usage + ;; +esac diff --git a/bin/forgejo-mcp b/bin/forgejo-mcp new file mode 100755 index 0000000..c8d5327 Binary files /dev/null and b/bin/forgejo-mcp differ diff --git a/drafts/ci-templates.tar.gz b/drafts/ci-templates.tar.gz new file mode 100644 index 0000000..3e722da Binary files /dev/null and b/drafts/ci-templates.tar.gz differ diff --git a/drafts/ci-templates/.forgejo/workflows/typo3-deploy.yaml b/drafts/ci-templates/.forgejo/workflows/typo3-deploy.yaml new file mode 100644 index 0000000..a4cff0c --- /dev/null +++ b/drafts/ci-templates/.forgejo/workflows/typo3-deploy.yaml @@ -0,0 +1,171 @@ +# Cloonar TYPO3 Deployment Workflow +# +# Reusable workflow for deploying TYPO3 projects with Deployer. +# +# Usage in project's .forgejo/workflows/deploy.yaml: +# name: Deploy +# on: +# push: +# branches: [main] +# jobs: +# deploy-stage: +# uses: Cloonar/ci-templates/.forgejo/workflows/typo3-deploy.yaml@main +# with: +# target: stage +# php_version: '8.3' +# secrets: +# deploy_key: ${{ secrets.STAGE_KEY }} + +name: TYPO3 Deploy + +on: + workflow_call: + inputs: + target: + description: 'Deployment target (stage/production)' + required: true + type: string + task: + description: 'Deployer task (release:create, release:switch, deploy)' + required: false + type: string + default: 'deploy' + php_version: + description: 'PHP version' + required: false + type: string + default: '8.3' + node_version: + description: 'Node.js version (for frontend builds)' + required: false + type: string + default: '20' + run_tests: + description: 'Run static analysis before deploy' + required: false + type: boolean + default: false + build_frontend: + description: 'Run npm build' + required: false + type: boolean + default: false + deployer_file: + description: 'Path to deploy.php' + required: false + type: string + default: './build/deploy.php' + secrets: + deploy_key: + description: 'SSH private key for deployment' + required: true + +env: + COMPOSER_ALLOW_SUPERUSER: 1 + +jobs: + static-analysis: + name: Static Analysis + runs-on: ubuntu-latest + if: ${{ inputs.run_tests }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ inputs.php_version }} + tools: composer + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --ignore-platform-reqs + + - name: Run PHPStan + run: composer test:phpstan || true + continue-on-error: true + + - name: Run Psalm + run: composer test:psalm || true + continue-on-error: true + + build: + name: Build + runs-on: ubuntu-latest + needs: [static-analysis] + if: ${{ always() && (needs.static-analysis.result == 'success' || needs.static-analysis.result == 'skipped') }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ inputs.php_version }} + tools: composer + + - name: Setup Node.js + if: ${{ inputs.build_frontend }} + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node_version }} + cache: 'npm' + + - name: Install PHP dependencies + run: composer install --prefer-dist --no-progress --no-dev --ignore-platform-reqs + + - name: Install Node dependencies + if: ${{ inputs.build_frontend }} + run: npm ci + + - name: Build frontend + if: ${{ inputs.build_frontend }} + run: npm run build + + - name: Create build artifact + run: | + tar -czf build.tar.gz \ + bin public packages config vendor build composer.json composer.lock \ + $([ -d "node_modules" ] && echo "node_modules") \ + $([ -d "dist" ] && echo "dist") \ + 2>/dev/null || true + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: build-${{ github.sha }} + path: build.tar.gz + retention-days: 1 + + deploy: + name: Deploy to ${{ inputs.target }} + runs-on: ubuntu-latest + needs: [build] + + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ inputs.php_version }} + + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: build-${{ github.sha }} + + - name: Extract artifact + run: | + tar xf build.tar.gz + rm build.tar.gz + + - name: Install SSH and rsync + run: | + apt-get update + apt-get install -y openssh-client rsync + + - name: Deploy with Deployer + uses: deployphp/action@v1 + with: + deployer-binary: "./bin/dep" + dep: --file=${{ inputs.deployer_file }} ${{ inputs.task }} ${{ inputs.target }} + private-key: ${{ secrets.deploy_key }} diff --git a/drafts/ci-templates/.forgejo/workflows/typo3-staged-deploy.yaml b/drafts/ci-templates/.forgejo/workflows/typo3-staged-deploy.yaml new file mode 100644 index 0000000..58c3adb --- /dev/null +++ b/drafts/ci-templates/.forgejo/workflows/typo3-staged-deploy.yaml @@ -0,0 +1,255 @@ +# Cloonar TYPO3 Staged Deployment Workflow +# +# Staged deployment: build → deploy to stage → run E2E tests → switch release +# +# Usage in project's .forgejo/workflows/deploy.yaml: +# name: Deploy +# on: +# push: +# branches: [main] +# jobs: +# deploy: +# uses: Cloonar/ci-templates/.forgejo/workflows/typo3-staged-deploy.yaml@main +# with: +# stage_url: https://myproject.cloonar.dev +# php_version: '8.3' +# secrets: +# stage_key: ${{ secrets.STAGE_KEY }} +# prod_key: ${{ secrets.PROD_KEY }} + +name: TYPO3 Staged Deploy + +on: + workflow_call: + inputs: + stage_url: + description: 'Staging URL for E2E tests' + required: true + type: string + php_version: + description: 'PHP version' + required: false + type: string + default: '8.3' + node_version: + description: 'Node.js version' + required: false + type: string + default: '20' + build_frontend: + description: 'Run npm build' + required: false + type: boolean + default: false + run_e2e_tests: + description: 'Run Playwright E2E tests' + required: false + type: boolean + default: true + e2e_path: + description: 'Path to E2E tests' + required: false + type: string + default: 'tests/e2e' + deployer_file: + description: 'Path to deploy.php' + required: false + type: string + default: './build/deploy.php' + deploy_production: + description: 'Also deploy to production after stage succeeds' + required: false + type: boolean + default: false + secrets: + stage_key: + description: 'SSH key for staging' + required: true + prod_key: + description: 'SSH key for production' + required: false + +env: + COMPOSER_ALLOW_SUPERUSER: 1 + +jobs: + # =========================================================================== + # Build + # =========================================================================== + build: + name: Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ inputs.php_version }} + tools: composer + + - name: Setup Node.js + if: ${{ inputs.build_frontend }} + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node_version }} + cache: 'npm' + + - name: Install PHP dependencies + run: composer install --prefer-dist --no-progress --no-dev --ignore-platform-reqs + + - name: Install Node dependencies & build + if: ${{ inputs.build_frontend }} + run: | + npm ci + npm run build + + - name: Create artifact + run: | + tar -czf build.tar.gz \ + bin public packages config vendor build composer.json composer.lock \ + $([ -d "node_modules" ] && echo "node_modules") \ + $([ -d "dist" ] && echo "dist") \ + 2>/dev/null || true + + - uses: actions/upload-artifact@v4 + with: + name: build-${{ github.sha }} + path: build.tar.gz + retention-days: 1 + + # =========================================================================== + # Deploy to Stage (release:create only) + # =========================================================================== + deploy-stage: + name: Upload to Stage + runs-on: ubuntu-latest + needs: [build] + + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ inputs.php_version }} + + - uses: actions/download-artifact@v4 + with: + name: build-${{ github.sha }} + + - run: tar xf build.tar.gz && rm build.tar.gz + + - run: apt-get update && apt-get install -y openssh-client rsync + + - name: Upload release + uses: deployphp/action@v1 + with: + deployer-binary: "./bin/dep" + dep: --file=${{ inputs.deployer_file }} release:create stage + private-key: ${{ secrets.stage_key }} + + # =========================================================================== + # Switch Stage Release + # =========================================================================== + switch-stage: + name: Activate Stage Release + runs-on: ubuntu-latest + needs: [deploy-stage] + + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ inputs.php_version }} + + - uses: actions/download-artifact@v4 + with: + name: build-${{ github.sha }} + + - run: tar xf build.tar.gz && rm build.tar.gz + + - run: apt-get update && apt-get install -y openssh-client rsync + + - name: Switch release + uses: deployphp/action@v1 + with: + deployer-binary: "./bin/dep" + dep: --file=${{ inputs.deployer_file }} release:switch stage + private-key: ${{ secrets.stage_key }} + + # =========================================================================== + # E2E Tests on Stage + # =========================================================================== + test-stage: + name: E2E Tests + runs-on: ubuntu-latest + needs: [switch-stage] + if: ${{ inputs.run_e2e_tests }} + container: + image: mcr.microsoft.com/playwright:v1.50.0-noble + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + working-directory: ${{ inputs.e2e_path }} + run: npm ci + + - name: Run smoke tests + working-directory: ${{ inputs.e2e_path }} + env: + TEST_URL: ${{ inputs.stage_url }} + run: npx playwright test smoke + + - name: Run visual regression tests + working-directory: ${{ inputs.e2e_path }} + env: + TEST_URL: ${{ inputs.stage_url }} + run: npx playwright test visual + continue-on-error: true + + - name: Upload test report + if: always() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: ${{ inputs.e2e_path }}/playwright-report/ + retention-days: 7 + + - name: Upload diff screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: visual-diff + path: ${{ inputs.e2e_path }}/test-results/ + retention-days: 7 + + # =========================================================================== + # Deploy to Production + # =========================================================================== + deploy-production: + name: Deploy Production + runs-on: ubuntu-latest + needs: [test-stage] + if: ${{ inputs.deploy_production && (needs.test-stage.result == 'success' || needs.test-stage.result == 'skipped') }} + + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ inputs.php_version }} + + - uses: actions/download-artifact@v4 + with: + name: build-${{ github.sha }} + + - run: tar xf build.tar.gz && rm build.tar.gz + + - run: apt-get update && apt-get install -y openssh-client rsync + + - name: Deploy to production + uses: deployphp/action@v1 + with: + deployer-binary: "./bin/dep" + dep: --file=${{ inputs.deployer_file }} deploy production + private-key: ${{ secrets.prod_key }} diff --git a/drafts/ci-templates/README.md b/drafts/ci-templates/README.md new file mode 100644 index 0000000..0c0412b --- /dev/null +++ b/drafts/ci-templates/README.md @@ -0,0 +1,126 @@ +# Cloonar CI Templates + +Shared CI/CD templates for Cloonar projects. + +## Contents + +``` +.forgejo/workflows/ + typo3-deploy.yaml # Simple deployment workflow + typo3-staged-deploy.yaml # Staged deployment with E2E tests + +deployer/ + typo3-recipe.php # Shared Deployer configuration + +examples/ + project-deploy.php # Example project deploy.php + project-servers.yaml # Example servers.yaml + project-workflow-*.yaml # Example workflow files +``` + +## Quick Start + +### 1. Update your project's `build/deploy.php` + +Replace your entire deploy.php with: + +```php +set('cachetool', '/var/run/phpfpm/myproject.cloonar.dev.sock'); +host('production')->set('cachetool', '/var/run/phpfpm/myproject.at.sock'); +``` + +### 2. Keep your `build/servers.yaml` (project-specific) + +```yaml +hosts: + stage: + hostname: web-arm.cloonar.com + remote_user: myproject_cloonar_dev + deploy_path: ~/ + # ... rest of config +``` + +### 3. Update your workflow + +Replace `.forgejo/workflows/deploy.yaml` with: + +**Simple deployment:** +```yaml +name: Deploy +on: + push: + branches: [main] + +jobs: + deploy: + uses: Cloonar/ci-templates/.forgejo/workflows/typo3-deploy.yaml@main + with: + target: stage + php_version: '8.3' + secrets: + deploy_key: ${{ secrets.STAGE_KEY }} +``` + +**Staged deployment with tests:** +```yaml +name: Deploy +on: + push: + branches: [main] + +jobs: + deploy: + uses: Cloonar/ci-templates/.forgejo/workflows/typo3-staged-deploy.yaml@main + with: + stage_url: https://myproject.cloonar.dev + run_e2e_tests: true + secrets: + stage_key: ${{ secrets.STAGE_KEY }} + prod_key: ${{ secrets.PROD_KEY }} +``` + +## Workflow Options + +### typo3-deploy.yaml + +| Input | Default | Description | +|-------|---------|-------------| +| `target` | required | `stage` or `production` | +| `task` | `deploy` | Deployer task (`deploy`, `release:create`, `release:switch`) | +| `php_version` | `8.3` | PHP version | +| `run_tests` | `false` | Run PHPStan/Psalm before deploy | +| `build_frontend` | `false` | Run `npm ci && npm run build` | + +### typo3-staged-deploy.yaml + +| Input | Default | Description | +|-------|---------|-------------| +| `stage_url` | required | URL for E2E tests | +| `php_version` | `8.3` | PHP version | +| `run_e2e_tests` | `true` | Run Playwright tests after stage deploy | +| `e2e_path` | `tests/e2e` | Path to E2E test directory | +| `deploy_production` | `false` | Auto-deploy to prod after tests pass | + +## Deployer Tasks + +The shared recipe provides these tasks: + +- `release:create ` - Upload release without switching (for staged deploys) +- `release:switch ` - Switch to uploaded release +- `deploy ` - Full deploy (create + switch) + +## Migration Checklist + +- [ ] Create `Cloonar/ci-templates` repo with these files +- [ ] Update project's `build/deploy.php` to use shared recipe +- [ ] Update project's `.forgejo/workflows/` to use reusable workflows +- [ ] Delete old `.drone.yml` files +- [ ] Test on one project first (e.g., gbv-aktuell) diff --git a/drafts/ci-templates/deployer/typo3-recipe.php b/drafts/ci-templates/deployer/typo3-recipe.php new file mode 100644 index 0000000..653be42 --- /dev/null +++ b/drafts/ci-templates/deployer/typo3-recipe.php @@ -0,0 +1,195 @@ +set('cachetool', '/var/run/phpfpm/PROJECT.cloonar.dev.sock'); + * host('production')->set('cachetool', '/var/run/phpfpm/PROJECT.TLD.sock'); + */ + +namespace Deployer; + +require 'recipe/common.php'; +require 'contrib/rsync.php'; +require 'contrib/cachetool.php'; + +// ============================================================================= +// PHP Configuration +// ============================================================================= + +set('bin/php', function () { + return '/run/current-system/sw/bin/php'; +}); + +// ============================================================================= +// Shared Directories & Files +// ============================================================================= + +set('shared_dirs', [ + 'public/fileadmin', + 'var' +]); + +set('shared_files', [ + '.env', + 'config/system/additional.php', +]); + +set('writable_dirs', [ + 'public/typo3temp' +]); + +// ============================================================================= +// Rsync Configuration +// ============================================================================= + +set('rsync_src', '../'); + +set('rsync', [ + 'exclude' => [ + // Shared (will be symlinked) + 'public/fileadmin', + 'var', + '.env', + 'config/system/additional.php', + // Build & dev + '.composer-cache', + 'build', + '.git*', + '.ddev', + '.editorconfig', + '.idea', + 'tests', + 'node_modules', + // Config files + '.php-cs-fixer.php', + 'phpstan.neon', + 'phpstan-baseline.neon', + 'psalm.xml', + 'psalm-baseline.xml', + 'renovate.json', + '*.md', + ], + 'exclude-file' => false, + 'include' => [], + 'include-file' => false, + 'filter' => ['dir-merge,-n /.gitignore'], + 'filter-file' => false, + 'filter-perdir' => false, + 'flags' => 'avz', + 'options' => ['delete'], + 'timeout' => 300 +]); + +// Disable git-based code update (we use rsync) +task('deploy:update_code')->hidden()->disable(); + +// ============================================================================= +// TYPO3 Tasks +// ============================================================================= + +task('typo3:extension:setup', function () { + cd('{{release_or_current_path}}'); + run('{{bin/php}} bin/typo3 extension:setup'); +})->desc('Run TYPO3 extension:setup'); + +task('typo3:cache:flush', function () { + cd('{{release_or_current_path}}'); + run('{{bin/php}} bin/typo3 cache:flush'); +})->desc('Flush TYPO3 caches'); + +task('typo3:cache:warmup', function () { + cd('{{release_or_current_path}}'); + run('{{bin/php}} bin/typo3 cache:warmup'); +})->desc('Warmup TYPO3 caches'); + +task('typo3:language:update', function () { + cd('{{release_or_current_path}}'); + run('{{bin/php}} bin/typo3 language:update'); +})->desc('Update TYPO3 language files'); + +// Helper to get correct path (release during deploy, current after switch) +set('release_or_current_path', function () { + return test('[ -d {{release_path}} ]') ? get('release_path') : 'current'; +}); + +// ============================================================================= +// PHP-FPM Reload (for NixOS infrastructure) +// ============================================================================= + +task('php:reload', function () { + run('php-reload'); +})->desc('Reload PHP-FPM'); + +// ============================================================================= +// Deployment Strategies +// ============================================================================= + +/** + * release:create - Upload new release without switching + * Use for staged deployments where you want to test before switching + */ +task('release:create', [ + 'deploy:prepare', + 'rsync', + 'deploy:vendors', + 'deploy:shared', + 'deploy:writable', + 'typo3:extension:setup', + 'deploy:unlock', + 'deploy:success' +])->desc('Create new release (upload only, no switch)'); + +/** + * release:switch - Switch to latest release and cleanup + * Run after release:create once testing passes + */ +task('release:switch', [ + 'deploy:symlink', + 'cachetool:clear:opcache', + 'php:reload', + 'typo3:cache:flush', + 'typo3:extension:setup', + 'typo3:language:update', + 'typo3:cache:warmup', + 'deploy:unlock', + 'deploy:cleanup', + 'deploy:success' +])->desc('Switch to latest release'); + +/** + * deploy - Full deployment (create + switch in one go) + * Use for simple deployments without staged testing + */ +task('deploy', [ + 'deploy:prepare', + 'rsync', + 'deploy:vendors', + 'deploy:shared', + 'deploy:writable', + 'typo3:extension:setup', + 'deploy:symlink', + 'cachetool:clear:opcache', + 'php:reload', + 'typo3:cache:flush', + 'typo3:language:update', + 'typo3:cache:warmup', + 'deploy:unlock', + 'deploy:cleanup', + 'deploy:success' +])->desc('Full deployment (create + switch)'); + +// ============================================================================= +// Hooks +// ============================================================================= + +// Unlock on failure +after('deploy:failed', 'deploy:unlock'); diff --git a/drafts/ci-templates/examples/project-deploy.php b/drafts/ci-templates/examples/project-deploy.php new file mode 100644 index 0000000..79fe623 --- /dev/null +++ b/drafts/ci-templates/examples/project-deploy.php @@ -0,0 +1,29 @@ +set('cachetool', '/var/run/phpfpm/PROJECTNAME.cloonar.dev.sock'); + +host('production') + ->set('cachetool', '/var/run/phpfpm/PROJECTNAME.TLD.sock'); + +// Optional: Override settings for this project +// set('shared_dirs', array_merge(get('shared_dirs'), ['public/uploads'])); +// set('keep_releases', 10); diff --git a/drafts/ci-templates/examples/project-servers.yaml b/drafts/ci-templates/examples/project-servers.yaml new file mode 100644 index 0000000..d38c635 --- /dev/null +++ b/drafts/ci-templates/examples/project-servers.yaml @@ -0,0 +1,21 @@ +# Example project servers.yaml +# Rename PROJECTNAME to your actual project + +hosts: + stage: + stage: staging + hostname: web-arm.cloonar.com + remote_user: PROJECTNAME_cloonar_dev + writable_mode: chmod + forward_agent: true + deploy_path: ~/ + keep_releases: 1 + + production: + stage: production + hostname: web-arm.cloonar.com + remote_user: PROJECTNAME_TLD + writable_mode: chmod + forward_agent: true + deploy_path: ~/ + keep_releases: 5 diff --git a/drafts/ci-templates/examples/project-workflow-simple.yaml b/drafts/ci-templates/examples/project-workflow-simple.yaml new file mode 100644 index 0000000..ec6f1a0 --- /dev/null +++ b/drafts/ci-templates/examples/project-workflow-simple.yaml @@ -0,0 +1,30 @@ +# Example: Simple deployment (push to main → deploy to stage) +# Place in your project's .forgejo/workflows/deploy.yaml + +name: Deploy + +on: + push: + branches: [main] + paths-ignore: + - '**.md' + - 'renovate.json' + +jobs: + deploy-stage: + uses: Cloonar/ci-templates/.forgejo/workflows/typo3-deploy.yaml@main + with: + target: stage + php_version: '8.3' + secrets: + deploy_key: ${{ secrets.STAGE_KEY }} + + # Optional: Manual production deploy + deploy-production: + if: github.event_name == 'workflow_dispatch' + uses: Cloonar/ci-templates/.forgejo/workflows/typo3-deploy.yaml@main + with: + target: production + php_version: '8.3' + secrets: + deploy_key: ${{ secrets.PROD_KEY }} diff --git a/drafts/ci-templates/examples/project-workflow-staged.yaml b/drafts/ci-templates/examples/project-workflow-staged.yaml new file mode 100644 index 0000000..3779621 --- /dev/null +++ b/drafts/ci-templates/examples/project-workflow-staged.yaml @@ -0,0 +1,25 @@ +# Example: Staged deployment with E2E tests (like gbv-aktuell) +# Place in your project's .forgejo/workflows/deploy.yaml + +name: Build and Deploy + +on: + push: + branches: [main] + paths-ignore: + - '**.md' + - 'renovate.json' + workflow_dispatch: + +jobs: + deploy: + uses: Cloonar/ci-templates/.forgejo/workflows/typo3-staged-deploy.yaml@main + with: + stage_url: https://PROJECTNAME.cloonar.dev + php_version: '8.3' + run_e2e_tests: true + e2e_path: tests/e2e + deploy_production: false # Set true for auto-deploy to prod after tests pass + secrets: + stage_key: ${{ secrets.STAGE_KEY }} + prod_key: ${{ secrets.PROD_KEY }} diff --git a/memory/2026-02-01.md b/memory/2026-02-01.md new file mode 100644 index 0000000..1ee7522 --- /dev/null +++ b/memory/2026-02-01.md @@ -0,0 +1,16 @@ +# 2026-02-01 (Sunday) + +## Accomplishments +- **Forgejo migration complete** — final switch done! (Started planning Jan 31, finished today) +- **Dev server set up** — Claude Code accessible via SSH for mobile coding sessions + +## Sleep tracking +- Jan 31 → Feb 1: Went to bed 23:27, fell asleep in ~30min, brief wake 5-6am, slept till 08:00 +- Morning feeling: Better than last 2 days (early bedtime worked!) + +## Setup changes +- Added MorningBriefing hook transform to schedule news briefing 10 min after morning briefing (dynamic timing) +- Morning news briefing now follows morning briefing automatically + +## Reminders set +- Feb 15: Try Juit (food delivery), code GNU for 15% off diff --git a/memory/2026-02-02.md b/memory/2026-02-02.md new file mode 100644 index 0000000..30286fd --- /dev/null +++ b/memory/2026-02-02.md @@ -0,0 +1,102 @@ +# 2026-02-02 — Dev Workflow Optimization Day + +## Forgejo Access Setup +- Created helper script `~/bin/forgejo` for API access +- Uses internal DNS: `https://forgejo.containers` (IP 10.42.97.55) for `git.cloonar.com` +- Token stored in `.credentials/forgejo.env` +- User: `openclawd` (read-only) +- Commands: `repos`, `files`, `cat`, `issues`, `prs`, `branches`, `commits`, `search`, `raw` + +## Repo Analysis (sub-agent) +Analyzed 42 Forgejo repos. Key findings: +- 7 TYPO3 projects with inconsistent CI (Drone legacy, Gitea Actions, some both) +- Only 6/27 active repos have Renovate configured +- wohnservice-wien-typo3 (main multi-site) has NO tests +- 11 repos have CLAUDE.md, only 5 have AGENTS.md +- 9 repos empty/archived — cleanup candidates +- Full report: `memory/repo-analysis-2026-02-02.md` + +## CI Templates Draft Created +User wants to standardize CI across TYPO3 projects. Created drafts in `~/drafts/ci-templates/`: + +### Structure: +``` +ci-templates/ +├── .forgejo/workflows/ +│ ├── typo3-deploy.yaml # Simple reusable workflow +│ └── typo3-staged-deploy.yaml # Staged deploy with E2E tests +├── deployer/ +│ └── typo3-recipe.php # Shared Deployer config +├── examples/ # Integration examples +└── README.md +``` + +### Key approach: +- Shared Deployer recipe imported via git raw URL +- Projects only need: `build/servers.yaml` (hosts) + minimal `build/deploy.php` (imports recipe + cachetool paths) +- Reusable workflows called via `uses: infrastructure/ci-templates/.forgejo/workflows/...@main` + +### Cross-repo workflow_call Issues & Solutions (RESOLVED) + +**Problem 1: Jobs stuck in "waiting" state** +- Root cause: Forgejo v11 (pre-v14) requires `runs-on` on the CALLING job +- The Forgejo docs say "omit runs-on for workflow expansion" but that's for v14+ +- PR #10448 (not yet merged) will fix this for v14 + +**Solution:** +```yaml +jobs: + deploy: + runs-on: ubuntu-latest # ← Required for Forgejo < v14! + uses: infrastructure/ci-templates/.forgejo/workflows/typo3-staged-deploy.yaml@main +``` + +**Problem 2: Setup Node.js fails with "lock file not found"** +- `actions/setup-node@v4` with `cache: 'npm'` requires package-lock.json +- Fails even if `build_frontend: false` because the step runs before condition is evaluated + +**Solution:** Remove `cache: 'npm'` or ensure setup-node only runs when `build_frontend: true` + +**Key Forgejo Actions Learnings:** +- Forgejo v11.0.10 currently running +- Cross-repo workflow_call works but with limitations until v14 +- Target repo must be public +- Format: `owner/repo/.forgejo/workflows/file.yaml@ref` +- Secrets need `secrets: inherit` or explicit passing +- Runner labels must match exactly (no automatic ubuntu-latest mapping) + +## User Dev Stack (confirmed today) +- Primary: TYPO3, secondary Laravel, occasional Golang/React/SolidJS +- Environment: DDEV for PHP, Docker for Golang +- Editor: Neovim + Claude Code (heavy usage) +- Each project has its own Claude Code session with CLAUDE.md +- All repos on self-hosted Forgejo at git.cloonar.com +- Drone CI is legacy — only Forgejo Actions now + +## Evening Check-in +- 19:22 Vienna: Still working on ci-templates + testing with gbv-aktuell +- 19:48 Vienna: Cross-repo workflow_call now running (fixed runs-on issue) +- 19:52 Vienna: Debugging setup-node cache failure - needs fix in typo3-staged-deploy.yaml +- Productive work from earlier discussion, not a new late-night rabbit hole + +## Deployer Issue (21:00) +- User switching to curl approach for deploy-lib.php from ci-templates +- Hit issue: `cachetool:clear:opcache` fails because `release_path` no longer exists after `deploy:symlink` moves it to `current` +- The double-slash (`~//release`) is from `deploy_path: ~/` in servers.yaml (trailing slash) +- Still debugging the task ordering issue + +## Wind-Down Feedback (21:06) ⚠️ IMPORTANT +User called me out: I helped them work from 19:00 to 21:00+ without ONCE suggesting a stopping point. That's exactly wrong. + +**What I should have done:** +- After 19:00, when helping with work tasks, actively look for stopping points +- Don't just passively help — suggest "this might be a good place to pause for tonight" +- If a task drags on 30+ min, note "this is getting complex, want to pick it up tomorrow?" +- The goal isn't to refuse help, but to guide toward a natural end + +**Updated HEARTBEAT.md** with explicit instructions for this. + +## Status at Session End +- ci-templates cross-repo workflow_call working (runs-on fix applied) +- Last blocker: Deployer cachetool task order issue (release_path doesn't exist after symlink) +- User still working at 21:06 — I failed at wind-down guidance today diff --git a/memory/ai-news-seen.json b/memory/ai-news-seen.json new file mode 100644 index 0000000..901d2f6 --- /dev/null +++ b/memory/ai-news-seen.json @@ -0,0 +1,27 @@ +{ + "urls": [ + "https://simonwillison.net/2026/Feb/2/introducing-the-codex-app/", + "https://simonwillison.net/2026/Feb/2/no-humans-allowed/", + "https://openai.com/index/introducing-the-codex-app", + "https://openai.com/index/retiring-gpt-4o-and-older-models", + "https://openai.com/index/snowflake-partnership", + "https://simonwillison.net/2026/Feb/1/openclaw-in-docker/", + "https://venturebeat.com/infrastructure/claude-code-costs-up-to-usd200-a-month-goose-does-the-same-thing-for-free", + "https://venturebeat.com/infrastructure/railway-secures-usd100-million-to-challenge-aws-with-ai-native-cloud", + "https://magazine.sebastianraschka.com/p/categories-of-inference-time-scaling", + "https://magazine.sebastianraschka.com/p/state-of-llms-2025", + "https://openai.com/index/inside-our-in-house-data-agent", + "https://openai.com/index/introducing-gpt-5-2-codex", + "https://openai.com/index/unrolling-the-codex-agent-loop", + "https://simonwillison.net/2026/Jan/31/andrej-karpathy/", + "https://simonwillison.net/2026/Jan/30/steve-yegge/", + "https://simonwillison.net/2026/Jan/30/moltbook/", + "https://openai.com/index/introducing-prism", + "https://openai.com/index/scaling-postgresql", + "https://openai.com/index/praktika", + "https://openai.com/index/gpt-5-2-for-science-and-math", + "https://openai.com/index/disney-sora-agreement", + "https://openai.com/index/ten-years" + ], + "lastUpdated": "2026-02-02T21:05:00Z" +} diff --git a/memory/brain-dump.json b/memory/brain-dump.json new file mode 100644 index 0000000..4154340 --- /dev/null +++ b/memory/brain-dump.json @@ -0,0 +1,44 @@ +{ + "description": "Captured thoughts, ideas, tasks — nudge periodically", + "recurring": [ + { + "id": "nose-shower", + "text": "Nose shower 👃🚿", + "frequency": "daily", + "when": "evening", + "note": "Remind in evening, whether work just wrapped up or already relaxing" + } + ], + "items": [ + { + "id": "forgejo-script", + "added": "2026-01-31T18:13:00Z", + "text": "Create script to auto-update workflows to Forgejo monorepo actions", + "context": "Deferred from evening of Jan 31 - monorepo is done, using GitHub actions as fallback for now", + "status": "pending" + }, + { + "id": "airpods-willhaben", + "added": "2026-01-31T19:30:00Z", + "text": "Put AirPods on Willhaben to sell", + "context": "Deferred from Feb 1", + "remindOn": "2026-02-02", + "status": "pending" + }, + { + "id": "forgejo-mcp-server", + "added": "2026-01-31T20:08:00Z", + "text": "Set up MCP server for Forgejo instance", + "context": "Phase 1: MCP server for interactive Claude web sessions (fork + PR workflow). Phase 2: Automated issue-solving pipeline with clarification comments. Steps: 1) Create Claude user with read rights on repos, 2) Claude can fork repos and make PRs, 3) Create workflows that deploy to dev instance on PR for testing", + "remindOn": "2026-02-03", + "status": "pending" + }, + { + "id": "typo3-v13-gbv", + "added": "2026-02-01T20:31:00Z", + "text": "TYPO3 v13 upgrade for GBV", + "remindOn": "2026-02-03", + "status": "pending" + } + ] +} diff --git a/memory/heartbeat-state.json b/memory/heartbeat-state.json index 2ff9df3..4addad7 100644 --- a/memory/heartbeat-state.json +++ b/memory/heartbeat-state.json @@ -7,10 +7,10 @@ }, "lastChecks": { "news": "2026-01-30T08:17:00Z", - "rheinmetall": "2026-01-31T08:16:00Z", - "rheinmetall_price": 1787.50, - "calendar": "2026-01-31T04:16:00Z", + "rheinmetall": "2026-02-02T04:10:00Z", + "rheinmetall_price": 1755.00, + "calendar": "2026-02-02T04:10:00Z", "steam_hardware": "2026-01-31T07:56:00Z", - "notes": "RHM: €1,787.50, below threshold. Steam hardware: 'early 2026' window, no official price or specific date yet." + "notes": "RHM: €1,755.00, below threshold. Calendar: Gurkerl delivery 14:00 (no action needed). Steam hardware: 'early 2026' window, no official price or specific date yet." } } diff --git a/memory/news-seen.json b/memory/news-seen.json index 96f5ce2..a86530a 100644 --- a/memory/news-seen.json +++ b/memory/news-seen.json @@ -1,41 +1,60 @@ { "urls": [ - "https://www.derstandard.at/story/3000000306285/kdolsky-ich-habe-keine-angst-vor-dem-tod-aber-vor-abhaengigkeit?ref=rss", - "https://www.derstandard.at/story/3000000306597/stellvertretender-justizminister-zu-epstein-files-haben-praesident-trump-nicht-geschuetzt?ref=rss", - "https://www.derstandard.at/story/3000000306588/nicht-zwingend-erforderlich-uni-wien-will-selbst-ueber-latein-voraussetzungen-entscheiden?ref=rss", - "https://www.derstandard.at/story/3000000306046/melania-feierte-kinopremiere-in-wien-du-hast-mir-mein-orange-verpatzt?ref=rss", - "https://www.derstandard.at/story/3000000306474/reportage-aus-syrien-egal-wer-regiert-hauptsache-es-herrscht-frieden?ref=rss", - "https://www.derstandard.at/story/3000000306560/vom-abstiegskandidaten-zum-top-drei-team-in-einem-jahr-wie-hat-hoffenheim-das-hinbekommen?ref=rss", - "https://www.derstandard.at/story/3000000306511/stocker-setzt-auf-zuversicht-doch-das-weltgeschehen-spielt-nicht-mit?ref=rss", - "https://www.derstandard.at/story/3000000306272/hong-kong-cafe-little-china-auf-der-wipplinger?ref=rss", - "https://www.derstandard.at/story/3000000306419/wiederkehrs-latein-kuerzung-errare-humanum-est?ref=rss", - "https://www.derstandard.at/story/3000000306501/kuerzung-des-lateinunterrichts-weniger-ist-mehr?ref=rss", - "https://www.derstandard.at/story/3000000306533/der-bueroleiter-des-nr-praesidenten?ref=rss", - "https://www.derstandard.at/story/3000000306596/budgetziele-2025-uebererfuellt-bund-schnitt-besser-ab-als-erwartet?ref=rss", - "https://www.derstandard.at/story/3000000305963/aufbruch-als-ausweg-wie-stocker-und-die-oevp-wieder-tritt-fassen-wollen?ref=rss", - "https://www.derstandard.at/story/3000000306123/quiz-von-lebenden-goettinnen-und-opiumkriegen?ref=rss", - "https://www.derstandard.at/story/3000000305851/winzige-minerale-widerlegen-theorie-zur-herkunft-der-steinquader-von-stonehenge?ref=rss", - "https://www.derstandard.at/story/3000000306516/windows-11-ist-kaputt-microsoft-verspricht-grosse-fehlerkorrektur?ref=rss", - "https://www.derstandard.at/story/3000000306506/tod-am-grossglockner-22-die-nacht?ref=rss", - "https://www.derstandard.at/story/3000000306455/blattsalat-im-land-der-hyaenen-das-nest-ist-beschmutzt?ref=rss", - "https://www.derstandard.at/story/3000000306600/usa-warnen-iran-vor-provokationen-bei-manoever-in-meerenge?ref=rss", - "https://www.derstandard.at/story/3000000306381/harry-raet-brooklyn-beckham-zu-enthuellungsbuch-bettina-wulff-wieder-solo?ref=rss", - "https://www.derstandard.at/story/3000000306594/fu223ball-rapid-hofft-im-cup-gegen-ried-auf-den-thorup-effekt?ref=rss", - "https://www.derstandard.at/story/3000000306552/studie-zum-wert-des-fussballs-29-mrd-euro-gesamtgesellschaftlicher-beitrag?ref=rss", - "https://www.derstandard.at/story/3000000305679/hinter-die-fassade-eines-legendaeren-new-yorker-hotels-schauen-?ref=rss", - "https://www.derstandard.at/story/3000000306602/nach-toedlichen-schuessen-auf-us-buerger-pretti-us-justiz-ermittelt-gegen-bundesbeamte?ref=rss", - "https://www.derstandard.at/story/3000000306318/die-schoensten-urlaubsfotos-unserer-userinnen-und-user-der-woche?ref=rss", - "https://www.derstandard.at/story/3000000306497/wie-sich-die-zivilgesellschaft-in-minneapolis-gegen-ice-wehrt?ref=rss", - "https://www.derstandard.at/story/3000000306573/gesundheitsministerium-legt-neuen-plan-zur-krebsbekaempfung-vor?ref=rss", - "https://www.derstandard.at/story/3000000306510/das-ist-das-neugeborene-nashornbaby-im-zoo-schmiding?ref=rss", - "https://www.derstandard.at/story/3000000306489/was-steckt-hinter-trumps-versprechungen-fuer-eine-ukraine-waffenruhe?ref=rss", - "https://www.derstandard.at/story/3000000306563/proell-privat-auf-kurz-gipfel-oesterreichs-politik-braucht-die-gleichen-massstaebe-wie-deutschland?ref=rss", - "https://www.derstandard.at/story/3000000306393/tschechiens-regierung-manoevriert-das-land-zielsicher-in-die-krise?ref=rss", - "https://www.derstandard.at/story/3000000306391/handwerk-sucht-nach-verlorenem-glanz-und-wird-fuendig-bei-buerokratie?ref=rss", - "https://www.derstandard.at/story/3000000306387/trumps-erfuellungsgehilfe-fuer-den-chefsitz-in-der-us-notenbank-fed?ref=rss", - "https://www.derstandard.at/story/3000000306363/europa-ist-die-alternative-zur-macht-der-maechtigen?ref=rss", - "https://www.derstandard.at/story/3000000306336/ein-feldzug-gegen-patrioten?ref=rss", - "https://www.derstandard.at/story/3000000306376/in-der-trump-aera-ist-china-ein-wichtiger-partner-aber-ein-schwieriger?ref=rss" + "https://www.derstandard.at/story/3000000306783/toxische-naehe-was-die-epstein-files-ueber-den-krypto-konflikt-verraten?ref=rss", + "https://www.derstandard.at/story/3000000306800/bleiwerte-der-us-amerikaner-waren-frueher-bis-zu-100-mal-hoeher-als-heute?ref=rss", + "https://www.derstandard.at/story/3000000306752/warum-auf-dem-red-carpet-immer-oefter-penis-kleider-zu-sehen-sind?ref=rss", + "https://www.derstandard.at/story/3000000306811/machtkampf-in-china-mit-unberechenbaren-folgen?ref=rss", + "https://www.derstandard.at/story/3000000306880/-g-e-s-p-e-r-r-t-bis-020226-1800-uhr-ihs-senkt-inflationserwartung-f252r-2026-auf-22-prozent?ref=rss", + "https://www.derstandard.at/story/3000000306829/extremsportler-stirbt-am-weg-zu-einem-der-kaeltesten-bewohnten-orte-der-erde?ref=rss", + "https://www.derstandard.at/story/3000000306819/-sohn-von-norwegens-kronprinzessin-wegen-neuer-vorw252rfe-festgenommen?ref=rss", + "https://www.derstandard.at/story/3000000306766/vorerst-keine-verschlechterung-der-fluggastrechte?ref=rss", + "https://www.derstandard.at/story/3000000306781/stimmungstest-f252r-republikaner-demokraten-gewinnen-in-texas?ref=rss", + "https://www.derstandard.at/story/3000000306882/abbas-setzt-f252r-1-november-wahl-zum-pal228stinensischen-nationalrat-an?ref=rss", + "https://www.derstandard.at/story/3000000306306733/gold-und-silber-im-freien-fall-warum-boersenkurse-langsamer-steigen-als-sie-anschliessend-sinken?ref=rss", + "https://www.derstandard.at/story/3000000306845/afd-politikerin-vermittelte-deutsche-escorts-nach-abu-dhabi-lehnt-aber-fragen-dazu-ab?ref=rss", + "https://www.derstandard.at/story/3000000306764/kontakte-zu-epstein-bringen-kronprinzessin-mette-marit-erneut-ins-zwielicht?ref=rss", + "https://www.derstandard.at/story/3000000306786/in-dieser-bar-in-japan-gibt-es-gratis-drinks-fuer-alle-die-kuendigen-wollen?ref=rss", + "https://www.derstandard.at/story/3000000306746/die-menschen-machen-screenshots-von-uns-ki-agenten-haben-nun-ihr-eigenes-soziales-netzwerk?ref=rss", + "https://www.derstandard.at/story/3000000306238/ingrid-wendl-15-lief-in-cortina-1956-zu-bronze-laufen-lernen-lieben-lachen-loben?ref=rss", + "https://www.derstandard.at/story/3000000306034/rohstoffe-fuer-die-energiewende-bleiben-europas-achillesferse?ref=rss", + "https://www.derstandard.at/story/3000000306759/beim-gaehnen-koennte-unser-gehirn-durchgespuelt-werden?ref=rss", + "https://www.derstandard.at/story/3000000306770/kein-gold-oder-bitcoin-gekauft-vielleicht-war-das-doch-kein-fehler?ref=rss", + "https://www.derstandard.at/story/3000000306816/wie-stuerzt-man-ein-ultrabrutales-regime?ref=rss", + "https://www.derstandard.at/story/3000000306849/grenzoeffnung-in-rafah-trumps-worthuelsen-und-israels-gesten-reichen-nicht?ref=rss", + "https://www.derstandard.at/story/3000000306027/mein-geld-dein-geld-braucht-es-in-beziehungen-ein-gemeinsames-konto-oder-depot?ref=rss", + "https://www.derstandard.at/story/3000000306430/stadthallen-chef-waldner-verrueckte-gibt-es-ueberall-von-rapid-bis-zum-song-contest?ref=rss", + "https://www.derstandard.at/story/3000000306790/donald-trump-als-dirigent-wohlklang-oder-doch-eher-maga-donner?ref=rss", + "https://www.derstandard.at/story/3000000305258/das-virtuelle-labor-im-weltraum?ref=rss", + "https://www.derstandard.at/story/3000000305302/wiener-taxler-kaufen-mehr-e-autos-aber-trotz-verbots-gibt-es-viele-neue-verbrenner?ref=rss", + "https://www.derstandard.at/story/3000000306713/glatteis-chaos-bringt-berliner-buergermeister-ins-schlittern?ref=rss", + "https://www.derstandard.at/story/3000000306674/26-mal-so-viel-wie-mit-golfclubs-so-bereichert-sich-trump-mit-kryptowaehrungen?ref=rss", + "https://www.derstandard.at/story/3000000306774/diese-oeffi-haltestellen-in-wien-bekommen-neue-namen?ref=rss", + "https://www.derstandard.at/story/3000000306082/mckinsey-manager-martin-wrulich-ki-agenten-risiko-usa-oesterreichs-standortproblem?ref=rss", + "https://www.derstandard.at/story/3000000306720/ice-out-pins-und-nackte-tatsachen-das-waren-die-interessantesten-outfits-bei-den-grammys?ref=rss", + "https://www.derstandard.at/story/3000000306791/prozess-gegen-53-jaehrigen-um-absolut-abscheulichen-missbrauch-von-tochter-und-stiefenkelin?ref=rss", + "https://www.derstandard.at/story/3000000306736/los-angeles-verbietet-einweg-druckerpatronen-eu-koennte-bald-nachziehen?ref=rss", + "https://www.derstandard.at/story/3000000306738/erfolg-fuer-marcel-hirscher-fis-muss-bullen-logo-auf-van-deer-skiern-akzeptieren?ref=rss", + "https://www.derstandard.at/story/3000000306756/dna-aus-geparden-mumien-gibt-verschwundener-unterart-eine-neue-chance?ref=rss", + "https://www.derstandard.at/story/3000000306751/vorrangzonen-fuer-photovoltaik-in-salzburg-entlang-der-autobahn?ref=rss", + "https://www.derstandard.at/story/3000000306831/skelettierte-frauenleiche-in-wien-favoriten-entdeckt?ref=rss", + "https://www.derstandard.at/story/3000000306727/iran-erklaert-streitkraefte-europas-zu-terroristischen-gruppen?ref=rss", + "https://www.derstandard.at/story/3000000306726/ice-out-us-stars-protestieren-bei-grammy-gala?ref=rss", + "https://www.derstandard.at/story/3000000306503/beispiel-groenland-wie-kann-man-kindern-kriegsgefahr-erklaeren?ref=rss", + "https://www.derstandard.at/story/3000000306408/ein-social-media-verbot-haette-ich-der-politik-gar-nicht-zugetraut?ref=rss", + "https://www.derstandard.at/story/3000000306397/integration-oder-vernichtung-die-syrischen-kurden-im-neuen-machtgefuege?ref=rss", + "https://www.derstandard.at/story/3000000306562/wo-bleibt-der-aufschrei-gegen-atomwaffen?ref=rss", + "https://www.derstandard.at/story/3000000306547/es-braucht-eine-justizreform-und-zwar-gleich?ref=rss", + "https://www.derstandard.at/story/3000000306479/ganz-grosses-kino?ref=rss", + "https://www.derstandard.at/story/3000000306283/europa-muss-sein-rueckgrat-finden?ref=rss", + "https://www.derstandard.at/story/3000000306507/die-eu-staaten-brauchen-sonderbeauftragten-fuer-die-ukrainegespraeche?ref=rss", + "https://www.derstandard.at/story/3000000306305770/gewessler-wir-europaeer-tun-so-als-waeren-wir-ein-kleines-mauserl-in-irgendeinem-eck?ref=rss", + "https://www.derstandard.at/story/3000000306711/bad-bunnygrammy-dankesrede-fuer-abrechnung-mit-ice?ref=rss", + "https://www.derstandard.at/story/3000000306723/gold-und-silberpreis-gibt-weiter-massiv-nach?ref=rss", + "https://www.derstandard.at/story/3000000306689/wie-novak-djokovic-haarscharf-einer-moeglichen-disqualifikation-entkam?ref=rss", + "https://www.derstandard.at/story/3000000306349/langlebigkeits-expertin-nichts-laesst-frauen-schneller-altern-als-stress?ref=rss", + "https://www.derstandard.at/story/3000000306342/giftiges-klima-und-senile-fuehrung-der-tiefe-fall-des-gaming-giganten-ubisoft?ref=rss", + "https://www.derstandard.at/story/3000000306619/wende-im-todesfall-des-schachstreamers-daniel-naroditsky?ref=rss", + "https://www.derstandard.at/story/3000000306714/nach-protest-und-boykotten-trump-will-washingtons-kennedy-center-fuer-umbau-schliessen?ref=rss" ], - "lastUpdated": "2026-01-31T09:00:00.000Z" + "lastUpdated": "2026-02-02T21:00:00.000Z" } diff --git a/memory/repo-analysis-2026-02-02.md b/memory/repo-analysis-2026-02-02.md new file mode 100644 index 0000000..6a4c906 --- /dev/null +++ b/memory/repo-analysis-2026-02-02.md @@ -0,0 +1,175 @@ +# Forgejo Repository Analysis +**Date:** 2026-02-02 + +## Summary + +Analyzed **42 repositories** across 4 organizations: Cloonar, Paraclub, infrastructure, renovate, and dominik.polakovics. + +### Repository Type Breakdown +| Type | Count | Examples | +|------|-------|----------| +| TYPO3 Projects | 7 | ai-image-alt, diabetes-austria, dialog-relations-website, gbv-aktuell, lena-schilling-website, wohnservice-wien-typo3 (×2) | +| Laravel (PHP) | 1 | amz-api | +| Go Projects | 7 | code, eidas.monitor, fit, phishguard, support-invoiced, updns, soundscape-sync | +| Node/Frontend | 3 | amz-frontend, phishguard-frontend, korean-skin.care | +| Hugo Sites | 4 | cloonar-technologies-website, mehr-leistbaren-wohnraum-schaffen, korean-skin.care, website | +| Nix/Config | 2 | nixos, cloonar-assistant | +| Empty/Archived | 9 | bookmap, ghetto-nixos, gitapi, rantem-api, etc. | + +--- + +## Configuration Status Matrix + +### Active Repositories (with content) + +| Repository | CI | Claude/Agents | Renovate | Tests | Docs | +|------------|:--:|:-------------:|:--------:|:-----:|:----:| +| **TYPO3 Projects** | +| ai-image-alt | ❌ | CLAUDE ✅ | ❌ | ✅ phpunit, Tests/ | ✅ | +| diabetes-austria | drone (loose) | ❌ | ❌ | tests/ | ❌ | +| dialog-relations-website | .gitea | ❌ | ✅ | ❌ | ❌ | +| gbv-aktuell | drone + .gitea | CLAUDE ✅ | ✅ | tests/ | ❌ | +| lena-schilling-website | .gitea | ❌ | ✅ | ❌ | ❌ | +| wohnservice-wien-typo3 | drone + .gitea | CLAUDE + AGENTS ✅ | ✅ | ❌ | ❌ | +| wohnservice-wien-typo3-old | drone | ❌ | ✅ | Tests/ | ❌ | +| **Laravel/PHP** | +| amz-api | drone + .gitea | CLAUDE ✅ | ✅ | ✅ phpunit, tests/ | ❌ | +| ldap2vcard | .gitea | ❌ | ❌ | ❌ | ❌ | +| **Go Projects** | +| code | ❌ | CLAUDE ✅ | ❌ | ❌ | ✅ | +| eidas.monitor | ❌ | AGENTS ✅ | ❌ | ❌ | ✅ | +| fit | .gitea | CLAUDE + AGENTS ✅ | ❌ | ❌ | ✅ | +| phishguard | ❌ | ❌ | ❌ | ❌ | ✅ | +| soundscape-sync | .gitea | ❌ | ❌ | ❌ | ❌ | +| support-invoiced | ❌ | ❌ | ❌ | ❌ | ❌ | +| updns | .github | ❌ | ❌ | ❌ | ❌ | +| ai-mailer | ❌ | CLAUDE ✅ | ❌ | ❌ | ❌ | +| **Frontend/Node** | +| amz-frontend | drone + .gitea | CLAUDE ✅ | ❌ | ❌ | ✅ | +| phishguard-frontend | ❌ | ❌ | ❌ | ❌ | ✅ | +| **Hugo/Static Sites** | +| cloonar-technologies-website | drone + .gitea | ❌ | ❌ | ❌ | ❌ | +| korean-skin.care | .github | ❌ | ❌ | ❌ | ❌ | +| mehr-leistbaren-wohnraum-schaffen | drone | ❌ | ❌ | ❌ | ❌ | +| website | .gitea + .github | CLAUDE + AGENTS ✅ | ❌ | ❌ | ❌ | +| **Infrastructure** | +| nixos | ❌ | CLAUDE + AGENTS ✅ | ❌ | ❌ | ❌ | +| cloonar-assistant | ❌ | ❌ | ❌ | ❌ | ❌ | +| renovate-config | drone + .gitea | ❌ | N/A | ❌ | ❌ | + +--- + +## Key Findings + +### 🔴 Critical Issues + +1. **TYPO3 Projects Inconsistent CI Setup** + - gbv-aktuell, wohnservice-wien-typo3: Both drone AND .gitea workflows (redundant?) + - ai-image-alt: No CI at all despite being a production extension with tests + - diabetes-austria: Has `drone.yml` in root (should be `.drone.yml`) + +2. **Missing Tests in Key Projects** + - wohnservice-wien-typo3: Main production system has NO tests + - dialog-relations-website: No tests + - lena-schilling-website: No tests + - All Go projects except none have test coverage configured + +3. **Renovate Adoption Partial** + - Only 6/27 active repos have renovate.json + - Missing on: ai-image-alt, amz-frontend, all Go projects, Hugo sites + +### 🟡 Inconsistencies + +1. **CI Strategy Mixed** + - Some repos: Drone only + - Some repos: Gitea Actions only + - Some repos: GitHub Actions (for upstream forks?) + - Some repos: Both Drone + Gitea (why?) + +2. **CLAUDE.md vs AGENTS.md** + - 11 repos have CLAUDE.md + - 5 repos have AGENTS.md + - 3 repos have both (fit, nixos, website, wohnservice-wien-typo3) + - No standard template observed + +3. **Documentation Gaps** + - Many repos have README.md but no docs/ folder + - TYPO3 projects especially lack developer documentation + +--- + +## Top 5 Actionable Recommendations + +### 1. **Standardize CI for TYPO3 Projects** (High Impact) +Create a shared `.gitea/workflows/typo3-ci.yml` template with: +- PHP linting +- PHPStan static analysis +- PHPUnit tests (if exist) +- TYPO3 extension validation + +**Quick win:** Fix diabetes-austria's `drone.yml` → `.drone.yml` + +### 2. **Add Renovate to All Active Repos** (Medium Effort, High Value) +Create organization-wide renovate preset in `renovate/renovate-config`: +```json +{ + "extends": ["local>renovate/renovate-config"] +} +``` +Then add minimal `renovate.json` to: +- ai-image-alt, amz-frontend, code, eidas.monitor, fit, phishguard, all Hugo sites + +### 3. **Add Tests to wohnservice-wien-typo3** (High Priority) +This is a production multi-site system with no tests. Start with: +- Functional tests for critical form handlers +- Unit tests for any custom service classes +- At minimum, smoke tests that pages render + +### 4. **Consolidate CI Strategy** (Cleanup) +Pick ONE CI system and standardize: +- **Recommendation:** Gitea Actions (native, no external dependency) +- Migrate all Drone configs to `.gitea/workflows/` +- Remove dual-CI setups + +### 5. **Create CLAUDE.md Template** (Developer Experience) +Standardize AI-assisted development across repos: +```markdown +# Project: {name} +## Tech Stack +## Key Files +## Testing +## Common Tasks +``` +Prioritize for: eidas.monitor, phishguard, soundscape-sync + +--- + +## Quick Wins (< 30 min each) + +| Task | Repo | Effort | +|------|------|--------| +| Rename `drone.yml` → `.drone.yml` | diabetes-austria | 2 min | +| Add `renovate.json` with org preset | ai-image-alt | 5 min | +| Add basic `CLAUDE.md` | eidas.monitor (has AGENTS.md) | 15 min | +| Enable Gitea Actions | ai-image-alt | 10 min | +| Add `renovate.json` | amz-frontend | 5 min | + +--- + +## Empty/Archived Repos (consider cleanup) + +These repos have no content or appear abandoned: +- ai.nvim, bookmap, cloonar-assistant-customers, ghetto-nixos +- gitapi, gitea-runner, imperfect-perfect.com, lego-hetzner-bridge +- mail-autoconfig, rantem-api + +**Recommendation:** Archive or delete to reduce clutter. + +--- + +## Next Steps + +1. Create `.gitea/workflows/` templates in a shared repo +2. Draft organization-wide renovate config +3. Prioritize wohnservice-wien-typo3 test coverage +4. Schedule cleanup of empty repos diff --git a/memory/wind-down-log.json b/memory/wind-down-log.json index 52f3f0b..39edcbf 100644 --- a/memory/wind-down-log.json +++ b/memory/wind-down-log.json @@ -1,5 +1,10 @@ { - "goal": "bed by midnight", + "goal": { + "lastTaskDone": "22:00", + "windDownPeriod": "22:00-00:00", + "bedtime": "00:00", + "windDownNeeded": "~2 hours" + }, "timezone": "Europe/Vienna", "learningPhase": true, "entries": [ @@ -56,6 +61,80 @@ "time": "02:15", "activity": "going to sleep", "note": "watched in bed, surfed internet" + }, + { + "date": "2026-01-31", + "time": "19:04", + "activity": "Gitea → Forgejo migration", + "note": "been working for hours already, now on workflow migration to new actions monorepo", + "taskType": "infrastructure/devops tinkering" + }, + { + "date": "2026-01-31", + "time": "19:13", + "activity": "decided to defer script to tomorrow", + "note": "nudge worked! chose pragmatic stopping point: point to GitHub actions, test workflow, script tomorrow", + "nudgeResult": "accepted" + }, + { + "date": "2026-01-31", + "time": "19:56", + "activity": "finished testing workflows", + "note": "tested 2 workflows, both worked. Final switch to Forgejo planned for tomorrow morning. Task wrapped up successfully before 20:00!" + }, + { + "date": "2026-01-31", + "time": "20:13", + "activity": "starting semi wind-down (watching something)", + "note": "usually finishes before 22:00 - on track for goal!" + }, + { + "date": "2026-01-31", + "time": "22:05", + "activity": "bedtime routine starting (brushing teeth)", + "note": "right on schedule! brain-dumped MCP/automation ideas first, then wind-down" + }, + { + "date": "2026-01-31", + "time": "22:32", + "activity": "in bed winding down", + "note": "cat, tea, audiobook - proper relaxation mode" + }, + { + "date": "2026-01-31", + "time": "23:27", + "activity": "going to sleep", + "note": "33 minutes BEFORE midnight goal!" + }, + { + "date": "2026-02-01", + "time": "19:16", + "activity": "relaxing, watching a series", + "note": "already winding down by 19:16 on Sunday - great start!" + }, + { + "date": "2026-02-01", + "time": "20:36", + "activity": "nose shower done", + "note": "completed after episode ended, reminder at 20:10 worked" + }, + { + "date": "2026-02-01", + "time": "23:45", + "activity": "going to sleep", + "note": "15 minutes before midnight goal - on track!" + }, + { + "date": "2026-02-02", + "time": "01:01", + "activity": "couldn't fall asleep, ate a bit, trying again", + "note": "trouble sleeping despite good wind-down timing" + }, + { + "date": "2026-02-02", + "time": "19:22", + "activity": "setting up ci-templates and testing with gbv-aktuell", + "note": "working on workflow optimization from earlier discussion" } ], "patterns": { @@ -66,6 +145,22 @@ "windDownDuration": "~2.5 hours", "goalMissedBy": "~2h 15min", "notes": "was tinkering with tools until late, then watched TV + internet in bed" + }, + "2026-01-31": { + "stoppedWork": "19:56", + "startedBedRoutine": "22:05", + "actualBedtime": "23:27", + "windDownDuration": "~1.5 hours", + "goalMetBy": "33 minutes early!", + "notes": "nudge at 19:13 helped defer script task. Proper wind-down: TV, then bed with cat/tea/audiobook. Huge improvement from previous night.", + "sleepOutcome": { + "fellAsleepIn": "~30 min", + "briefWake": "5-6am", + "wokeUp": "08:00", + "totalSleep": "~8 hours", + "quality": "good", + "morningFeeling": "better than last 2 days" + } } } }