151 lines
9.9 KiB
Markdown
151 lines
9.9 KiB
Markdown
# RSSPipe Rules & Automation Hooks Specification (`005_rules_and_hooks_specification.md`)
|
|
|
|
## 1. Overview & Vision
|
|
|
|
The **Rules & Hooks Engine** is the reactive event-processing backbone of RSSPipe. It connects the ingestion lifecycle with the **Multi-Variant Data Store** (`entry_variants`), allowing users to automate content enrichment (scraping, tagging, AI, audio), triage (read, starred, archive), and notifications without writing code.
|
|
|
|
---
|
|
|
|
## 2. Core Mental Model: 3-Level Execution Flow
|
|
|
|
Every Rule is evaluated deterministically using a three-level hierarchy:
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[Level 1: Event Trigger] --> B{Level 2: Conditions Evaluator}
|
|
B -- All / Any Match --> C[Level 3: Action Execution Pipeline]
|
|
B -- No Match --> D[No Action]
|
|
C --> E[Store Variant / Mutate Status / Dispatch Event]
|
|
E -. New Variant Event .-> A
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Level 1: Event Triggers
|
|
|
|
The trigger specifies **when** a rule is evaluated.
|
|
|
|
| Trigger Type | Identifier | Description | Sub-Options / Parameters |
|
|
| :--- | :--- | :--- | :--- |
|
|
| **New Entry Ingested** | `new_entry` (`on_post_created`) | Fires immediately after an article is discovered and parsed by crawler/OPML. | None |
|
|
| **Variant Changed** | `variant_change` (`on_variant_updated`) | Fires when a specific variant key is added, updated, or deleted. | **Sub-Choice (Event Type)**:<br>• `key_added` (Key didn't exist, now created)<br>• `value_changed` (Key existed, value modified)<br>• `key_deleted` (Variant removed)<br>**Target Key Filter**:<br>• Specific `variant_key` (e.g. `keywords`, `readability_full_text`, `ai_summary`) or `*` (any variant). |
|
|
| **Cron Schedule (Future)** | `cron` | Recurring background schedule (e.g., daily digests, archival runs). | Cron expression (e.g. `0 8 * * *`) |
|
|
| **Manual Execution** | `manual` | Triggered on-demand via UI or API. | None |
|
|
|
|
---
|
|
|
|
## 4. Level 2: Conditions & Allowlisted Fields
|
|
|
|
Rules evaluate conditions against an **allowlist of strongly typed fields**. The UI dynamically presents appropriate operators and input controls based on the field type.
|
|
|
|
### 4.1 Allowlisted Fields & Operators
|
|
|
|
| Field Name | Type | Allowed Operators | UI Input Type & Behavior | Example Evaluation |
|
|
| :--- | :--- | :--- | :--- | :--- |
|
|
| `feed_id` | `string` / `set` | `==`, `!=`, `in`, `not_in` | **Multi-Select Feed Picker** with search, "Select All", and "Invert" buttons. | `feed_id in ["feed-1", "feed-2", ...]` (Applies to 24/30 feeds in 2 clicks) |
|
|
| `category_id` | `string` / `set` | `==`, `!=`, `in`, `not_in` | **Category Dropdown / Multi-Select Chips**. | `category_id in ["cat-tech", "cat-ai"]` |
|
|
| `title` | `string` | `contains`, `not_contains`, `==`, `!=`, `matches` (regex), `starts_with`, `ends_with` | Text input or comma-separated token badges. | `title contains "DeepSeek"` |
|
|
| `raw_content` / `content` | `string` | `contains`, `not_contains`, `matches` (regex) | Text input. | `content contains "breaking news"` |
|
|
| `author` | `string` | `contains`, `not_contains`, `==`, `!=` | Text input. | `author == "Paul Graham"` |
|
|
| `created_at` / `published_at` | `timestamp` | `>=`, `<=`, `within_last_hours`, `older_than_days` | Number + unit dropdown (e.g. "Within last 24 hours"). | `published_at within_last_hours 48` |
|
|
| `is_read` | `boolean` | `==`, `!=` | Toggle (Read / Unread). | `is_read == false` |
|
|
| `is_starred` | `boolean` | `==`, `!=` | Toggle (Starred / Not Starred). | `is_starred == true` |
|
|
| `variants.<key>` | `variant_value` | `exists`, `not_exists`, `contains`, `not_contains`, `==`, `!=` | Key name + value input. | `variants.keywords contains "LLM"` |
|
|
|
|
### 4.2 Match Modes
|
|
|
|
- **`all` (AND)**: Every condition in the rule must evaluate to true.
|
|
- **`any` (OR)**: At least one condition must evaluate to true.
|
|
|
|
---
|
|
|
|
## 5. Level 3: Action Execution Pipeline
|
|
|
|
When a rule's conditions are satisfied, one or more actions execute sequentially.
|
|
|
|
### 5.1 Supported Core Actions
|
|
|
|
| Action Identifier | Parameters | Description |
|
|
| :--- | :--- | :--- |
|
|
| **`set_variant`** | `variant_key` (string), `value` (string/json/yaml), `content_type` (string) | Adds or updates a key-value variant (e.g., adding tags, keywords, custom annotations). |
|
|
| **`readability_scrape`** | None | Fetches webpage full-text HTML and stores in `entry_variants(key='readability_full_text')`. |
|
|
| **`mark_read`** | None | Sets `is_read = true` (auto-triage/archive). |
|
|
| **`mark_starred`** | None | Sets `is_starred = true`. |
|
|
|
|
---
|
|
|
|
## 6. Safety Architecture: Endless Loop Prevention
|
|
|
|
Because actions like `set_variant` emit `on_variant_updated` events, rules could theoretically trigger cyclic loops (e.g., Rule A triggers on `variant_change` for `key="tags"` and adds a new tag to `key="tags"`).
|
|
|
|
### Loop Guard Mechanisms
|
|
|
|
1. **Static Validation (Rule Creation Time)**:
|
|
- If Trigger is `variant_change` on `key="X"` and an Action is `set_variant` on `key="X"`, the UI / API warns and rejects identical recursive rules unless an explicit guard predicate (e.g., `value not contains "Y"`) is specified.
|
|
2. **Runtime Execution Context & Depth Guard**:
|
|
- Every event emitted by the Event Bus carries a `RecursionDepth: int` (max depth = 5) and `ExecutionPath: []string` tracking rule IDs.
|
|
- If an event exceeds depth 5 or encounters the same Rule ID in its call stack, execution is aborted with an error log: `WARN: Cyclic rule execution aborted for rule: {rule_id}`.
|
|
|
|
---
|
|
|
|
## 7. UI/UX Design Specification for Settings
|
|
|
|
### 7.1 Rule List View
|
|
|
|
- Clean card layout grouped by Trigger type (`New Entry`, `Variant Change`, `Cron`).
|
|
- Card shows a natural language sentence:
|
|
> *"When **New Post Arrives** in **24 Feeds** and **Title contains 'AI'** $\rightarrow$ **Scrape Full Text**, **Mark Starred**"*
|
|
- Toggle switch (Active/Inactive), Edit button, Test button, Delete button.
|
|
|
|
### 7.2 Interactive Rule Builder Modal Layout
|
|
|
|
```
|
|
┌────────────────────────────────────────────────────────────────────────┐
|
|
│ Create Automation Rule [ ✕ ] │
|
|
├────────────────────────────────────────────────────────────────────────┤
|
|
│ Rule Name: [ Auto-Scrape Tech Feeds ] │
|
|
│ │
|
|
│ 1. WHEN (Trigger) │
|
|
│ (●) New Entry Arrives ( ) Variant Changed ( ) Schedule │
|
|
│ │
|
|
│ [If Variant Changed Selected] │
|
|
│ Event: [ Key Added ▼ ] Target Key: [ keywords ] │
|
|
│ │
|
|
│ 2. IF (Conditions) Match: [ All Conditions (AND) ]│
|
|
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
|
│ │ Field: [ Feed ID ▼ ] Op: [ In List (24 Feeds) ▼ ] │ │
|
|
│ │ [ Quick Select: All | None | Invert ] │ │
|
|
│ │ [✓] Hacker News [✓] 36Kr [✓] FT Chinese [✓] InfoQ [ ] Douban│ │
|
|
│ │ [ ✕ Remove]│ │
|
|
│ ├─────────────────────────────────────────────────────────────────┤ │
|
|
│ │ Field: [ Title ▼ ] Op: [ Contains Any ▼ ] │ │
|
|
│ │ Value: [ AI, LLM, Machine Learning ] │ │
|
|
│ │ [ ✕ Remove]│ │
|
|
│ └─────────────────────────────────────────────────────────────────┘ │
|
|
│ [ + Add Condition ] │
|
|
│ │
|
|
│ 3. THEN DO (Actions) │
|
|
│ [✓] ⚡ Scrape Full Text (Readability) │
|
|
│ [✓] ⭐ Mark as Starred │
|
|
│ [ ] 🏷️ Set Variant / Tag -> Key: [tags ] Val: [ai-digest ] │
|
|
│ [ ] ✓ Mark as Read │
|
|
│ │
|
|
│ [ Cancel ] [ Create Rule ] │
|
|
└────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Backlog Roadmap for Rules & Hooks
|
|
|
|
The following capabilities are reserved for upcoming iterations:
|
|
|
|
1. **Auto-Delete / Retention Rule Action** (`delete_entry`):
|
|
- Scope: Automatically delete matching entries older than $N$ days (requires retention policy discussion).
|
|
2. **Aggregated Shares & Auto-Share Action** (`share_entry`):
|
|
- Scope: Action to automatically generate public share links for high-priority articles, plus a dedicated `/shares` dashboard aggregating all shared items.
|
|
3. **Advanced AI & Audio Pipeline Actions**:
|
|
- `generate_ai_summary`: Calls LLM to summarize content $\rightarrow$ stores in `entry_variants.ai_summary`.
|
|
- `generate_tts`: Synthesizes article voice audio $\rightarrow$ stores `.mp3` blob in `./data/blobs/{id}/tts.mp3`.
|
|
- `generate_podcast`: Generates dual-speaker conversational audio discussions.
|