Failed to authenticate. API Error: 401 {"type":"error","error":{"type":"authentication_error","message":"Invalid authentication credentials"},"request_id":"req_011Cb1xC2T1zeEq84Vd9hUrb"}
[2026-05-14] vault sync
This commit is contained in:
@@ -0,0 +1,115 @@
|
|||||||
|
# Plan: Executor as Spawned Worker — Orchestrator Pattern
|
||||||
|
|
||||||
|
**Date**: 2026-05-14
|
||||||
|
**Status**: Planning
|
||||||
|
**Parent**: [[executor-v2-redesign]]
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Currently Eagle (орёл) handles executor tasks inline in the conversation:
|
||||||
|
- Takes on the task directly in the discussion thread
|
||||||
|
- Asks Alex for permissions mid-task, blocking the flow
|
||||||
|
- Creates tight coupling between conversational layer and execution layer
|
||||||
|
|
||||||
|
## Desired Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Alex → Eagle (orchestrator, in Zulip topic)
|
||||||
|
↓ spawn
|
||||||
|
Executor (worker process, separate Hermes session)
|
||||||
|
↓ writes to Zulip #executor topic
|
||||||
|
Eagle monitors #executor
|
||||||
|
↓ reads recent output (not full thread)
|
||||||
|
↓ auto-approve obvious decisions
|
||||||
|
↓ escalate ambiguous → @mention Alex
|
||||||
|
```
|
||||||
|
|
||||||
|
## Roles
|
||||||
|
|
||||||
|
### Eagle (Orchestrator)
|
||||||
|
- Receives task request in any Zulip topic
|
||||||
|
- Queues task to `executor_queue` DB table
|
||||||
|
- Spawns Executor worker via `hermes cron run` or similar
|
||||||
|
- Monitors `#executor` topic for Executor messages
|
||||||
|
- Reads only recent N messages (sliding window, not full thread)
|
||||||
|
- Auto-approval logic:
|
||||||
|
- "create worktree" → approve
|
||||||
|
- "run tests" → approve
|
||||||
|
- "open draft PR" → approve
|
||||||
|
- "push to branch" → approve
|
||||||
|
- "modify unrelated file" → DENY + escalate
|
||||||
|
- "post Asana comment" → escalate to Alex
|
||||||
|
- Any scope expansion → escalate to Alex
|
||||||
|
- @mention Alex only when ambiguous or high-risk
|
||||||
|
|
||||||
|
### Executor (Worker)
|
||||||
|
- Separate Hermes session/process
|
||||||
|
- Bot identity: "Executor" or "Исполнитель" (not Eagle)
|
||||||
|
- Communicates via `#executor` Zulip topic
|
||||||
|
- Posts structured messages: `[REQUEST: <action>]`, `[STATUS: <phase>]`, `[DONE: <result>]`
|
||||||
|
- Does NOT ask Alex directly — all escalations go to Eagle
|
||||||
|
- On permission denied → explain why, ask what to do differently
|
||||||
|
|
||||||
|
## Message Protocol (Executor → Eagle)
|
||||||
|
|
||||||
|
```
|
||||||
|
[STATUS: investigation] Analysing bug GID 123456...
|
||||||
|
[REQUEST: create_worktree] Branch: fix/tab-preview-stuck
|
||||||
|
[REQUEST: run_tests] Scheme: macOS UI Tests CI
|
||||||
|
[STATUS: pr_open] Draft PR: https://github.com/...
|
||||||
|
[DONE: pr_ready] PR #1234 opened, CI green, ready for review
|
||||||
|
[ESCALATE: scope_expansion] Found unrelated issue in TabBar.swift — should I fix it?
|
||||||
|
```
|
||||||
|
|
||||||
|
## Auto-approval Rules (Eagle)
|
||||||
|
|
||||||
|
| Message type | Auto action |
|
||||||
|
|---|---|
|
||||||
|
| `[REQUEST: create_worktree]` | ✅ approve silently |
|
||||||
|
| `[REQUEST: run_build]` | ✅ approve silently |
|
||||||
|
| `[REQUEST: run_tests]` | ✅ approve silently |
|
||||||
|
| `[REQUEST: open_draft_pr]` | ✅ approve silently |
|
||||||
|
| `[REQUEST: push_branch]` | ✅ approve silently |
|
||||||
|
| `[REQUEST: post_asana_comment]` | ⚠️ show Alex, wait |
|
||||||
|
| `[REQUEST: merge_pr]` | ❌ always escalate |
|
||||||
|
| `[ESCALATE: *]` | ⚠️ always escalate |
|
||||||
|
| `[ESCALATE: scope_expansion]` | ❌ deny by default + notify Alex |
|
||||||
|
|
||||||
|
## Eagle Monitoring Loop
|
||||||
|
|
||||||
|
Eagle does NOT run a continuous monitoring process. Instead:
|
||||||
|
- Option A: Hermes cron (every 2 min) → check `#executor` for unprocessed `[REQUEST]` or `[ESCALATE]` messages → process them
|
||||||
|
- Option B: Webhook trigger on Zulip `#executor` stream → Eagle session wakes up
|
||||||
|
|
||||||
|
**Recommendation: Option A** (simpler, uses existing cron infra)
|
||||||
|
|
||||||
|
## Context Window Management
|
||||||
|
|
||||||
|
Eagle reads Executor's thread with sliding window:
|
||||||
|
- Last 20 messages from `#executor` for the current `executor_run_id`
|
||||||
|
- Filter by `run_id` tag in messages to handle concurrent runs
|
||||||
|
- Structured messages allow O(1) parsing without LLM
|
||||||
|
|
||||||
|
## Relation to Executor v2
|
||||||
|
|
||||||
|
This is a UI/coordination layer ON TOP of executor-v2 bash daemons:
|
||||||
|
- executor-runner.sh still does the heavy lifting
|
||||||
|
- Eagle becomes the gatekeeper/orchestrator that monitors and approves
|
||||||
|
- Executor worker sends structured messages via Zulip API (or hermes platform API)
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
1. Define message protocol (structured tags)
|
||||||
|
2. Add Zulip message sender to executor worker prompts
|
||||||
|
3. Create Eagle monitoring cron (2 min interval, `#executor` stream)
|
||||||
|
4. Implement auto-approval logic in Eagle monitoring prompt
|
||||||
|
5. Add escalation → `@mention Alex` in monitoring prompt
|
||||||
|
6. Test with a real executor run in dry-run mode
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- How does Executor "spawn" as a separate bot persona?
|
||||||
|
- Option A: Same Hermes instance, different SOUL/persona config
|
||||||
|
- Option B: Separate Hermes gateway with "Executor" identity
|
||||||
|
- Option C: `hermes run -p executor-worker.md` as subprocess (non-interactive)
|
||||||
|
- Concurrent runs: Eagle needs to track run_id per conversation thread
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
# Plan: GHA CI Executor — macOS Build + Peekaboo
|
||||||
|
|
||||||
|
**Date**: 2026-05-14
|
||||||
|
**Status**: Planning
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Migrate executor workers from Eagle's local Mac to GitHub Actions macOS runners:
|
||||||
|
- Each task spawns a GHA job
|
||||||
|
- Job installs Claude Code + Peekaboo + builds the app
|
||||||
|
- Executor (Claude Code) runs the fix, opens PR, posts report to Asana
|
||||||
|
- Eagle orchestrates: queues task → triggers GHA → monitors job → handles result
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Eagle (orchestrator) → triggers GHA workflow via gh CLI
|
||||||
|
↓
|
||||||
|
GHA macOS runner (macos-15-xlarge or self-hosted)
|
||||||
|
↓
|
||||||
|
Install: Xcode, Claude Code, Peekaboo, Simulator
|
||||||
|
↓
|
||||||
|
claude -p executor-worker.md (non-interactive)
|
||||||
|
↓
|
||||||
|
Claude Code: fix → build → UI test via Peekaboo → PR
|
||||||
|
↓
|
||||||
|
Post result to Asana (gh pr comment + asana-bot)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Analysis Agent (Cloud-side)
|
||||||
|
|
||||||
|
Separate from the executor worker:
|
||||||
|
- Runs in Claude Cloud (or Eagle's Mac)
|
||||||
|
- Picks task tagged "обработать" from Asana
|
||||||
|
- Reads full task context (description, stories, user reports)
|
||||||
|
- Generates detailed executor prompt (investigation plan + acceptance criteria)
|
||||||
|
- Triggers GHA workflow with this prompt as input
|
||||||
|
|
||||||
|
## GHA Workflow Design
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Executor Worker
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
task_gid:
|
||||||
|
description: 'Asana task GID'
|
||||||
|
executor_prompt:
|
||||||
|
description: 'Base64-encoded executor prompt'
|
||||||
|
branch_name:
|
||||||
|
description: 'Git branch to create'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
executor:
|
||||||
|
runs-on: macos-15 # or self-hosted M-series
|
||||||
|
timeout-minutes: 120
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Claude Code
|
||||||
|
run: npm install -g @anthropic-ai/claude-code
|
||||||
|
- name: Install Peekaboo
|
||||||
|
run: # brew install or download binary
|
||||||
|
- name: Bootstrap Xcode
|
||||||
|
run: # select Xcode version, accept license
|
||||||
|
- name: Run Executor
|
||||||
|
env:
|
||||||
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_CI_KEY }}
|
||||||
|
ASANA_TOKEN: ${{ secrets.ASANA_CI_TOKEN }}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
echo "${{ inputs.executor_prompt }}" | base64 -d > /tmp/prompt.md
|
||||||
|
claude -p /tmp/prompt.md \
|
||||||
|
--branch "${{ inputs.branch_name }}" \
|
||||||
|
--no-interactive
|
||||||
|
- name: Post Result
|
||||||
|
if: always()
|
||||||
|
run: # Post summary to Asana task + open PR if not already
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Questions to Investigate
|
||||||
|
|
||||||
|
1. **macOS runner availability**: GitHub-hosted `macos-15` or `macos-15-xlarge`?
|
||||||
|
- Large runners cost more but needed for Xcode + Simulator
|
||||||
|
- Self-hosted M4 Mac runner on Eagle itself? (no cloud cost but uses Eagle)
|
||||||
|
|
||||||
|
2. **Peekaboo on GHA**: Does Peekaboo work on GHA macOS runners?
|
||||||
|
- Needs Screen Recording permission → headless runner may not grant it
|
||||||
|
- Alternative: use `xcrun simctl` directly for UI tests
|
||||||
|
- Or: spin up VM via ddg-vm (if GHA runner has the VM tool)
|
||||||
|
|
||||||
|
3. **Build time**: Full prod build on GHA macos-15?
|
||||||
|
- Estimated 15-30 min cold, 5-10 min with cache
|
||||||
|
- DerivedData caching via `actions/cache` is critical
|
||||||
|
|
||||||
|
4. **Claude Code non-interactive**: Does `claude -p` (non-interactive) work for complex fix tasks?
|
||||||
|
- Yes — `claude -p` is the standard non-interactive mode
|
||||||
|
- Needs `--dangerously-skip-permissions` or pre-approved tool config
|
||||||
|
|
||||||
|
5. **Secrets**: Need in GHA:
|
||||||
|
- `ANTHROPIC_CI_KEY` — separate key for CI (not user key)
|
||||||
|
- `ASANA_CI_TOKEN` — for posting results
|
||||||
|
- `GITHUB_TOKEN` — auto-provided
|
||||||
|
|
||||||
|
## Validation Steps
|
||||||
|
|
||||||
|
Before full implementation:
|
||||||
|
1. Test prod build on `macos-15` runner: does it succeed without signing?
|
||||||
|
2. Test Peekaboo install + basic screenshot on GHA runner
|
||||||
|
3. Test `claude -p` with a simple prompt on GHA runner
|
||||||
|
4. Verify DerivedData caching works (build time < 10 min cached)
|
||||||
|
|
||||||
|
## Phased Implementation
|
||||||
|
|
||||||
|
### Phase 1: Validation (1-2 days)
|
||||||
|
- Create a test GHA workflow that: checks out repo, builds, runs basic UI test
|
||||||
|
- Verify Peekaboo accessibility permissions on headless runner
|
||||||
|
- Document what works / what doesn't
|
||||||
|
|
||||||
|
### Phase 2: Claude Code Integration (2-3 days)
|
||||||
|
- Add Claude Code step to workflow
|
||||||
|
- Test with a trivial fix prompt
|
||||||
|
- Verify PR creation from GHA bot context
|
||||||
|
|
||||||
|
### Phase 3: Analysis Agent (3-5 days)
|
||||||
|
- Cloud-side agent that picks Asana tasks + generates prompts
|
||||||
|
- Triggers GHA workflow via `gh workflow run`
|
||||||
|
- Monitors job, posts result to Asana
|
||||||
|
|
||||||
|
### Phase 4: Eagle Integration (1 day)
|
||||||
|
- Eagle can trigger Phase 3 analysis agent on demand
|
||||||
|
- `#executor` topic shows GHA job status
|
||||||
|
- Auto-approval for GHA-spawned actions
|
||||||
|
|
||||||
|
## Relation to Sentry Bot
|
||||||
|
|
||||||
|
Sentry bot pattern (already working):
|
||||||
|
- Cloud agent monitors Sentry errors
|
||||||
|
- Generates structured report
|
||||||
|
- Posts to Asana/Slack
|
||||||
|
|
||||||
|
GHA executor follows same pattern:
|
||||||
|
- Cloud agent picks Asana bug tasks
|
||||||
|
- Generates fix prompt
|
||||||
|
- GHA runner executes fix + validation
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- GHA macos-15 runners do NOT have Xcode pre-cached — each run installs from scratch
|
||||||
|
- Self-hosted runner on Eagle avoids this but ties up the main Mac
|
||||||
|
- DuckDuckGo likely has existing GHA macOS setup — check existing workflows first
|
||||||
|
- Peekaboo may need `tccutil` or System Preferences pre-grant on runner
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Plan: (no topic) Thread Routing
|
||||||
|
|
||||||
|
**Date**: 2026-05-14
|
||||||
|
**Status**: Planning
|
||||||
|
**Priority**: Medium
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
When Alex sends a message in Zulip's `(no topic)` thread (the default unnamed thread), it usually means one of two things:
|
||||||
|
1. He missed the correct topic — it should go to the most recent active thread in the same stream
|
||||||
|
2. He's starting a genuinely new discussion
|
||||||
|
|
||||||
|
Eagle needs to automatically detect which case it is and act accordingly.
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
### Detection Logic
|
||||||
|
|
||||||
|
**Step 1: Find most recent active thread in stream**
|
||||||
|
- Query Zulip API for recent messages in the current stream (last N hours)
|
||||||
|
- Exclude `(no topic)` thread itself
|
||||||
|
- Get the thread with the most recent message → `candidate_thread`
|
||||||
|
|
||||||
|
**Step 2: Relevance check**
|
||||||
|
- Take first ~200 chars of `(no topic)` message
|
||||||
|
- Take thread name + last 2-3 messages from `candidate_thread` as context
|
||||||
|
- Ask LLM (cheap/fast call): "Is this message topically related to this thread? yes/no + confidence"
|
||||||
|
|
||||||
|
**Step 3: Action**
|
||||||
|
|
||||||
|
If **related** (confidence > 0.7):
|
||||||
|
- Quote Alex's message in `candidate_thread` with attribution: `[Cross-posted from (no topic) — @Alex]`
|
||||||
|
- Reply in `(no topic)`: `Переношу в тред «{thread_name}» ↗`
|
||||||
|
- Continue discussion in `candidate_thread`
|
||||||
|
- ⚠️ Coordination: check if another Eagle instance is active in `candidate_thread` (see below)
|
||||||
|
|
||||||
|
If **not related** (new topic):
|
||||||
|
- Rename `(no topic)` thread to a descriptive topic name (2-5 words, inferred from message)
|
||||||
|
- Reply normally in the renamed thread
|
||||||
|
|
||||||
|
### Multi-instance Coordination
|
||||||
|
|
||||||
|
Eagle may have multiple instances running in different Zulip topics simultaneously. To avoid conflicts when routing to an existing thread:
|
||||||
|
|
||||||
|
**Option A: DB mutex** (preferred)
|
||||||
|
- `zulip_thread_locks` table: `(stream, topic, locked_by_session, locked_at)`
|
||||||
|
- Before posting in another thread → INSERT lock (fail on conflict)
|
||||||
|
- Release lock after posting
|
||||||
|
- Stale lock (>5 min) → auto-expire
|
||||||
|
|
||||||
|
**Option B: Message-based handoff**
|
||||||
|
- Before cross-posting → send a "silent" message to `candidate_thread` (deletable): `[Eagle routing incoming from (no topic)]`
|
||||||
|
- If another instance replies with `[ACK]` → let it handle
|
||||||
|
- If no reply in 30s → proceed
|
||||||
|
|
||||||
|
**Recommendation: Option A** — simpler, no timing issues.
|
||||||
|
|
||||||
|
### Multi-instance Awareness
|
||||||
|
|
||||||
|
Each Eagle instance should know its own Zulip topic context. When spawned in a topic, it registers itself in `zulip_thread_locks`. Eagle reads this to avoid double-posting.
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
1. Add Zulip stream message history query to Eagle's toolkit (REST API call via hermes)
|
||||||
|
2. Create `zulip_thread_locks` table in `personal_os` DB
|
||||||
|
3. Write topic routing logic as a function in the Eagle prompt or as a cron-triggered script
|
||||||
|
4. Test: send message in `(no topic)`, verify routing + rename behavior
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
- Does Eagle get invoked *automatically* for every (no topic) message, or only when mentioned?
|
||||||
|
- Is Eagle a single process or multiple instances per stream? (affects coordination complexity)
|
||||||
|
- What's the stream scope? Only `master`? Or all streams?
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Current session: this `(no topic)` thread in `master` stream should be renamed to something like `planning: system improvements 2026-05`
|
||||||
|
- Zulip topic rename API: `PATCH /api/v1/messages/{message_id}` with `topic` param (requires first message ID of the thread)
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# Plan: Time Machine Backup via SSH Tunnel
|
||||||
|
|
||||||
|
**Date**: 2026-05-14
|
||||||
|
**Status**: Planning
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Kraken (RPi5) is in a different network (VPS via tunnel, IP 91.207.28.205:2223).
|
||||||
|
Time Machine is configured for `smb://timemachine@kraken._smb._tcp.local./TimeMachine` via mDNS — not reachable when Kraken is on a remote network.
|
||||||
|
|
||||||
|
Last backup: May 12, 2026. Backup destination is 3.98 TB quota.
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### Option A: SSH Tunnel → SMB Port Forward (complex, fragile)
|
||||||
|
```bash
|
||||||
|
ssh -L 445:localhost:445 kraken@91.207.28.205 -p 2223
|
||||||
|
```
|
||||||
|
Then add TM destination: `smb://timemachine@localhost/TimeMachine`
|
||||||
|
|
||||||
|
**Problems:**
|
||||||
|
- SMB over SSH tunnel is slow for large backups
|
||||||
|
- macOS blocks port 445 forwarding without SIP disabled
|
||||||
|
- TM doesn't handle SMB reconnects well → corrupted sparsebundles
|
||||||
|
- Port 445 requires root privileges on Mac side
|
||||||
|
|
||||||
|
### Option B: Netatalk (AFP over TCP) via Tunnel (legacy, not recommended)
|
||||||
|
AFP support was removed from TM in macOS Ventura+. Not viable.
|
||||||
|
|
||||||
|
### Option C: restic/rclone to remote (not TM, but works)
|
||||||
|
Replace TM for Kraken backup with `restic`:
|
||||||
|
```bash
|
||||||
|
# On Eagle:
|
||||||
|
restic -r sftp:kraken@91.207.28.205:2223/backups/eagle backup ~/
|
||||||
|
```
|
||||||
|
Works over SSH, incremental, encrypted. But not native TM UX.
|
||||||
|
|
||||||
|
### Option D: Wireguard VPN between Eagle and Kraken (best)
|
||||||
|
- Kraken runs Wireguard server → Eagle connects as peer
|
||||||
|
- Eagle sees Kraken's SMB share at VPN IP (e.g. 10.0.0.2)
|
||||||
|
- TM configured to use VPN IP instead of mDNS
|
||||||
|
- Survives network changes, works from any location
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
1. Install Wireguard on Kraken (Docker or host)
|
||||||
|
2. Configure Eagle as Wireguard peer
|
||||||
|
3. Expose Samba port on Kraken's VPN interface
|
||||||
|
4. Update TM destination to VPN IP
|
||||||
|
|
||||||
|
### Option E: Keep WD12-TimeMachine local + periodic rsync to Kraken
|
||||||
|
- WD12-TimeMachine (6.01 TB) is LOCAL — backups fine
|
||||||
|
- Rsync the TM sparsebundle to Kraken weekly
|
||||||
|
- Kraken = offsite copy, not real-time TM
|
||||||
|
|
||||||
|
## Recommendation
|
||||||
|
|
||||||
|
**Short term**: Do nothing — WD12 local TM is working fine (last backup May 12).
|
||||||
|
|
||||||
|
**Medium term (1-2 weeks)**: Option D (Wireguard VPN)
|
||||||
|
- Best reliability, proper TM over SMB
|
||||||
|
- Kraken already has Docker — easy to add Wireguard container
|
||||||
|
|
||||||
|
**Alternatively**: Option E (rsync TM bundle to Kraken) — simpler, less real-time
|
||||||
|
|
||||||
|
## Current Status
|
||||||
|
|
||||||
|
- WD12-TimeMachine: local, 6.01 TB, working ✅
|
||||||
|
- Kraken SMB TM: 3.98 TB, last backup May 12, NOT reachable from Eagle currently ⚠️
|
||||||
|
- Kraken network: accessible only via SSH tunnel (VPS 91.207.28.205:2223)
|
||||||
|
|
||||||
|
## Next Steps (when ready to implement)
|
||||||
|
|
||||||
|
1. Decision: Wireguard vs rsync-only
|
||||||
|
2. If Wireguard: deploy wg-easy Docker container on Kraken
|
||||||
|
3. Configure Eagle as peer (macOS Wireguard app or CLI)
|
||||||
|
4. Test SMB reach over VPN
|
||||||
|
5. Update TM destination
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Don't try SMB over port-forward — port 445 blocked by macOS SIP on loopback
|
||||||
|
- Kraken SSH tunnel is stable (CF tunnel via cloudflared)
|
||||||
|
- TrueNAS (Taiga) also available for backup — might be better option if Taiga on local net
|
||||||
Reference in New Issue
Block a user