Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Thanks for considering contributing. Watchflow is a **rule engine** for GitHub
## Direction and scope

- **Rule engine** — Conditions map parameter keys to built-in logic (e.g. `require_linked_issue`, `max_lines`, `require_code_owner_reviewers`). New conditions live in `src/rules/conditions/` and are registered in `src/rules/registry.py` and `src/rules/acknowledgment.py`.
- **Webhooks** — Delivery ID–based dedup so handler and processor both run; welcome comment when no rules file exists.
- **Webhooks** — Delivery ID–based dedup so handler and processor both run; one setup-awareness comment on an initially opened PR when no rules file exists.
- **API** — Repo analysis and proceed-with-PR support `installation_id` so install-flow users don’t need a PAT.
- **Docs** — All MD files should speak to engineers: direct, no fluff, immune-system framing (Watchflow as necessary governance, not “another AI tool”).

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ Detailed steps: [Quick Start](docs/getting-started/quick-start.md). Configuratio
- **`POST /api/v1/rules/recommend`** — Analyze a repo (structure, PR history) and return suggested rules. Accepts `repo_url`; optional `installation_id` (from install link) or user token for private repos and higher rate limits.
- **`POST /api/v1/rules/recommend/proceed-with-pr`** — Create a PR that adds `.watchflow/rules.yaml` from recommended rules. Auth: Bearer token or `installation_id` in body.

When no `.watchflow/rules.yaml` exists and a PR is opened, Watchflow posts a **welcome comment** with a link to watchflow.dev (including `installation_id` and `repo`) so maintainers can run analysis and create a rules PR without entering a PAT.
When a newly opened PR has no `.watchflow/rules.yaml`, Watchflow posts one **setup-awareness comment** with a link to watchflow.dev (including `installation_id` and `repo`) so maintainers can run analysis and create a rules PR without entering a PAT. Later commits, reviews, and re-runs update the neutral check without repeating the comment.

---

Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ graph TD

1. **Webhook** — GitHub sends `pull_request` or `push`; router reads `X-GitHub-Delivery`, builds `WebhookEvent` with `delivery_id`.
2. **Handler** — Enqueues a processor task with `event_type + delivery_id + func` so dedup doesn’t skip the processor.
3. **Processor** — Loads `.watchflow/rules.yaml` from default branch (via GitHub API). If missing, creates a neutral check run and posts a **welcome comment** with a link to watchflow.dev (`installation_id` + `repo`).
3. **Processor** — Loads `.watchflow/rules.yaml` from default branch (via GitHub API). If missing, creates a neutral check run; on the initial PR-open event only, it also posts one setup-awareness comment with a link to watchflow.dev (`installation_id` + `repo`).
4. **Enrichment** — Fetches PR files, reviews, CODEOWNERS content so conditions can run without a local clone.
5. **Rule engine** — Passes **Rule objects** (with attached condition instances) to the engine. Engine runs each rule’s conditions; no conversion to dicts that would drop conditions.
6. **Output** — Violations → check run + PR comment; developers can reply `@watchflow acknowledge "reason"` where the rule allows it.
Expand Down
4 changes: 3 additions & 1 deletion docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Suggested rules use the **same parameter names** as above so they work out of th

## Welcome comment when no rules file

When `.watchflow/rules.yaml` is missing and a PR is opened, Watchflow:
When `.watchflow/rules.yaml` is missing when a PR is initially opened, Watchflow:

1. Creates a **neutral check run** (“Rules not configured”).
2. Posts a **welcome comment** with:
Expand All @@ -87,6 +87,8 @@ When `.watchflow/rules.yaml` is missing and a PR is opened, Watchflow:

So maintainers get one clear next step instead of a silent skip.

Later commits, reviews, review-thread changes, and re-runs refresh the neutral check run but do not repeat the setup-awareness comment.

---

## Webhook and task dedup
Expand Down
8 changes: 4 additions & 4 deletions docs/getting-started/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Get Watchflow running in a few minutes: install the app, add `.watchflow/rules.y
## What you get

- **Rule evaluation** on every PR and push against your YAML rules.
- **Check runs** and **PR comments** when rules are violated (or when no rules file exists, a welcome comment with a link to set one up).
- **Check runs** and **PR comments** when rules are violated (or one setup-awareness comment when a newly opened PR has no rules file).
- **Acknowledgment** in-thread: `@watchflow acknowledge "reason"` where the rule allows it.
- **One config file** — `.watchflow/rules.yaml` on the default branch; rules are loaded from there via the GitHub API.

Expand All @@ -26,15 +26,15 @@ Get Watchflow running in a few minutes: install the app, add `.watchflow/rules.y
2. Click **Install** and choose the org/repos you want to protect.
3. Grant the requested permissions (webhooks, repo content for rules and PR data).

Watchflow will start receiving webhooks. If there’s no `.watchflow/rules.yaml` yet, the first PR will get a **welcome comment** with a link to [watchflow.dev](https://watchflow.dev) (including `installation_id` and `repo`) so you can run repo analysis and create a rules PR **without entering a PAT**.
Watchflow will start receiving webhooks. If a newly opened PR has no `.watchflow/rules.yaml`, it gets one **setup-awareness comment** with a link to [watchflow.dev](https://watchflow.dev) (including `installation_id` and `repo`) so you can run repo analysis and create a rules PR **without entering a PAT**. Later commits and reviews do not repeat it.

---

## Step 2: Add rules

**Option A — From the welcome comment (no PAT)**
**Option A — From the setup-awareness comment (no PAT)**

1. Open a PR (or any PR) and find the Watchflow welcome comment.
1. Open a new PR and find the Watchflow setup-awareness comment.
2. Click the link to **watchflow.dev/analyze?installation_id=…&repo=owner/repo**.
3. Run repo analysis; review suggested rules and click **Create PR** to add `.watchflow/rules.yaml` to a branch.

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ We built it for teams that still care about traceability, CODEOWNERS, and review
- **Condition-based rules** — `require_linked_issue`, `max_lines`, `require_code_owner_reviewers`, `no_force_push`, title patterns, approvals, labels, and more.
- **CODEOWNERS-aware** — Require owners for modified paths to be requested as reviewers; or require every changed path to have an owner.
- **Webhook-native** — Uses GitHub delivery IDs so handler and processor both run; comments and check runs stay in sync.
- **Install-flow friendly** — When no rules file exists, we post a welcome comment with a link to watchflow.dev (installation_id + repo) so you can run analysis and create a rules PR without a PAT.
- **Install-flow friendly** — When a newly opened PR has no rules file, we post one setup-awareness comment with a link to watchflow.dev (installation_id + repo) so you can run analysis and create a rules PR without a PAT.

## Quick example

Expand Down
31 changes: 16 additions & 15 deletions src/event_processors/pull_request/enricher.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,25 +113,26 @@ async def fetch_acknowledgments(self, repo: str, pr_number: int, installation_id
"""Fetch and parse previous acknowledgments from PR comments."""
try:
comments = await self.github_client.get_issue_comments(repo, pr_number, installation_id)
if not comments:
return {}

acknowledgments = {}
for comment in comments:
comment_body = comment.get("body", "")
commenter = comment.get("user", {}).get("login", "")

if is_acknowledgment_comment(comment_body):
acknowledged_violations = parse_acknowledgment_comment(comment_body, commenter)
for ack in acknowledged_violations:
if ack.rule_id:
acknowledgments[ack.rule_id] = ack

return acknowledgments
return self.parse_acknowledgments(comments or [])
except Exception as e:
logger.error(f"Error fetching acknowledgments: {e}")
return {}

def parse_acknowledgments(self, comments: list[dict[str, Any]]) -> dict[str, Acknowledgment]:
"""Parse acknowledgments from a previously fetched PR-comment snapshot."""
acknowledgments = {}
for comment in comments:
comment_body = comment.get("body", "")
commenter = (comment.get("user") or {}).get("login", "")

if is_acknowledgment_comment(comment_body):
acknowledged_violations = parse_acknowledgment_comment(comment_body, commenter)
for ack in acknowledged_violations:
if ack.rule_id:
acknowledgments[ack.rule_id] = ack

return acknowledgments

def prepare_webhook_data(self, task: Any) -> dict[str, Any]:
"""Extract data available in webhook payload."""
if not task or not hasattr(task, "payload") or not task.payload:
Expand Down
Loading
Loading