[2026-05-22] taiga sync: .obsidian/community-plugins.json .obsidian/graph.json .obsidian/hotkeys.json .obsidian/plugins/obsidian-git/data.json .obsidian/plugins/obsidian-git/main.js
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
# Investigation: Asana Sync DNS Failure
|
||||
|
||||
**Date**: 2026-05-14
|
||||
**Status**: Identified, not fixed
|
||||
|
||||
## Symptom
|
||||
|
||||
`status.md` датирован Apr 30 — данные устарели на ~2 недели.
|
||||
|
||||
## Root Cause
|
||||
|
||||
`sync.js` (heartbeat pipeline) получает `ENOTFOUND app.asana.com` при каждом запросе к Asana API начиная с утра 2026-05-14 05:19.
|
||||
|
||||
Из `~/Developer/personal-os/logs/heartbeat.log`:
|
||||
```
|
||||
[asana] fetchMyTasks: error in section active: getaddrinfo ENOTFOUND app.asana.com
|
||||
[sync] Fatal error: getaddrinfo ENOTFOUND app.asana.com
|
||||
```
|
||||
|
||||
**Это не проблема токена** — это DNS не резолвит `app.asana.com`.
|
||||
|
||||
## What Needs Investigation
|
||||
|
||||
- Почему DNS не резолвит `app.asana.com` с Eagle? (другие хосты работают?)
|
||||
- Это временная проблема сети или системный DNS сбой?
|
||||
- Когда последний успешный sync?
|
||||
|
||||
## Next Step
|
||||
|
||||
Проверить DNS вручную: `nslookup app.asana.com`, `curl -v https://app.asana.com` — и понять масштаб проблемы перед любыми правками.
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: 'Контакт: Андрей Исаев (DDG Бишкек)'
|
||||
aliases:
|
||||
- контакт-андрей-исаев-ddg
|
||||
- keep-import
|
||||
tags:
|
||||
- work
|
||||
- contacts
|
||||
- ddg
|
||||
updated: '2026-05-17'
|
||||
---
|
||||
# Контакт: Андрей Исаев DDG
|
||||
|
||||
Коллега по DDG из Бишкека
|
||||
@@ -1,486 +0,0 @@
|
||||
# Plan: GHA CI Executor — AI-Powered Bug Fix Pipeline
|
||||
|
||||
**Date**: 2026-05-14
|
||||
**Status**: Planning
|
||||
**Owner**: Alex M
|
||||
|
||||
## Related
|
||||
|
||||
- **Asana task: MM Bots**: https://app.asana.com/1/137249556945/project/908478224964033/task/1214799615211686
|
||||
- **Mattermost Migration Plan**: [[mattermost-migration]]
|
||||
- **Executor Orchestrator Redesign**: [[executor-orchestrator-redesign]]
|
||||
- **Executor Orchestrator Redesign**: [[executor-orchestrator-redesign]]
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
Build an AI-powered bug fix pipeline that scales from personal use to org-wide deployment:
|
||||
|
||||
- Tasks arrive from multiple sources (Asana, Sentry, `@claude` in PRs)
|
||||
- Analysis Agent generates a rich, context-aware prompt
|
||||
- GHA executes Claude Code on the right runner (macOS, Linux, self-hosted)
|
||||
- Results posted back to source (Asana task, Sentry issue, PR comment, MM channel)
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
Three distinct layers, each independently replaceable:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ TRIGGER LAYER │
|
||||
│ │
|
||||
│ Asana task tagged "обработать" │
|
||||
│ Sentry crash report [→ AI Analysis Placeholder] │
|
||||
│ GitHub: @claude mention in PR/Issue (native, no glue) │
|
||||
│ Eagle: on-demand ("go [GID]") │
|
||||
└───────────────┬─────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ ORCHESTRATION LAYER — Prefect │
|
||||
│ │
|
||||
│ Flow: execute_task(source, task_id) │
|
||||
│ 1. Analysis Agent (Claude) → structured prompt │
|
||||
│ 2. Select execution target (repo + runner type) │
|
||||
│ 3. Dispatch GHA workflow via `gh workflow run` │
|
||||
│ 4. Monitor job status (poll / webhook) │
|
||||
│ 5. On complete: post result to source + notify MM │
|
||||
│ │
|
||||
│ State: PostgreSQL | UI: Prefect dashboard │
|
||||
│ Concurrency: max N in-flight | Retry: built-in │
|
||||
└───────────────┬─────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ EXECUTION LAYER — GitHub Actions │
|
||||
│ │
|
||||
│ uses: anthropics/claude-code-action@v1 (official GA) │
|
||||
│ │
|
||||
│ Runner selection by task type: │
|
||||
│ ├── macOS bug (iOS/macOS): macos-15 or self-hosted Mac │
|
||||
│ ├── macOS visual repro: ddg-vm (Peekaboo inside VM) │
|
||||
│ └── General (Linux): ubuntu-latest │
|
||||
│ │
|
||||
│ Output: draft PR + result artifact (JSON) │
|
||||
└───────────────┬─────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ NOTIFICATION LAYER — Mattermost / Zulip │
|
||||
│ │
|
||||
│ executor-bot → #executor-queue (MM) │
|
||||
│ Summary: task, PR link, test result, confidence score │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Analysis Agent
|
||||
|
||||
Runs inside Prefect flow (on Eagle or serverless), before GHA dispatch:
|
||||
|
||||
**Input:** task source (Asana GID / Sentry issue ID / GitHub issue URL)
|
||||
|
||||
**Steps:**
|
||||
1. Read full task context: description, comments, user reports, related code
|
||||
2. Search codebase for relevant files (git grep, symbol search)
|
||||
3. Generate structured executor prompt:
|
||||
- Problem statement
|
||||
- Reproduction steps
|
||||
- Investigation plan (files to check, hypotheses)
|
||||
- Acceptance criteria (what "fixed" looks like)
|
||||
- Test strategy (unit / UI / Peekaboo visual)
|
||||
4. Classify task → select runner type
|
||||
|
||||
**Output:** `executor_prompt.md` + runner classification + branch name
|
||||
|
||||
---
|
||||
|
||||
## Execution Layer: `claude-code-action@v1`
|
||||
|
||||
Anthropic's official GA action (released 2025). Key advantages over manual `claude -p`:
|
||||
- Handles GitHub context natively (PR diffs, issue body, comments)
|
||||
- Auto-posts results as PR review comments
|
||||
- `--dangerously-skip-permissions` not needed — action has scoped GitHub token
|
||||
- Works with `workflow_dispatch` for external triggers
|
||||
|
||||
```yaml
|
||||
name: Executor Worker
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
task_gid:
|
||||
description: 'Asana task GID'
|
||||
required: true
|
||||
executor_prompt:
|
||||
description: 'Base64-encoded executor prompt'
|
||||
required: true
|
||||
branch_name:
|
||||
description: 'Git branch to create'
|
||||
required: true
|
||||
runner_type:
|
||||
description: 'macos-15 | ubuntu-latest | self-hosted'
|
||||
default: 'macos-15'
|
||||
|
||||
jobs:
|
||||
executor:
|
||||
runs-on: ${{ inputs.runner_type }}
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Decode prompt
|
||||
run: echo "${{ inputs.executor_prompt }}" | base64 -d > /tmp/executor-prompt.md
|
||||
|
||||
- name: Run Claude Code
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
prompt: /tmp/executor-prompt.md
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_CI_KEY }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
claude_args: |
|
||||
--model claude-opus-4-7
|
||||
--max-turns 30
|
||||
|
||||
- name: Post result to Asana
|
||||
if: always()
|
||||
env:
|
||||
ASANA_TOKEN: ${{ secrets.ASANA_CI_TOKEN }}
|
||||
run: |
|
||||
# Post PR link + test summary to Asana task ${{ inputs.task_gid }}
|
||||
# See scripts/post-executor-result.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Orchestration Layer: Prefect
|
||||
|
||||
### Why Prefect (not Temporal, not Kestra)
|
||||
|
||||
| | Prefect | Temporal | Kestra |
|
||||
|---|---|---|---|
|
||||
| Learning curve | Low (Python decorators) | Very high (~1 month) | Medium (YAML) |
|
||||
| AI/agent workflows | ✅ Native Python | ⚠️ Lots of boilerplate | ❌ YAML gets messy |
|
||||
| Self-hosted | ✅ Docker, simple | ✅ Complex cluster | ✅ Docker |
|
||||
| Retry / state | ✅ Built-in | ✅ Indestructible | ✅ Kafka-backed |
|
||||
| Org-scale RBAC | Prefect Cloud (paid) | ✅ | ✅ |
|
||||
| **Fit for this use case** | **✅ Best** | ⚠️ Overkill | ⚠️ Wrong paradigm |
|
||||
|
||||
Temporal is overkill: our tasks complete in <2h, don't need month-long replay guarantees.
|
||||
Kestra is designed for ETL/data, not AI agent orchestration.
|
||||
|
||||
### Prefect Flow Design
|
||||
|
||||
```python
|
||||
from prefect import flow, task
|
||||
import subprocess, base64, time
|
||||
|
||||
@task(retries=2, retry_delay_seconds=60)
|
||||
def run_analysis_agent(task_source: str, task_id: str) -> dict:
|
||||
"""Claude analyzes the task and returns structured executor prompt."""
|
||||
# Runs Claude (hermes or claude -p) with task context
|
||||
# Returns: {prompt_b64, branch_name, runner_type, confidence}
|
||||
|
||||
@task
|
||||
def dispatch_gha(repo: str, prompt_b64: str, branch: str, runner: str, task_gid: str) -> str:
|
||||
"""Triggers GHA workflow_dispatch. Returns run_id."""
|
||||
result = subprocess.run([
|
||||
"gh", "workflow", "run", "executor-worker.yml",
|
||||
"--repo", repo,
|
||||
"-f", f"executor_prompt={prompt_b64}",
|
||||
"-f", f"branch_name={branch}",
|
||||
"-f", f"runner_type={runner}",
|
||||
"-f", f"task_gid={task_gid}",
|
||||
], capture_output=True, text=True)
|
||||
return extract_run_id(result.stdout)
|
||||
|
||||
@task(retries=60, retry_delay_seconds=60)
|
||||
def wait_for_gha(repo: str, run_id: str) -> dict:
|
||||
"""Polls GHA job until complete. Returns result artifact."""
|
||||
status = get_gha_status(repo, run_id)
|
||||
if status in ("in_progress", "queued"):
|
||||
raise Exception("Still running") # triggers retry
|
||||
return get_result_artifact(repo, run_id)
|
||||
|
||||
@task
|
||||
def post_result(source: str, task_id: str, result: dict):
|
||||
"""Posts PR link + summary back to Asana/Sentry/MM."""
|
||||
...
|
||||
|
||||
@flow(name="executor-task")
|
||||
def execute_task(source: str, task_id: str, repo: str = "duckduckgo/apple-browsers"):
|
||||
analysis = run_analysis_agent(source, task_id)
|
||||
run_id = dispatch_gha(repo, analysis["prompt_b64"], analysis["branch"], analysis["runner_type"], task_id)
|
||||
result = wait_for_gha(repo, run_id)
|
||||
post_result(source, task_id, result)
|
||||
```
|
||||
|
||||
### Prefect Deployment
|
||||
|
||||
Self-hosted on Eagle (or VPS) — single Docker container:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
prefect-server:
|
||||
image: prefecthq/prefect:3-latest
|
||||
ports: ["4200:4200"]
|
||||
environment:
|
||||
PREFECT_SERVER_DATABASE_CONNECTION_URL: "postgresql+asyncpg://..."
|
||||
volumes:
|
||||
- prefect-data:/root/.prefect
|
||||
|
||||
prefect-worker:
|
||||
image: prefecthq/prefect:3-latest
|
||||
command: prefect worker start --pool "local-process"
|
||||
environment:
|
||||
PREFECT_API_URL: "http://prefect-server:4200/api"
|
||||
ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY}"
|
||||
GH_TOKEN: "${GH_TOKEN}"
|
||||
```
|
||||
|
||||
Prefect UI: `http://localhost:4200` — shows all runs, retries, failures, timing.
|
||||
|
||||
---
|
||||
|
||||
## Sentry AI Analysis → Executor [PLACEHOLDER]
|
||||
|
||||
> ⚠️ **Placeholder** — implement after Asana direct access is restored.
|
||||
> Validate this section against current Sentry bot plan before implementation.
|
||||
|
||||
Planned flow:
|
||||
1. Sentry receives new crash report matching severity threshold
|
||||
2. Webhook triggers Prefect flow: `sentry_crash_to_executor`
|
||||
3. Analysis Agent reads: crash stack trace, affected versions, reproduction frequency, linked Sentry issues
|
||||
4. If fix confidence > threshold: dispatch GHA executor with targeted fix prompt
|
||||
5. If confidence low: create Asana task with AI-generated analysis, tag "обработать" for human review
|
||||
6. Post Sentry comment with AI analysis + PR link (if fix attempted)
|
||||
|
||||
```
|
||||
Sentry webhook
|
||||
→ Prefect: sentry_crash_to_executor(issue_id)
|
||||
→ Analysis Agent: read crash + codebase context
|
||||
→ confidence ≥ 0.7? → dispatch GHA executor
|
||||
→ confidence < 0.7? → create Asana task + post Sentry comment
|
||||
→ Result: PR draft OR Asana task + Sentry analysis comment
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Peekaboo on GHA — Revised Assessment
|
||||
|
||||
Earlier assessment ("SIP blocks TCC") was incorrect. Actual situation:
|
||||
|
||||
- GHA runner user has `sudo` access
|
||||
- SIP protects system files (`/System`, `/usr`) — NOT user-level `TCC.db`
|
||||
- `TCC.db` is at `~/Library/Application Support/com.apple.TCC/TCC.db`
|
||||
- Can be modified via `sqlite3` with sudo → grants Screen Recording + Accessibility
|
||||
- This is a standard CI pattern for screenshot testing
|
||||
|
||||
Xcode automation already works on GHA (AppleScript/XCUITest) — same TCC layer.
|
||||
|
||||
**Revised conclusion: Peekaboo on GHA hosted runner is likely viable.** Needs validation in Phase 1.
|
||||
|
||||
Fallback if sqlite3 approach fails: ddg-vm via SSH from GHA job (existing infra, confirmed working).
|
||||
|
||||
**Solution for visual reproduction:**
|
||||
|
||||
```
|
||||
GHA job (macos-15):
|
||||
→ build app artifact
|
||||
→ SSH into ddg-vm on Eagle
|
||||
→ upload artifact to VM
|
||||
→ run Peekaboo-based test inside VM
|
||||
→ retrieve screenshot + result
|
||||
→ post to PR
|
||||
```
|
||||
|
||||
ddg-vm is existing infra with Peekaboo pre-installed + TCC permissions granted.
|
||||
This is already how `macOS UI Tests CI` works today.
|
||||
|
||||
---
|
||||
|
||||
## Org-Scale Design
|
||||
|
||||
For scaling beyond personal use to full org:
|
||||
|
||||
### Capabilities Registry
|
||||
|
||||
Each repo declares its execution capabilities in `.github/executor-capabilities.json`:
|
||||
```json
|
||||
{
|
||||
"runner_types": ["macos-15", "ubuntu-latest"],
|
||||
"has_ios_build": true,
|
||||
"has_ui_tests": true,
|
||||
"has_peekaboo_vm": true,
|
||||
"max_concurrent_workers": 2
|
||||
}
|
||||
```
|
||||
|
||||
Prefect's Analysis Agent reads this to select the correct runner per task.
|
||||
|
||||
### Shared Infrastructure
|
||||
|
||||
| Component | Where | Notes |
|
||||
|---|---|---|
|
||||
| Prefect server | Eagle or VPS | Single instance, all repos |
|
||||
| `ANTHROPIC_CI_KEY` | GitHub org secrets | Shared across repos |
|
||||
| `ASANA_CI_TOKEN` | GitHub org secrets | Rate-limited bot token |
|
||||
| executor-bot (MM) | MM Admin → see Asana task | Bot account for notifications |
|
||||
| ddg-vm pool | Eagle | 2 VMs available concurrently |
|
||||
|
||||
### Multi-Repo Flows
|
||||
|
||||
```python
|
||||
@flow
|
||||
def route_task(task_id: str):
|
||||
"""Routes task to correct repo + executor."""
|
||||
repo = classify_repo(task_id) # Asana tags → repo mapping
|
||||
execute_task(source="asana", task_id=task_id, repo=repo)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GHA `@claude` Trigger (Direct, No Orchestration Needed)
|
||||
|
||||
For PR-level tasks, Anthropic's action handles everything natively:
|
||||
|
||||
```yaml
|
||||
# In any repo: .github/workflows/claude.yml
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
jobs:
|
||||
claude:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_CI_KEY }}
|
||||
trigger_phrase: "@claude"
|
||||
```
|
||||
|
||||
Usage: `@claude fix the failing unit test in this PR` → Claude reads diff, fixes, pushes commit.
|
||||
No Prefect needed — this path is synchronous and triggered by humans.
|
||||
|
||||
---
|
||||
|
||||
## Task Intake & Scope Control
|
||||
|
||||
### Asana Tagging Contract
|
||||
|
||||
Only tasks explicitly tagged for automation enter the pipeline. The agent never self-selects tasks.
|
||||
|
||||
| Tag | Meaning |
|
||||
|---|---|
|
||||
| `executor:ready` | Task approved for autonomous execution — analyst reviews and tags this |
|
||||
| `executor:analyze` | Analysis Agent runs, generates prompt, but does NOT dispatch GHA — output goes to Asana comment for human review |
|
||||
| `executor:hold` | Task in queue, blocked (dependency, unclear scope) |
|
||||
|
||||
Workflow:
|
||||
1. Alex (or designated reviewer) tags task `executor:ready` in Asana
|
||||
2. Prefect polls Asana for tasks with this tag (every N min, or webhook)
|
||||
3. Tag is removed / replaced with `executor:in-progress` once dispatched
|
||||
4. No tag = not touched, ever
|
||||
|
||||
### Scope Guardrails (Eagle auto-approval layer)
|
||||
|
||||
From [[executor-orchestrator-redesign]]: Eagle monitors `#executor` channel using message protocol.
|
||||
GHA workers use same `[REQUEST:]` / `[ESCALATE:]` protocol — Eagle is the gatekeeper regardless of where the worker runs.
|
||||
|
||||
```
|
||||
Asana task tagged executor:ready
|
||||
→ Prefect: run_analysis_agent()
|
||||
→ Prefect: dispatch_gha()
|
||||
→ GHA worker posts [STATUS:] / [REQUEST:] to MM #executor-queue
|
||||
→ Eagle monitoring cron (2 min) reads channel
|
||||
→ Auto-approve safe actions, escalate ambiguous to Alex
|
||||
→ Worker proceeds only after approval
|
||||
```
|
||||
|
||||
Auto-approval rules (same as orchestrator redesign):
|
||||
- `create_worktree`, `run_build`, `run_tests`, `open_draft_pr`, `push_branch` → ✅ silent approve
|
||||
- `post_asana_comment`, any scope expansion → ⚠️ escalate to Alex
|
||||
- `merge_pr` → ❌ always escalate
|
||||
|
||||
|
||||
|
||||
### Phase 1: GHA Validation (1-2 days)
|
||||
- [ ] Create `executor-worker.yml` in apple-browsers repo
|
||||
- [ ] Test `claude-code-action@v1` with trivial prompt on macOS-15 runner
|
||||
- [ ] Verify DerivedData cache (target: <10 min cached build)
|
||||
- [ ] Test ddg-vm SSH from GHA job (Peekaboo path)
|
||||
- [ ] Add `ANTHROPIC_CI_KEY` to org secrets
|
||||
|
||||
### Phase 2: Analysis Agent (2-3 days)
|
||||
- [ ] Write `analysis-agent.md` prompt template
|
||||
- [ ] Test against 3 real Asana bug tasks
|
||||
- [ ] Tune confidence scoring + runner selection
|
||||
- [ ] Validate prompt quality → PR quality correlation
|
||||
|
||||
### Phase 3: Prefect Orchestration (2-3 days)
|
||||
- [ ] Deploy Prefect server (Docker on Eagle or VPS)
|
||||
- [ ] Implement `execute_task` flow
|
||||
- [ ] Wire Asana "обработать" tag → Prefect trigger
|
||||
- [ ] Test end-to-end: Asana task → PR
|
||||
|
||||
### Phase 4: MM Integration (1 day)
|
||||
- [ ] Create executor-bot in MM (blocked by IT Ops — see Asana task)
|
||||
- [ ] Add MM notification step to Prefect flow
|
||||
- [ ] Create `#executor-queue` channel
|
||||
|
||||
### Phase 5: Sentry Integration [PLACEHOLDER]
|
||||
- [ ] Validate against current Sentry bot plan (requires Asana access restoration)
|
||||
- [ ] Implement `sentry_crash_to_executor` flow
|
||||
- [ ] Set confidence threshold for auto-fix vs. triage
|
||||
|
||||
### Phase 6: Org Rollout
|
||||
- [ ] Publish `executor-capabilities.json` spec
|
||||
- [ ] Template `executor-worker.yml` as reusable workflow
|
||||
- [ ] Onboard second repo (e.g. privacy-configuration)
|
||||
- [ ] Add `route_task` multi-repo flow to Prefect
|
||||
|
||||
---
|
||||
|
||||
## Research Findings (2026-05-14)
|
||||
|
||||
### GHA macOS Runners
|
||||
- `macos-15` = current `macos-latest` (since Sep 2025)
|
||||
- Xcode: max 3 simulator runtimes per image
|
||||
- Simulators: headless, no display server needed → XCUITests work
|
||||
- Build cache: DerivedData cacheable via `actions/cache`
|
||||
|
||||
### Peekaboo on GHA
|
||||
- Requires Screen Recording + Accessibility TCC permissions
|
||||
- SIP enabled on hosted runners → `tccutil insert` blocked
|
||||
- **Viable path: ddg-vm SSH from GHA job** (existing infra)
|
||||
|
||||
### `claude-code-action@v1` (GA)
|
||||
- Official Anthropic action, replaces manual `claude -p`
|
||||
- `workflow_dispatch` trigger works for external orchestration
|
||||
- `--model claude-opus-4-7` for complex fixes, sonnet for quick ones
|
||||
- GH token scoped to repo → no additional secrets needed for PR creation
|
||||
|
||||
### Prefect 3.x
|
||||
- Python decorators, low learning curve
|
||||
- Self-hosted: single Docker container + Postgres
|
||||
- Prefect Cloud free tier: 3 workspaces, unlimited flows (but limited runs/month)
|
||||
- Retry pattern for GHA polling: `@task(retries=60, retry_delay_seconds=60)`
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Eagle's current cron executor tick continues to run in parallel during transition
|
||||
- GHA `macos-15` runner: Xcode NOT pre-cached → first build ~30 min, cached ~8 min
|
||||
- Self-hosted runner on Eagle: avoids cache miss but ties up main Mac
|
||||
- DuckDuckGo has existing GHA macOS setup — reuse existing Xcode bootstrap steps
|
||||
- `ANTHROPIC_CI_KEY` = separate key from personal key, with usage limits
|
||||
@@ -1,148 +0,0 @@
|
||||
---
|
||||
tags:
|
||||
- mattermost
|
||||
- migration
|
||||
- plan
|
||||
- personal-os
|
||||
created: '2026-05-14'
|
||||
status: in-progress
|
||||
---
|
||||
# Plan: Mattermost Migration
|
||||
|
||||
**Контекст**: Корпоративная политика требует перехода на corporate-hosted Mattermost.
|
||||
Zulip остаётся (апгрейд отменяется), но работа переезжает в MM.
|
||||
Personal/family/journal остаются в Discord и Zulip соответственно.
|
||||
|
||||
**Источник**: [Zulip тред «✔ Zulip migration ⭐»](https://zulip.qentra.top/#narrow/stream/4-master/topic/.E2.9C.94.20Zulip.20migration.20.E2.AD.90/near/15190)
|
||||
|
||||
---
|
||||
|
||||
## Scope
|
||||
|
||||
| Пространство | Платформа |
|
||||
|---|---|
|
||||
| **Work** (Personal OS) | **Mattermost** |
|
||||
| Personal | Discord (без изменений) |
|
||||
| Family, journal | Zulip / Discord (без изменений) |
|
||||
|
||||
---
|
||||
|
||||
## Архитектура ботов
|
||||
|
||||
Проблема: сейчас executor-воркеры постят от имени Eagle → смешивает интерактив с автономной работой.
|
||||
Решение: отдельные bot-аккаунты с разными identity.
|
||||
|
||||
| Bot | Роль | Аватар |
|
||||
|---|---|---|
|
||||
| **Орёл** (Eagle) 🦅 | Персональный ассистент, интерактив, планирование | 🦅 |
|
||||
| **Executor** ⚡ | Автономные воркеры, code fixes, PR-отчёты | ⚡ |
|
||||
| **Dax** | Scheduled: daily brief, inbox triage, weekly review (переиспользуем корпоративный бот) | — |
|
||||
|
||||
Eagle и Executor = новые MM bot accounts. Dax = переиспользуем существующий.
|
||||
|
||||
---
|
||||
|
||||
## Структура каналов (Categories → Channels)
|
||||
|
||||
```
|
||||
📁 Personal OS
|
||||
# general ← Eagle интерактив
|
||||
# daily-brief ← Dax
|
||||
# inbox ← Dax (inbox triage)
|
||||
# focus ← статус фокуса
|
||||
|
||||
📁 Executor
|
||||
# queue ← задачи в очереди, статусы
|
||||
# worker-1 ← лог активного воркера 1
|
||||
# worker-2 ← лог активного воркера 2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Задачи
|
||||
|
||||
### 🔴 IT Ops (блокирующая)
|
||||
|
||||
**Asana задача**: [#1214799615211686](https://app.asana.com/0/0/1214799615211686)
|
||||
|
||||
**Задача для IT/Admin**: создать bot accounts в корпоративном MM.
|
||||
|
||||
Нужные боты:
|
||||
- `eagle-bot` — персональный ассистент (Eagle / Орёл)
|
||||
- `executor-bot` — автономные воркеры
|
||||
- `brief-bot` — scheduled доставка брифов. **Кандидат на замену: Dax bot** (корпоративный бот, уже постит GitHub-ивенты → токен вероятно доступен). Если переиспользуем Dax — `brief-bot` создавать не нужно, брифы будут приходить от Dax.
|
||||
|
||||
**Для каждого бота нужны**:
|
||||
- Bot Account (не incoming webhook — нужен постоянный токен и avatar)
|
||||
- Токен → передать Алексу для `~/.hermes/.env`
|
||||
- Права: читать/писать в каналах Personal OS и Executor
|
||||
|
||||
**Как проверить текущий доступ**:
|
||||
- В корпоративном MM: **☰ Main Menu → Integrations**
|
||||
- Если пункт есть → можно создавать самому
|
||||
- Если нет → запросить у MM Admin
|
||||
|
||||
---
|
||||
|
||||
### Технический план (после получения токенов)
|
||||
|
||||
**1. Выбор архитектуры**
|
||||
|
||||
| Подход | Когда использовать |
|
||||
|---|---|
|
||||
| Hermes gateway (MM адаптер) | Если нужен интерактивный Eagle (отвечает на сообщения в MM real-time) |
|
||||
| cron/scripts/prompt files | Только scheduled delivery (brief, inbox, executor-отчёты) |
|
||||
|
||||
Hermes уже имеет `gateway/platforms/mattermost.py` — WebSocket listener + REST API v4.
|
||||
Config: `MATTERMOST_URL`, `MATTERMOST_TOKEN`, `MATTERMOST_HOME_CHANNEL`.
|
||||
|
||||
**2. Конфигурация `.env`**
|
||||
|
||||
```bash
|
||||
# Mattermost
|
||||
MATTERMOST_URL=https://mattermost.company.com
|
||||
MATTERMOST_EAGLE_TOKEN=<eagle-bot token>
|
||||
MATTERMOST_EXECUTOR_TOKEN=<executor-bot token>
|
||||
MATTERMOST_DAX_TOKEN=<dax token>
|
||||
MATTERMOST_TEAM_ID=<team id>
|
||||
# Channel IDs (узнать после настройки каналов)
|
||||
MM_CHANNEL_GENERAL=<id>
|
||||
MM_CHANNEL_DAILY_BRIEF=<id>
|
||||
MM_CHANNEL_INBOX=<id>
|
||||
MM_CHANNEL_EXECUTOR_QUEUE=<id>
|
||||
```
|
||||
|
||||
**3. Scheduled delivery (Eagle ← scripts)**
|
||||
|
||||
Для брифов и inbox triage — чистые скрипты, никакого daemon-а:
|
||||
```bash
|
||||
hermes --quiet --once "$(cat ~/agent/prompts/daily-brief.md)" \
|
||||
| curl -s -X POST "$MATTERMOST_URL/api/v4/posts" \
|
||||
-H "Authorization: Bearer $MATTERMOST_BRIEF_TOKEN" \
|
||||
-d "{\"channel_id\": \"$MM_CHANNEL_DAILY_BRIEF\", \"message\": \"...\"}"
|
||||
```
|
||||
|
||||
**4. Интерактивный Eagle**
|
||||
|
||||
Опция A: `hermes gateway start` с MM адаптером (+ `MATTERMOST_TOKEN=<eagle token>`)
|
||||
Опция B: ~200 строк `aiohttp` WebSocket демон (самодостаточно, без Hermes)
|
||||
|
||||
**5. История Zulip (опционально)**
|
||||
|
||||
Прямого инструмента Zulip→MM нет. Варианты:
|
||||
- Написать конвертер: Zulip JSON export → MM bulk import JSONL
|
||||
- Кодировать Topics как префикс сообщения: `**[topic]** текст`
|
||||
- Или просто начать с чистого листа (если история некритична)
|
||||
|
||||
---
|
||||
|
||||
## Статус
|
||||
|
||||
- [ ] **IT Ops**: проверить доступ к MM Integrations → запросить создание ботов
|
||||
- [ ] Получить токены для eagle-bot, executor-bot, brief-bot
|
||||
- [ ] Создать категории + каналы в MM
|
||||
- [ ] Настроить `.env` с токенами
|
||||
- [ ] Выбрать архитектуру (Hermes vs custom scripts)
|
||||
- [ ] Подключить scheduled delivery (brief, inbox, executor)
|
||||
- [ ] Подключить интерактивного Eagle
|
||||
- [ ] Решить вопрос с историей Zulip (мигрировать или нет)
|
||||
@@ -1,124 +0,0 @@
|
||||
# Plan: (no topic) Thread Routing
|
||||
|
||||
**Date**: 2026-05-14
|
||||
**Status**: Done — tested and working (2026-05-14)
|
||||
**Priority**: High
|
||||
|
||||
## 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
|
||||
|
||||
### Mechanism: `pre_gateway_dispatch` plugin
|
||||
|
||||
Hermes plugins support a `pre_gateway_dispatch` hook that fires **before** auth
|
||||
and agent dispatch, can intercept, rewrite, or skip any incoming `MessageEvent`.
|
||||
|
||||
Plugin location: `~/.hermes/plugins/zulip-topic-routing/`
|
||||
|
||||
In Zulip adapter (`gateway/platforms/zulip.py`):
|
||||
- `event.source.thread_id` = Zulip topic name (set at line ~610)
|
||||
- `event.source.chat_id` = `"stream_name::topic_name"`
|
||||
- `(no topic)` messages have `thread_id == "(no topic)"`
|
||||
|
||||
### Auth for Zulip API calls
|
||||
|
||||
From `~/.hermes/.env`:
|
||||
```
|
||||
ZULIP_URL=https://zulip.qentra.top
|
||||
ZULIP_BOT_EMAIL=eagle-bot@zulip.local
|
||||
ZULIP_API_KEY=BT7zzT...XqAE
|
||||
```
|
||||
|
||||
Topic rename API (confirmed working via curl in prior session):
|
||||
```
|
||||
PATCH /api/v1/messages/{message_id}?propagate_mode=change_all&topic={new_topic}
|
||||
```
|
||||
|
||||
Create message in topic (effectively creates topic):
|
||||
```
|
||||
POST /api/v1/messages type=stream to=stream_name topic=new_topic content=...
|
||||
```
|
||||
|
||||
### Plugin Flow
|
||||
|
||||
1. `pre_gateway_dispatch` fires → check `event.source.thread_id == "(no topic)"`
|
||||
2. Get stream name from `chat_id.split("::")[0]`
|
||||
3. Query Zulip API: `GET /api/v1/messages?narrow=[{"operator":"stream","operand":"<stream>"}]&num_before=0&num_after=20&anchor=newest`
|
||||
4. Filter out `(no topic)` messages → get most recent topic name + last N messages
|
||||
5. LLM relevance check (fast, cheap): is `event.text` related to recent topic context?
|
||||
6. If **related** → rewrite event, prepend `[routed from (no topic)]`, change `event.source.thread_id`
|
||||
7. If **not related** → rename `(no topic)` thread → descriptive 2-5 word topic, return `allow`
|
||||
|
||||
### Multi-instance Coordination
|
||||
|
||||
Dropped for v1 — single Eagle instance in practice. Can add mutex later if needed.
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
- [x] Confirmed curl approach works for topic creation
|
||||
- [x] Identified `pre_gateway_dispatch` as correct hook
|
||||
- [x] Confirmed `thread_id == "(no topic)"` detection pattern
|
||||
- [x] Create `~/.hermes/plugins/zulip-topic-routing/plugin.yaml`
|
||||
- [x] Create `~/.hermes/plugins/zulip-topic-routing/__init__.py`
|
||||
- [x] Enable plugin in `~/.hermes/config.yaml`
|
||||
- [x] Test: send `(no topic)` message, verify routing
|
||||
|
||||
## Notes
|
||||
|
||||
- Topic rename API requires `propagate_mode=change_all` to rename all messages in thread
|
||||
- Zulip uses `subject` field internally, `thread_id` in Hermes maps to it
|
||||
- Free streams setting: `ZULIP_FREE_STREAMS=master,daily-brief,inbox,executor,personal,focus`
|
||||
- Plugin lives in `~/.hermes/plugins/` (user plugins, override bundled)
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: Performance Review — вопросы
|
||||
tags:
|
||||
- work
|
||||
- career
|
||||
- review
|
||||
updated: '2026-05-17'
|
||||
---
|
||||
|
||||
- Are you happy with my work?
|
||||
- What I suck at?
|
||||
- How can I improve?
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
title: Ошибки семинара — разбор
|
||||
aliases:
|
||||
- ошибки-семинара
|
||||
- keep-import
|
||||
tags:
|
||||
- work
|
||||
- feedback
|
||||
- speaking
|
||||
- retrospective
|
||||
updated: '2026-05-17'
|
||||
---
|
||||
# Ошибки семинара lead dev
|
||||
Привязать к записи lead dev masterclass на truenas edu
|
||||
Потеря контакта с аудиторией, мало калибровки ОС в течении долгого периода
|
||||
- вопросы в стиле "do you know who is __", "do you know what is imposter syndrome"
|
||||
- мало вопросов в стиле какие решения есть у этой проблемы
|
||||
-
|
||||
|
||||
Спикер иногда ведёт себя как еблан, тупо ржёт
|
||||
|
||||
Личные истории факапов - кринж
|
||||
|
||||
Breakout rooms activities
|
||||
- мало совместных активностей
|
||||
- слишком короткие активности прерывают разговор на полуслове
|
||||
- не хватает задач на креатив по заданным критериям: задачи уровня отсортировать массив
|
||||
-
|
||||
|
||||
Не хватает "волшебных пендалей"
|
||||
|
||||
Не хватает конкретных success stories с KPI
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
title: Zoom — личная комната
|
||||
tags:
|
||||
- work
|
||||
- meetings
|
||||
- zoom
|
||||
updated: '2026-05-17'
|
||||
---
|
||||
|
||||
https://duckduckgo.zoom.us/my/malex
|
||||
Reference in New Issue
Block a user