feat(agents): PR Approver + install + observability + bridge /role routing #27

Merged
navigator merged 1 commit from feature/swarm-approver-install into main 2026-05-24 14:55:41 -03:00
Owner

Summary

Final wave of the FluidPop autonomous swarm scaffolding. Builds on PRs #25 + #26. After this PR + ADR-017 acceptance, install-swarm.sh activates the loop.

What's in it

PR Approver (roles/pr-approver.sh, Type=simple, Restart=always, 60s poll):

  • 11-row triage table (first-match-wins): do-not-merge, agent:wip, bot-author anomaly, not-mergeable, CI failure/pending, governance-path hold, ready-for-review label, auto/ branch
  • Pure shell (no claude) — auditable, deterministic
  • Backgrounds infra/forgejo/auto-merge.sh per PR; reaps PIDs; exit-code-to-action mapping
  • Cap 6 merges/h. Circuit breaker shared with other roles

Install / observability:

  • install-swarm.sh — idempotent bootstrap. Pre-flight verifies tokens, loginctl enable-linger, creates state dirs, installs .git/hooks/pre-commit symlink (backs up existing), symlinks units, daemon-reload, starts in dependency order, announces via Telegram. --restart-bridge to pick up /role routing
  • uninstall-swarm.sh — reverse-order stop+disable, restores prior pre-commit hook from .bak, preserves state dirs for forensics
  • swarm-status.sh — text dashboard for SSH triage (per-role unit state + circuit + recent failures)
  • reset-circuit.sh <role|all> — manual circuit reset
  • log-rotate.sh + .timer (03:30 daily) — zstd stream.jsonl > 24h, delete dirs > 7d
  • digest.sh + .timer (09:00 BRT) — 24h activity summary → Telegram low

Security hook:

  • hooks/pre-commit-block-paths.sh — refuses commits by swarm@pop.coop touching ADR-017 off-limits paths. ADRs conditional on Status: Accepted. Best-effort Telegram alarm on block. No-op for human authors.

Bridge extension (infra/ops/telegram-claude-bridge.sh):

  • /operator <msg>, /opener <msg>, /dispatcher <msg>, /approver <msg> route to role inbox
  • /swarm status returns dashboard inline
  • /swarm pause|resume|reset <role|all> writes circuit state or invokes reset
  • No prefix → existing generic claude continuation (unchanged)

Helper (infra/ops/notify-telegram.sh):

  • --role <name> flag prepends [swarm:<role>] after priority prefix

Validation

  • bash -n green on all 11 new .sh
  • systemd-analyze --user verify green on 5 new .service / .timer
  • chmod +x applied
  • End-to-end test gated by Stage 4 cold start

Notes

This PR does NOT activate the swarm. install-swarm.sh is run explicitly in Stage 4 after Marcos approves ADR-017 via Telegram (approve PR 24).

Depends on PR #25 (merged) and PR #26 (merged).

## Summary Final wave of the FluidPop autonomous swarm scaffolding. Builds on PRs #25 + #26. After this PR + ADR-017 acceptance, `install-swarm.sh` activates the loop. ## What's in it **PR Approver** (`roles/pr-approver.sh`, `Type=simple`, `Restart=always`, 60s poll): - 11-row triage table (first-match-wins): `do-not-merge`, `agent:wip`, bot-author anomaly, not-mergeable, CI failure/pending, governance-path hold, `ready-for-review` label, `auto/` branch - Pure shell (no claude) — auditable, deterministic - Backgrounds `infra/forgejo/auto-merge.sh` per PR; reaps PIDs; exit-code-to-action mapping - Cap 6 merges/h. Circuit breaker shared with other roles **Install / observability**: - `install-swarm.sh` — idempotent bootstrap. Pre-flight verifies tokens, `loginctl enable-linger`, creates state dirs, installs `.git/hooks/pre-commit` symlink (backs up existing), symlinks units, daemon-reload, starts in dependency order, announces via Telegram. `--restart-bridge` to pick up `/role` routing - `uninstall-swarm.sh` — reverse-order stop+disable, restores prior pre-commit hook from `.bak`, preserves state dirs for forensics - `swarm-status.sh` — text dashboard for SSH triage (per-role unit state + circuit + recent failures) - `reset-circuit.sh <role|all>` — manual circuit reset - `log-rotate.sh` + `.timer` (03:30 daily) — zstd `stream.jsonl` > 24h, delete dirs > 7d - `digest.sh` + `.timer` (09:00 BRT) — 24h activity summary → Telegram `low` **Security hook**: - `hooks/pre-commit-block-paths.sh` — refuses commits by `swarm@pop.coop` touching ADR-017 off-limits paths. ADRs conditional on `Status: Accepted`. Best-effort Telegram alarm on block. No-op for human authors. **Bridge extension** (`infra/ops/telegram-claude-bridge.sh`): - `/operator <msg>`, `/opener <msg>`, `/dispatcher <msg>`, `/approver <msg>` route to role inbox - `/swarm status` returns dashboard inline - `/swarm pause|resume|reset <role|all>` writes circuit state or invokes reset - No prefix → existing generic claude continuation (unchanged) **Helper** (`infra/ops/notify-telegram.sh`): - `--role <name>` flag prepends `[swarm:<role>]` after priority prefix ## Validation - [x] `bash -n` green on all 11 new `.sh` - [x] `systemd-analyze --user verify` green on 5 new `.service` / `.timer` - [x] `chmod +x` applied - [ ] End-to-end test gated by Stage 4 cold start ## Notes This PR does **NOT** activate the swarm. `install-swarm.sh` is run explicitly in Stage 4 after Marcos approves ADR-017 via Telegram (`approve PR 24`). Depends on PR #25 (merged) and PR #26 (merged).
feat(agents): PR Approver + install + observability + bridge /role routing
All checks were successful
build / scalafmt-check (pull_request) Successful in 4s
build / sbt-compile (pull_request) Successful in 3s
build / shell-lint (pull_request) Successful in 20s
build / scalafmt-check (push) Successful in 3s
build / sbt-compile (push) Successful in 4s
build / shell-lint (push) Successful in 10s
749f0dac90
Final wave of the swarm scaffolding. After this PR + ADR-017 acceptance,
install-swarm.sh activates the autonomous loop.

## Roles

- pr-approver.sh: long-running rule-based PR triage + merge loop (60s
  poll). Does NOT invoke claude — decisions are deterministic and
  auditable. 11-row triage table (first-match-wins): do-not-merge,
  agent:wip, bot-author anomaly, not-mergeable, CI failure/pending,
  governance-path hold, ready-for-review label, auto/ branch.
  Backgrounds infra/forgejo/auto-merge.sh per PR; reaps PIDs;
  exit code -> action (label/notify/circuit-record).
  Cap 6 merges/h. Circuit breaker shared with other roles.

## Installer / observability

- install-swarm.sh: idempotent bootstrap.
  Pre-flight verifies settings.env tokens; loginctl enable-linger;
  creates state dirs; installs .git/hooks/pre-commit symlink (backs
  up existing non-symlink hook); symlinks units; daemon-reload;
  starts in dependency order (log-rotate.timer, digest.timer,
  issue-opener.timer, pr-approver.service, operator.timer,
  resolver-dispatcher.service LAST); announces via Telegram.
  --restart-bridge flag picks up the /role inbound routing.
- uninstall-swarm.sh: reverse-order stop+disable, removes our
  symlinks, restores pre-existing pre-commit hook from .bak,
  preserves state dirs for forensics.
- swarm-status.sh: text dashboard for SSH triage; shows per-role
  unit state + circuit state + recent failures from journalctl.
- reset-circuit.sh <role|all>: manual circuit reset after alarm.
- log-rotate.sh + .timer: daily 03:30, zstd stream.jsonl > 24h,
  delete log dirs > 7 days, append to archive.jsonl manifest.
- digest.sh + .timer: daily 09:00 BRT (= 12:00 UTC), summarizes
  24h activity (PRs merged/open, issues open/closed, active
  resolvers, circuit states), sends Telegram --priority low.

## Security hook

- hooks/pre-commit-block-paths.sh: refuses any commit by
  swarm@pop.coop touching ADR-017 off-limits paths. Conditional
  on ADRs: only blocks ADRs marked Status: Accepted (Proposed
  drafts are still editable). Best-effort Telegram alarm on
  block. NO-OP for human authors (navigator etc.).

## Bridge extension

- infra/ops/telegram-claude-bridge.sh: adds /role prefix routing.
  /operator <msg>, /opener <msg>, /dispatcher <msg>, /approver
  <msg> drop the body in ${role}/inbox/<ts>.txt for the role to
  consume next tick. /swarm status returns swarm-status.sh
  inline. /swarm pause|resume|reset <role|all> writes
  circuit.state or invokes reset-circuit.sh. Default (no
  prefix) keeps existing generic claude continuation unchanged.

## Helper

- infra/ops/notify-telegram.sh: adds --role <name> flag,
  prepending [swarm:<role>] to the message after the existing
  priority prefix.

## Validation

- bash -n green on all 11 new .sh files
- systemd-analyze --user verify green on 5 new .service / .timer
- chmod +x applied
- End-to-end test gated by Stage 4 cold start (next)

## Notes

This PR does NOT activate the swarm; install-swarm.sh is run
explicitly in Stage 4 after Marcos approves ADR-017 via Telegram.
fluidpop-bot left a comment
Collaborator

CI green (head 749f0dac90), auto-approving

CI green (head 749f0dac908b479ea35999d4585e4354f888a099), auto-approving
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Fluid/fluidpop-v1!27
No description provided.