-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/multi model sqlite state #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
zelvinator
wants to merge
10
commits into
main
Choose a base branch
from
feat/multi-model-sqlite-state
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
d25482b
Increase body preview limit from 1500 to 5000 chars
zelvinator df42fbd
feat: multi-model architecture with SQLite state machine
8771315
fix: enforce GLM review for planned items, prevent Qwen self-approval
7216c21
fix: escalate code review requests to GLM, not Qwen
6de19d5
feat: two-pass review — Qwen fast review + GLM architectural amend
71f5367
feat: slash command interface + bot self-triggering
9e2334f
feat: /help via binary (no LLM), qwen36-coding model, every-minute sc…
3bf2904
fix: always post PR link + summary comment on issue after implementation
75ef28e
feat: self-trigger /review after every implementation
268d6b4
fix: query queue --state=discovered in Phase 1 to process stragglers
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,13 @@ | ||
| # Tracker file — do not commit (changes every run) | ||
| # Compiled binary — do not commit | ||
| scripts/zelvinator/zelvinator | ||
|
|
||
| # Old tracker files (legacy, kept for migration reference) | ||
| .zelvinator-processed.txt | ||
| .zelvinator-ci-attempts.txt | ||
| *.lock | ||
|
|
||
| .zelvinator-* | ||
| scripts/zelvinator/zelvinator | ||
|
|
||
| # SQLite state database (lives outside the repo now, but just in case) | ||
| *.db | ||
| *.db-wal | ||
| *.db-shm |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,152 +1,168 @@ | ||
| # Zelvinator Bot Scripts | ||
|
|
||
| Automation scripts for the [zelvinator](https://github.com/zelvinator) GitHub bot, | ||
| driven by [Hermes Agent](https://hermes-agent.nousresearch.com) cron jobs. | ||
| Automation scripts for the [zelvinator](https://github.com/zelvinator) GitHub bot, driven by Hermes Agent cron jobs. | ||
|
|
||
| ## Overview | ||
|
|
||
| The bot reacts to `@zelvinator` mentions and issue assignments across ANY repo | ||
| the bot account has access to. No TARGET_ORGS config needed — just invite the | ||
| `zelvinator` GitHub user as a collaborator to your repo/org, and the bot | ||
| automatically picks up interactions on the next cron cycle. | ||
| The bot watches repositories across configured GitHub orgs for `@zelvinator` mentions and responds automatically using a **two-model architecture**: | ||
|
|
||
| **Reacts to:** | ||
| - `@zelvinator` in issue/PR body or title → implements feature / reviews PR | ||
| - `@zelvinator` in issue/PR comments or review comments → replies specifically | ||
| - Issue assigned to `zelvinator` user → implements the feature | ||
| - Bot's own PR with failing CI → diagnoses and fixes (max 3 attempts) | ||
| - **Qwen 3.6 35B** (worker) — discovery, triage, simple fixes, implementation, file-level review | ||
| - **GLM 5.2** (planner) — architectural planning, complex review, takeover fixes | ||
|
|
||
| ## Model: People-reacting, not org-watching | ||
| ### Architecture | ||
|
|
||
| The bot does NOT need to know which orgs or repos to watch. It searches ALL | ||
| repos its GitHub token has access to using: | ||
|
|
||
| | Detection step | Method | | ||
| |---|---| | ||
| | Issues/PRs mentioning @zelvinator | Search API (across all accessible repos) | | ||
| | Comments mentioning @zelvinator | Search API + comment verification (whitelisted users only) | | ||
| | PR review comments | Iterates open PRs, checks inline review comments | | ||
| | CI failures on bot's PRs | Issues API (`filter=created`) — bot's own PRs | | ||
| | Assigned issues | Issues API (`filter=assigned`) — issues assigned to bot | | ||
| ``` | ||
| ┌─────────────────────────────────────────────────────────────┐ | ||
| │ zelvinator-worker (Qwen 3.6, every 5 min) │ | ||
| │ │ | ||
| │ Phase 1: Discovery + Triage │ | ||
| │ ├── Run Go binary → discover items → SQLite: discovered │ | ||
| │ ├── Post 🐢 acknowledgment comment │ | ||
| │ ├── Simple items (comments, ≤2 file fixes) → handle direct │ | ||
| │ └── Complex items → state: needs_planning │ | ||
| │ │ | ||
| │ Phase 2: Implementation (pick up GLM's plans) │ | ||
| │ ├── Queue items in "planned" or "fix_needed" state │ | ||
| │ ├── Read plan, implement file-by-file, push, open PR │ | ||
| │ └── state: review_pending │ | ||
| │ │ | ||
| │ Phase 3: Review Triage │ | ||
| │ ├── Review diffs at file level │ | ||
| │ ├── Clean → done | Simple fix → fix_needed │ | ||
| │ └── Complex → needs_review (escalate to GLM) │ | ||
| │ │ | ||
| │ Phase 4: Stale reset (items stuck >20 min) │ | ||
| └─────────────────────────────────────────────────────────────┘ | ||
|
|
||
| ┌─────────────────────────────────────────────────────────────┐ | ||
| │ zelvinator-planner (GLM 5.2, every 15 min) │ | ||
| │ │ | ||
| │ Phase 1: Planning (items Qwen escalated) │ | ||
| │ ├── Analyze codebase, write structured plan JSON │ | ||
| │ └── state: planned │ | ||
| │ │ | ||
| │ Phase 2: Complex Review (items Qwen escalated) │ | ||
| │ ├── Architectural review of diffs │ | ||
| │ └── done | fix_needed | needs_planning (re-plan) │ | ||
| │ │ | ||
| │ Phase 3: Takeover (Qwen failed twice) │ | ||
| │ └── GLM implements the fix directly → done │ | ||
| └─────────────────────────────────────────────────────────────┘ | ||
| ``` | ||
|
|
||
| ## Prompt Injection Defense | ||
| ### State Machine | ||
|
|
||
| User-supplied content (issue bodies, comments, titles) could contain prompt | ||
| injection attacks. Two-tier defense: | ||
| Items flow through a SQLite-backed state machine at `~/.hermes/zelvinator-bot/state.db`: | ||
|
|
||
| **Tier 1 — Content boundary markers (Go binary):** All user-controlled fields | ||
| are wrapped in `╔═══ USER-SUPPLIED CONTENT ═══╗` markers before reaching the | ||
| LLM, making the data/instruction boundary visually unambiguous. Structural | ||
| anomalies (zero-width Unicode chars, encoded payloads) trigger a | ||
| `content_warning` flag. | ||
| ``` | ||
| discovered → needs_planning → planned → implementing → review_pending → done | ||
| ↑ ↓ ↑ | ||
| │ fix_needed ──────────┘ | ||
| │ ↓ | ||
| │ needs_review → done | ||
| │ ↓ | ||
| └──────── fix_needed (Qwen retry) | ||
| ↓ | ||
| GLM takeover → done | ||
| ``` | ||
|
|
||
| **Tier 2 — Subagent judge (cron prompt):** Flagged items are NOT processed | ||
| directly. Instead, a zero-tools subagent (no MCP, no terminal, no filesystem) | ||
| is spawned solely to classify the content as SAFE or INJECTION. Only SAFE | ||
| items proceed to processing. | ||
| Key improvement over the old flat-file tracker: **claim-on-complete, not claim-on-discover**. Items enter `discovered` state but only move to `done` after work finishes. If a cron session dies mid-implementation, the item stays in `implementing` and gets reset to `planned` for retry on the next cycle. | ||
|
|
||
| ## Scripts | ||
|
|
||
| ### `scripts/find-zelvinator-mentions.sh` | ||
|
|
||
| Thin wrapper that delegates to the Go binary. Falls back to the Bash | ||
| implementation if the binary is missing. | ||
| Discovers new @zelvinator mentions across all configured repos. Inserts items into the SQLite state database. Only newly discovered items are returned. | ||
|
|
||
| **Usage:** | ||
| ```bash | ||
| # Find new mentions (outputs JSON array of unprocessed items) | ||
| ./scripts/find-zelvinator-mentions.sh | ||
|
|
||
| # Reset processed-items tracker | ||
| ./scripts/find-zelvinator-mentions.sh --reset | ||
| ``` | ||
|
|
||
| **Output:** JSON array with items containing: | ||
| - `type` — `"issue"` or `"pr"` | ||
| - `repo` — `"owner/name"` | ||
| - `number`, `title`, `url` | ||
| - `trigger_source` — `"body"`, `"comment"`, `"assignment"`, `"ci_failure"`, or `"review_comment"` | ||
| - `trigger_comment` — the comment text that triggered (when applicable) | ||
| - `content_warning` — set to `"structural_anomaly"` if suspicious patterns detected | ||
|
|
||
| ### `scripts/find-zelvinator-mentions.sh.bash` | ||
| ### Zelvinator CLI (`scripts/zelvinator/`) | ||
|
|
||
| Standalone Bash implementation of the detection logic. Used as fallback if the | ||
| Go binary is not compiled. Same behavior, all logic in pure Bash + `gh` CLI. | ||
| The Go binary provides all state management and GitHub actions: | ||
|
|
||
| ### `scripts/zelvinator/` (Go binary source) | ||
| ```bash | ||
| # Discovery | ||
| zelvinator find # Discover new items, insert into SQLite | ||
|
|
||
| # State queries | ||
| zelvinator queue --state=discovered # Items to triage | ||
| zelvinator queue --state=planned # Items ready for implementation | ||
| zelvinator queue --state=fix_needed # Items needing fixes | ||
| zelvinator queue --state=review_pending # Items awaiting review | ||
| zelvinator queue --state=needs_planning # Items Qwen escalated to GLM | ||
| zelvinator queue --state=needs_review # Reviews Qwen escalated to GLM | ||
|
|
||
| # State transitions | ||
| zelvinator state <id> <new_state> [--plan=<file>] [--feedback=<text>] [--pr-url=<url>] [--error=<text>] | ||
|
|
||
| # Plan management | ||
| zelvinator plan <id> # Get plan JSON for an item | ||
|
|
||
| # Maintenance | ||
| zelvinator stale --reset # Reset items stuck in "implementing" >20 min | ||
| zelvinator stats # Show item counts per state | ||
| zelvinator reset --confirm # Reset entire state database | ||
|
|
||
| # GitHub actions | ||
| zelvinator comment <repo> <number> <body> | ||
| zelvinator review <repo> <number> <body> [event] | ||
| zelvinator reply-review <repo> <number> <review_comment_id> <body> | ||
| ``` | ||
|
|
||
| The primary detection engine. Written in Go for performance and reliability. | ||
| ### Rebuilding the Go binary | ||
|
|
||
| | File | Purpose | | ||
| |---|---| | ||
| | `main.go` | Entry point, command dispatch | | ||
| | `find.go` | Discovery of mentions, assignments, CI failures | | ||
| | `comment.go` | Post comments, reviews, reply to review comments | | ||
| | `cifix.go` | Diagnose and fix CI failures on bot PRs | | ||
| | `internal/config/config.go` | Config loader (WHITELIST_USERS only) | | ||
| | `internal/github/client.go` | GitHub API client (go-github wrapper) | | ||
| | `internal/tracker/tracker.go` | Atomic claim tracker (deduplication) | | ||
| ```bash | ||
| cd scripts/zelvinator | ||
| go build -o zelvinator . | ||
| ``` | ||
|
|
||
| ## Configuration | ||
|
|
||
| Edit `config.sh`: | ||
|
|
||
| ```bash | ||
| # Users whose @zelvinator mentions trigger bot actions | ||
| WHITELIST_USERS=(Hnatekmar xbedna MichalPustka mroncka) | ||
|
|
||
| # Path to Hermes .env file | ||
| HERMES_ENV="${HERMES_HOME:-$HOME/.hermes}/.env" | ||
| ``` | ||
|
|
||
| **There is no TARGET_ORGS.** The bot searches all repos its token can see. | ||
| To add the bot to a new repo, invite the `zelvinator` GitHub user as a | ||
| collaborator (no code changes needed). | ||
| | Variable | Purpose | | ||
| |----------|---------| | ||
| | `WHITELIST_USERS` | Users whose @zelvinator mentions trigger bot actions | | ||
| | `TARGET_ORGS` | GitHub orgs/accounts to search | | ||
| | `HERMES_ENV` | Path to Hermes .env file (normally `~/.hermes/.env`) | | ||
|
|
||
| ## Credentials | ||
|
|
||
| The bot needs a single `GITHUB_TOKEN` in `~/.hermes/.env`: | ||
| ``` | ||
| GITHUB_TOKEN=ghp_... | ||
| ``` | ||
|
|
||
| The token determines which repos the bot can see. Must have `repo` scope | ||
| (full control of private repos). | ||
| The bot reads `GITHUB_TOKEN` from `~/.hermes/.env`. No secrets stored in this repo. | ||
|
|
||
| ## Cron Setup | ||
| ## Cron Integration | ||
|
|
||
| The bot runs as a Hermes cron job (`zelvinator-mentions`) every 10 minutes. | ||
| Set it up with: | ||
| Two Hermes cron jobs: | ||
|
|
||
| ```bash | ||
| cronjob action=create \ | ||
| name=zelvinator-mentions \ | ||
| schedule='*/10 * * * *' \ | ||
| deliver=local \ | ||
| enabled_toolsets='["terminal","file","web","search"]' \ | ||
| workdir=/root/workspace/zelvinator \ | ||
| prompt="..." | ||
| ``` | ||
| | Job | Model | Schedule | Role | | ||
| |-----|-------|----------|------| | ||
| | `zelvinator-worker` | Qwen 3.6 35B | every 5 min | Discovery, triage, implementation, file-level review | | ||
| | `zelvinator-planner` | GLM 5.2 | every 15 min | Planning, complex review, takeover fixes | | ||
|
|
||
| See [references/cron-prompt.md](references/cron-prompt.md) for | ||
| the full cron prompt text (including injection defense guardrails). | ||
| Cron prompts are at: | ||
| - `references/cron-prompt-worker.md` — Qwen worker prompt | ||
| - `references/cron-prompt-planner.md` — GLM planner prompt | ||
|
|
||
| The cron job must run as the user who has: | ||
| - Access to `~/.hermes/.env` (contains GITHUB_TOKEN) | ||
| - The Go binary compiled at `~/.hermes/zelvinator-bot/scripts/zelvinator/zelvinator` | ||
| - `gh` CLI authenticated (optional, for fallback script) | ||
| ## State Database | ||
|
|
||
| ## Rebuilding the Go binary | ||
| SQLite database at `~/.hermes/zelvinator-bot/state.db` — **outside the git working directory**, immune to `git stash`/`checkout`/`reset`. | ||
|
|
||
| After pulling new source: | ||
| ```bash | ||
| cd ~/.hermes/zelvinator-bot/scripts/zelvinator | ||
| go build -o zelvinator . | ||
| Schema: | ||
| ```sql | ||
| CREATE TABLE items ( | ||
| id TEXT PRIMARY KEY, -- "issue:owner/repo#123" | ||
| repo TEXT NOT NULL, | ||
| number INTEGER NOT NULL, | ||
| type TEXT NOT NULL, -- "issue" | "pr" | ||
| trigger_source TEXT NOT NULL, | ||
| state TEXT NOT NULL, -- discovered → ... → done | ||
| plan TEXT, -- JSON plan from GLM | ||
| review_feedback TEXT, -- GLM's review notes | ||
| pr_url TEXT, | ||
| attempts INTEGER DEFAULT 0, | ||
| ... | ||
| ); | ||
| ``` | ||
|
|
||
| ## Adding the bot to a new repo | ||
|
|
||
| 1. Invite the `zelvinator` GitHub user as a collaborator to the repo | ||
| 2. Ensure the bot's `GITHUB_TOKEN` has access (repo scope) | ||
| 3. That's it — the bot picks it up on the next 10-minute cron cycle | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zelvinator Could there be guard against it going to negative?