Initial commit - but way too late.
Some checks failed
ci / site (push) Has been cancelled

This commit is contained in:
2026-02-10 00:22:18 -05:00
commit af112a713c
173 changed files with 27667 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
## ADDED Requirements
### Requirement: Umami pageview tracking
When Umami is enabled by configuration, the site MUST load Umami tracking on all indexable pages and MUST record pageviews.
When Umami is disabled or not configured, the site MUST still function and MUST NOT error in the browser due to missing analytics.
#### Scenario: Umami enabled
- **WHEN** Umami is enabled by configuration
- **THEN** the site includes the Umami script on `/`, `/videos`, `/podcast`, and `/about`
#### Scenario: Umami disabled
- **WHEN** Umami is not configured
- **THEN** the site renders normally and no analytics script is required
### Requirement: Custom event tracking
When Umami is enabled, the site MUST support custom event emission for:
- `cta_click`
- `outbound_click`
Each emitted event MUST include enough properties to segment reports by platform and placement when applicable.
#### Scenario: Emit outbound click event
- **WHEN** a user clicks a non-CTA outbound link from the homepage
- **THEN** the system emits an `outbound_click` event with a property identifying the destination domain
### Requirement: Environment configuration
The site MUST support configuration of Umami parameters (at minimum: website ID and script URL) without requiring code changes.
#### Scenario: Configure Umami via environment
- **WHEN** Umami configuration values are provided via environment or config file
- **THEN** the site uses those values to initialize analytics without modifying source code

View File

@@ -0,0 +1,35 @@
## ADDED Requirements
### Requirement: Reusable CTA component
The site MUST implement CTAs as a reusable component that can render at least the following actions:
- YouTube subscribe action (linking to the channel)
- Instagram follow action (linking to the profile)
- Podcast listen action (linking to a designated destination)
Each CTA MUST be configurable with:
- `platform` (`youtube`, `instagram`, `podcast`)
- `placement` (e.g., `hero`, `module_header`, `footer`)
- destination `url`
#### Scenario: Rendering a YouTube subscribe CTA
- **WHEN** the homepage includes a CTA with `platform: youtube`
- **THEN** the site renders a visible action that links to the configured YouTube channel destination URL
### Requirement: Trackable outbound links
CTA outbound links MUST support appending UTM parameters so traffic can be attributed in downstream analytics.
#### Scenario: UTM parameters applied
- **WHEN** a CTA is configured with UTM parameters
- **THEN** the rendered outbound link includes the UTM query parameters in its URL
### Requirement: CTA click event emission
CTA clicks MUST emit an analytics event with at least:
- event name `cta_click`
- `platform`
- `placement`
- `target` (destination URL or a stable identifier)
#### Scenario: User clicks CTA
- **WHEN** a user clicks an Instagram follow CTA in the hero placement
- **THEN** the system emits a `cta_click` event with `platform=instagram` and `placement=hero`

View File

@@ -0,0 +1,44 @@
## ADDED Requirements
### Requirement: Homepage modules and ordering
The homepage MUST render distinct content modules that include:
- newest content
- high-performing videos
- channel/podcast highlights
- prominent conversion CTAs
The homepage MUST render modules in a deterministic order configured by the site (not dependent on network timing).
#### Scenario: Homepage render with available data
- **WHEN** content data is available in the cache
- **THEN** the homepage renders the newest module and the high-performing videos module in the configured order
### Requirement: Newest content module
The system MUST compute a "newest" feed across sources by sorting normalized items by `publishedAt` descending.
The system MUST support filtering and limiting the number of items displayed for the newest module.
#### Scenario: Mixed-source newest list
- **WHEN** the cached dataset contains YouTube, Instagram, and podcast items with different publish dates
- **THEN** the newest module lists items ordered by `publishedAt` descending regardless of source
### Requirement: High-performing YouTube videos module
When `metrics.views` is available for YouTube items, the system MUST compute "high-performing" videos by ranking videos by `metrics.views` (descending) with an optional manual override list.
When `metrics.views` is not available, the system MUST render the high-performing module using a manual curated list and MUST NOT fail the page render.
#### Scenario: Views-based ranking available
- **WHEN** YouTube items include `metrics.views`
- **THEN** the high-performing module shows videos ranked by views, unless a manual override list is configured
#### Scenario: Views-based ranking unavailable
- **WHEN** YouTube items do not include `metrics.views`
- **THEN** the high-performing module renders using a manual curated list and the page still loads successfully
### Requirement: Graceful empty and error states
If a module has no content to display, the homepage MUST render a non-broken empty state for that module and MUST still render the rest of the page.
#### Scenario: No Instagram items available
- **WHEN** the cached dataset contains no Instagram items
- **THEN** the Instagram-related module renders an empty state and the homepage still renders other modules

View File

@@ -0,0 +1,48 @@
## ADDED Requirements
### Requirement: Indexable pages
The site MUST provide indexable HTML pages for:
- home (`/`)
- videos (`/videos`)
- podcast (`/podcast`)
- about (`/about`)
These pages MUST be server-rendered or statically generated HTML suitable for search engine crawling (not client-rendered only).
#### Scenario: Crawling the home page
- **WHEN** a crawler requests `/`
- **THEN** the server returns an HTML document containing the homepage content modules and metadata
### Requirement: Metadata and canonical URLs
Each indexable page MUST define:
- a document title
- a meta description
- a canonical URL
#### Scenario: Page metadata is present
- **WHEN** a crawler requests `/videos`
- **THEN** the HTML contains a `<title>`, a meta description, and a canonical URL for `/videos`
### Requirement: Social sharing cards
The site MUST provide Open Graph and Twitter card metadata for indexable pages so shared links render a preview.
#### Scenario: Sharing the home page
- **WHEN** a social crawler requests `/`
- **THEN** the response includes Open Graph and Twitter card tags with a title, description, and image when available
### Requirement: Sitemap and robots
The site MUST provide:
- `sitemap.xml` enumerating indexable pages
- `robots.txt` that allows indexing of indexable pages
#### Scenario: Sitemap is available
- **WHEN** a crawler requests `/sitemap.xml`
- **THEN** the server returns an XML sitemap listing `/`, `/videos`, `/podcast`, and `/about`
### Requirement: Structured data
The site MUST support structured data (JSON-LD) for Video and Podcast content when detail pages exist, and MUST ensure the JSON-LD is valid JSON.
#### Scenario: Video structured data present
- **WHEN** a video detail page exists and is requested
- **THEN** the HTML includes JSON-LD describing the video using a recognized schema type

View File

@@ -0,0 +1,67 @@
## ADDED Requirements
### Requirement: Normalized content items
The system MUST normalize all ingested items (YouTube videos, Instagram posts, podcast episodes) into a single internal schema so the website can render them consistently.
The normalized item MUST include at minimum:
- `id` (stable within its source)
- `source` (`youtube`, `instagram`, or `podcast`)
- `url`
- `title`
- `publishedAt` (ISO-8601)
- `thumbnailUrl` (optional)
#### Scenario: Normalizing a YouTube video
- **WHEN** the system ingests a YouTube video item
- **THEN** it produces a normalized item containing `id`, `source: youtube`, `url`, `title`, and `publishedAt`
#### Scenario: Normalizing a podcast episode
- **WHEN** the system ingests a podcast RSS episode
- **THEN** it produces a normalized item containing `id`, `source: podcast`, `url`, `title`, and `publishedAt`
### Requirement: YouTube ingestion with stats when available
The system MUST support ingesting YouTube videos for channel `youtube.com/santhoshj`.
When a YouTube API key is configured, the system MUST ingest video metadata and MUST ingest view count (and MAY ingest likes/comments if available) so "high-performing" can be computed.
When no YouTube API key is configured, the system MUST still ingest latest videos using a non-authenticated mechanism (for example, channel RSS) but MUST omit performance stats.
#### Scenario: API key configured
- **WHEN** a YouTube API key is configured
- **THEN** the system ingests video metadata and includes `metrics.views` for each ingested video when available from the API
#### Scenario: No API key configured
- **WHEN** no YouTube API key is configured
- **THEN** the system ingests latest videos and does not require `metrics.views` to be present
### Requirement: Podcast RSS ingestion
The system MUST ingest the Irregular Mind podcast RSS feed and produce normalized items representing podcast episodes.
#### Scenario: RSS feed fetch succeeds
- **WHEN** the system fetches the podcast RSS feed successfully
- **THEN** it produces one normalized item per episode with `source: podcast`
### Requirement: Instagram content support via embed-first approach
The system MUST support representing Instagram posts for `@santhoshjanan` in the site content surface.
If API-based ingestion is not configured/available, the system MUST support an embed-first representation where the normalized item contains a `url` to the Instagram post and any additional embed metadata needed by the renderer.
#### Scenario: Embed-first mode
- **WHEN** Instagram API ingestion is not configured
- **THEN** the system provides normalized Instagram items that contain a public post `url` suitable for embedding
### Requirement: Refresh and caching
The system MUST cache the latest successful ingestion output and MUST serve the cached data to the site renderer.
The system MUST support periodic refresh on a schedule (at minimum daily) and MUST support a manual refresh trigger.
On ingestion failure, the system MUST continue serving the most recent cached data.
#### Scenario: Scheduled refresh fails
- **WHEN** a scheduled refresh run fails to fetch one or more sources
- **THEN** the site continues to use the most recent successfully cached dataset
#### Scenario: Manual refresh requested
- **WHEN** a manual refresh is triggered
- **THEN** the system attempts ingestion immediately and updates the cache if ingestion succeeds