# Today-Hero Editorial Pipeline

## Why this exists

The Studio store ships an editorial Today-Hero card (`_StoreTodayHero` in
`lib/pages/store.dart`) that plays the role Apple's Today tab plays in the App
Store: a single curated story per release that sets context for what the
operator should care about right now.

Editorial curation beats algorithmic recommendations on the discovery surfaces
operators love — but it only works if the curation actually happens. Manual
copywriting per release does not survive a solo-dev cadence. This pipeline
produces draft stories every day, has the operator accept or reject them, and
feeds the accepted story straight into Studio without a code release.

## Loop overview

```
┌────────────────────────────────────────────────────────────────────┐
│  1. SIGNAL COLLECTOR        tools/today/collect.sh                 │
│     git log + store-index diff + audit-log highlights →            │
│     plain-text "what happened in the last N hours" summary         │
│                                                                    │
│  2. PROPOSER                tools/today/propose.sh                 │
│     local System-AI (Ollama by default) drafts 3 story candidates  │
│     using tools/today/prompt.template.md + the signal summary →    │
│     ~/.fai/today/proposals/<ISO-DATE>-<n>.yaml                     │
│                                                                    │
│  3. REVIEW + ACCEPT         tools/today/accept.sh <proposal-id>    │
│     operator skims the proposal files, picks one (or none) →       │
│     ~/.fai/today/active.yaml                                       │
│                                                                    │
│  4. STUDIO                  lib/pages/store.dart                   │
│     Studio reads active.yaml at startup; falls back to the         │
│     compiled-in `_kCurrentTodayStory` when the file is absent      │
│     or invalid (KRITIS / fresh install path)                       │
└────────────────────────────────────────────────────────────────────┘
```

The accept step is the only human gate. Everything else can run from a launchd
agent (macOS), systemd timer (Linux), or `cron` — see
`tools/today/README.md` for the on-disk recipes.

## Schema

`~/.fai/today/active.yaml` (and every proposal file) follows this shape:

```yaml
schema: today/v1
badge_en: CAPABILITY FEDERATION
badge_de: CAPABILITY-FEDERATION
title_en: Two MCP servers away from a richer hub
title_de: Zwei MCP-Server bis zu einem reicheren Hub
body_en: |
  Click a recommended public source below — DeepWiki for repository docs,
  Semgrep for security scans — and synthetic `mcp.<server>.<tool>`
  capabilities appear here within seconds. No Node, no API key, no
  subprocess.
body_de: |
  Klicke unten auf eine öffentliche Quelle — DeepWiki für Repo-Doku,
  Semgrep für Security-Scans — und synthetische
  `mcp.<server>.<tool>`-Capabilities erscheinen binnen Sekunden hier.
  Kein Node, kein API-Key, kein Subprozess.
icon: hub_outlined        # Material icon name; loader maps to IconData
cta: openSettings | none  # enum from _TodayCta
cta_label_en: Open MCP settings
cta_label_de: MCP-Einstellungen öffnen
```

`schema: today/v1` is mandatory and gates loader compatibility. Future schema
revisions bump the version and Studio renders the const fallback rather than
attempt a dirty migration.

## Design rules for the prompt

The prompt template (`tools/today/prompt.template.md`) hard-encodes:

1. **One story, not three feature bullets.** Apple-Today-style storytelling
   — narrative arc, single takeaway, calls the operator forward.
2. **Bilingual peers, not machine translation.** EN and DE are written in
   parallel by the same prompt pass; tone matches between languages.
3. **No marketing speak.** The "no buzzwords" rule from
   `feedback_no_marketing_speak.md` is included in the prompt: ban
   "viral", "killer", "powerful", "just works", "revolutionary",
   "game-changing".
4. **No private references.** Generic terms only — no pilot
   customer names, internal codenames, or specific
   institutions. The operator's banned-terms list lives at
   `~/.fai-security/banned-terms.txt` (outside the repo on
   purpose; gated by the pre-commit hook in
   `tools/security/check-staged.sh`).
5. **Concrete CTA.** Every story names exactly one action the operator
   can take in Studio (which page, which button).
6. **Body fits in ~3 sentences.** Hero card height is fixed; longer
   stories truncate.

## Audit + air-gap

- All proposed and accepted stories live as plain YAML on disk;
  reviewable by `git log` if checked in.
- The pipeline calls only the operator's **already-configured** System-AI
  (Ollama / OpenAI / vLLM). It never reaches out to a CMS, never phones
  home, and works air-gapped if the System-AI does.
- KRITIS deployments can disable the pipeline entirely by removing the
  cron entry; Studio falls back to `_kCurrentTodayStory` and continues
  to work.

## Out of scope (Phase 0.5)

- Multi-story rotation (one active story at a time).
- A/B-testing or click-through tracking — Ch∆In has no surveillance budget
  and no telemetry pipeline to feed it into.
- Auto-publication to git (operator-curated boundary stays manual).
- Hub-served stories. Studio reads `~/.fai/today/active.yaml` directly
  for now; promoting this to a HubAdmin RPC is a Phase 1 question once
  multi-tenant Studio appears.
