Skip to content
Draft
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
24 changes: 21 additions & 3 deletions .cursor/mcp.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
{
"mcpServers": {
"exstreamtv": {
"command": "python3",
"args": ["-m", "mcp_server"],
"cwd": "/Users/roto1231/Documents/XCode Projects/EXStreamTV"
"command": "uv",
"args": [
"run",
"--directory",
"/Users/roto1231/XCode Projects/EXStreamTV",
"--extra",
"dev",
"python",
"-m",
"mcp_server"
]
},
"mcp-atlassian": {
"command": "uvx",
"args": ["mcp-atlassian"],
"env": {
"CONFLUENCE_URL": "https://exstreamtv2.atlassian.net/wiki",
"CONFLUENCE_USERNAME": "roto1231@mac.com",
"CONFLUENCE_SPACES_FILTER": "ESTV",
"TOOLSETS": "default"
}
}
}
}
129 changes: 129 additions & 0 deletions .cursor/rules/design-pattern-decision-tree.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
description: >-
Design-pattern decision tree enforcement — apply the three-branch framework
(Creational / Structural / Behavioural) before generating or reviewing any
design pattern usage in this React + Python/FastAPI codebase.
globs: ["exstreamtv/**/*.py", "frontend/src/**/*.{ts,tsx}", "tests/**/*.py"]
alwaysApply: true
---

# Design-Pattern Decision Tree — Enforceable Standards

Full methodology: `.cursor/skills/design-pattern-decision-tree/SKILL.md`

## Core Principle

**Name the pain point before selecting a pattern.** Every design pattern
recommendation MUST trace back to exactly one branch of the decision tree.
Pattern usage that cannot be grounded in a specific, observable code pain
point is over-engineering and MUST be flagged.

---

## The Three-Branch Decision Tree

### 1. Is the pain about **creating objects**? (Creational)

Ask:
- Are constructors complex, with many optional parameters?
- Is there conditional instantiation (if/elif to pick which class)?
- Are defaults unclear or scattered?

| Pain Point | Pattern | Decision Path |
|-----------|---------|---------------|
| Complex construction with many fields | **Builder** | Creation → many optional params → Builder |
| Conditional class selection | **Factory Method / Abstract Factory** | Creation → conditional instantiation → Factory |
| One global instance required | **Singleton** | Creation → single shared instance → Singleton (use sparingly) |
| Deferred or expensive init | **Lazy Initialization** | Creation → costly setup → Lazy Init |
| Copying with variation | **Prototype** | Creation → clone with overrides → Prototype |

### 2. Is the pain about **how objects fit together**? (Structural)

Ask:
- Is there a leaking interface (internal details exposed)?
- Are subsystem boundaries awkward or overly coupled?
- Is composition difficult (wrappers, adapters, proxies)?

| Pain Point | Pattern | Decision Path |
|-----------|---------|---------------|
| Incompatible interfaces | **Adapter** | Structure → interface mismatch → Adapter |
| Complex subsystem needs simple entry point | **Facade** | Structure → subsystem complexity → Facade |
| Adding behaviour without subclassing | **Decorator** | Structure → optional behaviour layers → Decorator |
| Controlling access or adding indirection | **Proxy** | Structure → access control / caching → Proxy |
| Part-whole hierarchies (UI trees) | **Composite** | Structure → tree of components → Composite |

### 3. Is the pain about **behaviour that changes**? (Behavioural)

Ask:
- Are there accumulating if/elif/switch conditionals?
- Does branching logic vary by case, mode, or type?
- Are algorithms unstable (swap at runtime)?

| Pain Point | Pattern | Decision Path |
|-----------|---------|---------------|
| if/elif cascade selecting algorithm | **Strategy** | Behaviour → branching per algorithm → Strategy |
| Object changes behaviour based on mode | **State** | Behaviour → mode-driven transitions → State |
| Chain of fallback handlers | **Chain of Responsibility** | Behaviour → ordered try-then-pass → CoR |
| Notify many listeners of events | **Observer** | Behaviour → fan-out notifications → Observer |
| Encapsulate a request as an object | **Command** | Behaviour → deferred/queued operations → Command |
| Duplicated fetch/render/lifecycle logic | **Template Method / Hook** | Behaviour → identical structure, varying detail → Template Method |

---

## Enforcement Rules

### RULE PAT-01: Pain-First Selection
Before suggesting any pattern, state the pain point in one sentence.
Example: "The pain is a 10-branch if/elif in `_detect_source_type`
that grows with every new media source → Chain of Responsibility."

### RULE PAT-02: Adapter Purity
Adapters MUST contain only translation logic — no business rules, no
validation, no side effects. If an Adapter is accumulating logic, it
has become a Facade or Service and must be refactored.

### RULE PAT-03: Builder Early Validation
Builder `.build()` calls MUST validate required fields before returning
the constructed object. Never produce half-initialised objects.

### RULE PAT-04: Strategy Eliminates Branching
If a Strategy pattern is introduced, the original if/elif/switch MUST
be removed or reduced to a single dispatch lookup. Strategies that
still contain conditionals have not been fully applied.

### RULE PAT-05: State for Well-Defined Modes
Use State only when modes and their transitions are well-defined and
finite. If transitions are ad-hoc or unbounded, prefer Strategy.

### RULE PAT-06: No Pattern Without Pain
Never apply a pattern as an "aesthetic upgrade." Singleton for "easy
access," Decorator with interdependent wrappers, or Factory for a
single concrete class are all anti-patterns and MUST be flagged.

### RULE PAT-07: Template Method for Hook Extraction (React)
When multiple React components share identical fetch → error → loading
→ render structure, extract the common lifecycle into a custom hook
(the React idiom for Template Method). Do not duplicate the boilerplate.

### RULE PAT-08: Composite for UI Decomposition (React)
When a React component exceeds ~200 lines and has visually distinct
sections, decompose into child components (Composite pattern). Pass
data via props, not by duplicating state management.

---

## Anti-Patterns to Flag

- **Singleton for convenience** — use dependency injection instead
- **Decorator with interdependent wrappers** — order-sensitive layering is fragile
- **Observer with unbounded listeners** — always provide unsubscribe
- **Factory for single class** — direct construction is simpler
- **Pattern applied without observable pain point** — over-engineering

---

## Cross-References

- Existing patterns inventory: `.cursor/rules/patterns-implemented.mdc`
- Safety rules: `.cursor/rules/exstreamtv-critical.mdc`
- Skill reference: `.cursor/skills/design-pattern-decision-tree/SKILL.md`
39 changes: 39 additions & 0 deletions .cursor/rules/exstreamtv-design-pattern-selection.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
description: Pain-point-first design pattern selection (GoF decision tree) for Python/FastAPI and React UI
globs: exstreamtv/**/*.py, frontend/src/**/*.{ts,tsx}
alwaysApply: false
---

# Design pattern selection (decision tree)

Before suggesting or adding a **named GoF-style pattern**, classify the pain point using the three root questions:

1. **Creational** — Is the problem **creating** or configuring objects (many parameters, conditional concrete types, unclear defaults, scattered `new`/construction)?
2. **Structural** — Is the problem **how pieces connect** (leaky boundaries, incompatible interfaces, one huge subsystem API, tree structure, shared immutable data)?
3. **Behavioral** — Is the problem **algorithms or control flow** (growing `if/elif`, per-case branches, request pipelines, undo, notifications, mode-specific behavior)?

## Required steps for assistants

- **Name the pain point first** (one short sentence). Do not pick a pattern from aesthetics or habit.
- **Trace the branch** from the tree (e.g. Behavioral → switch algorithms at runtime → Strategy). If you cannot trace it, do not recommend that pattern.
- **Flag untraceable usage**: if existing code uses a pattern name but the pain point does not match that branch, call that out as misuse or dead naming.
- **Adapters** (`exstreamtv/api/` clients, HTTP/DTO mappers, `frontend/src/api/client.ts`): **translation only** — no domain rules, scheduling, or business validation. Put rules in services or domain modules.
- **Builders** (`exstreamtv/patterns/factory/ffmpeg_builders.py` and similar): validate inputs and invariants **before** emitting argv or DTOs; fail fast with clear errors.
- **Strategy**: each strategy should **replace a branch** of behavior, not wrap another strategy with interdependent state. Prefer a registry or map (e.g. `get_ffmpeg_builder(mode)`) over giant `if/elif` in callers.
- **State**: use when **modes and transitions** are explicit (e.g. stream lifecycle). Do not duplicate the same concern with parallel booleans (`is_running` + `is_failed` + ad-hoc flags) at the same layer.

## React (this repo: Vite + React; if using Next.js elsewhere, same rules)

- Repeated mount → fetch → error/success UI: use a **Template Method–style** hook (e.g. `useAsyncResource`) instead of copying `useEffect` + cancellation in every page.
- Do not use **Singleton** for React state; use context, hooks, or server/session-scoped state as appropriate.

## Anti-patterns (reject)

- Singleton for “global convenience” when dependency injection or `app.state` / request scope is enough.
- Decorator stacks that **depend on wrapper order** for correctness (interdependent wrappers).
- Applying a pattern **without** a matching pain point (“upgrade for elegance”).
- Putting **business rules** inside adapters or raw HTTP clients.

## Coexistence

- See `patterns-implemented.mdc` for **where** patterns already live in this repo. This rule governs **selection**, not duplication of `exstreamtv/ffmpeg/constants.py` or safety rules in `exstreamtv-safety.mdc`.
198 changes: 198 additions & 0 deletions .cursor/skills/design-pattern-decision-tree/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
---
name: design-pattern-decision-tree
description: >-
Structured methodology for selecting the correct design pattern based on the
specific pain point in code. Covers the three-branch decision tree
(Creational, Structural, Behavioural), the pattern-to-pain-point mapping,
and the anti-patterns to avoid. Use when writing new code, reviewing
existing code, or refactoring — in both the Python/FastAPI backend and the
React/Vite frontend.
---

# Design-Pattern Decision Tree

## When to Apply

- Creating a new module, service, or component and considering which pattern to use
- Reviewing code that introduces or changes a design pattern
- Refactoring code with observable pain points (complex constructors, conditional cascades, leaky abstractions)
- Answering "which pattern should I use here?" — always start from the tree

## Core Rule

**Identify the pain point first, then walk the decision tree to the correct pattern.**
Never select a pattern by name and look for a place to apply it.

---

## The Three Branches

### Branch 1 — Creational (Object Creation Pain)

**Root question:** Is the pain about *creating* objects?

Symptoms:
- Constructor has 5+ parameters, many optional
- `if/elif` or `match` to decide which class to instantiate
- Defaults are unclear, scattered across callers, or duplicated
- Expensive initialisation is repeated unnecessarily

Decision sub-tree:

```
Creation pain?
├── Many optional params / complex assembly
│ └── Builder (validate on .build())
├── Conditional class selection
│ └── Factory Method or Abstract Factory
├── Single shared instance
│ └── Singleton (use sparingly — prefer DI)
├── Expensive or deferred init
│ └── Lazy Initialization
└── Clone with variation
└── Prototype
```

**EXStreamTV examples:**
- `TranscodeConfig` dataclass with 10 optional fields → Builder would help (currently a dataclass, acceptable)
- `get_ffmpeg_builder(mode)` → Factory Method ✅ (already correct)
- `get_url_resolver()` module-level global → Lazy Singleton ✅ (already correct)

---

### Branch 2 — Structural (Composition / Interface Pain)

**Root question:** Is the pain about *how objects fit together*?

Symptoms:
- Internal details of a subsystem leak into callers
- Two interfaces don't match but need to interoperate
- Adding optional behaviour requires subclassing
- A subsystem is complex and needs a single entry point

Decision sub-tree:

```
Structure pain?
├── Interface mismatch
│ └── Adapter (translation only — no business logic!)
├── Complex subsystem needs simple entry
│ └── Facade
├── Optional behaviour layers
│ └── Decorator (no interdependent wrappers)
├── Access control / caching indirection
│ └── Proxy
└── Tree of uniform components
└── Composite
```

**EXStreamTV examples:**
- `_resolved_to_stream_source()` converts ResolvedURL → StreamSource → Adapter ✅
- `StreamingContractEnforcer` validates before FFmpeg → Facade ✅
- `StreamUrlProxy.get_url()` for refreshed URLs → Proxy ✅
- React: ChannelDetailPage decomposed into PlayoutsSection / NowPlayingSection / TimelineSection → Composite ✅

---

### Branch 3 — Behavioural (Conditional / Algorithm Pain)

**Root question:** Is the pain about *behaviour that changes*?

Symptoms:
- Growing if/elif/switch cascade (one branch per type/mode)
- Algorithm varies at runtime (swap strategy based on config)
- Object's behaviour depends on its current state/mode
- Need to notify multiple listeners of an event
- Identical lifecycle with varying details (fetch → error → render)

Decision sub-tree:

```
Behaviour pain?
├── if/elif cascade selecting algorithm
│ └── Strategy (keyed registry, eliminate conditionals)
├── Object behaviour changes with mode
│ └── State (finite modes, defined transitions)
├── Ordered try-then-delegate fallback
│ └── Chain of Responsibility
├── Fan-out notifications
│ └── Observer / Event Bus
├── Deferred / queued operations
│ └── Command
├── Save and restore state
│ └── Memento
└── Identical structure, varying detail
└── Template Method (Python: ABC; React: custom hook)
```

**EXStreamTV examples:**
- `_detect_source_type` 10+ fallback detectors → Chain of Responsibility ✅ (refactored in `source_type_detector.py`)
- `resolve_sequence_item` 9 directive types → Strategy registry ✅ (refactored in `directive_handlers.py`)
- Channel stream lifecycle → State pattern ✅ (`patterns/state/stream_states.py`)
- `StreamEventBus` → Observer ✅
- `StreamCommandQueue` → Command ✅
- `ScheduleMemento` → Memento ✅
- React: useAsync hook eliminates duplicated fetch boilerplate → Template Method ✅

---

## Pattern-to-Pain-Point Quick Reference

| Pattern | Branch | Pain Signal | Anti-Pattern If Used Without Pain |
|---------|--------|-------------|----------------------------------|
| Builder | Creational | 5+ constructor params, many optional | Over-engineering simple dataclasses |
| Factory | Creational | if/elif to pick class | Factory for single concrete class |
| Singleton | Creational | Must be exactly one instance | "Easy access" to global state |
| Adapter | Structural | Interface A ≠ Interface B | Adapter containing business logic |
| Facade | Structural | Callers need simplified API | Facade hiding useful flexibility |
| Decorator | Structural | Optional behaviour layers | Order-dependent, interdependent wrappers |
| Proxy | Structural | Access control / caching | Proxy doing more than delegation |
| Composite | Structural | Tree of similar objects | Composite for flat, non-hierarchical data |
| Strategy | Behavioural | if/elif per algorithm | Strategy with one implementation |
| State | Behavioural | Mode-driven transitions | State for unbounded/ad-hoc modes |
| Chain of Resp. | Behavioural | Ordered try → delegate | Chain for single handler |
| Observer | Behavioural | Fan-out event notification | Observer with no unsubscribe |
| Command | Behavioural | Deferred/queued operations | Command for synchronous calls |
| Memento | Behavioural | Save/restore object state | Memento for immutable data |
| Template Method | Behavioural | Same structure, different details | Template for unrelated code |

---

## Anti-Patterns to Reject

1. **Singleton for "easy access"** — Use dependency injection. Module-level globals with `get_*()` are acceptable in Python but should not proliferate.

2. **Pattern as aesthetic upgrade** — "Let's add a Decorator for elegance" without a pain point is over-engineering.

3. **Decorator with interdependent wrappers** — If wrapper A must be applied before wrapper B, the design is fragile. Consider a Pipeline or Builder instead.

4. **Factory for a single concrete class** — Direct instantiation is simpler and more readable.

5. **Observer without unsubscribe** — Memory leaks and ghost callbacks. Always pair `subscribe` with `unsubscribe` or `unsubscribe_all`.

6. **Strategy that doesn't eliminate the conditional** — If the original if/elif still exists alongside the Strategy, the refactoring is incomplete.

---

## Verification Checklist

When reviewing or generating pattern code:

- [ ] Pain point is stated explicitly before the pattern is named
- [ ] The decision tree path is traceable (Branch → Sub-question → Pattern)
- [ ] Adapters contain only translation logic
- [ ] Builders validate on `.build()`
- [ ] Strategies replace (not supplement) the original conditional
- [ ] State transitions are finite and documented
- [ ] Observers have unsubscribe mechanisms
- [ ] React hooks follow the Template Method idiom for lifecycle extraction
- [ ] No pattern is applied without a real, observable pain point

---

## Related Rules and Skills

- Enforcement rule: `.cursor/rules/design-pattern-decision-tree.mdc`
- Existing patterns: `.cursor/rules/patterns-implemented.mdc`
- Safety rules: `.cursor/rules/exstreamtv-critical.mdc`
- Codebase expert: `.cursor/skills/exstreamtv-expert/SKILL.md`
Loading