Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Supported tools that auto-discover this file: Cursor, Codex, Aider, Jules, Devin
- Unofficial Python CLI for [Google Flow](https://labs.google/fx/tools/flow) — drives Veo (image-to-video, text-to-video) and Imagen (text-to-image) generations from the terminal by reverse-engineering Flow's private REST API at `aisandbox-pa.googleapis.com`.
- Python 3.11+ · `uv`-managed · `hatchling` builds · Playwright Chromium transport · `pyright` strict · `ruff` · `pytest`.
- Single-package modular monolith. Top-level modules under `src/gflow_cli/`: `api/`, `auth/`, `data/`, `mcp/`, `services/`, `tools/`, `ui/`, `worker/`, `browser_manager.py`, `cli.py`, `_cli_helpers.py`, `cli_character.py`, `cli_data.py`, `cli_image.py`, `cli_instructions.py`, `cli_models.py`, `cli_movie.py`, `cli_run.py`, `cli_scene.py`, `cli_tools.py`, `cli_video.py`, `chain.py`, `chain_manifest.py`, `composition.py`, `config.py`, `errors.py`, `exceptions.py`, `image_batch.py`, `manifest.py`, `movie_manifest.py`, `observability.py`, `paths.py`, `profile_store.py`.
- Command surface: `gflow auth`, `gflow image` (t2i/i2i/batch/upload/upscale), `gflow video` (t2v/i2v/r2v/batch/chain), `gflow character` (create/list/show/rm/voices — reusable project-scoped Flow Character entities), `gflow scene` (create/show — Add Clip / Scenes, with `create --output` for credit-free server-side extended video), `gflow instructions` (persistent Agent-Mode brief cards — add/list/enable/disable/rm/apply/toggle-mode, credits-free, `--project` required), `gflow movie` (run/template — multi-scene manifest pipeline), `gflow tools` (list/show/run — prompt-rewriting tools, also `--tool` on generation commands), `gflow data` (catalog queries), `gflow models`, `gflow run`, `gflow mcp` (run/setup — stdio MCP server), and `gflow serve` (HTTP/SSE).
- Command surface: `gflow auth`, `gflow image` (t2i/i2i/batch/upload/upscale), `gflow video` (t2v/i2v/r2v/chain — `batch` is a Phase B stub), `gflow character` (create/list/show/rm/voices — reusable project-scoped Flow Character entities), `gflow scene` (create/show — Add Clip / Scenes, with `create --output` for credit-free server-side extended video), `gflow instructions` (persistent Agent-Mode brief cards — add/list/enable/disable/rm/apply/toggle-mode, credits-free, `--project` required), `gflow movie` (run/template — multi-scene manifest pipeline), `gflow tools` (list/show/run — prompt-rewriting tools, also `--tool` on generation commands), `gflow data` (catalog queries), `gflow models`, `gflow run`, `gflow mcp` (run/setup — stdio MCP server), and `gflow serve` (HTTP/SSE).
- Requires a Google AI Ultra or Pro subscription with Flow access. All generations bill against the user's own Google account.

## Headed-browser dependency (architectural reality)
Expand All @@ -28,7 +28,7 @@ If you can help unblock a pure HTTP transport (especially for video generation,
- `uv sync` then `uv run playwright install chromium`. No global Python install needed.
- Copy `.env.template` to `.env.local`; never commit `.env.local`. It documents every env var.
- Output goes to `./tmp/` for scripts/tests or `$GFLOW_CLI_OUTPUT_DIR` for CLI outputs (defaults to `./out/`).
- One-time auth: `gflow auth login --browser chrome`. The `--browser chrome` flag is mandatory; the CLI fails fast on other strategies.
- One-time auth: `gflow auth login --browser chrome` (recommended — a real-Chrome profile is what generation runs need; the default `--browser auto` can also pick the internal strategy, and generation later fails fast on profiles created with a non-chrome strategy).
- Use `/gflow:status` to see the current task before starting work; `/gflow:known-issues` before touching auth or reCAPTCHA code paths.

## Testing instructions — The Impeccable Routine
Expand Down
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.38.0] — 2026-07-17

### Added

- **Robust agentic↔classic UI mode control** (#332): `--ui-mode` is now driven by a state-aware mode controller that reads Flow's Agent toggle (`aria-pressed`, locale-invariant) instead of icon heuristics — the "Tools" icon present in both modes could previously be misread as a forced-agentic cohort, causing spurious "not recoverable" aborts. `--ui-mode classic` now reliably reaches the classic editor: the controller closes the chat sidebar, toggles off only when actually in agent mode, and verifies the result. Live-verified with a full classic→agent→classic round-trip on real Flow.
- **i2i reference dedup via picker filename search** (#314): repeated local `--ref` images in `gflow image i2i` are attached by selecting the already-uploaded library tile (exact-filename picker search, project-scoped) instead of re-uploading — ending duplicate library pile-ups (the reported 8× `son.jpg`). Falls back to upload when the filename isn't found, and scrolls the virtualised picker grid so off-screen matches are still selected rather than silently re-uploaded (#335). R2V video refs keep upload-only behavior.
- **PR-Triage Autopilot (#238, #333)**: deterministic Stage 0 pre-filter and ephemeral Docker container sandbox executing the `/gflow:pr-council-review` skill against qualified external PRs hourly on the host VPS. Includes Telegram notifications and a persistent audit ledger. Implementation + fixture evals ship in this release; host-side deployment is staged separately.

### Changed

- **Cognitive-complexity refactor of 5 pure-logic functions** (#331): extracted cohesive private helpers to bring them under the Sonar S3776 threshold with zero behavior change (verified by types, scoped tests, and an adversarial behavior-diff review). The remaining S3776/S107 findings in essential-complexity live-automation code were accepted in SonarCloud with justification.

## [0.37.0] — 2026-07-17

### Fixed
Expand Down Expand Up @@ -319,6 +331,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
mid-run edit to the home `.env` can no longer produce a task whose client config
disagrees with the parameters the task derived from `get_settings()`.


## [0.24.0] — 2026-07-01

### Added
Expand Down Expand Up @@ -2007,7 +2020,8 @@ shell-script template that branches on these codes.

First skeleton. Not functional end-to-end yet.

[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.37.0...HEAD
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.38.0...HEAD
[0.38.0]: https://github.com/ffroliva/gflow-cli/compare/v0.37.0...v0.38.0
[0.37.0]: https://github.com/ffroliva/gflow-cli/compare/v0.36.0...v0.37.0
[0.36.0]: https://github.com/ffroliva/gflow-cli/compare/v0.35.0...v0.36.0
[0.35.0]: https://github.com/ffroliva/gflow-cli/compare/v0.34.0...v0.35.0
Expand Down
15 changes: 15 additions & 0 deletions KNOWN_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,21 @@ key — surfaces as a `RuntimeError` that `auth/cookies.py` normalizes to

## Resolved

### False "forced agentic — not recoverable" aborts from an icon-heuristic cohort probe

- **Status:** Resolved · **Severity:** Was-Medium (spurious exit-25 aborts on profiles that could in fact reach classic mode) · **Was-affecting:** `--ui-mode classic` and classic-only operations through v0.37.0 · **Fixed in:** 0.38.0 · **Tracked:** [#299](https://github.com/ffroliva/gflow-cli/issues/299), [#332](https://github.com/ffroliva/gflow-cli/issues/332)

The forced-agentic detection keyed on UI icons including `apps_spark_2` — which is
Flow's **Tools** button, present in BOTH cohorts — so a classic-capable profile could
be misclassified as an unrecoverable agentic cohort and abort with exit 25. Recovery
was a blind single click on the Agent pill with no state verification. v0.38.0
replaces this with a state-aware mode controller (`mode_control.py`) that reads the
Agent toggle's `aria-pressed` attribute (false = classic media, true = agent —
locale-invariant), closes the expanded chat sidebar first, toggles off only when
actually in agent mode, and re-verifies. Live-verified with a full
classic→agent→classic round-trip and a real agentic→classic recovery in the v0.38.0
release run ([evidence](docs/LIVE_VERIFICATION_v0.38.0.md)).

### Agentic image generation could silently attribute a pre-existing project asset as the "generated" image

- **Status:** Resolved · **Severity:** Was-High (wrong file downloaded and reported as success, silently) · **Was-affecting:** the agentic driver through v0.30.0 (and, for the pre-download guard below, every transport) · **Fixed in:** unreleased (0.31.0) · **Tracked:** [#281](https://github.com/ffroliva/gflow-cli/issues/281); related picker fix [#282](https://github.com/ffroliva/gflow-cli/issues/282)
Expand Down
12 changes: 12 additions & 0 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,18 @@ Phase A (T2V library transport) shipped with v0.7.0 via PR #23. PR #36 (2026-05-

---

### PR-Triage Autopilot — COMPLETED

Autonomous hourly code review helper running in a Docker sandbox. Design spec in [2026-07-04-pr-triage-autopilot-design.md](file:///C:/development/github/gflow-cli-pr-triage/docs/superpowers/specs/2026-07-04-pr-triage-autopilot-design.md).

- [x] Task 1: Stage 0 deterministic pre-filter (`pr_triage_gate.py`)
- [x] Task 2: §9 Autonomous mode in `pr-council-review` skill
- [x] Task 3: Ephemeral Docker sandbox image (`Dockerfile.triage`)
- [x] Task 4: Main host orchestration script (`pr_triage_autopilot.py`)
- [x] Task 5: Operations runbook (`PR_TRIAGE_AUTOPILOT-OPS.md`)

---

### Technical Debt & Refactoring — BACKLOG

Identified during v0.6.0a1 Council Review. To be addressed before or during Phase 6.
Expand Down
94 changes: 94 additions & 0 deletions deploy/PR-TRIAGE-AUTOPILOT-OPS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# PR-Triage Autopilot Operations Runbook

This runbook guides the deployment, configuration, monitoring, and administration of the hourly PR-Triage Autopilot on the VPS host.

---

## 1. Environment & Credentials

The autopilot orchestrator (`scripts/autopilot/pr_triage_autopilot.py`) runs as an hourly cron job and requires the following environment variables:

| Variable | Scope | Purpose |
|---|---|---|
| `ANTHROPIC_API_KEY` | Autopilot orchestrator & sandbox | Mints the Claude agent tokens to execute the council review. |
| `GH_COMMENT_TOKEN` | Autopilot orchestrator (host) | The comment-only GitHub PAT used to post verdicts and reviews to `ffroliva/gflow-cli` PRs. |
| `TELEGRAM_BOT_TOKEN` | Autopilot orchestrator (host) | Bot token to dispatch alert messages. |
| `TELEGRAM_USER_ID` | Autopilot orchestrator (host) | The chat ID to receive triage alert messages. |
| `PR_TRIAGE_ENGINE` | Autopilot orchestrator (host), optional | Review engine selector. Default `council-claude`; any other value exits at startup (`council-multi-cli` is reserved backlog). |
| `HERMES_OPS_DIR` | Autopilot orchestrator (host), optional | Location of the hermes-ops checkout hosting the Resend email notifier. Default `/opt/hermes-ops`. |
| `RESEND_API_KEY` | Email notifier (hermes-ops) | Resend API key consumed by `$HERMES_OPS_DIR/scripts/notify/email_notify.py`. |
| `HERMES_NOTIFY_EMAIL_TO` | Email notifier (hermes-ops) | Recipient address for high-signal triage emails. |
| `HERMES_NOTIFY_EMAIL_FROM` | Email notifier (hermes-ops) | Verified sender address for the Resend account. |

**Email channel note:** on the VPS the three email vars live in `/opt/hermes/.env`, rendered from hermes-ops' SOPS store. When they are unset the email channel silently disables itself — the notifier logs "email disabled" and the autopilot run is unaffected (Telegram, ledger, and GitHub comments remain authoritative).

---

## 2. Directory Layout & Symlinks

Deploy the following layout on the VPS:
- `/opt/gflow-cli`: Dedicated git repository checkout representing the active branch.
- `/opt/experience-vault`: Host experience vault directory structure.
- `/opt/experience-vault/projects/C--development-github-gflow-cli/memory`: The project-specific memory namespace to mount.

---

## 3. Ephemeral Sandbox Firewall Hardening

The docker sandbox runs under `scripts/autopilot/run_sandboxed_review.sh`. If run with root/sudo privileges on the VPS host, it automatically invokes `iptables` rules restricting the container bridge network egress interface to:
- UDP/TCP port 53 (DNS resolution)
- TCP port 443 to `api.anthropic.com` resolved IPs
- TCP port 443 to `github.com` resolved IPs
- All other internet egress is blocked (`DROP`).

Ensure `iptables` is installed on the host VPS. If `iptables` permissions are withheld, the script falls back with a warning, but egress hardening will not be active.

---

## 4. Cron Configuration & Verification

### Setup Cron Tick
Configure a cron job checking every hour on the hour under the `hermes` system user:

```cron
0 * * * * set -a; . /opt/hermes/.env 2>/dev/null; set +a; export ANTHROPIC_API_KEY="xxx" && export GH_COMMENT_TOKEN="xxx" && export TELEGRAM_BOT_TOKEN="xxx" && export TELEGRAM_USER_ID="xxx" && cd /opt/gflow-cli && uv run python scripts/autopilot/pr_triage_autopilot.py --repo-dir /opt/gflow-cli --memory-dir /opt/experience-vault/projects/C--development-github-gflow-cli/memory >> /var/log/hermes/pr_triage.log 2>&1
```

Sourcing `/opt/hermes/.env` first (`set -a` exports everything it defines) is what delivers the `RESEND_API_KEY` / `HERMES_NOTIFY_EMAIL_*` vars to the process so the email channel works; without it the run still succeeds but emails are silently disabled.

### Deploy mechanism (warning)

Do **NOT** register the orchestrator directly via `hermes cron create --script pr_triage_autopilot.py`. Hermes' cron runs scripts with hermes-agent's own interpreter — which lacks this repo's dependencies (e.g. `structlog`) — and only accepts scripts under `HERMES_HOME/scripts/`. Supported mechanisms:

- **(a) Plain crontab** — the line above, under the `hermes` system user.
- **(b) Thin shim in `HERMES_HOME/scripts/`** — a script that does `cd /opt/gflow-cli && exec uv run python scripts/autopilot/pr_triage_autopilot.py "$@"` (mirrors the existing EV ops-health shim pattern), registered with `hermes cron create "1h" --no-agent --script <shim> --deliver telegram`.

### Check Logs & Status
- **Log Location**: `/var/log/hermes/pr_triage.log`
- **Triage Ledger**: `/opt/gflow-cli/pr_triage_ledger.jsonl` tracks verdicts and failure states.
- **Lock File**: `/tmp/pr_triage_autopilot.lock` prevents overlapping ticks.

---

## 5. Operations & Kill-Switches

### Incident: Infinite loop or excessive cost
1. **Pause Cron**: Comment out the cron entry in `crontab -e`.
2. **Kill Running Container**:
```bash
docker ps | grep gflow-triage
docker kill <container_id>
```
3. **Clean Up Lock**:
```bash
rm -f /tmp/pr_triage_autopilot.lock
```

### Incident: Permanently Failed PR
If a PR enters the `FAILED_PERMANENT` state in `pr_triage_ledger.jsonl` due to 3 consecutive failures:
1. Examine `/var/log/hermes/pr_triage.log` for the exact trace.
2. Fix the underlying environmental or syntax issue.
3. To trigger a re-review, delete or edit the `FAILED_PERMANENT` entries for that PR number/SHA in `pr_triage_ledger.jsonl`, then run the script manually:
```bash
uv run python scripts/autopilot/pr_triage_autopilot.py --repo-dir /opt/gflow-cli --memory-dir /opt/experience-vault/projects/C--development-github-gflow-cli/memory
```
19 changes: 11 additions & 8 deletions docs/GITHUB.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,25 @@ when deciding what to do with an external or internal PR.

## Automated External PR Triage

External PR routing is handled by
`.github/workflows/external-pr-triage.yml`. It uses `pull_request_target`, but
only for repository metadata operations:
External PR routing starts with `.github/workflows/external-pr-triage.yml` (triggered via `pull_request_target`), which performs basic repository metadata operations:

- label external PRs as `external-contribution`
- label them as `needs-maintainer-review`
- label them as `needs-copilot-review`
- request `@ffroliva` as reviewer
- post or update the external-contribution checklist comment

Bot PRs such as Dependabot updates are skipped by this human-contributor
triage. They still run normal CI and remain subject to branch protection.
Bot PRs such as Dependabot updates are skipped by this human-contributor triage. They still run normal CI and remain subject to branch protection.

This workflow must not checkout the PR branch, install dependencies, run tests,
or execute contributor code. Keep all code execution in the normal `pull_request`
CI workflow, where forked PRs do not receive repository secrets.
This workflow must not checkout the PR branch, install dependencies, run tests, or execute contributor code. Keep all code execution in the normal `pull_request` CI workflow, where forked PRs do not receive repository secrets.

### PR-Triage Autopilot (VPS Sandbox)

To automate deeper verification, an hourly `hermes-ops` cron job runs on the host VPS to triage eligible external PRs.
- It uses the deterministic **Stage 0 pre-filter** (`pr_triage_gate.py`) to confirm eligibility.
- It fetches the PR branch and runs the full `/gflow:pr-council-review` inside an **ephemeral, non-root, read-only Docker container sandbox** (with restricted network egress and no write credentials).
- The host orchestrator posts the review comment to the PR and alerts the maintainer via Telegram.
- For design details, see [2026-07-04-pr-triage-autopilot-design.md](superpowers/specs/2026-07-04-pr-triage-autopilot-design.md) and the implementation plan [PLAN.md](superpowers/plans/2026-07-08-pr-triage-autopilot/PLAN.md).

## GitHub Copilot Code Review

Expand Down
Loading
Loading