chain-studio/docs/today-pipeline.md
flemming-it ce97300a12 feat(studio): daily Today-Hero proposal pipeline (v0.31.0)
Cross-store research (Apple, Play, Steam, Docker, VS Code,
Chrome Web Store, Flathub) consistently rewards editorial
curation over algorithmic recommendations — but manual
copywriting per release does not survive a solo-dev cadence.
This commit lands a daily-build pipeline so the Today-Hero
card stays fresh without operator hand-edits per release.

Pipeline shape (full design in docs/today-pipeline.md):

1. tools/today/collect.sh aggregates "what happened in the
   last 24 hours" across the F∆I monorepos: git log per repo,
   store-index seed.yaml diffs, architecture/system-gaps doc
   changes, Studio release tags, and (opt-in) audit-log
   highlights. Outputs plain text.

2. tools/today/propose.sh feeds the signal summary plus
   prompt.template.md to the operator's already-configured
   System-AI (Ollama default; OpenAI-compatible endpoints
   work via env-var override). Drafts N candidate stories as
   YAML files under ~/.fai/today/proposals/<date>/.

3. tools/today/accept.sh validates a chosen candidate against
   the today/v1 schema and the no-marketing-speak banned-word
   list, then atomic-renames it into ~/.fai/today/active.yaml.

4. Studio reads active.yaml at store-page init via the new
   TodayStoryLoader (lib/data/today_story_loader.dart). On any
   failure (file missing, schema mismatch, banned-words hit,
   parse error) it falls back to the compiled-in
   _kFallbackTodayStory so KRITIS deployments and fresh
   installs always render something sensible.

Trust + audit:
- All proposed and accepted stories live as plain YAML on disk.
- The pipeline calls only the operator's already-configured
  System-AI; it never reaches a CMS, never phones home, works
  air-gapped if the System-AI does.
- The bash accept gate AND the Dart loader both enforce the
  banned-word list — a hand-edited active.yaml that bypassed
  the shell still won't reach the UI.
- Removing the cron entry disables the pipeline; Studio falls
  back to the const story and continues to work.

Cron / launchd / systemd recipes documented in
tools/today/README.md.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 12:59:33 +02:00

5.5 KiB

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:

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 ITDZ / Kammergericht / HTW / J∆I per feedback_confidentiality.md.
  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 — F∆I 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.