Job ID: af0fe442cf56
Run Time: 2026-06-25 18:15:11
Schedule: 0 18 *
[IMPORTANT: The user has invoked the "editorial-review-cycle" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]
name: editorial-review-cycle
description: "Produce periodic (daily/weekly) editorial reviews from tracked topics in a structured memory system — parallel research, signal synthesis, topic updates, cross-link generation, hypothesis creation, integrity verification, topic ideation, and batch article production via delegate_task."
version: 1.14.0
author: Hermes Agent
license: MIT
platforms: [linux, macos, windows]
metadata:
hermes:
tags: [editorial, review, briefing, memory, veille, research, synthesis, article, ideation]
related_skills: [editorial-agent-system, news-briefings, blogwatcher]
prerequisites:
require_skills: [editorial-agent-system]
Produce a complete periodic review (daily or weekly) from a tracked topic set within a constitutional editorial memory system. This covers the full cycle: research → synthesis → memory update → hypothesis generation → cross-linking → integrity verification.
If the user asks to activate a module (e.g. "active le module revue_editoriale"), follow the activation sequence in Prerequisites above before producing any content. This is a distinct task from producing a review — it involves loading context, updating entity tracking, and establishing which subjects are being followed.
The editorial memory system must exist. It can be in one of two layouts:
Direct layout — all files at /memory/ (canonical):
00_index.md listing all tracked topicstopics/ with structure (contexte, faits marquants, questions ouvertes, sources)Modular layout — module in one of two locations:
Primary vault path — /memory/modules/<module_name>/:
memory/modules/<module_name>/README.md describing the module's purpose and perimetermodule_config.md with rules, naming conventions, and output destinationsprofile.md describing the assistant's role, posture, and methodmemory/ (index, entities, decisions, hypotheses, glossary)Hermes profile path — ~/.hermes/profiles/<profile_name>/modules_revues/:
revue_editoriale profile).~/.hermes/profiles/<module_name>/ exists and has a modules_revues/ subdirectory.topics/, briefs/, hypotheses/, analyses/, agents/, sources/.cross_profile write guard does NOT apply to write_file (it only guards skills/plugins/cron/memories/ directories), so direct file writes through absolute paths work normally.editorial_feedback/) may live under modules_revues/ or in the profile's own memories/ — check both.Activation sequence for modular layouts: Before starting any work, always load the module's context files in order:
This establishes which paths to write to and what conventions to follow before any research or writing begins.
If neither layout exists, run the editorial-agent-system skill first to create the system.
Convention: /memory is the user-facing Obsidian vault. /opt/data/memory is reserved for the agent's internal technical memory (memory tool). Never write user-facing documents to /opt/data/memory. The module's own memory/ directory contains the local index, entities, decisions, hypotheses, and glossary.
Permission pitfall: /memory is often a separate mount point. If you get "Permission denied" writing to it, the user must chown it from the Docker host: chown -R <uid>:<gid> /memory (the uid/gid of the hermes user, typically 10000:10000).
~/.hermes/profiles/ AND that directory has a modules_revues/ subdirectory, the base path is ~/.hermes/profiles/<name>/modules_revues/./memory/modules/<name>/./memory/.<base>/00_index.md) to identify active topics, their priorities, and last-update dates.<base>/memory/): entities.md (who/what is tracked), decisions.md (past editorial choices), hypotheses.md (active theses).edge_ai = embedded MCU/TinyML vs desktop/GPU local inference — separate hardware tiers, different frameworks, different user profiles).status: brouillon and link both ways. A split can always be merged back; a bloated topic is harder to prune.When the user asks "propose-moi des sujets/topics" or "propose-moi des idées d'articles" — a creative proposal step distinct from researching a named entity or writing a brief. This is a curatorial phase: look at what exists, what's happening, and propose what's worth adding or writing.
Trigger: User says "liste des sujets du moment", "idées de topics à ajouter", "quels sujets devrais-je suivre".
00_index.md) — identify existing topics, their priorities, and last-update dates.briefs/daily/ and briefs/special/) — look for signals that don't have a natural topic home, or clusters of signals that warrant consolidation.Structure for presentation:
| # | Topic | Priorité | Description | Justification |
|---|-------|----------|-------------|---------------|
| 1 | `slug` | 🔴 | One-liner | Why now, what gap it fills |
Trigger: User says "propose-moi plusieurs idées d'article avec leur angle", "idées d'articles", "suggestions de contenu long".
This is a creative + editorial step. It combines knowledge of current events with a sense of narrative angle, structure, and tone. Each article idea should include:
Present as a structured list with clear distinction between articles:
### Article N : Titre
**Angle :** ...
**Structure :** ...
**Ton :** ...
**Contenu :** ...
Best practices for article ideation:
Trigger: User says "oui" or "go" after you proposed article ideas, or asks you to write multiple articles at once.
Use delegate_task(tasks=[...]) in batches of up to 3 (per max_concurrent_children=3) to write articles in parallel. Each subagent needs:
goal — "Rédige un article long (~12 000 signes) en français sur [topic]. Sauvegarde dans [output path]"context — must include:toolsets — ["terminal", "file", "web"] for articles needing fresh research (web search + file write). The web toolset enables Network access in subagents.Context structure template:
Contexte : [Key facts, dates, numbers, sources]
Angle : [Unique hook — why this story, from what perspective]
Structure : [Chapter breakdown with section descriptions]
Ton : [Voice, register, style notes]
Goal summary: [One-line reaffirmation]
After all batches complete:
ls -la on each output path)00_index.md — add each article to the outputs/ tree listingPitfall — max_concurrent_children=3: If you send 4+ tasks in one delegate_task(tasks=[...]), it fails. Split into batches of 3. The second batch runs after the first completes. Total wall-clock time stays roughly the same since parallelism is per-batch.
Pitfall — subagent language: Subagents default to English for summaries. When the article must be in French, explicitly say "en français" in the goal and context fields. Add "Réponds en français — l'article doit être rédigé en français" at the start of the context.
Pitfall — subagent summary reliability: A subagent that says "article written to path X" may be wrong. Always verify file creation yourself in Phase 7 (integrity verification).
Pitfall — subagent file naming convention mismatch: Delegate_task subagents may create files with names that do not match the module's established convention. For example, a subagent told to create briefs/daily/YYYY-MM-DD.md may instead write 2026-06-16-brief-quotidien.md (appending a descriptive suffix because the prompt was more descriptive than the exact filename). After every delegate_task batch, run ls -la on the target directory and rename any files that deviate from the convention with mv via terminal. Then update any file references in the subagent's summary to match the renamed paths.
Research method depends on context: live conversation vs automated cron, number of topics, and available extraction backends.
For simpler requests (1-3 topics) in a live conversation, research directly in the main thread. This avoids delegate_task overhead when the scope is narrow.
web_search calls across topics that need updating — send multiple searches at once, one per topic or signal type. Set limit=10 for broader coverage; default limit=5 may miss signals for fast-moving topics like geopolitics.web_extract(urls=[...]) — preferred for PDFs, API endpoints, plain-text pages. ⚠️ Only works when the configured web.extract_backend supports URL extraction. When the backend is ddgs (DuckDuckGo), web_extract fails with "DuckDuckGo (ddgs) is a search-only backend and cannot extract URL content."browser_navigate(url) + browser_snapshot(full=True) — fallback when web_extract fails. Handles JS-heavy news sites (Al Jazeera, Guardian, etc.) better than curl. Use browser_get_images() for illustration context when relevant.todo — mark completed topics so you don't miss one in a multi-topic brief.Pitfall: web_search with DuckDuckGo backend returns results with titles and descriptions but web_extract cannot fetch the underlying page content. This creates a gap: you see that something exists but cannot access the detail. Always plan to browser-navigate to at least the top 1-2 results per topic to fill this gap.
See references/browser-content-extraction.md for a full step-by-step guide to the browser-based extraction workflow, known-working sites, and pitfalls.
Use delegate_task to research each topic simultaneously (up to 3 per batch per the default max_concurrent_children=3). Pass toolsets=["terminal","web"] — the web toolset enables curl-based RSS/HTML fetching; browser is not needed for bulk text extraction.
Pitfall — max_concurrent_children limit: If you send 4+ tasks in one delegate_task(tasks=[...]) call, it fails with "Too many tasks: N provided, but max_concurrent_children is 3". Split into batches of 3. The second batch runs after the first completes, keeping total session time roughly the same (parallel is within each batch, not across batches).
When the user says "lance les revues quotidiennes manquantes" or asks you to fill a gap of 3+ missing days, follow this workflow instead of producing briefs one at a time.
Trigger: User explicitly asks for "missing" or "rattrapage" briefs, or the file listing shows a multi-day gap between existing briefs and today.
briefs/daily/ and compare against the calendar. Check 00_index.md separately (it may be out of date). Identify each truly missing day and its day-of-week.delegate_task(tasks=[...]) in batches of 3 (max_concurrent_children limit). Each subagent researches one day across all tracked topics. The context MUST specify:00_index.md. Update priorité and dernière mise à jour fields for topics that had new signals.Pitfall — table row double-pipe in index: When patching 00_index.md to add new briefs to the tree, the patch tool may introduce || (double pipe) or ||| (triple pipe) prefixes on replaced rows. After every patch on the index file, verify with grep -nP '^\\|\\|' <file>. Fix any stray double-pipes by patching again with single-pipe rows.
When setting up automated daily briefs, prefer a two-cron pipeline rather than a single monolithic cron:
script= that curl-fetches RSS feeds with || true isolation. Uses news-briefings skill. Produces raw data in sources/rss/YYYY-MM-DD.md. Delivers a short Telegram summary.context_from=<job_id_of_collection_cron> to receive the collected data. Uses editorial-review-cycle skill. Produces the full editorial brief in briefs/daily/YYYY-MM-DD.md. Updates 00_index.md. Delivers to Telegram + local.This separation has three advantages:
cronjob(action='run')) if it fails, without re-fetching feedsTo create the editorial cron:
cronjob(
action='create',
name='Brief quotidien éditorial',
schedule='0 18 * * *',
skills=['editorial-review-cycle'],
context_from=['<collection_job_id>'], # receives collected data as prompt
deliver='telegram:CHAT_ID,local', # Telegram + local archive
prompt="Produis un brief quotidien complet...",
)
Pitfall — context_from profile mismatch: The cron job that provides context (context_from) may run under a different Hermes profile than the editorial cron. Check the source cron's profile field in cron/jobs.json. If profiles differ, context_from may still work (the runtime fetches output from the shared output store), but test with cronjob(action='run') after creation to confirm data flows correctly.
Pitfall — context_from not persisted by cronjob API: The cronjob(action='update', context_from=[...]) API may save the update to the in-memory job object but NOT write it to ~/.hermes/profiles/<profile>/cron/jobs.json on disk. After updating via the API, always verify with a direct read of the JSON file:
python3 -c "import json; j=json.load(open(expanduser('~/.hermes/profiles/journal/cron/jobs.json'))); [x for x in j['jobs'] if x['id'] == '<job_id>'][0]['context_from']"
If context_from is None or missing, edit the JSON file directly with a terminal one-liner or patch — the cron scheduler reads from the JSON file, not from the API's in-memory state.
Pitfall — recovering data from a failed collection cron: When the data-collection cron (e.g. news-briefings at 16:00) crashes with a Broken pipe (Errno 32) during output delivery, the cron shows last_status: "error" and its context_from payload may be empty. However, the stored output file on disk often survives intact — the agent's data-collection script ran before the delivery crash. Always check the stored output before declaring data lost:
ls ~/.hermes/profiles/<collection_profile>/cron/output/<job_id>/
The file follows the naming convention YYYY-MM-DD_HH-MM-SS.md. Read the full file — the collected data (RSS feeds, script output) is typically stored in a ## Script Output section or code block. The final error appears at the end of the file, separate from the collected data. If the data survived, extract it and proceed with the editorial production as normal. If the stored output file is absent or empty, only then fall back to running fresh research (live session) or reporting the failure (cron context).
Mitigation: To prevent future losses, configure the data-collection cron with a script= parameter that writes raw data to sources/rss/YYYY-MM-DD.md in the editorial module. This decouples data collection from the agent's output delivery — even if the agent crashes, the file persists on disk.
See references/catch-up-batch-worked-example.md for a concrete example of this workflow from an actual session (5 missing briefs filled, 13 June 2026).
For existing topics (updating with new signals):
curl (reliable, no JS dependency)summary (inline text) rather than scattering /tmp/*.md files which must be cleaned up laterQuiet period handling: When research returns no signals for a topic (or all topics), do NOT fabricate content or pad with irrelevant news. Instead:
For weekly reviews (synthesising from existing daily briefs): research scope is narrower. You already have 3+ daily briefs covering previous days — only research NEW signals since the last daily brief was produced. The bulk of the weekly review comes from synthesising the daily briefs, not from fresh research. Allocate 2-3 parallel subagents to the most active topics (typically geopolitics, agents AI, open source AI) for fresh signals only.
# Example pattern (pseudocode) for updating existing topics:
tasks = [
{"goal": "Research recent developments in {topic}", "context": current_topic_text, "toolsets": ["browser","terminal"]},
...
]
results = delegate_task(tasks=tasks)
For creating a new topic from scratch (no existing signal base): use the 3-angle parallel research pattern. Spawn 3 subagents covering complementary dimensions of the same subject. This produces richer coverage than a single monolithic delegate:
| Angle | What it covers | Example for "inférence locale" |
|---|---|---|
| Frameworks & tools | Moteurs d'inférence, versions, controverses, tendances logicielles | Ollama v0.30, LM Studio 0.4.0, llama.cpp b9495, vLLM, Jan, LocalAI, GPT4All |
| Hardware & benchmarks | GPU benchmarks, CPU/NPU comparison, nouvelle génération hardware, recommandations budget | RTX 5090/4090 benchmarks, Apple Silicon vs NVIDIA, NPU laptop limits |
| Models & applications | Modèles open-weight récents, quantification, cas d'usage concrets | Qwen 3.6-A3B, Gemma 4, Phi-4, Mistral Small 4, RAG local, agents |
All three run in parallel via delegate_task(tasks=[...]). Give each the same high-level context (why this topic matters, what questions to answer) but a distinct goal per angle. Collect results, deduplicate, and synthesize into the topic file's structure.
See references/3-angle-research-template.md for a copy-paste template and a worked example from an actual session.
When the user asks to create a new topic in an existing module (e.g. "ajoute aussi en Topic : Eurailscout/Trealis, Altametris, Rail Open Lab"), this is an intermediate phase between Research and Synthesis. It involves researching multiple entities, writing a structured topic file, and updating the index — distinct from both updating an existing topic and creating a new module.
Trigger: User says "ajoute en topic", "crée un sujet sur", "add a topic about", or asks to document a new subject within an existing tracked set.
delegate_task:Parallel research works well when the user names 2-4 specific entities. Launch one subagent per entity (up to 3 per batch). Each subagent's context must specify:
tasks = [
{"goal": "Research Entity A in detail", "context": "...", "toolsets": ["web"]},
{"goal": "Research Entity B in detail", "context": "...", "toolsets": ["web"]},
{"goal": "Research Entity C in detail", "context": "...", "toolsets": ["web"]},
]
delegate_task(tasks=tasks)
Variant A — Signal-tracked topic (default): For topics that follow ongoing developments (regulations, markets, tech trends, geopolitics). This is the format used by most topics in the revue_editoriale module.
---
title: "Topic Title — description courte"
date: YYYY-MM-DD
updated: YYYY-MM-DD
status: actif
priority: 🟡
tags: [tag1, tag2]
sources: [Source1, Source2]
---
# Topic Title
> Suivi du sujet — one-line description.
> Dernière mise à jour : YYYY-MM-DD.
---
## Contexte
1-3 paragraphs explaining why this topic exists, what it covers, its significance.
## Signaux récents
| Date | Signal | Source | Priorité |
|------|--------|--------|----------|
| YYYY-MM-DD | Latest signal | Source | 🔴 |
Each row = one signal. Sort by date descending.
## Acteurs clés (optional)
Bullet list of key actors with brief description and status.
## Questions ouvertes
- Question 1?
- Question 2?
Variant B — Entity-focused topic (multi-actor domain): For topics where multiple organisations/products need structured profiles (e.g. railway inspection companies, drone manufacturers, space programs). Use type: topic, cycle: permanent, module: frontmatter fields.
---
title: "Topic Title"
date: YYYY-MM-DD
type: topic
cycle: permanent
priority: 🟡
module: revue_editoriale
---
# Topic Title
> One-line description.
> Créé le : YYYY-MM-DD
> Dernière mise à jour : YYYY-MM-DD
---
## 1. Contexte
## 2. Acteurs / Entités
### Entity A
| Champ | Valeur |
|---|---|
| **Statut** | Description |
### Entity B
...
## 3. Relations entre les entités
## 4. Actualités récentes
| Date | Fait | Acteur | Source |
|---|---|---|---|
## 5. Tendances clés
Variant C — Company/Product profile (single entity): For topics about a specific company, product, or technology where the primary value is its product catalog, competitive landscape, and use cases (e.g. LUNAAR smart glasses, Vectron X locomotive). Use type: topic, cycle: permanent, module: frontmatter fields.
---
title: "Company/Product Name — description"
date: YYYY-MM-DD
type: topic
cycle: permanent
priority: 🟡
module: revue_editoriale
tags: [tag1, tag2]
sources: [Source1, Source2]
---
# Company/Product Name
> One-line description.
> Créé le : YYYY-MM-DD
> Dernière mise à jour : YYYY-MM-DD
---
## 1. Contexte
1-3 paragraphs: what the company/product does, why it matters, market position.
## 2. Produits et solutions (if product-based)
| Produit | Usage | Certification | Description |
|---------|-------|---------------|-------------|
## 3. Acteurs / Entités
Table of key actors: company, role, details.
## 4. Concurrence et alternatives
| Produit | Concurrent | Forces | Faiblesses |
## 5. Actualités récentes
| Date | Fait | Source | Priorité |
## 6. Tendances clés
Bullet list of market/tech trends affecting this entity.
## 7. Liens
Cross-references to related topics (Variant A or B) that share domain or use cases.
## 8. Sources
Decision rule: Use Variant A for topics tracking ongoing signals and developments (most editorial topics). Use Variant B when the primary value is entity profiles and their inter-relationships, especially when the user named specific organisations as the reason for creating the topic. Use Variant C for single-entity company/product deep-dives where the focus is the product catalog, competitive landscape, and use cases rather than ongoing signal tracking or multi-actor dynamics.
topics/:/memory/: topics/<slug>.md~/.hermes/profiles/revue_editoriale/modules_revues/topics/<slug>.mdmodules_revues/ path when the module is a profile.skills/plugins/cron/memories/ under another profile trigger the cross-profile write guard. Writing topics/, briefs/, hypotheses/, analyses/, or sources/ under modules_revues/ in another profile does NOT trigger the guard — use plain write_file.date:, type: topic, cycle: permanent, priority:, module:00_index.md) with:topics/ or at module root, matching the layout of existing topics)│ ├──, │ └──, ├──, └──). When patching, match the EXACT indentation of the line above your insertion point. Adding a topic with the wrong indentation prefix (e.g. │ ├── when it should be ├──) will break the visual tree structure. After patching, verify by reading the affected lines.|| or ||| prefix (double/triple pipe) instead of |. Match the EXACT prefix of the rows you're replacing. When adding a new row, use the same prefix as the row above it. After patching, run grep -rn '^||' <file> to catch stray double-pipe prefixes.Dernière mise à jour date and add a note about the cross-referencereferences/github-releases-changelog-scraping.md for the techniqueWhen the user says "active l'agent contra/contrarian/contre-épreuve sur ce brief", they want the Contrarian agent's contre-épreuve workflow applied to a specific document. This is a distinct editorial action — not producing a new brief, but validating and challenging an existing one.
Trigger: "active l'agent [name] sur [document]", "lance le contra", "applique le controleur", "contre-épreuve sur".
Workflow:
agents/contrarian.md to understand its mission, workflow, format de sortie, and biais detection grid.## Thèse originale (principe de charité) — reformulate the thesis fairly## Antithèse — the opposing argument## Scénarios alternatifs — minimum 2 (most likely if wrong, most disruptive if right but poorly framed)## Biais identifiés — table with confirmation, disponibilité, autorité, groupe, ancrage, survie## Verdict — ACCEPTÉ | ACCEPTÉ AVEC RÉSERVES | REJETÉanalyses/contre-epreuve-<slug>-<date>.mdanalyses/Mode B — Multi-brief Contrarian (synthetic journal):
When the user says "passe au contra sur les N derniers jours" or asks you to apply Contrarian at the semantic level across a set of daily briefs, the goal is to challenge the dominant narratives of a period, not validate a single document.
Trigger: "passe au contra", "contre-épreuve sur les briefs de la semaine", "analyse les 5 derniers jours avec le contrarien".
Workflow:
briefs/daily/ for the requested period.## Thèse originale → ## Antithèse → ## Biais identifiés → ## Scénarios alternatifs. Use the same biais grid as Mode A.analyses/contre-epreuve-<slug>-<date>.mdanalyses/ tree in 00_index.mdKey difference from Mode A: Mode A validates a single document. Mode B synthesizes N documents into a meta-analysis. The Contrarian's output is not the final product — it feeds into a broader journal article that presents both the thesis and the counter-thesis.
Pitfalls:
When the user asks you to "édite un journal" from a set of existing daily briefs, the deliverable is a comprehensive analytical article that synthesizes the period — NOT a fresh weekly review with new research. This is a retrospective synthesis from already-written material.
Trigger: "édite un journal", "fais un journal des X derniers jours", "résumé synthétique de la semaine".
Workflow:
read_file on each daily brief. Also read any relevant special briefs.outputs/journal-<descriptor>-<date>.mdMEDIA:/path/to/journal.md for the full file.outputs/ tree in 00_index.mdStructure template:
# Journal Hermes — {Date range}
## La semaine qui a tout changé : {Narrative 1}, {Narrative 2} et {Narrative 3}
> One-paragraph hook summarizing the period.
---
## Chapitre 1 — {Narrative 1}
### Le récit dominant
{3-5 paragraphs of synthesis from the daily briefs}
### [CONTRARIEN] — {Counter-thesis}
**Thèse** : {fair statement}
**Antithèse** : {opposing argument}
**Scénario alternatif** : {what changes if wrong}
**Fait historique** : {reference older than 2 years}
## Chapitre 2 — {Narrative 2}
...
## Chapitre 3 — {Narrative 3}
...
## Chapitre 4 — Les signaux moins visibles
| Signal | Importance | Pourquoi |
|--------|-----------|---------|
## Chapitre 5 — Scénarios pour les 30 prochains jours
| Scénario | Probabilité | Impact | Déclencheur |
|----------|-------------|--------|-------------|
---
## Statistiques du module
| Métrique | Valeur |
|----------|--------|
Decision rule: Use this pattern when the user already has daily briefs and wants a synthetic product. Do NOT use it when the user asks for fresh research (use the weekly review workflow instead). The journal is a recombinant product — it remixes existing content, it does not gather new signals.
Difference from weekly review: A weekly review (Phase 3) does fresh research and updates topics. A synthetic journal reads existing briefs and applies Contrarian at the corpus level. The two can be combined: produce a weekly review with fresh research, then a synthetic journal with Contrarian overlay.
When a major event requires a dedicated brief beyond the daily/weekly cycle, create a special brief as a standalone document. Use this when the event is significant enough to deserve its own synthesis (peace treaty, regulatory bombshell, natural disaster, IPO, etc.).
Trigger: User says "brief spécial", "fais un point sur [event]", the user asks for a comprehensive overview of a specific event that isn't a daily or weekly cycle.
Workflow:
briefs/special/<date>-special-<slug>.mdStructure:
---
title: "Brief Spécial — Title"
date: YYYY-MM-DD
type: brief
cycle: special
canal: telegram
---
# 📜 BRIEF SPÉCIAL — Title
> Date — Signature prévue/événement
> Synthèse complète
---
## 🚨 Les faits
One-paragraph summary of what happened.
---
## 📋 Terms / Dispositions (if agreement)
| Disposition | Détail |
|---|---|
| Clause 1 | Detail |
| Clause 2 | Detail |
---
## 📆 Chronologie
| Date | Événement |
|---|---|
| DD/MM | Event |
---
## 🌍 Impacts
### Geopolitique
### Économie
### Humanitaire
---
## 🔮 Prochaines étapes
| Date | Événement |
|---|---|
## Questions ouvertes
Bullet list of key unknowns.
briefs/special/ tree in 00_index.mdThe special brief lives in briefs/special/ alongside daily and weekly subfolders. It is NOT an outputs/ article — articles are for long-form analysis, special briefs are for event-focused synthesis.
When the user asks for a comprehensive article ("génère moi un article complet", "analyse complète de X", "écris un article sur Y"), produce a standalone document in outputs/. This is distinct from a brief — articles are long-form, narrative, with chapters and analysis depth.
Trigger: "article complet", "analyse détaillée", "écris un truc complet sur", user asks for more depth than a brief allows.
Workflow:
outputs/<slug>-<date>.md (NOT in briefs/)outputs/ tree in 00_index.mdDelivery format (user preference): When delivering an article to Telegram, do NOT post the full text in the chat. Instead:
MEDIA:/absolute/path/to/article.md at the end — the platform delivers it as a native file attachmentChapter structure pattern for geopolitical/regulatory articles:
Architecture/technical articles — including ASCII diagrams: When the article describes a technical architecture, infrastructure stack, or multi-component system (e.g. how Hermes Agent orchestrates Home Assistant via MCP, or the RTX Spark + OpenShell stack for personal agents), include an ASCII architecture diagram. Use simple box+arrow diagrams:
┌─────────────────┐ ┌──────────────────┐
│ Component 1 │────▶│ Component 2 │
│ (detail) │ │ (detail) │
└─────────────────┘ └──────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ Component 3 │ │ Component 4 │
└─────────────────┘ └──────────────────┘
Rules for ASCII diagrams:
┌, ┐, └, ┘, ─, │, ▶ characters only (these render reliably in all terminals and markdown)outputs/ articles, not in daily briefs (briefs are consumed on Telegram where ASCII may break)Scenario-analysis variant (geopolitical/regulatory forecasting):
When the core value of the article is exploring multiple futures (not just narrating the past), use this enriched variant. It replaces the standard chapter structure with a scenario-centric architecture:
# Title — Subtitle
## Prologue — The hook
One-paragraph framing that establishes the tension. What's at stake, why now, what's the time window.
## Ch. 1 — Les acteurs et leurs contraintes
Cartography of every key stakeholder: their position, constraints, levers. For each: what they want, what they can't do, what they could do if pushed.
## Ch. 2-N — One chapter per scenario
Each scenario chapter includes ALL of:
**Probability estimate** (e.g. 35%) | **Impact level**
### Le récit
3-5 paragraphs of detailed chronology: what happens, when, who does what, what breaks.
### Conséquences
Bullet list: geopolitical, economic, humanitarian impacts.
### Signaux de détection précoce
5 numbered signs to watch — concrete, observable events (not general trends).
### Analyse Contrarian (embedded)
**Thèse** : what this scenario assumes — state it fairly.
**Antithèse** : the counter-argument within this specific scenario. Why it might not unfold this way.
**Parallèle historique** : a reference older than 2 years (e.g. Suez 1956, Oslo 1993, Doha 2020). What makes this scenario similar and different.
**Évaluation** : is the probability under- or over-estimated? What blind spots does this scenario have?
## Ch. N+1 — Le dénominateur commun
The cross-cutting constraint that ALL scenarios share (e.g. "the nuclear question was pushed to 60 days in every scenario"). This chapter identifies what doesn't change regardless of which future unfolds — the structural bottleneck.
## Épilogue
Summary table: scenario name | probability | impact | "signal clé à J+7" (one concrete thing to watch right now).
Then two sections:
- **Ce que l'accord/événement a changé** — the irreversible shifts
- **Ce qu'il n'a pas changé** — the open questions
- Final Contrarian assessment: score de robustesse global (1-10).
## Références
List of source briefs, analyses, and topics referenced.
See references/scenario-article-format.md for a complete worked example from the Iran accord analysis.
Decision rule: Use the standard chapter structure for historical/narrative articles (telling a story from past to present). Use the scenario-analysis variant when the user asks for "scénarios", "trajectoires", "que va-t-il se passer", or when the event is ongoing and the future is genuinely open. The scenario variant is NOT for articles about completed events (use the standard structure instead).
For scenario-focused articles on the other hand, use the scenario-analysis format instead of the general structure above: one chapter per scenario, each with its own Contrarian analysis, detection signals, and historical parallel. See references/scenario-analysis-article-format.md for the full template and a worked example.
Difference from special brief: A special brief is factual and event-focused (what happened, when, who). An article is analytical and narrative (why it happened, what it means, what comes next). Article = brief + analysis + scenarios + depth.
When the user follows up on a topic by asking for details on a specific physical site (e.g. "détaillé moi le triangle de Connerré"), this is distinct from both new-topic creation and existing-topic enrichment. The user wants deep context on one sub-element already referenced in the topic. The full workflow is in references/site-deep-dive-worked-example.md — including geography research, technical deployment details, historical context, and cross-referencing back to the parent topic.
When the user asks "détaillé moi [regulation/subject] et en quoi ça m'impacte" (e.g. "what does the EU AI Act mean for me?"), this is a specific pattern: the user wants a comprehensive explanation TAILORED to their personal and professional context, not a generic summary.
Trigger: User asks for an explanation of a regulation, law, or policy with explicit framing like "en quoi ça m'impacte", "what does this mean for me", "suis-je concerné".
Workflow:
Tone: Factual, structured, direct. No speculation. If a deadline is "J-48" say it. If an amende is "35M€" say it. The user asked for concrete impact, not editorial commentary.
See references/eu-ai-act-impact.md for a worked example of a regulatory deep-dive covering professional + personal impact (EU AI Act × SNCF Réseau, updated June 2026).
If the user mentions entities that fit within an existing topic (e.g. adding "Hermes Agent v0.16.0" info to the existing hermes_agent.md topic), expand the existing file instead of creating a new one. Add new sections or subsections as needed. The existing topic's Signaux récents table is the first place to add new entries.
Decision rule: Create a new topic when the entity/subject has no natural home in any existing topic. Enrich an existing topic when it clearly belongs there.
Cross-linking between related topics: When two topics share one or more entities (e.g. Altametris appears in both inspection and drones topics), add explicit cross-references in both directions. In each topic's Liens section, add - Voir aussi : topic — [reason]. Bump both topics' Dernière mise à jour date. This avoids duplicating entity descriptions across related topics.
For enriching an existing topic (adding a deep-dive on one of its sub-elements): When the user says "ajoute ceci au topics" after a verbal deep-dive, the info goes into the EXISTING topic file that already covers that domain — not a new file. Identify the section to expand, then patch to replace the brief bullet-point placeholder with the full structured deep-dive (geography, chronology, technology, partners, outcomes). Update the 00_index.md entry's description line if the topic's scope broadened.
Write the daily brief to briefs/daily/YYYY-MM-DD.md with this structure:
---
title: "Revue Quotidienne Hermes"
date: YYYY-MM-DD
type: brief
cycle: daily
canal: cli
certification: "Constitution Fondatrice Hermes — Agent: Analyst & Contrarian"
---
# Revue Quotidienne — {Date}
> Synthèse des signaux détectés sur les N territoires suivis.
> Agent Sentinel — revue par Analyst + contre-épreuve Contrarian.
---
## 🔴 ALERTE — {Titre de l'alerte majeure}
**Source** : ...
**Certitude** : {CERTAIN | PROBABLE | SPÉCULATIF}
**Implication** : ...
{Analyse 3-6 lignes}
**Contre-épreuve** : [CONTRARIEN] {Contre-argument / scénario alternatif}
---
## 1. {Topic 1 — Titre}
**Signaux** : tableau des signaux avec priorité + source
**Analyse** : 3-5 lignes
**Contre-épreuve** : [CONTRARIEN] ...
## 2. {Topic 2 — Titre}
...etc...
---
## 🔗 Liens entre sujets détectés
{Liste de 3-8 connexions inter-topics avec explication d'1-2 lignes}
---
## 💡 Hypothèses émergentes
{Liste de 3-6 hypothèses avec une ligne d'explication}
---
## 📋 Recommandations mémoire
1. Mettre à jour les topics avec les signaux du jour
2. Créer `hypotheses/` avec les hypothèses
3. Proposer nouveaux dossiers si nécessaire
4. Archiver les synthèses brutes dans `sources/`
---
*Signaux intégrés : N (X 🔴, Y 🟡, Z 🟢)*
*Contre-épreuve : ✓ (...)*
For weekly reviews — write to briefs/weekly/YYYY-MM-DD.md. Two format variants are acceptable; pick based on how much happened during the week.
Variant A — Topic-by-topic (calm week, 1-2 major stories):
--- (same frontmatter) ---
...
Variant B — Alertes-driven (intense week, 3+ major stories):
This variant is preferred when the week had 3+ major stories that span across topics. It opens with individual ALERTE sections for each major story, followed by a compact per-subject summary table and forward-looking sections. This avoids the problem of the same story needing to be mentioned in every topic section.
---
title: "Revue Hebdomadaire Hermes"
date: YYYY-MM-DD
type: brief
cycle: weekly
week: "DD-MM à DD-MM-AAAA"
canal: cli
certification: "Constitution Fondatrice Hermes — Agent: Analyst & Contrarian"
---
# Revue Hebdomadaire — Semaine du {start} au {end}
> Synthese hebdomadaire des signaux detectes sur N sujets suivis.
> N briefs quotidiens (dates).
> Agent Sentinel — revue par Analyst + contre-epreuve Contrarian.
---
## 🔴 ALERTE S1 — {Titre alerte majeure #1}
**Chronologie :**
| Date | Evenement |
|---|---|
| JJ | Detail |
**Implication :** 4-8 lignes.
**Contre-epreuve :** [CONTRARIEN] ...
---
## 🔴 ALERTE S2 — {Titre alerte majeure #2}
... (repeat S3, S4 if needed)
---
## 📋 Synthese par sujet
| Priorite | Sujet | Fait marquant |
|---|---|---|
| 🔴 | Topic A | Detail cles |
| 🟡 | Topic B | Detail cles |
| 🟢 | Topic C | Detail cles |
(Compact table with 1-2 lines per subject, no separate sections for quiet topics)
---
## 📊 Tendances lourdes de la semaine
| Tendance | Detail |
|---|---|
| Tendance 1 | 2-3 lignes |
---
## 💡 Hypotheses — mise a jour hebdo
| Hypothese | Statut |
|---|---|
| Hypotheses A | VALIDEE / CONFIRMEE / Stable |
---
## 📅 Agenda semaine a venir (dates)
| Date | Evenement |
|---|---|
---
## 📈 Statistiques du module
| Metrique | Valeur |
|---|---|
| Briefs quotidiens produits | N (dates range) [+X cette semaine] |
| Briefs hebdomadaires | N [+X] |
| Topics actifs | N [+X cette semaine] |
| Hypotheses suivies | N |
| Analyses | N |
Decision rule: Use Variant A (topic-by-topic) when in doubt — it's the default. Use Variant B (Alertes-driven) when the week had 3+ genuinely major stories that cut across multiple topics and would need to be repeated in each section. The Alertes-driven variant is NOT for weeks with 1-2 normal stories — Variant A works better for those.
Always include a "Statistiques du module" section in the weekly brief. It tracks the module's growth across briefs produced, topics active, hypotheses, and analyses. This gives the user a quick sense of the module's health and trajectory.
For each topic, rewrite the topic file with:
date: and updated: in frontmatterFormat for signal table:
| Date | Signal | Source | Priorité |
|------|--------|--------|----------|
In each topic file, add a "Liens" section with:
Lien fort — direct relationship to another topic (e.g., Edge AI + HA/ESPHome)Hypothèse — link to hypothesis that connects this topic to othersBrief — link to the daily/weekly briefFormat consistency: Use - Lien fort :, - Hypothèse :, - Brief : (dash prefix). Do NOT use | (pipe) prefix — pipes are for table rows, not list items. Verify after each patch that the list markers are consistent.
Apply the principle of charité — don't force links that don't exist. Quality over quantity.
For each pattern you notice across topics, create a hypothesis file in hypotheses/:
---
title: "Hypothesis title"
date: YYYY-MM-DD
status: active
tags: []
sources: []
---
# Hypothèse : Title
**Postulat** : {One-paragraph thesis}
**Arguments pour** : {Evidence from the research}
**Arguments contre** : {What would weaken this}
**Ce qui infirmerait** : {Specific condition that would falsify this}
**À réévaluer dans** : {N} jours.
Hypotheses MUST include:
After all files are written:
grep -rn '\\\\.\\\\.\\\\/' on all modified files. Also verify each referenced file actually exists on disk.- (dash), not | (pipe). A grep '^| ' on modified topic files can catch stray pipes in link sections.topics/, hypotheses/, briefs/ must be referenced in 00_index.mddate:, status:, tags:/memory/:chmod 664 <fichier.md> on every new or updated .md file (target: rw-rw-r--)chmod 775 <dossier> on every new directory created (target: rwxrwxr-x)ls -la <fichier.md> and fix immediately if wrong-rw-------) and fix them too — these break Obsidian via SSHFS on the host Macls -la shows group hermes (or 10000) instead of users, the SSHFS client user gets read-only access. Inform the user they must run chown -R 10000:users /path from the Docker host to fix group ownership. This cannot be done from inside the container (no sudo, hermes not in users group)./tmp/.md or /opt/data/.md research artifacts left by subagents. Also remove any files the subagents wrote to /opt/data/ during research phase.archive/sessions/YYYY-MM-DD-<slug>.md. The log should briefly describe what was done (not a transcript) and list the next steps. This prevents the "what happened last time" question and gives Archivist a reference point. Format:# Session — {Title}
**Date** : YYYY-MM-DD
**Contexte** : {1-2 line context}
## Actions
1. {bullet list of actions taken}
## Résultat
{what changed, key numbers}
## Prochaines étapes
- {from kanban or roadmap}
After writing the brief and before updating topics, deliver the finished brief to the user's preferred platform if applicable. This bridges the editorial review cycle with the messaging delivery layer.
When to deliver: If the user explicitly asked for a platform delivery (e.g. "envoie sur Telegram"), or if the brief was produced via cron with a deliver target, do this between writing the brief and starting topic updates.
When the user asks to send articles to a remote VPS (e.g. 185.229.202.36), use this workflow:
ls ~/.ssh/. If no key directory exists, generate one: mkdir -p ~/.ssh && ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa_vps -N ""
sshpass is often unavailable and apt may be locked, use a Python script with pexpect (available on most Hermes hosts) to enter the password: import pexpect, os
password = os.environ["VPS_PASSWORD"]
host = "root@HOST"
with open(os.path.expanduser("~/.ssh/id_rsa_vps.pub")) as f:
pubkey = f.read().strip()
child = pexpect.spawn(f"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null {host}", timeout=30)
child.expect('assword:')
child.sendline(password)
child.expect(['#', '$'])
child.sendline(f"mkdir -p ~/.ssh && echo '{pubkey}' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys && chmod 700 ~/.ssh")
child.expect(['#', '$'])
child.sendline("mkdir -p /root/articles")
child.expect(['#', '$'])
child.sendline("exit")
Write this to a file with write_file, then run via terminal(VPS_PASSWORD='*' python3 /path/to/script.py).
scp with the identity key: scp -i ~/.ssh/id_rsa_vps /path/to/article.md root@HOST:/root/articles/
for f in article-*.md; do scp -i ~/.ssh/id_rsa_vps "$f" root@HOST:/root/articles/; done
ls -lh /root/articles/ to confirm all files arrived.Pitfall — execute_code sandbox lacks pexpect: The delegated Python sandbox (execute_code) does not have pexpect. Write the SSH key script to a file first, then run via terminal(). pexpect IS available in the terminal environment.
Pitfall — password in script file: Use environment variables (VPS_PASSWORD='*' python3 script.py) instead of hardcoding the password. The script persists on disk.
Pitfall — sshpass unavailable: On many Hermes hosts, sshpass is not installed and apt-get may be locked (non-root user). pexpect is the reliable alternative — do not attempt to install sshpass.
The MEDIA: pattern for long-form content: Briefs routinely exceed Telegram's 4096-char limit (the briefs we produce are 8-12 KB). Do NOT truncate or split into multiple messages. Instead:
MEDIA:/absolute/path/to/brief.md at the end — the platform delivers it as a native file attachment📰 *Revue Quotidienne — JJ mois AAAA*
🔴 *ALERTE principale* : résumé en 1-2 lignes.
🔴 *Alerte secondaire* : résumé en 1-2 lignes.
🟡 Topic 1, 🟡 Topic 2, ...
N signaux intégrés.
MEDIA:/home/sebastien/.hermes/memory/modules/revue_editoriale/briefs/daily/YYYY-MM-DD.md
The MEDIA: path must be absolute. The attachment appears as a downloadable .md file on Telegram (and other platforms that support file attachments). This avoids character limits entirely while keeping the full content accessible.
Pitfall: Do NOT use MEDIA:/path on CLI — the marker renders as literal text. Only use it when delivering via send_message(target='telegram:...') or similar platform channels.
Update 00_index.md:
briefs/special/, not under briefs/weekly/ or briefs/daily/. Check the existing tree structure for sibling examples before inserting new lines.topics/ should also have a row in the Suivis Actifs table, even new ones (🆕 status)After all memory files are updated, run the editorial feedback procedure documented in memory/style/writing_preferences.md (section: Apprentissage continu):
rejected_patterns.mdquality_examples.md if they're reference-worthyrejected_patterns.md if a banned expression was usedwriting_preferences.md if user provided explicit feedbackrevision_count in the frontmatter of modified feedback filesThis creates a virtuous cycle: each review improves the next. Skipping this phase means rejected patterns repeat in the next review, quality stagnates, and the user eventually corrects the same issues again. This phase is mandatory, not optional — if under time constraint, spend 2 minutes minimum: scan rejected_patterns.md for any new violations and check one quality example.
The 6 feedback files and their purposes:
| File | Purpose |
|---|---|
writing_preferences.md | Profil intellectuel, ton, check-list pré-production. Updated when user corrects style/format. |
quality_examples.md | Corpus de référence : exemples classés (à imiter / à améliorer) avec raisons. Updated after each notable review. |
rejected_patterns.md | Patterns bannis : formulations, angles morts, mots interdits. Updated when a pattern repeats. |
stylistic_rules.md | Règles de style : voix, structure, ton, vocabulaire, lisibilité, marquage. Stable — only updated when constitution changes. |
analytical_depth.md | 4 niveaux de profondeur L1-L4 + grille d'approfondissement. Audited during verification. |
historical_connections.md | Grille temporelle + chronologies TODO. Each review should advance at least one chronology. |
When the user asks to "scan the kanban" and "process pending tasks", run this workflow to clean up the module's task board. This is distinct from the review cycle — it's a maintenance pass that can happen between reviews.
Trigger: User says "scan le kanban", "traite les taches en attente", "fais le point sur le backlog", or any variant asking to process the module's task list.
briefs/daily/)briefs/index_briefs.md — chronological index of all reviews producedbriefs/index_topics.md — subject index with status, priority, last signal datebriefs/template_daily.md — reusable daily brief template (copy from an existing brief structure)memory/index.md — add entries for new files00_index.md — update the tree and status tablekanban.md — reflect the new stateBacklog item: "Créer un index des briefs"
→ Audit: 4 daily + 1 weekly brief exist, listed in 00_index.md but no dedicated index
→ Action: Create briefs/index_briefs.md with chronological table, stats
→ Reference: Update memory/index.md, kanban.md
→ Classification: Terminé — index_briefs.md created
chmod 664, every new directory chmod 775. See Phase 7 for full permissions handling./tmp/.md and /opt/data/.md files — either integrate them into sources/ or clean them up. Prefer returning results via subagent summary (inline text) to avoid scattered files entirely.Liens entries, the patch tool may introduce pipe (|) characters instead of dashes (-). This creates broken list formatting. Always verify list markers after patching: grep '^[|-] Brief' <topic_file>./memory is a separate mount, it may have different ownership. If writes fail, tell the user to run chown -R <hermes_uid>:<hermes_gid> /memory from the Docker host. Do not attempt sudo/root inside the container.patch tool may introduce additional pipe characters on replaced rows — e.g. | row | becomes || row ||, or ||| row | becomes |||| row |||| on subsequent edits. Each patch compounds the extras by one pipe. This breaks table rendering. Two sub-cases:| (single pipe): patches produce || (double). Fix: patch back to single |.||| (triple pipe) — the Suivis Actifs table in 00_index.md uses this format. Patches produce |||| (quadruple). Fix: match the original prefix count, not single-pipe.Always verify table rows after every patch batch: grep -nP '^\|{4,}' <file> to catch prefixes that are longer than the original format (this catches ||| → |||| escalation). Fix by patching all affected rows at once with the correct prefix count. Do not patch rows one at a time — each individual patch re-escalates the pipe count. Batch all corrections into a single new_string spanning the full table section.
Special characters (emoji, arrows) make matching harder — when a table-row patch fails, include the surrounding header row and separator row in old_string for a broader match.
\n in patched files. When the patch tool's new_string contains a literal \n sequence (rather than an actual newline), it writes the literal characters \n into the file instead of a line break. This produces a single line like || row1 🆕\\n|| row2 🆕| instead of two lines. To fix: patch again replacing the literal \\n with an actual line break. To prevent: always use genuine newlines in new_string, never the escape sequence \n.00_index.md may not list all files that actually exist on disk. When catching up on missing briefs, always trust the filesystem listing (ls briefs/daily/) over the index. Files may have been created without updating the index. After writing new briefs, the index must be patched to reflect the actual file set.patch tool's fuzzy matcher detects content near the match boundary as "related" to the old_string, it may silently delete lines that were NOT included in your old_string. In this session, - NVIDIA/skills ajouté aux taps Skills Hub de confiance — a line preceding the matched block by one line — was dropped. The fuzzy matcher absorbed it into the replacement. This is distinct from anti-pattern 6 (pipe vs dash) — here the content is lost entirely, not just reformatted. Mitigation: after every patch, verify the 5 lines above and 5 lines below the edit site with a targeted grep or read_file. If content went missing, restore it with a second patch. When the match boundary is ambiguous (e.g. a bullet list or a table in the middle of other content), include MORE surrounding context in old_string to pin the match precisely.patch a file that was last read_file'd with offset and limit (partial read), the patch tool warns: "was last read with offset/limit pagination (partial view). Re-read the whole file before overwriting it." More importantly, patches may behave unpredictably on file views that don't reflect the full current state — especially if the file was modified between the partial read and the patch. Mitigation: after any write/update to a file, do a full read_file (no offset/limit) before the next patch on that same file.1., 2., ...), the patch tool matches on content, not number. If your old_string includes numbered items that exist in the file, those items are replaced — not spliced. This silently drops items you didn't intend to touch if they happen to fall within the matched span. Two failure modes:new_string with new numbers like 12., 13., 14. but omitting an existing item whose number range overlapped the match — that item vanishes.9. entries).new_string, carrying forward any existing items you want to keep. After patching, verify the list has no duplicate numbers: grep -nP '^\d+\.' <file> | sort | uniq -d (catches duplicates) and grep -nP '^\d+\.' <file> | wc -l vs expected count (catches missing items). If numbers are cosmetic (markdown renders them in order regardless), this is a formatting issue — fix the numbers for human readability.ddgs) is the configured web search backend, web_extract silently fails — it cannot fetch page content. This manifests mid-research as a blocker: you have promising search result titles but zero detail. Mitigate by either (a) using browser_navigate + browser_snapshot(full=True) as a fallback for the top 1-2 results per topic, or (b) configuring a URL-extraction-capable backend (Firecrawl, Tavily, Exa) in config.yaml under web.extract_backend. See references/browser-content-extraction.md for the full browser workflow.These rules are enforced by the style/stylistic_rules.md system and MUST be followed in every review:
style/analytical_depth.md## Chronologie section with 3+ datesstyle/historical_connections.md for guidancebriefs/daily/YYYY-MM-DD.md (daily) or briefs/weekly/YYYY-MM-DD.md (weekly) with complete structureBriefs quotidiens: N (dates))week: field in frontmatter is populated with the date rangestatus: brouillon) if a topic has grown too large or covers distinct ecosystems00_index.md table rows verified: grep -rn '^||' memory/ returns nothing across the whole vault (no double-pipe prefixes)find /memory/ -name '*.md' -not -perm 644 -a -not -perm 664 -a -not -perm 775 -a -not -perm 755 | head -5 returns nothing (no files with owner-only permissions)00_index.md updated with new files, priorities, and dates — verify parent placement in tree (special briefs under briefs/special/, analyses under analyses/)YYYY-MM-DD.md for daily briefs, YYYY-MM-DD-special-<slug>.md for specials, contre-epreuve-<slug>-YYYY-MM-DD.md for analyses) — rename any files that deviatesources/ or deletedrejected_patterns.md §3)quality_examples.md and rejected_patterns.md updated if applicableThe user has provided the following instruction alongside the skill invocation: [IMPORTANT: You are running as a scheduled cron job. DELIVERY: Your final response will be automatically delivered to the user — do NOT use send_message or try to deliver the output yourself. Just produce your report/output as your final response and the system handles the rest. SILENT: If there is genuinely nothing new to report, respond with exactly "[SILENT]" (nothing else) to suppress delivery. Never combine [SILENT] with content — either report your findings normally, or say [SILENT] and nothing more.]
The following is the most recent output from a preceding cron job. Use it as context for your analysis.
# Cron Job: Veille quotidienne d'actualités
**Job ID:** 76ce033272c7
**Run Time:** 2026-06-25 16:07:04
**Schedule:** 0 16 * * *
## Prompt
[IMPORTANT: The user has invoked the "news-briefings" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]
---
name: news-briefings
description: "Research current events and deliver formatted news briefings to the user's messaging platform (Telegram/WhatsApp). Covers one-shot news queries and recurring cron-based news briefings — aggregating from RSS feeds via curl, structuring summaries by region/topic, and delivering to the user's preferred channel."
version: 1.3.0
metadata:
hermes:
tags: [news, current-events, rss, briefing, delivery, telegram, whatsapp, cron]
prerequisites: {}
---
# News Briefings
Research and deliver current-events briefings to the user's messaging platform. Supports two modes:
- **One-shot** — user asks "what's happening?" in a live conversation
- **Recurring cron** — automated daily/weekly briefings via the cronjob tool
## Mode 1: One-shot queries (live conversation)
### 1. Gather news data
When the user asks for news/current events, gather from multiple RSS feeds using `curl` (browser dependency may be unavailable). Good free RSS sources:
BBC World: https://feeds.bbci.co.uk/news/world/rss.xml
NYT World: https://rss.nytimes.com/services/xml/rss/nyt/World.xml
Le Monde: https://www.lemonde.fr/international/rss_full.xml
France Info: https://www.francetvinfo.fr/monde.rss (⚠️ /monde/rss → 404, utiliser .rss)
France 24: https://www.france24.com/fr/rss
Hacker News: https://news.ycombinator.com/
Apple Newsroom: https://www.apple.com/newsroom/
The Guardian: https://www.theguardian.com/tone/news
⚠️ **Avoid sources with aggressive anti-bot protection** — Some sites (Reuters/DataDome, Google News, DuckDuckGo, Bing, Searx) are systematically blocked from certain server IPs. When `curl` hits these, the connection may get a TCP reset that propagates as a "Broken pipe" (Errno 32) crash, killing the whole agent run — far worse than a simple HTTP error. For **one-shot queries**, if these fail, just skip them. For **cron jobs**, remove them from the prompt entirely — a runtime error crashes the agent, and "skip and continue" won't help if curl itself segfaults on a blocked domain.
Use `delegate_task` for heavier research (multiple feeds → extraction → structured summary) so the main thread stays responsive. Pass `toolsets=["terminal"]` for simple RSS parsing.
For parallel research on multiple topics, use the tasks= array form:
delegate_task(tasks=[
{"goal": "Research {topic A}", "toolsets": ["terminal"]},
{"goal": "Research {topic B}", "toolsets": ["terminal"]},
])
This runs up to 3 subagents concurrently.
**Pitfall:** `delegate_task` is for ONE-SHOT queries only. Do NOT use it in cron job prompts — cron agents cannot use delegate_task.
### 2. Structure the summary
Format with clear sections by region/topic. Use emoji markers per region:
- 🔴 Middle East / Israel-Palestine-Lebanon
- 🟠 Ukraine / Russia
- 🟡 Americas / US politics
- 🔵 Asia-Pacific / China / Taiwan / Japan / Korea
- 🟢 Africa
- 🟣 Europe / EU
- ⚪ Defense / Alliances (NATO, AUKUS)
- 🇫🇷 France — national news (important for francophone users)
- Other notable stories as bullet points
Each section: 2-3 bullet points max. Bold the headline item (not the emoji). Include source attribution at end.
### 3. Delivery to messaging platform
Check the user's preferred delivery platform:
- User profile memory notes their preferred platforms
- If unspecified, default to Telegram if configured, then WhatsApp
The `send_message(action='list')` may not show platforms even when they're configured. If the platform is defined in `.env` (TELEGRAM_BOT_TOKEN, etc.), try `send_message(target='telegram')` directly — it routes to the home channel.
## Mode 2: Recurring cron briefings
Set up automated daily/weekly briefings that deliver directly to the user's messaging platform on a schedule.
### Creating the job
Use `cronjob(action='create')` with:
- **prompt** — self-contained instructions (the cron agent has no conversation context)
- **skills** — pass `skills=["news-briefings"]` to load this skill automatically
- **schedule** — `"0 16 * * *"` for daily at 16h, `"0 16 * * 0"` for Sundays
- **deliver** — where to send the result:
- `"origin"` (default) — auto-routes to the conversation's origin platform (Telegram/WhatsApp if the conversation came from there, CLI otherwise)
- `"telegram:CHAT_ID"` — explicit Telegram delivery (e.g., `"telegram:8818654814"`)
- `"local"` — no delivery, saved locally
- `"whatsapp:PHONE"` — explicit WhatsApp delivery
Example with explicit Telegram delivery:
cronjob(
action='create',
name='Veille quotidienne d\'actualités',
prompt="Génère un briefing quotidien en français...",
schedule='0 16 *',
skills=['news-briefings'],
deliver='telegram:8818654814',
)
### Rules for cron prompts
- **Fully self-contained** — no conversation context. Include the complete workflow: which RSS feeds to curl, how to structure, what delivery format
- **No `delegate_task`** — cron sessions cannot spawn subagents
- **No `execute_code`** — cron sessions cannot use execute_code (blocked for security). Use `write_file` or `terminal` for file operations instead.
- **No `clarify`** — cron agents cannot ask the user questions
- **No `send_message` needed** — the job's final response auto-delivers to the target platform
- **Include feed fallbacks** in the prompt: if a feed returns empty or errors, skip it and continue with the others
- **Francophone users** — write the entire prompt in French
### Troubleshooting
#### Cron job not running despite being scheduled
If `cronjob(action='list')` shows the job as `state: scheduled` but `last_run_at` stays `null` even after `next_run_at` has passed:
- **Cause:** The cron scheduler only runs inside the Hermes **gateway**. CLI-only instances do not have an active scheduler.
- **Fix:**
- Start the gateway: `hermes gateway start` or `hermes gateway install`
- Or keep cron jobs on a docker instance that already has a running gateway
- **Verify:** `hermes gateway status` — if not running, the scheduler is not active
#### Intermittent Broken pipe on feeds that normally work
Even feeds classified as "reliable" (BBC, Le Monde, France 24) can intermittently trigger a Broken pipe (Errno 32) crash in cron context. This happens when a CDN or reverse proxy intermittently sends a TCP reset — the curl subprocess crashes before any "skip on error" logic runs.
- Root cause: Same as blocked-domain Broken pipe — any TCP reset during curl's connect/read phase kills the subprocess. The agent never gets a chance to try the next feed.
- Workaround for cron: Add a script-based data-collection step (`no_agent=False`, `script=path`) that curl-fetches feeds sequentially in a single bash script with `|| true` fallbacks, then let the LLM summarize the collected data. This isolates the crash risk to the script layer.
- Emergency fallback: If the cron job has been failing for at least 1 day and the user asks for news, switch to one-shot mode (live conversation) using `delegate_task(tasks=[...], toolsets=["terminal"])`. Fetch the feeds in parallel subagents (max 3), each handles its own curl failure gracefully. Then compose and deliver the briefing manually. This bypasses the Broken pipe crash because each subagent's curl crash is isolated.
#### Cron job shows `last_status: "error"` with delivery error about "cannot schedule new futures"
When `last_status: "error"` AND the delivery error contains `"cannot schedule new futures after interpreter shutdown"` or `"Broken pipe"` appears in the output log:
- **Root cause:** The cron agent **crashed** during execution before it could produce output. The delivery error is a downstream symptom — it can't send an empty/broken result
[... output truncated ...]
Tu produis un brief quotidien structuré pour Sébastien, basé sur les données RSS injectées via context_from (output du cron 76ce033272c7 — Veille quotidienne d'actualités). Ces données sont fiables et déjà collectées.
NE fais PAS tes propres curls ou web_search — utilise uniquement les données RSS injectées dans le contexte.
Structure :
Archive le brief dans briefs/daily/YYYY-MM-DD.md du module revue_editoriale.
📰 Revue Quotidienne — 25 juin 2026
🔴 Venezuela : double séisme 164+ morts — Deux tremblements de terre magnitude 7,2/7,5 frappent Caracas. Le plus puissant depuis 126 ans. Aéroport endommagé, immeubles effondrés. ONU envoie des secours. Pays en transition depuis la capture de Maduro.
🔴 Iran : Trump demande $milliards au Congrès — Mais le budget fait face à l'opposition républicaine. L'Iran met en garde sur Ormuz. Paradoxe : le pétrole revient au niveau pré-guerre — les marchés anticipent une normalisation que la politique ne confirme pas.
🔴 Israël-Liban : retrait conditionné au désarmement du Hezbollah — Frappe drone : 3 morts sud Liban. Le cessez-feu du 19 juin continue de se fragiliser.
🟡 Canicule Europe — France alerte sanitaire maximale (premiers décès chez les jeunes). Espagne : 212 morts en 4 jours. Vague vers l'Allemagne (40°C).
🟡 Ukraine : 5e pétrolier flotte fantôme intercepté par la Marine française. Biélorussie joue l'apaisement face à l'ultimatum Zelensky. Conférence reconstruction : Kiev-Varsovie réconciliés.
🟡 TotalEnergies condamné — Le tribunal de Paris ordonne de comptabiliser les émissions carbone des clients (devoir de vigilance, scope 3 étendu). Précédent inédit.
🟢 IBM dévoile première puce <1nm — Architecture révolutionnaire mais pas encore prête production.
28 signaux intégrés (8🔴, 9🟡, 7🟢, 2🇫🇷, 2🇺🇦). 5 contre-épreuves documentées. 5 hypothèses nouvelles. RSS archivé dans sources/rss/2026-06-25.md.
Fichiers mis à jour : geopolitique_iran.md, ia_souveraine.md, 00_index.md.
MEDIA:/home/sebastien/.hermes/profiles/revue_editoriale/modules_revues/briefs/daily/2026-06-25.md