Skip to content

docs: My AI Feed design spec + implementation plan#4063

Open
rajesh-ms wants to merge 11 commits into
koala73:mainfrom
rajesh-ms:rajesh-ms/ai-feeds-integration
Open

docs: My AI Feed design spec + implementation plan#4063
rajesh-ms wants to merge 11 commits into
koala73:mainfrom
rajesh-ms:rajesh-ms/ai-feeds-integration

Conversation

@rajesh-ms

Copy link
Copy Markdown

Summary

Adds the design spec and a full TDD implementation plan for a new "My AI Feed" (my-ai-feed) news panel that aggregates seven curated AI sources into one panel shown in every variant. This PR is documentation only - no production code changes yet - so the approach can be reviewed before any implementation lands.

The motivation: surface a curated stream of high-signal AI sources (the accounts/orgs the user actually follows) directly in WorldMonitor, reusing the existing RSS pipeline rather than building a new subsystem.

The seven sources and how each is fetched:

  • Native RSS (always on): OpenAI News, Google DeepMind blog, AI Engineer YouTube
  • Self-hosted RSSHub (env-driven, best-effort): curated X handles, curated LinkedIn pages, Anthropic engineering, OpenAI engineering

Key design decisions (verified against the codebase)

  • CANONICAL-only category. my-ai-feed is registered in CANONICAL_FEEDS but intentionally left out of every variant''s FEEDS preset. resolveNewsCategories() then treats it as isCustom everywhere, so the client fetches its full set directly and the per-variant server digest needs zero changes.
  • No DEFAULT_ENABLED_SOURCES entry. Source enablement is computed only from FULL_FEEDS, so CANONICAL-only sources are enabled by default automatically; adding them would trip the DEV self-check. This also handles the dynamic X/LinkedIn source names that cannot be statically enumerated.
  • CI-safe URLs. Feeds are built via a variable (buildMyAiFeeds().map(...)), not rss(...) literals, so the feed validator and client/server parity tests stay green and credentials never land in client-visible URLs.
  • Import-safe builder. The new src/config/my-ai-feed.ts is designed to be unit-testable under the tsx runner (guarded import.meta.env access + injectable env), avoiding the known import.meta.env landmine in feeds.ts/proxy.ts.

The plan covers 6 bite-sized TDD tasks (builder + tests, feeds.ts registration, panels in all 6 variants, allowlist mirrors, validation) plus a self-host RSSHub runbook (Docker, build-time env vars, allowlist host, route verification, YouTube channel-id resolution).

Type of change

  • Bug fix
  • New feature
  • New data source / feed
  • New map layer
  • Refactor / code cleanup
  • Documentation
  • CI / Build / Infrastructure

Affected areas

  • Map / Globe
  • News panels / RSS feeds
  • AI Insights / World Brief
  • Market Radar / Crypto
  • Desktop app (Tauri)
  • API endpoints (/api/*)
  • Config / Settings
  • Other: Documentation (design spec + implementation plan)

Checklist

  • Tested on worldmonitor.app variant
  • Tested on tech.worldmonitor.app variant (if applicable)
  • New RSS feed domains added to api/rss-proxy.js allowlist (if adding feeds)
  • No API keys or secrets committed
  • TypeScript compiles without errors - N/A, docs-only PR (no code changes)

Screenshots

N/A - documentation only.

rajesh-ms and others added 2 commits May 31, 2026 11:22
…spec

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

@rajesh-ms is attempting to deploy a commit to the World Monitor Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added the trust:caution Brin: contributor trust score caution label Jun 3, 2026
@greptile-apps

greptile-apps Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This documentation-only PR adds a design spec and a full TDD implementation plan for a new "My AI Feed" panel that aggregates seven curated AI sources (X handles, LinkedIn pages, Anthropic engineering, OpenAI news/engineering, Google DeepMind, AI Engineer YouTube) into every WorldMonitor variant, reusing the existing RSS pipeline.

  • The implementation plan deliberately and correctly overrides several instructions in the spec (CANONICAL-only registration instead of spreading into variant presets, omitting DEFAULT_ENABLED_SOURCES, leaving data-loader.ts untouched), but those stale instructions remain in the spec without any visible callout, creating a risk that an agentic worker following the spec alone would implement incorrectly.
  • The spec's Goals section counts "five sources" where seven are actually listed, and describes five allowlist mirrors to edit where the plan (correctly) only edits four.
  • All three VITE_AI_* env vars are embedded in the client JS bundle at build time, exposing the RSSHub base URL, tracked X handles, and LinkedIn slugs; the runbook should call this out explicitly alongside its existing advice on ACCESS_KEY and IP allowlisting."

Confidence Score: 4/5

Safe to merge as a docs-only change; no production code is touched. The spec's stale implementation guidance should be corrected before the plan is executed by an agentic worker.

The plan is sound and no production code changes. The spec contains stale guidance that directly contradicts the plan on three implementation decisions, posing a real risk during execution if the two documents are not reconciled first.

docs/superpowers/specs/2026-05-30-my-ai-feed-design.md needs corrections to §4.2.2, the source count in §2, and the allowlist mirror count in §4.2.5 to stay consistent with the plan.

Important Files Changed

Filename Overview
docs/superpowers/specs/2026-05-30-my-ai-feed-design.md Design spec for the My AI Feed panel; contains three implementation instructions (DEFAULT_ENABLED_SOURCES, FULL/TECH_FEEDS spreading, data-loader.ts wiring) that the implementation plan explicitly overrides, plus a "five sources" count error when seven are listed.
docs/superpowers/plans/2026-05-30-my-ai-feed.md Well-reasoned TDD implementation plan with explicit overrides for spec inaccuracies; minor issues include a format-sensitive regex in the registration test and no mention that VITE_ env vars are embedded in the client JS bundle.

Reviews (1): Last reviewed commit: "docs: add My AI Feed implementation plan..." | Re-trigger Greptile

## 2. Goals / Non-Goals

### Goals
- Surface the five sources above in one new panel across every variant.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 "Five sources" should be "seven sources"

The opening paragraph of §1 explicitly lists 7 sources (X, LinkedIn, Anthropic engineering, OpenAI news, OpenAI engineering, Google DeepMind, AI Engineer YouTube), but the Goals section says "five sources above". An implementer who only skims the spec could treat two of the sources as optional/out-of-scope.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +106 to +127
2. **`src/config/feeds.ts`** — register the `my-ai-feed` category by importing and
spreading the list from `my-ai-feed.ts` into FULL_FEEDS and TECH_FEEDS (and via
`CANONICAL_FEEDS` so finance/commodity/energy/happy resolve it). Add the new
source `name`s to `SOURCE_TYPES` (type `tech`) and to
`DEFAULT_ENABLED_SOURCES['my-ai-feed']`. Source `name`s MUST match exactly
between the feed entries and `DEFAULT_ENABLED_SOURCES` or the panel starts empty.

3. **`src/config/panels.ts`** — define the `my-ai-feed` PanelConfig and add it to
**all six** `*_PANELS` blocks (FULL, TECH, FINANCE, COMMODITY, ENERGY, HAPPY)
so `VARIANT_DEFAULTS` (= `Object.keys(*_PANELS)`) includes it in every variant.
Optionally add per-variant name overrides in `VARIANT_PANEL_OVERRIDES`.

4. **`src/app/data-loader.ts`** — wire the `my-ai-feed` category into the
news/content loading path the same way existing feed panels are loaded.

5. **Allowlist — update ALL 5 mirrors** (drift causes runtime 403 or CI failure):
- `shared/rss-allowed-domains.json`
- `shared/rss-allowed-domains.cjs`
- `scripts/shared/rss-allowed-domains.json`
- `api/_rss-allowed-domains.js`
- `vite.config.ts` → `RSS_PROXY_ALLOWED_DOMAINS`
Add: the self-hosted RSSHub host, `deepmind.google`, `www.youtube.com`, and

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Spec §4.2.2 contains three implementation instructions that the plan explicitly overrides

The plan's "Key decisions" section (decisions 1–3) documents that all three instructions below are wrong and must NOT be followed:

  • "spreading the list from my-ai-feed.ts into FULL_FEEDS and TECH_FEEDS" → plan says CANONICAL-only, never in variant presets.
  • "Add the new source names to DEFAULT_ENABLED_SOURCES['my-ai-feed']" → plan says this trips the DEV self-check and is not needed.
  • "wire the my-ai-feed category into the news/content loading path in src/app/data-loader.ts" → plan says do NOT touch that file.

An agentic worker executing the spec without first reading the plan would implement all three incorrectly. Consider adding a callout at the top of the spec or striking through the affected instructions so the two documents are unambiguously consistent.

Comment on lines +121 to +128
5. **Allowlist — update ALL 5 mirrors** (drift causes runtime 403 or CI failure):
- `shared/rss-allowed-domains.json`
- `shared/rss-allowed-domains.cjs`
- `scripts/shared/rss-allowed-domains.json`
- `api/_rss-allowed-domains.js`
- `vite.config.ts` → `RSS_PROXY_ALLOWED_DOMAINS`
Add: the self-hosted RSSHub host, `deepmind.google`, `www.youtube.com`, and
`youtube.com` (YouTube feed redirects can switch between the two — allowlist both).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Spec says 5 allowlist mirrors; plan correctly updates only 4

§4.2.5 lists shared/rss-allowed-domains.cjs as a file to edit, but the plan (Task 4) correctly explains it is module.exports = require('./rss-allowed-domains.json') — a passthrough that auto-reflects the JSON. Editing it would create an inconsistency instead of fixing one. The spec should note that this file requires no change.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +749 to +757
These are read at **build time** by Vite (the `VITE_` prefix), so set them in
the Vercel project (Production + Preview) and in local `.env`, then redeploy:

| Env var | Example | Notes |
|---|---|---|
| `VITE_RSSHUB_BASE` | `https://rsshub.example.com` | Public HTTPS base, no trailing slash needed. Unset ⇒ only native sources appear. |
| `VITE_AI_X_HANDLES` | `swyx,karpathy,AnthropicAI` | Comma-separated, no `@`. Keep to ~3-5 to limit fan-out. |
| `VITE_AI_LINKEDIN_PAGES` | `anthropic,openai` | Comma-separated company slugs. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 VITE_ prefix embeds X handles and LinkedIn slugs in the client bundle

Vite statically replaces import.meta.env.VITE_* references at build time and includes the values verbatim in the public JS bundle. VITE_RSSHUB_BASE, VITE_AI_X_HANDLES, and VITE_AI_LINKEDIN_PAGES are therefore readable by anyone who inspects the downloaded assets — they expose the full RSSHub instance URL, every tracked X handle, and every LinkedIn slug. The runbook should explicitly mention that these values will be visible in dist/*.js and that operators should not use this mechanism if they want the monitored account list to remain private. Network-level access controls on the RSSHub host are advisable regardless of whether an ACCESS_KEY is set.

Comment on lines +219 to +231
it('registers my-ai-feed as a CANONICAL-only category with rss-wrapped urls', () => {
assert.match(
feedsSrc,
/'my-ai-feed':\s*buildMyAiFeeds\(\)\.map\(\(f\) => \(\{ \.\.\.f, url: rss\(f\.url as string\) \}\)\)/,
);
});

it('does NOT add my-ai-feed to any variant FEEDS preset', () => {
// CANONICAL-only: the only my-ai-feed key lives in the mergeCanonicalFeeds([...]) call.
const occurrences = feedsSrc.match(/'my-ai-feed':/g) ?? [];
assert.equal(occurrences.length, 1, 'my-ai-feed should appear exactly once (CANONICAL map only)');
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Registration test regex is format-sensitive and will break on minor reformatting

The pattern requires the .map() call to be on a single line with exact spacing. If Prettier wraps it across lines the regex will fail. Consider splitting into two independent assertions — one for 'my-ai-feed': and one for buildMyAiFeeds() — or using a multiline-aware pattern with [\s\S]*? between the key fragments.

rajesh-ms and others added 9 commits June 2, 2026 22:09
…sources)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tegory)

Adds a panel:my-ai-feed command in commands.ts and registers my-ai-feed in

PANEL_CATEGORY_MAP (topical + techAi) so it satisfies the panel-config

discoverability guardrails.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…re OpenAI

Adds end-to-end Azure Container Apps deployment for the self-hosted Docker
image (web app + redis-rest shim + Azure Cache for Redis) and the fixes
needed to make it actually serve traffic:

- infra/: Bicep for ACR, Container Apps env, web + redis-rest apps, Azure
  Cache for Redis, Log Analytics, and AcrPull role assignments; azure.yaml
  azd service map (both services language: docker).
- SSRF guard: in docker mode the sidecar now auto-allowlists the operator-
  configured UPSTASH_REDIS_REST_URL origin so the web app can reach the
  internal redis-rest shim (which resolves to a private IP). Gated to docker
  mode only; desktop/cloud startups keep private fetches blocked. Covered by
  two new regression tests.
- CRLF: normalize docker/entrypoint.sh to LF (Dockerfile sed + .gitattributes)
  so the sh shebang works on Linux containers.
- redis-rest shim: harden node-redis client with keepAlive, reconnectStrategy,
  and pingInterval to survive Azure Redis idle disconnects.
- _upstash-json: log redisPipeline HTTP/fetch failures instead of swallowing
  them silently.
- llm.ts: send api-key header for Azure OpenAI (*.openai.azure.com) so the
  analysis panels can use an Azure OpenAI deployment.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure OpenAI resources with key-based auth disabled require an Entra ID
bearer token instead of an api-key. Adds an OAuth2 client-credentials flow
(scope https://cognitiveservices.azure.com/.default) with token caching and
in-flight de-duplication in a new llm-azure-auth module.

getProviderCredentials now exposes an optional async authHeaderProvider; when
AZURE_OPENAI_TENANT_ID/CLIENT_ID/CLIENT_SECRET are set for an *.openai.azure.com
endpoint, it resolves a fresh Bearer token at call time. The three LLM fetch
sites (callLlm, callLlmReasoningStream, summarize-article) merge it before the
request. API-key auth remains the fallback when no Entra creds are present.

Infra: plumb the service-principal env vars through main.bicep ->
container-app-web.bicep (client secret stored as a Container App secret).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure OpenAI reasoning models (gpt-5.x, o-series) reject the legacy
max_tokens body field and require max_completion_tokens. Add a
maxTokensParam to ProviderCredentials, defaulting Azure OpenAI endpoints
to max_completion_tokens (override via LLM_MAX_TOKENS_PARAM), and emit it
at all three LLM call sites. Also plumb WM_SESSION_SECRET through the web
container-app bicep so anonymous browser session tokens (required for
tier-gated analysis RPCs) work after a full provision.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

trust:caution Brin: contributor trust score caution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant