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>
This commit is contained in:
parent
fe933a713f
commit
ce97300a12
11 changed files with 681 additions and 49 deletions
113
docs/today-pipeline.md
Normal file
113
docs/today-pipeline.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# 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
|
||||
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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue