Add Hoid identity and theater mask avatar 🎭

This commit is contained in:
Agent 2026-02-02 21:50:19 +00:00
parent 4c0199e71b
commit ba43da9541
25 changed files with 1622 additions and 62 deletions

View file

@ -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.

View file

@ -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.

View file

@ -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 <owner/repo> [path] # List files
forgejo cat <owner/repo> <path> # Get file content
forgejo issues <owner/repo> # List open issues
forgejo prs <owner/repo> # List open PRs
forgejo branches <owner/repo> # List branches
forgejo commits <owner/repo> [n] # Recent commits
forgejo search <query> # Search repos
forgejo raw <endpoint> # 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`:

View file

@ -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*

114
WORK-CONTEXT.md Normal file
View file

@ -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*

BIN
avatars/hoid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

79
bin/forgejo Executable file
View file

@ -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 <<EOF
Usage: forgejo <command> [args]
Commands:
repos [org] List repos (optionally filter by org)
files <owner/repo> [path] List files in repo (default: root)
cat <owner/repo> <path> Get file content
issues <owner/repo> List open issues
prs <owner/repo> List open pull requests
branches <owner/repo> List branches
commits <owner/repo> [n] Recent commits (default: 10)
search <query> Search repos
raw <endpoint> 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 <owner/repo> [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 <owner/repo> <path>"; 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 <owner/repo>"; 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 <owner/repo>"; 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 <owner/repo>"; exit 1; }
eval $CURL "${FORGEJO_URL}/api/v1/repos/$2/branches" | grep -oP '"name":"\K[^"]+'
;;
commits)
[ -z "${2:-}" ] && { echo "Usage: forgejo commits <owner/repo> [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 <query>"; exit 1; }
eval $CURL "${FORGEJO_URL}/api/v1/repos/search?q=$2" | grep -oP '"full_name":"\K[^"]+'
;;
raw)
[ -z "${2:-}" ] && { echo "Usage: forgejo raw <endpoint>"; exit 1; }
eval $CURL "${FORGEJO_URL}$2"
;;
*)
usage
;;
esac

BIN
bin/forgejo-mcp Executable file

Binary file not shown.

BIN
drafts/ci-templates.tar.gz Normal file

Binary file not shown.

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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
<?php
namespace Deployer;
// Import shared recipe
require 'https://git.cloonar.com/Cloonar/ci-templates/raw/branch/main/deployer/typo3-recipe.php';
import(__DIR__ . '/servers.yaml');
host('stage')->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 <target>` - Upload release without switching (for staged deploys)
- `release:switch <target>` - Switch to uploaded release
- `deploy <target>` - 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)

View file

@ -0,0 +1,195 @@
<?php
/**
* Cloonar TYPO3 Deployer Recipe
*
* Shared deployment configuration for all TYPO3 projects.
*
* Usage in project's build/deploy.php:
* require __DIR__ . '/../../vendor/cloonar/ci-templates/deployer/typo3-recipe.php';
* // Or via raw git URL during transition:
* // require 'https://git.cloonar.com/Cloonar/ci-templates/raw/branch/main/deployer/typo3-recipe.php';
*
* import(__DIR__ . '/servers.yaml');
*
* host('stage')->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');

View file

@ -0,0 +1,29 @@
<?php
/**
* Example project deploy.php
*
* This is what each TYPO3 project's build/deploy.php looks like
* when using the shared recipe.
*/
namespace Deployer;
// Option 1: Require from git raw URL (works immediately, no composer)
require 'https://git.cloonar.com/Cloonar/ci-templates/raw/branch/main/deployer/typo3-recipe.php';
// Option 2: Via composer (after adding to require-dev)
// require __DIR__ . '/../vendor/cloonar/ci-templates/deployer/typo3-recipe.php';
// Import project-specific server config
import(__DIR__ . '/servers.yaml');
// Project-specific cachetool sockets
host('stage')
->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);

View file

@ -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

View file

@ -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 }}

View file

@ -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 }}

16
memory/2026-02-01.md Normal file
View file

@ -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

102
memory/2026-02-02.md Normal file
View file

@ -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

27
memory/ai-news-seen.json Normal file
View file

@ -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"
}

44
memory/brain-dump.json Normal file
View file

@ -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"
}
]
}

View file

@ -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."
}
}

View file

@ -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"
}

View file

@ -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

View file

@ -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"
}
}
}
}